LCOV - code coverage report
Current view: top level - filters - mux_ts.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 759 850 89.3 %
Date: 2021-04-29 23:48:07 Functions: 19 19 100.0 %

          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             : }

Generated by: LCOV version 1.13