Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2018-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / MPEG-2 TS mux filter
9 : *
10 : * GPAC is free software; you can redistribute it and/or modify
11 : * it under the terms of the GNU Lesser General Public License as published by
12 : * the Free Software Foundation; either version 2, or (at your option)
13 : * any later version.
14 : *
15 : * GPAC is distributed in the hope that it will be useful,
16 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : * GNU Lesser General Public License for more details.
19 : *
20 : * You should have received a copy of the GNU Lesser General Public
21 : * License along with this library; see the file COPYING. If not, write to
22 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 : *
24 : */
25 :
26 : #include <gpac/filters.h>
27 : #include <gpac/constants.h>
28 : #include <gpac/mpegts.h>
29 : #include <gpac/iso639.h>
30 : #include <gpac/webvtt.h>
31 :
32 :
33 : #define M2TS_FILE_EXTS "ts|m2t|mts|dmb|trp"
34 : #define M2TS_MIMES "video/mpeg-2|video/mp2t|video/mpeg|audio/mp2t"
35 :
36 :
37 1489 : void mux_assign_mime_file_ext(GF_FilterPid *ipid, GF_FilterPid *opid, const char *file_exts, const char *mime_types, const char *def_ext)
38 : {
39 : Bool found=GF_FALSE;
40 : const GF_PropertyValue *p;
41 :
42 1489 : p = gf_filter_pid_get_property(ipid, GF_PROP_PID_FILE_EXT);
43 1489 : if (p) {
44 880 : char *match = strstr(file_exts, p->value.string);
45 880 : if (match) {
46 295 : u32 slen = (u32) strlen(match);
47 295 : if (!match[slen-1] || (match[slen-1]=='|'))
48 : found = GF_TRUE;
49 : }
50 : }
51 : if (!found)
52 1489 : gf_filter_pid_set_property(opid, GF_PROP_PID_FILE_EXT, &PROP_STRING(def_ext ? (char *)def_ext : "*") );
53 :
54 1489 : p = gf_filter_pid_get_property(ipid, GF_PROP_PID_MIME);
55 : found = GF_FALSE;
56 1489 : if (p) {
57 869 : char *match = strstr(mime_types, p->value.string);
58 869 : if (match) {
59 623 : u32 slen = (u32) strlen(match);
60 623 : if (!match[slen-1] || (match[slen-1]=='|'))
61 : found = GF_TRUE;
62 : }
63 : }
64 : if (!found)
65 1489 : gf_filter_pid_set_property(opid, GF_PROP_PID_MIME, &PROP_STRING("*") );
66 1489 : }
67 :
68 :
69 :
70 : enum
71 : {
72 : TEMI_TC64_AUTO=0,
73 : TEMI_TC64_OFF,
74 : TEMI_TC64_ALWAYS,
75 : };
76 :
77 :
78 : typedef struct
79 : {
80 : u64 sap_time;
81 : u64 offset;
82 : u32 nb_pck;
83 : u32 sap_type;
84 : u64 min_pts_plus_one;
85 : u64 max_pts;
86 : } TS_SIDX;
87 :
88 : typedef struct
89 : {
90 : //filter args
91 : u32 pmt_id, pmt_rate, pmt_version, sdt_rate, breq, mpeg4;
92 : u64 pcr_offset, first_pts;
93 : u32 rate, pat_rate, repeat_rate, repeat_img, max_pcr, nb_pack, sid, bifs_pes;
94 : GF_M2TS_PackMode pes_pack;
95 : Bool flush_rap, realtime, pcr_only, disc, latm;
96 : s64 pcr_init;
97 : char *name, *provider, *temi;
98 : u32 log_freq;
99 : s32 subs_sidx;
100 :
101 : //internal
102 : GF_FilterPid *opid;
103 : GF_FilterPid *idx_opid;
104 : GF_Filter *idx_filter;
105 : GF_M2TS_Mux *mux;
106 :
107 : GF_List *pids;
108 :
109 : Bool check_pcr;
110 : Bool update_mux;
111 : char *pack_buffer;
112 : u64 nb_pck;
113 : Bool init_buffering;
114 : u32 last_log_time;
115 : Bool pmt_update_pending;
116 :
117 : u32 dash_mode;
118 : Bool init_dash;
119 : u32 dash_seg_num;
120 : Bool wait_dash_flush;
121 : Bool dash_file_switch;
122 : Bool next_is_start;
123 : u32 nb_pck_in_seg;
124 : u64 pck_start_idx;
125 :
126 : char dash_file_name[GF_MAX_PATH];
127 : char idx_file_name[GF_MAX_PATH];
128 :
129 : //dash indexing
130 : u32 nb_sidx_entries, nb_sidx_alloc;
131 : TS_SIDX *sidx_entries;
132 : GF_BitStream *idx_bs;
133 : u32 nb_pck_in_file, nb_pck_first_sidx, ref_pid;
134 : u64 total_bytes_in;
135 :
136 : u32 nb_suspended, cur_file_idx_plus_one;
137 : char *cur_file_suffix;
138 : Bool notify_filename;
139 : } GF_TSMuxCtx;
140 :
141 : typedef struct
142 : {
143 : u32 id, delay, timescale;
144 : u64 offset, init_val;
145 : char *url;
146 : Bool ntp, use_init_val;
147 : u32 mode_64bits;
148 : u64 cts_at_init_val_plus_one;
149 : } TEMIDesc;
150 :
151 : typedef struct
152 : {
153 : GF_ESInterface esi;
154 : GF_FilterPid *ipid;
155 : GF_M2TS_Mux_Stream *mstream;
156 : GF_M2TS_Mux_Program *prog;
157 :
158 : u32 sid;
159 : u32 codec_id;
160 : u32 pmt_pid;
161 : u32 nb_pck;
162 : Bool is_repeat;
163 : u32 nb_repeat_last;
164 :
165 : GF_TSMuxCtx *ctx;
166 : u32 last_cv;
167 : //ts media skip
168 : s64 media_delay, max_media_skip;
169 : Bool done;
170 :
171 : GF_List *temi_descs;
172 :
173 : u32 last_temi_url;
174 : u8 *af_data;
175 : u32 af_data_alloc;
176 : GF_BitStream *temi_af_bs;
177 :
178 : Bool rewrite_odf;
179 : Bool has_seen_eods;
180 : u32 pck_duration;
181 :
182 : u64 loop_ts_offset;
183 : u64 last_dts;
184 : u32 last_dur;
185 :
186 : u8 *pck_data_buf;
187 :
188 : u32 suspended;
189 : } M2Pid;
190 :
191 1179 : static GF_Err tsmux_format_af_descriptor(GF_BitStream *bs, u32 timeline_id, u64 timecode, u32 timescale, u32 mode_64bits, u64 ntp, const char *temi_url, u32 temi_delay, u32 *last_url_time)
192 : {
193 : u32 len;
194 : u32 last_time;
195 1179 : if (ntp) {
196 0 : last_time = 1000*(ntp>>32);
197 0 : last_time += 1000*(ntp&0xFFFFFFFF)/0xFFFFFFFF;
198 : } else {
199 1179 : last_time = (u32) (1000*timecode/timescale);
200 : }
201 1179 : if (temi_url && (!*last_url_time || (last_time - *last_url_time + 1 >= temi_delay)) ) {
202 21 : u64 start = gf_bs_get_position(bs);
203 : u64 end;
204 21 : *last_url_time = last_time + 1;
205 : len = 0;
206 21 : gf_bs_write_int(bs, GF_M2TS_AFDESC_LOCATION_DESCRIPTOR, 8);
207 21 : gf_bs_write_int(bs, len, 8);
208 :
209 21 : gf_bs_write_int(bs, 0, 1); //force_reload
210 21 : gf_bs_write_int(bs, 0, 1); //is_announcement
211 21 : gf_bs_write_int(bs, 0, 1); //splicing_flag
212 21 : gf_bs_write_int(bs, 0, 1); //use_base_temi_url
213 21 : gf_bs_write_int(bs, 0xFF, 5); //reserved
214 21 : gf_bs_write_int(bs, timeline_id, 7); //timeline_id
215 :
216 21 : if (strlen(temi_url)) {
217 : char *url = (char *)temi_url;
218 21 : if (!strnicmp(temi_url, "http://", 7)) {
219 21 : gf_bs_write_int(bs, 1, 8); //url_scheme
220 21 : url = (char *) temi_url + 7;
221 0 : } else if (!strnicmp(temi_url, "https://", 8)) {
222 0 : gf_bs_write_int(bs, 2, 8); //url_scheme
223 0 : url = (char *) temi_url + 8;
224 : } else {
225 0 : gf_bs_write_int(bs, 0, 8); //url_scheme
226 : }
227 21 : gf_bs_write_u8(bs, (u32) strlen(url)); //url_path_len
228 21 : gf_bs_write_data(bs, url, (u32) strlen(url) ); //url
229 21 : gf_bs_write_u8(bs, 0); //nb_addons
230 : }
231 : //rewrite len
232 21 : end = gf_bs_get_position(bs);
233 21 : len = (u32) (end - start - 2);
234 21 : gf_bs_seek(bs, start+1);
235 21 : gf_bs_write_int(bs, len, 8);
236 21 : gf_bs_seek(bs, end);
237 : }
238 :
239 1179 : if (timescale || ntp) {
240 : Bool use64;
241 1179 : if (mode_64bits==TEMI_TC64_AUTO) {
242 1179 : use64 = (timecode > 0xFFFFFFFFUL) ? GF_TRUE : GF_FALSE;
243 0 : } else if (mode_64bits==TEMI_TC64_ALWAYS) {
244 : use64 = GF_TRUE;
245 : } else {
246 : use64 = GF_FALSE;
247 0 : while (timecode > 0xFFFFFFFFUL) {
248 0 : timecode -= 0xFFFFFFFFUL;
249 : }
250 :
251 : }
252 :
253 : len = 3; //3 bytes flags
254 :
255 1179 : if (timescale) len += 4 + (use64 ? 8 : 4);
256 1179 : if (ntp) len += 8;
257 :
258 : //write timeline descriptor
259 1179 : gf_bs_write_int(bs, GF_M2TS_AFDESC_TIMELINE_DESCRIPTOR, 8);
260 1179 : gf_bs_write_int(bs, len, 8);
261 :
262 1179 : gf_bs_write_int(bs, timescale ? (use64 ? 2 : 1) : 0, 2); //has_timestamp
263 1179 : gf_bs_write_int(bs, ntp ? 1 : 0, 1); //has_ntp
264 1179 : gf_bs_write_int(bs, 0, 1); //has_ptp
265 1179 : gf_bs_write_int(bs, 0, 2); //has_timecode
266 1179 : gf_bs_write_int(bs, 0, 1); //force_reload
267 1179 : gf_bs_write_int(bs, 0, 1); //paused
268 1179 : gf_bs_write_int(bs, 0, 1); //discontinuity
269 1179 : gf_bs_write_int(bs, 0xFF, 7); //reserved
270 1179 : gf_bs_write_int(bs, timeline_id, 8); //timeline_id
271 1179 : if (timescale) {
272 1179 : gf_bs_write_u32(bs, timescale); //timescale
273 1179 : if (use64)
274 0 : gf_bs_write_u64(bs, timecode); //timestamp
275 : else
276 1179 : gf_bs_write_u32(bs, (u32) timecode); //timestamp
277 : }
278 1179 : if (ntp) {
279 0 : gf_bs_write_u64(bs, ntp); //ntp
280 : }
281 : }
282 1179 : return GF_OK;
283 : }
284 :
285 :
286 7 : GF_SLConfig *tsmux_get_sl_config(GF_TSMuxCtx *ctx, u32 timescale, GF_SLConfig *slc)
287 : {
288 7 : if (!slc) slc = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG);
289 7 : slc->predefined = 0;
290 7 : slc->useAccessUnitStartFlag = 1;
291 7 : slc->useAccessUnitEndFlag = 1;
292 7 : slc->useRandomAccessPointFlag = 1;
293 7 : slc->useTimestampsFlag = 1;
294 7 : slc->timestampLength = 33;
295 7 : slc->timestampResolution = timescale;
296 :
297 : /*test mode in which time stamps are 90khz and not coded but copied over from PES header*/
298 7 : if (ctx->bifs_pes==2) {
299 0 : slc->timestampLength = 0;
300 0 : slc->timestampResolution = 90000;
301 : }
302 7 : return slc;
303 : }
304 :
305 1 : static void tsmux_rewrite_odf(GF_TSMuxCtx *ctx, GF_ESIPacket *es_pck)
306 : {
307 : u32 com_count, com_index, od_count, esd_index, od_index;
308 : GF_ODUpdate *odU;
309 : GF_ESDUpdate *esdU;
310 : GF_ESD *esd;
311 1 : GF_ODCodec *od_codec = gf_odf_codec_new();
312 :
313 1 : gf_odf_codec_set_au(od_codec, es_pck->data, es_pck->data_len);
314 1 : gf_odf_codec_decode(od_codec);
315 1 : com_count = gf_list_count(od_codec->CommandList);
316 2 : for (com_index = 0; com_index < com_count; com_index++) {
317 1 : GF_ODCom *com = (GF_ODCom *)gf_list_get(od_codec->CommandList, com_index);
318 1 : switch (com->tag) {
319 1 : case GF_ODF_OD_UPDATE_TAG:
320 : odU = (GF_ODUpdate*)com;
321 1 : od_count = gf_list_count(odU->objectDescriptors);
322 2 : for (od_index=0; od_index<od_count; od_index++) {
323 1 : GF_ObjectDescriptor *od = (GF_ObjectDescriptor *)gf_list_get(odU->objectDescriptors, od_index);
324 1 : esd_index = 0;
325 3 : while ( (esd = gf_list_enum(od->ESDescriptors, &esd_index)) ) {
326 : assert(esd->slConfig);
327 1 : esd->slConfig = tsmux_get_sl_config(ctx, esd->slConfig->timestampResolution, esd->slConfig);
328 : }
329 : }
330 : break;
331 0 : case GF_ODF_ESD_UPDATE_TAG:
332 : esdU = (GF_ESDUpdate*)com;
333 0 : esd_index = 0;
334 0 : while ( (esd = gf_list_enum(esdU->ESDescriptors, &esd_index)) ) {
335 : assert(esd->slConfig);
336 0 : esd->slConfig = tsmux_get_sl_config(ctx, esd->slConfig->timestampResolution, esd->slConfig);
337 : }
338 : break;
339 : }
340 : }
341 1 : gf_odf_codec_encode(od_codec, 1);
342 1 : es_pck->data = NULL;
343 1 : es_pck->data_len = 0;
344 1 : gf_odf_codec_get_au(od_codec, &es_pck->data, &es_pck->data_len);
345 1 : gf_odf_codec_del(od_codec);
346 :
347 1 : }
348 :
349 60277 : static GF_Err tsmux_esi_ctrl(GF_ESInterface *ifce, u32 act_type, void *param)
350 : {
351 : u32 cversion;
352 : u64 cts, cts_diff;
353 60277 : M2Pid *tspid = (M2Pid *)ifce->input_udta;
354 60277 : if (!tspid) return GF_BAD_PARAM;
355 :
356 60277 : switch (act_type) {
357 60277 : case GF_ESI_INPUT_DATA_FLUSH:
358 : {
359 : u64 dts;
360 : GF_ESIPacket es_pck;
361 : const GF_PropertyValue *p;
362 : GF_FilterPacket *pck;
363 60277 : pck = gf_filter_pid_get_packet(tspid->ipid);
364 : //if PMT update is pending after this packet fetch (reconfigure), don't send the packet
365 100702 : if (tspid->ctx->pmt_update_pending) return GF_OK;
366 :
367 60277 : if (!pck) {
368 15165 : if (gf_filter_pid_is_eos(tspid->ipid)) {
369 67 : if (tspid->ctx->realtime
370 0 : && tspid->ctx->repeat_img
371 0 : && (tspid->nb_pck==1)
372 0 : && (tspid->esi.stream_type==GF_STREAM_VISUAL)
373 : ) {
374 0 : tspid->nb_repeat_last++;
375 0 : tspid->is_repeat = GF_TRUE;
376 : } else {
377 67 : tspid->done = GF_TRUE;
378 67 : ifce->caps |= GF_ESI_STREAM_IS_OVER;
379 : }
380 67 : if (tspid->ctx->dash_mode)
381 20 : tspid->has_seen_eods = GF_TRUE;
382 : }
383 : return GF_OK;
384 : }
385 45112 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENUM);
386 45112 : if (tspid->ctx->dash_mode) {
387 :
388 36761 : if (tspid->has_seen_eods)
389 : return GF_OK;
390 :
391 : //detect segment change
392 11647 : if (p && tspid->ctx->dash_seg_num && (tspid->ctx->dash_seg_num < p->value.uint)) {
393 124 : tspid->ctx->wait_dash_flush = GF_TRUE;
394 124 : tspid->ctx->dash_seg_num = p->value.uint;
395 124 : tspid->ctx->dash_file_name[0] = 0;
396 : }
397 :
398 : //segment change is pending, check for filename as well - we don't do that in the previous test
399 : //since the filename property is sent on a single pid, not each of them
400 11647 : if (tspid->ctx->wait_dash_flush && p && (tspid->ctx->dash_seg_num == p->value.uint)) {
401 145 : tspid->has_seen_eods = GF_TRUE;
402 :
403 145 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENAME);
404 145 : if (p) {
405 117 : strcpy(tspid->ctx->dash_file_name, p->value.string);
406 117 : tspid->ctx->dash_file_switch = GF_TRUE;
407 : }
408 : return GF_OK;
409 : }
410 :
411 : //at this point the new segment is started
412 11502 : if (p)
413 156 : tspid->ctx->dash_seg_num = p->value.uint;
414 :
415 11502 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_IDXFILENAME);
416 11502 : if (p) {
417 8 : strcpy(tspid->ctx->idx_file_name, p->value.string);
418 : }
419 :
420 11502 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_EODS);
421 11502 : if (p && p->value.boolean) {
422 0 : tspid->has_seen_eods = GF_TRUE;
423 0 : tspid->ctx->wait_dash_flush = GF_TRUE;
424 0 : gf_filter_pid_drop_packet(tspid->ipid);
425 0 : return GF_OK;
426 : }
427 8351 : } else if (p) {
428 3 : if (!tspid->ctx->cur_file_idx_plus_one) {
429 1 : tspid->ctx->cur_file_idx_plus_one = p->value.uint + 1;
430 1 : if (!tspid->ctx->cur_file_suffix) {
431 1 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILESUF);
432 1 : if (p && p->value.string) tspid->ctx->cur_file_suffix = gf_strdup(p->value.string);
433 : }
434 1 : tspid->ctx->notify_filename = GF_TRUE;
435 2 : } else if (tspid->ctx->cur_file_idx_plus_one == p->value.uint+1) {
436 1 : } else if (tspid->suspended) {
437 : return GF_OK;
438 1 : } else if (!tspid->ctx->notify_filename) {
439 1 : tspid->suspended = GF_TRUE;
440 1 : tspid->ctx->nb_suspended++;
441 1 : if (tspid->ctx->nb_suspended==1) {
442 1 : tspid->ctx->cur_file_idx_plus_one = p->value.uint + 1;
443 1 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILESUF);
444 1 : if (p && p->value.string && !tspid->ctx->cur_file_suffix)
445 1 : tspid->ctx->cur_file_suffix = gf_strdup(p->value.string);
446 : }
447 1 : ifce->caps |= GF_ESI_STREAM_IS_OVER;
448 1 : return GF_OK;
449 : } else {
450 : //notify filename pending
451 : return GF_OK;
452 : }
453 : }
454 :
455 : memset(&es_pck, 0, sizeof(GF_ESIPacket));
456 19852 : es_pck.flags = GF_ESI_DATA_AU_START | GF_ESI_DATA_AU_END | GF_ESI_DATA_HAS_CTS;
457 19852 : es_pck.sap_type = gf_filter_pck_get_sap(pck);
458 19852 : tspid->pck_duration = gf_filter_pck_get_duration(pck);
459 19852 : cversion = gf_filter_pck_get_carousel_version(pck);
460 19852 : if (cversion+1 == tspid->last_cv) {
461 19803 : es_pck.flags |= GF_ESI_DATA_REPEAT;
462 : }
463 19852 : tspid->last_cv = cversion+1;
464 :
465 19852 : if (tspid->is_repeat)
466 0 : es_pck.flags |= GF_ESI_DATA_REPEAT;
467 :
468 19852 : cts = gf_filter_pck_get_cts(pck);
469 19852 : dts = gf_filter_pck_get_dts(pck);
470 19852 : if (dts != GF_FILTER_NO_TS) {
471 19852 : dts += tspid->loop_ts_offset;
472 : //loop detected
473 19852 : if (tspid->last_dts && (dts<tspid->last_dts)) {
474 : dts -= tspid->loop_ts_offset;
475 3 : tspid->loop_ts_offset = tspid->last_dts - dts + tspid->last_dur;
476 3 : dts += tspid->loop_ts_offset;
477 : }
478 : }
479 19852 : if (cts != GF_FILTER_NO_TS) {
480 19852 : cts += tspid->loop_ts_offset;
481 19852 : es_pck.cts = cts;
482 : } else {
483 0 : es_pck.cts = 0;
484 : }
485 :
486 19852 : if (tspid->temi_descs) {
487 676 : u32 i, count=gf_list_count(tspid->temi_descs);
488 :
489 676 : if (!tspid->temi_af_bs) tspid->temi_af_bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
490 673 : else gf_bs_reassign_buffer(tspid->temi_af_bs, tspid->af_data, tspid->af_data_alloc);
491 :
492 1179 : for (i=0; i<count; i++) {
493 1179 : TEMIDesc *temi = gf_list_get(tspid->temi_descs, i);
494 : u64 ntp=0;
495 1179 : u32 timescale = ifce->timescale;
496 : u64 tc;
497 :
498 1179 : if (temi->timescale && (temi->timescale!=timescale)) {
499 : timescale = temi->timescale;
500 : }
501 1179 : if (temi->use_init_val) {
502 173 : if (!temi->cts_at_init_val_plus_one) temi->cts_at_init_val_plus_one = cts+1;
503 :
504 173 : tc = (cts - (temi->cts_at_init_val_plus_one-1));
505 173 : if (timescale != ifce->timescale) {
506 173 : tc *= temi->timescale;
507 173 : tc /= ifce->timescale;
508 : }
509 173 : tc += temi->init_val;
510 : } else {
511 : //TOCHECK: do we want media timeline or composition timeline ?
512 : tc = cts;
513 1006 : if (timescale != ifce->timescale) {
514 0 : tc *= temi->timescale;
515 0 : tc /= ifce->timescale;
516 : }
517 : }
518 :
519 1179 : if (temi->offset) {
520 0 : if (timescale != ifce->timescale)
521 0 : tc += ((u64) temi->offset) * ifce->timescale / 1000;
522 : else
523 0 : tc += temi->offset;
524 : }
525 :
526 1179 : if (temi->ntp) {
527 : u32 sec, frac;
528 0 : gf_net_get_ntp(&sec, &frac);
529 0 : ntp = sec;
530 0 : ntp <<= 32;
531 0 : ntp |= frac;
532 : }
533 1179 : tsmux_format_af_descriptor(tspid->temi_af_bs, temi->id, tc, timescale, temi->mode_64bits, ntp, temi->url, temi->delay, &tspid->last_temi_url);
534 : }
535 676 : gf_bs_get_content_no_truncate(tspid->temi_af_bs, &tspid->af_data, &es_pck.mpeg2_af_descriptors_size, &tspid->af_data_alloc);
536 676 : es_pck.mpeg2_af_descriptors = tspid->af_data;
537 : }
538 19852 : es_pck.cts += tspid->max_media_skip + tspid->media_delay;
539 :
540 19852 : if (tspid->nb_repeat_last) {
541 0 : es_pck.cts += tspid->nb_repeat_last * ifce->timescale * tspid->ctx->repeat_img / 1000;
542 : }
543 :
544 : cts_diff = 0;
545 19852 : if (tspid->prog->cts_offset) {
546 1094 : cts_diff = tspid->prog->cts_offset;
547 1094 : cts_diff *= tspid->esi.timescale;
548 1094 : cts_diff /= 1000000;
549 :
550 1094 : es_pck.cts += cts_diff;
551 : }
552 :
553 19852 : es_pck.dts = es_pck.cts;
554 19852 : if (dts != GF_FILTER_NO_TS) {
555 19852 : tspid->last_dts = dts;
556 : es_pck.dts = dts;
557 19852 : es_pck.dts += tspid->max_media_skip + tspid->media_delay;
558 :
559 19852 : if (es_pck.dts > es_pck.cts) {
560 : u64 diff;
561 : //we don't have reliable dts - double the diff should make sure we don't try to adjust too often
562 5 : diff = cts_diff = 2*(es_pck.dts - es_pck.cts);
563 5 : diff *= 1000000;
564 5 : diff /= tspid->esi.timescale;
565 : assert(tspid->prog->cts_offset <= diff);
566 5 : tspid->prog->cts_offset += (u32) diff;
567 :
568 5 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[M2TSMux] Packet CTS "LLU" is less than packet DTS "LLU", adjusting all CTS by %d / %d!\n", es_pck.cts, es_pck.dts, cts_diff, tspid->esi.timescale));
569 :
570 5 : es_pck.cts += cts_diff;
571 : }
572 19852 : if (tspid->esi.stream_type!=GF_STREAM_VISUAL) {
573 12899 : es_pck.dts += cts_diff;
574 : }
575 :
576 19852 : if (es_pck.dts != es_pck.cts) {
577 1712 : es_pck.flags |= GF_ESI_DATA_HAS_DTS;
578 : }
579 : }
580 19852 : es_pck.data = (char *) gf_filter_pck_get_data(pck, &es_pck.data_len);
581 19852 : es_pck.duration = gf_filter_pck_get_duration(pck);
582 19852 : tspid->last_dur = es_pck.duration;
583 :
584 19852 : if (tspid->rewrite_odf) {
585 1 : tsmux_rewrite_odf(tspid->ctx, &es_pck);
586 : }
587 :
588 19852 : tspid->ctx->total_bytes_in += es_pck.data_len;
589 :
590 19852 : if (tspid->pck_data_buf) gf_free(tspid->pck_data_buf);
591 19852 : tspid->pck_data_buf = NULL;
592 :
593 : //drop formatting for TX3G
594 19852 : if (tspid->codec_id == GF_CODECID_TX3G) {
595 11 : u16 len = es_pck.data[0];
596 11 : len<<=8;
597 11 : len |= es_pck.data[1];
598 11 : es_pck.data += 2;
599 11 : es_pck.data_len = len;
600 : }
601 : //serialize webvtt cue formatting for TX3G
602 19841 : else if (tspid->codec_id == GF_CODECID_WEBVTT) {
603 : u32 i;
604 : u64 start_ts, end_ts;
605 : void webvtt_write_cue(GF_BitStream *bs, GF_WebVTTCue *cue, Bool write_srt);
606 : GF_List *cues;
607 10 : GF_BitStream *bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
608 :
609 10 : start_ts = es_pck.cts * 1000;
610 10 : start_ts /= tspid->esi.timescale;
611 10 : end_ts = (es_pck.cts + es_pck.duration) * 1000;
612 10 : end_ts /= tspid->esi.timescale;
613 :
614 10 : cues = gf_webvtt_parse_cues_from_data(es_pck.data, es_pck.data_len, start_ts, end_ts);
615 15 : for (i = 0; i < gf_list_count(cues); i++) {
616 5 : GF_WebVTTCue *cue = (GF_WebVTTCue *)gf_list_get(cues, i);
617 5 : webvtt_write_cue(bs, cue, GF_FALSE);
618 5 : gf_webvtt_cue_del(cue);
619 : }
620 10 : gf_list_del(cues);
621 10 : gf_bs_get_content(bs, &es_pck.data, &es_pck.data_len);
622 10 : gf_bs_del(bs);
623 10 : tspid->pck_data_buf = es_pck.data;
624 : }
625 : //for TTML we keep the entire payload as a PES packet
626 :
627 19852 : tspid->nb_pck++;
628 19852 : ifce->output_ctrl(ifce, GF_ESI_OUTPUT_DATA_DISPATCH, &es_pck);
629 19852 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[M2TSMux] PID %d: packet %d CTS "LLU"\n", tspid->esi.stream_id, tspid->nb_pck, es_pck.cts));
630 :
631 : //data is copied by muxer for now, should need rewrite to avoid un-needed allocations
632 19852 : gf_filter_pid_drop_packet(tspid->ipid);
633 :
634 19852 : if (tspid->rewrite_odf) {
635 1 : gf_free(es_pck.data);
636 : }
637 :
638 : }
639 19852 : return GF_OK;
640 :
641 : case GF_ESI_INPUT_DESTROY:
642 : return GF_OK;
643 0 : default:
644 0 : return GF_BAD_PARAM;
645 : }
646 : }
647 :
648 :
649 2 : void update_m4sys_info(GF_TSMuxCtx *ctx, GF_M2TS_Mux_Program *prog)
650 : {
651 2 : GF_M2TS_Mux_Stream *stream = prog->streams;
652 :
653 2 : if (prog->iod) gf_odf_desc_del(prog->iod);
654 2 : prog->iod = gf_odf_desc_new(GF_ODF_IOD_TAG);
655 7 : while (stream) {
656 3 : M2Pid *tspid = (M2Pid *)stream->ifce->input_udta;
657 3 : const GF_PropertyValue *p = gf_filter_pid_get_property(tspid->ipid, GF_PROP_PID_IN_IOD);
658 3 : if (p && p->value.boolean) {
659 3 : GF_ESD *esd = gf_odf_desc_esd_new(0);
660 3 : esd->decoderConfig->objectTypeIndication = stream->ifce->codecid;
661 3 : esd->decoderConfig->streamType = stream->ifce->stream_type;
662 3 : esd->ESID = stream->ifce->stream_id;
663 3 : esd->dependsOnESID = stream->ifce->depends_on_stream;
664 3 : if (stream->ifce->decoder_config_size) {
665 2 : esd->decoderConfig->decoderSpecificInfo->data = gf_malloc(sizeof(char)*stream->ifce->decoder_config_size);
666 2 : memcpy(esd->decoderConfig->decoderSpecificInfo->data, stream->ifce->decoder_config, stream->ifce->decoder_config_size);
667 2 : esd->decoderConfig->decoderSpecificInfo->dataLength = stream->ifce->decoder_config_size;
668 : }
669 3 : tsmux_get_sl_config(ctx, stream->ifce->timescale, esd->slConfig);
670 3 : gf_list_add( ((GF_ObjectDescriptor *)prog->iod)->ESDescriptors, esd);
671 : }
672 3 : stream->ifce->sl_config = tsmux_get_sl_config(ctx, stream->ifce->timescale, stream->ifce->sl_config);
673 3 : stream = stream->next;
674 : }
675 2 : }
676 :
677 49 : static void tsmux_setup_esi(GF_TSMuxCtx *ctx, GF_M2TS_Mux_Program *prog, M2Pid *tspid, u32 stream_type)
678 : {
679 : const GF_PropertyValue *p;
680 :
681 49 : memset(&tspid->esi, 0, sizeof(GF_ESInterface));
682 49 : tspid->esi.stream_type = stream_type;
683 :
684 49 : p = gf_filter_pid_get_property(tspid->ipid, GF_PROP_PID_TIMESCALE);
685 49 : tspid->esi.timescale = p->value.uint;
686 :
687 49 : p = gf_filter_pid_get_property(tspid->ipid, GF_PROP_PID_DECODER_CONFIG);
688 49 : if (p) {
689 22 : tspid->esi.decoder_config = p->value.data.ptr;
690 22 : tspid->esi.decoder_config_size = p->value.data.size;
691 : }
692 49 : p = gf_filter_pid_get_property(tspid->ipid, GF_PROP_PID_ID);
693 49 : if (p) tspid->esi.stream_id = p->value.uint;
694 :
695 49 : p = gf_filter_pid_get_property(tspid->ipid, GF_PROP_PID_DEPENDENCY_ID);
696 49 : if (p) tspid->esi.depends_on_stream = p->value.uint;
697 :
698 49 : tspid->esi.codecid = tspid->codec_id;
699 :
700 49 : p = gf_filter_pid_get_property(tspid->ipid, GF_PROP_PID_LANGUAGE);
701 49 : if (p) {
702 2 : s32 idx = gf_lang_find(p->value.string);
703 2 : if (idx>=0) {
704 2 : const char *code = gf_lang_get_3cc(idx);
705 2 : if (code) tspid->esi.lang = GF_4CC(code[0], code[1], code[2], ' ');
706 : }
707 : }
708 :
709 49 : p = gf_filter_pid_get_property(tspid->ipid, GF_PROP_PID_DURATION);
710 49 : if (p) {
711 36 : tspid->esi.duration = (Double) p->value.lfrac.num;
712 36 : tspid->esi.duration /= p->value.lfrac.den;
713 : }
714 49 : p = gf_filter_pid_get_property(tspid->ipid, GF_PROP_PID_BITRATE);
715 49 : if (p) tspid->esi.bit_rate = p->value.uint;
716 : /*repeat rate in ms for carrouseling - 0 if no repeat*/
717 :
718 49 : tspid->esi.repeat_rate = ctx->repeat_rate;
719 49 : p = gf_filter_pid_get_property(tspid->ipid, GF_PROP_PID_CAROUSEL_RATE);
720 49 : if (p) tspid->esi.repeat_rate = p->value.uint;
721 :
722 49 : tspid->rewrite_odf = GF_FALSE;
723 49 : if (tspid->esi.stream_type==GF_STREAM_OD) {
724 1 : tspid->rewrite_odf = GF_TRUE;
725 1 : update_m4sys_info(ctx, prog);
726 48 : } else if (prog->iod) {
727 1 : update_m4sys_info(ctx, prog);
728 : }
729 :
730 49 : tspid->esi.caps = 0;
731 49 : switch (tspid->esi.stream_type) {
732 22 : case GF_STREAM_AUDIO:
733 22 : if (ctx->latm) tspid->esi.caps |= GF_ESI_AAC_USE_LATM;
734 : case GF_STREAM_VISUAL:
735 45 : if (ctx->mpeg4==2) {
736 0 : tspid->esi.caps |= GF_ESI_STREAM_WITHOUT_MPEG4_SYSTEMS;
737 : }
738 : break;
739 : }
740 :
741 49 : tspid->esi.input_ctrl = tsmux_esi_ctrl;
742 49 : tspid->esi.input_udta = tspid;
743 49 : tspid->prog = prog;
744 49 : }
745 :
746 49 : static void tsmux_setup_temi(GF_TSMuxCtx *ctx, M2Pid *tspid)
747 : {
748 : GF_M2TS_Mux_Stream *a_stream;
749 : u32 st_idx=0;
750 : const GF_PropertyValue *p;
751 : char *temi_cfg;
752 :
753 49 : p = gf_filter_pid_get_property_str(tspid->ipid, "tsmux:temi");
754 49 : if (p && (p->type==GF_PROP_STRING)) temi_cfg = p->value.string;
755 49 : else temi_cfg = ctx->temi;
756 :
757 49 : if (!temi_cfg) return;
758 : u32 temi_id=0;
759 :
760 : //find our stream index
761 4 : a_stream = tspid->mstream->program->streams;
762 4 : while (a_stream) {
763 4 : if (tspid->mstream == a_stream) break;
764 0 : st_idx++;
765 0 : a_stream = a_stream->next;
766 : }
767 :
768 6 : while (temi_cfg) {
769 : u32 service_id=0;
770 : u32 temi_delay=1000;
771 6 : u64 temi_offset=0;
772 : u32 temi_timescale=0;
773 : Bool temi_ntp=GF_FALSE;
774 : u32 temi_64bits=TEMI_TC64_AUTO;
775 : Bool temi_use_init_val=GF_FALSE;
776 6 : u64 temi_init_val = 0;
777 : u32 temi_streamtype=0;
778 : u32 temi_index=0;
779 : Bool done=GF_FALSE;
780 : char *sep;
781 :
782 6 : if (!strlen(temi_cfg))
783 : break;
784 :
785 14 : while (temi_cfg[0]=='#') {
786 10 : sep = strchr(temi_cfg+1, '#');
787 10 : if (!sep) {
788 : temi_cfg += 1;
789 : break;
790 : }
791 8 : sep[0] = 0;
792 8 : switch (temi_cfg[1]) {
793 0 : case 'S':
794 0 : service_id = atoi(temi_cfg+2);
795 : break;
796 : case 'N':
797 : temi_ntp = GF_TRUE;
798 : break;
799 0 : case 'D':
800 0 : temi_delay = atoi(temi_cfg+2);
801 : break;
802 0 : case 'O':
803 0 : sscanf(temi_cfg+2, LLU, &temi_offset);
804 : break;
805 2 : case 'I':
806 2 : sscanf(temi_cfg+2, LLU, &temi_init_val);
807 : temi_use_init_val=GF_TRUE;
808 : break;
809 2 : case 'T':
810 4 : temi_timescale = atoi(temi_cfg+2);
811 : break;
812 2 : case 'L':
813 2 : if (temi_cfg[2]=='Y') temi_64bits = TEMI_TC64_ALWAYS;
814 2 : else if (temi_cfg[2]=='N') temi_64bits = TEMI_TC64_OFF;
815 : break;
816 2 : case 'P':
817 : //all pids target
818 2 : if (!temi_cfg[2] || (temi_cfg[2]=='*')) { }
819 2 : else if ((temi_cfg[2]=='v') || (temi_cfg[2]=='V')) { temi_streamtype = GF_STREAM_VISUAL; }
820 0 : else if ((temi_cfg[2]=='a') || (temi_cfg[2]=='A')) { temi_streamtype = GF_STREAM_AUDIO; }
821 0 : else if ((temi_cfg[2]=='t') || (temi_cfg[2]=='T')) { temi_streamtype = GF_STREAM_TEXT; }
822 : else {
823 0 : temi_index = atoi(temi_cfg+2);
824 : }
825 : break;
826 0 : default:
827 : done=GF_TRUE;
828 : break;
829 : }
830 8 : sep[0] = '#';
831 8 : if (done) {
832 : temi_cfg++;
833 : break;
834 : }
835 : temi_cfg = sep;
836 : }
837 6 : if (!temi_cfg || !strlen(temi_cfg)) {
838 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[M2TSMux] Invalid temi syntax, no ID or URL specified\n"));
839 0 : return;
840 : }
841 :
842 6 : sep = strchr(temi_cfg, ',');
843 6 : if (sep) sep[0] = 0;
844 :
845 : done = GF_TRUE;
846 6 : if (temi_index && (temi_index!=st_idx)) done = GF_FALSE;
847 6 : else if (temi_streamtype && (temi_streamtype!=tspid->esi.stream_type)) done = GF_FALSE;
848 5 : else if (service_id && (service_id != tspid->mstream->program->number)) done = GF_FALSE;
849 :
850 : //that's for this stream
851 : if (done) {
852 : TEMIDesc *temi;
853 5 : if (!tspid->temi_descs) tspid->temi_descs = gf_list_new();
854 5 : GF_SAFEALLOC(temi, TEMIDesc);
855 5 : temi->id = atoi(temi_cfg);
856 5 : if (!temi->id) {
857 2 : temi->url = gf_strdup(temi_cfg);
858 2 : temi->id = temi_id+1;
859 : }
860 5 : temi->ntp = temi_ntp;
861 5 : temi->offset = temi_offset;
862 5 : temi->delay = temi_delay;
863 5 : temi->timescale = temi_timescale;
864 5 : temi->mode_64bits = temi_64bits;
865 5 : temi->init_val = temi_init_val;
866 5 : temi->use_init_val = temi_use_init_val;
867 5 : gf_list_add(tspid->temi_descs, temi);
868 : }
869 6 : if (!sep) break;
870 2 : sep[0] = ',';
871 2 : temi_cfg = sep+1;
872 : }
873 : }
874 :
875 49 : static void tsmux_del_stream(M2Pid *tspid)
876 : {
877 49 : if (tspid->temi_descs) {
878 8 : while (gf_list_count(tspid->temi_descs)) {
879 5 : TEMIDesc *temi = gf_list_pop_back(tspid->temi_descs);
880 5 : if (temi->url) gf_free(temi->url);
881 5 : gf_free(temi);
882 : }
883 3 : gf_list_del(tspid->temi_descs);
884 : }
885 49 : if (tspid->af_data) gf_free(tspid->af_data);
886 49 : if (tspid->temi_af_bs) gf_bs_del(tspid->temi_af_bs);
887 :
888 49 : if (tspid->esi.sl_config) gf_free(tspid->esi.sl_config);
889 49 : if (tspid->pck_data_buf) gf_free(tspid->pck_data_buf);
890 49 : gf_free(tspid);
891 49 : }
892 :
893 50 : static GF_Err tsmux_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
894 : {
895 : u32 service_id, codec_id, streamtype;
896 : GF_M2TS_Mux_Stream *ts_stream;
897 : const GF_PropertyValue *p;
898 50 : GF_PropertyEntry *pe=NULL;
899 : M2Pid *tspid=NULL;
900 : char *sname, *pname;
901 : GF_M2TS_Mux_Program *prog;
902 50 : GF_TSMuxCtx *ctx = gf_filter_get_udta(filter);
903 :
904 50 : if (is_remove) {
905 0 : tspid = gf_filter_pid_get_udta(pid);
906 0 : if (!tspid) return GF_OK;
907 : //remove stream - this will update PMT as well
908 0 : gf_m2ts_program_stream_remove(tspid->mstream);
909 : //destroy or object
910 0 : gf_list_del_item(ctx->pids, tspid);
911 0 : tsmux_del_stream(tspid);
912 0 : return GF_OK;
913 : }
914 :
915 50 : if (!gf_filter_pid_check_caps(pid))
916 : return GF_NOT_SUPPORTED;
917 :
918 50 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
919 50 : if (!p) return GF_NOT_SUPPORTED;
920 50 : codec_id = p->value.uint;
921 50 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
922 50 : if (!p) return GF_NOT_SUPPORTED;
923 50 : streamtype = p->value.uint;
924 :
925 50 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SERVICE_ID);
926 50 : service_id = p ? p->value.uint : ctx->sid;
927 :
928 50 : sname = ctx->name;
929 50 : pname = ctx->provider;
930 50 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SERVICE_NAME);
931 50 : if (p) sname = p->value.string;
932 50 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SERVICE_PROVIDER);
933 50 : if (p) pname = p->value.string;
934 :
935 50 : if (!ctx->opid) {
936 37 : ctx->opid = gf_filter_pid_new(filter);
937 37 : gf_filter_pid_set_name(ctx->opid, "ts_mux");
938 : }
939 : //set output properties at init or reconfig
940 50 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG, NULL);
941 50 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG_ENHANCEMENT, NULL);
942 50 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, NULL);
943 50 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
944 50 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, &PROP_UINT(GF_CODECID_FAKE_MP2T));
945 50 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(90000));
946 50 : gf_filter_pid_set_property(ctx->opid, GF_PROP_NO_TS_LOOP, &PROP_BOOL(GF_TRUE));
947 50 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DASH_MODE, NULL);
948 :
949 50 : mux_assign_mime_file_ext(pid, ctx->opid, M2TS_FILE_EXTS, M2TS_MIMES, "ts");
950 :
951 50 : p = gf_filter_pid_get_info(pid, GF_PROP_PID_DASH_MODE, &pe);
952 50 : if (p) {
953 11 : if (!ctx->dash_mode && p->value.uint) ctx->init_dash = GF_TRUE;
954 11 : ctx->dash_mode = p->value.uint;
955 11 : if (ctx->dash_mode) {
956 11 : ctx->mux->flush_pes_at_rap = GF_TRUE;
957 11 : gf_m2ts_mux_set_initial_pcr(ctx->mux, 0);
958 : }
959 : }
960 50 : gf_filter_release_property(pe);
961 :
962 50 : tspid = gf_filter_pid_get_udta(pid);
963 50 : if (!tspid) {
964 49 : GF_SAFEALLOC(tspid, M2Pid);
965 49 : if (!tspid) return GF_OUT_OF_MEM;
966 49 : gf_filter_pid_set_udta(pid, tspid);
967 49 : tspid->ipid = pid;
968 49 : tspid->sid = service_id;
969 49 : tspid->ctx = ctx;
970 49 : gf_list_add(ctx->pids, tspid);
971 49 : gf_filter_pid_set_framing_mode(pid, GF_TRUE);
972 :
973 49 : if (ctx->breq) {
974 : GF_FilterEvent evt;
975 49 : GF_FEVT_INIT(evt, GF_FEVT_BUFFER_REQ, pid);
976 49 : evt.buffer_req.max_buffer_us = 1000*ctx->breq;
977 49 : evt.buffer_req.pid_only = GF_TRUE;
978 49 : gf_filter_pid_send_event(pid, &evt);
979 : }
980 : }
981 :
982 : //do we need a new program
983 50 : prog = gf_m2ts_mux_program_find(ctx->mux, service_id);
984 50 : if (!prog) {
985 : u32 nb_progs;
986 : u64 first_pts_val;
987 : u64 pcr_offset=0;
988 37 : u32 pmt_id = ctx->pmt_id;
989 :
990 37 : if (!pmt_id) pmt_id = 100;
991 37 : nb_progs = gf_m2ts_mux_program_count(ctx->mux);
992 37 : if (nb_progs>1) {
993 0 : if (ctx->dash_mode) {
994 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[M2TSMux] Muxing several programs (%d) in DASH mode is not allowed\n", nb_progs+1));
995 : return GF_BAD_PARAM;
996 : }
997 0 : pmt_id += (nb_progs - 1) * 100;
998 : }
999 :
1000 37 : p = gf_filter_pid_get_property_str(tspid->ipid, "tsmux:pcr_offset");
1001 37 : if (p && (p->type==GF_PROP_LUINT)) {
1002 0 : pcr_offset = p->value.longuint;
1003 37 : } else if (ctx->pcr_offset==(u64)-1) {
1004 36 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_MAX_FRAME_SIZE);
1005 36 : if (p && p->value.uint && ctx->rate) {
1006 1 : Double r = p->value.uint * 8;
1007 1 : r *= 90000;
1008 1 : r/= ctx->rate;
1009 : //add 10% of safety to cover TS signaling and other potential table update while sending the largest PES
1010 1 : r *= 1.1;
1011 1 : pcr_offset = (u64) r;
1012 : }
1013 : } else {
1014 : pcr_offset = ctx->pcr_offset;
1015 : }
1016 37 : p = gf_filter_pid_get_property_str(tspid->ipid, "tsmux:force_pts");
1017 37 : if (p && (p->type==GF_PROP_LUINT)) first_pts_val = p->value.longuint;
1018 37 : else first_pts_val = ctx->first_pts;
1019 :
1020 37 : prog = gf_m2ts_mux_program_add(ctx->mux, service_id, pmt_id, ctx->pmt_rate, pcr_offset, ctx->mpeg4, ctx->pmt_version, ctx->disc, first_pts_val);
1021 :
1022 37 : if (sname) gf_m2ts_mux_program_set_name(prog, sname, pname);
1023 :
1024 37 : GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[M2TSMux] Setting up program ID %d - send rates: PSI %d ms PCR every %d ms max - PCR offset %d\n", service_id, ctx->pmt_rate, ctx->max_pcr, ctx->pcr_offset));
1025 : }
1026 : //no changes in codec ID or stream type
1027 50 : if ((tspid->codec_id == codec_id) && (tspid->esi.stream_type == streamtype))
1028 : return GF_OK;
1029 :
1030 49 : if (!tspid->codec_id) {
1031 : Bool is_pcr=GF_FALSE;
1032 : Bool force_pes=GF_FALSE;
1033 : u32 pes_pid;
1034 : assert(!tspid->esi.stream_type);
1035 49 : tspid->codec_id = codec_id;
1036 49 : tspid->esi.stream_type = streamtype;
1037 :
1038 49 : if (ctx->bifs_pes && (tspid->esi.stream_type==GF_STREAM_SCENE))
1039 : force_pes = GF_TRUE;
1040 :
1041 49 : pes_pid = gf_m2ts_mux_program_get_pmt_pid(prog);
1042 49 : pes_pid += 1 + gf_m2ts_mux_program_get_stream_count(prog);
1043 49 : if (gf_m2ts_mux_program_get_pcr_pid(prog)==0) {
1044 49 : if (streamtype==GF_STREAM_VISUAL)
1045 : is_pcr = GF_TRUE;
1046 : else
1047 26 : ctx->check_pcr = GF_TRUE;
1048 : //if no video track we will update the PCR at the first packets
1049 : }
1050 :
1051 49 : tsmux_setup_esi(ctx, prog, tspid, streamtype);
1052 49 : tspid->mstream = gf_m2ts_program_stream_add(prog, &tspid->esi, pes_pid, is_pcr, force_pes, GF_FALSE);
1053 49 : tsmux_setup_temi(ctx, tspid);
1054 : } else {
1055 0 : tspid->codec_id = codec_id;
1056 0 : tsmux_setup_esi(ctx, prog, tspid, streamtype);
1057 0 : prog->pmt->table_needs_update = GF_TRUE;
1058 0 : ctx->pmt_update_pending = GF_TRUE;
1059 : }
1060 49 : ctx->update_mux = GF_TRUE;
1061 :
1062 49 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DELAY);
1063 49 : if (p) {
1064 7 : tspid->media_delay = p->value.longsint;
1065 :
1066 : //compute max ts skip for this program
1067 : s64 max_media_skip = 0;
1068 : u32 max_skip_ts = 0;
1069 7 : ts_stream = prog->streams;
1070 29 : while (ts_stream) {
1071 15 : M2Pid *atspid = ts_stream->ifce->input_udta;
1072 : s64 media_skip;
1073 15 : if (atspid->media_delay>=0) {
1074 8 : ts_stream = ts_stream->next;
1075 8 : continue;
1076 : }
1077 :
1078 7 : media_skip = -atspid->media_delay;
1079 7 : if (!max_media_skip || (media_skip * max_skip_ts > max_media_skip * atspid->esi.timescale) ) {
1080 : max_media_skip = media_skip ;
1081 7 : max_skip_ts = atspid->esi.timescale;
1082 : }
1083 7 : ts_stream = ts_stream->next;
1084 : }
1085 :
1086 : ts_stream = prog->streams;
1087 22 : while (ts_stream) {
1088 15 : M2Pid *atspid = ts_stream->ifce->input_udta;
1089 15 : if (max_skip_ts) {
1090 15 : atspid->max_media_skip = (max_media_skip * atspid->esi.timescale / max_skip_ts);
1091 : } else {
1092 0 : atspid->max_media_skip = 0;
1093 : }
1094 15 : ts_stream = ts_stream->next;
1095 : }
1096 : }
1097 49 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CTS_SHIFT);
1098 49 : if (p) {
1099 1 : u64 diff = p->value.uint;
1100 1 : diff *= 1000000;
1101 1 : diff /= tspid->esi.timescale;
1102 1 : if (diff > tspid->prog->cts_offset)
1103 1 : tspid->prog->cts_offset = (u32) diff;
1104 : }
1105 : return GF_OK;
1106 : }
1107 :
1108 25 : static void tsmux_assign_pcr(GF_TSMuxCtx *ctx)
1109 : {
1110 25 : GF_M2TS_Mux_Program *prog = ctx->mux->programs;
1111 50 : while (prog) {
1112 : GF_M2TS_Mux_Stream *stream;
1113 25 : if (prog->pcr) {
1114 11 : prog = prog->next;
1115 11 : continue;
1116 : }
1117 14 : stream = prog->streams;
1118 28 : while (stream) {
1119 14 : if (stream->ifce->stream_type==GF_STREAM_VISUAL) {
1120 0 : prog->pcr = stream;
1121 : break;
1122 : }
1123 14 : stream = stream->next;
1124 : }
1125 14 : if (!prog->pcr) prog->pcr = prog->streams;
1126 14 : ctx->update_mux = GF_TRUE;
1127 14 : prog = prog->next;
1128 : }
1129 25 : }
1130 :
1131 59 : static Bool tsmux_init_buffering(GF_Filter *filter, GF_TSMuxCtx *ctx)
1132 : {
1133 59 : u32 mbuf = ctx->breq*1000;
1134 59 : u32 i, count = gf_filter_get_ipid_count(filter);
1135 111 : for (i=0; i<count; i++) {
1136 : u32 buf;
1137 : Bool buf_ok;
1138 74 : GF_FilterPid *pid = gf_filter_get_ipid(filter, i);
1139 74 : buf_ok = gf_filter_pid_get_buffer_occupancy(pid, NULL, NULL, NULL, &buf);
1140 74 : if (buf_ok && (buf < mbuf) && !gf_filter_pid_has_seen_eos(pid))
1141 22 : return GF_FALSE;
1142 : }
1143 37 : ctx->init_buffering = GF_FALSE;
1144 37 : gf_m2ts_mux_update_config(ctx->mux, GF_TRUE);
1145 37 : return GF_TRUE;
1146 : }
1147 :
1148 161 : static void tsmux_send_seg_event(GF_Filter *filter, GF_TSMuxCtx *ctx)
1149 : {
1150 : GF_FilterEvent evt;
1151 : u32 i;
1152 : M2Pid *tspid = NULL;
1153 :
1154 339 : for (i=0; i<gf_list_count(ctx->pids); i++) {
1155 186 : tspid = gf_list_get(ctx->pids, i);
1156 186 : if (ctx->nb_sidx_entries) break;
1157 178 : if (ctx->ref_pid == tspid->mstream->pid) break;
1158 : tspid = NULL;
1159 : }
1160 161 : if (!tspid) tspid = gf_list_get(ctx->pids, 0);
1161 :
1162 161 : if (ctx->nb_sidx_entries) {
1163 : GF_FilterPacket *idx_pck;
1164 : u8 *output;
1165 : Bool large_sidx = GF_FALSE;
1166 : u32 segidx_size=0;
1167 8 : u64 last_pck_dur = tspid->pck_duration;
1168 8 : last_pck_dur *= 90000;
1169 8 : last_pck_dur /= tspid->esi.timescale;
1170 :
1171 8 : if (ctx->sidx_entries[ctx->nb_sidx_entries-1].sap_time > 0xFFFFFFFFUL)
1172 : large_sidx = GF_TRUE;
1173 8 : if (ctx->sidx_entries[0].offset*188 > 0xFFFFFFFFUL)
1174 : large_sidx = GF_TRUE;
1175 :
1176 : //styp box: 8(box) + 4(major) + 4(version) + 4(compat brand)
1177 : segidx_size = 20;
1178 : //sidx size: 12 (fullbox) + 8 + large ? 16 : 8 + 4 + nb_entries+12
1179 8 : segidx_size += 24 +( large_sidx ? 16 : 8) + ctx->nb_sidx_entries*12;
1180 :
1181 8 : if (!ctx->idx_opid) {
1182 1 : const char *ext = gf_file_ext_start(ctx->idx_file_name);
1183 1 : if (!ext) ext = "idx";
1184 1 : else ext++;
1185 1 : ctx->idx_filter = gf_filter_connect_destination(filter, ctx->idx_file_name, NULL);
1186 1 : ctx->idx_opid = gf_filter_pid_new(filter);
1187 1 : gf_filter_pid_set_property(ctx->idx_opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
1188 1 : gf_filter_pid_set_property(ctx->idx_opid, GF_PROP_PID_FILE_EXT, &PROP_STRING(ext) );
1189 1 : gf_filter_pid_set_property(ctx->idx_opid, GF_PROP_PID_MIME, &PROP_STRING("*") );
1190 1 : gf_filter_pid_set_name(ctx->idx_opid, "ts_idx");
1191 1 : if (ctx->idx_filter) gf_filter_set_source(ctx->idx_filter, filter, NULL);
1192 : }
1193 8 : idx_pck = gf_filter_pck_new_alloc(ctx->idx_opid, segidx_size, &output);
1194 8 : if (!idx_pck) return;
1195 :
1196 8 : if (!ctx->idx_bs) ctx->idx_bs = gf_bs_new(output, segidx_size, GF_BITSTREAM_WRITE);
1197 7 : else gf_bs_reassign_buffer(ctx->idx_bs, output, segidx_size);
1198 :
1199 : //write styp box
1200 8 : gf_bs_write_u32(ctx->idx_bs, 20);
1201 8 : gf_bs_write_u32(ctx->idx_bs, GF_4CC('s','t','y','p') );
1202 8 : gf_bs_write_u32(ctx->idx_bs, GF_4CC('s','i','s','x') );
1203 8 : gf_bs_write_u32(ctx->idx_bs, 0);
1204 8 : gf_bs_write_u32(ctx->idx_bs, GF_4CC('s','i','s','x') );
1205 :
1206 : //write sidx box
1207 8 : gf_bs_write_u32(ctx->idx_bs, segidx_size - 20);
1208 8 : gf_bs_write_u32(ctx->idx_bs, GF_4CC('s','i','d','x') );
1209 8 : gf_bs_write_u8(ctx->idx_bs, large_sidx ? 1 : 0);
1210 8 : gf_bs_write_int(ctx->idx_bs, 0, 24);
1211 : //reference id
1212 8 : gf_bs_write_u32(ctx->idx_bs, ctx->ref_pid);
1213 : //timescale
1214 8 : gf_bs_write_u32(ctx->idx_bs, 90000);
1215 8 : if (large_sidx) {
1216 0 : gf_bs_write_u64(ctx->idx_bs, ctx->sidx_entries[0].min_pts_plus_one-1);
1217 0 : gf_bs_write_u64(ctx->idx_bs, ctx->sidx_entries[0].offset*188);
1218 : } else {
1219 8 : gf_bs_write_u32(ctx->idx_bs, (u32) ctx->sidx_entries[0].min_pts_plus_one-1);
1220 8 : gf_bs_write_u32(ctx->idx_bs, (u32) ctx->sidx_entries[0].offset*188);
1221 : }
1222 8 : gf_bs_write_u16(ctx->idx_bs, 0);
1223 8 : gf_bs_write_u16(ctx->idx_bs, ctx->nb_sidx_entries);
1224 848 : for (i=0; i<ctx->nb_sidx_entries; i++) {
1225 832 : u64 duration = ctx->sidx_entries[i].max_pts - (ctx->sidx_entries[i].min_pts_plus_one-1);
1226 832 : if (i+1 == ctx->nb_sidx_entries) duration += last_pck_dur;
1227 :
1228 832 : gf_bs_write_int(ctx->idx_bs, 0, 1);
1229 832 : gf_bs_write_int(ctx->idx_bs, ctx->sidx_entries[i].nb_pck * 188, 31);
1230 832 : gf_bs_write_int(ctx->idx_bs, (u32) duration, 32);
1231 832 : gf_bs_write_int(ctx->idx_bs, ctx->sidx_entries[i].sap_type ? 1 : 0, 1);
1232 832 : gf_bs_write_int(ctx->idx_bs, ctx->sidx_entries[i].sap_type, 3);
1233 832 : gf_bs_write_int(ctx->idx_bs, (u32) (ctx->sidx_entries[i].sap_time - (ctx->sidx_entries[i].min_pts_plus_one-1) ), 28);
1234 : }
1235 8 : gf_filter_pck_set_property(idx_pck, GF_PROP_PCK_FILENAME, &PROP_STRING(ctx->idx_file_name) );
1236 :
1237 8 : gf_filter_pck_send(idx_pck);
1238 8 : ctx->nb_sidx_entries = 0;
1239 : }
1240 :
1241 :
1242 161 : GF_FEVT_INIT(evt, GF_FEVT_SEGMENT_SIZE, tspid->ipid);
1243 161 : evt.seg_size.media_range_start = 188*ctx->pck_start_idx;
1244 161 : evt.seg_size.media_range_end = evt.seg_size.media_range_start + 188*ctx->nb_pck_in_seg - 1;
1245 :
1246 161 : gf_filter_pid_send_event(tspid->ipid, &evt);
1247 161 : ctx->nb_pck_in_seg = 0;
1248 161 : ctx->nb_sidx_entries = 0;
1249 : }
1250 :
1251 127004 : static void tsmux_insert_sidx(GF_TSMuxCtx *ctx, Bool final_flush)
1252 : {
1253 127004 : if (ctx->subs_sidx<0) return;
1254 :
1255 13353 : if (!ctx->ref_pid && ctx->mux->sap_inserted)
1256 1 : ctx->ref_pid = ctx->mux->last_pid;
1257 13353 : if (!ctx->ref_pid) return;
1258 :
1259 13351 : if (ctx->nb_sidx_entries) {
1260 13329 : TS_SIDX *tsidx = &ctx->sidx_entries[ctx->nb_sidx_entries-1];
1261 :
1262 13329 : if (ctx->ref_pid == ctx->mux->last_pid) {
1263 1565 : if (!tsidx->min_pts_plus_one) tsidx->min_pts_plus_one = ctx->mux->last_pts + 1;
1264 1565 : else if (tsidx->min_pts_plus_one-1 > ctx->mux->last_pts) tsidx->min_pts_plus_one = ctx->mux->last_pts + 1;
1265 :
1266 1565 : if (tsidx->max_pts < ctx->mux->last_pts) tsidx->max_pts = ctx->mux->last_pts;
1267 : }
1268 :
1269 13329 : if (!final_flush && !ctx->mux->sap_inserted) return;
1270 :
1271 832 : tsidx->nb_pck = ctx->nb_pck_in_seg - tsidx->nb_pck;
1272 : }
1273 :
1274 854 : if (final_flush) return;
1275 846 : if (!ctx->mux->sap_inserted) return;
1276 :
1277 832 : if (ctx->nb_sidx_entries == ctx->nb_sidx_alloc) {
1278 16 : ctx->nb_sidx_alloc += 10;
1279 16 : ctx->sidx_entries = gf_realloc(ctx->sidx_entries, sizeof(TS_SIDX)*ctx->nb_sidx_alloc);
1280 : }
1281 832 : ctx->sidx_entries[ctx->nb_sidx_entries].sap_time = ctx->mux->sap_time;
1282 832 : ctx->sidx_entries[ctx->nb_sidx_entries].sap_type = ctx->mux->sap_type;
1283 832 : ctx->sidx_entries[ctx->nb_sidx_entries].min_pts_plus_one = ctx->mux->sap_time + 1;
1284 832 : ctx->sidx_entries[ctx->nb_sidx_entries].max_pts = ctx->mux->sap_time;
1285 832 : ctx->sidx_entries[ctx->nb_sidx_entries].nb_pck = ctx->nb_sidx_entries ? ctx->nb_pck_in_seg : 0;
1286 832 : ctx->sidx_entries[ctx->nb_sidx_entries].offset = ctx->nb_sidx_entries ? 0 : ctx->nb_pck_first_sidx;
1287 832 : ctx->nb_sidx_entries ++;
1288 : }
1289 :
1290 6160 : static GF_Err tsmux_process(GF_Filter *filter)
1291 : {
1292 : u32 nb_pck_in_pack, nb_pck_in_call;
1293 : GF_M2TSMuxState status;
1294 : u32 usec_till_next;
1295 : GF_FilterPacket *pck;
1296 6160 : GF_TSMuxCtx *ctx = gf_filter_get_udta(filter);
1297 :
1298 6160 : if (ctx->check_pcr) {
1299 25 : ctx->check_pcr = GF_FALSE;
1300 25 : tsmux_assign_pcr(ctx);
1301 : }
1302 :
1303 6160 : if (ctx->init_buffering && !tsmux_init_buffering(filter, ctx)) return GF_OK;
1304 :
1305 6138 : if (ctx->init_dash) {
1306 8 : u32 i, count = gf_list_count(ctx->pids);
1307 19 : for (i=0; i<count; i++) {
1308 : const GF_PropertyValue *p;
1309 11 : M2Pid *tspid = gf_list_get(ctx->pids, i);
1310 11 : pck = gf_filter_pid_get_packet(tspid->ipid);
1311 11 : if (!pck) return GF_OK;
1312 11 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENUM);
1313 11 : if (p)
1314 11 : tspid->ctx->dash_seg_num = p->value.uint;
1315 11 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENAME);
1316 11 : if (p)
1317 7 : strcpy(tspid->ctx->dash_file_name, p->value.string);
1318 11 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_IDXFILENAME);
1319 11 : if (p)
1320 1 : strcpy(tspid->ctx->idx_file_name, p->value.string);
1321 : }
1322 8 : ctx->init_dash = GF_FALSE;
1323 8 : ctx->next_is_start = GF_TRUE;
1324 8 : ctx->wait_dash_flush = GF_FALSE;
1325 : }
1326 :
1327 6138 : if (ctx->update_mux) {
1328 6138 : gf_m2ts_mux_update_config(ctx->mux, GF_FALSE);
1329 : }
1330 :
1331 :
1332 6138 : if (ctx->wait_dash_flush) {
1333 953 : u32 i, done=0, count = gf_list_count(ctx->pids);
1334 2756 : for (i=0; i<count; i++) {
1335 1803 : M2Pid *tspid = gf_list_get(ctx->pids, i);
1336 1803 : if (tspid->has_seen_eods) done++;
1337 : }
1338 :
1339 953 : if (done==count) {
1340 145 : for (i=0; i<count; i++) {
1341 145 : M2Pid *tspid = gf_list_get(ctx->pids, i);
1342 145 : tspid->has_seen_eods = 0;
1343 : }
1344 124 : ctx->wait_dash_flush = GF_FALSE;
1345 124 : ctx->next_is_start = ctx->dash_file_switch;
1346 124 : ctx->mux->force_pat = GF_TRUE;
1347 124 : ctx->dash_file_switch = GF_FALSE;
1348 124 : if (ctx->nb_pck_in_seg) {
1349 124 : tsmux_insert_sidx(ctx, GF_TRUE);
1350 124 : tsmux_send_seg_event(filter, ctx);
1351 : }
1352 124 : ctx->dash_seg_num = 0;
1353 :
1354 124 : ctx->nb_pck_in_seg = 0;
1355 124 : ctx->pck_start_idx = ctx->nb_pck;
1356 124 : if (ctx->next_is_start) ctx->nb_pck_in_file = 0;
1357 124 : ctx->nb_pck_first_sidx = ctx->nb_pck_in_file;
1358 : }
1359 : }
1360 :
1361 : nb_pck_in_call = 0;
1362 : nb_pck_in_pack=0;
1363 : while (1) {
1364 : u64 pck_ts;
1365 : u8 *output;
1366 : u32 osize;
1367 : Bool is_pack_flush = GF_FALSE;
1368 : const char *ts_pck;
1369 :
1370 131630 : ts_pck = gf_m2ts_mux_process(ctx->mux, &status, &usec_till_next);
1371 131630 : if (ts_pck == NULL) {
1372 4787 : if (!nb_pck_in_pack)
1373 : break;
1374 2506 : ts_pck = (const char *) ctx->pack_buffer;
1375 : is_pack_flush = GF_TRUE;
1376 : } else {
1377 :
1378 126843 : tsmux_insert_sidx(ctx, GF_FALSE);
1379 :
1380 126843 : if (ctx->nb_pack>1) {
1381 126843 : memcpy(ctx->pack_buffer + 188 * nb_pck_in_pack, ts_pck, 188);
1382 126843 : nb_pck_in_pack++;
1383 :
1384 126843 : if (nb_pck_in_pack < ctx->nb_pack)
1385 96356 : continue;
1386 :
1387 30487 : ts_pck = (const char *) ctx->pack_buffer;
1388 : } else {
1389 : nb_pck_in_pack = 1;
1390 : }
1391 : }
1392 32993 : osize = nb_pck_in_pack * 188;
1393 32993 : pck = gf_filter_pck_new_alloc(ctx->opid, osize, &output);
1394 32993 : if (!pck) return GF_OUT_OF_MEM;
1395 :
1396 32993 : memcpy(output, ts_pck, osize);
1397 32993 : gf_filter_pck_set_framing(pck, ctx->nb_pck ? ctx->next_is_start : GF_TRUE, (status==GF_M2TS_STATE_EOS) ? GF_TRUE : GF_FALSE);
1398 :
1399 32993 : if (ctx->next_is_start && ctx->dash_mode) {
1400 125 : GF_LOG(GF_LOG_INFO, GF_LOG_DASH, ("[M2TSMux] starting TS segment %d\r", ctx->dash_seg_num));
1401 125 : gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENUM, &PROP_UINT(ctx->dash_seg_num) );
1402 125 : if (ctx->dash_file_name[0])
1403 124 : gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENAME, &PROP_STRING(ctx->dash_file_name) ) ;
1404 :
1405 125 : ctx->dash_file_name[0] = 0;
1406 125 : ctx->next_is_start = GF_FALSE;
1407 : }
1408 :
1409 32993 : pck_ts = gf_m2ts_get_ts_clock_90k(ctx->mux);
1410 32993 : gf_filter_pck_set_dts(pck, pck_ts);
1411 32993 : gf_filter_pck_set_cts(pck, pck_ts);
1412 :
1413 32993 : if (ctx->notify_filename) {
1414 2 : gf_filter_pck_set_framing(pck, GF_TRUE, (status==GF_M2TS_STATE_EOS) ? GF_TRUE : GF_FALSE);
1415 2 : gf_filter_pck_set_property(pck, GF_PROP_PCK_FILENUM, &PROP_UINT(ctx->cur_file_idx_plus_one-1));
1416 2 : if (ctx->cur_file_suffix) {
1417 2 : gf_filter_pck_set_property(pck, GF_PROP_PCK_FILESUF, &PROP_STRING_NO_COPY(ctx->cur_file_suffix));
1418 2 : ctx->cur_file_suffix = NULL;
1419 : }
1420 2 : ctx->notify_filename = GF_FALSE;
1421 : }
1422 32993 : gf_filter_pck_send(pck);
1423 32993 : ctx->nb_pck += nb_pck_in_pack;
1424 32993 : ctx->nb_pck_in_seg += nb_pck_in_pack;
1425 32993 : ctx->nb_pck_in_file += nb_pck_in_pack;
1426 32993 : nb_pck_in_call += nb_pck_in_pack;
1427 : nb_pck_in_pack = 0;
1428 :
1429 32993 : if (is_pack_flush)
1430 : break;
1431 :
1432 30487 : if (status>=GF_M2TS_STATE_PADDING) {
1433 : break;
1434 : }
1435 29591 : if (nb_pck_in_call>100)
1436 : break;
1437 : }
1438 :
1439 6138 : if (gf_filter_reporting_enabled(filter)) {
1440 : char szStatus[1024];
1441 0 : if (status==GF_M2TS_STATE_EOS) {
1442 : Double ohead = 0;
1443 0 : u64 total_bytes_out = ctx->nb_pck;
1444 0 : total_bytes_out *= 188;
1445 :
1446 0 : if (ctx->total_bytes_in) ohead = ((Double) (total_bytes_out - ctx->total_bytes_in)*100 / ctx->total_bytes_in);
1447 :
1448 0 : sprintf(szStatus, "done - TS clock % 6d ms bitrate %d kbps - bytes "LLD" in "LLD" out overhead %02.02f%%", gf_m2ts_get_ts_clock(ctx->mux), ctx->mux->bit_rate/1000, ctx->total_bytes_in, total_bytes_out, ohead);
1449 0 : gf_filter_update_status(filter, 10000, szStatus);
1450 : } else {
1451 :
1452 0 : sprintf(szStatus, "sysclock % 6d ms TS clock % 6d ms bitrate % 8d kbps", gf_m2ts_get_sys_clock(ctx->mux), gf_m2ts_get_ts_clock(ctx->mux), ctx->mux->bit_rate/1000);
1453 0 : gf_filter_update_status(filter, -1, szStatus);
1454 : }
1455 : }
1456 :
1457 6138 : if (ctx->nb_suspended && (ctx->nb_suspended==gf_list_count(ctx->pids)) ) {
1458 1 : u32 i, count = gf_list_count(ctx->pids);
1459 2 : for (i=0; i<count; i++) {
1460 1 : M2Pid *tspid = gf_list_get(ctx->pids, i);
1461 1 : tspid->esi.caps &= ~GF_ESI_STREAM_IS_OVER;
1462 1 : tspid->suspended = GF_FALSE;
1463 : }
1464 1 : ctx->nb_suspended = 0;
1465 1 : ctx->mux->force_pat = GF_TRUE;
1466 1 : ctx->notify_filename = GF_TRUE;
1467 1 : status = GF_M2TS_STATE_IDLE;
1468 : }
1469 :
1470 6138 : if (status==GF_M2TS_STATE_EOS) {
1471 1209 : gf_filter_pid_set_eos(ctx->opid);
1472 1209 : if (ctx->nb_pck_in_seg) {
1473 37 : tsmux_insert_sidx(ctx, GF_TRUE);
1474 37 : tsmux_send_seg_event(filter, ctx);
1475 37 : ctx->nb_pck_in_seg = 0;
1476 : }
1477 : return GF_EOS;
1478 : }
1479 :
1480 4929 : if (ctx->realtime) {
1481 0 : u32 now = gf_sys_clock();
1482 0 : if (!ctx->last_log_time)
1483 0 : ctx->last_log_time = now;
1484 0 : else if (now > ctx->last_log_time + ctx->log_freq) {
1485 0 : ctx->last_log_time = now;
1486 0 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[M2TSMux] time % 6d TS time % 6d bitrate % 8d\r", gf_m2ts_get_sys_clock(ctx->mux), gf_m2ts_get_ts_clock(ctx->mux), ctx->mux->average_birate_kbps));
1487 : }
1488 0 : if (status == GF_M2TS_STATE_IDLE) {
1489 : #if 0
1490 : u64 sleep_for=0;
1491 : /*wait till next packet is ready to be sent*/
1492 : if (usec_till_next>1000) {
1493 : sleep_for = usec_till_next;
1494 : }
1495 : if (sleep_for)
1496 : gf_filter_ask_rt_reschedule(filter, (u32) sleep_for);
1497 : #else
1498 : //we don't have enough precision on usec counting and we end up eating one core on most machines,
1499 : // so let's just sleep one ms whenever we are idle - it's maybe too much but the muxer will catchup afterwards
1500 0 : gf_filter_ask_rt_reschedule(filter, 1000);
1501 : #endif
1502 : }
1503 : }
1504 : //PMT update management is still under progress...
1505 4929 : ctx->pmt_update_pending = 0;
1506 :
1507 4929 : return GF_OK;
1508 : }
1509 :
1510 37 : static GF_Err tsmux_initialize(GF_Filter *filter)
1511 : {
1512 37 : GF_TSMuxCtx *ctx = gf_filter_get_udta(filter);
1513 37 : gf_filter_set_max_extra_input_pids(filter, -1);
1514 :
1515 37 : ctx->mux = gf_m2ts_mux_new(ctx->rate, ctx->pat_rate, ctx->realtime);
1516 37 : ctx->mux->flush_pes_at_rap = ctx->flush_rap;
1517 :
1518 37 : if (gf_sys_is_test_mode() && ctx->pcr_init<0)
1519 8 : ctx->pcr_init = 1000000;
1520 :
1521 37 : gf_m2ts_mux_use_single_au_pes_mode(ctx->mux, ctx->pes_pack);
1522 37 : if (ctx->pcr_init>=0) gf_m2ts_mux_set_initial_pcr(ctx->mux, (u64) ctx->pcr_init);
1523 37 : gf_m2ts_mux_set_pcr_max_interval(ctx->mux, ctx->max_pcr);
1524 37 : gf_m2ts_mux_enable_pcr_only_packets(ctx->mux, ctx->pcr_only);
1525 :
1526 37 : if (!ctx->sid) ctx->sid = 1;
1527 37 : if (ctx->sdt_rate) {
1528 1 : gf_m2ts_mux_enable_sdt(ctx->mux, ctx->sdt_rate);
1529 : }
1530 :
1531 37 : if (!gf_filter_block_enabled(filter)) {
1532 0 : ctx->breq = 0;
1533 : } else {
1534 37 : ctx->init_buffering = GF_TRUE;
1535 : }
1536 37 : ctx->pids = gf_list_new();
1537 37 : if (ctx->nb_pack>1) ctx->pack_buffer = gf_malloc(sizeof(char)*188*ctx->nb_pack);
1538 :
1539 : #ifdef GPAC_ENABLE_COVERAGE
1540 37 : if (gf_sys_is_cov_mode()) {
1541 37 : gf_m2ts_get_sys_clock(ctx->mux);
1542 : }
1543 : #endif
1544 37 : return GF_OK;
1545 : }
1546 :
1547 :
1548 37 : static void tsmux_finalize(GF_Filter *filter)
1549 : {
1550 37 : GF_TSMuxCtx *ctx = gf_filter_get_udta(filter);
1551 : #ifndef GPAC_DISABLE_LOG
1552 37 : u64 bits = ctx->mux->tot_pck_sent*8*188;
1553 : #endif
1554 37 : u64 dur_ms = gf_m2ts_get_ts_clock(ctx->mux);
1555 37 : if (!dur_ms) dur_ms = 1;
1556 37 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[M2TSMux] Done muxing - %.02f sec - %sbitrate %d kbps "LLD" packets written\nPadding: "LLD" packets (%g kbps) - "LLD" PES padded bytes (%g kbps)\n",
1557 : ((Double) dur_ms)/1000.0, ctx->rate ? "" : "average ", (u32) (bits/dur_ms), ctx->mux->tot_pck_sent,
1558 : ctx->mux->tot_pad_sent, (Double) (ctx->mux->tot_pad_sent*188*8.0/dur_ms) , ctx->mux->tot_pes_pad_bytes, (Double) (ctx->mux->tot_pes_pad_bytes*8.0/dur_ms)
1559 : ));
1560 :
1561 86 : while (gf_list_count(ctx->pids)) {
1562 49 : M2Pid *tspid = gf_list_pop_back(ctx->pids);
1563 49 : tsmux_del_stream(tspid);
1564 : }
1565 37 : gf_list_del(ctx->pids);
1566 37 : gf_m2ts_mux_del(ctx->mux);
1567 37 : if (ctx->pack_buffer) gf_free(ctx->pack_buffer);
1568 37 : if (ctx->sidx_entries) gf_free(ctx->sidx_entries);
1569 37 : if (ctx->idx_bs) gf_bs_del(ctx->idx_bs);
1570 37 : if (ctx->cur_file_suffix) gf_free(ctx->cur_file_suffix);
1571 37 : }
1572 :
1573 29204 : static Bool tsmux_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
1574 : {
1575 29204 : if ((evt->base.type==GF_FEVT_STOP) || (evt->base.type==GF_FEVT_PLAY) ) {
1576 : u32 i;
1577 38 : GF_TSMuxCtx *ctx = gf_filter_get_udta(filter);
1578 89 : for (i=0; i<gf_list_count(ctx->pids); i++) {
1579 51 : M2Pid *tspid = gf_list_get(ctx->pids, i);
1580 51 : if (evt->base.type==GF_FEVT_STOP)
1581 0 : tspid->esi.caps |= GF_ESI_STREAM_IS_OVER;
1582 : else
1583 51 : tspid->esi.caps &= ~GF_ESI_STREAM_IS_OVER;
1584 : }
1585 : }
1586 29204 : return GF_FALSE;
1587 : }
1588 :
1589 : static const GF_FilterCapability TSMuxCaps[] =
1590 : {
1591 : //first set of caps describe streams that need reframing (NALU, mp4v, mpegh audio)
1592 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_VISUAL),
1593 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_STREAM_TYPE, GF_STREAM_AUDIO),
1594 : //unframed streams only
1595 : CAP_BOOL(GF_CAPS_INPUT, GF_PROP_PID_UNFRAMED, GF_TRUE),
1596 : //for NAL-based, we want annexB format
1597 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_CODECID, GF_CODECID_AVC),
1598 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_CODECID, GF_CODECID_MVC),
1599 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_CODECID, GF_CODECID_SVC),
1600 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_CODECID, GF_CODECID_HEVC),
1601 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_CODECID, GF_CODECID_LHVC),
1602 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_CODECID, GF_CODECID_VVC),
1603 : //for m4vp2 we want DSI reinsertion
1604 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_CODECID, GF_CODECID_MPEG4_PART2),
1605 : //for AAC we use the AAC->ADTS or AAC->LATM of the mux, so don't insert here
1606 :
1607 : //for MPEG-H audio MHAS we need to insert sync packets
1608 : CAP_UINT(GF_CAPS_INPUT, GF_PROP_PID_CODECID, GF_CODECID_MHAS),
1609 :
1610 : //static output cap file extension
1611 : CAP_UINT(GF_CAPS_OUTPUT_STATIC, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
1612 : CAP_STRING(GF_CAPS_OUTPUT_STATIC, GF_PROP_PID_FILE_EXT, M2TS_FILE_EXTS),
1613 : CAP_STRING(GF_CAPS_OUTPUT_STATIC, GF_PROP_PID_MIME, M2TS_MIMES),
1614 : {0},
1615 :
1616 : //for now don't accept files as input, although we could store them as items, to refine
1617 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
1618 : //these caps are framed
1619 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
1620 : //exclude caps from above
1621 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_AVC),
1622 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_MVC),
1623 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_SVC),
1624 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_HEVC),
1625 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_LHVC),
1626 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_VVC),
1627 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_MPEG4_PART2),
1628 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_MHAS),
1629 : //we don't accept MPEG-H audio without MHAS
1630 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_MPHA),
1631 : //no RAW support for now$
1632 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_RAW),
1633 : };
1634 :
1635 :
1636 : #define OFFS(_n) #_n, offsetof(GF_TSMuxCtx, _n)
1637 : static const GF_FilterArgs TSMuxArgs[] =
1638 : {
1639 : { OFFS(breq), "buffer requirements in ms for input pids", GF_PROP_UINT, "100", NULL, GF_FS_ARG_HINT_ADVANCED},
1640 : { OFFS(pmt_id), "define the ID of the first PMT to use in the mux", GF_PROP_UINT, "100", NULL, 0},
1641 : { OFFS(rate), "target rate in bps of the multiplex. If not set, variable rate is used", GF_PROP_UINT, "0", NULL, 0},
1642 : { OFFS(pmt_rate), "interval between PMT in ms", GF_PROP_UINT, "200", NULL, 0},
1643 : { OFFS(pat_rate), "interval between PAT in ms", GF_PROP_UINT, "200", NULL, 0},
1644 : { OFFS(first_pts), "force PTS value of first packet, in 90kHz", GF_PROP_LUINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
1645 : { OFFS(pcr_offset), "offset all timestamps from PCR by V, in 90kHz. Default value is computed based on input media", GF_PROP_LUINT, "-1", NULL, GF_FS_ARG_HINT_ADVANCED},
1646 : { OFFS(mpeg4), "force usage of MPEG-4 signaling (IOD and SL Config)\n"\
1647 : "- none: disables 4on2\n"\
1648 : "- full: sends AUs as SL packets over section for OD, section/pes for scene (cf bifs_pes)\n"\
1649 : "- scene: sends only scene streams as 4on2 but uses regular PES without SL for audio and video"\
1650 : , GF_PROP_UINT, "none", "none|full|scene", GF_FS_ARG_HINT_EXPERT},
1651 : { OFFS(pmt_version), "set version number of the PMT", GF_PROP_UINT, "200", NULL, GF_FS_ARG_HINT_ADVANCED},
1652 : { OFFS(disc), "set the discontinuity marker for the first packet of each stream", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1653 : { OFFS(repeat_rate), "interval in ms between two carousel send for MPEG-4 systems. Is overridden by carousel duration PID property if defined", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_EXPERT},
1654 : { OFFS(repeat_img), "interval in ms between re-sending (as PES) of single-image streams. If 0, image data is sent once only", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
1655 : { OFFS(max_pcr), "set max interval in ms between 2 PCR", GF_PROP_UINT, "100", NULL, GF_FS_ARG_HINT_ADVANCED},
1656 : { OFFS(nb_pack), "pack N TS packets in output packets", GF_PROP_UINT, "4", NULL, 0},
1657 : { OFFS(pes_pack), "set AU to PES packing mode\n"\
1658 : "- audio: will pack only multiple audio AUs in a PES\n"\
1659 : "- none: make exactly one AU per PES\n"\
1660 : "- all: will pack multiple AUs per PES for all streams", GF_PROP_UINT, "audio", "audio|none|all", GF_FS_ARG_HINT_ADVANCED},
1661 : { OFFS(realtime), "use real-time output", GF_PROP_BOOL, "false", NULL, 0},
1662 : { OFFS(bifs_pes), "select BIFS streams packetization (PES vs sections)\n"
1663 : "- on: uses BIFS PES\n"
1664 : "- off: uses BIFS sections\n"
1665 : "- copy: uses BIFS PES but removes timestamps in BIFS SL and only carries PES timestamps", GF_PROP_UINT, "off", "off|on|copy", GF_FS_ARG_HINT_EXPERT},
1666 : { OFFS(flush_rap), "force flushing mux program when RAP is found on video, and injects PAT and PMT before the next video PES begin", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1667 : { OFFS(pcr_only), "enable PCR-only TS packets", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
1668 : { OFFS(pcr_init), "set initial PCR value for the programs. Negative value implies random value is picked", GF_PROP_LSINT, "-1", NULL, 0},
1669 : { OFFS(sid), "set service ID for the program - see filter help", GF_PROP_UINT, "0", NULL, 0},
1670 : { OFFS(name), "set service name for the program - see filter help", GF_PROP_STRING, NULL, NULL, 0},
1671 : { OFFS(provider), "set service provider name for the program - see filter help", GF_PROP_STRING, NULL, NULL, 0},
1672 : { OFFS(sdt_rate), "interval in ms between two DVB SDT tables. If 0, SDT is disabled", GF_PROP_UINT, "0", NULL, 0},
1673 :
1674 : { OFFS(temi), "insert TEMI time codes in adaptation field - see filter help", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
1675 : { OFFS(log_freq), "delay between logs for realtime mux", GF_PROP_UINT, "500", NULL, GF_FS_ARG_HINT_ADVANCED},
1676 : { OFFS(latm), "use LATM AAC encapsulation instead of regular ADTS", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
1677 : { OFFS(subs_sidx), "number of subsegments per sidx. negative value disables sidx", GF_PROP_SINT, "-1", NULL, GF_FS_ARG_HINT_ADVANCED},
1678 : {0}
1679 : };
1680 :
1681 :
1682 : GF_FilterRegister TSMuxRegister = {
1683 : .name = "m2tsmx",
1684 : GF_FS_SET_DESCRIPTION("MPEG-2 TS muxer")
1685 : GF_FS_SET_HELP("GPAC TS multiplexer selects M2TS PID for media streams using the PID of the PMT plus the stream index.\n"
1686 : "For example, default config creates the first program with a PMT PID 100, the first stream will have a PID of 101.\n"
1687 : "Streams are grouped in programs based on input PID property ServiceID if present. If absent, stream will go in the program with service ID as indicated by [-sid]() option.\n"
1688 : "- [-name]() option is overridden by input PID property `ServiceName`.\n"
1689 : "- [-provider]() option is overridden by input PID property `ServiceProvider`.\n"
1690 : "- [-pcr_offset]() option is overridden by input PID property `\"tsmux:pcr_offset\"`\n"
1691 : "- [-first_pts]() option is overridden by input PID property `\"tsmux:force_pts\"`\n"
1692 : "- [-temi]() option is overridden by input PID property `\"tsmux:temi\"`\n"
1693 :
1694 : "\n"
1695 : "# Time and External Media Information (TEMI)\n"
1696 : "The [-temi]() option allows specifying a list of URLs or timeline IDs to insert in streams of a program.\n"
1697 : "One or more TEMI timeline can be specified per PID.\n"
1698 : "The syntax is a comma-separated list of one or more TEMI description.\n"
1699 : "Each TEMI description is formatted as ID_OR_URL or #OPT1[#OPT2]#ID_OR_URL. Options are:\n"
1700 : "- S`N`: gives number N indicating the target serviceID\n"
1701 : "- T`N`: set timescale to use (default: PID timescale)\n"
1702 : "- D`N`: set delay in ms between two TEMI url descriptors (default 1000)\n"
1703 : "- O`N`: set offset (max 64 bits) to add to TEMI timecodes (default 0). If timescale is not specified, offset value is in ms, otherwise in timescale units.\n"
1704 : "- I`N`: set initial value (max 64 bits) of TEMI timecodes. If not set, initial value will match first packet CTS. If timescale is not specified, value is in PID timescale units, otherwise in specified timescale units.\n"
1705 : "- P`N`: indicate target PID in program. Possible values are\n"
1706 : " - `V`: only insert for video streams.\n"
1707 : " - `A`: only insert for audio streams.\n"
1708 : " - `T`: only insert for text streams.\n"
1709 : " - N: only insert for stream with index N (0-based) in the program.\n"
1710 : "- L`N`: set 64bit timecode signaling. Possible values are:\n"
1711 : " - `A`: automatic switch between 32 and 64 bit depending on timecode value (default if not specified).\n"
1712 : " - `Y`: use 64 bit signaling only.\n"
1713 : " - `N`: use 32 bit signaling only and wrap around timecode value.\n"
1714 : "- N: insert NTP timestamp in TEMI timeline descriptor\n"
1715 : "- ID_OR_URL: If number, indicates the TEMI ID to use for external timeline. Otherwise, gives the URL to insert\n"
1716 : " \n"
1717 : "EX temi=\"url\"\n"
1718 : "Inserts a TEMI URL+timecode in the each stream of each program.\n"
1719 : "EX temi=\"#P0#url,#P1#4\"\n"
1720 : "Inserts a TEMI URL+timecode in the first stream of all programs and an external TEMI with ID 4 in the second stream of all programs.\n"
1721 : "EX temi=\"#P0#2,#P0#url,#P1#4\"\n"
1722 : "Inserts a TEMI with ID 2 and a TEMI URL+timecode in the first stream of all programs, and an external TEMI with ID 4 in the second stream of all programs.\n"
1723 : "EX temi=\"#S20#4,#S10#URL\"\n"
1724 : "Inserts an external TEMI with ID 4 in the each stream of program with ServiceID 20 and a TEMI URL in each stream of program with ServiceID 10.\n"
1725 : "EX temi=\"#N#D500#PV#T30000#4\"\n"
1726 : "Inserts an external TEMI with ID 4 and timescale 30000, NTP injection and carousel of 500 ms in the video stream of all programs.\n"
1727 : "\n"
1728 : "Warning: multipliers (k,m,g) are not supported in TEMI options.\n"
1729 : "# Notes\n"
1730 : "In DASH mode, the PCR is always initialized at 0, and [-flush_rap]() is automatically set.\n"
1731 : "The filter watches the property `FileNumber` on incoming packets to create new files or new segments in DASH mode.\n"
1732 : )
1733 : .private_size = sizeof(GF_TSMuxCtx),
1734 : .args = TSMuxArgs,
1735 : .initialize = tsmux_initialize,
1736 : .finalize = tsmux_finalize,
1737 : .flags = GF_FS_REG_DYNAMIC_REDIRECT,
1738 : SETCAPS(TSMuxCaps),
1739 : .configure_pid = tsmux_configure_pid,
1740 : .process = tsmux_process,
1741 : .process_event = tsmux_process_event,
1742 : };
1743 :
1744 :
1745 2877 : const GF_FilterRegister *tsmux_register(GF_FilterSession *session)
1746 : {
1747 2877 : return &TSMuxRegister;
1748 : }
|