timelineeditor: Support importing timecode format v1 and automatic timescale generation.
[L-SMASH.git] / isom.c
blob0209c5cc335b56e5d43b2f729fdfa3132ac63436
1 /*****************************************************************************
2 * isom.c:
3 *****************************************************************************
4 * Copyright (C) 2010 L-SMASH project
6 * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
7 * Contributors: Takashi Hirata <silverfilain@gmail.com>
9 * Permission to use, copy, modify, and/or distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 *****************************************************************************/
22 /* This file is available under an ISC license. */
24 #include "internal.h" /* must be placed first */
26 #include <stdlib.h>
27 #include <string.h>
28 #include <inttypes.h>
29 #include <ctype.h> /* for chapter handling */
31 #include "box.h"
32 #include "mp4a.h"
33 #include "mp4sys.h"
34 #ifdef LSMASH_DEMUXER_ENABLED
35 #include "read.h"
36 #include "print.h"
37 #include "timeline.h"
38 #endif
41 /*---- ----*/
42 /* Return 1 if the box is fullbox, Otherwise return 0. */
43 int isom_is_fullbox( void *box )
45 uint32_t type = ((isom_box_t *)box)->type;
46 static const uint32_t fullbox_table[] = {
47 ISOM_BOX_TYPE_MVHD,
48 ISOM_BOX_TYPE_IODS,
49 ISOM_BOX_TYPE_ESDS,
50 ISOM_BOX_TYPE_TKHD,
51 QT_BOX_TYPE_CLEF,
52 QT_BOX_TYPE_PROF,
53 QT_BOX_TYPE_ENOF,
54 ISOM_BOX_TYPE_ELST,
55 ISOM_BOX_TYPE_MDHD,
56 ISOM_BOX_TYPE_HDLR,
57 ISOM_BOX_TYPE_VMHD,
58 ISOM_BOX_TYPE_SMHD,
59 ISOM_BOX_TYPE_HMHD,
60 ISOM_BOX_TYPE_NMHD,
61 QT_BOX_TYPE_GMIN,
62 ISOM_BOX_TYPE_DREF,
63 ISOM_BOX_TYPE_URL ,
64 ISOM_BOX_TYPE_STSD,
65 ISOM_BOX_TYPE_STSL,
66 QT_BOX_TYPE_CHAN,
67 ISOM_BOX_TYPE_STTS,
68 ISOM_BOX_TYPE_CTTS,
69 ISOM_BOX_TYPE_CSLG,
70 ISOM_BOX_TYPE_STSS,
71 QT_BOX_TYPE_STPS,
72 ISOM_BOX_TYPE_SDTP,
73 ISOM_BOX_TYPE_STSC,
74 ISOM_BOX_TYPE_STSZ,
75 ISOM_BOX_TYPE_STCO,
76 ISOM_BOX_TYPE_CO64,
77 ISOM_BOX_TYPE_SGPD,
78 ISOM_BOX_TYPE_SBGP,
79 ISOM_BOX_TYPE_CHPL,
80 ISOM_BOX_TYPE_META,
81 ISOM_BOX_TYPE_MEAN,
82 ISOM_BOX_TYPE_NAME,
83 ISOM_BOX_TYPE_MEHD,
84 ISOM_BOX_TYPE_TREX,
85 ISOM_BOX_TYPE_MFHD,
86 ISOM_BOX_TYPE_TFHD,
87 ISOM_BOX_TYPE_TRUN,
88 ISOM_BOX_TYPE_TFRA,
89 ISOM_BOX_TYPE_MFRO
91 for( int i = 0; i < sizeof(fullbox_table)/sizeof(uint32_t); i++ )
92 if( type == fullbox_table[i] )
93 return 1;
94 return 0;
97 /* Return 1 if the sample type is LPCM audio, Otherwise return 0. */
98 int isom_is_lpcm_audio( uint32_t type )
100 return type == QT_CODEC_TYPE_23NI_AUDIO
101 || type == QT_CODEC_TYPE_NONE_AUDIO
102 || type == QT_CODEC_TYPE_LPCM_AUDIO
103 || type == QT_CODEC_TYPE_RAW_AUDIO
104 || type == QT_CODEC_TYPE_SOWT_AUDIO
105 || type == QT_CODEC_TYPE_TWOS_AUDIO
106 || type == QT_CODEC_TYPE_FL32_AUDIO
107 || type == QT_CODEC_TYPE_FL64_AUDIO
108 || type == QT_CODEC_TYPE_IN24_AUDIO
109 || type == QT_CODEC_TYPE_IN32_AUDIO
110 || type == QT_CODEC_TYPE_NOT_SPECIFIED;
113 char *isom_4cc2str( uint32_t fourcc )
115 static char str[5];
116 str[0] = (fourcc >> 24) & 0xff;
117 str[1] = (fourcc >> 16) & 0xff;
118 str[2] = (fourcc >> 8) & 0xff;
119 str[3] = fourcc & 0xff;
120 str[4] = 0;
121 return str;
124 static inline void isom_init_basebox_common( isom_box_t *box, isom_box_t *parent, uint32_t type )
126 box->root = parent->root;
127 box->parent = parent;
128 box->size = 0;
129 box->type = type;
130 box->usertype = NULL;
133 static inline void isom_init_fullbox_common( isom_box_t *box, isom_box_t *parent, uint32_t type )
135 box->root = parent->root;
136 box->parent = parent;
137 box->size = 0;
138 box->type = type;
139 box->usertype = NULL;
140 box->version = 0;
141 box->flags = 0;
144 void isom_init_box_common( void *box, void *parent, uint32_t type )
146 assert( parent && ((isom_box_t *)parent)->root );
147 if( ((isom_box_t *)parent)->type == ISOM_BOX_TYPE_STSD )
149 isom_init_basebox_common( (isom_box_t *)box, (isom_box_t *)parent, type );
150 return;
152 if( isom_is_fullbox( box ) )
153 isom_init_fullbox_common( (isom_box_t *)box, (isom_box_t *)parent, type );
154 else
155 isom_init_basebox_common( (isom_box_t *)box, (isom_box_t *)parent, type );
158 static void isom_bs_put_basebox_common( lsmash_bs_t *bs, isom_box_t *box )
160 if( box->size > UINT32_MAX )
162 lsmash_bs_put_be32( bs, 1 );
163 lsmash_bs_put_be32( bs, box->type );
164 lsmash_bs_put_be64( bs, box->size ); /* largesize */
166 else
168 lsmash_bs_put_be32( bs, (uint32_t)box->size );
169 lsmash_bs_put_be32( bs, box->type );
171 if( box->type == ISOM_BOX_TYPE_UUID )
172 lsmash_bs_put_bytes( bs, box->usertype, 16 );
175 static void isom_bs_put_fullbox_common( lsmash_bs_t *bs, isom_box_t *box )
177 isom_bs_put_basebox_common( bs, box );
178 lsmash_bs_put_byte( bs, box->version );
179 lsmash_bs_put_be24( bs, box->flags );
182 static void isom_bs_put_box_common( lsmash_bs_t *bs, void *box )
184 if( !box )
186 bs->error = 1;
187 return;
189 isom_box_t *parent = ((isom_box_t *)box)->parent;
190 if( parent && parent->type == ISOM_BOX_TYPE_STSD )
192 isom_bs_put_basebox_common( bs, (isom_box_t *)box );
193 return;
195 if( isom_is_fullbox( box ) )
196 isom_bs_put_fullbox_common( bs, (isom_box_t *)box );
197 else
198 isom_bs_put_basebox_common( bs, (isom_box_t *)box );
201 isom_trak_entry_t *isom_get_trak( lsmash_root_t *root, uint32_t track_ID )
203 if( !track_ID || !root || !root->moov || !root->moov->trak_list )
204 return NULL;
205 for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
207 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
208 if( !trak || !trak->tkhd )
209 return NULL;
210 if( trak->tkhd->track_ID == track_ID )
211 return trak;
213 return NULL;
216 static isom_trex_entry_t *isom_get_trex( isom_mvex_t *mvex, uint32_t track_ID )
218 if( !track_ID || !mvex || !mvex->trex_list )
219 return NULL;
220 for( lsmash_entry_t *entry = mvex->trex_list->head; entry; entry = entry->next )
222 isom_trex_entry_t *trex = (isom_trex_entry_t *)entry->data;
223 if( !trex )
224 return NULL;
225 if( trex->track_ID == track_ID )
226 return trex;
228 return NULL;
231 static isom_traf_entry_t *isom_get_traf( isom_moof_entry_t *moof, uint32_t track_ID )
233 if( !track_ID || !moof || !moof->traf_list )
234 return NULL;
235 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
237 isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
238 if( !traf || !traf->tfhd )
239 return NULL;
240 if( traf->tfhd->track_ID == track_ID )
241 return traf;
243 return NULL;
246 static isom_tfra_entry_t *isom_get_tfra( isom_mfra_t *mfra, uint32_t track_ID )
248 if( !track_ID || !mfra || !mfra->tfra_list )
249 return NULL;
250 for( lsmash_entry_t *entry = mfra->tfra_list->head; entry; entry = entry->next )
252 isom_tfra_entry_t *tfra = (isom_tfra_entry_t *)entry->data;
253 if( !tfra )
254 return NULL;
255 if( tfra->track_ID == track_ID )
256 return tfra;
258 return NULL;
261 static int isom_add_elst_entry( isom_elst_t *elst, uint64_t segment_duration, int64_t media_time, int32_t media_rate )
263 isom_elst_entry_t *data = malloc( sizeof(isom_elst_entry_t) );
264 if( !data )
265 return -1;
266 data->segment_duration = segment_duration;
267 data->media_time = media_time;
268 data->media_rate = media_rate;
269 if( lsmash_add_entry( elst->list, data ) )
271 free( data );
272 return -1;
274 if( data->segment_duration > UINT32_MAX || data->media_time > INT32_MAX || data->media_time < INT32_MIN )
275 elst->version = 1;
276 return 0;
279 static isom_tref_type_t *isom_add_track_reference_type( isom_tref_t *tref, isom_track_reference_type type, uint32_t ref_count, uint32_t *track_ID )
281 if( !tref || !tref->ref_list )
282 return NULL;
283 isom_tref_type_t *ref = malloc( sizeof(isom_tref_type_t) );
284 if( !ref )
285 return NULL;
286 isom_init_box_common( ref, tref, type );
287 ref->ref_count = ref_count;
288 ref->track_ID = track_ID;
289 if( lsmash_add_entry( tref->ref_list, ref ) )
291 free( ref );
292 return NULL;
294 return ref;
297 static int isom_add_dref_entry( isom_dref_t *dref, uint32_t flags, char *name, char *location )
299 if( !dref || !dref->list )
300 return -1;
301 isom_dref_entry_t *data = malloc( sizeof(isom_dref_entry_t) );
302 if( !data )
303 return -1;
304 memset( data, 0, sizeof(isom_dref_entry_t) );
305 isom_init_box_common( data, dref, name ? ISOM_BOX_TYPE_URN : ISOM_BOX_TYPE_URL );
306 data->flags = flags;
307 if( location )
309 data->location_length = strlen( location ) + 1;
310 data->location = malloc( data->location_length );
311 if( !data->location )
313 free( data );
314 return -1;
316 memcpy( data->location, location, data->location_length );
318 if( name )
320 data->name_length = strlen( name ) + 1;
321 data->name = malloc( data->name_length );
322 if( !data->name )
324 if( data->location )
325 free( data->location );
326 free( data );
327 return -1;
329 memcpy( data->name, name, data->name_length );
331 if( lsmash_add_entry( dref->list, data ) )
333 if( data->location )
334 free( data->location );
335 if( data->name )
336 free( data->name );
337 free( data );
338 return -1;
340 return 0;
343 isom_avcC_ps_entry_t *isom_create_ps_entry( uint8_t *ps, uint32_t ps_size )
345 isom_avcC_ps_entry_t *entry = malloc( sizeof(isom_avcC_ps_entry_t) );
346 if( !entry )
347 return NULL;
348 entry->parameterSetLength = ps_size;
349 entry->parameterSetNALUnit = malloc( ps_size );
350 if( !entry->parameterSetNALUnit )
352 free( entry );
353 return NULL;
355 memcpy( entry->parameterSetNALUnit, ps, ps_size );
356 return entry;
359 void isom_remove_avcC_ps( isom_avcC_ps_entry_t *ps )
361 if( !ps )
362 return;
363 if( ps->parameterSetNALUnit )
364 free( ps->parameterSetNALUnit );
365 free( ps );
368 int lsmash_add_sps_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint8_t *sps, uint32_t sps_size )
370 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
371 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
372 return -1;
373 isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number );
374 if( !data || !data->avcC )
375 return -1;
376 isom_avcC_t *avcC = (isom_avcC_t *)data->avcC;
377 isom_avcC_ps_entry_t *ps = isom_create_ps_entry( sps, sps_size );
378 if( !ps )
379 return -1;
380 if( lsmash_add_entry( avcC->sequenceParameterSets, ps ) )
382 isom_remove_avcC_ps( ps );
383 return -1;
385 avcC->numOfSequenceParameterSets = avcC->sequenceParameterSets->entry_count;
386 return 0;
389 int lsmash_add_pps_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint8_t *pps, uint32_t pps_size )
391 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
392 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
393 return -1;
394 isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number );
395 if( !data || !data->avcC )
396 return -1;
397 isom_avcC_t *avcC = (isom_avcC_t *)data->avcC;
398 isom_avcC_ps_entry_t *ps = isom_create_ps_entry( pps, pps_size );
399 if( !ps )
400 return -1;
401 if( lsmash_add_entry( avcC->pictureParameterSets, ps ) )
403 isom_remove_avcC_ps( ps );
404 return -1;
406 avcC->numOfPictureParameterSets = avcC->pictureParameterSets->entry_count;
407 return 0;
410 int lsmash_add_spsext_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint8_t *spsext, uint32_t spsext_size )
412 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
413 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
414 return -1;
415 isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number );
416 if( !data || !data->avcC )
417 return -1;
418 isom_avcC_t *avcC = (isom_avcC_t *)data->avcC;
419 isom_avcC_ps_entry_t *ps = isom_create_ps_entry( spsext, spsext_size );
420 if( !ps )
421 return -1;
422 if( lsmash_add_entry( avcC->sequenceParameterSetExt, ps ) )
424 isom_remove_avcC_ps( ps );
425 return -1;
427 avcC->numOfSequenceParameterSetExt = avcC->sequenceParameterSetExt->entry_count;
428 return 0;
431 int isom_add_avcC( isom_visual_entry_t *visual )
433 if( !visual )
434 return -1;
435 isom_create_box( avcC, visual, ISOM_BOX_TYPE_AVCC );
436 avcC->sequenceParameterSets = lsmash_create_entry_list();
437 if( !avcC->sequenceParameterSets )
439 free( avcC );
440 return -1;
442 avcC->pictureParameterSets = lsmash_create_entry_list();
443 if( !avcC->pictureParameterSets )
445 isom_remove_avcC( avcC );
446 return -1;
448 avcC->sequenceParameterSetExt = lsmash_create_entry_list();
449 if( !avcC->sequenceParameterSetExt )
451 isom_remove_avcC( avcC );
452 return -1;
454 visual->avcC = avcC;
455 return 0;
458 int isom_add_clap( isom_visual_entry_t *visual )
460 if( !visual || visual->clap )
461 return -1;
462 isom_create_box( clap, visual, ISOM_BOX_TYPE_CLAP );
463 clap->cleanApertureWidthN = 1;
464 clap->cleanApertureWidthD = 1;
465 clap->cleanApertureHeightN = 1;
466 clap->cleanApertureHeightD = 1;
467 clap->horizOffN = 0;
468 clap->horizOffD = 1;
469 clap->vertOffN = 0;
470 clap->vertOffD = 1;
471 visual->clap = clap;
472 return 0;
475 int isom_add_pasp( isom_visual_entry_t *visual )
477 if( !visual || visual->pasp )
478 return -1;
479 isom_create_box( pasp, visual, ISOM_BOX_TYPE_PASP );
480 pasp->hSpacing = 1;
481 pasp->vSpacing = 1;
482 visual->pasp = pasp;
483 return 0;
486 int isom_add_colr( isom_visual_entry_t *visual )
488 if( !visual || visual->colr )
489 return -1;
490 isom_create_box( colr, visual, QT_BOX_TYPE_COLR );
491 isom_color_parameter_t *param = (isom_color_parameter_t *)(&isom_color_parameter_tbl[0]);
492 colr->color_parameter_type = QT_COLOR_PARAMETER_TYPE_NCLC;
493 colr->primaries_index = param->primaries;
494 colr->transfer_function_index = param->transfer;
495 colr->matrix_index = param->matrix;
496 visual->colr = colr;
497 return 0;
500 int isom_add_stsl( isom_visual_entry_t *visual )
502 if( !visual || visual->stsl )
503 return -1;
504 isom_create_box( stsl, visual, ISOM_BOX_TYPE_STSL );
505 stsl->scale_method = ISOM_SCALING_METHOD_HIDDEN;
506 visual->stsl = stsl;
507 return 0;
510 static void isom_remove_esds( isom_esds_t *esds );
511 static void isom_remove_visual_extensions( isom_visual_entry_t *visual );
513 static int isom_add_visual_extensions( isom_visual_entry_t *visual, lsmash_video_summary_t *summary )
515 /* Check if set up Track Aperture Modes. */
516 isom_trak_entry_t *trak = (isom_trak_entry_t *)visual->parent->parent->parent->parent->parent;
517 int qt_compatible = trak->root->qt_compatible;
518 isom_tapt_t *tapt = trak->tapt;
519 int set_aperture_modes = qt_compatible /* Track Aperture Modes is only available under QuickTime file format. */
520 && !summary->scaling_method /* Sample scaling method might conflict with this feature. */
521 && tapt && tapt->clef && tapt->prof && tapt->enof /* Check if required boxes exist. */
522 && !((isom_stsd_t *)visual->parent)->list->entry_count; /* Multiple sample description might conflict with this, so in that case, disable this feature.
523 * Note: this sample description isn't added yet here. */
524 if( !set_aperture_modes )
525 isom_remove_tapt( trak->tapt );
526 /* Set up Clean Aperture. */
527 if( set_aperture_modes || summary->crop_top || summary->crop_left || summary->crop_bottom || summary->crop_right )
529 if( isom_add_clap( visual ) )
531 isom_remove_visual_extensions( visual );
532 return -1;
534 isom_clap_t *clap = visual->clap;
535 clap->cleanApertureWidthN = summary->width - (summary->crop_left + summary->crop_right);
536 clap->cleanApertureHeightN = summary->height - (summary->crop_top + summary->crop_bottom);
537 clap->horizOffN = (int64_t)summary->crop_left - summary->crop_right;
538 clap->vertOffN = (int64_t)summary->crop_top - summary->crop_bottom;
539 if( !(clap->horizOffN & 0x1) )
541 clap->horizOffN /= 2;
542 clap->horizOffD = 1;
544 else
545 clap->horizOffD = 2;
546 if( !(clap->vertOffN & 0x1) )
548 clap->vertOffN /= 2;
549 clap->vertOffD = 1;
551 else
552 clap->vertOffD = 2;
554 /* Set up Pixel Aspect Ratio. */
555 if( set_aperture_modes || (summary->par_h && summary->par_v) )
557 if( isom_add_pasp( visual ) )
559 isom_remove_visual_extensions( visual );
560 return -1;
562 isom_pasp_t *pasp = visual->pasp;
563 pasp->hSpacing = summary->par_h;
564 pasp->vSpacing = summary->par_v;
566 /* Set up Color Parameter. */
567 if( qt_compatible && (summary->primaries || summary->transfer || summary->matrix) )
569 if( isom_add_colr( visual ) )
571 isom_remove_visual_extensions( visual );
572 return -1;
574 isom_colr_t *colr = visual->colr;
575 uint16_t primaries = summary->primaries;
576 uint16_t transfer = summary->transfer;
577 uint16_t matrix = summary->matrix;
578 /* Set 'nclc' to parameter type, we don't support 'prof'. */
579 colr->color_parameter_type = QT_COLOR_PARAMETER_TYPE_NCLC;
580 /* primaries */
581 if( primaries >= QT_COLOR_PARAMETER_END )
582 return -1;
583 else if( primaries > UINT16_MAX )
584 colr->primaries_index = isom_color_parameter_tbl[primaries - UINT16_MAX_PLUS_ONE].primaries;
585 else
586 colr->primaries_index = (primaries == 1 || primaries == 5 || primaries == 6) ? primaries : 2;
587 /* transfer */
588 if( transfer >= QT_COLOR_PARAMETER_END )
589 return -1;
590 else if( transfer > UINT16_MAX )
591 colr->transfer_function_index = isom_color_parameter_tbl[transfer - UINT16_MAX_PLUS_ONE].transfer;
592 else
593 colr->transfer_function_index = (transfer == 1 || transfer == 7) ? transfer : 2;
594 /* matrix */
595 if( matrix >= QT_COLOR_PARAMETER_END )
596 return -1;
597 else if( matrix > UINT16_MAX )
598 colr->matrix_index = isom_color_parameter_tbl[matrix - UINT16_MAX_PLUS_ONE].matrix;
599 else
600 colr->matrix_index = (matrix == 1 || matrix == 6 || matrix == 7 ) ? matrix : 2;
602 /* Set up Sample Scaling. */
603 if( !qt_compatible && summary->scaling_method )
605 if( isom_add_stsl( visual ) )
607 isom_remove_visual_extensions( visual );
608 return -1;
610 isom_stsl_t *stsl = visual->stsl;
611 stsl->constraint_flag = 1;
612 stsl->scale_method = summary->scaling_method;
614 /* Set up AVC Decoder Configuration. */
615 static const uint32_t avc_type[] =
617 ISOM_CODEC_TYPE_AVC1_VIDEO,
618 ISOM_CODEC_TYPE_AVC2_VIDEO,
619 ISOM_CODEC_TYPE_AVCP_VIDEO
621 for( int i = 0; i < sizeof(avc_type)/sizeof(avc_type[0]); i++ )
622 if( visual->type == avc_type[i] )
624 if( isom_add_avcC( visual ) )
625 return -1;
626 break;
628 /* Set up Track Apeture Modes. */
629 if( set_aperture_modes )
631 uint32_t width = visual->width << 16;
632 uint32_t height = visual->height << 16;
633 double clap_width = ((double)visual->clap->cleanApertureWidthN / visual->clap->cleanApertureWidthD) * (1<<16);
634 double clap_height = ((double)visual->clap->cleanApertureHeightN / visual->clap->cleanApertureHeightD) * (1<<16);
635 double par = (double)visual->pasp->hSpacing / visual->pasp->vSpacing;
636 if( par >= 1.0 )
638 tapt->clef->width = clap_width * par;
639 tapt->clef->height = clap_height;
640 tapt->prof->width = width * par;
641 tapt->prof->height = height;
643 else
645 tapt->clef->width = clap_width;
646 tapt->clef->height = clap_height / par;
647 tapt->prof->width = width;
648 tapt->prof->height = height / par;
650 tapt->enof->width = width;
651 tapt->enof->height = height;
653 return 0;
656 static int isom_add_visual_entry( isom_stsd_t *stsd, uint32_t sample_type, lsmash_video_summary_t *summary )
658 if( !stsd || !stsd->list || !summary )
659 return -1;
660 lsmash_entry_list_t *list = stsd->list;
661 isom_visual_entry_t *visual = malloc( sizeof(isom_visual_entry_t) );
662 if( !visual )
663 return -1;
664 memset( visual, 0, sizeof(isom_visual_entry_t) );
665 isom_init_box_common( visual, stsd, sample_type );
666 visual->data_reference_index = 1;
667 visual->width = (uint16_t)summary->width;
668 visual->height = (uint16_t)summary->height;
669 visual->horizresolution = visual->vertresolution = 0x00480000;
670 visual->frame_count = 1;
671 switch( sample_type )
673 case ISOM_CODEC_TYPE_AVC1_VIDEO :
674 case ISOM_CODEC_TYPE_AVC2_VIDEO :
675 strcpy( visual->compressorname, "\012AVC Coding" );
676 break;
677 case ISOM_CODEC_TYPE_AVCP_VIDEO :
678 strcpy( visual->compressorname, "\016AVC Parameters" );
679 break;
680 default :
681 break;
683 visual->depth = 0x0018;
684 visual->color_table_ID = -1;
685 if( isom_add_visual_extensions( visual, summary )
686 || lsmash_add_entry( list, visual ) )
688 isom_remove_visual_extensions( visual );
689 free( visual );
690 return -1;
692 return 0;
695 #if 0
696 static int isom_add_mp4s_entry( isom_stsd_t *stsd )
698 if( !stsd || !stsd->list )
699 return -1;
700 isom_mp4s_entry_t *mp4s = malloc( sizeof(isom_mp4s_entry_t) );
701 if( !mp4s )
702 return -1;
703 memset( mp4s, 0, sizeof(isom_mp4s_entry_t) );
704 isom_init_box_common( mp4s, stsd, ISOM_CODEC_TYPE_MP4S_SYSTEM );
705 mp4s->data_reference_index = 1;
706 if( lsmash_add_entry( stsd->list, mp4s ) )
708 free( mp4s );
709 return -1;
711 return 0;
713 #endif
715 int isom_add_wave( isom_audio_entry_t *audio )
717 if( !audio || audio->wave )
718 return -1;
719 isom_create_box( wave, audio, QT_BOX_TYPE_WAVE );
720 audio->wave = wave;
721 return 0;
724 int isom_add_frma( isom_wave_t *wave )
726 if( !wave || wave->frma )
727 return -1;
728 isom_create_box( frma, wave, QT_BOX_TYPE_FRMA );
729 wave->frma = frma;
730 return 0;
733 int isom_add_enda( isom_wave_t *wave )
735 if( !wave || wave->enda )
736 return -1;
737 isom_create_box( enda, wave, QT_BOX_TYPE_ENDA );
738 wave->enda = enda;
739 return 0;
742 int isom_add_mp4a( isom_wave_t *wave )
744 if( !wave || wave->mp4a )
745 return -1;
746 isom_create_box( mp4a, wave, QT_BOX_TYPE_MP4A );
747 wave->mp4a = mp4a;
748 return 0;
751 int isom_add_terminator( isom_wave_t *wave )
753 if( !wave || wave->terminator )
754 return -1;
755 isom_create_box( terminator, wave, QT_BOX_TYPE_TERMINATOR );
756 wave->terminator = terminator;
757 return 0;
760 int isom_add_chan( isom_audio_entry_t *audio )
762 if( !audio || audio->chan )
763 return -1;
764 isom_create_box( chan, audio, QT_BOX_TYPE_CHAN );
765 chan->channelLayoutTag = QT_CHANNEL_LAYOUT_UNKNOWN;
766 audio->chan = chan;
767 return 0;
770 static int isom_set_qtff_mp4a_description( isom_audio_entry_t *audio )
772 lsmash_audio_summary_t *summary = &audio->summary;
773 if( isom_add_wave( audio )
774 || isom_add_frma( audio->wave )
775 || isom_add_mp4a( audio->wave )
776 || isom_add_terminator( audio->wave ) )
777 return -1;
778 audio->data_reference_index = 1;
779 audio->version = (summary->channels > 2 || summary->frequency > UINT16_MAX) ? 2 : 1;
780 audio->channelcount = audio->version == 2 ? 3 : LSMASH_MIN( summary->channels, 2 );
781 audio->samplesize = 16;
782 audio->compression_ID = QT_COMPRESSION_ID_VARIABLE_COMPRESSION;
783 audio->packet_size = 0;
784 if( audio->version == 1 )
786 audio->samplerate = summary->frequency << 16;
787 audio->samplesPerPacket = summary->samples_in_frame;
788 audio->bytesPerPacket = 1; /* Apparently, this field is set to 1. */
789 audio->bytesPerFrame = audio->bytesPerPacket * summary->channels;
790 audio->bytesPerSample = 1 + (summary->bit_depth != 8);
792 else /* audio->version == 2 */
794 audio->samplerate = 0x00010000;
795 audio->sizeOfStructOnly = 72;
796 audio->audioSampleRate = (union {double d; uint64_t i;}){summary->frequency}.i;
797 audio->numAudioChannels = summary->channels;
798 audio->always7F000000 = 0x7F000000;
799 audio->constBitsPerChannel = 0; /* compressed audio */
800 audio->formatSpecificFlags = 0;
801 audio->constBytesPerAudioPacket = 0; /* variable */
802 audio->constLPCMFramesPerAudioPacket = summary->samples_in_frame;
804 audio->wave->frma->data_format = audio->type;
805 /* create ES Descriptor */
806 isom_esds_t *esds = malloc( sizeof(isom_esds_t) );
807 if( !esds )
808 return -1;
809 memset( esds, 0, sizeof(isom_esds_t) );
810 isom_init_box_common( esds, audio->wave, ISOM_BOX_TYPE_ESDS );
811 mp4sys_ES_Descriptor_params_t esd_param;
812 memset( &esd_param, 0, sizeof(mp4sys_ES_Descriptor_params_t) );
813 esd_param.objectTypeIndication = summary->object_type_indication;
814 esd_param.streamType = summary->stream_type;
815 esd_param.dsi_payload = summary->exdata;
816 esd_param.dsi_payload_length = summary->exdata_length;
817 esds->ES = mp4sys_setup_ES_Descriptor( &esd_param );
818 if( !esds->ES )
819 return -1;
820 audio->wave->esds = esds;
821 return 0;
824 static int isom_set_isom_mp4a_description( isom_audio_entry_t *audio )
826 lsmash_audio_summary_t *summary = &audio->summary;
827 if( summary->stream_type != MP4SYS_STREAM_TYPE_AudioStream )
828 return -1;
829 switch( summary->object_type_indication )
831 case MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3:
832 case MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_Main_Profile:
833 case MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_LC_Profile:
834 case MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_SSR_Profile:
835 case MP4SYS_OBJECT_TYPE_Audio_ISO_13818_3: /* Legacy Interface */
836 case MP4SYS_OBJECT_TYPE_Audio_ISO_11172_3: /* Legacy Interface */
837 break;
838 default:
839 return -1;
841 isom_create_box( esds, audio, ISOM_BOX_TYPE_ESDS );
842 mp4sys_ES_Descriptor_params_t esd_param;
843 esd_param.ES_ID = 0; /* This is esds internal, so 0 is allowed. */
844 esd_param.objectTypeIndication = summary->object_type_indication;
845 esd_param.streamType = summary->stream_type;
846 esd_param.bufferSizeDB = 0; /* NOTE: ISO/IEC 14496-3 does not mention this, so we use 0. */
847 esd_param.maxBitrate = 0; /* This will be updated later if needed. or... I think this can be arbitrary value. */
848 esd_param.avgBitrate = 0; /* FIXME: 0 if VBR. */
849 esd_param.dsi_payload = summary->exdata;
850 esd_param.dsi_payload_length = summary->exdata_length;
851 esds->ES = mp4sys_setup_ES_Descriptor( &esd_param );
852 if( !esds->ES )
853 return -1;
854 audio->data_reference_index = 1;
855 /* WARNING: This field cannot retain frequency above 65535Hz.
856 This is not "FIXME", I just honestly implemented what the spec says.
857 BTW, who ever expects sampling frequency takes fixed-point decimal??? */
858 audio->samplerate = summary->frequency <= UINT16_MAX ? summary->frequency << 16 : 0;
859 /* In pure mp4 file, these "template" fields shall be default values according to the spec.
860 But not pure - hybrid with other spec - mp4 file can take other values.
861 Which is to say, these template values shall be ignored in terms of mp4, except some object_type_indications.
862 see 14496-14, "Template fields used". */
863 audio->channelcount = 2;
864 audio->samplesize = 16;
865 audio->esds = esds;
866 return 0;
869 static int isom_set_qtff_lpcm_description( isom_audio_entry_t *audio )
871 uint32_t sample_type = audio->type;
872 lsmash_audio_summary_t *summary = &audio->summary;
873 /* Convert the sample type into 'lpcm' if the description doesn't match the format or version = 2 fields are needed. */
874 if( (sample_type == QT_CODEC_TYPE_RAW_AUDIO && (summary->bit_depth != 8 || summary->sample_format))
875 || (sample_type == QT_CODEC_TYPE_FL32_AUDIO && (summary->bit_depth != 32 || !summary->sample_format))
876 || (sample_type == QT_CODEC_TYPE_FL64_AUDIO && (summary->bit_depth != 64 || !summary->sample_format))
877 || (sample_type == QT_CODEC_TYPE_IN24_AUDIO && (summary->bit_depth != 24 || summary->sample_format))
878 || (sample_type == QT_CODEC_TYPE_IN32_AUDIO && (summary->bit_depth != 32 || summary->sample_format))
879 || (sample_type == QT_CODEC_TYPE_23NI_AUDIO && (summary->bit_depth != 32 || summary->sample_format || !summary->endianness))
880 || (sample_type == QT_CODEC_TYPE_SOWT_AUDIO && (summary->bit_depth != 16 || summary->sample_format || !summary->endianness))
881 || (sample_type == QT_CODEC_TYPE_TWOS_AUDIO && ((summary->bit_depth != 16 && summary->bit_depth != 8) || summary->sample_format || summary->endianness))
882 || (sample_type == QT_CODEC_TYPE_NONE_AUDIO && ((summary->bit_depth != 16 && summary->bit_depth != 8) || summary->sample_format || summary->endianness))
883 || (sample_type == QT_CODEC_TYPE_NOT_SPECIFIED && ((summary->bit_depth != 16 && summary->bit_depth != 8) || summary->sample_format || summary->endianness))
884 || (summary->channels > 2 || summary->frequency > UINT16_MAX || summary->bit_depth % 8) )
886 audio->type = QT_CODEC_TYPE_LPCM_AUDIO;
887 audio->version = 2;
889 else if( sample_type == QT_CODEC_TYPE_LPCM_AUDIO )
890 audio->version = 2;
891 else if( summary->bit_depth > 16
892 || (sample_type != QT_CODEC_TYPE_RAW_AUDIO && sample_type != QT_CODEC_TYPE_TWOS_AUDIO
893 && sample_type != QT_CODEC_TYPE_NONE_AUDIO && sample_type != QT_CODEC_TYPE_NOT_SPECIFIED) )
894 audio->version = 1;
895 audio->data_reference_index = 1;
896 /* Set up constBytesPerAudioPacket field.
897 * We use constBytesPerAudioPacket as the actual size of audio frame even when version is not 2. */
898 audio->constBytesPerAudioPacket = (summary->bit_depth * summary->channels) / 8;
899 /* Set up other fields in this description by its version. */
900 if( audio->version == 2 )
902 audio->channelcount = 3;
903 audio->samplesize = 16;
904 audio->compression_ID = -2;
905 audio->samplerate = 0x00010000;
906 audio->sizeOfStructOnly = 72;
907 audio->audioSampleRate = (union {double d; uint64_t i;}){summary->frequency}.i;
908 audio->numAudioChannels = summary->channels;
909 audio->always7F000000 = 0x7F000000;
910 audio->constBitsPerChannel = summary->bit_depth;
911 audio->constLPCMFramesPerAudioPacket = 1;
912 if( summary->sample_format )
913 audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_FLOAT;
914 if( sample_type == QT_CODEC_TYPE_TWOS_AUDIO || !summary->endianness )
915 audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_BIG_ENDIAN;
916 if( !summary->sample_format && summary->signedness )
917 audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_SIGNED_INTEGER;
918 if( summary->packed )
919 audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_PACKED;
920 if( !summary->packed && summary->alignment )
921 audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_ALIGNED_HIGH;
922 if( !summary->interleaved )
923 audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_NON_INTERLEAVED;
925 else if( audio->version == 1 )
927 audio->channelcount = summary->channels;
928 audio->samplesize = 16;
929 /* Audio formats other than 'raw ' and 'twos' are treated as compressed audio. */
930 if( sample_type == QT_CODEC_TYPE_RAW_AUDIO || sample_type == QT_CODEC_TYPE_TWOS_AUDIO )
931 audio->compression_ID = QT_COMPRESSION_ID_NOT_COMPRESSED;
932 else
933 audio->compression_ID = QT_COMPRESSION_ID_FIXED_COMPRESSION;
934 audio->samplerate = summary->frequency << 16;
935 audio->samplesPerPacket = 1;
936 audio->bytesPerPacket = summary->bit_depth / 8;
937 audio->bytesPerFrame = audio->bytesPerPacket * summary->channels; /* sample_size field in stsz box is NOT used. */
938 audio->bytesPerSample = 1 + (summary->bit_depth != 8);
939 if( sample_type == QT_CODEC_TYPE_FL32_AUDIO || sample_type == QT_CODEC_TYPE_FL64_AUDIO
940 || sample_type == QT_CODEC_TYPE_IN24_AUDIO || sample_type == QT_CODEC_TYPE_IN32_AUDIO )
942 if( isom_add_wave( audio )
943 || isom_add_frma( audio->wave )
944 || isom_add_enda( audio->wave )
945 || isom_add_terminator( audio->wave ) )
946 return -1;
947 audio->wave->frma->data_format = sample_type;
948 audio->wave->enda->littleEndian = summary->endianness;
951 else /* audio->version == 0 */
953 audio->channelcount = summary->channels;
954 audio->samplesize = summary->bit_depth;
955 audio->compression_ID = QT_COMPRESSION_ID_NOT_COMPRESSED;
956 audio->samplerate = summary->frequency << 16;
958 return 0;
961 static int isom_set_extra_description( isom_audio_entry_t *audio )
963 lsmash_audio_summary_t *summary = &audio->summary;
964 audio->data_reference_index = 1;
965 audio->samplerate = summary->frequency <= UINT16_MAX ? summary->frequency << 16 : 0;
966 audio->channelcount = 2;
967 audio->samplesize = 16;
968 if( summary->exdata )
970 audio->exdata_length = summary->exdata_length;
971 audio->exdata = malloc( audio->exdata_length );
972 if( !audio->exdata )
973 return -1;
974 memcpy( audio->exdata, summary->exdata, audio->exdata_length );
976 else
977 audio->exdata = NULL;
978 return 0;
981 static int isom_add_audio_entry( isom_stsd_t *stsd, uint32_t sample_type, lsmash_audio_summary_t *summary )
983 if( !stsd || !stsd->list || !summary )
984 return -1;
985 isom_audio_entry_t *audio = malloc( sizeof(isom_audio_entry_t) );
986 if( !audio )
987 return -1;
988 memset( audio, 0, sizeof(isom_audio_entry_t) );
989 isom_init_box_common( audio, stsd, sample_type );
990 memcpy( &audio->summary, summary, sizeof(lsmash_audio_summary_t) );
991 int ret = 0;
992 lsmash_root_t *root = stsd->root;
993 if( sample_type == ISOM_CODEC_TYPE_MP4A_AUDIO )
995 if( root->ftyp && root->ftyp->major_brand == ISOM_BRAND_TYPE_QT )
996 ret = isom_set_qtff_mp4a_description( audio );
997 else
998 ret = isom_set_isom_mp4a_description( audio );
1000 else if( isom_is_lpcm_audio( sample_type ) )
1001 ret = isom_set_qtff_lpcm_description( audio );
1002 else
1003 ret = isom_set_extra_description( audio );
1004 if( ret )
1005 goto fail;
1006 if( root->qt_compatible )
1008 lsmash_channel_layout_tag layout_tag = summary->layout_tag;
1009 lsmash_channel_bitmap bitmap = summary->bitmap;
1010 if( layout_tag == QT_CHANNEL_LAYOUT_USE_CHANNEL_DESCRIPTIONS /* We don't support the feature of Channel Descriptions. */
1011 || (layout_tag == QT_CHANNEL_LAYOUT_USE_CHANNEL_BITMAP && (!bitmap || bitmap > QT_CHANNEL_BIT_FULL)) )
1013 layout_tag = summary->layout_tag = QT_CHANNEL_LAYOUT_UNKNOWN | summary->channels;
1014 bitmap = summary->bitmap = 0;
1016 /* Don't create Channel Compositor Box if the channel layout is unknown. */
1017 if( (layout_tag ^ QT_CHANNEL_LAYOUT_UNKNOWN) >> 16 )
1019 if( isom_add_chan( audio ) )
1020 goto fail;
1021 audio->chan->channelLayoutTag = layout_tag;
1022 audio->chan->channelBitmap = bitmap;
1025 if( lsmash_add_entry( stsd->list, audio ) )
1026 goto fail;
1027 return 0;
1028 fail:
1029 isom_remove_esds( audio->esds );
1030 isom_remove_wave( audio->wave );
1031 isom_remove_chan( audio->chan );
1032 if( audio->exdata )
1033 free( audio->exdata );
1034 free( audio );
1035 return -1;
1038 static int isom_add_text_entry( isom_stsd_t *stsd )
1040 if( !stsd || !stsd->list )
1041 return -1;
1042 isom_text_entry_t *text = malloc( sizeof(isom_text_entry_t) );
1043 if( !text )
1044 return -1;
1045 memset( text, 0, sizeof(isom_text_entry_t) );
1046 isom_init_box_common( text, stsd, QT_CODEC_TYPE_TEXT_TEXT );
1047 text->data_reference_index = 1;
1048 if( lsmash_add_entry( stsd->list, text ) )
1050 free( text );
1051 return -1;
1053 return 0;
1056 int isom_add_ftab( isom_tx3g_entry_t *tx3g )
1058 if( !tx3g )
1059 return -1;
1060 isom_ftab_t *ftab = malloc( sizeof(isom_ftab_t) );
1061 if( !ftab )
1062 return -1;
1063 memset( ftab, 0, sizeof(isom_ftab_t) );
1064 isom_init_box_common( ftab, tx3g, ISOM_BOX_TYPE_FTAB );
1065 ftab->list = lsmash_create_entry_list();
1066 if( !ftab->list )
1068 free( ftab );
1069 return -1;
1071 tx3g->ftab = ftab;
1072 return 0;
1075 static int isom_add_tx3g_entry( isom_stsd_t *stsd )
1077 if( !stsd || !stsd->list )
1078 return -1;
1079 isom_tx3g_entry_t *tx3g = malloc( sizeof(isom_tx3g_entry_t) );
1080 if( !tx3g )
1081 return -1;
1082 memset( tx3g, 0, sizeof(isom_tx3g_entry_t) );
1083 isom_init_box_common( tx3g, stsd, ISOM_CODEC_TYPE_TX3G_TEXT );
1084 tx3g->data_reference_index = 1;
1085 if( isom_add_ftab( tx3g ) ||
1086 lsmash_add_entry( stsd->list, tx3g ) )
1088 free( tx3g );
1089 return -1;
1091 return 0;
1094 /* This function returns 0 if failed, sample_entry_number if succeeded. */
1095 int lsmash_add_sample_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_type, void *summary )
1097 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
1098 if( !trak || !trak->root || !trak->root->ftyp || !trak->mdia || !trak->mdia->minf
1099 || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
1100 return 0;
1101 isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd;
1102 lsmash_entry_list_t *list = stsd->list;
1103 int ret = -1;
1104 switch( sample_type )
1106 case ISOM_CODEC_TYPE_AVC1_VIDEO :
1107 #if 0
1108 case ISOM_CODEC_TYPE_AVC2_VIDEO :
1109 case ISOM_CODEC_TYPE_AVCP_VIDEO :
1110 case ISOM_CODEC_TYPE_SVC1_VIDEO :
1111 case ISOM_CODEC_TYPE_MVC1_VIDEO :
1112 case ISOM_CODEC_TYPE_MVC2_VIDEO :
1113 case ISOM_CODEC_TYPE_MP4V_VIDEO :
1114 case ISOM_CODEC_TYPE_DRAC_VIDEO :
1115 case ISOM_CODEC_TYPE_ENCV_VIDEO :
1116 case ISOM_CODEC_TYPE_MJP2_VIDEO :
1117 case ISOM_CODEC_TYPE_S263_VIDEO :
1118 case ISOM_CODEC_TYPE_VC_1_VIDEO :
1119 #endif
1120 ret = isom_add_visual_entry( stsd, sample_type, (lsmash_video_summary_t *)summary );
1121 break;
1122 #if 0
1123 case ISOM_CODEC_TYPE_MP4S_SYSTEM :
1124 ret = isom_add_mp4s_entry( stsd );
1125 break;
1126 #endif
1127 case ISOM_CODEC_TYPE_MP4A_AUDIO :
1128 case ISOM_CODEC_TYPE_AC_3_AUDIO :
1129 case ISOM_CODEC_TYPE_ALAC_AUDIO :
1130 case ISOM_CODEC_TYPE_SAMR_AUDIO :
1131 case ISOM_CODEC_TYPE_SAWB_AUDIO :
1132 case QT_CODEC_TYPE_23NI_AUDIO :
1133 case QT_CODEC_TYPE_NONE_AUDIO :
1134 case QT_CODEC_TYPE_LPCM_AUDIO :
1135 case QT_CODEC_TYPE_RAW_AUDIO :
1136 case QT_CODEC_TYPE_SOWT_AUDIO :
1137 case QT_CODEC_TYPE_TWOS_AUDIO :
1138 case QT_CODEC_TYPE_FL32_AUDIO :
1139 case QT_CODEC_TYPE_FL64_AUDIO :
1140 case QT_CODEC_TYPE_IN24_AUDIO :
1141 case QT_CODEC_TYPE_IN32_AUDIO :
1142 case QT_CODEC_TYPE_NOT_SPECIFIED :
1143 #if 0
1144 case ISOM_CODEC_TYPE_DRA1_AUDIO :
1145 case ISOM_CODEC_TYPE_DTSC_AUDIO :
1146 case ISOM_CODEC_TYPE_DTSH_AUDIO :
1147 case ISOM_CODEC_TYPE_DTSL_AUDIO :
1148 case ISOM_CODEC_TYPE_EC_3_AUDIO :
1149 case ISOM_CODEC_TYPE_ENCA_AUDIO :
1150 case ISOM_CODEC_TYPE_G719_AUDIO :
1151 case ISOM_CODEC_TYPE_G726_AUDIO :
1152 case ISOM_CODEC_TYPE_M4AE_AUDIO :
1153 case ISOM_CODEC_TYPE_MLPA_AUDIO :
1154 case ISOM_CODEC_TYPE_RAW_AUDIO :
1155 case ISOM_CODEC_TYPE_SAWP_AUDIO :
1156 case ISOM_CODEC_TYPE_SEVC_AUDIO :
1157 case ISOM_CODEC_TYPE_SQCP_AUDIO :
1158 case ISOM_CODEC_TYPE_SSMV_AUDIO :
1159 case ISOM_CODEC_TYPE_TWOS_AUDIO :
1160 #endif
1161 ret = isom_add_audio_entry( stsd, sample_type, (lsmash_audio_summary_t *)summary );
1162 break;
1163 case ISOM_CODEC_TYPE_TX3G_TEXT :
1164 ret = isom_add_tx3g_entry( stsd );
1165 break;
1166 case QT_CODEC_TYPE_TEXT_TEXT :
1167 ret = isom_add_text_entry( stsd );
1168 break;
1169 default :
1170 return 0;
1172 return ret ? 0 : list->entry_count;
1175 static int isom_add_stts_entry( isom_stbl_t *stbl, uint32_t sample_delta )
1177 if( !stbl || !stbl->stts || !stbl->stts->list )
1178 return -1;
1179 isom_stts_entry_t *data = malloc( sizeof(isom_stts_entry_t) );
1180 if( !data )
1181 return -1;
1182 data->sample_count = 1;
1183 data->sample_delta = sample_delta;
1184 if( lsmash_add_entry( stbl->stts->list, data ) )
1186 free( data );
1187 return -1;
1189 return 0;
1192 static int isom_add_ctts_entry( isom_stbl_t *stbl, uint32_t sample_offset )
1194 if( !stbl || !stbl->ctts || !stbl->ctts->list )
1195 return -1;
1196 isom_ctts_entry_t *data = malloc( sizeof(isom_ctts_entry_t) );
1197 if( !data )
1198 return -1;
1199 data->sample_count = 1;
1200 data->sample_offset = sample_offset;
1201 if( lsmash_add_entry( stbl->ctts->list, data ) )
1203 free( data );
1204 return -1;
1206 return 0;
1209 static int isom_add_stsc_entry( isom_stbl_t *stbl, uint32_t first_chunk, uint32_t samples_per_chunk, uint32_t sample_description_index )
1211 if( !stbl || !stbl->stsc || !stbl->stsc->list )
1212 return -1;
1213 isom_stsc_entry_t *data = malloc( sizeof(isom_stsc_entry_t) );
1214 if( !data )
1215 return -1;
1216 data->first_chunk = first_chunk;
1217 data->samples_per_chunk = samples_per_chunk;
1218 data->sample_description_index = sample_description_index;
1219 if( lsmash_add_entry( stbl->stsc->list, data ) )
1221 free( data );
1222 return -1;
1224 return 0;
1227 static int isom_add_stsz_entry( isom_stbl_t *stbl, uint32_t entry_size )
1229 if( !stbl || !stbl->stsz )
1230 return -1;
1231 isom_stsz_t *stsz = stbl->stsz;
1232 /* retrieve initial sample_size */
1233 if( !stsz->sample_count )
1234 stsz->sample_size = entry_size;
1235 /* if it seems constant access_unit size at present, update sample_count only */
1236 if( !stsz->list && stsz->sample_size == entry_size )
1238 ++ stsz->sample_count;
1239 return 0;
1241 /* found sample_size varies, create sample_size list */
1242 if( !stsz->list )
1244 stsz->list = lsmash_create_entry_list();
1245 if( !stsz->list )
1246 return -1;
1247 for( uint32_t i = 0; i < stsz->sample_count; i++ )
1249 isom_stsz_entry_t *data = malloc( sizeof(isom_stsz_entry_t) );
1250 if( !data )
1251 return -1;
1252 data->entry_size = stsz->sample_size;
1253 if( lsmash_add_entry( stsz->list, data ) )
1255 free( data );
1256 return -1;
1259 stsz->sample_size = 0;
1261 isom_stsz_entry_t *data = malloc( sizeof(isom_stsz_entry_t) );
1262 if( !data )
1263 return -1;
1264 data->entry_size = entry_size;
1265 if( lsmash_add_entry( stsz->list, data ) )
1267 free( data );
1268 return -1;
1270 ++ stsz->sample_count;
1271 return 0;
1274 static int isom_add_stss_entry( isom_stbl_t *stbl, uint32_t sample_number )
1276 if( !stbl || !stbl->stss || !stbl->stss->list )
1277 return -1;
1278 isom_stss_entry_t *data = malloc( sizeof(isom_stss_entry_t) );
1279 if( !data )
1280 return -1;
1281 data->sample_number = sample_number;
1282 if( lsmash_add_entry( stbl->stss->list, data ) )
1284 free( data );
1285 return -1;
1287 return 0;
1290 static int isom_add_stps_entry( isom_stbl_t *stbl, uint32_t sample_number )
1292 if( !stbl || !stbl->stps || !stbl->stps->list )
1293 return -1;
1294 isom_stps_entry_t *data = malloc( sizeof(isom_stps_entry_t) );
1295 if( !data )
1296 return -1;
1297 data->sample_number = sample_number;
1298 if( lsmash_add_entry( stbl->stps->list, data ) )
1300 free( data );
1301 return -1;
1303 return 0;
1306 static int isom_add_sdtp_entry( isom_stbl_t *stbl, lsmash_sample_property_t *prop, uint8_t avc_extensions )
1308 if( !prop )
1309 return -1;
1310 if( !stbl || !stbl->sdtp || !stbl->sdtp->list )
1311 return -1;
1312 isom_sdtp_entry_t *data = malloc( sizeof(isom_sdtp_entry_t) );
1313 if( !data )
1314 return -1;
1315 /* isom_sdtp_entry_t is smaller than lsmash_sample_property_t. */
1316 data->is_leading = (avc_extensions ? prop->leading : prop->allow_earlier) & 0x03;
1317 data->sample_depends_on = prop->independent & 0x03;
1318 data->sample_is_depended_on = prop->disposable & 0x03;
1319 data->sample_has_redundancy = prop->redundant & 0x03;
1320 if( lsmash_add_entry( stbl->sdtp->list, data ) )
1322 free( data );
1323 return -1;
1325 return 0;
1328 static int isom_add_co64( isom_stbl_t *stbl )
1330 if( !stbl || stbl->stco )
1331 return -1;
1332 isom_create_list_box( stco, stbl, ISOM_BOX_TYPE_CO64 );
1333 stco->large_presentation = 1;
1334 stbl->stco = stco;
1335 return 0;
1338 static int isom_add_stco( isom_stbl_t *stbl )
1340 if( !stbl || stbl->stco )
1341 return -1;
1342 isom_create_list_box( stco, stbl, ISOM_BOX_TYPE_STCO );
1343 stco->large_presentation = 0;
1344 stbl->stco = stco;
1345 return 0;
1348 static int isom_add_co64_entry( isom_stbl_t *stbl, uint64_t chunk_offset )
1350 if( !stbl || !stbl->stco || !stbl->stco->list )
1351 return -1;
1352 isom_co64_entry_t *data = malloc( sizeof(isom_co64_entry_t) );
1353 if( !data )
1354 return -1;
1355 data->chunk_offset = chunk_offset;
1356 if( lsmash_add_entry( stbl->stco->list, data ) )
1358 free( data );
1359 return -1;
1361 return 0;
1364 static int isom_convert_stco_to_co64( isom_stbl_t* stbl )
1366 /* backup stco */
1367 isom_stco_t *stco = stbl->stco;
1368 stbl->stco = NULL;
1369 if( isom_add_co64( stbl ) )
1370 return -1;
1371 /* move chunk_offset to co64 from stco */
1372 for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next )
1374 isom_stco_entry_t *data = (isom_stco_entry_t*)entry->data;
1375 if( isom_add_co64_entry( stbl, data->chunk_offset ) )
1376 return -1;
1378 lsmash_remove_list( stco->list, NULL );
1379 free( stco );
1380 return 0;
1383 static int isom_add_stco_entry( isom_stbl_t *stbl, uint64_t chunk_offset )
1385 if( !stbl || !stbl->stco || !stbl->stco->list )
1386 return -1;
1387 if( stbl->stco->large_presentation )
1388 return isom_add_co64_entry( stbl, chunk_offset );
1389 if( chunk_offset > UINT32_MAX )
1391 if( isom_convert_stco_to_co64( stbl ) )
1392 return -1;
1393 return isom_add_co64_entry( stbl, chunk_offset );
1395 isom_stco_entry_t *data = malloc( sizeof(isom_stco_entry_t) );
1396 if( !data )
1397 return -1;
1398 data->chunk_offset = (uint32_t)chunk_offset;
1399 if( lsmash_add_entry( stbl->stco->list, data ) )
1401 free( data );
1402 return -1;
1404 return 0;
1407 isom_sgpd_entry_t *isom_get_sample_group_description( isom_stbl_t *stbl, uint32_t grouping_type )
1409 if( !stbl->sgpd_list )
1410 return NULL;
1411 for( lsmash_entry_t *entry = stbl->sgpd_list->head; entry; entry = entry->next )
1413 isom_sgpd_entry_t *sgpd = (isom_sgpd_entry_t *)entry->data;
1414 if( !sgpd || !sgpd->list )
1415 return NULL;
1416 if( sgpd->grouping_type == grouping_type )
1417 return sgpd;
1419 return NULL;
1422 isom_sbgp_entry_t *isom_get_sample_to_group( isom_stbl_t *stbl, uint32_t grouping_type )
1424 if( !stbl->sbgp_list )
1425 return NULL;
1426 for( lsmash_entry_t *entry = stbl->sbgp_list->head; entry; entry = entry->next )
1428 isom_sbgp_entry_t *sbgp = (isom_sbgp_entry_t *)entry->data;
1429 if( !sbgp || !sbgp->list )
1430 return NULL;
1431 if( sbgp->grouping_type == grouping_type )
1432 return sbgp;
1434 return NULL;
1437 static isom_rap_entry_t *isom_add_rap_group_entry( isom_sgpd_entry_t *sgpd )
1439 if( !sgpd )
1440 return NULL;
1441 isom_rap_entry_t *data = malloc( sizeof(isom_rap_entry_t) );
1442 if( !data )
1443 return NULL;
1444 memset( data, 0, sizeof(isom_rap_entry_t) );
1445 if( lsmash_add_entry( sgpd->list, data ) )
1447 free( data );
1448 return NULL;
1450 return data;
1453 static isom_roll_entry_t *isom_add_roll_group_entry( isom_sgpd_entry_t *sgpd, int16_t roll_distance )
1455 if( !sgpd )
1456 return NULL;
1457 isom_roll_entry_t *data = malloc( sizeof(isom_roll_entry_t) );
1458 if( !data )
1459 return NULL;
1460 data->description_length = 0;
1461 data->roll_distance = roll_distance;
1462 if( lsmash_add_entry( sgpd->list, data ) )
1464 free( data );
1465 return NULL;
1467 return data;
1470 static isom_group_assignment_entry_t *isom_add_group_assignment_entry( isom_sbgp_entry_t *sbgp, uint32_t sample_count, uint32_t group_description_index )
1472 if( !sbgp )
1473 return NULL;
1474 isom_group_assignment_entry_t *data = malloc( sizeof(isom_group_assignment_entry_t) );
1475 if( !data )
1476 return NULL;
1477 data->sample_count = sample_count;
1478 data->group_description_index = group_description_index;
1479 if( lsmash_add_entry( sbgp->list, data ) )
1481 free( data );
1482 return NULL;
1484 return data;
1487 static int isom_add_chpl_entry( isom_chpl_t *chpl, isom_chapter_entry_t *chap_data )
1489 if( !chap_data->chapter_name || !chpl || !chpl->list )
1490 return -1;
1491 isom_chpl_entry_t *data = malloc( sizeof(isom_chpl_entry_t) );
1492 if( !data )
1493 return -1;
1494 data->start_time = chap_data->start_time;
1495 data->chapter_name_length = strlen( chap_data->chapter_name );
1496 data->chapter_name = ( char* )malloc( data->chapter_name_length + 1 );
1497 if( !data->chapter_name )
1499 free( data );
1500 return -1;
1502 memcpy( data->chapter_name, chap_data->chapter_name, data->chapter_name_length );
1503 data->chapter_name[data->chapter_name_length] = '\0';
1504 if( lsmash_add_entry( chpl->list, data ) )
1506 free( data->chapter_name );
1507 free( data );
1508 return -1;
1510 return 0;
1513 static isom_trex_entry_t *isom_add_trex( isom_mvex_t *mvex )
1515 if( !mvex )
1516 return NULL;
1517 if( !mvex->trex_list )
1519 mvex->trex_list = lsmash_create_entry_list();
1520 if( !mvex->trex_list )
1521 return NULL;
1523 isom_trex_entry_t *trex = malloc( sizeof(isom_trex_entry_t) );
1524 if( !trex )
1525 return NULL;
1526 memset( trex, 0, sizeof(isom_trex_entry_t) );
1527 isom_init_box_common( trex, mvex, ISOM_BOX_TYPE_TREX );
1528 if( lsmash_add_entry( mvex->trex_list, trex ) )
1530 free( trex );
1531 return NULL;
1533 return trex;
1536 static isom_trun_entry_t *isom_add_trun( isom_traf_entry_t *traf )
1538 if( !traf )
1539 return NULL;
1540 if( !traf->trun_list )
1542 traf->trun_list = lsmash_create_entry_list();
1543 if( !traf->trun_list )
1544 return NULL;
1546 isom_trun_entry_t *trun = malloc( sizeof(isom_trun_entry_t) );
1547 if( !trun )
1548 return NULL;
1549 memset( trun, 0, sizeof(isom_trun_entry_t) );
1550 isom_init_box_common( trun, traf, ISOM_BOX_TYPE_TRUN );
1551 if( lsmash_add_entry( traf->trun_list, trun ) )
1553 free( trun );
1554 return NULL;
1556 return trun;
1559 static isom_traf_entry_t *isom_add_traf( lsmash_root_t *root, isom_moof_entry_t *moof )
1561 if( !root || !root->moof_list || !moof )
1562 return NULL;
1563 if( !moof->traf_list )
1565 moof->traf_list = lsmash_create_entry_list();
1566 if( !moof->traf_list )
1567 return NULL;
1569 isom_traf_entry_t *traf = malloc( sizeof(isom_traf_entry_t) );
1570 if( !traf )
1571 return NULL;
1572 memset( traf, 0, sizeof(isom_traf_entry_t) );
1573 isom_init_box_common( traf, moof, ISOM_BOX_TYPE_TRAF );
1574 isom_cache_t *cache = malloc( sizeof(isom_cache_t) );
1575 if( !cache )
1577 free( traf );
1578 return NULL;
1580 memset( cache, 0, sizeof(isom_cache_t) );
1581 if( lsmash_add_entry( moof->traf_list, traf ) )
1583 free( cache );
1584 free( traf );
1585 return NULL;
1587 traf->cache = cache;
1588 return traf;
1591 static isom_moof_entry_t *isom_add_moof( lsmash_root_t *root )
1593 if( !root )
1594 return NULL;
1595 if( !root->moof_list )
1597 root->moof_list = lsmash_create_entry_list();
1598 if( !root->moof_list )
1599 return NULL;
1601 isom_moof_entry_t *moof = malloc( sizeof(isom_moof_entry_t) );
1602 if( !moof )
1603 return NULL;
1604 memset( moof, 0, sizeof(isom_moof_entry_t) );
1605 isom_init_box_common( moof, root, ISOM_BOX_TYPE_MOOF );
1606 if( lsmash_add_entry( root->moof_list, moof ) )
1608 free( moof );
1609 return NULL;
1611 return moof;
1614 static isom_tfra_entry_t *isom_add_tfra( isom_mfra_t *mfra )
1616 if( !mfra )
1617 return NULL;
1618 if( !mfra->tfra_list )
1620 mfra->tfra_list = lsmash_create_entry_list();
1621 if( !mfra->tfra_list )
1622 return NULL;
1624 isom_tfra_entry_t *tfra = malloc( sizeof(isom_tfra_entry_t) );
1625 if( !tfra )
1626 return NULL;
1627 memset( tfra, 0, sizeof(isom_tfra_entry_t) );
1628 isom_init_box_common( tfra, mfra, ISOM_BOX_TYPE_TFRA );
1629 if( lsmash_add_entry( mfra->tfra_list, tfra ) )
1631 free( tfra );
1632 return NULL;
1634 return tfra;
1637 static int isom_add_ftyp( lsmash_root_t *root )
1639 if( root->ftyp )
1640 return -1;
1641 isom_create_box( ftyp, root, ISOM_BOX_TYPE_FTYP );
1642 ftyp->size = ISOM_BASEBOX_COMMON_SIZE + 8;
1643 root->ftyp = ftyp;
1644 return 0;
1647 static int isom_add_moov( lsmash_root_t *root )
1649 if( root->moov )
1650 return -1;
1651 isom_create_box( moov, root, ISOM_BOX_TYPE_MOOV );
1652 root->moov = moov;
1653 return 0;
1656 static int isom_add_mvhd( isom_moov_t *moov )
1658 if( !moov || moov->mvhd )
1659 return -1;
1660 isom_create_box( mvhd, moov, ISOM_BOX_TYPE_MVHD );
1661 mvhd->rate = 0x00010000;
1662 mvhd->volume = 0x0100;
1663 mvhd->matrix[0] = 0x00010000;
1664 mvhd->matrix[4] = 0x00010000;
1665 mvhd->matrix[8] = 0x40000000;
1666 mvhd->next_track_ID = 1;
1667 moov->mvhd = mvhd;
1668 return 0;
1671 static int isom_scan_trak_profileLevelIndication( isom_trak_entry_t* trak, mp4a_audioProfileLevelIndication* audio_pli, mp4sys_visualProfileLevelIndication* visual_pli )
1673 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl )
1674 return -1;
1675 isom_stsd_t* stsd = trak->mdia->minf->stbl->stsd;
1676 if( !stsd || !stsd->list || !stsd->list->head )
1677 return -1;
1678 for( lsmash_entry_t *entry = stsd->list->head; entry; entry = entry->next )
1680 isom_sample_entry_t* sample_entry = (isom_sample_entry_t*)entry->data;
1681 if( !sample_entry )
1682 return -1;
1683 switch( sample_entry->type )
1685 case ISOM_CODEC_TYPE_AVC1_VIDEO :
1686 #if 0
1687 case ISOM_CODEC_TYPE_AVC2_VIDEO :
1688 case ISOM_CODEC_TYPE_AVCP_VIDEO :
1689 case ISOM_CODEC_TYPE_SVC1_VIDEO :
1690 case ISOM_CODEC_TYPE_MVC1_VIDEO :
1691 case ISOM_CODEC_TYPE_MVC2_VIDEO :
1692 #endif
1693 /* FIXME: Do we have to arbitrate like audio? */
1694 if( *visual_pli == MP4SYS_VISUAL_PLI_NONE_REQUIRED )
1695 *visual_pli = MP4SYS_VISUAL_PLI_H264_AVC;
1696 break;
1697 case ISOM_CODEC_TYPE_MP4A_AUDIO :
1698 *audio_pli = mp4a_max_audioProfileLevelIndication( *audio_pli, mp4a_get_audioProfileLevelIndication( &((isom_audio_entry_t*)sample_entry)->summary ) );
1699 break;
1700 #if 0
1701 case ISOM_CODEC_TYPE_DRAC_VIDEO :
1702 case ISOM_CODEC_TYPE_ENCV_VIDEO :
1703 case ISOM_CODEC_TYPE_MJP2_VIDEO :
1704 case ISOM_CODEC_TYPE_S263_VIDEO :
1705 case ISOM_CODEC_TYPE_VC_1_VIDEO :
1706 /* FIXME: Do we have to arbitrate like audio? */
1707 if( *visual_pli == MP4SYS_VISUAL_PLI_NONE_REQUIRED )
1708 *visual_pli = MP4SYS_VISUAL_PLI_NOT_SPECIFIED;
1709 break;
1710 #endif
1711 case ISOM_CODEC_TYPE_AC_3_AUDIO :
1712 case ISOM_CODEC_TYPE_ALAC_AUDIO :
1713 case ISOM_CODEC_TYPE_SAMR_AUDIO :
1714 case ISOM_CODEC_TYPE_SAWB_AUDIO :
1715 #ifdef LSMASH_DEMUXER_ENABLED
1716 case ISOM_CODEC_TYPE_EC_3_AUDIO :
1717 #endif
1718 #if 0
1719 case ISOM_CODEC_TYPE_DRA1_AUDIO :
1720 case ISOM_CODEC_TYPE_DTSC_AUDIO :
1721 case ISOM_CODEC_TYPE_DTSH_AUDIO :
1722 case ISOM_CODEC_TYPE_DTSL_AUDIO :
1723 case ISOM_CODEC_TYPE_ENCA_AUDIO :
1724 case ISOM_CODEC_TYPE_G719_AUDIO :
1725 case ISOM_CODEC_TYPE_G726_AUDIO :
1726 case ISOM_CODEC_TYPE_M4AE_AUDIO :
1727 case ISOM_CODEC_TYPE_MLPA_AUDIO :
1728 case ISOM_CODEC_TYPE_RAW_AUDIO :
1729 case ISOM_CODEC_TYPE_SAWP_AUDIO :
1730 case ISOM_CODEC_TYPE_SEVC_AUDIO :
1731 case ISOM_CODEC_TYPE_SQCP_AUDIO :
1732 case ISOM_CODEC_TYPE_SSMV_AUDIO :
1733 case ISOM_CODEC_TYPE_TWOS_AUDIO :
1734 #endif
1735 /* NOTE: These audio codecs other than mp4a does not have appropriate pli. */
1736 *audio_pli = MP4A_AUDIO_PLI_NOT_SPECIFIED;
1737 break;
1738 #if 0
1739 case ISOM_CODEC_TYPE_FDP_HINT :
1740 case ISOM_CODEC_TYPE_M2TS_HINT :
1741 case ISOM_CODEC_TYPE_PM2T_HINT :
1742 case ISOM_CODEC_TYPE_PRTP_HINT :
1743 case ISOM_CODEC_TYPE_RM2T_HINT :
1744 case ISOM_CODEC_TYPE_RRTP_HINT :
1745 case ISOM_CODEC_TYPE_RSRP_HINT :
1746 case ISOM_CODEC_TYPE_RTP_HINT :
1747 case ISOM_CODEC_TYPE_SM2T_HINT :
1748 case ISOM_CODEC_TYPE_SRTP_HINT :
1749 /* FIXME: Do we have to set OD_profileLevelIndication? */
1750 break;
1751 case ISOM_CODEC_TYPE_IXSE_META :
1752 case ISOM_CODEC_TYPE_METT_META :
1753 case ISOM_CODEC_TYPE_METX_META :
1754 case ISOM_CODEC_TYPE_MLIX_META :
1755 case ISOM_CODEC_TYPE_OKSD_META :
1756 case ISOM_CODEC_TYPE_SVCM_META :
1757 case ISOM_CODEC_TYPE_TEXT_META :
1758 case ISOM_CODEC_TYPE_URIM_META :
1759 case ISOM_CODEC_TYPE_XML_META :
1760 /* FIXME: Do we have to set OD_profileLevelIndication? */
1761 break;
1762 #endif
1765 return 0;
1768 static int isom_add_iods( isom_moov_t *moov )
1770 if( !moov || !moov->trak_list || moov->iods )
1771 return -1;
1772 isom_create_box( iods, moov, ISOM_BOX_TYPE_IODS );
1773 iods->OD = mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */
1774 if( !iods->OD )
1776 free( iods );
1777 return -1;
1779 mp4a_audioProfileLevelIndication audio_pli = MP4A_AUDIO_PLI_NONE_REQUIRED;
1780 mp4sys_visualProfileLevelIndication visual_pli = MP4SYS_VISUAL_PLI_NONE_REQUIRED;
1781 for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
1783 isom_trak_entry_t* trak = (isom_trak_entry_t*)entry->data;
1784 if( !trak || !trak->tkhd )
1785 return -1;
1786 if( isom_scan_trak_profileLevelIndication( trak, &audio_pli, &visual_pli ) )
1787 return -1;
1788 if( mp4sys_add_ES_ID_Inc( iods->OD, trak->tkhd->track_ID ) )
1789 return -1;
1791 if( mp4sys_to_InitialObjectDescriptor( iods->OD,
1792 0, /* FIXME: I'm not quite sure what the spec says. */
1793 MP4SYS_OD_PLI_NONE_REQUIRED, MP4SYS_SCENE_PLI_NONE_REQUIRED,
1794 audio_pli, visual_pli,
1795 MP4SYS_GRAPHICS_PLI_NONE_REQUIRED ) )
1797 free( iods );
1798 return -1;
1800 moov->iods = iods;
1801 return 0;
1804 static int isom_add_tkhd( isom_trak_entry_t *trak, uint32_t handler_type )
1806 if( !trak || !trak->root || !trak->root->moov || !trak->root->moov->mvhd || !trak->root->moov->trak_list )
1807 return -1;
1808 if( !trak->tkhd )
1810 isom_create_box( tkhd, trak, ISOM_BOX_TYPE_TKHD );
1811 switch( handler_type )
1813 case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK :
1814 tkhd->matrix[0] = 0x00010000;
1815 tkhd->matrix[4] = 0x00010000;
1816 tkhd->matrix[8] = 0x40000000;
1817 break;
1818 case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK :
1819 tkhd->volume = 0x0100;
1820 break;
1821 default :
1822 break;
1824 tkhd->duration = 0xffffffff;
1825 tkhd->track_ID = trak->root->moov->mvhd->next_track_ID;
1826 ++ trak->root->moov->mvhd->next_track_ID;
1827 trak->tkhd = tkhd;
1829 return 0;
1832 static int isom_add_clef( isom_tapt_t *tapt )
1834 if( tapt->clef )
1835 return 0;
1836 isom_create_box( clef, tapt, QT_BOX_TYPE_CLEF );
1837 tapt->clef = clef;
1838 return 0;
1841 static int isom_add_prof( isom_tapt_t *tapt )
1843 if( tapt->prof )
1844 return 0;
1845 isom_create_box( prof, tapt, QT_BOX_TYPE_PROF );
1846 tapt->prof = prof;
1847 return 0;
1850 static int isom_add_enof( isom_tapt_t *tapt )
1852 if( tapt->enof )
1853 return 0;
1854 isom_create_box( enof, tapt, QT_BOX_TYPE_ENOF );
1855 tapt->enof = enof;
1856 return 0;
1859 static int isom_add_tapt( isom_trak_entry_t *trak )
1861 if( trak->tapt )
1862 return 0;
1863 isom_create_box( tapt, trak, QT_BOX_TYPE_TAPT );
1864 trak->tapt = tapt;
1865 return 0;
1868 int isom_add_elst( isom_edts_t *edts )
1870 if( edts->elst )
1871 return 0;
1872 isom_create_list_box( elst, edts, ISOM_BOX_TYPE_ELST );
1873 edts->elst = elst;
1874 return 0;
1877 int isom_add_edts( isom_trak_entry_t *trak )
1879 if( trak->edts )
1880 return 0;
1881 isom_create_box( edts, trak, ISOM_BOX_TYPE_EDTS );
1882 trak->edts = edts;
1883 return 0;
1886 static int isom_add_tref( isom_trak_entry_t *trak )
1888 if( trak->tref )
1889 return 0;
1890 isom_create_box( tref, trak, ISOM_BOX_TYPE_TREF );
1891 tref->ref_list = lsmash_create_entry_list();
1892 if( !tref->ref_list )
1894 free( tref );
1895 return -1;
1897 trak->tref = tref;
1898 return 0;
1901 static int isom_add_mdhd( isom_mdia_t *mdia, uint16_t default_language )
1903 if( !mdia || mdia->mdhd )
1904 return -1;
1905 isom_create_box( mdhd, mdia, ISOM_BOX_TYPE_MDHD );
1906 mdhd->language = default_language;
1907 mdia->mdhd = mdhd;
1908 return 0;
1911 static int isom_add_mdia( isom_trak_entry_t *trak )
1913 if( !trak || trak->mdia )
1914 return -1;
1915 isom_create_box( mdia, trak, ISOM_BOX_TYPE_MDIA );
1916 trak->mdia = mdia;
1917 return 0;
1920 static int isom_add_hdlr( isom_mdia_t *mdia, isom_meta_t *meta, isom_minf_t *minf, uint32_t media_type )
1922 if( (!mdia && !meta && !minf) || (mdia && meta) || (meta && minf) || (minf && mdia) )
1923 return -1; /* Either one must be given. */
1924 if( (mdia && mdia->hdlr) || (meta && meta->hdlr) || (minf && minf->hdlr) )
1925 return -1; /* Selected one must not have hdlr yet. */
1926 isom_box_t *parent = mdia ? (isom_box_t *)mdia : meta ? (isom_box_t *)meta : (isom_box_t *)minf;
1927 isom_create_box( hdlr, parent, ISOM_BOX_TYPE_HDLR );
1928 lsmash_root_t *root = hdlr->root;
1929 uint32_t type = mdia ? (root->qt_compatible ? QT_HANDLER_TYPE_MEDIA : 0) : (meta ? 0 : QT_HANDLER_TYPE_DATA);
1930 uint32_t subtype = media_type;
1931 hdlr->componentType = type;
1932 hdlr->componentSubtype = subtype;
1933 char *type_name = NULL;
1934 char *subtype_name = NULL;
1935 uint8_t type_name_length = 0;
1936 uint8_t subtype_name_length = 0;
1937 if( mdia )
1938 type_name = "Media ";
1939 else if( meta )
1940 type_name = "Metadata ";
1941 else /* if( minf ) */
1942 type_name = "Data ";
1943 type_name_length = strlen( type_name );
1944 struct
1946 uint32_t subtype;
1947 char *subtype_name;
1948 uint8_t subtype_name_length;
1949 } subtype_table[] =
1951 { ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK, "Sound ", 6 },
1952 { ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK, "Video", 6 },
1953 { ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK, "Hint ", 5 },
1954 { ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK, "Metadata ", 9 },
1955 { ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK, "Text ", 5 },
1956 { ISOM_META_HANDLER_TYPE_ITUNES_METADATA, "iTunes ", 7 },
1957 { QT_REFERENCE_HANDLER_TYPE_ALIAS, "Alias ", 6 },
1958 { QT_REFERENCE_HANDLER_TYPE_RESOURCE, "Resource ", 9 },
1959 { QT_REFERENCE_HANDLER_TYPE_URL, "URL ", 4 },
1960 { subtype, "Unknown ", 8 }
1962 for( int i = 0; subtype_table[i].subtype; i++ )
1963 if( subtype == subtype_table[i].subtype )
1965 subtype_name = subtype_table[i].subtype_name;
1966 subtype_name_length = subtype_table[i].subtype_name_length;
1967 break;
1969 uint32_t name_length = 15 + subtype_name_length + type_name_length + root->isom_compatible + root->qt_compatible;
1970 uint8_t *name = malloc( name_length );
1971 if( !name )
1972 return -1;
1973 if( root->qt_compatible )
1974 name[0] = name_length & 0xff;
1975 memcpy( name + root->qt_compatible, "L-SMASH ", 8 );
1976 memcpy( name + root->qt_compatible + 8, subtype_name, subtype_name_length );
1977 memcpy( name + root->qt_compatible + 8 + subtype_name_length, type_name, type_name_length );
1978 memcpy( name + root->qt_compatible + 8 + subtype_name_length + type_name_length, "Handler", 7 );
1979 if( root->isom_compatible )
1980 name[name_length - 1] = 0;
1981 hdlr->componentName = name;
1982 hdlr->componentName_length = name_length;
1983 if( mdia )
1984 mdia->hdlr = hdlr;
1985 else if( meta )
1986 meta->hdlr = hdlr;
1987 else
1988 minf->hdlr = hdlr;
1989 return 0;
1992 static int isom_add_minf( isom_mdia_t *mdia )
1994 if( !mdia || mdia->minf )
1995 return -1;
1996 isom_create_box( minf, mdia, ISOM_BOX_TYPE_MINF );
1997 mdia->minf = minf;
1998 return 0;
2001 static int isom_add_vmhd( isom_minf_t *minf )
2003 if( !minf || minf->vmhd )
2004 return -1;
2005 isom_create_box( vmhd, minf, ISOM_BOX_TYPE_VMHD );
2006 vmhd->flags = 0x000001;
2007 minf->vmhd = vmhd;
2008 return 0;
2011 static int isom_add_smhd( isom_minf_t *minf )
2013 if( !minf || minf->smhd )
2014 return -1;
2015 isom_create_box( smhd, minf, ISOM_BOX_TYPE_SMHD );
2016 minf->smhd = smhd;
2017 return 0;
2020 static int isom_add_hmhd( isom_minf_t *minf )
2022 if( !minf || minf->hmhd )
2023 return -1;
2024 isom_create_box( hmhd, minf, ISOM_BOX_TYPE_HMHD );
2025 minf->hmhd = hmhd;
2026 return 0;
2029 static int isom_add_nmhd( isom_minf_t *minf )
2031 if( !minf || minf->nmhd )
2032 return -1;
2033 isom_create_box( nmhd, minf, ISOM_BOX_TYPE_NMHD );
2034 minf->nmhd = nmhd;
2035 return 0;
2038 static int isom_add_gmin( isom_gmhd_t *gmhd )
2040 if( !gmhd || gmhd->gmin )
2041 return -1;
2042 isom_create_box( gmin, gmhd, QT_BOX_TYPE_GMIN );
2043 gmhd->gmin = gmin;
2044 return 0;
2047 static int isom_add_text( isom_gmhd_t *gmhd )
2049 if( !gmhd || gmhd->text )
2050 return -1;
2051 isom_create_box( text, gmhd, QT_BOX_TYPE_TEXT );
2052 text->matrix[0] = 0x00010000;
2053 text->matrix[4] = 0x00010000;
2054 text->matrix[8] = 0x40000000;
2055 gmhd->text = text;
2056 return 0;
2059 static int isom_add_gmhd( isom_minf_t *minf )
2061 if( !minf || minf->gmhd )
2062 return -1;
2063 isom_create_box( gmhd, minf, QT_BOX_TYPE_GMHD );
2064 minf->gmhd = gmhd;
2065 return 0;
2068 static int isom_add_dinf( isom_minf_t *minf )
2070 if( !minf || minf->dinf )
2071 return -1;
2072 isom_create_box( dinf, minf, ISOM_BOX_TYPE_DINF );
2073 minf->dinf = dinf;
2074 return 0;
2077 static int isom_add_dref( isom_dinf_t *dinf )
2079 if( !dinf || dinf->dref )
2080 return -1;
2081 isom_create_list_box( dref, dinf, ISOM_BOX_TYPE_DREF );
2082 dinf->dref = dref;
2083 if( isom_add_dref_entry( dref, 0x000001, NULL, NULL ) )
2084 return -1;
2085 return 0;
2088 static int isom_add_stsd( isom_stbl_t *stbl )
2090 if( !stbl || stbl->stsd )
2091 return -1;
2092 isom_create_list_box( stsd, stbl, ISOM_BOX_TYPE_STSD );
2093 stbl->stsd = stsd;
2094 return 0;
2097 int isom_add_btrt( isom_visual_entry_t *visual )
2099 if( !visual || visual->btrt )
2100 return -1;
2101 isom_create_box( btrt, visual, ISOM_BOX_TYPE_BTRT );
2102 visual->btrt = btrt;
2103 return 0;
2106 int lsmash_add_btrt( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number )
2108 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
2109 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
2110 return -1;
2111 isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number );
2112 return isom_add_btrt( data );
2115 static int isom_add_stts( isom_stbl_t *stbl )
2117 if( !stbl || stbl->stts )
2118 return -1;
2119 isom_create_list_box( stts, stbl, ISOM_BOX_TYPE_STTS );
2120 stbl->stts = stts;
2121 return 0;
2124 static int isom_add_ctts( isom_stbl_t *stbl )
2126 if( !stbl || stbl->ctts )
2127 return -1;
2128 isom_create_list_box( ctts, stbl, ISOM_BOX_TYPE_CTTS );
2129 stbl->ctts = ctts;
2130 return 0;
2133 static int isom_add_cslg( isom_stbl_t *stbl )
2135 if( !stbl || stbl->cslg )
2136 return -1;
2137 isom_create_box( cslg, stbl, ISOM_BOX_TYPE_CSLG );
2138 stbl->cslg = cslg;
2139 return 0;
2142 static int isom_add_stsc( isom_stbl_t *stbl )
2144 if( !stbl || stbl->stsc )
2145 return -1;
2146 isom_create_list_box( stsc, stbl, ISOM_BOX_TYPE_STSC );
2147 stbl->stsc = stsc;
2148 return 0;
2151 static int isom_add_stsz( isom_stbl_t *stbl )
2153 if( !stbl || stbl->stsz )
2154 return -1;
2155 isom_create_box( stsz, stbl, ISOM_BOX_TYPE_STSZ ); /* We don't create a list here. */
2156 stbl->stsz = stsz;
2157 return 0;
2160 static int isom_add_stss( isom_stbl_t *stbl )
2162 if( !stbl || stbl->stss )
2163 return -1;
2164 isom_create_list_box( stss, stbl, ISOM_BOX_TYPE_STSS );
2165 stbl->stss = stss;
2166 return 0;
2169 static int isom_add_stps( isom_stbl_t *stbl )
2171 if( !stbl || stbl->stps )
2172 return -1;
2173 isom_create_list_box( stps, stbl, QT_BOX_TYPE_STPS );
2174 stbl->stps = stps;
2175 return 0;
2178 static int isom_add_sdtp( isom_stbl_t *stbl )
2180 if( !stbl || stbl->sdtp )
2181 return -1;
2182 isom_create_list_box( sdtp, stbl, ISOM_BOX_TYPE_SDTP );
2183 stbl->sdtp = sdtp;
2184 return 0;
2187 static isom_sgpd_entry_t *isom_add_sgpd( isom_stbl_t *stbl, uint32_t grouping_type )
2189 if( !stbl )
2190 return NULL;
2191 if( !stbl->sgpd_list )
2193 stbl->sgpd_list = lsmash_create_entry_list();
2194 if( !stbl->sgpd_list )
2195 return NULL;
2197 isom_sgpd_entry_t *sgpd = malloc( sizeof(isom_sgpd_entry_t) );
2198 if( !sgpd )
2199 return NULL;
2200 memset( sgpd, 0, sizeof(isom_sgpd_entry_t) );
2201 isom_init_box_common( sgpd, stbl, ISOM_BOX_TYPE_SGPD );
2202 sgpd->list = lsmash_create_entry_list();
2203 if( !sgpd->list || lsmash_add_entry( stbl->sgpd_list, sgpd ) )
2205 free( sgpd );
2206 return NULL;
2208 sgpd->grouping_type = grouping_type;
2209 sgpd->version = 1; /* We use version 1 because it is recommended in the spec. */
2210 switch( grouping_type )
2212 case ISOM_GROUP_TYPE_RAP :
2213 sgpd->default_length = 1;
2214 break;
2215 case ISOM_GROUP_TYPE_ROLL :
2216 sgpd->default_length = 2;
2217 break;
2218 default :
2219 /* We don't consider other grouping types currently. */
2220 break;
2222 return sgpd;
2225 static isom_sbgp_entry_t *isom_add_sbgp( isom_stbl_t *stbl, uint32_t grouping_type )
2227 if( !stbl )
2228 return NULL;
2229 if( !stbl->sbgp_list )
2231 stbl->sbgp_list = lsmash_create_entry_list();
2232 if( !stbl->sbgp_list )
2233 return NULL;
2235 isom_sbgp_entry_t *sbgp = malloc( sizeof(isom_sbgp_entry_t) );
2236 if( !sbgp )
2237 return NULL;
2238 memset( sbgp, 0, sizeof(isom_sbgp_entry_t) );
2239 isom_init_box_common( sbgp, stbl, ISOM_BOX_TYPE_SBGP );
2240 sbgp->list = lsmash_create_entry_list();
2241 if( !sbgp->list || lsmash_add_entry( stbl->sbgp_list, sbgp ) )
2243 free( sbgp );
2244 return NULL;
2246 sbgp->grouping_type = grouping_type;
2247 return sbgp;
2250 static int isom_add_stbl( isom_minf_t *minf )
2252 if( !minf || minf->stbl )
2253 return -1;
2254 isom_create_box( stbl, minf, ISOM_BOX_TYPE_STBL );
2255 minf->stbl = stbl;
2256 return 0;
2259 static int isom_add_chpl( isom_moov_t *moov )
2261 if( !moov || !moov->udta || moov->udta->chpl )
2262 return -1;
2263 isom_create_list_box( chpl, moov->udta, ISOM_BOX_TYPE_CHPL );
2264 chpl->version = 1; /* version = 1 is popular. */
2265 moov->udta->chpl = chpl;
2266 return 0;
2269 #ifdef LSMASH_DEMUXER_ENABLED
2270 static int isom_add_mean( isom_metaitem_t *metaitem )
2272 if( !metaitem || metaitem->mean )
2273 return -1;
2274 isom_create_box( mean, metaitem, ISOM_BOX_TYPE_MEAN );
2275 metaitem->mean = mean;
2276 return 0;
2279 static int isom_add_name( isom_metaitem_t *metaitem )
2281 if( !metaitem || metaitem->name )
2282 return -1;
2283 isom_create_box( name, metaitem, ISOM_BOX_TYPE_NAME );
2284 metaitem->name = name;
2285 return 0;
2288 static int isom_add_data( isom_metaitem_t *metaitem )
2290 if( !metaitem || metaitem->data )
2291 return -1;
2292 isom_create_box( data, metaitem, ISOM_BOX_TYPE_DATA );
2293 metaitem->data = data;
2294 return 0;
2297 static int isom_add_ilst( isom_moov_t *moov )
2299 if( !moov || !moov->udta || !moov->udta->meta || moov->udta->meta->ilst )
2300 return -1;
2301 isom_create_box( ilst, moov->udta->meta, ISOM_BOX_TYPE_ILST );
2302 ilst->item_list = lsmash_create_entry_list();
2303 if( !ilst->item_list )
2305 free( ilst );
2306 return -1;
2308 moov->udta->meta->ilst = ilst;
2309 return 0;
2312 static int isom_add_meta( isom_box_t *parent )
2314 if( !parent )
2315 return -1;
2316 isom_create_box( meta, parent, ISOM_BOX_TYPE_META );
2317 if( !parent->type )
2319 lsmash_root_t *root = (lsmash_root_t *)root;
2320 if( root->meta )
2321 return -1;
2322 root->meta = meta;
2324 else if( parent->type == ISOM_BOX_TYPE_MOOV )
2326 isom_moov_t *moov = (isom_moov_t *)parent;
2327 if( moov->meta )
2328 return -1;
2329 moov->meta = meta;
2331 else if( parent->type == ISOM_BOX_TYPE_TRAK )
2333 isom_trak_entry_t *trak = (isom_trak_entry_t *)trak;
2334 if( trak->meta )
2335 return -1;
2336 trak->meta = meta;
2338 else
2340 isom_udta_t *udta = (isom_udta_t *)parent;
2341 if( udta->meta )
2342 return -1;
2343 udta->meta = meta;
2345 return 0;
2347 #endif /* LSMASH_DEMUXER_ENABLED */
2349 static int isom_add_udta( lsmash_root_t *root, uint32_t track_ID )
2351 /* track_ID == 0 means the direct addition to moov box */
2352 if( !track_ID )
2354 if( !root || !root->moov )
2355 return -1;
2356 if( root->moov->udta )
2357 return 0;
2358 isom_create_box( udta, root->moov, ISOM_BOX_TYPE_UDTA );
2359 root->moov->udta = udta;
2360 return 0;
2362 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
2363 if( !trak )
2364 return -1;
2365 if( trak->udta )
2366 return 0;
2367 isom_create_box( udta, trak, ISOM_BOX_TYPE_UDTA );
2368 trak->udta = udta;
2369 return 0;
2372 static isom_trak_entry_t *isom_add_trak( lsmash_root_t *root )
2374 if( !root || !root->moov )
2375 return NULL;
2376 isom_moov_t *moov = root->moov;
2377 if( !moov->trak_list )
2379 moov->trak_list = lsmash_create_entry_list();
2380 if( !moov->trak_list )
2381 return NULL;
2383 isom_trak_entry_t *trak = malloc( sizeof(isom_trak_entry_t) );
2384 if( !trak )
2385 return NULL;
2386 memset( trak, 0, sizeof(isom_trak_entry_t) );
2387 isom_init_box_common( trak, moov, ISOM_BOX_TYPE_TRAK );
2388 isom_cache_t *cache = malloc( sizeof(isom_cache_t) );
2389 if( !cache )
2391 free( trak );
2392 return NULL;
2394 memset( cache, 0, sizeof(isom_cache_t) );
2395 isom_fragment_t *fragment = NULL;
2396 if( root->fragment )
2398 fragment = malloc( sizeof(isom_fragment_t) );
2399 if( !fragment )
2401 free( cache );
2402 free( trak );
2403 return NULL;
2405 memset( fragment, 0, sizeof(isom_fragment_t) );
2406 cache->fragment = fragment;
2408 if( lsmash_add_entry( moov->trak_list, trak ) )
2410 if( fragment )
2411 free( fragment );
2412 free( cache );
2413 free( trak );
2414 return NULL;
2416 trak->cache = cache;
2417 return trak;
2420 static int isom_add_mvex( isom_moov_t *moov )
2422 if( !moov || moov->mvex )
2423 return -1;
2424 isom_create_box( mvex, moov, ISOM_BOX_TYPE_MVEX );
2425 moov->mvex = mvex;
2426 return 0;
2429 static int isom_add_mehd( isom_mvex_t *mvex )
2431 if( !mvex || mvex->mehd )
2432 return -1;
2433 isom_create_box( mehd, mvex, ISOM_BOX_TYPE_MEHD );
2434 mvex->mehd = mehd;
2435 return 0;
2438 static int isom_add_tfhd( isom_traf_entry_t *traf )
2440 if( !traf || traf->tfhd )
2441 return -1;
2442 isom_create_box( tfhd, traf, ISOM_BOX_TYPE_TFHD );
2443 traf->tfhd = tfhd;
2444 return 0;
2447 static int isom_add_mfhd( isom_moof_entry_t *moof )
2449 if( !moof || moof->mfhd )
2450 return -1;
2451 isom_create_box( mfhd, moof, ISOM_BOX_TYPE_MFHD );
2452 moof->mfhd = mfhd;
2453 return 0;
2456 static int isom_add_mfra( lsmash_root_t *root )
2458 if( !root || root->mfra )
2459 return -1;
2460 isom_create_box( mfra, root, ISOM_BOX_TYPE_MFRA );
2461 root->mfra = mfra;
2462 return 0;
2465 static int isom_add_mfro( isom_mfra_t *mfra )
2467 if( !mfra || mfra->mfro )
2468 return -1;
2469 isom_create_box( mfro, mfra, ISOM_BOX_TYPE_MFRO );
2470 mfra->mfro = mfro;
2471 return 0;
2474 #define isom_remove_box( box_name, parent_type ) \
2475 do \
2477 parent_type *parent = (parent_type *)box_name->parent; \
2478 free( box_name ); \
2479 if( parent ) \
2480 parent->box_name = NULL; \
2481 } while( 0 )
2483 static void isom_remove_ftyp( isom_ftyp_t *ftyp )
2485 if( !ftyp )
2486 return;
2487 if( ftyp->compatible_brands )
2488 free( ftyp->compatible_brands );
2489 isom_remove_box( ftyp, lsmash_root_t );
2492 static void isom_remove_tkhd( isom_tkhd_t *tkhd )
2494 if( !tkhd )
2495 return;
2496 isom_remove_box( tkhd, isom_trak_entry_t );
2499 static void isom_remove_clef( isom_clef_t *clef )
2501 if( !clef )
2502 return;
2503 isom_remove_box( clef, isom_tapt_t );
2506 static void isom_remove_prof( isom_prof_t *prof )
2508 if( !prof )
2509 return;
2510 isom_remove_box( prof, isom_tapt_t );
2513 static void isom_remove_enof( isom_enof_t *enof )
2515 if( !enof )
2516 return;
2517 isom_remove_box( enof, isom_tapt_t );
2520 void isom_remove_tapt( isom_tapt_t *tapt )
2522 if( !tapt )
2523 return;
2524 isom_remove_clef( tapt->clef );
2525 isom_remove_prof( tapt->prof );
2526 isom_remove_enof( tapt->enof );
2527 isom_remove_box( tapt, isom_trak_entry_t );
2530 static void isom_remove_elst( isom_elst_t *elst )
2532 if( !elst )
2533 return;
2534 lsmash_remove_list( elst->list, NULL );
2535 isom_remove_box( elst, isom_edts_t );
2538 static void isom_remove_edts( isom_edts_t *edts )
2540 if( !edts )
2541 return;
2542 isom_remove_elst( edts->elst );
2543 isom_remove_box( edts, isom_trak_entry_t );
2546 static void isom_remove_track_reference_type( isom_tref_type_t *ref )
2548 if( !ref )
2549 return;
2550 if( ref->track_ID )
2551 free( ref->track_ID );
2552 free( ref );
2555 static void isom_remove_tref( isom_tref_t *tref )
2557 if( !tref )
2558 return;
2559 lsmash_remove_list( tref->ref_list, isom_remove_track_reference_type );
2560 isom_remove_box( tref, isom_trak_entry_t );
2563 static void isom_remove_mdhd( isom_mdhd_t *mdhd )
2565 if( !mdhd )
2566 return;
2567 isom_remove_box( mdhd, isom_mdia_t );
2570 static void isom_remove_vmhd( isom_vmhd_t *vmhd )
2572 if( !vmhd )
2573 return;
2574 isom_remove_box( vmhd, isom_minf_t );
2577 static void isom_remove_smhd( isom_smhd_t *smhd )
2579 if( !smhd )
2580 return;
2581 isom_remove_box( smhd, isom_minf_t );
2584 static void isom_remove_hmhd( isom_hmhd_t *hmhd )
2586 if( !hmhd )
2587 return;
2588 isom_remove_box( hmhd, isom_minf_t );
2591 static void isom_remove_nmhd( isom_nmhd_t *nmhd )
2593 if( !nmhd )
2594 return;
2595 isom_remove_box( nmhd, isom_minf_t );
2598 static void isom_remove_gmin( isom_gmin_t *gmin )
2600 if( !gmin )
2601 return;
2602 isom_remove_box( gmin, isom_gmhd_t );
2605 static void isom_remove_text( isom_text_t *text )
2607 if( !text )
2608 return;
2609 isom_remove_box( text, isom_gmhd_t );
2612 static void isom_remove_gmhd( isom_gmhd_t *gmhd )
2614 if( !gmhd )
2615 return;
2616 isom_remove_gmin( gmhd->gmin );
2617 isom_remove_text( gmhd->text );
2618 isom_remove_box( gmhd, isom_minf_t );
2621 static void isom_remove_hdlr( isom_hdlr_t *hdlr )
2623 if( !hdlr )
2624 return;
2625 if( hdlr->componentName )
2626 free( hdlr->componentName );
2627 if( hdlr->parent )
2629 if( hdlr->parent->type == ISOM_BOX_TYPE_MDIA )
2630 isom_remove_box( hdlr, isom_mdia_t );
2631 else if( hdlr->parent->type == ISOM_BOX_TYPE_META )
2632 isom_remove_box( hdlr, isom_meta_t );
2633 else if( hdlr->parent->type == ISOM_BOX_TYPE_MINF )
2634 isom_remove_box( hdlr, isom_minf_t );
2635 else
2636 assert( 0 );
2637 return;
2639 free( hdlr );
2642 void isom_remove_clap( isom_clap_t *clap )
2644 if( !clap )
2645 return;
2646 isom_remove_box( clap, isom_visual_entry_t );
2649 void isom_remove_pasp( isom_pasp_t *pasp )
2651 if( !pasp )
2652 return;
2653 isom_remove_box( pasp, isom_visual_entry_t );
2656 void isom_remove_colr( isom_colr_t *colr )
2658 if( !colr )
2659 return;
2660 isom_remove_box( colr, isom_visual_entry_t );
2663 void isom_remove_stsl( isom_stsl_t *stsl )
2665 if( !stsl )
2666 return;
2667 isom_remove_box( stsl, isom_visual_entry_t );
2670 static void isom_remove_esds( isom_esds_t *esds )
2672 if( !esds )
2673 return;
2674 mp4sys_remove_ES_Descriptor( esds->ES );
2675 if( esds->parent )
2677 switch( esds->parent->type )
2679 case ISOM_CODEC_TYPE_MP4V_VIDEO :
2680 isom_remove_box( esds, isom_visual_entry_t );
2681 break;
2682 case ISOM_CODEC_TYPE_MP4A_AUDIO :
2683 case ISOM_CODEC_TYPE_M4AE_AUDIO :
2684 isom_remove_box( esds, isom_audio_entry_t );
2685 break;
2686 case QT_BOX_TYPE_WAVE :
2687 isom_remove_box( esds, isom_wave_t );
2688 break;
2689 case ISOM_CODEC_TYPE_MP4S_SYSTEM :
2690 isom_remove_box( esds, isom_mp4s_entry_t );
2691 break;
2692 default :
2693 assert( 0 );
2695 return;
2697 free( esds );
2700 void isom_remove_avcC( isom_avcC_t *avcC )
2702 if( !avcC )
2703 return;
2704 lsmash_remove_list( avcC->sequenceParameterSets, isom_remove_avcC_ps );
2705 lsmash_remove_list( avcC->pictureParameterSets, isom_remove_avcC_ps );
2706 lsmash_remove_list( avcC->sequenceParameterSetExt, isom_remove_avcC_ps );
2707 isom_remove_box( avcC, isom_visual_entry_t );
2710 void isom_remove_btrt( isom_btrt_t *btrt )
2712 if( !btrt )
2713 return;
2714 isom_remove_box( btrt, isom_visual_entry_t );
2717 static void isom_remove_visual_extensions( isom_visual_entry_t *visual )
2719 if( !visual )
2720 return;
2721 isom_remove_clap( visual->clap );
2722 isom_remove_pasp( visual->pasp );
2723 isom_remove_colr( visual->colr );
2724 isom_remove_stsl( visual->stsl );
2725 isom_remove_esds( visual->esds );
2726 isom_remove_avcC( visual->avcC );
2727 isom_remove_btrt( visual->btrt );
2730 static void isom_remove_font_record( isom_font_record_t *font_record )
2732 if( !font_record )
2733 return;
2734 if( font_record->font_name )
2735 free( font_record->font_name );
2736 free( font_record );
2739 void isom_remove_ftab( isom_ftab_t *ftab )
2741 if( !ftab )
2742 return;
2743 lsmash_remove_list( ftab->list, isom_remove_font_record );
2744 isom_remove_box( ftab, isom_tx3g_entry_t );
2747 void isom_remove_frma( isom_frma_t *frma )
2749 if( !frma )
2750 return;
2751 isom_remove_box( frma, isom_wave_t );
2754 void isom_remove_enda( isom_enda_t *enda )
2756 if( !enda )
2757 return;
2758 isom_remove_box( enda, isom_wave_t );
2761 void isom_remove_mp4a( isom_mp4a_t *mp4a )
2763 if( !mp4a )
2764 return;
2765 isom_remove_box( mp4a, isom_wave_t );
2768 void isom_remove_terminator( isom_terminator_t *terminator )
2770 if( !terminator )
2771 return;
2772 isom_remove_box( terminator, isom_wave_t );
2775 void isom_remove_wave( isom_wave_t *wave )
2777 if( !wave )
2778 return;
2779 isom_remove_frma( wave->frma );
2780 isom_remove_enda( wave->enda );
2781 isom_remove_mp4a( wave->mp4a );
2782 isom_remove_esds( wave->esds );
2783 isom_remove_terminator( wave->terminator );
2784 if( wave->exdata )
2785 free( wave->exdata );
2786 isom_remove_box( wave, isom_audio_entry_t );
2789 void isom_remove_chan( isom_chan_t *chan )
2791 if( !chan )
2792 return;
2793 if( chan->channelDescriptions )
2794 free( chan->channelDescriptions );
2795 isom_remove_box( chan, isom_audio_entry_t );
2798 void isom_remove_sample_description( isom_sample_entry_t *sample )
2800 if( !sample )
2801 return;
2802 switch( sample->type )
2804 case ISOM_CODEC_TYPE_AVC1_VIDEO :
2805 case ISOM_CODEC_TYPE_AVC2_VIDEO :
2806 case ISOM_CODEC_TYPE_AVCP_VIDEO :
2807 case ISOM_CODEC_TYPE_SVC1_VIDEO :
2808 case ISOM_CODEC_TYPE_MVC1_VIDEO :
2809 case ISOM_CODEC_TYPE_MVC2_VIDEO :
2810 case ISOM_CODEC_TYPE_MP4V_VIDEO :
2811 case ISOM_CODEC_TYPE_DRAC_VIDEO :
2812 case ISOM_CODEC_TYPE_ENCV_VIDEO :
2813 case ISOM_CODEC_TYPE_MJP2_VIDEO :
2814 case ISOM_CODEC_TYPE_S263_VIDEO :
2815 case ISOM_CODEC_TYPE_VC_1_VIDEO :
2817 isom_visual_entry_t *visual = (isom_visual_entry_t *)sample;
2818 isom_remove_visual_extensions( (isom_visual_entry_t *)visual );
2819 free( visual );
2820 break;
2822 case ISOM_CODEC_TYPE_MP4A_AUDIO :
2823 case ISOM_CODEC_TYPE_AC_3_AUDIO :
2824 case ISOM_CODEC_TYPE_ALAC_AUDIO :
2825 case ISOM_CODEC_TYPE_SAMR_AUDIO :
2826 case ISOM_CODEC_TYPE_SAWB_AUDIO :
2827 case QT_CODEC_TYPE_23NI_AUDIO :
2828 case QT_CODEC_TYPE_NONE_AUDIO :
2829 case QT_CODEC_TYPE_LPCM_AUDIO :
2830 case QT_CODEC_TYPE_RAW_AUDIO :
2831 case QT_CODEC_TYPE_SOWT_AUDIO :
2832 case QT_CODEC_TYPE_TWOS_AUDIO :
2833 case QT_CODEC_TYPE_FL32_AUDIO :
2834 case QT_CODEC_TYPE_FL64_AUDIO :
2835 case QT_CODEC_TYPE_IN24_AUDIO :
2836 case QT_CODEC_TYPE_IN32_AUDIO :
2837 case QT_CODEC_TYPE_NOT_SPECIFIED :
2838 case ISOM_CODEC_TYPE_DRA1_AUDIO :
2839 case ISOM_CODEC_TYPE_DTSC_AUDIO :
2840 case ISOM_CODEC_TYPE_DTSH_AUDIO :
2841 case ISOM_CODEC_TYPE_DTSL_AUDIO :
2842 case ISOM_CODEC_TYPE_EC_3_AUDIO :
2843 case ISOM_CODEC_TYPE_ENCA_AUDIO :
2844 case ISOM_CODEC_TYPE_G719_AUDIO :
2845 case ISOM_CODEC_TYPE_G726_AUDIO :
2846 case ISOM_CODEC_TYPE_M4AE_AUDIO :
2847 case ISOM_CODEC_TYPE_MLPA_AUDIO :
2848 //case ISOM_CODEC_TYPE_RAW_AUDIO :
2849 case ISOM_CODEC_TYPE_SAWP_AUDIO :
2850 case ISOM_CODEC_TYPE_SEVC_AUDIO :
2851 case ISOM_CODEC_TYPE_SQCP_AUDIO :
2852 case ISOM_CODEC_TYPE_SSMV_AUDIO :
2853 //case ISOM_CODEC_TYPE_TWOS_AUDIO :
2855 isom_audio_entry_t *audio = (isom_audio_entry_t *)sample;
2856 isom_remove_esds( audio->esds );
2857 isom_remove_wave( audio->wave );
2858 isom_remove_chan( audio->chan );
2859 if( audio->exdata )
2860 free( audio->exdata );
2861 free( audio );
2862 break;
2864 case ISOM_CODEC_TYPE_FDP_HINT :
2865 case ISOM_CODEC_TYPE_M2TS_HINT :
2866 case ISOM_CODEC_TYPE_PM2T_HINT :
2867 case ISOM_CODEC_TYPE_PRTP_HINT :
2868 case ISOM_CODEC_TYPE_RM2T_HINT :
2869 case ISOM_CODEC_TYPE_RRTP_HINT :
2870 case ISOM_CODEC_TYPE_RSRP_HINT :
2871 case ISOM_CODEC_TYPE_RTP_HINT :
2872 case ISOM_CODEC_TYPE_SM2T_HINT :
2873 case ISOM_CODEC_TYPE_SRTP_HINT :
2875 isom_hint_entry_t *hint = (isom_hint_entry_t *)sample;
2876 if( hint->data )
2877 free( hint->data );
2878 free( hint );
2879 break;
2881 case ISOM_CODEC_TYPE_IXSE_META :
2882 case ISOM_CODEC_TYPE_METT_META :
2883 case ISOM_CODEC_TYPE_METX_META :
2884 case ISOM_CODEC_TYPE_MLIX_META :
2885 case ISOM_CODEC_TYPE_OKSD_META :
2886 case ISOM_CODEC_TYPE_SVCM_META :
2887 //case ISOM_CODEC_TYPE_TEXT_META :
2888 case ISOM_CODEC_TYPE_URIM_META :
2889 case ISOM_CODEC_TYPE_XML_META :
2891 isom_metadata_entry_t *metadata = (isom_metadata_entry_t *)sample;
2892 free( metadata );
2893 break;
2895 case ISOM_CODEC_TYPE_TX3G_TEXT :
2897 isom_tx3g_entry_t *tx3g = (isom_tx3g_entry_t *)sample;
2898 if( tx3g->ftab )
2899 isom_remove_ftab( tx3g->ftab );
2900 free( tx3g );
2901 break;
2903 case QT_CODEC_TYPE_TEXT_TEXT :
2905 isom_text_entry_t *text = (isom_text_entry_t *)sample;
2906 if( text->font_name )
2907 free( text->font_name );
2908 free( text );
2909 break;
2911 case ISOM_CODEC_TYPE_MP4S_SYSTEM :
2913 isom_mp4s_entry_t *mp4s = (isom_mp4s_entry_t *)sample;
2914 isom_remove_esds( mp4s->esds );
2915 free( mp4s );
2916 break;
2918 default :
2919 break;
2923 static void isom_remove_stsd( isom_stsd_t *stsd )
2925 if( !stsd )
2926 return;
2927 lsmash_remove_list( stsd->list, isom_remove_sample_description );
2928 isom_remove_box( stsd, isom_stbl_t );
2931 static void isom_remove_stts( isom_stts_t *stts )
2933 if( !stts )
2934 return;
2935 lsmash_remove_list( stts->list, NULL );
2936 isom_remove_box( stts, isom_stbl_t );
2939 static void isom_remove_ctts( isom_ctts_t *ctts )
2941 if( !ctts )
2942 return;
2943 lsmash_remove_list( ctts->list, NULL );
2944 isom_remove_box( ctts, isom_stbl_t );
2947 static void isom_remove_cslg( isom_cslg_t *cslg )
2949 if( !cslg )
2950 return;
2951 isom_remove_box( cslg, isom_stbl_t );
2954 static void isom_remove_stsc( isom_stsc_t *stsc )
2956 if( !stsc )
2957 return;
2958 lsmash_remove_list( stsc->list, NULL );
2959 isom_remove_box( stsc, isom_stbl_t );
2962 static void isom_remove_stsz( isom_stsz_t *stsz )
2964 if( !stsz )
2965 return;
2966 lsmash_remove_list( stsz->list, NULL );
2967 isom_remove_box( stsz, isom_stbl_t );
2970 static void isom_remove_stss( isom_stss_t *stss )
2972 if( !stss )
2973 return;
2974 lsmash_remove_list( stss->list, NULL );
2975 isom_remove_box( stss, isom_stbl_t );
2978 static void isom_remove_stps( isom_stps_t *stps )
2980 if( !stps )
2981 return;
2982 lsmash_remove_list( stps->list, NULL );
2983 isom_remove_box( stps, isom_stbl_t );
2986 static void isom_remove_sdtp( isom_sdtp_t *sdtp )
2988 if( !sdtp )
2989 return;
2990 lsmash_remove_list( sdtp->list, NULL );
2991 isom_remove_box( sdtp, isom_stbl_t );
2994 static void isom_remove_stco( isom_stco_t *stco )
2996 if( !stco )
2997 return;
2998 lsmash_remove_list( stco->list, NULL );
2999 isom_remove_box( stco, isom_stbl_t );
3002 static void isom_remove_sgpd( isom_sgpd_entry_t *sgpd )
3004 if( !sgpd )
3005 return;
3006 lsmash_remove_list( sgpd->list, NULL );
3007 free( sgpd );
3010 static void isom_remove_sbgp( isom_sbgp_entry_t *sbgp )
3012 if( !sbgp )
3013 return;
3014 lsmash_remove_list( sbgp->list, NULL );
3015 free( sbgp );
3018 static void isom_remove_stbl( isom_stbl_t *stbl )
3020 if( !stbl )
3021 return;
3022 isom_remove_stsd( stbl->stsd );
3023 isom_remove_stts( stbl->stts );
3024 isom_remove_ctts( stbl->ctts );
3025 isom_remove_cslg( stbl->cslg );
3026 isom_remove_stsc( stbl->stsc );
3027 isom_remove_stsz( stbl->stsz );
3028 isom_remove_stss( stbl->stss );
3029 isom_remove_stps( stbl->stps );
3030 isom_remove_sdtp( stbl->sdtp );
3031 isom_remove_stco( stbl->stco );
3032 lsmash_remove_list( stbl->sgpd_list, isom_remove_sgpd );
3033 lsmash_remove_list( stbl->sbgp_list, isom_remove_sbgp );
3034 isom_remove_box( stbl, isom_minf_t );
3037 static void isom_remove_dref( isom_dref_t *dref )
3039 if( !dref )
3040 return;
3041 if( !dref->list )
3043 free( dref );
3044 return;
3046 for( lsmash_entry_t *entry = dref->list->head; entry; )
3048 isom_dref_entry_t *data = (isom_dref_entry_t *)entry->data;
3049 if( data )
3051 if( data->name )
3052 free( data->name );
3053 if( data->location )
3054 free( data->location );
3055 free( data );
3057 lsmash_entry_t *next = entry->next;
3058 free( entry );
3059 entry = next;
3061 free( dref->list );
3062 isom_remove_box( dref, isom_dinf_t );
3065 static void isom_remove_dinf( isom_dinf_t *dinf )
3067 if( !dinf )
3068 return;
3069 isom_remove_dref( dinf->dref );
3070 isom_remove_box( dinf, isom_minf_t );
3073 static void isom_remove_minf( isom_minf_t *minf )
3075 if( !minf )
3076 return;
3077 isom_remove_vmhd( minf->vmhd );
3078 isom_remove_smhd( minf->smhd );
3079 isom_remove_hmhd( minf->hmhd );
3080 isom_remove_nmhd( minf->nmhd );
3081 isom_remove_gmhd( minf->gmhd );
3082 isom_remove_hdlr( minf->hdlr );
3083 isom_remove_dinf( minf->dinf );
3084 isom_remove_stbl( minf->stbl );
3085 isom_remove_box( minf, isom_mdia_t );
3088 static void isom_remove_mdia( isom_mdia_t *mdia )
3090 if( !mdia )
3091 return;
3092 isom_remove_mdhd( mdia->mdhd );
3093 isom_remove_minf( mdia->minf );
3094 isom_remove_hdlr( mdia->hdlr );
3095 isom_remove_box( mdia, isom_trak_entry_t );
3098 static void isom_remove_chpl( isom_chpl_t *chpl )
3100 if( !chpl )
3101 return;
3102 if( !chpl->list )
3104 free( chpl );
3105 return;
3107 for( lsmash_entry_t *entry = chpl->list->head; entry; )
3109 isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data;
3110 if( data )
3112 if( data->chapter_name )
3113 free( data->chapter_name );
3114 free( data );
3116 lsmash_entry_t *next = entry->next;
3117 free( entry );
3118 entry = next;
3120 free( chpl->list );
3121 isom_remove_box( chpl, isom_udta_t );
3124 static void isom_remove_udta( isom_udta_t *udta )
3126 if( !udta )
3127 return;
3128 isom_remove_chpl( udta->chpl );
3129 if( udta->parent )
3131 if( udta->parent->type == ISOM_BOX_TYPE_MOOV )
3132 isom_remove_box( udta, isom_moov_t );
3133 else if( udta->parent->type == ISOM_BOX_TYPE_TRAK )
3134 isom_remove_box( udta, isom_trak_entry_t );
3135 else
3136 assert( 0 );
3137 return;
3139 free( udta );
3142 static void isom_remove_mean( isom_mean_t *mean )
3144 if( !mean )
3145 return;
3146 if( mean->meaning_string )
3147 free( mean->meaning_string );
3148 isom_remove_box( mean, isom_metaitem_t );
3151 static void isom_remove_name( isom_name_t *name )
3153 if( !name )
3154 return;
3155 if( name->name )
3156 free( name->name );
3157 isom_remove_box( name, isom_metaitem_t );
3160 static void isom_remove_data( isom_data_t *data )
3162 if( !data )
3163 return;
3164 if( data->value )
3165 free( data->value );
3166 isom_remove_box( data, isom_metaitem_t );
3169 static void isom_remove_metaitem( isom_metaitem_t *metaitem )
3171 if( !metaitem )
3172 return;
3173 isom_remove_mean( metaitem->mean );
3174 isom_remove_name( metaitem->name );
3175 isom_remove_data( metaitem->data );
3176 free( metaitem );
3179 static void isom_remove_ilst( isom_ilst_t *ilst )
3181 if( !ilst )
3182 return;
3183 lsmash_remove_list( ilst->item_list, isom_remove_metaitem );
3184 isom_remove_box( ilst, isom_meta_t );
3187 static void isom_remove_meta( isom_meta_t *meta )
3189 if( !meta )
3190 return;
3191 isom_remove_hdlr( meta->hdlr );
3192 isom_remove_dinf( meta->dinf );
3193 isom_remove_ilst( meta->ilst );
3194 if( meta->parent )
3196 if( !meta->parent->type )
3197 isom_remove_box( meta, lsmash_root_t );
3198 else if( meta->parent->type == ISOM_BOX_TYPE_MOOV )
3199 isom_remove_box( meta, isom_moov_t );
3200 else if( meta->parent->type == ISOM_BOX_TYPE_TRAK )
3201 isom_remove_box( meta, isom_trak_entry_t );
3202 else if( meta->parent->type == ISOM_BOX_TYPE_UDTA )
3203 isom_remove_box( meta, isom_udta_t );
3204 else
3205 assert( 0 );
3206 return;
3208 free( meta );
3211 static void isom_remove_sample_pool( isom_sample_pool_t *pool );
3213 static void isom_remove_trak( isom_trak_entry_t *trak )
3215 if( !trak )
3216 return;
3217 isom_remove_tkhd( trak->tkhd );
3218 isom_remove_tapt( trak->tapt );
3219 isom_remove_edts( trak->edts );
3220 isom_remove_tref( trak->tref );
3221 isom_remove_mdia( trak->mdia );
3222 isom_remove_udta( trak->udta );
3223 isom_remove_meta( trak->meta );
3224 if( trak->cache )
3226 isom_remove_sample_pool( trak->cache->chunk.pool );
3227 lsmash_remove_list( trak->cache->roll.pool, NULL );
3228 if( trak->cache->rap )
3229 free( trak->cache->rap );
3230 free( trak->cache );
3232 free( trak ); /* Note: the list that contains this trak still has the address of the entry. */
3235 static void isom_remove_iods( isom_iods_t *iods )
3237 if( !iods )
3238 return;
3239 mp4sys_remove_ObjectDescriptor( iods->OD );
3240 isom_remove_box( iods, isom_moov_t );
3243 static void isom_remove_mehd( isom_mehd_t *mehd )
3245 if( !mehd )
3246 return;
3247 isom_remove_box( mehd, isom_mvex_t );
3250 static void isom_remove_mvex( isom_mvex_t *mvex )
3252 if( !mvex )
3253 return;
3254 isom_remove_mehd( mvex->mehd );
3255 lsmash_remove_list( mvex->trex_list, NULL );
3256 isom_remove_box( mvex, isom_moov_t );
3259 static void isom_remove_moov( lsmash_root_t *root )
3261 if( !root || !root->moov )
3262 return;
3263 isom_moov_t *moov = root->moov;
3264 if( moov->mvhd )
3265 free( moov->mvhd );
3266 isom_remove_iods( moov->iods );
3267 lsmash_remove_list( moov->trak_list, isom_remove_trak );
3268 isom_remove_udta( moov->udta );
3269 isom_remove_meta( moov->meta );
3270 isom_remove_mvex( moov->mvex );
3271 free( moov );
3272 root->moov = NULL;
3275 static void isom_remove_mfhd( isom_mfhd_t *mfhd )
3277 if( !mfhd )
3278 return;
3279 isom_remove_box( mfhd, isom_moof_entry_t );
3282 static void isom_remove_tfhd( isom_tfhd_t *tfhd )
3284 if( !tfhd )
3285 return;
3286 isom_remove_box( tfhd, isom_traf_entry_t );
3289 static void isom_remove_trun( isom_trun_entry_t *trun )
3291 if( !trun )
3292 return;
3293 lsmash_remove_list( trun->optional, NULL );
3294 free( trun ); /* Note: the list that contains this trun still has the address of the entry. */
3297 static void isom_remove_traf( isom_traf_entry_t *traf )
3299 if( !traf )
3300 return;
3301 isom_remove_tfhd( traf->tfhd );
3302 lsmash_remove_list( traf->trun_list, isom_remove_trun );
3303 free( traf ); /* Note: the list that contains this traf still has the address of the entry. */
3306 static void isom_remove_moof( isom_moof_entry_t *moof )
3308 if( !moof )
3309 return;
3310 isom_remove_mfhd( moof->mfhd );
3311 lsmash_remove_list( moof->traf_list, isom_remove_traf );
3312 free( moof );
3315 static void isom_remove_mdat( isom_mdat_t *mdat )
3317 if( !mdat )
3318 return;
3319 isom_remove_box( mdat, lsmash_root_t );
3322 static void isom_remove_free( isom_free_t *skip )
3324 if( !skip )
3325 return;
3326 if( skip->data )
3327 free( skip->data );
3328 lsmash_root_t *root = (lsmash_root_t *)skip->parent;
3329 free( skip );
3330 root->free = NULL;
3333 static void isom_remove_tfra( isom_tfra_entry_t *tfra )
3335 if( !tfra )
3336 return;
3337 lsmash_remove_list( tfra->list, NULL );
3338 free( tfra );
3341 static void isom_remove_mfro( isom_mfro_t *mfro )
3343 if( !mfro )
3344 return;
3345 isom_remove_box( mfro, isom_mfra_t );
3348 static void isom_remove_mfra( isom_mfra_t *mfra )
3350 if( !mfra )
3351 return;
3352 lsmash_remove_list( mfra->tfra_list, isom_remove_tfra );
3353 isom_remove_mfro( mfra->mfro );
3354 isom_remove_box( mfra, lsmash_root_t );
3357 /* Box writers */
3358 static int isom_write_tkhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3360 isom_tkhd_t *tkhd = trak->tkhd;
3361 if( !tkhd )
3362 return -1;
3363 isom_bs_put_box_common( bs, tkhd );
3364 if( tkhd->version )
3366 lsmash_bs_put_be64( bs, tkhd->creation_time );
3367 lsmash_bs_put_be64( bs, tkhd->modification_time );
3368 lsmash_bs_put_be32( bs, tkhd->track_ID );
3369 lsmash_bs_put_be32( bs, tkhd->reserved1 );
3370 lsmash_bs_put_be64( bs, tkhd->duration );
3372 else
3374 lsmash_bs_put_be32( bs, (uint32_t)tkhd->creation_time );
3375 lsmash_bs_put_be32( bs, (uint32_t)tkhd->modification_time );
3376 lsmash_bs_put_be32( bs, tkhd->track_ID );
3377 lsmash_bs_put_be32( bs, tkhd->reserved1 );
3378 lsmash_bs_put_be32( bs, (uint32_t)tkhd->duration );
3380 lsmash_bs_put_be32( bs, tkhd->reserved2[0] );
3381 lsmash_bs_put_be32( bs, tkhd->reserved2[1] );
3382 lsmash_bs_put_be16( bs, tkhd->layer );
3383 lsmash_bs_put_be16( bs, tkhd->alternate_group );
3384 lsmash_bs_put_be16( bs, tkhd->volume );
3385 lsmash_bs_put_be16( bs, tkhd->reserved3 );
3386 for( uint32_t i = 0; i < 9; i++ )
3387 lsmash_bs_put_be32( bs, tkhd->matrix[i] );
3388 lsmash_bs_put_be32( bs, tkhd->width );
3389 lsmash_bs_put_be32( bs, tkhd->height );
3390 return lsmash_bs_write_data( bs );
3393 static int isom_write_clef( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3395 isom_clef_t *clef = trak->tapt->clef;
3396 if( !clef )
3397 return 0;
3398 isom_bs_put_box_common( bs, clef );
3399 lsmash_bs_put_be32( bs, clef->width );
3400 lsmash_bs_put_be32( bs, clef->height );
3401 return lsmash_bs_write_data( bs );
3404 static int isom_write_prof( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3406 isom_prof_t *prof = trak->tapt->prof;
3407 if( !prof )
3408 return 0;
3409 isom_bs_put_box_common( bs, prof );
3410 lsmash_bs_put_be32( bs, prof->width );
3411 lsmash_bs_put_be32( bs, prof->height );
3412 return lsmash_bs_write_data( bs );
3415 static int isom_write_enof( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3417 isom_enof_t *enof = trak->tapt->enof;
3418 if( !enof )
3419 return 0;
3420 isom_bs_put_box_common( bs, enof );
3421 lsmash_bs_put_be32( bs, enof->width );
3422 lsmash_bs_put_be32( bs, enof->height );
3423 return lsmash_bs_write_data( bs );
3426 static int isom_write_tapt( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3428 isom_tapt_t *tapt = trak->tapt;
3429 if( !tapt )
3430 return 0;
3431 isom_bs_put_box_common( bs, tapt );
3432 if( lsmash_bs_write_data( bs ) )
3433 return -1;
3434 if( isom_write_clef( bs, trak )
3435 || isom_write_prof( bs, trak )
3436 || isom_write_enof( bs, trak ) )
3437 return -1;
3438 return 0;
3441 static int isom_write_elst( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3443 isom_elst_t *elst = trak->edts->elst;
3444 if( !elst )
3445 return -1;
3446 if( !elst->list->entry_count )
3447 return 0;
3448 if( elst->root->fragment && elst->root->bs->stream != stdout )
3449 elst->pos = elst->root->bs->written; /* Remember to rewrite entries. */
3450 isom_bs_put_box_common( bs, elst );
3451 lsmash_bs_put_be32( bs, elst->list->entry_count );
3452 for( lsmash_entry_t *entry = elst->list->head; entry; entry = entry->next )
3454 isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data;
3455 if( !data )
3456 return -1;
3457 if( elst->version )
3459 lsmash_bs_put_be64( bs, data->segment_duration );
3460 lsmash_bs_put_be64( bs, data->media_time );
3462 else
3464 lsmash_bs_put_be32( bs, (uint32_t)data->segment_duration );
3465 lsmash_bs_put_be32( bs, (uint32_t)data->media_time );
3467 lsmash_bs_put_be32( bs, data->media_rate );
3469 return lsmash_bs_write_data( bs );
3472 static int isom_write_edts( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3474 isom_edts_t *edts = trak->edts;
3475 if( !edts )
3476 return 0;
3477 isom_bs_put_box_common( bs, edts );
3478 if( lsmash_bs_write_data( bs ) )
3479 return -1;
3480 return isom_write_elst( bs, trak );
3483 static int isom_write_tref( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3485 isom_tref_t *tref = trak->tref;
3486 if( !tref )
3487 return 0;
3488 isom_bs_put_box_common( bs, tref );
3489 if( tref->ref_list )
3490 for( lsmash_entry_t *entry = tref->ref_list->head; entry; entry = entry->next )
3492 isom_tref_type_t *ref = (isom_tref_type_t *)entry->data;
3493 if( !ref )
3494 return -1;
3495 isom_bs_put_box_common( bs, ref );
3496 for( uint32_t i = 0; i < ref->ref_count; i++ )
3497 lsmash_bs_put_be32( bs, ref->track_ID[i] );
3499 return lsmash_bs_write_data( bs );
3502 static int isom_write_mdhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3504 isom_mdhd_t *mdhd = trak->mdia->mdhd;
3505 if( !mdhd )
3506 return -1;
3507 isom_bs_put_box_common( bs, mdhd );
3508 if( mdhd->version )
3510 lsmash_bs_put_be64( bs, mdhd->creation_time );
3511 lsmash_bs_put_be64( bs, mdhd->modification_time );
3512 lsmash_bs_put_be32( bs, mdhd->timescale );
3513 lsmash_bs_put_be64( bs, mdhd->duration );
3515 else
3517 lsmash_bs_put_be32( bs, (uint32_t)mdhd->creation_time );
3518 lsmash_bs_put_be32( bs, (uint32_t)mdhd->modification_time );
3519 lsmash_bs_put_be32( bs, mdhd->timescale );
3520 lsmash_bs_put_be32( bs, (uint32_t)mdhd->duration );
3522 lsmash_bs_put_be16( bs, mdhd->language );
3523 lsmash_bs_put_be16( bs, mdhd->quality );
3524 return lsmash_bs_write_data( bs );
3527 static int isom_write_hdlr( lsmash_bs_t *bs, isom_hdlr_t *hdlr, uint32_t parent_type )
3529 if( !hdlr )
3530 return parent_type == ISOM_BOX_TYPE_MINF ? 0 : -1;
3531 isom_bs_put_box_common( bs, hdlr );
3532 lsmash_bs_put_be32( bs, hdlr->componentType );
3533 lsmash_bs_put_be32( bs, hdlr->componentSubtype );
3534 lsmash_bs_put_be32( bs, hdlr->componentManufacturer );
3535 lsmash_bs_put_be32( bs, hdlr->componentFlags );
3536 lsmash_bs_put_be32( bs, hdlr->componentFlagsMask );
3537 lsmash_bs_put_bytes( bs, hdlr->componentName, hdlr->componentName_length );
3538 return lsmash_bs_write_data( bs );
3541 static int isom_write_vmhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3543 isom_vmhd_t *vmhd = trak->mdia->minf->vmhd;
3544 if( !vmhd )
3545 return -1;
3546 isom_bs_put_box_common( bs, vmhd );
3547 lsmash_bs_put_be16( bs, vmhd->graphicsmode );
3548 for( uint32_t i = 0; i < 3; i++ )
3549 lsmash_bs_put_be16( bs, vmhd->opcolor[i] );
3550 return lsmash_bs_write_data( bs );
3553 static int isom_write_smhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3555 isom_smhd_t *smhd = trak->mdia->minf->smhd;
3556 if( !smhd )
3557 return -1;
3558 isom_bs_put_box_common( bs, smhd );
3559 lsmash_bs_put_be16( bs, smhd->balance );
3560 lsmash_bs_put_be16( bs, smhd->reserved );
3561 return lsmash_bs_write_data( bs );
3564 static int isom_write_hmhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3566 isom_hmhd_t *hmhd = trak->mdia->minf->hmhd;
3567 if( !hmhd )
3568 return -1;
3569 isom_bs_put_box_common( bs, hmhd );
3570 lsmash_bs_put_be16( bs, hmhd->maxPDUsize );
3571 lsmash_bs_put_be16( bs, hmhd->avgPDUsize );
3572 lsmash_bs_put_be32( bs, hmhd->maxbitrate );
3573 lsmash_bs_put_be32( bs, hmhd->avgbitrate );
3574 lsmash_bs_put_be32( bs, hmhd->reserved );
3575 return lsmash_bs_write_data( bs );
3578 static int isom_write_nmhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3580 isom_nmhd_t *nmhd = trak->mdia->minf->nmhd;
3581 if( !nmhd )
3582 return -1;
3583 isom_bs_put_box_common( bs, nmhd );
3584 return lsmash_bs_write_data( bs );
3587 static int isom_write_gmin( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3589 isom_gmin_t *gmin = trak->mdia->minf->gmhd->gmin;
3590 if( !gmin )
3591 return -1;
3592 isom_bs_put_box_common( bs, gmin );
3593 lsmash_bs_put_be16( bs, gmin->graphicsmode );
3594 for( uint32_t i = 0; i < 3; i++ )
3595 lsmash_bs_put_be16( bs, gmin->opcolor[i] );
3596 lsmash_bs_put_be16( bs, gmin->balance );
3597 lsmash_bs_put_be16( bs, gmin->reserved );
3598 return lsmash_bs_write_data( bs );
3601 static int isom_write_text( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3603 isom_text_t *text = trak->mdia->minf->gmhd->text;
3604 if( !text )
3605 return -1;
3606 isom_bs_put_box_common( bs, text );
3607 for( uint32_t i = 0; i < 9; i++ )
3608 lsmash_bs_put_be32( bs, text->matrix[i] );
3609 return lsmash_bs_write_data( bs );
3612 static int isom_write_gmhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3614 isom_gmhd_t *gmhd = trak->mdia->minf->gmhd;
3615 if( !gmhd )
3616 return -1;
3617 isom_bs_put_box_common( bs, gmhd );
3618 if( isom_write_gmin( bs, trak ) ||
3619 isom_write_text( bs, trak ) )
3620 return -1;
3621 return 0;
3624 static int isom_write_dref( lsmash_bs_t *bs, isom_dref_t *dref )
3626 if( !dref || !dref->list )
3627 return -1;
3628 isom_bs_put_box_common( bs, dref );
3629 lsmash_bs_put_be32( bs, dref->list->entry_count );
3630 for( lsmash_entry_t *entry = dref->list->head; entry; entry = entry->next )
3632 isom_dref_entry_t *data = (isom_dref_entry_t *)entry->data;
3633 if( !data )
3634 return -1;
3635 isom_bs_put_box_common( bs, data );
3636 if( data->type == ISOM_BOX_TYPE_URN )
3637 lsmash_bs_put_bytes( bs, data->name, data->name_length );
3638 lsmash_bs_put_bytes( bs, data->location, data->location_length );
3640 return lsmash_bs_write_data( bs );
3643 static int isom_write_dinf( lsmash_bs_t *bs, isom_dinf_t *dinf, uint32_t parent_type )
3645 if( !dinf )
3646 return parent_type == ISOM_BOX_TYPE_MINF ? -1 : 0;
3647 isom_bs_put_box_common( bs, dinf );
3648 if( lsmash_bs_write_data( bs ) )
3649 return -1;
3650 return isom_write_dref( bs, dinf->dref );
3653 static int isom_write_pasp( lsmash_bs_t *bs, isom_pasp_t *pasp )
3655 if( !pasp )
3656 return 0;
3657 isom_bs_put_box_common( bs, pasp );
3658 lsmash_bs_put_be32( bs, pasp->hSpacing );
3659 lsmash_bs_put_be32( bs, pasp->vSpacing );
3660 return lsmash_bs_write_data( bs );
3663 static int isom_write_clap( lsmash_bs_t *bs, isom_clap_t *clap )
3665 if( !clap )
3666 return 0;
3667 isom_bs_put_box_common( bs, clap );
3668 lsmash_bs_put_be32( bs, clap->cleanApertureWidthN );
3669 lsmash_bs_put_be32( bs, clap->cleanApertureWidthD );
3670 lsmash_bs_put_be32( bs, clap->cleanApertureHeightN );
3671 lsmash_bs_put_be32( bs, clap->cleanApertureHeightD );
3672 lsmash_bs_put_be32( bs, clap->horizOffN );
3673 lsmash_bs_put_be32( bs, clap->horizOffD );
3674 lsmash_bs_put_be32( bs, clap->vertOffN );
3675 lsmash_bs_put_be32( bs, clap->vertOffD );
3676 return lsmash_bs_write_data( bs );
3679 static int isom_write_colr( lsmash_bs_t *bs, isom_colr_t *colr )
3681 if( !colr || colr->color_parameter_type == QT_COLOR_PARAMETER_TYPE_PROF )
3682 return 0;
3683 isom_bs_put_box_common( bs, colr );
3684 lsmash_bs_put_be32( bs, colr->color_parameter_type );
3685 lsmash_bs_put_be16( bs, colr->primaries_index );
3686 lsmash_bs_put_be16( bs, colr->transfer_function_index );
3687 lsmash_bs_put_be16( bs, colr->matrix_index );
3688 return lsmash_bs_write_data( bs );
3691 static int isom_write_stsl( lsmash_bs_t *bs, isom_stsl_t *stsl )
3693 if( !stsl )
3694 return 0;
3695 isom_bs_put_box_common( bs, stsl );
3696 lsmash_bs_put_byte( bs, stsl->constraint_flag );
3697 lsmash_bs_put_byte( bs, stsl->scale_method );
3698 lsmash_bs_put_be16( bs, stsl->display_center_x );
3699 lsmash_bs_put_be16( bs, stsl->display_center_y );
3700 return lsmash_bs_write_data( bs );
3703 static int isom_write_esds( lsmash_bs_t *bs, isom_esds_t *esds )
3705 if( !esds )
3706 return 0;
3707 isom_bs_put_box_common( bs, esds );
3708 return mp4sys_write_ES_Descriptor( bs, esds->ES );
3711 static int isom_put_ps_entries( lsmash_bs_t *bs, lsmash_entry_list_t *list )
3713 for( lsmash_entry_t *entry = list->head; entry; entry = entry->next )
3715 isom_avcC_ps_entry_t *data = (isom_avcC_ps_entry_t *)entry->data;
3716 if( !data )
3717 return -1;
3718 lsmash_bs_put_be16( bs, data->parameterSetLength );
3719 lsmash_bs_put_bytes( bs, data->parameterSetNALUnit, data->parameterSetLength );
3721 return 0;
3724 static int isom_write_avcC( lsmash_bs_t *bs, isom_avcC_t *avcC )
3726 if( !avcC )
3727 return 0;
3728 if( !avcC->sequenceParameterSets || !avcC->pictureParameterSets )
3729 return -1;
3730 isom_bs_put_box_common( bs, avcC );
3731 lsmash_bs_put_byte( bs, avcC->configurationVersion );
3732 lsmash_bs_put_byte( bs, avcC->AVCProfileIndication );
3733 lsmash_bs_put_byte( bs, avcC->profile_compatibility );
3734 lsmash_bs_put_byte( bs, avcC->AVCLevelIndication );
3735 lsmash_bs_put_byte( bs, avcC->lengthSizeMinusOne | 0xfc ); /* upper 6-bits are reserved as 111111b */
3736 lsmash_bs_put_byte( bs, avcC->numOfSequenceParameterSets | 0xe0 ); /* upper 3-bits are reserved as 111b */
3737 if( isom_put_ps_entries( bs, avcC->sequenceParameterSets ) )
3738 return -1;
3739 lsmash_bs_put_byte( bs, avcC->numOfPictureParameterSets );
3740 if( isom_put_ps_entries( bs, avcC->pictureParameterSets ) )
3741 return -1;
3742 if( ISOM_REQUIRES_AVCC_EXTENSION( avcC->AVCProfileIndication ) )
3744 lsmash_bs_put_byte( bs, avcC->chroma_format | 0xfc ); /* upper 6-bits are reserved as 111111b */
3745 lsmash_bs_put_byte( bs, avcC->bit_depth_luma_minus8 | 0xf8 ); /* upper 5-bits are reserved as 11111b */
3746 lsmash_bs_put_byte( bs, avcC->bit_depth_chroma_minus8 | 0xf8 ); /* upper 5-bits are reserved as 11111b */
3747 lsmash_bs_put_byte( bs, avcC->numOfSequenceParameterSetExt );
3748 if( isom_put_ps_entries( bs, avcC->sequenceParameterSetExt ) )
3749 return -1;
3751 return lsmash_bs_write_data( bs );
3754 static int isom_write_btrt( lsmash_bs_t *bs, isom_btrt_t *btrt )
3756 if( !btrt )
3757 return 0;
3758 isom_bs_put_box_common( bs, btrt );
3759 lsmash_bs_put_be32( bs, btrt->bufferSizeDB );
3760 lsmash_bs_put_be32( bs, btrt->maxBitrate );
3761 lsmash_bs_put_be32( bs, btrt->avgBitrate );
3762 return lsmash_bs_write_data( bs );
3765 static int isom_write_visual_extensions( lsmash_bs_t *bs, isom_visual_entry_t *visual )
3767 if( !visual )
3768 return 0;
3769 if( isom_write_clap( bs, visual->clap )
3770 || isom_write_pasp( bs, visual->pasp )
3771 || isom_write_colr( bs, visual->colr )
3772 || isom_write_stsl( bs, visual->stsl )
3773 || isom_write_esds( bs, visual->esds )
3774 || isom_write_avcC( bs, visual->avcC )
3775 || isom_write_btrt( bs, visual->btrt ) )
3776 return -1;
3777 return 0;
3780 static int isom_write_frma( lsmash_bs_t *bs, isom_frma_t *frma )
3782 if( !frma )
3783 return -1;
3784 isom_bs_put_box_common( bs, frma );
3785 lsmash_bs_put_be32( bs, frma->data_format );
3786 return lsmash_bs_write_data( bs );
3789 static int isom_write_enda( lsmash_bs_t *bs, isom_enda_t *enda )
3791 if( !enda )
3792 return 0;
3793 isom_bs_put_box_common( bs, enda );
3794 lsmash_bs_put_be16( bs, enda->littleEndian );
3795 return lsmash_bs_write_data( bs );
3798 static int isom_write_mp4a( lsmash_bs_t *bs, isom_mp4a_t *mp4a )
3800 if( !mp4a )
3801 return 0;
3802 isom_bs_put_box_common( bs, mp4a );
3803 lsmash_bs_put_be32( bs, mp4a->unknown );
3804 return lsmash_bs_write_data( bs );
3807 static int isom_write_terminator( lsmash_bs_t *bs, isom_terminator_t *terminator )
3809 if( !terminator )
3810 return -1;
3811 isom_bs_put_box_common( bs, terminator );
3812 return lsmash_bs_write_data( bs );
3815 static int isom_write_wave( lsmash_bs_t *bs, isom_wave_t *wave )
3817 if( !wave )
3818 return 0;
3819 isom_bs_put_box_common( bs, wave );
3820 if( lsmash_bs_write_data( bs ) )
3821 return -1;
3822 if( isom_write_frma( bs, wave->frma )
3823 || isom_write_enda( bs, wave->enda )
3824 || isom_write_mp4a( bs, wave->mp4a ) )
3825 return -1;
3826 lsmash_bs_put_bytes( bs, wave->exdata, wave->exdata_length );
3827 if( lsmash_bs_write_data( bs ) )
3828 return -1;
3829 if( isom_write_esds( bs, wave->esds ) )
3830 return -1;
3831 return isom_write_terminator( bs, wave->terminator );
3834 static int isom_write_chan( lsmash_bs_t *bs, isom_chan_t *chan )
3836 if( !chan )
3837 return 0;
3838 isom_bs_put_box_common( bs, chan );
3839 lsmash_bs_put_be32( bs, chan->channelLayoutTag );
3840 lsmash_bs_put_be32( bs, chan->channelBitmap );
3841 lsmash_bs_put_be32( bs, chan->numberChannelDescriptions );
3842 if( chan->channelDescriptions )
3843 for( uint32_t i = 0; i < chan->numberChannelDescriptions; i++ )
3845 isom_channel_description_t *channelDescriptions = (isom_channel_description_t *)(&chan->channelDescriptions[i]);
3846 if( !channelDescriptions )
3847 return -1;
3848 lsmash_bs_put_be32( bs, channelDescriptions->channelLabel );
3849 lsmash_bs_put_be32( bs, channelDescriptions->channelFlags );
3850 lsmash_bs_put_be32( bs, channelDescriptions->coordinates[0] );
3851 lsmash_bs_put_be32( bs, channelDescriptions->coordinates[1] );
3852 lsmash_bs_put_be32( bs, channelDescriptions->coordinates[2] );
3854 return lsmash_bs_write_data( bs );
3857 static int isom_write_audio_extensions( lsmash_bs_t *bs, isom_audio_entry_t *audio )
3859 if( !audio )
3860 return 0;
3861 if( isom_write_esds( bs, audio->esds )
3862 || isom_write_wave( bs, audio->wave )
3863 || isom_write_chan( bs, audio->chan ) )
3864 return -1;
3865 return 0;
3868 static int isom_write_visual_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
3870 isom_visual_entry_t *data = (isom_visual_entry_t *)entry->data;
3871 if( !data )
3872 return -1;
3873 isom_bs_put_box_common( bs, data );
3874 lsmash_bs_put_bytes( bs, data->reserved, 6 );
3875 lsmash_bs_put_be16( bs, data->data_reference_index );
3876 lsmash_bs_put_be16( bs, data->version );
3877 lsmash_bs_put_be16( bs, data->revision_level );
3878 lsmash_bs_put_be32( bs, data->vendor );
3879 lsmash_bs_put_be32( bs, data->temporalQuality );
3880 lsmash_bs_put_be32( bs, data->spatialQuality );
3881 lsmash_bs_put_be16( bs, data->width );
3882 lsmash_bs_put_be16( bs, data->height );
3883 lsmash_bs_put_be32( bs, data->horizresolution );
3884 lsmash_bs_put_be32( bs, data->vertresolution );
3885 lsmash_bs_put_be32( bs, data->dataSize );
3886 lsmash_bs_put_be16( bs, data->frame_count );
3887 lsmash_bs_put_bytes( bs, data->compressorname, 32 );
3888 lsmash_bs_put_be16( bs, data->depth );
3889 lsmash_bs_put_be16( bs, data->color_table_ID );
3890 if( lsmash_bs_write_data( bs ) )
3891 return -1;
3892 return isom_write_visual_extensions( bs, data );
3895 static int isom_write_audio_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
3897 isom_audio_entry_t *data = (isom_audio_entry_t *)entry->data;
3898 if( !data )
3899 return -1;
3900 isom_bs_put_box_common( bs, data );
3901 lsmash_bs_put_bytes( bs, data->reserved, 6 );
3902 lsmash_bs_put_be16( bs, data->data_reference_index );
3903 lsmash_bs_put_be16( bs, data->version );
3904 lsmash_bs_put_be16( bs, data->revision_level );
3905 lsmash_bs_put_be32( bs, data->vendor );
3906 lsmash_bs_put_be16( bs, data->channelcount );
3907 lsmash_bs_put_be16( bs, data->samplesize );
3908 lsmash_bs_put_be16( bs, data->compression_ID );
3909 lsmash_bs_put_be16( bs, data->packet_size );
3910 lsmash_bs_put_be32( bs, data->samplerate );
3911 if( data->version == 1 )
3913 lsmash_bs_put_be32( bs, data->samplesPerPacket );
3914 lsmash_bs_put_be32( bs, data->bytesPerPacket );
3915 lsmash_bs_put_be32( bs, data->bytesPerFrame );
3916 lsmash_bs_put_be32( bs, data->bytesPerSample );
3918 else if( data->version == 2 )
3920 lsmash_bs_put_be32( bs, data->sizeOfStructOnly );
3921 lsmash_bs_put_be64( bs, data->audioSampleRate );
3922 lsmash_bs_put_be32( bs, data->numAudioChannels );
3923 lsmash_bs_put_be32( bs, data->always7F000000 );
3924 lsmash_bs_put_be32( bs, data->constBitsPerChannel );
3925 lsmash_bs_put_be32( bs, data->formatSpecificFlags );
3926 lsmash_bs_put_be32( bs, data->constBytesPerAudioPacket );
3927 lsmash_bs_put_be32( bs, data->constLPCMFramesPerAudioPacket );
3929 lsmash_bs_put_bytes( bs, data->exdata, data->exdata_length );
3930 if( lsmash_bs_write_data( bs ) )
3931 return -1;
3932 return isom_write_audio_extensions( bs, data );
3935 #if 0
3936 static int isom_write_hint_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
3938 isom_hint_entry_t *data = (isom_hint_entry_t *)entry->data;
3939 if( !data )
3940 return -1;
3941 isom_bs_put_box_common( bs, data );
3942 lsmash_bs_put_bytes( bs, data->reserved, 6 );
3943 lsmash_bs_put_be16( bs, data->data_reference_index );
3944 if( data->data && data->data_length )
3945 lsmash_bs_put_bytes( bs, data->data, data->data_length );
3946 return lsmash_bs_write_data( bs );
3949 static int isom_write_metadata_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
3951 isom_metadata_entry_t *data = (isom_metadata_entry_t *)entry->data;
3952 if( !data )
3953 return -1;
3954 isom_bs_put_box_common( bs, data );
3955 lsmash_bs_put_bytes( bs, data->reserved, 6 );
3956 lsmash_bs_put_be16( bs, data->data_reference_index );
3957 return lsmash_bs_write_data( bs );
3959 #endif
3961 static int isom_write_text_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
3963 isom_text_entry_t *data = (isom_text_entry_t *)entry->data;
3964 if( !data )
3965 return -1;
3966 isom_bs_put_box_common( bs, data );
3967 lsmash_bs_put_bytes( bs, data->reserved, 6 );
3968 lsmash_bs_put_be16( bs, data->data_reference_index );
3969 lsmash_bs_put_be32( bs, data->displayFlags );
3970 lsmash_bs_put_be32( bs, data->textJustification );
3971 for( uint32_t i = 0; i < 3; i++ )
3972 lsmash_bs_put_be16( bs, data->bgColor[i] );
3973 lsmash_bs_put_be16( bs, data->top );
3974 lsmash_bs_put_be16( bs, data->left );
3975 lsmash_bs_put_be16( bs, data->bottom );
3976 lsmash_bs_put_be16( bs, data->right );
3977 lsmash_bs_put_be32( bs, data->scrpStartChar );
3978 lsmash_bs_put_be16( bs, data->scrpHeight );
3979 lsmash_bs_put_be16( bs, data->scrpAscent );
3980 lsmash_bs_put_be16( bs, data->scrpFont );
3981 lsmash_bs_put_be16( bs, data->scrpFace );
3982 lsmash_bs_put_be16( bs, data->scrpSize );
3983 for( uint32_t i = 0; i < 3; i++ )
3984 lsmash_bs_put_be16( bs, data->scrpColor[i] );
3985 lsmash_bs_put_byte( bs, data->font_name_length );
3986 if( data->font_name && data->font_name_length )
3987 lsmash_bs_put_bytes( bs, data->font_name, data->font_name_length );
3988 return lsmash_bs_write_data( bs );
3991 static int isom_put_ftab( lsmash_bs_t *bs, isom_ftab_t *ftab )
3993 if( !ftab || !ftab->list )
3994 return -1;
3995 isom_bs_put_box_common( bs, ftab );
3996 lsmash_bs_put_be16( bs, ftab->list->entry_count );
3997 for( lsmash_entry_t *entry = ftab->list->head; entry; entry = entry->next )
3999 isom_font_record_t *data = (isom_font_record_t *)entry->data;
4000 if( !data )
4001 return -1;
4002 lsmash_bs_put_be16( bs, data->font_ID );
4003 lsmash_bs_put_byte( bs, data->font_name_length );
4004 if( data->font_name && data->font_name_length )
4005 lsmash_bs_put_bytes( bs, data->font_name, data->font_name_length );
4007 return 0;
4010 static int isom_write_tx3g_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
4012 isom_tx3g_entry_t *data = (isom_tx3g_entry_t *)entry->data;
4013 if( !data )
4014 return -1;
4015 isom_bs_put_box_common( bs, data );
4016 lsmash_bs_put_bytes( bs, data->reserved, 6 );
4017 lsmash_bs_put_be16( bs, data->data_reference_index );
4018 lsmash_bs_put_be32( bs, data->displayFlags );
4019 lsmash_bs_put_byte( bs, data->horizontal_justification );
4020 lsmash_bs_put_byte( bs, data->vertical_justification );
4021 for( uint32_t i = 0; i < 4; i++ )
4022 lsmash_bs_put_byte( bs, data->background_color_rgba[i] );
4023 lsmash_bs_put_be16( bs, data->top );
4024 lsmash_bs_put_be16( bs, data->left );
4025 lsmash_bs_put_be16( bs, data->bottom );
4026 lsmash_bs_put_be16( bs, data->right );
4027 lsmash_bs_put_be16( bs, data->startChar );
4028 lsmash_bs_put_be16( bs, data->endChar );
4029 lsmash_bs_put_be16( bs, data->font_ID );
4030 lsmash_bs_put_byte( bs, data->face_style_flags );
4031 lsmash_bs_put_byte( bs, data->font_size );
4032 for( uint32_t i = 0; i < 4; i++ )
4033 lsmash_bs_put_byte( bs, data->text_color_rgba[i] );
4034 isom_put_ftab( bs, data->ftab );
4035 return lsmash_bs_write_data( bs );
4038 static int isom_write_stsd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4040 isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd;
4041 if( !stsd || !stsd->list || !stsd->list->head )
4042 return -1;
4043 isom_bs_put_box_common( bs, stsd );
4044 lsmash_bs_put_be32( bs, stsd->list->entry_count );
4045 int ret = -1;
4046 for( lsmash_entry_t *entry = stsd->list->head; entry; entry = entry->next )
4048 isom_sample_entry_t *sample = (isom_sample_entry_t *)entry->data;
4049 if( !sample )
4050 return -1;
4051 switch( sample->type )
4053 case ISOM_CODEC_TYPE_AVC1_VIDEO :
4054 #ifdef LSMASH_DEMUXER_ENABLED
4055 case ISOM_CODEC_TYPE_MP4V_VIDEO :
4056 #endif
4057 #if 0
4058 case ISOM_CODEC_TYPE_AVC2_VIDEO :
4059 case ISOM_CODEC_TYPE_AVCP_VIDEO :
4060 case ISOM_CODEC_TYPE_SVC1_VIDEO :
4061 case ISOM_CODEC_TYPE_MVC1_VIDEO :
4062 case ISOM_CODEC_TYPE_MVC2_VIDEO :
4063 case ISOM_CODEC_TYPE_DRAC_VIDEO :
4064 case ISOM_CODEC_TYPE_ENCV_VIDEO :
4065 case ISOM_CODEC_TYPE_MJP2_VIDEO :
4066 case ISOM_CODEC_TYPE_S263_VIDEO :
4067 case ISOM_CODEC_TYPE_VC_1_VIDEO :
4068 #endif
4069 ret = isom_write_visual_entry( bs, entry );
4070 break;
4071 case ISOM_CODEC_TYPE_MP4A_AUDIO :
4072 case ISOM_CODEC_TYPE_AC_3_AUDIO :
4073 case ISOM_CODEC_TYPE_ALAC_AUDIO :
4074 case ISOM_CODEC_TYPE_SAMR_AUDIO :
4075 case ISOM_CODEC_TYPE_SAWB_AUDIO :
4076 case QT_CODEC_TYPE_23NI_AUDIO :
4077 case QT_CODEC_TYPE_NONE_AUDIO :
4078 case QT_CODEC_TYPE_LPCM_AUDIO :
4079 case QT_CODEC_TYPE_RAW_AUDIO :
4080 case QT_CODEC_TYPE_SOWT_AUDIO :
4081 case QT_CODEC_TYPE_TWOS_AUDIO :
4082 case QT_CODEC_TYPE_FL32_AUDIO :
4083 case QT_CODEC_TYPE_FL64_AUDIO :
4084 case QT_CODEC_TYPE_IN24_AUDIO :
4085 case QT_CODEC_TYPE_IN32_AUDIO :
4086 case QT_CODEC_TYPE_NOT_SPECIFIED :
4087 #ifdef LSMASH_DEMUXER_ENABLED
4088 case ISOM_CODEC_TYPE_EC_3_AUDIO :
4089 #endif
4090 #if 0
4091 case ISOM_CODEC_TYPE_DRA1_AUDIO :
4092 case ISOM_CODEC_TYPE_DTSC_AUDIO :
4093 case ISOM_CODEC_TYPE_DTSH_AUDIO :
4094 case ISOM_CODEC_TYPE_DTSL_AUDIO :
4095 case ISOM_CODEC_TYPE_ENCA_AUDIO :
4096 case ISOM_CODEC_TYPE_G719_AUDIO :
4097 case ISOM_CODEC_TYPE_G726_AUDIO :
4098 case ISOM_CODEC_TYPE_M4AE_AUDIO :
4099 case ISOM_CODEC_TYPE_MLPA_AUDIO :
4100 case ISOM_CODEC_TYPE_RAW_AUDIO :
4101 case ISOM_CODEC_TYPE_SAWP_AUDIO :
4102 case ISOM_CODEC_TYPE_SEVC_AUDIO :
4103 case ISOM_CODEC_TYPE_SQCP_AUDIO :
4104 case ISOM_CODEC_TYPE_SSMV_AUDIO :
4105 case ISOM_CODEC_TYPE_TWOS_AUDIO :
4106 #endif
4107 ret = isom_write_audio_entry( bs, entry );
4108 break;
4109 #if 0
4110 case ISOM_CODEC_TYPE_FDP_HINT :
4111 case ISOM_CODEC_TYPE_M2TS_HINT :
4112 case ISOM_CODEC_TYPE_PM2T_HINT :
4113 case ISOM_CODEC_TYPE_PRTP_HINT :
4114 case ISOM_CODEC_TYPE_RM2T_HINT :
4115 case ISOM_CODEC_TYPE_RRTP_HINT :
4116 case ISOM_CODEC_TYPE_RSRP_HINT :
4117 case ISOM_CODEC_TYPE_RTP_HINT :
4118 case ISOM_CODEC_TYPE_SM2T_HINT :
4119 case ISOM_CODEC_TYPE_SRTP_HINT :
4120 ret = isom_write_hint_entry( bs, entry );
4121 break;
4122 case ISOM_CODEC_TYPE_IXSE_META :
4123 case ISOM_CODEC_TYPE_METT_META :
4124 case ISOM_CODEC_TYPE_METX_META :
4125 case ISOM_CODEC_TYPE_MLIX_META :
4126 case ISOM_CODEC_TYPE_OKSD_META :
4127 case ISOM_CODEC_TYPE_SVCM_META :
4128 case ISOM_CODEC_TYPE_TEXT_META :
4129 case ISOM_CODEC_TYPE_URIM_META :
4130 case ISOM_CODEC_TYPE_XML_META :
4131 ret = isom_write_metadata_entry( bs, entry );
4132 break;
4133 #endif
4134 case ISOM_CODEC_TYPE_TX3G_TEXT :
4135 ret = isom_write_tx3g_entry( bs, entry );
4136 break;
4137 case QT_CODEC_TYPE_TEXT_TEXT :
4138 ret = isom_write_text_entry( bs, entry );
4139 break;
4140 default :
4141 break;
4143 if( ret )
4144 break;
4146 return ret;
4149 static int isom_write_stts( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4151 isom_stts_t *stts = trak->mdia->minf->stbl->stts;
4152 if( !stts || !stts->list )
4153 return -1;
4154 isom_bs_put_box_common( bs, stts );
4155 lsmash_bs_put_be32( bs, stts->list->entry_count );
4156 for( lsmash_entry_t *entry = stts->list->head; entry; entry = entry->next )
4158 isom_stts_entry_t *data = (isom_stts_entry_t *)entry->data;
4159 if( !data )
4160 return -1;
4161 lsmash_bs_put_be32( bs, data->sample_count );
4162 lsmash_bs_put_be32( bs, data->sample_delta );
4164 return lsmash_bs_write_data( bs );
4167 static int isom_write_ctts( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4169 isom_ctts_t *ctts = trak->mdia->minf->stbl->ctts;
4170 if( !ctts )
4171 return 0;
4172 if( !ctts->list )
4173 return -1;
4174 isom_bs_put_box_common( bs, ctts );
4175 lsmash_bs_put_be32( bs, ctts->list->entry_count );
4176 for( lsmash_entry_t *entry = ctts->list->head; entry; entry = entry->next )
4178 isom_ctts_entry_t *data = (isom_ctts_entry_t *)entry->data;
4179 if( !data )
4180 return -1;
4181 lsmash_bs_put_be32( bs, data->sample_count );
4182 lsmash_bs_put_be32( bs, data->sample_offset );
4184 return lsmash_bs_write_data( bs );
4187 static int isom_write_cslg( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4189 isom_cslg_t *cslg = trak->mdia->minf->stbl->cslg;
4190 if( !cslg )
4191 return 0;
4192 isom_bs_put_box_common( bs, cslg );
4193 lsmash_bs_put_be32( bs, cslg->compositionToDTSShift );
4194 lsmash_bs_put_be32( bs, cslg->leastDecodeToDisplayDelta );
4195 lsmash_bs_put_be32( bs, cslg->greatestDecodeToDisplayDelta );
4196 lsmash_bs_put_be32( bs, cslg->compositionStartTime );
4197 lsmash_bs_put_be32( bs, cslg->compositionEndTime );
4198 return lsmash_bs_write_data( bs );
4201 static int isom_write_stsz( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4203 isom_stsz_t *stsz = trak->mdia->minf->stbl->stsz;
4204 if( !stsz )
4205 return -1;
4206 isom_bs_put_box_common( bs, stsz );
4207 lsmash_bs_put_be32( bs, stsz->sample_size );
4208 lsmash_bs_put_be32( bs, stsz->sample_count );
4209 if( stsz->sample_size == 0 && stsz->list )
4210 for( lsmash_entry_t *entry = stsz->list->head; entry; entry = entry->next )
4212 isom_stsz_entry_t *data = (isom_stsz_entry_t *)entry->data;
4213 if( !data )
4214 return -1;
4215 lsmash_bs_put_be32( bs, data->entry_size );
4217 return lsmash_bs_write_data( bs );
4220 static int isom_write_stss( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4222 isom_stss_t *stss = trak->mdia->minf->stbl->stss;
4223 if( !stss )
4224 return 0; /* If the sync sample box is not present, every sample is a random access point. */
4225 if( !stss->list )
4226 return -1;
4227 isom_bs_put_box_common( bs, stss );
4228 lsmash_bs_put_be32( bs, stss->list->entry_count );
4229 for( lsmash_entry_t *entry = stss->list->head; entry; entry = entry->next )
4231 isom_stss_entry_t *data = (isom_stss_entry_t *)entry->data;
4232 if( !data )
4233 return -1;
4234 lsmash_bs_put_be32( bs, data->sample_number );
4236 return lsmash_bs_write_data( bs );
4239 static int isom_write_stps( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4241 isom_stps_t *stps = trak->mdia->minf->stbl->stps;
4242 if( !stps )
4243 return 0;
4244 if( !stps->list )
4245 return -1;
4246 isom_bs_put_box_common( bs, stps );
4247 lsmash_bs_put_be32( bs, stps->list->entry_count );
4248 for( lsmash_entry_t *entry = stps->list->head; entry; entry = entry->next )
4250 isom_stps_entry_t *data = (isom_stps_entry_t *)entry->data;
4251 if( !data )
4252 return -1;
4253 lsmash_bs_put_be32( bs, data->sample_number );
4255 return lsmash_bs_write_data( bs );
4258 static int isom_write_sdtp( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4260 isom_sdtp_t *sdtp = trak->mdia->minf->stbl->sdtp;
4261 if( !sdtp )
4262 return 0;
4263 if( !sdtp->list )
4264 return -1;
4265 isom_bs_put_box_common( bs, sdtp );
4266 for( lsmash_entry_t *entry = sdtp->list->head; entry; entry = entry->next )
4268 isom_sdtp_entry_t *data = (isom_sdtp_entry_t *)entry->data;
4269 if( !data )
4270 return -1;
4271 uint8_t temp = (data->is_leading << 6)
4272 | (data->sample_depends_on << 4)
4273 | (data->sample_is_depended_on << 2)
4274 | data->sample_has_redundancy;
4275 lsmash_bs_put_byte( bs, temp );
4277 return lsmash_bs_write_data( bs );
4280 static int isom_write_stsc( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4282 isom_stsc_t *stsc = trak->mdia->minf->stbl->stsc;
4283 if( !stsc || !stsc->list )
4284 return -1;
4285 isom_bs_put_box_common( bs, stsc );
4286 lsmash_bs_put_be32( bs, stsc->list->entry_count );
4287 for( lsmash_entry_t *entry = stsc->list->head; entry; entry = entry->next )
4289 isom_stsc_entry_t *data = (isom_stsc_entry_t *)entry->data;
4290 if( !data )
4291 return -1;
4292 lsmash_bs_put_be32( bs, data->first_chunk );
4293 lsmash_bs_put_be32( bs, data->samples_per_chunk );
4294 lsmash_bs_put_be32( bs, data->sample_description_index );
4296 return lsmash_bs_write_data( bs );
4299 static int isom_write_co64( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4301 isom_stco_t *co64 = trak->mdia->minf->stbl->stco;
4302 if( !co64 || !co64->list )
4303 return -1;
4304 isom_bs_put_box_common( bs, co64 );
4305 lsmash_bs_put_be32( bs, co64->list->entry_count );
4306 for( lsmash_entry_t *entry = co64->list->head; entry; entry = entry->next )
4308 isom_co64_entry_t *data = (isom_co64_entry_t *)entry->data;
4309 if( !data )
4310 return -1;
4311 lsmash_bs_put_be64( bs, data->chunk_offset );
4313 return lsmash_bs_write_data( bs );
4316 static int isom_write_stco( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4318 isom_stco_t *stco = trak->mdia->minf->stbl->stco;
4319 if( !stco || !stco->list )
4320 return -1;
4321 if( stco->large_presentation )
4322 return isom_write_co64( bs, trak );
4323 isom_bs_put_box_common( bs, stco );
4324 lsmash_bs_put_be32( bs, stco->list->entry_count );
4325 for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next )
4327 isom_stco_entry_t *data = (isom_stco_entry_t *)entry->data;
4328 if( !data )
4329 return -1;
4330 lsmash_bs_put_be32( bs, data->chunk_offset );
4332 return lsmash_bs_write_data( bs );
4335 static int isom_write_sgpd( lsmash_bs_t *bs, isom_trak_entry_t *trak, uint32_t grouping_number )
4337 isom_sgpd_entry_t *sgpd = (isom_sgpd_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->sgpd_list, grouping_number );
4338 if( !sgpd || !sgpd->list )
4339 return -1;
4340 isom_bs_put_box_common( bs, sgpd );
4341 lsmash_bs_put_be32( bs, sgpd->grouping_type );
4342 if( sgpd->version == 1 )
4343 lsmash_bs_put_be32( bs, sgpd->default_length );
4344 lsmash_bs_put_be32( bs, sgpd->list->entry_count );
4345 for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next )
4347 if( !entry->data )
4348 return -1;
4349 switch( sgpd->grouping_type )
4351 case ISOM_GROUP_TYPE_RAP :
4353 isom_rap_entry_t *rap = (isom_rap_entry_t *)entry->data;
4354 uint8_t temp = (rap->num_leading_samples_known << 7)
4355 | rap->num_leading_samples;
4356 lsmash_bs_put_byte( bs, temp );
4357 break;
4359 case ISOM_GROUP_TYPE_ROLL :
4360 lsmash_bs_put_be16( bs, ((isom_roll_entry_t *)entry->data)->roll_distance );
4361 break;
4362 default :
4363 /* We don't consider other grouping types currently. */
4364 // if( sgpd->version == 1 && !sgpd->default_length )
4365 // lsmash_bs_put_be32( bs, ((isom_sgpd_entry_t *)entry->data)->description_length );
4366 break;
4369 return lsmash_bs_write_data( bs );
4372 static int isom_write_sbgp( lsmash_bs_t *bs, isom_trak_entry_t *trak, uint32_t grouping_number )
4374 isom_sbgp_entry_t *sbgp = (isom_sbgp_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->sbgp_list, grouping_number );
4375 if( !sbgp || !sbgp->list )
4376 return -1;
4377 isom_bs_put_box_common( bs, sbgp );
4378 lsmash_bs_put_be32( bs, sbgp->grouping_type );
4379 if( sbgp->version == 1 )
4380 lsmash_bs_put_be32( bs, sbgp->grouping_type_parameter );
4381 lsmash_bs_put_be32( bs, sbgp->list->entry_count );
4382 for( lsmash_entry_t *entry = sbgp->list->head; entry; entry = entry->next )
4384 isom_group_assignment_entry_t *data = (isom_group_assignment_entry_t *)entry->data;
4385 if( !data )
4386 return -1;
4387 lsmash_bs_put_be32( bs, data->sample_count );
4388 lsmash_bs_put_be32( bs, data->group_description_index );
4390 return lsmash_bs_write_data( bs );
4393 static int isom_write_stbl( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4395 isom_stbl_t *stbl = trak->mdia->minf->stbl;
4396 if( !stbl )
4397 return -1;
4398 isom_bs_put_box_common( bs, stbl );
4399 if( lsmash_bs_write_data( bs ) )
4400 return -1;
4401 if( isom_write_stsd( bs, trak )
4402 || isom_write_stts( bs, trak )
4403 || isom_write_ctts( bs, trak )
4404 || isom_write_cslg( bs, trak )
4405 || isom_write_stss( bs, trak )
4406 || isom_write_stps( bs, trak )
4407 || isom_write_sdtp( bs, trak )
4408 || isom_write_stsc( bs, trak )
4409 || isom_write_stsz( bs, trak )
4410 || isom_write_stco( bs, trak ) )
4411 return -1;
4412 if( stbl->sgpd_list )
4413 for( uint32_t i = 1; i <= stbl->sgpd_list->entry_count; i++ )
4414 if( isom_write_sgpd( bs, trak, i ) )
4415 return -1;
4416 if( stbl->sbgp_list )
4417 for( uint32_t i = 1; i <= stbl->sbgp_list->entry_count; i++ )
4418 if( isom_write_sbgp( bs, trak, i ) )
4419 return -1;
4420 return 0;
4423 static int isom_write_minf( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4425 isom_minf_t *minf = trak->mdia->minf;
4426 if( !minf )
4427 return -1;
4428 isom_bs_put_box_common( bs, minf );
4429 if( lsmash_bs_write_data( bs ) )
4430 return -1;
4431 if( (minf->vmhd && isom_write_vmhd( bs, trak ))
4432 || (minf->smhd && isom_write_smhd( bs, trak ))
4433 || (minf->hmhd && isom_write_hmhd( bs, trak ))
4434 || (minf->nmhd && isom_write_nmhd( bs, trak ))
4435 || (minf->gmhd && isom_write_gmhd( bs, trak )) )
4436 return -1;
4437 if( isom_write_hdlr( bs, minf->hdlr, minf->type )
4438 || isom_write_dinf( bs, minf->dinf, minf->type )
4439 || isom_write_stbl( bs, trak ) )
4440 return -1;
4441 return 0;
4444 static int isom_write_mdia( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4446 isom_mdia_t *mdia = trak->mdia;
4447 if( !mdia )
4448 return -1;
4449 isom_bs_put_box_common( bs, mdia );
4450 if( lsmash_bs_write_data( bs ) )
4451 return -1;
4452 if( isom_write_mdhd( bs, trak )
4453 || isom_write_hdlr( bs, mdia->hdlr, mdia->type )
4454 || isom_write_minf( bs, trak ) )
4455 return -1;
4456 return 0;
4459 static int isom_write_chpl( lsmash_bs_t *bs, isom_chpl_t *chpl )
4461 if( !chpl )
4462 return 0;
4463 if( !chpl->list || chpl->version > 1 )
4464 return -1;
4465 isom_bs_put_box_common( bs, chpl );
4466 if( chpl->version == 1 )
4468 lsmash_bs_put_byte( bs, chpl->unknown );
4469 lsmash_bs_put_be32( bs, chpl->list->entry_count );
4471 else /* chpl->version == 0 */
4472 lsmash_bs_put_byte( bs, (uint8_t)chpl->list->entry_count );
4473 for( lsmash_entry_t *entry = chpl->list->head; entry; entry = entry->next )
4475 isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data;
4476 if( !data )
4477 return -1;
4478 lsmash_bs_put_be64( bs, data->start_time );
4479 lsmash_bs_put_byte( bs, data->chapter_name_length );
4480 lsmash_bs_put_bytes( bs, data->chapter_name, data->chapter_name_length );
4482 return lsmash_bs_write_data( bs );
4485 static int isom_write_mean( lsmash_bs_t *bs, isom_mean_t *mean )
4487 if( !mean )
4488 return 0;
4489 isom_bs_put_box_common( bs, mean );
4490 if( mean->meaning_string && mean->meaning_string_length )
4491 lsmash_bs_put_bytes( bs, mean->meaning_string, mean->meaning_string_length );
4492 return lsmash_bs_write_data( bs );
4495 static int isom_write_name( lsmash_bs_t *bs, isom_name_t *name )
4497 if( !name )
4498 return 0;
4499 isom_bs_put_box_common( bs, name );
4500 if( name->name && name->name_length )
4501 lsmash_bs_put_bytes( bs, name->name, name->name_length );
4502 return lsmash_bs_write_data( bs );
4505 static int isom_write_data( lsmash_bs_t *bs, isom_data_t *data )
4507 if( !data || data->size < 16 )
4508 return -1;
4509 isom_bs_put_box_common( bs, data );
4510 lsmash_bs_put_be16( bs, data->reserved );
4511 lsmash_bs_put_byte( bs, data->type_set_identifier );
4512 lsmash_bs_put_byte( bs, data->type_code );
4513 lsmash_bs_put_be32( bs, data->the_locale );
4514 if( data->value && data->value_length )
4515 lsmash_bs_put_bytes( bs, data->value, data->value_length );
4516 return lsmash_bs_write_data( bs );
4519 static int isom_write_metaitem( lsmash_bs_t *bs, isom_metaitem_t *metaitem )
4521 if( !metaitem )
4522 return -1;
4523 isom_bs_put_box_common( bs, metaitem );
4524 if( lsmash_bs_write_data( bs ) )
4525 return -1;
4526 if( isom_write_mean( bs, metaitem->mean )
4527 || isom_write_name( bs, metaitem->name )
4528 || isom_write_data( bs, metaitem->data ) )
4529 return -1;
4530 return 0;
4533 static int isom_write_ilst( lsmash_bs_t *bs, isom_ilst_t *ilst )
4535 if( !ilst )
4536 return 0;
4537 isom_bs_put_box_common( bs, ilst );
4538 if( lsmash_bs_write_data( bs ) )
4539 return -1;
4540 if( ilst->item_list )
4541 for( lsmash_entry_t *entry = ilst->item_list->head; entry; entry = entry->next )
4542 if( isom_write_metaitem( bs, (isom_metaitem_t *)entry->data ) )
4543 return -1;
4544 return 0;
4547 static int isom_write_meta( lsmash_bs_t *bs, isom_meta_t *meta )
4549 if( !meta )
4550 return 0;
4551 isom_bs_put_box_common( bs, meta );
4552 if( lsmash_bs_write_data( bs ) )
4553 return -1;
4554 if( isom_write_hdlr( bs, meta->hdlr, meta->type )
4555 || isom_write_dinf( bs, meta->dinf, meta->type )
4556 || isom_write_ilst( bs, meta->ilst ) )
4557 return -1;
4558 return 0;
4561 static int isom_write_udta( lsmash_bs_t *bs, isom_moov_t *moov, isom_trak_entry_t *trak )
4563 /* Setting non-NULL pointer to trak means trak->udta data will be written in stream.
4564 * If trak is set by NULL while moov is set by non-NULL pointer, moov->udta data will be written in stream. */
4565 isom_udta_t *udta = trak ? trak->udta : moov ? moov->udta : NULL;
4566 if( !udta )
4567 return 0;
4568 isom_bs_put_box_common( bs, udta );
4569 if( lsmash_bs_write_data( bs ) )
4570 return -1;
4571 if( moov && isom_write_chpl( bs, udta->chpl ) )
4572 return -1;
4573 return isom_write_meta( bs, udta->meta );
4576 static int isom_write_trak( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4578 if( !trak )
4579 return -1;
4580 isom_bs_put_box_common( bs, trak );
4581 if( lsmash_bs_write_data( bs ) )
4582 return -1;
4583 if( isom_write_tkhd( bs, trak )
4584 || isom_write_tapt( bs, trak )
4585 || isom_write_edts( bs, trak )
4586 || isom_write_tref( bs, trak )
4587 || isom_write_mdia( bs, trak )
4588 || isom_write_udta( bs, NULL, trak )
4589 || isom_write_meta( bs, trak->meta ) )
4590 return -1;
4591 return 0;
4594 static int isom_write_iods( lsmash_root_t *root )
4596 if( !root || !root->moov )
4597 return -1;
4598 if( !root->moov->iods )
4599 return 0;
4600 isom_iods_t *iods = root->moov->iods;
4601 lsmash_bs_t *bs = root->bs;
4602 isom_bs_put_box_common( bs, iods );
4603 return mp4sys_write_ObjectDescriptor( bs, iods->OD );
4606 static int isom_write_mvhd( lsmash_root_t *root )
4608 if( !root || !root->moov || !root->moov->mvhd )
4609 return -1;
4610 isom_mvhd_t *mvhd = root->moov->mvhd;
4611 lsmash_bs_t *bs = root->bs;
4612 isom_bs_put_box_common( bs, mvhd );
4613 if( mvhd->version )
4615 lsmash_bs_put_be64( bs, mvhd->creation_time );
4616 lsmash_bs_put_be64( bs, mvhd->modification_time );
4617 lsmash_bs_put_be32( bs, mvhd->timescale );
4618 lsmash_bs_put_be64( bs, mvhd->duration );
4620 else
4622 lsmash_bs_put_be32( bs, (uint32_t)mvhd->creation_time );
4623 lsmash_bs_put_be32( bs, (uint32_t)mvhd->modification_time );
4624 lsmash_bs_put_be32( bs, mvhd->timescale );
4625 lsmash_bs_put_be32( bs, (uint32_t)mvhd->duration );
4627 lsmash_bs_put_be32( bs, mvhd->rate );
4628 lsmash_bs_put_be16( bs, mvhd->volume );
4629 lsmash_bs_put_be16( bs, mvhd->reserved );
4630 lsmash_bs_put_be32( bs, mvhd->preferredLong[0] );
4631 lsmash_bs_put_be32( bs, mvhd->preferredLong[1] );
4632 for( int i = 0; i < 9; i++ )
4633 lsmash_bs_put_be32( bs, mvhd->matrix[i] );
4634 lsmash_bs_put_be32( bs, mvhd->previewTime );
4635 lsmash_bs_put_be32( bs, mvhd->previewDuration );
4636 lsmash_bs_put_be32( bs, mvhd->posterTime );
4637 lsmash_bs_put_be32( bs, mvhd->selectionTime );
4638 lsmash_bs_put_be32( bs, mvhd->selectionDuration );
4639 lsmash_bs_put_be32( bs, mvhd->currentTime );
4640 lsmash_bs_put_be32( bs, mvhd->next_track_ID );
4641 return lsmash_bs_write_data( bs );
4644 static void isom_bs_put_sample_flags( lsmash_bs_t *bs, isom_sample_flags_t *flags )
4646 uint32_t temp = (flags->reserved << 28)
4647 | (flags->is_leading << 26)
4648 | (flags->sample_depends_on << 24)
4649 | (flags->sample_is_depended_on << 22)
4650 | (flags->sample_has_redundancy << 20)
4651 | (flags->sample_padding_value << 17)
4652 | (flags->sample_is_non_sync_sample << 16)
4653 | flags->sample_degradation_priority;
4654 lsmash_bs_put_be32( bs, temp );
4657 static int isom_write_mehd( lsmash_bs_t *bs, isom_mehd_t *mehd )
4659 if( !mehd )
4660 return -1;
4661 isom_bs_put_box_common( bs, mehd );
4662 if( mehd->version == 1 )
4663 lsmash_bs_put_be64( bs, mehd->fragment_duration );
4664 else
4665 lsmash_bs_put_be32( bs, (uint32_t)mehd->fragment_duration );
4666 return lsmash_bs_write_data( bs );
4669 static int isom_write_trex( lsmash_bs_t *bs, isom_trex_entry_t *trex )
4671 if( !trex )
4672 return -1;
4673 isom_bs_put_box_common( bs, trex );
4674 lsmash_bs_put_be32( bs, trex->track_ID );
4675 lsmash_bs_put_be32( bs, trex->default_sample_description_index );
4676 lsmash_bs_put_be32( bs, trex->default_sample_duration );
4677 lsmash_bs_put_be32( bs, trex->default_sample_size );
4678 isom_bs_put_sample_flags( bs, &trex->default_sample_flags );
4679 return lsmash_bs_write_data( bs );
4682 static int isom_bs_write_movie_extends_placeholder( lsmash_bs_t *bs )
4684 /* The following will be overwritten by Movie Extends Header Box.
4685 * We use version 1 Movie Extends Header Box since it causes extra 4 bytes region
4686 * we cannot replace with empty Free Space Box as we place version 0 one. */
4687 lsmash_bs_put_be32( bs, ISOM_FULLBOX_COMMON_SIZE + 8 );
4688 lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_FREE );
4689 lsmash_bs_put_be32( bs, 0 );
4690 lsmash_bs_put_be64( bs, 0 );
4691 return lsmash_bs_write_data( bs );
4694 static int isom_write_mvex( lsmash_bs_t *bs, isom_mvex_t *mvex )
4696 if( !mvex )
4697 return 0;
4698 isom_bs_put_box_common( bs, mvex );
4699 if( lsmash_bs_write_data( bs ) )
4700 return -1;
4701 /* Movie Extends Header Box is not written immediately.
4702 * It's done after finishing all movie fragments. */
4703 if( mvex->mehd )
4705 if( isom_write_mehd( bs, mvex->mehd ) )
4706 return -1;
4708 else if( bs->stream != stdout )
4711 [ROOT]
4712 |--[ftyp]
4713 |--[moov]
4714 |--[mvhd]
4715 |--[trak]
4719 |--[mvex]
4720 |--[mehd] <--- mehd->pos == mvex->placeholder_pos
4722 mvex->placeholder_pos = mvex->root->bs->written;
4723 if( isom_bs_write_movie_extends_placeholder( bs ) )
4724 return -1;
4726 if( mvex->trex_list )
4727 for( lsmash_entry_t *entry = mvex->trex_list->head; entry; entry = entry->next )
4728 if( isom_write_trex( bs, (isom_trex_entry_t *)entry->data ) )
4729 return -1;
4730 return 0;
4733 static int isom_write_mfhd( lsmash_bs_t *bs, isom_mfhd_t *mfhd )
4735 if( !mfhd )
4736 return -1;
4737 isom_bs_put_box_common( bs, mfhd );
4738 lsmash_bs_put_be32( bs, mfhd->sequence_number );
4739 return lsmash_bs_write_data( bs );
4742 static int isom_write_tfhd( lsmash_bs_t *bs, isom_tfhd_t *tfhd )
4744 if( !tfhd )
4745 return -1;
4746 isom_bs_put_box_common( bs, tfhd );
4747 lsmash_bs_put_be32( bs, tfhd->track_ID );
4748 if( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT ) lsmash_bs_put_be64( bs, tfhd->base_data_offset );
4749 if( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT ) lsmash_bs_put_be32( bs, tfhd->sample_description_index );
4750 if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT ) lsmash_bs_put_be32( bs, tfhd->default_sample_duration );
4751 if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT ) lsmash_bs_put_be32( bs, tfhd->default_sample_size );
4752 if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT ) isom_bs_put_sample_flags( bs, &tfhd->default_sample_flags );
4753 return lsmash_bs_write_data( bs );
4756 static int isom_write_trun( lsmash_bs_t *bs, isom_trun_entry_t *trun )
4758 if( !trun )
4759 return -1;
4760 isom_bs_put_box_common( bs, trun );
4761 lsmash_bs_put_be32( bs, trun->sample_count );
4762 if( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT ) lsmash_bs_put_be32( bs, trun->data_offset );
4763 if( trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT ) isom_bs_put_sample_flags( bs, &trun->first_sample_flags );
4764 if( trun->optional )
4765 for( lsmash_entry_t *entry = trun->optional->head; entry; entry = entry->next )
4767 isom_trun_optional_row_t *data = (isom_trun_optional_row_t *)entry->data;
4768 if( !data )
4769 return -1;
4770 if( trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT ) lsmash_bs_put_be32( bs, data->sample_duration );
4771 if( trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT ) lsmash_bs_put_be32( bs, data->sample_size );
4772 if( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT ) isom_bs_put_sample_flags( bs, &data->sample_flags );
4773 if( trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT ) lsmash_bs_put_be32( bs, data->sample_composition_time_offset );
4775 return lsmash_bs_write_data( bs );
4778 static int isom_write_traf( lsmash_bs_t *bs, isom_traf_entry_t *traf )
4780 if( !traf )
4781 return -1;
4782 isom_bs_put_box_common( bs, traf );
4783 if( lsmash_bs_write_data( bs ) )
4784 return -1;
4785 if( isom_write_tfhd( bs, traf->tfhd ) )
4786 return -1;
4787 if( traf->trun_list )
4788 for( lsmash_entry_t *entry = traf->trun_list->head; entry; entry = entry->next )
4789 if( isom_write_trun( bs, (isom_trun_entry_t *)entry->data ) )
4790 return -1;
4791 return 0;
4794 static int isom_write_moof( lsmash_bs_t *bs, isom_moof_entry_t *moof )
4796 if( !moof )
4797 return -1;
4798 isom_bs_put_box_common( bs, moof );
4799 if( lsmash_bs_write_data( bs ) )
4800 return -1;
4801 if( isom_write_mfhd( bs, moof->mfhd ) )
4802 return -1;
4803 if( moof->traf_list )
4804 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
4805 if( isom_write_traf( bs, (isom_traf_entry_t *)entry->data ) )
4806 return -1;
4807 return 0;
4810 static int isom_write_tfra( lsmash_bs_t *bs, isom_tfra_entry_t *tfra )
4812 if( !tfra )
4813 return -1;
4814 isom_bs_put_box_common( bs, tfra );
4815 uint32_t temp = (tfra->reserved << 6)
4816 | (tfra->length_size_of_traf_num << 4)
4817 | (tfra->length_size_of_trun_num << 2)
4818 | tfra->length_size_of_sample_num;
4819 lsmash_bs_put_be32( bs, tfra->track_ID );
4820 lsmash_bs_put_be32( bs, temp );
4821 lsmash_bs_put_be32( bs, tfra->number_of_entry );
4822 if( tfra->list )
4824 void (*bs_put_funcs[5])( lsmash_bs_t *, uint64_t ) =
4826 lsmash_bs_put_byte_from_64,
4827 lsmash_bs_put_be16_from_64,
4828 lsmash_bs_put_be24_from_64,
4829 lsmash_bs_put_be32_from_64,
4830 lsmash_bs_put_be64
4832 void (*bs_put_time) ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ 3 + (tfra->version == 1) ];
4833 void (*bs_put_moof_offset) ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ 3 + (tfra->version == 1) ];
4834 void (*bs_put_traf_number) ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ tfra->length_size_of_traf_num ];
4835 void (*bs_put_trun_number) ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ tfra->length_size_of_trun_num ];
4836 void (*bs_put_sample_number)( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ tfra->length_size_of_sample_num ];
4837 for( lsmash_entry_t *entry = tfra->list->head; entry; entry = entry->next )
4839 isom_tfra_location_time_entry_t *data = (isom_tfra_location_time_entry_t *)entry->data;
4840 if( !data )
4841 return -1;
4842 bs_put_time ( bs, data->time );
4843 bs_put_moof_offset ( bs, data->moof_offset );
4844 bs_put_traf_number ( bs, data->traf_number );
4845 bs_put_trun_number ( bs, data->trun_number );
4846 bs_put_sample_number( bs, data->sample_number );
4849 return lsmash_bs_write_data( bs );
4852 static int isom_write_mfro( lsmash_bs_t *bs, isom_mfro_t *mfro )
4854 if( !mfro )
4855 return -1;
4856 isom_bs_put_box_common( bs, mfro );
4857 lsmash_bs_put_be32( bs, mfro->length );
4858 return lsmash_bs_write_data( bs );
4861 static int isom_write_mfra( lsmash_bs_t *bs, isom_mfra_t *mfra )
4863 if( !mfra )
4864 return -1;
4865 isom_bs_put_box_common( bs, mfra );
4866 if( lsmash_bs_write_data( bs ) )
4867 return -1;
4868 if( mfra->tfra_list )
4869 for( lsmash_entry_t *entry = mfra->tfra_list->head; entry; entry = entry->next )
4870 if( isom_write_tfra( bs, (isom_tfra_entry_t *)entry->data ) )
4871 return -1;
4872 return isom_write_mfro( bs, mfra->mfro );
4875 static int isom_bs_write_largesize_placeholder( lsmash_bs_t *bs )
4877 lsmash_bs_put_be32( bs, ISOM_BASEBOX_COMMON_SIZE );
4878 lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_FREE );
4879 return lsmash_bs_write_data( bs );
4882 static int isom_write_mdat_header( lsmash_root_t *root, uint64_t media_size )
4884 if( !root || !root->bs || !root->mdat )
4885 return -1;
4886 isom_mdat_t *mdat = root->mdat;
4887 lsmash_bs_t *bs = root->bs;
4888 if( media_size )
4890 mdat->size = ISOM_BASEBOX_COMMON_SIZE + media_size;
4891 if( mdat->size > UINT32_MAX )
4892 mdat->size += 8; /* large_size */
4893 isom_bs_put_box_common( bs, mdat );
4894 return 0;
4896 mdat->placeholder_pos = lsmash_ftell( bs->stream );
4897 if( isom_bs_write_largesize_placeholder( bs ) )
4898 return -1;
4899 mdat->size = ISOM_BASEBOX_COMMON_SIZE;
4900 isom_bs_put_box_common( bs, mdat );
4901 return lsmash_bs_write_data( bs );
4904 static int isom_write_mdat_size( lsmash_root_t *root )
4906 if( !root || !root->bs || !root->bs->stream )
4907 return -1;
4908 if( !root->mdat )
4909 return 0;
4910 isom_mdat_t *mdat = root->mdat;
4911 uint8_t large_flag = mdat->size > UINT32_MAX;
4912 lsmash_bs_t *bs = root->bs;
4913 FILE *stream = bs->stream;
4914 uint64_t current_pos = lsmash_ftell( stream );
4915 if( large_flag )
4917 lsmash_fseek( stream, mdat->placeholder_pos, SEEK_SET );
4918 lsmash_bs_put_be32( bs, 1 );
4919 lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_MDAT );
4920 lsmash_bs_put_be64( bs, mdat->size + ISOM_BASEBOX_COMMON_SIZE );
4922 else
4924 lsmash_fseek( stream, mdat->placeholder_pos + ISOM_BASEBOX_COMMON_SIZE, SEEK_SET );
4925 lsmash_bs_put_be32( bs, mdat->size );
4926 lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_MDAT );
4928 int ret = lsmash_bs_write_data( bs );
4929 lsmash_fseek( stream, current_pos, SEEK_SET );
4930 return ret;
4933 /* We put a placeholder for 64-bit media data if the media_size of the argument is set to 0.
4934 * If a Media Data Box already exists and we don't pick movie fragments structure,
4935 * write the actual size of the current one and start a new one. */
4936 static int isom_new_mdat( lsmash_root_t *root, uint64_t media_size )
4938 if( !root )
4939 return 0;
4940 if( root->mdat )
4942 /* Write the actual size of the current Media Data Box. */
4943 if( !root->fragment && isom_write_mdat_size( root ) )
4944 return -1;
4946 else
4948 isom_create_box( mdat, root, ISOM_BOX_TYPE_MDAT );
4949 root->mdat = mdat;
4951 /* Start a new Media Data Box. */
4952 return isom_write_mdat_header( root, media_size );
4955 int isom_check_compatibility( lsmash_root_t *root )
4957 if( !root )
4958 return -1;
4959 root->qt_compatible = 0;
4960 /* Check brand to decide mandatory boxes. */
4961 if( !root->ftyp || !root->ftyp->brand_count )
4963 /* No brand declaration means this file is a MP4 version 1 or QuickTime file format. */
4964 if( root->moov && root->moov->iods )
4966 root->mp4_version1 = 1;
4967 root->isom_compatible = 1;
4969 else
4970 root->qt_compatible = 1;
4971 return 0;
4973 for( uint32_t i = 0; i < root->ftyp->brand_count; i++ )
4975 switch( root->ftyp->compatible_brands[i] )
4977 case ISOM_BRAND_TYPE_QT :
4978 root->qt_compatible = 1;
4979 break;
4980 case ISOM_BRAND_TYPE_MP41 :
4981 root->mp4_version1 = 1;
4982 break;
4983 case ISOM_BRAND_TYPE_MP42 :
4984 root->mp4_version2 = 1;
4985 break;
4986 case ISOM_BRAND_TYPE_ISOM :
4987 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 1 );
4988 break;
4989 case ISOM_BRAND_TYPE_ISO2 :
4990 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 2 );
4991 break;
4992 case ISOM_BRAND_TYPE_ISO3 :
4993 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 3 );
4994 break;
4995 case ISOM_BRAND_TYPE_ISO4 :
4996 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 4 );
4997 break;
4998 case ISOM_BRAND_TYPE_ISO5 :
4999 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 5 );
5000 break;
5001 case ISOM_BRAND_TYPE_ISO6 :
5002 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 6 );
5003 break;
5004 case ISOM_BRAND_TYPE_M4A :
5005 case ISOM_BRAND_TYPE_M4B :
5006 root->itunes_audio = 1;
5007 case ISOM_BRAND_TYPE_M4P :
5008 case ISOM_BRAND_TYPE_M4V :
5009 root->itunes_movie = 1;
5010 break;
5011 case ISOM_BRAND_TYPE_3GP4 :
5012 root->max_3gpp_version = LSMASH_MAX( root->max_3gpp_version, 4 );
5013 break;
5014 case ISOM_BRAND_TYPE_3GP5 :
5015 root->max_3gpp_version = LSMASH_MAX( root->max_3gpp_version, 5 );
5016 break;
5017 case ISOM_BRAND_TYPE_3GE6 :
5018 case ISOM_BRAND_TYPE_3GG6 :
5019 case ISOM_BRAND_TYPE_3GP6 :
5020 case ISOM_BRAND_TYPE_3GR6 :
5021 case ISOM_BRAND_TYPE_3GS6 :
5022 root->max_3gpp_version = LSMASH_MAX( root->max_3gpp_version, 6 );
5023 break;
5024 default :
5025 break;
5027 switch( root->ftyp->compatible_brands[i] )
5029 case ISOM_BRAND_TYPE_AVC1 :
5030 case ISOM_BRAND_TYPE_ISO2 :
5031 case ISOM_BRAND_TYPE_ISO3 :
5032 case ISOM_BRAND_TYPE_ISO4 :
5033 case ISOM_BRAND_TYPE_ISO5 :
5034 case ISOM_BRAND_TYPE_ISO6 :
5035 root->avc_extensions = 1;
5036 break;
5037 default :
5038 break;
5041 root->isom_compatible = !root->qt_compatible || root->mp4_version1 || root->mp4_version2 || root->itunes_movie || root->max_3gpp_version;
5042 return 0;
5045 static uint32_t isom_get_sample_count( isom_trak_entry_t *trak )
5047 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsz )
5048 return 0;
5049 return trak->mdia->minf->stbl->stsz->sample_count;
5052 static uint64_t isom_get_dts( isom_stts_t *stts, uint32_t sample_number )
5054 if( !stts || !stts->list )
5055 return 0;
5056 uint64_t dts = 0;
5057 uint32_t i = 1;
5058 lsmash_entry_t *entry;
5059 isom_stts_entry_t *data;
5060 for( entry = stts->list->head; entry; entry = entry->next )
5062 data = (isom_stts_entry_t *)entry->data;
5063 if( !data )
5064 return 0;
5065 if( i + data->sample_count > sample_number )
5066 break;
5067 dts += (uint64_t)data->sample_delta * data->sample_count;
5068 i += data->sample_count;
5070 if( !entry )
5071 return 0;
5072 dts += (uint64_t)data->sample_delta * (sample_number - i);
5073 return dts;
5076 #if 0
5077 static uint64_t isom_get_cts( isom_stts_t *stts, isom_ctts_t *ctts, uint32_t sample_number )
5079 if( !stts || !stts->list )
5080 return 0;
5081 if( !ctts )
5082 return isom_get_dts( stts, sample_number );
5083 uint32_t i = 1; /* This can be 0 (and then condition below shall be changed) but I dare use same algorithm with isom_get_dts. */
5084 lsmash_entry_t *entry;
5085 isom_ctts_entry_t *data;
5086 if( sample_number == 0 )
5087 return 0;
5088 for( entry = ctts->list->head; entry; entry = entry->next )
5090 data = (isom_ctts_entry_t *)entry->data;
5091 if( !data )
5092 return 0;
5093 if( i + data->sample_count > sample_number )
5094 break;
5095 i += data->sample_count;
5097 if( !entry )
5098 return 0;
5099 return isom_get_dts( stts, sample_number ) + data->sample_offset;
5101 #endif
5103 static int isom_replace_last_sample_delta( isom_stbl_t *stbl, uint32_t sample_delta )
5105 if( !stbl || !stbl->stts || !stbl->stts->list || !stbl->stts->list->tail || !stbl->stts->list->tail->data )
5106 return -1;
5107 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stbl->stts->list->tail->data;
5108 if( sample_delta != last_stts_data->sample_delta )
5110 if( last_stts_data->sample_count > 1 )
5112 last_stts_data->sample_count -= 1;
5113 if( isom_add_stts_entry( stbl, sample_delta ) )
5114 return -1;
5116 else
5117 last_stts_data->sample_delta = sample_delta;
5119 return 0;
5122 static int isom_update_mdhd_duration( isom_trak_entry_t *trak, uint32_t last_sample_delta )
5124 if( !trak || !trak->root || !trak->cache || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf
5125 || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list )
5126 return -1;
5127 lsmash_root_t *root = trak->root;
5128 isom_mdhd_t *mdhd = trak->mdia->mdhd;
5129 isom_stbl_t *stbl = trak->mdia->minf->stbl;
5130 isom_stts_t *stts = stbl->stts;
5131 isom_ctts_t *ctts = stbl->ctts;
5132 isom_cslg_t *cslg = stbl->cslg;
5133 mdhd->duration = 0;
5134 uint32_t sample_count = isom_get_sample_count( trak );
5135 if( !sample_count )
5137 /* Return error if non-fragmented movie has no samples. */
5138 if( !root->fragment && !stts->list->entry_count )
5139 return -1;
5140 return 0;
5142 /* Now we have at least 1 sample, so do stts_entry. */
5143 lsmash_entry_t *last_stts = stts->list->tail;
5144 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)last_stts->data;
5145 if( sample_count == 1 )
5146 mdhd->duration = last_stts_data->sample_delta;
5147 /* Now we have at least 2 samples,
5148 * but dunno whether 1 stts_entry which has 2 samples or 2 stts_entry which has 1 samle each. */
5149 else if( !ctts )
5151 /* use dts instead of cts */
5152 mdhd->duration = isom_get_dts( stts, sample_count );
5153 if( last_sample_delta )
5155 mdhd->duration += last_sample_delta;
5156 if( isom_replace_last_sample_delta( stbl, last_sample_delta ) )
5157 return -1;
5159 else if( last_stts_data->sample_count > 1 )
5160 mdhd->duration += last_stts_data->sample_delta; /* no need to update last_stts_data->sample_delta */
5161 else
5163 /* Remove the last entry. */
5164 if( lsmash_remove_entry( stts->list, stts->list->entry_count, NULL ) )
5165 return -1;
5166 /* copy the previous sample_delta. */
5167 ++ ((isom_stts_entry_t *)stts->list->tail->data)->sample_count;
5168 mdhd->duration += ((isom_stts_entry_t *)stts->list->tail->data)->sample_delta;
5171 else
5173 if( !ctts->list || ctts->list->entry_count == 0 )
5174 return -1;
5175 uint64_t dts = 0;
5176 uint64_t max_cts = 0, max2_cts = 0, min_cts = UINT64_MAX;
5177 uint32_t max_offset = 0, min_offset = UINT32_MAX;
5178 int32_t ctd_shift = trak->cache->timestamp.ctd_shift;
5179 uint32_t j, k;
5180 lsmash_entry_t *stts_entry = stts->list->head;
5181 lsmash_entry_t *ctts_entry = ctts->list->head;
5182 j = k = 0;
5183 for( uint32_t i = 0; i < sample_count; i++ )
5185 if( !ctts_entry || !stts_entry )
5186 return -1;
5187 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
5188 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
5189 if( !stts_data || !ctts_data )
5190 return -1;
5191 uint64_t cts;
5192 if( ctd_shift )
5194 /* Anyway, add composition to decode timeline shift for calculating maximum and minimum CTS correctly. */
5195 int32_t sample_offset = (int32_t)ctts_data->sample_offset;
5196 cts = dts + sample_offset + ctd_shift;
5197 max_offset = LSMASH_MAX( (int32_t)max_offset, sample_offset );
5198 min_offset = LSMASH_MIN( (int32_t)min_offset, sample_offset );
5200 else
5202 cts = dts + ctts_data->sample_offset;
5203 max_offset = LSMASH_MAX( max_offset, ctts_data->sample_offset );
5204 min_offset = LSMASH_MIN( min_offset, ctts_data->sample_offset );
5206 min_cts = LSMASH_MIN( min_cts, cts );
5207 if( max_cts < cts )
5209 max2_cts = max_cts;
5210 max_cts = cts;
5212 else if( max2_cts < cts )
5213 max2_cts = cts;
5214 dts += stts_data->sample_delta;
5215 /* If finished sample_count of current entry, move to next. */
5216 if( ++j == ctts_data->sample_count )
5218 ctts_entry = ctts_entry->next;
5219 j = 0;
5221 if( ++k == stts_data->sample_count )
5223 stts_entry = stts_entry->next;
5224 k = 0;
5227 dts -= last_stts_data->sample_delta;
5228 if( root->fragment )
5229 /* Overall presentation is extended exceeding this initial movie.
5230 * So, any players shall display the movie exceeding the durations
5231 * indicated in Movie Header Box, Track Header Boxes and Media Header Boxes.
5232 * Samples up to the duration indicated in Movie Extends Header Box shall be displayed.
5233 * In the absence of Movie Extends Header Box, all samples shall be displayed. */
5234 mdhd->duration += dts + last_sample_delta;
5235 else
5237 if( !last_sample_delta )
5239 /* The spec allows an arbitrary value for the duration of the last sample. So, we pick last-1 sample's. */
5240 last_sample_delta = max_cts - max2_cts;
5242 mdhd->duration = max_cts - min_cts + last_sample_delta;
5243 /* To match dts and media duration, update stts and mdhd relatively. */
5244 if( mdhd->duration > dts )
5245 last_sample_delta = mdhd->duration - dts;
5246 else
5247 mdhd->duration = dts + last_sample_delta; /* media duration must not less than last dts. */
5249 if( isom_replace_last_sample_delta( stbl, last_sample_delta ) )
5250 return -1;
5251 /* Explicit composition information and timeline shifting */
5252 if( cslg || root->qt_compatible || root->max_isom_version >= 4 )
5254 if( ctd_shift )
5256 /* Remove composition to decode timeline shift. */
5257 max_cts -= ctd_shift;
5258 max2_cts -= ctd_shift;
5259 min_cts -= ctd_shift;
5261 int64_t composition_end_time = max_cts + (max_cts - max2_cts);
5262 if( !root->fragment
5263 && ((int32_t)min_offset <= INT32_MAX) && ((int32_t)max_offset <= INT32_MAX)
5264 && ((int64_t)min_cts <= INT32_MAX) && (composition_end_time <= INT32_MAX) )
5266 if( !cslg )
5268 if( isom_add_cslg( trak->mdia->minf->stbl ) )
5269 return -1;
5270 cslg = stbl->cslg;
5272 cslg->compositionToDTSShift = ctd_shift;
5273 cslg->leastDecodeToDisplayDelta = min_offset;
5274 cslg->greatestDecodeToDisplayDelta = max_offset;
5275 cslg->compositionStartTime = min_cts;
5276 cslg->compositionEndTime = composition_end_time;
5278 else
5280 if( cslg )
5281 free( cslg );
5282 stbl->cslg = NULL;
5286 if( mdhd->duration > UINT32_MAX )
5287 mdhd->version = 1;
5288 return 0;
5291 static int isom_update_mvhd_duration( isom_moov_t *moov )
5293 if( !moov || !moov->mvhd )
5294 return -1;
5295 isom_mvhd_t *mvhd = moov->mvhd;
5296 mvhd->duration = 0;
5297 for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
5299 /* We pick maximum track duration as movie duration. */
5300 isom_trak_entry_t *data = (isom_trak_entry_t *)entry->data;
5301 if( !data || !data->tkhd )
5302 return -1;
5303 mvhd->duration = entry != moov->trak_list->head ? LSMASH_MAX( mvhd->duration, data->tkhd->duration ) : data->tkhd->duration;
5305 if( mvhd->duration > UINT32_MAX )
5306 mvhd->version = 1;
5307 return 0;
5310 static int isom_update_tkhd_duration( isom_trak_entry_t *trak )
5312 if( !trak || !trak->tkhd || !trak->root || !trak->root->moov )
5313 return -1;
5314 lsmash_root_t *root = trak->root;
5315 isom_tkhd_t *tkhd = trak->tkhd;
5316 tkhd->duration = 0;
5317 if( root->fragment || !trak->edts || !trak->edts->elst )
5319 /* If this presentation might be extended or this track doesn't have edit list, calculate track duration from media duration. */
5320 if( !trak->mdia || !trak->mdia->mdhd || !root->moov->mvhd || !trak->mdia->mdhd->timescale )
5321 return -1;
5322 if( !trak->mdia->mdhd->duration && isom_update_mdhd_duration( trak, 0 ) )
5323 return -1;
5324 tkhd->duration = trak->mdia->mdhd->duration * ((double)root->moov->mvhd->timescale / trak->mdia->mdhd->timescale);
5326 else
5328 /* If the presentation won't be extended and this track has any edit, then track duration is just the sum of the segment_duartions. */
5329 for( lsmash_entry_t *entry = trak->edts->elst->list->head; entry; entry = entry->next )
5331 isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data;
5332 if( !data )
5333 return -1;
5334 tkhd->duration += data->segment_duration;
5337 if( tkhd->duration > UINT32_MAX )
5338 tkhd->version = 1;
5339 if( !root->fragment && !tkhd->duration )
5340 tkhd->duration = tkhd->version == 1 ? 0xffffffffffffffff : 0xffffffff;
5341 return isom_update_mvhd_duration( root->moov );
5344 int lsmash_update_track_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta )
5346 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
5347 if( !trak )
5348 return -1;
5349 if( isom_update_mdhd_duration( trak, last_sample_delta ) )
5350 return -1;
5351 /* If the presentation won't be extended and this track has any edit, we don't change or update duration in tkhd. */
5352 return (!root->fragment && trak->edts && trak->edts->elst)
5353 ? isom_update_mvhd_duration( root->moov ) /* Only update movie duration. */
5354 : isom_update_tkhd_duration( trak ); /* Also update movie duration internally. */
5357 int lsmash_set_avc_config( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number,
5358 uint8_t configurationVersion, uint8_t AVCProfileIndication, uint8_t profile_compatibility, uint8_t AVCLevelIndication, uint8_t lengthSizeMinusOne,
5359 uint8_t chroma_format, uint8_t bit_depth_luma_minus8, uint8_t bit_depth_chroma_minus8 )
5361 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
5362 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
5363 return -1;
5364 isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number );
5365 if( !data )
5366 return -1;
5367 isom_avcC_t *avcC = (isom_avcC_t *)data->avcC;
5368 if( !avcC )
5369 return -1;
5370 avcC->configurationVersion = configurationVersion;
5371 avcC->AVCProfileIndication = AVCProfileIndication;
5372 avcC->profile_compatibility = profile_compatibility;
5373 avcC->AVCLevelIndication = AVCLevelIndication;
5374 avcC->lengthSizeMinusOne = lengthSizeMinusOne;
5375 if( ISOM_REQUIRES_AVCC_EXTENSION( AVCProfileIndication ) )
5377 avcC->chroma_format = chroma_format;
5378 avcC->bit_depth_luma_minus8 = bit_depth_luma_minus8;
5379 avcC->bit_depth_chroma_minus8 = bit_depth_chroma_minus8;
5381 return 0;
5384 static int isom_update_bitrate_info( isom_mdia_t *mdia )
5386 if( !mdia || !mdia->mdhd || !mdia->minf || !mdia->minf->stbl
5387 || !mdia->minf->stbl->stsd || !mdia->minf->stbl->stsd->list
5388 || !mdia->minf->stbl->stsz || !mdia->minf->stbl->stts || !mdia->minf->stbl->stts->list )
5389 return -1;
5390 /* Not supporting multi sample entries yet. */
5391 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( mdia->minf->stbl->stsd->list, 1 );
5392 if( !sample_entry )
5393 return -1;
5394 struct bitrate_info_t
5396 uint32_t bufferSizeDB;
5397 uint32_t maxBitrate;
5398 uint32_t avgBitrate;
5399 } info = { 0, 0, 0 };
5400 uint32_t i = 0;
5401 uint32_t rate = 0;
5402 uint32_t time_wnd = 0;
5403 uint32_t timescale = mdia->mdhd->timescale;
5404 uint64_t dts = 0;
5405 isom_stsz_t *stsz = mdia->minf->stbl->stsz;
5406 lsmash_entry_t *stsz_entry = stsz->list ? stsz->list->head : NULL;
5407 lsmash_entry_t *stts_entry = mdia->minf->stbl->stts->list->head;
5408 isom_stts_entry_t *stts_data = NULL;
5409 while( stts_entry )
5411 uint32_t size;
5412 if( stsz->list )
5414 if( !stsz_entry )
5415 break;
5416 isom_stsz_entry_t *stsz_data = (isom_stsz_entry_t *)stsz_entry->data;
5417 if( !stsz_data )
5418 return -1;
5419 size = stsz_data->entry_size;
5420 stsz_entry = stsz_entry->next;
5422 else
5423 size = stsz->sample_size;
5424 if( stts_data )
5425 dts += stts_data->sample_delta;
5426 stts_data = (isom_stts_entry_t *)stts_entry->data;
5427 if( ++i == stts_data->sample_count )
5429 stts_entry = stts_entry->next;
5430 i = 0;
5432 if( info.bufferSizeDB < size )
5433 info.bufferSizeDB = size;
5434 info.avgBitrate += size;
5435 rate += size;
5436 if( dts > time_wnd + timescale )
5438 if( rate > info.maxBitrate )
5439 info.maxBitrate = rate;
5440 time_wnd = dts;
5441 rate = 0;
5444 double duration = (double)mdia->mdhd->duration / timescale;
5445 info.avgBitrate = (uint32_t)(info.avgBitrate / duration);
5446 if( !info.maxBitrate )
5447 info.maxBitrate = info.avgBitrate;
5448 /* move to bps */
5449 info.maxBitrate *= 8;
5450 info.avgBitrate *= 8;
5451 /* set bitrate info */
5452 switch( sample_entry->type )
5454 case ISOM_CODEC_TYPE_AVC1_VIDEO :
5455 case ISOM_CODEC_TYPE_AVC2_VIDEO :
5456 case ISOM_CODEC_TYPE_AVCP_VIDEO :
5458 isom_visual_entry_t *stsd_data = (isom_visual_entry_t *)sample_entry;
5459 if( !stsd_data )
5460 return -1;
5461 isom_btrt_t *btrt = stsd_data->btrt;
5462 if( btrt )
5464 btrt->bufferSizeDB = info.bufferSizeDB;
5465 btrt->maxBitrate = info.maxBitrate;
5466 btrt->avgBitrate = info.avgBitrate;
5468 break;
5470 case ISOM_CODEC_TYPE_MP4V_VIDEO :
5472 isom_visual_entry_t *stsd_data = (isom_visual_entry_t *)sample_entry;
5473 if( !stsd_data || !stsd_data->esds || !stsd_data->esds->ES )
5474 return -1;
5475 isom_esds_t *esds = stsd_data->esds;
5476 /* FIXME: avgBitrate is 0 only if VBR in proper. */
5477 if( mp4sys_update_DecoderConfigDescriptor( esds->ES, info.bufferSizeDB, info.maxBitrate, 0 ) )
5478 return -1;
5479 break;
5481 case ISOM_CODEC_TYPE_MP4A_AUDIO :
5483 isom_esds_t *esds = NULL;
5484 if( ((isom_audio_entry_t *)sample_entry)->version )
5486 /* MPEG-4 Audio in QTFF */
5487 isom_audio_entry_t *stsd_data = (isom_audio_entry_t *)sample_entry;
5488 if( !stsd_data || !stsd_data->wave || !stsd_data->wave->esds || !stsd_data->wave->esds->ES )
5489 return -1;
5490 esds = stsd_data->wave->esds;
5492 else
5494 isom_audio_entry_t *stsd_data = (isom_audio_entry_t *)sample_entry;
5495 if( !stsd_data || !stsd_data->esds || !stsd_data->esds->ES )
5496 return -1;
5497 esds = stsd_data->esds;
5499 /* FIXME: avgBitrate is 0 only if VBR in proper. */
5500 if( mp4sys_update_DecoderConfigDescriptor( esds->ES, info.bufferSizeDB, info.maxBitrate, 0 ) )
5501 return -1;
5502 break;
5504 case ISOM_CODEC_TYPE_ALAC_AUDIO :
5506 isom_audio_entry_t *alac = (isom_audio_entry_t *)sample_entry;
5507 if( !alac )
5508 return -1;
5509 if( alac->exdata_length < 36 || !alac->exdata )
5511 isom_wave_t *wave = alac->wave;
5512 if( !wave || wave->exdata_length < 36 || !wave->exdata )
5513 return -1;
5514 break; /* Apparently, average bitrate field is 0. */
5516 uint8_t *exdata = (uint8_t *)alac->exdata + 28;
5517 exdata[0] = (info.avgBitrate >> 24) & 0xff;
5518 exdata[1] = (info.avgBitrate >> 16) & 0xff;
5519 exdata[2] = (info.avgBitrate >> 8) & 0xff;
5520 exdata[3] = info.avgBitrate & 0xff;
5521 break;
5523 default :
5524 break;
5526 return 0;
5529 static int isom_check_mandatory_boxes( lsmash_root_t *root )
5531 if( !root )
5532 return -1;
5533 if( !root->moov || !root->moov->mvhd )
5534 return -1;
5535 if( !root->moov->trak_list )
5536 return -1;
5537 /* A movie requires at least one track. */
5538 if( !root->moov->trak_list->head )
5539 return -1;
5540 for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
5542 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
5543 if( !trak
5544 || !trak->tkhd
5545 || !trak->mdia
5546 || !trak->mdia->mdhd
5547 || !trak->mdia->hdlr
5548 || !trak->mdia->minf
5549 || !trak->mdia->minf->dinf
5550 || !trak->mdia->minf->dinf->dref
5551 || !trak->mdia->minf->stbl
5552 || !trak->mdia->minf->stbl->stsd
5553 || !trak->mdia->minf->stbl->stsz
5554 || !trak->mdia->minf->stbl->stts
5555 || !trak->mdia->minf->stbl->stsc
5556 || !trak->mdia->minf->stbl->stco )
5557 return -1;
5558 if( root->qt_compatible && !trak->mdia->minf->hdlr )
5559 return -1;
5560 isom_stbl_t *stbl = trak->mdia->minf->stbl;
5561 if( !stbl->stsd->list || !stbl->stsd->list->head )
5562 return -1;
5563 if( !root->fragment
5564 && (!stbl->stsd->list || !stbl->stsd->list->head
5565 || !stbl->stts->list || !stbl->stts->list->head
5566 || !stbl->stsc->list || !stbl->stsc->list->head
5567 || !stbl->stco->list || !stbl->stco->list->head) )
5568 return -1;
5570 if( !root->fragment )
5571 return 0;
5572 if( !root->moov->mvex || !root->moov->mvex->trex_list )
5573 return -1;
5574 for( lsmash_entry_t *entry = root->moov->mvex->trex_list->head; entry; entry = entry->next )
5575 if( !entry->data ) /* trex */
5576 return -1;
5577 return 0;
5580 static inline uint64_t isom_get_current_mp4time( void )
5582 return (uint64_t)time( NULL ) + ISOM_MAC_EPOCH_OFFSET;
5585 static int isom_set_media_creation_time( isom_trak_entry_t *trak, uint64_t current_mp4time )
5587 if( !trak->mdia || !trak->mdia->mdhd )
5588 return -1;
5589 isom_mdhd_t *mdhd = trak->mdia->mdhd;
5590 if( !mdhd->creation_time )
5591 mdhd->creation_time = mdhd->modification_time = current_mp4time;
5592 return 0;
5595 static int isom_set_track_creation_time( isom_trak_entry_t *trak, uint64_t current_mp4time )
5597 if( !trak || !trak->tkhd )
5598 return -1;
5599 isom_tkhd_t *tkhd = trak->tkhd;
5600 if( !tkhd->creation_time )
5601 tkhd->creation_time = tkhd->modification_time = current_mp4time;
5602 if( isom_set_media_creation_time( trak, current_mp4time ) )
5603 return -1;
5604 return 0;
5607 static int isom_set_movie_creation_time( lsmash_root_t *root )
5609 if( !root || !root->moov || !root->moov->mvhd || !root->moov->trak_list )
5610 return -1;
5611 uint64_t current_mp4time = isom_get_current_mp4time();
5612 for( uint32_t i = 1; i <= root->moov->trak_list->entry_count; i++ )
5613 if( isom_set_track_creation_time( isom_get_trak( root, i ), current_mp4time ) )
5614 return -1;
5615 isom_mvhd_t *mvhd = root->moov->mvhd;
5616 if( !mvhd->creation_time )
5617 mvhd->creation_time = mvhd->modification_time = current_mp4time;
5618 return 0;
5621 #define CHECK_LARGESIZE( size ) if( (size) > UINT32_MAX ) (size) += 8
5623 static uint64_t isom_update_mvhd_size( isom_mvhd_t *mvhd )
5625 if( !mvhd )
5626 return 0;
5627 mvhd->version = 0;
5628 if( mvhd->creation_time > UINT32_MAX || mvhd->modification_time > UINT32_MAX || mvhd->duration > UINT32_MAX )
5629 mvhd->version = 1;
5630 mvhd->size = ISOM_FULLBOX_COMMON_SIZE + 96 + (uint64_t)mvhd->version * 12;
5631 CHECK_LARGESIZE( mvhd->size );
5632 return mvhd->size;
5635 static uint64_t isom_update_iods_size( isom_iods_t *iods )
5637 if( !iods || !iods->OD )
5638 return 0;
5639 iods->size = ISOM_FULLBOX_COMMON_SIZE + mp4sys_update_ObjectDescriptor_size( iods->OD );
5640 CHECK_LARGESIZE( iods->size );
5641 return iods->size;
5644 static uint64_t isom_update_tkhd_size( isom_tkhd_t *tkhd )
5646 if( !tkhd )
5647 return 0;
5648 tkhd->version = 0;
5649 if( tkhd->creation_time > UINT32_MAX || tkhd->modification_time > UINT32_MAX || tkhd->duration > UINT32_MAX )
5650 tkhd->version = 1;
5651 tkhd->size = ISOM_FULLBOX_COMMON_SIZE + 80 + (uint64_t)tkhd->version * 12;
5652 CHECK_LARGESIZE( tkhd->size );
5653 return tkhd->size;
5656 static uint64_t isom_update_clef_size( isom_clef_t *clef )
5658 if( !clef )
5659 return 0;
5660 clef->size = ISOM_FULLBOX_COMMON_SIZE + 8;
5661 CHECK_LARGESIZE( clef->size );
5662 return clef->size;
5665 static uint64_t isom_update_prof_size( isom_prof_t *prof )
5667 if( !prof )
5668 return 0;
5669 prof->size = ISOM_FULLBOX_COMMON_SIZE + 8;
5670 CHECK_LARGESIZE( prof->size );
5671 return prof->size;
5674 static uint64_t isom_update_enof_size( isom_enof_t *enof )
5676 if( !enof )
5677 return 0;
5678 enof->size = ISOM_FULLBOX_COMMON_SIZE + 8;
5679 CHECK_LARGESIZE( enof->size );
5680 return enof->size;
5683 static uint64_t isom_update_tapt_size( isom_tapt_t *tapt )
5685 if( !tapt )
5686 return 0;
5687 tapt->size = ISOM_BASEBOX_COMMON_SIZE
5688 + isom_update_clef_size( tapt->clef )
5689 + isom_update_prof_size( tapt->prof )
5690 + isom_update_enof_size( tapt->enof );
5691 CHECK_LARGESIZE( tapt->size );
5692 return tapt->size;
5695 static uint64_t isom_update_elst_size( isom_elst_t *elst )
5697 if( !elst || !elst->list )
5698 return 0;
5699 uint32_t i = 0;
5700 elst->version = 0;
5701 for( lsmash_entry_t *entry = elst->list->head; entry; entry = entry->next, i++ )
5703 isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data;
5704 if( data->segment_duration > UINT32_MAX || data->media_time > INT32_MAX || data->media_time < INT32_MIN )
5705 elst->version = 1;
5707 elst->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)i * ( elst->version ? 20 : 12 );
5708 CHECK_LARGESIZE( elst->size );
5709 return elst->size;
5712 static uint64_t isom_update_edts_size( isom_edts_t *edts )
5714 if( !edts )
5715 return 0;
5716 edts->size = ISOM_BASEBOX_COMMON_SIZE + isom_update_elst_size( edts->elst );
5717 CHECK_LARGESIZE( edts->size );
5718 return edts->size;
5721 static uint64_t isom_update_tref_size( isom_tref_t *tref )
5723 if( !tref )
5724 return 0;
5725 tref->size = ISOM_BASEBOX_COMMON_SIZE;
5726 if( tref->ref_list )
5727 for( lsmash_entry_t *entry = tref->ref_list->head; entry; entry = entry->next )
5729 isom_tref_type_t *ref = (isom_tref_type_t *)entry->data;
5730 ref->size = ISOM_BASEBOX_COMMON_SIZE + (uint64_t)ref->ref_count * 4;
5731 CHECK_LARGESIZE( ref->size );
5732 tref->size += ref->size;
5734 CHECK_LARGESIZE( tref->size );
5735 return tref->size;
5738 static uint64_t isom_update_mdhd_size( isom_mdhd_t *mdhd )
5740 if( !mdhd )
5741 return 0;
5742 mdhd->version = 0;
5743 if( mdhd->creation_time > UINT32_MAX || mdhd->modification_time > UINT32_MAX || mdhd->duration > UINT32_MAX )
5744 mdhd->version = 1;
5745 mdhd->size = ISOM_FULLBOX_COMMON_SIZE + 20 + (uint64_t)mdhd->version * 12;
5746 CHECK_LARGESIZE( mdhd->size );
5747 return mdhd->size;
5750 static uint64_t isom_update_hdlr_size( isom_hdlr_t *hdlr )
5752 if( !hdlr )
5753 return 0;
5754 hdlr->size = ISOM_FULLBOX_COMMON_SIZE + 20 + (uint64_t)hdlr->componentName_length;
5755 CHECK_LARGESIZE( hdlr->size );
5756 return hdlr->size;
5759 static uint64_t isom_update_dref_entry_size( isom_dref_entry_t *urln )
5761 if( !urln )
5762 return 0;
5763 urln->size = ISOM_FULLBOX_COMMON_SIZE + (uint64_t)urln->name_length + urln->location_length;
5764 CHECK_LARGESIZE( urln->size );
5765 return urln->size;
5768 static uint64_t isom_update_dref_size( isom_dref_t *dref )
5770 if( !dref || !dref->list )
5771 return 0;
5772 dref->size = ISOM_LIST_FULLBOX_COMMON_SIZE;
5773 if( dref->list )
5774 for( lsmash_entry_t *entry = dref->list->head; entry; entry = entry->next )
5776 isom_dref_entry_t *data = (isom_dref_entry_t *)entry->data;
5777 dref->size += isom_update_dref_entry_size( data );
5779 CHECK_LARGESIZE( dref->size );
5780 return dref->size;
5783 static uint64_t isom_update_dinf_size( isom_dinf_t *dinf )
5785 if( !dinf )
5786 return 0;
5787 dinf->size = ISOM_BASEBOX_COMMON_SIZE + isom_update_dref_size( dinf->dref );
5788 CHECK_LARGESIZE( dinf->size );
5789 return dinf->size;
5792 static uint64_t isom_update_vmhd_size( isom_vmhd_t *vmhd )
5794 if( !vmhd )
5795 return 0;
5796 vmhd->size = ISOM_FULLBOX_COMMON_SIZE + 8;
5797 CHECK_LARGESIZE( vmhd->size );
5798 return vmhd->size;
5801 static uint64_t isom_update_smhd_size( isom_smhd_t *smhd )
5803 if( !smhd )
5804 return 0;
5805 smhd->size = ISOM_FULLBOX_COMMON_SIZE + 4;
5806 CHECK_LARGESIZE( smhd->size );
5807 return smhd->size;
5810 static uint64_t isom_update_hmhd_size( isom_hmhd_t *hmhd )
5812 if( !hmhd )
5813 return 0;
5814 hmhd->size = ISOM_FULLBOX_COMMON_SIZE + 16;
5815 CHECK_LARGESIZE( hmhd->size );
5816 return hmhd->size;
5819 static uint64_t isom_update_nmhd_size( isom_nmhd_t *nmhd )
5821 if( !nmhd )
5822 return 0;
5823 nmhd->size = ISOM_FULLBOX_COMMON_SIZE;
5824 CHECK_LARGESIZE( nmhd->size );
5825 return nmhd->size;
5828 static uint64_t isom_update_gmin_size( isom_gmin_t *gmin )
5830 if( !gmin )
5831 return 0;
5832 gmin->size = ISOM_FULLBOX_COMMON_SIZE + 12;
5833 CHECK_LARGESIZE( gmin->size );
5834 return gmin->size;
5837 static uint64_t isom_update_text_size( isom_text_t *text )
5839 if( !text )
5840 return 0;
5841 text->size = ISOM_BASEBOX_COMMON_SIZE + 36;
5842 CHECK_LARGESIZE( text->size );
5843 return text->size;
5846 static uint64_t isom_update_gmhd_size( isom_gmhd_t *gmhd )
5848 if( !gmhd )
5849 return 0;
5850 gmhd->size = ISOM_BASEBOX_COMMON_SIZE
5851 + isom_update_gmin_size( gmhd->gmin )
5852 + isom_update_text_size( gmhd->text );
5853 CHECK_LARGESIZE( gmhd->size );
5854 return gmhd->size;
5857 static uint64_t isom_update_pasp_size( isom_pasp_t *pasp )
5859 if( !pasp )
5860 return 0;
5861 pasp->size = ISOM_BASEBOX_COMMON_SIZE + 8;
5862 CHECK_LARGESIZE( pasp->size );
5863 return pasp->size;
5866 static uint64_t isom_update_clap_size( isom_clap_t *clap )
5868 if( !clap )
5869 return 0;
5870 clap->size = ISOM_BASEBOX_COMMON_SIZE + 32;
5871 CHECK_LARGESIZE( clap->size );
5872 return clap->size;
5875 static uint64_t isom_update_colr_size( isom_colr_t *colr )
5877 if( !colr || colr->color_parameter_type == QT_COLOR_PARAMETER_TYPE_PROF )
5878 return 0;
5879 colr->size = ISOM_BASEBOX_COMMON_SIZE + 10;
5880 CHECK_LARGESIZE( colr->size );
5881 return colr->size;
5884 static uint64_t isom_update_stsl_size( isom_stsl_t *stsl )
5886 if( !stsl )
5887 return 0;
5888 stsl->size = ISOM_FULLBOX_COMMON_SIZE + 6;
5889 CHECK_LARGESIZE( stsl->size );
5890 return stsl->size;
5893 static uint64_t isom_update_esds_size( isom_esds_t *esds )
5895 if( !esds )
5896 return 0;
5897 esds->size = ISOM_FULLBOX_COMMON_SIZE + mp4sys_update_ES_Descriptor_size( esds->ES );
5898 CHECK_LARGESIZE( esds->size );
5899 return esds->size;
5902 static uint64_t isom_update_avcC_size( isom_avcC_t *avcC )
5904 if( !avcC || !avcC->sequenceParameterSets || !avcC->pictureParameterSets )
5905 return 0;
5906 uint64_t size = ISOM_BASEBOX_COMMON_SIZE + 7;
5907 for( lsmash_entry_t *entry = avcC->sequenceParameterSets->head; entry; entry = entry->next )
5909 isom_avcC_ps_entry_t *data = (isom_avcC_ps_entry_t *)entry->data;
5910 size += 2 + data->parameterSetLength;
5912 for( lsmash_entry_t *entry = avcC->pictureParameterSets->head; entry; entry = entry->next )
5914 isom_avcC_ps_entry_t *data = (isom_avcC_ps_entry_t *)entry->data;
5915 size += 2 + data->parameterSetLength;
5917 if( ISOM_REQUIRES_AVCC_EXTENSION( avcC->AVCProfileIndication ) )
5919 size += 4;
5920 for( lsmash_entry_t *entry = avcC->sequenceParameterSetExt->head; entry; entry = entry->next )
5922 isom_avcC_ps_entry_t *data = (isom_avcC_ps_entry_t *)entry->data;
5923 size += 2 + data->parameterSetLength;
5926 avcC->size = size;
5927 CHECK_LARGESIZE( avcC->size );
5928 return avcC->size;
5931 static uint64_t isom_update_btrt_size( isom_btrt_t *btrt )
5933 if( !btrt )
5934 return 0;
5935 btrt->size = ISOM_BASEBOX_COMMON_SIZE + 12;
5936 CHECK_LARGESIZE( btrt->size );
5937 return btrt->size;
5940 static uint64_t isom_update_visual_entry_size( isom_visual_entry_t *visual )
5942 if( !visual )
5943 return 0;
5944 visual->size = ISOM_BASEBOX_COMMON_SIZE + 78
5945 + isom_update_clap_size( visual->clap )
5946 + isom_update_pasp_size( visual->pasp )
5947 + isom_update_colr_size( visual->colr )
5948 + isom_update_stsl_size( visual->stsl )
5949 + isom_update_esds_size( visual->esds )
5950 + isom_update_avcC_size( visual->avcC )
5951 + isom_update_btrt_size( visual->btrt );
5952 CHECK_LARGESIZE( visual->size );
5953 return visual->size;
5956 #if 0
5957 static uint64_t isom_update_mp4s_entry_size( isom_mp4s_entry_t *mp4s )
5959 if( !mp4s || mp4s->type != ISOM_CODEC_TYPE_MP4S_SYSTEM )
5960 return 0;
5961 mp4s->size = ISOM_BASEBOX_COMMON_SIZE + 8 + isom_update_esds_size( mp4s->esds );
5962 CHECK_LARGESIZE( mp4s->size );
5963 return mp4s->size;
5965 #endif
5967 static uint64_t isom_update_frma_size( isom_frma_t *frma )
5969 if( !frma )
5970 return 0;
5971 frma->size = ISOM_BASEBOX_COMMON_SIZE + 4;
5972 CHECK_LARGESIZE( frma->size );
5973 return frma->size;
5976 static uint64_t isom_update_enda_size( isom_enda_t *enda )
5978 if( !enda )
5979 return 0;
5980 enda->size = ISOM_BASEBOX_COMMON_SIZE + 2;
5981 CHECK_LARGESIZE( enda->size );
5982 return enda->size;
5985 static uint64_t isom_update_mp4a_size( isom_mp4a_t *mp4a )
5987 if( !mp4a )
5988 return 0;
5989 mp4a->size = ISOM_BASEBOX_COMMON_SIZE + 4;
5990 CHECK_LARGESIZE( mp4a->size );
5991 return mp4a->size;
5994 static uint64_t isom_update_terminator_size( isom_terminator_t *terminator )
5996 if( !terminator )
5997 return 0;
5998 terminator->size = ISOM_BASEBOX_COMMON_SIZE;
5999 CHECK_LARGESIZE( terminator->size );
6000 return terminator->size;
6003 static uint64_t isom_update_wave_size( isom_wave_t *wave )
6005 if( !wave )
6006 return 0;
6007 wave->size = ISOM_BASEBOX_COMMON_SIZE
6008 + isom_update_frma_size( wave->frma )
6009 + isom_update_enda_size( wave->enda )
6010 + isom_update_mp4a_size( wave->mp4a )
6011 + isom_update_esds_size( wave->esds )
6012 + isom_update_terminator_size( wave->terminator )
6013 + (uint64_t)wave->exdata_length;
6014 CHECK_LARGESIZE( wave->size );
6015 return wave->size;
6018 static uint64_t isom_update_chan_size( isom_chan_t *chan )
6020 if( !chan )
6021 return 0;
6022 chan->size = ISOM_FULLBOX_COMMON_SIZE + 12 + 20 * (uint64_t)chan->numberChannelDescriptions;
6023 CHECK_LARGESIZE( chan->size );
6024 return chan->size;
6027 static uint64_t isom_update_audio_entry_size( isom_audio_entry_t *audio )
6029 if( !audio )
6030 return 0;
6031 audio->size = ISOM_BASEBOX_COMMON_SIZE + 28
6032 + isom_update_esds_size( audio->esds )
6033 + isom_update_wave_size( audio->wave )
6034 + isom_update_chan_size( audio->chan )
6035 + (uint64_t)audio->exdata_length;
6036 if( audio->version == 1 )
6037 audio->size += 16;
6038 else if( audio->version == 2 )
6039 audio->size += 36;
6040 CHECK_LARGESIZE( audio->size );
6041 return audio->size;
6044 static uint64_t isom_update_text_entry_size( isom_text_entry_t *text )
6046 if( !text )
6047 return 0;
6048 text->size = ISOM_BASEBOX_COMMON_SIZE + 51 + (uint64_t)text->font_name_length;
6049 CHECK_LARGESIZE( text->size );
6050 return text->size;
6053 static uint64_t isom_update_ftab_size( isom_ftab_t *ftab )
6055 if( !ftab || !ftab->list )
6056 return 0;
6057 ftab->size = ISOM_BASEBOX_COMMON_SIZE + 2;
6058 for( lsmash_entry_t *entry = ftab->list->head; entry; entry = entry->next )
6060 isom_font_record_t *data = (isom_font_record_t *)entry->data;
6061 ftab->size += 3 + data->font_name_length;
6063 CHECK_LARGESIZE( ftab->size );
6064 return ftab->size;
6067 static uint64_t isom_update_tx3g_entry_size( isom_tx3g_entry_t *tx3g )
6069 if( !tx3g )
6070 return 0;
6071 tx3g->size = ISOM_BASEBOX_COMMON_SIZE + 38 + isom_update_ftab_size( tx3g->ftab );
6072 CHECK_LARGESIZE( tx3g->size );
6073 return tx3g->size;
6076 static uint64_t isom_update_stsd_size( isom_stsd_t *stsd )
6078 if( !stsd || !stsd->list )
6079 return 0;
6080 uint64_t size = ISOM_LIST_FULLBOX_COMMON_SIZE;
6081 for( lsmash_entry_t *entry = stsd->list->head; entry; entry = entry->next )
6083 isom_sample_entry_t *data = (isom_sample_entry_t *)entry->data;
6084 switch( data->type )
6086 case ISOM_CODEC_TYPE_AVC1_VIDEO :
6087 #ifdef LSMASH_DEMUXER_ENABLED
6088 case ISOM_CODEC_TYPE_MP4V_VIDEO :
6089 #endif
6090 #if 0
6091 case ISOM_CODEC_TYPE_AVC2_VIDEO :
6092 case ISOM_CODEC_TYPE_AVCP_VIDEO :
6093 case ISOM_CODEC_TYPE_SVC1_VIDEO :
6094 case ISOM_CODEC_TYPE_MVC1_VIDEO :
6095 case ISOM_CODEC_TYPE_MVC2_VIDEO :
6096 case ISOM_CODEC_TYPE_DRAC_VIDEO :
6097 case ISOM_CODEC_TYPE_ENCV_VIDEO :
6098 case ISOM_CODEC_TYPE_MJP2_VIDEO :
6099 case ISOM_CODEC_TYPE_S263_VIDEO :
6100 case ISOM_CODEC_TYPE_VC_1_VIDEO :
6101 #endif
6102 size += isom_update_visual_entry_size( (isom_visual_entry_t *)data );
6103 break;
6104 #if 0
6105 case ISOM_CODEC_TYPE_MP4S_SYSTEM :
6106 size += isom_update_mp4s_entry_size( (isom_mp4s_entry_t *)data );
6107 break;
6108 #endif
6109 case ISOM_CODEC_TYPE_MP4A_AUDIO :
6110 case ISOM_CODEC_TYPE_AC_3_AUDIO :
6111 case ISOM_CODEC_TYPE_ALAC_AUDIO :
6112 case ISOM_CODEC_TYPE_SAMR_AUDIO :
6113 case ISOM_CODEC_TYPE_SAWB_AUDIO :
6114 case QT_CODEC_TYPE_23NI_AUDIO :
6115 case QT_CODEC_TYPE_NONE_AUDIO :
6116 case QT_CODEC_TYPE_LPCM_AUDIO :
6117 case QT_CODEC_TYPE_RAW_AUDIO :
6118 case QT_CODEC_TYPE_SOWT_AUDIO :
6119 case QT_CODEC_TYPE_TWOS_AUDIO :
6120 case QT_CODEC_TYPE_FL32_AUDIO :
6121 case QT_CODEC_TYPE_FL64_AUDIO :
6122 case QT_CODEC_TYPE_IN24_AUDIO :
6123 case QT_CODEC_TYPE_IN32_AUDIO :
6124 case QT_CODEC_TYPE_NOT_SPECIFIED :
6125 #ifdef LSMASH_DEMUXER_ENABLED
6126 case ISOM_CODEC_TYPE_EC_3_AUDIO :
6127 #endif
6128 #if 0
6129 case ISOM_CODEC_TYPE_DRA1_AUDIO :
6130 case ISOM_CODEC_TYPE_DTSC_AUDIO :
6131 case ISOM_CODEC_TYPE_DTSH_AUDIO :
6132 case ISOM_CODEC_TYPE_DTSL_AUDIO :
6133 case ISOM_CODEC_TYPE_ENCA_AUDIO :
6134 case ISOM_CODEC_TYPE_G719_AUDIO :
6135 case ISOM_CODEC_TYPE_G726_AUDIO :
6136 case ISOM_CODEC_TYPE_M4AE_AUDIO :
6137 case ISOM_CODEC_TYPE_MLPA_AUDIO :
6138 case ISOM_CODEC_TYPE_RAW_AUDIO :
6139 case ISOM_CODEC_TYPE_SAWP_AUDIO :
6140 case ISOM_CODEC_TYPE_SEVC_AUDIO :
6141 case ISOM_CODEC_TYPE_SQCP_AUDIO :
6142 case ISOM_CODEC_TYPE_SSMV_AUDIO :
6143 case ISOM_CODEC_TYPE_TWOS_AUDIO :
6144 #endif
6145 size += isom_update_audio_entry_size( (isom_audio_entry_t *)data );
6146 break;
6147 case ISOM_CODEC_TYPE_TX3G_TEXT :
6148 size += isom_update_tx3g_entry_size( (isom_tx3g_entry_t *)data );
6149 break;
6150 case QT_CODEC_TYPE_TEXT_TEXT :
6151 size += isom_update_text_entry_size( (isom_text_entry_t *)data );
6152 break;
6153 default :
6154 break;
6157 stsd->size = size;
6158 CHECK_LARGESIZE( stsd->size );
6159 return stsd->size;
6162 static uint64_t isom_update_stts_size( isom_stts_t *stts )
6164 if( !stts || !stts->list )
6165 return 0;
6166 stts->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stts->list->entry_count * 8;
6167 CHECK_LARGESIZE( stts->size );
6168 return stts->size;
6171 static uint64_t isom_update_ctts_size( isom_ctts_t *ctts )
6173 if( !ctts || !ctts->list )
6174 return 0;
6175 ctts->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)ctts->list->entry_count * 8;
6176 CHECK_LARGESIZE( ctts->size );
6177 return ctts->size;
6180 static uint64_t isom_update_cslg_size( isom_cslg_t *cslg )
6182 if( !cslg )
6183 return 0;
6184 cslg->size = ISOM_FULLBOX_COMMON_SIZE + 20;
6185 CHECK_LARGESIZE( cslg->size );
6186 return cslg->size;
6189 static uint64_t isom_update_stsz_size( isom_stsz_t *stsz )
6191 if( !stsz )
6192 return 0;
6193 stsz->size = ISOM_FULLBOX_COMMON_SIZE + 8 + ( stsz->list ? (uint64_t)stsz->list->entry_count * 4 : 0 );
6194 CHECK_LARGESIZE( stsz->size );
6195 return stsz->size;
6198 static uint64_t isom_update_stss_size( isom_stss_t *stss )
6200 if( !stss || !stss->list )
6201 return 0;
6202 stss->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stss->list->entry_count * 4;
6203 CHECK_LARGESIZE( stss->size );
6204 return stss->size;
6207 static uint64_t isom_update_stps_size( isom_stps_t *stps )
6209 if( !stps || !stps->list )
6210 return 0;
6211 stps->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stps->list->entry_count * 4;
6212 CHECK_LARGESIZE( stps->size );
6213 return stps->size;
6216 static uint64_t isom_update_sdtp_size( isom_sdtp_t *sdtp )
6218 if( !sdtp || !sdtp->list )
6219 return 0;
6220 sdtp->size = ISOM_FULLBOX_COMMON_SIZE + (uint64_t)sdtp->list->entry_count;
6221 CHECK_LARGESIZE( sdtp->size );
6222 return sdtp->size;
6225 static uint64_t isom_update_stsc_size( isom_stsc_t *stsc )
6227 if( !stsc || !stsc->list )
6228 return 0;
6229 stsc->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stsc->list->entry_count * 12;
6230 CHECK_LARGESIZE( stsc->size );
6231 return stsc->size;
6234 static uint64_t isom_update_stco_size( isom_stco_t *stco )
6236 if( !stco || !stco->list )
6237 return 0;
6238 stco->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stco->list->entry_count * (stco->large_presentation ? 8 : 4);
6239 CHECK_LARGESIZE( stco->size );
6240 return stco->size;
6243 static uint64_t isom_update_sbgp_size( isom_sbgp_entry_t *sbgp )
6245 if( !sbgp || !sbgp->list )
6246 return 0;
6247 sbgp->size = ISOM_LIST_FULLBOX_COMMON_SIZE + 4 + (uint64_t)sbgp->list->entry_count * 8;
6248 CHECK_LARGESIZE( sbgp->size );
6249 return sbgp->size;
6252 static uint64_t isom_update_sgpd_size( isom_sgpd_entry_t *sgpd )
6254 if( !sgpd || !sgpd->list )
6255 return 0;
6256 uint64_t size = ISOM_LIST_FULLBOX_COMMON_SIZE + (1 + (sgpd->version == 1)) * 4;
6257 size += (uint64_t)sgpd->list->entry_count * ((sgpd->version == 1) && !sgpd->default_length) * 4;
6258 switch( sgpd->grouping_type )
6260 case ISOM_GROUP_TYPE_RAP :
6261 size += sgpd->list->entry_count;
6262 break;
6263 case ISOM_GROUP_TYPE_ROLL :
6264 size += (uint64_t)sgpd->list->entry_count * 2;
6265 break;
6266 default :
6267 /* We don't consider other grouping types currently. */
6268 break;
6270 sgpd->size = size;
6271 CHECK_LARGESIZE( sgpd->size );
6272 return sgpd->size;
6275 static uint64_t isom_update_stbl_size( isom_stbl_t *stbl )
6277 if( !stbl )
6278 return 0;
6279 stbl->size = ISOM_BASEBOX_COMMON_SIZE
6280 + isom_update_stsd_size( stbl->stsd )
6281 + isom_update_stts_size( stbl->stts )
6282 + isom_update_ctts_size( stbl->ctts )
6283 + isom_update_cslg_size( stbl->cslg )
6284 + isom_update_stsz_size( stbl->stsz )
6285 + isom_update_stss_size( stbl->stss )
6286 + isom_update_stps_size( stbl->stps )
6287 + isom_update_sdtp_size( stbl->sdtp )
6288 + isom_update_stsc_size( stbl->stsc )
6289 + isom_update_stco_size( stbl->stco );
6290 if( stbl->sgpd_list )
6291 for( lsmash_entry_t *entry = stbl->sgpd_list->head; entry; entry = entry->next )
6292 stbl->size += isom_update_sgpd_size( (isom_sgpd_entry_t *)entry->data );
6293 if( stbl->sbgp_list )
6294 for( lsmash_entry_t *entry = stbl->sbgp_list->head; entry; entry = entry->next )
6295 stbl->size += isom_update_sbgp_size( (isom_sbgp_entry_t *)entry->data );
6296 CHECK_LARGESIZE( stbl->size );
6297 return stbl->size;
6300 static uint64_t isom_update_minf_size( isom_minf_t *minf )
6302 if( !minf )
6303 return 0;
6304 minf->size = ISOM_BASEBOX_COMMON_SIZE
6305 + isom_update_vmhd_size( minf->vmhd )
6306 + isom_update_smhd_size( minf->smhd )
6307 + isom_update_hmhd_size( minf->hmhd )
6308 + isom_update_nmhd_size( minf->nmhd )
6309 + isom_update_gmhd_size( minf->gmhd )
6310 + isom_update_hdlr_size( minf->hdlr )
6311 + isom_update_dinf_size( minf->dinf )
6312 + isom_update_stbl_size( minf->stbl );
6313 CHECK_LARGESIZE( minf->size );
6314 return minf->size;
6317 static uint64_t isom_update_mdia_size( isom_mdia_t *mdia )
6319 if( !mdia )
6320 return 0;
6321 mdia->size = ISOM_BASEBOX_COMMON_SIZE
6322 + isom_update_mdhd_size( mdia->mdhd )
6323 + isom_update_hdlr_size( mdia->hdlr )
6324 + isom_update_minf_size( mdia->minf );
6325 CHECK_LARGESIZE( mdia->size );
6326 return mdia->size;
6329 static uint64_t isom_update_chpl_size( isom_chpl_t *chpl )
6331 if( !chpl )
6332 return 0;
6333 chpl->size = ISOM_FULLBOX_COMMON_SIZE + 4 * (chpl->version == 1) + 1;
6334 for( lsmash_entry_t *entry = chpl->list->head; entry; entry = entry->next )
6336 isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data;
6337 chpl->size += 9 + data->chapter_name_length;
6339 CHECK_LARGESIZE( chpl->size );
6340 return chpl->size;
6343 static uint64_t isom_update_mean_size( isom_mean_t *mean )
6345 if( !mean )
6346 return 0;
6347 mean->size = ISOM_FULLBOX_COMMON_SIZE + mean->meaning_string_length;
6348 CHECK_LARGESIZE( mean->size );
6349 return mean->size;
6352 static uint64_t isom_update_name_size( isom_name_t *name )
6354 if( !name )
6355 return 0;
6356 name->size = ISOM_FULLBOX_COMMON_SIZE + name->name_length;
6357 CHECK_LARGESIZE( name->size );
6358 return name->size;
6361 static uint64_t isom_update_data_size( isom_data_t *data )
6363 if( !data )
6364 return 0;
6365 data->size = ISOM_BASEBOX_COMMON_SIZE + 8 + data->value_length;
6366 CHECK_LARGESIZE( data->size );
6367 return data->size;
6370 static uint64_t isom_update_metaitem_size( isom_metaitem_t *metaitem )
6372 if( !metaitem )
6373 return 0;
6374 metaitem->size = ISOM_BASEBOX_COMMON_SIZE
6375 + isom_update_mean_size( metaitem->mean )
6376 + isom_update_name_size( metaitem->name )
6377 + isom_update_data_size( metaitem->data );
6378 CHECK_LARGESIZE( metaitem->size );
6379 return metaitem->size;
6382 static uint64_t isom_update_ilst_size( isom_ilst_t *ilst )
6384 if( !ilst )
6385 return 0;
6386 ilst->size = ISOM_BASEBOX_COMMON_SIZE;
6387 for( lsmash_entry_t *entry = ilst->item_list->head; entry; entry = entry->next )
6388 ilst->size += isom_update_metaitem_size( (isom_metaitem_t *)entry->data );
6389 CHECK_LARGESIZE( ilst->size );
6390 return ilst->size;
6393 static uint64_t isom_update_meta_size( isom_meta_t *meta )
6395 if( !meta )
6396 return 0;
6397 meta->size = ISOM_FULLBOX_COMMON_SIZE
6398 + isom_update_hdlr_size( meta->hdlr )
6399 + isom_update_dinf_size( meta->dinf )
6400 + isom_update_ilst_size( meta->ilst );
6401 CHECK_LARGESIZE( meta->size );
6402 return meta->size;
6405 static uint64_t isom_update_udta_size( isom_udta_t *udta_moov, isom_udta_t *udta_trak )
6407 isom_udta_t *udta = udta_trak ? udta_trak : udta_moov ? udta_moov : NULL;
6408 if( !udta )
6409 return 0;
6410 udta->size = ISOM_BASEBOX_COMMON_SIZE
6411 + ( udta_moov ? isom_update_chpl_size( udta->chpl ) : 0 )
6412 + isom_update_meta_size( udta->meta );
6413 CHECK_LARGESIZE( udta->size );
6414 return udta->size;
6417 static uint64_t isom_update_trak_entry_size( isom_trak_entry_t *trak )
6419 if( !trak )
6420 return 0;
6421 trak->size = ISOM_BASEBOX_COMMON_SIZE
6422 + isom_update_tkhd_size( trak->tkhd )
6423 + isom_update_tapt_size( trak->tapt )
6424 + isom_update_edts_size( trak->edts )
6425 + isom_update_tref_size( trak->tref )
6426 + isom_update_mdia_size( trak->mdia )
6427 + isom_update_udta_size( NULL, trak->udta )
6428 + isom_update_meta_size( trak->meta );
6429 CHECK_LARGESIZE( trak->size );
6430 return trak->size;
6433 static uint64_t isom_update_mehd_size( isom_mehd_t *mehd )
6435 if( !mehd )
6436 return 0;
6437 if( mehd->fragment_duration > UINT32_MAX )
6438 mehd->version = 1;
6439 mehd->size = ISOM_FULLBOX_COMMON_SIZE + 4 * (1 + (mehd->version == 1));
6440 CHECK_LARGESIZE( mehd->size );
6441 return mehd->size;
6444 static uint64_t isom_update_trex_entry_size( isom_trex_entry_t *trex )
6446 if( !trex )
6447 return 0;
6448 trex->size = ISOM_FULLBOX_COMMON_SIZE + 20;
6449 CHECK_LARGESIZE( trex->size );
6450 return trex->size;
6453 static uint64_t isom_update_mvex_size( isom_mvex_t *mvex )
6455 if( !mvex )
6456 return 0;
6457 mvex->size = ISOM_BASEBOX_COMMON_SIZE;
6458 if( mvex->trex_list )
6459 for( lsmash_entry_t *entry = mvex->trex_list->head; entry; entry = entry->next )
6461 isom_trex_entry_t *trex = (isom_trex_entry_t *)entry->data;
6462 mvex->size += isom_update_trex_entry_size( trex );
6464 if( mvex->root->bs->stream != stdout )
6465 mvex->size += mvex->mehd ? isom_update_mehd_size( mvex->mehd ) : 20; /* 20 bytes is of placeholder. */
6466 CHECK_LARGESIZE( mvex->size );
6467 return mvex->size;
6470 static int isom_update_moov_size( isom_moov_t *moov )
6472 if( !moov )
6473 return -1;
6474 moov->size = ISOM_BASEBOX_COMMON_SIZE
6475 + isom_update_mvhd_size( moov->mvhd )
6476 + isom_update_iods_size( moov->iods )
6477 + isom_update_udta_size( moov->udta, NULL )
6478 + isom_update_meta_size( moov->meta )
6479 + isom_update_mvex_size( moov->mvex );
6480 if( moov->trak_list )
6481 for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
6483 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
6484 moov->size += isom_update_trak_entry_size( trak );
6486 CHECK_LARGESIZE( moov->size );
6487 return 0;
6490 static uint64_t isom_update_mfhd_size( isom_mfhd_t *mfhd )
6492 if( !mfhd )
6493 return 0;
6494 mfhd->size = ISOM_FULLBOX_COMMON_SIZE + 4;
6495 CHECK_LARGESIZE( mfhd->size );
6496 return mfhd->size;
6499 static uint64_t isom_update_tfhd_size( isom_tfhd_t *tfhd )
6501 if( !tfhd )
6502 return 0;
6503 tfhd->size = ISOM_FULLBOX_COMMON_SIZE
6505 + 8 * !!( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT )
6506 + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT )
6507 + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT )
6508 + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT )
6509 + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT );
6510 CHECK_LARGESIZE( tfhd->size );
6511 return tfhd->size;
6514 static uint64_t isom_update_trun_entry_size( isom_trun_entry_t *trun )
6516 if( !trun )
6517 return 0;
6518 trun->size = ISOM_FULLBOX_COMMON_SIZE
6520 + 4 * !!( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT )
6521 + 4 * !!( trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT );
6522 uint64_t row_size = 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT )
6523 + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT )
6524 + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT )
6525 + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT );
6526 trun->size += row_size * trun->sample_count;
6527 CHECK_LARGESIZE( trun->size );
6528 return trun->size;
6531 static uint64_t isom_update_traf_entry_size( isom_traf_entry_t *traf )
6533 if( !traf )
6534 return 0;
6535 traf->size = ISOM_BASEBOX_COMMON_SIZE + isom_update_tfhd_size( traf->tfhd );
6536 if( traf->trun_list )
6537 for( lsmash_entry_t *entry = traf->trun_list->head; entry; entry = entry->next )
6539 isom_trun_entry_t *trun = (isom_trun_entry_t *)entry->data;
6540 traf->size += isom_update_trun_entry_size( trun );
6542 CHECK_LARGESIZE( traf->size );
6543 return traf->size;
6546 static int isom_update_moof_entry_size( isom_moof_entry_t *moof )
6548 if( !moof )
6549 return -1;
6550 moof->size = ISOM_BASEBOX_COMMON_SIZE + isom_update_mfhd_size( moof->mfhd );
6551 if( moof->traf_list )
6552 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
6554 isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
6555 moof->size += isom_update_traf_entry_size( traf );
6557 CHECK_LARGESIZE( moof->size );
6558 return 0;
6561 static uint64_t isom_update_tfra_entry_size( isom_tfra_entry_t *tfra )
6563 if( !tfra )
6564 return 0;
6565 tfra->size = ISOM_FULLBOX_COMMON_SIZE + 12;
6566 uint32_t entry_size = 8 * (1 + (tfra->version == 1))
6567 + tfra->length_size_of_traf_num + 1
6568 + tfra->length_size_of_trun_num + 1
6569 + tfra->length_size_of_sample_num + 1;
6570 tfra->size += entry_size * tfra->number_of_entry;
6571 CHECK_LARGESIZE( tfra->size );
6572 return tfra->size;
6575 static uint64_t isom_update_mfro_size( isom_mfro_t *mfro )
6577 if( !mfro )
6578 return 0;
6579 mfro->size = ISOM_FULLBOX_COMMON_SIZE + 4;
6580 CHECK_LARGESIZE( mfro->size );
6581 return mfro->size;
6584 static int isom_update_mfra_size( isom_mfra_t *mfra )
6586 if( !mfra )
6587 return -1;
6588 mfra->size = ISOM_BASEBOX_COMMON_SIZE;
6589 if( mfra->tfra_list )
6590 for( lsmash_entry_t *entry = mfra->tfra_list->head; entry; entry = entry->next )
6592 isom_tfra_entry_t *tfra = (isom_tfra_entry_t *)entry->data;
6593 mfra->size += isom_update_tfra_entry_size( tfra );
6595 CHECK_LARGESIZE( mfra->size );
6596 if( mfra->mfro )
6598 mfra->size += isom_update_mfro_size( mfra->mfro );
6599 mfra->mfro->length = mfra->size;
6601 return 0;
6604 /*******************************
6605 public interfaces
6606 *******************************/
6608 /*---- track manipulators ----*/
6610 void lsmash_delete_track( lsmash_root_t *root, uint32_t track_ID )
6612 if( !root || !root->moov || !root->moov->trak_list )
6613 return;
6614 for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
6616 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
6617 if( !trak || !trak->tkhd )
6618 return;
6619 if( trak->tkhd->track_ID == track_ID )
6621 lsmash_entry_t *next = entry->next;
6622 lsmash_entry_t *prev = entry->prev;
6623 isom_remove_trak( trak );
6624 free( entry );
6625 entry = next;
6626 if( entry )
6628 if( prev )
6629 prev->next = entry;
6630 entry->prev = prev;
6632 return;
6637 uint32_t lsmash_create_track( lsmash_root_t *root, lsmash_media_type media_type )
6639 isom_trak_entry_t *trak = isom_add_trak( root );
6640 if( !trak )
6641 return 0;
6642 if( isom_add_tkhd( trak, media_type )
6643 || isom_add_mdia( trak )
6644 || isom_add_mdhd( trak->mdia, root->qt_compatible ? 0 : ISOM_LANGUAGE_CODE_UNDEFINED )
6645 || isom_add_minf( trak->mdia )
6646 || isom_add_stbl( trak->mdia->minf )
6647 || isom_add_dinf( trak->mdia->minf )
6648 || isom_add_dref( trak->mdia->minf->dinf )
6649 || isom_add_stsd( trak->mdia->minf->stbl )
6650 || isom_add_stts( trak->mdia->minf->stbl )
6651 || isom_add_stsc( trak->mdia->minf->stbl )
6652 || isom_add_stco( trak->mdia->minf->stbl )
6653 || isom_add_stsz( trak->mdia->minf->stbl ) )
6654 return 0;
6655 if( isom_add_hdlr( trak->mdia, NULL, NULL, media_type ) )
6656 return 0;
6657 if( root->qt_compatible && isom_add_hdlr( NULL, NULL, trak->mdia->minf, QT_REFERENCE_HANDLER_TYPE_URL ) )
6658 return 0;
6659 switch( media_type )
6661 case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK :
6662 if( isom_add_vmhd( trak->mdia->minf ) )
6663 return 0;
6664 break;
6665 case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK :
6666 if( isom_add_smhd( trak->mdia->minf ) )
6667 return 0;
6668 break;
6669 case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK :
6670 if( isom_add_hmhd( trak->mdia->minf ) )
6671 return 0;
6672 break;
6673 case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK :
6674 if( root->qt_compatible || root->itunes_audio )
6676 if( isom_add_gmhd( trak->mdia->minf )
6677 || isom_add_gmin( trak->mdia->minf->gmhd )
6678 || isom_add_text( trak->mdia->minf->gmhd ) )
6679 return 0;
6681 else
6682 return 0; /* We support only reference text media track for chapter yet. */
6683 break;
6684 default :
6685 if( isom_add_nmhd( trak->mdia->minf ) )
6686 return 0;
6687 break;
6689 return trak->tkhd->track_ID;
6692 uint32_t lsmash_get_track_ID( lsmash_root_t *root, uint32_t track_number )
6694 if( !root || !root->moov )
6695 return 0;
6696 isom_trak_entry_t *trak = (isom_trak_entry_t *)lsmash_get_entry_data( root->moov->trak_list, track_number );
6697 if( !trak || !trak->tkhd )
6698 return 0;
6699 return trak->tkhd->track_ID;
6702 void lsmash_initialize_track_parameters( lsmash_track_parameters_t *param )
6704 memset( param, 0, sizeof(lsmash_track_parameters_t) );
6705 param->audio_volume = 0x0100;
6706 param->matrix[0] = 0x00010000;
6707 param->matrix[4] = 0x00010000;
6708 param->matrix[8] = 0x40000000;
6711 int lsmash_set_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param )
6713 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6714 if( !trak || !trak->mdia || !trak->mdia->hdlr || !root->moov->mvhd )
6715 return -1;
6716 /* Prepare Track Aperture Modes if required. */
6717 if( root->qt_compatible && param->aperture_modes )
6719 if( !trak->tapt && isom_add_tapt( trak ) )
6720 return -1;
6721 isom_tapt_t *tapt = trak->tapt;
6722 if( (!tapt->clef && isom_add_clef( tapt ))
6723 || (!tapt->prof && isom_add_prof( tapt ))
6724 || (!tapt->enof && isom_add_enof( tapt )) )
6725 return -1;
6727 else
6728 isom_remove_tapt( trak->tapt );
6729 /* Set up Track Header. */
6730 uint32_t media_type = trak->mdia->hdlr->componentSubtype;
6731 isom_tkhd_t *tkhd = trak->tkhd;
6732 tkhd->flags = param->mode;
6733 tkhd->track_ID = param->track_ID ? param->track_ID : tkhd->track_ID;
6734 tkhd->duration = !trak->edts || !trak->edts->elst ? param->duration : tkhd->duration;
6735 tkhd->alternate_group = root->qt_compatible || root->itunes_audio || root->max_3gpp_version >= 4 ? param->alternate_group : 0;
6736 if( root->qt_compatible || root->itunes_audio )
6738 tkhd->layer = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->video_layer : 0;
6739 tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? param->audio_volume : 0;
6741 else
6743 tkhd->layer = 0;
6744 tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? 0x0100 : 0;
6746 for( int i = 0; i < 9; i++ )
6747 tkhd->matrix[i] = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->matrix[i] : 0;
6748 tkhd->width = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_width : 0;
6749 tkhd->height = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_height : 0;
6750 /* Update next_track_ID if needed. */
6751 if( root->moov->mvhd->next_track_ID <= tkhd->track_ID )
6752 root->moov->mvhd->next_track_ID = tkhd->track_ID + 1;
6753 return 0;
6756 int lsmash_get_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param )
6758 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6759 if( !trak )
6760 return -1;
6761 isom_tkhd_t *tkhd = trak->tkhd;
6762 param->mode = tkhd->flags;
6763 param->track_ID = tkhd->track_ID;
6764 param->duration = tkhd->duration;
6765 param->video_layer = tkhd->layer;
6766 param->alternate_group = tkhd->alternate_group;
6767 param->audio_volume = tkhd->volume;
6768 for( int i = 0; i < 9; i++ )
6769 param->matrix[i] = tkhd->matrix[i];
6770 param->display_width = tkhd->width;
6771 param->display_height = tkhd->height;
6772 param->aperture_modes = !!trak->tapt;
6773 return 0;
6776 static int isom_set_media_handler_name( lsmash_root_t *root, uint32_t track_ID, char *handler_name )
6778 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6779 if( !trak || !trak->mdia || !trak->mdia->hdlr )
6780 return -1;
6781 isom_hdlr_t *hdlr = trak->mdia->hdlr;
6782 uint8_t *name = NULL;
6783 uint32_t name_length = strlen( handler_name ) + root->isom_compatible + root->qt_compatible;
6784 if( root->qt_compatible )
6785 name_length = LSMASH_MIN( name_length, 255 );
6786 if( name_length > hdlr->componentName_length && hdlr->componentName )
6787 name = realloc( hdlr->componentName, name_length );
6788 else if( !hdlr->componentName )
6789 name = malloc( name_length );
6790 else
6791 name = hdlr->componentName;
6792 if( !name )
6793 return -1;
6794 if( root->qt_compatible )
6795 name[0] = name_length & 0xff;
6796 memcpy( name + root->qt_compatible, handler_name, strlen( handler_name ) );
6797 if( root->isom_compatible )
6798 name[name_length - 1] = 0;
6799 hdlr->componentName = name;
6800 hdlr->componentName_length = name_length;
6801 return 0;
6804 static int isom_set_data_handler_name( lsmash_root_t *root, uint32_t track_ID, char *handler_name )
6806 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6807 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->hdlr )
6808 return -1;
6809 isom_hdlr_t *hdlr = trak->mdia->minf->hdlr;
6810 uint8_t *name = NULL;
6811 uint32_t name_length = strlen( handler_name ) + root->isom_compatible + root->qt_compatible;
6812 if( root->qt_compatible )
6813 name_length = LSMASH_MIN( name_length, 255 );
6814 if( name_length > hdlr->componentName_length && hdlr->componentName )
6815 name = realloc( hdlr->componentName, name_length );
6816 else if( !hdlr->componentName )
6817 name = malloc( name_length );
6818 else
6819 name = hdlr->componentName;
6820 if( !name )
6821 return -1;
6822 if( root->qt_compatible )
6823 name[0] = name_length & 0xff;
6824 memcpy( name + root->qt_compatible, handler_name, strlen( handler_name ) );
6825 if( root->isom_compatible )
6826 name[name_length - 1] = 0;
6827 hdlr->componentName = name;
6828 hdlr->componentName_length = name_length;
6829 return 0;
6832 uint32_t lsmash_get_media_timescale( lsmash_root_t *root, uint32_t track_ID )
6834 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6835 if( !trak || !trak->mdia || !trak->mdia->mdhd )
6836 return 0;
6837 return trak->mdia->mdhd->timescale;
6840 uint64_t lsmash_get_media_duration( lsmash_root_t *root, uint32_t track_ID )
6842 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6843 if( !trak || !trak->mdia || !trak->mdia->mdhd )
6844 return 0;
6845 return trak->mdia->mdhd->duration;
6848 uint64_t lsmash_get_track_duration( lsmash_root_t *root, uint32_t track_ID )
6850 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6851 if( !trak || !trak->tkhd )
6852 return 0;
6853 return trak->tkhd->duration;
6856 uint32_t lsmash_get_last_sample_delta( lsmash_root_t *root, uint32_t track_ID )
6858 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6859 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl
6860 || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list
6861 || !trak->mdia->minf->stbl->stts->list->head || !trak->mdia->minf->stbl->stts->list->head->data )
6862 return 0;
6863 return ((isom_stts_entry_t *)trak->mdia->minf->stbl->stts->list->head->data)->sample_delta;
6866 uint32_t lsmash_get_start_time_offset( lsmash_root_t *root, uint32_t track_ID )
6868 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6869 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl
6870 || !trak->mdia->minf->stbl->ctts || !trak->mdia->minf->stbl->ctts->list
6871 || !trak->mdia->minf->stbl->ctts->list->head || !trak->mdia->minf->stbl->ctts->list->head->data )
6872 return 0;
6873 return ((isom_ctts_entry_t *)trak->mdia->minf->stbl->ctts->list->head->data)->sample_offset;
6876 uint16_t lsmash_pack_iso_language( char *iso_language )
6878 if( !iso_language || strlen( iso_language ) != 3 )
6879 return 0;
6880 return (uint16_t)LSMASH_PACK_ISO_LANGUAGE( iso_language[0], iso_language[1], iso_language[2] );
6883 static int isom_iso2mac_language( uint16_t ISO_language, uint16_t *MAC_language )
6885 if( !MAC_language )
6886 return -1;
6887 int i = 0;
6888 for( ; isom_languages[i].iso_name; i++ )
6889 if( ISO_language == isom_languages[i].iso_name )
6890 break;
6891 if( !isom_languages[i].iso_name )
6892 return -1;
6893 *MAC_language = isom_languages[i].mac_value;
6894 return 0;
6897 static int isom_mac2iso_language( uint16_t MAC_language, uint16_t *ISO_language )
6899 if( !ISO_language )
6900 return -1;
6901 int i = 0;
6902 for( ; isom_languages[i].iso_name; i++ )
6903 if( MAC_language == isom_languages[i].mac_value )
6904 break;
6905 *ISO_language = isom_languages[i].iso_name ? isom_languages[i].iso_name : ISOM_LANGUAGE_CODE_UNDEFINED;
6906 return 0;
6909 static int isom_set_media_language( lsmash_root_t *root, uint32_t track_ID, uint16_t ISO_language, uint16_t MAC_language )
6911 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6912 if( !trak || !trak->mdia || !trak->mdia->mdhd )
6913 return -1;
6914 uint16_t language = 0;
6915 if( root->isom_compatible )
6917 if( ISO_language )
6918 language = ISO_language;
6919 else if( MAC_language )
6921 if( isom_mac2iso_language( MAC_language, &language ) )
6922 return -1;
6924 else
6925 language = ISOM_LANGUAGE_CODE_UNDEFINED;
6927 else if( root->qt_compatible )
6929 if( ISO_language )
6931 if( isom_iso2mac_language( ISO_language, &language ) )
6932 return -1;
6934 else
6935 language = MAC_language;
6937 else
6938 return -1;
6939 trak->mdia->mdhd->language = language;
6940 return 0;
6943 static int isom_create_grouping( isom_trak_entry_t *trak, isom_grouping_type grouping_type )
6945 lsmash_root_t *root = trak->root;
6946 switch( grouping_type )
6948 case ISOM_GROUP_TYPE_RAP :
6949 assert( root->max_isom_version >= 6 );
6950 break;
6951 case ISOM_GROUP_TYPE_ROLL :
6952 assert( root->avc_extensions );
6953 break;
6954 default :
6955 assert( 0 );
6956 break;
6958 if( !isom_add_sgpd( trak->mdia->minf->stbl, grouping_type )
6959 || !isom_add_sbgp( trak->mdia->minf->stbl, grouping_type ) )
6960 return -1;
6961 return 0;
6964 void lsmash_initialize_media_parameters( lsmash_media_parameters_t *param )
6966 memset( param, 0, sizeof(lsmash_media_parameters_t) );
6967 param->timescale = 1;
6970 int lsmash_set_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param )
6972 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6973 if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl )
6974 return -1;
6975 trak->mdia->mdhd->timescale = param->timescale;
6976 if( isom_set_media_language( root, track_ID, param->ISO_language, param->MAC_language ) )
6977 return -1;
6978 if( param->media_handler_name
6979 && isom_set_media_handler_name( root, track_ID, param->media_handler_name ) )
6980 return -1;
6981 if( root->qt_compatible && param->data_handler_name
6982 && isom_set_data_handler_name( root, track_ID, param->data_handler_name ) )
6983 return -1;
6984 if( root->avc_extensions && param->roll_grouping
6985 && isom_create_grouping( trak, ISOM_GROUP_TYPE_ROLL ) )
6986 return -1;
6987 if( (root->max_isom_version >= 6) && param->rap_grouping
6988 && isom_create_grouping( trak, ISOM_GROUP_TYPE_RAP ) )
6989 return -1;
6990 return 0;
6993 int lsmash_get_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param )
6995 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6996 if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->hdlr
6997 || !trak->mdia->minf || !trak->mdia->minf->stbl )
6998 return -1;
6999 isom_mdhd_t *mdhd = trak->mdia->mdhd;
7000 isom_stbl_t *stbl = trak->mdia->minf->stbl;
7001 isom_sbgp_entry_t *sbgp;
7002 isom_sgpd_entry_t *sgpd;
7003 param->timescale = mdhd->timescale;
7004 param->handler_type = trak->mdia->hdlr->componentSubtype;
7005 param->duration = mdhd->duration;
7006 /* Whether sample grouping present. */
7007 sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_ROLL );
7008 sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_ROLL );
7009 param->roll_grouping = sbgp && sgpd;
7010 sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_RAP );
7011 sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
7012 param->rap_grouping = sbgp && sgpd;
7013 /* Get media language. */
7014 if( mdhd->language >= 0x800 )
7016 param->MAC_language = 0;
7017 param->ISO_language = mdhd->language;
7019 else
7021 param->MAC_language = mdhd->language;
7022 param->ISO_language = 0;
7024 /* Get handler name(s). */
7025 isom_hdlr_t *hdlr = trak->mdia->hdlr;
7026 int length = LSMASH_MIN( 255, hdlr->componentName_length );
7027 if( length )
7029 memcpy( param->media_handler_name_shadow, hdlr->componentName + root->qt_compatible, length );
7030 param->media_handler_name_shadow[length - 2 + root->isom_compatible + root->qt_compatible] = '\0';
7031 param->media_handler_name = param->media_handler_name_shadow;
7033 else
7035 param->media_handler_name = NULL;
7036 memset( param->media_handler_name_shadow, 0, sizeof(param->media_handler_name_shadow) );
7038 if( trak->mdia->minf->hdlr )
7040 hdlr = trak->mdia->minf->hdlr;
7041 length = LSMASH_MIN( 255, hdlr->componentName_length );
7042 if( length )
7044 memcpy( param->data_handler_name_shadow, hdlr->componentName + root->qt_compatible, length );
7045 param->data_handler_name_shadow[length - 2 + root->isom_compatible + root->qt_compatible] = '\0';
7046 param->data_handler_name = param->data_handler_name_shadow;
7048 else
7050 param->data_handler_name = NULL;
7051 memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) );
7054 else
7056 param->data_handler_name = NULL;
7057 memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) );
7059 return 0;
7062 /*---- movie manipulators ----*/
7064 lsmash_root_t *lsmash_open_movie( const char *filename, lsmash_file_mode mode )
7066 char open_mode[4] = { 0 };
7067 if( mode & LSMASH_FILE_MODE_WRITE )
7068 memcpy( open_mode, "w+b", 4 );
7069 #ifdef LSMASH_DEMUXER_ENABLED
7070 else if( mode & LSMASH_FILE_MODE_READ )
7071 memcpy( open_mode, "rb", 3 );
7072 #endif
7073 if( !open_mode[0] )
7074 return NULL;
7075 lsmash_root_t *root = malloc( sizeof(lsmash_root_t) );
7076 if( !root )
7077 return NULL;
7078 memset( root, 0, sizeof(lsmash_root_t) );
7079 root->root = root;
7080 root->bs = malloc( sizeof(lsmash_bs_t) );
7081 if( !root->bs )
7082 goto fail;
7083 memset( root->bs, 0, sizeof(lsmash_bs_t) );
7084 if( !strcmp( filename, "-" ) )
7086 if( mode & LSMASH_FILE_MODE_READ )
7087 root->bs->stream = stdin;
7088 else if( (mode & LSMASH_FILE_MODE_WRITE) && (mode & LSMASH_FILE_MODE_FRAGMENTED) )
7089 root->bs->stream = stdout;
7091 else
7092 root->bs->stream = fopen( filename, open_mode );
7093 if( !root->bs->stream )
7094 goto fail;
7095 root->flags = mode;
7096 if( mode & LSMASH_FILE_MODE_WRITE )
7098 if( isom_add_moov( root ) || isom_add_mvhd( root->moov ) )
7099 goto fail;
7100 root->qt_compatible = 1; /* QTFF is default file format. */
7102 #ifdef LSMASH_DEMUXER_ENABLED
7103 if( (mode & (LSMASH_FILE_MODE_READ | LSMASH_FILE_MODE_DUMP)) )
7105 if( isom_read_root( root ) )
7106 goto fail;
7107 root->max_read_size = 4 * 1024 * 1024;
7109 #endif
7110 if( mode & LSMASH_FILE_MODE_FRAGMENTED )
7112 root->fragment = malloc( sizeof(isom_fragment_manager_t) );
7113 if( !root->fragment )
7114 goto fail;
7115 memset( root->fragment, 0, sizeof(isom_fragment_manager_t) );
7116 root->fragment->pool = lsmash_create_entry_list();
7117 if( !root->fragment->pool )
7118 goto fail;
7120 return root;
7121 fail:
7122 lsmash_destroy_root( root );
7123 return NULL;
7126 static int isom_finish_fragment_movie( lsmash_root_t *root );
7128 /* A movie fragment cannot switch a sample description to another.
7129 * So you must call this function before switching sample descriptions. */
7130 int lsmash_create_fragment_movie( lsmash_root_t *root )
7132 if( !root || !root->bs || !root->fragment || !root->moov || !root->moov->trak_list )
7133 return -1;
7134 /* Finish the previous movie fragment before starting a new one. */
7135 if( isom_finish_fragment_movie( root ) )
7136 return -1;
7137 /* We always hold only one movie fragment except for the initial movie (a pair of moov and mdat). */
7138 if( root->fragment->movie && root->moof_list->entry_count != 1 )
7139 return -1;
7140 isom_moof_entry_t *moof = isom_add_moof( root );
7141 if( isom_add_mfhd( moof ) )
7142 return -1;
7143 root->fragment->movie = moof;
7144 moof->mfhd->sequence_number = ++ root->fragment->fragment_count;
7145 if( root->moof_list->entry_count == 1 )
7146 return 0;
7147 /* Remove the previous movie fragment. */
7148 return lsmash_remove_entry( root->moof_list, 1, isom_remove_moof );
7151 static int isom_set_brands( lsmash_root_t *root, lsmash_brand_type major_brand, uint32_t minor_version, lsmash_brand_type *brands, uint32_t brand_count )
7153 if( brand_count > 50 )
7154 return -1; /* We support setting brands up to 50. */
7155 if( !brand_count )
7157 /* Absence of File Type Box means this file is a QuickTime or MP4 version 1 format file. */
7158 if( root->ftyp )
7160 if( root->ftyp->compatible_brands )
7161 free( root->ftyp->compatible_brands );
7162 free( root->ftyp );
7163 root->ftyp = NULL;
7165 return 0;
7167 if( !root->ftyp && isom_add_ftyp( root ) )
7168 return -1;
7169 isom_ftyp_t *ftyp = root->ftyp;
7170 ftyp->major_brand = major_brand;
7171 ftyp->minor_version = minor_version;
7172 lsmash_brand_type *compatible_brands;
7173 if( !ftyp->compatible_brands )
7174 compatible_brands = malloc( brand_count * sizeof(uint32_t) );
7175 else
7176 compatible_brands = realloc( ftyp->compatible_brands, brand_count * sizeof(uint32_t) );
7177 if( !compatible_brands )
7178 return -1;
7179 ftyp->compatible_brands = compatible_brands;
7180 for( uint32_t i = 0; i < brand_count; i++ )
7182 ftyp->compatible_brands[i] = brands[i];
7183 ftyp->size += 4;
7185 ftyp->brand_count = brand_count;
7186 return isom_check_compatibility( root );
7189 void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t *param )
7191 memset( param, 0, sizeof(lsmash_movie_parameters_t) );
7192 param->max_chunk_duration = 0.5;
7193 param->max_async_tolerance = 2.0;
7194 param->max_chunk_size = 4 * 1024 * 1024;
7195 param->max_read_size = 4 * 1024 * 1024;
7196 param->timescale = 600;
7197 param->playback_rate = 0x00010000;
7198 param->playback_volume = 0x0100;
7201 int lsmash_set_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param )
7203 if( !root || !root->moov || !root->moov->mvhd
7204 || isom_set_brands( root, param->major_brand, param->minor_version, param->brands, param->number_of_brands ) )
7205 return -1;
7206 isom_mvhd_t *mvhd = root->moov->mvhd;
7207 root->max_chunk_duration = param->max_chunk_duration;
7208 root->max_async_tolerance = LSMASH_MAX( param->max_async_tolerance, 2 * param->max_chunk_duration );
7209 root->max_chunk_size = param->max_chunk_size;
7210 root->max_read_size = param->max_read_size;
7211 mvhd->timescale = param->timescale;
7212 if( root->qt_compatible || root->itunes_audio )
7214 mvhd->rate = param->playback_rate;
7215 mvhd->volume = param->playback_volume;
7216 mvhd->previewTime = param->preview_time;
7217 mvhd->previewDuration = param->preview_duration;
7218 mvhd->posterTime = param->poster_time;
7220 else
7222 mvhd->rate = 0x00010000;
7223 mvhd->volume = 0x0100;
7224 mvhd->previewTime = 0;
7225 mvhd->previewDuration = 0;
7226 mvhd->posterTime = 0;
7228 return 0;
7231 int lsmash_get_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param )
7233 if( !root || !root->moov || !root->moov->mvhd )
7234 return -1;
7235 isom_mvhd_t *mvhd = root->moov->mvhd;
7236 if( root->ftyp )
7238 isom_ftyp_t *ftyp = root->ftyp;
7239 uint32_t brand_count = LSMASH_MIN( ftyp->brand_count, 50 ); /* brands up to 50 */
7240 for( uint32_t i = 0; i < brand_count; i++ )
7241 param->brands_shadow[i] = ftyp->compatible_brands[i];
7242 param->major_brand = ftyp->major_brand;
7243 param->brands = param->brands_shadow;
7244 param->number_of_brands = brand_count;
7245 param->minor_version = ftyp->minor_version;
7247 param->max_chunk_duration = root->max_chunk_duration;
7248 param->max_async_tolerance = root->max_async_tolerance;
7249 param->max_chunk_size = root->max_chunk_size;
7250 param->max_read_size = root->max_read_size;
7251 param->timescale = mvhd->timescale;
7252 param->duration = mvhd->duration;
7253 param->playback_rate = mvhd->rate;
7254 param->playback_volume = mvhd->volume;
7255 param->preview_time = mvhd->previewTime;
7256 param->preview_duration = mvhd->previewDuration;
7257 param->poster_time = mvhd->posterTime;
7258 param->number_of_tracks = root->moov->trak_list ? root->moov->trak_list->entry_count : 0;
7259 return 0;
7262 uint32_t lsmash_get_movie_timescale( lsmash_root_t *root )
7264 if( !root || !root->moov || !root->moov->mvhd )
7265 return 0;
7266 return root->moov->mvhd->timescale;
7269 static int isom_write_ftyp( lsmash_root_t *root )
7271 isom_ftyp_t *ftyp = root->ftyp;
7272 if( !ftyp || !ftyp->brand_count )
7273 return 0;
7274 lsmash_bs_t *bs = root->bs;
7275 isom_bs_put_box_common( bs, ftyp );
7276 lsmash_bs_put_be32( bs, ftyp->major_brand );
7277 lsmash_bs_put_be32( bs, ftyp->minor_version );
7278 for( uint32_t i = 0; i < ftyp->brand_count; i++ )
7279 lsmash_bs_put_be32( bs, ftyp->compatible_brands[i] );
7280 if( lsmash_bs_write_data( bs ) )
7281 return -1;
7282 root->size += ftyp->size;
7283 root->file_type_written = 1;
7284 return 0;
7287 static int isom_write_moov( lsmash_root_t *root )
7289 if( !root || !root->moov )
7290 return -1;
7291 lsmash_bs_t *bs = root->bs;
7292 isom_moov_t *moov = root->moov;
7293 isom_bs_put_box_common( bs, moov );
7294 if( lsmash_bs_write_data( bs ) )
7295 return -1;
7296 if( isom_write_mvhd( root )
7297 || isom_write_iods( root ) )
7298 return -1;
7299 if( moov->trak_list )
7300 for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
7301 if( isom_write_trak( bs, (isom_trak_entry_t *)entry->data ) )
7302 return -1;
7303 if( isom_write_udta( bs, moov, NULL )
7304 || isom_write_meta( bs, moov->meta ) )
7305 return -1;
7306 return isom_write_mvex( bs, moov->mvex );
7309 int lsmash_set_free( lsmash_root_t *root, uint8_t *data, uint64_t data_length )
7311 if( !root || !root->free || !data || !data_length )
7312 return -1;
7313 isom_free_t *skip = root->free;
7314 uint8_t *tmp = NULL;
7315 if( !skip->data )
7316 tmp = malloc( data_length );
7317 else if( skip->length < data_length )
7318 tmp = realloc( skip->data, data_length );
7319 if( !tmp )
7320 return -1;
7321 memcpy( tmp, data, data_length );
7322 skip->data = tmp;
7323 skip->length = data_length;
7324 return 0;
7327 int lsmash_add_free( lsmash_root_t *root, uint8_t *data, uint64_t data_length )
7329 if( !root )
7330 return -1;
7331 if( !root->free )
7333 isom_create_box( skip, root, ISOM_BOX_TYPE_FREE );
7334 root->free = skip;
7336 if( data && data_length )
7337 return lsmash_set_free( root, data, data_length );
7338 return 0;
7341 int lsmash_write_free( lsmash_root_t *root )
7343 if( !root || !root->bs || !root->free )
7344 return -1;
7345 isom_free_t *skip = root->free;
7346 lsmash_bs_t *bs = root->bs;
7347 skip->size = 8 + skip->length;
7348 isom_bs_put_box_common( bs, skip );
7349 if( skip->data && skip->length )
7350 lsmash_bs_put_bytes( bs, skip->data, skip->length );
7351 return lsmash_bs_write_data( bs );
7354 int lsmash_create_object_descriptor( lsmash_root_t *root )
7356 if( !root )
7357 return -1;
7358 /* Return error if this file is not compatible with MP4 file format. */
7359 if( !root->mp4_version1 && !root->mp4_version2 )
7360 return -1;
7361 return isom_add_iods( root->moov );
7364 /*---- finishing functions ----*/
7366 static int isom_set_fragment_overall_duration( lsmash_root_t *root )
7368 if( root->bs->stream == stdout )
7369 return 0;
7370 isom_mvex_t *mvex = root->moov->mvex;
7371 if( isom_add_mehd( mvex ) )
7372 return -1;
7373 /* Get the longest duration of the tracks. */
7374 uint64_t longest_duration = 0;
7375 for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
7377 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
7378 if( !trak || !trak->cache || !trak->cache->fragment || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale )
7379 return -1;
7380 uint64_t duration;
7381 if( !trak->edts || !trak->edts->elst || !trak->edts->elst->list )
7383 duration = trak->cache->fragment->largest_cts + trak->cache->fragment->last_duration;
7384 duration = (uint64_t)(((double)duration / trak->mdia->mdhd->timescale) * root->moov->mvhd->timescale);
7386 else
7388 duration = 0;
7389 for( lsmash_entry_t *elst_entry = trak->edts->elst->list->head; elst_entry; elst_entry = elst_entry->next )
7391 isom_elst_entry_t *data = (isom_elst_entry_t *)elst_entry->data;
7392 if( !data )
7393 return -1;
7394 duration += data->segment_duration;
7397 longest_duration = LSMASH_MAX( duration, longest_duration );
7399 mvex->mehd->fragment_duration = longest_duration;
7400 mvex->mehd->version = 1;
7401 isom_update_mehd_size( mvex->mehd );
7402 /* Write Movie Extends Header Box here. */
7403 lsmash_bs_t *bs = root->bs;
7404 FILE *stream = bs->stream;
7405 uint64_t current_pos = lsmash_ftell( stream );
7406 lsmash_fseek( stream, mvex->placeholder_pos, SEEK_SET );
7407 int ret = isom_write_mehd( bs, mvex->mehd );
7408 if( !ret )
7409 ret = lsmash_bs_write_data( bs );
7410 lsmash_fseek( stream, current_pos, SEEK_SET );
7411 return ret;
7414 static int isom_write_fragment_random_access_info( lsmash_root_t *root )
7416 if( root->bs->stream == stdout )
7417 return 0;
7418 if( isom_update_mfra_size( root->mfra ) )
7419 return -1;
7420 return isom_write_mfra( root->bs, root->mfra );
7423 int lsmash_finish_movie( lsmash_root_t *root, lsmash_adhoc_remux_t* remux )
7425 if( !root || !root->bs || !root->moov || !root->moov->trak_list )
7426 return -1;
7427 if( root->fragment )
7429 /* Output the final movie fragment. */
7430 if( isom_finish_fragment_movie( root ) )
7431 return -1;
7432 /* Write the overall random access information at the tail of the movie. */
7433 if( isom_write_fragment_random_access_info( root ) )
7434 return -1;
7435 /* Set overall duration of the movie. */
7436 return isom_set_fragment_overall_duration( root );
7438 isom_moov_t *moov = root->moov;
7439 for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
7441 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
7442 if( !trak || !trak->cache || !trak->tkhd || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl )
7443 return -1;
7444 uint32_t track_ID = trak->tkhd->track_ID;
7445 uint32_t related_track_ID = trak->related_track_ID;
7446 /* Disable the track if the track is a track reference chapter. */
7447 if( trak->is_chapter )
7448 trak->tkhd->flags &= ~ISOM_TRACK_ENABLED;
7449 if( trak->is_chapter && related_track_ID )
7451 /* In order that the track duration of the chapter track doesn't exceed that of the related track. */
7452 uint64_t track_duration = LSMASH_MIN( trak->tkhd->duration, lsmash_get_track_duration( root, related_track_ID ) );
7453 if( lsmash_create_explicit_timeline_map( root, track_ID, track_duration, 0, ISOM_EDIT_MODE_NORMAL ) )
7454 return -1;
7456 /* Add stss box if any samples aren't sync sample. */
7457 isom_stbl_t *stbl = trak->mdia->minf->stbl;
7458 if( !trak->cache->all_sync && !stbl->stss && isom_add_stss( stbl ) )
7459 return -1;
7460 if( isom_update_bitrate_info( trak->mdia ) )
7461 return -1;
7463 if( root->mp4_version1 == 1 && isom_add_iods( moov ) )
7464 return -1;
7465 if( isom_check_mandatory_boxes( root )
7466 || isom_set_movie_creation_time( root )
7467 || isom_update_moov_size( moov )
7468 || isom_write_mdat_size( root ) )
7469 return -1;
7471 lsmash_bs_t *bs = root->bs;
7472 uint64_t meta_size = root->meta ? root->meta->size : 0;
7473 if( !remux )
7475 if( isom_write_moov( root )
7476 || isom_write_meta( bs, root->meta ) )
7477 return -1;
7478 root->size += moov->size + meta_size;
7479 return 0;
7482 /* stco->co64 conversion, depending on last chunk's offset */
7483 for( lsmash_entry_t* entry = moov->trak_list->head; entry; )
7485 isom_trak_entry_t* trak = (isom_trak_entry_t*)entry->data;
7486 isom_stco_t* stco = trak->mdia->minf->stbl->stco;
7487 if( !stco->list->tail )
7488 return -1;
7489 if( stco->large_presentation
7490 || (((isom_stco_entry_t*)stco->list->tail->data)->chunk_offset + moov->size + meta_size) <= UINT32_MAX )
7492 entry = entry->next;
7493 continue; /* no need to convert stco into co64 */
7495 /* stco->co64 conversion */
7496 if( isom_convert_stco_to_co64( trak->mdia->minf->stbl )
7497 || isom_update_moov_size( moov ) )
7498 return -1;
7499 entry = moov->trak_list->head; /* whenever any conversion, re-check all traks */
7502 /* now the amount of offset is fixed. */
7503 uint64_t mtf_size = moov->size + meta_size; /* sum of size of boxes moved to front */
7505 /* buffer size must be at least mtf_size * 2 */
7506 remux->buffer_size = LSMASH_MAX( remux->buffer_size, mtf_size * 2 );
7508 uint8_t* buf[2];
7509 if( (buf[0] = (uint8_t*)malloc( remux->buffer_size )) == NULL )
7510 return -1; /* NOTE: i think we still can fallback to "return isom_write_moov( root );" here. */
7511 uint64_t size = remux->buffer_size / 2;
7512 buf[1] = buf[0] + size; /* split to 2 buffers */
7514 /* now the amount of offset is fixed. apply that to stco/co64 */
7515 for( lsmash_entry_t* entry = moov->trak_list->head; entry; entry = entry->next )
7517 isom_stco_t* stco = ((isom_trak_entry_t*)entry->data)->mdia->minf->stbl->stco;
7518 if( stco->large_presentation )
7519 for( lsmash_entry_t* co64_entry = stco->list->head ; co64_entry ; co64_entry = co64_entry->next )
7520 ((isom_co64_entry_t*)co64_entry->data)->chunk_offset += mtf_size;
7521 else
7522 for( lsmash_entry_t* stco_entry = stco->list->head ; stco_entry ; stco_entry = stco_entry->next )
7523 ((isom_stco_entry_t*)stco_entry->data)->chunk_offset += mtf_size;
7526 FILE *stream = bs->stream;
7527 isom_mdat_t *mdat = root->mdat;
7528 uint64_t total = root->size + mtf_size;
7529 uint64_t readnum;
7530 /* backup starting area of mdat and write moov + meta there instead */
7531 if( lsmash_fseek( stream, mdat->placeholder_pos, SEEK_SET ) )
7532 goto fail;
7533 readnum = fread( buf[0], 1, size, stream );
7534 uint64_t read_pos = lsmash_ftell( stream );
7536 /* write moov + meta there instead */
7537 if( lsmash_fseek( stream, mdat->placeholder_pos, SEEK_SET )
7538 || isom_write_moov( root )
7539 || isom_write_meta( bs, root->meta ) )
7540 goto fail;
7541 uint64_t write_pos = lsmash_ftell( stream );
7543 mdat->placeholder_pos += mtf_size; /* update placeholder */
7545 /* copy-pastan */
7546 int buf_switch = 1;
7547 while( readnum == size )
7549 if( lsmash_fseek( stream, read_pos, SEEK_SET ) )
7550 goto fail;
7551 readnum = fread( buf[buf_switch], 1, size, stream );
7552 read_pos = lsmash_ftell( stream );
7554 buf_switch ^= 0x1;
7556 if( lsmash_fseek( stream, write_pos, SEEK_SET )
7557 || fwrite( buf[buf_switch], 1, size, stream ) != size )
7558 goto fail;
7559 write_pos = lsmash_ftell( stream );
7560 if( remux->func ) remux->func( remux->param, write_pos, total ); // FIXME:
7562 if( fwrite( buf[buf_switch^0x1], 1, readnum, stream ) != readnum )
7563 goto fail;
7564 if( remux->func ) remux->func( remux->param, total, total ); // FIXME:
7566 root->size += mtf_size;
7567 free( buf[0] );
7568 return 0;
7570 fail:
7571 free( buf[0] );
7572 return -1;
7575 #define GET_MOST_USED( box_name, index, flag_name ) \
7576 if( most_used[index] < stats.flag_name[i] ) \
7578 most_used[index] = stats.flag_name[i]; \
7579 box_name->default_sample_flags.flag_name = i; \
7582 static int isom_create_fragment_overall_default_settings( lsmash_root_t *root )
7584 if( isom_add_mvex( root->moov ) )
7585 return -1;
7586 for( lsmash_entry_t *trak_entry = root->moov->trak_list->head; trak_entry; trak_entry = trak_entry->next )
7588 isom_trak_entry_t *trak = (isom_trak_entry_t *)trak_entry->data;
7589 if( !trak || !trak->cache || !trak->tkhd || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl )
7590 return -1;
7591 isom_stbl_t *stbl = trak->mdia->minf->stbl;
7592 if( !stbl->stts || !stbl->stts->list || !stbl->stsz
7593 || (stbl->stts->list->tail && !stbl->stts->list->tail->data)
7594 || (stbl->stsz->list && stbl->stsz->list->head && !stbl->stsz->list->head->data) )
7595 return -1;
7596 isom_trex_entry_t *trex = isom_add_trex( root->moov->mvex );
7597 if( !trex )
7598 return -1;
7599 trex->track_ID = trak->tkhd->track_ID;
7600 /* Set up defaults. */
7601 trex->default_sample_description_index = trak->cache->chunk.sample_description_index ? trak->cache->chunk.sample_description_index : 1;
7602 trex->default_sample_duration = stbl->stts->list->tail ? ((isom_stts_entry_t *)stbl->stts->list->tail->data)->sample_delta : 1;
7603 trex->default_sample_size = !stbl->stsz->list
7604 ? stbl->stsz->sample_size : stbl->stsz->list->head
7605 ? ((isom_stsz_entry_t *)stbl->stsz->list->head->data)->entry_size : 0;
7606 if( stbl->sdtp && stbl->sdtp->list )
7608 struct sample_flags_stats_t
7610 uint32_t is_leading [4];
7611 uint32_t sample_depends_on [4];
7612 uint32_t sample_is_depended_on[4];
7613 uint32_t sample_has_redundancy[4];
7614 } stats = { { 0 }, { 0 }, { 0 }, { 0 } };
7615 for( lsmash_entry_t *sdtp_entry = stbl->sdtp->list->head; sdtp_entry; sdtp_entry = sdtp_entry->next )
7617 isom_sdtp_entry_t *data = (isom_sdtp_entry_t *)sdtp_entry->data;
7618 if( !data )
7619 return -1;
7620 ++ stats.is_leading [ data->is_leading ];
7621 ++ stats.sample_depends_on [ data->sample_depends_on ];
7622 ++ stats.sample_is_depended_on[ data->sample_is_depended_on ];
7623 ++ stats.sample_has_redundancy[ data->sample_has_redundancy ];
7625 uint32_t most_used[4] = { 0, 0, 0, 0 };
7626 for( int i = 0; i < 4; i++ )
7628 GET_MOST_USED( trex, 0, is_leading );
7629 GET_MOST_USED( trex, 1, sample_depends_on );
7630 GET_MOST_USED( trex, 2, sample_is_depended_on );
7631 GET_MOST_USED( trex, 3, sample_has_redundancy );
7634 trex->default_sample_flags.sample_is_non_sync_sample = !trak->cache->all_sync;
7636 return 0;
7639 static int isom_prepare_random_access_info( lsmash_root_t *root )
7641 if( root->bs->stream == stdout )
7642 return 0;
7643 if( isom_add_mfra( root )
7644 || isom_add_mfro( root->mfra ) )
7645 return -1;
7646 return 0;
7649 static int isom_output_fragment_media_data( lsmash_root_t *root )
7651 isom_fragment_manager_t *fragment = root->fragment;
7652 if( !fragment->pool->entry_count )
7654 /* no need to write media data */
7655 lsmash_remove_entries( fragment->pool, lsmash_delete_sample );
7656 fragment->pool_size = 0;
7657 return 0;
7659 /* If there is no available Media Data Box to write samples, add and write a new one. */
7660 if( isom_new_mdat( root, fragment->pool_size ) )
7661 return -1;
7662 /* Write samples in the current movie fragment. */
7663 for( lsmash_entry_t* entry = fragment->pool->head; entry; entry = entry->next )
7665 isom_sample_pool_t *pool = (isom_sample_pool_t *)entry->data;
7666 if( !pool )
7667 return -1;
7668 lsmash_bs_put_bytes( root->bs, pool->data, pool->size );
7670 if( lsmash_bs_write_data( root->bs ) )
7671 return -1;
7672 root->size += root->mdat->size;
7673 lsmash_remove_entries( fragment->pool, isom_remove_sample_pool );
7674 fragment->pool_size = 0;
7675 return 0;
7678 static int isom_finish_fragment_initial_movie( lsmash_root_t *root )
7680 if( !root->moov || !root->moov->trak_list )
7681 return -1;
7682 isom_moov_t *moov = root->moov;
7683 for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
7685 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
7686 if( !trak || !trak->cache || !trak->tkhd || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl )
7687 return -1;
7688 if( isom_get_sample_count( trak ) )
7690 /* Add stss box if any samples aren't sync sample. */
7691 isom_stbl_t *stbl = trak->mdia->minf->stbl;
7692 if( !trak->cache->all_sync && !stbl->stss && isom_add_stss( stbl ) )
7693 return -1;
7695 else
7696 trak->tkhd->duration = 0;
7697 if( isom_update_bitrate_info( trak->mdia ) )
7698 return -1;
7700 if( root->mp4_version1 == 1 && isom_add_iods( moov ) )
7701 return -1;
7702 if( isom_create_fragment_overall_default_settings( root )
7703 || isom_prepare_random_access_info( root )
7704 || isom_check_mandatory_boxes( root )
7705 || isom_set_movie_creation_time( root )
7706 || isom_update_moov_size( moov ) )
7707 return -1;
7708 /* stco->co64 conversion, depending on last chunk's offset */
7709 uint64_t meta_size = root->meta ? root->meta->size : 0;
7710 for( lsmash_entry_t* entry = moov->trak_list->head; entry; )
7712 isom_trak_entry_t* trak = (isom_trak_entry_t*)entry->data;
7713 isom_stco_t* stco = trak->mdia->minf->stbl->stco;
7714 if( !stco->list->tail /* no samples */
7715 || stco->large_presentation
7716 || (((isom_stco_entry_t*)stco->list->tail->data)->chunk_offset + moov->size + meta_size) <= UINT32_MAX )
7718 entry = entry->next;
7719 continue; /* no need to convert stco into co64 */
7721 /* stco->co64 conversion */
7722 if( isom_convert_stco_to_co64( trak->mdia->minf->stbl )
7723 || isom_update_moov_size( moov ) )
7724 return -1;
7725 entry = moov->trak_list->head; /* whenever any conversion, re-check all traks */
7727 /* Now, the amount of offset is fixed. Apply that to stco/co64. */
7728 uint64_t preceding_size = moov->size + meta_size;
7729 for( lsmash_entry_t* entry = moov->trak_list->head; entry; entry = entry->next )
7731 isom_stco_t* stco = ((isom_trak_entry_t*)entry->data)->mdia->minf->stbl->stco;
7732 if( stco->large_presentation )
7733 for( lsmash_entry_t* co64_entry = stco->list->head ; co64_entry ; co64_entry = co64_entry->next )
7734 ((isom_co64_entry_t*)co64_entry->data)->chunk_offset += preceding_size;
7735 else
7736 for( lsmash_entry_t* stco_entry = stco->list->head ; stco_entry ; stco_entry = stco_entry->next )
7737 ((isom_stco_entry_t*)stco_entry->data)->chunk_offset += preceding_size;
7739 /* Write File Type Box here if it was not written yet. */
7740 if( !root->file_type_written && isom_write_ftyp( root ) )
7741 return -1;
7742 /* Write Movie Box. */
7743 if( isom_write_moov( root )
7744 || isom_write_meta( root->bs, root->meta ) )
7745 return -1;
7746 root->size += preceding_size;
7747 /* Output samples. */
7748 return isom_output_fragment_media_data( root );
7751 static int isom_finish_fragment_movie( lsmash_root_t *root )
7753 if( !root->moov || !root->moov->trak_list || !root->fragment || !root->fragment->pool )
7754 return -1;
7755 isom_moof_entry_t *moof = root->fragment->movie;
7756 if( !moof )
7757 return isom_finish_fragment_initial_movie( root );
7758 /* Calculate appropriate default_sample_flags of each Track Fragment Header Box.
7759 * And check whether that default_sample_flags is useful or not. */
7760 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
7762 isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
7763 if( !traf || !traf->tfhd || !traf->root || !traf->root->moov || !traf->root->moov->mvex )
7764 return -1;
7765 isom_tfhd_t *tfhd = traf->tfhd;
7766 isom_trex_entry_t *trex = isom_get_trex( root->moov->mvex, tfhd->track_ID );
7767 if( !trex )
7768 return -1;
7769 struct sample_flags_stats_t
7771 uint32_t is_leading [4];
7772 uint32_t sample_depends_on [4];
7773 uint32_t sample_is_depended_on [4];
7774 uint32_t sample_has_redundancy [4];
7775 uint32_t sample_is_non_sync_sample[2];
7776 } stats = { { 0 }, { 0 }, { 0 }, { 0 }, { 0 } };
7777 for( lsmash_entry_t *trun_entry = traf->trun_list->head; trun_entry; trun_entry = trun_entry->next )
7779 isom_trun_entry_t *trun = (isom_trun_entry_t *)trun_entry->data;
7780 if( !trun || !trun->sample_count )
7781 return -1;
7782 isom_sample_flags_t *sample_flags;
7783 if( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT )
7785 if( !trun->optional )
7786 return -1;
7787 for( lsmash_entry_t *optional_entry = trun->optional->head; optional_entry; optional_entry = optional_entry->next )
7789 isom_trun_optional_row_t *row = (isom_trun_optional_row_t *)optional_entry->data;
7790 if( !row )
7791 return -1;
7792 sample_flags = &row->sample_flags;
7793 ++ stats.is_leading [ sample_flags->is_leading ];
7794 ++ stats.sample_depends_on [ sample_flags->sample_depends_on ];
7795 ++ stats.sample_is_depended_on [ sample_flags->sample_is_depended_on ];
7796 ++ stats.sample_has_redundancy [ sample_flags->sample_has_redundancy ];
7797 ++ stats.sample_is_non_sync_sample[ sample_flags->sample_is_non_sync_sample ];
7800 else
7802 sample_flags = &tfhd->default_sample_flags;
7803 stats.is_leading [ sample_flags->is_leading ] += trun->sample_count;
7804 stats.sample_depends_on [ sample_flags->sample_depends_on ] += trun->sample_count;
7805 stats.sample_is_depended_on [ sample_flags->sample_is_depended_on ] += trun->sample_count;
7806 stats.sample_has_redundancy [ sample_flags->sample_has_redundancy ] += trun->sample_count;
7807 stats.sample_is_non_sync_sample[ sample_flags->sample_is_non_sync_sample ] += trun->sample_count;
7810 uint32_t most_used[5] = { 0, 0, 0, 0, 0 };
7811 for( int i = 0; i < 4; i++ )
7813 GET_MOST_USED( tfhd, 0, is_leading );
7814 GET_MOST_USED( tfhd, 1, sample_depends_on );
7815 GET_MOST_USED( tfhd, 2, sample_is_depended_on );
7816 GET_MOST_USED( tfhd, 3, sample_has_redundancy );
7817 if( i < 2 )
7818 GET_MOST_USED( tfhd, 4, sample_is_non_sync_sample );
7820 int useful_default_sample_duration = 0;
7821 int useful_default_sample_size = 0;
7822 for( lsmash_entry_t *trun_entry = traf->trun_list->head; trun_entry; trun_entry = trun_entry->next )
7824 isom_trun_entry_t *trun = (isom_trun_entry_t *)trun_entry->data;
7825 if( !(trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT) )
7826 useful_default_sample_duration = 1;
7827 if( !(trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT) )
7828 useful_default_sample_size = 1;
7829 int useful_first_sample_flags = 1;
7830 int useful_default_sample_flags = 1;
7831 if( trun->sample_count == 1 )
7833 /* It is enough to check only if first_sample_flags equals default_sample_flags or not.
7834 * If it is equal, just use default_sample_flags.
7835 * If not, just use first_sample_flags of this run. */
7836 if( !memcmp( &trun->first_sample_flags, &tfhd->default_sample_flags, sizeof(isom_sample_flags_t) ) )
7837 useful_first_sample_flags = 0;
7839 else if( trun->optional && trun->optional->head )
7841 lsmash_entry_t *optional_entry = trun->optional->head->next;
7842 isom_trun_optional_row_t *row = (isom_trun_optional_row_t *)optional_entry->data;
7843 isom_sample_flags_t representative_sample_flags = row->sample_flags;
7844 if( memcmp( &tfhd->default_sample_flags, &representative_sample_flags, sizeof(isom_sample_flags_t) ) )
7845 useful_default_sample_flags = 0;
7846 if( !memcmp( &trun->first_sample_flags, &representative_sample_flags, sizeof(isom_sample_flags_t) ) )
7847 useful_first_sample_flags = 0;
7848 if( useful_default_sample_flags )
7849 for( optional_entry = optional_entry->next; optional_entry; optional_entry = optional_entry->next )
7851 row = (isom_trun_optional_row_t *)optional_entry->data;
7852 if( memcmp( &representative_sample_flags, &row->sample_flags, sizeof(isom_sample_flags_t) ) )
7854 useful_default_sample_flags = 0;
7855 break;
7859 if( useful_default_sample_flags )
7861 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT;
7862 trun->flags &= ~ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT;
7864 else
7866 useful_first_sample_flags = 0;
7867 trun->flags |= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT;
7869 if( useful_first_sample_flags )
7870 trun->flags |= ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT;
7872 if( useful_default_sample_duration && tfhd->default_sample_duration != trex->default_sample_duration )
7873 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
7874 else
7875 tfhd->default_sample_duration = trex->default_sample_duration; /* This might be redundant, but is to be more natural. */
7876 if( useful_default_sample_size && tfhd->default_sample_size != trex->default_sample_size )
7877 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT;
7878 else
7879 tfhd->default_sample_size = trex->default_sample_size; /* This might be redundant, but is to be more natural. */
7880 if( !(tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT) )
7881 tfhd->default_sample_flags = trex->default_sample_flags; /* This might be redundant, but is to be more natural. */
7882 else if( !memcmp( &tfhd->default_sample_flags, &trex->default_sample_flags, sizeof(isom_sample_flags_t) ) )
7883 tfhd->flags &= ~ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT;
7885 /* When using for live streaming, setting explicit base_data_offset is not preferable.
7886 * However, it's OK because we haven't supported this yet.
7887 * Implicit base_data_offsets that originate in the first byte of each Movie Fragment Box will be implemented
7888 * by the feature of ISO Base Media File Format version 5 or later.
7889 * Media Data Box starts immediately after Movie Fragment Box. */
7890 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
7892 isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
7893 traf->tfhd->flags |= ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT;
7895 /* Consider the update of tf_flags here. */
7896 if( isom_update_moof_entry_size( moof ) )
7897 return -1;
7898 /* Now, we can calculate offsets in the current movie fragment, so do it. */
7899 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
7901 isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
7902 traf->tfhd->base_data_offset = root->size + moof->size + ISOM_BASEBOX_COMMON_SIZE;
7904 if( isom_write_moof( root->bs, moof ) )
7905 return -1;
7906 root->size += moof->size;
7907 /* Output samples. */
7908 return isom_output_fragment_media_data( root );
7911 #undef GET_MOST_USED
7913 static isom_trun_optional_row_t *isom_request_trun_optional_row( isom_trun_entry_t *trun, isom_tfhd_t *tfhd, uint32_t sample_number )
7915 isom_trun_optional_row_t *row = NULL;
7916 if( !trun->optional )
7918 trun->optional = lsmash_create_entry_list();
7919 if( !trun->optional )
7920 return NULL;
7922 if( trun->optional->entry_count < sample_number )
7924 while( trun->optional->entry_count < sample_number )
7926 row = malloc( sizeof(isom_trun_optional_row_t) );
7927 if( !row )
7928 return NULL;
7929 /* Copy from default. */
7930 row->sample_duration = tfhd->default_sample_duration;
7931 row->sample_size = tfhd->default_sample_size;
7932 row->sample_flags = tfhd->default_sample_flags;
7933 row->sample_composition_time_offset = 0;
7934 if( lsmash_add_entry( trun->optional, row ) )
7936 free( row );
7937 return NULL;
7940 return row;
7942 uint32_t i = 0;
7943 for( lsmash_entry_t *entry = trun->optional->head; entry; entry = entry->next )
7945 row = (isom_trun_optional_row_t *)entry->data;
7946 if( !row )
7947 return NULL;
7948 if( ++i == sample_number )
7949 return row;
7951 return NULL;
7954 int lsmash_create_fragment_empty_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t duration )
7956 if( !root || !root->fragment || !root->fragment->movie || !root->moov )
7957 return -1;
7958 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
7959 if( !trak || !trak->tkhd )
7960 return -1;
7961 isom_trex_entry_t *trex = isom_get_trex( root->moov->mvex, track_ID );
7962 if( !trex )
7963 return -1;
7964 isom_moof_entry_t *moof = root->fragment->movie;
7965 isom_traf_entry_t *traf = isom_get_traf( moof, track_ID );
7966 if( traf )
7967 return -1;
7968 traf = isom_add_traf( root, moof );
7969 if( isom_add_tfhd( traf ) )
7970 return -1;
7971 isom_tfhd_t *tfhd = traf->tfhd;
7972 tfhd->flags = ISOM_TF_FLAGS_DURATION_IS_EMPTY; /* no samples for this track fragment yet */
7973 tfhd->track_ID = trak->tkhd->track_ID;
7974 tfhd->default_sample_duration = duration;
7975 if( duration != trex->default_sample_duration )
7976 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
7977 traf->cache = trak->cache;
7978 traf->cache->fragment->traf_number = moof->traf_list->entry_count;
7979 traf->cache->fragment->last_duration += duration; /* The duration of the last sample includes this empty-duration. */
7980 return 0;
7983 static int isom_set_fragment_last_duration( isom_traf_entry_t *traf, uint32_t last_duration )
7985 isom_tfhd_t *tfhd = traf->tfhd;
7986 if( !traf->trun_list || !traf->trun_list->tail || !traf->trun_list->tail->data )
7988 /* There are no track runs in this track fragment, so it is a empty-duration. */
7989 isom_trex_entry_t *trex = isom_get_trex( traf->root->moov->mvex, tfhd->track_ID );
7990 if( !trex )
7991 return -1;
7992 tfhd->flags |= ISOM_TF_FLAGS_DURATION_IS_EMPTY;
7993 if( last_duration != trex->default_sample_duration )
7994 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
7995 tfhd->default_sample_duration = last_duration;
7996 traf->cache->fragment->last_duration = last_duration;
7997 return 0;
7999 /* Update the last sample_duration if needed. */
8000 isom_trun_entry_t *trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
8001 if( trun->sample_count == 1 && traf->trun_list->entry_count == 1 )
8003 isom_trex_entry_t *trex = isom_get_trex( traf->root->moov->mvex, tfhd->track_ID );
8004 if( !trex )
8005 return -1;
8006 if( last_duration != trex->default_sample_duration )
8007 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
8008 tfhd->default_sample_duration = last_duration;
8010 else if( last_duration != tfhd->default_sample_duration )
8011 trun->flags |= ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT;
8012 if( trun->flags )
8014 isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, trun->sample_count );
8015 if( !row )
8016 return -1;
8017 row->sample_duration = last_duration;
8019 traf->cache->fragment->last_duration = last_duration;
8020 return 0;
8023 int lsmash_set_last_sample_delta( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_delta )
8025 if( !root || !track_ID )
8026 return -1;
8027 if( root->fragment && root->fragment->movie )
8029 isom_traf_entry_t *traf = isom_get_traf( root->fragment->movie, track_ID );
8030 if( !traf || !traf->cache || !traf->tfhd || !traf->trun_list )
8031 return -1;
8032 return isom_set_fragment_last_duration( traf, sample_delta );
8034 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
8035 if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl
8036 || !trak->mdia->minf->stbl->stsz || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list )
8037 return -1;
8038 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8039 isom_stts_t *stts = stbl->stts;
8040 uint32_t sample_count = isom_get_sample_count( trak );
8041 if( !stts->list->tail )
8043 if( !sample_count )
8044 return 0; /* no samples */
8045 if( sample_count > 1 )
8046 return -1; /* irregular sample_count */
8047 if( isom_add_stts_entry( stbl, sample_delta ) )
8048 return -1;
8049 return lsmash_update_track_duration( root, track_ID, 0 );
8051 uint32_t i = 0;
8052 for( lsmash_entry_t *entry = stts->list->head; entry; entry = entry->next )
8053 i += ((isom_stts_entry_t *)entry->data)->sample_count;
8054 if( sample_count < i )
8055 return -1;
8056 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stts->list->tail->data;
8057 if( !last_stts_data )
8058 return -1;
8059 if( sample_count > i )
8061 if( sample_count - i > 1 )
8062 return -1;
8063 /* Add a sample_delta. */
8064 if( sample_delta == last_stts_data->sample_delta )
8065 ++ last_stts_data->sample_count;
8066 else if( isom_add_stts_entry( stbl, sample_delta ) )
8067 return -1;
8069 else if( sample_count == i && isom_replace_last_sample_delta( stbl, sample_delta ) )
8070 return -1;
8071 return lsmash_update_track_duration( root, track_ID, sample_delta );
8074 void lsmash_discard_boxes( lsmash_root_t *root )
8076 if( !root )
8077 return;
8078 isom_remove_ftyp( root->ftyp );
8079 isom_remove_moov( root );
8080 lsmash_remove_list( root->moof_list, isom_remove_moof );
8081 isom_remove_mdat( root->mdat );
8082 isom_remove_free( root->free );
8083 isom_remove_meta( root->meta );
8084 isom_remove_mfra( root->mfra );
8085 root->ftyp = NULL;
8086 root->moov = NULL;
8087 root->moof_list = NULL;
8088 root->mdat = NULL;
8089 root->free = NULL;
8090 root->mfra = NULL;
8093 void lsmash_destroy_root( lsmash_root_t *root )
8095 if( !root )
8096 return;
8097 #ifdef LSMASH_DEMUXER_ENABLED
8098 isom_remove_print_funcs( root );
8099 isom_remove_timelines( root );
8100 #endif
8101 lsmash_discard_boxes( root );
8102 if( root->bs )
8104 if( root->bs->stream )
8105 fclose( root->bs->stream );
8106 if( root->bs->data )
8107 free( root->bs->data );
8108 free( root->bs );
8110 if( root->fragment )
8112 lsmash_remove_list( root->fragment->pool, lsmash_delete_sample );
8113 free( root->fragment );
8115 free( root );
8118 /*---- timeline manipulator ----*/
8120 int lsmash_modify_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint64_t segment_duration, int64_t media_time, int32_t media_rate )
8122 if( !segment_duration || media_time < -1 )
8123 return -1;
8124 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
8125 if( !trak || !trak->edts || !trak->edts->elst || !trak->edts->elst->list )
8126 return -1;
8127 isom_elst_t *elst = trak->edts->elst;
8128 isom_elst_entry_t *data = (isom_elst_entry_t *)lsmash_get_entry_data( elst->list, entry_number );
8129 if( !data )
8130 return -1;
8131 data->segment_duration = segment_duration;
8132 data->media_time = media_time;
8133 data->media_rate = media_rate;
8134 if( !elst->pos || !root->fragment || root->bs->stream == stdout )
8135 return isom_update_tkhd_duration( trak );
8136 /* Rewrite the specified entry.
8137 * Note: we don't update the version of the Edit List Box. */
8138 lsmash_bs_t *bs = root->bs;
8139 FILE *stream = bs->stream;
8140 uint64_t current_pos = lsmash_ftell( stream );
8141 uint64_t entry_pos = elst->pos + ISOM_LIST_FULLBOX_COMMON_SIZE + ((uint64_t)entry_number - 1) * (elst->version == 1 ? 20 : 12);
8142 lsmash_fseek( stream, entry_pos, SEEK_SET );
8143 if( elst->version )
8145 lsmash_bs_put_be64( bs, data->segment_duration );
8146 lsmash_bs_put_be64( bs, data->media_time );
8148 else
8150 lsmash_bs_put_be32( bs, (uint32_t)data->segment_duration );
8151 lsmash_bs_put_be32( bs, (uint32_t)data->media_time );
8153 lsmash_bs_put_be32( bs, data->media_rate );
8154 int ret = lsmash_bs_write_data( bs );
8155 lsmash_fseek( stream, current_pos, SEEK_SET );
8156 return ret;
8159 int lsmash_create_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint64_t segment_duration, int64_t media_time, int32_t media_rate )
8161 if( media_time < -1 )
8162 return -1;
8163 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
8164 if( !trak || !trak->tkhd )
8165 return -1;
8166 segment_duration = (segment_duration || root->fragment) ? segment_duration
8167 : trak->tkhd->duration ? trak->tkhd->duration
8168 : isom_update_tkhd_duration( trak ) ? 0
8169 : trak->tkhd->duration;
8170 if( isom_add_edts( trak )
8171 || isom_add_elst( trak->edts )
8172 || isom_add_elst_entry( trak->edts->elst, segment_duration, media_time, media_rate ) )
8173 return -1;
8174 return isom_update_tkhd_duration( trak );
8177 /*---- create / modification time fields manipulators ----*/
8179 int lsmash_update_media_modification_time( lsmash_root_t *root, uint32_t track_ID )
8181 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
8182 if( !trak || !trak->mdia || !trak->mdia->mdhd )
8183 return -1;
8184 isom_mdhd_t *mdhd = trak->mdia->mdhd;
8185 mdhd->modification_time = isom_get_current_mp4time();
8186 /* overwrite strange creation_time */
8187 if( mdhd->creation_time > mdhd->modification_time )
8188 mdhd->creation_time = mdhd->modification_time;
8189 return 0;
8192 int lsmash_update_track_modification_time( lsmash_root_t *root, uint32_t track_ID )
8194 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
8195 if( !trak || !trak->tkhd )
8196 return -1;
8197 isom_tkhd_t *tkhd = trak->tkhd;
8198 tkhd->modification_time = isom_get_current_mp4time();
8199 /* overwrite strange creation_time */
8200 if( tkhd->creation_time > tkhd->modification_time )
8201 tkhd->creation_time = tkhd->modification_time;
8202 return 0;
8205 int lsmash_update_movie_modification_time( lsmash_root_t *root )
8207 if( !root || !root->moov || !root->moov->mvhd )
8208 return -1;
8209 isom_mvhd_t *mvhd = root->moov->mvhd;
8210 mvhd->modification_time = isom_get_current_mp4time();
8211 /* overwrite strange creation_time */
8212 if( mvhd->creation_time > mvhd->modification_time )
8213 mvhd->creation_time = mvhd->modification_time;
8214 return 0;
8217 /*---- sample manipulators ----*/
8218 lsmash_sample_t *lsmash_create_sample( uint32_t size )
8220 lsmash_sample_t *sample = malloc( sizeof(lsmash_sample_t) );
8221 if( !sample )
8222 return NULL;
8223 memset( sample, 0, sizeof(lsmash_sample_t) );
8224 if( !size )
8225 return sample;
8226 sample->data = malloc( size );
8227 if( !sample->data )
8229 free( sample );
8230 return NULL;
8232 sample->length = size;
8233 return sample;
8236 int lsmash_sample_alloc( lsmash_sample_t *sample, uint32_t size )
8238 if( !sample )
8239 return -1;
8240 if( !size )
8242 if( sample->data )
8243 free( sample->data );
8244 sample->data = NULL;
8245 sample->length = 0;
8246 return 0;
8248 if( size == sample->length )
8249 return 0;
8250 uint8_t *data;
8251 if( !sample->data )
8252 data = malloc( size );
8253 else
8254 data = realloc( sample->data, size );
8255 if( !data )
8256 return -1;
8257 sample->data = data;
8258 sample->length = size;
8259 return 0;
8262 void lsmash_delete_sample( lsmash_sample_t *sample )
8264 if( !sample )
8265 return;
8266 if( sample->data )
8267 free( sample->data );
8268 free( sample );
8271 isom_sample_pool_t *isom_create_sample_pool( uint64_t size )
8273 isom_sample_pool_t *pool = malloc( sizeof(isom_sample_pool_t) );
8274 if( !pool )
8275 return NULL;
8276 memset( pool, 0, sizeof(isom_sample_pool_t) );
8277 if( !size )
8278 return pool;
8279 pool->data = malloc( size );
8280 if( !pool->data )
8282 free( pool );
8283 return NULL;
8285 pool->alloc = size;
8286 return pool;
8289 static void isom_remove_sample_pool( isom_sample_pool_t *pool )
8291 if( !pool )
8292 return;
8293 if( pool->data )
8294 free( pool->data );
8295 free( pool );
8298 static uint32_t isom_add_size( isom_trak_entry_t *trak, uint32_t sample_size )
8300 if( isom_add_stsz_entry( trak->mdia->minf->stbl, sample_size ) )
8301 return 0;
8302 return isom_get_sample_count( trak );
8305 static uint32_t isom_add_dts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t dts )
8307 isom_stts_t *stts = stbl->stts;
8308 if( !stts->list->entry_count )
8310 if( isom_add_stts_entry( stbl, dts ) )
8311 return 0;
8312 cache->dts = dts;
8313 return dts;
8315 if( dts <= cache->dts )
8316 return 0;
8317 uint32_t sample_delta = dts - cache->dts;
8318 isom_stts_entry_t *data = (isom_stts_entry_t *)stts->list->tail->data;
8319 if( data->sample_delta == sample_delta )
8320 ++ data->sample_count;
8321 else if( isom_add_stts_entry( stbl, sample_delta ) )
8322 return 0;
8323 cache->dts = dts;
8324 return sample_delta;
8327 static int isom_add_cts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t cts )
8329 isom_ctts_t *ctts = stbl->ctts;
8330 if( !ctts )
8332 if( cts == cache->dts )
8334 cache->cts = cts;
8335 return 0;
8337 /* Add ctts box and the first ctts entry. */
8338 if( isom_add_ctts( stbl ) || isom_add_ctts_entry( stbl, 0 ) )
8339 return -1;
8340 ctts = stbl->ctts;
8341 isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->head->data;
8342 uint32_t sample_count = stbl->stsz->sample_count;
8343 if( sample_count != 1 )
8345 data->sample_count = sample_count - 1;
8346 if( isom_add_ctts_entry( stbl, cts - cache->dts ) )
8347 return -1;
8349 else
8350 data->sample_offset = cts;
8351 cache->cts = cts;
8352 return 0;
8354 if( !ctts->list )
8355 return -1;
8356 isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->tail->data;
8357 uint32_t sample_offset = cts - cache->dts;
8358 if( data->sample_offset == sample_offset )
8359 ++ data->sample_count;
8360 else if( isom_add_ctts_entry( stbl, sample_offset ) )
8361 return -1;
8362 cache->cts = cts;
8363 return 0;
8366 static int isom_add_timestamp( isom_trak_entry_t *trak, uint64_t dts, uint64_t cts )
8368 if( !trak->cache || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list )
8369 return -1;
8370 lsmash_root_t *root = trak->root;
8371 if( root->isom_compatible && root->qt_compatible && (cts - dts) > INT32_MAX )
8372 return -1; /* sample_offset is not compatible. */
8373 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8374 isom_timestamp_t *ts_cache = &trak->cache->timestamp;
8375 uint32_t sample_count = isom_get_sample_count( trak );
8376 uint32_t sample_delta = sample_count > 1 ? isom_add_dts( stbl, ts_cache, dts ) : 0;
8377 if( sample_count > 1 && !sample_delta )
8378 return -1;
8379 if( isom_add_cts( stbl, ts_cache, cts ) )
8380 return -1;
8381 if( (cts + ts_cache->ctd_shift) < dts )
8383 if( (root->max_isom_version < 4 && !root->qt_compatible) /* Negative sample offset is not supported. */
8384 || (root->max_isom_version >= 4 && trak->root->qt_compatible) /* ctts version 1 is not defined in QTFF. */
8385 || root->fragment /* Composition time offset is positive. */
8386 || ((dts - cts) > INT32_MAX) ) /* Overflow */
8387 return -1;
8388 ts_cache->ctd_shift = dts - cts;
8389 if( !stbl->ctts->version && !trak->root->qt_compatible )
8390 stbl->ctts->version = 1;
8392 if( trak->cache->fragment )
8394 isom_fragment_t *fragment_cache = trak->cache->fragment;
8395 fragment_cache->last_duration = sample_delta;
8396 fragment_cache->largest_cts = LSMASH_MAX( ts_cache->cts, fragment_cache->largest_cts );
8398 return 0;
8401 static int isom_add_sync_point( isom_trak_entry_t *trak, uint32_t sample_number, lsmash_sample_property_t *prop )
8403 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8404 isom_cache_t *cache = trak->cache;
8405 if( prop->random_access_type != ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC ) /* no null check for prop */
8407 if( !cache->all_sync )
8408 return 0;
8409 if( !stbl->stss && isom_add_stss( stbl ) )
8410 return -1;
8411 if( isom_add_stss_entry( stbl, 1 ) ) /* Declare here the first sample is a sync sample. */
8412 return -1;
8413 cache->all_sync = 0;
8414 return 0;
8416 if( cache->all_sync ) /* We don't need stss box if all samples are sync sample. */
8417 return 0;
8418 if( !stbl->stss )
8420 if( isom_get_sample_count( trak ) == 1 )
8422 cache->all_sync = 1; /* Also the first sample is a sync sample. */
8423 return 0;
8425 if( isom_add_stss( stbl ) )
8426 return -1;
8428 return isom_add_stss_entry( stbl, sample_number );
8431 static int isom_add_partial_sync( isom_trak_entry_t *trak, uint32_t sample_number, lsmash_sample_property_t *prop )
8433 if( !trak->root->qt_compatible )
8434 return 0;
8435 if( prop->random_access_type != QT_SAMPLE_RANDOM_ACCESS_TYPE_PARTIAL_SYNC
8436 && !(prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_RECOVERY && prop->recovery.identifier == prop->recovery.complete) )
8437 return 0;
8438 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8439 if( !stbl->stps && isom_add_stps( stbl ) )
8440 return -1;
8441 return isom_add_stps_entry( stbl, sample_number );
8444 static int isom_add_dependency_type( isom_trak_entry_t *trak, lsmash_sample_property_t *prop )
8446 if( !trak->root->qt_compatible && !trak->root->avc_extensions )
8447 return 0;
8448 uint8_t avc_extensions = trak->root->avc_extensions;
8449 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8450 if( stbl->sdtp )
8451 return isom_add_sdtp_entry( stbl, prop, avc_extensions );
8452 if( !prop->allow_earlier && !prop->leading && !prop->independent && !prop->disposable && !prop->redundant ) /* no null check for prop */
8453 return 0;
8454 if( isom_add_sdtp( stbl ) )
8455 return -1;
8456 uint32_t count = isom_get_sample_count( trak );
8457 /* fill past samples with ISOM_SAMPLE_*_UNKNOWN */
8458 lsmash_sample_property_t null_prop = { 0 };
8459 for( uint32_t i = 1; i < count; i++ )
8460 if( isom_add_sdtp_entry( stbl, &null_prop, avc_extensions ) )
8461 return -1;
8462 return isom_add_sdtp_entry( stbl, prop, avc_extensions );
8465 static int isom_group_random_access( isom_trak_entry_t *trak, lsmash_sample_property_t *prop )
8467 if( trak->root->max_isom_version < 6 )
8468 return 0;
8469 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8470 isom_sbgp_entry_t *sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_RAP );
8471 isom_sgpd_entry_t *sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
8472 if( !sbgp || !sgpd )
8473 return 0;
8474 uint8_t is_rap = prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP
8475 || prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_OPEN_RAP
8476 || prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_UNKNOWN_RAP
8477 || (prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_RECOVERY && prop->recovery.identifier == prop->recovery.complete);
8478 isom_rap_group_t *group = trak->cache->rap;
8479 if( !group )
8481 /* This sample is the first sample, create a grouping cache. */
8482 assert( isom_get_sample_count( trak ) == 1 );
8483 group = malloc( sizeof(isom_rap_group_t) );
8484 if( !group )
8485 return -1;
8486 if( is_rap )
8488 group->random_access = isom_add_rap_group_entry( sgpd );
8489 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count );
8491 else
8493 /* The first sample is not always random access point. */
8494 group->random_access = NULL;
8495 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
8497 if( !group->assignment )
8499 free( group );
8500 return -1;
8502 /* No need checking if group->assignment exists from here. */
8503 group->is_prev_rap = is_rap;
8504 trak->cache->rap = group;
8505 return 0;
8507 if( group->is_prev_rap )
8509 /* OK. here, the previous sample is a menber of 'rap '. */
8510 if( !is_rap )
8512 /* This sample isn't a member of 'rap ' and the previous sample is.
8513 * So we create a new group and set 0 on its group_description_index. */
8514 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
8515 if( !group->assignment )
8517 free( group );
8518 return -1;
8521 else if( prop->random_access_type != ISOM_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP )
8523 /* Create a new group since there is the possibility the next sample is a leading sample.
8524 * This sample is a member of 'rap ', so we set appropriate value on its group_description_index. */
8525 if( group->random_access )
8526 group->random_access->num_leading_samples_known = 1;
8527 group->random_access = isom_add_rap_group_entry( sgpd );
8528 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count );
8529 if( !group->assignment )
8531 free( group );
8532 return -1;
8535 else /* The previous and current sample are a member of 'rap ', and the next sample must not be a leading sample. */
8536 ++ group->assignment->sample_count;
8538 else if( is_rap )
8540 /* This sample is a member of 'rap ' and the previous sample isn't.
8541 * So we create a new group and set appropriate value on its group_description_index. */
8542 if( group->random_access )
8543 group->random_access->num_leading_samples_known = 1;
8544 group->random_access = isom_add_rap_group_entry( sgpd );
8545 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count );
8546 if( !group->assignment )
8548 free( group );
8549 return -1;
8552 else /* The previous and current sample aren't a member of 'rap '. */
8553 ++ group->assignment->sample_count;
8554 /* Obtain the property of the latest random access point group. */
8555 if( !is_rap && group->random_access )
8557 if( prop->leading == ISOM_SAMPLE_LEADING_UNKNOWN )
8559 /* We can no longer know num_leading_samples in this group. */
8560 group->random_access->num_leading_samples_known = 0;
8561 group->random_access = NULL;
8563 else
8565 if( prop->leading == ISOM_SAMPLE_IS_UNDECODABLE_LEADING || prop->leading == ISOM_SAMPLE_IS_DECODABLE_LEADING )
8566 ++ group->random_access->num_leading_samples;
8567 else
8569 /* no more consecutive leading samples in this group */
8570 group->random_access->num_leading_samples_known = 1;
8571 group->random_access = NULL;
8575 group->is_prev_rap = is_rap;
8576 return 0;
8579 static int isom_group_roll_recovery( isom_trak_entry_t *trak, lsmash_sample_property_t *prop )
8581 if( !trak->root->avc_extensions )
8582 return 0;
8583 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8584 isom_sbgp_entry_t *sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_ROLL );
8585 isom_sgpd_entry_t *sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_ROLL );
8586 if( !sbgp || !sgpd )
8587 return 0;
8588 lsmash_entry_list_t *pool = trak->cache->roll.pool;
8589 if( !pool )
8591 pool = lsmash_create_entry_list();
8592 if( !pool )
8593 return -1;
8594 trak->cache->roll.pool = pool;
8596 isom_roll_group_t *group = (isom_roll_group_t *)lsmash_get_entry_data( pool, pool->entry_count );
8597 uint32_t sample_count = isom_get_sample_count( trak );
8598 if( !group || prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_RECOVERY )
8600 if( group )
8601 group->delimited = 1;
8602 else
8603 assert( sample_count == 1 );
8604 /* Create a new group. This group is not 'roll' yet, so we set 0 on its group_description_index. */
8605 group = malloc( sizeof(isom_roll_group_t) );
8606 if( !group )
8607 return -1;
8608 memset( group, 0, sizeof(isom_roll_group_t) );
8609 group->first_sample = sample_count;
8610 group->recovery_point = prop->recovery.complete;
8611 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
8612 if( !group->assignment || lsmash_add_entry( pool, group ) )
8614 free( group );
8615 return -1;
8618 else
8619 ++ group->assignment->sample_count;
8620 /* If encountered a sync sample, all recoveries are completed here. */
8621 if( prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP )
8623 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
8625 group = (isom_roll_group_t *)entry->data;
8626 if( !group )
8627 return -1;
8628 group->described = 1;
8630 return 0;
8632 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
8634 group = (isom_roll_group_t *)entry->data;
8635 if( !group )
8636 return -1;
8637 if( group->described )
8638 continue;
8639 if( prop->recovery.identifier == group->recovery_point )
8641 group->described = 1;
8642 int16_t distance = sample_count - group->first_sample;
8643 /* Add a roll recovery entry only when roll_distance isn't zero since roll_distance = 0 must not be used. */
8644 if( distance )
8646 /* Now, this group is a 'roll'. */
8647 if( !isom_add_roll_group_entry( sgpd, distance ) )
8648 return -1;
8649 group->assignment->group_description_index = sgpd->list->entry_count;
8650 /* All groups before the current group are described. */
8651 lsmash_entry_t *current = entry;
8652 for( entry = pool->head; entry != current; entry = entry->next )
8654 group = (isom_roll_group_t *)entry->data;
8655 if( !group )
8656 return -1;
8657 group->described = 1;
8660 break; /* Avoid evaluating groups, in the pool, having the same identifier for recovery point again. */
8663 /* Remove pooled caches that has become unnecessary. */
8664 for( lsmash_entry_t *entry = pool->head; entry; entry = pool->head )
8666 group = (isom_roll_group_t *)entry->data;
8667 if( !group )
8668 return -1;
8669 if( !group->delimited || !group->described )
8670 break;
8671 if( lsmash_remove_entry_direct( pool, entry, NULL ) )
8672 return -1;
8674 return 0;
8677 /* returns 1 if pooled samples must be flushed. */
8678 /* FIXME: I wonder if this function should have a extra argument which indicates force_to_flush_cached_chunk.
8679 see lsmash_append_sample for detail. */
8680 static int isom_add_chunk( isom_trak_entry_t *trak, lsmash_sample_t *sample )
8682 if( !trak->root || !trak->cache || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale
8683 || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
8684 return -1;
8685 lsmash_root_t *root = trak->root;
8686 isom_chunk_t *current = &trak->cache->chunk;
8687 if( !current->pool )
8689 /* Very initial settings, just once per track */
8690 current->pool = isom_create_sample_pool( 0 );
8691 if( !current->pool )
8692 return -1;
8694 if( !current->pool->sample_count )
8696 /* Cannot decide whether we should flush the current sample or not here yet. */
8697 ++ current->chunk_number;
8698 current->sample_description_index = sample->index;
8699 current->first_dts = sample->dts;
8700 return 0;
8702 if( sample->dts < current->first_dts )
8703 return -1; /* easy error check. */
8704 if( (root->max_chunk_duration >= ((double)(sample->dts - current->first_dts) / trak->mdia->mdhd->timescale))
8705 && (root->max_chunk_size >= current->pool->size + sample->length)
8706 && (current->sample_description_index == sample->index) )
8707 return 0; /* No need to flush current cached chunk, the current sample must be put into that. */
8708 /* NOTE: chunk relative stuff must be pushed into root after a chunk is fully determined with its contents. */
8709 /* now current cached chunk is fixed, actually add chunk relative properties to root accordingly. */
8710 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8711 lsmash_entry_t *last_stsc_entry = stbl->stsc->list->tail;
8712 /* Create a new chunk sequence in this track if needed. */
8713 if( (!last_stsc_entry || current->pool->sample_count != ((isom_stsc_entry_t *)last_stsc_entry->data)->samples_per_chunk)
8714 && isom_add_stsc_entry( stbl, current->chunk_number, current->pool->sample_count, current->sample_description_index ) )
8715 return -1;
8716 /* Add a new chunk offset in this track. */
8717 uint64_t offset = root->size;
8718 if( root->fragment )
8719 offset += ISOM_BASEBOX_COMMON_SIZE + root->fragment->pool_size;
8720 if( isom_add_stco_entry( stbl, offset ) )
8721 return -1;
8722 /* update cache information */
8723 ++ current->chunk_number;
8724 /* re-initialize cache, using the current sample */
8725 current->sample_description_index = sample->index;
8726 current->first_dts = sample->dts;
8727 /* current->pool must be flushed in isom_append_sample_internal() */
8728 return 1;
8731 static int isom_write_pooled_samples( lsmash_root_t *root, isom_sample_pool_t *pool )
8733 if( !root || !root->mdat || !root->bs || !root->bs->stream )
8734 return -1;
8735 lsmash_bs_put_bytes( root->bs, pool->data, pool->size );
8736 if( lsmash_bs_write_data( root->bs ) )
8737 return -1;
8738 root->mdat->size += pool->size;
8739 root->size += pool->size;
8740 pool->sample_count = 0;
8741 pool->size = 0;
8742 return 0;
8745 static int isom_update_sample_tables( isom_trak_entry_t *trak, lsmash_sample_t *sample )
8747 /* Add a sample_size and increment sample_count. */
8748 uint32_t sample_count = isom_add_size( trak, sample->length );
8749 if( !sample_count )
8750 return -1;
8751 /* Add a decoding timestamp and a composition timestamp. */
8752 if( isom_add_timestamp( trak, sample->dts, sample->cts ) )
8753 return -1;
8754 /* Add a sync point if needed. */
8755 if( isom_add_sync_point( trak, sample_count, &sample->prop ) )
8756 return -1;
8757 /* Add a partial sync point if needed. */
8758 if( isom_add_partial_sync( trak, sample_count, &sample->prop ) )
8759 return -1;
8760 /* Add leading, independent, disposable and redundant information if needed. */
8761 if( isom_add_dependency_type( trak, &sample->prop ) )
8762 return -1;
8763 /* Group samples into random access point type if needed. */
8764 if( isom_group_random_access( trak, &sample->prop ) )
8765 return -1;
8766 /* Group samples into random access recovery point type if needed. */
8767 if( isom_group_roll_recovery( trak, &sample->prop ) )
8768 return -1;
8769 /* Add a chunk if needed. */
8770 return isom_add_chunk( trak, sample );
8773 static int isom_append_fragment_track_run( lsmash_root_t *root, isom_chunk_t *chunk )
8775 if( !chunk->pool || !chunk->pool->size )
8776 return 0;
8777 isom_fragment_manager_t *fragment = root->fragment;
8778 /* Move data in the pool of the current track fragment to the pool of the current movie fragment.
8779 * Empty the pool of current track. We don't delete data of samples here. */
8780 if( lsmash_add_entry( fragment->pool, chunk->pool ) )
8781 return -1;
8782 fragment->pool->entry_count += chunk->pool->sample_count;
8783 fragment->pool_size += chunk->pool->size;
8784 chunk->pool = isom_create_sample_pool( chunk->pool->size );
8785 return chunk->pool ? 0 : -1;
8788 static int isom_output_cached_chunk( isom_trak_entry_t *trak )
8790 lsmash_root_t *root = trak->root;
8791 isom_chunk_t *chunk = &trak->cache->chunk;
8792 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8793 lsmash_entry_t *last_stsc_entry = stbl->stsc->list->tail;
8794 /* Create a new chunk sequence in this track if needed. */
8795 if( (!last_stsc_entry || chunk->pool->sample_count != ((isom_stsc_entry_t *)last_stsc_entry->data)->samples_per_chunk)
8796 && isom_add_stsc_entry( stbl, chunk->chunk_number, chunk->pool->sample_count, chunk->sample_description_index ) )
8797 return -1;
8798 if( root->fragment )
8800 /* Add a new chunk offset in this track. */
8801 if( isom_add_stco_entry( stbl, root->size + ISOM_BASEBOX_COMMON_SIZE + root->fragment->pool_size ) )
8802 return -1;
8803 return isom_append_fragment_track_run( root, chunk );
8805 /* Add a new chunk offset in this track. */
8806 if( isom_add_stco_entry( stbl, root->size ) )
8807 return -1;
8808 /* Output pooled samples in this track. */
8809 return isom_write_pooled_samples( root, chunk->pool );
8812 static int isom_pool_sample( isom_sample_pool_t *pool, lsmash_sample_t *sample )
8814 uint64_t pool_size = pool->size + sample->length;
8815 if( pool->alloc < pool_size )
8817 uint8_t *data;
8818 uint64_t alloc = pool_size + (1<<16);
8819 if( !pool->data )
8820 data = malloc( alloc );
8821 else
8822 data = realloc( pool->data, alloc );
8823 if( !data )
8824 return -1;
8825 pool->data = data;
8826 pool->alloc = alloc;
8828 memcpy( pool->data + pool->size, sample->data, sample->length );
8829 pool->size = pool_size;
8830 pool->sample_count += 1;
8831 lsmash_delete_sample( sample );
8832 return 0;
8835 static int isom_append_sample_internal( isom_trak_entry_t *trak, lsmash_sample_t *sample )
8837 int flush = isom_update_sample_tables( trak, sample );
8838 if( flush < 0 )
8839 return -1;
8840 /* flush == 1 means pooled samples must be flushed. */
8841 lsmash_root_t *root = trak->root;
8842 isom_sample_pool_t *current_pool = trak->cache->chunk.pool;
8843 if( flush == 1 && isom_write_pooled_samples( root, current_pool ) )
8844 return -1;
8845 /* Arbitration system between tracks with extremely scattering dts.
8846 * Here, we check whether asynchronization between the tracks exceeds the tolerance.
8847 * If a track has too old "first DTS" in its cached chunk than current sample's DTS, then its pooled samples must be flushed.
8848 * We don't consider presentation of media since any edit can pick an arbitrary portion of media in track.
8849 * Note: you needn't read this loop until you grasp the basic handling of chunks. */
8850 double tolerance = root->max_async_tolerance;
8851 for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
8853 isom_trak_entry_t *other = (isom_trak_entry_t *)entry->data;
8854 if( trak == other )
8855 continue;
8856 if( !other || !other->cache || !other->mdia || !other->mdia->mdhd || !other->mdia->mdhd->timescale
8857 || !other->mdia->minf || !other->mdia->minf->stbl || !other->mdia->minf->stbl->stsc || !other->mdia->minf->stbl->stsc->list )
8858 return -1;
8859 isom_chunk_t *chunk = &other->cache->chunk;
8860 if( !chunk->pool || !chunk->pool->sample_count )
8861 continue;
8862 double diff = ((double)sample->dts / trak->mdia->mdhd->timescale)
8863 - ((double)chunk->first_dts / other->mdia->mdhd->timescale);
8864 if( diff > tolerance && isom_output_cached_chunk( other ) )
8865 return -1;
8866 /* Note: we don't flush the cached chunk in the current track and the current sample here
8867 * even if the conditional expression of '-diff > tolerance' meets.
8868 * That's useless because appending a sample to another track would be a good equivalent.
8869 * It's even harmful because it causes excess chunk division by calling
8870 * isom_output_cached_chunk() which always generates a new chunk.
8871 * Anyway some excess chunk division will be there, but rather less without it.
8872 * To completely avoid this, we need to observe at least whether the current sample will be placed
8873 * right next to the previous chunk of the same track or not. */
8875 /* anyway the current sample must be pooled. */
8876 return isom_pool_sample( current_pool, sample );
8879 static int isom_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample )
8881 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
8882 if( !trak || !trak->root || !trak->cache || !trak->mdia
8883 || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale
8884 || !trak->mdia->minf || !trak->mdia->minf->stbl
8885 || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list
8886 || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
8887 return -1;
8888 /* If there is no available Media Data Box to write samples, add and write a new one before any chunk offset is decided. */
8889 if( !root->mdat )
8891 if( isom_new_mdat( root, 0 ) )
8892 return -1;
8893 /* Add the size of the Media Data Box and the placeholder. */
8894 root->size += 2 * ISOM_BASEBOX_COMMON_SIZE;
8896 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, sample->index );
8897 if( !sample_entry )
8898 return -1;
8899 if( isom_is_lpcm_audio( sample_entry->type ) )
8901 uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket;
8902 if( sample->length == frame_size )
8903 return isom_append_sample_internal( trak, sample );
8904 else if( sample->length < frame_size )
8905 return -1;
8906 /* Append samples splitted into each LPCMFrame. */
8907 uint64_t dts = sample->dts;
8908 uint64_t cts = sample->cts;
8909 for( uint32_t offset = 0; offset < sample->length; offset += frame_size )
8911 lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size );
8912 if( !lpcm_sample )
8913 return -1;
8914 memcpy( lpcm_sample->data, sample->data + offset, frame_size );
8915 lpcm_sample->dts = dts++;
8916 lpcm_sample->cts = cts++;
8917 lpcm_sample->prop = sample->prop;
8918 lpcm_sample->index = sample->index;
8919 if( isom_append_sample_internal( trak, lpcm_sample ) )
8921 lsmash_delete_sample( lpcm_sample );
8922 return -1;
8925 lsmash_delete_sample( sample );
8926 return 0;
8928 return isom_append_sample_internal( trak, sample );
8931 static int isom_output_cache( isom_trak_entry_t *trak )
8933 if( trak->cache->chunk.pool && trak->cache->chunk.pool->sample_count
8934 && isom_output_cached_chunk( trak ) )
8935 return -1;
8936 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8937 if( !stbl->sgpd_list )
8938 return 0;
8939 for( lsmash_entry_t *entry = stbl->sgpd_list->head; entry; entry = entry->next )
8941 isom_sgpd_entry_t *sgpd = (isom_sgpd_entry_t *)entry->data;
8942 if( !sgpd )
8943 return -1;
8944 switch( sgpd->grouping_type )
8946 case ISOM_GROUP_TYPE_RAP :
8948 isom_rap_group_t *group = trak->cache->rap;
8949 if( !group )
8951 if( trak->root->fragment )
8952 continue;
8953 else
8954 return -1;
8956 if( !group->random_access )
8957 continue;
8958 group->random_access->num_leading_samples_known = 1;
8959 break;
8961 case ISOM_GROUP_TYPE_ROLL :
8962 if( !trak->cache->roll.pool )
8964 if( trak->root->fragment )
8965 continue;
8966 else
8967 return -1;
8969 for( lsmash_entry_t *roll_entry = trak->cache->roll.pool->head; roll_entry; roll_entry = roll_entry->next )
8971 isom_roll_group_t *group = (isom_roll_group_t *)roll_entry->data;
8972 if( !group )
8973 return -1;
8974 group->described = 1;
8976 break;
8977 default :
8978 break;
8981 return 0;
8984 static int isom_flush_fragment_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_duration )
8986 isom_traf_entry_t *traf = isom_get_traf( root->fragment->movie, track_ID );
8987 if( !traf )
8988 return 0; /* no samples */
8989 if( !traf->cache || !traf->cache->fragment )
8990 return -1;
8991 if( traf->trun_list && traf->trun_list->entry_count && traf->trun_list->tail && traf->trun_list->tail->data )
8993 /* Media Data Box preceded by Movie Fragment Box could change base_data_offsets in each track fragments later.
8994 * We can't consider this here because the length of Movie Fragment Box is unknown at this step yet. */
8995 isom_trun_entry_t *trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
8996 if( root->fragment->pool_size )
8997 trun->flags |= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT;
8998 trun->data_offset = root->fragment->pool_size;
9000 if( isom_append_fragment_track_run( root, &traf->cache->chunk ) )
9001 return -1;
9002 return isom_set_fragment_last_duration( traf, last_sample_duration );
9005 int lsmash_flush_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta )
9007 if( !root )
9008 return -1;
9009 if( root->fragment && root->fragment->movie )
9010 return isom_flush_fragment_pooled_samples( root, track_ID, last_sample_delta );
9011 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
9012 if( !trak || !trak->cache || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl
9013 || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
9014 return -1;
9015 if( isom_output_cache( trak ) )
9016 return -1;
9017 return lsmash_set_last_sample_delta( root, track_ID, last_sample_delta );
9020 /* This function doesn't update sample_duration of the last sample in the previous movie fragment.
9021 * Instead of this, isom_finish_movie_fragment undertakes this task. */
9022 static int isom_update_fragment_previous_sample_duration( isom_traf_entry_t *traf, isom_trex_entry_t *trex, uint32_t duration )
9024 isom_tfhd_t *tfhd = traf->tfhd;
9025 isom_trun_entry_t *trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
9026 int previous_run_has_previous_sample = 0;
9027 if( trun->sample_count == 1 )
9029 if( traf->trun_list->entry_count == 1 )
9030 return 0; /* The previous track run belongs to the previous movie fragment if it exists. */
9031 if( !traf->trun_list->tail->prev || !traf->trun_list->tail->prev->data )
9032 return -1;
9033 /* OK. The previous sample exists in the previous track run in the same track fragment. */
9034 trun = (isom_trun_entry_t *)traf->trun_list->tail->prev->data;
9035 previous_run_has_previous_sample = 1;
9037 /* Update default_sample_duration of the Track Fragment Header Box
9038 * if this duration is what the first sample in the current track fragment owns. */
9039 if( (trun->sample_count == 2 && traf->trun_list->entry_count == 1)
9040 || (trun->sample_count == 1 && traf->trun_list->entry_count == 2) )
9042 if( duration != trex->default_sample_duration )
9043 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
9044 tfhd->default_sample_duration = duration;
9046 /* Update the previous sample_duration if needed. */
9047 if( duration != tfhd->default_sample_duration )
9048 trun->flags |= ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT;
9049 if( trun->flags )
9051 uint32_t sample_number = trun->sample_count - !previous_run_has_previous_sample;
9052 isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, sample_number );
9053 if( !row )
9054 return -1;
9055 row->sample_duration = duration;
9057 traf->cache->fragment->last_duration = duration;
9058 return 0;
9061 static isom_sample_flags_t isom_generate_fragment_sample_flags( lsmash_sample_t *sample )
9063 isom_sample_flags_t flags;
9064 flags.reserved = 0;
9065 flags.is_leading = sample->prop.leading & 0x3;
9066 flags.sample_depends_on = sample->prop.independent & 0x3;
9067 flags.sample_is_depended_on = sample->prop.disposable & 0x3;
9068 flags.sample_has_redundancy = sample->prop.redundant & 0x3;
9069 flags.sample_padding_value = 0;
9070 flags.sample_is_non_sync_sample = sample->prop.random_access_type != ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
9071 flags.sample_degradation_priority = 0;
9072 return flags;
9075 static int isom_update_fragment_sample_tables( isom_traf_entry_t *traf, lsmash_sample_t *sample )
9077 isom_tfhd_t *tfhd = traf->tfhd;
9078 isom_trex_entry_t *trex = isom_get_trex( traf->root->moov->mvex, tfhd->track_ID );
9079 if( !trex )
9080 return -1;
9081 lsmash_root_t *root = traf->root;
9082 isom_cache_t *cache = traf->cache;
9083 isom_chunk_t *current = &cache->chunk;
9084 /* Create a new track run if the duration exceeds max_chunk_duration.
9085 * Old one will be appended to the pool of this movie fragment. */
9086 int delimit = (root->max_chunk_duration < ((double)(sample->dts - current->first_dts) / lsmash_get_media_timescale( root, tfhd->track_ID )))
9087 || (root->max_chunk_size < (current->pool->size + sample->length));
9088 isom_trun_entry_t *trun = NULL;
9089 if( !traf->trun_list || !traf->trun_list->entry_count || delimit )
9091 if( delimit && traf->trun_list && traf->trun_list->entry_count && traf->trun_list->tail && traf->trun_list->tail->data )
9093 /* Media Data Box preceded by Movie Fragment Box could change base data offsets in each track fragments later.
9094 * We can't consider this here because the length of Movie Fragment Box is unknown at this step yet. */
9095 trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
9096 if( root->fragment->pool_size )
9097 trun->flags |= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT;
9098 trun->data_offset = root->fragment->pool_size;
9100 trun = isom_add_trun( traf );
9101 if( !trun )
9102 return -1;
9103 if( !current->pool )
9105 /* Very initial settings, just once per track */
9106 current->pool = isom_create_sample_pool( 0 );
9107 if( !current->pool )
9108 return -1;
9111 else
9113 if( !traf->trun_list->tail || !traf->trun_list->tail->data )
9114 return -1;
9115 trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
9117 uint32_t sample_composition_time_offset = sample->cts - sample->dts;
9118 isom_sample_flags_t sample_flags = isom_generate_fragment_sample_flags( sample );
9119 if( ++trun->sample_count == 1 )
9121 if( traf->trun_list->entry_count == 1 )
9123 /* This track fragment isn't empty-duration-fragment any more. */
9124 tfhd->flags &= ~ISOM_TF_FLAGS_DURATION_IS_EMPTY;
9125 /* Set up sample_description_index in this track fragment. */
9126 if( sample->index != trex->default_sample_description_index )
9127 tfhd->flags |= ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT;
9128 tfhd->sample_description_index = current->sample_description_index = sample->index;
9129 /* Set up default_sample_size used in this track fragment. */
9130 tfhd->default_sample_size = sample->length;
9131 /* Set up default_sample_flags used in this track fragment.
9132 * Note: we decide an appropriate default value at the end of this movie fragment. */
9133 tfhd->default_sample_flags = sample_flags;
9134 /* Set up random access information if this sample is random accessible sample.
9135 * We inform only the first sample in each movie fragment. */
9136 if( root->bs->stream != stdout && sample->prop.random_access_type )
9138 isom_tfra_entry_t *tfra = isom_get_tfra( root->mfra, tfhd->track_ID );
9139 if( !tfra )
9141 tfra = isom_add_tfra( root->mfra );
9142 if( !tfra )
9143 return -1;
9144 tfra->track_ID = tfhd->track_ID;
9146 if( !tfra->list )
9148 tfra->list = lsmash_create_entry_list();
9149 if( !tfra->list )
9150 return -1;
9152 isom_tfra_location_time_entry_t *rap = malloc( sizeof(isom_tfra_location_time_entry_t) );
9153 if( !rap )
9154 return -1;
9155 rap->time = sample->cts; /* If this is wrong, blame vague descriptions of 'presentation time' in the spec. */
9156 rap->moof_offset = root->size; /* We place Movie Fragment Box in the head of each movie fragment. */
9157 rap->traf_number = cache->fragment->traf_number;
9158 rap->trun_number = traf->trun_list->entry_count;
9159 rap->sample_number = trun->sample_count;
9160 if( lsmash_add_entry( tfra->list, rap ) )
9161 return -1;
9162 tfra->number_of_entry = tfra->list->entry_count;
9163 int length;
9164 for( length = 1; rap->traf_number >> (length * 8); length++ );
9165 tfra->length_size_of_traf_num = LSMASH_MAX( length - 1, tfra->length_size_of_traf_num );
9166 for( length = 1; rap->traf_number >> (length * 8); length++ );
9167 tfra->length_size_of_trun_num = LSMASH_MAX( length - 1, tfra->length_size_of_trun_num );
9168 for( length = 1; rap->sample_number >> (length * 8); length++ );
9169 tfra->length_size_of_sample_num = LSMASH_MAX( length - 1, tfra->length_size_of_sample_num );
9172 trun->first_sample_flags = sample_flags;
9173 current->first_dts = sample->dts;
9175 /* Update the optional rows in the current track run except for sample_duration if needed. */
9176 if( sample->length != tfhd->default_sample_size )
9177 trun->flags |= ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT;
9178 if( memcmp( &sample_flags, &tfhd->default_sample_flags, sizeof(isom_sample_flags_t) ) )
9179 trun->flags |= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT;
9180 if( sample_composition_time_offset )
9181 trun->flags |= ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT;
9182 if( trun->flags )
9184 isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, trun->sample_count );
9185 if( !row )
9186 return -1;
9187 row->sample_size = sample->length;
9188 row->sample_flags = sample_flags;
9189 row->sample_composition_time_offset = sample_composition_time_offset;
9191 /* Set up the previous sample_duration if this sample is not the first sample in the overall movie. */
9192 if( cache->fragment->has_samples )
9194 /* Note: when using for live streaming, it is not good idea to return error (-1) by sample->dts < prev_dts
9195 * since that's trivial for such semi-permanent presentation. */
9196 uint64_t prev_dts = cache->timestamp.dts;
9197 if( sample->dts <= prev_dts || sample->dts > prev_dts + UINT32_MAX )
9198 return -1;
9199 uint32_t sample_duration = sample->dts - prev_dts;
9200 if( isom_update_fragment_previous_sample_duration( traf, trex, sample_duration ) )
9201 return -1;
9203 cache->timestamp.dts = sample->dts;
9204 cache->fragment->largest_cts = LSMASH_MAX( sample->cts, cache->fragment->largest_cts );
9205 return delimit;
9208 static int isom_append_fragment_sample_internal_initial( isom_trak_entry_t *trak, lsmash_sample_t *sample )
9210 int delimit = 0;
9211 /* Update the sample tables of this track fragment.
9212 * If a new chunk was created, append the previous one to the pool of this movie fragment. */
9213 delimit = isom_update_sample_tables( trak, sample );
9214 if( delimit < 0 )
9215 return -1;
9216 else if( delimit == 1 )
9217 isom_append_fragment_track_run( trak->root, &trak->cache->chunk );
9218 /* Add a new sample into the pool of this track fragment. */
9219 if( isom_pool_sample( trak->cache->chunk.pool, sample ) )
9220 return -1;
9221 trak->cache->fragment->has_samples = 1;
9222 return 0;
9225 static int isom_append_fragment_sample_internal( isom_traf_entry_t *traf, lsmash_sample_t *sample )
9227 int delimit = 0;
9228 /* Update the sample tables of this track fragment.
9229 * If a new track run was created, append the previous one to the pool of this movie fragment. */
9230 delimit = isom_update_fragment_sample_tables( traf, sample );
9231 if( delimit < 0 )
9232 return -1;
9233 else if( delimit == 1 )
9234 isom_append_fragment_track_run( traf->root, &traf->cache->chunk );
9235 /* Add a new sample into the pool of this track fragment. */
9236 if( isom_pool_sample( traf->cache->chunk.pool, sample ) )
9237 return -1;
9238 traf->cache->fragment->has_samples = 1;
9239 return 0;
9242 static int isom_append_fragment_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample )
9244 isom_fragment_manager_t *fragment = root->fragment;
9245 if( !fragment || !fragment->pool )
9246 return -1;
9247 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
9248 if( !trak || !trak->root || !trak->cache || !trak->cache->fragment || !trak->tkhd || !trak->mdia
9249 || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale
9250 || !trak->mdia->minf || !trak->mdia->minf->stbl
9251 || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list
9252 || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
9253 return -1;
9254 int (*append_sample_func)( void *, lsmash_sample_t * ) = NULL;
9255 void *track_fragment = NULL;
9256 if( !fragment->movie )
9258 append_sample_func = (int (*)( void *, lsmash_sample_t * ))isom_append_fragment_sample_internal_initial;
9259 track_fragment = trak;
9261 else
9263 isom_traf_entry_t *traf = isom_get_traf( fragment->movie, track_ID );
9264 if( !traf )
9266 traf = isom_add_traf( root, fragment->movie );
9267 if( isom_add_tfhd( traf ) )
9268 return -1;
9269 traf->tfhd->flags = ISOM_TF_FLAGS_DURATION_IS_EMPTY; /* no samples for this track fragment yet */
9270 traf->tfhd->track_ID = trak->tkhd->track_ID;
9271 traf->cache = trak->cache;
9272 traf->cache->fragment->traf_number = fragment->movie->traf_list->entry_count;
9274 else if( !traf->root || !traf->root->moov || !traf->root->moov->mvex || !traf->cache || !traf->tfhd )
9275 return -1;
9276 append_sample_func = (int (*)( void *, lsmash_sample_t * ))isom_append_fragment_sample_internal;
9277 track_fragment = traf;
9279 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, sample->index );
9280 if( !sample_entry )
9281 return -1;
9282 if( isom_is_lpcm_audio( sample_entry->type ) )
9284 uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket;
9285 if( sample->length == frame_size )
9286 return append_sample_func( track_fragment, sample );
9287 else if( sample->length < frame_size )
9288 return -1;
9289 /* Append samples splitted into each LPCMFrame. */
9290 uint64_t dts = sample->dts;
9291 uint64_t cts = sample->cts;
9292 for( uint32_t offset = 0; offset < sample->length; offset += frame_size )
9294 lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size );
9295 if( !lpcm_sample )
9296 return -1;
9297 memcpy( lpcm_sample->data, sample->data + offset, frame_size );
9298 lpcm_sample->dts = dts++;
9299 lpcm_sample->cts = cts++;
9300 lpcm_sample->prop = sample->prop;
9301 lpcm_sample->index = sample->index;
9302 if( append_sample_func( track_fragment, lpcm_sample ) )
9304 lsmash_delete_sample( lpcm_sample );
9305 return -1;
9308 lsmash_delete_sample( sample );
9309 return 0;
9311 return append_sample_func( track_fragment, sample );
9314 int lsmash_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample )
9316 /* We think max_chunk_duration == 0, which means all samples will be cached on memory, should be prevented.
9317 * This means removal of a feature that we used to have, but anyway very alone chunk does not make sense. */
9318 if( !root || !root->bs || !sample || !sample->data || !track_ID
9319 || root->max_chunk_duration == 0 || root->max_async_tolerance == 0 )
9320 return -1;
9321 /* Write File Type Box here if it was not written yet. */
9322 if( !root->file_type_written && isom_write_ftyp( root ) )
9323 return -1;
9324 if( root->fragment && root->fragment->pool )
9325 return isom_append_fragment_sample( root, track_ID, sample );
9326 return isom_append_sample( root, track_ID, sample );
9329 /*---- misc functions ----*/
9331 #define CHAPTER_BUFSIZE 512 /* for chapter handling */
9333 static int isom_get_start_time( char *chap_time, isom_chapter_entry_t *data )
9335 uint64_t hh, mm;
9336 double ss;
9337 if( sscanf( chap_time, "%"SCNu64":%2"SCNu64":%lf", &hh, &mm, &ss ) != 3 )
9338 return -1;
9339 /* check overflow */
9340 if( hh >= 5124095 || mm >= 60 || ss >= 60 )
9341 return -1;
9342 /* 1ns timescale */
9343 data->start_time = (hh * 3600 + mm * 60 + ss) * 1e9;
9344 return 0;
9347 static int isom_lumber_line( char *buff, int bufsize, FILE *chapter )
9349 char *tail;
9350 /* remove newline codes and skip empty line */
9352 if( fgets( buff, bufsize, chapter ) == NULL )
9353 return -1;
9354 tail = &buff[ strlen( buff ) - 1 ];
9355 while( tail >= buff && ( *tail == '\n' || *tail == '\r' ) )
9356 *tail-- = '\0';
9357 }while( tail < buff );
9358 return 0;
9361 static int isom_read_simple_chapter( FILE *chapter, isom_chapter_entry_t *data )
9363 char buff[CHAPTER_BUFSIZE];
9364 int len;
9366 /* get start_time */
9367 if( isom_lumber_line( buff, CHAPTER_BUFSIZE, chapter ) )
9368 return -1;
9369 char *chapter_time = strchr( buff, '=' ); /* find separator */
9370 if( !chapter_time++
9371 || isom_get_start_time( chapter_time, data )
9372 || isom_lumber_line( buff, CHAPTER_BUFSIZE, chapter ) ) /* get chapter_name */
9373 return -1;
9374 char *chapter_name = strchr( buff, '=' ); /* find separator */
9375 if( !chapter_name++ )
9376 return -1;
9377 len = LSMASH_MIN( 255, strlen( chapter_name ) ); /* We support length of chapter_name up to 255 */
9378 data->chapter_name = ( char* )malloc( len + 1 );
9379 if( !data->chapter_name )
9380 return -1;
9381 memcpy( data->chapter_name, chapter_name, len );
9382 data->chapter_name[len] = '\0';
9383 return 0;
9386 static int isom_read_minimum_chapter( FILE *chapter, isom_chapter_entry_t *data )
9388 char buff[CHAPTER_BUFSIZE];
9389 int len;
9391 if( isom_lumber_line( buff, CHAPTER_BUFSIZE, chapter ) /* read newline */
9392 || isom_get_start_time( buff, data ) ) /* get start_time */
9393 return -1;
9394 /* get chapter_name */
9395 char *chapter_name = strchr( buff, ' ' ); /* find separator */
9396 if( !chapter_name++ )
9397 return -1;
9398 len = LSMASH_MIN( 255, strlen( chapter_name ) ); /* We support length of chapter_name up to 255 */
9399 data->chapter_name = ( char* )malloc( len + 1 );
9400 if( !data->chapter_name )
9401 return -1;
9402 memcpy( data->chapter_name, chapter_name, len );
9403 data->chapter_name[len] = '\0';
9404 return 0;
9407 typedef int (*fn_get_chapter_data)( FILE *, isom_chapter_entry_t * );
9409 static fn_get_chapter_data isom_check_chap_line( char *file_name )
9411 char buff[CHAPTER_BUFSIZE];
9412 FILE *fp = fopen( file_name, "rb" );
9413 if( !fp )
9414 return NULL;
9415 fn_get_chapter_data fnc = NULL;
9416 if( fgets( buff, CHAPTER_BUFSIZE, fp ) != NULL )
9418 if( strncmp( buff, "CHAPTER", 7 ) == 0 )
9419 fnc = isom_read_simple_chapter;
9420 else if( isdigit( buff[0] ) && isdigit( buff[1] ) && buff[2] == ':'
9421 && isdigit( buff[3] ) && isdigit( buff[4] ) && buff[5] == ':' )
9422 fnc = isom_read_minimum_chapter;
9424 fclose( fp );
9425 return fnc;
9428 int lsmash_set_tyrant_chapter( lsmash_root_t *root, char *file_name )
9430 /* This function should be called after updating of the latest movie duration. */
9431 if( !root || !root->moov || !root->moov->mvhd || !root->moov->mvhd->timescale || !root->moov->mvhd->duration )
9432 return -1;
9433 /* check each line format */
9434 fn_get_chapter_data fnc = isom_check_chap_line( file_name );
9435 if( !fnc )
9436 return -1;
9437 FILE *chapter = fopen( file_name, "rb" );
9438 if( !chapter )
9439 return -1;
9440 if( isom_add_udta( root, 0 ) || isom_add_chpl( root->moov ) )
9441 goto fail;
9442 isom_chapter_entry_t data;
9443 while( !fnc( chapter, &data ) )
9445 data.start_time = (data.start_time + 50) / 100; /* convert to 100ns unit */
9446 if( data.start_time / 1e7 > (double)root->moov->mvhd->duration / root->moov->mvhd->timescale
9447 || isom_add_chpl_entry( root->moov->udta->chpl, &data ) )
9448 goto fail;
9449 free( data.chapter_name );
9450 data.chapter_name = NULL;
9452 fclose( chapter );
9453 return 0;
9454 fail:
9455 if( data.chapter_name )
9456 free( data.chapter_name );
9457 fclose( chapter );
9458 return -1;
9461 int lsmash_create_reference_chapter_track( lsmash_root_t *root, uint32_t track_ID, char *file_name )
9463 if( !root || (!root->qt_compatible && !root->itunes_audio) || !root->moov || !root->moov->mvhd )
9464 return -1;
9465 FILE *chapter = NULL; /* shut up 'uninitialized' warning */
9466 /* Create a Track Reference Box. */
9467 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
9468 if( !trak || isom_add_tref( trak ) )
9469 return -1;
9470 /* Create a track_ID for a new chapter track. */
9471 uint32_t *id = (uint32_t *)malloc( sizeof(uint32_t) );
9472 if( !id )
9473 return -1;
9474 uint32_t chapter_track_ID = *id = root->moov->mvhd->next_track_ID;
9475 /* Create a Track Reference Type Box. */
9476 isom_tref_type_t *chap = isom_add_track_reference_type( trak->tref, QT_TREF_TYPE_CHAP, 1, id );
9477 if( !chap )
9478 return -1; /* no need to free id */
9479 /* Create a reference chapter track. */
9480 if( chapter_track_ID != lsmash_create_track( root, ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK ) )
9481 return -1;
9482 /* Set track parameters. */
9483 lsmash_track_parameters_t track_param;
9484 lsmash_initialize_track_parameters( &track_param );
9485 track_param.mode = ISOM_TRACK_IN_MOVIE | ISOM_TRACK_IN_PREVIEW;
9486 if( lsmash_set_track_parameters( root, chapter_track_ID, &track_param ) )
9487 goto fail;
9488 /* Set media parameters. */
9489 uint64_t media_timescale = lsmash_get_media_timescale( root, track_ID );
9490 if( !media_timescale )
9491 goto fail;
9492 lsmash_media_parameters_t media_param;
9493 lsmash_initialize_media_parameters( &media_param );
9494 media_param.timescale = media_timescale;
9495 media_param.ISO_language = root->max_3gpp_version >= 6 || root->itunes_audio ? ISOM_LANGUAGE_CODE_UNDEFINED : 0;
9496 media_param.MAC_language = 0;
9497 if( lsmash_set_media_parameters( root, chapter_track_ID, &media_param ) )
9498 goto fail;
9499 /* Create a sample description. */
9500 uint32_t sample_type = root->max_3gpp_version >= 6 || root->itunes_audio ? ISOM_CODEC_TYPE_TX3G_TEXT : QT_CODEC_TYPE_TEXT_TEXT;
9501 uint32_t sample_entry = lsmash_add_sample_entry( root, chapter_track_ID, sample_type, NULL );
9502 if( !sample_entry )
9503 goto fail;
9504 /* Check each line format. */
9505 fn_get_chapter_data fnc = isom_check_chap_line( file_name );
9506 if( !fnc )
9507 return -1;
9508 /* Open chapter format file. */
9509 chapter = fopen( file_name, "rb" );
9510 if( !chapter )
9511 goto fail;
9512 /* Parse the file and write text samples. */
9513 isom_chapter_entry_t data;
9514 while( !fnc( chapter, &data ) )
9516 /* set start_time */
9517 data.start_time = data.start_time * 1e-9 * media_timescale + 0.5;
9518 /* write a text sample here */
9519 uint16_t name_length = strlen( data.chapter_name );
9520 lsmash_sample_t *sample = lsmash_create_sample( 2 + name_length + 12 * (sample_type == QT_CODEC_TYPE_TEXT_TEXT) );
9521 if( !sample )
9522 goto fail;
9523 sample->data[0] = (name_length >> 8) & 0xff;
9524 sample->data[1] = name_length & 0xff;
9525 memcpy( sample->data + 2, data.chapter_name, name_length );
9526 if( sample_type == QT_CODEC_TYPE_TEXT_TEXT )
9528 /* QuickTime Player requires Text Encoding Attribute Box ('encd') if media language is ISO language codes : undefined.
9529 * Also this box can avoid garbling if the QuickTime text sample is encoded by Unicode characters.
9530 * Note: 3GPP Timed Text supports only UTF-8 or UTF-16, so this box isn't needed. */
9531 static const uint8_t encd[12] =
9533 0x00, 0x00, 0x00, 0x0C, /* size: 12 */
9534 0x65, 0x6E, 0x63, 0x64, /* type: 'encd' */
9535 0x00, 0x00, 0x01, 0x00 /* Unicode Encoding */
9537 memcpy( sample->data + 2 + name_length, encd, 12 );
9539 sample->dts = sample->cts = data.start_time;
9540 sample->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
9541 sample->index = sample_entry;
9542 if( lsmash_append_sample( root, chapter_track_ID, sample ) )
9543 goto fail;
9544 free( data.chapter_name );
9545 data.chapter_name = NULL;
9547 if( lsmash_flush_pooled_samples( root, chapter_track_ID, 0 ) )
9548 goto fail;
9549 trak = isom_get_trak( root, chapter_track_ID );
9550 if( !trak )
9551 goto fail;
9552 fclose( chapter );
9553 trak->is_chapter = 1;
9554 trak->related_track_ID = track_ID;
9555 return 0;
9556 fail:
9557 if( chapter )
9558 fclose( chapter );
9559 if( data.chapter_name )
9560 free( data.chapter_name );
9561 free( chap->track_ID );
9562 chap->track_ID = NULL;
9563 /* Remove the reference chapter track attached at tail of the list. */
9564 lsmash_remove_entry_direct( root->moov->trak_list, root->moov->trak_list->tail, isom_remove_trak );
9565 return -1;
9568 int lsmash_delete_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID )
9570 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
9571 if( !trak )
9572 return -1;
9573 isom_remove_edts( trak->edts );
9574 trak->edts = NULL;
9575 return isom_update_tkhd_duration( trak );
9578 void lsmash_delete_tyrant_chapter( lsmash_root_t *root )
9580 if( !root || !root->moov || !root->moov->udta )
9581 return;
9582 isom_remove_chpl( root->moov->udta->chpl );
9583 root->moov->udta->chpl = NULL;
9586 #ifdef LSMASH_DEMUXER_ENABLED
9587 /*---- remuxer functions ----*/
9589 static int isom_copy_mean( isom_metaitem_t *dst, isom_metaitem_t *src )
9591 if( !dst )
9592 return 0;
9593 isom_remove_mean( dst->mean );
9594 if( !src || !src->mean )
9595 return 0;
9596 if( isom_add_mean( dst ) )
9597 return -1;
9598 if( src->mean->meaning_string )
9600 dst->mean->meaning_string = lsmash_memdup( src->mean->meaning_string, src->mean->meaning_string_length );
9601 if( !dst->mean->meaning_string )
9602 return -1;
9603 dst->mean->meaning_string_length = src->mean->meaning_string_length;
9605 return 0;
9608 static int isom_copy_name( isom_metaitem_t *dst, isom_metaitem_t *src )
9610 if( !dst )
9611 return 0;
9612 isom_remove_name( dst->name );
9613 if( !src || !src->name )
9614 return 0;
9615 if( isom_add_name( dst ) )
9616 return -1;
9617 if( src->name->name )
9619 dst->name->name = lsmash_memdup( src->name->name, src->name->name_length );
9620 if( !dst->name->name )
9621 return -1;
9622 dst->name->name_length = src->name->name_length;
9624 return 0;
9627 static int isom_copy_data( isom_metaitem_t *dst, isom_metaitem_t *src )
9629 if( !dst )
9630 return 0;
9631 isom_remove_data( dst->data );
9632 if( !src || !src->data )
9633 return 0;
9634 if( isom_add_data( dst ) )
9635 return -1;
9636 isom_copy_fields( dst, src, data );
9637 if( src->data->value )
9639 dst->data->value = lsmash_memdup( src->data->value, src->data->value_length );
9640 if( !dst->data->value )
9641 return -1;
9642 dst->data->value_length = src->data->value_length;
9644 return 0;
9647 static isom_metaitem_t *isom_duplicate_metaitem( isom_metaitem_t *src )
9649 isom_metaitem_t *dst = lsmash_memdup( src, sizeof(isom_metaitem_t) );
9650 if( !dst )
9651 return NULL;
9652 dst->mean = NULL;
9653 dst->name = NULL;
9654 dst->data = NULL;
9655 /* Copy children. */
9656 if( isom_copy_mean( dst, src )
9657 || isom_copy_name( dst, src )
9658 || isom_copy_data( dst, src ) )
9660 isom_remove_metaitem( dst );
9661 return NULL;
9663 return dst;
9666 lsmash_itunes_metadata_list_t *lsmash_export_itunes_metadata( lsmash_root_t *root )
9668 if( !root || !root->moov )
9669 return NULL;
9670 if( !root->moov->udta || !root->moov->udta->meta || !root->moov->udta->meta->ilst )
9672 isom_ilst_t *dst = malloc( sizeof(isom_ilst_t) );
9673 if( !dst )
9674 return NULL;
9675 memset( dst, 0, sizeof(isom_ilst_t) );
9676 return (lsmash_itunes_metadata_list_t *)dst;
9678 isom_ilst_t *src = root->moov->udta->meta->ilst;
9679 isom_ilst_t *dst = lsmash_memdup( src, sizeof(isom_ilst_t) );
9680 if( !dst )
9681 return NULL;
9682 dst->root = NULL;
9683 dst->parent = NULL;
9684 dst->item_list = NULL;
9685 if( src->item_list )
9687 dst->item_list = lsmash_create_entry_list();
9688 if( !dst->item_list )
9690 free( dst );
9691 return NULL;
9693 for( lsmash_entry_t *entry = src->item_list->head; entry; entry = entry->next )
9695 isom_metaitem_t *dst_metaitem = isom_duplicate_metaitem( (isom_metaitem_t *)entry->data );
9696 if( !dst_metaitem )
9698 isom_remove_ilst( dst );
9699 return NULL;
9701 if( lsmash_add_entry( dst->item_list, dst_metaitem ) )
9703 isom_remove_metaitem( dst_metaitem );
9704 isom_remove_ilst( dst );
9705 return NULL;
9709 return (lsmash_itunes_metadata_list_t *)dst;
9712 int lsmash_import_itunes_metadata( lsmash_root_t *root, lsmash_itunes_metadata_list_t *list )
9714 if( !root || !list )
9715 return -1;
9716 if( !root->itunes_movie )
9717 return 0;
9718 isom_ilst_t *src = (isom_ilst_t *)list;
9719 if( !src->item_list || !src->item_list->entry_count )
9720 return 0;
9721 if( (!root->moov->udta && isom_add_udta( root, 0 ))
9722 || (!root->moov->udta->meta && isom_add_meta( (isom_box_t *)root->moov->udta ))
9723 || (!root->moov->udta->meta->hdlr && isom_add_hdlr( NULL, root->moov->udta->meta, NULL, ISOM_META_HANDLER_TYPE_ITUNES_METADATA ))
9724 || (!root->moov->udta->meta->ilst && isom_add_ilst( root->moov )) )
9725 return -1;
9726 isom_ilst_t *dst = root->moov->udta->meta->ilst;
9727 for( lsmash_entry_t *entry = src->item_list->head; entry; entry = entry->next )
9729 isom_metaitem_t *dst_metaitem = isom_duplicate_metaitem( (isom_metaitem_t *)entry->data );
9730 if( !dst_metaitem )
9731 return -1;
9732 if( lsmash_add_entry( dst->item_list, dst_metaitem ) )
9734 isom_remove_metaitem( dst_metaitem );
9735 return -1;
9738 return 0;
9741 void lsmash_destroy_itunes_metadata( lsmash_itunes_metadata_list_t *list )
9743 isom_remove_ilst( (isom_ilst_t *)list );
9745 #endif /* LSMASH_DEMUXER_ENABLED */