1 /*
2 * GPAC - Multimedia Framework C SDK
3 *
4 * Authors: Jean Le Feuvre
5 * Copyright (c) Telecom ParisTech 2000-2019
6 * All rights reserved
7 *
8 * This file is part of GPAC / ISO Media File Format sub-project
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/internal/isomedia_dev.h>
27 #include <gpac/constants.h>
28
29 #ifndef GPAC_DISABLE_ISOM
30
GetTrackbyID(GF_MovieBox * moov,GF_ISOTrackID TrackID)31 GF_TrackBox *GetTrackbyID(GF_MovieBox *moov, GF_ISOTrackID TrackID)
32 {
33 GF_TrackBox *trak;
34 u32 i;
35 if (!moov) return NULL;
36 i=0;
37 while ((trak = (GF_TrackBox *)gf_list_enum(moov->trackList, &i))) {
38 if (trak->Header->trackID == TrackID) return trak;
39 }
40 return NULL;
41 }
42
gf_isom_get_track(GF_MovieBox * moov,u32 trackNumber)43 GF_TrackBox *gf_isom_get_track(GF_MovieBox *moov, u32 trackNumber)
44 {
45 GF_TrackBox *trak;
46 if (!moov || !trackNumber || (trackNumber > gf_list_count(moov->trackList))) return NULL;
47 trak = (GF_TrackBox*)gf_list_get(moov->trackList, trackNumber - 1);
48 return trak;
49
50 }
51
52 //get the number of a track given its ID
53 //return 0 if not found error
gf_isom_get_tracknum_from_id(GF_MovieBox * moov,GF_ISOTrackID trackID)54 u32 gf_isom_get_tracknum_from_id(GF_MovieBox *moov, GF_ISOTrackID trackID)
55 {
56 u32 i;
57 GF_TrackBox *trak;
58 i=0;
59 while ((trak = (GF_TrackBox *)gf_list_enum(moov->trackList, &i))) {
60 if (trak->Header->trackID == trackID) return i;
61 }
62 return 0;
63 }
64
65 //extraction of the ESD from the track
GetESD(GF_MovieBox * moov,GF_ISOTrackID trackID,u32 StreamDescIndex,GF_ESD ** outESD)66 GF_Err GetESD(GF_MovieBox *moov, GF_ISOTrackID trackID, u32 StreamDescIndex, GF_ESD **outESD)
67 {
68 GF_Err e;
69 GF_ESD *esd;
70 u32 track_num = 0;
71 u32 k;
72 GF_SampleTableBox *stbl;
73 GF_TrackBox *trak, *OCRTrack;
74 GF_TrackReferenceTypeBox *dpnd;
75 GF_SLConfig *slc;
76 GF_MPEGSampleEntryBox *entry;
77
78 if (!moov) return GF_ISOM_INVALID_FILE;
79
80 track_num = gf_isom_get_tracknum_from_id(moov, trackID);
81 dpnd = NULL;
82 *outESD = NULL;
83
84 trak = gf_isom_get_track(moov, track_num);
85 if (!trak) return GF_ISOM_INVALID_FILE;
86
87 e = Media_GetESD(trak->Media, StreamDescIndex, &esd, 0);
88 if (e) return e;
89 if (!esd) return GF_NON_COMPLIANT_BITSTREAM;
90
91 e = Media_GetSampleDesc(trak->Media, StreamDescIndex, (GF_SampleEntryBox **) &entry, NULL);
92 if (e) return e;
93 //set the ID
94 esd->ESID = trackID;
95
96 //find stream dependencies: dpnd, sbas and scal
97 for (k=0; k<3; k++) {
98 u32 ref = GF_ISOM_BOX_TYPE_DPND;
99 if (k==1) ref = GF_ISOM_REF_BASE;
100 else if (k==2) ref = GF_ISOM_REF_SCAL;
101
102 e = Track_FindRef(trak, ref , &dpnd);
103 if (e) return e;
104 if (dpnd) {
105 //ONLY ONE STREAM DEPENDENCY IS ALLOWED
106 if (!k && (dpnd->trackIDCount != 1)) return GF_ISOM_INVALID_MEDIA;
107 //fix the spec: where is the index located ??
108 esd->dependsOnESID = dpnd->trackIDs[0];
109 break;
110 } else {
111 esd->dependsOnESID = 0;
112 }
113 }
114
115 if (trak->udta) {
116 GF_UserDataMap *map;
117 u32 i = 0;
118 while ((map = (GF_UserDataMap*)gf_list_enum(trak->udta->recordList, &i))) {
119 if (map->boxType == GF_ISOM_BOX_TYPE_AUXV) {
120 GF_Descriptor *d = gf_odf_desc_new(GF_ODF_AUX_VIDEO_DATA);
121 gf_list_add(esd->extensionDescriptors, d);
122 break;
123 }
124 }
125 }
126
127 //OK, get the OCR (in a REAL MP4File, OCR is 0 in ESD and is specified through track reference
128 dpnd = NULL;
129 OCRTrack = NULL;
130 //find OCR dependencies
131 e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_SYNC, &dpnd);
132 if (e) return e;
133 if (dpnd) {
134 if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA;
135 esd->OCRESID = dpnd->trackIDs[0];
136 OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]);
137
138 while (OCRTrack) {
139 /*if we have a dependency on a track that doesn't have OCR dep, remove that dependency*/
140 e = Track_FindRef(OCRTrack, GF_ISOM_BOX_TYPE_SYNC, &dpnd);
141 if (e || !dpnd || !dpnd->trackIDCount) {
142 OCRTrack = NULL;
143 goto default_sync;
144 }
145 /*this is explicit desync*/
146 if ((dpnd->trackIDs[0]==0) || (dpnd->trackIDs[0]==OCRTrack->Header->trackID))
147 break;
148 /*loop in OCRs, break it*/
149 if (esd->ESID == (u16) OCRTrack->Header->trackID) {
150 OCRTrack = NULL;
151 goto default_sync;
152 }
153 /*check next*/
154 OCRTrack = gf_isom_get_track_from_id(trak->moov, dpnd->trackIDs[0]);
155 }
156 if (!OCRTrack) goto default_sync;
157 } else {
158 default_sync:
159 /*all tracks are sync'ed by default*/
160 if (trak->moov->mov->es_id_default_sync<0) {
161 if (esd->OCRESID)
162 trak->moov->mov->es_id_default_sync = esd->OCRESID;
163 else
164 trak->moov->mov->es_id_default_sync = esd->ESID;
165 }
166 if (trak->moov->mov->es_id_default_sync) esd->OCRESID = (u16) trak->moov->mov->es_id_default_sync;
167 /*cf ESD writer*/
168 if (esd->OCRESID == esd->ESID) esd->OCRESID = 0;
169 }
170
171
172
173 //update the IPI stuff if needed
174 if (esd->ipiPtr != NULL) {
175 dpnd = NULL;
176 e = Track_FindRef(trak, GF_ISOM_BOX_TYPE_IPIR, &dpnd);
177 if (e) return e;
178 if (dpnd) {
179 if (esd->ipiPtr->tag != GF_ODF_ISOM_IPI_PTR_TAG) return GF_ISOM_INVALID_FILE;
180 //OK, retrieve the ID: the IPI_ES_Id is currently the ref track
181 esd->ipiPtr->IPI_ES_Id = dpnd->trackIDs[esd->ipiPtr->IPI_ES_Id - 1];
182 //and change the tag
183 esd->ipiPtr->tag = GF_ODF_IPI_PTR_TAG;
184 } else {
185 return GF_ISOM_INVALID_FILE;
186 }
187 }
188
189 if ((trak->Media->mediaHeader->packedLanguage[0] != 'u')
190 || (trak->Media->mediaHeader->packedLanguage[1] != 'n')
191 || (trak->Media->mediaHeader->packedLanguage[2] != 'd') ) {
192 if (!esd->langDesc) esd->langDesc = (GF_Language *) gf_odf_desc_new(GF_ODF_LANG_TAG);
193
194 esd->langDesc->langCode = trak->Media->mediaHeader->packedLanguage[0];
195 esd->langDesc->langCode <<= 8;
196 esd->langDesc->langCode |= trak->Media->mediaHeader->packedLanguage[1];
197 esd->langDesc->langCode <<= 8;
198 esd->langDesc->langCode |= trak->Media->mediaHeader->packedLanguage[2];
199 }
200
201
202 {
203 u16 rvc_predefined;
204 u8 *rvc_cfg_data;
205 const char *mime_type;
206 u32 rvc_cfg_size;
207 e = gf_isom_get_rvc_config(moov->mov, track_num, 1, &rvc_predefined, &rvc_cfg_data, &rvc_cfg_size, &mime_type);
208 if (e==GF_OK) {
209 if (rvc_predefined) {
210 esd->decoderConfig->predefined_rvc_config = rvc_predefined;
211 } else {
212 esd->decoderConfig->rvc_config = (GF_DefaultDescriptor *) gf_odf_desc_new(GF_ODF_DSI_TAG);
213 if (mime_type && !strcmp(mime_type, "application/rvc-config+xml+gz") ) {
214 #if !defined(GPAC_DISABLE_CORE_TOOLS) && !defined(GPAC_DISABLE_ZLIB)
215 gf_gz_decompress_payload(rvc_cfg_data, rvc_cfg_size, &esd->decoderConfig->rvc_config->data, &esd->decoderConfig->rvc_config->dataLength);
216 gf_free(rvc_cfg_data);
217 #endif
218 } else {
219 esd->decoderConfig->rvc_config->data = rvc_cfg_data;
220 esd->decoderConfig->rvc_config->dataLength = rvc_cfg_size;
221 }
222 }
223 }
224 }
225
226
227 /*normally all files shall be stored with predefined=SLPredef_MP4, but of course some are broken (philips)
228 so we just check the ESD_URL. If set, use the given cfg, otherwise always rewrite it*/
229 if (esd->URLString != NULL) {
230 *outESD = esd;
231 return GF_OK;
232 }
233
234 //if we are in publishing mode and we have an SLConfig specified, use it as is
235 switch (entry->type) {
236 case GF_ISOM_BOX_TYPE_MP4V:
237 slc = ((GF_MPEGVisualSampleEntryBox *)entry)->slc;
238 break;
239 case GF_ISOM_BOX_TYPE_MP4A:
240 slc = ((GF_MPEGAudioSampleEntryBox *)entry)->slc;
241 break;
242 case GF_ISOM_BOX_TYPE_MP4S:
243 slc = entry->slc;
244 break;
245 default:
246 slc = NULL;
247 break;
248 }
249 if (slc) {
250 gf_odf_desc_del((GF_Descriptor *)esd->slConfig);
251 gf_odf_desc_copy((GF_Descriptor *)slc, (GF_Descriptor **)&esd->slConfig);
252 *outESD = esd;
253 return GF_OK;
254 }
255 //otherwise use the regular mapping
256
257 if (!esd->slConfig)
258 esd->slConfig = (GF_SLConfig *) gf_odf_desc_new(GF_ODF_SLC_TAG);
259
260 //this is a desc for a media in the file, let's rewrite some param
261 esd->slConfig->timestampLength = 32;
262 esd->slConfig->timestampResolution = trak->Media->mediaHeader->timeScale;
263 //NO OCR from MP4File streams (eg, constant OC Res one)
264 esd->slConfig->OCRLength = 0;
265 esd->slConfig->OCRResolution = 0;
266 // if (OCRTrack) esd->slConfig->OCRResolution = OCRTrack->Media->mediaHeader->timeScale;
267
268 stbl = trak->Media->information->sampleTable;
269 // a little optimization here: if all our samples are sync,
270 //set the RAPOnly to true... for external users...
271 if (! stbl->SyncSample) {
272 if (
273 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
274 moov->mvex &&
275 #endif
276 esd->decoderConfig && esd->decoderConfig->streamType &&
277 (esd->decoderConfig->streamType==GF_STREAM_VISUAL)
278 ) {
279 esd->slConfig->hasRandomAccessUnitsOnlyFlag = 0;
280 esd->slConfig->useRandomAccessPointFlag = 1;
281 if (trak->moov->mov->openMode!=GF_ISOM_OPEN_READ) {
282 stbl->SyncSample = (GF_SyncSampleBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSS);
283 if (!stbl->SyncSample) return GF_OUT_OF_MEM;
284 }
285 } else {
286 esd->slConfig->hasRandomAccessUnitsOnlyFlag = 1;
287 esd->slConfig->useRandomAccessPointFlag = 0;
288 }
289 } else {
290 esd->slConfig->hasRandomAccessUnitsOnlyFlag = 0;
291 //signal we are NOT using sync points if no info is present in the table
292 esd->slConfig->useRandomAccessPointFlag = stbl->SyncSample->nb_entries ? 1 : 0;
293 }
294 //change to support reflecting OD streams
295 esd->slConfig->useAccessUnitEndFlag = 1;
296 esd->slConfig->useAccessUnitStartFlag = 1;
297
298 //signal we do have padding flag (since we only use logical SL packet
299 //the user can decide whether to use the info or not
300 esd->slConfig->usePaddingFlag = stbl->PaddingBits ? 1 : 0;
301
302 //same with degradation priority
303 esd->slConfig->degradationPriorityLength = stbl->DegradationPriority ? 32 : 0;
304
305 //this new SL will be OUT OF THE FILE. Let's set its predefined to 0
306 esd->slConfig->predefined = 0;
307
308
309 *outESD = esd;
310 return GF_OK;
311 }
312
313
314 //extraction of the ESD from the track for the given time
GetESDForTime(GF_MovieBox * moov,GF_ISOTrackID trackID,u64 CTS,GF_ESD ** outESD)315 GF_Err GetESDForTime(GF_MovieBox *moov, GF_ISOTrackID trackID, u64 CTS, GF_ESD **outESD)
316 {
317 GF_Err e;
318 u32 sampleDescIndex;
319 GF_TrackBox *trak;
320
321 trak = gf_isom_get_track(moov, gf_isom_get_tracknum_from_id(moov, trackID));
322 if (!trak) return GF_ISOM_INVALID_FILE;
323
324 e = Media_GetSampleDescIndex(trak->Media, CTS, &sampleDescIndex );
325 if (e) return e;
326 return GetESD(moov, trackID, sampleDescIndex, outESD);
327 }
328
329
Track_FindRef(GF_TrackBox * trak,u32 ReferenceType,GF_TrackReferenceTypeBox ** dpnd)330 GF_Err Track_FindRef(GF_TrackBox *trak, u32 ReferenceType, GF_TrackReferenceTypeBox **dpnd)
331 {
332 GF_TrackReferenceBox *ref;
333 GF_TrackReferenceTypeBox *a;
334 u32 i;
335 if (! trak) return GF_BAD_PARAM;
336 if (! trak->References) {
337 *dpnd = NULL;
338 return GF_OK;
339 }
340 ref = trak->References;
341 i=0;
342 while ((a = (GF_TrackReferenceTypeBox *)gf_list_enum(ref->child_boxes, &i))) {
343 if (a->reference_type == ReferenceType) {
344 *dpnd = a;
345 return GF_OK;
346 }
347 }
348 *dpnd = NULL;
349 return GF_OK;
350 }
351
Track_IsMPEG4Stream(u32 HandlerType)352 Bool Track_IsMPEG4Stream(u32 HandlerType)
353 {
354 switch (HandlerType) {
355 case GF_ISOM_MEDIA_VISUAL:
356 case GF_ISOM_MEDIA_AUXV:
357 case GF_ISOM_MEDIA_PICT:
358 case GF_ISOM_MEDIA_AUDIO:
359 case GF_ISOM_MEDIA_SUBPIC:
360 case GF_ISOM_MEDIA_OD:
361 case GF_ISOM_MEDIA_OCR:
362 case GF_ISOM_MEDIA_SCENE:
363 case GF_ISOM_MEDIA_MPEG7:
364 case GF_ISOM_MEDIA_OCI:
365 case GF_ISOM_MEDIA_IPMP:
366 case GF_ISOM_MEDIA_MPEGJ:
367 case GF_ISOM_MEDIA_ESM:
368 return 1;
369 /*Timedtext is NOT an MPEG-4 stream*/
370 default:
371 /*consider xxsm as MPEG-4 handlers*/
372 if ( (((HandlerType>>8) & 0xFF)== 's') && ((HandlerType& 0xFF)== 'm'))
373 return 1;
374 return 0;
375 }
376 }
377
378
SetTrackDuration(GF_TrackBox * trak)379 GF_Err SetTrackDuration(GF_TrackBox *trak)
380 {
381 u64 trackDuration;
382 u32 i;
383 GF_Err e;
384
385 //the total duration is the media duration: adjust it in case...
386 e = Media_SetDuration(trak);
387 if (e) return e;
388
389 //assert the timeScales are non-NULL
390 if (!trak->moov->mvhd->timeScale || !trak->Media->mediaHeader->timeScale) return GF_ISOM_INVALID_FILE;
391 trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale;
392
393 //if we have an edit list, the duration is the sum of all the editList
394 //entries' duration (always expressed in MovieTimeScale)
395 if (trak->editBox && trak->editBox->editList) {
396 GF_EdtsEntry *ent;
397 GF_EditListBox *elst = trak->editBox->editList;
398 trackDuration = 0;
399 i=0;
400 while ((ent = (GF_EdtsEntry*)gf_list_enum(elst->entryList, &i))) {
401 trackDuration += ent->segmentDuration;
402 }
403 }
404 if (!trackDuration) {
405 trackDuration = (trak->Media->mediaHeader->duration * trak->moov->mvhd->timeScale) / trak->Media->mediaHeader->timeScale;
406 }
407 trak->Header->duration = trackDuration;
408 if (!trak->moov->mov->keep_utc && !gf_sys_is_test_mode() )
409 trak->Header->modificationTime = gf_isom_get_mp4time();
410 return GF_OK;
411 }
412
413
414 #ifndef GPAC_DISABLE_ISOM_FRAGMENTS
415
traf_get_sample_entry(GF_TrackFragmentBox * traf,u32 sample_index)416 GF_TrunEntry *traf_get_sample_entry(GF_TrackFragmentBox *traf, u32 sample_index)
417 {
418 u32 i, idx;
419 GF_TrackFragmentRunBox *trun;
420 idx=0;
421 i=0;
422 while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
423 u32 j;
424 for (j=0; j<trun->sample_count; j++) {
425 GF_TrunEntry *ent = gf_list_get(trun->entries, j);
426 if (idx==sample_index) return ent;
427 if (ent->nb_pack>1) {
428 if (idx < sample_index + ent->nb_pack)
429 return ent;
430 idx += ent->nb_pack;
431 } else {
432 idx++;
433 }
434 }
435 }
436 return NULL;
437 }
438
MergeTrack(GF_TrackBox * trak,GF_TrackFragmentBox * traf,GF_MovieFragmentBox * moof_box,u64 moof_offset,s32 compressed_diff,u64 * cumulated_offset,Bool is_first_merge)439 GF_Err MergeTrack(GF_TrackBox *trak, GF_TrackFragmentBox *traf, GF_MovieFragmentBox *moof_box, u64 moof_offset, s32 compressed_diff, u64 *cumulated_offset, Bool is_first_merge)
440 {
441 u32 i, j, chunk_size, track_num;
442 u64 base_offset, data_offset, traf_duration;
443 u32 def_duration, DescIndex, def_size, def_flags;
444 u32 duration, size, flags, prev_trun_data_offset, sample_index;
445 u8 pad, sync;
446 u16 degr;
447 Bool first_samp_in_traf=GF_TRUE;
448 Bool store_traf_map=GF_FALSE;
449 u8 *moof_template=NULL;
450 u32 moof_template_size=0;
451 Bool is_seg_start = GF_FALSE;
452 u64 seg_start=0, sidx_start=0, sidx_end=0, frag_start=0;
453 GF_TrackFragmentRunBox *trun;
454 GF_TrunEntry *ent;
455 #ifdef GF_ENABLE_CTRN
456 GF_TrackFragmentBox *traf_ref = NULL;
457 #endif
458
459 GF_Err stbl_AppendTime(GF_SampleTableBox *stbl, u32 duration, u32 nb_pack);
460 GF_Err stbl_AppendSize(GF_SampleTableBox *stbl, u32 size, u32 nb_pack);
461 GF_Err stbl_AppendChunk(GF_SampleTableBox *stbl, u64 offset);
462 GF_Err stbl_AppendSampleToChunk(GF_SampleTableBox *stbl, u32 DescIndex, u32 samplesInChunk);
463 GF_Err stbl_AppendCTSOffset(GF_SampleTableBox *stbl, s32 CTSOffset);
464 GF_Err stbl_AppendRAP(GF_SampleTableBox *stbl, u8 isRap);
465 GF_Err stbl_AppendPadding(GF_SampleTableBox *stbl, u8 padding);
466 GF_Err stbl_AppendDegradation(GF_SampleTableBox *stbl, u16 DegradationPriority);
467
468 if (trak->Header->trackID != traf->tfhd->trackID) return GF_OK;
469 if (!trak->Media->information->sampleTable
470 || !trak->Media->information->sampleTable->SampleSize
471 || !trak->Media->information->sampleTable->TimeToSample
472 || !trak->Media->information->sampleTable->SampleToChunk
473 || !trak->Media->information->sampleTable->ChunkOffset
474 ) {
475 return GF_ISOM_INVALID_FILE;
476 }
477
478 if (!traf->trex->track)
479 traf->trex->track = trak;
480
481 //setup all our defaults
482 DescIndex = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_DESC) ? traf->tfhd->sample_desc_index : traf->trex->def_sample_desc_index;
483 if (!DescIndex) {
484 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] default sample description set to 0, likely broken ! Fixing to 1\n" ));
485 DescIndex = 1;
486 } else if (DescIndex > gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes)) {
487 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] default sample description set to %d but only %d sample description(s), likely broken ! Fixing to 1\n", DescIndex, gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes)));
488 DescIndex = 1;
489 }
490 #ifdef GF_ENABLE_CTRN
491 if (traf->trex->inherit_from_traf_id) {
492 u32 traf_count = gf_list_count(moof_box->TrackList);
493 for (i=0; i<traf_count; i++) {
494 GF_TrackFragmentBox *atraf = gf_list_get(moof_box->TrackList, i);
495 if (atraf->tfhd && atraf->tfhd->trackID==traf->trex->inherit_from_traf_id) {
496 traf_ref = atraf;
497 break;
498 }
499 }
500 }
501 #endif
502
503 def_duration = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_DUR) ? traf->tfhd->def_sample_duration : traf->trex->def_sample_duration;
504 def_size = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_SIZE) ? traf->tfhd->def_sample_size : traf->trex->def_sample_size;
505 def_flags = (traf->tfhd->flags & GF_ISOM_TRAF_SAMPLE_FLAGS) ? traf->tfhd->def_sample_flags : traf->trex->def_sample_flags;
506
507 //locate base offset, by default use moof (dash-like)
508 base_offset = moof_offset;
509 //explicit base offset, use it
510 if (traf->tfhd->flags & GF_ISOM_TRAF_BASE_OFFSET)
511 base_offset = traf->tfhd->base_data_offset;
512 //no moof offset and no explicit offset, the offset is the end of the last written chunk of
513 //the previous traf. For the first traf, *cumulated_offset is actually moof offset
514 else if (!(traf->tfhd->flags & GF_ISOM_MOOF_BASE_OFFSET))
515 base_offset = *cumulated_offset;
516
517 chunk_size = 0;
518 prev_trun_data_offset = 0;
519 data_offset = 0;
520 traf_duration = 0;
521
522 /*in playback mode*/
523 if (traf->tfdt && is_first_merge) {
524 #ifndef GPAC_DISABLE_LOG
525 if (trak->moov->mov->NextMoofNumber && trak->present_in_scalable_segment && trak->sample_count_at_seg_start && (trak->dts_at_seg_start != traf->tfdt->baseMediaDecodeTime)) {
526 s32 drift = (s32) ((s64) traf->tfdt->baseMediaDecodeTime - (s64)trak->dts_at_seg_start);
527 if (drift<0) {
528 GF_LOG(GF_LOG_WARNING, GF_LOG_CONTAINER, ("[iso file] Warning: TFDT timing "LLD" less than cumulated timing "LLD" - using tfdt\n", traf->tfdt->baseMediaDecodeTime, trak->dts_at_seg_start ));
529 } else {
530 GF_LOG(GF_LOG_INFO, GF_LOG_CONTAINER, ("[iso file] TFDT timing "LLD" higher than cumulated timing "LLD" (last sample got extended in duration)\n", traf->tfdt->baseMediaDecodeTime, trak->dts_at_seg_start ));
531 }
532 }
533 #endif
534 trak->dts_at_seg_start = traf->tfdt->baseMediaDecodeTime;
535 }
536
537 if (trak->moov->mov->signal_frag_bounds) {
538 store_traf_map = GF_TRUE;
539 if (is_first_merge) {
540 GF_MovieFragmentBox *moof_clone = NULL;
541 gf_isom_box_freeze_order((GF_Box *)moof_box);
542 gf_isom_clone_box((GF_Box *)moof_box, (GF_Box **)&moof_clone);
543
544 if (moof_clone) {
545 GF_BitStream *bs;
546 for (i=0; i<gf_list_count(moof_clone->TrackList); i++) {
547 GF_TrackFragmentBox *traf_clone = gf_list_get(moof_clone->TrackList, i);
548 gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->TrackRuns);
549 gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->sampleGroups);
550 gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->sampleGroupsDescription);
551 gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->sub_samples);
552 gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->sai_offsets);
553 gf_isom_box_array_reset_parent(&traf_clone->child_boxes, traf_clone->sai_sizes);
554 if (traf_clone->sample_encryption) {
555 gf_isom_box_del_parent(&traf_clone->child_boxes, (GF_Box *) traf_clone->sample_encryption);
556 traf_clone->sample_encryption = NULL;
557 }
558 if (traf_clone->sdtp) {
559 gf_isom_box_del_parent(&traf_clone->child_boxes, (GF_Box *) traf_clone->sdtp);
560 traf_clone->sdtp = NULL;
561 }
562 }
563 gf_isom_box_size((GF_Box *)moof_clone);
564 bs = gf_bs_new(NULL, 0, GF_BITSTREAM_WRITE);
565
566 if (trak->moov->mov->seg_styp) {
567 gf_isom_box_size(trak->moov->mov->seg_styp);
568 gf_isom_box_write(trak->moov->mov->seg_styp, bs);
569 }
570 if (trak->moov->mov->root_sidx) {
571 gf_isom_box_size((GF_Box *)trak->moov->mov->root_sidx);
572 gf_isom_box_write((GF_Box *)trak->moov->mov->root_sidx, bs);
573 }
574 if (trak->moov->mov->seg_ssix) {
575 gf_isom_box_size(trak->moov->mov->seg_ssix);
576 gf_isom_box_write(trak->moov->mov->seg_ssix, bs);
577 }
578 gf_isom_box_write((GF_Box *)moof_clone, bs);
579 gf_isom_box_del((GF_Box*)moof_clone);
580
581 gf_bs_get_content(bs, &moof_template, &moof_template_size);
582 gf_bs_del(bs);
583 }
584 }
585 if (trak->moov->mov->seg_styp) {
586 is_seg_start = GF_TRUE;
587 seg_start = trak->moov->mov->styp_start_offset;
588 }
589 if (trak->moov->mov->root_sidx) {
590 is_seg_start = GF_TRUE;
591 sidx_start = trak->moov->mov->sidx_start_offset;
592 sidx_end = trak->moov->mov->sidx_end_offset;
593 if (! seg_start || (sidx_start<seg_start))
594 seg_start = sidx_start;
595 }
596 frag_start = trak->moov->mov->current_top_box_start;
597 }
598 else if (trak->moov->mov->store_traf_map) {
599 store_traf_map = GF_TRUE;
600 }
601
602
603 sample_index = 0;
604 i=0;
605 while ((trun = (GF_TrackFragmentRunBox *)gf_list_enum(traf->TrackRuns, &i))) {
606 //merge the run
607 for (j=0; j<trun->sample_count; j++) {
608 GF_Err e;
609 s32 cts_offset=0;
610 ent = (GF_TrunEntry*)gf_list_get(trun->entries, j);
611
612 if (!ent) {
613 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Track %d doesn't have enough trun entries (%d) compared to sample count (%d) in run\n", traf->trex->trackID, gf_list_count(trun->entries), trun->sample_count ));
614 break;
615 }
616 size = def_size;
617 duration = def_duration;
618 flags = def_flags;
619
620 //CTS - if flag not set (trun or ctrn) defaults to 0 which is the base value after alloc
621 //we just need to overrite its value if inherited
622 cts_offset = ent->CTS_Offset;
623
624 #ifdef GF_ENABLE_CTRN
625 if (trun->use_ctrn) {
626 if (!j && (trun->ctrn_flags & GF_ISOM_CTRN_FIRST_SAMPLE) ) {
627 if (trun->ctrn_first_dur) duration = ent->Duration;
628 if (trun->ctrn_first_size) size = ent->size;
629 if (trun->ctrn_first_ctts) flags = ent->flags;
630 } else {
631 if (trun->ctrn_dur) duration = ent->Duration;
632 if (trun->ctrn_size) size = ent->size;
633 if (trun->ctrn_sample_flags) flags = ent->flags;
634 }
635 /*re-override*/
636 if (trun->ctrn_flags & 0xF0) {
637 GF_TrunEntry *ref_entry;
638 if (!traf_ref) {
639 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Track %d use traf inheritance to track ID %d but reference traf not found\n", traf->trex->trackID, traf->trex->inherit_from_traf_id ));
640 break;
641 }
642 ref_entry = traf_get_sample_entry(traf_ref, sample_index);
643 if (!ref_entry) {
644 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[iso file] Track %d use traf inheritance but sample %d not found in reference traf\n", traf->trex->trackID, sample_index+1 ));
645 break;
646 }
647 if (trun->ctrn_flags & GF_ISOM_CTRN_INHERIT_DUR)
648 duration = ref_entry->Duration;
649 if (trun->ctrn_flags & GF_ISOM_CTRN_INHERIT_SIZE)
650 size = ref_entry->size;
651 if (trun->ctrn_flags & GF_ISOM_CTRN_INHERIT_FLAGS)
652 flags = ref_entry->flags;
653 if (trun->ctrn_flags & GF_ISOM_CTRN_INHERIT_CTSO)
654 cts_offset = ref_entry->CTS_Offset;
655 }
656
657 } else
658 #endif
659 {
660 if (trun->flags & GF_ISOM_TRUN_DURATION) duration = ent->Duration;
661 if (trun->flags & GF_ISOM_TRUN_SIZE) size = ent->size;
662 if (trun->flags & GF_ISOM_TRUN_FLAGS) {
663 flags = ent->flags;
664 } else if (!j && (trun->flags & GF_ISOM_TRUN_FIRST_FLAG)) {
665 flags = trun->first_sample_flags;
666 }
667 }
668 sample_index++;
669 /*store the resolved value in case we have inheritance*/
670 ent->size = size;
671 ent->Duration = duration;
672 ent->flags = flags;
673 ent->CTS_Offset = cts_offset;
674
675 //add size first
676 if (!trak->Media->information->sampleTable->SampleSize) {
677 trak->Media->information->sampleTable->SampleSize = (GF_SampleSizeBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_STSZ);
678 if (!trak->Media->information->sampleTable->SampleSize)
679 return GF_OUT_OF_MEM;
680 }
681 e = stbl_AppendSize(trak->Media->information->sampleTable, size, ent->nb_pack);
682 if (e) return e;
683
684 //then TS
685 if (!trak->Media->information->sampleTable->TimeToSample) {
686 trak->Media->information->sampleTable->TimeToSample = (GF_TimeToSampleBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_STTS);
687 if (!trak->Media->information->sampleTable->TimeToSample)
688 return GF_OUT_OF_MEM;
689 }
690 e = stbl_AppendTime(trak->Media->information->sampleTable, duration, ent->nb_pack);
691 if (e) return e;
692
693 //add chunk on first sample
694 if (!j) {
695 u64 final_offset;
696 data_offset = base_offset;
697 //we have an explicit data offset for this trun
698 if (trun->flags & GF_ISOM_TRUN_DATA_OFFSET) {
699 data_offset += trun->data_offset;
700 /*reset chunk size since data is now relative to this trun*/
701 chunk_size = 0;
702 /*remember this data offset for following trun*/
703 prev_trun_data_offset = trun->data_offset;
704 /*if mdat is located after the moof, and the moof was compressed, adjust offset
705 otherwise the offset does not need adjustment*/
706 if (trun->data_offset>=0) {
707 data_offset -= compressed_diff;
708 prev_trun_data_offset -= compressed_diff;
709 }
710 }
711 //we had an explicit data offset for the previous trun, use it + chunk size
712 else if (prev_trun_data_offset) {
713 /*data offset is previous chunk size plus previous offset of the trun*/
714 data_offset += prev_trun_data_offset + chunk_size;
715 }
716 //no explicit data offset, continuous data after last data in previous chunk
717 else {
718 data_offset += chunk_size;
719 //data offset of first trun in first traf, adjust if compressed moof
720 if ((i==1) && (trun->data_offset>=0)) {
721 data_offset -= compressed_diff;
722 }
723 }
724
725 final_offset = data_offset;
726 //adjust offset if moov was also compressed and we are still in the same file
727 //so that later call to gf_isom_get_sample properly adjust back the offset
728 if (trak->moov->compressed_diff) {
729 final_offset += trak->moov->compressed_diff;
730 }
731
732 if (!trak->Media->information->sampleTable->ChunkOffset) {
733 trak->Media->information->sampleTable->ChunkOffset = gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_STCO);
734 if (!trak->Media->information->sampleTable->ChunkOffset)
735 return GF_OUT_OF_MEM;
736 }
737 e = stbl_AppendChunk(trak->Media->information->sampleTable, final_offset);
738 if (e) return e;
739 //then sampleToChunk
740 if (!trak->Media->information->sampleTable->SampleToChunk) {
741 trak->Media->information->sampleTable->SampleToChunk = (GF_SampleToChunkBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_STSC);
742 if (!trak->Media->information->sampleTable->SampleToChunk)
743 return GF_OUT_OF_MEM;
744 }
745 e = stbl_AppendSampleToChunk(trak->Media->information->sampleTable,
746 DescIndex, trun->sample_count);
747 if (e) return e;
748 }
749 chunk_size += size;
750
751 if (store_traf_map && first_samp_in_traf) {
752 first_samp_in_traf = GF_FALSE;
753 e = stbl_AppendTrafMap(trak->Media->information->sampleTable, is_seg_start, seg_start, frag_start, moof_template, moof_template_size, sidx_start, sidx_end);
754 if (e) return e;
755 //do not deallocate, the memory is now owned by traf map
756 moof_template = NULL;
757 moof_template_size = 0;
758 }
759 if (ent->nb_pack) {
760 j+= ent->nb_pack-1;
761 traf_duration += ent->nb_pack*duration;
762 continue;
763 }
764
765 traf_duration += duration;
766
767 e = stbl_AppendCTSOffset(trak->Media->information->sampleTable, cts_offset);
768 if (e) return e;
769 //flags
770 sync = GF_ISOM_GET_FRAG_SYNC(flags);
771 if (trak->Media->information->sampleTable->no_sync_found && sync) {
772 trak->Media->information->sampleTable->no_sync_found = 0;
773 }
774 e = stbl_AppendRAP(trak->Media->information->sampleTable, sync);
775 if (e) return e;
776 pad = GF_ISOM_GET_FRAG_PAD(flags);
777 if (pad) {
778 e = stbl_AppendPadding(trak->Media->information->sampleTable, pad);
779 if (e) return e;
780 }
781 degr = GF_ISOM_GET_FRAG_DEG(flags);
782 if (degr) {
783 e = stbl_AppendDegradation(trak->Media->information->sampleTable, degr);
784 if (e) return e;
785 }
786 e = stbl_AppendDependencyType(trak->Media->information->sampleTable, GF_ISOM_GET_FRAG_LEAD(flags), GF_ISOM_GET_FRAG_DEPENDS(flags), GF_ISOM_GET_FRAG_DEPENDED(flags), GF_ISOM_GET_FRAG_REDUNDANT(flags));
787 if (e) return e;
788 }
789 }
790 if (traf_duration && trak->editBox && trak->editBox->editList) {
791 for (i=0; i<gf_list_count(trak->editBox->editList->entryList); i++) {
792 GF_EdtsEntry *edts_e = gf_list_get(trak->editBox->editList->entryList, i);
793 if (edts_e->was_empty_dur) {
794 u64 extend_dur = traf_duration;
795 extend_dur *= trak->moov->mvhd->timeScale;
796 extend_dur /= trak->Media->mediaHeader->timeScale;
797 edts_e->segmentDuration += extend_dur;
798 }
799 else if (!edts_e->segmentDuration) {
800 edts_e->was_empty_dur = GF_TRUE;
801 if ((s64) traf_duration > edts_e->mediaTime)
802 traf_duration -= edts_e->mediaTime;
803 else
804 traf_duration = 0;
805
806 edts_e->segmentDuration = traf_duration;
807 edts_e->segmentDuration *= trak->moov->mvhd->timeScale;
808 edts_e->segmentDuration /= trak->Media->mediaHeader->timeScale;
809 }
810
811 }
812 }
813
814 //in any case, update the cumulated offset
815 //this will handle hypothetical files mixing MOOF offset and implicit non-moof offset
816 *cumulated_offset = data_offset + chunk_size;
817
818 /*merge sample groups*/
819 if (traf->sampleGroups) {
820 GF_List *groups;
821 GF_List *groupDescs;
822 Bool is_identical_sgpd = GF_TRUE;
823 u32 *new_idx = NULL;
824
825 if (!trak->Media->information->sampleTable->sampleGroups)
826 trak->Media->information->sampleTable->sampleGroups = gf_list_new();
827
828 if (!trak->Media->information->sampleTable->sampleGroupsDescription)
829 trak->Media->information->sampleTable->sampleGroupsDescription = gf_list_new();
830
831 groupDescs = trak->Media->information->sampleTable->sampleGroupsDescription;
832 for (i=0; i<gf_list_count(traf->sampleGroupsDescription); i++) {
833 GF_SampleGroupDescriptionBox *new_sgdesc = NULL;
834 GF_SampleGroupDescriptionBox *sgdesc = gf_list_get(traf->sampleGroupsDescription, i);
835 for (j=0; j<gf_list_count(groupDescs); j++) {
836 new_sgdesc = gf_list_get(groupDescs, j);
837 if (new_sgdesc->grouping_type==sgdesc->grouping_type) break;
838 new_sgdesc = NULL;
839 }
840 /*new description, move it to our sample table*/
841 if (!new_sgdesc) {
842 gf_list_add(groupDescs, sgdesc);
843 gf_list_add(trak->Media->information->sampleTable->child_boxes, sgdesc);
844 gf_list_rem(traf->sampleGroupsDescription, i);
845 gf_list_del_item(traf->child_boxes, sgdesc);
846 i--;
847 }
848 /*merge descriptions*/
849 else {
850 u32 count;
851
852 is_identical_sgpd = gf_isom_is_identical_sgpd(new_sgdesc, sgdesc, 0);
853 if (is_identical_sgpd)
854 continue;
855
856 new_idx = (u32 *)gf_malloc(gf_list_count(sgdesc->group_descriptions)*sizeof(u32));
857 if (!new_idx) return GF_OUT_OF_MEM;
858
859 count = 0;
860 while (gf_list_count(sgdesc->group_descriptions)) {
861 void *sgpd_entry = gf_list_get(sgdesc->group_descriptions, 0);
862 Bool new_entry = GF_TRUE;
863
864 for (j = 0; j < gf_list_count(new_sgdesc->group_descriptions); j++) {
865 void *ptr = gf_list_get(new_sgdesc->group_descriptions, j);
866 if (gf_isom_is_identical_sgpd(sgpd_entry, ptr, new_sgdesc->grouping_type)) {
867 new_idx[count] = j + 1;
868 count ++;
869 new_entry = GF_FALSE;
870 gf_free(sgpd_entry);
871 break;
872 }
873 }
874
875 if (new_entry) {
876 gf_list_add(new_sgdesc->group_descriptions, sgpd_entry);
877 new_idx[count] = gf_list_count(new_sgdesc->group_descriptions);
878 count ++;
879 }
880
881 gf_list_rem(sgdesc->group_descriptions, 0);
882 }
883 }
884 }
885
886 groups = trak->Media->information->sampleTable->sampleGroups;
887 for (i=0; i<gf_list_count(traf->sampleGroups); i++) {
888 GF_SampleGroupBox *stbl_group = NULL;
889 GF_SampleGroupBox *frag_group = gf_list_get(traf->sampleGroups, i);
890
891
892 for (j=0; j<gf_list_count(groups); j++) {
893 stbl_group = gf_list_get(groups, j);
894 if ((frag_group->grouping_type==stbl_group->grouping_type) && (frag_group->grouping_type_parameter==stbl_group->grouping_type_parameter))
895 break;
896 stbl_group = NULL;
897 }
898 if (!stbl_group) {
899 stbl_group = (GF_SampleGroupBox *) gf_isom_box_new_parent(&trak->Media->information->sampleTable->child_boxes, GF_ISOM_BOX_TYPE_SBGP);
900 if (!stbl_group) return GF_OUT_OF_MEM;
901 stbl_group->grouping_type = frag_group->grouping_type;
902 stbl_group->grouping_type_parameter = frag_group->grouping_type_parameter;
903 stbl_group->version = frag_group->version;
904 gf_list_add(groups, stbl_group);
905 }
906
907 if (is_identical_sgpd) {
908 //adjust sgpd index: in traf index start at 0x1001
909 for (j = 0; j < frag_group->entry_count; j++)
910 frag_group->sample_entries[j].group_description_index &= 0x0FFFF;
911 if (frag_group->entry_count && stbl_group->entry_count &&
912 (frag_group->sample_entries[0].group_description_index==stbl_group->sample_entries[stbl_group->entry_count-1].group_description_index)
913 ) {
914 stbl_group->sample_entries[stbl_group->entry_count - 1].sample_count += frag_group->sample_entries[0].sample_count;
915 if (frag_group->entry_count>1) {
916 stbl_group->sample_entries = gf_realloc(stbl_group->sample_entries, sizeof(GF_SampleGroupEntry) * (stbl_group->entry_count + frag_group->entry_count - 1));
917 memcpy(&stbl_group->sample_entries[stbl_group->entry_count], &frag_group->sample_entries[1], sizeof(GF_SampleGroupEntry) * (frag_group->entry_count - 1));
918 stbl_group->entry_count += frag_group->entry_count - 1;
919 }
920 } else {
921 stbl_group->sample_entries = gf_realloc(stbl_group->sample_entries, sizeof(GF_SampleGroupEntry) * (stbl_group->entry_count + frag_group->entry_count));
922 memcpy(&stbl_group->sample_entries[stbl_group->entry_count], &frag_group->sample_entries[0], sizeof(GF_SampleGroupEntry) * frag_group->entry_count);
923 stbl_group->entry_count += frag_group->entry_count;
924 }
925 } else {
926 stbl_group->sample_entries = gf_realloc(stbl_group->sample_entries, sizeof(GF_SampleGroupEntry) * (stbl_group->entry_count + frag_group->entry_count));
927 //adjust sgpd index
928 for (j = 0; j < frag_group->entry_count; j++)
929 frag_group->sample_entries[j].group_description_index = new_idx[j];
930 memcpy(&stbl_group->sample_entries[stbl_group->entry_count], &frag_group->sample_entries[0], sizeof(GF_SampleGroupEntry) * frag_group->entry_count);
931 stbl_group->entry_count += frag_group->entry_count;
932 }
933 }
934
935 if (new_idx) gf_free(new_idx);
936 }
937
938 /*content is encrypted*/
939 track_num = gf_isom_get_tracknum_from_id(trak->moov, trak->Header->trackID);
940 if (gf_isom_is_cenc_media(trak->moov->mov, track_num, DescIndex)
941 || traf->sample_encryption) {
942 /*Merge sample auxiliary encryption information*/
943 GF_SampleEncryptionBox *senc = NULL;
944 GF_List *sais = NULL;
945 u32 scheme_type;
946 gf_isom_get_cenc_info(trak->moov->mov, track_num, DescIndex, NULL, &scheme_type, NULL, NULL);
947
948 if (traf->sample_encryption) {
949 for (i = 0; i < gf_list_count(trak->Media->information->sampleTable->child_boxes); i++) {
950 GF_Box *a = (GF_Box *)gf_list_get(trak->Media->information->sampleTable->child_boxes, i);
951 if (a->type != traf->sample_encryption->type) continue;
952
953 if ((a->type ==GF_ISOM_BOX_TYPE_UUID) && (((GF_UUIDBox *)a)->internal_4cc == GF_ISOM_BOX_UUID_PSEC)) {
954 senc = (GF_SampleEncryptionBox *)a;
955 break;
956 }
957 else if (a->type ==GF_ISOM_BOX_TYPE_SENC) {
958 senc = (GF_SampleEncryptionBox *)a;
959 break;
960 }
961 }
962 if (!senc && trak->sample_encryption)
963 senc = trak->sample_encryption;
964
965 if (!senc) {
966 if (traf->sample_encryption->is_piff) {
967 senc = (GF_SampleEncryptionBox *)gf_isom_create_piff_psec_box(1, 0x2, 0, 0, NULL);
968 } else {
969 senc = gf_isom_create_samp_enc_box(1, 0x2);
970 }
971
972 if (!trak->Media->information->sampleTable->child_boxes) trak->Media->information->sampleTable->child_boxes = gf_list_new();
973
974 trak->sample_encryption = senc;
975 if (!trak->child_boxes) trak->child_boxes = gf_list_new();
976 gf_list_add(trak->child_boxes, senc);
977 }
978
979 sais = traf->sample_encryption->samp_aux_info;
980 }
981
982 /*get sample auxiliary information by saiz/saio rather than by parsing senc box*/
983 if (gf_isom_cenc_has_saiz_saio_traf(traf, scheme_type)) {
984 u32 nb_saio;
985 u32 aux_info_type;
986 u64 offset;
987 GF_Err e;
988 Bool is_encrypted;
989 GF_SampleAuxiliaryInfoOffsetBox *saio = NULL;
990 GF_SampleAuxiliaryInfoSizeBox *saiz = NULL;
991
992 offset = nb_saio = 0;
993
994 for (i = 0; i < gf_list_count(traf->sai_offsets); i++) {
995 saio = (GF_SampleAuxiliaryInfoOffsetBox *)gf_list_get(traf->sai_offsets, i);
996 aux_info_type = saio->aux_info_type;
997 if (!aux_info_type) aux_info_type = scheme_type;
998
999 /*if we have only 1 sai_offsets, assume that its type is cenc*/
1000 if ((aux_info_type == GF_ISOM_CENC_SCHEME) || (aux_info_type == GF_ISOM_CBC_SCHEME) ||
1001 (aux_info_type == GF_ISOM_CENS_SCHEME) || (aux_info_type == GF_ISOM_CBCS_SCHEME) ||
1002 (gf_list_count(traf->sai_offsets) == 1)) {
1003 offset = saio->offsets[0] + moof_offset;
1004 nb_saio = saio->entry_count;
1005 break;
1006 }
1007 }
1008 for (i = 0; i < gf_list_count(traf->sai_sizes); i++) {
1009 saiz = (GF_SampleAuxiliaryInfoSizeBox *)gf_list_get(traf->sai_sizes, i);
1010 aux_info_type = saiz->aux_info_type;
1011 if (!aux_info_type) aux_info_type = scheme_type;
1012 /*if we have only 1 sai_sizes, assume that its type is cenc*/
1013 if ((aux_info_type == GF_ISOM_CENC_SCHEME) || (aux_info_type == GF_ISOM_CBC_SCHEME) ||
1014 (aux_info_type == GF_ISOM_CENS_SCHEME) || (aux_info_type == GF_ISOM_CBCS_SCHEME) ||
1015 (gf_list_count(traf->sai_sizes) == 1)) {
1016 break;
1017 }
1018 }
1019 if (saiz && saio) {
1020 for (i = 0; i < saiz->sample_count; i++) {
1021 GF_CENCSampleAuxInfo *sai;
1022
1023 u64 cur_position;
1024 if (nb_saio != 1)
1025 offset = saio->offsets[i] + moof_offset;
1026 size = saiz->default_sample_info_size ? saiz->default_sample_info_size : saiz->sample_info_size[i];
1027
1028
1029 cur_position = gf_bs_get_position(trak->moov->mov->movieFileMap->bs);
1030 gf_bs_seek(trak->moov->mov->movieFileMap->bs, offset);
1031
1032 GF_SAFEALLOC(sai, GF_CENCSampleAuxInfo);
1033 if (!sai) return GF_OUT_OF_MEM;
1034
1035 e = gf_isom_get_sample_cenc_info_ex(trak, traf, senc, i+1, &is_encrypted, &sai->IV_size, NULL, NULL, NULL, NULL, NULL);
1036 if (e) {
1037 GF_LOG(GF_LOG_ERROR, GF_LOG_CONTAINER, ("[isobmf] could not get cenc info for sample %d: %s\n", i+1, gf_error_to_string(e) ));
1038 return e;
1039 }
1040
1041 if (is_encrypted) {
1042 gf_bs_read_data(trak->moov->mov->movieFileMap->bs, (char *)sai->IV, sai->IV_size);
1043 if (size > sai->IV_size) {
1044 sai->subsample_count = gf_bs_read_u16(trak->moov->mov->movieFileMap->bs);
1045 sai->subsamples = (GF_CENCSubSampleEntry *)gf_malloc(sizeof(GF_CENCSubSampleEntry)*sai->subsample_count);
1046 if (!sai->subsamples) return GF_OUT_OF_MEM;
1047 for (j = 0; j < sai->subsample_count; j++) {
1048 sai->subsamples[j].bytes_clear_data = gf_bs_read_u16(trak->moov->mov->movieFileMap->bs);
1049 sai->subsamples[j].bytes_encrypted_data = gf_bs_read_u32(trak->moov->mov->movieFileMap->bs);
1050 }
1051 }
1052 } else {
1053 sai->IV_size=0;
1054 sai->subsample_count=0;
1055 }
1056
1057 gf_bs_seek(trak->moov->mov->movieFileMap->bs, cur_position);
1058
1059 gf_list_add(senc->samp_aux_info, sai);
1060 if (sai->subsample_count) senc->flags = 0x00000002;
1061 e = gf_isom_cenc_merge_saiz_saio(senc, trak->Media->information->sampleTable, offset, size);
1062 if (e) return e;
1063 if (nb_saio == 1)
1064 offset += size;
1065 }
1066 }
1067 } else if (sais) {
1068 for (i = 0; i < gf_list_count(sais); i++) {
1069 GF_CENCSampleAuxInfo *sai, *new_sai;
1070
1071 sai = (GF_CENCSampleAuxInfo *)gf_list_get(sais, i);
1072
1073 new_sai = (GF_CENCSampleAuxInfo *)gf_malloc(sizeof(GF_CENCSampleAuxInfo));
1074 if (!new_sai) return GF_OUT_OF_MEM;
1075 new_sai->IV_size = sai->IV_size;
1076 memmove((char *)new_sai->IV, (const char*)sai->IV, 16);
1077 new_sai->subsample_count = sai->subsample_count;
1078 new_sai->subsamples = (GF_CENCSubSampleEntry *)gf_malloc(new_sai->subsample_count*sizeof(GF_CENCSubSampleEntry));
1079 if (!new_sai->subsamples) return GF_OUT_OF_MEM;
1080 memmove(new_sai->subsamples, sai->subsamples, new_sai->subsample_count*sizeof(GF_CENCSubSampleEntry));
1081
1082 gf_list_add(senc->samp_aux_info, new_sai);
1083 if (sai->subsample_count) senc->flags = 0x00000002;
1084 }
1085 } else if (traf->sample_encryption) {
1086 senc_Parse(trak->moov->mov->movieFileMap->bs, trak, traf, traf->sample_encryption);
1087 trak->sample_encryption->AlgorithmID = traf->sample_encryption->AlgorithmID;
1088 if (!trak->sample_encryption->IV_size)
1089 trak->sample_encryption->IV_size = traf->sample_encryption->IV_size;
1090 if (!trak->sample_encryption->samp_aux_info) trak->sample_encryption->samp_aux_info = gf_list_new();
1091 gf_list_transfer(trak->sample_encryption->samp_aux_info, traf->sample_encryption->samp_aux_info);
1092 }
1093 }
1094 return GF_OK;
1095 }
1096
1097 #endif
1098
1099
1100 #ifndef GPAC_DISABLE_ISOM_WRITE
1101
1102 //used to check if a TrackID is available
RequestTrack(GF_MovieBox * moov,GF_ISOTrackID TrackID)1103 u8 RequestTrack(GF_MovieBox *moov, GF_ISOTrackID TrackID)
1104 {
1105 u32 i;
1106 GF_TrackBox *trak;
1107
1108 i=0;
1109 while ((trak = (GF_TrackBox *)gf_list_enum(moov->trackList, &i))) {
1110 if (trak->Header->trackID == TrackID) {
1111 gf_isom_set_last_error(moov->mov, GF_BAD_PARAM);
1112 return 0;
1113 }
1114 }
1115 return 1;
1116 }
1117
Track_RemoveRef(GF_TrackBox * trak,u32 ReferenceType)1118 GF_Err Track_RemoveRef(GF_TrackBox *trak, u32 ReferenceType)
1119 {
1120 GF_TrackReferenceBox *ref;
1121 GF_Box *a;
1122 u32 i;
1123 if (! trak) return GF_BAD_PARAM;
1124 if (! trak->References) return GF_OK;
1125 ref = trak->References;
1126 i=0;
1127 while ((a = (GF_Box *)gf_list_enum(ref->child_boxes, &i))) {
1128 if (a->type == ReferenceType) {
1129 gf_isom_box_del_parent(&ref->child_boxes, a);
1130 return GF_OK;
1131 }
1132 }
1133 return GF_OK;
1134 }
1135
NewMedia(GF_MediaBox ** mdia,u32 MediaType,u32 TimeScale)1136 GF_Err NewMedia(GF_MediaBox **mdia, u32 MediaType, u32 TimeScale)
1137 {
1138 GF_MediaHeaderBox *mdhd;
1139 GF_Box *mediaInfo;
1140 GF_HandlerBox *hdlr;
1141 GF_MediaInformationBox *minf;
1142 GF_DataInformationBox *dinf;
1143 GF_SampleTableBox *stbl;
1144 GF_DataReferenceBox *dref;
1145 char *str="";
1146
1147 GF_Err e;
1148
1149 if (!mdia) return GF_BAD_PARAM;
1150
1151 minf = *mdia ? (*mdia)->information : NULL;
1152 mdhd = *mdia ? (*mdia)->mediaHeader : NULL;
1153 hdlr = *mdia ? (*mdia)->handler : NULL;
1154 dinf = minf ? minf->dataInformation : NULL;
1155 stbl = minf ? minf->sampleTable : NULL;
1156 dref = dinf ? dinf->dref : NULL;
1157 mediaInfo = minf ? minf->InfoHeader : NULL;
1158
1159 //first create the media
1160 if (!*mdia) {
1161 *mdia = (GF_MediaBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MDIA);
1162 if (! *mdia) { e = GF_OUT_OF_MEM; goto err_exit; }
1163 }
1164 if (!mdhd) {
1165 mdhd = (GF_MediaHeaderBox *) gf_isom_box_new_parent( & ((*mdia)->child_boxes), GF_ISOM_BOX_TYPE_MDHD);
1166 if (! mdhd) { e = GF_OUT_OF_MEM; goto err_exit; }
1167 e = mdia_on_child_box((GF_Box*)*mdia, (GF_Box *) mdhd);
1168 if (e) goto err_exit;
1169 }
1170 if (!hdlr) {
1171 hdlr = (GF_HandlerBox *) gf_isom_box_new_parent(& ((*mdia)->child_boxes), GF_ISOM_BOX_TYPE_HDLR);
1172 if (! hdlr) { e = GF_OUT_OF_MEM; goto err_exit; }
1173 e = mdia_on_child_box((GF_Box*)*mdia, (GF_Box *) hdlr);
1174 if (e) goto err_exit;
1175 }
1176 if (!minf) {
1177 minf = (GF_MediaInformationBox *) gf_isom_box_new_parent(& ((*mdia)->child_boxes), GF_ISOM_BOX_TYPE_MINF);
1178 if (! minf) { e = GF_OUT_OF_MEM; goto err_exit; }
1179 e = mdia_on_child_box((GF_Box*)*mdia, (GF_Box *) minf);
1180 if (e) goto err_exit;
1181 }
1182 if (!dinf) {
1183 dinf = (GF_DataInformationBox *) gf_isom_box_new_parent(&minf->child_boxes, GF_ISOM_BOX_TYPE_DINF);
1184 if (! dinf) { e = GF_OUT_OF_MEM; goto err_exit; }
1185 e = minf_on_child_box((GF_Box*)minf, (GF_Box *) dinf);
1186 if (e) goto err_exit;
1187 }
1188
1189 if (!mediaInfo) {
1190 //"handler name" is for debugging purposes. Let's stick our name here ;)
1191 switch (MediaType) {
1192 case GF_ISOM_MEDIA_VISUAL:
1193 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD);
1194 str = "GPAC ISO Video Handler";
1195 break;
1196 case GF_ISOM_MEDIA_AUXV:
1197 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD);
1198 str = "GPAC ISO Auxiliary Video Handler";
1199 break;
1200 case GF_ISOM_MEDIA_PICT:
1201 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD);
1202 str = "GPAC ISO Picture Sequence Handler";
1203 break;
1204 case GF_ISOM_MEDIA_AUDIO:
1205 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_SMHD);
1206 str = "GPAC ISO Audio Handler";
1207 break;
1208 case GF_ISOM_MEDIA_HINT:
1209 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_HMHD);
1210 str = "GPAC ISO Hint Handler";
1211 break;
1212 case GF_ISOM_MEDIA_META:
1213 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1214 str = "GPAC Timed MetaData Handler";
1215 break;
1216 case GF_ISOM_MEDIA_OD:
1217 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1218 str = "GPAC MPEG-4 OD Handler";
1219 break;
1220 case GF_ISOM_MEDIA_OCR:
1221 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1222 str = "GPAC MPEG-4 OCR Handler";
1223 break;
1224 case GF_ISOM_MEDIA_SCENE:
1225 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1226 str = "GPAC MPEG-4 Scene Description Handler";
1227 break;
1228 case GF_ISOM_MEDIA_MPEG7:
1229 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1230 str = "GPAC MPEG-4 MPEG-7 Handler";
1231 break;
1232 case GF_ISOM_MEDIA_OCI:
1233 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1234 str = "GPAC MPEG-4 OCI Handler";
1235 break;
1236 case GF_ISOM_MEDIA_IPMP:
1237 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1238 str = "GPAC MPEG-4 IPMP Handler";
1239 break;
1240 case GF_ISOM_MEDIA_MPEGJ:
1241 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1242 str = "GPAC MPEG-4 MPEG-J Handler";
1243 break;
1244 case GF_ISOM_MEDIA_TEXT:
1245 case GF_ISOM_MEDIA_SUBT:
1246 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1247 str = "GPAC Streaming Text Handler";
1248 break;
1249 case GF_ISOM_MEDIA_MPEG_SUBT:
1250 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_STHD);
1251 str = "GPAC MPEG Subtitle Handler";
1252 break;
1253 case GF_ISOM_MEDIA_DIMS:
1254 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_VMHD);
1255 MediaType = GF_ISOM_MEDIA_SCENE;
1256 str = "GPAC DIMS Handler";
1257 break;
1258 case GF_ISOM_MEDIA_TMCD:
1259 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_GMHD);
1260 str = "GPAC TMCD Handler";
1261 break;
1262 default:
1263 mediaInfo = gf_isom_box_new(GF_ISOM_BOX_TYPE_NMHD);
1264 str = "GPAC IsoMedia Handler";
1265 break;
1266 }
1267 if (! mediaInfo) { e = GF_OUT_OF_MEM; goto err_exit; }
1268 if (!minf->child_boxes) minf->child_boxes = gf_list_new();
1269 gf_list_add(minf->child_boxes, mediaInfo);
1270
1271 e = minf_on_child_box((GF_Box*)minf, (GF_Box *) mediaInfo);
1272 if (e) goto err_exit;
1273 }
1274
1275 mdhd->timeScale = TimeScale;
1276 hdlr->handlerType = MediaType;
1277 if (!hdlr->nameUTF8)
1278 hdlr->nameUTF8 = gf_strdup(str);
1279
1280 if (!dref) {
1281 //Create a data reference WITHOUT DATA ENTRY (we don't know anything yet about the media Data)
1282 dref = (GF_DataReferenceBox *) gf_isom_box_new_parent(&dinf->child_boxes, GF_ISOM_BOX_TYPE_DREF);
1283 if (! dref) { e = GF_OUT_OF_MEM; goto err_exit; }
1284 e = dinf_on_child_box((GF_Box*)dinf, (GF_Box *)dref);
1285 if (e) goto err_exit;
1286 }
1287
1288 if (!stbl) {
1289 //first set-up the sample table...
1290 stbl = (GF_SampleTableBox *) gf_isom_box_new_parent(&minf->child_boxes, GF_ISOM_BOX_TYPE_STBL);
1291 if (! stbl) { e = GF_OUT_OF_MEM; goto err_exit; }
1292
1293 e = minf_on_child_box((GF_Box*)minf, (GF_Box *) stbl);
1294 if (e) goto err_exit;
1295 }
1296 if (!stbl->SampleDescription) {
1297 stbl->SampleDescription = (GF_SampleDescriptionBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSD);
1298 if (! stbl->SampleDescription) { e = GF_OUT_OF_MEM; goto err_exit; }
1299 }
1300
1301 //by default create a regular table, 32 but offset and normal sample size
1302 if (!stbl->ChunkOffset) {
1303 stbl->ChunkOffset = gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STCO);
1304 if (! stbl->ChunkOffset) { e = GF_OUT_OF_MEM; goto err_exit; }
1305 }
1306 if (!stbl->SampleSize) {
1307 stbl->SampleSize = (GF_SampleSizeBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSZ);
1308 if (! stbl->SampleSize) { e = GF_OUT_OF_MEM; goto err_exit; }
1309 }
1310 if (!stbl->SampleToChunk) {
1311 stbl->SampleToChunk = (GF_SampleToChunkBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSC);
1312 if (! stbl->SampleToChunk) { e = GF_OUT_OF_MEM; goto err_exit; }
1313 }
1314 if (!stbl->TimeToSample) {
1315 stbl->TimeToSample = (GF_TimeToSampleBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STTS);
1316 if (! stbl->TimeToSample) { e = GF_OUT_OF_MEM; goto err_exit; }
1317 }
1318 if (!stbl->SampleDescription) {
1319 stbl->SampleDescription = (GF_SampleDescriptionBox *) gf_isom_box_new_parent(&stbl->child_boxes, GF_ISOM_BOX_TYPE_STSD);
1320 if (! stbl->SampleDescription) { e = GF_OUT_OF_MEM; goto err_exit; }
1321 }
1322 return GF_OK;
1323
1324 err_exit:
1325 if (mdhd) gf_isom_box_del_parent(& ((*mdia)->child_boxes), (GF_Box *)mdhd);
1326 if (minf) gf_isom_box_del_parent(& ((*mdia)->child_boxes), (GF_Box *)minf);
1327 if (hdlr) {
1328 gf_isom_box_del_parent(& ((*mdia)->child_boxes) , (GF_Box *)hdlr);
1329 }
1330 return e;
1331
1332 }
1333
Track_SetStreamDescriptor(GF_TrackBox * trak,u32 StreamDescriptionIndex,u32 DataReferenceIndex,GF_ESD * esd,u32 * outStreamIndex)1334 GF_Err Track_SetStreamDescriptor(GF_TrackBox *trak, u32 StreamDescriptionIndex, u32 DataReferenceIndex, GF_ESD *esd, u32 *outStreamIndex)
1335 {
1336 GF_Err e;
1337 GF_MPEGSampleEntryBox *entry;
1338 GF_MPEGVisualSampleEntryBox *entry_v;
1339 GF_MPEGAudioSampleEntryBox *entry_a;
1340 GF_TrackReferenceBox *tref;
1341 GF_TrackReferenceTypeBox *dpnd;
1342 u16 tmpRef;
1343
1344 entry = NULL;
1345 tref = NULL;
1346
1347 if (!trak || !esd || (!outStreamIndex && !DataReferenceIndex) ) return GF_BAD_PARAM;
1348 if (!Track_IsMPEG4Stream(trak->Media->handler->handlerType)) return GF_ISOM_INVALID_MEDIA;
1349
1350
1351 esd->ESID = 0;
1352 //set SL to predefined if no url
1353 if (esd->URLString == NULL) {
1354 if (!esd->slConfig) esd->slConfig = (GF_SLConfig*) gf_odf_desc_new(GF_ODF_SLC_TAG);
1355 esd->slConfig->predefined = SLPredef_MP4;
1356 esd->slConfig->durationFlag = 0;
1357 esd->slConfig->useTimestampsFlag = 1;
1358 }
1359
1360 //get the REF box if needed
1361 if (esd->dependsOnESID || (esd->OCRESID && (esd->OCRESID != trak->moov->mov->es_id_default_sync)) ) {
1362 if (!trak->References) {
1363 tref = (GF_TrackReferenceBox *) gf_isom_box_new_parent(&trak->child_boxes, GF_ISOM_BOX_TYPE_TREF);
1364 if (!tref) return GF_OUT_OF_MEM;
1365 e = trak_on_child_box((GF_Box*)trak, (GF_Box *)tref);
1366 if (e) return e;
1367 }
1368 tref = trak->References;
1369 }
1370
1371 //Update Stream dependencies
1372 e = Track_FindRef(trak, GF_ISOM_REF_DECODE, &dpnd);
1373 if (e) return e;
1374
1375 if (!dpnd && esd->dependsOnESID) {
1376 e = Track_FindRef(trak, GF_ISOM_REF_BASE, &dpnd);
1377 if (e) return e;
1378 }
1379
1380 if (!dpnd && esd->dependsOnESID) {
1381 dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT);
1382 dpnd->reference_type = GF_ISOM_BOX_TYPE_DPND;
1383 e = reftype_AddRefTrack(dpnd, esd->dependsOnESID, NULL);
1384 if (e) return e;
1385 } else if (dpnd && !esd->dependsOnESID) {
1386 Track_RemoveRef(trak, GF_ISOM_BOX_TYPE_DPND);
1387 }
1388 esd->dependsOnESID = 0;
1389
1390 //Update GF_Clock dependencies
1391 e = Track_FindRef(trak, GF_ISOM_REF_OCR, &dpnd);
1392 if (e) return e;
1393 if (!dpnd && esd->OCRESID && (esd->OCRESID != trak->moov->mov->es_id_default_sync)) {
1394 dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT);
1395 if (!dpnd) return GF_OUT_OF_MEM;
1396 dpnd->reference_type = GF_ISOM_BOX_TYPE_SYNC;
1397 e = reftype_AddRefTrack(dpnd, esd->OCRESID, NULL);
1398 if (e) return e;
1399 } else if (dpnd && !esd->OCRESID) {
1400 Track_RemoveRef(trak, GF_ISOM_BOX_TYPE_SYNC);
1401 } else if (dpnd && esd->OCRESID) {
1402 if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA;
1403 dpnd->trackIDs[0] = esd->OCRESID;
1404 }
1405 esd->OCRESID = 0;
1406
1407 //brand new case: we have to change the IPI desc
1408 if (esd->ipiPtr) {
1409 e = Track_FindRef(trak, GF_ISOM_REF_IPI, &dpnd);
1410 if (e) return e;
1411 if (!dpnd) {
1412 tmpRef = 0;
1413 dpnd = (GF_TrackReferenceTypeBox *) gf_isom_box_new_parent(&tref->child_boxes, GF_ISOM_BOX_TYPE_REFT);
1414 if (!dpnd) return GF_OUT_OF_MEM;
1415 dpnd->reference_type = GF_ISOM_BOX_TYPE_IPIR;
1416 e = reftype_AddRefTrack(dpnd, esd->ipiPtr->IPI_ES_Id, &tmpRef);
1417 if (e) return e;
1418 //and replace the tag and value...
1419 esd->ipiPtr->IPI_ES_Id = tmpRef;
1420 esd->ipiPtr->tag = GF_ODF_ISOM_IPI_PTR_TAG;
1421 } else {
1422 //Watch out! ONLY ONE IPI dependency is allowed per stream
1423 if (dpnd->trackIDCount != 1) return GF_ISOM_INVALID_MEDIA;
1424 //if an existing one is there, what shall we do ???
1425 //donno, erase it
1426 dpnd->trackIDs[0] = esd->ipiPtr->IPI_ES_Id;
1427 //and replace the tag and value...
1428 esd->ipiPtr->IPI_ES_Id = 1;
1429 esd->ipiPtr->tag = GF_ODF_ISOM_IPI_PTR_TAG;
1430 }
1431 }
1432
1433 /*don't store the lang desc in ESD, use the media header language info*/
1434 if (esd->langDesc) {
1435 trak->Media->mediaHeader->packedLanguage[0] = (esd->langDesc->langCode>>16)&0xFF;
1436 trak->Media->mediaHeader->packedLanguage[1] = (esd->langDesc->langCode>>8)&0xFF;
1437 trak->Media->mediaHeader->packedLanguage[2] = (esd->langDesc->langCode)&0xFF;
1438 gf_odf_desc_del((GF_Descriptor *)esd->langDesc);
1439 esd->langDesc = NULL;
1440 }
1441
1442 //we have a streamDescritpionIndex, use it
1443 if (StreamDescriptionIndex) {
1444 u32 entry_type;
1445 entry = (GF_MPEGSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex - 1);
1446 if (!entry) return GF_ISOM_INVALID_FILE;
1447
1448 entry_type = entry->type;
1449 GF_ProtectionSchemeInfoBox *sinf = (GF_ProtectionSchemeInfoBox *) gf_isom_box_find_child(entry->child_boxes, GF_ISOM_BOX_TYPE_SINF);
1450 if (sinf && sinf->original_format) entry_type = sinf->original_format->data_format;
1451
1452 switch (entry_type) {
1453 case GF_ISOM_BOX_TYPE_MP4S:
1454 //OK, delete the previous ESD
1455 gf_odf_desc_del((GF_Descriptor *) entry->esd->desc);
1456 entry->esd->desc = esd;
1457 break;
1458 case GF_ISOM_BOX_TYPE_MP4V:
1459 entry_v = (GF_MPEGVisualSampleEntryBox*) entry;
1460 //OK, delete the previous ESD
1461 gf_odf_desc_del((GF_Descriptor *) entry_v->esd->desc);
1462 entry_v->esd->desc = esd;
1463 break;
1464 case GF_ISOM_BOX_TYPE_MP4A:
1465 entry_a = (GF_MPEGAudioSampleEntryBox*) entry;
1466 if (entry_a->esd) { // some non-conformant files may not have an ESD ...
1467 //OK, delete the previous ESD
1468 gf_odf_desc_del((GF_Descriptor *) entry_a->esd->desc);
1469 entry_a->esd->desc = esd;
1470 } else {
1471 // can't return OK here otherwise we can't know if esd hasn't been used
1472 // and need to be freed
1473 return GF_ISOM_INVALID_MEDIA;
1474 }
1475 break;
1476 case GF_ISOM_BOX_TYPE_AVC1:
1477 case GF_ISOM_BOX_TYPE_AVC2:
1478 case GF_ISOM_BOX_TYPE_AVC3:
1479 case GF_ISOM_BOX_TYPE_AVC4:
1480 case GF_ISOM_BOX_TYPE_SVC1:
1481 case GF_ISOM_BOX_TYPE_MVC1:
1482 case GF_ISOM_BOX_TYPE_HVC1:
1483 case GF_ISOM_BOX_TYPE_HEV1:
1484 case GF_ISOM_BOX_TYPE_HVC2:
1485 case GF_ISOM_BOX_TYPE_HEV2:
1486 case GF_ISOM_BOX_TYPE_LHE1:
1487 case GF_ISOM_BOX_TYPE_LHV1:
1488 case GF_ISOM_BOX_TYPE_HVT1:
1489 e = AVC_HEVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry, esd);
1490 if (e) return e;
1491 break;
1492 case GF_ISOM_BOX_TYPE_LSR1:
1493 e = LSR_UpdateESD((GF_LASeRSampleEntryBox*)entry, esd);
1494 if (e) return e;
1495 break;
1496 case GF_ISOM_BOX_TYPE_AV01:
1497 case GF_ISOM_BOX_TYPE_AV1C:
1498 case GF_ISOM_BOX_TYPE_OPUS:
1499 case GF_ISOM_BOX_TYPE_DOPS:
1500 case GF_ISOM_BOX_TYPE_STXT:
1501 case GF_ISOM_BOX_TYPE_WVTT:
1502 case GF_ISOM_BOX_TYPE_STPP:
1503 if (esd) gf_odf_desc_del((GF_Descriptor *) esd);
1504 break;
1505
1506 default:
1507 //silently fail, not an MPEG-4 esd
1508 gf_odf_desc_del((GF_Descriptor *) esd);
1509 return GF_OK;
1510 }
1511 } else {
1512 //need to check we're not in URL mode where only ONE description is allowed...
1513 StreamDescriptionIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes);
1514 if (StreamDescriptionIndex) {
1515 entry = (GF_MPEGSampleEntryBox*)gf_list_get(trak->Media->information->sampleTable->SampleDescription->child_boxes, StreamDescriptionIndex - 1);
1516 if (!entry) return GF_ISOM_INVALID_FILE;
1517 if (entry->esd && entry->esd->desc->URLString) return GF_BAD_PARAM;
1518 }
1519
1520 //OK, check the handler and create the entry
1521 switch (trak->Media->handler->handlerType) {
1522 case GF_ISOM_MEDIA_AUXV:
1523 case GF_ISOM_MEDIA_PICT:
1524 case GF_ISOM_MEDIA_VISUAL:
1525 if ((esd->decoderConfig->objectTypeIndication==GF_CODECID_AVC) || (esd->decoderConfig->objectTypeIndication==GF_CODECID_SVC) || (esd->decoderConfig->objectTypeIndication==GF_CODECID_MVC)) {
1526 entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_AVC1);
1527 if (!entry_v) return GF_OUT_OF_MEM;
1528 e = AVC_HEVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry_v, esd);
1529 if (e) return e;
1530 } else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_HEVC) {
1531 entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_HVC1);
1532 if (!entry_v) return GF_OUT_OF_MEM;
1533 e = AVC_HEVC_UpdateESD((GF_MPEGVisualSampleEntryBox*)entry_v, esd);
1534 if (e) return e;
1535 } else {
1536 entry_v = (GF_MPEGVisualSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4V);
1537 if (!entry_v) return GF_OUT_OF_MEM;
1538 entry_v->esd = (GF_ESDBox *) gf_isom_box_new_parent(&entry_v->child_boxes, GF_ISOM_BOX_TYPE_ESDS);
1539 if (!entry_v->esd) return GF_OUT_OF_MEM;
1540 entry_v->esd->desc = esd;
1541 }
1542
1543 //type cast possible now
1544 entry = (GF_MPEGSampleEntryBox*) entry_v;
1545 break;
1546 case GF_ISOM_MEDIA_AUDIO:
1547 if (esd->decoderConfig->objectTypeIndication == GF_CODECID_OPUS) {
1548 GF_MPEGAudioSampleEntryBox *opus = (GF_MPEGAudioSampleEntryBox *)gf_isom_box_new(GF_ISOM_BOX_TYPE_OPUS);
1549 if (!opus) return GF_OUT_OF_MEM;
1550 opus->cfg_opus = (GF_OpusSpecificBox *)gf_isom_box_new_parent(&opus->child_boxes, GF_ISOM_BOX_TYPE_DOPS);
1551 if (!opus->cfg_opus) return GF_OUT_OF_MEM;
1552 entry = (GF_MPEGSampleEntryBox*)opus;
1553 gf_odf_desc_del((GF_Descriptor *) esd);
1554 } else if (esd->decoderConfig->objectTypeIndication == GF_CODECID_AC3) {
1555 GF_MPEGAudioSampleEntryBox *ac3 = (GF_MPEGAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_AC3);
1556 if (!ac3) return GF_OUT_OF_MEM;
1557 ac3->cfg_ac3 = (GF_AC3ConfigBox *) gf_isom_box_new_parent(&ac3->child_boxes, GF_ISOM_BOX_TYPE_DAC3);
1558 if (!ac3->cfg_ac3) return GF_OUT_OF_MEM;
1559 entry = (GF_MPEGSampleEntryBox*) ac3;
1560 gf_odf_desc_del((GF_Descriptor *) esd);
1561 } else if (esd->decoderConfig->objectTypeIndication==GF_CODECID_EAC3) {
1562 GF_MPEGAudioSampleEntryBox *eac3 = (GF_MPEGAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_EC3);
1563 if (!eac3) return GF_OUT_OF_MEM;
1564 eac3->cfg_ac3 = (GF_AC3ConfigBox *) gf_isom_box_new_parent(&eac3->child_boxes, GF_ISOM_BOX_TYPE_DEC3);
1565 if (!eac3->cfg_ac3) return GF_OUT_OF_MEM;
1566 entry = (GF_MPEGSampleEntryBox*) eac3;
1567 gf_odf_desc_del((GF_Descriptor *) esd);
1568 } else {
1569 entry_a = (GF_MPEGAudioSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4A);
1570 if (!entry_a) return GF_OUT_OF_MEM;
1571 entry_a->samplerate_hi = trak->Media->mediaHeader->timeScale;
1572 entry_a->esd = (GF_ESDBox *) gf_isom_box_new_parent(&entry_a->child_boxes, GF_ISOM_BOX_TYPE_ESDS);
1573 if (!entry_a->esd) return GF_OUT_OF_MEM;
1574 entry_a->esd->desc = esd;
1575 //type cast possible now
1576 entry = (GF_MPEGSampleEntryBox*) entry_a;
1577 }
1578 break;
1579 default:
1580 if ((esd->decoderConfig->streamType==0x03) && (esd->decoderConfig->objectTypeIndication==0x09)) {
1581 entry = (GF_MPEGSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_LSR1);
1582 if (!entry) return GF_OUT_OF_MEM;
1583 e = LSR_UpdateESD((GF_LASeRSampleEntryBox*)entry, esd);
1584 if (e) return e;
1585 } else {
1586 entry = (GF_MPEGSampleEntryBox *) gf_isom_box_new(GF_ISOM_BOX_TYPE_MP4S);
1587 entry->esd = (GF_ESDBox *) gf_isom_box_new_parent(&entry->child_boxes, GF_ISOM_BOX_TYPE_ESDS);
1588 if (!entry->esd) return GF_OUT_OF_MEM;
1589 entry->esd->desc = esd;
1590 }
1591 break;
1592 }
1593 entry->dataReferenceIndex = DataReferenceIndex;
1594
1595 if (!trak->Media->information->sampleTable->SampleDescription->child_boxes)
1596 trak->Media->information->sampleTable->SampleDescription->child_boxes = gf_list_new();
1597 gf_list_add(trak->Media->information->sampleTable->SampleDescription->child_boxes, entry);
1598
1599 e = stsd_on_child_box((GF_Box*)trak->Media->information->sampleTable->SampleDescription, (GF_Box *) entry);
1600 if (e) return e;
1601 if(outStreamIndex) *outStreamIndex = gf_list_count(trak->Media->information->sampleTable->SampleDescription->child_boxes);
1602 }
1603 return GF_OK;
1604 }
1605
1606 #endif /*GPAC_DISABLE_ISOM_WRITE*/
1607
1608 #endif /*GPAC_DISABLE_ISOM*/
1609