Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2017-2021
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / ISOBMF 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/internal/isomedia_dev.h>
29 : #include <gpac/internal/media_dev.h>
30 :
31 : #ifndef GPAC_DISABLE_ISOM_WRITE
32 :
33 : #define TEXT_DEFAULT_WIDTH 400
34 : #define TEXT_DEFAULT_HEIGHT 60
35 : #define TEXT_DEFAULT_FONT_SIZE 18
36 :
37 : #define GF_VENDOR_GPAC GF_4CC('G','P','A','C')
38 :
39 :
40 : #define ISOM_FILE_EXT "mp4|mpg4|m4a|m4i|3gp|3gpp|3g2|3gp2|iso|ismv|m4s|heif|heic|iff|avci|avif|mj2|mov|qt"
41 : #define ISOM_FILE_MIME "application/x-isomedia|application/mp4|video/mp4|audio/mp4|video/3gpp|audio/3gpp|video/3gp2|audio/3gp2|video/iso.segment|audio/iso.segment|image/heif|image/heic|image/avci|video/quicktime"
42 :
43 : enum{
44 : NALU_NONE,
45 : NALU_AVC,
46 : NALU_HEVC,
47 : NALU_VVC
48 : };
49 :
50 :
51 : enum
52 : {
53 : CENC_NONE=0,
54 : CENC_NEED_SETUP,
55 : CENC_SETUP_DONE,
56 : CENC_SETUP_ERROR
57 : };
58 :
59 : enum{
60 : TAG_NONE,
61 : TAG_STRICT,
62 : TAG_ALL
63 : };
64 :
65 :
66 : typedef struct
67 : {
68 : GF_FilterPid *ipid;
69 : u32 track_num, track_id;
70 : GF_ISOSample sample;
71 :
72 : u32 src_timescale;
73 : u32 tk_timescale;
74 : u32 stream_type;
75 : u32 codecid;
76 : Bool is_encrypted;
77 :
78 : u32 cfg_crc, enh_cfg_crc;
79 : u32 dep_id;
80 : u32 stsd_idx;
81 : u32 clear_stsd_idx;
82 :
83 : Bool use_dref;
84 : Bool aborted;
85 : Bool suspended;
86 : Bool has_append;
87 : Bool has_ctts;
88 : s64 min_neg_ctts;
89 : u32 nb_samples, samples_in_stsd;
90 : u32 nb_frames_per_sample;
91 : u64 ts_shift;
92 : Bool has_subs;
93 :
94 : Bool skip_bitrate_update;
95 : Bool has_open_gop;
96 : GF_FilterSAPType gdr_type;
97 :
98 : Bool next_is_first_sample;
99 :
100 : u32 media_profile_level;
101 :
102 : Bool import_msg_header_done;
103 :
104 : u32 nal_unit_size;
105 :
106 : GF_AVCConfig *avcc, *svcc;
107 : GF_HEVCConfig *hvcc, *lvcc;
108 : GF_VVCConfig *vvcc;
109 :
110 : u8 *inband_hdr, *inband_hdr_non_rap;
111 : u32 inband_hdr_size, inband_hdr_non_rap_size;
112 : u32 is_nalu;
113 : Bool is_av1, is_vpx;
114 : Bool fragment_done;
115 : s32 ts_delay, negctts_shift;
116 : Bool insert_tfdt, probe_min_ctts;
117 : u64 first_dts_in_seg, next_seg_cts, cts_next;
118 : u64 offset_dts;
119 : u32 samples_in_frag;
120 : Bool patch_tfdt;
121 :
122 : //0: not cenc, 1: needs setup of stsd entry, 2: setup done
123 : u32 cenc_state;
124 : Bool cenc_subsamples;
125 : u32 scheme_type;
126 : u32 def_skip_byte_block, def_crypt_byte_block;
127 : u32 def_cenc_key_info_crc;
128 : const GF_PropertyValue *cenc_ki;
129 : u32 cenc_key_info_crc;
130 : u32 constant_IV_size;
131 : Bool cenc_multikey;
132 : Bool cenc_frag_protected;
133 :
134 : Bool fake_track;
135 :
136 : Bool has_brands;
137 : Bool force_inband_inject;
138 :
139 : u64 dur_in_frag;
140 :
141 : u32 amr_mode_set;
142 : Bool has_seig;
143 : u64 empty_init_dur;
144 : u32 raw_audio_bytes_per_sample, raw_samplerate;
145 : u64 dts_patch;
146 :
147 : Bool is_item;
148 : u32 item_id;
149 : char status_type;
150 : u32 last_import_pc;
151 :
152 : u32 nb_frames;
153 : u64 down_bytes, down_size;
154 : GF_Fraction64 pid_dur;
155 : //for import message
156 : u64 prog_done, prog_total;
157 :
158 : u32 prev_tid_group;
159 :
160 : Bool box_patched;
161 :
162 : u64 imported_edit_sdur, imported_edit_offset;
163 :
164 : Bool force_ctts;
165 :
166 : Bool is_hevc_tile_base;
167 : Bool insert_pssh;
168 :
169 : Bool wait_sap;
170 : u64 min_ts_seek_plus_one;
171 : u64 clamp_ts_plus_one;
172 : Bool check_seek_ts;
173 :
174 : u64 max_cts, min_cts;
175 : u32 max_cts_samp_dur;
176 :
177 : } TrackWriter;
178 :
179 : enum
180 : {
181 : MP4MX_MODE_INTER=0,
182 : MP4MX_MODE_FLAT,
183 : MP4MX_MODE_FASTSTART,
184 : MP4MX_MODE_TIGHT,
185 : MP4MX_MODE_FRAG,
186 : MP4MX_MODE_SFRAG,
187 : };
188 :
189 :
190 : enum
191 : {
192 : MP4MX_DASH_OFF=0,
193 : MP4MX_DASH_ON,
194 : MP4MX_DASH_VOD,
195 : };
196 :
197 : enum
198 : {
199 : MP4MX_PSSH_MOOV=0,
200 : MP4MX_PSSH_MOOF,
201 : MP4MX_PSSH_SKIP,
202 : };
203 :
204 : enum
205 : {
206 : MP4MX_CT_EDIT=0,
207 : MP4MX_CT_NOEDIT,
208 : MP4MX_CT_NEGCTTS,
209 : };
210 :
211 : enum
212 : {
213 : MP4MX_VODCACHE_ON=0,
214 : MP4MX_VODCACHE_INSERT,
215 : MP4MX_VODCACHE_REPLACE,
216 : };
217 :
218 : enum
219 : {
220 : MP4MX_CMAF_NO=0,
221 : MP4MX_CMAF_CMFC,
222 : MP4MX_CMAF_CMF2,
223 : };
224 :
225 : typedef struct
226 : {
227 : //filter args
228 : GF_ISOFile *file;
229 : Bool m4sys, dref;
230 : GF_Fraction idur;
231 : u32 pack3gp, ctmode;
232 : Bool importer, pack_nal, moof_first, abs_offset, fsap, tfdt_traf, keep_utc, pps_inband;
233 : u32 xps_inband, moovpad;
234 : u32 block_size;
235 : u32 store, tktpl, mudta;
236 : s32 subs_sidx;
237 : GF_Fraction cdur;
238 : s32 moovts;
239 : char *m4cc;
240 : Bool chain_sidx;
241 : u32 msn, msninc;
242 : GF_Fraction64 tfdt;
243 : Bool nofragdef, straf, strun, sgpd_traf, noinit;
244 : u32 vodcache;
245 : u32 psshs;
246 : u32 trackid;
247 : Bool fragdur;
248 : Bool btrt;
249 : Bool ssix;
250 : Bool ccst;
251 : s32 mediats;
252 : GF_AudioSampleEntryImportMode ase;
253 : char *styp;
254 : Bool sseg;
255 : Bool noroll;
256 : Bool saio32, tfdt64;
257 : u32 compress;
258 : Bool trun_inter;
259 : Bool truns_first;
260 : char *boxpatch;
261 : Bool fcomp, otyp;
262 : Bool deps;
263 : Bool mvex;
264 : u32 sdtp_traf;
265 : u32 cmaf;
266 : #ifdef GF_ENABLE_CTRN
267 : Bool ctrn;
268 : Bool ctrni;
269 : #endif
270 : Bool mfra;
271 : Bool forcesync, refrag;
272 : u32 itags;
273 : Double start;
274 :
275 : //internal
276 : Bool owns_mov;
277 : GF_FilterPid *opid;
278 : Bool first_pck_sent;
279 :
280 : GF_List *tracks;
281 :
282 : GF_BitStream *bs_r;
283 : //fragmentation state
284 : Bool init_movie_done, fragment_started, segment_started, insert_tfdt, insert_pssh, cdur_set;
285 :
286 : u64 next_frag_start, adjusted_next_frag_start;
287 :
288 : u64 current_offset;
289 : u64 current_size;
290 :
291 : u32 nb_segs, nb_frags, nb_frags_in_seg;
292 :
293 : GF_FilterPacket *dst_pck;
294 : char *seg_name;
295 : u32 dash_seg_num_plus_one;
296 : Bool flush_seg;
297 : u32 eos_marker;
298 : TrackWriter *ref_tkw;
299 : Bool single_file;
300 : Bool store_output;
301 : FILE *tmp_store;
302 : u64 flush_size, flush_done;
303 :
304 : u32 dash_mode, llhls_mode;
305 : GF_Fraction dash_dur;
306 : Double media_dur;
307 : u32 sidx_max_size, sidx_chunk_offset;
308 : Bool final_sidx_flush;
309 : Bool sidx_size_exact;
310 :
311 : u32 *seg_sizes;
312 : u32 nb_seg_sizes, alloc_seg_sizes;
313 : Bool config_timing;
314 :
315 : u32 major_brand_set;
316 : Bool def_brand_patched;
317 :
318 : Bool force_play;
319 :
320 : Bool moov_inserted;
321 : Bool update_report;
322 : u64 total_bytes_in, total_bytes_out;
323 : u32 total_samples, last_mux_pc;
324 :
325 : u32 maxchunk;
326 : u32 make_qt;
327 : TrackWriter *prores_track;
328 :
329 : GF_SegmentIndexBox *cloned_sidx;
330 : u32 cloned_sidx_index;
331 : GF_Fraction faststart_ts_regulate;
332 :
333 : Bool is_rewind;
334 : Bool box_patched;
335 : u32 cur_file_idx_plus_one;
336 : char *cur_file_suffix;
337 : Bool notify_filename;
338 :
339 : u32 next_file_idx;
340 : const char *next_file_suffix;
341 :
342 : //for route scheduling
343 : u64 min_cts_plus_one, next_seg_start;
344 : u64 min_cts_next_frag;
345 :
346 : u64 frag_size, frag_offset;
347 : u32 frag_num;
348 : u64 frag_duration;
349 : u32 frag_timescale;
350 : Bool frag_has_intra;
351 :
352 : u64 wait_dts_plus_one;
353 : u32 wait_dts_timescale;
354 : } GF_MP4MuxCtx;
355 :
356 : static void mp4_mux_set_hevc_groups(GF_MP4MuxCtx *ctx, TrackWriter *tkw);
357 :
358 18 : static GF_Err mp4mx_setup_dash_vod(GF_MP4MuxCtx *ctx, TrackWriter *tkw)
359 : {
360 18 : if (tkw) {
361 : const GF_PropertyValue *p;
362 18 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_DASH_DUR);
363 18 : if (p) {
364 18 : ctx->dash_dur = p->value.frac;
365 : }
366 18 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_DURATION);
367 18 : if (p && p->value.lfrac.den) {
368 18 : Double mdur = (Double) p->value.lfrac.num;
369 18 : mdur /= p->value.lfrac.den;
370 18 : if (ctx->media_dur < mdur) ctx->media_dur = mdur;
371 : }
372 : }
373 18 : ctx->dash_mode = MP4MX_DASH_VOD;
374 18 : ctx->llhls_mode = 0;
375 18 : if ((ctx->vodcache==MP4MX_VODCACHE_ON) && !ctx->tmp_store) {
376 17 : ctx->tmp_store = gf_file_temp(NULL);
377 17 : if (!ctx->tmp_store) {
378 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Cannot allocate temp file for VOD sidx generation\n"));
379 : return GF_IO_ERR;
380 : }
381 17 : if (!ctx->block_size) ctx->block_size = 10000;
382 : }
383 :
384 : return GF_OK;
385 : }
386 :
387 :
388 1453 : static u32 gf_isom_stream_type_to_media_type(u32 stream_type, u32 codecid)
389 : {
390 1453 : switch (stream_type) {
391 : case GF_STREAM_SCENE: return GF_ISOM_MEDIA_SCENE;
392 13 : case GF_STREAM_OD: return GF_ISOM_MEDIA_OD;
393 0 : case GF_STREAM_OCR: return GF_ISOM_MEDIA_OCR;
394 0 : case GF_STREAM_OCI: return GF_ISOM_MEDIA_OCI;
395 0 : case GF_STREAM_MPEG7: return GF_ISOM_MEDIA_MPEG7;
396 0 : case GF_STREAM_METADATA: return GF_ISOM_MEDIA_META;
397 974 : case GF_STREAM_VISUAL: return GF_ISOM_MEDIA_VISUAL;
398 313 : case GF_STREAM_AUDIO: return GF_ISOM_MEDIA_AUDIO;
399 111 : case GF_STREAM_TEXT:
400 111 : if (codecid==GF_ISOM_SUBTYPE_STPP)
401 : return GF_ISOM_MEDIA_MPEG_SUBT;
402 94 : if (codecid == GF_CODECID_SUBPIC)
403 : return GF_ISOM_MEDIA_SUBPIC;
404 93 : return GF_ISOM_MEDIA_TEXT;
405 : case GF_STREAM_INTERACT: return GF_ISOM_MEDIA_SCENE;
406 0 : case GF_STREAM_IPMP: return GF_ISOM_MEDIA_IPMP;
407 0 : case GF_STREAM_MPEGJ: return GF_ISOM_MEDIA_MPEGJ;
408 0 : case GF_STREAM_IPMP_TOOL: return GF_ISOM_MEDIA_IPMP;
409 0 : case GF_STREAM_FONT: return GF_ISOM_MEDIA_MPEGJ;//TOCHECK !!
410 :
411 0 : case GF_STREAM_PRIVATE_SCENE:
412 : case GF_STREAM_ENCRYPTED:
413 : case GF_STREAM_FILE:
414 0 : return 0;
415 26 : default:
416 26 : return stream_type;
417 : }
418 : return 0;
419 : }
420 :
421 94 : static void mp4_mux_write_ps_list(GF_BitStream *bs, GF_List *list, u32 nalu_size_length)
422 : {
423 94 : u32 i, count = list ? gf_list_count(list) : 0;
424 194 : for (i=0; i<count; i++) {
425 100 : GF_NALUFFParam *sl = gf_list_get(list, i);
426 100 : gf_bs_write_int(bs, sl->size, 8*nalu_size_length);
427 100 : gf_bs_write_data(bs, sl->data, sl->size);
428 : }
429 94 : }
430 :
431 48 : static GF_List *mp4_mux_get_nalus_ps(GF_List *list, u8 type)
432 : {
433 48 : u32 i, count = gf_list_count(list);
434 96 : for (i=0; i<count; i++) {
435 96 : GF_NALUFFParamArray *pa = gf_list_get(list, i);
436 96 : if (pa->type == type) return pa->nalus;
437 : }
438 : return NULL;
439 : }
440 :
441 39 : static void mp4_mux_make_inband_header(GF_MP4MuxCtx *ctx, TrackWriter *tkw, Bool for_non_rap)
442 : {
443 : GF_BitStream *bs;
444 39 : if (for_non_rap) {
445 0 : if (tkw->inband_hdr_non_rap) gf_free(tkw->inband_hdr_non_rap);
446 0 : tkw->inband_hdr_non_rap = NULL;
447 : } else {
448 39 : if (tkw->inband_hdr) gf_free(tkw->inband_hdr);
449 39 : tkw->inband_hdr = NULL;
450 : }
451 :
452 39 : tkw->nal_unit_size = 0;
453 39 : bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
454 39 : if (tkw->avcc || tkw->svcc) {
455 23 : if (tkw->avcc) {
456 23 : if (!for_non_rap)
457 23 : mp4_mux_write_ps_list(bs, tkw->avcc->sequenceParameterSets, tkw->avcc->nal_unit_size);
458 23 : /*if (!tkw->nal_unit_size) */tkw->nal_unit_size = tkw->avcc->nal_unit_size;
459 : }
460 :
461 23 : if (tkw->svcc) {
462 0 : if (!for_non_rap)
463 0 : mp4_mux_write_ps_list(bs, tkw->svcc->sequenceParameterSets, tkw->svcc->nal_unit_size);
464 0 : if (!tkw->nal_unit_size) tkw->nal_unit_size = tkw->svcc->nal_unit_size;
465 : }
466 :
467 23 : if (tkw->avcc && tkw->avcc->sequenceParameterSetExtensions && !for_non_rap)
468 0 : mp4_mux_write_ps_list(bs, tkw->avcc->sequenceParameterSetExtensions, tkw->avcc->nal_unit_size);
469 :
470 23 : if (tkw->svcc && tkw->svcc->sequenceParameterSetExtensions && !for_non_rap)
471 0 : mp4_mux_write_ps_list(bs, tkw->svcc->sequenceParameterSetExtensions, tkw->svcc->nal_unit_size);
472 :
473 23 : if (tkw->avcc)
474 23 : mp4_mux_write_ps_list(bs, tkw->avcc->pictureParameterSets, tkw->avcc->nal_unit_size);
475 :
476 23 : if (tkw->svcc)
477 0 : mp4_mux_write_ps_list(bs, tkw->svcc->pictureParameterSets, tkw->svcc->nal_unit_size);
478 : }
479 39 : if (tkw->hvcc || tkw->lvcc) {
480 16 : if (tkw->hvcc) {
481 16 : if (!for_non_rap)
482 16 : mp4_mux_write_ps_list(bs, mp4_mux_get_nalus_ps(tkw->hvcc->param_array, GF_HEVC_NALU_VID_PARAM), tkw->hvcc->nal_unit_size);
483 16 : if (!tkw->nal_unit_size) tkw->nal_unit_size = tkw->hvcc->nal_unit_size;
484 : }
485 16 : if (tkw->lvcc) {
486 0 : if (!for_non_rap)
487 0 : mp4_mux_write_ps_list(bs, mp4_mux_get_nalus_ps(tkw->lvcc->param_array, GF_HEVC_NALU_VID_PARAM), tkw->lvcc->nal_unit_size);
488 0 : if (!tkw->nal_unit_size) tkw->nal_unit_size = tkw->lvcc->nal_unit_size;
489 : }
490 16 : if (tkw->hvcc && !for_non_rap)
491 16 : mp4_mux_write_ps_list(bs, mp4_mux_get_nalus_ps(tkw->hvcc->param_array, GF_HEVC_NALU_SEQ_PARAM), tkw->hvcc->nal_unit_size);
492 16 : if (tkw->lvcc && !for_non_rap)
493 0 : mp4_mux_write_ps_list(bs, mp4_mux_get_nalus_ps(tkw->lvcc->param_array, GF_HEVC_NALU_SEQ_PARAM), tkw->lvcc->nal_unit_size);
494 16 : if (tkw->hvcc)
495 16 : mp4_mux_write_ps_list(bs, mp4_mux_get_nalus_ps(tkw->hvcc->param_array, GF_HEVC_NALU_PIC_PARAM), tkw->hvcc->nal_unit_size);
496 16 : if (tkw->lvcc)
497 0 : mp4_mux_write_ps_list(bs, mp4_mux_get_nalus_ps(tkw->lvcc->param_array, GF_HEVC_NALU_PIC_PARAM), tkw->lvcc->nal_unit_size);
498 : }
499 :
500 39 : if (tkw->vvcc) {
501 0 : if (!for_non_rap)
502 0 : mp4_mux_write_ps_list(bs, mp4_mux_get_nalus_ps(tkw->vvcc->param_array, GF_VVC_NALU_VID_PARAM), tkw->vvcc->nal_unit_size);
503 0 : if (!tkw->nal_unit_size) tkw->nal_unit_size = tkw->vvcc->nal_unit_size;
504 :
505 0 : if (!for_non_rap)
506 0 : mp4_mux_write_ps_list(bs, mp4_mux_get_nalus_ps(tkw->vvcc->param_array, GF_VVC_NALU_SEQ_PARAM), tkw->vvcc->nal_unit_size);
507 :
508 0 : mp4_mux_write_ps_list(bs, mp4_mux_get_nalus_ps(tkw->vvcc->param_array, GF_VVC_NALU_PIC_PARAM), tkw->vvcc->nal_unit_size);
509 0 : mp4_mux_write_ps_list(bs, mp4_mux_get_nalus_ps(tkw->vvcc->param_array, GF_VVC_NALU_APS_PREFIX), tkw->vvcc->nal_unit_size);
510 : }
511 :
512 39 : if (for_non_rap) {
513 0 : gf_bs_get_content(bs, &tkw->inband_hdr_non_rap, &tkw->inband_hdr_non_rap_size);
514 : } else {
515 39 : gf_bs_get_content(bs, &tkw->inband_hdr, &tkw->inband_hdr_size);
516 : }
517 39 : gf_bs_del(bs);
518 : //we may have cases where the param sets are updated before a non-IDR/SAP3 picture, we must inject asap at least once
519 39 : tkw->force_inband_inject = GF_TRUE;
520 39 : }
521 :
522 66 : void mp4_mux_get_video_size(GF_MP4MuxCtx *ctx, u32 *width, u32 *height)
523 : {
524 : u32 w, h, f_w, f_h, i;
525 :
526 : f_w = f_h = 0;
527 206 : for (i=0; i<gf_isom_get_track_count(ctx->file); i++) {
528 74 : switch (gf_isom_get_media_type(ctx->file, i+1)) {
529 5 : case GF_ISOM_MEDIA_SCENE:
530 : case GF_ISOM_MEDIA_VISUAL:
531 5 : gf_isom_get_visual_info(ctx->file, i+1, 1, &w, &h);
532 5 : if (w > f_w) f_w = w;
533 5 : if (h > f_h) f_h = h;
534 : //fallthrough
535 : case GF_ISOM_MEDIA_TEXT:
536 71 : gf_isom_get_track_layout_info(ctx->file, i+1, &w, &h, NULL, NULL, NULL);
537 71 : if (w > f_w) f_w = w;
538 71 : if (h > f_h) f_h = h;
539 : break;
540 : }
541 : }
542 66 : (*width) = f_w ? f_w : TEXT_DEFAULT_WIDTH;
543 66 : (*height) = f_h ? f_h : TEXT_DEFAULT_HEIGHT;
544 66 : }
545 :
546 1376 : static void mp4_mux_track_writer_del(TrackWriter *tkw)
547 : {
548 1376 : if (tkw->avcc) gf_odf_avc_cfg_del(tkw->avcc);
549 1376 : if (tkw->svcc) gf_odf_avc_cfg_del(tkw->svcc);
550 1376 : if (tkw->hvcc) gf_odf_hevc_cfg_del(tkw->hvcc);
551 1376 : if (tkw->lvcc) gf_odf_hevc_cfg_del(tkw->lvcc);
552 1376 : if (tkw->vvcc) gf_odf_vvc_cfg_del(tkw->vvcc);
553 1376 : if (tkw->inband_hdr) gf_free(tkw->inband_hdr);
554 1376 : if (tkw->inband_hdr_non_rap) gf_free(tkw->inband_hdr_non_rap);
555 1376 : gf_free(tkw);
556 1376 : }
557 :
558 4404 : static void mp4_mux_write_track_refs(GF_MP4MuxCtx *ctx, TrackWriter *tkw, const char *rname, u32 rtype)
559 : {
560 : u32 i;
561 4404 : const GF_PropertyValue *p = gf_filter_pid_get_property_str(tkw->ipid, rname);
562 4404 : if (!p) return;
563 241 : for (i=0; i<p->value.uint_list.nb_items; i++) {
564 241 : gf_isom_set_track_reference(ctx->file, tkw->track_num, rtype, p->value.uint_list.vals[i]);
565 : }
566 : }
567 :
568 0 : static void mp4mux_track_reorder(void *udta, u32 old_track_num, u32 new_track_num)
569 : {
570 : GF_MP4MuxCtx *ctx = (GF_MP4MuxCtx *) udta;
571 : u32 i, count;
572 0 : count = gf_list_count(ctx->tracks);
573 0 : for (i=0; i<count; i++) {
574 0 : TrackWriter *tkw = gf_list_get(ctx->tracks, i);
575 0 : if (tkw->track_num==old_track_num) {
576 0 : tkw->track_num = new_track_num;
577 0 : return;
578 : }
579 : }
580 : }
581 :
582 461 : static void mp4mux_reorder_tracks(GF_MP4MuxCtx *ctx)
583 : {
584 : u32 i, count, prev_num, prev_pos;
585 461 : GF_List *new_tracks = gf_list_new();
586 : prev_num = prev_pos = 0;
587 461 : count = gf_list_count(ctx->tracks);
588 469 : for (i=0; i<count; i++) {
589 469 : TrackWriter *tkw = gf_list_get(ctx->tracks, i);
590 469 : if (tkw->track_num<prev_num) {
591 1 : gf_list_insert(new_tracks, tkw, prev_pos);
592 : } else {
593 468 : gf_list_add(new_tracks, tkw);
594 : }
595 469 : prev_pos = gf_list_count(new_tracks) - 1;
596 469 : prev_num = tkw->track_num;
597 : }
598 461 : if (gf_list_count(new_tracks)!=count) {
599 0 : gf_list_del(new_tracks);
600 : return;
601 : }
602 461 : gf_list_del(ctx->tracks);
603 461 : ctx->tracks = new_tracks;
604 : }
605 :
606 1461 : static void mp4_mux_set_tags(GF_MP4MuxCtx *ctx, TrackWriter *tkw)
607 : {
608 1461 : u32 idx=0;
609 1461 : if (ctx->itags==TAG_NONE) return;
610 :
611 : while (1) {
612 : GF_Err e;
613 : u32 len;
614 42054 : u32 prop_4cc=0;
615 : u32 itag;
616 : s32 tag_idx;
617 42054 : const char *tag_name=NULL;
618 42054 : const GF_PropertyValue *tag = gf_filter_pid_enum_properties(tkw->ipid, &idx, &prop_4cc, &tag_name);
619 42054 : if (!tag) break;
620 :
621 40593 : if (prop_4cc==GF_PROP_PID_COVER_ART) {
622 0 : e = gf_isom_apple_set_tag(ctx->file, GF_ISOM_ITUNE_COVER_ART, tag->value.data.ptr, tag->value.data.size, 0, 0);
623 0 : if (e) {
624 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to set cover art: %s\n", gf_error_to_string(e)));
625 : }
626 : }
627 40593 : if (!tag_name)
628 80898 : continue;
629 :
630 288 : tag_idx = gf_itags_find_by_name(tag_name);
631 288 : if (tag_idx>=0) {
632 0 : itag = gf_itags_get_itag(tag_idx);
633 : } else {
634 288 : if (ctx->itags==TAG_STRICT)
635 288 : continue;
636 :
637 0 : if (strnicmp(tag_name, "tag_", 4))
638 0 : continue;
639 :
640 0 : tag_name += 4;
641 :
642 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MP4Mux] Unrecognized tag %s: %s\n", tag_name, tag->value.string));
643 :
644 0 : if (strlen(tag_name)==4) {
645 0 : itag = GF_4CC(tag_name[0], tag_name[1], tag_name[2], tag_name[3]);
646 0 : } else if (strlen(tag_name)==3) {
647 0 : itag = GF_4CC(0xA9, tag_name[0], tag_name[1], tag_name[2]);
648 : } else {
649 0 : itag = gf_crc_32(tag_name, (u32) strlen(tag_name));
650 0 : GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MP4Mux] Tag name %s is not a 4CC, using CRC32 %08X as value\n", tag_name, itag));
651 : }
652 : }
653 :
654 0 : switch (tag->type) {
655 0 : case GF_PROP_STRING:
656 : case GF_PROP_NAME:
657 0 : len = tag->value.string ? (u32) strlen(tag->value.string) : 0;
658 0 : e = gf_isom_apple_set_tag(ctx->file, GF_ISOM_ITUNE_COVER_ART, tag->value.string, len, 0, 0);
659 : break;
660 0 : case GF_PROP_BOOL:
661 0 : e = gf_isom_apple_set_tag(ctx->file, itag, NULL, 0, tag->value.boolean, 0);
662 : break;
663 0 : case GF_PROP_UINT:
664 : case GF_PROP_4CC:
665 0 : e = gf_isom_apple_set_tag(ctx->file, itag, NULL, 0, tag->value.uint, 0);
666 : break;
667 0 : case GF_PROP_LUINT:
668 0 : e = gf_isom_apple_set_tag(ctx->file, itag, NULL, 0, tag->value.longuint, 0);
669 : break;
670 0 : case GF_PROP_FRACTION:
671 0 : e = gf_isom_apple_set_tag(ctx->file, itag, NULL, 0, tag->value.frac.num, tag->value.frac.den);
672 : break;
673 0 : case GF_PROP_DATA:
674 : case GF_PROP_CONST_DATA:
675 0 : e = gf_isom_apple_set_tag(ctx->file, itag, tag->value.data.ptr, tag->value.data.size, 0, 0);
676 : break;
677 0 : default:
678 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to set tag %s: invalid data format\n", gf_itags_get_name(tag_idx) ));
679 : e = GF_OK;
680 : break;
681 : }
682 :
683 0 : if (e) {
684 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to set tag %s: %s\n", tag_name, gf_error_to_string(e)));
685 : }
686 : }
687 : }
688 :
689 :
690 2794 : static GF_Err mp4_mux_setup_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_true_pid)
691 : {
692 : void mux_assign_mime_file_ext(GF_FilterPid *ipid, GF_FilterPid *opid, const char *file_exts, const char *mime_types, const char *def_ext);
693 : Bool use_m4sys = GF_FALSE;
694 : Bool use_tx3g = GF_FALSE;
695 : Bool use_webvtt = GF_FALSE;
696 : Bool needs_track = GF_FALSE;
697 : u32 needs_sample_entry = 0; //1: change of codecID, 2 change of decoder config
698 : Bool use_gen_sample_entry = GF_FALSE;
699 : Bool use_3gpp_config = GF_FALSE;
700 : Bool use_ac3_entry = GF_FALSE;
701 : Bool use_flac_entry = GF_FALSE;
702 : Bool use_avc = GF_FALSE;
703 : Bool use_hevc = GF_FALSE;
704 : Bool use_vvc = GF_FALSE;
705 : Bool use_hvt1 = GF_FALSE;
706 : Bool use_av1 = GF_FALSE;
707 : Bool use_vpX = GF_FALSE;
708 : Bool use_mj2 = GF_FALSE;
709 : Bool use_opus = GF_FALSE;
710 : Bool use_dref = GF_FALSE;
711 : Bool skip_dsi = GF_FALSE;
712 : Bool is_text_subs = GF_FALSE;
713 : Bool force_colr = GF_FALSE;
714 : u32 m_subtype=0;
715 : u32 m_subtype_src=0;
716 : u32 m_subtype_alt_raw=0;
717 : u32 raw_bitdepth=0;
718 : u32 override_stype=0;
719 : u32 width, height, sr, nb_chan, nb_bps, z_order, txt_fsize;
720 : u64 ch_layout;
721 : GF_Fraction fps, sar;
722 : GF_List *multi_pid_stsd = NULL;
723 : u32 multi_pid_idx = 0;
724 : GF_FilterPid *orig_pid = NULL;
725 : u32 codec_id;
726 : u32 frames_per_sample_backup=0;
727 : u32 is_nalu_backup = NALU_NONE;
728 : Bool is_tile_base = GF_FALSE;
729 : Bool unknown_generic = GF_FALSE;
730 : u32 multi_pid_final_stsd_idx = 0;
731 : u32 audio_pli=0;
732 : Bool force_tk_layout = GF_FALSE;
733 : Bool force_mix_xps = GF_FALSE;
734 : Bool make_inband_headers = GF_FALSE;
735 : Bool is_prores = GF_FALSE;
736 :
737 : const char *lang_name = NULL;
738 : const char *comp_name = NULL;
739 : const char *imp_name = NULL;
740 : const char *src_url = NULL;
741 : const char *meta_mime = NULL;
742 : const char *meta_encoding = NULL;
743 : const char *meta_config = NULL;
744 : const char *meta_xmlns = NULL;
745 : const char *meta_schemaloc = NULL;
746 : const char *meta_auxmimes = NULL;
747 : const char *meta_content_encoding = NULL;
748 : char *txt_font = NULL;
749 :
750 : u32 i, count, reuse_stsd = 0;
751 : GF_Err e;
752 : const GF_PropertyValue *dsi=NULL;
753 : const GF_PropertyValue *enh_dsi=NULL;
754 : const GF_PropertyValue *p;
755 2794 : GF_MP4MuxCtx *ctx = gf_filter_get_udta(filter);
756 2794 : GF_AudioSampleEntryImportMode ase_mode = ctx->ase;
757 : TrackWriter *tkw;
758 :
759 2794 : if (ctx->owns_mov && !ctx->opid) {
760 : char *dst;
761 664 : ctx->opid = gf_filter_pid_new(filter);
762 :
763 664 : dst = gf_filter_get_dst_name(filter);
764 664 : if (dst) {
765 664 : char *ext = gf_file_ext_start(dst);
766 664 : if (ext && (!stricmp(ext, ".mov") || !stricmp(ext, ".qt")) ) {
767 1 : ctx->make_qt = 1;
768 : }
769 664 : gf_free(dst);
770 : }
771 : }
772 : //copy properties at init or reconfig
773 2794 : if (ctx->opid && is_true_pid) {
774 1439 : gf_filter_pid_copy_properties(ctx->opid, pid);
775 1439 : if (gf_list_count(ctx->tracks)>1)
776 145 : gf_filter_pid_set_name(ctx->opid, "isobmf_mux");
777 :
778 1439 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG, NULL);
779 1439 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DECODER_CONFIG_ENHANCEMENT, NULL);
780 1439 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_CODECID, NULL);
781 1439 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_UNFRAMED, NULL);
782 1439 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_STREAM_TYPE, &PROP_UINT(GF_STREAM_FILE) );
783 :
784 1439 : mux_assign_mime_file_ext(pid, ctx->opid, ISOM_FILE_EXT, ISOM_FILE_MIME, NULL);
785 :
786 1439 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DASH_MODE, NULL);
787 : //we dispatch timing in milliseconds
788 1439 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_TIMESCALE, &PROP_UINT(1000));
789 1439 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
790 1439 : if (p)
791 1439 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_ORIG_STREAM_TYPE, &PROP_UINT(p->value.uint));
792 :
793 1439 : switch (ctx->store) {
794 2 : case MP4MX_MODE_FLAT:
795 : case MP4MX_MODE_FASTSTART:
796 2 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DISABLE_PROGRESSIVE, &PROP_UINT(GF_PID_FILE_PATCH_INSERT) );
797 2 : break;
798 894 : case MP4MX_MODE_INTER:
799 : case MP4MX_MODE_TIGHT:
800 894 : gf_filter_pid_allow_direct_dispatch(ctx->opid);
801 894 : break;
802 : }
803 : }
804 :
805 2794 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_TILE_BASE);
806 2794 : if (p && p->value.boolean)
807 : is_tile_base = GF_TRUE;
808 :
809 2794 : if (is_true_pid && !is_tile_base) {
810 2762 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DASH_MULTI_TRACK);
811 2762 : if (p) {
812 : u32 j, count2;
813 3 : GF_List *multi_tracks = p->value.ptr;
814 3 : count = gf_list_count(multi_tracks);
815 6 : for (i=0; i<count; i++) {
816 3 : GF_FilterPid *a_ipid = gf_list_get(multi_tracks, i);
817 3 : const GF_PropertyValue *a_pidid = gf_filter_pid_get_property(a_ipid, GF_PROP_PID_ID);
818 3 : count2 = gf_list_count(ctx->tracks);
819 3 : for (j=0; j<count2; j++) {
820 3 : TrackWriter *atkw = gf_list_get(ctx->tracks, j);
821 3 : const GF_PropertyValue *c_pidid = gf_filter_pid_get_property(atkw->ipid, GF_PROP_PID_ID);
822 3 : if (gf_props_equal(a_pidid, c_pidid)) {
823 : a_ipid = NULL;
824 : break;
825 : }
826 : }
827 3 : if (a_ipid)
828 0 : mp4_mux_setup_pid(filter, a_ipid, GF_FALSE);
829 : }
830 : }
831 : }
832 :
833 2794 : audio_pli = gf_isom_get_pl_indication(ctx->file, GF_ISOM_PL_AUDIO);
834 :
835 : //new pid ?
836 2794 : tkw = gf_filter_pid_get_udta(pid);
837 2794 : if (!tkw) {
838 : GF_FilterEvent evt;
839 1387 : GF_SAFEALLOC(tkw, TrackWriter);
840 1387 : if (!tkw) return GF_OUT_OF_MEM;
841 :
842 1387 : gf_list_add(ctx->tracks, tkw);
843 1387 : tkw->ipid = pid;
844 1387 : tkw->fake_track = !is_true_pid;
845 1387 : tkw->min_cts = (u64) -1;
846 :
847 1387 : if (is_true_pid) {
848 1360 : gf_filter_pid_set_udta(pid, tkw);
849 :
850 1360 : tkw->is_hevc_tile_base = is_tile_base;
851 : #ifdef GPAC_ENABLE_COVERAGE
852 1360 : if (gf_sys_is_cov_mode()) {
853 1359 : gf_filter_pid_get_min_pck_duration(pid);
854 : }
855 : #endif
856 1360 : if (!ctx->owns_mov || ctx->force_play) {
857 624 : if (!ctx->owns_mov) {
858 615 : if (ctx->start != 0)
859 0 : tkw->wait_sap = GF_TRUE;
860 615 : gf_filter_pid_init_play_event(pid, &evt, ctx->start, 0, "MP4Mux");
861 : } else {
862 9 : GF_FEVT_INIT(evt, GF_FEVT_PLAY, pid);
863 : }
864 624 : gf_filter_pid_send_event(pid, &evt);
865 : }
866 1360 : gf_filter_pid_set_framing_mode(pid, GF_TRUE);
867 :
868 1360 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ITEM_ID);
869 1360 : if (p) {
870 32 : tkw->is_item = GF_TRUE;
871 : } else {
872 1328 : ctx->config_timing = GF_TRUE;
873 1328 : ctx->update_report = GF_TRUE;
874 : }
875 : }
876 : }
877 :
878 : //check change of pid config
879 2794 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DEPENDENCY_ID);
880 2794 : if (p) {
881 83 : if (p->value.uint!=tkw->dep_id) needs_track = GF_TRUE;
882 83 : tkw->dep_id = p->value.uint;
883 : }
884 :
885 : //check change of pid config
886 2794 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
887 2794 : if (p) {
888 2794 : if (p->value.uint!=tkw->codecid) needs_sample_entry = 1;
889 2794 : tkw->codecid = p->value.uint;
890 : }
891 :
892 2794 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_STREAM_TYPE);
893 2794 : if (p) {
894 2794 : u32 stype = p->value.uint;
895 2794 : if (tkw->is_encrypted && (p->value.uint==GF_STREAM_ENCRYPTED) ) {
896 336 : stype = gf_codecid_type(tkw->codecid);
897 : }
898 2794 : if (stype != tkw->stream_type) {
899 : needs_track = GF_TRUE;
900 1387 : tkw->stream_type = stype;
901 1387 : const char *name = gf_stream_type_name(stype);
902 1387 : tkw->status_type = name ? name[0] : 'U';
903 : }
904 : }
905 :
906 2794 : dsi = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG);
907 2794 : if (dsi) {
908 2499 : u32 cfg_crc = gf_crc_32(dsi->value.data.ptr, dsi->value.data.size);
909 2499 : if (cfg_crc!=tkw->cfg_crc) needs_sample_entry = 2;
910 2499 : tkw->cfg_crc = cfg_crc;
911 295 : } else if (tkw->cfg_crc) {
912 0 : tkw->cfg_crc = 0;
913 : needs_sample_entry = 2;
914 : }
915 :
916 2794 : enh_dsi = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG_ENHANCEMENT);
917 2810 : if (enh_dsi && (enh_dsi->type==GF_PROP_DATA) ) {
918 16 : u32 cfg_crc = gf_crc_32(enh_dsi->value.data.ptr, enh_dsi->value.data.size);
919 16 : if (cfg_crc!=tkw->enh_cfg_crc) needs_sample_entry = 2;
920 16 : tkw->enh_cfg_crc = cfg_crc;
921 2778 : } else if (tkw->enh_cfg_crc) {
922 0 : tkw->enh_cfg_crc = 0;
923 : needs_sample_entry = 2;
924 : }
925 :
926 : //TODO: try to merge PPS/SPS for AVC and HEVC rather than creating a new sample description
927 :
928 2794 : switch (tkw->codecid) {
929 1245 : case GF_CODECID_AAC_MPEG4:
930 : case GF_CODECID_AAC_MPEG2_MP:
931 : case GF_CODECID_AAC_MPEG2_LCP:
932 : case GF_CODECID_AAC_MPEG2_SSRP:
933 : case GF_CODECID_USAC:
934 : case GF_CODECID_MPEG4_PART2:
935 : case GF_CODECID_AVC:
936 : case GF_CODECID_SVC:
937 : case GF_CODECID_HEVC:
938 : case GF_CODECID_LHVC:
939 1245 : if (!dsi && !enh_dsi) return GF_OK;
940 : break;
941 4 : case GF_CODECID_APCH:
942 : case GF_CODECID_APCO:
943 : case GF_CODECID_APCN:
944 : case GF_CODECID_APCS:
945 : case GF_CODECID_AP4X:
946 : case GF_CODECID_AP4H:
947 4 : if (!ctx->make_qt) {
948 2 : GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MP4Mux] ProRes track detected, muxing to QTFF even though ISOBMFF was asked\n"));
949 2 : ctx->make_qt = 2;
950 : }
951 4 : if (ctx->prores_track && (ctx->prores_track != tkw)) {
952 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] More than one ProRes track detected, result might be non compliant\n"));
953 : }
954 : is_prores = GF_TRUE;
955 : break;
956 : default:
957 : break;
958 : }
959 2786 : if (!tkw->track_num) {
960 : needs_sample_entry = 1;
961 : needs_track = GF_TRUE;
962 : }
963 :
964 2786 : if (ctx->make_qt) {
965 4 : gf_isom_remove_root_od(ctx->file);
966 4 : gf_isom_set_brand_info(ctx->file, GF_ISOM_BRAND_QT, 512);
967 4 : gf_isom_reset_alt_brands(ctx->file);
968 4 : tkw->has_brands = GF_TRUE;
969 4 : ctx->major_brand_set = GF_ISOM_BRAND_QT;
970 4 : ctx->btrt = GF_FALSE;
971 :
972 4 : if (is_prores && !ctx->prores_track) {
973 3 : ctx->prores_track = tkw;
974 : }
975 : }
976 :
977 2786 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_URL);
978 2786 : if (p) src_url = p->value.string;
979 :
980 :
981 2786 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DASH_MODE);
982 2786 : if (p) {
983 360 : ctx->dash_mode = MP4MX_DASH_ON;
984 360 : if (p->value.uint==2) {
985 18 : e = mp4mx_setup_dash_vod(ctx, tkw);
986 18 : if (e) return e;
987 : }
988 : }
989 : //we consider that when muxing single segments, we are always in DASH, not VoD mode
990 2426 : else if (ctx->noinit) {
991 3 : ctx->dash_mode = MP4MX_DASH_ON;
992 : }
993 :
994 2786 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_LLHLS);
995 2786 : ctx->llhls_mode = p ? p->value.uint : 0;
996 : //insert tfdt in each traf for LL-HLS so that correct timing can be found when doing in-segment tune-in
997 2786 : if (ctx->llhls_mode) {
998 12 : ctx->tfdt_traf = GF_TRUE;
999 12 : ctx->store = MP4MX_MODE_SFRAG;
1000 : }
1001 :
1002 2786 : if (!ctx->cdur_set) {
1003 1272 : ctx->cdur_set = GF_TRUE;
1004 1272 : if (ctx->cdur.num<0) {
1005 1022 : if (ctx->make_qt) {
1006 3 : ctx->cdur.num = 1000;
1007 3 : ctx->cdur.den = 2000;
1008 : } else {
1009 1019 : ctx->cdur.num = 1000;
1010 1019 : ctx->cdur.den = 1000;
1011 1019 : if (ctx->dash_mode)
1012 301 : ctx->fragdur = GF_FALSE;
1013 : }
1014 250 : } else if (ctx->dash_mode)
1015 26 : ctx->fragdur = GF_TRUE;
1016 : }
1017 :
1018 2786 : if (needs_track) {
1019 1501 : if (ctx->init_movie_done) {
1020 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Cannot add track to already finalized movie in fragmented file, will request a new muxer for that track\n"));
1021 : return GF_REQUIRES_NEW_INSTANCE;
1022 : }
1023 1501 : if (tkw->is_item) {
1024 : needs_track = GF_FALSE;
1025 :
1026 48 : if (tkw->stream_type == GF_STREAM_ENCRYPTED) {
1027 10 : tkw->is_encrypted = GF_TRUE;
1028 10 : tkw->stream_type = gf_codecid_type(tkw->codecid);
1029 10 : tkw->insert_pssh = GF_TRUE;
1030 : }
1031 : }
1032 : }
1033 :
1034 2738 : if (needs_track) {
1035 : u32 tkid=0;
1036 : u32 tk_idx=0;
1037 : u32 mtype=0;
1038 : u32 target_timescale = 0;
1039 :
1040 1453 : if (ctx->make_qt && (tkw->stream_type==GF_STREAM_VISUAL)) {
1041 3 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_FPS);
1042 3 : if (p) {
1043 3 : u32 ts=p->value.frac.num, inc=p->value.frac.den;
1044 3 : if (inc * 24000 == ts * 1001) target_timescale = 24000;
1045 3 : else if (inc * 2400 == ts * 100) target_timescale = 2400;
1046 3 : else if (inc * 2500 == ts * 100) target_timescale = 2500;
1047 1 : else if (inc * 30000 == ts * 1001) target_timescale = 30000;
1048 1 : else if (inc * 2997 == ts * 100) target_timescale = 30000;
1049 0 : else if (inc * 3000 == ts * 100) target_timescale = 3000;
1050 0 : else if (inc * 5000 == ts * 100) target_timescale = 5000;
1051 0 : else if (inc * 60000 == ts * 1001) target_timescale = 60000;
1052 0 : else if (inc * 5994 == ts * 100) target_timescale = 60000;
1053 0 : else if (is_prores) {
1054 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[ProRes] Unrecognized frame rate %g\n", ((Double)ts)/inc ));
1055 : return GF_NON_COMPLIANT_BITSTREAM;
1056 : }
1057 : }
1058 3 : if (!ctx->prores_track)
1059 0 : ctx->prores_track = tkw;
1060 : }
1061 :
1062 1453 : if (!ctx->moov_inserted) {
1063 1453 : if (target_timescale) {
1064 3 : ctx->moovts = target_timescale;
1065 3 : gf_isom_set_timescale(ctx->file, target_timescale);
1066 1450 : } else if (ctx->moovts>=0) {
1067 1446 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ISOM_MOVIE_TIME);
1068 1446 : if (p && p->value.lfrac.den) {
1069 612 : gf_isom_set_timescale(ctx->file, (u32) p->value.lfrac.den);
1070 612 : ctx->moovts = (u32) p->value.lfrac.den;
1071 : } else {
1072 834 : gf_isom_set_timescale(ctx->file, ctx->moovts);
1073 : }
1074 : }
1075 1453 : if (ctx->store==MP4MX_MODE_FASTSTART) {
1076 4 : gf_isom_make_interleave_ex(ctx->file, &ctx->cdur);
1077 : }
1078 : }
1079 :
1080 : //assign some defaults
1081 1453 : tkw->src_timescale = 0;
1082 1453 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_TIMESCALE);
1083 1453 : if (p) tkw->src_timescale = p->value.uint;
1084 :
1085 : u32 mtimescale = 1000;
1086 1453 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
1087 1453 : if (p) mtimescale = p->value.uint;
1088 : else {
1089 1161 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_FPS);
1090 1161 : if (p && p->value.frac.den) mtimescale = p->value.frac.den;
1091 : }
1092 1453 : if (!tkw->src_timescale) {
1093 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] No timescale specified, guessing from media: %d\n", mtimescale));
1094 0 : tkw->src_timescale = mtimescale;
1095 : }
1096 1453 : if (target_timescale) tkw->tk_timescale = target_timescale;
1097 1450 : else if (ctx->mediats>0) tkw->tk_timescale = ctx->mediats;
1098 1450 : else if (ctx->mediats<0) tkw->tk_timescale = mtimescale;
1099 1450 : else tkw->tk_timescale = tkw->src_timescale;
1100 :
1101 1453 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ESID);
1102 1453 : if (!p) p = gf_filter_pid_get_property(pid, GF_PROP_PID_ID);
1103 1453 : if (p) tkid = p->value.uint;
1104 :
1105 1453 : if (ctx->trackid) tkid = ctx->trackid;
1106 :
1107 1453 : if (tkw->stream_type == GF_STREAM_ENCRYPTED) {
1108 229 : tkw->is_encrypted = GF_TRUE;
1109 229 : tkw->stream_type = gf_codecid_type(tkw->codecid);
1110 : }
1111 1453 : mtype = gf_isom_stream_type_to_media_type(tkw->stream_type, tkw->codecid);
1112 :
1113 1453 : if (ctx->moovts<0) {
1114 4 : ctx->moovts = tkw->tk_timescale;
1115 4 : gf_isom_set_timescale(ctx->file, (u32) ctx->moovts);
1116 : }
1117 :
1118 1453 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_MUX_INDEX);
1119 1453 : if (p) {
1120 461 : tk_idx = p->value.uint;
1121 461 : if (!ctx->owns_mov) {
1122 461 : u32 nb_dst_tk = gf_isom_get_track_count(ctx->file);
1123 461 : if (tk_idx < nb_dst_tk) {
1124 : tk_idx = nb_dst_tk;
1125 : }
1126 : }
1127 : }
1128 :
1129 1453 : if (ctx->keep_utc) {
1130 0 : if (!gf_isom_get_track_count(ctx->file)) {
1131 : u64 create_date=0, modif_date=0;
1132 0 : p = gf_filter_pid_get_property_str(tkw->ipid, "isom:creation_date");
1133 0 : if (p && (p->type==GF_PROP_LUINT)) create_date = p->value.longuint;
1134 0 : p = gf_filter_pid_get_property_str(tkw->ipid, "isom:modification_date");
1135 0 : if (p && (p->type==GF_PROP_LUINT)) modif_date = p->value.longuint;
1136 :
1137 0 : if (create_date && modif_date)
1138 0 : gf_isom_set_creation_time(ctx->file, create_date, modif_date);
1139 : }
1140 0 : gf_isom_keep_utc_times(ctx->file, GF_TRUE);
1141 : }
1142 :
1143 1453 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ISOM_TRACK_TEMPLATE);
1144 1453 : if (ctx->tktpl && p && p->value.data.ptr) {
1145 615 : Bool udta_only = (ctx->tktpl==2) ? GF_TRUE : GF_FALSE;
1146 :
1147 :
1148 615 : tkw->track_num = gf_isom_new_track_from_template(ctx->file, tkid, mtype, tkw->tk_timescale, p->value.data.ptr, p->value.data.size, udta_only);
1149 615 : if (!tkw->track_num) {
1150 0 : tkw->track_num = gf_isom_new_track_from_template(ctx->file, 0, mtype, tkw->tk_timescale, p->value.data.ptr, p->value.data.size, udta_only);
1151 : }
1152 : //purge all track references we inject internally
1153 615 : if (tkw->track_num) {
1154 615 : gf_isom_remove_track_reference(ctx->file, tkw->track_num, GF_ISOM_REF_SCAL);
1155 615 : gf_isom_remove_track_reference(ctx->file, tkw->track_num, GF_ISOM_REF_SABT);
1156 615 : gf_isom_remove_track_reference(ctx->file, tkw->track_num, GF_ISOM_REF_TBAS);
1157 615 : gf_isom_remove_track_reference(ctx->file, tkw->track_num, GF_ISOM_REF_OREF);
1158 615 : gf_isom_remove_track_reference(ctx->file, tkw->track_num, GF_ISOM_REF_BASE);
1159 : }
1160 :
1161 615 : if (!ctx->btrt) {
1162 1 : gf_isom_update_bitrate(ctx->file, tkw->track_num, 0, 0, 0, 0);
1163 : }
1164 : } else {
1165 838 : if (!mtype) {
1166 : mtype = GF_4CC('u','n','k','n');
1167 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Unable to find ISOM media type for stream type %s codec %s\n", gf_stream_type_name(tkw->stream_type), gf_codecid_name(tkw->codecid) ));
1168 : }
1169 838 : if (!tkid) tkid = tk_idx;
1170 :
1171 838 : tkw->track_num = gf_isom_new_track(ctx->file, tkid, mtype, tkw->tk_timescale);
1172 838 : if (!tkw->track_num) {
1173 13 : tkw->track_num = gf_isom_new_track(ctx->file, 0, mtype, tkw->tk_timescale);
1174 : }
1175 : //FIXME once we finally merge to filters, there is an old bug in isobmff initializing the width and height to 320x240 which breaks text import
1176 : //this should be removed and hashes regenerated
1177 838 : gf_isom_set_track_layout_info(ctx->file, tkw->track_num, 0, 0, 0, 0, 0);
1178 :
1179 838 : if (!gf_sys_is_test_mode()) {
1180 1 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_URL);
1181 1 : if (tkw->track_num && p && p->value.string) {
1182 : char szHName[1025];
1183 1 : char *f = gf_file_basename(p->value.string);
1184 1 : szHName[1024]=0;
1185 1 : snprintf(szHName, 1024, "*%s@GPAC%s", f ? f : "", gf_gpac_version() );
1186 1 : gf_isom_set_handler_name(ctx->file, tkw->track_num, szHName);
1187 : }
1188 : }
1189 : }
1190 :
1191 1453 : if (!tkw->track_num) {
1192 0 : e = gf_isom_last_error(ctx->file);
1193 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to create new track: %s\n", gf_error_to_string(e) ));
1194 : return e;
1195 : }
1196 1453 : tkw->track_id = gf_isom_get_track_id(ctx->file, tkw->track_num);
1197 1453 : tkw->next_is_first_sample = GF_TRUE;
1198 :
1199 : //TODO: this should be the case for any new file since the redefinition of track_in_movie and track_in_preview in ISOBMFF
1200 : //This will require modifying ALL our isobmf hashes
1201 1453 : if (ctx->cmaf) {
1202 0 : gf_isom_set_track_flags(ctx->file, tkw->track_num, 0x7, GF_ISOM_TKFLAGS_SET);
1203 : }
1204 :
1205 1453 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_ISOM_TRACK_FLAGS);
1206 1453 : if (p) {
1207 0 : gf_isom_set_track_flags(ctx->file, tkw->track_num, p->value.uint, GF_ISOM_TKFLAGS_SET);
1208 : } else {
1209 1453 : gf_isom_set_track_enabled(ctx->file, tkw->track_num, GF_TRUE);
1210 : }
1211 :
1212 : //if we have a subtype set for the pid, use it
1213 1453 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SUBTYPE);
1214 1453 : if (p) gf_isom_set_media_type(ctx->file, tkw->track_num, p->value.uint);
1215 :
1216 1453 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_ISOM_HANDLER);
1217 1453 : if (p && p->value.string) {
1218 0 : gf_isom_set_handler_name(ctx->file, tkw->track_num, p->value.string);
1219 : }
1220 :
1221 1453 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_ISOM_TRACK_MATRIX);
1222 1453 : if (p && (p->value.uint_list.nb_items==9)) {
1223 0 : gf_isom_set_track_matrix(ctx->file, tkw->track_num, (s32 *) p->value.uint_list.vals);
1224 : }
1225 :
1226 1453 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_SRC_MAGIC);
1227 1453 : if (p) {
1228 561 : gf_isom_set_track_magic(ctx->file, tkw->track_num, p->value.longuint);
1229 : }
1230 1453 : if (tk_idx) {
1231 461 : gf_isom_set_track_index(ctx->file, tkw->track_num, tk_idx, mp4mux_track_reorder, ctx);
1232 461 : mp4mux_reorder_tracks(ctx);
1233 : }
1234 :
1235 : //by default use cttsv1 (negative ctts)
1236 1453 : gf_isom_set_composition_offset_mode(ctx->file, tkw->track_num, GF_TRUE);
1237 :
1238 1453 : p = ctx->make_qt ? NULL : gf_filter_pid_get_property(pid, GF_PROP_PID_PROFILE_LEVEL);
1239 1450 : if (p) {
1240 512 : tkw->media_profile_level = p->value.uint;
1241 512 : if (tkw->stream_type == GF_STREAM_AUDIO) {
1242 : //patch to align old arch (IOD not written in dash) with new
1243 203 : if (!ctx->dash_mode) {
1244 142 : gf_isom_set_pl_indication(ctx->file, GF_ISOM_PL_AUDIO, p->value.uint);
1245 : }
1246 309 : } else if (tkw->stream_type == GF_STREAM_VISUAL) {
1247 : //patch to align old arch (IOD not written in dash) with new
1248 309 : if (!ctx->dash_mode) {
1249 167 : gf_isom_set_pl_indication(ctx->file, GF_ISOM_PL_VISUAL, p->value.uint);
1250 : }
1251 : }
1252 : }
1253 :
1254 1453 : if (ctx->mudta && gf_isom_get_track_count(ctx->file)==1) {
1255 1213 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ISOM_UDTA);
1256 1213 : if (ctx->tktpl && p && p->value.data.ptr) {
1257 164 : gf_isom_load_extra_boxes(ctx->file, p->value.data.ptr, p->value.data.size, (ctx->mudta==2) ? GF_TRUE : GF_FALSE);
1258 : }
1259 : }
1260 :
1261 1453 : if (ctx->sgpd_traf)
1262 1 : gf_isom_set_sample_group_in_traf(ctx->file);
1263 :
1264 1453 : if (ctx->noroll) {
1265 0 : gf_isom_remove_sample_group(ctx->file, tkw->track_num, GF_ISOM_SAMPLE_GROUP_ROLL);
1266 : }
1267 :
1268 :
1269 1453 : if (ctx->dash_mode==MP4MX_DASH_VOD) {
1270 18 : Bool use_cache = (ctx->vodcache == MP4MX_VODCACHE_ON) ? GF_TRUE : GF_FALSE;
1271 18 : if ((ctx->vodcache == MP4MX_VODCACHE_REPLACE) && (!ctx->media_dur || !ctx->dash_dur.num) ) {
1272 : use_cache = GF_TRUE;
1273 : }
1274 :
1275 18 : if (ctx->vodcache==MP4MX_VODCACHE_INSERT) {
1276 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DISABLE_PROGRESSIVE, &PROP_UINT(GF_PID_FILE_PATCH_INSERT) );
1277 : }
1278 18 : else if (!use_cache) {
1279 0 : gf_filter_pid_set_property(ctx->opid, GF_PROP_PID_DISABLE_PROGRESSIVE, &PROP_UINT(GF_PID_FILE_PATCH_REPLACE) );
1280 : }
1281 : }
1282 :
1283 1453 : if (gf_sys_old_arch_compat()) {
1284 1433 : p = gf_filter_pid_get_property_str(pid, "isom_force_ctts");
1285 1433 : if (p && p->value.boolean) tkw->force_ctts = GF_TRUE;
1286 : }
1287 : }
1288 :
1289 2786 : if (!tkw->has_brands) {
1290 : Bool is_isom = GF_FALSE;
1291 2194 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ISOM_MBRAND);
1292 2194 : if (p) {
1293 532 : if (!ctx->major_brand_set) {
1294 478 : gf_isom_set_brand_info(ctx->file, p->value.uint, 1);
1295 478 : ctx->major_brand_set = p->value.uint;
1296 : } else {
1297 54 : gf_isom_modify_alternate_brand(ctx->file, p->value.uint, GF_TRUE);
1298 : }
1299 532 : if (p->value.uint == GF_ISOM_BRAND_ISOM) is_isom = GF_TRUE;
1300 : }
1301 2194 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ISOM_BRANDS);
1302 2194 : if (p && p->value.uint_list.nb_items) {
1303 532 : tkw->has_brands = GF_TRUE;
1304 532 : if (!ctx->major_brand_set) {
1305 0 : ctx->major_brand_set = p->value.uint_list.vals[0];
1306 0 : gf_isom_set_brand_info(ctx->file, p->value.uint_list.vals[0], 1);
1307 : }
1308 : //reset alt brands, push old ones
1309 532 : gf_isom_reset_alt_brands_ex(ctx->file, GF_TRUE);
1310 1424 : for (i=0; i<p->value.uint_list.nb_items; i++) {
1311 892 : gf_isom_modify_alternate_brand(ctx->file, p->value.uint_list.vals[i], GF_TRUE);
1312 892 : if (p->value.uint_list.vals[i] == GF_ISOM_BRAND_ISOM) is_isom = GF_TRUE;
1313 : }
1314 : //and in case it was not present add major brand
1315 532 : gf_isom_modify_alternate_brand(ctx->file, ctx->major_brand_set, GF_TRUE);
1316 : }
1317 2194 : if (!ctx->m4sys && !is_isom && !ctx->def_brand_patched) {
1318 : //remove default brand
1319 945 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISOM, GF_FALSE);
1320 945 : ctx->def_brand_patched = GF_TRUE;
1321 : }
1322 :
1323 2194 : if (ctx->cmaf) {
1324 0 : gf_isom_modify_alternate_brand(ctx->file, (ctx->cmaf==MP4MX_CMAF_CMF2) ? GF_ISOM_BRAND_CMF2 : GF_ISOM_BRAND_CMFC, GF_TRUE);
1325 : }
1326 : }
1327 :
1328 2786 : width = height = sr = nb_chan = z_order = txt_fsize = 0;
1329 : nb_bps = 16;
1330 : ch_layout = 0;
1331 : fps.num = 25;
1332 : fps.den = 1;
1333 : sar.num = sar.den = 0;
1334 2786 : codec_id = tkw->codecid;
1335 2786 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DASH_MULTI_PID);
1336 2786 : if (p) {
1337 0 : multi_pid_stsd = p->value.ptr;
1338 :
1339 0 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_DASH_MULTI_PID_IDX);
1340 : assert(p);
1341 0 : multi_pid_final_stsd_idx = p->value.uint;
1342 :
1343 : //should never be the case
1344 0 : ctx->xps_inband = 0;
1345 0 : ctx->dref = GF_FALSE;
1346 : orig_pid = pid;
1347 0 : goto multipid_stsd_setup;
1348 : }
1349 :
1350 :
1351 : //WARNING !! from this point on until the goto multipid_stsd_setup, use pid and not tkw->ipid
1352 : //so that we setup the sample entry properly for each PIDs
1353 2786 : sample_entry_setup:
1354 :
1355 2786 : use_m4sys = ctx->m4sys;
1356 : use_gen_sample_entry = GF_TRUE;
1357 2786 : use_dref = ctx->dref;
1358 :
1359 2786 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_WIDTH);
1360 2786 : if (p) width = p->value.uint;
1361 2786 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_HEIGHT);
1362 2786 : if (p) height = p->value.uint;
1363 2786 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_FPS);
1364 2786 : if (p) fps = p->value.frac;
1365 2786 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAR);
1366 2786 : if (p) sar = p->value.frac;
1367 2786 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ZORDER);
1368 2786 : if (p) z_order = p->value.uint;
1369 :
1370 2786 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
1371 2786 : if (p) sr = p->value.uint;
1372 2786 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_NUM_CHANNELS);
1373 2786 : if (p) nb_chan = p->value.uint;
1374 2786 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_BPS);
1375 2786 : if (p) nb_bps = p->value.uint;
1376 2786 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CHANNEL_LAYOUT);
1377 2786 : if (p) ch_layout = p->value.longuint;
1378 :
1379 2786 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_LANGUAGE);
1380 2786 : if (p) lang_name = p->value.string;
1381 :
1382 2786 : if (is_true_pid) {
1383 2759 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_NB_FRAMES);
1384 2759 : if (p) tkw->nb_frames = p->value.uint;
1385 1595 : else tkw->nb_frames = 0;
1386 : }
1387 2786 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ISOM_SUBTYPE);
1388 2786 : if (p) m_subtype_src = p->value.uint;
1389 :
1390 :
1391 : //get our subtype
1392 2786 : switch (codec_id) {
1393 82 : case GF_CODECID_MPEG_AUDIO:
1394 : case GF_CODECID_MPEG2_PART3:
1395 : m_subtype = GF_ISOM_SUBTYPE_MP3;
1396 : comp_name = "MP3";
1397 : //if source had a DSI, this was mpeg4 systems signaling, reuse that
1398 82 : if (dsi)
1399 : use_m4sys = GF_TRUE;
1400 : break;
1401 350 : case GF_CODECID_AAC_MPEG4:
1402 : case GF_CODECID_AAC_MPEG2_MP:
1403 : case GF_CODECID_AAC_MPEG2_LCP:
1404 : case GF_CODECID_AAC_MPEG2_SSRP:
1405 : m_subtype = GF_ISOM_SUBTYPE_MPEG4;
1406 : use_m4sys = GF_TRUE;
1407 : comp_name = "AAC";
1408 : use_gen_sample_entry = GF_FALSE;
1409 :
1410 350 : if (ctx->importer) {
1411 74 : const char *pid_args = gf_filter_pid_get_args(pid);
1412 74 : if (pid_args) {
1413 74 : Bool sbr_i = strstr(pid_args, "sbr=imp") ? GF_TRUE : GF_FALSE;
1414 74 : Bool sbr_x = strstr(pid_args, "sbr=exp") ? GF_TRUE : GF_FALSE;
1415 74 : Bool ps_i = strstr(pid_args, "ps=imp") ? GF_TRUE : GF_FALSE;
1416 74 : Bool ps_x = strstr(pid_args, "ps=exp") ? GF_TRUE : GF_FALSE;
1417 :
1418 74 : if (sbr_x) {
1419 3 : if (ps_i) imp_name = "AAC explicit SBR implict PS";
1420 2 : else if (ps_x) imp_name = "AAC explicit SBR+PS";
1421 : else imp_name = "AAC explicit SBR";
1422 71 : } else if (sbr_i) {
1423 3 : if (ps_i) imp_name = "AAC implicit SBR+PS";
1424 2 : else if (ps_x) imp_name = "AAC implicit SBR explicit PS";
1425 : else imp_name = "AAC implicit SBR";
1426 : } else {
1427 68 : if (ps_i) imp_name = "AAC implicit PS";
1428 67 : else if (ps_x) imp_name = "AAC explicit PS";
1429 : else imp_name = "AAC ";
1430 : }
1431 : }
1432 : }
1433 : break;
1434 : case GF_CODECID_USAC:
1435 : m_subtype = GF_ISOM_SUBTYPE_MPEG4;
1436 : use_m4sys = GF_TRUE;
1437 : comp_name = "xHE-AAC / USAC";
1438 : use_gen_sample_entry = GF_FALSE;
1439 : break;
1440 38 : case GF_CODECID_JPEG:
1441 : m_subtype = GF_ISOM_BOX_TYPE_JPEG;
1442 : comp_name = "JPEG";
1443 38 : break;
1444 21 : case GF_CODECID_PNG:
1445 : m_subtype = GF_ISOM_BOX_TYPE_PNG;
1446 : comp_name = "PNG";
1447 21 : break;
1448 7 : case GF_CODECID_J2K:
1449 : m_subtype = GF_ISOM_BOX_TYPE_MJP2;
1450 : comp_name = "JPEG2000";
1451 : use_mj2 = GF_TRUE;
1452 7 : break;
1453 :
1454 8 : case GF_CODECID_AMR:
1455 : m_subtype = GF_ISOM_SUBTYPE_3GP_AMR;
1456 : comp_name = "AMR";
1457 : use_3gpp_config = GF_TRUE;
1458 8 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_AMR_MODE_SET);
1459 8 : if (p && (p->value.uint!=tkw->amr_mode_set)) {
1460 3 : tkw->amr_mode_set = p->value.uint;
1461 : needs_sample_entry = 2;
1462 : }
1463 : break;
1464 4 : case GF_CODECID_AMR_WB:
1465 : m_subtype = GF_ISOM_SUBTYPE_3GP_AMR_WB;
1466 : comp_name = "AMR-WB";
1467 : use_3gpp_config = GF_TRUE;
1468 4 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_AMR_MODE_SET);
1469 4 : if (p && (p->value.uint!=tkw->amr_mode_set)) {
1470 2 : tkw->amr_mode_set = p->value.uint;
1471 : needs_sample_entry = 2;
1472 : }
1473 : break;
1474 1 : case GF_CODECID_EVRC:
1475 : m_subtype = GF_ISOM_SUBTYPE_3GP_EVRC;
1476 : comp_name = "EVRC";
1477 : use_3gpp_config = GF_TRUE;
1478 1 : break;
1479 0 : case GF_CODECID_SMV:
1480 : m_subtype = GF_ISOM_SUBTYPE_3GP_SMV;
1481 : comp_name = "SMV";
1482 : use_3gpp_config = GF_TRUE;
1483 0 : break;
1484 3 : case GF_CODECID_QCELP:
1485 : m_subtype = GF_ISOM_SUBTYPE_3GP_QCELP;
1486 : comp_name = "QCELP";
1487 : use_3gpp_config = GF_TRUE;
1488 3 : break;
1489 5 : case GF_CODECID_S263:
1490 : case GF_CODECID_H263:
1491 : m_subtype = GF_ISOM_SUBTYPE_3GP_H263;
1492 : comp_name = "H263";
1493 : use_3gpp_config = GF_TRUE;
1494 5 : break;
1495 9 : case GF_CODECID_AC3:
1496 : m_subtype = GF_ISOM_SUBTYPE_AC3;
1497 : comp_name = "AC-3";
1498 : use_ac3_entry = GF_TRUE;
1499 9 : break;
1500 2 : case GF_CODECID_EAC3:
1501 : m_subtype = GF_ISOM_SUBTYPE_AC3;
1502 : comp_name = "EAC-3";
1503 : use_ac3_entry = GF_TRUE;
1504 2 : break;
1505 1 : case GF_CODECID_MPHA:
1506 1 : if ((m_subtype_src!=GF_ISOM_SUBTYPE_MH3D_MHA1) && (m_subtype_src!=GF_ISOM_SUBTYPE_MH3D_MHA2))
1507 : m_subtype = GF_ISOM_SUBTYPE_MH3D_MHA1;
1508 : else
1509 : m_subtype = m_subtype_src;
1510 : comp_name = "MPEG-H Audio";
1511 : nb_chan = 0;
1512 : break;
1513 2 : case GF_CODECID_MHAS:
1514 2 : if ((m_subtype_src!=GF_ISOM_SUBTYPE_MH3D_MHM1) && (m_subtype_src!=GF_ISOM_SUBTYPE_MH3D_MHM2))
1515 : m_subtype = GF_ISOM_SUBTYPE_MH3D_MHM1;
1516 : else
1517 : m_subtype = m_subtype_src;
1518 : comp_name = "MPEG-H AudioMux";
1519 : nb_chan = 0;
1520 : break;
1521 2 : case GF_CODECID_FLAC:
1522 : m_subtype = GF_ISOM_SUBTYPE_FLAC;
1523 : comp_name = "FLAC";
1524 : use_flac_entry = GF_TRUE;
1525 2 : break;
1526 2 : case GF_CODECID_OPUS:
1527 : m_subtype = GF_ISOM_SUBTYPE_OPUS;
1528 : comp_name = "Opus";
1529 : use_opus = GF_TRUE;
1530 2 : break;
1531 86 : case GF_CODECID_MPEG4_PART2:
1532 : m_subtype = GF_ISOM_SUBTYPE_MPEG4;
1533 : use_m4sys = GF_TRUE;
1534 : comp_name = "MPEG-4 Visual Part 2";
1535 : use_gen_sample_entry = GF_FALSE;
1536 86 : break;
1537 519 : case GF_CODECID_AVC:
1538 : case GF_CODECID_SVC:
1539 519 : m_subtype = ((ctx->xps_inband==1) || (ctx->xps_inband==2)) ? GF_ISOM_SUBTYPE_AVC3_H264 : GF_ISOM_SUBTYPE_AVC_H264;
1540 : use_avc = GF_TRUE;
1541 519 : comp_name = (codec_id == GF_CODECID_SVC) ? "MPEG-4 SVC" : "MPEG-4 AVC";
1542 : use_gen_sample_entry = GF_FALSE;
1543 519 : if (ctx->xps_inband) {
1544 : use_m4sys = GF_FALSE;
1545 23 : if (ctx->xps_inband==1) skip_dsi = GF_TRUE;
1546 : }
1547 : break;
1548 280 : case GF_CODECID_HEVC:
1549 : case GF_CODECID_LHVC:
1550 280 : m_subtype = ((ctx->xps_inband==1) || (ctx->xps_inband==2)) ? GF_ISOM_SUBTYPE_HEV1 : GF_ISOM_SUBTYPE_HVC1;
1551 : use_hevc = GF_TRUE;
1552 280 : comp_name = (codec_id == GF_CODECID_LHVC) ? "L-HEVC" : "HEVC";
1553 : use_gen_sample_entry = GF_FALSE;
1554 280 : if (ctx->xps_inband) {
1555 : use_m4sys = GF_FALSE;
1556 16 : if (ctx->xps_inband==1) skip_dsi = GF_TRUE;
1557 : }
1558 280 : if (codec_id==GF_CODECID_HEVC_TILES) {
1559 : m_subtype = GF_ISOM_SUBTYPE_HVT1;
1560 : skip_dsi = GF_TRUE;
1561 : }
1562 : break;
1563 78 : case GF_CODECID_HEVC_TILES:
1564 : m_subtype = GF_ISOM_SUBTYPE_HVT1;
1565 : skip_dsi = GF_TRUE;
1566 : use_hvt1 = GF_TRUE;
1567 : use_m4sys = GF_FALSE;
1568 : comp_name = "HEVC Tiles";
1569 : use_gen_sample_entry = GF_FALSE;
1570 78 : break;
1571 0 : case GF_CODECID_VVC:
1572 0 : m_subtype = ((ctx->xps_inband==1) || (ctx->xps_inband==2)) ? GF_ISOM_SUBTYPE_VVI1 : GF_ISOM_SUBTYPE_VVC1;
1573 : use_vvc = GF_TRUE;
1574 : comp_name = "HEVC";
1575 : use_gen_sample_entry = GF_FALSE;
1576 0 : if (ctx->xps_inband==1) skip_dsi = GF_TRUE;
1577 : break;
1578 45 : case GF_CODECID_MPEG1:
1579 : case GF_CODECID_MPEG2_422:
1580 : case GF_CODECID_MPEG2_SNR:
1581 : case GF_CODECID_MPEG2_HIGH:
1582 : case GF_CODECID_MPEG2_MAIN:
1583 : case GF_CODECID_MPEG2_SIMPLE:
1584 : case GF_CODECID_MPEG2_SPATIAL:
1585 : m_subtype = GF_ISOM_SUBTYPE_MPEG4;
1586 : use_m4sys = GF_TRUE;
1587 : comp_name = "MPEG-2 Video";
1588 : use_gen_sample_entry = GF_FALSE;
1589 45 : break;
1590 0 : case 0:
1591 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] muxing codecID %d not yet implemented - patch welcome\n", codec_id));
1592 : return GF_NOT_SUPPORTED;
1593 :
1594 45 : case GF_ISOM_SUBTYPE_TX3G:
1595 : m_subtype = GF_ISOM_SUBTYPE_TX3G;
1596 : use_tx3g = GF_TRUE;
1597 : comp_name = "Timed Text";
1598 : is_text_subs = GF_TRUE;
1599 45 : break;
1600 42 : case GF_ISOM_SUBTYPE_WVTT:
1601 : m_subtype = GF_ISOM_SUBTYPE_WVTT;
1602 : use_webvtt = GF_TRUE;
1603 : comp_name = "WebVTT";
1604 : is_text_subs = GF_TRUE;
1605 42 : break;
1606 1 : case GF_CODECID_SUBPIC:
1607 : use_m4sys = GF_TRUE;
1608 : override_stype = GF_STREAM_ND_SUBPIC;
1609 : comp_name = "VobSub";
1610 1 : break;
1611 1 : case GF_CODECID_TEXT_MPEG4:
1612 : use_m4sys = GF_TRUE;
1613 1 : gf_isom_set_media_type(ctx->file, tkw->track_num, GF_ISOM_MEDIA_SCENE);
1614 : comp_name = "MPEG4 Streaming Text";
1615 1 : break;
1616 226 : case GF_CODECID_AV1:
1617 : use_gen_sample_entry = GF_FALSE;
1618 : m_subtype = GF_ISOM_SUBTYPE_AV01;
1619 : use_av1 = GF_TRUE;
1620 : comp_name = "AOM AV1 Video";
1621 226 : break;
1622 :
1623 1 : case GF_CODECID_VP8:
1624 : use_gen_sample_entry = GF_FALSE;
1625 : m_subtype = GF_ISOM_SUBTYPE_VP08;
1626 : use_vpX = GF_TRUE;
1627 : comp_name = "VP8 Video";
1628 1 : break;
1629 132 : case GF_CODECID_VP9:
1630 : use_gen_sample_entry = GF_FALSE;
1631 : m_subtype = GF_ISOM_SUBTYPE_VP09;
1632 : use_vpX = GF_TRUE;
1633 : comp_name = "VP9 Video";
1634 132 : break;
1635 0 : case GF_CODECID_VP10:
1636 : use_gen_sample_entry = GF_FALSE;
1637 : m_subtype = GF_ISOM_SUBTYPE_VP10;
1638 : use_vpX = GF_TRUE;
1639 : comp_name = "VP10 Video";
1640 0 : break;
1641 :
1642 10 : case GF_CODECID_VORBIS:
1643 : case GF_CODECID_THEORA:
1644 : use_m4sys = GF_TRUE;
1645 10 : break;
1646 :
1647 693 : case GF_CODECID_TRUEHD:
1648 : m_subtype = GF_ISOM_SUBTYPE_MLPA;
1649 : comp_name = "Dolby TrueHD";
1650 693 : break;
1651 :
1652 :
1653 27 : case GF_CODECID_BIFS:
1654 : /* == GF_CODECID_OD_V1:*/
1655 : case GF_CODECID_BIFS_V2:
1656 : /* == GF_CODECID_OD_V2:*/
1657 : case GF_CODECID_BIFS_EXTENDED:
1658 : case GF_CODECID_LASER:
1659 : use_m4sys = GF_TRUE;
1660 27 : break;
1661 0 : case GF_CODECID_V210:
1662 : m_subtype = GF_QT_SUBTYPE_YUV422_10;
1663 : comp_name = "v210 YUV 422 10 bit";
1664 : use_gen_sample_entry = GF_TRUE;
1665 : unknown_generic = GF_FALSE;
1666 0 : break;
1667 :
1668 6 : case GF_CODECID_RAW:
1669 : m_subtype = codec_id;
1670 : unknown_generic = GF_TRUE;
1671 : use_gen_sample_entry = GF_TRUE;
1672 : use_m4sys = GF_FALSE;
1673 6 : tkw->skip_bitrate_update = GF_TRUE;
1674 6 : if (tkw->stream_type == GF_STREAM_AUDIO) {
1675 : u32 req_non_planar_type = 0;
1676 5 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_AUDIO_FORMAT);
1677 5 : if (!p) break;
1678 : comp_name = "RawAudio";
1679 : unknown_generic = GF_FALSE;
1680 : //m_subtype used for QTFF-style raw media, m_subtype_alt_raw for ISOBMFF raw audio
1681 5 : switch (p->value.uint) {
1682 0 : case GF_AUDIO_FMT_U8P:
1683 : req_non_planar_type = GF_AUDIO_FMT_U8;
1684 : case GF_AUDIO_FMT_U8:
1685 : m_subtype = GF_QT_SUBTYPE_RAW;
1686 : break;
1687 0 : case GF_AUDIO_FMT_S16P:
1688 : req_non_planar_type = GF_AUDIO_FMT_S16;
1689 : case GF_AUDIO_FMT_S16:
1690 : m_subtype = GF_QT_SUBTYPE_SOWT;
1691 : m_subtype_alt_raw = GF_ISOM_SUBTYPE_IPCM;
1692 : break;
1693 0 : case GF_AUDIO_FMT_S24P:
1694 : req_non_planar_type = GF_AUDIO_FMT_S24;
1695 : case GF_AUDIO_FMT_S24:
1696 : m_subtype = GF_QT_SUBTYPE_IN24;
1697 : m_subtype_alt_raw = GF_ISOM_SUBTYPE_IPCM;
1698 : break;
1699 0 : case GF_AUDIO_FMT_S32P:
1700 : req_non_planar_type = GF_AUDIO_FMT_S32P;
1701 : case GF_AUDIO_FMT_S32:
1702 : m_subtype = GF_QT_SUBTYPE_IN32;
1703 : m_subtype_alt_raw = GF_ISOM_SUBTYPE_IPCM;
1704 : break;
1705 0 : case GF_AUDIO_FMT_FLTP:
1706 : req_non_planar_type = GF_AUDIO_FMT_FLTP;
1707 : case GF_AUDIO_FMT_FLT:
1708 : m_subtype = GF_QT_SUBTYPE_FL32;
1709 : m_subtype_alt_raw = GF_ISOM_SUBTYPE_FPCM;
1710 : break;
1711 0 : case GF_AUDIO_FMT_DBLP:
1712 : req_non_planar_type = GF_AUDIO_FMT_DBL;
1713 : case GF_AUDIO_FMT_DBL:
1714 : m_subtype = GF_QT_SUBTYPE_FL64;
1715 : m_subtype_alt_raw = GF_ISOM_SUBTYPE_FPCM;
1716 : break;
1717 : default:
1718 : unknown_generic = GF_TRUE;
1719 : m_subtype = p->value.uint;
1720 : break;
1721 : }
1722 5 : if (req_non_planar_type) {
1723 0 : if (is_true_pid)
1724 0 : gf_filter_pid_negociate_property(pid, GF_PROP_PID_AUDIO_FORMAT, &PROP_UINT(GF_AUDIO_FMT_S16));
1725 : else {
1726 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] raw audio format planar in DASH multi-stsd mode is not supported, try assigning a resampler before the dasher\n"));
1727 : return GF_NOT_SUPPORTED;
1728 : }
1729 : }
1730 5 : raw_bitdepth = gf_audio_fmt_bit_depth(p->value.uint);
1731 : tkw->raw_audio_bytes_per_sample = raw_bitdepth;
1732 5 : tkw->raw_audio_bytes_per_sample *= nb_chan;
1733 5 : tkw->raw_audio_bytes_per_sample /= 8;
1734 5 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_SAMPLE_RATE);
1735 5 : tkw->raw_samplerate = p ? p->value.uint : 0;
1736 : //force timescale to be samplerate, except if explicit overwrite
1737 5 : if (ctx->mediats==0)
1738 5 : tkw->tk_timescale = tkw->raw_samplerate;
1739 : }
1740 1 : else if (tkw->stream_type == GF_STREAM_VISUAL) {
1741 1 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_PIXFMT);
1742 1 : if (!p) break;
1743 : comp_name = "RawVideo";
1744 : unknown_generic = GF_FALSE;
1745 1 : tkw->skip_bitrate_update = GF_TRUE;
1746 :
1747 1 : m_subtype = gf_pixel_fmt_to_qt_type(p->value.uint);
1748 1 : if (m_subtype) {
1749 1 : if (gf_pixel_fmt_is_yuv(p->value.uint))
1750 : force_colr = GF_TRUE;
1751 : } else {
1752 : unknown_generic = GF_TRUE;
1753 0 : m_subtype = p->value.uint;
1754 : }
1755 : }
1756 : break;
1757 :
1758 55 : default:
1759 : m_subtype = codec_id;
1760 : unknown_generic = GF_TRUE;
1761 : use_gen_sample_entry = GF_TRUE;
1762 : use_m4sys = GF_FALSE;
1763 55 : if (is_prores)
1764 : unknown_generic = GF_FALSE;
1765 :
1766 55 : p = gf_filter_pid_get_property_str(pid, "meta:mime");
1767 55 : if (p) meta_mime = p->value.string;
1768 55 : p = gf_filter_pid_get_property_str(pid, "meta:encoding");
1769 55 : if (p) meta_encoding = p->value.string;
1770 55 : p = gf_filter_pid_get_property_str(pid, "meta:content_encoding");
1771 55 : if (p) meta_content_encoding = p->value.string;
1772 55 : p = gf_filter_pid_get_property_str(pid, "meta:xmlns");
1773 55 : if (p) meta_xmlns = p->value.string;
1774 55 : p = gf_filter_pid_get_property_str(pid, "meta:schemaloc");
1775 55 : if (p) meta_schemaloc = p->value.string;
1776 55 : p = gf_filter_pid_get_property_str(pid, "meta:aux_mimes");
1777 55 : if (p) meta_auxmimes = p->value.string;
1778 : break;
1779 : }
1780 2786 : if (!comp_name) comp_name = gf_codecid_name(codec_id);
1781 2786 : if (!comp_name) comp_name = gf_4cc_to_str(m_subtype);
1782 :
1783 2786 : if (dsi)
1784 2499 : meta_config = dsi->value.data.ptr;
1785 :
1786 2786 : if (is_text_subs && !width && !height) {
1787 66 : mp4_mux_get_video_size(ctx, &width, &height);
1788 : }
1789 :
1790 2786 : if (!ctx->init_movie_done && !tkw->nb_samples && (ctx->mediats<0) && (tkw->tk_timescale==1000)) {
1791 0 : if (sr) {
1792 0 : tkw->tk_timescale = sr;
1793 0 : gf_isom_set_media_timescale(ctx->file, tkw->track_num, sr, 0, 1);
1794 : }
1795 0 : else if (width && fps.den) {
1796 0 : tkw->tk_timescale = fps.den;
1797 0 : gf_isom_set_media_timescale(ctx->file, tkw->track_num, fps.den, 0, 1);
1798 : }
1799 : }
1800 4332 : if (!needs_sample_entry || tkw->is_item) {
1801 : goto sample_entry_done;
1802 : }
1803 :
1804 : //we are fragmented, init movie done, we cannot update the sample description
1805 1498 : if (ctx->init_movie_done) {
1806 8 : if (needs_sample_entry==1) {
1807 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Cannot create a new sample description entry (codec change) for finalized movie in fragmented mode\n"));
1808 : return GF_NOT_SUPPORTED;
1809 : }
1810 : force_mix_xps = GF_TRUE;
1811 1490 : } else if (ctx->store < MP4MX_MODE_FRAG) {
1812 1099 : if ((needs_sample_entry==2) && (ctx->xps_inband==2)) {
1813 : force_mix_xps = GF_TRUE;
1814 : }
1815 1092 : else if ((needs_sample_entry==2) && ((ctx->xps_inband==1)||(ctx->xps_inband==3)) ) {
1816 : needs_sample_entry = 0;
1817 : make_inband_headers = GF_TRUE;
1818 : }
1819 : }
1820 :
1821 1498 : if (force_mix_xps) {
1822 :
1823 : //for AVC and HEVC, move to inband params if config changed
1824 15 : if (use_avc && dsi) {
1825 9 : if (tkw->avcc) gf_odf_avc_cfg_del(tkw->avcc);
1826 :
1827 9 : tkw->avcc = gf_odf_avc_cfg_read(dsi->value.data.ptr, dsi->value.data.size);
1828 :
1829 9 : if (enh_dsi) {
1830 0 : if (tkw->svcc) gf_odf_avc_cfg_del(tkw->svcc);
1831 0 : tkw->svcc = gf_odf_avc_cfg_read(enh_dsi->value.data.ptr, enh_dsi->value.data.size);
1832 : }
1833 9 : if (!ctx->xps_inband) {
1834 0 : if (ctx->init_movie_done) {
1835 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] AVC config update after movie has been finalized, moving all SPS/PPS inband (file might not be compliant)\n"));
1836 : }
1837 0 : ctx->xps_inband = 2;
1838 : }
1839 9 : mp4_mux_make_inband_header(ctx, tkw, GF_FALSE);
1840 9 : if (ctx->pps_inband)
1841 0 : mp4_mux_make_inband_header(ctx, tkw, GF_TRUE);
1842 : return GF_OK;
1843 : }
1844 6 : else if (use_hevc && dsi) {
1845 6 : if (tkw->hvcc) gf_odf_hevc_cfg_del(tkw->hvcc);
1846 6 : tkw->hvcc = gf_odf_hevc_cfg_read(dsi->value.data.ptr, dsi->value.data.size, (codec_id == GF_CODECID_LHVC) ? GF_TRUE : GF_FALSE);
1847 :
1848 6 : if (enh_dsi) {
1849 0 : if (tkw->lvcc) gf_odf_hevc_cfg_del(tkw->lvcc);
1850 0 : tkw->lvcc = gf_odf_hevc_cfg_read(enh_dsi->value.data.ptr, enh_dsi->value.data.size, GF_TRUE);
1851 : }
1852 6 : if (!ctx->xps_inband) {
1853 0 : if (ctx->init_movie_done) {
1854 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] HEVC config update after movie has been finalized, moving all SPS/PPS inband (file might not be compliant)\n"));
1855 : }
1856 0 : ctx->xps_inband = 2;
1857 : }
1858 6 : mp4_mux_make_inband_header(ctx, tkw, GF_FALSE);
1859 6 : if (ctx->pps_inband)
1860 0 : mp4_mux_make_inband_header(ctx, tkw, GF_TRUE);
1861 : return GF_OK;
1862 : }
1863 0 : else if (use_vvc && dsi) {
1864 0 : if (tkw->vvcc) gf_odf_vvc_cfg_del(tkw->vvcc);
1865 0 : tkw->vvcc = gf_odf_vvc_cfg_read(dsi->value.data.ptr, dsi->value.data.size);
1866 :
1867 0 : if (!ctx->xps_inband) {
1868 0 : if (ctx->init_movie_done) {
1869 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] VVC config update after movie has been finalized, moving all SPS/PPS inband (file might not be compliant)\n"));
1870 : }
1871 0 : ctx->xps_inband = 2;
1872 : }
1873 0 : mp4_mux_make_inband_header(ctx, tkw, GF_FALSE);
1874 0 : if (ctx->pps_inband)
1875 0 : mp4_mux_make_inband_header(ctx, tkw, GF_TRUE);
1876 : return GF_OK;
1877 : }
1878 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Cannot create a new sample description entry (config changed) for finalized movie in fragmented mode\n"));
1879 : return GF_NOT_SUPPORTED;
1880 : }
1881 :
1882 :
1883 : //little optim here: if no samples were added on the stream description remove it
1884 1483 : if (!tkw->samples_in_stsd && tkw->stsd_idx) {
1885 14 : gf_isom_remove_stream_description(ctx->file, tkw->track_num, tkw->stsd_idx);
1886 : }
1887 :
1888 1483 : if (!use_dref) src_url = NULL;
1889 :
1890 1483 : if (use_m4sys && !gf_codecid_oti(codec_id)) {
1891 : use_m4sys = GF_FALSE;
1892 : }
1893 : //nope, create sample entry
1894 1482 : if (use_m4sys) {
1895 401 : GF_ESD *esd = gf_odf_desc_esd_new(2);
1896 401 : esd->decoderConfig->streamType = override_stype ? override_stype : tkw->stream_type;
1897 401 : esd->decoderConfig->objectTypeIndication = gf_codecid_oti(codec_id);
1898 401 : if (!esd->decoderConfig->objectTypeIndication) {
1899 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Codec %s does not have an official MPEG-4 systems mapping, cannot mux\n", gf_codecid_name(codec_id) ));
1900 : return GF_NOT_SUPPORTED;
1901 :
1902 : }
1903 401 : esd->slConfig->timestampResolution = tkw->tk_timescale;
1904 401 : if (dsi && !skip_dsi) {
1905 355 : esd->decoderConfig->decoderSpecificInfo->data = dsi->value.data.ptr;
1906 355 : esd->decoderConfig->decoderSpecificInfo->dataLength = dsi->value.data.size;
1907 : }
1908 :
1909 401 : e = gf_isom_new_mpeg4_description(ctx->file, tkw->track_num, esd, (char *)src_url, NULL, &tkw->stsd_idx);
1910 401 : if (dsi && !skip_dsi) {
1911 355 : esd->decoderConfig->decoderSpecificInfo->data = NULL;
1912 355 : esd->decoderConfig->decoderSpecificInfo->dataLength = 0;
1913 : }
1914 401 : gf_odf_desc_del((GF_Descriptor *) esd);
1915 :
1916 401 : if (e) {
1917 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new MPEG-4 Systems sample description for stream type %d OTI %d: %s\n", tkw->stream_type, codec_id, gf_error_to_string(e) ));
1918 : return e;
1919 : }
1920 :
1921 401 : tkw->use_dref = src_url ? GF_TRUE : GF_FALSE;
1922 :
1923 401 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_IN_IOD);
1924 401 : if (p && p->value.boolean)
1925 27 : gf_isom_add_track_to_root_od(ctx->file, tkw->track_num);
1926 :
1927 :
1928 : #ifndef GPAC_DISABLE_AV_PARSERS
1929 401 : if (dsi && (tkw->stream_type==GF_STREAM_AUDIO)) {
1930 : GF_M4ADecSpecInfo acfg;
1931 216 : gf_m4a_get_config(dsi->value.data.ptr, dsi->value.data.size, &acfg);
1932 216 : audio_pli = acfg.audioPL;
1933 : }
1934 : //patch to align old arch (IOD not written in dash) with new
1935 401 : if (audio_pli && !ctx->dash_mode)
1936 278 : gf_isom_set_pl_indication(ctx->file, GF_ISOM_PL_AUDIO, audio_pli);
1937 : #endif
1938 :
1939 1082 : } else if (use_avc) {
1940 397 : if (tkw->avcc) gf_odf_avc_cfg_del(tkw->avcc);
1941 :
1942 : //not yet known
1943 397 : if (!dsi && !enh_dsi) return GF_OK;
1944 :
1945 397 : if (!dsi) {
1946 : dsi = enh_dsi;
1947 : enh_dsi = NULL;
1948 : }
1949 397 : tkw->avcc = gf_odf_avc_cfg_read(dsi->value.data.ptr, dsi->value.data.size);
1950 :
1951 397 : if (needs_sample_entry) {
1952 :
1953 393 : if (tkw->codecid == GF_CODECID_SVC) {
1954 2 : e = gf_isom_svc_config_new(ctx->file, tkw->track_num, tkw->avcc, NULL, NULL, &tkw->stsd_idx);
1955 391 : } else if (tkw->codecid == GF_CODECID_MVC) {
1956 0 : e = gf_isom_mvc_config_new(ctx->file, tkw->track_num, tkw->avcc, NULL, NULL, &tkw->stsd_idx);
1957 : } else {
1958 391 : e = gf_isom_avc_config_new(ctx->file, tkw->track_num, tkw->avcc, NULL, NULL, &tkw->stsd_idx);
1959 : }
1960 :
1961 393 : if (!e && enh_dsi) {
1962 6 : if (tkw->svcc) gf_odf_avc_cfg_del(tkw->svcc);
1963 6 : tkw->svcc = gf_odf_avc_cfg_read(enh_dsi->value.data.ptr, enh_dsi->value.data.size);
1964 6 : if (tkw->svcc) {
1965 6 : if ((tkw->svcc->AVCProfileIndication==118) || (tkw->svcc->AVCProfileIndication==128)) {
1966 0 : e = gf_isom_mvc_config_update(ctx->file, tkw->track_num, tkw->stsd_idx, tkw->svcc, GF_TRUE);
1967 : } else {
1968 6 : e = gf_isom_svc_config_update(ctx->file, tkw->track_num, tkw->stsd_idx, tkw->svcc, GF_TRUE);
1969 : }
1970 6 : if (e) {
1971 0 : gf_odf_avc_cfg_del(tkw->svcc);
1972 0 : tkw->svcc = NULL;
1973 : }
1974 :
1975 6 : if (ctx->xps_inband) {
1976 0 : gf_isom_avc_set_inband_config(ctx->file, tkw->track_num, tkw->stsd_idx, (ctx->xps_inband==2) ? GF_TRUE : GF_FALSE);
1977 : }
1978 : }
1979 : }
1980 393 : if (e) {
1981 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new AVC sample description: %s\n", gf_error_to_string(e) ));
1982 : return e;
1983 : }
1984 : }
1985 :
1986 397 : if (ctx->xps_inband) {
1987 : //this will cleanup all PS in avcC / svcC
1988 14 : gf_isom_avc_set_inband_config(ctx->file, tkw->track_num, tkw->stsd_idx, (ctx->xps_inband==2) ? GF_TRUE : GF_FALSE);
1989 14 : if (ctx->xps_inband==2) make_inband_headers = GF_TRUE;
1990 : } else {
1991 383 : gf_odf_avc_cfg_del(tkw->avcc);
1992 383 : tkw->avcc = NULL;
1993 : }
1994 : //patch to align old arch with filters
1995 397 : if (!ctx->dash_mode && !ctx->make_qt && !gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_ISOM_TREX_TEMPLATE) )
1996 246 : gf_isom_set_pl_indication(ctx->file, GF_ISOM_PL_VISUAL, 0x7F);
1997 :
1998 397 : if (!tkw->has_brands)
1999 210 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_AVC1, GF_TRUE);
2000 :
2001 397 : tkw->is_nalu = NALU_AVC;
2002 :
2003 397 : tkw->use_dref = GF_FALSE;
2004 :
2005 685 : } else if (use_hvt1) {
2006 78 : if (tkw->hvcc) gf_odf_hevc_cfg_del(tkw->hvcc);
2007 78 : tkw->hvcc = gf_odf_hevc_cfg_new();
2008 78 : e = gf_isom_hevc_config_new(ctx->file, tkw->track_num, tkw->hvcc, NULL, NULL, &tkw->stsd_idx);
2009 78 : if (!e) {
2010 78 : gf_isom_hevc_set_tile_config(ctx->file, tkw->track_num, tkw->stsd_idx, NULL, GF_FALSE);
2011 : }
2012 78 : gf_odf_hevc_cfg_del(tkw->hvcc);
2013 78 : tkw->hvcc = NULL;
2014 78 : tkw->is_nalu = NALU_HEVC;
2015 78 : tkw->use_dref = GF_FALSE;
2016 78 : if (!tkw->has_brands)
2017 33 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_HVTI, GF_TRUE);
2018 607 : } else if (use_hevc) {
2019 154 : if (tkw->hvcc) gf_odf_hevc_cfg_del(tkw->hvcc);
2020 :
2021 154 : if (!dsi && !enh_dsi) {
2022 : //not yet known
2023 : return GF_OK;
2024 : }
2025 154 : if (dsi) {
2026 154 : tkw->hvcc = gf_odf_hevc_cfg_read(dsi->value.data.ptr, dsi->value.data.size, (codec_id == GF_CODECID_LHVC) ? GF_TRUE : GF_FALSE);
2027 : } else {
2028 0 : tkw->hvcc = gf_odf_hevc_cfg_new();
2029 : }
2030 154 : tkw->is_nalu = NALU_HEVC;
2031 :
2032 154 : if (needs_sample_entry) {
2033 151 : e = gf_isom_hevc_config_new(ctx->file, tkw->track_num, tkw->hvcc, NULL, NULL, &tkw->stsd_idx);
2034 :
2035 151 : if (!tkw->has_brands) {
2036 95 : gf_isom_set_brand_info(ctx->file, GF_ISOM_BRAND_ISO4, 1);
2037 95 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISOM, GF_FALSE);
2038 : }
2039 : //patch for old arch
2040 56 : else if (ctx->dash_mode) {
2041 : Bool force_brand=GF_FALSE;
2042 22 : if (((ctx->major_brand_set>>24)=='i') && (((ctx->major_brand_set>>16)&0xFF)=='s') && (((ctx->major_brand_set>>8)&0xFF)=='o')) {
2043 22 : if ( (ctx->major_brand_set&0xFF) <'6') force_brand=GF_TRUE;
2044 : }
2045 :
2046 1 : if (!force_brand && ctx->major_brand_set) {
2047 1 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISO6, GF_TRUE);
2048 : } else {
2049 21 : gf_isom_set_brand_info(ctx->file, GF_ISOM_BRAND_ISO6, 1);
2050 21 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISOM, GF_FALSE);
2051 : }
2052 : }
2053 :
2054 151 : if (!e && enh_dsi) {
2055 8 : if (tkw->lvcc) gf_odf_hevc_cfg_del(tkw->lvcc);
2056 8 : tkw->lvcc = gf_odf_hevc_cfg_read(enh_dsi->value.data.ptr, enh_dsi->value.data.size, GF_TRUE);
2057 8 : if (tkw->lvcc) {
2058 8 : e = gf_isom_lhvc_config_update(ctx->file, tkw->track_num, tkw->stsd_idx, tkw->lvcc, dsi ? GF_ISOM_LEHVC_WITH_BASE_BACKWARD : GF_ISOM_LEHVC_ONLY);
2059 8 : if (e) {
2060 0 : gf_odf_hevc_cfg_del(tkw->lvcc);
2061 0 : tkw->lvcc = NULL;
2062 : }
2063 :
2064 8 : if (!dsi && ctx->xps_inband) {
2065 0 : gf_isom_hevc_set_inband_config(ctx->file, tkw->track_num, tkw->stsd_idx, (ctx->xps_inband==2) ? GF_TRUE : GF_FALSE);
2066 : }
2067 : }
2068 143 : } else if (codec_id == GF_CODECID_LHVC) {
2069 0 : gf_isom_lhvc_config_update(ctx->file, tkw->track_num, tkw->stsd_idx, tkw->hvcc, GF_ISOM_LEHVC_ONLY);
2070 143 : } else if (is_tile_base) {
2071 5 : gf_isom_lhvc_config_update(ctx->file, tkw->track_num, tkw->stsd_idx, tkw->hvcc, GF_ISOM_HEVC_TILE_BASE);
2072 : }
2073 151 : if (e) {
2074 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new HEVC sample description: %s\n", gf_error_to_string(e) ));
2075 : return e;
2076 : }
2077 : }
2078 :
2079 154 : if (dsi && ctx->xps_inband) {
2080 : //this will cleanup all PS in avcC / svcC
2081 10 : gf_isom_hevc_set_inband_config(ctx->file, tkw->track_num, tkw->stsd_idx, (ctx->xps_inband==2) ? GF_TRUE : GF_FALSE);
2082 : } else {
2083 144 : gf_odf_hevc_cfg_del(tkw->hvcc);
2084 144 : tkw->hvcc = NULL;
2085 : }
2086 :
2087 154 : tkw->use_dref = GF_FALSE;
2088 453 : } else if (use_vvc) {
2089 0 : if (tkw->vvcc) gf_odf_vvc_cfg_del(tkw->vvcc);
2090 :
2091 0 : if (!dsi) {
2092 : //not yet known
2093 : return GF_OK;
2094 : }
2095 0 : tkw->vvcc = gf_odf_vvc_cfg_read(dsi->value.data.ptr, dsi->value.data.size);
2096 :
2097 0 : tkw->is_nalu = NALU_VVC;
2098 :
2099 0 : if (needs_sample_entry) {
2100 0 : e = gf_isom_vvc_config_new(ctx->file, tkw->track_num, tkw->vvcc, NULL, NULL, &tkw->stsd_idx);
2101 :
2102 0 : if (!tkw->has_brands) {
2103 0 : gf_isom_set_brand_info(ctx->file, GF_ISOM_BRAND_ISO4, 1);
2104 0 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISOM, GF_FALSE);
2105 : }
2106 : //patch for old arch
2107 0 : else if (ctx->dash_mode) {
2108 : Bool force_brand=GF_FALSE;
2109 0 : if (((ctx->major_brand_set>>24)=='i') && (((ctx->major_brand_set>>16)&0xFF)=='s') && (((ctx->major_brand_set>>8)&0xFF)=='o')) {
2110 0 : if ( (ctx->major_brand_set&0xFF) <'6') force_brand=GF_TRUE;
2111 : }
2112 :
2113 0 : if (!force_brand && ctx->major_brand_set) {
2114 0 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISO6, GF_TRUE);
2115 : } else {
2116 0 : gf_isom_set_brand_info(ctx->file, GF_ISOM_BRAND_ISO6, 1);
2117 0 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISOM, GF_FALSE);
2118 : }
2119 : }
2120 :
2121 0 : if (e) {
2122 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new HEVC sample description: %s\n", gf_error_to_string(e) ));
2123 : return e;
2124 : }
2125 : }
2126 :
2127 0 : if (ctx->xps_inband) {
2128 : //this will cleanup all PS in vvcC
2129 0 : gf_isom_vvc_set_inband_config(ctx->file, tkw->track_num, tkw->stsd_idx, (ctx->xps_inband==2) ? GF_TRUE : GF_FALSE);
2130 : } else {
2131 0 : gf_odf_vvc_cfg_del(tkw->vvcc);
2132 0 : tkw->vvcc = NULL;
2133 : }
2134 :
2135 0 : tkw->use_dref = GF_FALSE;
2136 453 : } else if (use_av1) {
2137 : GF_AV1Config *av1c;
2138 :
2139 154 : if (!dsi) {
2140 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] No decoder specific info found for AV1\n"));
2141 : return GF_NON_COMPLIANT_BITSTREAM;
2142 : }
2143 154 : av1c = gf_odf_av1_cfg_read(dsi->value.data.ptr, dsi->value.data.size);
2144 154 : if (!av1c) {
2145 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to parser AV1 decoder specific info\n"));
2146 : return GF_NON_COMPLIANT_BITSTREAM;
2147 : }
2148 :
2149 154 : e = gf_isom_av1_config_new(ctx->file, tkw->track_num, av1c, (char *) src_url, NULL, &tkw->stsd_idx);
2150 154 : if (e) {
2151 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new AV1 sample description: %s\n", gf_error_to_string(e) ));
2152 : return e;
2153 : }
2154 154 : tkw->is_av1 = GF_TRUE;
2155 :
2156 154 : if (!tkw->has_brands) {
2157 64 : gf_isom_set_brand_info(ctx->file, GF_ISOM_BRAND_ISO4, 1);
2158 64 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISOM, GF_FALSE);
2159 64 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_AV01, GF_TRUE);
2160 : }
2161 :
2162 154 : gf_odf_av1_cfg_del(av1c);
2163 299 : } else if (use_vpX) {
2164 : GF_VPConfig *vpc;
2165 :
2166 28 : if (!dsi) {
2167 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] No decoder specific info found for %s\n", gf_4cc_to_str(codec_id) ));
2168 : return GF_NON_COMPLIANT_BITSTREAM;
2169 : }
2170 28 : vpc = gf_odf_vp_cfg_read(dsi->value.data.ptr, dsi->value.data.size);
2171 28 : if (!vpc) {
2172 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to parser %s decoder specific info\n", gf_4cc_to_str(codec_id)));
2173 : return GF_NON_COMPLIANT_BITSTREAM;
2174 : }
2175 :
2176 28 : e = gf_isom_vp_config_new(ctx->file, tkw->track_num, vpc, (char *) src_url, NULL, &tkw->stsd_idx, m_subtype);
2177 28 : if (e) {
2178 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new %s sample description: %s\n", gf_4cc_to_str(codec_id), gf_error_to_string(e) ));
2179 : return e;
2180 : }
2181 28 : tkw->is_vpx = GF_TRUE;
2182 28 : gf_odf_vp_cfg_del(vpc);
2183 271 : } else if (use_3gpp_config) {
2184 : GF_3GPConfig gpp_cfg;
2185 : memset(&gpp_cfg, 0, sizeof(GF_3GPConfig));
2186 21 : gpp_cfg.type = m_subtype;
2187 21 : gpp_cfg.vendor = GF_VENDOR_GPAC;
2188 :
2189 21 : if (use_dref) {
2190 0 : gpp_cfg.frames_per_sample = 1;
2191 : } else {
2192 21 : gpp_cfg.frames_per_sample = ctx->pack3gp;
2193 21 : if (!gpp_cfg.frames_per_sample) gpp_cfg.frames_per_sample = 1;
2194 21 : else if (gpp_cfg.frames_per_sample >15) gpp_cfg.frames_per_sample = 15;
2195 : }
2196 21 : gpp_cfg.AMR_mode_set = tkw->amr_mode_set;
2197 21 : if (tkw->stream_type==GF_STREAM_VISUAL) {
2198 : /*FIXME - we need more in-depth parsing of the bitstream to detect P3@L10 (streaming wireless)*/
2199 5 : gpp_cfg.H263_profile = 0;
2200 5 : gpp_cfg.H263_level = 10;
2201 5 : gpp_cfg.frames_per_sample = 0;
2202 : }
2203 21 : tkw->nb_frames_per_sample = gpp_cfg.frames_per_sample;
2204 :
2205 21 : e = gf_isom_3gp_config_new(ctx->file, tkw->track_num, &gpp_cfg, (char *) src_url, NULL, &tkw->stsd_idx);
2206 21 : if (e) {
2207 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new 3GPP audio sample description for stream type %d codecid %d: %s\n", tkw->stream_type, codec_id, gf_error_to_string(e) ));
2208 0 : return e;
2209 : }
2210 21 : tkw->use_dref = src_url ? GF_TRUE : GF_FALSE;
2211 :
2212 21 : if (!tkw->has_brands) {
2213 21 : switch (gpp_cfg.type) {
2214 4 : case GF_ISOM_SUBTYPE_3GP_QCELP:
2215 : case GF_ISOM_SUBTYPE_3GP_EVRC:
2216 : case GF_ISOM_SUBTYPE_3GP_SMV:
2217 4 : gf_isom_set_brand_info(ctx->file, GF_ISOM_BRAND_3G2A, 65536);
2218 4 : break;
2219 5 : case GF_ISOM_SUBTYPE_3GP_H263:
2220 5 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_3GG6, GF_TRUE);
2221 5 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_3GG5, GF_TRUE);
2222 5 : break;
2223 : }
2224 : }
2225 21 : tkw->skip_bitrate_update = GF_TRUE;
2226 250 : } else if (use_ac3_entry) {
2227 : GF_AC3Config ac3cfg;
2228 : memset(&ac3cfg, 0, sizeof(GF_AC3Config));
2229 :
2230 11 : if (dsi) {
2231 8 : gf_odf_ac3_config_parse(dsi->value.data.ptr, dsi->value.data.size, (codec_id==GF_CODECID_EAC3) ? GF_TRUE : GF_FALSE, &ac3cfg);
2232 : }
2233 11 : e = gf_isom_ac3_config_new(ctx->file, tkw->track_num, &ac3cfg, (char *)src_url, NULL, &tkw->stsd_idx);
2234 11 : if (e) {
2235 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new AC3 audio sample description for stream type %d codecid %d: %s\n", tkw->stream_type, codec_id, gf_error_to_string(e) ));
2236 0 : return e;
2237 : }
2238 11 : tkw->use_dref = src_url ? GF_TRUE : GF_FALSE;
2239 239 : } else if (use_flac_entry) {
2240 2 : e = gf_isom_flac_config_new(ctx->file, tkw->track_num, dsi ? dsi->value.data.ptr : NULL, dsi ? dsi->value.data.size : 0, (char *)src_url, NULL, &tkw->stsd_idx);
2241 2 : if (e) {
2242 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new FLAC audio sample description for stream type %d codecid %d: %s\n", tkw->stream_type, codec_id, gf_error_to_string(e) ));
2243 : return e;
2244 : }
2245 2 : tkw->use_dref = src_url ? GF_TRUE : GF_FALSE;
2246 237 : } else if (use_opus) {
2247 2 : GF_OpusSpecificBox *opus_cfg = NULL;
2248 : GF_BitStream *bs;
2249 :
2250 2 : if (!dsi) {
2251 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] No decoder specific info found for opus\n" ));
2252 0 : return GF_NON_COMPLIANT_BITSTREAM;
2253 : }
2254 :
2255 2 : bs = gf_bs_new(dsi->value.data.ptr, dsi->value.data.size, GF_BITSTREAM_READ);
2256 2 : e = gf_isom_box_parse((GF_Box**)&opus_cfg, bs);
2257 2 : gf_bs_del(bs);
2258 2 : if (e) {
2259 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error parsing opus configuration data: %s\n", gf_error_to_string(e) ));
2260 : return e;
2261 : }
2262 :
2263 2 : e = gf_isom_opus_config_new(ctx->file, tkw->track_num, opus_cfg, (char *)src_url, NULL, &tkw->stsd_idx);
2264 2 : if (opus_cfg) gf_isom_box_del((GF_Box*)opus_cfg);
2265 2 : if (e) {
2266 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new AC3 audio sample description for stream type %d codecid %d: %s\n", tkw->stream_type, codec_id, gf_error_to_string(e) ));
2267 : return e;
2268 : }
2269 2 : tkw->use_dref = src_url ? GF_TRUE : GF_FALSE;
2270 235 : } else if (m_subtype == GF_ISOM_SUBTYPE_METX) {
2271 : comp_name = "XML Metadata";
2272 3 : e = gf_isom_new_xml_metadata_description(ctx->file, tkw->track_num, meta_xmlns, meta_schemaloc, meta_encoding, &tkw->stsd_idx);
2273 3 : if (e) {
2274 1 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new METX sample description: %s\n", gf_error_to_string(e) ));
2275 : return e;
2276 : }
2277 232 : } else if (m_subtype == GF_ISOM_SUBTYPE_METT) {
2278 : comp_name = "Text Metadata";
2279 8 : e = gf_isom_new_stxt_description(ctx->file, tkw->track_num, GF_ISOM_SUBTYPE_METT, meta_mime, meta_encoding, meta_config, &tkw->stsd_idx);
2280 8 : if (e) {
2281 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new METT sample description: %s\n", gf_error_to_string(e) ));
2282 : return e;
2283 : }
2284 224 : } else if (m_subtype == GF_ISOM_SUBTYPE_STPP) {
2285 21 : if (meta_xmlns && !strcmp(meta_xmlns, "http://www.w3.org/ns/ttml")) {
2286 : comp_name = "TTML";
2287 : } else {
2288 : comp_name = "XML Subtitle";
2289 : }
2290 21 : e = gf_isom_new_xml_subtitle_description(ctx->file, tkw->track_num, meta_xmlns, meta_schemaloc, meta_auxmimes, &tkw->stsd_idx);
2291 21 : if (e) {
2292 1 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new XML subtitle sample description: %s\n", gf_error_to_string(e) ));
2293 : return e;
2294 : }
2295 :
2296 : //CMAF 11.3.2
2297 20 : if (ctx->cmaf) {
2298 0 : if (!meta_mime) meta_mime = gf_isom_subtitle_get_mime(ctx->file, tkw->track_num, tkw->stsd_idx);
2299 0 : if (!meta_mime || (!strstr(meta_mime, "im1t") && !strstr(meta_mime, "im1i"))) {
2300 0 : gf_isom_subtitle_set_mime(ctx->file, tkw->track_num, tkw->stsd_idx, "application/ttml+xml;codecs=im1t");
2301 : }
2302 : }
2303 :
2304 :
2305 203 : } else if ((m_subtype == GF_ISOM_SUBTYPE_SBTT) || (m_subtype == GF_ISOM_SUBTYPE_STXT) ) {
2306 17 : comp_name = (m_subtype == GF_ISOM_SUBTYPE_STXT) ? "Simple Timed Text" : "Textual Subtitle";
2307 17 : e = gf_isom_new_stxt_description(ctx->file, tkw->track_num, m_subtype, meta_mime, meta_content_encoding, meta_config, &tkw->stsd_idx);
2308 17 : if (e) {
2309 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new %s sample description: %s\n", gf_4cc_to_str(m_subtype), gf_error_to_string(e) ));
2310 : return e;
2311 : }
2312 17 : if (m_subtype == GF_ISOM_SUBTYPE_STXT) force_tk_layout = GF_TRUE;
2313 186 : } else if (use_tx3g) {
2314 : GF_TextSampleDescriptor *txtc;
2315 45 : if (!dsi) {
2316 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] No decoder specific info found for TX3G\n"));
2317 : return GF_NON_COMPLIANT_BITSTREAM;
2318 : }
2319 45 : txtc = gf_odf_tx3g_read(dsi->value.data.ptr, dsi->value.data.size);
2320 45 : if (!txtc) {
2321 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to parse TX3G config\n"));
2322 : return GF_NON_COMPLIANT_BITSTREAM;
2323 : }
2324 :
2325 45 : if (!txtc->default_pos.right) txtc->default_pos.right = width + txtc->default_pos.left;
2326 45 : if (!txtc->default_pos.bottom) txtc->default_pos.bottom = height + txtc->default_pos.top;
2327 :
2328 :
2329 45 : e = gf_isom_new_text_description(ctx->file, tkw->track_num, txtc, NULL, NULL, &tkw->stsd_idx);
2330 45 : if (e) {
2331 0 : gf_odf_desc_del((GF_Descriptor *)txtc);
2332 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new %s sample description: %s\n", gf_4cc_to_str(m_subtype), gf_error_to_string(e) ));
2333 : return e;
2334 : }
2335 45 : if (ctx->importer) {
2336 29 : txt_fsize = txtc->default_style.font_size;
2337 29 : if (txtc->font_count && txtc->fonts[0].fontName) txt_font = gf_strdup(txtc->fonts[0].fontName);
2338 : }
2339 45 : gf_odf_desc_del((GF_Descriptor *)txtc);
2340 :
2341 45 : tkw->skip_bitrate_update = GF_TRUE;
2342 141 : } else if (use_webvtt) {
2343 42 : e = gf_isom_new_webvtt_description(ctx->file, tkw->track_num, NULL, NULL, &tkw->stsd_idx, dsi ? dsi->value.data.ptr : NULL);
2344 42 : if (e) {
2345 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new %s sample description: %s\n", gf_4cc_to_str(m_subtype), gf_error_to_string(e) ));
2346 : return e;
2347 : }
2348 42 : tkw->skip_bitrate_update = GF_TRUE;
2349 99 : } else if (use_mj2) {
2350 7 : e = gf_isom_new_mj2k_description(ctx->file, tkw->track_num, NULL, NULL, &tkw->stsd_idx, dsi ? dsi->value.data.ptr : NULL, dsi ? dsi->value.data.size : 0);
2351 7 : if (e) {
2352 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new %s sample description: %s\n", gf_4cc_to_str(m_subtype), gf_error_to_string(e) ));
2353 : return e;
2354 : }
2355 92 : } else if (codec_id==GF_CODECID_TMCD) {
2356 : u32 tmcd_flags=0, tmcd_fps_num=0, tmcd_fps_den=0;
2357 : s32 tmcd_fpt=0;
2358 :
2359 0 : p = gf_filter_pid_get_property_str(pid, "tmcd:flags");
2360 0 : if (p) tmcd_flags = p->value.uint;
2361 0 : p = gf_filter_pid_get_property_str(pid, "tmcd:framerate");
2362 0 : if (p) {
2363 0 : tmcd_fps_num = p->value.frac.num;
2364 0 : tmcd_fps_den = p->value.frac.den;
2365 : }
2366 0 : p = gf_filter_pid_get_property_str(pid, "tmcd:frames_per_tick");
2367 0 : if (p) tmcd_fpt = p->value.uint;
2368 0 : if (tkw->tk_timescale != tmcd_fps_num) {
2369 0 : tmcd_fps_den *= tmcd_fps_num;
2370 0 : tmcd_fps_den /= tkw->tk_timescale;
2371 : }
2372 :
2373 0 : e = gf_isom_tmcd_config_new(ctx->file, tkw->track_num, tmcd_fps_num, tmcd_fps_den, tmcd_fpt, (tmcd_flags & 0x1), (tmcd_flags & 0x8), &tkw->stsd_idx);
2374 0 : if (e) {
2375 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new tmcd sample description: %s\n", gf_error_to_string(e) ));
2376 : return e;
2377 : }
2378 92 : } else if (codec_id==GF_CODECID_DIMS) {
2379 : GF_DIMSDescription dims_c;
2380 : memset(&dims_c, 0, sizeof(GF_DIMSDescription));
2381 2 : dims_c.contentEncoding = meta_content_encoding;
2382 2 : dims_c.mime_type = meta_mime;
2383 2 : dims_c.textEncoding = meta_encoding;
2384 2 : dims_c.xml_schema_loc = meta_xmlns;
2385 :
2386 2 : p = gf_filter_pid_get_property_str(tkw->ipid, "dims:profile");
2387 2 : if (p) dims_c.profile = p->value.uint;
2388 2 : p = gf_filter_pid_get_property_str(tkw->ipid, "dims:level");
2389 2 : if (p) dims_c.level = p->value.uint;
2390 2 : p = gf_filter_pid_get_property_str(tkw->ipid, "dims:pathComponents");
2391 2 : if (p) dims_c.pathComponents = p->value.uint;
2392 2 : p = gf_filter_pid_get_property_str(tkw->ipid, "dims:fullRequestHost");
2393 2 : if (p) dims_c.fullRequestHost = p->value.uint;
2394 2 : p = gf_filter_pid_get_property_str(tkw->ipid, "dims:streamType");
2395 2 : if (p) dims_c.streamType = p->value.boolean;
2396 2 : p = gf_filter_pid_get_property_str(tkw->ipid, "dims:redundant");
2397 2 : if (p) dims_c.containsRedundant = p->value.uint;
2398 2 : p = gf_filter_pid_get_property_str(tkw->ipid, "dims:scriptTypes");
2399 2 : if (p) dims_c.content_script_types = p->value.string;
2400 :
2401 2 : e = gf_isom_new_dims_description(ctx->file, tkw->track_num, &dims_c, NULL, NULL, &tkw->stsd_idx);
2402 2 : if (e) {
2403 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new DIMS sample description: %s\n", gf_error_to_string(e) ));
2404 0 : return e;
2405 : }
2406 90 : } else if (codec_id==GF_CODECID_MPHA) {
2407 : //not ready yet
2408 1 : if (!dsi) return GF_OK;
2409 :
2410 1 : e = gf_isom_new_mpha_description(ctx->file, tkw->track_num, NULL, NULL, &tkw->stsd_idx, dsi->value.data.ptr, dsi->value.data.size);
2411 1 : if (e) {
2412 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new MPEG-H Audio sample description: %s\n", gf_error_to_string(e) ));
2413 : return e;
2414 : }
2415 89 : } else if (codec_id==GF_CODECID_TRUEHD) {
2416 : u32 fmt=0, prate=0;
2417 : //not ready yet
2418 1 : if (!dsi) return GF_OK;
2419 1 : if (dsi->value.data.size < 6) return GF_NON_COMPLIANT_BITSTREAM;
2420 :
2421 1 : fmt = dsi->value.data.ptr[0];
2422 1 : fmt <<= 8;
2423 1 : fmt |= dsi->value.data.ptr[1];
2424 1 : prate = dsi->value.data.ptr[2];
2425 1 : prate <<= 8;
2426 1 : prate |= dsi->value.data.ptr[3];
2427 1 : prate >>= 1;
2428 :
2429 1 : e = gf_isom_truehd_config_new(ctx->file, tkw->track_num, (char *)src_url, NULL, fmt, prate, &tkw->stsd_idx);
2430 1 : if (e) {
2431 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new TrueHD Audio sample description: %s\n", gf_error_to_string(e) ));
2432 : return e;
2433 : }
2434 88 : } else if (use_gen_sample_entry) {
2435 : u8 isor_ext_buf[14];
2436 : u32 len = 0;
2437 : GF_GenericSampleDescription udesc;
2438 : memset(&udesc, 0, sizeof(GF_GenericSampleDescription));
2439 :
2440 88 : if (!comp_name) comp_name = "Unknown";
2441 88 : len = (u32) strlen(comp_name);
2442 88 : if (len>32) len = 32;
2443 88 : udesc.compressor_name[0] = len;
2444 88 : memcpy(udesc.compressor_name+1, comp_name, len);
2445 88 : if ((codec_id==GF_CODECID_RAW) || unknown_generic)
2446 6 : udesc.vendor_code = GF_4CC('G','P','A','C');
2447 :
2448 88 : udesc.samplerate = sr;
2449 88 : udesc.nb_channels = nb_chan;
2450 88 : if (codec_id==GF_CODECID_RAW) {
2451 6 : if (ase_mode==GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_MPEG) {
2452 : m_subtype = m_subtype_alt_raw;
2453 5 : udesc.extension_buf_size = 14;
2454 5 : udesc.extension_buf = isor_ext_buf;
2455 : memset(isor_ext_buf, 0, sizeof(u8)*14);
2456 5 : isor_ext_buf[3] = 14;
2457 5 : isor_ext_buf[4] = 'p';
2458 5 : isor_ext_buf[5] = 'c';
2459 5 : isor_ext_buf[6] = 'm';
2460 5 : isor_ext_buf[7] = 'C';
2461 5 : isor_ext_buf[12] = 1; //little endian only for now
2462 5 : isor_ext_buf[13] = raw_bitdepth; //little endian only for now
2463 : } else {
2464 1 : udesc.is_qtff = GF_TRUE;
2465 1 : udesc.version = 1;
2466 : ase_mode = GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_QTFF;
2467 : }
2468 : }
2469 88 : udesc.codec_tag = m_subtype;
2470 88 : udesc.width = width;
2471 88 : udesc.height = height;
2472 88 : if (width) {
2473 20 : udesc.v_res = 72;
2474 20 : udesc.h_res = 72;
2475 20 : udesc.depth = 24;
2476 : }
2477 88 : if (unknown_generic) {
2478 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] muxing unknown codec ID %s, using generic sample entry with 4CC \"%s\"\n", gf_codecid_name(codec_id), gf_4cc_to_str(m_subtype) ));
2479 : }
2480 :
2481 88 : e = gf_isom_new_generic_sample_description(ctx->file, tkw->track_num, (char *)src_url, NULL, &udesc, &tkw->stsd_idx);
2482 88 : if (e) {
2483 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error creating new sample description for stream type %d codecid %d: %s\n", tkw->stream_type, codec_id, gf_error_to_string(e) ));
2484 0 : return e;
2485 : }
2486 88 : tkw->use_dref = src_url ? GF_TRUE : GF_FALSE;
2487 : } else {
2488 : assert(0);
2489 : }
2490 :
2491 1481 : if (ctx->btrt && !tkw->skip_bitrate_update) {
2492 : u32 avg_rate, max_rate, dbsize;
2493 1364 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_BITRATE);
2494 1364 : avg_rate = p ? p->value.uint : 0;
2495 1364 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_MAXRATE);
2496 1364 : max_rate = p ? p->value.uint : 0;
2497 1364 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DBSIZE);
2498 1364 : dbsize = p ? p->value.uint : 0;
2499 :
2500 1364 : if (avg_rate && max_rate) {
2501 580 : gf_isom_update_bitrate(ctx->file, tkw->track_num, tkw->stsd_idx, avg_rate, max_rate, dbsize);
2502 : }
2503 : } else {
2504 117 : gf_isom_update_bitrate(ctx->file, tkw->track_num, tkw->stsd_idx, 0, 0, 0);
2505 : }
2506 :
2507 1481 : multipid_stsd_setup:
2508 1481 : if (multi_pid_stsd) {
2509 0 : if (multi_pid_idx<gf_list_count(multi_pid_stsd)) {
2510 :
2511 0 : if (multi_pid_final_stsd_idx == multi_pid_idx) {
2512 0 : frames_per_sample_backup = tkw->nb_frames_per_sample;
2513 0 : is_nalu_backup = tkw->is_nalu;
2514 : }
2515 0 : pid = gf_list_get(multi_pid_stsd, multi_pid_idx);
2516 0 : multi_pid_idx ++;
2517 : //reload codecID, decoder config and enhancement decoder config
2518 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_CODECID);
2519 0 : if (p) codec_id = p->value.uint;
2520 0 : dsi = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG);
2521 0 : enh_dsi = gf_filter_pid_get_property(pid, GF_PROP_PID_DECODER_CONFIG_ENHANCEMENT);
2522 : //force stsd idx to be 0 to avoid removing the stsd
2523 0 : tkw->stsd_idx = 0;
2524 0 : goto sample_entry_setup;
2525 : }
2526 0 : tkw->stsd_idx = multi_pid_final_stsd_idx;
2527 : //restore input pid
2528 : pid = orig_pid;
2529 0 : codec_id = tkw->codecid;
2530 :
2531 0 : tkw->is_nalu = is_nalu_backup;
2532 0 : tkw->nb_frames_per_sample = frames_per_sample_backup;
2533 : }
2534 :
2535 :
2536 : //final opt: we couldn't detect before if the same stsd was possible, now that we have create a new one, check again
2537 1481 : if (needs_sample_entry) {
2538 : reuse_stsd = 0;
2539 : //don't try to reuse STSDs in multi STSD setup for DASH
2540 1474 : if (multi_pid_stsd) count = 0;
2541 1474 : else count = gf_isom_get_sample_description_count(ctx->file, tkw->track_num);
2542 4423 : for (i=0; i<count; i++) {
2543 1480 : if (i+1 == tkw->stsd_idx) continue;
2544 :
2545 11 : if (gf_isom_is_same_sample_description(ctx->file, tkw->track_num, tkw->stsd_idx, ctx->file, tkw->track_num, i+1) ) {
2546 5 : gf_isom_remove_stream_description(ctx->file, tkw->track_num, tkw->stsd_idx);
2547 5 : tkw->stsd_idx = i+1;
2548 : reuse_stsd = 1;
2549 : break;
2550 : }
2551 : }
2552 : if (!reuse_stsd) {
2553 1469 : tkw->samples_in_stsd = 0;
2554 5 : } else if (use_3gpp_config) {
2555 0 : GF_3GPConfig *gpp_cfg = gf_isom_3gp_config_get(ctx->file, tkw->track_num, tkw->stsd_idx);
2556 0 : if (gpp_cfg) {
2557 0 : gpp_cfg->AMR_mode_set = tkw->amr_mode_set;
2558 0 : gf_isom_3gp_config_update(ctx->file, tkw->track_num, gpp_cfg, tkw->stsd_idx);
2559 0 : gf_free(gpp_cfg);
2560 : }
2561 : }
2562 : }
2563 :
2564 1481 : if (tkw->is_encrypted) {
2565 : const char *scheme_uri=NULL;
2566 : const char *kms_uri=NULL;
2567 : u32 scheme_version=0;
2568 : u32 scheme_type = 0;
2569 : Bool is_sel_enc = GF_FALSE;
2570 : u32 KI_length=0;
2571 : u32 IV_length=0;
2572 : /*todo !*/
2573 : const char *oma_contentID=0;
2574 : u32 oma_encryption_type=0;
2575 : u64 oma_plainTextLength=0;
2576 : const char *oma_textual_headers=NULL;
2577 : u32 textual_headers_len=0;
2578 :
2579 229 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_PROTECTION_SCHEME_TYPE);
2580 229 : if (p) scheme_type = p->value.uint;
2581 229 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_PROTECTION_SCHEME_VERSION);
2582 229 : if (p) scheme_version = p->value.uint;
2583 229 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_PROTECTION_SCHEME_URI);
2584 229 : if (p) scheme_uri = p->value.string;
2585 229 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_PROTECTION_KMS_URI);
2586 229 : if (p) kms_uri = p->value.string;
2587 :
2588 229 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ISMA_SELECTIVE_ENC);
2589 229 : if (p) is_sel_enc = p->value.boolean;
2590 229 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ISMA_IV_LENGTH);
2591 229 : if (p) IV_length = p->value.uint;
2592 229 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ISMA_KI_LENGTH);
2593 229 : if (p) KI_length = p->value.uint;
2594 :
2595 229 : tkw->scheme_type = scheme_type;
2596 229 : switch (scheme_type) {
2597 11 : case GF_ISOM_ISMACRYP_SCHEME:
2598 11 : gf_isom_set_ismacryp_protection(ctx->file, tkw->track_num, tkw->stsd_idx, scheme_type, scheme_version, (char *) scheme_uri, (char *) kms_uri, is_sel_enc, KI_length, IV_length);
2599 11 : break;
2600 0 : case GF_ISOM_OMADRM_SCHEME:
2601 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_OMA_CRYPT_TYPE);
2602 0 : if (p) oma_encryption_type = p->value.uint;
2603 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_OMA_CID);
2604 0 : if (p) oma_contentID = p->value.string;
2605 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_OMA_TXT_HDR);
2606 0 : if (p) oma_textual_headers = p->value.string;
2607 0 : if (oma_textual_headers) textual_headers_len = (u32) strlen(oma_textual_headers);
2608 0 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_OMA_CLEAR_LEN);
2609 0 : if (p) oma_plainTextLength = p->value.longuint;
2610 0 : gf_isom_set_oma_protection(ctx->file, tkw->track_num, tkw->stsd_idx, (char *) oma_contentID, (char*) kms_uri, oma_encryption_type, oma_plainTextLength, (char*)oma_textual_headers, textual_headers_len,
2611 : is_sel_enc, KI_length, IV_length);
2612 :
2613 0 : break;
2614 10 : case GF_ISOM_ADOBE_SCHEME:
2615 10 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ADOBE_CRYPT_META);
2616 10 : gf_isom_set_adobe_protection(ctx->file, tkw->track_num, tkw->stsd_idx, scheme_type, 1/*scheme_version*/, 1/*is_sel_enc*/,p ? p->value.data.ptr : NULL, p ? p->value.data.size : 0);
2617 10 : break;
2618 208 : case GF_ISOM_PIFF_SCHEME:
2619 : case GF_ISOM_CENC_SCHEME:
2620 : case GF_ISOM_CENS_SCHEME:
2621 : case GF_ISOM_CBC_SCHEME:
2622 : case GF_ISOM_CBCS_SCHEME:
2623 208 : tkw->cenc_state = CENC_NEED_SETUP;
2624 208 : if (tkw->is_nalu || tkw->is_av1 || tkw->is_vpx) tkw->cenc_subsamples = GF_TRUE;
2625 : break;
2626 0 : default:
2627 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Unrecognized protection scheme type %s, using generic signaling\n", gf_4cc_to_str(tkw->stream_type) ));
2628 0 : switch (tkw->stream_type) {
2629 0 : case GF_STREAM_VISUAL:
2630 0 : gf_isom_set_media_type(ctx->file, tkw->track_num, GF_ISOM_BOX_TYPE_ENCV);
2631 0 : break;
2632 0 : case GF_STREAM_AUDIO:
2633 0 : gf_isom_set_media_type(ctx->file, tkw->track_num, GF_ISOM_BOX_TYPE_ENCA);
2634 0 : break;
2635 0 : case GF_STREAM_TEXT:
2636 0 : gf_isom_set_media_type(ctx->file, tkw->track_num, GF_ISOM_BOX_TYPE_ENCT);
2637 0 : break;
2638 0 : case GF_STREAM_FONT:
2639 0 : gf_isom_set_media_type(ctx->file, tkw->track_num, GF_ISOM_BOX_TYPE_ENCF);
2640 0 : break;
2641 0 : default:
2642 0 : gf_isom_set_media_type(ctx->file, tkw->track_num, GF_ISOM_BOX_TYPE_ENCS);
2643 0 : break;
2644 : }
2645 0 : gf_isom_set_generic_protection(ctx->file, tkw->track_num, tkw->stsd_idx, scheme_type, scheme_version, (char*)scheme_uri, (char*)kms_uri);
2646 : }
2647 : } else {
2648 : //in case we used track template
2649 1252 : gf_isom_remove_samp_enc_box(ctx->file, tkw->track_num);
2650 1252 : gf_isom_remove_samp_group_box(ctx->file, tkw->track_num);
2651 : }
2652 :
2653 1481 : if (is_true_pid) {
2654 1454 : mp4_mux_write_track_refs(ctx, tkw, "isom:scal", GF_ISOM_REF_SCAL);
2655 1454 : mp4_mux_write_track_refs(ctx, tkw, "isom:sabt", GF_ISOM_REF_SABT);
2656 1454 : mp4_mux_write_track_refs(ctx, tkw, "isom:tbas", GF_ISOM_REF_TBAS);
2657 : //whenever we add a new tile track, rewrite sabt on main tile track
2658 1454 : if (codec_id==GF_CODECID_HEVC_TILES) {
2659 51 : count = gf_list_count(ctx->tracks);
2660 168 : for (i=0; i<count; i++) {
2661 117 : TrackWriter *base_tk = gf_list_get(ctx->tracks, i);
2662 117 : if (base_tk->is_hevc_tile_base)
2663 15 : mp4_mux_write_track_refs(ctx, base_tk, "isom:sabt", GF_ISOM_REF_SABT);
2664 : }
2665 : }
2666 :
2667 : //check if we have sample-accurate seek info for the pid. If so, enable seek ts checking
2668 1454 : p = gf_filter_pid_get_property(pid, GF_PROP_PCK_SKIP_BEGIN);
2669 1454 : if (p && p->value.sint)
2670 10 : tkw->check_seek_ts = GF_TRUE;
2671 :
2672 27 : } else if (codec_id==GF_CODECID_HEVC_TILES) {
2673 27 : mp4_mux_write_track_refs(ctx, tkw, "isom:tbas", GF_ISOM_REF_TBAS);
2674 : }
2675 :
2676 1481 : if (is_true_pid && ctx->dash_mode && is_tile_base) {
2677 3 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_DASH_MULTI_TRACK);
2678 3 : if (p) {
2679 3 : GF_List *multi_tracks = p->value.ptr;
2680 3 : count = gf_list_count(multi_tracks);
2681 30 : for (i=0; i<count; i++) {
2682 27 : GF_FilterPid *a_ipid = gf_list_get(multi_tracks, i);
2683 27 : mp4_mux_setup_pid(filter, a_ipid, GF_FALSE);
2684 : }
2685 : }
2686 : }
2687 :
2688 1481 : if (width) {
2689 1082 : if (ctx->ccst) {
2690 0 : e = gf_isom_set_image_sequence_coding_constraints(ctx->file, tkw->track_num, tkw->stsd_idx, GF_FALSE, GF_FALSE, GF_TRUE, 15);
2691 0 : if (e) {
2692 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Failed to set coding constraints parameter: %s\n", gf_error_to_string(e) ));
2693 : }
2694 : }
2695 1082 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_ALPHA);
2696 1083 : if (p && p->value.boolean) {
2697 1 : e = gf_isom_set_image_sequence_alpha(ctx->file, tkw->track_num, tkw->stsd_idx, GF_FALSE);
2698 1 : if (e) {
2699 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Failed to set alpha config: %s\n", gf_error_to_string(e) ));
2700 : }
2701 : }
2702 : }
2703 :
2704 5538 : sample_entry_done:
2705 2769 : if (!tkw->is_item) {
2706 2721 : if (ctx->maxchunk)
2707 0 : gf_isom_hint_max_chunk_size(ctx->file, tkw->track_num, ctx->maxchunk);
2708 :
2709 2721 : if (ctx->store==MP4MX_MODE_FLAT)
2710 0 : gf_isom_hint_max_chunk_duration(ctx->file, tkw->track_num, tkw->tk_timescale * ctx->cdur.num / ctx->cdur.den);
2711 :
2712 2721 : if (sr) {
2713 1150 : if (use_flac_entry) {
2714 2 : while (sr>65535) {
2715 0 : u32 val = sr/2;
2716 0 : if (val*2 != sr) {
2717 : sr=65535;
2718 : break;
2719 : }
2720 : sr = val;
2721 : }
2722 : }
2723 1150 : gf_isom_set_audio_info(ctx->file, tkw->track_num, tkw->stsd_idx, sr, nb_chan, nb_bps, ctx->make_qt ? GF_IMPORT_AUDIO_SAMPLE_ENTRY_v1_QTFF : ase_mode);
2724 1150 : if ((m_subtype==GF_ISOM_SUBTYPE_IPCM) || (m_subtype==GF_ISOM_SUBTYPE_FPCM)) {
2725 : GF_AudioChannelLayout layout;
2726 : memset(&layout, 0, sizeof(GF_AudioChannelLayout));
2727 5 : layout.stream_structure = 1;
2728 5 : layout.channels_count = nb_chan;
2729 5 : if (ch_layout)
2730 0 : layout.definedLayout = gf_audio_fmt_get_cicp_from_layout(ch_layout);
2731 : else
2732 5 : layout.definedLayout = gf_audio_fmt_get_cicp_layout(nb_chan, 0, 0);
2733 5 : gf_isom_set_audio_layout(ctx->file, tkw->track_num, tkw->stsd_idx, &layout);
2734 : }
2735 : }
2736 1571 : else if (width) {
2737 1458 : u32 colour_type=0;
2738 1458 : u16 colour_primaries=0, transfer_characteristics=0, matrix_coefficients=0;
2739 1458 : Bool full_range_flag=GF_FALSE;
2740 :
2741 1458 : gf_isom_set_visual_info(ctx->file, tkw->track_num, tkw->stsd_idx, width, height);
2742 1458 : if (sar.den) {
2743 113 : if (sar.num != sar.den) {
2744 26 : gf_isom_set_pixel_aspect_ratio(ctx->file, tkw->track_num, tkw->stsd_idx, sar.num, sar.den, GF_FALSE);
2745 26 : width = width * sar.num / sar.den;
2746 : }
2747 : //old importer did not set PASP for
2748 87 : else if (!gf_sys_old_arch_compat() || (codec_id!=GF_CODECID_MPEG4_PART2) ) {
2749 39 : gf_isom_set_pixel_aspect_ratio(ctx->file, tkw->track_num, tkw->stsd_idx, 1, 1, GF_TRUE);
2750 : }
2751 : }
2752 :
2753 1458 : gf_isom_set_track_layout_info(ctx->file, tkw->track_num, width<<16, height<<16, 0, 0, z_order);
2754 1458 : if (codec_id==GF_CODECID_HEVC_TILES) {
2755 78 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ORIG_SIZE);
2756 78 : if (p) {
2757 45 : gf_isom_set_track_layout_info(ctx->file, tkw->track_num, p->value.vec2i.x<<16, p->value.vec2i.y<<16, 0, 0, z_order);
2758 : }
2759 : }
2760 :
2761 1458 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_COLR_PRIMARIES);
2762 1458 : if (p) colour_primaries = p->value.uint;
2763 1458 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_COLR_TRANSFER);
2764 1458 : if (p) transfer_characteristics = p->value.uint;
2765 1458 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_COLR_MX);
2766 1458 : if (p) matrix_coefficients = p->value.uint;
2767 1458 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_COLR_RANGE);
2768 1458 : if (p) full_range_flag = p->value.boolean;
2769 :
2770 1458 : if (ctx->cmaf) {
2771 0 : u32 hspac=0, vspac=0;
2772 : force_colr = GF_TRUE;
2773 0 : gf_isom_get_pixel_aspect_ratio(ctx->file, tkw->track_num, tkw->stsd_idx, &hspac, &vspac);
2774 0 : if (hspac && vspac) {
2775 0 : sar.num = hspac;
2776 0 : sar.den = vspac;
2777 : } else {
2778 : sar.den = 0;
2779 : }
2780 : }
2781 :
2782 1458 : if ((ctx->prores_track == tkw) || force_colr) {
2783 : u32 colr_mode;
2784 :
2785 4 : if ((ctx->prores_track == tkw) || ctx->make_qt)
2786 : colr_mode = GF_4CC('n','c','l','c');
2787 : else
2788 : colr_mode = GF_4CC('n','c','l','x');
2789 :
2790 :
2791 : //other conditions were set above, here we force 1:1 pasp box even if no sar or 1:1
2792 4 : if (!sar.den || (sar.num == 1)) {
2793 3 : gf_isom_set_pixel_aspect_ratio(ctx->file, tkw->track_num, tkw->stsd_idx, -1, -1, GF_TRUE);
2794 : }
2795 :
2796 4 : if (colour_primaries || transfer_characteristics || matrix_coefficients) {
2797 2 : gf_isom_set_visual_color_info(ctx->file, tkw->track_num, tkw->stsd_idx, colr_mode, colour_primaries, transfer_characteristics, matrix_coefficients, GF_FALSE, NULL, 0);
2798 : } else {
2799 2 : e = gf_isom_get_color_info(ctx->file, tkw->track_num, tkw->stsd_idx, &colour_type, &colour_primaries, &transfer_characteristics, &matrix_coefficients, &full_range_flag);
2800 2 : if (e==GF_NOT_FOUND) {
2801 1 : e = gf_media_get_color_info(ctx->file, tkw->track_num, tkw->stsd_idx, &colour_type, &colour_primaries, &transfer_characteristics, &matrix_coefficients, &full_range_flag);
2802 1 : if (e)
2803 : e = GF_NOT_FOUND;
2804 : }
2805 1 : if (e==GF_NOT_FOUND) {
2806 1 : colour_primaries = 1;
2807 1 : transfer_characteristics = 1;
2808 1 : matrix_coefficients = 1;
2809 1 : full_range_flag = GF_FALSE;
2810 1 : if (ctx->make_qt==1) {
2811 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[ProRes] No color info present in visual track, defaulting to BT709\n"));
2812 : }
2813 1 : else if (ctx->cmaf) {
2814 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_AUTHOR, ("[CMAF] No color info present in visual track, defaulting to BT709\n"));
2815 : }
2816 : }
2817 2 : gf_isom_set_visual_color_info(ctx->file, tkw->track_num, tkw->stsd_idx, colr_mode, colour_primaries, transfer_characteristics, matrix_coefficients, full_range_flag, NULL, 0);
2818 : }
2819 :
2820 4 : if (ctx->prores_track == tkw) {
2821 : u32 chunk_size;
2822 4 : if ((width<=720) && (height<=576)) chunk_size = 2000000;
2823 : else chunk_size = 4000000;
2824 4 : gf_isom_hint_max_chunk_size(ctx->file, tkw->track_num, chunk_size);
2825 : }
2826 : } else {
2827 1454 : if (colour_primaries || transfer_characteristics || matrix_coefficients) {
2828 75 : gf_isom_set_visual_color_info(ctx->file, tkw->track_num, tkw->stsd_idx, GF_4CC('n','c','l','x'), colour_primaries, transfer_characteristics, matrix_coefficients, full_range_flag, NULL, 0);
2829 : }
2830 :
2831 : }
2832 : }
2833 : //default for old arch
2834 113 : else if (force_tk_layout
2835 106 : || (use_m4sys && (tkw->stream_type==GF_STREAM_VISUAL) && !width && !height)
2836 : ) {
2837 35 : gf_isom_set_track_layout_info(ctx->file, tkw->track_num, 320<<16, 240<<16, 0, 0, 0);
2838 : }
2839 :
2840 2721 : if (lang_name) gf_isom_set_media_language(ctx->file, tkw->track_num, (char*)lang_name);
2841 :
2842 2721 : p = gf_filter_pid_get_property(pid, GF_PROP_PID_ISOM_STSD_TEMPLATE);
2843 2721 : if (ctx->tktpl && p && p->value.data.ptr) {
2844 1109 : gf_isom_update_sample_description_from_template(ctx->file, tkw->track_num, tkw->stsd_idx, p->value.data.ptr, p->value.data.size);
2845 : }
2846 : }
2847 :
2848 2769 : if (tkw->is_encrypted) {
2849 575 : tkw->cenc_ki = gf_filter_pid_get_property(pid, GF_PROP_PID_CENC_KEY_INFO);
2850 575 : if (tkw->cenc_ki && ((tkw->cenc_ki->type != GF_PROP_DATA) || !gf_cenc_validate_key_info(tkw->cenc_ki->value.data.ptr, tkw->cenc_ki->value.data.size))
2851 : ) {
2852 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Invalid CENC key info\n"));
2853 0 : tkw->cenc_ki = NULL;
2854 : }
2855 :
2856 575 : tkw->constant_IV_size = 0;
2857 575 : if (tkw->cenc_ki && tkw->cenc_ki->value.data.ptr) {
2858 554 : tkw->cenc_multikey = tkw->cenc_ki->value.data.ptr[0] ? GF_TRUE : GF_FALSE;
2859 :
2860 554 : if (!tkw->cenc_ki->value.data.ptr[3])
2861 19 : tkw->constant_IV_size = !tkw->cenc_ki->value.data.ptr[20];
2862 :
2863 554 : tkw->cenc_key_info_crc = gf_crc_32(tkw->cenc_ki->value.data.ptr, tkw->cenc_ki->value.data.size);
2864 : }
2865 : }
2866 :
2867 2769 : if (is_true_pid) {
2868 2742 : const GF_PropertyValue *ster = gf_filter_pid_get_property(pid, GF_PROP_PID_STEREO_TYPE);
2869 2742 : const GF_PropertyValue *proj = gf_filter_pid_get_property(pid, GF_PROP_PID_PROJECTION_TYPE);
2870 2742 : const GF_PropertyValue *pose = gf_filter_pid_get_property(pid, GF_PROP_PID_VR_POSE);
2871 :
2872 2742 : if (ster || proj) {
2873 : GF_ISOM_Y3D_Info yt3d;
2874 : memset(&yt3d, 0, sizeof(GF_ISOM_Y3D_Info));
2875 0 : yt3d.projection_type = proj ? proj->value.uint : 0;
2876 0 : yt3d.stereo_type = ster ? ster->value.uint : 0;
2877 0 : if (pose) {
2878 0 : yt3d.pose_present = GF_TRUE;
2879 0 : yt3d.yaw = pose->value.vec3i.x;
2880 0 : yt3d.pitch = pose->value.vec3i.y;
2881 0 : yt3d.roll = pose->value.vec3i.z;
2882 0 : yt3d.stereo_type = ster ? ster->value.uint : 0;
2883 : }
2884 0 : if (yt3d.projection_type==GF_PROJ360_CUBE_MAP) {
2885 0 : proj = gf_filter_pid_get_property(pid, GF_PROP_PID_CUBE_MAP_PAD);
2886 0 : yt3d.padding = proj ? proj->value.uint : 0;
2887 : }
2888 0 : else if (yt3d.projection_type==GF_PROJ360_EQR) {
2889 0 : proj = gf_filter_pid_get_property(pid, GF_PROP_PID_EQR_CLAMP);
2890 0 : if (proj) {
2891 0 : yt3d.top = proj->value.vec4i.x;
2892 0 : yt3d.bottom = proj->value.vec4i.y;
2893 0 : yt3d.left = proj->value.vec4i.z;
2894 0 : yt3d.right = proj->value.vec4i.w;
2895 : }
2896 : }
2897 0 : gf_isom_set_y3d_info(ctx->file, tkw->track_num, tkw->stsd_idx, &yt3d);
2898 : }
2899 : }
2900 :
2901 :
2902 2769 : if (is_true_pid && ctx->importer && !tkw->import_msg_header_done) {
2903 : #ifndef GPAC_DISABLE_LOG
2904 613 : const char *dst_type = tkw->is_item ? "Item Importing" : "Track Importing";
2905 : #endif
2906 613 : tkw->import_msg_header_done = GF_TRUE;
2907 613 : if (!imp_name) imp_name = comp_name;
2908 613 : if (sr) {
2909 120 : if (nb_chan) {
2910 119 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("%s %s - SampleRate %d Num Channels %d\n", dst_type, imp_name, sr, nb_chan));
2911 : } else {
2912 1 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("%s %s - SampleRate %d\n", dst_type, imp_name, sr));
2913 : }
2914 493 : } else if (is_text_subs) {
2915 70 : if (txt_fsize || txt_font) {
2916 28 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("%s %s - Text track %d x %d font %s (size %d) layer %d\n", dst_type, imp_name, width, height, txt_font ? txt_font : "unspecified", txt_fsize, z_order));
2917 : } else {
2918 42 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("%s %s - Text track %d x %d layer %d\n", dst_type, imp_name, width, height, z_order));
2919 :
2920 : }
2921 423 : } else if (width) {
2922 379 : if (sar.den && sar.num) {
2923 37 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("%s %s - Width %d Height %d FPS %d/%d SAR %d/%u\n", dst_type, imp_name, width, height, fps.num, fps.den, sar.num, sar.den));
2924 : } else {
2925 342 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("%s %s - Width %d Height %d FPS %d/%d\n", dst_type, imp_name, width, height, fps.num, fps.den));
2926 : }
2927 : } else {
2928 44 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("%s %s\n", dst_type, imp_name));
2929 : }
2930 : #ifndef GPAC_DISABLE_AV_PARSERS
2931 613 : if (tkw->svcc) {
2932 : AVCState avc;
2933 : memset(&avc, 0, sizeof(AVCState));
2934 3 : count = gf_list_count(tkw->svcc->sequenceParameterSets);
2935 8 : for (i=0; i<count; i++) {
2936 5 : GF_NALUFFParam *sl = gf_list_get(tkw->svcc->sequenceParameterSets, i);
2937 5 : u8 nal_type = sl->data[0] & 0x1F;
2938 5 : Bool is_subseq = (nal_type == GF_AVC_NALU_SVC_SUBSEQ_PARAM) ? GF_TRUE : GF_FALSE;
2939 5 : s32 ps_idx = gf_avc_read_sps(sl->data, sl->size, &avc, is_subseq, NULL);
2940 5 : if (ps_idx>=0) {
2941 5 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("SVC Detected - SSPS ID %d - frame size %d x %d\n", ps_idx-GF_SVC_SSPS_ID_SHIFT, avc.sps[ps_idx].width, avc.sps[ps_idx].height ));
2942 :
2943 : }
2944 : }
2945 : }
2946 : #endif
2947 :
2948 : }
2949 2769 : if (txt_font) gf_free(txt_font);
2950 2769 : if (!ctx->xps_inband || tkw->is_item) {
2951 2745 : if (tkw->svcc) {
2952 6 : gf_odf_avc_cfg_del(tkw->svcc);
2953 6 : tkw->svcc = NULL;
2954 : }
2955 2745 : if (tkw->lvcc) {
2956 8 : gf_odf_hevc_cfg_del(tkw->lvcc);
2957 8 : tkw->lvcc = NULL;
2958 : }
2959 24 : } else if (needs_sample_entry || make_inband_headers) {
2960 24 : mp4_mux_make_inband_header(ctx, tkw, GF_FALSE);
2961 24 : if (ctx->pps_inband)
2962 0 : mp4_mux_make_inband_header(ctx, tkw, GF_TRUE);
2963 : }
2964 :
2965 2769 : tkw->negctts_shift = 0;
2966 2769 : tkw->probe_min_ctts = GF_FALSE;
2967 2769 : if (is_true_pid && !tkw->nb_samples && !tkw->is_item) {
2968 : Bool use_negccts = GF_FALSE;
2969 : Bool remove_edits = GF_FALSE;
2970 1461 : s64 moffset=0;
2971 1461 : ctx->config_timing = GF_TRUE;
2972 1461 : ctx->update_report = GF_TRUE;
2973 :
2974 : //if we have an edit list (due to track template) only providing media offset, trash it
2975 1461 : if (!gf_isom_get_edit_list_type(ctx->file, tkw->track_num, &moffset)) {
2976 1461 : if (!gf_sys_old_arch_compat()) {
2977 11 : gf_isom_remove_edits(ctx->file, tkw->track_num);
2978 : } else {
2979 : //old arch compat: if we had a simple edit list in source, keep dur and offset
2980 : //and avoid rewriting it when recomputing edit for b-frames
2981 : u64 etime, sdur;
2982 : GF_ISOEditType etype;
2983 1450 : gf_isom_get_edit(ctx->file, tkw->track_num, 1, &etime, &sdur, &moffset, &etype);
2984 1450 : if (!etime && sdur) {
2985 192 : tkw->imported_edit_sdur = sdur;
2986 192 : tkw->imported_edit_offset = moffset;
2987 : }
2988 : remove_edits = GF_TRUE;
2989 : }
2990 : }
2991 1461 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_DELAY);
2992 1461 : if (p) {
2993 : //media skip
2994 136 : if (p->value.longsint < 0) {
2995 : //if cmf2, remove edits and use negctss
2996 136 : if ((ctx->cmaf==MP4MX_CMAF_CMF2) && (tkw->stream_type==GF_STREAM_VISUAL)) {
2997 0 : ctx->ctmode = MP4MX_CT_NEGCTTS;
2998 0 : gf_isom_remove_edits(ctx->file, tkw->track_num);
2999 0 : use_negccts = GF_TRUE;
3000 : }
3001 136 : else if ((ctx->ctmode==MP4MX_CT_NEGCTTS) && (tkw->stream_type==GF_STREAM_VISUAL)) {
3002 : use_negccts = GF_TRUE;
3003 : } else {
3004 136 : if (remove_edits) {
3005 136 : gf_isom_remove_edits(ctx->file, tkw->track_num);
3006 : }
3007 136 : gf_isom_set_edit(ctx->file, tkw->track_num, 0, 0, -p->value.longsint, GF_ISOM_EDIT_NORMAL);
3008 : }
3009 : }
3010 : //media delay
3011 0 : else if (p->value.longsint > 0) {
3012 : //if cmaf (whether cmfc or cmf2), remove edits and add delay to tfdt
3013 0 : if (ctx->cmaf) {
3014 0 : gf_isom_remove_edits(ctx->file, tkw->track_num);
3015 0 : tkw->patch_tfdt = GF_TRUE;
3016 : } else {
3017 : s64 dur = p->value.longsint;
3018 0 : dur *= (u32) ctx->moovts;
3019 0 : dur /= tkw->src_timescale;
3020 0 : if (remove_edits) {
3021 0 : gf_isom_remove_edits(ctx->file, tkw->track_num);
3022 : }
3023 0 : gf_isom_set_edit(ctx->file, tkw->track_num, 0, dur, 0, GF_ISOM_EDIT_DWELL);
3024 0 : gf_isom_set_edit(ctx->file, tkw->track_num, dur, 0, 0, GF_ISOM_EDIT_NORMAL);
3025 : }
3026 : }
3027 136 : tkw->ts_delay = p->value.sint;
3028 1325 : } else if (tkw->stream_type==GF_STREAM_VISUAL) {
3029 824 : tkw->probe_min_ctts = GF_TRUE;
3030 : }
3031 136 : if (use_negccts) {
3032 0 : gf_isom_set_composition_offset_mode(ctx->file, tkw->track_num, GF_TRUE);
3033 :
3034 0 : if (!tkw->has_brands) {
3035 0 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISO4, GF_TRUE);
3036 0 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISOM, GF_FALSE);
3037 0 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISO1, GF_FALSE);
3038 0 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISO2, GF_FALSE);
3039 0 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISO3, GF_FALSE);
3040 : }
3041 :
3042 0 : tkw->negctts_shift = (tkw->ts_delay<0) ? -tkw->ts_delay : 0;
3043 : } else {
3044 : //this will remove any cslg in the track due to template
3045 1461 : gf_isom_set_composition_offset_mode(ctx->file, tkw->track_num, GF_FALSE);
3046 : }
3047 :
3048 1461 : mp4_mux_set_tags(ctx, tkw);
3049 : }
3050 : return GF_OK;
3051 : }
3052 :
3053 : static GF_Err mp4_mux_flush_fragmented(GF_Filter *filter, GF_MP4MuxCtx *ctx);
3054 : static GF_Err mp4_mux_done(GF_Filter *filter, GF_MP4MuxCtx *ctx, Bool is_final);
3055 :
3056 2778 : static GF_Err mp4_mux_configure_pid(GF_Filter *filter, GF_FilterPid *pid, Bool is_remove)
3057 : {
3058 2778 : GF_MP4MuxCtx *ctx = gf_filter_get_udta(filter);
3059 :
3060 2778 : if (is_remove) {
3061 11 : TrackWriter *tkw = gf_filter_pid_get_udta(pid);
3062 11 : if (tkw) {
3063 11 : gf_list_del_item(ctx->tracks, tkw);
3064 11 : gf_free(tkw);
3065 : }
3066 : //removing last pid
3067 11 : if (ctx->opid && !gf_list_count(ctx->tracks)) {
3068 9 : if (ctx->file) {
3069 : //non-frag file, flush file
3070 9 : if (!ctx->init_movie_done) {
3071 0 : mp4_mux_done(filter, ctx, GF_TRUE);
3072 : }
3073 : } else {
3074 0 : while (ctx->flush_size) {
3075 0 : GF_Err e = mp4_mux_flush_fragmented(filter, ctx);
3076 0 : if (e) break;
3077 : }
3078 : }
3079 : //delete output pid (to flush destruction of filter chain)
3080 9 : gf_filter_pid_remove(ctx->opid);
3081 9 : ctx->opid = NULL;
3082 : }
3083 : return GF_OK;
3084 : }
3085 2767 : return mp4_mux_setup_pid(filter, pid, GF_TRUE);
3086 : }
3087 :
3088 283757 : static Bool mp4_mux_process_event(GF_Filter *filter, const GF_FilterEvent *evt)
3089 : {
3090 283757 : GF_MP4MuxCtx *ctx = gf_filter_get_udta(filter);
3091 :
3092 283757 : if (evt->base.on_pid && (evt->base.type==GF_FEVT_INFO_UPDATE) ) {
3093 283068 : TrackWriter *tkw = gf_filter_pid_get_udta(evt->base.on_pid);
3094 283068 : if (tkw) {
3095 283068 : GF_PropertyEntry *pe=NULL;
3096 : const GF_PropertyValue *p;
3097 283068 : p = gf_filter_pid_get_info(tkw->ipid, GF_PROP_PID_DOWN_BYTES, &pe);
3098 283068 : if (p) tkw->down_bytes = p->value.longuint;
3099 :
3100 283068 : p = gf_filter_pid_get_info(tkw->ipid, GF_PROP_PID_DOWN_SIZE, &pe);
3101 283068 : if (p) tkw->down_size = p->value.longuint;
3102 :
3103 283068 : gf_filter_release_property(pe);
3104 : }
3105 :
3106 : return GF_FALSE;
3107 : }
3108 689 : if (!evt->base.on_pid && (evt->base.type==GF_FEVT_STOP) ) {
3109 0 : if (ctx->file && ctx->owns_mov) {
3110 0 : mp4_mux_done(filter, ctx, GF_TRUE);
3111 : }
3112 : }
3113 689 : if (evt->base.on_pid && (evt->base.type==GF_FEVT_PLAY) ) {
3114 : u32 i, count;
3115 : GF_FilterEvent anevt;
3116 663 : ctx->force_play = GF_TRUE;
3117 663 : if (evt->play.speed<0)
3118 1 : ctx->is_rewind = GF_TRUE;
3119 :
3120 663 : if (ctx->start == 0)
3121 : return GF_FALSE;
3122 :
3123 0 : count = gf_list_count(ctx->tracks);
3124 0 : for (i=0; i<count; i++) {
3125 0 : TrackWriter *tkw = gf_list_get(ctx->tracks, i);
3126 0 : if (tkw->fake_track) continue;
3127 :
3128 0 : anevt.play = evt->play;
3129 0 : gf_filter_pid_init_play_event(tkw->ipid, &anevt, ctx->start, 0, "MP4Mux");
3130 0 : if (anevt.play.start_range > 0)
3131 0 : tkw->wait_sap = GF_TRUE;
3132 :
3133 0 : gf_filter_pid_send_event(tkw->ipid, &anevt);
3134 : }
3135 : return GF_TRUE;
3136 : }
3137 : return GF_FALSE;
3138 : }
3139 :
3140 : enum
3141 : {
3142 : CENC_CONFIG=0,
3143 : CENC_ADD_NORMAL,
3144 : CENC_ADD_FRAG,
3145 : };
3146 :
3147 218 : static void mp4_mux_cenc_insert_pssh(GF_MP4MuxCtx *ctx, TrackWriter *tkw)
3148 : {
3149 : bin128 *keyIDs=NULL;
3150 : u32 max_keys = 0;
3151 : u32 i, nb_pssh;
3152 :
3153 : //set pssh
3154 218 : const GF_PropertyValue *p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_CENC_PSSH);
3155 218 : if (!p) return;
3156 :
3157 218 : if (!ctx->bs_r) ctx->bs_r = gf_bs_new(p->value.data.ptr, p->value.data.size, GF_BITSTREAM_READ);
3158 0 : else gf_bs_reassign_buffer(ctx->bs_r, p->value.data.ptr, p->value.data.size);
3159 :
3160 218 : nb_pssh = gf_bs_read_u32(ctx->bs_r);
3161 296 : for (i = 0; i < nb_pssh; i++) {
3162 : u32 mode;
3163 : bin128 sysID;
3164 : u32 j, kid_count, version=0;
3165 : char *data;
3166 : u32 len;
3167 :
3168 296 : gf_bs_read_data(ctx->bs_r, sysID, 16);
3169 296 : version = gf_bs_read_u32(ctx->bs_r);
3170 296 : kid_count = gf_bs_read_u32(ctx->bs_r);
3171 :
3172 296 : if (kid_count>=max_keys) {
3173 : max_keys = kid_count;
3174 296 : keyIDs = gf_realloc(keyIDs, sizeof(bin128)*max_keys);
3175 : }
3176 273 : for (j=0; j<kid_count; j++) {
3177 273 : gf_bs_read_data(ctx->bs_r, keyIDs[j], 16);
3178 : }
3179 296 : len = gf_bs_read_u32(ctx->bs_r);
3180 296 : data = p->value.data.ptr + gf_bs_get_position(ctx->bs_r);
3181 :
3182 296 : if (tkw->is_item) mode = 2;
3183 281 : else if (tkw->scheme_type==GF_ISOM_PIFF_SCHEME) mode = 1;
3184 : else mode = 0;
3185 :
3186 296 : gf_cenc_set_pssh(ctx->file, sysID, version, kid_count, keyIDs, data, len, mode);
3187 296 : gf_bs_skip_bytes(ctx->bs_r, len);
3188 : }
3189 218 : if (keyIDs) gf_free(keyIDs);
3190 : }
3191 :
3192 41855 : static GF_Err mp4_mux_cenc_update(GF_MP4MuxCtx *ctx, TrackWriter *tkw, GF_FilterPacket *pck, u32 act_type, u32 pck_size)
3193 : {
3194 : const GF_PropertyValue *p;
3195 : GF_Err e;
3196 : Bool pck_is_encrypted;
3197 : u32 skip_byte_block=0, crypt_byte_block=0;
3198 : u32 IV_size=0;
3199 : u32 scheme_type=0;
3200 : u32 scheme_version=0;
3201 : u8 *fake_sai = NULL;
3202 : u8 *sai = NULL;
3203 : u32 sai_size = 0;
3204 : Bool needs_seig = GF_FALSE;
3205 : u32 sample_num;
3206 :
3207 41855 : if (tkw->cenc_state == CENC_SETUP_ERROR)
3208 : return GF_SERVICE_ERROR;
3209 :
3210 41855 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_CENC_PATTERN);
3211 41855 : if (p) {
3212 8697 : skip_byte_block = p->value.frac.num;
3213 8697 : crypt_byte_block = p->value.frac.den;
3214 : }
3215 :
3216 41855 : if (pck) {
3217 41854 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_CENC_SAI);
3218 41854 : if (p) {
3219 36201 : sai = p->value.data.ptr;
3220 36201 : sai_size = p->value.data.size;
3221 : }
3222 : }
3223 :
3224 :
3225 41855 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_PROTECTION_SCHEME_TYPE);
3226 41855 : if (p) scheme_type = p->value.uint;
3227 41855 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_PROTECTION_SCHEME_VERSION);
3228 41855 : if (p) scheme_version = p->value.uint;
3229 :
3230 : //initial setup
3231 41855 : if (tkw->cenc_state==CENC_NEED_SETUP) {
3232 : u32 cenc_stsd_mode=0;
3233 : u32 container_type = GF_ISOM_BOX_TYPE_SENC;
3234 :
3235 208 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_CENC_STSD_MODE);
3236 208 : if (p) cenc_stsd_mode = p->value.uint;
3237 :
3238 208 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_ENCRYPTED);
3239 208 : if (p) pck_is_encrypted = p->value.boolean;
3240 : else pck_is_encrypted = GF_FALSE;
3241 :
3242 :
3243 208 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_CENC_STORE);
3244 208 : if (p && p->value.uint) container_type = p->value.uint;
3245 :
3246 208 : tkw->clear_stsd_idx = 0;
3247 208 : if (cenc_stsd_mode) {
3248 : u32 clone_stsd_idx;
3249 6 : e = gf_isom_clone_sample_description(ctx->file, tkw->track_num, ctx->file, tkw->track_num, tkw->stsd_idx, NULL, NULL, &clone_stsd_idx);
3250 6 : if (e) {
3251 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to clone sample description: %s\n", gf_error_to_string(e) ));
3252 0 : return e;
3253 : }
3254 : //before
3255 6 : if (cenc_stsd_mode==1) {
3256 2 : tkw->clear_stsd_idx = tkw->stsd_idx;
3257 2 : tkw->stsd_idx = clone_stsd_idx;
3258 : }
3259 : //after
3260 : else {
3261 4 : tkw->clear_stsd_idx = clone_stsd_idx;
3262 : }
3263 : }
3264 :
3265 208 : tkw->cenc_state = CENC_SETUP_DONE;
3266 208 : tkw->def_cenc_key_info_crc = tkw->cenc_key_info_crc;
3267 208 : e = gf_isom_set_cenc_protection(ctx->file, tkw->track_num, tkw->stsd_idx, scheme_type, scheme_version, pck_is_encrypted, crypt_byte_block, skip_byte_block, tkw->cenc_ki->value.data.ptr, tkw->cenc_ki->value.data.size);
3268 :
3269 208 : if (e) {
3270 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to setup CENC information: %s\n", gf_error_to_string(e) ));
3271 0 : tkw->cenc_state = CENC_SETUP_ERROR;
3272 : return e;
3273 : }
3274 :
3275 208 : if (ctx->psshs == MP4MX_PSSH_MOOV)
3276 208 : mp4_mux_cenc_insert_pssh(ctx, tkw);
3277 :
3278 208 : tkw->def_crypt_byte_block = crypt_byte_block;
3279 208 : tkw->def_skip_byte_block = skip_byte_block;
3280 :
3281 208 : if (!tkw->has_brands && (scheme_type==GF_ISOM_OMADRM_SCHEME))
3282 0 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_OPF2, GF_TRUE);
3283 :
3284 208 : if (container_type) {
3285 208 : if (container_type==GF_ISOM_BOX_UUID_PSEC) {
3286 12 : e = gf_isom_piff_allocate_storage(ctx->file, tkw->track_num, 0, 0, NULL);
3287 : } else {
3288 196 : e = gf_isom_cenc_allocate_storage(ctx->file, tkw->track_num);
3289 : }
3290 208 : if (e) {
3291 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to setup CENC storage: %s\n", gf_error_to_string(e) ));
3292 0 : tkw->cenc_state = CENC_SETUP_ERROR;
3293 : return e;
3294 : }
3295 : }
3296 : }
3297 41855 : if (act_type==CENC_CONFIG) return GF_OK;
3298 :
3299 : pck_is_encrypted = GF_FALSE;
3300 41647 : if (pck)
3301 41647 : pck_is_encrypted = gf_filter_pck_get_crypt_flags(pck);
3302 :
3303 : //!! tkw->nb_samples / tkw->samples_in_frag not yet incremented !!
3304 41647 : if (act_type == CENC_ADD_FRAG) {
3305 20396 : sample_num = tkw->samples_in_frag + 1;
3306 :
3307 20396 : if (ctx->cmaf) {
3308 0 : if (!tkw->samples_in_frag) {
3309 0 : tkw->cenc_frag_protected = pck_is_encrypted;
3310 : } else {
3311 0 : if (tkw->cenc_frag_protected != pck_is_encrypted) {
3312 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] CMAF forbids mixing protected and unprotected samples in a single fragment, please re-encrypt or change target segment duration\n"));
3313 : return GF_NON_COMPLIANT_BITSTREAM;
3314 : }
3315 : }
3316 : }
3317 : } else {
3318 21251 : sample_num = tkw->nb_samples + 1;
3319 : }
3320 41647 : if (!pck_is_encrypted) {
3321 4983 : if (tkw->clear_stsd_idx) {
3322 546 : if (act_type==CENC_ADD_FRAG) {
3323 273 : return gf_isom_fragment_set_cenc_sai(ctx->file, tkw->track_id, NULL, 0, GF_FALSE, ctx->saio32, tkw->cenc_multikey);
3324 : } else {
3325 273 : return gf_isom_track_cenc_add_sample_info(ctx->file, tkw->track_num, GF_ISOM_BOX_TYPE_SENC, NULL, 0, tkw->cenc_subsamples, ctx->saio32, tkw->cenc_multikey);
3326 : }
3327 : } else {
3328 : char dumb_key[20];
3329 : memset(dumb_key, 0, 20); //dumb key, IV size 0, not protected
3330 4437 : e = gf_isom_set_sample_cenc_group(ctx->file, tkw->track_num, sample_num, GF_FALSE, 0, 0, dumb_key, 20);
3331 : IV_size = 0;
3332 4437 : tkw->has_seig = GF_TRUE;
3333 : }
3334 : } else {
3335 :
3336 : e = GF_OK;
3337 : //multikey ALWAYS uses seig
3338 36664 : if (tkw->cenc_ki->value.data.ptr[0])
3339 : needs_seig = GF_TRUE;
3340 34414 : else if (tkw->def_crypt_byte_block != crypt_byte_block)
3341 : needs_seig = GF_TRUE;
3342 34414 : else if (tkw->def_skip_byte_block != skip_byte_block)
3343 : needs_seig = GF_TRUE;
3344 34414 : else if (tkw->def_cenc_key_info_crc != tkw->cenc_key_info_crc)
3345 : needs_seig = GF_TRUE;
3346 :
3347 : if (needs_seig) {
3348 4102 : e = gf_isom_set_sample_cenc_group(ctx->file, tkw->track_num, sample_num, 1, crypt_byte_block, skip_byte_block, tkw->cenc_ki->value.data.ptr, tkw->cenc_ki->value.data.size);
3349 4102 : tkw->has_seig = GF_TRUE;
3350 32562 : } else if (tkw->has_seig) {
3351 690 : e = gf_isom_set_sample_cenc_default_group(ctx->file, tkw->track_num, sample_num);
3352 : }
3353 : }
3354 9229 : if (e) {
3355 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to set sample encryption group entry: %s)\n", gf_error_to_string(e) ));
3356 : return e;
3357 : }
3358 :
3359 41101 : if (!sai) {
3360 5098 : if (tkw->constant_IV_size && !tkw->cenc_subsamples)
3361 : return GF_OK;
3362 :
3363 : if (IV_size) {
3364 : //generate clear SAI data with a non-0 IV
3365 : u32 olen = pck_size;
3366 : GF_BitStream *bs = gf_bs_new(NULL, 9, GF_BITSTREAM_WRITE);
3367 : if (tkw->cenc_multikey) {
3368 : gf_bs_write_u16(bs, 0);
3369 : } else {
3370 : gf_bs_write_long_int(bs, 0, IV_size*8);
3371 : }
3372 :
3373 : if (tkw->cenc_subsamples) {
3374 : u32 i;
3375 : u32 subsample_count = 1;
3376 : while (olen>0xFFFF) {
3377 : olen -= 0xFFFF;
3378 : subsample_count ++;
3379 : }
3380 : gf_bs_write_u16(bs, subsample_count);
3381 : olen = pck_size;
3382 : for (i = 0; i < subsample_count; i++) {
3383 : u32 clear_size;
3384 : if (olen<0xFFFF) {
3385 : clear_size = olen;
3386 : } else {
3387 : clear_size = 0xFFFF;
3388 : olen -= 0xFFFF;
3389 : }
3390 :
3391 : if (tkw->cenc_multikey)
3392 : gf_bs_write_u16(bs, 0);
3393 : gf_bs_write_u16(bs, clear_size);
3394 : gf_bs_write_u32(bs, 0);
3395 : }
3396 : }
3397 : gf_bs_get_content(bs, &fake_sai, &sai_size);
3398 : gf_bs_del(bs);
3399 : sai = fake_sai;
3400 : }
3401 : }
3402 :
3403 41101 : if (act_type==CENC_ADD_FRAG) {
3404 20123 : if (pck_is_encrypted) {
3405 17990 : e = gf_isom_fragment_set_cenc_sai(ctx->file, tkw->track_id, sai, sai_size, tkw->cenc_subsamples, ctx->saio32, tkw->cenc_multikey);
3406 : } else {
3407 2133 : e = gf_isom_fragment_set_cenc_sai(ctx->file, tkw->track_id, NULL, 0, GF_FALSE, ctx->saio32, tkw->cenc_multikey);
3408 : }
3409 : } else {
3410 20978 : if (sai) {
3411 18344 : e = gf_isom_track_cenc_add_sample_info(ctx->file, tkw->track_num, GF_ISOM_BOX_TYPE_SENC, sai, sai_size, tkw->cenc_subsamples, ctx->saio32, tkw->cenc_multikey);
3412 2634 : } else if (!pck_is_encrypted) {
3413 2304 : e = gf_isom_track_cenc_add_sample_info(ctx->file, tkw->track_num, GF_ISOM_BOX_TYPE_SENC, NULL, 0, tkw->cenc_subsamples, ctx->saio32, tkw->cenc_multikey);
3414 : }
3415 : }
3416 : if (fake_sai) gf_free(fake_sai);
3417 : return e;
3418 : }
3419 :
3420 0 : GF_FilterSAPType mp4_mux_get_sap(GF_MP4MuxCtx *ctx, GF_FilterPacket *pck)
3421 : {
3422 562170 : GF_FilterSAPType sap = gf_filter_pck_get_sap(pck);
3423 562170 : if (!sap) return sap;
3424 196556 : if (ctx->forcesync) return GF_FILTER_SAP_1;
3425 0 : return sap;
3426 : }
3427 :
3428 562238 : static GF_Err mp4_mux_process_sample(GF_MP4MuxCtx *ctx, TrackWriter *tkw, GF_FilterPacket *pck, Bool for_fragment)
3429 : {
3430 : GF_Err e=GF_OK;
3431 : u64 cts, prev_dts;
3432 : u32 prev_size=0;
3433 : u32 duration = 0;
3434 : u32 timescale = 0;
3435 : const GF_PropertyValue *subs;
3436 : GF_FilterSAPType sap_type;
3437 : u32 insert_subsample_dsi_size = 0;
3438 : u32 first_nal_is_audelim = GF_FALSE;
3439 562238 : u32 sample_desc_index = tkw->stsd_idx;
3440 :
3441 562238 : timescale = gf_filter_pck_get_timescale(pck);
3442 :
3443 562238 : prev_dts = tkw->nb_samples ? tkw->sample.DTS : GF_FILTER_NO_TS;
3444 562238 : prev_size = tkw->sample.dataLength;
3445 562238 : tkw->sample.CTS_Offset = 0;
3446 562238 : tkw->sample.data = (char *)gf_filter_pck_get_data(pck, &tkw->sample.dataLength);
3447 :
3448 562238 : ctx->update_report = GF_TRUE;
3449 562238 : ctx->total_bytes_in += tkw->sample.dataLength;
3450 562238 : ctx->total_samples++;
3451 :
3452 562238 : tkw->sample.DTS = gf_filter_pck_get_dts(pck);
3453 562238 : cts = gf_filter_pck_get_cts(pck);
3454 :
3455 562238 : if (tkw->sample.DTS == GF_FILTER_NO_TS) {
3456 0 : if (cts == GF_FILTER_NO_TS) {
3457 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Sample with no DTS/CTS, cannot add (last DTS "LLU", last size %d)\n", prev_dts, prev_size ));
3458 : return GF_NON_COMPLIANT_BITSTREAM;
3459 : } else {
3460 0 : u32 min_pck_dur = gf_filter_pid_get_min_pck_duration(tkw->ipid);
3461 0 : if (min_pck_dur) {
3462 0 : tkw->sample.DTS = prev_dts;
3463 0 : if (timescale != tkw->tk_timescale) {
3464 0 : tkw->sample.DTS *= timescale;
3465 0 : tkw->sample.DTS /= tkw->tk_timescale;
3466 : }
3467 0 : tkw->sample.DTS += min_pck_dur;
3468 : } else {
3469 0 : tkw->sample.DTS = cts;
3470 : }
3471 : }
3472 : } else {
3473 562238 : tkw->sample.CTS_Offset = (s32) ((s64) cts - (s64) tkw->sample.DTS);
3474 : }
3475 :
3476 : //tkw->ts_shift is in source timescale, apply it before rescaling TSs/duration
3477 562238 : if (tkw->ts_shift) {
3478 17980 : if (ctx->is_rewind) {
3479 30 : if (tkw->sample.DTS <= tkw->ts_shift) {
3480 30 : tkw->sample.DTS = tkw->ts_shift - tkw->sample.DTS;
3481 30 : cts = tkw->ts_shift - cts;
3482 : } else {
3483 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] broken timing in track, initial ts "LLU" less than TS "LLU"\n", tkw->ts_shift, tkw->sample.DTS));
3484 : }
3485 : } else {
3486 17950 : if (tkw->sample.DTS >= tkw->ts_shift) {
3487 17950 : tkw->sample.DTS -= tkw->ts_shift;
3488 17950 : cts -= tkw->ts_shift;
3489 : } else {
3490 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] broken timing in track, initial ts "LLU" greater than TS "LLU"\n", tkw->ts_shift, tkw->sample.DTS));
3491 : }
3492 : }
3493 : }
3494 :
3495 : //sample-accurate seek info, start logging min CTS of packets marked as non-sync
3496 562238 : if (tkw->check_seek_ts && !gf_filter_pck_get_seek_flag(pck)) {
3497 : u64 ts_check = cts;
3498 20 : subs = gf_filter_pck_get_property(pck, GF_PROP_PCK_SKIP_BEGIN);
3499 20 : if (subs)
3500 8 : ts_check += subs->value.uint;
3501 :
3502 20 : if (!tkw->min_ts_seek_plus_one) {
3503 6 : tkw->min_ts_seek_plus_one = ts_check + 1;
3504 14 : } else if (tkw->min_ts_seek_plus_one > ts_check + 1) {
3505 4 : tkw->min_ts_seek_plus_one = ts_check + 1;
3506 : } else {
3507 : //TS is greater than last non-seek packet TS, we're done seeking
3508 10 : tkw->check_seek_ts = GF_FALSE;
3509 : }
3510 : }
3511 :
3512 562238 : duration = gf_filter_pck_get_duration(pck);
3513 562238 : if (timescale != tkw->tk_timescale) {
3514 : s64 ctso;
3515 92 : tkw->sample.DTS *= tkw->tk_timescale;
3516 92 : tkw->sample.DTS /= timescale;
3517 92 : ctso = (s64) tkw->sample.CTS_Offset;
3518 92 : ctso *= tkw->tk_timescale;
3519 92 : ctso /= timescale;
3520 92 : tkw->sample.CTS_Offset = (s32) ctso;
3521 92 : duration *= tkw->tk_timescale;
3522 92 : duration /= timescale;
3523 : }
3524 :
3525 :
3526 562238 : tkw->sample.IsRAP = 0;
3527 562238 : if (tkw->codecid==GF_CODECID_RAW) {
3528 : sap_type = GF_FILTER_SAP_1;
3529 : } else {
3530 : sap_type = mp4_mux_get_sap(ctx, pck);
3531 : }
3532 562118 : if (sap_type==GF_FILTER_SAP_1)
3533 195683 : tkw->sample.IsRAP = SAP_TYPE_1;
3534 366555 : else if ( (sap_type == GF_FILTER_SAP_4) && (tkw->stream_type != GF_STREAM_VISUAL) )
3535 0 : tkw->sample.IsRAP = SAP_TYPE_1;
3536 :
3537 : /*RFC8216bis is not clear here:
3538 : "if the Partial Segment contains an independent frame."
3539 : -> this would allow SAP1,2,3 (independent being only defined for segments)
3540 :
3541 : but
3542 :
3543 : "Partial Segment containing an independent frame SHOULD carry it to increase the efficiency with which clients can join and switch Renditions"
3544 : -> if used for switching, this only allows SAP 1 and 2
3545 :
3546 : Spec should be fixed to allow for both cases (fast tune-in or in-segment switchingà )
3547 : */
3548 562238 : if ((tkw->sample.IsRAP == SAP_TYPE_1) || (tkw->sample.IsRAP == SAP_TYPE_2))
3549 195683 : ctx->frag_has_intra = GF_TRUE;
3550 :
3551 562238 : tkw->sample.DTS += tkw->dts_patch;
3552 562238 : if (tkw->nb_samples && (prev_dts >= tkw->sample.DTS) ) {
3553 : //the fragmented API will patch the duration on the fly
3554 7 : if (!for_fragment) {
3555 4 : gf_isom_patch_last_sample_duration(ctx->file, tkw->track_num, prev_dts ? prev_dts : 1);
3556 : }
3557 7 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] PID %s ID %d Sample %d with DTS "LLU" less than previous sample DTS "LLU", adjusting prev sample duration\n", gf_filter_pid_get_name(tkw->ipid), tkw->track_id, tkw->nb_samples+1, tkw->sample.DTS, prev_dts ));
3558 :
3559 7 : if (prev_dts) {
3560 7 : tkw->dts_patch = prev_dts - tkw->sample.DTS;
3561 7 : tkw->sample.DTS += tkw->dts_patch;
3562 : } else {
3563 0 : tkw->sample.DTS += 1;
3564 0 : if (tkw->sample.CTS_Offset) tkw->sample.CTS_Offset -= 1;
3565 0 : duration-=1;
3566 : }
3567 : }
3568 :
3569 :
3570 562238 : if (tkw->negctts_shift)
3571 0 : tkw->sample.CTS_Offset -= tkw->negctts_shift;
3572 :
3573 562238 : if (tkw->probe_min_ctts) {
3574 334953 : s32 diff = (s32) ((s64) cts - (s64) tkw->sample.DTS);
3575 334953 : if (diff < tkw->min_neg_ctts)
3576 246 : tkw->min_neg_ctts = diff;
3577 : }
3578 562238 : if (tkw->sample.CTS_Offset) tkw->has_ctts = GF_TRUE;
3579 :
3580 562238 : if (tkw->sample.CTS_Offset < tkw->min_neg_ctts)
3581 0 : tkw->min_neg_ctts = tkw->sample.CTS_Offset;
3582 :
3583 562238 : tkw->sample.nb_pack = 0;
3584 562238 : if (tkw->raw_audio_bytes_per_sample) {
3585 90 : tkw->sample.nb_pack = tkw->sample.dataLength / tkw->raw_audio_bytes_per_sample;
3586 90 : if (tkw->sample.nb_pack) {
3587 : duration = 1;
3588 90 : if (tkw->raw_samplerate && (tkw->tk_timescale != tkw->raw_samplerate)) {
3589 : duration *= tkw->tk_timescale;
3590 0 : duration /= tkw->raw_samplerate;
3591 : }
3592 : }
3593 : }
3594 :
3595 562238 : if (tkw->cenc_state && tkw->clear_stsd_idx && !gf_filter_pck_get_crypt_flags(pck)) {
3596 546 : sample_desc_index = tkw->clear_stsd_idx;
3597 : }
3598 :
3599 562238 : if (tkw->use_dref) {
3600 0 : u64 data_offset = gf_filter_pck_get_byte_offset(pck);
3601 0 : if (data_offset != GF_FILTER_NO_BO) {
3602 0 : e = gf_isom_add_sample_reference(ctx->file, tkw->track_num, sample_desc_index, &tkw->sample, data_offset);
3603 0 : if (e) {
3604 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to add sample DTS "LLU" as reference: %s\n", tkw->sample.DTS, gf_error_to_string(e) ));
3605 : }
3606 : } else {
3607 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Cannot add sample reference at DTS "LLU" , input sample data is not continous in source\n", tkw->sample.DTS ));
3608 : }
3609 562238 : } else if (tkw->nb_frames_per_sample && (tkw->nb_samples % tkw->nb_frames_per_sample)) {
3610 0 : if (for_fragment) {
3611 0 : e = gf_isom_fragment_append_data(ctx->file, tkw->track_id, tkw->sample.data, tkw->sample.dataLength, 0);
3612 : } else {
3613 0 : e = gf_isom_append_sample_data(ctx->file, tkw->track_num, tkw->sample.data, tkw->sample.dataLength);
3614 : }
3615 0 : tkw->has_append = GF_TRUE;
3616 0 : if (e) {
3617 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to append sample DTS "LLU" data: %s\n", tkw->sample.DTS, gf_error_to_string(e) ));
3618 : }
3619 : } else {
3620 562238 : if ((tkw->sample.IsRAP || tkw->force_inband_inject || ctx->pps_inband) && ctx->xps_inband) {
3621 : u8 *inband_xps;
3622 : u32 inband_xps_size;
3623 : char *au_delim=NULL;
3624 : u32 au_delim_size=0;
3625 202 : char *pck_data = tkw->sample.data;
3626 202 : u32 pck_data_len = tkw->sample.dataLength;
3627 202 : if (tkw->sample.IsRAP || tkw->force_inband_inject) {
3628 202 : inband_xps = tkw->inband_hdr;
3629 202 : inband_xps_size = tkw->inband_hdr_size;
3630 202 : tkw->force_inband_inject = GF_FALSE;
3631 : } else {
3632 0 : inband_xps = tkw->inband_hdr_non_rap;
3633 0 : inband_xps_size = tkw->inband_hdr_non_rap_size;
3634 : }
3635 202 : tkw->sample.data = inband_xps;
3636 202 : tkw->sample.dataLength = inband_xps_size;
3637 :
3638 202 : if (tkw->is_nalu==NALU_AVC) {
3639 30 : char *nal = pck_data + tkw->nal_unit_size;
3640 30 : if ((nal[0] & 0x1F) == GF_AVC_NALU_ACCESS_UNIT) {
3641 2 : first_nal_is_audelim = au_delim_size = 2 + tkw->nal_unit_size;
3642 : au_delim = pck_data;
3643 : }
3644 : } else {
3645 172 : char *nal = pck_data + tkw->nal_unit_size;
3646 172 : if (((nal[0] & 0x7E)>>1) == GF_HEVC_NALU_ACCESS_UNIT) {
3647 11 : first_nal_is_audelim = au_delim_size = 3 + tkw->nal_unit_size;
3648 : au_delim = pck_data;
3649 : }
3650 : }
3651 :
3652 202 : if (au_delim) {
3653 13 : tkw->sample.data = au_delim;
3654 13 : tkw->sample.dataLength = au_delim_size;
3655 13 : pck_data += au_delim_size;
3656 13 : pck_data_len -= au_delim_size;
3657 : }
3658 :
3659 202 : if (for_fragment) {
3660 16 : e = gf_isom_fragment_add_sample(ctx->file, tkw->track_id, &tkw->sample, sample_desc_index, duration, 0, 0, 0);
3661 16 : if (!e && au_delim) {
3662 0 : e = gf_isom_fragment_append_data(ctx->file, tkw->track_id, inband_xps, inband_xps_size, 0);
3663 : }
3664 16 : if (!e) e = gf_isom_fragment_append_data(ctx->file, tkw->track_id, pck_data, pck_data_len, 0);
3665 : } else {
3666 186 : e = gf_isom_add_sample(ctx->file, tkw->track_num, sample_desc_index, &tkw->sample);
3667 186 : if (au_delim && !e) {
3668 13 : e = gf_isom_append_sample_data(ctx->file, tkw->track_num, inband_xps, inband_xps_size);
3669 : }
3670 186 : if (!e) e = gf_isom_append_sample_data(ctx->file, tkw->track_num, pck_data, pck_data_len);
3671 : }
3672 : insert_subsample_dsi_size = inband_xps_size;
3673 562036 : } else if (for_fragment) {
3674 111898 : e = gf_isom_fragment_add_sample(ctx->file, tkw->track_id, &tkw->sample, sample_desc_index, duration, 0, 0, 0);
3675 : } else {
3676 450138 : e = gf_isom_add_sample(ctx->file, tkw->track_num, sample_desc_index, &tkw->sample);
3677 450138 : if (!e && !duration) {
3678 190 : gf_isom_set_last_sample_duration(ctx->file, tkw->track_num, 0);
3679 : }
3680 : }
3681 :
3682 562238 : if (e) {
3683 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to add sample DTS "LLU" - prev DTS "LLU": %s\n", tkw->sample.DTS, prev_dts, gf_error_to_string(e) ));
3684 : } else {
3685 562238 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CONTAINER, ("[MP4Mux] added sample DTS "LLU" - prev DTS "LLU" - prev size %d\n", tkw->sample.DTS, prev_dts, prev_size));
3686 : }
3687 :
3688 562238 : if (!e && tkw->cenc_state) {
3689 41647 : e = mp4_mux_cenc_update(ctx, tkw, pck, for_fragment ? CENC_ADD_FRAG : CENC_ADD_NORMAL, tkw->sample.dataLength);
3690 41647 : if (e) {
3691 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to set sample CENC information: %s\n", gf_error_to_string(e) ));
3692 : return e;
3693 : }
3694 : }
3695 : }
3696 :
3697 562238 : tkw->nb_samples++;
3698 562238 : tkw->samples_in_stsd++;
3699 562238 : tkw->samples_in_frag++;
3700 :
3701 562238 : if (e) return e;
3702 :
3703 562238 : if (!for_fragment) {
3704 : u64 samp_cts;
3705 450324 : if (!tkw->clamp_ts_plus_one) {
3706 450324 : const GF_PropertyValue *skp = gf_filter_pck_get_property(pck, GF_PROP_PCK_SKIP_PRES);
3707 450324 : if (skp && skp->value.boolean) {
3708 1 : tkw->clamp_ts_plus_one = 1 + tkw->sample.DTS + tkw->sample.CTS_Offset;
3709 : }
3710 : }
3711 : //store min max cts for edit list updates
3712 450324 : samp_cts = tkw->sample.DTS + tkw->sample.CTS_Offset;
3713 450324 : if (!tkw->clamp_ts_plus_one || (samp_cts + 1 < tkw->clamp_ts_plus_one)) {
3714 450323 : if (samp_cts > tkw->max_cts) {
3715 355637 : tkw->max_cts = samp_cts;
3716 355637 : tkw->max_cts_samp_dur = duration;
3717 : }
3718 :
3719 450323 : if (tkw->min_cts > samp_cts)
3720 1056 : tkw->min_cts = samp_cts;
3721 : }
3722 : }
3723 :
3724 : //compat with old arch: write sample to group info for all samples
3725 562238 : if ((sap_type==3) || tkw->has_open_gop) {
3726 22565 : if (for_fragment) {
3727 8111 : e = gf_isom_fragment_set_sample_rap_group(ctx->file, tkw->track_id, tkw->samples_in_frag, (sap_type==3) ? GF_TRUE : GF_FALSE, 0);
3728 14454 : } else if (sap_type==3) {
3729 479 : e = gf_isom_set_sample_rap_group(ctx->file, tkw->track_num, tkw->nb_samples, GF_TRUE /*(sap_type==3) ? GF_TRUE : GF_FALSE*/, 0);
3730 : }
3731 22565 : if (e) {
3732 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to set sample DTS "LLU" SAP 3 in RAP group: %s\n", tkw->sample.DTS, gf_error_to_string(e) ));
3733 : }
3734 22565 : tkw->has_open_gop = GF_TRUE;
3735 : }
3736 562238 : if (!ctx->noroll) {
3737 562238 : if ((sap_type==GF_FILTER_SAP_4) || (sap_type==GF_FILTER_SAP_4_PROL) || tkw->gdr_type) {
3738 : GF_ISOSampleRollType roll_type = 0;
3739 1484 : s16 roll = gf_filter_pck_get_roll_info(pck);
3740 1484 : if (sap_type==GF_FILTER_SAP_4) roll_type = GF_ISOM_SAMPLE_ROLL;
3741 1298 : else if (sap_type==GF_FILTER_SAP_4_PROL) roll_type = GF_ISOM_SAMPLE_PREROLL;
3742 1298 : else if (tkw->gdr_type==GF_FILTER_SAP_4_PROL) {
3743 : roll_type = GF_ISOM_SAMPLE_PREROLL_NONE;
3744 : }
3745 :
3746 1484 : if (for_fragment) {
3747 742 : e = gf_isom_fragment_set_sample_roll_group(ctx->file, tkw->track_id, tkw->samples_in_frag, roll_type, roll);
3748 : } else {
3749 742 : e = gf_isom_set_sample_roll_group(ctx->file, tkw->track_num, tkw->nb_samples, roll_type, roll);
3750 : }
3751 1484 : if (e) {
3752 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Failed to set sample DTS "LLU" SAP 4 roll %s in roll group: %s\n", tkw->sample.DTS, roll, gf_error_to_string(e) ));
3753 : }
3754 1484 : if (sap_type && !tkw->gdr_type)
3755 2 : tkw->gdr_type = sap_type;
3756 : }
3757 : }
3758 :
3759 562238 : subs = gf_filter_pck_get_property(pck, GF_PROP_PCK_SUBS);
3760 562238 : if (subs) {
3761 : //if no AUDelim nal and inband header injection, push new subsample
3762 1532 : if (!first_nal_is_audelim && insert_subsample_dsi_size) {
3763 0 : if (for_fragment) {
3764 0 : gf_isom_fragment_add_subsample(ctx->file, tkw->track_id, 0, insert_subsample_dsi_size, 0, 0, 0);
3765 : } else {
3766 0 : gf_isom_add_subsample(ctx->file, tkw->track_num, tkw->nb_samples, 0, insert_subsample_dsi_size, 0, 0, 0);
3767 : }
3768 : insert_subsample_dsi_size = 0;
3769 : }
3770 1532 : tkw->has_subs = GF_TRUE;
3771 :
3772 1532 : if (!ctx->bs_r) ctx->bs_r = gf_bs_new(subs->value.data.ptr, subs->value.data.size, GF_BITSTREAM_READ);
3773 1527 : else gf_bs_reassign_buffer(ctx->bs_r, subs->value.data.ptr, subs->value.data.size);
3774 :
3775 4598 : while (gf_bs_available(ctx->bs_r)) {
3776 3066 : u32 flags = gf_bs_read_u32(ctx->bs_r);
3777 3066 : u32 subs_size = gf_bs_read_u32(ctx->bs_r);
3778 3066 : u32 reserved = gf_bs_read_u32(ctx->bs_r);
3779 3066 : u8 priority = gf_bs_read_u8(ctx->bs_r);
3780 3066 : u8 discardable = gf_bs_read_u8(ctx->bs_r);
3781 :
3782 3066 : if (for_fragment) {
3783 1523 : gf_isom_fragment_add_subsample(ctx->file, tkw->track_id, flags, subs_size, priority, reserved, discardable);
3784 : } else {
3785 1543 : gf_isom_add_subsample(ctx->file, tkw->track_num, tkw->nb_samples, flags, subs_size, priority, reserved, discardable);
3786 : }
3787 :
3788 : //we have AUDelim nal and inband header injection, push new subsample for inband header once we have pushed the first subsample (au delim)
3789 3066 : if (insert_subsample_dsi_size) {
3790 0 : if (first_nal_is_audelim != subs_size) {
3791 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] inserting inband param after AU delimiter NALU, but sample has subsample information not aligned on NALU (got %d subsample size but expecting %d) - file might be broken!\n", subs_size, first_nal_is_audelim));
3792 : }
3793 0 : if (for_fragment) {
3794 0 : gf_isom_fragment_add_subsample(ctx->file, tkw->track_id, 0, insert_subsample_dsi_size, 0, 0, 0);
3795 : } else {
3796 0 : gf_isom_add_subsample(ctx->file, tkw->track_num, tkw->nb_samples, 0, insert_subsample_dsi_size, 0, 0, 0);
3797 : }
3798 : insert_subsample_dsi_size = GF_FALSE;
3799 : }
3800 : }
3801 560706 : } else if (for_fragment && tkw->has_subs && ctx->cmaf && (tkw->codecid==GF_CODECID_SUBS_XML)) {
3802 : //tentative implemntation of CMAF 7.5.20 which is just nonsense text !!:
3803 : //"the value of subsample_count shall equal 1 for the first image sub-sample, and the subsample_count of the TTML document shall equal 0."
3804 : //
3805 : //we simply signal a single subsample
3806 0 : gf_isom_fragment_add_subsample(ctx->file, tkw->track_id, 0, tkw->sample.dataLength, 0, 0, 0);
3807 : }
3808 :
3809 562238 : if (ctx->deps) {
3810 562238 : u8 dep_flags = gf_filter_pck_get_dependency_flags(pck);
3811 562238 : if (dep_flags) {
3812 4921 : u32 is_leading = (dep_flags>>6) & 0x3;
3813 4921 : u32 depends_on = (dep_flags>>4) & 0x3;
3814 4921 : u32 depended_on = (dep_flags>>2) & 0x3;
3815 4921 : u32 redundant = (dep_flags) & 0x3;
3816 4921 : if (for_fragment) {
3817 1575 : gf_isom_fragment_set_sample_flags(ctx->file, tkw->track_id, is_leading, depends_on, depended_on, redundant);
3818 : } else {
3819 3346 : gf_isom_set_sample_flags(ctx->file, tkw->track_num, tkw->nb_samples, is_leading, depends_on, depended_on, redundant);
3820 : }
3821 : }
3822 : }
3823 :
3824 :
3825 562238 : tkw->next_is_first_sample = GF_FALSE;
3826 :
3827 562238 : if (duration && !for_fragment && !tkw->raw_audio_bytes_per_sample)
3828 450044 : gf_isom_set_last_sample_duration(ctx->file, tkw->track_num, duration);
3829 :
3830 562238 : if (ctx->idur.num) {
3831 : Bool abort = GF_FALSE;
3832 8983 : if (ctx->idur.num>0) {
3833 8982 : u64 mdur = gf_isom_get_media_duration(ctx->file, tkw->track_num);
3834 :
3835 : /*patch to align to old arch */
3836 8982 : if (gf_sys_old_arch_compat() && (tkw->stream_type==GF_STREAM_VISUAL)) {
3837 3862 : mdur = tkw->sample.DTS;
3838 : }
3839 :
3840 8982 : if (ctx->importer) {
3841 8982 : tkw->prog_done = mdur * ctx->idur.den;
3842 8982 : tkw->prog_total = ((u64)tkw->tk_timescale) * ctx->idur.num;
3843 : }
3844 :
3845 : /*patch to align to old arch */
3846 8982 : if (gf_sys_old_arch_compat()) {
3847 8982 : if (mdur * (u64) ctx->idur.den > tkw->tk_timescale * (u64) ctx->idur.num)
3848 : abort = GF_TRUE;
3849 : } else {
3850 0 : if (mdur * (u64) ctx->idur.den >= tkw->tk_timescale * (u64) ctx->idur.num)
3851 : abort = GF_TRUE;
3852 : }
3853 : } else {
3854 1 : if ((s32) tkw->nb_samples >= -ctx->idur.num)
3855 : abort = GF_TRUE;
3856 : }
3857 :
3858 : if (abort) {
3859 : GF_FilterEvent evt;
3860 82 : GF_FEVT_INIT(evt, GF_FEVT_STOP, tkw->ipid);
3861 82 : gf_filter_pid_send_event(tkw->ipid, &evt);
3862 :
3863 82 : tkw->aborted = GF_TRUE;
3864 : }
3865 553255 : } else if (ctx->importer) {
3866 346937 : if (tkw->nb_frames) {
3867 47 : tkw->prog_done = tkw->nb_samples;
3868 47 : tkw->prog_total = tkw->nb_frames;
3869 : } else {
3870 346890 : u64 data_offset = gf_filter_pck_get_byte_offset(pck);
3871 346890 : if (data_offset == GF_FILTER_NO_BO) {
3872 239435 : data_offset = tkw->down_bytes;
3873 : }
3874 346890 : if ((data_offset != GF_FILTER_NO_BO) && tkw->down_size) {
3875 338684 : tkw->prog_done = data_offset;
3876 338684 : tkw->prog_total = tkw->down_size;
3877 : } else {
3878 8206 : if (tkw->pid_dur.den && tkw->pid_dur.num) {
3879 0 : tkw->prog_done = tkw->sample.DTS * tkw->pid_dur.den;
3880 0 : tkw->prog_total = tkw->pid_dur.num * tkw->tk_timescale;
3881 : } else {
3882 8206 : tkw->prog_done = 0;
3883 8206 : tkw->prog_total = 1;
3884 : }
3885 :
3886 : }
3887 : }
3888 : }
3889 : return GF_OK;
3890 : }
3891 :
3892 64 : static GF_Err mp4_mux_process_item(GF_MP4MuxCtx *ctx, TrackWriter *tkw, GF_FilterPacket *pck)
3893 : {
3894 : GF_Err e;
3895 : u32 meta_type, item_id, size, item_type, nb_items, media_brand;
3896 : GF_ImageItemProperties image_props;
3897 : GF_ImageItemProtection cenc_info;
3898 : const char *data, *item_name=NULL;
3899 : const GF_PropertyValue *p, *dsi, *dsi_enh;
3900 : GF_Box *config_box = NULL;
3901 :
3902 :
3903 64 : if (ctx->init_movie_done) {
3904 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Cannot add item to a finalized movie, not supported\n"));
3905 : return GF_NOT_SUPPORTED;
3906 : }
3907 :
3908 64 : if (tkw->stream_type != GF_STREAM_VISUAL) {
3909 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Cannot add item other than visual, not supported - use MP4Box for this\n"));
3910 : return GF_NOT_SUPPORTED;
3911 : }
3912 64 : ctx->update_report = GF_TRUE;
3913 :
3914 64 : meta_type = gf_isom_get_meta_type(ctx->file, GF_TRUE, 0);
3915 64 : if (!meta_type) {
3916 29 : e = gf_isom_set_meta_type(ctx->file, GF_TRUE, 0, GF_META_ITEM_TYPE_PICT);
3917 35 : } else if (meta_type != GF_META_ITEM_TYPE_PICT) {
3918 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] File already has a root 'meta' box of type %s\n", gf_4cc_to_str(meta_type)));
3919 : e= GF_BAD_PARAM;
3920 : } else {
3921 : e = GF_OK;
3922 : }
3923 29 : if (e) return e;
3924 :
3925 64 : data = (char *)gf_filter_pck_get_data(pck, &size);
3926 64 : if (!data) {
3927 0 : if (gf_filter_pck_get_frame_interface(pck)) {
3928 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Cannot add items from raw decoder outputs, not supported\n"));
3929 : return GF_NOT_SUPPORTED;
3930 : }
3931 : return GF_OK;
3932 : }
3933 64 : ctx->total_bytes_in += size;
3934 64 : ctx->total_samples++;
3935 :
3936 :
3937 64 : item_id = 0;
3938 64 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_ITEM_ID);
3939 64 : if (p) item_id = p->value.uint;
3940 :
3941 : item_name = "Image";
3942 64 : p = gf_filter_pid_get_property_str(tkw->ipid, "meta:name");
3943 64 : if (p && p->value.string) item_name = p->value.string;
3944 :
3945 : memset(&image_props, 0, sizeof(GF_ImageItemProperties));
3946 :
3947 64 : dsi = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_DECODER_CONFIG);
3948 64 : dsi_enh = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_DECODER_CONFIG_ENHANCEMENT);
3949 :
3950 64 : switch (tkw->codecid) {
3951 20 : case GF_CODECID_AVC:
3952 : case GF_ISOM_SUBTYPE_SVC_H264:
3953 : case GF_ISOM_SUBTYPE_MVC_H264:
3954 20 : if (!dsi) return GF_OK;
3955 :
3956 20 : if (tkw->codecid==GF_CODECID_AVC) {
3957 20 : config_box = gf_isom_box_new(GF_ISOM_BOX_TYPE_AVCC);
3958 : item_type = GF_ISOM_SUBTYPE_AVC_H264;
3959 0 : } else if (tkw->codecid==GF_CODECID_MVC) {
3960 0 : config_box = gf_isom_box_new(GF_ISOM_BOX_TYPE_MVCC);
3961 : item_type = GF_ISOM_SUBTYPE_MVC_H264;
3962 0 : if (dsi_enh) dsi = dsi_enh;
3963 : } else {
3964 0 : config_box = gf_isom_box_new(GF_ISOM_BOX_TYPE_SVCC);
3965 : item_type = GF_ISOM_SUBTYPE_SVC_H264;
3966 0 : if (dsi_enh) dsi = dsi_enh;
3967 : }
3968 :
3969 20 : ((GF_AVCConfigurationBox *)config_box)->config = gf_odf_avc_cfg_read(dsi->value.data.ptr, dsi->value.data.size);
3970 20 : if (! ((GF_AVCConfigurationBox *)config_box)->config) return GF_NON_COMPLIANT_BITSTREAM;
3971 :
3972 20 : image_props.num_channels = 3;
3973 20 : image_props.bits_per_channel[0] = ((GF_AVCConfigurationBox *)config_box)->config->luma_bit_depth;
3974 20 : image_props.bits_per_channel[1] = ((GF_AVCConfigurationBox *)config_box)->config->chroma_bit_depth;
3975 20 : image_props.bits_per_channel[2] = ((GF_AVCConfigurationBox *)config_box)->config->chroma_bit_depth;
3976 : media_brand = GF_ISOM_BRAND_AVCI;
3977 20 : break;
3978 :
3979 41 : case GF_CODECID_HEVC:
3980 : case GF_CODECID_HEVC_TILES:
3981 : case GF_CODECID_LHVC:
3982 41 : if (tkw->codecid == GF_CODECID_LHVC) {
3983 0 : if (dsi_enh) dsi = dsi_enh;
3984 0 : if (!dsi) return GF_OK;
3985 : }
3986 41 : config_box = gf_isom_box_new(GF_ISOM_BOX_TYPE_HVCC);
3987 :
3988 41 : if (dsi_enh) {
3989 0 : ((GF_HEVCConfigurationBox *)config_box)->config = gf_odf_hevc_cfg_read(dsi->value.data.ptr, dsi->value.data.size, GF_TRUE);
3990 : item_type = GF_ISOM_SUBTYPE_LHV1;
3991 : } else {
3992 41 : if ((tkw->codecid == GF_CODECID_HEVC) && !dsi) return GF_OK;
3993 :
3994 41 : ((GF_HEVCConfigurationBox *)config_box)->config = gf_odf_hevc_cfg_read(dsi->value.data.ptr, dsi->value.data.size, GF_FALSE);
3995 41 : item_type = (tkw->codecid == GF_CODECID_HEVC_TILES) ? GF_ISOM_SUBTYPE_HVT1 : GF_ISOM_SUBTYPE_HVC1;
3996 : }
3997 41 : if (! ((GF_HEVCConfigurationBox *)config_box)->config) {
3998 0 : if ((tkw->codecid != GF_CODECID_HEVC_TILES) && !dsi) return GF_NON_COMPLIANT_BITSTREAM;
3999 : } else {
4000 41 : image_props.num_channels = 3;
4001 41 : image_props.bits_per_channel[0] = ((GF_HEVCConfigurationBox *)config_box)->config->luma_bit_depth;
4002 41 : image_props.bits_per_channel[1] = ((GF_HEVCConfigurationBox *)config_box)->config->chroma_bit_depth;
4003 41 : image_props.bits_per_channel[2] = ((GF_HEVCConfigurationBox *)config_box)->config->chroma_bit_depth;
4004 : }
4005 : media_brand = GF_ISOM_BRAND_HEIC;
4006 41 : if (tkw->codecid==GF_CODECID_LHVC) {
4007 : media_brand = GF_ISOM_BRAND_HEIM;
4008 : }
4009 : break;
4010 0 : case GF_CODECID_AV1:
4011 0 : if (!dsi) return GF_OK;
4012 :
4013 0 : config_box = gf_isom_box_new(GF_ISOM_BOX_TYPE_AV1C);
4014 0 : ((GF_AV1ConfigurationBox *)config_box)->config = gf_odf_av1_cfg_read(dsi->value.data.ptr, dsi->value.data.size);
4015 :
4016 0 : if (! ((GF_AV1ConfigurationBox *)config_box)->config) return GF_NON_COMPLIANT_BITSTREAM;
4017 :
4018 : item_type = GF_ISOM_SUBTYPE_AV01;
4019 0 : u8 depth = ((GF_AV1ConfigurationBox *)config_box)->config->high_bitdepth ? (((GF_AV1ConfigurationBox *)config_box)->config->twelve_bit ? 12 : 10 ) : 8;
4020 0 : if (((GF_AV1ConfigurationBox *)config_box)->config->monochrome) {
4021 0 : image_props.num_channels = 1;
4022 0 : image_props.bits_per_channel[0] = depth;
4023 0 : image_props.bits_per_channel[1] = 0;
4024 0 : image_props.bits_per_channel[2] = 0;
4025 : } else {
4026 0 : image_props.num_channels = 3;
4027 0 : image_props.bits_per_channel[0] = depth;
4028 0 : image_props.bits_per_channel[1] = depth;
4029 0 : image_props.bits_per_channel[2] = depth;
4030 : }
4031 : media_brand = GF_ISOM_BRAND_AVIF;
4032 : break;
4033 : case GF_CODECID_JPEG:
4034 : item_type = GF_ISOM_SUBTYPE_JPEG;
4035 : media_brand = GF_ISOM_SUBTYPE_JPEG /* == GF_4CC('j', 'p', 'e', 'g') */;
4036 : break;
4037 0 : case GF_CODECID_J2K:
4038 : item_type = GF_ISOM_SUBTYPE_JP2K;
4039 : media_brand = GF_4CC('j', '2', 'k', 'i');
4040 0 : break;
4041 0 : case GF_CODECID_PNG:
4042 : item_type = GF_ISOM_SUBTYPE_PNG;
4043 : //not defined !
4044 : media_brand = GF_ISOM_SUBTYPE_PNG /* == GF_4CC('j', 'p', 'e', 'g') */;
4045 0 : break;
4046 0 : default:
4047 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Error: Codec %s not supported to create HEIF image items\n", gf_codecid_name(tkw->codecid) ));
4048 : return GF_NOT_SUPPORTED;
4049 : }
4050 :
4051 64 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_WIDTH);
4052 64 : if (p) image_props.width = p->value.uint;
4053 64 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_HEIGHT);
4054 64 : if (p) image_props.height = p->value.uint;
4055 64 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_ALPHA);
4056 64 : if (p) image_props.alpha = p->value.boolean;
4057 64 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_SAR);
4058 64 : if (p) {
4059 0 : image_props.hSpacing = p->value.frac.num;
4060 0 : image_props.vSpacing = p->value.frac.den;
4061 : } else {
4062 64 : image_props.hSpacing = image_props.vSpacing = 1;
4063 : }
4064 64 : image_props.config = config_box;
4065 64 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_HIDDEN);
4066 64 : if (p) image_props.hidden = p->value.boolean;
4067 :
4068 : //setup crypto
4069 64 : if (tkw->is_encrypted && gf_filter_pck_get_crypt_flags(pck)) {
4070 : memset(&cenc_info, 0, sizeof(GF_ImageItemProtection));
4071 10 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_CENC_SAI);
4072 10 : if (!p) {
4073 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Error: Missing CENC SAI on protected packet\n"));
4074 : return GF_SERVICE_ERROR;
4075 : }
4076 10 : cenc_info.sai_data = p->value.data.ptr;
4077 10 : cenc_info.sai_data_size = p->value.data.size;
4078 :
4079 10 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_PROTECTION_SCHEME_TYPE);
4080 10 : if (!p) {
4081 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Error: Missing CENC scheme type on protected item\n"));
4082 : return GF_SERVICE_ERROR;
4083 : }
4084 10 : cenc_info.scheme_type = p->value.uint;
4085 :
4086 10 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_PROTECTION_SCHEME_VERSION);
4087 10 : if (!p) {
4088 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Error: Missing CENC scheme version on protected item\n"));
4089 : return GF_SERVICE_ERROR;
4090 : }
4091 10 : cenc_info.scheme_version = p->value.uint;
4092 :
4093 10 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_CENC_KEY_INFO);
4094 10 : if (!p || (p->type != GF_PROP_DATA) || !gf_cenc_validate_key_info(p->value.data.ptr, p->value.data.size)) {
4095 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("Error: %s CENC Key info on protected item\n", p ? "Corrupted" : "Missing"));
4096 : return GF_NON_COMPLIANT_BITSTREAM;
4097 : }
4098 10 : cenc_info.key_info = p->value.data.ptr;
4099 10 : cenc_info.key_info_size = p->value.data.size;
4100 :
4101 10 : image_props.cenc_info = &cenc_info;
4102 :
4103 10 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_CENC_PATTERN);
4104 10 : if (p) {
4105 4 : cenc_info.skip_byte_block = p->value.frac.num;
4106 4 : cenc_info.crypt_byte_block = p->value.frac.den;
4107 : }
4108 :
4109 :
4110 10 : if (tkw->insert_pssh) {
4111 10 : mp4_mux_cenc_insert_pssh(ctx, tkw);
4112 10 : tkw->insert_pssh = GF_FALSE;
4113 : }
4114 : }
4115 :
4116 64 : nb_items = gf_isom_get_meta_item_count(ctx->file, GF_TRUE, 0);
4117 :
4118 64 : e = gf_isom_add_meta_item_memory(ctx->file, GF_TRUE, 0, item_name, &item_id, item_type, NULL, NULL, &image_props, (u8 *)data, size, NULL);
4119 :
4120 64 : if (config_box) gf_isom_box_del(config_box);
4121 :
4122 64 : if (e) return e;
4123 :
4124 :
4125 : //retrieve the final itemID
4126 64 : gf_isom_get_meta_item_info(ctx->file, GF_TRUE, 0, nb_items+1, &item_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
4127 64 : tkw->item_id = item_id;
4128 :
4129 64 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_PRIMARY_ITEM);
4130 64 : if (p && p->value.boolean) {
4131 10 : e = gf_isom_set_meta_primary_item(ctx->file, GF_TRUE, 0, item_id);
4132 10 : if (e) return e;
4133 : }
4134 : //if primary item is not set, assign one
4135 54 : else if (! gf_isom_get_meta_primary_item_id(ctx->file, GF_TRUE, 0)) {
4136 19 : e = gf_isom_set_meta_primary_item(ctx->file, GF_TRUE, 0, item_id);
4137 19 : if (e) return e;
4138 : }
4139 :
4140 64 : if (!ctx->major_brand_set) {
4141 29 : gf_isom_set_brand_info(ctx->file, GF_ISOM_BRAND_MIF1, 0);
4142 29 : gf_isom_reset_alt_brands(ctx->file);
4143 29 : ctx->major_brand_set = 2;
4144 : }
4145 64 : if (media_brand && (ctx->major_brand_set==2)) {
4146 64 : gf_isom_modify_alternate_brand(ctx->file, media_brand, 1);
4147 : }
4148 : #if 0
4149 : if (e == GF_OK && meta->ref_type) {
4150 : e = gf_isom_meta_add_item_ref(file, meta->root_meta, tk, meta->item_id, meta->ref_item_id, meta->ref_type, NULL);
4151 : }
4152 : #endif
4153 : return GF_OK;
4154 : }
4155 :
4156 40961 : static void mp4mux_send_output(GF_MP4MuxCtx *ctx)
4157 : {
4158 40961 : if (ctx->dst_pck) {
4159 20901 : if (ctx->notify_filename) {
4160 83 : gf_filter_pck_set_framing(ctx->dst_pck, GF_TRUE, GF_FALSE);
4161 83 : gf_filter_pck_set_property(ctx->dst_pck, GF_PROP_PCK_FILENUM, &PROP_UINT(ctx->cur_file_idx_plus_one-1) );
4162 83 : if (ctx->cur_file_suffix) {
4163 83 : gf_filter_pck_set_property(ctx->dst_pck, GF_PROP_PCK_FILESUF, &PROP_STRING_NO_COPY(ctx->cur_file_suffix) );
4164 83 : ctx->cur_file_suffix = NULL;
4165 : }
4166 83 : ctx->notify_filename = 0;
4167 : }
4168 20901 : gf_filter_pck_send(ctx->dst_pck);
4169 20901 : ctx->dst_pck = NULL;
4170 : }
4171 40961 : }
4172 :
4173 330 : static void mp4_mux_flush_frag_hls(GF_MP4MuxCtx *ctx)
4174 : {
4175 : GF_FilterEvent evt;
4176 : TrackWriter *tkw = NULL;
4177 : //send event on first track only
4178 330 : tkw = gf_list_get(ctx->tracks, 0);
4179 330 : GF_FEVT_INIT(evt, GF_FEVT_FRAGMENT_SIZE, tkw->ipid);
4180 330 : evt.frag_size.is_last = ctx->flush_seg ? GF_TRUE : GF_FALSE;
4181 330 : evt.frag_size.offset = ctx->frag_offset;
4182 330 : evt.frag_size.size = ctx->frag_size;
4183 330 : evt.frag_size.duration.num = (s64) ctx->frag_duration;
4184 330 : evt.frag_size.duration.den = ctx->frag_timescale;
4185 330 : evt.frag_size.independent = ctx->frag_has_intra;
4186 :
4187 330 : gf_filter_pid_send_event(tkw->ipid, &evt);
4188 :
4189 330 : ctx->frag_offset += ctx->frag_size;
4190 330 : ctx->frag_size = 0;
4191 330 : ctx->frag_duration = 0;
4192 330 : ctx->frag_has_intra = GF_FALSE;
4193 330 : }
4194 :
4195 3417 : static void mp4_mux_flush_seg(GF_MP4MuxCtx *ctx, Bool is_init, u64 idx_start_range, u64 idx_end_range)
4196 : {
4197 : GF_FilterEvent evt;
4198 : TrackWriter *tkw = NULL;
4199 :
4200 3417 : if (ctx->dst_pck) {
4201 3124 : if (!ctx->single_file) {
4202 : Bool s, e;
4203 2725 : gf_filter_pck_get_framing(ctx->dst_pck, &s, &e);
4204 2725 : gf_filter_pck_set_framing(ctx->dst_pck, s, GF_TRUE);
4205 2725 : if (!is_init) {
4206 2487 : u64 dur = ctx->next_seg_start - (ctx->min_cts_plus_one-1);
4207 2487 : gf_filter_pck_set_duration(ctx->dst_pck, (u32) dur);
4208 : }
4209 2725 : ctx->first_pck_sent = GF_FALSE;
4210 2725 : ctx->current_offset = 0;
4211 2725 : if (is_init && s)
4212 238 : gf_filter_pck_set_property(ctx->dst_pck, GF_PROP_PCK_INIT, &PROP_BOOL(GF_TRUE) );
4213 : }
4214 3124 : if (is_init) {
4215 294 : gf_filter_pck_set_dependency_flags(ctx->dst_pck, 0xFF);
4216 294 : gf_filter_pck_set_carousel_version(ctx->dst_pck, 1);
4217 : }
4218 3124 : mp4mux_send_output(ctx);
4219 : }
4220 3417 : if (!is_init && ctx->llhls_mode && ctx->frag_size) {
4221 51 : mp4_mux_flush_frag_hls(ctx);
4222 : }
4223 3417 : if (ctx->dash_mode) {
4224 : //send event on first track only
4225 3324 : tkw = gf_list_get(ctx->tracks, 0);
4226 3324 : GF_FEVT_INIT(evt, GF_FEVT_SEGMENT_SIZE, tkw->ipid);
4227 : evt.seg_size.seg_url = NULL;
4228 3324 : evt.seg_size.is_init = is_init ? GF_TRUE : GF_FALSE;
4229 3324 : if (!is_init || !idx_end_range) {
4230 3307 : evt.seg_size.media_range_start = ctx->current_offset;
4231 3307 : evt.seg_size.media_range_end = ctx->current_offset + ctx->current_size - 1;
4232 : }
4233 3324 : evt.seg_size.idx_range_start = idx_start_range;
4234 3324 : evt.seg_size.idx_range_end = idx_end_range;
4235 3324 : gf_filter_pid_send_event(tkw->ipid, &evt);
4236 :
4237 3324 : ctx->current_offset += ctx->current_size;
4238 3324 : ctx->current_size = 0;
4239 3324 : ctx->frag_offset = 0;
4240 3324 : ctx->frag_size = 0;
4241 3324 : ctx->frag_num = 0;
4242 3324 : ctx->frag_has_intra = GF_FALSE;
4243 : //changing file
4244 3324 : if (ctx->seg_name) {
4245 2484 : ctx->first_pck_sent = GF_FALSE;
4246 : }
4247 : }
4248 3417 : }
4249 :
4250 :
4251 339 : static GF_Err mp4_mux_initialize_movie(GF_MP4MuxCtx *ctx)
4252 : {
4253 : GF_Err e;
4254 339 : u32 i, count = gf_list_count(ctx->tracks);
4255 : TrackWriter *ref_tkw = NULL;
4256 : u64 min_dts = 0;
4257 : u32 min_dts_scale=0;
4258 : u32 def_fake_dur=0;
4259 : u32 def_fake_scale=0;
4260 : #ifdef GF_ENABLE_CTRN
4261 : u32 traf_inherit_base_id=0;
4262 : #endif
4263 : u32 nb_segments=0;
4264 : GF_Fraction64 max_dur;
4265 339 : ctx->single_file = GF_TRUE;
4266 339 : ctx->current_offset = ctx->current_size = 0;
4267 : max_dur.den = 1;
4268 : max_dur.num = 0;
4269 :
4270 339 : if (ctx->sseg && ctx->noinit)
4271 3 : ctx->single_file = GF_FALSE;
4272 :
4273 339 : if (ctx->idur.num && ctx->idur.den) {
4274 0 : max_dur.num = ctx->idur.num;
4275 0 : max_dur.den = ctx->idur.den;
4276 : }
4277 :
4278 : //make sure we have one sample from each PID. This will trigger potential pending reconfigure
4279 : //for filters updating the PID caps before the first packet dispatch
4280 389 : for (i=0; i<count; i++) {
4281 : const GF_PropertyValue *p;
4282 389 : TrackWriter *tkw = gf_list_get(ctx->tracks, i);
4283 : GF_FilterPacket *pck;
4284 389 : if (tkw->fake_track) continue;
4285 :
4286 362 : pck = gf_filter_pid_get_packet(tkw->ipid);
4287 362 : if (!pck) {
4288 0 : if (gf_filter_pid_is_eos(tkw->ipid)) continue;
4289 : return GF_OK;
4290 : }
4291 :
4292 362 : if (!ctx->dash_mode && !ctx->cur_file_idx_plus_one) {
4293 14 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENUM);
4294 14 : if (p) {
4295 4 : ctx->cur_file_idx_plus_one = p->value.uint + 1;
4296 4 : if (!ctx->cur_file_suffix) {
4297 4 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILESUF);
4298 4 : if (p && p->value.string) ctx->cur_file_suffix = gf_strdup(p->value.string);
4299 : }
4300 4 : ctx->notify_filename = GF_TRUE;
4301 : }
4302 : }
4303 :
4304 362 : if (tkw->cenc_state==CENC_NEED_SETUP) {
4305 112 : mp4_mux_cenc_update(ctx, tkw, pck, CENC_CONFIG, 0);
4306 : }
4307 :
4308 362 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENAME);
4309 362 : if (p && strlen(p->value.string)) ctx->single_file = GF_FALSE;
4310 :
4311 362 : def_fake_dur = gf_filter_pck_get_duration(pck);
4312 362 : def_fake_scale = tkw->src_timescale;
4313 :
4314 362 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_DURATION);
4315 362 : if (p && p->value.lfrac.num>0 && p->value.lfrac.den) {
4316 350 : tkw->pid_dur = p->value.lfrac;
4317 350 : if (max_dur.num * (s64) p->value.lfrac.den < (s64) max_dur.den * p->value.lfrac.num) {
4318 : max_dur.num = p->value.lfrac.num;
4319 : max_dur.den = p->value.lfrac.den;
4320 : }
4321 : }
4322 : #ifdef GF_ENABLE_CTRN
4323 : if (tkw->codecid==GF_CODECID_HEVC)
4324 : traf_inherit_base_id = tkw->track_id;
4325 : #endif
4326 : }
4327 : //good to go, finalize for fragments
4328 389 : for (i=0; i<count; i++) {
4329 : u32 def_pck_dur;
4330 : u32 def_samp_size=0;
4331 : u32 def_is_rap;
4332 : #ifdef GF_ENABLE_CTRN
4333 : u32 inherit_traf_from_track = 0;
4334 : #endif
4335 : u64 dts;
4336 : const GF_PropertyValue *p;
4337 :
4338 389 : TrackWriter *tkw = gf_list_get(ctx->tracks, i);
4339 :
4340 389 : if (tkw->fake_track) {
4341 27 : if (def_fake_scale) {
4342 : def_pck_dur = def_fake_dur;
4343 27 : def_pck_dur *= tkw->src_timescale;
4344 27 : def_pck_dur /= def_fake_scale;
4345 : } else {
4346 : def_pck_dur = 0;
4347 : }
4348 : } else {
4349 362 : GF_FilterPacket *pck = gf_filter_pid_get_packet(tkw->ipid);
4350 : //can be null if eos
4351 362 : if (pck) {
4352 : u32 tscale;
4353 : //otherwise setup fragmentation, using first sample desc as default idx
4354 : //first pck dur as default
4355 362 : def_pck_dur = gf_filter_pck_get_duration(pck);
4356 :
4357 362 : dts = gf_filter_pck_get_dts(pck);
4358 362 : if (dts == GF_FILTER_NO_TS)
4359 0 : dts = gf_filter_pck_get_cts(pck);
4360 362 : tscale = gf_filter_pck_get_timescale(pck);
4361 :
4362 362 : if (!min_dts || min_dts * tscale > dts * min_dts_scale) {
4363 : min_dts = dts;
4364 : min_dts_scale = tscale;
4365 : }
4366 362 : if (tkw->raw_audio_bytes_per_sample) {
4367 : u32 pck_size;
4368 0 : gf_filter_pck_get_data(pck, &pck_size);
4369 0 : pck_size /= tkw->raw_audio_bytes_per_sample;
4370 0 : if (pck_size)
4371 0 : def_pck_dur /= pck_size;
4372 : }
4373 : } else {
4374 : def_pck_dur = 0;
4375 : }
4376 362 : if (tkw->raw_audio_bytes_per_sample)
4377 : def_samp_size = tkw->raw_audio_bytes_per_sample;
4378 : }
4379 389 : if (tkw->src_timescale != tkw->tk_timescale) {
4380 0 : def_pck_dur *= tkw->tk_timescale;
4381 0 : def_pck_dur /= tkw->src_timescale;
4382 : }
4383 :
4384 : //and consider audio & text all RAPs, the rest not rap - this will need refinement later on
4385 : //but won't break the generated files
4386 389 : switch (tkw->stream_type) {
4387 89 : case GF_STREAM_AUDIO:
4388 : case GF_STREAM_TEXT:
4389 : def_is_rap = GF_TRUE;
4390 89 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_HAS_SYNC);
4391 89 : if (p && p->value.boolean)
4392 : def_is_rap = GF_FALSE;
4393 : break;
4394 296 : case GF_STREAM_VISUAL:
4395 296 : switch (tkw->codecid) {
4396 : case GF_CODECID_PNG:
4397 : case GF_CODECID_JPEG:
4398 : case GF_CODECID_J2K:
4399 : break;
4400 : case GF_CODECID_HEVC_TILES:
4401 : #ifdef GF_ENABLE_CTRN
4402 : if (ctx->ctrn && ctx->ctrni)
4403 : inherit_traf_from_track = traf_inherit_base_id;
4404 : #endif
4405 : break;
4406 233 : default:
4407 233 : if (!ref_tkw) ref_tkw = tkw;
4408 : break;
4409 : }
4410 : def_is_rap = GF_FALSE;
4411 : break;
4412 :
4413 : default:
4414 : def_is_rap = GF_FALSE;
4415 : break;
4416 : }
4417 :
4418 389 : mp4_mux_set_hevc_groups(ctx, tkw);
4419 :
4420 : //use 1 for the default sample description index. If no multi stsd, this is always the case
4421 : //otherwise we need to update the stsd idx in the traf headers
4422 389 : e = gf_isom_setup_track_fragment(ctx->file, tkw->track_id, tkw->stsd_idx, def_pck_dur, def_samp_size, (u8) def_is_rap, 0, 0, ctx->nofragdef ? GF_TRUE : GF_FALSE);
4423 389 : if (e) {
4424 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Unable to setup fragmentation for track ID %d: %s\n", tkw->track_id, gf_error_to_string(e) ));
4425 : return e;
4426 : }
4427 :
4428 389 : if (ctx->refrag) {
4429 3 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_ISOM_TREX_TEMPLATE);
4430 3 : if (p) {
4431 3 : gf_isom_setup_track_fragment_template(ctx->file, tkw->track_id, p->value.data.ptr, p->value.data.size, ctx->nofragdef);
4432 0 : } else if (!ctx->nofragdef) {
4433 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Refragmentation with default track fragment flags signaling but no TREX found in source track %d, using defaults computed from PID, result might be broken\n", tkw->track_id));
4434 : }
4435 : }
4436 :
4437 :
4438 389 : if (ctx->tfdt.den && ctx->tfdt.num) {
4439 0 : tkw->offset_dts = ctx->tfdt.num * tkw->tk_timescale;
4440 0 : tkw->offset_dts /= ctx->tfdt.den;
4441 : }
4442 :
4443 389 : if (tkw->fake_track) {
4444 27 : gf_list_del_item(ctx->tracks, tkw);
4445 27 : if (ref_tkw==tkw) ref_tkw=NULL;
4446 27 : mp4_mux_track_writer_del(tkw);
4447 27 : i--;
4448 27 : count--;
4449 27 : continue;
4450 : }
4451 :
4452 : #ifdef GF_ENABLE_CTRN
4453 : if (inherit_traf_from_track)
4454 : gf_isom_enable_traf_inherit(ctx->file, tkw->track_id, inherit_traf_from_track);
4455 : #endif
4456 :
4457 362 : if (!tkw->box_patched) {
4458 353 : p = gf_filter_pid_get_property_str(tkw->ipid, "boxpatch");
4459 353 : if (p && p->value.string) {
4460 0 : e = gf_isom_apply_box_patch(ctx->file, tkw->track_id, p->value.string, GF_FALSE);
4461 0 : if (e) {
4462 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Unable to apply box patch %s to track %d: %s\n",
4463 : p->value.string, tkw->track_id, gf_error_to_string(e) ));
4464 : }
4465 : }
4466 353 : tkw->box_patched = GF_TRUE;
4467 : }
4468 :
4469 362 : p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_DASH_SEGMENTS);
4470 362 : if (p && (p->value.uint>nb_segments))
4471 : nb_segments = p->value.uint;
4472 :
4473 362 : if (!ctx->dash_mode)
4474 28 : gf_isom_purge_track_reference(ctx->file, tkw->track_num);
4475 : }
4476 :
4477 666 : if (max_dur.num && max_dur.den) {
4478 327 : u64 mdur = max_dur.num;
4479 327 : if (ctx->moovts != max_dur.den) {
4480 290 : mdur *= (u32) ctx->moovts;
4481 290 : mdur /= max_dur.den;
4482 : }
4483 327 : gf_isom_set_movie_duration(ctx->file, mdur, GF_FALSE);
4484 : }
4485 12 : else if (ctx->cmaf) {
4486 : //CMAF 7.3.2.1.c.6) "The MovieExtendsBox may contain a MovieExtendsHeaderBox,
4487 : //as defined in ISO/IEC 14496-12, and if so, shall provide the overall duration
4488 : //of the CMAF track. If the duration is unknown, this box shall be omitted."
4489 0 : gf_isom_set_movie_duration(ctx->file, 0, GF_TRUE);
4490 : }
4491 :
4492 : //if we have an explicit track reference for fragmenting, move it first in our list
4493 339 : if (ref_tkw) {
4494 230 : gf_list_del_item(ctx->tracks, ref_tkw);
4495 230 : gf_list_insert(ctx->tracks, ref_tkw, 0);
4496 : }
4497 339 : ctx->ref_tkw = gf_list_get(ctx->tracks, 0);
4498 :
4499 339 : if (!ctx->abs_offset) {
4500 335 : u32 mval = ctx->dash_mode ? '6' : '5';
4501 : u32 mbrand, mcount, found=0;
4502 : u8 szB[GF_4CC_MSIZE];
4503 335 : gf_isom_set_fragment_option(ctx->file, 0, GF_ISOM_TFHD_FORCE_MOOF_BASE_OFFSET, 1);
4504 :
4505 335 : gf_isom_get_brand_info(ctx->file, &mbrand, NULL, &mcount);
4506 335 : strcpy(szB, gf_4cc_to_str(mbrand));
4507 335 : if (!strncmp(szB, "iso", 3) && (szB[3] >= mval) && (szB[3] <= 'F') ) found = 1;
4508 : i=0;
4509 1143 : while (!found && (i<mcount)) {
4510 485 : i++;
4511 485 : gf_isom_get_alternate_brand(ctx->file, i, &mbrand);
4512 485 : strcpy(szB, gf_4cc_to_str(mbrand));
4513 485 : if (!strncmp(szB, "iso", 3) && (szB[3] >= mval) && (szB[3] <= 'F') ) found = 1;
4514 : }
4515 :
4516 : /*because of movie fragments MOOF based offset, ISOM <4 is forbidden*/
4517 335 : if (!found) {
4518 273 : gf_isom_set_brand_info(ctx->file, ctx->dash_mode ? GF_ISOM_BRAND_ISO6 : GF_ISOM_BRAND_ISO5, 1);
4519 : }
4520 :
4521 335 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISOM, GF_FALSE);
4522 335 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISO1, GF_FALSE);
4523 335 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISO2, GF_FALSE);
4524 335 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISO3, GF_FALSE);
4525 335 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISO4, GF_FALSE);
4526 335 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_AVC1, GF_FALSE);
4527 335 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_MP41, GF_FALSE);
4528 335 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_MP42, GF_FALSE);
4529 : }
4530 :
4531 339 : if (ctx->dash_mode) {
4532 : /*DASH self-init media segment*/
4533 326 : if (ctx->dash_mode==MP4MX_DASH_VOD) {
4534 17 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_DSMS, GF_TRUE);
4535 : } else {
4536 309 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_DASH, GF_TRUE);
4537 : }
4538 326 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_MSIX, ((ctx->dash_mode==MP4MX_DASH_VOD) && (ctx->subs_sidx>=0)) ? GF_TRUE : GF_FALSE);
4539 : }
4540 :
4541 339 : if (ctx->boxpatch && !ctx->box_patched) {
4542 1 : e = gf_isom_apply_box_patch(ctx->file, 0, ctx->boxpatch, GF_FALSE);
4543 1 : if (e) {
4544 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Unable to apply box patch %s: %s\n", ctx->boxpatch, gf_error_to_string(e) ));
4545 : }
4546 1 : ctx->box_patched = GF_TRUE;
4547 : }
4548 :
4549 339 : e = gf_isom_finalize_for_fragment(ctx->file, ctx->dash_mode ? 1 : 0, ctx->mvex);
4550 339 : if (e) {
4551 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Unable to finalize moov for fragmentation: %s\n", gf_error_to_string(e) ));
4552 : return e;
4553 : }
4554 339 : ctx->init_movie_done = GF_TRUE;
4555 :
4556 339 : if (min_dts_scale) {
4557 : u64 rs_dts = min_dts;
4558 339 : rs_dts *= ctx->cdur.den;
4559 339 : rs_dts /= min_dts_scale;
4560 339 : ctx->next_frag_start = rs_dts;
4561 : }
4562 339 : ctx->next_frag_start += ctx->cdur.num;
4563 339 : ctx->adjusted_next_frag_start = ctx->next_frag_start;
4564 339 : ctx->fragment_started = GF_FALSE;
4565 :
4566 339 : if (ctx->noinit) {
4567 45 : if (ctx->dst_pck) gf_filter_pck_discard(ctx->dst_pck);
4568 45 : ctx->dst_pck = NULL;
4569 45 : ctx->current_size = ctx->current_offset = 0;
4570 45 : ctx->first_pck_sent = GF_FALSE;
4571 : } else {
4572 294 : mp4_mux_flush_seg(ctx, GF_TRUE, 0, 0);
4573 : }
4574 : assert(!ctx->dst_pck);
4575 :
4576 : //change major brand for segments
4577 339 : if (ctx->styp && (strlen(ctx->styp)>=4)) {
4578 0 : u32 styp_brand = GF_4CC(ctx->styp[0], ctx->styp[1], ctx->styp[2], ctx->styp[3]);
4579 : u32 version = 0;
4580 0 : char *sep = strchr(ctx->styp, '.');
4581 0 : if (sep) version = atoi(sep+1);
4582 0 : gf_isom_set_brand_info(ctx->file, styp_brand, version);
4583 : }
4584 :
4585 339 : if (ctx->dash_mode==MP4MX_DASH_VOD) {
4586 17 : if ((ctx->vodcache==MP4MX_VODCACHE_REPLACE) && !nb_segments && (!ctx->media_dur || !ctx->dash_dur.num) ) {
4587 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Media duration unknown, cannot use replace mode of vodcache, using temp file for VoD storage\n"));
4588 0 : ctx->vodcache = MP4MX_VODCACHE_ON;
4589 0 : e = mp4mx_setup_dash_vod(ctx, NULL);
4590 0 : if (e) return e;
4591 : }
4592 :
4593 17 : if (ctx->vodcache==MP4MX_VODCACHE_REPLACE) {
4594 : GF_BitStream *bs;
4595 : u8 *output;
4596 : char *msg;
4597 : GF_FilterPacket *pck;
4598 : u32 len;
4599 : Bool exact_sidx = GF_TRUE;
4600 :
4601 0 : if (!nb_segments) {
4602 : exact_sidx = GF_FALSE;
4603 0 : nb_segments = (u32) ( ctx->media_dur * ctx->dash_dur.den / ctx->dash_dur.num);
4604 : //always add an extra segment
4605 0 : nb_segments ++;
4606 : //and safety alloc of 10%
4607 0 : if (nb_segments>10)
4608 0 : nb_segments += 10*nb_segments/100;
4609 : else
4610 0 : nb_segments ++;
4611 : }
4612 :
4613 : //max sidx size: full box + sidx fields + timing 64 bit + nb segs (each 12 bytes)
4614 0 : ctx->sidx_max_size = 12 + (12 + 16) + 12 * nb_segments;
4615 :
4616 : //we produce an ssix, add full box + nb subsegs + nb_segments * (range_count=2 + 2*(range+size))
4617 0 : if (ctx->ssix) {
4618 0 : ctx->sidx_max_size += 12 + 4 + nb_segments * 12;
4619 : }
4620 :
4621 0 : if (!exact_sidx) {
4622 : //and a free box
4623 0 : ctx->sidx_max_size += 8;
4624 0 : ctx->sidx_size_exact = GF_FALSE;
4625 : } else {
4626 0 : ctx->sidx_size_exact = GF_TRUE;
4627 : }
4628 0 : ctx->sidx_chunk_offset = (u32) (ctx->current_offset + ctx->current_size);
4629 : //send a dummy packet
4630 0 : pck = gf_filter_pck_new_alloc(ctx->opid, ctx->sidx_max_size, &output);
4631 0 : if (!pck) return GF_OUT_OF_MEM;
4632 :
4633 0 : gf_filter_pck_set_framing(pck, GF_FALSE, GF_FALSE);
4634 : //format as free box for now
4635 0 : bs = gf_bs_new(output, ctx->sidx_max_size, GF_BITSTREAM_WRITE);
4636 0 : gf_bs_write_u32(bs, ctx->sidx_max_size);
4637 0 : gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_FREE);
4638 : msg = "GPAC " GPAC_VERSION" SIDX placeholder";
4639 : len = (u32) strlen(msg);
4640 0 : if (len+8>ctx->sidx_max_size) len = ctx->sidx_max_size - 8;
4641 0 : gf_bs_write_data(bs, msg, len );
4642 0 : gf_bs_del(bs);
4643 0 : gf_filter_pck_send(pck);
4644 17 : } else if (ctx->vodcache==MP4MX_VODCACHE_ON) {
4645 17 : ctx->store_output = GF_TRUE;
4646 : } else {
4647 0 : ctx->store_output = GF_FALSE;
4648 0 : ctx->sidx_chunk_offset = (u32) (ctx->current_offset + ctx->current_size);
4649 : }
4650 17 : gf_isom_allocate_sidx(ctx->file, ctx->subs_sidx, ctx->chain_sidx, 0, NULL, NULL, NULL, ctx->ssix);
4651 : }
4652 : return GF_OK;
4653 : }
4654 :
4655 3571 : static GF_Err mp4_mux_start_fragment(GF_MP4MuxCtx *ctx, GF_FilterPacket *pck)
4656 : {
4657 : GF_Err e;
4658 3571 : u32 i, count = gf_list_count(ctx->tracks);
4659 3571 : Bool has_tfdt=GF_FALSE;
4660 : GF_ISOStartFragmentFlags flags=0;
4661 :
4662 : //setup some default
4663 3571 : gf_isom_set_next_moof_number(ctx->file, ctx->msn);
4664 3571 : ctx->msn += ctx->msninc;
4665 3571 : ctx->min_cts_plus_one = 0;
4666 :
4667 3571 : if (ctx->moof_first) flags |= GF_ISOM_FRAG_MOOF_FIRST;
4668 : #ifdef GF_ENABLE_CTRN
4669 : if (ctx->ctrn) flags |= GF_ISOM_FRAG_USE_COMPACT;
4670 : #endif
4671 :
4672 3571 : e = gf_isom_start_fragment(ctx->file, flags);
4673 3571 : if (e) {
4674 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Unable to start new fragment: %s\n", gf_error_to_string(e) ));
4675 : return e;
4676 : }
4677 3571 : if (pck) {
4678 123 : const GF_PropertyValue *p = gf_filter_pck_get_property(pck, GF_PROP_PCK_MOOF_TEMPLATE);
4679 123 : if (p && p->value.data.ptr) {
4680 57 : GF_SegmentIndexBox *out_sidx = NULL;
4681 57 : gf_isom_set_fragment_template(ctx->file, p->value.data.ptr, p->value.data.size, &has_tfdt, &out_sidx);
4682 57 : if (out_sidx) {
4683 57 : if (ctx->cloned_sidx) gf_isom_box_del((GF_Box *)ctx->cloned_sidx);
4684 57 : ctx->cloned_sidx = out_sidx;
4685 57 : ctx->cloned_sidx_index = 0;
4686 : }
4687 : }
4688 : }
4689 :
4690 : //setup some default
4691 3772 : for (i=0; i<count; i++) {
4692 3772 : TrackWriter *tkw = gf_list_get(ctx->tracks, i);
4693 : e = GF_OK;
4694 3772 : if (ctx->strun) {
4695 57 : e = gf_isom_set_fragment_option(ctx->file, tkw->track_id, GF_ISOM_TRAF_RANDOM_ACCESS, 0);
4696 : }
4697 : //fragment at sap boundaries for video, but not in dash mode (compatibility with old arch)
4698 3715 : else if (ctx->fsap && (tkw->stream_type == GF_STREAM_VISUAL) && !ctx->dash_mode) {
4699 80 : e = gf_isom_set_fragment_option(ctx->file, tkw->track_id, GF_ISOM_TRAF_RANDOM_ACCESS, 1);
4700 : }
4701 137 : if (e) {
4702 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Unable set fragment options: %s\n", gf_error_to_string(e) ));
4703 : }
4704 3772 : tkw->fragment_done = GF_FALSE;
4705 3772 : tkw->insert_tfdt = (has_tfdt || ctx->tfdt_traf) ? GF_TRUE : ctx->insert_tfdt;
4706 3772 : tkw->dur_in_frag = 0;
4707 :
4708 3772 : if (ctx->trun_inter) {
4709 0 : gf_isom_set_fragment_option(ctx->file, tkw->track_id, GF_ISOM_TRUN_SET_INTERLEAVE_ID, 0);
4710 : }
4711 3772 : if (ctx->truns_first) {
4712 0 : gf_isom_set_fragment_option(ctx->file, tkw->track_id, GF_ISOM_TRAF_TRUNS_FIRST, 1);
4713 : }
4714 : //7.7 cmf2 For video CMAF Tracks not contained in Track Files, Version 1 shall be used.
4715 3772 : if ((ctx->cmaf==MP4MX_CMAF_CMF2) && (tkw->stream_type==GF_STREAM_VISUAL))
4716 0 : gf_isom_set_fragment_option(ctx->file, tkw->track_id, GF_ISOM_TRAF_TRUN_V1, 1);
4717 :
4718 3772 : if (ctx->sdtp_traf)
4719 0 : gf_isom_set_fragment_option(ctx->file, tkw->track_id, GF_ISOM_TRAF_USE_SAMPLE_DEPS_BOX, ctx->sdtp_traf);
4720 :
4721 3772 : if (ctx->tfdt64)
4722 0 : gf_isom_set_fragment_option(ctx->file, tkw->track_id, GF_ISOM_TRAF_USE_LARGE_TFDT, ctx->tfdt64);
4723 :
4724 3772 : if (ctx->insert_pssh)
4725 0 : mp4_mux_cenc_insert_pssh(ctx, tkw);
4726 : }
4727 3571 : ctx->fragment_started = GF_TRUE;
4728 3571 : ctx->insert_tfdt = GF_FALSE;
4729 3571 : ctx->insert_pssh = GF_FALSE;
4730 3571 : return GF_OK;
4731 : }
4732 :
4733 2618 : static GF_Err mp4_mux_flush_fragmented(GF_Filter *filter, GF_MP4MuxCtx *ctx)
4734 : {
4735 : GF_FilterPacket *pck;
4736 : u8 *output;
4737 2618 : u32 nb_read, blocksize = ctx->block_size;
4738 2618 : if (ctx->flush_done + blocksize>ctx->flush_size) {
4739 17 : blocksize = (u32) (ctx->flush_size - ctx->flush_done);
4740 : }
4741 2618 : if (!blocksize) return GF_EOS;
4742 2618 : pck = gf_filter_pck_new_alloc(ctx->opid, blocksize, &output);
4743 2618 : if (!pck) return GF_OUT_OF_MEM;
4744 :
4745 2618 : nb_read = (u32) gf_fread(output, blocksize, ctx->tmp_store);
4746 2618 : if (nb_read != blocksize) {
4747 : char tmp[1];
4748 : //weird behavior on some file systems, dump debug info
4749 0 : gf_fread(tmp, 1, ctx->tmp_store);
4750 0 : Bool is_eof = gf_feof(ctx->tmp_store);
4751 0 : GF_LOG(is_eof ? GF_LOG_WARNING : GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error reading from VOD temp cache, read %d bytes but asked %d bytes\n\tCache EOF %d - cache size "LLU" - read pos "LLU" - file pos "LLU"\n", nb_read, blocksize, is_eof, ctx->flush_size, ctx->flush_done, gf_ftell(ctx->tmp_store)));
4752 : }
4753 2618 : ctx->flush_done += nb_read;
4754 2618 : if (ctx->flush_done==ctx->flush_size) {
4755 17 : gf_filter_pck_set_framing(pck, GF_FALSE, GF_TRUE);
4756 17 : gf_filter_pck_send(pck);
4757 17 : gf_filter_pid_set_eos(ctx->opid);
4758 17 : return GF_EOS;
4759 : }
4760 2601 : gf_filter_pck_set_framing(pck, GF_FALSE, GF_FALSE);
4761 2601 : gf_filter_pck_send(pck);
4762 : //we are not done flushing but we have no more input packets, signal we still need processing
4763 2601 : gf_filter_ask_rt_reschedule(filter, 1);
4764 2601 : return GF_OK;
4765 : }
4766 :
4767 3571 : static void mp4mx_frag_box_patch(GF_MP4MuxCtx *ctx)
4768 : {
4769 : GF_Err e;
4770 3571 : u32 i, count = gf_list_count(ctx->tracks);
4771 7343 : for (i=0; i<count; i++) {
4772 : const GF_PropertyValue *p;
4773 3772 : TrackWriter *tkw = gf_list_get(ctx->tracks, i);
4774 3772 : if (!tkw->track_id) continue;
4775 : //no box patched set (todo, do we want to allow changing boxpatch props ?)
4776 3772 : if (!tkw->box_patched) continue;
4777 :
4778 3772 : p = gf_filter_pid_get_property_str(tkw->ipid, "boxpatch");
4779 3772 : if (p && p->value.string) {
4780 0 : e = gf_isom_apply_box_patch(ctx->file, tkw->track_id ? tkw->track_id : tkw->item_id, p->value.string, GF_TRUE);
4781 0 : if (e) {
4782 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Unable to apply box patch %s to track fragment %d: %s\n",
4783 : p->value.string, tkw->track_id, gf_error_to_string(e) ));
4784 : }
4785 : }
4786 : }
4787 :
4788 3571 : if (ctx->boxpatch) {
4789 2 : e = gf_isom_apply_box_patch(ctx->file, 0, ctx->boxpatch, GF_TRUE);
4790 2 : if (e) {
4791 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Unable to apply box patch %s to fragment: %s\n", ctx->boxpatch, gf_error_to_string(e) ));
4792 : }
4793 2 : ctx->box_patched = GF_TRUE;
4794 : }
4795 3571 : }
4796 :
4797 :
4798 : static GF_Err mp4_mux_initialize(GF_Filter *filter);
4799 :
4800 60 : GF_Err mp4mx_reload_output(GF_Filter *filter, GF_MP4MuxCtx *ctx)
4801 : {
4802 : GF_Err e;
4803 60 : u32 i, count = gf_list_count(ctx->tracks);
4804 :
4805 : //done with the file
4806 60 : if (ctx->file) {
4807 60 : e = mp4_mux_done(filter, ctx, GF_FALSE);
4808 60 : if (e) return e;
4809 60 : ctx->file = NULL;
4810 : }
4811 60 : ctx->init_movie_done = GF_FALSE;
4812 60 : e = mp4_mux_initialize(filter);
4813 60 : if (e) return e;
4814 60 : ctx->config_timing = GF_TRUE;
4815 :
4816 158 : for (i=0; i<count; i++) {
4817 98 : TrackWriter *tkw = gf_list_get(ctx->tracks, i);
4818 98 : tkw->suspended = GF_FALSE;
4819 98 : tkw->track_num = 0;
4820 98 : tkw->nb_samples = 0;
4821 98 : tkw->max_cts = 0;
4822 98 : tkw->min_cts = (u64) -1;
4823 98 : e = mp4_mux_configure_pid(filter, tkw->ipid, GF_FALSE);
4824 98 : if (e) return e;
4825 98 : tkw->nb_samples = 0;
4826 98 : tkw->sample.DTS = 0;
4827 98 : tkw->sample.CTS_Offset = 0;
4828 98 : tkw->samples_in_stsd = 0;
4829 98 : tkw->samples_in_frag = 0;
4830 : }
4831 : assert(ctx->next_file_idx);
4832 60 : ctx->cur_file_idx_plus_one = ctx->next_file_idx;
4833 60 : ctx->next_file_idx = 0;
4834 60 : ctx->notify_filename = GF_TRUE;
4835 : assert(!ctx->cur_file_suffix);
4836 60 : if (ctx->next_file_suffix) {
4837 60 : ctx->cur_file_suffix = gf_strdup(ctx->next_file_suffix);
4838 60 : ctx->next_file_suffix = NULL;
4839 : }
4840 : return GF_OK;
4841 : }
4842 :
4843 :
4844 120838 : static GF_Err mp4_mux_process_fragmented(GF_Filter *filter, GF_MP4MuxCtx *ctx)
4845 : {
4846 : GF_Err e = GF_OK;
4847 : u32 nb_eos, nb_done, nb_suspended, i, count;
4848 :
4849 120838 : if (ctx->flush_size) {
4850 2618 : return mp4_mux_flush_fragmented(filter, ctx);
4851 : }
4852 :
4853 118220 : if (!ctx->file)
4854 : return GF_EOS;
4855 :
4856 : //init movie not yet produced
4857 118220 : if (!ctx->init_movie_done) {
4858 339 : e = mp4_mux_initialize_movie(ctx);
4859 339 : if (e) return e;
4860 339 : if (!ctx->init_movie_done)
4861 : return GF_OK;
4862 : }
4863 : /*get count after init, some tracks may have been remove*/
4864 118220 : count = gf_list_count(ctx->tracks);
4865 118220 : if (ctx->dash_mode && !ctx->segment_started) {
4866 : //don't start a new segment if all pids are in eos
4867 : nb_eos=0;
4868 3207 : for (i=0; i<count; i++) {
4869 3207 : TrackWriter *tkw = gf_list_get(ctx->tracks, i);
4870 3207 : if (gf_filter_pid_is_eos(tkw->ipid)) {
4871 28 : nb_eos ++;
4872 : }
4873 : }
4874 3069 : if (nb_eos==count)
4875 : goto check_eos;
4876 :
4877 3044 : ctx->segment_started = GF_TRUE;
4878 3044 : ctx->insert_tfdt = GF_TRUE;
4879 3044 : ctx->insert_pssh = (ctx->psshs == MP4MX_PSSH_MOOF) ? GF_TRUE : GF_FALSE;
4880 :
4881 3044 : gf_isom_start_segment(ctx->file, ctx->single_file ? NULL : "_gpac_isobmff_redirect", GF_FALSE);
4882 : }
4883 :
4884 : //process pid by pid
4885 : nb_eos=0;
4886 : nb_done = 0;
4887 : nb_suspended = 0;
4888 5329 : for (i=0; i<count; i++) {
4889 : u64 cts, dts, ncts;
4890 119925 : TrackWriter *tkw = gf_list_get(ctx->tracks, i);
4891 :
4892 119925 : if (ctx->fragment_started && tkw->fragment_done) {
4893 1554 : nb_done ++;
4894 1554 : continue;
4895 : }
4896 118371 : if (tkw->suspended) {
4897 2 : if (ctx->fragment_started) nb_done++;
4898 2 : nb_suspended++;
4899 2 : continue;
4900 : }
4901 :
4902 : while (1) {
4903 : const GF_PropertyValue *p;
4904 : u32 orig_frag_bounds=0;
4905 230283 : GF_FilterPacket *pck = gf_filter_pid_get_packet(tkw->ipid);
4906 :
4907 230283 : if (!pck) {
4908 114942 : if (gf_filter_pid_is_eos(tkw->ipid)) {
4909 346 : tkw->fragment_done = GF_TRUE;
4910 346 : if (ctx->dash_mode) ctx->flush_seg = GF_TRUE;
4911 346 : if (ctx->next_file_idx)
4912 0 : nb_suspended++;
4913 346 : nb_done ++;
4914 346 : nb_eos++;
4915 346 : break;
4916 : }
4917 : return GF_OK;
4918 : }
4919 115341 : if (tkw->aborted) {
4920 0 : gf_filter_pid_drop_packet(tkw->ipid);
4921 0 : nb_eos++;
4922 0 : nb_done ++;
4923 0 : tkw->fragment_done = GF_TRUE;
4924 0 : if (ctx->dash_mode) ctx->flush_seg = GF_TRUE;
4925 : break;
4926 : }
4927 :
4928 115341 : cts = gf_filter_pck_get_cts(pck);
4929 :
4930 115341 : if (cts == GF_FILTER_NO_TS) {
4931 104 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_EODS);
4932 104 : if (p && p->value.boolean) {
4933 104 : nb_done ++;
4934 104 : tkw->fragment_done = GF_TRUE;
4935 104 : tkw->samples_in_frag = 0;
4936 104 : gf_filter_pid_drop_packet(tkw->ipid);
4937 104 : ctx->flush_seg = GF_TRUE;
4938 104 : tkw->next_seg_cts = tkw->cts_next;
4939 104 : break;
4940 : }
4941 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MuxIsom] Packet with no CTS assigned, cannot store to track, ignoring\n"));
4942 0 : gf_filter_pid_drop_packet(tkw->ipid);
4943 0 : continue;
4944 : }
4945 :
4946 115237 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FRAG_START);
4947 115237 : if (p) {
4948 216 : orig_frag_bounds = p->value.uint;
4949 :
4950 216 : if (orig_frag_bounds==2) {
4951 84 : if (!ctx->segment_started) {
4952 0 : ctx->dash_mode = 1;
4953 0 : ctx->insert_tfdt = GF_TRUE;
4954 0 : gf_isom_start_segment(ctx->file, ctx->single_file ? NULL : "_gpac_isobmff_redirect", GF_FALSE);
4955 84 : } else if (tkw->samples_in_frag) {
4956 27 : tkw->fragment_done = GF_TRUE;
4957 27 : tkw->samples_in_frag = 0;
4958 27 : nb_done ++;
4959 : //make sure we flush until the end of the segment
4960 27 : ctx->flush_seg = GF_TRUE;
4961 : //store CTS of next packet (first in next segment) for sidx compute
4962 27 : tkw->next_seg_cts = cts;
4963 : }
4964 : }
4965 : }
4966 :
4967 : //get dash/file segment number
4968 115237 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENUM);
4969 :
4970 : //not dash and file end, we need to wait for all streams and resetup
4971 115237 : if (!ctx->dash_mode && p) {
4972 27 : if (!ctx->cur_file_idx_plus_one) {
4973 0 : ctx->cur_file_idx_plus_one = p->value.uint + 1;
4974 0 : if (!ctx->cur_file_suffix) {
4975 0 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILESUF);
4976 0 : if (p && p->value.string) ctx->cur_file_suffix = gf_strdup(p->value.string);
4977 : }
4978 0 : ctx->notify_filename = GF_TRUE;
4979 27 : } else if (ctx->cur_file_idx_plus_one == p->value.uint+1) {
4980 9 : } else if (!tkw->suspended) {
4981 9 : tkw->suspended = GF_TRUE;
4982 9 : nb_suspended++;
4983 9 : ctx->next_file_idx = p->value.uint + 1;
4984 9 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILESUF);
4985 9 : if (p && p->value.string)
4986 9 : ctx->next_file_suffix = p->value.string;
4987 : break;
4988 : }
4989 : }
4990 :
4991 :
4992 115228 : if (!ctx->fragment_started) {
4993 3571 : e = mp4_mux_start_fragment(ctx, orig_frag_bounds ? pck : NULL);
4994 3571 : if (e) return e;
4995 :
4996 3571 : ctx->nb_frags++;
4997 3571 : if (ctx->dash_mode)
4998 3491 : ctx->nb_frags_in_seg++;
4999 :
5000 : }
5001 :
5002 :
5003 115228 : if (ctx->dash_mode) {
5004 111897 : if (p) {
5005 : //start of next segment, abort fragmentation for this track and flush all other writers
5006 5895 : if (ctx->dash_seg_num_plus_one && (ctx->dash_seg_num_plus_one != 1 + p->value.uint) ) {
5007 2739 : tkw->fragment_done = GF_TRUE;
5008 2739 : tkw->samples_in_frag = 0;
5009 2739 : nb_done ++;
5010 : //make sure we flush until the end of the segment
5011 2739 : ctx->flush_seg = GF_TRUE;
5012 : //store CTS of next packet (first in next segment) for sidx compute
5013 2739 : tkw->next_seg_cts = cts;
5014 :
5015 2739 : break;
5016 : }
5017 : //start of current segment, remember segment number and name
5018 3156 : ctx->dash_seg_num_plus_one = 1 + p->value.uint;
5019 : //get file name prop if any - only send on one pid for muxed content
5020 3156 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENAME);
5021 3156 : if (p && p->value.string) {
5022 2484 : if (ctx->seg_name) gf_free(ctx->seg_name);
5023 2484 : ctx->seg_name = gf_strdup(p->value.string);
5024 : }
5025 : //store PRFT only for reference track at segment start
5026 3156 : if (tkw==ctx->ref_tkw) {
5027 3023 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_SENDER_NTP);
5028 3023 : if (p) {
5029 0 : gf_isom_set_fragment_reference_time(ctx->file, tkw->track_id, p->value.longuint, cts);
5030 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_DASH, ("[MuxIsom] Segment %s, storing NTP TS "LLU" for CTS "LLU" at "LLU" us, at UTC "LLU"\n", ctx->seg_name ? ctx->seg_name : "singlefile", p->value.longuint, cts, gf_sys_clock_high_res(), gf_net_get_utc()));
5031 : }
5032 : }
5033 : }
5034 :
5035 109158 : dts = gf_filter_pck_get_dts(pck);
5036 109158 : if (dts==GF_FILTER_NO_TS) dts = cts;
5037 109158 : if (tkw->first_dts_in_seg > dts)
5038 0 : tkw->first_dts_in_seg = dts;
5039 : }
5040 112489 : ncts = cts + gf_filter_pck_get_duration(pck);
5041 112489 : if (tkw->cts_next < ncts)
5042 101799 : tkw->cts_next = ncts;
5043 :
5044 :
5045 112489 : if (tkw->samples_in_frag && orig_frag_bounds) {
5046 66 : tkw->fragment_done = GF_TRUE;
5047 66 : nb_done ++;
5048 66 : tkw->samples_in_frag = 0;
5049 66 : tkw->dur_in_frag = 0;
5050 66 : break;
5051 112423 : } else if (ctx->fragdur && (!ctx->dash_mode || !tkw->fragment_done) ) {
5052 : Bool frag_done = GF_FALSE;
5053 6526 : u32 dur = gf_filter_pck_get_duration(pck);
5054 6526 : if (tkw->dur_in_frag && (tkw->dur_in_frag * ctx->cdur.den >= ((u64)ctx->cdur.num) * tkw->src_timescale)) {
5055 : frag_done = GF_TRUE;
5056 6039 : } else if ((ctx->store==MP4MX_MODE_SFRAG)
5057 1650 : && (cts >= (u64) (ctx->adjusted_next_frag_start * tkw->src_timescale / ctx->cdur.den) + tkw->ts_delay)
5058 : ) {
5059 : GF_FilterSAPType sap = mp4_mux_get_sap(ctx, pck);
5060 0 : if ((sap && sap<GF_FILTER_SAP_3)) {
5061 : frag_done = GF_TRUE;
5062 : }
5063 : }
5064 : if (frag_done) {
5065 487 : ctx->adjusted_next_frag_start = (cts - tkw->ts_delay);
5066 487 : ctx->adjusted_next_frag_start *= ctx->cdur.den;
5067 487 : ctx->adjusted_next_frag_start /= tkw->src_timescale;
5068 : //
5069 487 : tkw->fragment_done = GF_TRUE;
5070 487 : nb_done ++;
5071 487 : tkw->dur_in_frag = 0;
5072 487 : tkw->samples_in_frag = 0;
5073 487 : break;
5074 : }
5075 6039 : tkw->dur_in_frag += dur;
5076 6039 : if (ctx->llhls_mode && (ctx->frag_duration * tkw->src_timescale <= tkw->dur_in_frag * ctx->frag_timescale)) {
5077 1650 : ctx->frag_duration = tkw->dur_in_frag;
5078 1650 : ctx->frag_timescale = tkw->src_timescale;
5079 : }
5080 105897 : } else if (!ctx->flush_seg && !ctx->dash_mode
5081 782 : && (cts >= (u64) (ctx->adjusted_next_frag_start * tkw->src_timescale / ctx->cdur.den) + tkw->ts_delay)
5082 : ) {
5083 : GF_FilterSAPType sap = mp4_mux_get_sap(ctx, pck);
5084 22 : if ((ctx->store==MP4MX_MODE_FRAG) || (sap && sap<GF_FILTER_SAP_3)) {
5085 22 : tkw->fragment_done = GF_TRUE;
5086 22 : tkw->samples_in_frag = 0;
5087 22 : nb_done ++;
5088 22 : if (ctx->store==MP4MX_MODE_SFRAG) {
5089 0 : ctx->adjusted_next_frag_start = (cts - tkw->ts_delay);
5090 0 : ctx->adjusted_next_frag_start *= ctx->cdur.den;
5091 0 : ctx->adjusted_next_frag_start /= tkw->src_timescale;
5092 : }
5093 : break;
5094 : }
5095 : }
5096 111914 : if (tkw->insert_tfdt) {
5097 3438 : u64 odts = gf_filter_pck_get_dts(pck);
5098 3438 : if (odts==GF_FILTER_NO_TS)
5099 0 : odts = gf_filter_pck_get_cts(pck);
5100 :
5101 3438 : if (tkw->offset_dts) odts += tkw->offset_dts;
5102 :
5103 3438 : tkw->insert_tfdt = GF_FALSE;
5104 3438 : if (tkw->patch_tfdt)
5105 0 : gf_isom_set_traf_base_media_decode_time(ctx->file, tkw->track_id, odts + tkw->ts_delay);
5106 : else
5107 3438 : gf_isom_set_traf_base_media_decode_time(ctx->file, tkw->track_id, odts);
5108 3438 : tkw->first_dts_in_seg = (u64) odts;
5109 : }
5110 :
5111 111914 : if (ctx->trun_inter) {
5112 : GF_FilterSAPType sap = mp4_mux_get_sap(ctx, pck);
5113 : s32 tid_group = 0;
5114 0 : if (sap) {
5115 0 : tkw->prev_tid_group = 0;
5116 : } else {
5117 : s64 dts_diff;
5118 0 : s64 p_dts = gf_filter_pck_get_dts(pck);
5119 0 : s64 p_cts = gf_filter_pck_get_cts(pck);
5120 0 : s64 cts_o = p_cts - p_dts;
5121 0 : dts_diff = p_dts - tkw->sample.DTS;
5122 0 : tid_group = (s32) (cts_o / dts_diff);
5123 0 : tid_group = 20 - tid_group;
5124 0 : if (tid_group != tkw->prev_tid_group) {
5125 0 : tkw->prev_tid_group = tid_group;
5126 0 : gf_isom_set_fragment_option(ctx->file, tkw->track_id, GF_ISOM_TRUN_SET_INTERLEAVE_ID, tid_group);
5127 : }
5128 : }
5129 : }
5130 :
5131 : //process packet
5132 111914 : e = mp4_mux_process_sample(ctx, tkw, pck, GF_TRUE);
5133 :
5134 : //discard
5135 111914 : gf_filter_pid_drop_packet(tkw->ipid);
5136 :
5137 111914 : cts *= 1000;
5138 111914 : cts /= tkw->src_timescale;
5139 111914 : if (!ctx->min_cts_plus_one) ctx->min_cts_plus_one = cts + 1;
5140 108343 : else if (ctx->min_cts_plus_one-1 > cts) ctx->min_cts_plus_one = cts + 1;
5141 :
5142 111914 : if (e) return e;
5143 : }
5144 : //done with this track - if single track per moof, request new fragment but don't touch the
5145 : //fragmentation state of the track writers
5146 3773 : if (ctx->straf && (i+1 < count)) {
5147 : GF_ISOStartFragmentFlags flags = 0;
5148 0 : if (ctx->moof_first) flags |= GF_ISOM_FRAG_MOOF_FIRST;
5149 : #ifdef GF_ENABLE_CTRN
5150 : if (ctx->ctrn) flags |= GF_ISOM_FRAG_USE_COMPACT;
5151 : #endif
5152 0 : e = gf_isom_start_fragment(ctx->file, flags);
5153 0 : if (e) {
5154 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Unable to start new fragment: %s\n", gf_error_to_string(e) ));
5155 : return e;
5156 : }
5157 0 : gf_isom_set_next_moof_number(ctx->file, ctx->msn);
5158 0 : ctx->msn++;
5159 0 : if (ctx->sdtp_traf)
5160 0 : gf_isom_set_fragment_option(ctx->file, tkw->track_id, GF_ISOM_TRAF_USE_SAMPLE_DEPS_BOX, ctx->sdtp_traf);
5161 : }
5162 : }
5163 :
5164 : //all suspended tracks done, flush fragment
5165 3599 : if (nb_suspended && (nb_suspended==count)) {
5166 : nb_done = count;
5167 : }
5168 :
5169 :
5170 3599 : if (nb_done==count) {
5171 3571 : Bool is_eos = (count == nb_eos) ? GF_TRUE : GF_FALSE;
5172 : u32 ref_timescale;
5173 3571 : u64 next_ref_ts = ctx->ref_tkw->next_seg_cts;
5174 3571 : if (is_eos)
5175 316 : next_ref_ts = ctx->ref_tkw->cts_next;
5176 :
5177 3571 : ref_timescale = ctx->ref_tkw->src_timescale;
5178 : //both in ms
5179 3571 : ctx->next_seg_start = (u64) (next_ref_ts) * 1000 / ref_timescale;
5180 3571 : ctx->min_cts_next_frag = (u64) (ctx->next_frag_start) * 1000 / ctx->cdur.den;
5181 :
5182 3571 : ctx->next_frag_start += ctx->cdur.num;
5183 7148 : while (ctx->next_frag_start <= ctx->adjusted_next_frag_start) {
5184 6 : ctx->next_frag_start += ctx->cdur.num;
5185 : }
5186 3571 : ctx->adjusted_next_frag_start = ctx->next_frag_start;
5187 :
5188 3571 : mp4mx_frag_box_patch(ctx);
5189 :
5190 : //end of DASH segment
5191 6597 : if (ctx->dash_mode && (ctx->flush_seg || is_eos) ) {
5192 3026 : u64 offset = ctx->single_file ? ctx->current_offset : 0;
5193 : u64 idx_start_range, idx_end_range, segment_size_in_bytes;
5194 : s32 subs_sidx = -1;
5195 : u32 track_ref_id = 0;
5196 :
5197 3026 : idx_start_range = idx_end_range = 0;
5198 3026 : if (ctx->subs_sidx>=0) {
5199 : subs_sidx = ctx->subs_sidx;
5200 2293 : track_ref_id = ctx->ref_tkw->track_id;
5201 : }
5202 3026 : if (ctx->cloned_sidx && (ctx->subs_sidx!=-2) ) {
5203 57 : subs_sidx = (s32) ctx->cloned_sidx->nb_refs;
5204 57 : track_ref_id = ctx->cloned_sidx->reference_ID;
5205 57 : gf_isom_box_del((GF_Box *)ctx->cloned_sidx);
5206 57 : ctx->cloned_sidx = NULL;
5207 : }
5208 :
5209 3026 : e = gf_isom_close_segment(ctx->file, subs_sidx, track_ref_id, ctx->ref_tkw->first_dts_in_seg, ctx->ref_tkw->ts_delay, next_ref_ts, ctx->chain_sidx, ctx->ssix, ctx->sseg ? GF_FALSE : is_eos, GF_FALSE, ctx->eos_marker, &idx_start_range, &idx_end_range, &segment_size_in_bytes);
5210 3026 : if (e) return e;
5211 :
5212 3026 : GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MP4Mux] Done writing segment %d - estimated next fragment times start %g end %g\n", ctx->dash_seg_num_plus_one - 1, ((Double)next_ref_ts)/ref_timescale, ((Double)ctx->next_frag_start)/ctx->cdur.den ));
5213 :
5214 3026 : if (ctx->dash_mode != MP4MX_DASH_VOD) {
5215 2750 : mp4_mux_flush_seg(ctx, GF_FALSE, offset + idx_start_range, idx_end_range ? offset + idx_end_range : 0);
5216 276 : } else if (ctx->vodcache==MP4MX_VODCACHE_REPLACE) {
5217 0 : mp4_mux_flush_seg(ctx, GF_FALSE, 0, 0);
5218 : } else {
5219 276 : if (ctx->nb_seg_sizes == ctx->alloc_seg_sizes) {
5220 33 : ctx->alloc_seg_sizes *= 2;
5221 33 : if (!ctx->alloc_seg_sizes) ctx->alloc_seg_sizes = 10;
5222 33 : ctx->seg_sizes = gf_realloc(ctx->seg_sizes, sizeof(u32) * ctx->alloc_seg_sizes);
5223 : }
5224 : assert(segment_size_in_bytes);
5225 276 : ctx->seg_sizes[ctx->nb_seg_sizes] = (u32) segment_size_in_bytes;
5226 276 : ctx->nb_seg_sizes++;
5227 : }
5228 : }
5229 : //cannot flush in DASH mode if using sidx (vod single sidx or live 1 sidx/seg)
5230 545 : else if (!ctx->dash_mode || ((ctx->subs_sidx<0) && (ctx->dash_mode<MP4MX_DASH_VOD) && !ctx->cloned_sidx) ) {
5231 453 : gf_isom_flush_fragments(ctx->file, GF_FALSE);
5232 :
5233 453 : if (ctx->llhls_mode) {
5234 279 : mp4_mux_flush_frag_hls(ctx);
5235 : }
5236 :
5237 453 : if (!ctx->dash_mode || ctx->flush_seg) {
5238 80 : mp4_mux_flush_seg(ctx, GF_FALSE, 0, 0);
5239 : }
5240 :
5241 453 : GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[MP4Mux] Done writing fragment - next fragment start time %g\n", ((Double)ctx->next_frag_start)/ctx->cdur.den ));
5242 : }
5243 3571 : ctx->fragment_started = GF_FALSE;
5244 :
5245 3571 : if (ctx->flush_seg) {
5246 3026 : ctx->segment_started = GF_FALSE;
5247 3026 : ctx->flush_seg = GF_FALSE;
5248 3026 : ctx->dash_seg_num_plus_one = 0;
5249 3026 : ctx->nb_segs++;
5250 3026 : ctx->nb_frags_in_seg=0;
5251 : }
5252 : }
5253 :
5254 3599 : if (nb_suspended && (nb_suspended==count)) {
5255 4 : ctx->nb_segs=0;
5256 4 : return mp4mx_reload_output(filter, ctx);
5257 : }
5258 :
5259 3620 : check_eos:
5260 3620 : if (count == nb_eos) {
5261 341 : if (ctx->dash_mode==MP4MX_DASH_VOD) {
5262 17 : if (ctx->vodcache!=MP4MX_VODCACHE_ON) {
5263 0 : ctx->final_sidx_flush = GF_TRUE;
5264 : //flush SIDX in given space - this will reserve 8 bytes for free box if not fitting
5265 0 : gf_isom_flush_sidx(ctx->file, ctx->sidx_max_size, (ctx->sidx_size_exact || ctx->tfdt64) ? GF_TRUE : GF_FALSE);
5266 : } else {
5267 : u64 start_offset;
5268 : //reenable packet dispatch
5269 17 : ctx->store_output = GF_FALSE;
5270 17 : gf_isom_flush_sidx(ctx->file, 0, ctx->tfdt64);
5271 : //flush sidx packet
5272 17 : mp4mux_send_output(ctx);
5273 :
5274 17 : mp4_mux_flush_seg(ctx, GF_TRUE, ctx->current_offset, ctx->current_offset + ctx->current_size - 1);
5275 :
5276 17 : gf_fflush(ctx->tmp_store);
5277 17 : ctx->flush_size = gf_ftell(ctx->tmp_store);
5278 17 : ctx->flush_done = 0;
5279 17 : gf_fseek(ctx->tmp_store, 0, SEEK_SET);
5280 :
5281 17 : if (ctx->seg_sizes) {
5282 17 : start_offset = ctx->current_offset;
5283 293 : for (i=0; i<ctx->nb_seg_sizes; i++) {
5284 276 : ctx->current_size = ctx->seg_sizes[i];
5285 276 : mp4_mux_flush_seg(ctx, GF_FALSE, 0, 0);
5286 : }
5287 17 : ctx->current_offset = start_offset;
5288 17 : ctx->current_size = 0;
5289 :
5290 17 : gf_free(ctx->seg_sizes);
5291 17 : ctx->seg_sizes = NULL;
5292 17 : ctx->alloc_seg_sizes = ctx->nb_seg_sizes = 0;
5293 : }
5294 : }
5295 : }
5296 : //only destroy file if not dash or not onDemand, otherwise (regular dash) the file will be needed to append further segments
5297 341 : if (ctx->dash_mode!=MP4MX_DASH_ON) {
5298 : //only delete file in vod mode
5299 26 : if (ctx->file) {
5300 26 : gf_isom_close(ctx->file);
5301 26 : ctx->file = NULL;
5302 : }
5303 : }
5304 :
5305 341 : mp4mux_send_output(ctx);
5306 :
5307 341 : if (!ctx->flush_size) gf_filter_pid_set_eos(ctx->opid);
5308 :
5309 341 : return ctx->flush_size ? GF_OK : GF_EOS;
5310 : }
5311 :
5312 : return GF_OK;
5313 : }
5314 :
5315 2885 : static void mp4_mux_config_timing(GF_MP4MuxCtx *ctx)
5316 : {
5317 2886 : u32 i, count = gf_list_count(ctx->tracks);
5318 : //compute min dts of first packet on each track - this assume all tracks are synchronized, might need adjustment for MPEG4 Systems
5319 : u64 first_ts_min = (u64) -1;
5320 5100 : for (i=0; i<count; i++) {
5321 : u64 ts, dts_min;
5322 : GF_FilterPacket *pck;
5323 3801 : TrackWriter *tkw = gf_list_get(ctx->tracks, i);
5324 3801 : if (tkw->fake_track) continue;
5325 :
5326 : //already setup (happens when new PIDs are declared after a packet has already been written on other PIDs)
5327 3774 : if (tkw->nb_samples) {
5328 0 : dts_min = tkw->ts_shift * 1000000;
5329 0 : dts_min /= tkw->src_timescale;
5330 :
5331 0 : if (first_ts_min > dts_min) {
5332 : first_ts_min = (u64) dts_min;
5333 : }
5334 0 : continue;
5335 : }
5336 3774 : retry_pck:
5337 3774 : pck = gf_filter_pid_get_packet(tkw->ipid);
5338 : //check this after fetching a packet since it may reconfigure the track
5339 3774 : if (!tkw->track_num) {
5340 577 : if (gf_filter_pid_is_eos(tkw->ipid)) {
5341 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] PID has no input packet and configuration not known after 10 retries, aborting initial timing sync\n"));
5342 0 : continue;
5343 : }
5344 : return;
5345 : }
5346 :
5347 3197 : if (pck) {
5348 2179 : if (tkw->wait_sap) {
5349 0 : GF_FilterSAPType sap = gf_filter_pck_get_sap(pck);
5350 0 : Bool seek = gf_filter_pck_get_seek_flag(pck);
5351 0 : if (seek || !sap) {
5352 0 : gf_filter_pid_drop_packet(tkw->ipid);
5353 0 : goto retry_pck;
5354 : } else {
5355 0 : if (sap)
5356 0 : tkw->wait_sap = GF_FALSE;
5357 :
5358 0 : if (!ctx->wait_dts_plus_one) {
5359 0 : ctx->wait_dts_plus_one = 1 + gf_filter_pck_get_dts(pck);
5360 0 : ctx->wait_dts_timescale = tkw->src_timescale;
5361 : }
5362 : }
5363 : }
5364 :
5365 2179 : if (ctx->wait_dts_plus_one) {
5366 0 : ts = gf_filter_pck_get_dts(pck);
5367 0 : if (ts==GF_FILTER_NO_TS)
5368 0 : ts = gf_filter_pck_get_cts(pck);
5369 0 : if (ts==GF_FILTER_NO_TS)
5370 : ts=0;
5371 :
5372 0 : if (ts * ctx->wait_dts_timescale < (ctx->wait_dts_plus_one-1) * tkw->src_timescale) {
5373 0 : gf_filter_pid_drop_packet(tkw->ipid);
5374 0 : goto retry_pck;
5375 : }
5376 : }
5377 : }
5378 :
5379 3197 : if (!pck) {
5380 1018 : if (gf_filter_pid_is_eos(tkw->ipid)) {
5381 9 : if (tkw->cenc_state==CENC_NEED_SETUP)
5382 1 : mp4_mux_cenc_update(ctx, tkw, NULL, CENC_CONFIG, 0);
5383 :
5384 9 : if (!tkw->nb_samples) {
5385 9 : const GF_PropertyValue *p = gf_filter_pid_get_property(tkw->ipid, GF_PROP_PID_ISOM_TREX_TEMPLATE);
5386 9 : if (p) {
5387 2 : gf_isom_setup_track_fragment_template(ctx->file, tkw->track_id, p->value.data.ptr, p->value.data.size, ctx->nofragdef);
5388 : }
5389 : }
5390 9 : continue;
5391 : }
5392 : return;
5393 : }
5394 : //we may have reorder tracks after the get_packet, redo
5395 2179 : if (gf_list_find(ctx->tracks, tkw) != i) {
5396 : mp4_mux_config_timing(ctx);
5397 : return;
5398 : }
5399 2178 : ts = gf_filter_pck_get_dts(pck);
5400 2178 : if (ts==GF_FILTER_NO_TS)
5401 0 : ts = gf_filter_pck_get_cts(pck);
5402 2178 : if (ts==GF_FILTER_NO_TS)
5403 : ts=0;
5404 :
5405 2178 : dts_min = ts * 1000000;
5406 2178 : dts_min /= tkw->src_timescale;
5407 :
5408 2178 : if (first_ts_min > dts_min) {
5409 : first_ts_min = (u64) dts_min;
5410 : }
5411 2178 : tkw->ts_shift = ts;
5412 : }
5413 1299 : if (first_ts_min==(u64)-1)
5414 : first_ts_min = 0;
5415 :
5416 : //for all packets with dts greater than min dts, we need to add a pause
5417 2747 : for (i=0; i<count; i++) {
5418 : s64 dts_diff, dur;
5419 1448 : TrackWriter *tkw = gf_list_get(ctx->tracks, i);
5420 :
5421 : //compute offsets
5422 1448 : dts_diff = first_ts_min;
5423 1448 : dts_diff *= tkw->src_timescale;
5424 1448 : dts_diff /= 1000000;
5425 1448 : dts_diff = (s64) tkw->ts_shift - dts_diff;
5426 1448 : if (ctx->is_rewind) dts_diff = -dts_diff;
5427 : //negative could happen due to rounding, ignore them
5428 1448 : if (dts_diff<=0) continue;
5429 :
5430 : // dts_diff > 0, we need to delay the track
5431 : dur = dts_diff;
5432 85 : dur *= (u32) ctx->moovts;
5433 85 : dur /= tkw->src_timescale;
5434 85 : if (dur) {
5435 52 : gf_isom_remove_edits(ctx->file, tkw->track_num);
5436 :
5437 52 : gf_isom_set_edit(ctx->file, tkw->track_num, 0, dur, dts_diff, GF_ISOM_EDIT_EMPTY);
5438 52 : gf_isom_set_edit(ctx->file, tkw->track_num, dur, 0, 0, GF_ISOM_EDIT_NORMAL);
5439 52 : tkw->empty_init_dur = (u64) dur;
5440 : }
5441 : }
5442 :
5443 1299 : ctx->config_timing = GF_FALSE;
5444 : }
5445 :
5446 684042 : void mp4_mux_format_report(GF_Filter *filter, GF_MP4MuxCtx *ctx, u64 done, u64 total)
5447 : {
5448 : Bool status_changed=GF_FALSE;
5449 : u32 total_pc = 0;
5450 : char szStatus[2048], szTK[20];
5451 684042 : if (!gf_filter_reporting_enabled(filter))
5452 684042 : return;
5453 0 : if (!ctx->update_report)
5454 : return;
5455 :
5456 0 : ctx->update_report = GF_FALSE;
5457 :
5458 0 : if (ctx->config_timing) {
5459 : sprintf(szStatus, "waiting for clock init");
5460 : status_changed = GF_TRUE;
5461 0 : } else if (total) {
5462 0 : if (done>=total) {
5463 : Double ohead = 0;
5464 0 : if (ctx->total_bytes_in) ohead = ((Double) (ctx->total_bytes_out - ctx->total_bytes_in)*100 / ctx->total_bytes_in);
5465 :
5466 0 : sprintf(szStatus, "done %d samples - bytes "LLU" in "LLU" out - overhead %02.02f%% (%02.02g B/sample)", ctx->total_samples, ctx->total_bytes_in, ctx->total_bytes_out, ohead, ((Double)(ctx->total_bytes_out-ctx->total_bytes_in))/ctx->total_samples);
5467 : status_changed = GF_TRUE;
5468 : total_pc = 10000;
5469 :
5470 : } else {
5471 0 : u32 pc = (u32) ((done*10000)/total);
5472 0 : if (ctx->last_mux_pc == pc + 1) return;
5473 0 : ctx->last_mux_pc = pc + 1;
5474 : sprintf(szStatus, "mux %d%%", pc);
5475 : status_changed = GF_TRUE;
5476 : }
5477 : } else {
5478 0 : u32 i, count = gf_list_count(ctx->tracks);
5479 : Bool is_frag = GF_FALSE;
5480 :
5481 0 : if (ctx->store>=MP4MX_MODE_FRAG) {
5482 0 : Double next = ((Double)ctx->next_frag_start)/ctx->cdur.den;
5483 : is_frag = GF_TRUE;
5484 0 : if (ctx->dash_mode) {
5485 0 : sprintf(szStatus, "mux segments %d (frags %d) next %02.02g", ctx->nb_segs, ctx->nb_frags_in_seg, next);
5486 : } else {
5487 0 : sprintf(szStatus, "mux frags %d next %02.02g", ctx->nb_frags, next);
5488 : }
5489 : } else {
5490 0 : sprintf(szStatus, "%s", ((ctx->store==MP4MX_MODE_FLAT) || (ctx->store==MP4MX_MODE_FASTSTART)) ? "mux" : "import");
5491 : }
5492 0 : for (i=0; i<count; i++) {
5493 : u32 pc=0;
5494 0 : TrackWriter *tkw = gf_list_get(ctx->tracks, i);
5495 0 : if (tkw->aborted) {
5496 : pc=10000;
5497 0 : } else if (ctx->idur.num) {
5498 0 : if (ctx->idur.num>0) {
5499 0 : u64 mdur = gf_isom_get_media_duration(ctx->file, tkw->track_num);
5500 0 : u64 tk_done = mdur * ctx->idur.den;
5501 0 : u64 tk_total = ((u64)tkw->tk_timescale) * ctx->idur.num;
5502 0 : pc = (u32) ((tk_done*10000)/tk_total);
5503 : } else {
5504 0 : pc = (u32) ( (10000 * (u64) tkw->nb_samples) / (-ctx->idur.num) );
5505 : }
5506 : } else {
5507 0 : if (tkw->nb_frames) {
5508 0 : pc = (u32) ( (10000 * (u64) tkw->nb_samples) / tkw->nb_frames);
5509 : } else {
5510 0 : if (tkw->pid_dur.num && tkw->pid_dur.den) {
5511 0 : pc = (u32) ((tkw->sample.DTS*10000 * tkw->pid_dur.den) / (tkw->pid_dur.num * tkw->tk_timescale));
5512 0 : } else if (tkw->down_bytes && tkw->down_size) {
5513 0 : pc = (u32) (((tkw->down_bytes*10000) / tkw->down_size));
5514 : }
5515 : }
5516 : }
5517 0 : if (pc>10000)
5518 : pc=0;
5519 0 : if (tkw->last_import_pc != pc + 1) {
5520 : status_changed = GF_TRUE;
5521 0 : tkw->last_import_pc = pc + 1;
5522 : }
5523 0 : if (!total_pc || (total_pc > pc))
5524 : total_pc = pc;
5525 :
5526 0 : if (is_frag) {
5527 0 : sprintf(szTK, " TK%d(%c): %d", tkw->track_id, tkw->status_type, tkw->samples_in_frag);
5528 : strcat(szStatus, szTK);
5529 : status_changed = GF_TRUE;
5530 0 : if (pc) {
5531 0 : sprintf(szTK, " %d %%", pc/100);
5532 : strcat(szStatus, szTK);
5533 : }
5534 : } else {
5535 0 : sprintf(szTK, " %s%d(%c): %d %%", tkw->is_item ? "IT" : "TK", tkw->track_id, tkw->status_type, pc/100);
5536 : strcat(szStatus, szTK);
5537 : }
5538 : }
5539 : }
5540 0 : if (status_changed) {
5541 0 : gf_filter_update_status(filter, total_pc, szStatus);
5542 : }
5543 : }
5544 :
5545 :
5546 571765 : GF_Err mp4_mux_process(GF_Filter *filter)
5547 : {
5548 571765 : GF_MP4MuxCtx *ctx = gf_filter_get_udta(filter);
5549 571765 : u32 nb_skip, nb_eos, nb_suspended, i, count = gf_list_count(ctx->tracks);
5550 : nb_skip = 0;
5551 : nb_eos = 0;
5552 :
5553 571765 : if (ctx->config_timing) {
5554 2885 : mp4_mux_config_timing(ctx);
5555 2885 : if (ctx->config_timing) {
5556 1586 : mp4_mux_format_report(filter, ctx, 0, 0);
5557 1586 : return GF_OK;
5558 : }
5559 : }
5560 :
5561 : //fragmented mode
5562 570179 : if (ctx->store>=MP4MX_MODE_FRAG) {
5563 : u32 done=0;
5564 120838 : GF_Err e = mp4_mux_process_fragmented(filter, ctx);
5565 120838 : if (e==GF_EOS) done=100;
5566 120838 : mp4_mux_format_report(filter, ctx, done, done);
5567 120838 : return e;
5568 : }
5569 :
5570 : //regular mode
5571 : nb_suspended = 0;
5572 576735 : for (i=0; i<count; i++) {
5573 576735 : TrackWriter *tkw = gf_list_get(ctx->tracks, i);
5574 576735 : GF_FilterPacket *pck = gf_filter_pid_get_packet(tkw->ipid);
5575 :
5576 576735 : if (tkw->suspended) {
5577 319 : nb_suspended++;
5578 319 : continue;
5579 : }
5580 :
5581 576416 : if (!pck) {
5582 125633 : if (gf_filter_pid_is_eos(tkw->ipid)) {
5583 14866 : tkw->suspended = GF_FALSE;
5584 14866 : nb_eos++;
5585 : }
5586 125633 : if (tkw->aborted) {
5587 571 : nb_eos++;
5588 : }
5589 125633 : continue;
5590 : }
5591 :
5592 450783 : if (tkw->aborted) {
5593 8 : gf_filter_pid_drop_packet(tkw->ipid);
5594 8 : nb_eos++;
5595 8 : continue;
5596 : }
5597 :
5598 450775 : if (ctx->owns_mov) {
5599 : const GF_PropertyValue *p;
5600 94695 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILENUM);
5601 94695 : if (p) {
5602 213 : if (!ctx->cur_file_idx_plus_one) {
5603 20 : ctx->cur_file_idx_plus_one = p->value.uint + 1;
5604 20 : if (!ctx->cur_file_suffix) {
5605 20 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILESUF);
5606 20 : if (p && p->value.string) ctx->cur_file_suffix = gf_strdup(p->value.string);
5607 : }
5608 20 : ctx->notify_filename = GF_TRUE;
5609 193 : } else if (ctx->cur_file_idx_plus_one == p->value.uint+1) {
5610 89 : } else if (!tkw->suspended) {
5611 89 : tkw->suspended = GF_TRUE;
5612 89 : nb_suspended++;
5613 89 : ctx->next_file_idx = p->value.uint + 1;
5614 89 : p = gf_filter_pck_get_property(pck, GF_PROP_PCK_FILESUF);
5615 89 : if (p && p->value.string)
5616 89 : ctx->next_file_suffix = p->value.string;
5617 89 : continue;
5618 : }
5619 : }
5620 : }
5621 :
5622 : //basic regulation in case we do on-the-fly interleaving
5623 : //we need to regulate because sources do not produce packets at the same rate
5624 450686 : if ((ctx->store==MP4MX_MODE_FASTSTART) && ctx->cdur.num) {
5625 1304 : u64 cts = gf_filter_pck_get_cts(pck);
5626 1304 : if (ctx->is_rewind)
5627 0 : cts = tkw->ts_shift - cts;
5628 : else
5629 1304 : cts -= tkw->ts_shift;
5630 :
5631 1304 : if (!ctx->faststart_ts_regulate.num) {
5632 2 : ctx->faststart_ts_regulate = ctx->cdur;
5633 : }
5634 : //ahead of our interleaving window, don't write yet
5635 1302 : else if (cts * ctx->faststart_ts_regulate.den > ((u64) ctx->faststart_ts_regulate.num) * tkw->src_timescale) {
5636 298 : nb_skip++;
5637 298 : continue;
5638 : }
5639 : }
5640 :
5641 450388 : if (tkw->cenc_state==CENC_NEED_SETUP)
5642 95 : mp4_mux_cenc_update(ctx, tkw, pck, CENC_CONFIG, 0);
5643 :
5644 450388 : if (tkw->is_item) {
5645 64 : mp4_mux_process_item(ctx, tkw, pck);
5646 : } else {
5647 450324 : mp4_mux_process_sample(ctx, tkw, pck, GF_FALSE);
5648 : }
5649 :
5650 450388 : gf_filter_pid_drop_packet(tkw->ipid);
5651 450388 : if (tkw->aborted) {
5652 82 : nb_eos++;
5653 : }
5654 : }
5655 449341 : mp4_mux_format_report(filter, ctx, 0, 0);
5656 :
5657 449341 : if (nb_suspended && (nb_suspended+nb_eos==count)) {
5658 56 : return mp4mx_reload_output(filter, ctx);
5659 : }
5660 :
5661 449285 : if (count == nb_eos) {
5662 4268 : if (ctx->file) {
5663 932 : GF_Err e = mp4_mux_done(filter, ctx, GF_TRUE);
5664 932 : if (e) return e;
5665 : }
5666 : return GF_EOS;
5667 : }
5668 : //done with this interleaving window, start next one
5669 445017 : else if (nb_skip + nb_eos == count) {
5670 21 : ctx->faststart_ts_regulate.num += ctx->cdur.num;
5671 444996 : } else if (ctx->importer) {
5672 : u64 prog_done=0, prog_total=0;
5673 362608 : for (i=0; i<count; i++) {
5674 362608 : TrackWriter *tkw = gf_list_get(ctx->tracks, i);
5675 362608 : if (prog_done * tkw->prog_total <= tkw->prog_done * prog_total) {
5676 : prog_done = tkw->prog_done;
5677 : prog_total = tkw->prog_total;
5678 : }
5679 : }
5680 353532 : gf_set_progress("Import", prog_done, prog_total);
5681 : }
5682 :
5683 : return GF_OK;
5684 : }
5685 :
5686 2 : static GF_Err mp4_mux_on_data_patch(void *cbk, u8 *data, u32 block_size, u64 file_offset, Bool is_insert)
5687 : {
5688 : GF_Filter *filter = (GF_Filter *) cbk;
5689 : u8 *output;
5690 2 : GF_MP4MuxCtx *ctx = gf_filter_get_udta(filter);
5691 :
5692 2 : GF_FilterPacket *pck = gf_filter_pck_new_alloc(ctx->opid, block_size, &output);
5693 2 : if (!pck) return GF_OUT_OF_MEM;
5694 :
5695 2 : memcpy(output, data, block_size);
5696 2 : gf_filter_pck_set_framing(pck, GF_FALSE, GF_FALSE);
5697 2 : gf_filter_pck_set_seek_flag(pck, GF_TRUE);
5698 2 : if (is_insert)
5699 1 : gf_filter_pck_set_interlaced(pck, 1);
5700 2 : gf_filter_pck_set_byte_offset(pck, file_offset);
5701 2 : gf_filter_pck_send(pck);
5702 2 : return GF_OK;
5703 : }
5704 :
5705 21222 : static GF_Err mp4_mux_on_data(void *cbk, u8 *data, u32 block_size)
5706 : {
5707 : GF_Filter *filter = (GF_Filter *) cbk;
5708 : u8 *output;
5709 21222 : GF_MP4MuxCtx *ctx = gf_filter_get_udta(filter);
5710 :
5711 21222 : ctx->total_bytes_out += block_size;
5712 :
5713 : //flush pending packet in frag mode
5714 21222 : mp4mux_send_output(ctx);
5715 :
5716 21222 : if (ctx->final_sidx_flush) {
5717 : GF_FilterPacket *pck;
5718 : u32 free_size=0;
5719 :
5720 0 : if (ctx->vodcache==MP4MX_VODCACHE_INSERT) {
5721 0 : pck = gf_filter_pck_new_alloc(ctx->opid, block_size, &output);
5722 0 : if (!pck) return GF_OUT_OF_MEM;
5723 :
5724 0 : memcpy(output, data, block_size);
5725 0 : gf_filter_pck_set_framing(pck, GF_FALSE, GF_FALSE);
5726 0 : gf_filter_pck_set_byte_offset(pck, ctx->sidx_chunk_offset);
5727 0 : gf_filter_pck_set_seek_flag(pck, GF_TRUE);
5728 0 : gf_filter_pck_set_interlaced(pck, 1);
5729 0 : gf_filter_pck_send(pck);
5730 : } else {
5731 : GF_BitStream *bs;
5732 : assert(!ctx->dst_pck);
5733 :
5734 0 : if (block_size > ctx->sidx_max_size) {
5735 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Final SIDX chunk larger than preallocated block, will not flush SIDX (output file still readable). Try disabling nocache mode\n"));
5736 : return GF_OK;
5737 : }
5738 0 : free_size = ctx->sidx_max_size - block_size;
5739 0 : pck = gf_filter_pck_new_alloc(ctx->opid, ctx->sidx_max_size, &output);
5740 0 : if (!pck) return GF_OUT_OF_MEM;
5741 :
5742 0 : gf_filter_pck_set_framing(pck, GF_FALSE, GF_FALSE);
5743 0 : gf_filter_pck_set_byte_offset(pck, ctx->sidx_chunk_offset);
5744 0 : gf_filter_pck_set_seek_flag(pck, GF_TRUE);
5745 0 : bs = gf_bs_new(output, ctx->sidx_max_size, GF_BITSTREAM_WRITE);
5746 0 : if (free_size) {
5747 0 : gf_bs_write_u32(bs, free_size);
5748 0 : gf_bs_write_u32(bs, GF_ISOM_BOX_TYPE_FREE);
5749 0 : gf_bs_skip_bytes(bs, free_size-8);
5750 : }
5751 0 : gf_bs_write_data(bs, data, block_size);
5752 0 : gf_bs_del(bs);
5753 0 : gf_filter_pck_send(pck);
5754 : }
5755 0 : mp4_mux_flush_seg(ctx, GF_TRUE, ctx->sidx_chunk_offset+free_size, ctx->sidx_chunk_offset+free_size + block_size - 1);
5756 0 : return GF_OK;
5757 : }
5758 :
5759 21222 : if (ctx->store_output) {
5760 276 : u32 nb_write = (u32) gf_fwrite(data, block_size, ctx->tmp_store);
5761 276 : if (nb_write != block_size) {
5762 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Error writing to temp cache: %d bytes write instead of %d\n", nb_write, block_size));
5763 : return GF_IO_ERR;
5764 : }
5765 : return GF_OK;
5766 : }
5767 :
5768 : //allocate new one
5769 20946 : ctx->dst_pck = gf_filter_pck_new_alloc(ctx->opid, block_size, &output);
5770 20946 : if (!ctx->dst_pck) return GF_OUT_OF_MEM;
5771 :
5772 20946 : memcpy(output, data, block_size);
5773 20946 : gf_filter_pck_set_framing(ctx->dst_pck, !ctx->first_pck_sent, GF_FALSE);
5774 :
5775 : //set packet prop as string since we may discard the seg_name packet before this packet is processed
5776 20946 : if (!ctx->first_pck_sent && ctx->seg_name) {
5777 2484 : ctx->current_offset = 0;
5778 2484 : gf_filter_pck_set_property(ctx->dst_pck, GF_PROP_PCK_FILENAME, &PROP_STRING(ctx->seg_name) );
5779 2484 : gf_filter_pck_set_property(ctx->dst_pck, GF_PROP_PCK_FILENUM, &PROP_UINT(ctx->dash_seg_num_plus_one-1) );
5780 : }
5781 20946 : if (ctx->min_cts_plus_one) {
5782 4354 : u64 orig = ctx->min_cts_plus_one-1;
5783 4354 : gf_filter_pck_set_cts(ctx->dst_pck, orig);
5784 4354 : gf_filter_pck_set_duration(ctx->dst_pck, (u32) (ctx->min_cts_next_frag - orig) );
5785 : }
5786 :
5787 20946 : if ((ctx->llhls_mode>1) && ctx->fragment_started && !ctx->frag_size && ctx->dst_pck) {
5788 130 : ctx->frag_num++;
5789 130 : gf_filter_pck_set_property(ctx->dst_pck, GF_PROP_PCK_HLS_FRAG_NUM, &PROP_UINT(ctx->frag_num));
5790 : }
5791 20946 : ctx->frag_size += block_size;
5792 :
5793 20946 : ctx->first_pck_sent = GF_TRUE;
5794 20946 : ctx->current_size += block_size;
5795 : //non-frag mode, send right away
5796 20946 : if (ctx->store<MP4MX_MODE_FRAG) {
5797 16257 : mp4mux_send_output(ctx);
5798 : }
5799 : return GF_OK;
5800 : }
5801 :
5802 112277 : void mp4_mux_progress_cbk(void *udta, u64 done, u64 total)
5803 : {
5804 : GF_Filter *filter = (GF_Filter *)udta;
5805 112277 : GF_MP4MuxCtx *ctx = gf_filter_get_udta(filter);
5806 112277 : ctx->update_report = GF_TRUE;
5807 112277 : mp4_mux_format_report(filter, ctx, done, total);
5808 112277 : }
5809 :
5810 1332 : static GF_Err mp4_mux_initialize(GF_Filter *filter)
5811 : {
5812 1332 : GF_MP4MuxCtx *ctx = gf_filter_get_udta(filter);
5813 1332 : gf_filter_set_max_extra_input_pids(filter, -1);
5814 :
5815 1332 : if (ctx->file) {
5816 608 : if (gf_isom_get_mode(ctx->file) < GF_ISOM_OPEN_WRITE) return GF_BAD_PARAM;
5817 608 : if (ctx->store>=MP4MX_MODE_FRAG) {
5818 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Cannot use fragmented output on already opened ISOBMF file\n"));
5819 : return GF_BAD_PARAM;
5820 : }
5821 608 : ctx->owns_mov = GF_FALSE;
5822 608 : gf_filter_act_as_sink(filter);
5823 : } else {
5824 : u32 open_mode = GF_ISOM_OPEN_WRITE;
5825 724 : ctx->owns_mov = GF_TRUE;
5826 :
5827 724 : switch (ctx->store) {
5828 383 : case MP4MX_MODE_INTER:
5829 : case MP4MX_MODE_TIGHT:
5830 : open_mode = GF_ISOM_WRITE_EDIT;
5831 383 : break;
5832 : }
5833 724 : ctx->file = gf_isom_open("_gpac_isobmff_redirect", open_mode, NULL);
5834 724 : if (!ctx->file) return GF_OUT_OF_MEM;
5835 :
5836 724 : gf_isom_set_write_callback(ctx->file, mp4_mux_on_data, mp4_mux_on_data_patch, filter, ctx->block_size);
5837 :
5838 724 : gf_isom_set_progress_callback(ctx->file, mp4_mux_progress_cbk, filter);
5839 :
5840 724 : if (ctx->dref && (ctx->store>=MP4MX_MODE_FRAG)) {
5841 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Cannot use data reference in movie fragments, not supported. Ignoring it\n"));
5842 0 : ctx->dref = GF_FALSE;
5843 : }
5844 :
5845 724 : if (ctx->store==MP4MX_MODE_FASTSTART) {
5846 1 : gf_isom_set_storage_mode(ctx->file, GF_ISOM_STORE_FASTSTART);
5847 : }
5848 :
5849 : }
5850 1332 : if (!ctx->moovts)
5851 0 : ctx->moovts=600;
5852 1332 : if (!ctx->cdur.den) {
5853 0 : ctx->cdur.num = 0;
5854 0 : ctx->cdur.den = 1000;
5855 : }
5856 : //we need at least ms precision for sfrag mode
5857 1332 : if (ctx->cdur.den < 1000) {
5858 1262 : ctx->cdur.num = (s32) ( ((s64)ctx->cdur.num) * 1000 / ctx->cdur.den);
5859 1262 : ctx->cdur.den = 1000;
5860 : }
5861 :
5862 1332 : if (ctx->mfra && (ctx->store>=MP4MX_MODE_FRAG)) {
5863 1 : GF_Err e = gf_isom_enable_mfra(ctx->file);
5864 1 : if (e) return e;
5865 : }
5866 :
5867 1332 : if (!ctx->tracks)
5868 1272 : ctx->tracks = gf_list_new();
5869 :
5870 : #ifdef GF_ENABLE_CTRN
5871 : if (ctx->ctrni)
5872 : ctx->ctrn = GF_TRUE;
5873 : #endif
5874 :
5875 1332 : if (ctx->m4cc) {
5876 0 : if (strlen(ctx->m4cc)==4)
5877 0 : ctx->eos_marker = GF_4CC(ctx->m4cc[0], ctx->m4cc[1], ctx->m4cc[2], ctx->m4cc[3]);
5878 : else {
5879 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Invalid segment marker 4cc %s, ignoring\n", ctx->m4cc));
5880 : }
5881 : }
5882 1332 : if (ctx->compress) {
5883 : u32 flags = 0;
5884 2 : if (ctx->fcomp) flags |= GF_ISOM_COMP_FORCE_ALL;
5885 2 : if (ctx->otyp) flags |= GF_ISOM_COMP_WRAP_FTYPE;
5886 2 : gf_isom_enable_compression(ctx->file, ctx->compress, flags);
5887 : }
5888 :
5889 1332 : if (ctx->cmaf) {
5890 : //cf table 3, 4, 5 of CMAF
5891 0 : ctx->mvex = GF_TRUE;
5892 0 : ctx->truns_first = GF_TRUE;
5893 : //single trun, single traf (table 5 of CMAF)
5894 0 : ctx->strun = GF_TRUE;
5895 0 : ctx->straf = GF_TRUE;
5896 : //7.5.16 Every TrackFragmentBox shall contain a TrackFragmentBaseMediaDecodeTimeBox
5897 0 : ctx->tfdt_traf = GF_TRUE;
5898 : //7.3.3 : If SegmentIndexBoxes exist, each subsegment referenced in the SegmentIndexBox shall be a single CMAF fragment
5899 0 : ctx->chain_sidx = GF_FALSE;
5900 0 : if (ctx->subs_sidx>0)
5901 0 : ctx->subs_sidx = 0;
5902 :
5903 0 : if (ctx->cmaf==MP4MX_CMAF_CMF2) {
5904 : /*7.7 cmf2
5905 : - default_sample_flags, sample_flags and first_sample_flags shall be set in the TrackFragmentHeaderBox and/or TrackRunBox to provide sample dependency information within each CMAF chunk and CMAF fragment.
5906 : - Default values or per sample values of sample duration and sample size shall be stored in each CMAF chunk’s TrackRunBox and/or TrackFragmentHeaderBox
5907 : */
5908 0 : ctx->nofragdef = GF_TRUE;
5909 : }
5910 : }
5911 : return GF_OK;
5912 : }
5913 :
5914 : //old code, commented - we track min/max cts while adding to avoid the time-consuming rescan below
5915 : #if 0
5916 : static void mp4_mux_get_min_max_cts(GF_MP4MuxCtx *ctx, TrackWriter *tkw, u64 *omax_cts, u64 *omin_cts)
5917 : {
5918 : u32 i, count, di;
5919 : u64 max_cts, min_cts, doff;
5920 :
5921 : count = gf_isom_get_sample_count(ctx->file, tkw->track_num);
5922 : max_cts = 0;
5923 : min_cts = (u64) -1;
5924 : for (i=0; i<count; i++) {
5925 : GF_ISOSample *s = gf_isom_get_sample_info(ctx->file, tkw->track_num, i+1, &di, &doff);
5926 : if (!s)
5927 : break;
5928 : if (tkw->clamp_ts_plus_one) {
5929 : if (s->DTS + s->CTS_Offset + 1 >= tkw->clamp_ts_plus_one ) {
5930 : gf_isom_sample_del(&s);
5931 : continue;
5932 : }
5933 : }
5934 :
5935 : if (s->DTS + s->CTS_Offset > max_cts)
5936 : max_cts = s->DTS + s->CTS_Offset;
5937 :
5938 : if (min_cts > s->DTS + s->CTS_Offset)
5939 : min_cts = s->DTS + s->CTS_Offset;
5940 :
5941 : gf_isom_sample_del(&s);
5942 : }
5943 : *omax_cts = max_cts;
5944 : *omin_cts = min_cts;
5945 : }
5946 : #endif
5947 :
5948 276 : static void mp4_mux_update_edit_list_for_bframes(GF_MP4MuxCtx *ctx, TrackWriter *tkw)
5949 : {
5950 : u64 max_cts, min_cts;
5951 : s64 moffset;
5952 :
5953 276 : if (ctx->ctmode > MP4MX_CT_EDIT) return;
5954 :
5955 : //if we have a complex edit list (due to track template), don't override
5956 276 : if (gf_isom_get_edit_list_type(ctx->file, tkw->track_num, &moffset)) return;
5957 :
5958 276 : gf_isom_remove_edits(ctx->file, tkw->track_num);
5959 :
5960 : #if 0
5961 : mp4_mux_get_min_max_cts(ctx, tkw, &max_cts, &min_cts);
5962 : #else
5963 276 : max_cts = tkw->max_cts - tkw->min_neg_ctts;
5964 276 : min_cts = tkw->min_cts - tkw->min_neg_ctts;
5965 : #endif
5966 :
5967 276 : if (min_cts || tkw->empty_init_dur) {
5968 276 : max_cts -= min_cts;
5969 276 : u32 count = gf_isom_get_sample_count(ctx->file, tkw->track_num);
5970 276 : max_cts += gf_isom_get_sample_duration(ctx->file, tkw->track_num, count);
5971 :
5972 276 : max_cts *= ctx->moovts;
5973 276 : max_cts /= tkw->tk_timescale;
5974 276 : if (tkw->empty_init_dur) {
5975 :
5976 27 : gf_isom_set_edit(ctx->file, tkw->track_num, 0, tkw->empty_init_dur, 0, GF_ISOM_EDIT_EMPTY);
5977 27 : if (max_cts >= tkw->empty_init_dur) max_cts -= tkw->empty_init_dur;
5978 : else max_cts = 0;
5979 : }
5980 : //old arch compat: if we had a simple edit list in source try to keep the original segduration indicated
5981 : //we tolerate a diff of 100ms
5982 249 : else if (gf_sys_old_arch_compat() && tkw->imported_edit_sdur && (tkw->imported_edit_offset==min_cts)) {
5983 : s32 diff;
5984 68 : u64 old_dur_ms = tkw->imported_edit_sdur * 1000 / tkw->src_timescale;
5985 68 : u64 new_dur_ms = max_cts * 1000 / tkw->tk_timescale;
5986 68 : diff = (s32) new_dur_ms - (s32) old_dur_ms;
5987 68 : if (ABS(diff)<100)
5988 : max_cts = tkw->imported_edit_sdur;
5989 : }
5990 :
5991 276 : gf_isom_set_edit(ctx->file, tkw->track_num, tkw->empty_init_dur, max_cts, min_cts, GF_ISOM_EDIT_NORMAL);
5992 : }
5993 : }
5994 :
5995 : //todo: move from media_import.c to here once done
5996 : void gf_media_update_bitrate(GF_ISOFile *file, u32 track);
5997 :
5998 :
5999 1488 : static void mp4_mux_set_hevc_groups(GF_MP4MuxCtx *ctx, TrackWriter *tkw)
6000 : {
6001 : u32 avc_base_track, hevc_base_track, ref_track_id;
6002 : avc_base_track = hevc_base_track = 0;
6003 : u32 i;
6004 1488 : GF_PropertyEntry *pe=NULL;
6005 1488 : const GF_PropertyValue *p = gf_filter_pid_get_info_str(tkw->ipid, "hevc:oinf", &pe);
6006 1488 : if (p) {
6007 6 : u32 gi=0;
6008 6 : gf_isom_add_sample_group_info(ctx->file, tkw->track_num, GF_ISOM_SAMPLE_GROUP_OINF, p->value.data.ptr, p->value.data.size, GF_TRUE, &gi);
6009 :
6010 6 : p = gf_filter_pid_get_info_str(tkw->ipid, "hevc:linf", &pe);
6011 6 : if (p) {
6012 6 : gf_isom_add_sample_group_info(ctx->file, tkw->track_num, GF_ISOM_SAMPLE_GROUP_LINF, p->value.data.ptr, p->value.data.size, GF_TRUE, &gi);
6013 6 : gf_isom_set_track_group(ctx->file, tkw->track_num, 1000+gf_isom_get_track_id(ctx->file, tkw->track_num), GF_ISOM_BOX_TYPE_CSTG, GF_TRUE);
6014 : }
6015 : }
6016 :
6017 1488 : gf_filter_release_property(pe);
6018 :
6019 1488 : p = gf_filter_pid_get_property_str(tkw->ipid, "hevc:min_lid");
6020 1488 : if ((!p || !p->value.uint) && (tkw->codecid!=GF_CODECID_LHVC)) {
6021 1488 : return;
6022 : }
6023 : //set linf
6024 0 : for (i=0; i < gf_isom_get_track_count(ctx->file); i++) {
6025 0 : u32 subtype = gf_isom_get_media_subtype(ctx->file, i+1, 1);
6026 0 : switch (subtype) {
6027 0 : case GF_ISOM_SUBTYPE_AVC_H264:
6028 : case GF_ISOM_SUBTYPE_AVC2_H264:
6029 : case GF_ISOM_SUBTYPE_AVC3_H264:
6030 : case GF_ISOM_SUBTYPE_AVC4_H264:
6031 0 : if (!avc_base_track) {
6032 : avc_base_track = i+1;
6033 : } else {
6034 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Warning: More than one AVC bitstream found, use track %d as base layer", avc_base_track));
6035 : }
6036 : break;
6037 0 : case GF_ISOM_SUBTYPE_HVC1:
6038 : case GF_ISOM_SUBTYPE_HEV1:
6039 : case GF_ISOM_SUBTYPE_HVC2:
6040 : case GF_ISOM_SUBTYPE_HEV2:
6041 0 : if (!hevc_base_track) {
6042 : hevc_base_track = i+1;
6043 0 : if (avc_base_track) {
6044 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Warning: Found both AVC and HEVC tracks, using HEVC track %d as base layer\n", hevc_base_track));
6045 : }
6046 : } else {
6047 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Warning: More than one HEVC bitstream found, use track %d as base layer\n", avc_base_track));
6048 : }
6049 : break;
6050 : }
6051 : }
6052 0 : if (!hevc_base_track && !avc_base_track) {
6053 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Using LHVC external base layer, but no base layer not found - NOT SETTING SBAS TRACK REFERENCE!\n"));
6054 : } else {
6055 0 : ref_track_id = gf_isom_get_track_id(ctx->file, hevc_base_track ? hevc_base_track : avc_base_track);
6056 0 : gf_isom_set_track_reference(ctx->file, tkw->track_num, GF_ISOM_REF_BASE, ref_track_id);
6057 :
6058 0 : if (hevc_base_track) {
6059 0 : ref_track_id = gf_isom_get_track_id(ctx->file, hevc_base_track);
6060 0 : gf_isom_set_track_reference(ctx->file, tkw->track_num, GF_ISOM_REF_OREF, ref_track_id);
6061 : }
6062 : }
6063 : }
6064 :
6065 992 : static GF_Err mp4_mux_done(GF_Filter *filter, GF_MP4MuxCtx *ctx, Bool is_final)
6066 : {
6067 : GF_Err e = GF_OK;
6068 : u32 i, count;
6069 992 : GF_PropertyEntry *pe=NULL;
6070 :
6071 992 : count = gf_list_count(ctx->tracks);
6072 1099 : for (i=0; i<count; i++) {
6073 : const GF_PropertyValue *p;
6074 : Bool has_bframes = GF_FALSE;
6075 1099 : TrackWriter *tkw = gf_list_get(ctx->tracks, i);
6076 :
6077 1099 : if (tkw->min_neg_ctts<0) {
6078 : //use ctts v1 negative offsets
6079 177 : if (ctx->ctmode==MP4MX_CT_NEGCTTS) {
6080 0 : gf_isom_set_ctts_v1(ctx->file, tkw->track_num, (u32) -tkw->min_neg_ctts);
6081 : }
6082 : //ctts v0
6083 : else {
6084 177 : gf_isom_set_cts_packing(ctx->file, tkw->track_num, GF_TRUE);
6085 177 : gf_isom_shift_cts_offset(ctx->file, tkw->track_num, (s32) tkw->min_neg_ctts);
6086 177 : gf_isom_set_cts_packing(ctx->file, tkw->track_num, GF_FALSE);
6087 177 : gf_isom_set_composition_offset_mode(ctx->file, tkw->track_num, GF_FALSE);
6088 :
6089 177 : mp4_mux_update_edit_list_for_bframes(ctx, tkw);
6090 : }
6091 : has_bframes = GF_TRUE;
6092 922 : } else if (tkw->has_ctts && (tkw->stream_type==GF_STREAM_VISUAL)) {
6093 99 : mp4_mux_update_edit_list_for_bframes(ctx, tkw);
6094 :
6095 : has_bframes = GF_TRUE;
6096 823 : } else if (tkw->ts_delay || tkw->empty_init_dur) {
6097 20 : gf_isom_update_edit_list_duration(ctx->file, tkw->track_num);
6098 : }
6099 :
6100 1099 : if (tkw->min_ts_seek_plus_one) {
6101 8 : u64 min_ts = tkw->min_ts_seek_plus_one - 1;
6102 8 : u64 mdur = gf_isom_get_media_duration(ctx->file, tkw->track_num);
6103 : u32 delay = 0;
6104 8 : if (tkw->clamp_ts_plus_one) {
6105 : #if 0
6106 : u64 maxcts, mincts;
6107 : mp4_mux_get_min_max_cts(ctx, tkw, &maxcts, &mincts);
6108 : mdur = maxcts - mincts;
6109 : #else
6110 0 : mdur = tkw->max_cts - tkw->min_cts;
6111 0 : mdur += tkw->max_cts_samp_dur;
6112 : #endif
6113 : }
6114 8 : if (mdur > min_ts)
6115 6 : mdur -= min_ts;
6116 : else
6117 : mdur = 0;
6118 :
6119 8 : if ((ctx->ctmode!=MP4MX_CT_NEGCTTS) && (tkw->ts_delay<0) && (tkw->stream_type==GF_STREAM_VISUAL)) {
6120 0 : delay = (u32) -tkw->ts_delay;
6121 : }
6122 :
6123 8 : if (tkw->src_timescale != tkw->tk_timescale) {
6124 0 : min_ts *= tkw->tk_timescale;
6125 0 : min_ts /= tkw->src_timescale;
6126 0 : delay *= tkw->tk_timescale;
6127 0 : delay /= tkw->src_timescale;
6128 : }
6129 8 : mdur += delay;
6130 :
6131 8 : if (ctx->moovts != tkw->tk_timescale) {
6132 8 : mdur *= ctx->moovts;
6133 8 : mdur /= tkw->tk_timescale;
6134 : }
6135 8 : gf_isom_remove_edits(ctx->file, tkw->track_num);
6136 8 : if (tkw->empty_init_dur)
6137 2 : gf_isom_set_edit(ctx->file, tkw->track_num, 0, tkw->empty_init_dur, 0, GF_ISOM_EDIT_EMPTY);
6138 8 : gf_isom_set_edit(ctx->file, tkw->track_num, tkw->empty_init_dur, mdur, min_ts, GF_ISOM_EDIT_NORMAL);
6139 : }
6140 :
6141 1099 : if (tkw->force_ctts) {
6142 : GF_Err gf_isom_force_ctts(GF_ISOFile *file, u32 track);
6143 85 : gf_isom_force_ctts(ctx->file, tkw->track_num);
6144 : }
6145 :
6146 1099 : gf_isom_purge_track_reference(ctx->file, tkw->track_num);
6147 :
6148 1099 : if (ctx->importer && ctx->idur.num && ctx->idur.den) {
6149 93 : u64 mdur = gf_isom_get_media_duration(ctx->file, tkw->track_num);
6150 93 : u64 pdur = gf_isom_get_track_duration(ctx->file, tkw->track_num);
6151 93 : if (pdur==mdur) {
6152 1 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[MP4Mux] Imported %d frames - duration %g\n", tkw->nb_samples, ((Double)mdur)/tkw->tk_timescale ));
6153 : } else {
6154 92 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[MP4Mux] Imported %d frames - media duration %g - track duration %g\n", tkw->nb_samples, ((Double)mdur)/tkw->tk_timescale, ((Double)pdur)/ctx->moovts ));
6155 : }
6156 : }
6157 :
6158 : /*this is plain ugly but since some encoders (divx) don't use the video PL correctly
6159 : we force the system video_pl to ASP@L5 since we have I, P, B in base layer*/
6160 1099 : if (tkw->codecid == GF_CODECID_MPEG4_PART2) {
6161 : Bool force_rewrite = GF_FALSE;
6162 78 : u32 PL = tkw->media_profile_level;
6163 78 : if (!PL) PL = 0x01;
6164 :
6165 78 : if (ctx->importer) {
6166 21 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("Indicated Profile: %s\n", gf_m4v_get_profile_name((u8) PL) ));
6167 : }
6168 :
6169 78 : if (has_bframes && (tkw->media_profile_level <= 3)) {
6170 : PL = 0xF5;
6171 1 : GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[MP4Mux] Indicated profile doesn't include B-VOPs - forcing %s\n", gf_m4v_get_profile_name((u8) PL) ));
6172 : force_rewrite = GF_TRUE;
6173 : }
6174 78 : if (PL != tkw->media_profile_level) {
6175 1 : if (force_rewrite) {
6176 : #ifndef GPAC_DISABLE_AV_PARSERS
6177 1 : GF_ESD *esd = gf_isom_get_esd(ctx->file, tkw->track_num, tkw->stsd_idx);
6178 : assert(esd);
6179 1 : gf_m4v_rewrite_pl(&esd->decoderConfig->decoderSpecificInfo->data, &esd->decoderConfig->decoderSpecificInfo->dataLength, (u8) PL);
6180 1 : gf_isom_change_mpeg4_description(ctx->file, tkw->track_num, tkw->stsd_idx, esd);
6181 1 : gf_odf_desc_del((GF_Descriptor*)esd);
6182 : #endif
6183 :
6184 : }
6185 1 : if (!ctx->make_qt)
6186 1 : gf_isom_set_pl_indication(ctx->file, GF_ISOM_PL_VISUAL, PL);
6187 : }
6188 : }
6189 :
6190 :
6191 1099 : if (tkw->has_append)
6192 0 : gf_isom_refresh_size_info(ctx->file, tkw->track_num);
6193 :
6194 1099 : if ((tkw->nb_samples == 1) && (ctx->idur.num>0) && ctx->idur.den) {
6195 11 : u32 dur = tkw->tk_timescale * ctx->idur.num;
6196 11 : dur /= ctx->idur.den;
6197 11 : gf_isom_set_last_sample_duration(ctx->file, tkw->track_num, dur);
6198 : }
6199 :
6200 1099 : if (tkw->has_open_gop) {
6201 19 : if (ctx->importer) {
6202 15 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("OpenGOP detected - adjusting file brand\n"));
6203 : }
6204 19 : gf_isom_modify_alternate_brand(ctx->file, GF_ISOM_BRAND_ISO6, GF_TRUE);
6205 : }
6206 :
6207 1099 : mp4_mux_set_hevc_groups(ctx, tkw);
6208 :
6209 1099 : p = gf_filter_pid_get_info_str(tkw->ipid, "ttxt:rem_last", &pe);
6210 1099 : if (p && p->value.boolean)
6211 3 : gf_isom_remove_sample(ctx->file, tkw->track_num, tkw->nb_samples);
6212 :
6213 1099 : p = gf_filter_pid_get_info_str(tkw->ipid, "ttxt:last_dur", &pe);
6214 1099 : if (p) {
6215 22 : u64 val = p->value.uint;
6216 22 : if (tkw->src_timescale != tkw->tk_timescale) {
6217 0 : val *= tkw->tk_timescale;
6218 0 : val /= tkw->src_timescale;
6219 : }
6220 22 : gf_isom_set_last_sample_duration(ctx->file, tkw->track_num, (u32) val);
6221 : }
6222 :
6223 1099 : if (tkw->is_nalu && ctx->pack_nal && (gf_isom_get_mode(ctx->file)!=GF_ISOM_OPEN_WRITE)) {
6224 : u32 msize = 0;
6225 : Bool do_rewrite = GF_FALSE;
6226 1 : u32 j, stsd_count = gf_isom_get_sample_description_count(ctx->file, tkw->track_num);
6227 1 : p = gf_filter_pid_get_info(tkw->ipid, GF_PROP_PID_MAX_NALU_SIZE, &pe);
6228 1 : msize = gf_get_bit_size(p->value.uint);
6229 1 : if (msize<8) msize = 8;
6230 1 : else if (msize<16) msize = 16;
6231 : else msize = 32;
6232 :
6233 : if (msize<=0xFFFF) {
6234 2 : for (j=0; j<stsd_count; j++) {
6235 1 : u32 k = 8 * gf_isom_get_nalu_length_field(ctx->file, tkw->track_num, j+1);
6236 1 : if (k > msize) {
6237 : do_rewrite = GF_TRUE;
6238 : }
6239 : }
6240 1 : if (do_rewrite) {
6241 1 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[MP4Mux] Adjusting NALU SizeLength to %d bits\n", msize ));
6242 1 : gf_media_nal_rewrite_samples(ctx->file, tkw->track_num, msize);
6243 1 : msize /= 8;
6244 2 : for (j=0; j<stsd_count; j++) {
6245 1 : gf_isom_set_nalu_length_field(ctx->file, tkw->track_num, j+1, msize);
6246 : }
6247 : }
6248 : }
6249 : }
6250 :
6251 : //don't update bitrate info for single sample tracks, unless MPEG-4 Systems - compatibility with old arch
6252 1099 : if (ctx->btrt && !tkw->skip_bitrate_update && ((tkw->nb_samples>1) || ctx->m4sys) )
6253 863 : gf_media_update_bitrate(ctx->file, tkw->track_num);
6254 :
6255 1099 : if (!tkw->box_patched) {
6256 1002 : p = gf_filter_pid_get_property_str(tkw->ipid, "boxpatch");
6257 1002 : if (p && p->value.string) {
6258 4 : e = gf_isom_apply_box_patch(ctx->file, tkw->track_id ? tkw->track_id : tkw->item_id, p->value.string, GF_FALSE);
6259 4 : if (e) {
6260 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Unable to apply box patch %s to track %d: %s\n",
6261 : p->value.string, tkw->track_id, gf_error_to_string(e) ));
6262 : }
6263 : }
6264 1002 : tkw->box_patched = GF_TRUE;
6265 : }
6266 : }
6267 :
6268 992 : gf_filter_release_property(pe);
6269 :
6270 992 : if (ctx->boxpatch && !ctx->box_patched) {
6271 3 : e = gf_isom_apply_box_patch(ctx->file, 0, ctx->boxpatch, GF_FALSE);
6272 3 : if (e) {
6273 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[MP4Mux] Unable to apply box patch %s: %s\n", ctx->boxpatch, gf_error_to_string(e) ));
6274 : }
6275 3 : ctx->box_patched = GF_TRUE;
6276 : }
6277 :
6278 :
6279 992 : if (ctx->owns_mov) {
6280 386 : if (ctx->moovpad)
6281 0 : gf_isom_set_inplace_padding(ctx->file, ctx->moovpad);
6282 :
6283 386 : switch (ctx->store) {
6284 381 : case MP4MX_MODE_INTER:
6285 381 : if (ctx->cdur.num==0) {
6286 0 : e = gf_isom_set_storage_mode(ctx->file, GF_ISOM_STORE_STREAMABLE);
6287 : } else {
6288 381 : e = gf_isom_make_interleave_ex(ctx->file, &ctx->cdur);
6289 : }
6290 : break;
6291 0 : case MP4MX_MODE_FLAT:
6292 0 : e = gf_isom_set_storage_mode(ctx->file, GF_ISOM_STORE_FLAT);
6293 : break;
6294 1 : case MP4MX_MODE_FASTSTART:
6295 1 : e = gf_isom_set_storage_mode(ctx->file, GF_ISOM_STORE_FASTSTART);
6296 : break;
6297 0 : case MP4MX_MODE_TIGHT:
6298 0 : e = gf_isom_set_storage_mode(ctx->file, GF_ISOM_STORE_TIGHT);
6299 : break;
6300 : }
6301 386 : if (e) {
6302 0 : GF_LOG(GF_LOG_INFO, GF_LOG_AUTHOR, ("[MP4Mux] Failed to set storage mode: %s\n", gf_error_to_string(e) ));
6303 : } else {
6304 386 : e = gf_isom_close(ctx->file);
6305 386 : if (e) {
6306 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_AUTHOR, ("[MP4Mux] Failed to write file: %s\n", gf_error_to_string(e) ));
6307 : }
6308 : }
6309 386 : ctx->file = NULL;
6310 386 : if (is_final)
6311 326 : gf_filter_pid_set_eos(ctx->opid);
6312 : } else {
6313 606 : ctx->file = NULL;
6314 : }
6315 992 : return e;
6316 : }
6317 :
6318 1272 : static void mp4_mux_finalize(GF_Filter *filter)
6319 : {
6320 1272 : GF_MP4MuxCtx *ctx = gf_filter_get_udta(filter);
6321 :
6322 1272 : if (ctx->owns_mov && (ctx->file || (ctx->store>=MP4MX_MODE_FRAG))) {
6323 338 : gf_isom_delete(ctx->file);
6324 : }
6325 :
6326 2621 : while (gf_list_count(ctx->tracks)) {
6327 1349 : TrackWriter *tkw = gf_list_pop_back(ctx->tracks);
6328 1349 : mp4_mux_track_writer_del(tkw);
6329 : }
6330 1272 : gf_list_del(ctx->tracks);
6331 1272 : if (ctx->bs_r) gf_bs_del(ctx->bs_r);
6332 1272 : if (ctx->seg_name) gf_free(ctx->seg_name);
6333 1272 : if (ctx->tmp_store) gf_fclose(ctx->tmp_store);
6334 1272 : if (ctx->seg_sizes) gf_free(ctx->seg_sizes);
6335 :
6336 1272 : if (ctx->cur_file_suffix) gf_free(ctx->cur_file_suffix);
6337 :
6338 1272 : }
6339 :
6340 : static const GF_FilterCapability MP4MuxCaps[] =
6341 : {
6342 : //for now don't accept files as input, although we could store them as items, to refine
6343 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_FILE),
6344 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_SCENE),
6345 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_STREAM_TYPE, GF_STREAM_OD),
6346 : //we want framed media only
6347 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
6348 : //and any codecid
6349 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_NONE),
6350 : CAP_STRING(GF_CAPS_OUTPUT_STATIC, GF_PROP_PID_FILE_EXT, ISOM_FILE_EXT),
6351 : CAP_STRING(GF_CAPS_OUTPUT_STATIC, GF_PROP_PID_MIME, ISOM_FILE_MIME),
6352 : {0},
6353 : //for scene / OD / text, we don't want raw codecid (filters modifying a scene graph we don't expose)
6354 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_SCENE),
6355 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_OD),
6356 : CAP_UINT(GF_CAPS_INPUT,GF_PROP_PID_STREAM_TYPE, GF_STREAM_TEXT),
6357 : CAP_BOOL(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_UNFRAMED, GF_TRUE),
6358 : CAP_UINT(GF_CAPS_INPUT_EXCLUDED, GF_PROP_PID_CODECID, GF_CODECID_RAW),
6359 : };
6360 :
6361 :
6362 : #define OFFS(_n) #_n, offsetof(GF_MP4MuxCtx, _n)
6363 : static const GF_FilterArgs MP4MuxArgs[] =
6364 : {
6365 : { OFFS(m4sys), "force MPEG-4 Systems signaling of tracks", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
6366 : { OFFS(dref), "only references data from source file - not compatible with all media sources", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
6367 : { OFFS(ctmode), "set composition offset mode for video tracks\n"
6368 : "- edit: uses edit lists to shift first frame to presentation time 0\n"
6369 : "- noedit: ignore edit lists and does not shift timeline\n"
6370 : "- negctts: uses ctts v1 with possibly negative offsets and no edit lists", GF_PROP_UINT, "edit", "edit|noedit|negctts", GF_FS_ARG_HINT_ADVANCED},
6371 : { OFFS(idur), "only import the specified duration. If negative, specify the number of coded frames to import", GF_PROP_FRACTION, "0", NULL, 0},
6372 : { OFFS(pack3gp), "pack a given number of 3GPP audio frames in one sample", GF_PROP_UINT, "1", NULL, GF_FS_ARG_HINT_ADVANCED},
6373 : { OFFS(importer), "compatibility with old importer, displays import progress", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
6374 : { OFFS(pack_nal), "repack NALU size length to minimum possible size for NALU-based video (AVC/HEVC/...)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
6375 : { OFFS(xps_inband), "use inband (in sample data) parameter set for NALU-based video (AVC/HEVC/...)\n"
6376 : "- no: paramater sets are not inband, several sample descriptions might be created\n"
6377 : "- all: paramater sets are inband, no parameter sets in sample description\n"
6378 : "- both: paramater sets are inband, signaled as inband, and also first set is kept in sample description\n"
6379 : "- mix: creates non-standard files using single sample entry with first PSs found, and moves other PS inband", GF_PROP_UINT, "no", "no|all|both|mix", 0},
6380 : { OFFS(store), "file storage mode\n"
6381 : "- inter: perform precise interleave of the file using [-cdur]() (requires temporary storage of all media)\n"
6382 : "- flat: write samples as they arrive and moov at end (fastest mode)\n"
6383 : "- fstart: write samples as they arrive and moov before mdat\n"
6384 : "- tight: uses per-sample interleaving of all tracks (requires temporary storage of all media)\n"
6385 : "- frag: fragments the file using cdur duration\n"
6386 : "- sfrag: framents the file using cdur duration but adjusting to start with SAP1/3", GF_PROP_UINT, "inter", "inter|flat|fstart|tight|frag|sfrag", 0},
6387 : { OFFS(cdur), "chunk duration for flat and interleaving modes or fragment duration for fragmentation modes\n"
6388 : "- 0: no specific interleaving but moov first\n"
6389 : "- negative: defaults to 1.0 unless overridden by storage profile", GF_PROP_FRACTION, "-1/1", NULL, 0},
6390 : { OFFS(moovts), "timescale to use for movie. A negative value picks the media timescale of the first track added", GF_PROP_SINT, "600", NULL, GF_FS_ARG_HINT_ADVANCED},
6391 : { OFFS(moof_first), "generate fragments starting with moof then mdat", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
6392 : { OFFS(abs_offset), "use absolute file offset in fragments rather than offsets from moof", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
6393 : { OFFS(fsap), "split truns in video fragments at SAPs to reduce file size", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_ADVANCED},
6394 : { OFFS(file), "pointer to a write/edit ISOBMF file used internally by importers and exporters", GF_PROP_POINTER, NULL, NULL, GF_FS_ARG_HINT_HIDE},
6395 : { OFFS(subs_sidx), "number of subsegments per sidx. negative value disables sidx, -2 removes sidx if present in source pid", GF_PROP_SINT, "-1", NULL, GF_FS_ARG_HINT_ADVANCED},
6396 : { OFFS(m4cc), "4 character code of empty box to append at the end of a segment", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_ADVANCED},
6397 : { OFFS(chain_sidx), "use daisy-chaining of SIDX", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
6398 : { OFFS(msn), "sequence number of first moof to N", GF_PROP_UINT, "1", NULL, 0},
6399 : { OFFS(msninc), "sequence number increase between moofs", GF_PROP_UINT, "1", NULL, 0},
6400 : { OFFS(tfdt), "set TFDT of first traf", GF_PROP_FRACTION64, "0", NULL, 0},
6401 : { OFFS(tfdt_traf), "set TFDT in each traf", GF_PROP_BOOL, "false", NULL, 0},
6402 : { OFFS(nofragdef), "disable default flags in fragments", GF_PROP_BOOL, "false", NULL, 0},
6403 : { OFFS(straf), "use a single traf per moof (smooth streaming and co)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
6404 : { OFFS(strun), "use a single trun per traf (smooth streaming and co)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
6405 : { OFFS(psshs), "set PSSH boxes store mode\n"
6406 : "- moof: in first moof of each segments\n"
6407 : "- moov: in movie box\n"
6408 : "- none: pssh is discarded", GF_PROP_UINT, "moov", "moov|moof|none", GF_FS_ARG_HINT_ADVANCED},
6409 : { OFFS(sgpd_traf), "store sample group descriptions in traf (duplicated for each traf). If not used, sample group descriptions are stored in the movie box", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
6410 : { OFFS(vodcache), "enable temp storage for VoD dash modes - see filter help\n"
6411 : "- on: use temp storage of complete file for sidx and ssix injection\n"
6412 : "- insert: insert sidx and ssix by shifting bytes in output file\n"
6413 : "- replace: precompute pace requirements for sidx and ssix and rewrite file range at end", GF_PROP_UINT, "replace", "on|insert|replace", 0},
6414 : { OFFS(noinit), "do not produce initial moov, used for DASH bitstream switching mode", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
6415 : { OFFS(tktpl), "use track box from input if any as a template to create new track\n"
6416 : "- no: disables template\n"
6417 : "- yes: clones the track (except edits and decoder config)\n"
6418 : "- udta: only loads udta", GF_PROP_UINT, "yes", "no|yes|udta", GF_FS_ARG_HINT_EXPERT},
6419 : { OFFS(mudta), "use udta and other moov extension boxes from input if any\n"
6420 : "- no: disables import\n"
6421 : "- yes: clones all extension boxes\n"
6422 : "- udta: only loads udta", GF_PROP_UINT, "yes", "no|yes|udta", GF_FS_ARG_HINT_EXPERT},
6423 : { OFFS(mvex), "set mvex after tracks", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
6424 : { OFFS(sdtp_traf), "use sdtp in traf rather than using flags in trun sample entries\n"
6425 : "- no: do not use sdtp\n"
6426 : "- sdtp: use sdtp box to indicate sample dependencies and do not write info in trun sample flags\n"
6427 : "- both: use sdtp box to indicate sample dependencies and also write info in trun sample flags", GF_PROP_UINT, "no", "no|sdtp|both", GF_FS_ARG_HINT_EXPERT},
6428 : { OFFS(trackid), "track ID of created track for single track. Default 0 uses next available trackID", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
6429 : { OFFS(fragdur), "fragment based on fragment duration rather than CTS. Mostly used for MP4Box -frag option", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
6430 : { OFFS(btrt), "set btrt box in sample description", GF_PROP_BOOL, "true", NULL, 0},
6431 : { OFFS(styp), "set segment styp major brand to the given 4CC[.version]", GF_PROP_STRING, NULL, NULL, 0},
6432 : { OFFS(mediats), "set media timescale. A value of 0 means inherit from pid, a value of -1 means derive from samplerate or frame rate", GF_PROP_SINT, "0", NULL, 0},
6433 : { OFFS(ase), "set audio sample entry mode for more than stereo layouts\n"\
6434 : "- v0: use v0 signaling but channel count from stream, recommended for backward compatibility\n"\
6435 : "- v0s: use v0 signaling and force channel count to 2 (stereo) if more than 2 channels\n"\
6436 : "- v1: use v1 signaling, ISOBMFF style (will mux raw PCM as ISOBMFF style)\n"\
6437 : "- v1qt: use v1 signaling, QTFF style"\
6438 : , GF_PROP_UINT, "v0", "|v0|v0s|v1|v1qt", 0},
6439 : { OFFS(ssix), "create ssix when sidx is present, level 1 mapping I-frames byte ranges, level 0xFF mapping the rest", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
6440 : { OFFS(ccst), "insert coding constraint box for video tracks", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
6441 : { OFFS(maxchunk), "set max chunk size in bytes for runs (only used in non-fragmented mode). 0 means no constraints", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_ADVANCED},
6442 : { OFFS(noroll), "disable roll sample grouping", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
6443 : { OFFS(saio32), "use 32 bit offset for side data location instead of 64 bit offset", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
6444 : { OFFS(tfdt64), "use 64 bit tfdt and sidx even for 32 bits timestamps", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
6445 : #ifdef GF_ENABLE_CTRN
6446 : { OFFS(ctrn), "use compact track run (experimental)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
6447 : { OFFS(ctrni), "use inheritance in compact track run for HEVC tile tracks (highly experimental)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
6448 : #endif
6449 : { OFFS(sseg), "set single segment mode for dash", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_HIDE},
6450 :
6451 : { OFFS(compress), "set top-level box compression mode\n"
6452 : "- no: disable box compression\n"
6453 : "- moov: compress only moov box\n"
6454 : "- moof: compress only moof boxes\n"
6455 : "- sidx: compress moof and sidx boxes\n"
6456 : "- ssix: compress moof, sidx and ssix boxes\n"
6457 : "- all: compress moov, moof, sidx and ssix boxes", GF_PROP_UINT, "no", "no|moov|moof|sidx|ssix|all", GF_FS_ARG_HINT_EXPERT},
6458 : { OFFS(fcomp), "force using compress box even when compressed size is larger than uncompressed", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
6459 : { OFFS(otyp), "inject original file type when using compressed boxes", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
6460 :
6461 : { OFFS(trun_inter), "interleave samples in trun based on the temporal level, the lowest level are stored first - this will create as many trun as required", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
6462 : { OFFS(truns_first), "store track runs before sample group description and sample encryption information", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
6463 : { OFFS(block_size), "target output block size, 0 for default internal value (10k)", GF_PROP_UINT, "10000", NULL, GF_FS_ARG_HINT_ADVANCED},
6464 : { OFFS(boxpatch), "apply box patch before writing", GF_PROP_STRING, NULL, NULL, GF_FS_ARG_HINT_EXPERT},
6465 : { OFFS(deps), "add samples dependencies information", GF_PROP_BOOL, "true", NULL, GF_FS_ARG_HINT_EXPERT},
6466 : { OFFS(mfra), "enable movie fragment random access when fragmenting (ignored when dashing)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
6467 : { OFFS(forcesync), "force all SAP types to be considered sync samples (might produce non-conformant files)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_EXPERT},
6468 : { OFFS(refrag), "indicate to use track fragment defaults from initial file if any rather than computing them from PID properties (used when processing standalone segments/fragments)", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
6469 : { OFFS(itags), "tag injection mode\n"
6470 : "- none: do not inject tags\n"
6471 : "- strict: only inject recognized itunes tags\n"
6472 : "- all: inject all possible tags"
6473 : , GF_PROP_UINT, "strict", "none|strict|all", GF_FS_ARG_HINT_EXPERT},
6474 :
6475 : { OFFS(keep_utc), "force all new files and tracks to keep the source UTC creation and modification times", GF_PROP_BOOL, "false", NULL, GF_FS_ARG_HINT_ADVANCED},
6476 : { OFFS(pps_inband), "when xps_inband is set, inject PPS in each non SAP 1/2/3 sample", GF_PROP_BOOL, "no", NULL, GF_FS_ARG_HINT_ADVANCED},
6477 : { OFFS(moovpad), "insert free box of given size after moov for future in-place editing", GF_PROP_UINT, "0", NULL, GF_FS_ARG_HINT_EXPERT},
6478 : { OFFS(cmaf), "use cmaf guidelines\n"
6479 : "- no: CMAF not enforced\n"
6480 : "- cmfc: use CMAF `cmfc` guidelines\n"
6481 : "- cmf2: use CMAF `cmf2` guidelines"
6482 : , GF_PROP_UINT, "no", "no|cmfc|cmf2", GF_FS_ARG_HINT_EXPERT},
6483 : { OFFS(start), "set playback start offset (MP4Box import only). Negative value means percent of media duration with -1 equal to duration", GF_PROP_DOUBLE, "0.0", NULL, GF_FS_ARG_HINT_HIDE},
6484 : {0}
6485 : };
6486 :
6487 :
6488 : GF_FilterRegister MP4MuxRegister = {
6489 : .name = "mp4mx",
6490 : GF_FS_SET_DESCRIPTION("ISOBMFF/QT muxer")
6491 : GF_FS_SET_HELP("Muxes file according to ISOBMFF (14496-12 and derived specifications) or QuickTime\n"
6492 : " \n"
6493 : "# Tracks and Items\n"
6494 : "By default all input PIDs with ItemID property set are muxed as items, otherwise they are muxed as tracks.\n"
6495 : "To prevent source items to be muxed as items, use [-itemid](mp4dmx) option from ISOBMF demuxer.\n"
6496 : "EX -i source.mp4:itemid=false -o file.mp4\n"
6497 : " \n"
6498 : "To force non-item streams to be muxed as items, use __#ItemID__ option on that PID:\n"
6499 : "EX -i source.jpg:#ItemID=1 -o file.mp4\n"
6500 : " \n"
6501 : "# Storage\n"
6502 : "The [-store]() option allows controlling if the file is fragmented ot not, and when not fragmented, how interleaving is done. For cases where disk requirements are tight and fragmentation cannot be used, it is recommended to use either `flat` or `fstart` modes.\n"
6503 : " \n"
6504 : "The [-vodcache]() option allows controlling how DASH onDemand segments are generated:\n"
6505 : "- If set to `on`, file data is stored to a temporary file on disk and flushed upon completion, no padding is present.\n"
6506 : "- If set to `insert`, SIDX/SSIX will be injected upon completion of the file by shifting bytes in file. In this case, no padding is required but this might not be compatible with all output sinks and will take longer to write the file.\n"
6507 : "- If set to `replace`, SIDX/SSIX size will be estimated based on duration and DASH segment length, and padding will be used in the file __before__ the final SIDX. If input pids have the properties `DSegs` set, this will be as the number of segments.\n"
6508 : "The `on` and `insert` modes will produce exactly the same file, while the mode `replace` may inject a `free` box before the sidx.\n"
6509 : " \n"
6510 : "# Custom boxes\n"
6511 : "Custom boxes can be specified as box patches:\n"
6512 : "For movie-level patch, the [-boxpatch]() option of the filter should be used.\n"
6513 : "Per PID box patch can be specified through the PID property `boxpatch`.\n"
6514 : "EX src=source:#boxpatch=myfile.xml dst=mux.mp4\n"
6515 : "Per Item box patch can be specified through the PID property `boxpatch`.\n"
6516 : "EX src=source:1ItemID=1:#boxpatch=myfile.xml dst=mux.mp4\n"
6517 : " \n"
6518 : "The box patch is applied before writing the initial moov box in fragmented mode, or when writing the complete file otherwise.\n"
6519 : "The box patch can either be a filename or the full XML string.\n"
6520 : " \n"
6521 : "# Tagging\n"
6522 : "When tagging is enabled, the filter will watch the property `CoverArt` and all custom properties on incoming pid.\n"
6523 : "The built-in tag names are indicated by `MP4Box -h tags`.\n"
6524 : "Other tag class may be specified using `tag_NAME` property names, and will be added if [-tags]() is set to `all` using:\n"
6525 : "- `NAME` as a box 4CC if `NAME` is four characters long\n"
6526 : "- `NAME` as a box 4CC if `NAME` is 3 characters long, and will be prefixed by 0xA9\n"
6527 : "- the CRC32 of the `NAME` as a box 4CC if `NAME` is not four characters long\n"
6528 : " \n"
6529 : "# Notes\n"
6530 : "The filter watches the property `FileNumber` on incoming packets to create new files or new segments in DASH mode.\n"
6531 : )
6532 : .private_size = sizeof(GF_MP4MuxCtx),
6533 : .args = MP4MuxArgs,
6534 : .initialize = mp4_mux_initialize,
6535 : .finalize = mp4_mux_finalize,
6536 : .flags = GF_FS_REG_DYNAMIC_REDIRECT,
6537 : SETCAPS(MP4MuxCaps),
6538 : .configure_pid = mp4_mux_configure_pid,
6539 : .process = mp4_mux_process,
6540 : .process_event = mp4_mux_process_event
6541 : };
6542 :
6543 :
6544 2877 : const GF_FilterRegister *mp4_mux_register(GF_FilterSession *session)
6545 : {
6546 2877 : return &MP4MuxRegister;
6547 : }
6548 :
6549 : #else
6550 : const GF_FilterRegister *mp4_mux_register(GF_FilterSession *session)
6551 : {
6552 : return NULL;
6553 : }
6554 : #endif // GPAC_DISABLE_ISOM_WRITE
6555 :
|