Fix caching the last accessed entry number when removing a entry.
[L-SMASH.git] / isom.c
blobd93221732b80afbc3d19c441693387fcfad0d0f1
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_MEHD,
81 ISOM_BOX_TYPE_TREX,
82 ISOM_BOX_TYPE_MFHD,
83 ISOM_BOX_TYPE_TFHD,
84 ISOM_BOX_TYPE_TRUN,
85 ISOM_BOX_TYPE_TFRA,
86 ISOM_BOX_TYPE_MFRO
88 for( int i = 0; i < sizeof(fullbox_table)/sizeof(uint32_t); i++ )
89 if( type == fullbox_table[i] )
90 return 1;
91 return 0;
94 /* Return 1 if the sample type is LPCM audio, Otherwise return 0. */
95 int isom_is_lpcm_audio( uint32_t type )
97 return type == QT_CODEC_TYPE_23NI_AUDIO
98 || type == QT_CODEC_TYPE_NONE_AUDIO
99 || type == QT_CODEC_TYPE_LPCM_AUDIO
100 || type == QT_CODEC_TYPE_RAW_AUDIO
101 || type == QT_CODEC_TYPE_SOWT_AUDIO
102 || type == QT_CODEC_TYPE_TWOS_AUDIO
103 || type == QT_CODEC_TYPE_FL32_AUDIO
104 || type == QT_CODEC_TYPE_FL64_AUDIO
105 || type == QT_CODEC_TYPE_IN24_AUDIO
106 || type == QT_CODEC_TYPE_IN32_AUDIO
107 || type == QT_CODEC_TYPE_NOT_SPECIFIED;
110 char *isom_4cc2str( uint32_t fourcc )
112 static char str[5];
113 str[0] = (fourcc >> 24) & 0xff;
114 str[1] = (fourcc >> 16) & 0xff;
115 str[2] = (fourcc >> 8) & 0xff;
116 str[3] = fourcc & 0xff;
117 str[4] = 0;
118 return str;
121 static inline void isom_init_basebox_common( isom_box_t *box, isom_box_t *parent, uint32_t type )
123 box->root = parent->root;
124 box->parent = parent;
125 box->size = 0;
126 box->type = type;
127 box->usertype = NULL;
130 static inline void isom_init_fullbox_common( isom_box_t *box, isom_box_t *parent, uint32_t type )
132 box->root = parent->root;
133 box->parent = parent;
134 box->size = 0;
135 box->type = type;
136 box->usertype = NULL;
137 box->version = 0;
138 box->flags = 0;
141 void isom_init_box_common( void *box, void *parent, uint32_t type )
143 assert( parent && ((isom_box_t *)parent)->root );
144 if( ((isom_box_t *)parent)->type == ISOM_BOX_TYPE_STSD )
146 isom_init_basebox_common( (isom_box_t *)box, (isom_box_t *)parent, type );
147 return;
149 if( isom_is_fullbox( box ) )
150 isom_init_fullbox_common( (isom_box_t *)box, (isom_box_t *)parent, type );
151 else
152 isom_init_basebox_common( (isom_box_t *)box, (isom_box_t *)parent, type );
155 static void isom_bs_put_basebox_common( lsmash_bs_t *bs, isom_box_t *box )
157 if( box->size > UINT32_MAX )
159 lsmash_bs_put_be32( bs, 1 );
160 lsmash_bs_put_be32( bs, box->type );
161 lsmash_bs_put_be64( bs, box->size ); /* largesize */
163 else
165 lsmash_bs_put_be32( bs, (uint32_t)box->size );
166 lsmash_bs_put_be32( bs, box->type );
168 if( box->type == ISOM_BOX_TYPE_UUID )
169 lsmash_bs_put_bytes( bs, box->usertype, 16 );
172 static void isom_bs_put_fullbox_common( lsmash_bs_t *bs, isom_box_t *box )
174 isom_bs_put_basebox_common( bs, box );
175 lsmash_bs_put_byte( bs, box->version );
176 lsmash_bs_put_be24( bs, box->flags );
179 static void isom_bs_put_box_common( lsmash_bs_t *bs, void *box )
181 if( !box )
183 bs->error = 1;
184 return;
186 isom_box_t *parent = ((isom_box_t *)box)->parent;
187 if( parent && parent->type == ISOM_BOX_TYPE_STSD )
189 isom_bs_put_basebox_common( bs, (isom_box_t *)box );
190 return;
192 if( isom_is_fullbox( box ) )
193 isom_bs_put_fullbox_common( bs, (isom_box_t *)box );
194 else
195 isom_bs_put_basebox_common( bs, (isom_box_t *)box );
198 isom_trak_entry_t *isom_get_trak( lsmash_root_t *root, uint32_t track_ID )
200 if( !track_ID || !root || !root->moov || !root->moov->trak_list )
201 return NULL;
202 for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
204 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
205 if( !trak || !trak->tkhd )
206 return NULL;
207 if( trak->tkhd->track_ID == track_ID )
208 return trak;
210 return NULL;
213 static isom_trex_entry_t *isom_get_trex( isom_mvex_t *mvex, uint32_t track_ID )
215 if( !track_ID || !mvex || !mvex->trex_list )
216 return NULL;
217 for( lsmash_entry_t *entry = mvex->trex_list->head; entry; entry = entry->next )
219 isom_trex_entry_t *trex = (isom_trex_entry_t *)entry->data;
220 if( !trex )
221 return NULL;
222 if( trex->track_ID == track_ID )
223 return trex;
225 return NULL;
228 static isom_traf_entry_t *isom_get_traf( isom_moof_entry_t *moof, uint32_t track_ID )
230 if( !track_ID || !moof || !moof->traf_list )
231 return NULL;
232 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
234 isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
235 if( !traf || !traf->tfhd )
236 return NULL;
237 if( traf->tfhd->track_ID == track_ID )
238 return traf;
240 return NULL;
243 static isom_tfra_entry_t *isom_get_tfra( isom_mfra_t *mfra, uint32_t track_ID )
245 if( !track_ID || !mfra || !mfra->tfra_list )
246 return NULL;
247 for( lsmash_entry_t *entry = mfra->tfra_list->head; entry; entry = entry->next )
249 isom_tfra_entry_t *tfra = (isom_tfra_entry_t *)entry->data;
250 if( !tfra )
251 return NULL;
252 if( tfra->track_ID == track_ID )
253 return tfra;
255 return NULL;
258 static int isom_add_elst_entry( isom_elst_t *elst, uint64_t segment_duration, int64_t media_time, int32_t media_rate )
260 isom_elst_entry_t *data = malloc( sizeof(isom_elst_entry_t) );
261 if( !data )
262 return -1;
263 data->segment_duration = segment_duration;
264 data->media_time = media_time;
265 data->media_rate = media_rate;
266 if( lsmash_add_entry( elst->list, data ) )
268 free( data );
269 return -1;
271 if( data->segment_duration > UINT32_MAX || data->media_time > INT32_MAX || data->media_time < INT32_MIN )
272 elst->version = 1;
273 return 0;
276 static isom_tref_type_t *isom_add_track_reference_type( isom_tref_t *tref, lsmash_track_reference_type_code type, uint32_t ref_count, uint32_t *track_ID )
278 if( !tref || !tref->ref_list )
279 return NULL;
280 isom_tref_type_t *ref = malloc( sizeof(isom_tref_type_t) );
281 if( !ref )
282 return NULL;
283 isom_init_box_common( ref, tref, type );
284 ref->ref_count = ref_count;
285 ref->track_ID = track_ID;
286 if( lsmash_add_entry( tref->ref_list, ref ) )
288 free( ref );
289 return NULL;
291 return ref;
294 static int isom_add_dref_entry( isom_dref_t *dref, uint32_t flags, char *name, char *location )
296 if( !dref || !dref->list )
297 return -1;
298 isom_dref_entry_t *data = malloc( sizeof(isom_dref_entry_t) );
299 if( !data )
300 return -1;
301 memset( data, 0, sizeof(isom_dref_entry_t) );
302 isom_init_box_common( data, dref, name ? ISOM_BOX_TYPE_URN : ISOM_BOX_TYPE_URL );
303 data->flags = flags;
304 if( location )
306 data->location_length = strlen( location ) + 1;
307 data->location = malloc( data->location_length );
308 if( !data->location )
310 free( data );
311 return -1;
313 memcpy( data->location, location, data->location_length );
315 if( name )
317 data->name_length = strlen( name ) + 1;
318 data->name = malloc( data->name_length );
319 if( !data->name )
321 if( data->location )
322 free( data->location );
323 free( data );
324 return -1;
326 memcpy( data->name, name, data->name_length );
328 if( lsmash_add_entry( dref->list, data ) )
330 if( data->location )
331 free( data->location );
332 if( data->name )
333 free( data->name );
334 free( data );
335 return -1;
337 return 0;
340 isom_avcC_ps_entry_t *isom_create_ps_entry( uint8_t *ps, uint32_t ps_size )
342 isom_avcC_ps_entry_t *entry = malloc( sizeof(isom_avcC_ps_entry_t) );
343 if( !entry )
344 return NULL;
345 entry->parameterSetLength = ps_size;
346 entry->parameterSetNALUnit = malloc( ps_size );
347 if( !entry->parameterSetNALUnit )
349 free( entry );
350 return NULL;
352 memcpy( entry->parameterSetNALUnit, ps, ps_size );
353 return entry;
356 void isom_remove_avcC_ps( isom_avcC_ps_entry_t *ps )
358 if( !ps )
359 return;
360 if( ps->parameterSetNALUnit )
361 free( ps->parameterSetNALUnit );
362 free( ps );
365 int lsmash_add_sps_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint8_t *sps, uint32_t sps_size )
367 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
368 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
369 return -1;
370 isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number );
371 if( !data || !data->avcC )
372 return -1;
373 isom_avcC_t *avcC = (isom_avcC_t *)data->avcC;
374 isom_avcC_ps_entry_t *ps = isom_create_ps_entry( sps, sps_size );
375 if( !ps )
376 return -1;
377 if( lsmash_add_entry( avcC->sequenceParameterSets, ps ) )
379 isom_remove_avcC_ps( ps );
380 return -1;
382 avcC->numOfSequenceParameterSets = avcC->sequenceParameterSets->entry_count;
383 return 0;
386 int lsmash_add_pps_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint8_t *pps, uint32_t pps_size )
388 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
389 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
390 return -1;
391 isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number );
392 if( !data || !data->avcC )
393 return -1;
394 isom_avcC_t *avcC = (isom_avcC_t *)data->avcC;
395 isom_avcC_ps_entry_t *ps = isom_create_ps_entry( pps, pps_size );
396 if( !ps )
397 return -1;
398 if( lsmash_add_entry( avcC->pictureParameterSets, ps ) )
400 isom_remove_avcC_ps( ps );
401 return -1;
403 avcC->numOfPictureParameterSets = avcC->pictureParameterSets->entry_count;
404 return 0;
407 int lsmash_add_spsext_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number, uint8_t *spsext, uint32_t spsext_size )
409 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
410 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
411 return -1;
412 isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number );
413 if( !data || !data->avcC )
414 return -1;
415 isom_avcC_t *avcC = (isom_avcC_t *)data->avcC;
416 isom_avcC_ps_entry_t *ps = isom_create_ps_entry( spsext, spsext_size );
417 if( !ps )
418 return -1;
419 if( lsmash_add_entry( avcC->sequenceParameterSetExt, ps ) )
421 isom_remove_avcC_ps( ps );
422 return -1;
424 avcC->numOfSequenceParameterSetExt = avcC->sequenceParameterSetExt->entry_count;
425 return 0;
428 int isom_add_avcC( isom_visual_entry_t *visual )
430 if( !visual )
431 return -1;
432 isom_create_box( avcC, visual, ISOM_BOX_TYPE_AVCC );
433 avcC->sequenceParameterSets = lsmash_create_entry_list();
434 if( !avcC->sequenceParameterSets )
436 free( avcC );
437 return -1;
439 avcC->pictureParameterSets = lsmash_create_entry_list();
440 if( !avcC->pictureParameterSets )
442 isom_remove_avcC( avcC );
443 return -1;
445 avcC->sequenceParameterSetExt = lsmash_create_entry_list();
446 if( !avcC->sequenceParameterSetExt )
448 isom_remove_avcC( avcC );
449 return -1;
451 visual->avcC = avcC;
452 return 0;
455 int isom_add_clap( isom_visual_entry_t *visual )
457 if( !visual || visual->clap )
458 return -1;
459 isom_create_box( clap, visual, ISOM_BOX_TYPE_CLAP );
460 clap->cleanApertureWidthN = 1;
461 clap->cleanApertureWidthD = 1;
462 clap->cleanApertureHeightN = 1;
463 clap->cleanApertureHeightD = 1;
464 clap->horizOffN = 0;
465 clap->horizOffD = 1;
466 clap->vertOffN = 0;
467 clap->vertOffD = 1;
468 visual->clap = clap;
469 return 0;
472 int isom_add_pasp( isom_visual_entry_t *visual )
474 if( !visual || visual->pasp )
475 return -1;
476 isom_create_box( pasp, visual, ISOM_BOX_TYPE_PASP );
477 pasp->hSpacing = 1;
478 pasp->vSpacing = 1;
479 visual->pasp = pasp;
480 return 0;
483 int isom_add_colr( isom_visual_entry_t *visual )
485 if( !visual || visual->colr )
486 return -1;
487 isom_create_box( colr, visual, QT_BOX_TYPE_COLR );
488 isom_color_parameter_t *param = (isom_color_parameter_t *)(&isom_color_parameter_tbl[0]);
489 colr->color_parameter_type = QT_COLOR_PARAMETER_TYPE_NCLC;
490 colr->primaries_index = param->primaries;
491 colr->transfer_function_index = param->transfer;
492 colr->matrix_index = param->matrix;
493 visual->colr = colr;
494 return 0;
497 int isom_add_stsl( isom_visual_entry_t *visual )
499 if( !visual || visual->stsl )
500 return -1;
501 isom_create_box( stsl, visual, ISOM_BOX_TYPE_STSL );
502 stsl->scale_method = ISOM_SCALING_METHOD_HIDDEN;
503 visual->stsl = stsl;
504 return 0;
507 static void isom_remove_esds( isom_esds_t *esds );
508 static void isom_remove_visual_extensions( isom_visual_entry_t *visual );
510 static int isom_add_visual_extensions( isom_visual_entry_t *visual, lsmash_video_summary_t *summary )
512 /* Check if set up Track Aperture Modes. */
513 isom_trak_entry_t *trak = (isom_trak_entry_t *)visual->parent->parent->parent->parent->parent;
514 int qt_compatible = trak->root->qt_compatible;
515 isom_tapt_t *tapt = trak->tapt;
516 int set_aperture_modes = qt_compatible /* Track Aperture Modes is only available under QuickTime file format. */
517 && !summary->scaling_method /* Sample scaling method might conflict with this feature. */
518 && tapt && tapt->clef && tapt->prof && tapt->enof /* Check if required boxes exist. */
519 && !((isom_stsd_t *)visual->parent)->list->entry_count; /* Multiple sample description might conflict with this, so in that case, disable this feature.
520 * Note: this sample description isn't added yet here. */
521 if( !set_aperture_modes )
522 isom_remove_tapt( trak->tapt );
523 /* Set up Clean Aperture. */
524 if( set_aperture_modes || summary->crop_top || summary->crop_left || summary->crop_bottom || summary->crop_right )
526 if( isom_add_clap( visual ) )
528 isom_remove_visual_extensions( visual );
529 return -1;
531 isom_clap_t *clap = visual->clap;
532 clap->cleanApertureWidthN = summary->width - (summary->crop_left + summary->crop_right);
533 clap->cleanApertureHeightN = summary->height - (summary->crop_top + summary->crop_bottom);
534 clap->horizOffN = (int64_t)summary->crop_left - summary->crop_right;
535 clap->vertOffN = (int64_t)summary->crop_top - summary->crop_bottom;
536 if( !(clap->horizOffN & 0x1) )
538 clap->horizOffN /= 2;
539 clap->horizOffD = 1;
541 else
542 clap->horizOffD = 2;
543 if( !(clap->vertOffN & 0x1) )
545 clap->vertOffN /= 2;
546 clap->vertOffD = 1;
548 else
549 clap->vertOffD = 2;
551 /* Set up Pixel Aspect Ratio. */
552 if( set_aperture_modes || (summary->par_h && summary->par_v) )
554 if( isom_add_pasp( visual ) )
556 isom_remove_visual_extensions( visual );
557 return -1;
559 isom_pasp_t *pasp = visual->pasp;
560 pasp->hSpacing = summary->par_h;
561 pasp->vSpacing = summary->par_v;
563 /* Set up Color Parameter. */
564 if( qt_compatible && (summary->primaries || summary->transfer || summary->matrix) )
566 if( isom_add_colr( visual ) )
568 isom_remove_visual_extensions( visual );
569 return -1;
571 isom_colr_t *colr = visual->colr;
572 uint16_t primaries = summary->primaries;
573 uint16_t transfer = summary->transfer;
574 uint16_t matrix = summary->matrix;
575 /* Set 'nclc' to parameter type, we don't support 'prof'. */
576 colr->color_parameter_type = QT_COLOR_PARAMETER_TYPE_NCLC;
577 /* primaries */
578 if( primaries >= QT_COLOR_PARAMETER_END )
579 return -1;
580 else if( primaries > UINT16_MAX )
581 colr->primaries_index = isom_color_parameter_tbl[primaries - UINT16_MAX_PLUS_ONE].primaries;
582 else
583 colr->primaries_index = (primaries == 1 || primaries == 5 || primaries == 6) ? primaries : 2;
584 /* transfer */
585 if( transfer >= QT_COLOR_PARAMETER_END )
586 return -1;
587 else if( transfer > UINT16_MAX )
588 colr->transfer_function_index = isom_color_parameter_tbl[transfer - UINT16_MAX_PLUS_ONE].transfer;
589 else
590 colr->transfer_function_index = (transfer == 1 || transfer == 7) ? transfer : 2;
591 /* matrix */
592 if( matrix >= QT_COLOR_PARAMETER_END )
593 return -1;
594 else if( matrix > UINT16_MAX )
595 colr->matrix_index = isom_color_parameter_tbl[matrix - UINT16_MAX_PLUS_ONE].matrix;
596 else
597 colr->matrix_index = (matrix == 1 || matrix == 6 || matrix == 7 ) ? matrix : 2;
599 /* Set up Sample Scaling. */
600 if( !qt_compatible && summary->scaling_method )
602 if( isom_add_stsl( visual ) )
604 isom_remove_visual_extensions( visual );
605 return -1;
607 isom_stsl_t *stsl = visual->stsl;
608 stsl->constraint_flag = 1;
609 stsl->scale_method = summary->scaling_method;
611 /* Set up AVC Decoder Configuration. */
612 static const uint32_t avc_type[] =
614 ISOM_CODEC_TYPE_AVC1_VIDEO,
615 ISOM_CODEC_TYPE_AVC2_VIDEO,
616 ISOM_CODEC_TYPE_AVCP_VIDEO
618 for( int i = 0; i < sizeof(avc_type)/sizeof(avc_type[0]); i++ )
619 if( visual->type == avc_type[i] )
621 if( isom_add_avcC( visual ) )
622 return -1;
623 break;
625 /* Set up Track Apeture Modes. */
626 if( set_aperture_modes )
628 uint32_t width = visual->width << 16;
629 uint32_t height = visual->height << 16;
630 double clap_width = ((double)visual->clap->cleanApertureWidthN / visual->clap->cleanApertureWidthD) * (1<<16);
631 double clap_height = ((double)visual->clap->cleanApertureHeightN / visual->clap->cleanApertureHeightD) * (1<<16);
632 double par = (double)visual->pasp->hSpacing / visual->pasp->vSpacing;
633 if( par >= 1.0 )
635 tapt->clef->width = clap_width * par;
636 tapt->clef->height = clap_height;
637 tapt->prof->width = width * par;
638 tapt->prof->height = height;
640 else
642 tapt->clef->width = clap_width;
643 tapt->clef->height = clap_height / par;
644 tapt->prof->width = width;
645 tapt->prof->height = height / par;
647 tapt->enof->width = width;
648 tapt->enof->height = height;
650 return 0;
653 static int isom_add_visual_entry( isom_stsd_t *stsd, uint32_t sample_type, lsmash_video_summary_t *summary )
655 if( !stsd || !stsd->list || !summary )
656 return -1;
657 lsmash_entry_list_t *list = stsd->list;
658 isom_visual_entry_t *visual = malloc( sizeof(isom_visual_entry_t) );
659 if( !visual )
660 return -1;
661 memset( visual, 0, sizeof(isom_visual_entry_t) );
662 isom_init_box_common( visual, stsd, sample_type );
663 visual->data_reference_index = 1;
664 visual->width = (uint16_t)summary->width;
665 visual->height = (uint16_t)summary->height;
666 visual->horizresolution = visual->vertresolution = 0x00480000;
667 visual->frame_count = 1;
668 switch( sample_type )
670 case ISOM_CODEC_TYPE_AVC1_VIDEO :
671 case ISOM_CODEC_TYPE_AVC2_VIDEO :
672 strcpy( visual->compressorname, "\012AVC Coding" );
673 break;
674 case ISOM_CODEC_TYPE_AVCP_VIDEO :
675 strcpy( visual->compressorname, "\016AVC Parameters" );
676 break;
677 default :
678 break;
680 visual->depth = 0x0018;
681 visual->color_table_ID = -1;
682 if( isom_add_visual_extensions( visual, summary )
683 || lsmash_add_entry( list, visual ) )
685 isom_remove_visual_extensions( visual );
686 free( visual );
687 return -1;
689 return 0;
692 #if 0
693 static int isom_add_mp4s_entry( isom_stsd_t *stsd )
695 if( !stsd || !stsd->list )
696 return -1;
697 isom_mp4s_entry_t *mp4s = malloc( sizeof(isom_mp4s_entry_t) );
698 if( !mp4s )
699 return -1;
700 memset( mp4s, 0, sizeof(isom_mp4s_entry_t) );
701 isom_init_box_common( mp4s, stsd, ISOM_CODEC_TYPE_MP4S_SYSTEM );
702 mp4s->data_reference_index = 1;
703 if( lsmash_add_entry( stsd->list, mp4s ) )
705 free( mp4s );
706 return -1;
708 return 0;
710 #endif
712 int isom_add_wave( isom_audio_entry_t *audio )
714 if( !audio || audio->wave )
715 return -1;
716 isom_create_box( wave, audio, QT_BOX_TYPE_WAVE );
717 audio->wave = wave;
718 return 0;
721 int isom_add_frma( isom_wave_t *wave )
723 if( !wave || wave->frma )
724 return -1;
725 isom_create_box( frma, wave, QT_BOX_TYPE_FRMA );
726 wave->frma = frma;
727 return 0;
730 int isom_add_enda( isom_wave_t *wave )
732 if( !wave || wave->enda )
733 return -1;
734 isom_create_box( enda, wave, QT_BOX_TYPE_ENDA );
735 wave->enda = enda;
736 return 0;
739 int isom_add_mp4a( isom_wave_t *wave )
741 if( !wave || wave->mp4a )
742 return -1;
743 isom_create_box( mp4a, wave, QT_BOX_TYPE_MP4A );
744 wave->mp4a = mp4a;
745 return 0;
748 int isom_add_terminator( isom_wave_t *wave )
750 if( !wave || wave->terminator )
751 return -1;
752 isom_create_box( terminator, wave, QT_BOX_TYPE_TERMINATOR );
753 wave->terminator = terminator;
754 return 0;
757 int isom_add_chan( isom_audio_entry_t *audio )
759 if( !audio || audio->chan )
760 return -1;
761 isom_create_box( chan, audio, QT_BOX_TYPE_CHAN );
762 chan->channelLayoutTag = QT_CHANNEL_LAYOUT_UNKNOWN;
763 audio->chan = chan;
764 return 0;
767 static int isom_set_qtff_mp4a_description( isom_audio_entry_t *audio )
769 lsmash_audio_summary_t *summary = &audio->summary;
770 if( isom_add_wave( audio )
771 || isom_add_frma( audio->wave )
772 || isom_add_mp4a( audio->wave )
773 || isom_add_terminator( audio->wave ) )
774 return -1;
775 audio->data_reference_index = 1;
776 audio->version = (summary->channels > 2 || summary->frequency > UINT16_MAX) ? 2 : 1;
777 audio->channelcount = audio->version == 2 ? 3 : LSMASH_MIN( summary->channels, 2 );
778 audio->samplesize = 16;
779 audio->compression_ID = QT_COMPRESSION_ID_VARIABLE_COMPRESSION;
780 audio->packet_size = 0;
781 if( audio->version == 1 )
783 audio->samplerate = summary->frequency << 16;
784 audio->samplesPerPacket = summary->samples_in_frame;
785 audio->bytesPerPacket = 1; /* Apparently, this field is set to 1. */
786 audio->bytesPerFrame = audio->bytesPerPacket * summary->channels;
787 audio->bytesPerSample = 1 + (summary->bit_depth != 8);
789 else /* audio->version == 2 */
791 audio->samplerate = 0x00010000;
792 audio->sizeOfStructOnly = 72;
793 audio->audioSampleRate = (union {double d; uint64_t i;}){summary->frequency}.i;
794 audio->numAudioChannels = summary->channels;
795 audio->always7F000000 = 0x7F000000;
796 audio->constBitsPerChannel = 0; /* compressed audio */
797 audio->formatSpecificFlags = 0;
798 audio->constBytesPerAudioPacket = 0; /* variable */
799 audio->constLPCMFramesPerAudioPacket = summary->samples_in_frame;
801 audio->wave->frma->data_format = audio->type;
802 /* create ES Descriptor */
803 isom_esds_t *esds = malloc( sizeof(isom_esds_t) );
804 if( !esds )
805 return -1;
806 memset( esds, 0, sizeof(isom_esds_t) );
807 isom_init_box_common( esds, audio->wave, ISOM_BOX_TYPE_ESDS );
808 mp4sys_ES_Descriptor_params_t esd_param;
809 memset( &esd_param, 0, sizeof(mp4sys_ES_Descriptor_params_t) );
810 esd_param.objectTypeIndication = summary->object_type_indication;
811 esd_param.streamType = summary->stream_type;
812 esd_param.dsi_payload = summary->exdata;
813 esd_param.dsi_payload_length = summary->exdata_length;
814 esds->ES = mp4sys_setup_ES_Descriptor( &esd_param );
815 if( !esds->ES )
816 return -1;
817 audio->wave->esds = esds;
818 return 0;
821 static int isom_set_isom_mp4a_description( isom_audio_entry_t *audio )
823 lsmash_audio_summary_t *summary = &audio->summary;
824 if( summary->stream_type != MP4SYS_STREAM_TYPE_AudioStream )
825 return -1;
826 switch( summary->object_type_indication )
828 case MP4SYS_OBJECT_TYPE_Audio_ISO_14496_3:
829 case MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_Main_Profile:
830 case MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_LC_Profile:
831 case MP4SYS_OBJECT_TYPE_Audio_ISO_13818_7_SSR_Profile:
832 case MP4SYS_OBJECT_TYPE_Audio_ISO_13818_3: /* Legacy Interface */
833 case MP4SYS_OBJECT_TYPE_Audio_ISO_11172_3: /* Legacy Interface */
834 break;
835 default:
836 return -1;
838 isom_create_box( esds, audio, ISOM_BOX_TYPE_ESDS );
839 mp4sys_ES_Descriptor_params_t esd_param;
840 esd_param.ES_ID = 0; /* This is esds internal, so 0 is allowed. */
841 esd_param.objectTypeIndication = summary->object_type_indication;
842 esd_param.streamType = summary->stream_type;
843 esd_param.bufferSizeDB = 0; /* NOTE: ISO/IEC 14496-3 does not mention this, so we use 0. */
844 esd_param.maxBitrate = 0; /* This will be updated later if needed. or... I think this can be arbitrary value. */
845 esd_param.avgBitrate = 0; /* FIXME: 0 if VBR. */
846 esd_param.dsi_payload = summary->exdata;
847 esd_param.dsi_payload_length = summary->exdata_length;
848 esds->ES = mp4sys_setup_ES_Descriptor( &esd_param );
849 if( !esds->ES )
850 return -1;
851 audio->data_reference_index = 1;
852 /* WARNING: This field cannot retain frequency above 65535Hz.
853 This is not "FIXME", I just honestly implemented what the spec says.
854 BTW, who ever expects sampling frequency takes fixed-point decimal??? */
855 audio->samplerate = summary->frequency <= UINT16_MAX ? summary->frequency << 16 : 0;
856 /* In pure mp4 file, these "template" fields shall be default values according to the spec.
857 But not pure - hybrid with other spec - mp4 file can take other values.
858 Which is to say, these template values shall be ignored in terms of mp4, except some object_type_indications.
859 see 14496-14, "Template fields used". */
860 audio->channelcount = 2;
861 audio->samplesize = 16;
862 audio->esds = esds;
863 return 0;
866 static int isom_set_qtff_lpcm_description( isom_audio_entry_t *audio )
868 uint32_t sample_type = audio->type;
869 lsmash_audio_summary_t *summary = &audio->summary;
870 /* Convert the sample type into 'lpcm' if the description doesn't match the format or version = 2 fields are needed. */
871 if( (sample_type == QT_CODEC_TYPE_RAW_AUDIO && (summary->bit_depth != 8 || summary->sample_format))
872 || (sample_type == QT_CODEC_TYPE_FL32_AUDIO && (summary->bit_depth != 32 || !summary->sample_format))
873 || (sample_type == QT_CODEC_TYPE_FL64_AUDIO && (summary->bit_depth != 64 || !summary->sample_format))
874 || (sample_type == QT_CODEC_TYPE_IN24_AUDIO && (summary->bit_depth != 24 || summary->sample_format))
875 || (sample_type == QT_CODEC_TYPE_IN32_AUDIO && (summary->bit_depth != 32 || summary->sample_format))
876 || (sample_type == QT_CODEC_TYPE_23NI_AUDIO && (summary->bit_depth != 32 || summary->sample_format || !summary->endianness))
877 || (sample_type == QT_CODEC_TYPE_SOWT_AUDIO && (summary->bit_depth != 16 || summary->sample_format || !summary->endianness))
878 || (sample_type == QT_CODEC_TYPE_TWOS_AUDIO && ((summary->bit_depth != 16 && summary->bit_depth != 8) || summary->sample_format || summary->endianness))
879 || (sample_type == QT_CODEC_TYPE_NONE_AUDIO && ((summary->bit_depth != 16 && summary->bit_depth != 8) || summary->sample_format || summary->endianness))
880 || (sample_type == QT_CODEC_TYPE_NOT_SPECIFIED && ((summary->bit_depth != 16 && summary->bit_depth != 8) || summary->sample_format || summary->endianness))
881 || (summary->channels > 2 || summary->frequency > UINT16_MAX || summary->bit_depth % 8) )
883 audio->type = QT_CODEC_TYPE_LPCM_AUDIO;
884 audio->version = 2;
886 else if( sample_type == QT_CODEC_TYPE_LPCM_AUDIO )
887 audio->version = 2;
888 else if( summary->bit_depth > 16
889 || (sample_type != QT_CODEC_TYPE_RAW_AUDIO && sample_type != QT_CODEC_TYPE_TWOS_AUDIO
890 && sample_type != QT_CODEC_TYPE_NONE_AUDIO && sample_type != QT_CODEC_TYPE_NOT_SPECIFIED) )
891 audio->version = 1;
892 audio->data_reference_index = 1;
893 /* Set up constBytesPerAudioPacket field.
894 * We use constBytesPerAudioPacket as the actual size of audio frame even when version is not 2. */
895 audio->constBytesPerAudioPacket = (summary->bit_depth * summary->channels) / 8;
896 /* Set up other fields in this description by its version. */
897 if( audio->version == 2 )
899 audio->channelcount = 3;
900 audio->samplesize = 16;
901 audio->compression_ID = -2;
902 audio->samplerate = 0x00010000;
903 audio->sizeOfStructOnly = 72;
904 audio->audioSampleRate = (union {double d; uint64_t i;}){summary->frequency}.i;
905 audio->numAudioChannels = summary->channels;
906 audio->always7F000000 = 0x7F000000;
907 audio->constBitsPerChannel = summary->bit_depth;
908 audio->constLPCMFramesPerAudioPacket = 1;
909 if( summary->sample_format )
910 audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_FLOAT;
911 if( sample_type == QT_CODEC_TYPE_TWOS_AUDIO || !summary->endianness )
912 audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_BIG_ENDIAN;
913 if( !summary->sample_format && summary->signedness )
914 audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_SIGNED_INTEGER;
915 if( summary->packed )
916 audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_PACKED;
917 if( !summary->packed && summary->alignment )
918 audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_ALIGNED_HIGH;
919 if( !summary->interleaved )
920 audio->formatSpecificFlags |= QT_LPCM_FORMAT_FLAG_NON_INTERLEAVED;
922 else if( audio->version == 1 )
924 audio->channelcount = summary->channels;
925 audio->samplesize = 16;
926 /* Audio formats other than 'raw ' and 'twos' are treated as compressed audio. */
927 if( sample_type == QT_CODEC_TYPE_RAW_AUDIO || sample_type == QT_CODEC_TYPE_TWOS_AUDIO )
928 audio->compression_ID = QT_COMPRESSION_ID_NOT_COMPRESSED;
929 else
930 audio->compression_ID = QT_COMPRESSION_ID_FIXED_COMPRESSION;
931 audio->samplerate = summary->frequency << 16;
932 audio->samplesPerPacket = 1;
933 audio->bytesPerPacket = summary->bit_depth / 8;
934 audio->bytesPerFrame = audio->bytesPerPacket * summary->channels; /* sample_size field in stsz box is NOT used. */
935 audio->bytesPerSample = 1 + (summary->bit_depth != 8);
936 if( sample_type == QT_CODEC_TYPE_FL32_AUDIO || sample_type == QT_CODEC_TYPE_FL64_AUDIO
937 || sample_type == QT_CODEC_TYPE_IN24_AUDIO || sample_type == QT_CODEC_TYPE_IN32_AUDIO )
939 if( isom_add_wave( audio )
940 || isom_add_frma( audio->wave )
941 || isom_add_enda( audio->wave )
942 || isom_add_terminator( audio->wave ) )
943 return -1;
944 audio->wave->frma->data_format = sample_type;
945 audio->wave->enda->littleEndian = summary->endianness;
948 else /* audio->version == 0 */
950 audio->channelcount = summary->channels;
951 audio->samplesize = summary->bit_depth;
952 audio->compression_ID = QT_COMPRESSION_ID_NOT_COMPRESSED;
953 audio->samplerate = summary->frequency << 16;
955 return 0;
958 static int isom_set_extra_description( isom_audio_entry_t *audio )
960 lsmash_audio_summary_t *summary = &audio->summary;
961 audio->data_reference_index = 1;
962 audio->samplerate = summary->frequency <= UINT16_MAX ? summary->frequency << 16 : 0;
963 audio->channelcount = 2;
964 audio->samplesize = 16;
965 if( summary->exdata )
967 audio->exdata_length = summary->exdata_length;
968 audio->exdata = malloc( audio->exdata_length );
969 if( !audio->exdata )
970 return -1;
971 memcpy( audio->exdata, summary->exdata, audio->exdata_length );
973 else
974 audio->exdata = NULL;
975 return 0;
978 static int isom_add_audio_entry( isom_stsd_t *stsd, uint32_t sample_type, lsmash_audio_summary_t *summary )
980 if( !stsd || !stsd->list || !summary )
981 return -1;
982 isom_audio_entry_t *audio = malloc( sizeof(isom_audio_entry_t) );
983 if( !audio )
984 return -1;
985 memset( audio, 0, sizeof(isom_audio_entry_t) );
986 isom_init_box_common( audio, stsd, sample_type );
987 memcpy( &audio->summary, summary, sizeof(lsmash_audio_summary_t) );
988 int ret = 0;
989 lsmash_root_t *root = stsd->root;
990 if( sample_type == ISOM_CODEC_TYPE_MP4A_AUDIO )
992 if( root->ftyp && root->ftyp->major_brand == ISOM_BRAND_TYPE_QT )
993 ret = isom_set_qtff_mp4a_description( audio );
994 else
995 ret = isom_set_isom_mp4a_description( audio );
997 else if( isom_is_lpcm_audio( sample_type ) )
998 ret = isom_set_qtff_lpcm_description( audio );
999 else
1000 ret = isom_set_extra_description( audio );
1001 if( ret )
1002 goto fail;
1003 if( root->qt_compatible )
1005 lsmash_channel_layout_tag_code layout_tag = summary->layout_tag;
1006 lsmash_channel_bitmap_code bitmap = summary->bitmap;
1007 if( layout_tag == QT_CHANNEL_LAYOUT_USE_CHANNEL_DESCRIPTIONS /* We don't support the feature of Channel Descriptions. */
1008 || (layout_tag == QT_CHANNEL_LAYOUT_USE_CHANNEL_BITMAP && (!bitmap || bitmap > QT_CHANNEL_BIT_FULL)) )
1010 layout_tag = summary->layout_tag = QT_CHANNEL_LAYOUT_UNKNOWN | summary->channels;
1011 bitmap = summary->bitmap = 0;
1013 /* Don't create Channel Compositor Box if the channel layout is unknown. */
1014 if( (layout_tag ^ QT_CHANNEL_LAYOUT_UNKNOWN) >> 16 )
1016 if( isom_add_chan( audio ) )
1017 goto fail;
1018 audio->chan->channelLayoutTag = layout_tag;
1019 audio->chan->channelBitmap = bitmap;
1022 if( lsmash_add_entry( stsd->list, audio ) )
1023 goto fail;
1024 return 0;
1025 fail:
1026 isom_remove_esds( audio->esds );
1027 isom_remove_wave( audio->wave );
1028 isom_remove_chan( audio->chan );
1029 if( audio->exdata )
1030 free( audio->exdata );
1031 free( audio );
1032 return -1;
1035 static int isom_add_text_entry( isom_stsd_t *stsd )
1037 if( !stsd || !stsd->list )
1038 return -1;
1039 isom_text_entry_t *text = malloc( sizeof(isom_text_entry_t) );
1040 if( !text )
1041 return -1;
1042 memset( text, 0, sizeof(isom_text_entry_t) );
1043 isom_init_box_common( text, stsd, QT_CODEC_TYPE_TEXT_TEXT );
1044 text->data_reference_index = 1;
1045 if( lsmash_add_entry( stsd->list, text ) )
1047 free( text );
1048 return -1;
1050 return 0;
1053 int isom_add_ftab( isom_tx3g_entry_t *tx3g )
1055 if( !tx3g )
1056 return -1;
1057 isom_ftab_t *ftab = malloc( sizeof(isom_ftab_t) );
1058 if( !ftab )
1059 return -1;
1060 memset( ftab, 0, sizeof(isom_ftab_t) );
1061 isom_init_box_common( ftab, tx3g, ISOM_BOX_TYPE_FTAB );
1062 ftab->list = lsmash_create_entry_list();
1063 if( !ftab->list )
1065 free( ftab );
1066 return -1;
1068 tx3g->ftab = ftab;
1069 return 0;
1072 static int isom_add_tx3g_entry( isom_stsd_t *stsd )
1074 if( !stsd || !stsd->list )
1075 return -1;
1076 isom_tx3g_entry_t *tx3g = malloc( sizeof(isom_tx3g_entry_t) );
1077 if( !tx3g )
1078 return -1;
1079 memset( tx3g, 0, sizeof(isom_tx3g_entry_t) );
1080 isom_init_box_common( tx3g, stsd, ISOM_CODEC_TYPE_TX3G_TEXT );
1081 tx3g->data_reference_index = 1;
1082 if( isom_add_ftab( tx3g ) ||
1083 lsmash_add_entry( stsd->list, tx3g ) )
1085 free( tx3g );
1086 return -1;
1088 return 0;
1091 /* This function returns 0 if failed, sample_entry_number if succeeded. */
1092 int lsmash_add_sample_entry( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_type, void *summary )
1094 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
1095 if( !trak || !trak->root || !trak->root->ftyp || !trak->mdia || !trak->mdia->minf
1096 || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
1097 return 0;
1098 isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd;
1099 lsmash_entry_list_t *list = stsd->list;
1100 int ret = -1;
1101 switch( sample_type )
1103 case ISOM_CODEC_TYPE_AVC1_VIDEO :
1104 #if 0
1105 case ISOM_CODEC_TYPE_AVC2_VIDEO :
1106 case ISOM_CODEC_TYPE_AVCP_VIDEO :
1107 case ISOM_CODEC_TYPE_SVC1_VIDEO :
1108 case ISOM_CODEC_TYPE_MVC1_VIDEO :
1109 case ISOM_CODEC_TYPE_MVC2_VIDEO :
1110 case ISOM_CODEC_TYPE_MP4V_VIDEO :
1111 case ISOM_CODEC_TYPE_DRAC_VIDEO :
1112 case ISOM_CODEC_TYPE_ENCV_VIDEO :
1113 case ISOM_CODEC_TYPE_MJP2_VIDEO :
1114 case ISOM_CODEC_TYPE_S263_VIDEO :
1115 case ISOM_CODEC_TYPE_VC_1_VIDEO :
1116 #endif
1117 ret = isom_add_visual_entry( stsd, sample_type, (lsmash_video_summary_t *)summary );
1118 break;
1119 #if 0
1120 case ISOM_CODEC_TYPE_MP4S_SYSTEM :
1121 ret = isom_add_mp4s_entry( stsd );
1122 break;
1123 #endif
1124 case ISOM_CODEC_TYPE_MP4A_AUDIO :
1125 case ISOM_CODEC_TYPE_AC_3_AUDIO :
1126 case ISOM_CODEC_TYPE_ALAC_AUDIO :
1127 case ISOM_CODEC_TYPE_SAMR_AUDIO :
1128 case ISOM_CODEC_TYPE_SAWB_AUDIO :
1129 case QT_CODEC_TYPE_23NI_AUDIO :
1130 case QT_CODEC_TYPE_NONE_AUDIO :
1131 case QT_CODEC_TYPE_LPCM_AUDIO :
1132 case QT_CODEC_TYPE_RAW_AUDIO :
1133 case QT_CODEC_TYPE_SOWT_AUDIO :
1134 case QT_CODEC_TYPE_TWOS_AUDIO :
1135 case QT_CODEC_TYPE_FL32_AUDIO :
1136 case QT_CODEC_TYPE_FL64_AUDIO :
1137 case QT_CODEC_TYPE_IN24_AUDIO :
1138 case QT_CODEC_TYPE_IN32_AUDIO :
1139 case QT_CODEC_TYPE_NOT_SPECIFIED :
1140 #if 0
1141 case ISOM_CODEC_TYPE_DRA1_AUDIO :
1142 case ISOM_CODEC_TYPE_DTSC_AUDIO :
1143 case ISOM_CODEC_TYPE_DTSH_AUDIO :
1144 case ISOM_CODEC_TYPE_DTSL_AUDIO :
1145 case ISOM_CODEC_TYPE_EC_3_AUDIO :
1146 case ISOM_CODEC_TYPE_ENCA_AUDIO :
1147 case ISOM_CODEC_TYPE_G719_AUDIO :
1148 case ISOM_CODEC_TYPE_G726_AUDIO :
1149 case ISOM_CODEC_TYPE_M4AE_AUDIO :
1150 case ISOM_CODEC_TYPE_MLPA_AUDIO :
1151 case ISOM_CODEC_TYPE_RAW_AUDIO :
1152 case ISOM_CODEC_TYPE_SAWP_AUDIO :
1153 case ISOM_CODEC_TYPE_SEVC_AUDIO :
1154 case ISOM_CODEC_TYPE_SQCP_AUDIO :
1155 case ISOM_CODEC_TYPE_SSMV_AUDIO :
1156 case ISOM_CODEC_TYPE_TWOS_AUDIO :
1157 #endif
1158 ret = isom_add_audio_entry( stsd, sample_type, (lsmash_audio_summary_t *)summary );
1159 break;
1160 case ISOM_CODEC_TYPE_TX3G_TEXT :
1161 ret = isom_add_tx3g_entry( stsd );
1162 break;
1163 case QT_CODEC_TYPE_TEXT_TEXT :
1164 ret = isom_add_text_entry( stsd );
1165 break;
1166 default :
1167 return 0;
1169 return ret ? 0 : list->entry_count;
1172 static int isom_add_stts_entry( isom_stbl_t *stbl, uint32_t sample_delta )
1174 if( !stbl || !stbl->stts || !stbl->stts->list )
1175 return -1;
1176 isom_stts_entry_t *data = malloc( sizeof(isom_stts_entry_t) );
1177 if( !data )
1178 return -1;
1179 data->sample_count = 1;
1180 data->sample_delta = sample_delta;
1181 if( lsmash_add_entry( stbl->stts->list, data ) )
1183 free( data );
1184 return -1;
1186 return 0;
1189 static int isom_add_ctts_entry( isom_stbl_t *stbl, uint32_t sample_offset )
1191 if( !stbl || !stbl->ctts || !stbl->ctts->list )
1192 return -1;
1193 isom_ctts_entry_t *data = malloc( sizeof(isom_ctts_entry_t) );
1194 if( !data )
1195 return -1;
1196 data->sample_count = 1;
1197 data->sample_offset = sample_offset;
1198 if( lsmash_add_entry( stbl->ctts->list, data ) )
1200 free( data );
1201 return -1;
1203 return 0;
1206 static int isom_add_stsc_entry( isom_stbl_t *stbl, uint32_t first_chunk, uint32_t samples_per_chunk, uint32_t sample_description_index )
1208 if( !stbl || !stbl->stsc || !stbl->stsc->list )
1209 return -1;
1210 isom_stsc_entry_t *data = malloc( sizeof(isom_stsc_entry_t) );
1211 if( !data )
1212 return -1;
1213 data->first_chunk = first_chunk;
1214 data->samples_per_chunk = samples_per_chunk;
1215 data->sample_description_index = sample_description_index;
1216 if( lsmash_add_entry( stbl->stsc->list, data ) )
1218 free( data );
1219 return -1;
1221 return 0;
1224 static int isom_add_stsz_entry( isom_stbl_t *stbl, uint32_t entry_size )
1226 if( !stbl || !stbl->stsz )
1227 return -1;
1228 isom_stsz_t *stsz = stbl->stsz;
1229 /* retrieve initial sample_size */
1230 if( !stsz->sample_count )
1231 stsz->sample_size = entry_size;
1232 /* if it seems constant access_unit size at present, update sample_count only */
1233 if( !stsz->list && stsz->sample_size == entry_size )
1235 ++ stsz->sample_count;
1236 return 0;
1238 /* found sample_size varies, create sample_size list */
1239 if( !stsz->list )
1241 stsz->list = lsmash_create_entry_list();
1242 if( !stsz->list )
1243 return -1;
1244 for( uint32_t i = 0; i < stsz->sample_count; i++ )
1246 isom_stsz_entry_t *data = malloc( sizeof(isom_stsz_entry_t) );
1247 if( !data )
1248 return -1;
1249 data->entry_size = stsz->sample_size;
1250 if( lsmash_add_entry( stsz->list, data ) )
1252 free( data );
1253 return -1;
1256 stsz->sample_size = 0;
1258 isom_stsz_entry_t *data = malloc( sizeof(isom_stsz_entry_t) );
1259 if( !data )
1260 return -1;
1261 data->entry_size = entry_size;
1262 if( lsmash_add_entry( stsz->list, data ) )
1264 free( data );
1265 return -1;
1267 ++ stsz->sample_count;
1268 return 0;
1271 static int isom_add_stss_entry( isom_stbl_t *stbl, uint32_t sample_number )
1273 if( !stbl || !stbl->stss || !stbl->stss->list )
1274 return -1;
1275 isom_stss_entry_t *data = malloc( sizeof(isom_stss_entry_t) );
1276 if( !data )
1277 return -1;
1278 data->sample_number = sample_number;
1279 if( lsmash_add_entry( stbl->stss->list, data ) )
1281 free( data );
1282 return -1;
1284 return 0;
1287 static int isom_add_stps_entry( isom_stbl_t *stbl, uint32_t sample_number )
1289 if( !stbl || !stbl->stps || !stbl->stps->list )
1290 return -1;
1291 isom_stps_entry_t *data = malloc( sizeof(isom_stps_entry_t) );
1292 if( !data )
1293 return -1;
1294 data->sample_number = sample_number;
1295 if( lsmash_add_entry( stbl->stps->list, data ) )
1297 free( data );
1298 return -1;
1300 return 0;
1303 static int isom_add_sdtp_entry( isom_stbl_t *stbl, lsmash_sample_property_t *prop, uint8_t avc_extensions )
1305 if( !prop )
1306 return -1;
1307 if( !stbl || !stbl->sdtp || !stbl->sdtp->list )
1308 return -1;
1309 isom_sdtp_entry_t *data = malloc( sizeof(isom_sdtp_entry_t) );
1310 if( !data )
1311 return -1;
1312 /* isom_sdtp_entry_t is smaller than lsmash_sample_property_t. */
1313 data->is_leading = (avc_extensions ? prop->leading : prop->allow_earlier) & 0x03;
1314 data->sample_depends_on = prop->independent & 0x03;
1315 data->sample_is_depended_on = prop->disposable & 0x03;
1316 data->sample_has_redundancy = prop->redundant & 0x03;
1317 if( lsmash_add_entry( stbl->sdtp->list, data ) )
1319 free( data );
1320 return -1;
1322 return 0;
1325 static int isom_add_co64( isom_stbl_t *stbl )
1327 if( !stbl || stbl->stco )
1328 return -1;
1329 isom_create_list_box( stco, stbl, ISOM_BOX_TYPE_CO64 );
1330 stco->large_presentation = 1;
1331 stbl->stco = stco;
1332 return 0;
1335 static int isom_add_stco( isom_stbl_t *stbl )
1337 if( !stbl || stbl->stco )
1338 return -1;
1339 isom_create_list_box( stco, stbl, ISOM_BOX_TYPE_STCO );
1340 stco->large_presentation = 0;
1341 stbl->stco = stco;
1342 return 0;
1345 static int isom_add_co64_entry( isom_stbl_t *stbl, uint64_t chunk_offset )
1347 if( !stbl || !stbl->stco || !stbl->stco->list )
1348 return -1;
1349 isom_co64_entry_t *data = malloc( sizeof(isom_co64_entry_t) );
1350 if( !data )
1351 return -1;
1352 data->chunk_offset = chunk_offset;
1353 if( lsmash_add_entry( stbl->stco->list, data ) )
1355 free( data );
1356 return -1;
1358 return 0;
1361 static int isom_convert_stco_to_co64( isom_stbl_t* stbl )
1363 /* backup stco */
1364 isom_stco_t *stco = stbl->stco;
1365 stbl->stco = NULL;
1366 if( isom_add_co64( stbl ) )
1367 return -1;
1368 /* move chunk_offset to co64 from stco */
1369 for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next )
1371 isom_stco_entry_t *data = (isom_stco_entry_t*)entry->data;
1372 if( isom_add_co64_entry( stbl, data->chunk_offset ) )
1373 return -1;
1375 lsmash_remove_list( stco->list, NULL );
1376 free( stco );
1377 return 0;
1380 static int isom_add_stco_entry( isom_stbl_t *stbl, uint64_t chunk_offset )
1382 if( !stbl || !stbl->stco || !stbl->stco->list )
1383 return -1;
1384 if( stbl->stco->large_presentation )
1385 return isom_add_co64_entry( stbl, chunk_offset );
1386 if( chunk_offset > UINT32_MAX )
1388 if( isom_convert_stco_to_co64( stbl ) )
1389 return -1;
1390 return isom_add_co64_entry( stbl, chunk_offset );
1392 isom_stco_entry_t *data = malloc( sizeof(isom_stco_entry_t) );
1393 if( !data )
1394 return -1;
1395 data->chunk_offset = (uint32_t)chunk_offset;
1396 if( lsmash_add_entry( stbl->stco->list, data ) )
1398 free( data );
1399 return -1;
1401 return 0;
1404 isom_sgpd_entry_t *isom_get_sample_group_description( isom_stbl_t *stbl, uint32_t grouping_type )
1406 if( !stbl->sgpd_list )
1407 return NULL;
1408 for( lsmash_entry_t *entry = stbl->sgpd_list->head; entry; entry = entry->next )
1410 isom_sgpd_entry_t *sgpd = (isom_sgpd_entry_t *)entry->data;
1411 if( !sgpd || !sgpd->list )
1412 return NULL;
1413 if( sgpd->grouping_type == grouping_type )
1414 return sgpd;
1416 return NULL;
1419 isom_sbgp_entry_t *isom_get_sample_to_group( isom_stbl_t *stbl, uint32_t grouping_type )
1421 if( !stbl->sbgp_list )
1422 return NULL;
1423 for( lsmash_entry_t *entry = stbl->sbgp_list->head; entry; entry = entry->next )
1425 isom_sbgp_entry_t *sbgp = (isom_sbgp_entry_t *)entry->data;
1426 if( !sbgp || !sbgp->list )
1427 return NULL;
1428 if( sbgp->grouping_type == grouping_type )
1429 return sbgp;
1431 return NULL;
1434 static isom_rap_entry_t *isom_add_rap_group_entry( isom_sgpd_entry_t *sgpd )
1436 if( !sgpd )
1437 return NULL;
1438 isom_rap_entry_t *data = malloc( sizeof(isom_rap_entry_t) );
1439 if( !data )
1440 return NULL;
1441 memset( data, 0, sizeof(isom_rap_entry_t) );
1442 if( lsmash_add_entry( sgpd->list, data ) )
1444 free( data );
1445 return NULL;
1447 return data;
1450 static isom_roll_entry_t *isom_add_roll_group_entry( isom_sgpd_entry_t *sgpd, int16_t roll_distance )
1452 if( !sgpd )
1453 return NULL;
1454 isom_roll_entry_t *data = malloc( sizeof(isom_roll_entry_t) );
1455 if( !data )
1456 return NULL;
1457 data->description_length = 0;
1458 data->roll_distance = roll_distance;
1459 if( lsmash_add_entry( sgpd->list, data ) )
1461 free( data );
1462 return NULL;
1464 return data;
1467 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 )
1469 if( !sbgp )
1470 return NULL;
1471 isom_group_assignment_entry_t *data = malloc( sizeof(isom_group_assignment_entry_t) );
1472 if( !data )
1473 return NULL;
1474 data->sample_count = sample_count;
1475 data->group_description_index = group_description_index;
1476 if( lsmash_add_entry( sbgp->list, data ) )
1478 free( data );
1479 return NULL;
1481 return data;
1484 static int isom_add_chpl_entry( isom_chpl_t *chpl, isom_chapter_entry_t *chap_data )
1486 if( !chap_data->chapter_name || !chpl || !chpl->list )
1487 return -1;
1488 isom_chpl_entry_t *data = malloc( sizeof(isom_chpl_entry_t) );
1489 if( !data )
1490 return -1;
1491 data->start_time = chap_data->start_time;
1492 data->chapter_name_length = strlen( chap_data->chapter_name );
1493 data->chapter_name = ( char* )malloc( data->chapter_name_length + 1 );
1494 if( !data->chapter_name )
1496 free( data );
1497 return -1;
1499 memcpy( data->chapter_name, chap_data->chapter_name, data->chapter_name_length );
1500 data->chapter_name[data->chapter_name_length] = '\0';
1501 if( lsmash_add_entry( chpl->list, data ) )
1503 free( data->chapter_name );
1504 free( data );
1505 return -1;
1507 return 0;
1510 static isom_trex_entry_t *isom_add_trex( isom_mvex_t *mvex )
1512 if( !mvex )
1513 return NULL;
1514 if( !mvex->trex_list )
1516 mvex->trex_list = lsmash_create_entry_list();
1517 if( !mvex->trex_list )
1518 return NULL;
1520 isom_trex_entry_t *trex = malloc( sizeof(isom_trex_entry_t) );
1521 if( !trex )
1522 return NULL;
1523 memset( trex, 0, sizeof(isom_trex_entry_t) );
1524 isom_init_box_common( trex, mvex, ISOM_BOX_TYPE_TREX );
1525 if( lsmash_add_entry( mvex->trex_list, trex ) )
1527 free( trex );
1528 return NULL;
1530 return trex;
1533 static isom_trun_entry_t *isom_add_trun( isom_traf_entry_t *traf )
1535 if( !traf )
1536 return NULL;
1537 if( !traf->trun_list )
1539 traf->trun_list = lsmash_create_entry_list();
1540 if( !traf->trun_list )
1541 return NULL;
1543 isom_trun_entry_t *trun = malloc( sizeof(isom_trun_entry_t) );
1544 if( !trun )
1545 return NULL;
1546 memset( trun, 0, sizeof(isom_trun_entry_t) );
1547 isom_init_box_common( trun, traf, ISOM_BOX_TYPE_TRUN );
1548 if( lsmash_add_entry( traf->trun_list, trun ) )
1550 free( trun );
1551 return NULL;
1553 return trun;
1556 static isom_traf_entry_t *isom_add_traf( lsmash_root_t *root, isom_moof_entry_t *moof )
1558 if( !root || !root->moof_list || !moof )
1559 return NULL;
1560 if( !moof->traf_list )
1562 moof->traf_list = lsmash_create_entry_list();
1563 if( !moof->traf_list )
1564 return NULL;
1566 isom_traf_entry_t *traf = malloc( sizeof(isom_traf_entry_t) );
1567 if( !traf )
1568 return NULL;
1569 memset( traf, 0, sizeof(isom_traf_entry_t) );
1570 isom_init_box_common( traf, moof, ISOM_BOX_TYPE_TRAF );
1571 isom_cache_t *cache = malloc( sizeof(isom_cache_t) );
1572 if( !cache )
1574 free( traf );
1575 return NULL;
1577 memset( cache, 0, sizeof(isom_cache_t) );
1578 if( lsmash_add_entry( moof->traf_list, traf ) )
1580 free( cache );
1581 free( traf );
1582 return NULL;
1584 traf->cache = cache;
1585 return traf;
1588 static isom_moof_entry_t *isom_add_moof( lsmash_root_t *root )
1590 if( !root )
1591 return NULL;
1592 if( !root->moof_list )
1594 root->moof_list = lsmash_create_entry_list();
1595 if( !root->moof_list )
1596 return NULL;
1598 isom_moof_entry_t *moof = malloc( sizeof(isom_moof_entry_t) );
1599 if( !moof )
1600 return NULL;
1601 memset( moof, 0, sizeof(isom_moof_entry_t) );
1602 isom_init_box_common( moof, root, ISOM_BOX_TYPE_MOOF );
1603 if( lsmash_add_entry( root->moof_list, moof ) )
1605 free( moof );
1606 return NULL;
1608 return moof;
1611 static isom_tfra_entry_t *isom_add_tfra( isom_mfra_t *mfra )
1613 if( !mfra )
1614 return NULL;
1615 if( !mfra->tfra_list )
1617 mfra->tfra_list = lsmash_create_entry_list();
1618 if( !mfra->tfra_list )
1619 return NULL;
1621 isom_tfra_entry_t *tfra = malloc( sizeof(isom_tfra_entry_t) );
1622 if( !tfra )
1623 return NULL;
1624 memset( tfra, 0, sizeof(isom_tfra_entry_t) );
1625 isom_init_box_common( tfra, mfra, ISOM_BOX_TYPE_TFRA );
1626 if( lsmash_add_entry( mfra->tfra_list, tfra ) )
1628 free( tfra );
1629 return NULL;
1631 return tfra;
1634 static int isom_add_ftyp( lsmash_root_t *root )
1636 if( root->ftyp )
1637 return -1;
1638 isom_create_box( ftyp, root, ISOM_BOX_TYPE_FTYP );
1639 ftyp->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 8;
1640 root->ftyp = ftyp;
1641 return 0;
1644 static int isom_add_moov( lsmash_root_t *root )
1646 if( root->moov )
1647 return -1;
1648 isom_create_box( moov, root, ISOM_BOX_TYPE_MOOV );
1649 root->moov = moov;
1650 return 0;
1653 static int isom_add_mvhd( isom_moov_t *moov )
1655 if( !moov || moov->mvhd )
1656 return -1;
1657 isom_create_box( mvhd, moov, ISOM_BOX_TYPE_MVHD );
1658 mvhd->rate = 0x00010000;
1659 mvhd->volume = 0x0100;
1660 mvhd->matrix[0] = 0x00010000;
1661 mvhd->matrix[4] = 0x00010000;
1662 mvhd->matrix[8] = 0x40000000;
1663 mvhd->next_track_ID = 1;
1664 moov->mvhd = mvhd;
1665 return 0;
1668 static int isom_scan_trak_profileLevelIndication( isom_trak_entry_t* trak, mp4a_audioProfileLevelIndication* audio_pli, mp4sys_visualProfileLevelIndication* visual_pli )
1670 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl )
1671 return -1;
1672 isom_stsd_t* stsd = trak->mdia->minf->stbl->stsd;
1673 if( !stsd || !stsd->list || !stsd->list->head )
1674 return -1;
1675 for( lsmash_entry_t *entry = stsd->list->head; entry; entry = entry->next )
1677 isom_sample_entry_t* sample_entry = (isom_sample_entry_t*)entry->data;
1678 if( !sample_entry )
1679 return -1;
1680 switch( sample_entry->type )
1682 case ISOM_CODEC_TYPE_AVC1_VIDEO :
1683 #if 0
1684 case ISOM_CODEC_TYPE_AVC2_VIDEO :
1685 case ISOM_CODEC_TYPE_AVCP_VIDEO :
1686 case ISOM_CODEC_TYPE_SVC1_VIDEO :
1687 case ISOM_CODEC_TYPE_MVC1_VIDEO :
1688 case ISOM_CODEC_TYPE_MVC2_VIDEO :
1689 #endif
1690 /* FIXME: Do we have to arbitrate like audio? */
1691 if( *visual_pli == MP4SYS_VISUAL_PLI_NONE_REQUIRED )
1692 *visual_pli = MP4SYS_VISUAL_PLI_H264_AVC;
1693 break;
1694 case ISOM_CODEC_TYPE_MP4A_AUDIO :
1695 *audio_pli = mp4a_max_audioProfileLevelIndication( *audio_pli, mp4a_get_audioProfileLevelIndication( &((isom_audio_entry_t*)sample_entry)->summary ) );
1696 break;
1697 #if 0
1698 case ISOM_CODEC_TYPE_DRAC_VIDEO :
1699 case ISOM_CODEC_TYPE_ENCV_VIDEO :
1700 case ISOM_CODEC_TYPE_MJP2_VIDEO :
1701 case ISOM_CODEC_TYPE_S263_VIDEO :
1702 case ISOM_CODEC_TYPE_VC_1_VIDEO :
1703 /* FIXME: Do we have to arbitrate like audio? */
1704 if( *visual_pli == MP4SYS_VISUAL_PLI_NONE_REQUIRED )
1705 *visual_pli = MP4SYS_VISUAL_PLI_NOT_SPECIFIED;
1706 break;
1707 #endif
1708 case ISOM_CODEC_TYPE_AC_3_AUDIO :
1709 case ISOM_CODEC_TYPE_ALAC_AUDIO :
1710 case ISOM_CODEC_TYPE_SAMR_AUDIO :
1711 case ISOM_CODEC_TYPE_SAWB_AUDIO :
1712 #ifdef LSMASH_DEMUXER_ENABLED
1713 case ISOM_CODEC_TYPE_EC_3_AUDIO :
1714 #endif
1715 #if 0
1716 case ISOM_CODEC_TYPE_DRA1_AUDIO :
1717 case ISOM_CODEC_TYPE_DTSC_AUDIO :
1718 case ISOM_CODEC_TYPE_DTSH_AUDIO :
1719 case ISOM_CODEC_TYPE_DTSL_AUDIO :
1720 case ISOM_CODEC_TYPE_ENCA_AUDIO :
1721 case ISOM_CODEC_TYPE_G719_AUDIO :
1722 case ISOM_CODEC_TYPE_G726_AUDIO :
1723 case ISOM_CODEC_TYPE_M4AE_AUDIO :
1724 case ISOM_CODEC_TYPE_MLPA_AUDIO :
1725 case ISOM_CODEC_TYPE_RAW_AUDIO :
1726 case ISOM_CODEC_TYPE_SAWP_AUDIO :
1727 case ISOM_CODEC_TYPE_SEVC_AUDIO :
1728 case ISOM_CODEC_TYPE_SQCP_AUDIO :
1729 case ISOM_CODEC_TYPE_SSMV_AUDIO :
1730 case ISOM_CODEC_TYPE_TWOS_AUDIO :
1731 #endif
1732 /* NOTE: These audio codecs other than mp4a does not have appropriate pli. */
1733 *audio_pli = MP4A_AUDIO_PLI_NOT_SPECIFIED;
1734 break;
1735 #if 0
1736 case ISOM_CODEC_TYPE_FDP_HINT :
1737 case ISOM_CODEC_TYPE_M2TS_HINT :
1738 case ISOM_CODEC_TYPE_PM2T_HINT :
1739 case ISOM_CODEC_TYPE_PRTP_HINT :
1740 case ISOM_CODEC_TYPE_RM2T_HINT :
1741 case ISOM_CODEC_TYPE_RRTP_HINT :
1742 case ISOM_CODEC_TYPE_RSRP_HINT :
1743 case ISOM_CODEC_TYPE_RTP_HINT :
1744 case ISOM_CODEC_TYPE_SM2T_HINT :
1745 case ISOM_CODEC_TYPE_SRTP_HINT :
1746 /* FIXME: Do we have to set OD_profileLevelIndication? */
1747 break;
1748 case ISOM_CODEC_TYPE_IXSE_META :
1749 case ISOM_CODEC_TYPE_METT_META :
1750 case ISOM_CODEC_TYPE_METX_META :
1751 case ISOM_CODEC_TYPE_MLIX_META :
1752 case ISOM_CODEC_TYPE_OKSD_META :
1753 case ISOM_CODEC_TYPE_SVCM_META :
1754 case ISOM_CODEC_TYPE_TEXT_META :
1755 case ISOM_CODEC_TYPE_URIM_META :
1756 case ISOM_CODEC_TYPE_XML_META :
1757 /* FIXME: Do we have to set OD_profileLevelIndication? */
1758 break;
1759 #endif
1762 return 0;
1765 static int isom_add_iods( isom_moov_t *moov )
1767 if( !moov || !moov->trak_list || moov->iods )
1768 return -1;
1769 isom_create_box( iods, moov, ISOM_BOX_TYPE_IODS );
1770 iods->OD = mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */
1771 if( !iods->OD )
1773 free( iods );
1774 return -1;
1776 mp4a_audioProfileLevelIndication audio_pli = MP4A_AUDIO_PLI_NONE_REQUIRED;
1777 mp4sys_visualProfileLevelIndication visual_pli = MP4SYS_VISUAL_PLI_NONE_REQUIRED;
1778 for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
1780 isom_trak_entry_t* trak = (isom_trak_entry_t*)entry->data;
1781 if( !trak || !trak->tkhd )
1782 return -1;
1783 if( isom_scan_trak_profileLevelIndication( trak, &audio_pli, &visual_pli ) )
1784 return -1;
1785 if( mp4sys_add_ES_ID_Inc( iods->OD, trak->tkhd->track_ID ) )
1786 return -1;
1788 if( mp4sys_to_InitialObjectDescriptor( iods->OD,
1789 0, /* FIXME: I'm not quite sure what the spec says. */
1790 MP4SYS_OD_PLI_NONE_REQUIRED, MP4SYS_SCENE_PLI_NONE_REQUIRED,
1791 audio_pli, visual_pli,
1792 MP4SYS_GRAPHICS_PLI_NONE_REQUIRED ) )
1794 free( iods );
1795 return -1;
1797 moov->iods = iods;
1798 return 0;
1801 static int isom_add_tkhd( isom_trak_entry_t *trak, uint32_t handler_type )
1803 if( !trak || !trak->root || !trak->root->moov || !trak->root->moov->mvhd || !trak->root->moov->trak_list )
1804 return -1;
1805 if( !trak->tkhd )
1807 isom_create_box( tkhd, trak, ISOM_BOX_TYPE_TKHD );
1808 switch( handler_type )
1810 case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK :
1811 tkhd->matrix[0] = 0x00010000;
1812 tkhd->matrix[4] = 0x00010000;
1813 tkhd->matrix[8] = 0x40000000;
1814 break;
1815 case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK :
1816 tkhd->volume = 0x0100;
1817 break;
1818 default :
1819 break;
1821 tkhd->duration = 0xffffffff;
1822 tkhd->track_ID = trak->root->moov->mvhd->next_track_ID;
1823 ++ trak->root->moov->mvhd->next_track_ID;
1824 trak->tkhd = tkhd;
1826 return 0;
1829 static int isom_add_clef( isom_tapt_t *tapt )
1831 if( tapt->clef )
1832 return 0;
1833 isom_create_box( clef, tapt, QT_BOX_TYPE_CLEF );
1834 tapt->clef = clef;
1835 return 0;
1838 static int isom_add_prof( isom_tapt_t *tapt )
1840 if( tapt->prof )
1841 return 0;
1842 isom_create_box( prof, tapt, QT_BOX_TYPE_PROF );
1843 tapt->prof = prof;
1844 return 0;
1847 static int isom_add_enof( isom_tapt_t *tapt )
1849 if( tapt->enof )
1850 return 0;
1851 isom_create_box( enof, tapt, QT_BOX_TYPE_ENOF );
1852 tapt->enof = enof;
1853 return 0;
1856 static int isom_add_tapt( isom_trak_entry_t *trak )
1858 if( trak->tapt )
1859 return 0;
1860 isom_create_box( tapt, trak, QT_BOX_TYPE_TAPT );
1861 trak->tapt = tapt;
1862 return 0;
1865 int isom_add_elst( isom_edts_t *edts )
1867 if( edts->elst )
1868 return 0;
1869 isom_create_list_box( elst, edts, ISOM_BOX_TYPE_ELST );
1870 edts->elst = elst;
1871 return 0;
1874 int isom_add_edts( isom_trak_entry_t *trak )
1876 if( trak->edts )
1877 return 0;
1878 isom_create_box( edts, trak, ISOM_BOX_TYPE_EDTS );
1879 trak->edts = edts;
1880 return 0;
1883 static int isom_add_tref( isom_trak_entry_t *trak )
1885 if( trak->tref )
1886 return 0;
1887 isom_create_box( tref, trak, ISOM_BOX_TYPE_TREF );
1888 tref->ref_list = lsmash_create_entry_list();
1889 if( !tref->ref_list )
1891 free( tref );
1892 return -1;
1894 trak->tref = tref;
1895 return 0;
1898 static int isom_add_mdhd( isom_mdia_t *mdia, uint16_t default_language )
1900 if( !mdia || mdia->mdhd )
1901 return -1;
1902 isom_create_box( mdhd, mdia, ISOM_BOX_TYPE_MDHD );
1903 mdhd->language = default_language;
1904 mdia->mdhd = mdhd;
1905 return 0;
1908 static int isom_add_mdia( isom_trak_entry_t *trak )
1910 if( !trak || trak->mdia )
1911 return -1;
1912 isom_create_box( mdia, trak, ISOM_BOX_TYPE_MDIA );
1913 trak->mdia = mdia;
1914 return 0;
1917 static int isom_add_hdlr( isom_mdia_t *mdia, isom_minf_t *minf, uint32_t media_type )
1919 if( (!mdia && !minf) || (mdia && minf) )
1920 return -1; /* Either one must be given. */
1921 if( (mdia && mdia->hdlr) || (minf && minf->hdlr) )
1922 return -1; /* Selected one must not have hdlr yet. */
1923 isom_box_t *parent = mdia ? (isom_box_t *)mdia : (isom_box_t *)minf;
1924 isom_create_box( hdlr, parent, ISOM_BOX_TYPE_HDLR );
1925 lsmash_root_t *root = hdlr->root;
1926 uint32_t type = mdia ? (root->qt_compatible ? QT_HANDLER_TYPE_MEDIA : 0) : QT_HANDLER_TYPE_DATA;
1927 uint32_t subtype = media_type;
1928 hdlr->componentType = type;
1929 hdlr->componentSubtype = subtype;
1930 char *type_name = NULL;
1931 char *subtype_name = NULL;
1932 uint8_t type_name_length = 0;
1933 uint8_t subtype_name_length = 0;
1934 switch( type )
1936 case QT_HANDLER_TYPE_DATA :
1937 type_name = "Data ";
1938 type_name_length = 5;
1939 break;
1940 default :
1941 type_name = "Media ";
1942 type_name_length = 6;
1943 break;
1945 switch( subtype )
1947 case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK :
1948 subtype_name = "Sound ";
1949 subtype_name_length = 6;
1950 break;
1951 case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK :
1952 subtype_name = "Video ";
1953 subtype_name_length = 6;
1954 break;
1955 case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK :
1956 subtype_name = "Hint ";
1957 subtype_name_length = 5;
1958 break;
1959 case ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK :
1960 subtype_name = "Meta ";
1961 subtype_name_length = 5;
1962 break;
1963 case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK :
1964 subtype_name = "Text ";
1965 subtype_name_length = 5;
1966 break;
1967 case QT_REFERENCE_HANDLER_TYPE_ALIAS :
1968 subtype_name = "Alias ";
1969 subtype_name_length = 6;
1970 break;
1971 case QT_REFERENCE_HANDLER_TYPE_RESOURCE :
1972 subtype_name = "Resource ";
1973 subtype_name_length = 9;
1974 break;
1975 case QT_REFERENCE_HANDLER_TYPE_URL :
1976 subtype_name = "URL ";
1977 subtype_name_length = 4;
1978 break;
1979 default :
1980 subtype_name = "Unknown ";
1981 subtype_name_length = 8;
1982 break;
1984 uint32_t name_length = 15 + subtype_name_length + type_name_length + root->isom_compatible + root->qt_compatible;
1985 uint8_t *name = malloc( name_length );
1986 if( !name )
1987 return -1;
1988 if( root->qt_compatible )
1989 name[0] = name_length & 0xff;
1990 memcpy( name + root->qt_compatible, "L-SMASH ", 8 );
1991 memcpy( name + root->qt_compatible + 8, subtype_name, subtype_name_length );
1992 memcpy( name + root->qt_compatible + 8 + subtype_name_length, type_name, type_name_length );
1993 memcpy( name + root->qt_compatible + 8 + subtype_name_length + type_name_length, "Handler", 7 );
1994 if( root->isom_compatible )
1995 name[name_length - 1] = 0;
1996 hdlr->componentName = name;
1997 hdlr->componentName_length = name_length;
1998 if( mdia )
1999 mdia->hdlr = hdlr;
2000 else
2001 minf->hdlr = hdlr;
2002 return 0;
2005 static int isom_add_minf( isom_mdia_t *mdia )
2007 if( !mdia || mdia->minf )
2008 return -1;
2009 isom_create_box( minf, mdia, ISOM_BOX_TYPE_MINF );
2010 mdia->minf = minf;
2011 return 0;
2014 static int isom_add_vmhd( isom_minf_t *minf )
2016 if( !minf || minf->vmhd )
2017 return -1;
2018 isom_create_box( vmhd, minf, ISOM_BOX_TYPE_VMHD );
2019 vmhd->flags = 0x000001;
2020 minf->vmhd = vmhd;
2021 return 0;
2024 static int isom_add_smhd( isom_minf_t *minf )
2026 if( !minf || minf->smhd )
2027 return -1;
2028 isom_create_box( smhd, minf, ISOM_BOX_TYPE_SMHD );
2029 minf->smhd = smhd;
2030 return 0;
2033 static int isom_add_hmhd( isom_minf_t *minf )
2035 if( !minf || minf->hmhd )
2036 return -1;
2037 isom_create_box( hmhd, minf, ISOM_BOX_TYPE_HMHD );
2038 minf->hmhd = hmhd;
2039 return 0;
2042 static int isom_add_nmhd( isom_minf_t *minf )
2044 if( !minf || minf->nmhd )
2045 return -1;
2046 isom_create_box( nmhd, minf, ISOM_BOX_TYPE_NMHD );
2047 minf->nmhd = nmhd;
2048 return 0;
2051 static int isom_add_gmin( isom_gmhd_t *gmhd )
2053 if( !gmhd || gmhd->gmin )
2054 return -1;
2055 isom_create_box( gmin, gmhd, QT_BOX_TYPE_GMIN );
2056 gmhd->gmin = gmin;
2057 return 0;
2060 static int isom_add_text( isom_gmhd_t *gmhd )
2062 if( !gmhd || gmhd->text )
2063 return -1;
2064 isom_create_box( text, gmhd, QT_BOX_TYPE_TEXT );
2065 text->matrix[0] = 0x00010000;
2066 text->matrix[4] = 0x00010000;
2067 text->matrix[8] = 0x40000000;
2068 gmhd->text = text;
2069 return 0;
2072 static int isom_add_gmhd( isom_minf_t *minf )
2074 if( !minf || minf->gmhd )
2075 return -1;
2076 isom_create_box( gmhd, minf, QT_BOX_TYPE_GMHD );
2077 minf->gmhd = gmhd;
2078 return 0;
2081 static int isom_add_dinf( isom_minf_t *minf )
2083 if( !minf || minf->dinf )
2084 return -1;
2085 isom_create_box( dinf, minf, ISOM_BOX_TYPE_DINF );
2086 minf->dinf = dinf;
2087 return 0;
2090 static int isom_add_dref( isom_dinf_t *dinf )
2092 if( !dinf || dinf->dref )
2093 return -1;
2094 isom_create_list_box( dref, dinf, ISOM_BOX_TYPE_DREF );
2095 dinf->dref = dref;
2096 if( isom_add_dref_entry( dref, 0x000001, NULL, NULL ) )
2097 return -1;
2098 return 0;
2101 static int isom_add_stsd( isom_stbl_t *stbl )
2103 if( !stbl || stbl->stsd )
2104 return -1;
2105 isom_create_list_box( stsd, stbl, ISOM_BOX_TYPE_STSD );
2106 stbl->stsd = stsd;
2107 return 0;
2110 int isom_add_btrt( isom_visual_entry_t *visual )
2112 if( !visual || visual->btrt )
2113 return -1;
2114 isom_create_box( btrt, visual, ISOM_BOX_TYPE_BTRT );
2115 visual->btrt = btrt;
2116 return 0;
2119 int lsmash_add_btrt( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number )
2121 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
2122 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
2123 return -1;
2124 isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number );
2125 return isom_add_btrt( data );
2128 static int isom_add_stts( isom_stbl_t *stbl )
2130 if( !stbl || stbl->stts )
2131 return -1;
2132 isom_create_list_box( stts, stbl, ISOM_BOX_TYPE_STTS );
2133 stbl->stts = stts;
2134 return 0;
2137 static int isom_add_ctts( isom_stbl_t *stbl )
2139 if( !stbl || stbl->ctts )
2140 return -1;
2141 isom_create_list_box( ctts, stbl, ISOM_BOX_TYPE_CTTS );
2142 stbl->ctts = ctts;
2143 return 0;
2146 static int isom_add_cslg( isom_stbl_t *stbl )
2148 if( !stbl || stbl->cslg )
2149 return -1;
2150 isom_create_box( cslg, stbl, ISOM_BOX_TYPE_CSLG );
2151 stbl->cslg = cslg;
2152 return 0;
2155 static int isom_add_stsc( isom_stbl_t *stbl )
2157 if( !stbl || stbl->stsc )
2158 return -1;
2159 isom_create_list_box( stsc, stbl, ISOM_BOX_TYPE_STSC );
2160 stbl->stsc = stsc;
2161 return 0;
2164 static int isom_add_stsz( isom_stbl_t *stbl )
2166 if( !stbl || stbl->stsz )
2167 return -1;
2168 isom_create_box( stsz, stbl, ISOM_BOX_TYPE_STSZ ); /* We don't create a list here. */
2169 stbl->stsz = stsz;
2170 return 0;
2173 static int isom_add_stss( isom_stbl_t *stbl )
2175 if( !stbl || stbl->stss )
2176 return -1;
2177 isom_create_list_box( stss, stbl, ISOM_BOX_TYPE_STSS );
2178 stbl->stss = stss;
2179 return 0;
2182 static int isom_add_stps( isom_stbl_t *stbl )
2184 if( !stbl || stbl->stps )
2185 return -1;
2186 isom_create_list_box( stps, stbl, QT_BOX_TYPE_STPS );
2187 stbl->stps = stps;
2188 return 0;
2191 static int isom_add_sdtp( isom_stbl_t *stbl )
2193 if( !stbl || stbl->sdtp )
2194 return -1;
2195 isom_create_list_box( sdtp, stbl, ISOM_BOX_TYPE_SDTP );
2196 stbl->sdtp = sdtp;
2197 return 0;
2200 static isom_sgpd_entry_t *isom_add_sgpd( isom_stbl_t *stbl, uint32_t grouping_type )
2202 if( !stbl )
2203 return NULL;
2204 if( !stbl->sgpd_list )
2206 stbl->sgpd_list = lsmash_create_entry_list();
2207 if( !stbl->sgpd_list )
2208 return NULL;
2210 isom_sgpd_entry_t *sgpd = malloc( sizeof(isom_sgpd_entry_t) );
2211 if( !sgpd )
2212 return NULL;
2213 memset( sgpd, 0, sizeof(isom_sgpd_entry_t) );
2214 isom_init_box_common( sgpd, stbl, ISOM_BOX_TYPE_SGPD );
2215 sgpd->list = lsmash_create_entry_list();
2216 if( !sgpd->list || lsmash_add_entry( stbl->sgpd_list, sgpd ) )
2218 free( sgpd );
2219 return NULL;
2221 sgpd->grouping_type = grouping_type;
2222 sgpd->version = 1; /* We use version 1 because it is recommended in the spec. */
2223 switch( grouping_type )
2225 case ISOM_GROUP_TYPE_RAP :
2226 sgpd->default_length = 1;
2227 break;
2228 case ISOM_GROUP_TYPE_ROLL :
2229 sgpd->default_length = 2;
2230 break;
2231 default :
2232 /* We don't consider other grouping types currently. */
2233 break;
2235 return sgpd;
2238 static isom_sbgp_entry_t *isom_add_sbgp( isom_stbl_t *stbl, uint32_t grouping_type )
2240 if( !stbl )
2241 return NULL;
2242 if( !stbl->sbgp_list )
2244 stbl->sbgp_list = lsmash_create_entry_list();
2245 if( !stbl->sbgp_list )
2246 return NULL;
2248 isom_sbgp_entry_t *sbgp = malloc( sizeof(isom_sbgp_entry_t) );
2249 if( !sbgp )
2250 return NULL;
2251 memset( sbgp, 0, sizeof(isom_sbgp_entry_t) );
2252 isom_init_box_common( sbgp, stbl, ISOM_BOX_TYPE_SBGP );
2253 sbgp->list = lsmash_create_entry_list();
2254 if( !sbgp->list || lsmash_add_entry( stbl->sbgp_list, sbgp ) )
2256 free( sbgp );
2257 return NULL;
2259 sbgp->grouping_type = grouping_type;
2260 return sbgp;
2263 static int isom_add_stbl( isom_minf_t *minf )
2265 if( !minf || minf->stbl )
2266 return -1;
2267 isom_create_box( stbl, minf, ISOM_BOX_TYPE_STBL );
2268 minf->stbl = stbl;
2269 return 0;
2272 static int isom_add_chpl( isom_moov_t *moov )
2274 if( !moov || !moov->udta || moov->udta->chpl )
2275 return -1;
2276 isom_create_list_box( chpl, moov, ISOM_BOX_TYPE_CHPL );
2277 chpl->version = 1; /* version = 1 is popular. */
2278 moov->udta->chpl = chpl;
2279 return 0;
2282 static int isom_add_udta( lsmash_root_t *root, uint32_t track_ID )
2284 /* track_ID == 0 means the direct addition to moov box */
2285 if( !track_ID )
2287 if( !root || !root->moov )
2288 return -1;
2289 if( root->moov->udta )
2290 return 0;
2291 isom_create_box( udta, root->moov, ISOM_BOX_TYPE_UDTA );
2292 root->moov->udta = udta;
2293 return 0;
2295 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
2296 if( !trak )
2297 return -1;
2298 if( trak->udta )
2299 return 0;
2300 isom_create_box( udta, trak, ISOM_BOX_TYPE_UDTA );
2301 trak->udta = udta;
2302 return 0;
2305 static isom_trak_entry_t *isom_add_trak( lsmash_root_t *root )
2307 if( !root || !root->moov )
2308 return NULL;
2309 isom_moov_t *moov = root->moov;
2310 if( !moov->trak_list )
2312 moov->trak_list = lsmash_create_entry_list();
2313 if( !moov->trak_list )
2314 return NULL;
2316 isom_trak_entry_t *trak = malloc( sizeof(isom_trak_entry_t) );
2317 if( !trak )
2318 return NULL;
2319 memset( trak, 0, sizeof(isom_trak_entry_t) );
2320 isom_init_box_common( trak, moov, ISOM_BOX_TYPE_TRAK );
2321 isom_cache_t *cache = malloc( sizeof(isom_cache_t) );
2322 if( !cache )
2324 free( trak );
2325 return NULL;
2327 memset( cache, 0, sizeof(isom_cache_t) );
2328 isom_fragment_t *fragment = NULL;
2329 if( root->fragment )
2331 fragment = malloc( sizeof(isom_fragment_t) );
2332 if( !fragment )
2334 free( cache );
2335 free( trak );
2336 return NULL;
2338 memset( fragment, 0, sizeof(isom_fragment_t) );
2339 cache->fragment = fragment;
2341 if( lsmash_add_entry( moov->trak_list, trak ) )
2343 if( fragment )
2344 free( fragment );
2345 free( cache );
2346 free( trak );
2347 return NULL;
2349 trak->cache = cache;
2350 return trak;
2353 static int isom_add_mvex( isom_moov_t *moov )
2355 if( !moov || moov->mvex )
2356 return -1;
2357 isom_create_box( mvex, moov, ISOM_BOX_TYPE_MVEX );
2358 moov->mvex = mvex;
2359 return 0;
2362 static int isom_add_mehd( isom_mvex_t *mvex )
2364 if( !mvex || mvex->mehd )
2365 return -1;
2366 isom_create_box( mehd, mvex, ISOM_BOX_TYPE_MEHD );
2367 mvex->mehd = mehd;
2368 return 0;
2371 static int isom_add_tfhd( isom_traf_entry_t *traf )
2373 if( !traf || traf->tfhd )
2374 return -1;
2375 isom_create_box( tfhd, traf, ISOM_BOX_TYPE_TFHD );
2376 traf->tfhd = tfhd;
2377 return 0;
2380 static int isom_add_mfhd( isom_moof_entry_t *moof )
2382 if( !moof || moof->mfhd )
2383 return -1;
2384 isom_create_box( mfhd, moof, ISOM_BOX_TYPE_MFHD );
2385 moof->mfhd = mfhd;
2386 return 0;
2389 static int isom_add_mfra( lsmash_root_t *root )
2391 if( !root || root->mfra )
2392 return -1;
2393 isom_create_box( mfra, root, ISOM_BOX_TYPE_MFRA );
2394 root->mfra = mfra;
2395 return 0;
2398 static int isom_add_mfro( isom_mfra_t *mfra )
2400 if( !mfra || mfra->mfro )
2401 return -1;
2402 isom_create_box( mfro, mfra, ISOM_BOX_TYPE_MFRO );
2403 mfra->mfro = mfro;
2404 return 0;
2407 #define isom_remove_box( box_name, parent_type ) \
2408 do \
2410 parent_type *parent = (parent_type *)box_name->parent; \
2411 free( box_name ); \
2412 if( parent ) \
2413 parent->box_name = NULL; \
2414 } while( 0 )
2416 static void isom_remove_ftyp( isom_ftyp_t *ftyp )
2418 if( !ftyp )
2419 return;
2420 if( ftyp->compatible_brands )
2421 free( ftyp->compatible_brands );
2422 isom_remove_box( ftyp, lsmash_root_t );
2425 static void isom_remove_tkhd( isom_tkhd_t *tkhd )
2427 if( !tkhd )
2428 return;
2429 isom_remove_box( tkhd, isom_trak_entry_t );
2432 static void isom_remove_clef( isom_clef_t *clef )
2434 if( !clef )
2435 return;
2436 isom_remove_box( clef, isom_tapt_t );
2439 static void isom_remove_prof( isom_prof_t *prof )
2441 if( !prof )
2442 return;
2443 isom_remove_box( prof, isom_tapt_t );
2446 static void isom_remove_enof( isom_enof_t *enof )
2448 if( !enof )
2449 return;
2450 isom_remove_box( enof, isom_tapt_t );
2453 void isom_remove_tapt( isom_tapt_t *tapt )
2455 if( !tapt )
2456 return;
2457 isom_remove_clef( tapt->clef );
2458 isom_remove_prof( tapt->prof );
2459 isom_remove_enof( tapt->enof );
2460 isom_remove_box( tapt, isom_trak_entry_t );
2463 static void isom_remove_elst( isom_elst_t *elst )
2465 if( !elst )
2466 return;
2467 lsmash_remove_list( elst->list, NULL );
2468 isom_remove_box( elst, isom_edts_t );
2471 static void isom_remove_edts( isom_edts_t *edts )
2473 if( !edts )
2474 return;
2475 isom_remove_elst( edts->elst );
2476 isom_remove_box( edts, isom_trak_entry_t );
2479 static void isom_remove_track_reference_type( isom_tref_type_t *ref )
2481 if( !ref )
2482 return;
2483 if( ref->track_ID )
2484 free( ref->track_ID );
2485 free( ref );
2488 static void isom_remove_tref( isom_tref_t *tref )
2490 if( !tref )
2491 return;
2492 lsmash_remove_list( tref->ref_list, isom_remove_track_reference_type );
2493 isom_remove_box( tref, isom_trak_entry_t );
2496 static void isom_remove_mdhd( isom_mdhd_t *mdhd )
2498 if( !mdhd )
2499 return;
2500 isom_remove_box( mdhd, isom_mdia_t );
2503 static void isom_remove_vmhd( isom_vmhd_t *vmhd )
2505 if( !vmhd )
2506 return;
2507 isom_remove_box( vmhd, isom_minf_t );
2510 static void isom_remove_smhd( isom_smhd_t *smhd )
2512 if( !smhd )
2513 return;
2514 isom_remove_box( smhd, isom_minf_t );
2517 static void isom_remove_hmhd( isom_hmhd_t *hmhd )
2519 if( !hmhd )
2520 return;
2521 isom_remove_box( hmhd, isom_minf_t );
2524 static void isom_remove_nmhd( isom_nmhd_t *nmhd )
2526 if( !nmhd )
2527 return;
2528 isom_remove_box( nmhd, isom_minf_t );
2531 static void isom_remove_gmin( isom_gmin_t *gmin )
2533 if( !gmin )
2534 return;
2535 isom_remove_box( gmin, isom_gmhd_t );
2538 static void isom_remove_text( isom_text_t *text )
2540 if( !text )
2541 return;
2542 isom_remove_box( text, isom_gmhd_t );
2545 static void isom_remove_gmhd( isom_gmhd_t *gmhd )
2547 if( !gmhd )
2548 return;
2549 isom_remove_gmin( gmhd->gmin );
2550 isom_remove_text( gmhd->text );
2551 isom_remove_box( gmhd, isom_minf_t );
2554 static void isom_remove_hdlr( isom_hdlr_t *hdlr )
2556 if( !hdlr )
2557 return;
2558 if( hdlr->componentName )
2559 free( hdlr->componentName );
2560 if( hdlr->parent )
2562 if( hdlr->parent->type == ISOM_BOX_TYPE_MDIA )
2563 isom_remove_box( hdlr, isom_mdia_t );
2564 else if( hdlr->parent->type == ISOM_BOX_TYPE_MINF )
2565 isom_remove_box( hdlr, isom_minf_t );
2566 else
2567 assert( 0 );
2568 return;
2570 free( hdlr );
2573 void isom_remove_clap( isom_clap_t *clap )
2575 if( !clap )
2576 return;
2577 isom_remove_box( clap, isom_visual_entry_t );
2580 void isom_remove_pasp( isom_pasp_t *pasp )
2582 if( !pasp )
2583 return;
2584 isom_remove_box( pasp, isom_visual_entry_t );
2587 void isom_remove_colr( isom_colr_t *colr )
2589 if( !colr )
2590 return;
2591 isom_remove_box( colr, isom_visual_entry_t );
2594 void isom_remove_stsl( isom_stsl_t *stsl )
2596 if( !stsl )
2597 return;
2598 isom_remove_box( stsl, isom_visual_entry_t );
2601 static void isom_remove_esds( isom_esds_t *esds )
2603 if( !esds )
2604 return;
2605 mp4sys_remove_ES_Descriptor( esds->ES );
2606 if( esds->parent )
2608 switch( esds->parent->type )
2610 case ISOM_CODEC_TYPE_MP4V_VIDEO :
2611 isom_remove_box( esds, isom_visual_entry_t );
2612 break;
2613 case ISOM_CODEC_TYPE_MP4A_AUDIO :
2614 case ISOM_CODEC_TYPE_M4AE_AUDIO :
2615 isom_remove_box( esds, isom_audio_entry_t );
2616 break;
2617 case QT_BOX_TYPE_WAVE :
2618 isom_remove_box( esds, isom_wave_t );
2619 break;
2620 case ISOM_CODEC_TYPE_MP4S_SYSTEM :
2621 isom_remove_box( esds, isom_mp4s_entry_t );
2622 break;
2623 default :
2624 assert( 0 );
2626 return;
2628 free( esds );
2631 void isom_remove_avcC( isom_avcC_t *avcC )
2633 if( !avcC )
2634 return;
2635 lsmash_remove_list( avcC->sequenceParameterSets, isom_remove_avcC_ps );
2636 lsmash_remove_list( avcC->pictureParameterSets, isom_remove_avcC_ps );
2637 lsmash_remove_list( avcC->sequenceParameterSetExt, isom_remove_avcC_ps );
2638 isom_remove_box( avcC, isom_visual_entry_t );
2641 void isom_remove_btrt( isom_btrt_t *btrt )
2643 if( !btrt )
2644 return;
2645 isom_remove_box( btrt, isom_visual_entry_t );
2648 static void isom_remove_visual_extensions( isom_visual_entry_t *visual )
2650 if( !visual )
2651 return;
2652 isom_remove_clap( visual->clap );
2653 isom_remove_pasp( visual->pasp );
2654 isom_remove_colr( visual->colr );
2655 isom_remove_stsl( visual->stsl );
2656 isom_remove_esds( visual->esds );
2657 isom_remove_avcC( visual->avcC );
2658 isom_remove_btrt( visual->btrt );
2661 static void isom_remove_font_record( isom_font_record_t *font_record )
2663 if( !font_record )
2664 return;
2665 if( font_record->font_name )
2666 free( font_record->font_name );
2667 free( font_record );
2670 void isom_remove_ftab( isom_ftab_t *ftab )
2672 if( !ftab )
2673 return;
2674 lsmash_remove_list( ftab->list, isom_remove_font_record );
2675 isom_remove_box( ftab, isom_tx3g_entry_t );
2678 void isom_remove_frma( isom_frma_t *frma )
2680 if( !frma )
2681 return;
2682 isom_remove_box( frma, isom_wave_t );
2685 void isom_remove_enda( isom_enda_t *enda )
2687 if( !enda )
2688 return;
2689 isom_remove_box( enda, isom_wave_t );
2692 void isom_remove_mp4a( isom_mp4a_t *mp4a )
2694 if( !mp4a )
2695 return;
2696 isom_remove_box( mp4a, isom_wave_t );
2699 void isom_remove_terminator( isom_terminator_t *terminator )
2701 if( !terminator )
2702 return;
2703 isom_remove_box( terminator, isom_wave_t );
2706 void isom_remove_wave( isom_wave_t *wave )
2708 if( !wave )
2709 return;
2710 isom_remove_frma( wave->frma );
2711 isom_remove_enda( wave->enda );
2712 isom_remove_mp4a( wave->mp4a );
2713 isom_remove_esds( wave->esds );
2714 isom_remove_terminator( wave->terminator );
2715 if( wave->exdata )
2716 free( wave->exdata );
2717 isom_remove_box( wave, isom_audio_entry_t );
2720 void isom_remove_chan( isom_chan_t *chan )
2722 if( !chan )
2723 return;
2724 if( chan->channelDescriptions )
2725 free( chan->channelDescriptions );
2726 isom_remove_box( chan, isom_audio_entry_t );
2729 void isom_remove_sample_description( isom_sample_entry_t *sample )
2731 if( !sample )
2732 return;
2733 switch( sample->type )
2735 case ISOM_CODEC_TYPE_AVC1_VIDEO :
2736 case ISOM_CODEC_TYPE_AVC2_VIDEO :
2737 case ISOM_CODEC_TYPE_AVCP_VIDEO :
2738 case ISOM_CODEC_TYPE_SVC1_VIDEO :
2739 case ISOM_CODEC_TYPE_MVC1_VIDEO :
2740 case ISOM_CODEC_TYPE_MVC2_VIDEO :
2741 case ISOM_CODEC_TYPE_MP4V_VIDEO :
2742 case ISOM_CODEC_TYPE_DRAC_VIDEO :
2743 case ISOM_CODEC_TYPE_ENCV_VIDEO :
2744 case ISOM_CODEC_TYPE_MJP2_VIDEO :
2745 case ISOM_CODEC_TYPE_S263_VIDEO :
2746 case ISOM_CODEC_TYPE_VC_1_VIDEO :
2748 isom_visual_entry_t *visual = (isom_visual_entry_t *)sample;
2749 isom_remove_visual_extensions( (isom_visual_entry_t *)visual );
2750 free( visual );
2751 break;
2753 case ISOM_CODEC_TYPE_MP4A_AUDIO :
2754 case ISOM_CODEC_TYPE_AC_3_AUDIO :
2755 case ISOM_CODEC_TYPE_ALAC_AUDIO :
2756 case ISOM_CODEC_TYPE_SAMR_AUDIO :
2757 case ISOM_CODEC_TYPE_SAWB_AUDIO :
2758 case QT_CODEC_TYPE_23NI_AUDIO :
2759 case QT_CODEC_TYPE_NONE_AUDIO :
2760 case QT_CODEC_TYPE_LPCM_AUDIO :
2761 case QT_CODEC_TYPE_RAW_AUDIO :
2762 case QT_CODEC_TYPE_SOWT_AUDIO :
2763 case QT_CODEC_TYPE_TWOS_AUDIO :
2764 case QT_CODEC_TYPE_FL32_AUDIO :
2765 case QT_CODEC_TYPE_FL64_AUDIO :
2766 case QT_CODEC_TYPE_IN24_AUDIO :
2767 case QT_CODEC_TYPE_IN32_AUDIO :
2768 case QT_CODEC_TYPE_NOT_SPECIFIED :
2769 case ISOM_CODEC_TYPE_DRA1_AUDIO :
2770 case ISOM_CODEC_TYPE_DTSC_AUDIO :
2771 case ISOM_CODEC_TYPE_DTSH_AUDIO :
2772 case ISOM_CODEC_TYPE_DTSL_AUDIO :
2773 case ISOM_CODEC_TYPE_EC_3_AUDIO :
2774 case ISOM_CODEC_TYPE_ENCA_AUDIO :
2775 case ISOM_CODEC_TYPE_G719_AUDIO :
2776 case ISOM_CODEC_TYPE_G726_AUDIO :
2777 case ISOM_CODEC_TYPE_M4AE_AUDIO :
2778 case ISOM_CODEC_TYPE_MLPA_AUDIO :
2779 //case ISOM_CODEC_TYPE_RAW_AUDIO :
2780 case ISOM_CODEC_TYPE_SAWP_AUDIO :
2781 case ISOM_CODEC_TYPE_SEVC_AUDIO :
2782 case ISOM_CODEC_TYPE_SQCP_AUDIO :
2783 case ISOM_CODEC_TYPE_SSMV_AUDIO :
2784 //case ISOM_CODEC_TYPE_TWOS_AUDIO :
2786 isom_audio_entry_t *audio = (isom_audio_entry_t *)sample;
2787 isom_remove_esds( audio->esds );
2788 isom_remove_wave( audio->wave );
2789 isom_remove_chan( audio->chan );
2790 if( audio->exdata )
2791 free( audio->exdata );
2792 free( audio );
2793 break;
2795 case ISOM_CODEC_TYPE_FDP_HINT :
2796 case ISOM_CODEC_TYPE_M2TS_HINT :
2797 case ISOM_CODEC_TYPE_PM2T_HINT :
2798 case ISOM_CODEC_TYPE_PRTP_HINT :
2799 case ISOM_CODEC_TYPE_RM2T_HINT :
2800 case ISOM_CODEC_TYPE_RRTP_HINT :
2801 case ISOM_CODEC_TYPE_RSRP_HINT :
2802 case ISOM_CODEC_TYPE_RTP_HINT :
2803 case ISOM_CODEC_TYPE_SM2T_HINT :
2804 case ISOM_CODEC_TYPE_SRTP_HINT :
2806 isom_hint_entry_t *hint = (isom_hint_entry_t *)sample;
2807 if( hint->data )
2808 free( hint->data );
2809 free( hint );
2810 break;
2812 case ISOM_CODEC_TYPE_IXSE_META :
2813 case ISOM_CODEC_TYPE_METT_META :
2814 case ISOM_CODEC_TYPE_METX_META :
2815 case ISOM_CODEC_TYPE_MLIX_META :
2816 case ISOM_CODEC_TYPE_OKSD_META :
2817 case ISOM_CODEC_TYPE_SVCM_META :
2818 //case ISOM_CODEC_TYPE_TEXT_META :
2819 case ISOM_CODEC_TYPE_URIM_META :
2820 case ISOM_CODEC_TYPE_XML_META :
2822 isom_metadata_entry_t *metadata = (isom_metadata_entry_t *)sample;
2823 free( metadata );
2824 break;
2826 case ISOM_CODEC_TYPE_TX3G_TEXT :
2828 isom_tx3g_entry_t *tx3g = (isom_tx3g_entry_t *)sample;
2829 if( tx3g->ftab )
2830 isom_remove_ftab( tx3g->ftab );
2831 free( tx3g );
2832 break;
2834 case QT_CODEC_TYPE_TEXT_TEXT :
2836 isom_text_entry_t *text = (isom_text_entry_t *)sample;
2837 if( text->font_name )
2838 free( text->font_name );
2839 free( text );
2840 break;
2842 case ISOM_CODEC_TYPE_MP4S_SYSTEM :
2844 isom_mp4s_entry_t *mp4s = (isom_mp4s_entry_t *)sample;
2845 isom_remove_esds( mp4s->esds );
2846 free( mp4s );
2847 break;
2849 default :
2850 break;
2854 static void isom_remove_stsd( isom_stsd_t *stsd )
2856 if( !stsd )
2857 return;
2858 lsmash_remove_list( stsd->list, isom_remove_sample_description );
2859 isom_remove_box( stsd, isom_stbl_t );
2862 static void isom_remove_stts( isom_stts_t *stts )
2864 if( !stts )
2865 return;
2866 lsmash_remove_list( stts->list, NULL );
2867 isom_remove_box( stts, isom_stbl_t );
2870 static void isom_remove_ctts( isom_ctts_t *ctts )
2872 if( !ctts )
2873 return;
2874 lsmash_remove_list( ctts->list, NULL );
2875 isom_remove_box( ctts, isom_stbl_t );
2878 static void isom_remove_cslg( isom_cslg_t *cslg )
2880 if( !cslg )
2881 return;
2882 isom_remove_box( cslg, isom_stbl_t );
2885 static void isom_remove_stsc( isom_stsc_t *stsc )
2887 if( !stsc )
2888 return;
2889 lsmash_remove_list( stsc->list, NULL );
2890 isom_remove_box( stsc, isom_stbl_t );
2893 static void isom_remove_stsz( isom_stsz_t *stsz )
2895 if( !stsz )
2896 return;
2897 lsmash_remove_list( stsz->list, NULL );
2898 isom_remove_box( stsz, isom_stbl_t );
2901 static void isom_remove_stss( isom_stss_t *stss )
2903 if( !stss )
2904 return;
2905 lsmash_remove_list( stss->list, NULL );
2906 isom_remove_box( stss, isom_stbl_t );
2909 static void isom_remove_stps( isom_stps_t *stps )
2911 if( !stps )
2912 return;
2913 lsmash_remove_list( stps->list, NULL );
2914 isom_remove_box( stps, isom_stbl_t );
2917 static void isom_remove_sdtp( isom_sdtp_t *sdtp )
2919 if( !sdtp )
2920 return;
2921 lsmash_remove_list( sdtp->list, NULL );
2922 isom_remove_box( sdtp, isom_stbl_t );
2925 static void isom_remove_stco( isom_stco_t *stco )
2927 if( !stco )
2928 return;
2929 lsmash_remove_list( stco->list, NULL );
2930 isom_remove_box( stco, isom_stbl_t );
2933 static void isom_remove_sgpd( isom_sgpd_entry_t *sgpd )
2935 if( !sgpd )
2936 return;
2937 lsmash_remove_list( sgpd->list, NULL );
2938 free( sgpd );
2941 static void isom_remove_sbgp( isom_sbgp_entry_t *sbgp )
2943 if( !sbgp )
2944 return;
2945 lsmash_remove_list( sbgp->list, NULL );
2946 free( sbgp );
2949 static void isom_remove_stbl( isom_stbl_t *stbl )
2951 if( !stbl )
2952 return;
2953 isom_remove_stsd( stbl->stsd );
2954 isom_remove_stts( stbl->stts );
2955 isom_remove_ctts( stbl->ctts );
2956 isom_remove_cslg( stbl->cslg );
2957 isom_remove_stsc( stbl->stsc );
2958 isom_remove_stsz( stbl->stsz );
2959 isom_remove_stss( stbl->stss );
2960 isom_remove_stps( stbl->stps );
2961 isom_remove_sdtp( stbl->sdtp );
2962 isom_remove_stco( stbl->stco );
2963 lsmash_remove_list( stbl->sgpd_list, isom_remove_sgpd );
2964 lsmash_remove_list( stbl->sbgp_list, isom_remove_sbgp );
2965 isom_remove_box( stbl, isom_minf_t );
2968 static void isom_remove_dref( isom_dref_t *dref )
2970 if( !dref )
2971 return;
2972 if( !dref->list )
2974 free( dref );
2975 return;
2977 for( lsmash_entry_t *entry = dref->list->head; entry; )
2979 isom_dref_entry_t *data = (isom_dref_entry_t *)entry->data;
2980 if( data )
2982 if( data->name )
2983 free( data->name );
2984 if( data->location )
2985 free( data->location );
2986 free( data );
2988 lsmash_entry_t *next = entry->next;
2989 free( entry );
2990 entry = next;
2992 free( dref->list );
2993 isom_remove_box( dref, isom_dinf_t );
2996 static void isom_remove_dinf( isom_dinf_t *dinf )
2998 if( !dinf )
2999 return;
3000 isom_remove_dref( dinf->dref );
3001 isom_remove_box( dinf, isom_minf_t );
3004 static void isom_remove_minf( isom_minf_t *minf )
3006 if( !minf )
3007 return;
3008 isom_remove_vmhd( minf->vmhd );
3009 isom_remove_smhd( minf->smhd );
3010 isom_remove_hmhd( minf->hmhd );
3011 isom_remove_nmhd( minf->nmhd );
3012 isom_remove_gmhd( minf->gmhd );
3013 isom_remove_hdlr( minf->hdlr );
3014 isom_remove_dinf( minf->dinf );
3015 isom_remove_stbl( minf->stbl );
3016 isom_remove_box( minf, isom_mdia_t );
3019 static void isom_remove_mdia( isom_mdia_t *mdia )
3021 if( !mdia )
3022 return;
3023 isom_remove_mdhd( mdia->mdhd );
3024 isom_remove_minf( mdia->minf );
3025 isom_remove_hdlr( mdia->hdlr );
3026 isom_remove_box( mdia, isom_trak_entry_t );
3029 static void isom_remove_chpl( isom_chpl_t *chpl )
3031 if( !chpl )
3032 return;
3033 if( !chpl->list )
3035 free( chpl );
3036 return;
3038 for( lsmash_entry_t *entry = chpl->list->head; entry; )
3040 isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data;
3041 if( data )
3043 if( data->chapter_name )
3044 free( data->chapter_name );
3045 free( data );
3047 lsmash_entry_t *next = entry->next;
3048 free( entry );
3049 entry = next;
3051 free( chpl->list );
3052 isom_remove_box( chpl, isom_udta_t );
3055 static void isom_remove_udta( isom_udta_t *udta )
3057 if( !udta )
3058 return;
3059 isom_remove_chpl( udta->chpl );
3060 if( udta->parent )
3062 if( udta->parent->type == ISOM_BOX_TYPE_MOOV )
3063 isom_remove_box( udta, isom_moov_t );
3064 else if( udta->parent->type == ISOM_BOX_TYPE_TRAK )
3065 isom_remove_box( udta, isom_trak_entry_t );
3066 else
3067 assert( 0 );
3068 return;
3070 free( udta );
3073 static void isom_remove_trak( isom_trak_entry_t *trak )
3075 if( !trak )
3076 return;
3077 isom_remove_tkhd( trak->tkhd );
3078 isom_remove_tapt( trak->tapt );
3079 isom_remove_edts( trak->edts );
3080 isom_remove_tref( trak->tref );
3081 isom_remove_mdia( trak->mdia );
3082 isom_remove_udta( trak->udta );
3083 if( trak->cache )
3085 lsmash_remove_list( trak->cache->chunk.pool, lsmash_delete_sample );
3086 lsmash_remove_list( trak->cache->roll.pool, NULL );
3087 if( trak->cache->rap )
3088 free( trak->cache->rap );
3089 free( trak->cache );
3091 free( trak ); /* Note: the list that contains this trak still has the address of the entry. */
3094 static void isom_remove_iods( isom_iods_t *iods )
3096 if( !iods )
3097 return;
3098 mp4sys_remove_ObjectDescriptor( iods->OD );
3099 isom_remove_box( iods, isom_moov_t );
3102 static void isom_remove_mehd( isom_mehd_t *mehd )
3104 if( !mehd )
3105 return;
3106 isom_remove_box( mehd, isom_mvex_t );
3109 static void isom_remove_mvex( isom_mvex_t *mvex )
3111 if( !mvex )
3112 return;
3113 isom_remove_mehd( mvex->mehd );
3114 lsmash_remove_list( mvex->trex_list, NULL );
3115 isom_remove_box( mvex, isom_moov_t );
3118 static void isom_remove_moov( lsmash_root_t *root )
3120 if( !root || !root->moov )
3121 return;
3122 isom_moov_t *moov = root->moov;
3123 if( moov->mvhd )
3124 free( moov->mvhd );
3125 isom_remove_iods( moov->iods );
3126 isom_remove_udta( moov->udta );
3127 lsmash_remove_list( moov->trak_list, isom_remove_trak );
3128 isom_remove_mvex( moov->mvex );
3129 free( moov );
3130 root->moov = NULL;
3133 static void isom_remove_mfhd( isom_mfhd_t *mfhd )
3135 if( !mfhd )
3136 return;
3137 isom_remove_box( mfhd, isom_moof_entry_t );
3140 static void isom_remove_tfhd( isom_tfhd_t *tfhd )
3142 if( !tfhd )
3143 return;
3144 isom_remove_box( tfhd, isom_traf_entry_t );
3147 static void isom_remove_trun( isom_trun_entry_t *trun )
3149 if( !trun )
3150 return;
3151 lsmash_remove_list( trun->optional, NULL );
3152 free( trun ); /* Note: the list that contains this trun still has the address of the entry. */
3155 static void isom_remove_traf( isom_traf_entry_t *traf )
3157 if( !traf )
3158 return;
3159 isom_remove_tfhd( traf->tfhd );
3160 lsmash_remove_list( traf->trun_list, isom_remove_trun );
3161 free( traf ); /* Note: the list that contains this traf still has the address of the entry. */
3164 static void isom_remove_moof( isom_moof_entry_t *moof )
3166 if( !moof )
3167 return;
3168 isom_remove_mfhd( moof->mfhd );
3169 lsmash_remove_list( moof->traf_list, isom_remove_traf );
3170 free( moof );
3173 static void isom_remove_mdat( isom_mdat_t *mdat )
3175 if( !mdat )
3176 return;
3177 isom_remove_box( mdat, lsmash_root_t );
3180 static void isom_remove_free( isom_free_t *skip )
3182 if( !skip )
3183 return;
3184 if( skip->data )
3185 free( skip->data );
3186 lsmash_root_t *root = (lsmash_root_t *)skip->parent;
3187 free( skip );
3188 root->free = NULL;
3191 static void isom_remove_tfra( isom_tfra_entry_t *tfra )
3193 if( !tfra )
3194 return;
3195 lsmash_remove_list( tfra->list, NULL );
3196 free( tfra );
3199 static void isom_remove_mfro( isom_mfro_t *mfro )
3201 if( !mfro )
3202 return;
3203 isom_remove_box( mfro, isom_mfra_t );
3206 static void isom_remove_mfra( isom_mfra_t *mfra )
3208 if( !mfra )
3209 return;
3210 lsmash_remove_list( mfra->tfra_list, isom_remove_tfra );
3211 isom_remove_mfro( mfra->mfro );
3212 isom_remove_box( mfra, lsmash_root_t );
3215 /* Box writers */
3216 static int isom_write_tkhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3218 isom_tkhd_t *tkhd = trak->tkhd;
3219 if( !tkhd )
3220 return -1;
3221 isom_bs_put_box_common( bs, tkhd );
3222 if( tkhd->version )
3224 lsmash_bs_put_be64( bs, tkhd->creation_time );
3225 lsmash_bs_put_be64( bs, tkhd->modification_time );
3226 lsmash_bs_put_be32( bs, tkhd->track_ID );
3227 lsmash_bs_put_be32( bs, tkhd->reserved1 );
3228 lsmash_bs_put_be64( bs, tkhd->duration );
3230 else
3232 lsmash_bs_put_be32( bs, (uint32_t)tkhd->creation_time );
3233 lsmash_bs_put_be32( bs, (uint32_t)tkhd->modification_time );
3234 lsmash_bs_put_be32( bs, tkhd->track_ID );
3235 lsmash_bs_put_be32( bs, tkhd->reserved1 );
3236 lsmash_bs_put_be32( bs, (uint32_t)tkhd->duration );
3238 lsmash_bs_put_be32( bs, tkhd->reserved2[0] );
3239 lsmash_bs_put_be32( bs, tkhd->reserved2[1] );
3240 lsmash_bs_put_be16( bs, tkhd->layer );
3241 lsmash_bs_put_be16( bs, tkhd->alternate_group );
3242 lsmash_bs_put_be16( bs, tkhd->volume );
3243 lsmash_bs_put_be16( bs, tkhd->reserved3 );
3244 for( uint32_t i = 0; i < 9; i++ )
3245 lsmash_bs_put_be32( bs, tkhd->matrix[i] );
3246 lsmash_bs_put_be32( bs, tkhd->width );
3247 lsmash_bs_put_be32( bs, tkhd->height );
3248 return lsmash_bs_write_data( bs );
3251 static int isom_write_clef( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3253 isom_clef_t *clef = trak->tapt->clef;
3254 if( !clef )
3255 return 0;
3256 isom_bs_put_box_common( bs, clef );
3257 lsmash_bs_put_be32( bs, clef->width );
3258 lsmash_bs_put_be32( bs, clef->height );
3259 return lsmash_bs_write_data( bs );
3262 static int isom_write_prof( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3264 isom_prof_t *prof = trak->tapt->prof;
3265 if( !prof )
3266 return 0;
3267 isom_bs_put_box_common( bs, prof );
3268 lsmash_bs_put_be32( bs, prof->width );
3269 lsmash_bs_put_be32( bs, prof->height );
3270 return lsmash_bs_write_data( bs );
3273 static int isom_write_enof( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3275 isom_enof_t *enof = trak->tapt->enof;
3276 if( !enof )
3277 return 0;
3278 isom_bs_put_box_common( bs, enof );
3279 lsmash_bs_put_be32( bs, enof->width );
3280 lsmash_bs_put_be32( bs, enof->height );
3281 return lsmash_bs_write_data( bs );
3284 static int isom_write_tapt( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3286 isom_tapt_t *tapt = trak->tapt;
3287 if( !tapt )
3288 return 0;
3289 isom_bs_put_box_common( bs, tapt );
3290 if( lsmash_bs_write_data( bs ) )
3291 return -1;
3292 if( isom_write_clef( bs, trak )
3293 || isom_write_prof( bs, trak )
3294 || isom_write_enof( bs, trak ) )
3295 return -1;
3296 return 0;
3299 static int isom_write_elst( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3301 isom_elst_t *elst = trak->edts->elst;
3302 if( !elst )
3303 return -1;
3304 if( !elst->list->entry_count )
3305 return 0;
3306 if( elst->root->fragment && elst->root->bs->stream != stdout )
3307 elst->pos = elst->root->bs->written; /* Remember to rewrite entries. */
3308 isom_bs_put_box_common( bs, elst );
3309 lsmash_bs_put_be32( bs, elst->list->entry_count );
3310 for( lsmash_entry_t *entry = elst->list->head; entry; entry = entry->next )
3312 isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data;
3313 if( !data )
3314 return -1;
3315 if( elst->version )
3317 lsmash_bs_put_be64( bs, data->segment_duration );
3318 lsmash_bs_put_be64( bs, data->media_time );
3320 else
3322 lsmash_bs_put_be32( bs, (uint32_t)data->segment_duration );
3323 lsmash_bs_put_be32( bs, (uint32_t)data->media_time );
3325 lsmash_bs_put_be32( bs, data->media_rate );
3327 return lsmash_bs_write_data( bs );
3330 static int isom_write_edts( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3332 isom_edts_t *edts = trak->edts;
3333 if( !edts )
3334 return 0;
3335 isom_bs_put_box_common( bs, edts );
3336 if( lsmash_bs_write_data( bs ) )
3337 return -1;
3338 return isom_write_elst( bs, trak );
3341 static int isom_write_tref( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3343 isom_tref_t *tref = trak->tref;
3344 if( !tref )
3345 return 0;
3346 isom_bs_put_box_common( bs, tref );
3347 if( tref->ref_list )
3348 for( lsmash_entry_t *entry = tref->ref_list->head; entry; entry = entry->next )
3350 isom_tref_type_t *ref = (isom_tref_type_t *)entry->data;
3351 if( !ref )
3352 return -1;
3353 isom_bs_put_box_common( bs, ref );
3354 for( uint32_t i = 0; i < ref->ref_count; i++ )
3355 lsmash_bs_put_be32( bs, ref->track_ID[i] );
3357 return lsmash_bs_write_data( bs );
3360 static int isom_write_mdhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3362 isom_mdhd_t *mdhd = trak->mdia->mdhd;
3363 if( !mdhd )
3364 return -1;
3365 isom_bs_put_box_common( bs, mdhd );
3366 if( mdhd->version )
3368 lsmash_bs_put_be64( bs, mdhd->creation_time );
3369 lsmash_bs_put_be64( bs, mdhd->modification_time );
3370 lsmash_bs_put_be32( bs, mdhd->timescale );
3371 lsmash_bs_put_be64( bs, mdhd->duration );
3373 else
3375 lsmash_bs_put_be32( bs, (uint32_t)mdhd->creation_time );
3376 lsmash_bs_put_be32( bs, (uint32_t)mdhd->modification_time );
3377 lsmash_bs_put_be32( bs, mdhd->timescale );
3378 lsmash_bs_put_be32( bs, (uint32_t)mdhd->duration );
3380 lsmash_bs_put_be16( bs, mdhd->language );
3381 lsmash_bs_put_be16( bs, mdhd->quality );
3382 return lsmash_bs_write_data( bs );
3385 static int isom_write_hdlr( lsmash_bs_t *bs, isom_trak_entry_t *trak, uint8_t is_media_handler )
3387 isom_hdlr_t *hdlr = is_media_handler ? trak->mdia->hdlr : trak->mdia->minf->hdlr;
3388 if( !hdlr )
3389 return 0;
3390 isom_bs_put_box_common( bs, hdlr );
3391 lsmash_bs_put_be32( bs, hdlr->componentType );
3392 lsmash_bs_put_be32( bs, hdlr->componentSubtype );
3393 lsmash_bs_put_be32( bs, hdlr->componentManufacturer );
3394 lsmash_bs_put_be32( bs, hdlr->componentFlags );
3395 lsmash_bs_put_be32( bs, hdlr->componentFlagsMask );
3396 lsmash_bs_put_bytes( bs, hdlr->componentName, hdlr->componentName_length );
3397 return lsmash_bs_write_data( bs );
3400 static int isom_write_vmhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3402 isom_vmhd_t *vmhd = trak->mdia->minf->vmhd;
3403 if( !vmhd )
3404 return -1;
3405 isom_bs_put_box_common( bs, vmhd );
3406 lsmash_bs_put_be16( bs, vmhd->graphicsmode );
3407 for( uint32_t i = 0; i < 3; i++ )
3408 lsmash_bs_put_be16( bs, vmhd->opcolor[i] );
3409 return lsmash_bs_write_data( bs );
3412 static int isom_write_smhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3414 isom_smhd_t *smhd = trak->mdia->minf->smhd;
3415 if( !smhd )
3416 return -1;
3417 isom_bs_put_box_common( bs, smhd );
3418 lsmash_bs_put_be16( bs, smhd->balance );
3419 lsmash_bs_put_be16( bs, smhd->reserved );
3420 return lsmash_bs_write_data( bs );
3423 static int isom_write_hmhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3425 isom_hmhd_t *hmhd = trak->mdia->minf->hmhd;
3426 if( !hmhd )
3427 return -1;
3428 isom_bs_put_box_common( bs, hmhd );
3429 lsmash_bs_put_be16( bs, hmhd->maxPDUsize );
3430 lsmash_bs_put_be16( bs, hmhd->avgPDUsize );
3431 lsmash_bs_put_be32( bs, hmhd->maxbitrate );
3432 lsmash_bs_put_be32( bs, hmhd->avgbitrate );
3433 lsmash_bs_put_be32( bs, hmhd->reserved );
3434 return lsmash_bs_write_data( bs );
3437 static int isom_write_nmhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3439 isom_nmhd_t *nmhd = trak->mdia->minf->nmhd;
3440 if( !nmhd )
3441 return -1;
3442 isom_bs_put_box_common( bs, nmhd );
3443 return lsmash_bs_write_data( bs );
3446 static int isom_write_gmin( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3448 isom_gmin_t *gmin = trak->mdia->minf->gmhd->gmin;
3449 if( !gmin )
3450 return -1;
3451 isom_bs_put_box_common( bs, gmin );
3452 lsmash_bs_put_be16( bs, gmin->graphicsmode );
3453 for( uint32_t i = 0; i < 3; i++ )
3454 lsmash_bs_put_be16( bs, gmin->opcolor[i] );
3455 lsmash_bs_put_be16( bs, gmin->balance );
3456 lsmash_bs_put_be16( bs, gmin->reserved );
3457 return lsmash_bs_write_data( bs );
3460 static int isom_write_text( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3462 isom_text_t *text = trak->mdia->minf->gmhd->text;
3463 if( !text )
3464 return -1;
3465 isom_bs_put_box_common( bs, text );
3466 for( uint32_t i = 0; i < 9; i++ )
3467 lsmash_bs_put_be32( bs, text->matrix[i] );
3468 return lsmash_bs_write_data( bs );
3471 static int isom_write_gmhd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3473 isom_gmhd_t *gmhd = trak->mdia->minf->gmhd;
3474 if( !gmhd )
3475 return -1;
3476 isom_bs_put_box_common( bs, gmhd );
3477 if( isom_write_gmin( bs, trak ) ||
3478 isom_write_text( bs, trak ) )
3479 return -1;
3480 return 0;
3483 static int isom_write_dref( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3485 isom_dref_t *dref = trak->mdia->minf->dinf->dref;
3486 if( !dref || !dref->list )
3487 return -1;
3488 isom_bs_put_box_common( bs, dref );
3489 lsmash_bs_put_be32( bs, dref->list->entry_count );
3490 for( lsmash_entry_t *entry = dref->list->head; entry; entry = entry->next )
3492 isom_dref_entry_t *data = (isom_dref_entry_t *)entry->data;
3493 if( !data )
3494 return -1;
3495 isom_bs_put_box_common( bs, data );
3496 if( data->type == ISOM_BOX_TYPE_URN )
3497 lsmash_bs_put_bytes( bs, data->name, data->name_length );
3498 lsmash_bs_put_bytes( bs, data->location, data->location_length );
3500 return lsmash_bs_write_data( bs );
3503 static int isom_write_dinf( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3505 isom_dinf_t *dinf = trak->mdia->minf->dinf;
3506 if( !dinf )
3507 return -1;
3508 isom_bs_put_box_common( bs, dinf );
3509 if( lsmash_bs_write_data( bs ) )
3510 return -1;
3511 return isom_write_dref( bs, trak );
3514 static int isom_write_pasp( lsmash_bs_t *bs, isom_pasp_t *pasp )
3516 if( !pasp )
3517 return 0;
3518 isom_bs_put_box_common( bs, pasp );
3519 lsmash_bs_put_be32( bs, pasp->hSpacing );
3520 lsmash_bs_put_be32( bs, pasp->vSpacing );
3521 return lsmash_bs_write_data( bs );
3524 static int isom_write_clap( lsmash_bs_t *bs, isom_clap_t *clap )
3526 if( !clap )
3527 return 0;
3528 isom_bs_put_box_common( bs, clap );
3529 lsmash_bs_put_be32( bs, clap->cleanApertureWidthN );
3530 lsmash_bs_put_be32( bs, clap->cleanApertureWidthD );
3531 lsmash_bs_put_be32( bs, clap->cleanApertureHeightN );
3532 lsmash_bs_put_be32( bs, clap->cleanApertureHeightD );
3533 lsmash_bs_put_be32( bs, clap->horizOffN );
3534 lsmash_bs_put_be32( bs, clap->horizOffD );
3535 lsmash_bs_put_be32( bs, clap->vertOffN );
3536 lsmash_bs_put_be32( bs, clap->vertOffD );
3537 return lsmash_bs_write_data( bs );
3540 static int isom_write_colr( lsmash_bs_t *bs, isom_colr_t *colr )
3542 if( !colr || colr->color_parameter_type == QT_COLOR_PARAMETER_TYPE_PROF )
3543 return 0;
3544 isom_bs_put_box_common( bs, colr );
3545 lsmash_bs_put_be32( bs, colr->color_parameter_type );
3546 lsmash_bs_put_be16( bs, colr->primaries_index );
3547 lsmash_bs_put_be16( bs, colr->transfer_function_index );
3548 lsmash_bs_put_be16( bs, colr->matrix_index );
3549 return lsmash_bs_write_data( bs );
3552 static int isom_write_stsl( lsmash_bs_t *bs, isom_stsl_t *stsl )
3554 if( !stsl )
3555 return 0;
3556 isom_bs_put_box_common( bs, stsl );
3557 lsmash_bs_put_byte( bs, stsl->constraint_flag );
3558 lsmash_bs_put_byte( bs, stsl->scale_method );
3559 lsmash_bs_put_be16( bs, stsl->display_center_x );
3560 lsmash_bs_put_be16( bs, stsl->display_center_y );
3561 return lsmash_bs_write_data( bs );
3564 static int isom_write_esds( lsmash_bs_t *bs, isom_esds_t *esds )
3566 if( !esds )
3567 return 0;
3568 isom_bs_put_box_common( bs, esds );
3569 return mp4sys_write_ES_Descriptor( bs, esds->ES );
3572 static int isom_put_ps_entries( lsmash_bs_t *bs, lsmash_entry_list_t *list )
3574 for( lsmash_entry_t *entry = list->head; entry; entry = entry->next )
3576 isom_avcC_ps_entry_t *data = (isom_avcC_ps_entry_t *)entry->data;
3577 if( !data )
3578 return -1;
3579 lsmash_bs_put_be16( bs, data->parameterSetLength );
3580 lsmash_bs_put_bytes( bs, data->parameterSetNALUnit, data->parameterSetLength );
3582 return 0;
3585 static int isom_write_avcC( lsmash_bs_t *bs, isom_avcC_t *avcC )
3587 if( !bs || !avcC || !avcC->sequenceParameterSets || !avcC->pictureParameterSets )
3588 return -1;
3589 isom_bs_put_box_common( bs, avcC );
3590 lsmash_bs_put_byte( bs, avcC->configurationVersion );
3591 lsmash_bs_put_byte( bs, avcC->AVCProfileIndication );
3592 lsmash_bs_put_byte( bs, avcC->profile_compatibility );
3593 lsmash_bs_put_byte( bs, avcC->AVCLevelIndication );
3594 lsmash_bs_put_byte( bs, avcC->lengthSizeMinusOne | 0xfc ); /* upper 6-bits are reserved as 111111b */
3595 lsmash_bs_put_byte( bs, avcC->numOfSequenceParameterSets | 0xe0 ); /* upper 3-bits are reserved as 111b */
3596 if( isom_put_ps_entries( bs, avcC->sequenceParameterSets ) )
3597 return -1;
3598 lsmash_bs_put_byte( bs, avcC->numOfPictureParameterSets );
3599 if( isom_put_ps_entries( bs, avcC->pictureParameterSets ) )
3600 return -1;
3601 if( ISOM_REQUIRES_AVCC_EXTENSION( avcC->AVCProfileIndication ) )
3603 lsmash_bs_put_byte( bs, avcC->chroma_format | 0xfc ); /* upper 6-bits are reserved as 111111b */
3604 lsmash_bs_put_byte( bs, avcC->bit_depth_luma_minus8 | 0xf8 ); /* upper 5-bits are reserved as 11111b */
3605 lsmash_bs_put_byte( bs, avcC->bit_depth_chroma_minus8 | 0xf8 ); /* upper 5-bits are reserved as 11111b */
3606 lsmash_bs_put_byte( bs, avcC->numOfSequenceParameterSetExt );
3607 if( isom_put_ps_entries( bs, avcC->sequenceParameterSetExt ) )
3608 return -1;
3610 return lsmash_bs_write_data( bs );
3613 static int isom_write_btrt( lsmash_bs_t *bs, isom_btrt_t *btrt )
3615 if( !btrt )
3616 return 0;
3617 isom_bs_put_box_common( bs, btrt );
3618 lsmash_bs_put_be32( bs, btrt->bufferSizeDB );
3619 lsmash_bs_put_be32( bs, btrt->maxBitrate );
3620 lsmash_bs_put_be32( bs, btrt->avgBitrate );
3621 return lsmash_bs_write_data( bs );
3624 static int isom_write_visual_extensions( lsmash_bs_t *bs, isom_visual_entry_t *visual )
3626 if( !visual )
3627 return 0;
3628 if( isom_write_clap( bs, visual->clap )
3629 || isom_write_pasp( bs, visual->pasp )
3630 || isom_write_colr( bs, visual->colr )
3631 || isom_write_stsl( bs, visual->stsl )
3632 || isom_write_esds( bs, visual->esds )
3633 || isom_write_avcC( bs, visual->avcC )
3634 || isom_write_btrt( bs, visual->btrt ) )
3635 return -1;
3636 return 0;
3639 static int isom_write_frma( lsmash_bs_t *bs, isom_frma_t *frma )
3641 if( !frma )
3642 return -1;
3643 isom_bs_put_box_common( bs, frma );
3644 lsmash_bs_put_be32( bs, frma->data_format );
3645 return lsmash_bs_write_data( bs );
3648 static int isom_write_enda( lsmash_bs_t *bs, isom_enda_t *enda )
3650 if( !enda )
3651 return 0;
3652 isom_bs_put_box_common( bs, enda );
3653 lsmash_bs_put_be16( bs, enda->littleEndian );
3654 return lsmash_bs_write_data( bs );
3657 static int isom_write_mp4a( lsmash_bs_t *bs, isom_mp4a_t *mp4a )
3659 if( !mp4a )
3660 return 0;
3661 isom_bs_put_box_common( bs, mp4a );
3662 lsmash_bs_put_be32( bs, mp4a->unknown );
3663 return lsmash_bs_write_data( bs );
3666 static int isom_write_terminator( lsmash_bs_t *bs, isom_terminator_t *terminator )
3668 if( !terminator )
3669 return -1;
3670 isom_bs_put_box_common( bs, terminator );
3671 return lsmash_bs_write_data( bs );
3674 static int isom_write_wave( lsmash_bs_t *bs, isom_wave_t *wave )
3676 if( !wave )
3677 return 0;
3678 isom_bs_put_box_common( bs, wave );
3679 if( lsmash_bs_write_data( bs ) )
3680 return -1;
3681 if( isom_write_frma( bs, wave->frma )
3682 || isom_write_enda( bs, wave->enda )
3683 || isom_write_mp4a( bs, wave->mp4a ) )
3684 return -1;
3685 lsmash_bs_put_bytes( bs, wave->exdata, wave->exdata_length );
3686 if( lsmash_bs_write_data( bs ) )
3687 return -1;
3688 if( isom_write_esds( bs, wave->esds ) )
3689 return -1;
3690 return isom_write_terminator( bs, wave->terminator );
3693 static int isom_write_chan( lsmash_bs_t *bs, isom_chan_t *chan )
3695 if( !chan )
3696 return 0;
3697 isom_bs_put_box_common( bs, chan );
3698 lsmash_bs_put_be32( bs, chan->channelLayoutTag );
3699 lsmash_bs_put_be32( bs, chan->channelBitmap );
3700 lsmash_bs_put_be32( bs, chan->numberChannelDescriptions );
3701 if( chan->channelDescriptions )
3702 for( uint32_t i = 0; i < chan->numberChannelDescriptions; i++ )
3704 isom_channel_description_t *channelDescriptions = (isom_channel_description_t *)(&chan->channelDescriptions[i]);
3705 if( !channelDescriptions )
3706 return -1;
3707 lsmash_bs_put_be32( bs, channelDescriptions->channelLabel );
3708 lsmash_bs_put_be32( bs, channelDescriptions->channelFlags );
3709 lsmash_bs_put_be32( bs, channelDescriptions->coordinates[0] );
3710 lsmash_bs_put_be32( bs, channelDescriptions->coordinates[1] );
3711 lsmash_bs_put_be32( bs, channelDescriptions->coordinates[2] );
3713 return lsmash_bs_write_data( bs );
3716 static int isom_write_audio_extensions( lsmash_bs_t *bs, isom_audio_entry_t *audio )
3718 if( !audio )
3719 return 0;
3720 if( isom_write_esds( bs, audio->esds )
3721 || isom_write_wave( bs, audio->wave )
3722 || isom_write_chan( bs, audio->chan ) )
3723 return -1;
3724 return 0;
3727 static int isom_write_visual_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
3729 isom_visual_entry_t *data = (isom_visual_entry_t *)entry->data;
3730 if( !data )
3731 return -1;
3732 isom_bs_put_box_common( bs, data );
3733 lsmash_bs_put_bytes( bs, data->reserved, 6 );
3734 lsmash_bs_put_be16( bs, data->data_reference_index );
3735 lsmash_bs_put_be16( bs, data->version );
3736 lsmash_bs_put_be16( bs, data->revision_level );
3737 lsmash_bs_put_be32( bs, data->vendor );
3738 lsmash_bs_put_be32( bs, data->temporalQuality );
3739 lsmash_bs_put_be32( bs, data->spatialQuality );
3740 lsmash_bs_put_be16( bs, data->width );
3741 lsmash_bs_put_be16( bs, data->height );
3742 lsmash_bs_put_be32( bs, data->horizresolution );
3743 lsmash_bs_put_be32( bs, data->vertresolution );
3744 lsmash_bs_put_be32( bs, data->dataSize );
3745 lsmash_bs_put_be16( bs, data->frame_count );
3746 lsmash_bs_put_bytes( bs, data->compressorname, 32 );
3747 lsmash_bs_put_be16( bs, data->depth );
3748 lsmash_bs_put_be16( bs, data->color_table_ID );
3749 if( lsmash_bs_write_data( bs ) )
3750 return -1;
3751 return isom_write_visual_extensions( bs, data );
3754 static int isom_write_audio_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
3756 isom_audio_entry_t *data = (isom_audio_entry_t *)entry->data;
3757 if( !data )
3758 return -1;
3759 isom_bs_put_box_common( bs, data );
3760 lsmash_bs_put_bytes( bs, data->reserved, 6 );
3761 lsmash_bs_put_be16( bs, data->data_reference_index );
3762 lsmash_bs_put_be16( bs, data->version );
3763 lsmash_bs_put_be16( bs, data->revision_level );
3764 lsmash_bs_put_be32( bs, data->vendor );
3765 lsmash_bs_put_be16( bs, data->channelcount );
3766 lsmash_bs_put_be16( bs, data->samplesize );
3767 lsmash_bs_put_be16( bs, data->compression_ID );
3768 lsmash_bs_put_be16( bs, data->packet_size );
3769 lsmash_bs_put_be32( bs, data->samplerate );
3770 if( data->version == 1 )
3772 lsmash_bs_put_be32( bs, data->samplesPerPacket );
3773 lsmash_bs_put_be32( bs, data->bytesPerPacket );
3774 lsmash_bs_put_be32( bs, data->bytesPerFrame );
3775 lsmash_bs_put_be32( bs, data->bytesPerSample );
3777 else if( data->version == 2 )
3779 lsmash_bs_put_be32( bs, data->sizeOfStructOnly );
3780 lsmash_bs_put_be64( bs, data->audioSampleRate );
3781 lsmash_bs_put_be32( bs, data->numAudioChannels );
3782 lsmash_bs_put_be32( bs, data->always7F000000 );
3783 lsmash_bs_put_be32( bs, data->constBitsPerChannel );
3784 lsmash_bs_put_be32( bs, data->formatSpecificFlags );
3785 lsmash_bs_put_be32( bs, data->constBytesPerAudioPacket );
3786 lsmash_bs_put_be32( bs, data->constLPCMFramesPerAudioPacket );
3788 lsmash_bs_put_bytes( bs, data->exdata, data->exdata_length );
3789 if( lsmash_bs_write_data( bs ) )
3790 return -1;
3791 return isom_write_audio_extensions( bs, data );
3794 #if 0
3795 static int isom_write_hint_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
3797 isom_hint_entry_t *data = (isom_hint_entry_t *)entry->data;
3798 if( !data )
3799 return -1;
3800 isom_bs_put_box_common( bs, data );
3801 lsmash_bs_put_bytes( bs, data->reserved, 6 );
3802 lsmash_bs_put_be16( bs, data->data_reference_index );
3803 if( data->data && data->data_length )
3804 lsmash_bs_put_bytes( bs, data->data, data->data_length );
3805 return lsmash_bs_write_data( bs );
3808 static int isom_write_metadata_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
3810 isom_metadata_entry_t *data = (isom_metadata_entry_t *)entry->data;
3811 if( !data )
3812 return -1;
3813 isom_bs_put_box_common( bs, data );
3814 lsmash_bs_put_bytes( bs, data->reserved, 6 );
3815 lsmash_bs_put_be16( bs, data->data_reference_index );
3816 return lsmash_bs_write_data( bs );
3818 #endif
3820 static int isom_write_text_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
3822 isom_text_entry_t *data = (isom_text_entry_t *)entry->data;
3823 if( !data )
3824 return -1;
3825 isom_bs_put_box_common( bs, data );
3826 lsmash_bs_put_bytes( bs, data->reserved, 6 );
3827 lsmash_bs_put_be16( bs, data->data_reference_index );
3828 lsmash_bs_put_be32( bs, data->displayFlags );
3829 lsmash_bs_put_be32( bs, data->textJustification );
3830 for( uint32_t i = 0; i < 3; i++ )
3831 lsmash_bs_put_be16( bs, data->bgColor[i] );
3832 lsmash_bs_put_be16( bs, data->top );
3833 lsmash_bs_put_be16( bs, data->left );
3834 lsmash_bs_put_be16( bs, data->bottom );
3835 lsmash_bs_put_be16( bs, data->right );
3836 lsmash_bs_put_be32( bs, data->scrpStartChar );
3837 lsmash_bs_put_be16( bs, data->scrpHeight );
3838 lsmash_bs_put_be16( bs, data->scrpAscent );
3839 lsmash_bs_put_be16( bs, data->scrpFont );
3840 lsmash_bs_put_be16( bs, data->scrpFace );
3841 lsmash_bs_put_be16( bs, data->scrpSize );
3842 for( uint32_t i = 0; i < 3; i++ )
3843 lsmash_bs_put_be16( bs, data->scrpColor[i] );
3844 lsmash_bs_put_byte( bs, data->font_name_length );
3845 if( data->font_name && data->font_name_length )
3846 lsmash_bs_put_bytes( bs, data->font_name, data->font_name_length );
3847 return lsmash_bs_write_data( bs );
3850 static int isom_put_ftab( lsmash_bs_t *bs, isom_ftab_t *ftab )
3852 if( !ftab || !ftab->list )
3853 return -1;
3854 isom_bs_put_box_common( bs, ftab );
3855 lsmash_bs_put_be16( bs, ftab->list->entry_count );
3856 for( lsmash_entry_t *entry = ftab->list->head; entry; entry = entry->next )
3858 isom_font_record_t *data = (isom_font_record_t *)entry->data;
3859 if( !data )
3860 return -1;
3861 lsmash_bs_put_be16( bs, data->font_ID );
3862 lsmash_bs_put_byte( bs, data->font_name_length );
3863 if( data->font_name && data->font_name_length )
3864 lsmash_bs_put_bytes( bs, data->font_name, data->font_name_length );
3866 return 0;
3869 static int isom_write_tx3g_entry( lsmash_bs_t *bs, lsmash_entry_t *entry )
3871 isom_tx3g_entry_t *data = (isom_tx3g_entry_t *)entry->data;
3872 if( !data )
3873 return -1;
3874 isom_bs_put_box_common( bs, data );
3875 lsmash_bs_put_bytes( bs, data->reserved, 6 );
3876 lsmash_bs_put_be16( bs, data->data_reference_index );
3877 lsmash_bs_put_be32( bs, data->displayFlags );
3878 lsmash_bs_put_byte( bs, data->horizontal_justification );
3879 lsmash_bs_put_byte( bs, data->vertical_justification );
3880 for( uint32_t i = 0; i < 4; i++ )
3881 lsmash_bs_put_byte( bs, data->background_color_rgba[i] );
3882 lsmash_bs_put_be16( bs, data->top );
3883 lsmash_bs_put_be16( bs, data->left );
3884 lsmash_bs_put_be16( bs, data->bottom );
3885 lsmash_bs_put_be16( bs, data->right );
3886 lsmash_bs_put_be16( bs, data->startChar );
3887 lsmash_bs_put_be16( bs, data->endChar );
3888 lsmash_bs_put_be16( bs, data->font_ID );
3889 lsmash_bs_put_byte( bs, data->face_style_flags );
3890 lsmash_bs_put_byte( bs, data->font_size );
3891 for( uint32_t i = 0; i < 4; i++ )
3892 lsmash_bs_put_byte( bs, data->text_color_rgba[i] );
3893 isom_put_ftab( bs, data->ftab );
3894 return lsmash_bs_write_data( bs );
3897 static int isom_write_stsd( lsmash_bs_t *bs, isom_trak_entry_t *trak )
3899 isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd;
3900 if( !stsd || !stsd->list || !stsd->list->head )
3901 return -1;
3902 isom_bs_put_box_common( bs, stsd );
3903 lsmash_bs_put_be32( bs, stsd->list->entry_count );
3904 int ret = -1;
3905 for( lsmash_entry_t *entry = stsd->list->head; entry; entry = entry->next )
3907 isom_sample_entry_t *sample = (isom_sample_entry_t *)entry->data;
3908 if( !sample )
3909 return -1;
3910 switch( sample->type )
3912 case ISOM_CODEC_TYPE_AVC1_VIDEO :
3913 #if 0
3914 case ISOM_CODEC_TYPE_AVC2_VIDEO :
3915 case ISOM_CODEC_TYPE_AVCP_VIDEO :
3916 case ISOM_CODEC_TYPE_SVC1_VIDEO :
3917 case ISOM_CODEC_TYPE_MVC1_VIDEO :
3918 case ISOM_CODEC_TYPE_MVC2_VIDEO :
3919 case ISOM_CODEC_TYPE_MP4V_VIDEO :
3920 case ISOM_CODEC_TYPE_DRAC_VIDEO :
3921 case ISOM_CODEC_TYPE_ENCV_VIDEO :
3922 case ISOM_CODEC_TYPE_MJP2_VIDEO :
3923 case ISOM_CODEC_TYPE_S263_VIDEO :
3924 case ISOM_CODEC_TYPE_VC_1_VIDEO :
3925 #endif
3926 ret = isom_write_visual_entry( bs, entry );
3927 break;
3928 case ISOM_CODEC_TYPE_MP4A_AUDIO :
3929 case ISOM_CODEC_TYPE_AC_3_AUDIO :
3930 case ISOM_CODEC_TYPE_ALAC_AUDIO :
3931 case ISOM_CODEC_TYPE_SAMR_AUDIO :
3932 case ISOM_CODEC_TYPE_SAWB_AUDIO :
3933 case QT_CODEC_TYPE_23NI_AUDIO :
3934 case QT_CODEC_TYPE_NONE_AUDIO :
3935 case QT_CODEC_TYPE_LPCM_AUDIO :
3936 case QT_CODEC_TYPE_RAW_AUDIO :
3937 case QT_CODEC_TYPE_SOWT_AUDIO :
3938 case QT_CODEC_TYPE_TWOS_AUDIO :
3939 case QT_CODEC_TYPE_FL32_AUDIO :
3940 case QT_CODEC_TYPE_FL64_AUDIO :
3941 case QT_CODEC_TYPE_IN24_AUDIO :
3942 case QT_CODEC_TYPE_IN32_AUDIO :
3943 case QT_CODEC_TYPE_NOT_SPECIFIED :
3944 #ifdef LSMASH_DEMUXER_ENABLED
3945 case ISOM_CODEC_TYPE_EC_3_AUDIO :
3946 #endif
3947 #if 0
3948 case ISOM_CODEC_TYPE_DRA1_AUDIO :
3949 case ISOM_CODEC_TYPE_DTSC_AUDIO :
3950 case ISOM_CODEC_TYPE_DTSH_AUDIO :
3951 case ISOM_CODEC_TYPE_DTSL_AUDIO :
3952 case ISOM_CODEC_TYPE_ENCA_AUDIO :
3953 case ISOM_CODEC_TYPE_G719_AUDIO :
3954 case ISOM_CODEC_TYPE_G726_AUDIO :
3955 case ISOM_CODEC_TYPE_M4AE_AUDIO :
3956 case ISOM_CODEC_TYPE_MLPA_AUDIO :
3957 case ISOM_CODEC_TYPE_RAW_AUDIO :
3958 case ISOM_CODEC_TYPE_SAWP_AUDIO :
3959 case ISOM_CODEC_TYPE_SEVC_AUDIO :
3960 case ISOM_CODEC_TYPE_SQCP_AUDIO :
3961 case ISOM_CODEC_TYPE_SSMV_AUDIO :
3962 case ISOM_CODEC_TYPE_TWOS_AUDIO :
3963 #endif
3964 ret = isom_write_audio_entry( bs, entry );
3965 break;
3966 #if 0
3967 case ISOM_CODEC_TYPE_FDP_HINT :
3968 case ISOM_CODEC_TYPE_M2TS_HINT :
3969 case ISOM_CODEC_TYPE_PM2T_HINT :
3970 case ISOM_CODEC_TYPE_PRTP_HINT :
3971 case ISOM_CODEC_TYPE_RM2T_HINT :
3972 case ISOM_CODEC_TYPE_RRTP_HINT :
3973 case ISOM_CODEC_TYPE_RSRP_HINT :
3974 case ISOM_CODEC_TYPE_RTP_HINT :
3975 case ISOM_CODEC_TYPE_SM2T_HINT :
3976 case ISOM_CODEC_TYPE_SRTP_HINT :
3977 ret = isom_write_hint_entry( bs, entry );
3978 break;
3979 case ISOM_CODEC_TYPE_IXSE_META :
3980 case ISOM_CODEC_TYPE_METT_META :
3981 case ISOM_CODEC_TYPE_METX_META :
3982 case ISOM_CODEC_TYPE_MLIX_META :
3983 case ISOM_CODEC_TYPE_OKSD_META :
3984 case ISOM_CODEC_TYPE_SVCM_META :
3985 case ISOM_CODEC_TYPE_TEXT_META :
3986 case ISOM_CODEC_TYPE_URIM_META :
3987 case ISOM_CODEC_TYPE_XML_META :
3988 ret = isom_write_metadata_entry( bs, entry );
3989 break;
3990 #endif
3991 case ISOM_CODEC_TYPE_TX3G_TEXT :
3992 ret = isom_write_tx3g_entry( bs, entry );
3993 break;
3994 case QT_CODEC_TYPE_TEXT_TEXT :
3995 ret = isom_write_text_entry( bs, entry );
3996 break;
3997 default :
3998 break;
4000 if( ret )
4001 break;
4003 return ret;
4006 static int isom_write_stts( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4008 isom_stts_t *stts = trak->mdia->minf->stbl->stts;
4009 if( !stts || !stts->list )
4010 return -1;
4011 isom_bs_put_box_common( bs, stts );
4012 lsmash_bs_put_be32( bs, stts->list->entry_count );
4013 for( lsmash_entry_t *entry = stts->list->head; entry; entry = entry->next )
4015 isom_stts_entry_t *data = (isom_stts_entry_t *)entry->data;
4016 if( !data )
4017 return -1;
4018 lsmash_bs_put_be32( bs, data->sample_count );
4019 lsmash_bs_put_be32( bs, data->sample_delta );
4021 return lsmash_bs_write_data( bs );
4024 static int isom_write_ctts( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4026 isom_ctts_t *ctts = trak->mdia->minf->stbl->ctts;
4027 if( !ctts )
4028 return 0;
4029 if( !ctts->list )
4030 return -1;
4031 isom_bs_put_box_common( bs, ctts );
4032 lsmash_bs_put_be32( bs, ctts->list->entry_count );
4033 for( lsmash_entry_t *entry = ctts->list->head; entry; entry = entry->next )
4035 isom_ctts_entry_t *data = (isom_ctts_entry_t *)entry->data;
4036 if( !data )
4037 return -1;
4038 lsmash_bs_put_be32( bs, data->sample_count );
4039 lsmash_bs_put_be32( bs, data->sample_offset );
4041 return lsmash_bs_write_data( bs );
4044 static int isom_write_cslg( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4046 isom_cslg_t *cslg = trak->mdia->minf->stbl->cslg;
4047 if( !cslg )
4048 return 0;
4049 isom_bs_put_box_common( bs, cslg );
4050 lsmash_bs_put_be32( bs, cslg->compositionToDTSShift );
4051 lsmash_bs_put_be32( bs, cslg->leastDecodeToDisplayDelta );
4052 lsmash_bs_put_be32( bs, cslg->greatestDecodeToDisplayDelta );
4053 lsmash_bs_put_be32( bs, cslg->compositionStartTime );
4054 lsmash_bs_put_be32( bs, cslg->compositionEndTime );
4055 return lsmash_bs_write_data( bs );
4058 static int isom_write_stsz( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4060 isom_stsz_t *stsz = trak->mdia->minf->stbl->stsz;
4061 if( !stsz )
4062 return -1;
4063 isom_bs_put_box_common( bs, stsz );
4064 lsmash_bs_put_be32( bs, stsz->sample_size );
4065 lsmash_bs_put_be32( bs, stsz->sample_count );
4066 if( stsz->sample_size == 0 && stsz->list )
4067 for( lsmash_entry_t *entry = stsz->list->head; entry; entry = entry->next )
4069 isom_stsz_entry_t *data = (isom_stsz_entry_t *)entry->data;
4070 if( !data )
4071 return -1;
4072 lsmash_bs_put_be32( bs, data->entry_size );
4074 return lsmash_bs_write_data( bs );
4077 static int isom_write_stss( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4079 isom_stss_t *stss = trak->mdia->minf->stbl->stss;
4080 if( !stss )
4081 return 0; /* If the sync sample box is not present, every sample is a random access point. */
4082 if( !stss->list )
4083 return -1;
4084 isom_bs_put_box_common( bs, stss );
4085 lsmash_bs_put_be32( bs, stss->list->entry_count );
4086 for( lsmash_entry_t *entry = stss->list->head; entry; entry = entry->next )
4088 isom_stss_entry_t *data = (isom_stss_entry_t *)entry->data;
4089 if( !data )
4090 return -1;
4091 lsmash_bs_put_be32( bs, data->sample_number );
4093 return lsmash_bs_write_data( bs );
4096 static int isom_write_stps( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4098 isom_stps_t *stps = trak->mdia->minf->stbl->stps;
4099 if( !stps )
4100 return 0;
4101 if( !stps->list )
4102 return -1;
4103 isom_bs_put_box_common( bs, stps );
4104 lsmash_bs_put_be32( bs, stps->list->entry_count );
4105 for( lsmash_entry_t *entry = stps->list->head; entry; entry = entry->next )
4107 isom_stps_entry_t *data = (isom_stps_entry_t *)entry->data;
4108 if( !data )
4109 return -1;
4110 lsmash_bs_put_be32( bs, data->sample_number );
4112 return lsmash_bs_write_data( bs );
4115 static int isom_write_sdtp( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4117 isom_sdtp_t *sdtp = trak->mdia->minf->stbl->sdtp;
4118 if( !sdtp )
4119 return 0;
4120 if( !sdtp->list )
4121 return -1;
4122 isom_bs_put_box_common( bs, sdtp );
4123 for( lsmash_entry_t *entry = sdtp->list->head; entry; entry = entry->next )
4125 isom_sdtp_entry_t *data = (isom_sdtp_entry_t *)entry->data;
4126 if( !data )
4127 return -1;
4128 uint8_t temp = (data->is_leading << 6)
4129 | (data->sample_depends_on << 4)
4130 | (data->sample_is_depended_on << 2)
4131 | data->sample_has_redundancy;
4132 lsmash_bs_put_byte( bs, temp );
4134 return lsmash_bs_write_data( bs );
4137 static int isom_write_stsc( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4139 isom_stsc_t *stsc = trak->mdia->minf->stbl->stsc;
4140 if( !stsc || !stsc->list )
4141 return -1;
4142 isom_bs_put_box_common( bs, stsc );
4143 lsmash_bs_put_be32( bs, stsc->list->entry_count );
4144 for( lsmash_entry_t *entry = stsc->list->head; entry; entry = entry->next )
4146 isom_stsc_entry_t *data = (isom_stsc_entry_t *)entry->data;
4147 if( !data )
4148 return -1;
4149 lsmash_bs_put_be32( bs, data->first_chunk );
4150 lsmash_bs_put_be32( bs, data->samples_per_chunk );
4151 lsmash_bs_put_be32( bs, data->sample_description_index );
4153 return lsmash_bs_write_data( bs );
4156 static int isom_write_co64( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4158 isom_stco_t *co64 = trak->mdia->minf->stbl->stco;
4159 if( !co64 || !co64->list )
4160 return -1;
4161 isom_bs_put_box_common( bs, co64 );
4162 lsmash_bs_put_be32( bs, co64->list->entry_count );
4163 for( lsmash_entry_t *entry = co64->list->head; entry; entry = entry->next )
4165 isom_co64_entry_t *data = (isom_co64_entry_t *)entry->data;
4166 if( !data )
4167 return -1;
4168 lsmash_bs_put_be64( bs, data->chunk_offset );
4170 return lsmash_bs_write_data( bs );
4173 static int isom_write_stco( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4175 isom_stco_t *stco = trak->mdia->minf->stbl->stco;
4176 if( !stco || !stco->list )
4177 return -1;
4178 if( stco->large_presentation )
4179 return isom_write_co64( bs, trak );
4180 isom_bs_put_box_common( bs, stco );
4181 lsmash_bs_put_be32( bs, stco->list->entry_count );
4182 for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next )
4184 isom_stco_entry_t *data = (isom_stco_entry_t *)entry->data;
4185 if( !data )
4186 return -1;
4187 lsmash_bs_put_be32( bs, data->chunk_offset );
4189 return lsmash_bs_write_data( bs );
4192 static int isom_write_sgpd( lsmash_bs_t *bs, isom_trak_entry_t *trak, uint32_t grouping_number )
4194 isom_sgpd_entry_t *sgpd = (isom_sgpd_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->sgpd_list, grouping_number );
4195 if( !sgpd || !sgpd->list )
4196 return -1;
4197 isom_bs_put_box_common( bs, sgpd );
4198 lsmash_bs_put_be32( bs, sgpd->grouping_type );
4199 if( sgpd->version == 1 )
4200 lsmash_bs_put_be32( bs, sgpd->default_length );
4201 lsmash_bs_put_be32( bs, sgpd->list->entry_count );
4202 for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next )
4204 if( !entry->data )
4205 return -1;
4206 switch( sgpd->grouping_type )
4208 case ISOM_GROUP_TYPE_RAP :
4210 isom_rap_entry_t *rap = (isom_rap_entry_t *)entry->data;
4211 uint8_t temp = (rap->num_leading_samples_known << 7)
4212 | rap->num_leading_samples;
4213 lsmash_bs_put_byte( bs, temp );
4214 break;
4216 case ISOM_GROUP_TYPE_ROLL :
4217 lsmash_bs_put_be16( bs, ((isom_roll_entry_t *)entry->data)->roll_distance );
4218 break;
4219 default :
4220 /* We don't consider other grouping types currently. */
4221 // if( sgpd->version == 1 && !sgpd->default_length )
4222 // lsmash_bs_put_be32( bs, ((isom_sgpd_entry_t *)entry->data)->description_length );
4223 break;
4226 return lsmash_bs_write_data( bs );
4229 static int isom_write_sbgp( lsmash_bs_t *bs, isom_trak_entry_t *trak, uint32_t grouping_number )
4231 isom_sbgp_entry_t *sbgp = (isom_sbgp_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->sbgp_list, grouping_number );
4232 if( !sbgp || !sbgp->list )
4233 return -1;
4234 isom_bs_put_box_common( bs, sbgp );
4235 lsmash_bs_put_be32( bs, sbgp->grouping_type );
4236 if( sbgp->version == 1 )
4237 lsmash_bs_put_be32( bs, sbgp->grouping_type_parameter );
4238 lsmash_bs_put_be32( bs, sbgp->list->entry_count );
4239 for( lsmash_entry_t *entry = sbgp->list->head; entry; entry = entry->next )
4241 isom_group_assignment_entry_t *data = (isom_group_assignment_entry_t *)entry->data;
4242 if( !data )
4243 return -1;
4244 lsmash_bs_put_be32( bs, data->sample_count );
4245 lsmash_bs_put_be32( bs, data->group_description_index );
4247 return lsmash_bs_write_data( bs );
4250 static int isom_write_stbl( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4252 isom_stbl_t *stbl = trak->mdia->minf->stbl;
4253 if( !stbl )
4254 return -1;
4255 isom_bs_put_box_common( bs, stbl );
4256 if( lsmash_bs_write_data( bs ) )
4257 return -1;
4258 if( isom_write_stsd( bs, trak )
4259 || isom_write_stts( bs, trak )
4260 || isom_write_ctts( bs, trak )
4261 || isom_write_cslg( bs, trak )
4262 || isom_write_stss( bs, trak )
4263 || isom_write_stps( bs, trak )
4264 || isom_write_sdtp( bs, trak )
4265 || isom_write_stsc( bs, trak )
4266 || isom_write_stsz( bs, trak )
4267 || isom_write_stco( bs, trak ) )
4268 return -1;
4269 if( stbl->sgpd_list )
4270 for( uint32_t i = 1; i <= stbl->sgpd_list->entry_count; i++ )
4271 if( isom_write_sgpd( bs, trak, i ) )
4272 return -1;
4273 if( stbl->sbgp_list )
4274 for( uint32_t i = 1; i <= stbl->sbgp_list->entry_count; i++ )
4275 if( isom_write_sbgp( bs, trak, i ) )
4276 return -1;
4277 return 0;
4280 static int isom_write_minf( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4282 isom_minf_t *minf = trak->mdia->minf;
4283 if( !minf )
4284 return -1;
4285 isom_bs_put_box_common( bs, minf );
4286 if( lsmash_bs_write_data( bs ) )
4287 return -1;
4288 if( (minf->vmhd && isom_write_vmhd( bs, trak ))
4289 || (minf->smhd && isom_write_smhd( bs, trak ))
4290 || (minf->hmhd && isom_write_hmhd( bs, trak ))
4291 || (minf->nmhd && isom_write_nmhd( bs, trak ))
4292 || (minf->gmhd && isom_write_gmhd( bs, trak )) )
4293 return -1;
4294 if( isom_write_hdlr( bs, trak, 0 )
4295 || isom_write_dinf( bs, trak )
4296 || isom_write_stbl( bs, trak ) )
4297 return -1;
4298 return 0;
4301 static int isom_write_mdia( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4303 isom_mdia_t *mdia = trak->mdia;
4304 if( !mdia )
4305 return -1;
4306 isom_bs_put_box_common( bs, mdia );
4307 if( lsmash_bs_write_data( bs ) )
4308 return -1;
4309 if( isom_write_mdhd( bs, trak )
4310 || isom_write_hdlr( bs, trak, 1 )
4311 || isom_write_minf( bs, trak ) )
4312 return -1;
4313 return 0;
4316 static int isom_write_chpl( lsmash_bs_t *bs, isom_chpl_t *chpl )
4318 if( !chpl )
4319 return 0;
4320 if( !chpl->list || chpl->version > 1 )
4321 return -1;
4322 isom_bs_put_box_common( bs, chpl );
4323 if( chpl->version == 1 )
4325 lsmash_bs_put_byte( bs, chpl->unknown );
4326 lsmash_bs_put_be32( bs, chpl->list->entry_count );
4328 else /* chpl->version == 0 */
4329 lsmash_bs_put_byte( bs, (uint8_t)chpl->list->entry_count );
4330 for( lsmash_entry_t *entry = chpl->list->head; entry; entry = entry->next )
4332 isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data;
4333 if( !data )
4334 return -1;
4335 lsmash_bs_put_be64( bs, data->start_time );
4336 lsmash_bs_put_byte( bs, data->chapter_name_length );
4337 lsmash_bs_put_bytes( bs, data->chapter_name, data->chapter_name_length );
4339 return lsmash_bs_write_data( bs );
4342 static int isom_write_udta( lsmash_bs_t *bs, isom_moov_t *moov, isom_trak_entry_t *trak )
4344 /* Setting non-NULL pointer to trak means trak->udta data will be written in stream.
4345 * If trak is set by NULL while moov is set by non-NULL pointer, moov->udta data will be written in stream. */
4346 isom_udta_t *udta = trak ? trak->udta : moov ? moov->udta : NULL;
4347 if( !udta )
4348 return 0;
4349 isom_bs_put_box_common( bs, udta );
4350 if( lsmash_bs_write_data( bs ) )
4351 return -1;
4352 if( moov && isom_write_chpl( bs, udta->chpl ) )
4353 return -1;
4354 return 0;
4357 static int isom_write_trak( lsmash_bs_t *bs, isom_trak_entry_t *trak )
4359 if( !trak )
4360 return -1;
4361 isom_bs_put_box_common( bs, trak );
4362 if( lsmash_bs_write_data( bs ) )
4363 return -1;
4364 if( isom_write_tkhd( bs, trak )
4365 || isom_write_tapt( bs, trak )
4366 || isom_write_edts( bs, trak )
4367 || isom_write_tref( bs, trak )
4368 || isom_write_mdia( bs, trak )
4369 || isom_write_udta( bs, NULL, trak ) )
4370 return -1;
4371 return 0;
4374 static int isom_write_iods( lsmash_root_t *root )
4376 if( !root || !root->moov )
4377 return -1;
4378 if( !root->moov->iods )
4379 return 0;
4380 isom_iods_t *iods = root->moov->iods;
4381 lsmash_bs_t *bs = root->bs;
4382 isom_bs_put_box_common( bs, iods );
4383 return mp4sys_write_ObjectDescriptor( bs, iods->OD );
4386 static int isom_write_mvhd( lsmash_root_t *root )
4388 if( !root || !root->moov || !root->moov->mvhd )
4389 return -1;
4390 isom_mvhd_t *mvhd = root->moov->mvhd;
4391 lsmash_bs_t *bs = root->bs;
4392 isom_bs_put_box_common( bs, mvhd );
4393 if( mvhd->version )
4395 lsmash_bs_put_be64( bs, mvhd->creation_time );
4396 lsmash_bs_put_be64( bs, mvhd->modification_time );
4397 lsmash_bs_put_be32( bs, mvhd->timescale );
4398 lsmash_bs_put_be64( bs, mvhd->duration );
4400 else
4402 lsmash_bs_put_be32( bs, (uint32_t)mvhd->creation_time );
4403 lsmash_bs_put_be32( bs, (uint32_t)mvhd->modification_time );
4404 lsmash_bs_put_be32( bs, mvhd->timescale );
4405 lsmash_bs_put_be32( bs, (uint32_t)mvhd->duration );
4407 lsmash_bs_put_be32( bs, mvhd->rate );
4408 lsmash_bs_put_be16( bs, mvhd->volume );
4409 lsmash_bs_put_be16( bs, mvhd->reserved );
4410 lsmash_bs_put_be32( bs, mvhd->preferredLong[0] );
4411 lsmash_bs_put_be32( bs, mvhd->preferredLong[1] );
4412 for( int i = 0; i < 9; i++ )
4413 lsmash_bs_put_be32( bs, mvhd->matrix[i] );
4414 lsmash_bs_put_be32( bs, mvhd->previewTime );
4415 lsmash_bs_put_be32( bs, mvhd->previewDuration );
4416 lsmash_bs_put_be32( bs, mvhd->posterTime );
4417 lsmash_bs_put_be32( bs, mvhd->selectionTime );
4418 lsmash_bs_put_be32( bs, mvhd->selectionDuration );
4419 lsmash_bs_put_be32( bs, mvhd->currentTime );
4420 lsmash_bs_put_be32( bs, mvhd->next_track_ID );
4421 return lsmash_bs_write_data( bs );
4424 static void isom_bs_put_sample_flags( lsmash_bs_t *bs, isom_sample_flags_t *flags )
4426 uint32_t temp = (flags->reserved << 28)
4427 | (flags->is_leading << 26)
4428 | (flags->sample_depends_on << 24)
4429 | (flags->sample_is_depended_on << 22)
4430 | (flags->sample_has_redundancy << 20)
4431 | (flags->sample_padding_value << 17)
4432 | (flags->sample_is_non_sync_sample << 16)
4433 | flags->sample_degradation_priority;
4434 lsmash_bs_put_be32( bs, temp );
4437 static int isom_write_mehd( lsmash_bs_t *bs, isom_mehd_t *mehd )
4439 if( !mehd )
4440 return -1;
4441 isom_bs_put_box_common( bs, mehd );
4442 if( mehd->version == 1 )
4443 lsmash_bs_put_be64( bs, mehd->fragment_duration );
4444 else
4445 lsmash_bs_put_be32( bs, (uint32_t)mehd->fragment_duration );
4446 return lsmash_bs_write_data( bs );
4449 static int isom_write_trex( lsmash_bs_t *bs, isom_trex_entry_t *trex )
4451 if( !trex )
4452 return -1;
4453 isom_bs_put_box_common( bs, trex );
4454 lsmash_bs_put_be32( bs, trex->track_ID );
4455 lsmash_bs_put_be32( bs, trex->default_sample_description_index );
4456 lsmash_bs_put_be32( bs, trex->default_sample_duration );
4457 lsmash_bs_put_be32( bs, trex->default_sample_size );
4458 isom_bs_put_sample_flags( bs, &trex->default_sample_flags );
4459 return lsmash_bs_write_data( bs );
4462 static int isom_bs_write_movie_extends_placeholder( lsmash_bs_t *bs )
4464 /* The following will be overwritten by Movie Extends Header Box.
4465 * We use version 1 Movie Extends Header Box since it causes extra 4 bytes region
4466 * we cannot replace with empty Free Space Box as we place version 0 one. */
4467 lsmash_bs_put_be32( bs, ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 8 );
4468 lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_FREE );
4469 lsmash_bs_put_be32( bs, 0 );
4470 lsmash_bs_put_be64( bs, 0 );
4471 return lsmash_bs_write_data( bs );
4474 static int isom_write_mvex( lsmash_bs_t *bs, isom_mvex_t *mvex )
4476 if( !mvex )
4477 return 0;
4478 isom_bs_put_box_common( bs, mvex );
4479 if( lsmash_bs_write_data( bs ) )
4480 return -1;
4481 /* Movie Extends Header Box is not written immediately.
4482 * It's done after finishing all movie fragments. */
4483 if( mvex->mehd )
4485 if( isom_write_mehd( bs, mvex->mehd ) )
4486 return -1;
4488 else if( bs->stream != stdout )
4491 [ROOT]
4492 |--[ftyp]
4493 |--[moov]
4494 |--[mvhd]
4495 |--[trak]
4499 |--[mvex]
4500 |--[mehd] <--- mehd->pos == mvex->placeholder_pos
4502 mvex->placeholder_pos = mvex->root->bs->written;
4503 if( isom_bs_write_movie_extends_placeholder( bs ) )
4504 return -1;
4506 if( mvex->trex_list )
4507 for( lsmash_entry_t *entry = mvex->trex_list->head; entry; entry = entry->next )
4508 if( isom_write_trex( bs, (isom_trex_entry_t *)entry->data ) )
4509 return -1;
4510 return 0;
4513 static int isom_write_mfhd( lsmash_bs_t *bs, isom_mfhd_t *mfhd )
4515 if( !mfhd )
4516 return -1;
4517 isom_bs_put_box_common( bs, mfhd );
4518 lsmash_bs_put_be32( bs, mfhd->sequence_number );
4519 return lsmash_bs_write_data( bs );
4522 static int isom_write_tfhd( lsmash_bs_t *bs, isom_tfhd_t *tfhd )
4524 if( !tfhd )
4525 return -1;
4526 isom_bs_put_box_common( bs, tfhd );
4527 lsmash_bs_put_be32( bs, tfhd->track_ID );
4528 if( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT ) lsmash_bs_put_be64( bs, tfhd->base_data_offset );
4529 if( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT ) lsmash_bs_put_be32( bs, tfhd->sample_description_index );
4530 if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT ) lsmash_bs_put_be32( bs, tfhd->default_sample_duration );
4531 if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT ) lsmash_bs_put_be32( bs, tfhd->default_sample_size );
4532 if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT ) isom_bs_put_sample_flags( bs, &tfhd->default_sample_flags );
4533 return lsmash_bs_write_data( bs );
4536 static int isom_write_trun( lsmash_bs_t *bs, isom_trun_entry_t *trun )
4538 if( !trun )
4539 return -1;
4540 isom_bs_put_box_common( bs, trun );
4541 lsmash_bs_put_be32( bs, trun->sample_count );
4542 if( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT ) lsmash_bs_put_be32( bs, trun->data_offset );
4543 if( trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT ) isom_bs_put_sample_flags( bs, &trun->first_sample_flags );
4544 if( trun->optional )
4545 for( lsmash_entry_t *entry = trun->optional->head; entry; entry = entry->next )
4547 isom_trun_optional_row_t *data = (isom_trun_optional_row_t *)entry->data;
4548 if( !data )
4549 return -1;
4550 if( trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT ) lsmash_bs_put_be32( bs, data->sample_duration );
4551 if( trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT ) lsmash_bs_put_be32( bs, data->sample_size );
4552 if( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT ) isom_bs_put_sample_flags( bs, &data->sample_flags );
4553 if( trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT ) lsmash_bs_put_be32( bs, data->sample_composition_time_offset );
4555 return lsmash_bs_write_data( bs );
4558 static int isom_write_traf( lsmash_bs_t *bs, isom_traf_entry_t *traf )
4560 if( !traf )
4561 return -1;
4562 isom_bs_put_box_common( bs, traf );
4563 if( lsmash_bs_write_data( bs ) )
4564 return -1;
4565 if( isom_write_tfhd( bs, traf->tfhd ) )
4566 return -1;
4567 if( traf->trun_list )
4568 for( lsmash_entry_t *entry = traf->trun_list->head; entry; entry = entry->next )
4569 if( isom_write_trun( bs, (isom_trun_entry_t *)entry->data ) )
4570 return -1;
4571 return 0;
4574 static int isom_write_moof( lsmash_bs_t *bs, isom_moof_entry_t *moof )
4576 if( !moof )
4577 return -1;
4578 isom_bs_put_box_common( bs, moof );
4579 if( lsmash_bs_write_data( bs ) )
4580 return -1;
4581 if( isom_write_mfhd( bs, moof->mfhd ) )
4582 return -1;
4583 if( moof->traf_list )
4584 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
4585 if( isom_write_traf( bs, (isom_traf_entry_t *)entry->data ) )
4586 return -1;
4587 return 0;
4590 static int isom_write_tfra( lsmash_bs_t *bs, isom_tfra_entry_t *tfra )
4592 if( !tfra )
4593 return -1;
4594 isom_bs_put_box_common( bs, tfra );
4595 uint32_t temp = (tfra->reserved << 6)
4596 | (tfra->length_size_of_traf_num << 4)
4597 | (tfra->length_size_of_trun_num << 2)
4598 | tfra->length_size_of_sample_num;
4599 lsmash_bs_put_be32( bs, tfra->track_ID );
4600 lsmash_bs_put_be32( bs, temp );
4601 lsmash_bs_put_be32( bs, tfra->number_of_entry );
4602 if( tfra->list )
4604 void (*bs_put_funcs[5])( lsmash_bs_t *, uint64_t ) =
4606 lsmash_bs_put_byte_from_64,
4607 lsmash_bs_put_be16_from_64,
4608 lsmash_bs_put_be24_from_64,
4609 lsmash_bs_put_be32_from_64,
4610 lsmash_bs_put_be64
4612 void (*bs_put_time) ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ 3 + (tfra->version == 1) ];
4613 void (*bs_put_moof_offset) ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ 3 + (tfra->version == 1) ];
4614 void (*bs_put_traf_number) ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ tfra->length_size_of_traf_num ];
4615 void (*bs_put_trun_number) ( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ tfra->length_size_of_trun_num ];
4616 void (*bs_put_sample_number)( lsmash_bs_t *, uint64_t ) = bs_put_funcs[ tfra->length_size_of_sample_num ];
4617 for( lsmash_entry_t *entry = tfra->list->head; entry; entry = entry->next )
4619 isom_tfra_location_time_entry_t *data = (isom_tfra_location_time_entry_t *)entry->data;
4620 if( !data )
4621 return -1;
4622 bs_put_time ( bs, data->time );
4623 bs_put_moof_offset ( bs, data->moof_offset );
4624 bs_put_traf_number ( bs, data->traf_number );
4625 bs_put_trun_number ( bs, data->trun_number );
4626 bs_put_sample_number( bs, data->sample_number );
4629 return lsmash_bs_write_data( bs );
4632 static int isom_write_mfro( lsmash_bs_t *bs, isom_mfro_t *mfro )
4634 if( !mfro )
4635 return -1;
4636 isom_bs_put_box_common( bs, mfro );
4637 lsmash_bs_put_be32( bs, mfro->length );
4638 return lsmash_bs_write_data( bs );
4641 static int isom_write_mfra( lsmash_bs_t *bs, isom_mfra_t *mfra )
4643 if( !mfra )
4644 return -1;
4645 isom_bs_put_box_common( bs, mfra );
4646 if( lsmash_bs_write_data( bs ) )
4647 return -1;
4648 if( mfra->tfra_list )
4649 for( lsmash_entry_t *entry = mfra->tfra_list->head; entry; entry = entry->next )
4650 if( isom_write_tfra( bs, (isom_tfra_entry_t *)entry->data ) )
4651 return -1;
4652 return isom_write_mfro( bs, mfra->mfro );
4655 static int isom_bs_write_largesize_placeholder( lsmash_bs_t *bs )
4657 lsmash_bs_put_be32( bs, ISOM_DEFAULT_BOX_HEADER_SIZE );
4658 lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_FREE );
4659 return lsmash_bs_write_data( bs );
4662 static int isom_write_mdat_header( lsmash_root_t *root, uint64_t media_size )
4664 if( !root || !root->bs || !root->mdat )
4665 return -1;
4666 isom_mdat_t *mdat = root->mdat;
4667 lsmash_bs_t *bs = root->bs;
4668 if( media_size )
4670 mdat->size = ISOM_DEFAULT_BOX_HEADER_SIZE + media_size;
4671 if( mdat->size > UINT32_MAX )
4672 mdat->size += 8; /* large_size */
4673 isom_bs_put_box_common( bs, mdat );
4674 return 0;
4676 mdat->placeholder_pos = lsmash_ftell( bs->stream );
4677 if( isom_bs_write_largesize_placeholder( bs ) )
4678 return -1;
4679 mdat->size = ISOM_DEFAULT_BOX_HEADER_SIZE;
4680 isom_bs_put_box_common( bs, mdat );
4681 return lsmash_bs_write_data( bs );
4684 static int isom_write_mdat_size( lsmash_root_t *root )
4686 if( !root || !root->bs || !root->bs->stream )
4687 return -1;
4688 if( !root->mdat )
4689 return 0;
4690 isom_mdat_t *mdat = root->mdat;
4691 uint8_t large_flag = mdat->size > UINT32_MAX;
4692 lsmash_bs_t *bs = root->bs;
4693 FILE *stream = bs->stream;
4694 uint64_t current_pos = lsmash_ftell( stream );
4695 if( large_flag )
4697 lsmash_fseek( stream, mdat->placeholder_pos, SEEK_SET );
4698 lsmash_bs_put_be32( bs, 1 );
4699 lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_MDAT );
4700 lsmash_bs_put_be64( bs, mdat->size + ISOM_DEFAULT_BOX_HEADER_SIZE );
4702 else
4704 lsmash_fseek( stream, mdat->placeholder_pos + ISOM_DEFAULT_BOX_HEADER_SIZE, SEEK_SET );
4705 lsmash_bs_put_be32( bs, mdat->size );
4706 lsmash_bs_put_be32( bs, ISOM_BOX_TYPE_MDAT );
4708 int ret = lsmash_bs_write_data( bs );
4709 lsmash_fseek( stream, current_pos, SEEK_SET );
4710 return ret;
4713 /* We put a placeholder for 64-bit media data if the media_size of the argument is set to 0.
4714 * If a Media Data Box already exists and we don't pick movie fragments structure,
4715 * write the actual size of the current one and start a new one. */
4716 static int isom_new_mdat( lsmash_root_t *root, uint64_t media_size )
4718 if( !root )
4719 return 0;
4720 if( root->mdat )
4722 /* Write the actual size of the current Media Data Box. */
4723 if( !root->fragment && isom_write_mdat_size( root ) )
4724 return -1;
4726 else
4728 isom_create_box( mdat, root, ISOM_BOX_TYPE_MDAT );
4729 root->mdat = mdat;
4731 /* Start a new Media Data Box. */
4732 return isom_write_mdat_header( root, media_size );
4735 int isom_check_compatibility( lsmash_root_t *root )
4737 if( !root )
4738 return -1;
4739 root->qt_compatible = 0;
4740 /* Check brand to decide mandatory boxes. */
4741 if( !root->ftyp || !root->ftyp->brand_count )
4743 /* No brand declaration means this file is a MP4 version 1 or QuickTime file format. */
4744 if( root->moov && root->moov->iods )
4746 root->mp4_version1 = 1;
4747 root->isom_compatible = 1;
4749 else
4750 root->qt_compatible = 1;
4751 return 0;
4753 for( uint32_t i = 0; i < root->ftyp->brand_count; i++ )
4755 switch( root->ftyp->compatible_brands[i] )
4757 case ISOM_BRAND_TYPE_QT :
4758 root->qt_compatible = 1;
4759 break;
4760 case ISOM_BRAND_TYPE_MP41 :
4761 root->mp4_version1 = 1;
4762 break;
4763 case ISOM_BRAND_TYPE_MP42 :
4764 root->mp4_version2 = 1;
4765 break;
4766 case ISOM_BRAND_TYPE_ISOM :
4767 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 1 );
4768 break;
4769 case ISOM_BRAND_TYPE_ISO2 :
4770 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 2 );
4771 break;
4772 case ISOM_BRAND_TYPE_ISO3 :
4773 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 3 );
4774 break;
4775 case ISOM_BRAND_TYPE_ISO4 :
4776 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 4 );
4777 break;
4778 case ISOM_BRAND_TYPE_ISO5 :
4779 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 5 );
4780 break;
4781 case ISOM_BRAND_TYPE_ISO6 :
4782 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 6 );
4783 break;
4784 case ISOM_BRAND_TYPE_M4A :
4785 case ISOM_BRAND_TYPE_M4B :
4786 root->itunes_audio = 1;
4787 break;
4788 case ISOM_BRAND_TYPE_3GP4 :
4789 root->max_3gpp_version = LSMASH_MAX( root->max_3gpp_version, 4 );
4790 break;
4791 case ISOM_BRAND_TYPE_3GP5 :
4792 root->max_3gpp_version = LSMASH_MAX( root->max_3gpp_version, 5 );
4793 break;
4794 case ISOM_BRAND_TYPE_3GE6 :
4795 case ISOM_BRAND_TYPE_3GG6 :
4796 case ISOM_BRAND_TYPE_3GP6 :
4797 case ISOM_BRAND_TYPE_3GR6 :
4798 case ISOM_BRAND_TYPE_3GS6 :
4799 root->max_3gpp_version = LSMASH_MAX( root->max_3gpp_version, 6 );
4800 break;
4801 default :
4802 break;
4804 switch( root->ftyp->compatible_brands[i] )
4806 case ISOM_BRAND_TYPE_AVC1 :
4807 case ISOM_BRAND_TYPE_ISO2 :
4808 case ISOM_BRAND_TYPE_ISO3 :
4809 case ISOM_BRAND_TYPE_ISO4 :
4810 case ISOM_BRAND_TYPE_ISO5 :
4811 case ISOM_BRAND_TYPE_ISO6 :
4812 root->avc_extensions = 1;
4813 break;
4814 default :
4815 break;
4818 root->isom_compatible = !root->qt_compatible || root->mp4_version1 || root->mp4_version2 || root->itunes_audio || root->max_3gpp_version;
4819 return 0;
4822 static uint32_t isom_get_sample_count( isom_trak_entry_t *trak )
4824 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsz )
4825 return 0;
4826 return trak->mdia->minf->stbl->stsz->sample_count;
4829 static uint64_t isom_get_dts( isom_stts_t *stts, uint32_t sample_number )
4831 if( !stts || !stts->list )
4832 return 0;
4833 uint64_t dts = 0;
4834 uint32_t i = 1;
4835 lsmash_entry_t *entry;
4836 isom_stts_entry_t *data;
4837 for( entry = stts->list->head; entry; entry = entry->next )
4839 data = (isom_stts_entry_t *)entry->data;
4840 if( !data )
4841 return 0;
4842 if( i + data->sample_count > sample_number )
4843 break;
4844 dts += (uint64_t)data->sample_delta * data->sample_count;
4845 i += data->sample_count;
4847 if( !entry )
4848 return 0;
4849 dts += (uint64_t)data->sample_delta * (sample_number - i);
4850 return dts;
4853 #if 0
4854 static uint64_t isom_get_cts( isom_stts_t *stts, isom_ctts_t *ctts, uint32_t sample_number )
4856 if( !stts || !stts->list )
4857 return 0;
4858 if( !ctts )
4859 return isom_get_dts( stts, sample_number );
4860 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. */
4861 lsmash_entry_t *entry;
4862 isom_ctts_entry_t *data;
4863 if( sample_number == 0 )
4864 return 0;
4865 for( entry = ctts->list->head; entry; entry = entry->next )
4867 data = (isom_ctts_entry_t *)entry->data;
4868 if( !data )
4869 return 0;
4870 if( i + data->sample_count > sample_number )
4871 break;
4872 i += data->sample_count;
4874 if( !entry )
4875 return 0;
4876 return isom_get_dts( stts, sample_number ) + data->sample_offset;
4878 #endif
4880 static int isom_replace_last_sample_delta( isom_stbl_t *stbl, uint32_t sample_delta )
4882 if( !stbl || !stbl->stts || !stbl->stts->list || !stbl->stts->list->tail || !stbl->stts->list->tail->data )
4883 return -1;
4884 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stbl->stts->list->tail->data;
4885 if( sample_delta != last_stts_data->sample_delta )
4887 if( last_stts_data->sample_count > 1 )
4889 last_stts_data->sample_count -= 1;
4890 if( isom_add_stts_entry( stbl, sample_delta ) )
4891 return -1;
4893 else
4894 last_stts_data->sample_delta = sample_delta;
4896 return 0;
4899 static int isom_update_mdhd_duration( isom_trak_entry_t *trak, uint32_t last_sample_delta )
4901 if( !trak || !trak->root || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl
4902 || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list )
4903 return -1;
4904 lsmash_root_t *root = trak->root;
4905 isom_mdhd_t *mdhd = trak->mdia->mdhd;
4906 isom_stbl_t *stbl = trak->mdia->minf->stbl;
4907 isom_stts_t *stts = stbl->stts;
4908 isom_ctts_t *ctts = stbl->ctts;
4909 isom_cslg_t *cslg = stbl->cslg;
4910 mdhd->duration = 0;
4911 uint32_t sample_count = isom_get_sample_count( trak );
4912 if( !sample_count )
4914 /* Return error if non-fragmented movie has no samples. */
4915 if( !root->fragment && !stts->list->entry_count )
4916 return -1;
4917 return 0;
4919 /* Now we have at least 1 sample, so do stts_entry. */
4920 lsmash_entry_t *last_stts = stts->list->tail;
4921 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)last_stts->data;
4922 if( sample_count == 1 )
4923 mdhd->duration = last_stts_data->sample_delta;
4924 /* Now we have at least 2 samples,
4925 * but dunno whether 1 stts_entry which has 2 samples or 2 stts_entry which has 1 samle each. */
4926 else if( !ctts )
4928 /* use dts instead of cts */
4929 mdhd->duration = isom_get_dts( stts, sample_count );
4930 if( last_sample_delta )
4932 mdhd->duration += last_sample_delta;
4933 if( isom_replace_last_sample_delta( stbl, last_sample_delta ) )
4934 return -1;
4936 else if( last_stts_data->sample_count > 1 )
4937 mdhd->duration += last_stts_data->sample_delta; /* no need to update last_stts_data->sample_delta */
4938 else
4940 /* Remove the last entry. */
4941 if( lsmash_remove_entry( stts->list, stts->list->entry_count, NULL ) )
4942 return -1;
4943 /* copy the previous sample_delta. */
4944 ++ ((isom_stts_entry_t *)stts->list->tail->data)->sample_count;
4945 mdhd->duration += ((isom_stts_entry_t *)stts->list->tail->data)->sample_delta;
4948 else
4950 if( !ctts->list || ctts->list->entry_count == 0 )
4951 return -1;
4952 uint64_t dts = 0;
4953 uint64_t max_cts = 0, max2_cts = 0, min_cts = UINT64_MAX;
4954 uint32_t max_offset = 0, min_offset = UINT32_MAX;
4955 uint32_t j, k;
4956 lsmash_entry_t *stts_entry = stts->list->head;
4957 lsmash_entry_t *ctts_entry = ctts->list->head;
4958 j = k = 0;
4959 for( uint32_t i = 0; i < sample_count; i++ )
4961 if( !ctts_entry || !stts_entry )
4962 return -1;
4963 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
4964 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
4965 if( !stts_data || !ctts_data )
4966 return -1;
4967 uint64_t cts = dts + ctts_data->sample_offset;
4968 min_cts = LSMASH_MIN( min_cts, cts );
4969 max_offset = LSMASH_MAX( max_offset, ctts_data->sample_offset );
4970 min_offset = LSMASH_MIN( min_offset, ctts_data->sample_offset );
4971 if( max_cts < cts )
4973 max2_cts = max_cts;
4974 max_cts = cts;
4976 else if( max2_cts < cts )
4977 max2_cts = cts;
4978 dts += stts_data->sample_delta;
4979 /* If finished sample_count of current entry, move to next. */
4980 if( ++j == ctts_data->sample_count )
4982 ctts_entry = ctts_entry->next;
4983 j = 0;
4985 if( ++k == stts_data->sample_count )
4987 stts_entry = stts_entry->next;
4988 k = 0;
4991 dts -= last_stts_data->sample_delta;
4992 if( root->fragment )
4993 /* Overall presentation is extended exceeding this initial movie.
4994 * So, any players shall display the movie exceeding the durations
4995 * indicated in Movie Header Box, Track Header Boxes and Media Header Boxes.
4996 * Samples up to the duration indicated in Movie Extends Header Box shall be displayed.
4997 * In the absence of Movie Extends Header Box, all samples shall be displayed. */
4998 mdhd->duration += dts + last_sample_delta;
4999 else
5001 if( !last_sample_delta )
5003 /* The spec allows an arbitrary value for the duration of the last sample. So, we pick last-1 sample's. */
5004 last_sample_delta = max_cts - max2_cts;
5006 mdhd->duration = max_cts - min_cts + last_sample_delta;
5007 /* To match dts and media duration, update stts and mdhd relatively. */
5008 if( mdhd->duration > dts )
5009 last_sample_delta = mdhd->duration - dts;
5010 else
5011 mdhd->duration = dts + last_sample_delta; /* media duration must not less than last dts. */
5013 if( isom_replace_last_sample_delta( stbl, last_sample_delta ) )
5014 return -1;
5015 /* Explicit composition information and DTS shifting */
5016 if( cslg || root->qt_compatible || root->max_isom_version >= 4 )
5018 int64_t composition_end_time = max_cts + (max_cts - max2_cts);
5019 if( !root->fragment
5020 && (min_offset <= INT32_MAX) && (max_offset <= INT32_MAX)
5021 && (min_cts <= INT32_MAX) && (composition_end_time <= INT32_MAX) )
5023 if( !cslg )
5025 if( isom_add_cslg( trak->mdia->minf->stbl ) )
5026 return -1;
5027 cslg = stbl->cslg;
5029 cslg->compositionToDTSShift = 0; /* We don't consider DTS shifting at present. */
5030 cslg->leastDecodeToDisplayDelta = min_offset;
5031 cslg->greatestDecodeToDisplayDelta = max_offset;
5032 cslg->compositionStartTime = min_cts;
5033 cslg->compositionEndTime = composition_end_time;
5035 else
5037 if( cslg )
5038 free( cslg );
5039 stbl->cslg = NULL;
5043 if( mdhd->duration > UINT32_MAX )
5044 mdhd->version = 1;
5045 return 0;
5048 static int isom_update_mvhd_duration( isom_moov_t *moov )
5050 if( !moov || !moov->mvhd )
5051 return -1;
5052 isom_mvhd_t *mvhd = moov->mvhd;
5053 mvhd->duration = 0;
5054 for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
5056 /* We pick maximum track duration as movie duration. */
5057 isom_trak_entry_t *data = (isom_trak_entry_t *)entry->data;
5058 if( !data || !data->tkhd )
5059 return -1;
5060 mvhd->duration = entry != moov->trak_list->head ? LSMASH_MAX( mvhd->duration, data->tkhd->duration ) : data->tkhd->duration;
5062 if( mvhd->duration > UINT32_MAX )
5063 mvhd->version = 1;
5064 return 0;
5067 static int isom_update_tkhd_duration( isom_trak_entry_t *trak )
5069 if( !trak || !trak->tkhd || !trak->root || !trak->root->moov )
5070 return -1;
5071 lsmash_root_t *root = trak->root;
5072 isom_tkhd_t *tkhd = trak->tkhd;
5073 tkhd->duration = 0;
5074 if( root->fragment || !trak->edts || !trak->edts->elst )
5076 /* If this presentation might be extended or this track doesn't have edit list, calculate track duration from media duration. */
5077 if( !trak->mdia || !trak->mdia->mdhd || !root->moov->mvhd || !trak->mdia->mdhd->timescale )
5078 return -1;
5079 if( !trak->mdia->mdhd->duration && isom_update_mdhd_duration( trak, 0 ) )
5080 return -1;
5081 tkhd->duration = trak->mdia->mdhd->duration * ((double)root->moov->mvhd->timescale / trak->mdia->mdhd->timescale);
5083 else
5085 /* If the presentation won't be extended and this track has any edit, then track duration is just the sum of the segment_duartions. */
5086 for( lsmash_entry_t *entry = trak->edts->elst->list->head; entry; entry = entry->next )
5088 isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data;
5089 if( !data )
5090 return -1;
5091 tkhd->duration += data->segment_duration;
5094 if( tkhd->duration > UINT32_MAX )
5095 tkhd->version = 1;
5096 if( !root->fragment && !tkhd->duration )
5097 tkhd->duration = tkhd->version == 1 ? 0xffffffffffffffff : 0xffffffff;
5098 return isom_update_mvhd_duration( root->moov );
5101 int lsmash_update_track_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta )
5103 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
5104 if( !trak )
5105 return -1;
5106 if( isom_update_mdhd_duration( trak, last_sample_delta ) )
5107 return -1;
5108 /* If the presentation won't be extended and this track has any edit, we don't change or update duration in tkhd. */
5109 return (!root->fragment && trak->edts && trak->edts->elst)
5110 ? isom_update_mvhd_duration( root->moov ) /* Only update movie duration. */
5111 : isom_update_tkhd_duration( trak ); /* Also update movie duration internally. */
5114 int lsmash_set_avc_config( lsmash_root_t *root, uint32_t track_ID, uint32_t entry_number,
5115 uint8_t configurationVersion, uint8_t AVCProfileIndication, uint8_t profile_compatibility, uint8_t AVCLevelIndication, uint8_t lengthSizeMinusOne,
5116 uint8_t chroma_format, uint8_t bit_depth_luma_minus8, uint8_t bit_depth_chroma_minus8 )
5118 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
5119 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
5120 return -1;
5121 isom_visual_entry_t *data = (isom_visual_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, entry_number );
5122 if( !data )
5123 return -1;
5124 isom_avcC_t *avcC = (isom_avcC_t *)data->avcC;
5125 if( !avcC )
5126 return -1;
5127 avcC->configurationVersion = configurationVersion;
5128 avcC->AVCProfileIndication = AVCProfileIndication;
5129 avcC->profile_compatibility = profile_compatibility;
5130 avcC->AVCLevelIndication = AVCLevelIndication;
5131 avcC->lengthSizeMinusOne = lengthSizeMinusOne;
5132 if( ISOM_REQUIRES_AVCC_EXTENSION( AVCProfileIndication ) )
5134 avcC->chroma_format = chroma_format;
5135 avcC->bit_depth_luma_minus8 = bit_depth_luma_minus8;
5136 avcC->bit_depth_chroma_minus8 = bit_depth_chroma_minus8;
5138 return 0;
5141 static int isom_update_bitrate_info( isom_mdia_t *mdia )
5143 if( !mdia || !mdia->mdhd || !mdia->minf || !mdia->minf->stbl
5144 || !mdia->minf->stbl->stsd || !mdia->minf->stbl->stsd->list
5145 || !mdia->minf->stbl->stsz || !mdia->minf->stbl->stts || !mdia->minf->stbl->stts->list )
5146 return -1;
5147 /* Not supporting multi sample entries yet. */
5148 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( mdia->minf->stbl->stsd->list, 1 );
5149 if( !sample_entry )
5150 return -1;
5151 struct bitrate_info_t
5153 uint32_t bufferSizeDB;
5154 uint32_t maxBitrate;
5155 uint32_t avgBitrate;
5156 } info = { 0, 0, 0 };
5157 uint32_t i = 0;
5158 uint32_t rate = 0;
5159 uint32_t time_wnd = 0;
5160 uint32_t timescale = mdia->mdhd->timescale;
5161 uint64_t dts = 0;
5162 isom_stsz_t *stsz = mdia->minf->stbl->stsz;
5163 lsmash_entry_t *stsz_entry = stsz->list ? stsz->list->head : NULL;
5164 lsmash_entry_t *stts_entry = mdia->minf->stbl->stts->list->head;
5165 isom_stts_entry_t *stts_data = NULL;
5166 while( stts_entry )
5168 uint32_t size;
5169 if( stsz->list )
5171 if( !stsz_entry )
5172 break;
5173 isom_stsz_entry_t *stsz_data = (isom_stsz_entry_t *)stsz_entry->data;
5174 if( !stsz_data )
5175 return -1;
5176 size = stsz_data->entry_size;
5177 stsz_entry = stsz_entry->next;
5179 else
5180 size = stsz->sample_size;
5181 if( stts_data )
5182 dts += stts_data->sample_delta;
5183 stts_data = (isom_stts_entry_t *)stts_entry->data;
5184 if( ++i == stts_data->sample_count )
5186 stts_entry = stts_entry->next;
5187 i = 0;
5189 if( info.bufferSizeDB < size )
5190 info.bufferSizeDB = size;
5191 info.avgBitrate += size;
5192 rate += size;
5193 if( dts > time_wnd + timescale )
5195 if( rate > info.maxBitrate )
5196 info.maxBitrate = rate;
5197 time_wnd = dts;
5198 rate = 0;
5201 double duration = (double)mdia->mdhd->duration / timescale;
5202 info.avgBitrate = (uint32_t)(info.avgBitrate / duration);
5203 if( !info.maxBitrate )
5204 info.maxBitrate = info.avgBitrate;
5205 /* move to bps */
5206 info.maxBitrate *= 8;
5207 info.avgBitrate *= 8;
5208 /* set bitrate info */
5209 switch( sample_entry->type )
5211 case ISOM_CODEC_TYPE_AVC1_VIDEO :
5212 case ISOM_CODEC_TYPE_AVC2_VIDEO :
5213 case ISOM_CODEC_TYPE_AVCP_VIDEO :
5215 isom_visual_entry_t *stsd_data = (isom_visual_entry_t *)sample_entry;
5216 if( !stsd_data )
5217 return -1;
5218 //isom_btrt_t *btrt = (isom_btrt_t *)stsd_data->btrt;
5219 isom_btrt_t *btrt = stsd_data->btrt;
5220 if( btrt )
5222 btrt->bufferSizeDB = info.bufferSizeDB;
5223 btrt->maxBitrate = info.maxBitrate;
5224 btrt->avgBitrate = info.avgBitrate;
5226 break;
5228 case ISOM_CODEC_TYPE_MP4A_AUDIO :
5230 isom_esds_t *esds = NULL;
5231 if( ((isom_audio_entry_t *)sample_entry)->version )
5233 /* MPEG-4 Audio in QTFF */
5234 isom_audio_entry_t *stsd_data = (isom_audio_entry_t *)sample_entry;
5235 if( !stsd_data || !stsd_data->wave || !stsd_data->wave->esds || !stsd_data->wave->esds->ES )
5236 return -1;
5237 esds = stsd_data->wave->esds;
5239 else
5241 isom_audio_entry_t *stsd_data = (isom_audio_entry_t *)sample_entry;
5242 if( !stsd_data || !stsd_data->esds || !stsd_data->esds->ES )
5243 return -1;
5244 esds = stsd_data->esds;
5246 /* FIXME: avgBitrate is 0 only if VBR in proper. */
5247 if( mp4sys_update_DecoderConfigDescriptor( esds->ES, info.bufferSizeDB, info.maxBitrate, 0 ) )
5248 return -1;
5249 break;
5251 case ISOM_CODEC_TYPE_ALAC_AUDIO :
5253 isom_audio_entry_t *alac = (isom_audio_entry_t *)sample_entry;
5254 if( !alac )
5255 return -1;
5256 if( alac->exdata_length < 36 || !alac->exdata )
5258 isom_wave_t *wave = alac->wave;
5259 if( !wave || wave->exdata_length < 36 || !wave->exdata )
5260 return -1;
5261 break; /* Apparently, average bitrate field is 0. */
5263 uint8_t *exdata = (uint8_t *)alac->exdata + 28;
5264 exdata[0] = (info.avgBitrate >> 24) & 0xff;
5265 exdata[1] = (info.avgBitrate >> 16) & 0xff;
5266 exdata[2] = (info.avgBitrate >> 8) & 0xff;
5267 exdata[3] = info.avgBitrate & 0xff;
5268 break;
5270 default :
5271 break;
5273 return 0;
5276 static int isom_check_mandatory_boxes( lsmash_root_t *root )
5278 if( !root )
5279 return -1;
5280 if( !root->moov || !root->moov->mvhd )
5281 return -1;
5282 if( !root->moov->trak_list )
5283 return -1;
5284 /* A movie requires at least one track. */
5285 if( !root->moov->trak_list->head )
5286 return -1;
5287 for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
5289 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
5290 if( !trak
5291 || !trak->tkhd
5292 || !trak->mdia
5293 || !trak->mdia->mdhd
5294 || !trak->mdia->hdlr
5295 || !trak->mdia->minf
5296 || !trak->mdia->minf->dinf
5297 || !trak->mdia->minf->dinf->dref
5298 || !trak->mdia->minf->stbl
5299 || !trak->mdia->minf->stbl->stsd
5300 || !trak->mdia->minf->stbl->stsz
5301 || !trak->mdia->minf->stbl->stts
5302 || !trak->mdia->minf->stbl->stsc
5303 || !trak->mdia->minf->stbl->stco )
5304 return -1;
5305 if( root->qt_compatible && !trak->mdia->minf->hdlr )
5306 return -1;
5307 isom_stbl_t *stbl = trak->mdia->minf->stbl;
5308 if( !stbl->stsd->list || !stbl->stsd->list->head )
5309 return -1;
5310 if( !root->fragment
5311 && (!stbl->stsd->list || !stbl->stsd->list->head
5312 || !stbl->stts->list || !stbl->stts->list->head
5313 || !stbl->stsc->list || !stbl->stsc->list->head
5314 || !stbl->stco->list || !stbl->stco->list->head) )
5315 return -1;
5317 if( !root->fragment )
5318 return 0;
5319 if( !root->moov->mvex || !root->moov->mvex->trex_list )
5320 return -1;
5321 for( lsmash_entry_t *entry = root->moov->mvex->trex_list->head; entry; entry = entry->next )
5322 if( !entry->data ) /* trex */
5323 return -1;
5324 return 0;
5327 static inline uint64_t isom_get_current_mp4time( void )
5329 return (uint64_t)time( NULL ) + ISOM_MAC_EPOCH_OFFSET;
5332 static int isom_set_media_creation_time( isom_trak_entry_t *trak, uint64_t current_mp4time )
5334 if( !trak->mdia || !trak->mdia->mdhd )
5335 return -1;
5336 isom_mdhd_t *mdhd = trak->mdia->mdhd;
5337 if( !mdhd->creation_time )
5338 mdhd->creation_time = mdhd->modification_time = current_mp4time;
5339 return 0;
5342 static int isom_set_track_creation_time( isom_trak_entry_t *trak, uint64_t current_mp4time )
5344 if( !trak || !trak->tkhd )
5345 return -1;
5346 isom_tkhd_t *tkhd = trak->tkhd;
5347 if( !tkhd->creation_time )
5348 tkhd->creation_time = tkhd->modification_time = current_mp4time;
5349 if( isom_set_media_creation_time( trak, current_mp4time ) )
5350 return -1;
5351 return 0;
5354 static int isom_set_movie_creation_time( lsmash_root_t *root )
5356 if( !root || !root->moov || !root->moov->mvhd || !root->moov->trak_list )
5357 return -1;
5358 uint64_t current_mp4time = isom_get_current_mp4time();
5359 for( uint32_t i = 1; i <= root->moov->trak_list->entry_count; i++ )
5360 if( isom_set_track_creation_time( isom_get_trak( root, i ), current_mp4time ) )
5361 return -1;
5362 isom_mvhd_t *mvhd = root->moov->mvhd;
5363 if( !mvhd->creation_time )
5364 mvhd->creation_time = mvhd->modification_time = current_mp4time;
5365 return 0;
5368 #define CHECK_LARGESIZE( size ) if( (size) > UINT32_MAX ) (size) += 8
5370 static uint64_t isom_update_mvhd_size( isom_mvhd_t *mvhd )
5372 if( !mvhd )
5373 return 0;
5374 mvhd->version = 0;
5375 if( mvhd->creation_time > UINT32_MAX || mvhd->modification_time > UINT32_MAX || mvhd->duration > UINT32_MAX )
5376 mvhd->version = 1;
5377 mvhd->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 96 + (uint64_t)mvhd->version * 12;
5378 CHECK_LARGESIZE( mvhd->size );
5379 return mvhd->size;
5382 static uint64_t isom_update_iods_size( isom_iods_t *iods )
5384 if( !iods || !iods->OD )
5385 return 0;
5386 iods->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + mp4sys_update_ObjectDescriptor_size( iods->OD );
5387 CHECK_LARGESIZE( iods->size );
5388 return iods->size;
5391 static uint64_t isom_update_tkhd_size( isom_tkhd_t *tkhd )
5393 if( !tkhd )
5394 return 0;
5395 tkhd->version = 0;
5396 if( tkhd->creation_time > UINT32_MAX || tkhd->modification_time > UINT32_MAX || tkhd->duration > UINT32_MAX )
5397 tkhd->version = 1;
5398 tkhd->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 80 + (uint64_t)tkhd->version * 12;
5399 CHECK_LARGESIZE( tkhd->size );
5400 return tkhd->size;
5403 static uint64_t isom_update_clef_size( isom_clef_t *clef )
5405 if( !clef )
5406 return 0;
5407 clef->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 8;
5408 CHECK_LARGESIZE( clef->size );
5409 return clef->size;
5412 static uint64_t isom_update_prof_size( isom_prof_t *prof )
5414 if( !prof )
5415 return 0;
5416 prof->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 8;
5417 CHECK_LARGESIZE( prof->size );
5418 return prof->size;
5421 static uint64_t isom_update_enof_size( isom_enof_t *enof )
5423 if( !enof )
5424 return 0;
5425 enof->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 8;
5426 CHECK_LARGESIZE( enof->size );
5427 return enof->size;
5430 static uint64_t isom_update_tapt_size( isom_tapt_t *tapt )
5432 if( !tapt )
5433 return 0;
5434 tapt->size = ISOM_DEFAULT_BOX_HEADER_SIZE
5435 + isom_update_clef_size( tapt->clef )
5436 + isom_update_prof_size( tapt->prof )
5437 + isom_update_enof_size( tapt->enof );
5438 CHECK_LARGESIZE( tapt->size );
5439 return tapt->size;
5442 static uint64_t isom_update_elst_size( isom_elst_t *elst )
5444 if( !elst || !elst->list )
5445 return 0;
5446 uint32_t i = 0;
5447 elst->version = 0;
5448 for( lsmash_entry_t *entry = elst->list->head; entry; entry = entry->next, i++ )
5450 isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data;
5451 if( data->segment_duration > UINT32_MAX || data->media_time > INT32_MAX || data->media_time < INT32_MIN )
5452 elst->version = 1;
5454 elst->size = ISOM_DEFAULT_LIST_FULLBOX_HEADER_SIZE + (uint64_t)i * ( elst->version ? 20 : 12 );
5455 CHECK_LARGESIZE( elst->size );
5456 return elst->size;
5459 static uint64_t isom_update_edts_size( isom_edts_t *edts )
5461 if( !edts )
5462 return 0;
5463 edts->size = ISOM_DEFAULT_BOX_HEADER_SIZE + isom_update_elst_size( edts->elst );
5464 CHECK_LARGESIZE( edts->size );
5465 return edts->size;
5468 static uint64_t isom_update_tref_size( isom_tref_t *tref )
5470 if( !tref )
5471 return 0;
5472 tref->size = ISOM_DEFAULT_BOX_HEADER_SIZE;
5473 if( tref->ref_list )
5474 for( lsmash_entry_t *entry = tref->ref_list->head; entry; entry = entry->next )
5476 isom_tref_type_t *ref = (isom_tref_type_t *)entry->data;
5477 ref->size = ISOM_DEFAULT_BOX_HEADER_SIZE + (uint64_t)ref->ref_count * 4;
5478 CHECK_LARGESIZE( ref->size );
5479 tref->size += ref->size;
5481 CHECK_LARGESIZE( tref->size );
5482 return tref->size;
5485 static uint64_t isom_update_mdhd_size( isom_mdhd_t *mdhd )
5487 if( !mdhd )
5488 return 0;
5489 mdhd->version = 0;
5490 if( mdhd->creation_time > UINT32_MAX || mdhd->modification_time > UINT32_MAX || mdhd->duration > UINT32_MAX )
5491 mdhd->version = 1;
5492 mdhd->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 20 + (uint64_t)mdhd->version * 12;
5493 CHECK_LARGESIZE( mdhd->size );
5494 return mdhd->size;
5497 static uint64_t isom_update_hdlr_size( isom_hdlr_t *hdlr )
5499 if( !hdlr )
5500 return 0;
5501 hdlr->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 20 + (uint64_t)hdlr->componentName_length;
5502 CHECK_LARGESIZE( hdlr->size );
5503 return hdlr->size;
5506 static uint64_t isom_update_dref_entry_size( isom_dref_entry_t *urln )
5508 if( !urln )
5509 return 0;
5510 urln->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + (uint64_t)urln->name_length + urln->location_length;
5511 CHECK_LARGESIZE( urln->size );
5512 return urln->size;
5515 static uint64_t isom_update_dref_size( isom_dref_t *dref )
5517 if( !dref || !dref->list )
5518 return 0;
5519 dref->size = ISOM_DEFAULT_LIST_FULLBOX_HEADER_SIZE;
5520 if( dref->list )
5521 for( lsmash_entry_t *entry = dref->list->head; entry; entry = entry->next )
5523 isom_dref_entry_t *data = (isom_dref_entry_t *)entry->data;
5524 dref->size += isom_update_dref_entry_size( data );
5526 CHECK_LARGESIZE( dref->size );
5527 return dref->size;
5530 static uint64_t isom_update_dinf_size( isom_dinf_t *dinf )
5532 if( !dinf )
5533 return 0;
5534 dinf->size = ISOM_DEFAULT_BOX_HEADER_SIZE + isom_update_dref_size( dinf->dref );
5535 CHECK_LARGESIZE( dinf->size );
5536 return dinf->size;
5539 static uint64_t isom_update_vmhd_size( isom_vmhd_t *vmhd )
5541 if( !vmhd )
5542 return 0;
5543 vmhd->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 8;
5544 CHECK_LARGESIZE( vmhd->size );
5545 return vmhd->size;
5548 static uint64_t isom_update_smhd_size( isom_smhd_t *smhd )
5550 if( !smhd )
5551 return 0;
5552 smhd->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 4;
5553 CHECK_LARGESIZE( smhd->size );
5554 return smhd->size;
5557 static uint64_t isom_update_hmhd_size( isom_hmhd_t *hmhd )
5559 if( !hmhd )
5560 return 0;
5561 hmhd->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 16;
5562 CHECK_LARGESIZE( hmhd->size );
5563 return hmhd->size;
5566 static uint64_t isom_update_nmhd_size( isom_nmhd_t *nmhd )
5568 if( !nmhd )
5569 return 0;
5570 nmhd->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE;
5571 CHECK_LARGESIZE( nmhd->size );
5572 return nmhd->size;
5575 static uint64_t isom_update_gmin_size( isom_gmin_t *gmin )
5577 if( !gmin )
5578 return 0;
5579 gmin->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 12;
5580 CHECK_LARGESIZE( gmin->size );
5581 return gmin->size;
5584 static uint64_t isom_update_text_size( isom_text_t *text )
5586 if( !text )
5587 return 0;
5588 text->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 36;
5589 CHECK_LARGESIZE( text->size );
5590 return text->size;
5593 static uint64_t isom_update_gmhd_size( isom_gmhd_t *gmhd )
5595 if( !gmhd )
5596 return 0;
5597 gmhd->size = ISOM_DEFAULT_BOX_HEADER_SIZE
5598 + isom_update_gmin_size( gmhd->gmin )
5599 + isom_update_text_size( gmhd->text );
5600 CHECK_LARGESIZE( gmhd->size );
5601 return gmhd->size;
5604 static uint64_t isom_update_pasp_size( isom_pasp_t *pasp )
5606 if( !pasp )
5607 return 0;
5608 pasp->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 8;
5609 CHECK_LARGESIZE( pasp->size );
5610 return pasp->size;
5613 static uint64_t isom_update_clap_size( isom_clap_t *clap )
5615 if( !clap )
5616 return 0;
5617 clap->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 32;
5618 CHECK_LARGESIZE( clap->size );
5619 return clap->size;
5622 static uint64_t isom_update_colr_size( isom_colr_t *colr )
5624 if( !colr || colr->color_parameter_type == QT_COLOR_PARAMETER_TYPE_PROF )
5625 return 0;
5626 colr->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 10;
5627 CHECK_LARGESIZE( colr->size );
5628 return colr->size;
5631 static uint64_t isom_update_stsl_size( isom_stsl_t *stsl )
5633 if( !stsl )
5634 return 0;
5635 stsl->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 6;
5636 CHECK_LARGESIZE( stsl->size );
5637 return stsl->size;
5640 static uint64_t isom_update_esds_size( isom_esds_t *esds )
5642 if( !esds )
5643 return 0;
5644 esds->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + mp4sys_update_ES_Descriptor_size( esds->ES );
5645 CHECK_LARGESIZE( esds->size );
5646 return esds->size;
5649 static uint64_t isom_update_avcC_size( isom_avcC_t *avcC )
5651 if( !avcC || !avcC->sequenceParameterSets || !avcC->pictureParameterSets )
5652 return 0;
5653 uint64_t size = ISOM_DEFAULT_BOX_HEADER_SIZE + 7;
5654 for( lsmash_entry_t *entry = avcC->sequenceParameterSets->head; entry; entry = entry->next )
5656 isom_avcC_ps_entry_t *data = (isom_avcC_ps_entry_t *)entry->data;
5657 size += 2 + data->parameterSetLength;
5659 for( lsmash_entry_t *entry = avcC->pictureParameterSets->head; entry; entry = entry->next )
5661 isom_avcC_ps_entry_t *data = (isom_avcC_ps_entry_t *)entry->data;
5662 size += 2 + data->parameterSetLength;
5664 if( ISOM_REQUIRES_AVCC_EXTENSION( avcC->AVCProfileIndication ) )
5666 size += 4;
5667 for( lsmash_entry_t *entry = avcC->sequenceParameterSetExt->head; entry; entry = entry->next )
5669 isom_avcC_ps_entry_t *data = (isom_avcC_ps_entry_t *)entry->data;
5670 size += 2 + data->parameterSetLength;
5673 avcC->size = size;
5674 CHECK_LARGESIZE( avcC->size );
5675 return avcC->size;
5678 static uint64_t isom_update_btrt_size( isom_btrt_t *btrt )
5680 if( !btrt )
5681 return 0;
5682 btrt->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 12;
5683 CHECK_LARGESIZE( btrt->size );
5684 return btrt->size;
5687 static uint64_t isom_update_visual_entry_size( isom_visual_entry_t *visual )
5689 if( !visual )
5690 return 0;
5691 visual->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 78
5692 + isom_update_clap_size( visual->clap )
5693 + isom_update_pasp_size( visual->pasp )
5694 + isom_update_colr_size( visual->colr )
5695 + isom_update_stsl_size( visual->stsl )
5696 + isom_update_esds_size( visual->esds )
5697 + isom_update_avcC_size( visual->avcC )
5698 + isom_update_btrt_size( visual->btrt );
5699 CHECK_LARGESIZE( visual->size );
5700 return visual->size;
5703 #if 0
5704 static uint64_t isom_update_mp4s_entry_size( isom_mp4s_entry_t *mp4s )
5706 if( !mp4s || mp4s->type != ISOM_CODEC_TYPE_MP4S_SYSTEM )
5707 return 0;
5708 mp4s->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 8 + isom_update_esds_size( mp4s->esds );
5709 CHECK_LARGESIZE( mp4s->size );
5710 return mp4s->size;
5712 #endif
5714 static uint64_t isom_update_frma_size( isom_frma_t *frma )
5716 if( !frma )
5717 return 0;
5718 frma->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 4;
5719 CHECK_LARGESIZE( frma->size );
5720 return frma->size;
5723 static uint64_t isom_update_enda_size( isom_enda_t *enda )
5725 if( !enda )
5726 return 0;
5727 enda->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 2;
5728 CHECK_LARGESIZE( enda->size );
5729 return enda->size;
5732 static uint64_t isom_update_mp4a_size( isom_mp4a_t *mp4a )
5734 if( !mp4a )
5735 return 0;
5736 mp4a->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 4;
5737 CHECK_LARGESIZE( mp4a->size );
5738 return mp4a->size;
5741 static uint64_t isom_update_terminator_size( isom_terminator_t *terminator )
5743 if( !terminator )
5744 return 0;
5745 terminator->size = ISOM_DEFAULT_BOX_HEADER_SIZE;
5746 CHECK_LARGESIZE( terminator->size );
5747 return terminator->size;
5750 static uint64_t isom_update_wave_size( isom_wave_t *wave )
5752 if( !wave )
5753 return 0;
5754 wave->size = ISOM_DEFAULT_BOX_HEADER_SIZE
5755 + isom_update_frma_size( wave->frma )
5756 + isom_update_enda_size( wave->enda )
5757 + isom_update_mp4a_size( wave->mp4a )
5758 + isom_update_esds_size( wave->esds )
5759 + isom_update_terminator_size( wave->terminator )
5760 + (uint64_t)wave->exdata_length;
5761 CHECK_LARGESIZE( wave->size );
5762 return wave->size;
5765 static uint64_t isom_update_chan_size( isom_chan_t *chan )
5767 if( !chan )
5768 return 0;
5769 chan->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 12 + 20 * (uint64_t)chan->numberChannelDescriptions;
5770 CHECK_LARGESIZE( chan->size );
5771 return chan->size;
5774 static uint64_t isom_update_audio_entry_size( isom_audio_entry_t *audio )
5776 if( !audio )
5777 return 0;
5778 audio->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 28
5779 + isom_update_esds_size( audio->esds )
5780 + isom_update_wave_size( audio->wave )
5781 + isom_update_chan_size( audio->chan )
5782 + (uint64_t)audio->exdata_length;
5783 if( audio->version == 1 )
5784 audio->size += 16;
5785 else if( audio->version == 2 )
5786 audio->size += 36;
5787 CHECK_LARGESIZE( audio->size );
5788 return audio->size;
5791 static uint64_t isom_update_text_entry_size( isom_text_entry_t *text )
5793 if( !text )
5794 return 0;
5795 text->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 51 + (uint64_t)text->font_name_length;
5796 CHECK_LARGESIZE( text->size );
5797 return text->size;
5800 static uint64_t isom_update_ftab_size( isom_ftab_t *ftab )
5802 if( !ftab || !ftab->list )
5803 return 0;
5804 ftab->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 2;
5805 for( lsmash_entry_t *entry = ftab->list->head; entry; entry = entry->next )
5807 isom_font_record_t *data = (isom_font_record_t *)entry->data;
5808 ftab->size += 3 + data->font_name_length;
5810 CHECK_LARGESIZE( ftab->size );
5811 return ftab->size;
5814 static uint64_t isom_update_tx3g_entry_size( isom_tx3g_entry_t *tx3g )
5816 if( !tx3g )
5817 return 0;
5818 tx3g->size = ISOM_DEFAULT_BOX_HEADER_SIZE + 38 + isom_update_ftab_size( tx3g->ftab );
5819 CHECK_LARGESIZE( tx3g->size );
5820 return tx3g->size;
5823 static uint64_t isom_update_stsd_size( isom_stsd_t *stsd )
5825 if( !stsd || !stsd->list )
5826 return 0;
5827 uint64_t size = ISOM_DEFAULT_LIST_FULLBOX_HEADER_SIZE;
5828 for( lsmash_entry_t *entry = stsd->list->head; entry; entry = entry->next )
5830 isom_sample_entry_t *data = (isom_sample_entry_t *)entry->data;
5831 switch( data->type )
5833 case ISOM_CODEC_TYPE_AVC1_VIDEO :
5834 #if 0
5835 case ISOM_CODEC_TYPE_AVC2_VIDEO :
5836 case ISOM_CODEC_TYPE_AVCP_VIDEO :
5837 case ISOM_CODEC_TYPE_SVC1_VIDEO :
5838 case ISOM_CODEC_TYPE_MVC1_VIDEO :
5839 case ISOM_CODEC_TYPE_MVC2_VIDEO :
5840 case ISOM_CODEC_TYPE_MP4V_VIDEO :
5841 case ISOM_CODEC_TYPE_DRAC_VIDEO :
5842 case ISOM_CODEC_TYPE_ENCV_VIDEO :
5843 case ISOM_CODEC_TYPE_MJP2_VIDEO :
5844 case ISOM_CODEC_TYPE_S263_VIDEO :
5845 case ISOM_CODEC_TYPE_VC_1_VIDEO :
5846 #endif
5847 size += isom_update_visual_entry_size( (isom_visual_entry_t *)data );
5848 break;
5849 #if 0
5850 case ISOM_CODEC_TYPE_MP4S_SYSTEM :
5851 size += isom_update_mp4s_entry_size( (isom_mp4s_entry_t *)data );
5852 break;
5853 #endif
5854 case ISOM_CODEC_TYPE_MP4A_AUDIO :
5855 case ISOM_CODEC_TYPE_AC_3_AUDIO :
5856 case ISOM_CODEC_TYPE_ALAC_AUDIO :
5857 case ISOM_CODEC_TYPE_SAMR_AUDIO :
5858 case ISOM_CODEC_TYPE_SAWB_AUDIO :
5859 case QT_CODEC_TYPE_23NI_AUDIO :
5860 case QT_CODEC_TYPE_NONE_AUDIO :
5861 case QT_CODEC_TYPE_LPCM_AUDIO :
5862 case QT_CODEC_TYPE_RAW_AUDIO :
5863 case QT_CODEC_TYPE_SOWT_AUDIO :
5864 case QT_CODEC_TYPE_TWOS_AUDIO :
5865 case QT_CODEC_TYPE_FL32_AUDIO :
5866 case QT_CODEC_TYPE_FL64_AUDIO :
5867 case QT_CODEC_TYPE_IN24_AUDIO :
5868 case QT_CODEC_TYPE_IN32_AUDIO :
5869 case QT_CODEC_TYPE_NOT_SPECIFIED :
5870 #ifdef LSMASH_DEMUXER_ENABLED
5871 case ISOM_CODEC_TYPE_EC_3_AUDIO :
5872 #endif
5873 #if 0
5874 case ISOM_CODEC_TYPE_DRA1_AUDIO :
5875 case ISOM_CODEC_TYPE_DTSC_AUDIO :
5876 case ISOM_CODEC_TYPE_DTSH_AUDIO :
5877 case ISOM_CODEC_TYPE_DTSL_AUDIO :
5878 case ISOM_CODEC_TYPE_ENCA_AUDIO :
5879 case ISOM_CODEC_TYPE_G719_AUDIO :
5880 case ISOM_CODEC_TYPE_G726_AUDIO :
5881 case ISOM_CODEC_TYPE_M4AE_AUDIO :
5882 case ISOM_CODEC_TYPE_MLPA_AUDIO :
5883 case ISOM_CODEC_TYPE_RAW_AUDIO :
5884 case ISOM_CODEC_TYPE_SAWP_AUDIO :
5885 case ISOM_CODEC_TYPE_SEVC_AUDIO :
5886 case ISOM_CODEC_TYPE_SQCP_AUDIO :
5887 case ISOM_CODEC_TYPE_SSMV_AUDIO :
5888 case ISOM_CODEC_TYPE_TWOS_AUDIO :
5889 #endif
5890 size += isom_update_audio_entry_size( (isom_audio_entry_t *)data );
5891 break;
5892 case ISOM_CODEC_TYPE_TX3G_TEXT :
5893 size += isom_update_tx3g_entry_size( (isom_tx3g_entry_t *)data );
5894 break;
5895 case QT_CODEC_TYPE_TEXT_TEXT :
5896 size += isom_update_text_entry_size( (isom_text_entry_t *)data );
5897 break;
5898 default :
5899 break;
5902 stsd->size = size;
5903 CHECK_LARGESIZE( stsd->size );
5904 return stsd->size;
5907 static uint64_t isom_update_stts_size( isom_stts_t *stts )
5909 if( !stts || !stts->list )
5910 return 0;
5911 stts->size = ISOM_DEFAULT_LIST_FULLBOX_HEADER_SIZE + (uint64_t)stts->list->entry_count * 8;
5912 CHECK_LARGESIZE( stts->size );
5913 return stts->size;
5916 static uint64_t isom_update_ctts_size( isom_ctts_t *ctts )
5918 if( !ctts || !ctts->list )
5919 return 0;
5920 ctts->size = ISOM_DEFAULT_LIST_FULLBOX_HEADER_SIZE + (uint64_t)ctts->list->entry_count * 8;
5921 CHECK_LARGESIZE( ctts->size );
5922 return ctts->size;
5925 static uint64_t isom_update_cslg_size( isom_cslg_t *cslg )
5927 if( !cslg )
5928 return 0;
5929 cslg->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 20;
5930 CHECK_LARGESIZE( cslg->size );
5931 return cslg->size;
5934 static uint64_t isom_update_stsz_size( isom_stsz_t *stsz )
5936 if( !stsz )
5937 return 0;
5938 stsz->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 8 + ( stsz->list ? (uint64_t)stsz->list->entry_count * 4 : 0 );
5939 CHECK_LARGESIZE( stsz->size );
5940 return stsz->size;
5943 static uint64_t isom_update_stss_size( isom_stss_t *stss )
5945 if( !stss || !stss->list )
5946 return 0;
5947 stss->size = ISOM_DEFAULT_LIST_FULLBOX_HEADER_SIZE + (uint64_t)stss->list->entry_count * 4;
5948 CHECK_LARGESIZE( stss->size );
5949 return stss->size;
5952 static uint64_t isom_update_stps_size( isom_stps_t *stps )
5954 if( !stps || !stps->list )
5955 return 0;
5956 stps->size = ISOM_DEFAULT_LIST_FULLBOX_HEADER_SIZE + (uint64_t)stps->list->entry_count * 4;
5957 CHECK_LARGESIZE( stps->size );
5958 return stps->size;
5961 static uint64_t isom_update_sdtp_size( isom_sdtp_t *sdtp )
5963 if( !sdtp || !sdtp->list )
5964 return 0;
5965 sdtp->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + (uint64_t)sdtp->list->entry_count;
5966 CHECK_LARGESIZE( sdtp->size );
5967 return sdtp->size;
5970 static uint64_t isom_update_stsc_size( isom_stsc_t *stsc )
5972 if( !stsc || !stsc->list )
5973 return 0;
5974 stsc->size = ISOM_DEFAULT_LIST_FULLBOX_HEADER_SIZE + (uint64_t)stsc->list->entry_count * 12;
5975 CHECK_LARGESIZE( stsc->size );
5976 return stsc->size;
5979 static uint64_t isom_update_stco_size( isom_stco_t *stco )
5981 if( !stco || !stco->list )
5982 return 0;
5983 stco->size = ISOM_DEFAULT_LIST_FULLBOX_HEADER_SIZE + (uint64_t)stco->list->entry_count * (stco->large_presentation ? 8 : 4);
5984 CHECK_LARGESIZE( stco->size );
5985 return stco->size;
5988 static uint64_t isom_update_sbgp_size( isom_sbgp_entry_t *sbgp )
5990 if( !sbgp || !sbgp->list )
5991 return 0;
5992 sbgp->size = ISOM_DEFAULT_LIST_FULLBOX_HEADER_SIZE + 4 + (uint64_t)sbgp->list->entry_count * 8;
5993 CHECK_LARGESIZE( sbgp->size );
5994 return sbgp->size;
5997 static uint64_t isom_update_sgpd_size( isom_sgpd_entry_t *sgpd )
5999 if( !sgpd || !sgpd->list )
6000 return 0;
6001 uint64_t size = ISOM_DEFAULT_LIST_FULLBOX_HEADER_SIZE + (1 + (sgpd->version == 1)) * 4;
6002 size += (uint64_t)sgpd->list->entry_count * ((sgpd->version == 1) && !sgpd->default_length) * 4;
6003 switch( sgpd->grouping_type )
6005 case ISOM_GROUP_TYPE_RAP :
6006 size += sgpd->list->entry_count;
6007 break;
6008 case ISOM_GROUP_TYPE_ROLL :
6009 size += (uint64_t)sgpd->list->entry_count * 2;
6010 break;
6011 default :
6012 /* We don't consider other grouping types currently. */
6013 break;
6015 sgpd->size = size;
6016 CHECK_LARGESIZE( sgpd->size );
6017 return sgpd->size;
6020 static uint64_t isom_update_stbl_size( isom_stbl_t *stbl )
6022 if( !stbl )
6023 return 0;
6024 stbl->size = ISOM_DEFAULT_BOX_HEADER_SIZE
6025 + isom_update_stsd_size( stbl->stsd )
6026 + isom_update_stts_size( stbl->stts )
6027 + isom_update_ctts_size( stbl->ctts )
6028 + isom_update_cslg_size( stbl->cslg )
6029 + isom_update_stsz_size( stbl->stsz )
6030 + isom_update_stss_size( stbl->stss )
6031 + isom_update_stps_size( stbl->stps )
6032 + isom_update_sdtp_size( stbl->sdtp )
6033 + isom_update_stsc_size( stbl->stsc )
6034 + isom_update_stco_size( stbl->stco );
6035 if( stbl->sgpd_list )
6036 for( lsmash_entry_t *entry = stbl->sgpd_list->head; entry; entry = entry->next )
6037 stbl->size += isom_update_sgpd_size( (isom_sgpd_entry_t *)entry->data );
6038 if( stbl->sbgp_list )
6039 for( lsmash_entry_t *entry = stbl->sbgp_list->head; entry; entry = entry->next )
6040 stbl->size += isom_update_sbgp_size( (isom_sbgp_entry_t *)entry->data );
6041 CHECK_LARGESIZE( stbl->size );
6042 return stbl->size;
6045 static uint64_t isom_update_minf_size( isom_minf_t *minf )
6047 if( !minf )
6048 return 0;
6049 minf->size = ISOM_DEFAULT_BOX_HEADER_SIZE
6050 + isom_update_vmhd_size( minf->vmhd )
6051 + isom_update_smhd_size( minf->smhd )
6052 + isom_update_hmhd_size( minf->hmhd )
6053 + isom_update_nmhd_size( minf->nmhd )
6054 + isom_update_gmhd_size( minf->gmhd )
6055 + isom_update_hdlr_size( minf->hdlr )
6056 + isom_update_dinf_size( minf->dinf )
6057 + isom_update_stbl_size( minf->stbl );
6058 CHECK_LARGESIZE( minf->size );
6059 return minf->size;
6062 static uint64_t isom_update_mdia_size( isom_mdia_t *mdia )
6064 if( !mdia )
6065 return 0;
6066 mdia->size = ISOM_DEFAULT_BOX_HEADER_SIZE
6067 + isom_update_mdhd_size( mdia->mdhd )
6068 + isom_update_hdlr_size( mdia->hdlr )
6069 + isom_update_minf_size( mdia->minf );
6070 CHECK_LARGESIZE( mdia->size );
6071 return mdia->size;
6074 static uint64_t isom_update_chpl_size( isom_chpl_t *chpl )
6076 if( !chpl )
6077 return 0;
6078 chpl->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 4 * (chpl->version == 1) + 1;
6079 for( lsmash_entry_t *entry = chpl->list->head; entry; entry = entry->next )
6081 isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data;
6082 chpl->size += 9 + data->chapter_name_length;
6084 CHECK_LARGESIZE( chpl->size );
6085 return chpl->size;
6088 static uint64_t isom_update_udta_size( isom_udta_t *udta_moov, isom_udta_t *udta_trak )
6090 isom_udta_t *udta = udta_trak ? udta_trak : udta_moov ? udta_moov : NULL;
6091 if( !udta )
6092 return 0;
6093 udta->size = ISOM_DEFAULT_BOX_HEADER_SIZE + ( udta_moov ? isom_update_chpl_size( udta->chpl ) : 0 );
6094 CHECK_LARGESIZE( udta->size );
6095 return udta->size;
6098 static uint64_t isom_update_trak_entry_size( isom_trak_entry_t *trak )
6100 if( !trak )
6101 return 0;
6102 trak->size = ISOM_DEFAULT_BOX_HEADER_SIZE
6103 + isom_update_tkhd_size( trak->tkhd )
6104 + isom_update_tapt_size( trak->tapt )
6105 + isom_update_edts_size( trak->edts )
6106 + isom_update_tref_size( trak->tref )
6107 + isom_update_mdia_size( trak->mdia )
6108 + isom_update_udta_size( NULL, trak->udta );
6109 CHECK_LARGESIZE( trak->size );
6110 return trak->size;
6113 static uint64_t isom_update_mehd_size( isom_mehd_t *mehd )
6115 if( !mehd )
6116 return 0;
6117 if( mehd->fragment_duration > UINT32_MAX )
6118 mehd->version = 1;
6119 mehd->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 4 * (1 + (mehd->version == 1));
6120 CHECK_LARGESIZE( mehd->size );
6121 return mehd->size;
6124 static uint64_t isom_update_trex_entry_size( isom_trex_entry_t *trex )
6126 if( !trex )
6127 return 0;
6128 trex->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 20;
6129 CHECK_LARGESIZE( trex->size );
6130 return trex->size;
6133 static uint64_t isom_update_mvex_size( isom_mvex_t *mvex )
6135 if( !mvex )
6136 return 0;
6137 mvex->size = ISOM_DEFAULT_BOX_HEADER_SIZE;
6138 if( mvex->trex_list )
6139 for( lsmash_entry_t *entry = mvex->trex_list->head; entry; entry = entry->next )
6141 isom_trex_entry_t *trex = (isom_trex_entry_t *)entry->data;
6142 mvex->size += isom_update_trex_entry_size( trex );
6144 if( mvex->root->bs->stream != stdout )
6145 mvex->size += mvex->mehd ? isom_update_mehd_size( mvex->mehd ) : 20; /* 20 bytes is of placeholder. */
6146 CHECK_LARGESIZE( mvex->size );
6147 return mvex->size;
6150 static int isom_update_moov_size( isom_moov_t *moov )
6152 if( !moov )
6153 return -1;
6154 moov->size = ISOM_DEFAULT_BOX_HEADER_SIZE
6155 + isom_update_mvhd_size( moov->mvhd )
6156 + isom_update_iods_size( moov->iods )
6157 + isom_update_udta_size( moov->udta, NULL )
6158 + isom_update_mvex_size( moov->mvex );
6159 if( moov->trak_list )
6160 for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
6162 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
6163 moov->size += isom_update_trak_entry_size( trak );
6165 CHECK_LARGESIZE( moov->size );
6166 return 0;
6169 static uint64_t isom_update_mfhd_size( isom_mfhd_t *mfhd )
6171 if( !mfhd )
6172 return 0;
6173 mfhd->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 4;
6174 CHECK_LARGESIZE( mfhd->size );
6175 return mfhd->size;
6178 static uint64_t isom_update_tfhd_size( isom_tfhd_t *tfhd )
6180 if( !tfhd )
6181 return 0;
6182 tfhd->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE
6184 + 8 * !!( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT )
6185 + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT )
6186 + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT )
6187 + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT )
6188 + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT );
6189 CHECK_LARGESIZE( tfhd->size );
6190 return tfhd->size;
6193 static uint64_t isom_update_trun_entry_size( isom_trun_entry_t *trun )
6195 if( !trun )
6196 return 0;
6197 trun->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE
6199 + 4 * !!( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT )
6200 + 4 * !!( trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT );
6201 uint64_t row_size = 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT )
6202 + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT )
6203 + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT )
6204 + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT );
6205 trun->size += row_size * trun->sample_count;
6206 CHECK_LARGESIZE( trun->size );
6207 return trun->size;
6210 static uint64_t isom_update_traf_entry_size( isom_traf_entry_t *traf )
6212 if( !traf )
6213 return 0;
6214 traf->size = ISOM_DEFAULT_BOX_HEADER_SIZE + isom_update_tfhd_size( traf->tfhd );
6215 if( traf->trun_list )
6216 for( lsmash_entry_t *entry = traf->trun_list->head; entry; entry = entry->next )
6218 isom_trun_entry_t *trun = (isom_trun_entry_t *)entry->data;
6219 traf->size += isom_update_trun_entry_size( trun );
6221 CHECK_LARGESIZE( traf->size );
6222 return traf->size;
6225 static int isom_update_moof_entry_size( isom_moof_entry_t *moof )
6227 if( !moof )
6228 return -1;
6229 moof->size = ISOM_DEFAULT_BOX_HEADER_SIZE + isom_update_mfhd_size( moof->mfhd );
6230 if( moof->traf_list )
6231 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
6233 isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
6234 moof->size += isom_update_traf_entry_size( traf );
6236 CHECK_LARGESIZE( moof->size );
6237 return 0;
6240 static uint64_t isom_update_tfra_entry_size( isom_tfra_entry_t *tfra )
6242 if( !tfra )
6243 return 0;
6244 tfra->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 12;
6245 uint32_t entry_size = 8 * (1 + (tfra->version == 1))
6246 + tfra->length_size_of_traf_num + 1
6247 + tfra->length_size_of_trun_num + 1
6248 + tfra->length_size_of_sample_num + 1;
6249 tfra->size += entry_size * tfra->number_of_entry;
6250 CHECK_LARGESIZE( tfra->size );
6251 return tfra->size;
6254 static uint64_t isom_update_mfro_size( isom_mfro_t *mfro )
6256 if( !mfro )
6257 return 0;
6258 mfro->size = ISOM_DEFAULT_FULLBOX_HEADER_SIZE + 4;
6259 CHECK_LARGESIZE( mfro->size );
6260 return mfro->size;
6263 static int isom_update_mfra_size( isom_mfra_t *mfra )
6265 if( !mfra )
6266 return -1;
6267 mfra->size = ISOM_DEFAULT_BOX_HEADER_SIZE;
6268 if( mfra->tfra_list )
6269 for( lsmash_entry_t *entry = mfra->tfra_list->head; entry; entry = entry->next )
6271 isom_tfra_entry_t *tfra = (isom_tfra_entry_t *)entry->data;
6272 mfra->size += isom_update_tfra_entry_size( tfra );
6274 CHECK_LARGESIZE( mfra->size );
6275 if( mfra->mfro )
6277 mfra->size += isom_update_mfro_size( mfra->mfro );
6278 mfra->mfro->length = mfra->size;
6280 return 0;
6283 /*******************************
6284 public interfaces
6285 *******************************/
6287 /*---- track manipulators ----*/
6289 void lsmash_delete_track( lsmash_root_t *root, uint32_t track_ID )
6291 if( !root || !root->moov || !root->moov->trak_list )
6292 return;
6293 for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
6295 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
6296 if( !trak || !trak->tkhd )
6297 return;
6298 if( trak->tkhd->track_ID == track_ID )
6300 lsmash_entry_t *next = entry->next;
6301 lsmash_entry_t *prev = entry->prev;
6302 isom_remove_trak( trak );
6303 free( entry );
6304 entry = next;
6305 if( entry )
6307 if( prev )
6308 prev->next = entry;
6309 entry->prev = prev;
6311 return;
6316 uint32_t lsmash_create_track( lsmash_root_t *root, uint32_t media_type )
6318 isom_trak_entry_t *trak = isom_add_trak( root );
6319 if( !trak )
6320 return 0;
6321 if( isom_add_tkhd( trak, media_type )
6322 || isom_add_mdia( trak )
6323 || isom_add_mdhd( trak->mdia, root->qt_compatible ? 0 : ISOM_LANGUAGE_CODE_UNDEFINED )
6324 || isom_add_minf( trak->mdia )
6325 || isom_add_stbl( trak->mdia->minf )
6326 || isom_add_dinf( trak->mdia->minf )
6327 || isom_add_dref( trak->mdia->minf->dinf )
6328 || isom_add_stsd( trak->mdia->minf->stbl )
6329 || isom_add_stts( trak->mdia->minf->stbl )
6330 || isom_add_stsc( trak->mdia->minf->stbl )
6331 || isom_add_stco( trak->mdia->minf->stbl )
6332 || isom_add_stsz( trak->mdia->minf->stbl ) )
6333 return 0;
6334 if( isom_add_hdlr( trak->mdia, NULL, media_type ) )
6335 return 0;
6336 if( root->qt_compatible && isom_add_hdlr( NULL, trak->mdia->minf, QT_REFERENCE_HANDLER_TYPE_URL ) )
6337 return 0;
6338 switch( media_type )
6340 case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK :
6341 if( isom_add_vmhd( trak->mdia->minf ) )
6342 return 0;
6343 break;
6344 case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK :
6345 if( isom_add_smhd( trak->mdia->minf ) )
6346 return 0;
6347 break;
6348 case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK :
6349 if( isom_add_hmhd( trak->mdia->minf ) )
6350 return 0;
6351 break;
6352 case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK :
6353 if( root->qt_compatible || root->itunes_audio )
6355 if( isom_add_gmhd( trak->mdia->minf )
6356 || isom_add_gmin( trak->mdia->minf->gmhd )
6357 || isom_add_text( trak->mdia->minf->gmhd ) )
6358 return 0;
6360 else
6361 return 0; /* We support only reference text media track for chapter yet. */
6362 break;
6363 default :
6364 if( isom_add_nmhd( trak->mdia->minf ) )
6365 return 0;
6366 break;
6368 return trak->tkhd->track_ID;
6371 uint32_t lsmash_get_track_ID( lsmash_root_t *root, uint32_t track_number )
6373 if( !root || !root->moov )
6374 return 0;
6375 isom_trak_entry_t *trak = (isom_trak_entry_t *)lsmash_get_entry_data( root->moov->trak_list, track_number );
6376 if( !trak || !trak->tkhd )
6377 return 0;
6378 return trak->tkhd->track_ID;
6381 void lsmash_initialize_track_parameters( lsmash_track_parameters_t *param )
6383 memset( param, 0, sizeof(lsmash_track_parameters_t) );
6384 param->audio_volume = 0x0100;
6385 param->matrix[0] = 0x00010000;
6386 param->matrix[4] = 0x00010000;
6387 param->matrix[8] = 0x40000000;
6390 int lsmash_set_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param )
6392 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6393 if( !trak || !trak->mdia || !trak->mdia->hdlr || !root->moov->mvhd )
6394 return -1;
6395 /* Prepare Track Aperture Modes if required. */
6396 if( root->qt_compatible && param->aperture_modes )
6398 if( !trak->tapt && isom_add_tapt( trak ) )
6399 return -1;
6400 isom_tapt_t *tapt = trak->tapt;
6401 if( (!tapt->clef && isom_add_clef( tapt ))
6402 || (!tapt->prof && isom_add_prof( tapt ))
6403 || (!tapt->enof && isom_add_enof( tapt )) )
6404 return -1;
6406 else
6407 isom_remove_tapt( trak->tapt );
6408 /* Set up Track Header. */
6409 uint32_t media_type = trak->mdia->hdlr->componentSubtype;
6410 isom_tkhd_t *tkhd = trak->tkhd;
6411 tkhd->flags = param->mode;
6412 tkhd->track_ID = param->track_ID ? param->track_ID : tkhd->track_ID;
6413 tkhd->duration = !trak->edts || !trak->edts->elst ? param->duration : tkhd->duration;
6414 tkhd->alternate_group = root->qt_compatible || root->itunes_audio || root->max_3gpp_version >= 4 ? param->alternate_group : 0;
6415 if( root->qt_compatible || root->itunes_audio )
6417 tkhd->layer = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->video_layer : 0;
6418 tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? param->audio_volume : 0;
6420 else
6422 tkhd->layer = 0;
6423 tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? 0x0100 : 0;
6425 for( int i = 0; i < 9; i++ )
6426 tkhd->matrix[i] = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->matrix[i] : 0;
6427 tkhd->width = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_width : 0;
6428 tkhd->height = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_height : 0;
6429 /* Update next_track_ID if needed. */
6430 if( root->moov->mvhd->next_track_ID <= tkhd->track_ID )
6431 root->moov->mvhd->next_track_ID = tkhd->track_ID + 1;
6432 return 0;
6435 int lsmash_get_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param )
6437 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6438 if( !trak )
6439 return -1;
6440 isom_tkhd_t *tkhd = trak->tkhd;
6441 param->mode = tkhd->flags;
6442 param->track_ID = tkhd->track_ID;
6443 param->duration = tkhd->duration;
6444 param->video_layer = tkhd->layer;
6445 param->alternate_group = tkhd->alternate_group;
6446 param->audio_volume = tkhd->volume;
6447 for( int i = 0; i < 9; i++ )
6448 param->matrix[i] = tkhd->matrix[i];
6449 param->display_width = tkhd->width;
6450 param->display_height = tkhd->height;
6451 param->aperture_modes = !!trak->tapt;
6452 return 0;
6455 static int isom_set_media_handler_name( lsmash_root_t *root, uint32_t track_ID, char *handler_name )
6457 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6458 if( !trak || !trak->mdia || !trak->mdia->hdlr )
6459 return -1;
6460 isom_hdlr_t *hdlr = trak->mdia->hdlr;
6461 uint8_t *name = NULL;
6462 uint32_t name_length = strlen( handler_name ) + root->isom_compatible + root->qt_compatible;
6463 if( root->qt_compatible )
6464 name_length = LSMASH_MIN( name_length, 255 );
6465 if( name_length > hdlr->componentName_length && hdlr->componentName )
6466 name = realloc( hdlr->componentName, name_length );
6467 else if( !hdlr->componentName )
6468 name = malloc( name_length );
6469 else
6470 name = hdlr->componentName;
6471 if( !name )
6472 return -1;
6473 if( root->qt_compatible )
6474 name[0] = name_length & 0xff;
6475 memcpy( name + root->qt_compatible, handler_name, strlen( handler_name ) );
6476 if( root->isom_compatible )
6477 name[name_length - 1] = 0;
6478 hdlr->componentName = name;
6479 hdlr->componentName_length = name_length;
6480 return 0;
6483 static int isom_set_data_handler_name( lsmash_root_t *root, uint32_t track_ID, char *handler_name )
6485 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6486 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->hdlr )
6487 return -1;
6488 isom_hdlr_t *hdlr = trak->mdia->minf->hdlr;
6489 uint8_t *name = NULL;
6490 uint32_t name_length = strlen( handler_name ) + root->isom_compatible + root->qt_compatible;
6491 if( root->qt_compatible )
6492 name_length = LSMASH_MIN( name_length, 255 );
6493 if( name_length > hdlr->componentName_length && hdlr->componentName )
6494 name = realloc( hdlr->componentName, name_length );
6495 else if( !hdlr->componentName )
6496 name = malloc( name_length );
6497 else
6498 name = hdlr->componentName;
6499 if( !name )
6500 return -1;
6501 if( root->qt_compatible )
6502 name[0] = name_length & 0xff;
6503 memcpy( name + root->qt_compatible, handler_name, strlen( handler_name ) );
6504 if( root->isom_compatible )
6505 name[name_length - 1] = 0;
6506 hdlr->componentName = name;
6507 hdlr->componentName_length = name_length;
6508 return 0;
6511 uint32_t lsmash_get_media_timescale( lsmash_root_t *root, uint32_t track_ID )
6513 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6514 if( !trak || !trak->mdia || !trak->mdia->mdhd )
6515 return 0;
6516 return trak->mdia->mdhd->timescale;
6519 uint64_t lsmash_get_media_duration( lsmash_root_t *root, uint32_t track_ID )
6521 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6522 if( !trak || !trak->mdia || !trak->mdia->mdhd )
6523 return 0;
6524 return trak->mdia->mdhd->duration;
6527 uint64_t lsmash_get_track_duration( lsmash_root_t *root, uint32_t track_ID )
6529 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6530 if( !trak || !trak->tkhd )
6531 return 0;
6532 return trak->tkhd->duration;
6535 uint32_t lsmash_get_last_sample_delta( lsmash_root_t *root, uint32_t track_ID )
6537 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6538 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl
6539 || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list
6540 || !trak->mdia->minf->stbl->stts->list->head || !trak->mdia->minf->stbl->stts->list->head->data )
6541 return 0;
6542 return ((isom_stts_entry_t *)trak->mdia->minf->stbl->stts->list->head->data)->sample_delta;
6545 uint32_t lsmash_get_start_time_offset( lsmash_root_t *root, uint32_t track_ID )
6547 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6548 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl
6549 || !trak->mdia->minf->stbl->ctts || !trak->mdia->minf->stbl->ctts->list
6550 || !trak->mdia->minf->stbl->ctts->list->head || !trak->mdia->minf->stbl->ctts->list->head->data )
6551 return 0;
6552 return ((isom_ctts_entry_t *)trak->mdia->minf->stbl->ctts->list->head->data)->sample_offset;
6555 char *isom_unpack_iso_language( uint16_t language )
6557 static char unpacked[4];
6558 unpacked[0] = ((language >> 10) & 0x1f) + 0x60;
6559 unpacked[1] = ((language >> 5) & 0x1f) + 0x60;
6560 unpacked[2] = ( language & 0x1f) + 0x60;
6561 unpacked[3] = 0;
6562 return unpacked;
6565 static int isom_iso2mac_language( uint16_t ISO_language, uint16_t *MAC_language )
6567 if( !MAC_language )
6568 return -1;
6569 int i = 0;
6570 for( ; isom_languages[i].iso_name; i++ )
6571 if( ISO_language == isom_languages[i].iso_name )
6572 break;
6573 if( !isom_languages[i].iso_name )
6574 return -1;
6575 *MAC_language = isom_languages[i].mac_value;
6576 return 0;
6579 static int isom_mac2iso_language( uint16_t MAC_language, uint16_t *ISO_language )
6581 if( !ISO_language )
6582 return -1;
6583 int i = 0;
6584 for( ; isom_languages[i].iso_name; i++ )
6585 if( MAC_language == isom_languages[i].mac_value )
6586 break;
6587 *ISO_language = isom_languages[i].iso_name ? isom_languages[i].iso_name : ISOM_LANGUAGE_CODE_UNDEFINED;
6588 return 0;
6591 static int isom_set_media_language( lsmash_root_t *root, uint32_t track_ID, char *ISO_language, uint16_t MAC_language )
6593 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6594 if( !trak || !trak->mdia || !trak->mdia->mdhd )
6595 return -1;
6596 uint16_t language = 0;
6597 if( root->isom_compatible )
6599 if( ISO_language && (strlen( ISO_language ) == 3) )
6600 language = ISOM_LANG( ISO_language );
6601 else if( MAC_language )
6603 if( isom_mac2iso_language( MAC_language, &language ) )
6604 return -1;
6606 else
6607 language = ISOM_LANGUAGE_CODE_UNDEFINED;
6609 else if( root->qt_compatible )
6611 if( ISO_language && (strlen( ISO_language ) == 3) )
6613 if( isom_iso2mac_language( ISOM_LANG( ISO_language ), &language ) )
6614 return -1;
6616 else
6617 language = MAC_language;
6619 else
6620 return -1;
6621 trak->mdia->mdhd->language = language;
6622 return 0;
6625 static int isom_create_grouping( isom_trak_entry_t *trak, lsmash_grouping_type_code grouping_type )
6627 lsmash_root_t *root = trak->root;
6628 switch( grouping_type )
6630 case ISOM_GROUP_TYPE_RAP :
6631 assert( root->max_isom_version >= 6 );
6632 break;
6633 case ISOM_GROUP_TYPE_ROLL :
6634 assert( root->avc_extensions );
6635 break;
6636 default :
6637 assert( 0 );
6638 break;
6640 if( !isom_add_sgpd( trak->mdia->minf->stbl, grouping_type )
6641 || !isom_add_sbgp( trak->mdia->minf->stbl, grouping_type ) )
6642 return -1;
6643 return 0;
6646 void lsmash_initialize_media_parameters( lsmash_media_parameters_t *param )
6648 memset( param, 0, sizeof(lsmash_media_parameters_t) );
6649 param->timescale = 1;
6652 int lsmash_set_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param )
6654 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6655 if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl )
6656 return -1;
6657 trak->mdia->mdhd->timescale = param->timescale;
6658 if( isom_set_media_language( root, track_ID, param->ISO_language, param->MAC_language ) )
6659 return -1;
6660 if( param->media_handler_name
6661 && isom_set_media_handler_name( root, track_ID, param->media_handler_name ) )
6662 return -1;
6663 if( root->qt_compatible && param->data_handler_name
6664 && isom_set_data_handler_name( root, track_ID, param->data_handler_name ) )
6665 return -1;
6666 if( root->avc_extensions && param->roll_grouping
6667 && isom_create_grouping( trak, ISOM_GROUP_TYPE_ROLL ) )
6668 return -1;
6669 if( (root->max_isom_version >= 6) && param->rap_grouping
6670 && isom_create_grouping( trak, ISOM_GROUP_TYPE_RAP ) )
6671 return -1;
6672 return 0;
6675 int lsmash_get_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param )
6677 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6678 if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->hdlr
6679 || !trak->mdia->minf || !trak->mdia->minf->stbl )
6680 return -1;
6681 isom_mdhd_t *mdhd = trak->mdia->mdhd;
6682 isom_stbl_t *stbl = trak->mdia->minf->stbl;
6683 isom_sbgp_entry_t *sbgp;
6684 isom_sgpd_entry_t *sgpd;
6685 param->timescale = mdhd->timescale;
6686 param->handler_type = trak->mdia->hdlr->componentSubtype;
6687 param->duration = mdhd->duration;
6688 /* Whether sample grouping present. */
6689 sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_ROLL );
6690 sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_ROLL );
6691 param->roll_grouping = sbgp && sgpd;
6692 sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_RAP );
6693 sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
6694 param->rap_grouping = sbgp && sgpd;
6695 /* Get media language. */
6696 if( mdhd->language >= 0x800 )
6698 param->MAC_language = 0;
6699 param->ISO_language = isom_unpack_iso_language( mdhd->language );
6700 memcpy( param->language_shadow, param->ISO_language, sizeof(param->language_shadow) );
6701 param->ISO_language = param->language_shadow;
6703 else
6705 param->MAC_language = mdhd->language;
6706 param->ISO_language = NULL;
6707 memset( param->language_shadow, 0, sizeof(param->language_shadow) );
6709 /* Get handler name(s). */
6710 isom_hdlr_t *hdlr = trak->mdia->hdlr;
6711 int length = LSMASH_MIN( 255, hdlr->componentName_length );
6712 if( length )
6714 memcpy( param->media_handler_name_shadow, hdlr->componentName + root->qt_compatible, length );
6715 param->media_handler_name_shadow[length - 2 + root->isom_compatible + root->qt_compatible] = '\0';
6716 param->media_handler_name = param->media_handler_name_shadow;
6718 else
6720 param->media_handler_name = NULL;
6721 memset( param->media_handler_name_shadow, 0, sizeof(param->media_handler_name_shadow) );
6723 if( trak->mdia->minf->hdlr )
6725 hdlr = trak->mdia->minf->hdlr;
6726 length = LSMASH_MIN( 255, hdlr->componentName_length );
6727 if( length )
6729 memcpy( param->data_handler_name_shadow, hdlr->componentName + root->qt_compatible, length );
6730 param->data_handler_name_shadow[length - 2 + root->isom_compatible + root->qt_compatible] = '\0';
6731 param->data_handler_name = param->data_handler_name_shadow;
6733 else
6735 param->data_handler_name = NULL;
6736 memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) );
6739 else
6741 param->data_handler_name = NULL;
6742 memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) );
6744 return 0;
6747 /*---- movie manipulators ----*/
6749 lsmash_root_t *lsmash_open_movie( const char *filename, lsmash_file_mode_code mode )
6751 char open_mode[4] = { 0 };
6752 if( mode & LSMASH_FILE_MODE_WRITE )
6753 memcpy( open_mode, "w+b", 4 );
6754 #ifdef LSMASH_DEMUXER_ENABLED
6755 else if( mode & LSMASH_FILE_MODE_READ )
6756 memcpy( open_mode, "rb", 3 );
6757 #endif
6758 if( !open_mode[0] )
6759 return NULL;
6760 lsmash_root_t *root = malloc( sizeof(lsmash_root_t) );
6761 if( !root )
6762 return NULL;
6763 memset( root, 0, sizeof(lsmash_root_t) );
6764 root->root = root;
6765 root->bs = malloc( sizeof(lsmash_bs_t) );
6766 if( !root->bs )
6767 goto fail;
6768 memset( root->bs, 0, sizeof(lsmash_bs_t) );
6769 if( !strcmp( filename, "-" ) )
6771 if( mode & LSMASH_FILE_MODE_READ )
6772 root->bs->stream = stdin;
6773 else if( (mode & LSMASH_FILE_MODE_WRITE) && (mode & LSMASH_FILE_MODE_FRAGMENTED) )
6774 root->bs->stream = stdout;
6776 else
6777 root->bs->stream = fopen( filename, open_mode );
6778 if( !root->bs->stream )
6779 goto fail;
6780 root->flags = mode;
6781 if( mode & LSMASH_FILE_MODE_WRITE )
6783 if( isom_add_moov( root ) || isom_add_mvhd( root->moov ) )
6784 goto fail;
6785 root->qt_compatible = 1; /* QTFF is default file format. */
6787 #ifdef LSMASH_DEMUXER_ENABLED
6788 if( (mode & (LSMASH_FILE_MODE_READ | LSMASH_FILE_MODE_DUMP)) )
6790 if( isom_read_root( root ) )
6791 goto fail;
6792 root->max_read_size = 4 * 1024 * 1024;
6794 #endif
6795 if( mode & LSMASH_FILE_MODE_FRAGMENTED )
6797 root->fragment = malloc( sizeof(isom_fragment_manager_t) );
6798 if( !root->fragment )
6799 goto fail;
6800 memset( root->fragment, 0, sizeof(isom_fragment_manager_t) );
6801 root->fragment->pool = lsmash_create_entry_list();
6802 if( !root->fragment->pool )
6803 goto fail;
6805 return root;
6806 fail:
6807 lsmash_destroy_root( root );
6808 return NULL;
6811 static int isom_finish_fragment_movie( lsmash_root_t *root );
6813 /* A movie fragment cannot switch a sample description to another.
6814 * So you must call this function before switching sample descriptions. */
6815 int lsmash_create_fragment_movie( lsmash_root_t *root )
6817 if( !root || !root->bs || !root->fragment || !root->moov || !root->moov->trak_list )
6818 return -1;
6819 /* Finish the previous movie fragment before starting a new one. */
6820 if( isom_finish_fragment_movie( root ) )
6821 return -1;
6822 /* We always hold only one movie fragment except for the initial movie (a pair of moov and mdat). */
6823 if( root->fragment->movie && root->moof_list->entry_count != 1 )
6824 return -1;
6825 isom_moof_entry_t *moof = isom_add_moof( root );
6826 if( isom_add_mfhd( moof ) )
6827 return -1;
6828 root->fragment->movie = moof;
6829 moof->mfhd->sequence_number = ++ root->fragment->fragment_count;
6830 if( root->moof_list->entry_count == 1 )
6831 return 0;
6832 /* Remove the previous movie fragment. */
6833 return lsmash_remove_entry( root->moof_list, 1, isom_remove_moof );
6836 static int isom_set_brands( lsmash_root_t *root, lsmash_brand_type_code major_brand, uint32_t minor_version, lsmash_brand_type_code *brands, uint32_t brand_count )
6838 if( brand_count > 50 )
6839 return -1; /* We support setting brands up to 50. */
6840 if( !brand_count )
6842 /* Absence of File Type Box means this file is a QuickTime or MP4 version 1 format file. */
6843 if( root->ftyp )
6845 if( root->ftyp->compatible_brands )
6846 free( root->ftyp->compatible_brands );
6847 free( root->ftyp );
6848 root->ftyp = NULL;
6850 return 0;
6852 if( !root->ftyp && isom_add_ftyp( root ) )
6853 return -1;
6854 isom_ftyp_t *ftyp = root->ftyp;
6855 ftyp->major_brand = major_brand;
6856 ftyp->minor_version = minor_version;
6857 lsmash_brand_type_code *compatible_brands;
6858 if( !ftyp->compatible_brands )
6859 compatible_brands = malloc( brand_count * sizeof(uint32_t) );
6860 else
6861 compatible_brands = realloc( ftyp->compatible_brands, brand_count * sizeof(uint32_t) );
6862 if( !compatible_brands )
6863 return -1;
6864 ftyp->compatible_brands = compatible_brands;
6865 for( uint32_t i = 0; i < brand_count; i++ )
6867 ftyp->compatible_brands[i] = brands[i];
6868 ftyp->size += 4;
6870 ftyp->brand_count = brand_count;
6871 return isom_check_compatibility( root );
6874 void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t *param )
6876 memset( param, 0, sizeof(lsmash_movie_parameters_t) );
6877 param->max_chunk_duration = 0.5;
6878 param->max_async_tolerance = 2.0;
6879 param->max_chunk_size = 4 * 1024 * 1024;
6880 param->max_read_size = 4 * 1024 * 1024;
6881 param->timescale = 600;
6882 param->playback_rate = 0x00010000;
6883 param->playback_volume = 0x0100;
6886 int lsmash_set_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param )
6888 if( !root || !root->moov || !root->moov->mvhd
6889 || isom_set_brands( root, param->major_brand, param->minor_version, param->brands, param->number_of_brands ) )
6890 return -1;
6891 isom_mvhd_t *mvhd = root->moov->mvhd;
6892 root->max_chunk_duration = param->max_chunk_duration;
6893 root->max_async_tolerance = LSMASH_MAX( param->max_async_tolerance, 2 * param->max_chunk_duration );
6894 root->max_chunk_size = param->max_chunk_size;
6895 root->max_read_size = param->max_read_size;
6896 mvhd->timescale = param->timescale;
6897 if( root->qt_compatible || root->itunes_audio )
6899 mvhd->rate = param->playback_rate;
6900 mvhd->volume = param->playback_volume;
6901 mvhd->previewTime = param->preview_time;
6902 mvhd->previewDuration = param->preview_duration;
6903 mvhd->posterTime = param->poster_time;
6905 else
6907 mvhd->rate = 0x00010000;
6908 mvhd->volume = 0x0100;
6909 mvhd->previewTime = 0;
6910 mvhd->previewDuration = 0;
6911 mvhd->posterTime = 0;
6913 return 0;
6916 int lsmash_get_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param )
6918 if( !root || !root->moov || !root->moov->mvhd )
6919 return -1;
6920 isom_mvhd_t *mvhd = root->moov->mvhd;
6921 if( root->ftyp )
6923 isom_ftyp_t *ftyp = root->ftyp;
6924 uint32_t brand_count = LSMASH_MIN( ftyp->brand_count, 50 ); /* brands up to 50 */
6925 for( uint32_t i = 0; i < brand_count; i++ )
6926 param->brands_shadow[i] = ftyp->compatible_brands[i];
6927 param->major_brand = ftyp->major_brand;
6928 param->brands = param->brands_shadow;
6929 param->number_of_brands = brand_count;
6930 param->minor_version = ftyp->minor_version;
6932 param->max_chunk_duration = root->max_chunk_duration;
6933 param->max_async_tolerance = root->max_async_tolerance;
6934 param->max_chunk_size = root->max_chunk_size;
6935 param->max_read_size = root->max_read_size;
6936 param->timescale = mvhd->timescale;
6937 param->duration = mvhd->duration;
6938 param->playback_rate = mvhd->rate;
6939 param->playback_volume = mvhd->volume;
6940 param->preview_time = mvhd->previewTime;
6941 param->preview_duration = mvhd->previewDuration;
6942 param->poster_time = mvhd->posterTime;
6943 param->number_of_tracks = root->moov->trak_list ? root->moov->trak_list->entry_count : 0;
6944 return 0;
6947 uint32_t lsmash_get_movie_timescale( lsmash_root_t *root )
6949 if( !root || !root->moov || !root->moov->mvhd )
6950 return 0;
6951 return root->moov->mvhd->timescale;
6954 static int isom_write_ftyp( lsmash_root_t *root )
6956 isom_ftyp_t *ftyp = root->ftyp;
6957 if( !ftyp || !ftyp->brand_count )
6958 return 0;
6959 lsmash_bs_t *bs = root->bs;
6960 isom_bs_put_box_common( bs, ftyp );
6961 lsmash_bs_put_be32( bs, ftyp->major_brand );
6962 lsmash_bs_put_be32( bs, ftyp->minor_version );
6963 for( uint32_t i = 0; i < ftyp->brand_count; i++ )
6964 lsmash_bs_put_be32( bs, ftyp->compatible_brands[i] );
6965 if( lsmash_bs_write_data( bs ) )
6966 return -1;
6967 root->size += ftyp->size;
6968 root->file_type_written = 1;
6969 return 0;
6972 static int isom_write_moov( lsmash_root_t *root )
6974 if( !root || !root->moov )
6975 return -1;
6976 lsmash_bs_t *bs = root->bs;
6977 isom_bs_put_box_common( bs, root->moov );
6978 if( lsmash_bs_write_data( bs ) )
6979 return -1;
6980 if( isom_write_mvhd( root )
6981 || isom_write_iods( root ) )
6982 return -1;
6983 if( root->moov->trak_list )
6984 for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
6985 if( isom_write_trak( bs, (isom_trak_entry_t *)entry->data ) )
6986 return -1;
6987 if( isom_write_udta( bs, root->moov, NULL ) )
6988 return -1;
6989 return isom_write_mvex( bs, root->moov->mvex );
6992 int lsmash_set_free( lsmash_root_t *root, uint8_t *data, uint64_t data_length )
6994 if( !root || !root->free || !data || !data_length )
6995 return -1;
6996 isom_free_t *skip = root->free;
6997 uint8_t *tmp = NULL;
6998 if( !skip->data )
6999 tmp = malloc( data_length );
7000 else if( skip->length < data_length )
7001 tmp = realloc( skip->data, data_length );
7002 if( !tmp )
7003 return -1;
7004 memcpy( tmp, data, data_length );
7005 skip->data = tmp;
7006 skip->length = data_length;
7007 return 0;
7010 int lsmash_add_free( lsmash_root_t *root, uint8_t *data, uint64_t data_length )
7012 if( !root )
7013 return -1;
7014 if( !root->free )
7016 isom_create_box( skip, root, ISOM_BOX_TYPE_FREE );
7017 root->free = skip;
7019 if( data && data_length )
7020 return lsmash_set_free( root, data, data_length );
7021 return 0;
7024 int lsmash_write_free( lsmash_root_t *root )
7026 if( !root || !root->bs || !root->free )
7027 return -1;
7028 isom_free_t *skip = root->free;
7029 lsmash_bs_t *bs = root->bs;
7030 skip->size = 8 + skip->length;
7031 isom_bs_put_box_common( bs, skip );
7032 if( skip->data && skip->length )
7033 lsmash_bs_put_bytes( bs, skip->data, skip->length );
7034 return lsmash_bs_write_data( bs );
7037 int lsmash_create_object_descriptor( lsmash_root_t *root )
7039 if( !root )
7040 return -1;
7041 /* Return error if this file is not compatible with MP4 file format. */
7042 if( !root->mp4_version1 && !root->mp4_version2 )
7043 return -1;
7044 return isom_add_iods( root->moov );
7047 /*---- finishing functions ----*/
7049 static int isom_set_fragment_overall_duration( lsmash_root_t *root )
7051 if( root->bs->stream == stdout )
7052 return 0;
7053 isom_mvex_t *mvex = root->moov->mvex;
7054 if( isom_add_mehd( mvex ) )
7055 return -1;
7056 /* Get the longest duration of the tracks. */
7057 uint64_t longest_duration = 0;
7058 for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
7060 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
7061 if( !trak || !trak->cache || !trak->cache->fragment || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale )
7062 return -1;
7063 uint64_t duration;
7064 if( !trak->edts || !trak->edts->elst || !trak->edts->elst->list )
7066 duration = trak->cache->fragment->largest_cts + trak->cache->fragment->last_duration;
7067 duration = (uint64_t)(((double)duration / trak->mdia->mdhd->timescale) * root->moov->mvhd->timescale);
7069 else
7071 duration = 0;
7072 for( lsmash_entry_t *elst_entry = trak->edts->elst->list->head; elst_entry; elst_entry = elst_entry->next )
7074 isom_elst_entry_t *data = (isom_elst_entry_t *)elst_entry->data;
7075 if( !data )
7076 return -1;
7077 duration += data->segment_duration;
7080 longest_duration = LSMASH_MAX( duration, longest_duration );
7082 mvex->mehd->fragment_duration = longest_duration;
7083 mvex->mehd->version = 1;
7084 isom_update_mehd_size( mvex->mehd );
7085 /* Write Movie Extends Header Box here. */
7086 lsmash_bs_t *bs = root->bs;
7087 FILE *stream = bs->stream;
7088 uint64_t current_pos = lsmash_ftell( stream );
7089 lsmash_fseek( stream, mvex->placeholder_pos, SEEK_SET );
7090 int ret = isom_write_mehd( bs, mvex->mehd );
7091 if( !ret )
7092 ret = lsmash_bs_write_data( bs );
7093 lsmash_fseek( stream, current_pos, SEEK_SET );
7094 return ret;
7097 static int isom_write_fragment_random_access_info( lsmash_root_t *root )
7099 if( root->bs->stream == stdout )
7100 return 0;
7101 if( isom_update_mfra_size( root->mfra ) )
7102 return -1;
7103 return isom_write_mfra( root->bs, root->mfra );
7106 int lsmash_finish_movie( lsmash_root_t *root, lsmash_adhoc_remux_t* remux )
7108 if( !root || !root->bs || !root->moov || !root->moov->trak_list )
7109 return -1;
7110 if( root->fragment )
7112 /* Output the final movie fragment. */
7113 if( isom_finish_fragment_movie( root ) )
7114 return -1;
7115 /* Write the overall random access information at the tail of the movie. */
7116 if( isom_write_fragment_random_access_info( root ) )
7117 return -1;
7118 /* Set overall duration of the movie. */
7119 return isom_set_fragment_overall_duration( root );
7121 isom_moov_t *moov = root->moov;
7122 for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
7124 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
7125 if( !trak || !trak->cache || !trak->tkhd || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl )
7126 return -1;
7127 uint32_t track_ID = trak->tkhd->track_ID;
7128 uint32_t related_track_ID = trak->related_track_ID;
7129 /* Disable the track if the track is a track reference chapter. */
7130 if( trak->is_chapter )
7131 trak->tkhd->flags &= ~ISOM_TRACK_ENABLED;
7132 if( trak->is_chapter && related_track_ID )
7134 /* In order that the track duration of the chapter track doesn't exceed that of the related track. */
7135 uint64_t track_duration = LSMASH_MIN( trak->tkhd->duration, lsmash_get_track_duration( root, related_track_ID ) );
7136 if( lsmash_create_explicit_timeline_map( root, track_ID, track_duration, 0, ISOM_EDIT_MODE_NORMAL ) )
7137 return -1;
7139 /* Add stss box if any samples aren't sync sample. */
7140 isom_stbl_t *stbl = trak->mdia->minf->stbl;
7141 if( !trak->cache->all_sync && !stbl->stss && isom_add_stss( stbl ) )
7142 return -1;
7143 if( isom_update_bitrate_info( trak->mdia ) )
7144 return -1;
7146 if( root->mp4_version1 == 1 && isom_add_iods( moov ) )
7147 return -1;
7148 if( isom_check_mandatory_boxes( root )
7149 || isom_set_movie_creation_time( root )
7150 || isom_update_moov_size( moov )
7151 || isom_write_mdat_size( root ) )
7152 return -1;
7154 if( !remux )
7156 if( isom_write_moov( root ) )
7157 return -1;
7158 root->size += moov->size;
7159 return 0;
7162 /* stco->co64 conversion, depending on last chunk's offset */
7163 for( lsmash_entry_t* entry = moov->trak_list->head; entry; )
7165 isom_trak_entry_t* trak = (isom_trak_entry_t*)entry->data;
7166 isom_stco_t* stco = trak->mdia->minf->stbl->stco;
7167 if( !stco->list->tail )
7168 return -1;
7169 if( stco->large_presentation
7170 || ((isom_stco_entry_t*)stco->list->tail->data)->chunk_offset + moov->size <= UINT32_MAX )
7172 entry = entry->next;
7173 continue; /* no need to convert stco into co64 */
7175 /* stco->co64 conversion */
7176 if( isom_convert_stco_to_co64( trak->mdia->minf->stbl )
7177 || isom_update_moov_size( moov ) )
7178 return -1;
7179 entry = moov->trak_list->head; /* whenever any conversion, re-check all traks */
7182 /* now the amount of offset is fixed. */
7184 /* buffer size must be at least sizeof(moov)*2 */
7185 remux->buffer_size = LSMASH_MAX( remux->buffer_size, moov->size * 2 );
7187 uint8_t* buf[2];
7188 if( (buf[0] = (uint8_t*)malloc( remux->buffer_size )) == NULL )
7189 return -1; /* NOTE: i think we still can fallback to "return isom_write_moov( root );" here. */
7190 uint64_t size = remux->buffer_size / 2;
7191 buf[1] = buf[0] + size; /* split to 2 buffers */
7193 /* now the amount of offset is fixed. apply that to stco/co64 */
7194 for( lsmash_entry_t* entry = moov->trak_list->head; entry; entry = entry->next )
7196 isom_stco_t* stco = ((isom_trak_entry_t*)entry->data)->mdia->minf->stbl->stco;
7197 if( stco->large_presentation )
7198 for( lsmash_entry_t* co64_entry = stco->list->head ; co64_entry ; co64_entry = co64_entry->next )
7199 ((isom_co64_entry_t*)co64_entry->data)->chunk_offset += moov->size;
7200 else
7201 for( lsmash_entry_t* stco_entry = stco->list->head ; stco_entry ; stco_entry = stco_entry->next )
7202 ((isom_stco_entry_t*)stco_entry->data)->chunk_offset += moov->size;
7205 FILE *stream = root->bs->stream;
7206 isom_mdat_t *mdat = root->mdat;
7207 uint64_t total = lsmash_ftell( stream ) + moov->size; // FIXME:
7208 uint64_t readnum;
7209 /* backup starting area of mdat and write moov there instead */
7210 if( lsmash_fseek( stream, mdat->placeholder_pos, SEEK_SET ) )
7211 goto fail;
7212 readnum = fread( buf[0], 1, size, stream );
7213 uint64_t read_pos = lsmash_ftell( stream );
7215 /* write moov there instead */
7216 if( lsmash_fseek( stream, mdat->placeholder_pos, SEEK_SET )
7217 || isom_write_moov( root ) )
7218 goto fail;
7219 uint64_t write_pos = lsmash_ftell( stream );
7221 mdat->placeholder_pos += moov->size; /* update placeholder */
7223 /* copy-pastan */
7224 int buf_switch = 1;
7225 while( readnum == size )
7227 if( lsmash_fseek( stream, read_pos, SEEK_SET ) )
7228 goto fail;
7229 readnum = fread( buf[buf_switch], 1, size, stream );
7230 read_pos = lsmash_ftell( stream );
7232 buf_switch ^= 0x1;
7234 if( lsmash_fseek( stream, write_pos, SEEK_SET )
7235 || fwrite( buf[buf_switch], 1, size, stream ) != size )
7236 goto fail;
7237 write_pos = lsmash_ftell( stream );
7238 if( remux->func ) remux->func( remux->param, write_pos, total ); // FIXME:
7240 if( fwrite( buf[buf_switch^0x1], 1, readnum, stream ) != readnum )
7241 goto fail;
7242 if( remux->func ) remux->func( remux->param, total, total ); // FIXME:
7244 root->size += moov->size;
7245 free( buf[0] );
7246 return 0;
7248 fail:
7249 free( buf[0] );
7250 return -1;
7253 #define GET_MOST_USED( box_name, index, flag_name ) \
7254 if( most_used[index] < stats.flag_name[i] ) \
7256 most_used[index] = stats.flag_name[i]; \
7257 box_name->default_sample_flags.flag_name = i; \
7260 static int isom_create_fragment_overall_default_settings( lsmash_root_t *root )
7262 if( isom_add_mvex( root->moov ) )
7263 return -1;
7264 for( lsmash_entry_t *trak_entry = root->moov->trak_list->head; trak_entry; trak_entry = trak_entry->next )
7266 isom_trak_entry_t *trak = (isom_trak_entry_t *)trak_entry->data;
7267 if( !trak || !trak->cache || !trak->tkhd || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl )
7268 return -1;
7269 isom_stbl_t *stbl = trak->mdia->minf->stbl;
7270 if( !stbl->stts || !stbl->stts->list || !stbl->stsz
7271 || (stbl->stts->list->tail && !stbl->stts->list->tail->data)
7272 || (stbl->stsz->list && stbl->stsz->list->head && !stbl->stsz->list->head->data) )
7273 return -1;
7274 isom_trex_entry_t *trex = isom_add_trex( root->moov->mvex );
7275 if( !trex )
7276 return -1;
7277 trex->track_ID = trak->tkhd->track_ID;
7278 /* Set up defaults. */
7279 trex->default_sample_description_index = trak->cache->chunk.sample_description_index ? trak->cache->chunk.sample_description_index : 1;
7280 trex->default_sample_duration = stbl->stts->list->tail ? ((isom_stts_entry_t *)stbl->stts->list->tail->data)->sample_delta : 1;
7281 trex->default_sample_size = !stbl->stsz->list
7282 ? stbl->stsz->sample_size : stbl->stsz->list->head
7283 ? ((isom_stsz_entry_t *)stbl->stsz->list->head->data)->entry_size : 0;
7284 if( stbl->sdtp && stbl->sdtp->list )
7286 struct sample_flags_stats_t
7288 uint32_t is_leading [4];
7289 uint32_t sample_depends_on [4];
7290 uint32_t sample_is_depended_on[4];
7291 uint32_t sample_has_redundancy[4];
7292 } stats = { { 0 }, { 0 }, { 0 }, { 0 } };
7293 for( lsmash_entry_t *sdtp_entry = stbl->sdtp->list->head; sdtp_entry; sdtp_entry = sdtp_entry->next )
7295 isom_sdtp_entry_t *data = (isom_sdtp_entry_t *)sdtp_entry->data;
7296 if( !data )
7297 return -1;
7298 ++ stats.is_leading [ data->is_leading ];
7299 ++ stats.sample_depends_on [ data->sample_depends_on ];
7300 ++ stats.sample_is_depended_on[ data->sample_is_depended_on ];
7301 ++ stats.sample_has_redundancy[ data->sample_has_redundancy ];
7303 uint32_t most_used[4] = { 0, 0, 0, 0 };
7304 for( int i = 0; i < 4; i++ )
7306 GET_MOST_USED( trex, 0, is_leading );
7307 GET_MOST_USED( trex, 1, sample_depends_on );
7308 GET_MOST_USED( trex, 2, sample_is_depended_on );
7309 GET_MOST_USED( trex, 3, sample_has_redundancy );
7312 trex->default_sample_flags.sample_is_non_sync_sample = !trak->cache->all_sync;
7314 return 0;
7317 static int isom_prepare_random_access_info( lsmash_root_t *root )
7319 if( root->bs->stream == stdout )
7320 return 0;
7321 if( isom_add_mfra( root )
7322 || isom_add_mfro( root->mfra ) )
7323 return -1;
7324 return 0;
7327 static int isom_output_fragment_media_data( lsmash_root_t *root )
7329 isom_fragment_manager_t *fragment = root->fragment;
7330 if( !fragment->pool->entry_count )
7332 /* no need to write media data */
7333 lsmash_remove_entries( fragment->pool, lsmash_delete_sample );
7334 fragment->pool_size = 0;
7335 return 0;
7337 /* If there is no available Media Data Box to write samples, add and write a new one. */
7338 if( isom_new_mdat( root, fragment->pool_size ) )
7339 return -1;
7340 /* Write samples in the current movie fragment. */
7341 for( lsmash_entry_t* entry = fragment->pool->head; entry; entry = entry->next )
7343 lsmash_sample_t *sample = (lsmash_sample_t *)entry->data;
7344 if( !sample || !sample->data )
7345 return -1;
7346 lsmash_bs_put_bytes( root->bs, sample->data, sample->length );
7348 if( lsmash_bs_write_data( root->bs ) )
7349 return -1;
7350 root->size += root->mdat->size;
7351 lsmash_remove_entries( fragment->pool, lsmash_delete_sample );
7352 fragment->pool_size = 0;
7353 return 0;
7356 static int isom_finish_fragment_initial_movie( lsmash_root_t *root )
7358 if( !root->moov || !root->moov->trak_list )
7359 return -1;
7360 isom_moov_t *moov = root->moov;
7361 for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
7363 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
7364 if( !trak || !trak->cache || !trak->tkhd || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl )
7365 return -1;
7366 if( isom_get_sample_count( trak ) )
7368 /* Add stss box if any samples aren't sync sample. */
7369 isom_stbl_t *stbl = trak->mdia->minf->stbl;
7370 if( !trak->cache->all_sync && !stbl->stss && isom_add_stss( stbl ) )
7371 return -1;
7373 else
7374 trak->tkhd->duration = 0;
7375 if( isom_update_bitrate_info( trak->mdia ) )
7376 return -1;
7378 if( root->mp4_version1 == 1 && isom_add_iods( moov ) )
7379 return -1;
7380 if( isom_create_fragment_overall_default_settings( root )
7381 || isom_prepare_random_access_info( root )
7382 || isom_check_mandatory_boxes( root )
7383 || isom_set_movie_creation_time( root )
7384 || isom_update_moov_size( moov ) )
7385 return -1;
7386 /* stco->co64 conversion, depending on last chunk's offset */
7387 for( lsmash_entry_t* entry = moov->trak_list->head; entry; )
7389 isom_trak_entry_t* trak = (isom_trak_entry_t*)entry->data;
7390 isom_stco_t* stco = trak->mdia->minf->stbl->stco;
7391 if( !stco->list->tail /* no samples */
7392 || stco->large_presentation
7393 || ((isom_stco_entry_t*)stco->list->tail->data)->chunk_offset + moov->size <= UINT32_MAX )
7395 entry = entry->next;
7396 continue; /* no need to convert stco into co64 */
7398 /* stco->co64 conversion */
7399 if( isom_convert_stco_to_co64( trak->mdia->minf->stbl )
7400 || isom_update_moov_size( moov ) )
7401 return -1;
7402 entry = moov->trak_list->head; /* whenever any conversion, re-check all traks */
7404 /* Now, the amount of offset is fixed. Apply that to stco/co64. */
7405 for( lsmash_entry_t* entry = moov->trak_list->head; entry; entry = entry->next )
7407 isom_stco_t* stco = ((isom_trak_entry_t*)entry->data)->mdia->minf->stbl->stco;
7408 if( stco->large_presentation )
7409 for( lsmash_entry_t* co64_entry = stco->list->head ; co64_entry ; co64_entry = co64_entry->next )
7410 ((isom_co64_entry_t*)co64_entry->data)->chunk_offset += moov->size;
7411 else
7412 for( lsmash_entry_t* stco_entry = stco->list->head ; stco_entry ; stco_entry = stco_entry->next )
7413 ((isom_stco_entry_t*)stco_entry->data)->chunk_offset += moov->size;
7415 /* Write File Type Box here if it was not written yet. */
7416 if( !root->file_type_written && isom_write_ftyp( root ) )
7417 return -1;
7418 /* Write Movie Box. */
7419 if( isom_write_moov( root ) )
7420 return -1;
7421 root->size += moov->size;
7422 /* Output samples. */
7423 return isom_output_fragment_media_data( root );
7426 static int isom_finish_fragment_movie( lsmash_root_t *root )
7428 if( !root->moov || !root->moov->trak_list || !root->fragment || !root->fragment->pool )
7429 return -1;
7430 isom_moof_entry_t *moof = root->fragment->movie;
7431 if( !moof )
7432 return isom_finish_fragment_initial_movie( root );
7433 /* Calculate appropriate default_sample_flags of each Track Fragment Header Box.
7434 * And check whether that default_sample_flags is useful or not. */
7435 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
7437 isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
7438 if( !traf || !traf->tfhd || !traf->root || !traf->root->moov || !traf->root->moov->mvex )
7439 return -1;
7440 isom_tfhd_t *tfhd = traf->tfhd;
7441 isom_trex_entry_t *trex = isom_get_trex( root->moov->mvex, tfhd->track_ID );
7442 if( !trex )
7443 return -1;
7444 struct sample_flags_stats_t
7446 uint32_t is_leading [4];
7447 uint32_t sample_depends_on [4];
7448 uint32_t sample_is_depended_on [4];
7449 uint32_t sample_has_redundancy [4];
7450 uint32_t sample_is_non_sync_sample[2];
7451 } stats = { { 0 }, { 0 }, { 0 }, { 0 }, { 0 } };
7452 for( lsmash_entry_t *trun_entry = traf->trun_list->head; trun_entry; trun_entry = trun_entry->next )
7454 isom_trun_entry_t *trun = (isom_trun_entry_t *)trun_entry->data;
7455 if( !trun || !trun->sample_count )
7456 return -1;
7457 isom_sample_flags_t *sample_flags;
7458 if( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT )
7460 if( !trun->optional )
7461 return -1;
7462 for( lsmash_entry_t *optional_entry = trun->optional->head; optional_entry; optional_entry = optional_entry->next )
7464 isom_trun_optional_row_t *row = (isom_trun_optional_row_t *)optional_entry->data;
7465 if( !row )
7466 return -1;
7467 sample_flags = &row->sample_flags;
7468 ++ stats.is_leading [ sample_flags->is_leading ];
7469 ++ stats.sample_depends_on [ sample_flags->sample_depends_on ];
7470 ++ stats.sample_is_depended_on [ sample_flags->sample_is_depended_on ];
7471 ++ stats.sample_has_redundancy [ sample_flags->sample_has_redundancy ];
7472 ++ stats.sample_is_non_sync_sample[ sample_flags->sample_is_non_sync_sample ];
7475 else
7477 sample_flags = &tfhd->default_sample_flags;
7478 stats.is_leading [ sample_flags->is_leading ] += trun->sample_count;
7479 stats.sample_depends_on [ sample_flags->sample_depends_on ] += trun->sample_count;
7480 stats.sample_is_depended_on [ sample_flags->sample_is_depended_on ] += trun->sample_count;
7481 stats.sample_has_redundancy [ sample_flags->sample_has_redundancy ] += trun->sample_count;
7482 stats.sample_is_non_sync_sample[ sample_flags->sample_is_non_sync_sample ] += trun->sample_count;
7485 uint32_t most_used[5] = { 0, 0, 0, 0, 0 };
7486 for( int i = 0; i < 4; i++ )
7488 GET_MOST_USED( tfhd, 0, is_leading );
7489 GET_MOST_USED( tfhd, 1, sample_depends_on );
7490 GET_MOST_USED( tfhd, 2, sample_is_depended_on );
7491 GET_MOST_USED( tfhd, 3, sample_has_redundancy );
7492 if( i < 2 )
7493 GET_MOST_USED( tfhd, 4, sample_is_non_sync_sample );
7495 int useful_default_sample_duration = 0;
7496 int useful_default_sample_size = 0;
7497 for( lsmash_entry_t *trun_entry = traf->trun_list->head; trun_entry; trun_entry = trun_entry->next )
7499 isom_trun_entry_t *trun = (isom_trun_entry_t *)trun_entry->data;
7500 if( !(trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT) )
7501 useful_default_sample_duration = 1;
7502 if( !(trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT) )
7503 useful_default_sample_size = 1;
7504 int useful_first_sample_flags = 1;
7505 int useful_default_sample_flags = 1;
7506 if( trun->sample_count == 1 )
7508 /* It is enough to check only if first_sample_flags equals default_sample_flags or not.
7509 * If it is equal, just use default_sample_flags.
7510 * If not, just use first_sample_flags of this run. */
7511 if( !memcmp( &trun->first_sample_flags, &tfhd->default_sample_flags, sizeof(isom_sample_flags_t) ) )
7512 useful_first_sample_flags = 0;
7514 else if( trun->optional && trun->optional->head )
7516 lsmash_entry_t *optional_entry = trun->optional->head->next;
7517 isom_trun_optional_row_t *row = (isom_trun_optional_row_t *)optional_entry->data;
7518 isom_sample_flags_t representative_sample_flags = row->sample_flags;
7519 if( memcmp( &tfhd->default_sample_flags, &representative_sample_flags, sizeof(isom_sample_flags_t) ) )
7520 useful_default_sample_flags = 0;
7521 if( !memcmp( &trun->first_sample_flags, &representative_sample_flags, sizeof(isom_sample_flags_t) ) )
7522 useful_first_sample_flags = 0;
7523 if( useful_default_sample_flags )
7524 for( optional_entry = optional_entry->next; optional_entry; optional_entry = optional_entry->next )
7526 row = (isom_trun_optional_row_t *)optional_entry->data;
7527 if( memcmp( &representative_sample_flags, &row->sample_flags, sizeof(isom_sample_flags_t) ) )
7529 useful_default_sample_flags = 0;
7530 break;
7534 if( useful_default_sample_flags )
7536 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT;
7537 trun->flags &= ~ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT;
7539 else
7541 useful_first_sample_flags = 0;
7542 trun->flags |= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT;
7544 if( useful_first_sample_flags )
7545 trun->flags |= ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT;
7547 if( useful_default_sample_duration && tfhd->default_sample_duration != trex->default_sample_duration )
7548 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
7549 else
7550 tfhd->default_sample_duration = trex->default_sample_duration; /* This might be redundant, but is to be more natural. */
7551 if( useful_default_sample_size && tfhd->default_sample_size != trex->default_sample_size )
7552 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT;
7553 else
7554 tfhd->default_sample_size = trex->default_sample_size; /* This might be redundant, but is to be more natural. */
7555 if( !(tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT) )
7556 tfhd->default_sample_flags = trex->default_sample_flags; /* This might be redundant, but is to be more natural. */
7557 else if( !memcmp( &tfhd->default_sample_flags, &trex->default_sample_flags, sizeof(isom_sample_flags_t) ) )
7558 tfhd->flags &= ~ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT;
7560 /* When using for live streaming, setting explicit base_data_offset is not preferable.
7561 * However, it's OK because we haven't supported this yet.
7562 * Implicit base_data_offsets that originate in the first byte of each Movie Fragment Box will be implemented
7563 * by the feature of ISO Base Media File Format version 5 or later.
7564 * Media Data Box starts immediately after Movie Fragment Box. */
7565 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
7567 isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
7568 traf->tfhd->flags |= ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT;
7570 /* Consider the update of tf_flags here. */
7571 if( isom_update_moof_entry_size( moof ) )
7572 return -1;
7573 /* Now, we can calculate offsets in the current movie fragment, so do it. */
7574 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
7576 isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
7577 traf->tfhd->base_data_offset = root->size + moof->size + ISOM_DEFAULT_BOX_HEADER_SIZE;
7579 if( isom_write_moof( root->bs, moof ) )
7580 return -1;
7581 root->size += moof->size;
7582 /* Output samples. */
7583 return isom_output_fragment_media_data( root );
7586 #undef GET_MOST_USED
7588 static isom_trun_optional_row_t *isom_request_trun_optional_row( isom_trun_entry_t *trun, isom_tfhd_t *tfhd, uint32_t sample_number )
7590 isom_trun_optional_row_t *row = NULL;
7591 if( !trun->optional )
7593 trun->optional = lsmash_create_entry_list();
7594 if( !trun->optional )
7595 return NULL;
7597 if( trun->optional->entry_count < sample_number )
7599 while( trun->optional->entry_count < sample_number )
7601 row = malloc( sizeof(isom_trun_optional_row_t) );
7602 if( !row )
7603 return NULL;
7604 /* Copy from default. */
7605 row->sample_duration = tfhd->default_sample_duration;
7606 row->sample_size = tfhd->default_sample_size;
7607 row->sample_flags = tfhd->default_sample_flags;
7608 row->sample_composition_time_offset = 0;
7609 if( lsmash_add_entry( trun->optional, row ) )
7611 free( row );
7612 return NULL;
7615 return row;
7617 uint32_t i = 0;
7618 for( lsmash_entry_t *entry = trun->optional->head; entry; entry = entry->next )
7620 row = (isom_trun_optional_row_t *)entry->data;
7621 if( !row )
7622 return NULL;
7623 if( ++i == sample_number )
7624 return row;
7626 return NULL;
7629 int lsmash_create_fragment_empty_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t duration )
7631 if( !root || !root->fragment || !root->fragment->movie || !root->moov )
7632 return -1;
7633 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
7634 if( !trak || !trak->tkhd )
7635 return -1;
7636 isom_trex_entry_t *trex = isom_get_trex( root->moov->mvex, track_ID );
7637 if( !trex )
7638 return -1;
7639 isom_moof_entry_t *moof = root->fragment->movie;
7640 isom_traf_entry_t *traf = isom_get_traf( moof, track_ID );
7641 if( traf )
7642 return -1;
7643 traf = isom_add_traf( root, moof );
7644 if( isom_add_tfhd( traf ) )
7645 return -1;
7646 isom_tfhd_t *tfhd = traf->tfhd;
7647 tfhd->flags = ISOM_TF_FLAGS_DURATION_IS_EMPTY; /* no samples for this track fragment yet */
7648 tfhd->track_ID = trak->tkhd->track_ID;
7649 tfhd->default_sample_duration = duration;
7650 if( duration != trex->default_sample_duration )
7651 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
7652 traf->cache = trak->cache;
7653 traf->cache->fragment->traf_number = moof->traf_list->entry_count;
7654 traf->cache->fragment->last_duration += duration; /* The duration of the last sample includes this empty-duration. */
7655 return 0;
7658 static int isom_set_fragment_last_duration( isom_traf_entry_t *traf, uint32_t last_duration )
7660 isom_tfhd_t *tfhd = traf->tfhd;
7661 if( !traf->trun_list || !traf->trun_list->tail || !traf->trun_list->tail->data )
7663 /* There are no track runs in this track fragment, so it is a empty-duration. */
7664 isom_trex_entry_t *trex = isom_get_trex( traf->root->moov->mvex, tfhd->track_ID );
7665 if( !trex )
7666 return -1;
7667 tfhd->flags |= ISOM_TF_FLAGS_DURATION_IS_EMPTY;
7668 if( last_duration != trex->default_sample_duration )
7669 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
7670 tfhd->default_sample_duration = last_duration;
7671 traf->cache->fragment->last_duration = last_duration;
7672 return 0;
7674 /* Update the last sample_duration if needed. */
7675 isom_trun_entry_t *trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
7676 if( trun->sample_count == 1 && traf->trun_list->entry_count == 1 )
7678 isom_trex_entry_t *trex = isom_get_trex( traf->root->moov->mvex, tfhd->track_ID );
7679 if( !trex )
7680 return -1;
7681 if( last_duration != trex->default_sample_duration )
7682 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
7683 tfhd->default_sample_duration = last_duration;
7685 else if( last_duration != tfhd->default_sample_duration )
7686 trun->flags |= ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT;
7687 if( trun->flags )
7689 isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, trun->sample_count );
7690 if( !row )
7691 return -1;
7692 row->sample_duration = last_duration;
7694 traf->cache->fragment->last_duration = last_duration;
7695 return 0;
7698 int lsmash_set_last_sample_delta( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_delta )
7700 if( !root || !track_ID )
7701 return -1;
7702 if( root->fragment && root->fragment->movie )
7704 isom_traf_entry_t *traf = isom_get_traf( root->fragment->movie, track_ID );
7705 if( !traf || !traf->cache || !traf->tfhd || !traf->trun_list )
7706 return -1;
7707 return isom_set_fragment_last_duration( traf, sample_delta );
7709 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
7710 if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl
7711 || !trak->mdia->minf->stbl->stsz || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list )
7712 return -1;
7713 isom_stbl_t *stbl = trak->mdia->minf->stbl;
7714 isom_stts_t *stts = stbl->stts;
7715 uint32_t sample_count = isom_get_sample_count( trak );
7716 if( !stts->list->tail )
7718 if( !sample_count )
7719 return 0; /* no samples */
7720 if( sample_count > 1 )
7721 return -1; /* irregular sample_count */
7722 if( isom_add_stts_entry( stbl, sample_delta ) )
7723 return -1;
7724 return lsmash_update_track_duration( root, track_ID, 0 );
7726 uint32_t i = 0;
7727 for( lsmash_entry_t *entry = stts->list->head; entry; entry = entry->next )
7728 i += ((isom_stts_entry_t *)entry->data)->sample_count;
7729 if( sample_count < i )
7730 return -1;
7731 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stts->list->tail->data;
7732 if( !last_stts_data )
7733 return -1;
7734 if( sample_count > i )
7736 if( sample_count - i > 1 )
7737 return -1;
7738 /* Add a sample_delta. */
7739 if( sample_delta == last_stts_data->sample_delta )
7740 ++ last_stts_data->sample_count;
7741 else if( isom_add_stts_entry( stbl, sample_delta ) )
7742 return -1;
7744 else if( sample_count == i && isom_replace_last_sample_delta( stbl, sample_delta ) )
7745 return -1;
7746 return lsmash_update_track_duration( root, track_ID, sample_delta );
7749 void lsmash_discard_boxes( lsmash_root_t *root )
7751 if( !root )
7752 return;
7753 isom_remove_ftyp( root->ftyp );
7754 isom_remove_moov( root );
7755 lsmash_remove_list( root->moof_list, isom_remove_moof );
7756 isom_remove_mdat( root->mdat );
7757 isom_remove_free( root->free );
7758 isom_remove_mfra( root->mfra );
7759 root->ftyp = NULL;
7760 root->moov = NULL;
7761 root->moof_list = NULL;
7762 root->mdat = NULL;
7763 root->free = NULL;
7764 root->mfra = NULL;
7767 void lsmash_destroy_root( lsmash_root_t *root )
7769 if( !root )
7770 return;
7771 #ifdef LSMASH_DEMUXER_ENABLED
7772 isom_remove_print_funcs( root );
7773 isom_remove_timelines( root );
7774 #endif
7775 lsmash_discard_boxes( root );
7776 if( root->bs )
7778 if( root->bs->stream )
7779 fclose( root->bs->stream );
7780 if( root->bs->data )
7781 free( root->bs->data );
7782 free( root->bs );
7784 if( root->fragment )
7786 lsmash_remove_list( root->fragment->pool, lsmash_delete_sample );
7787 free( root->fragment );
7789 free( root );
7792 /*---- timeline manipulator ----*/
7794 int lsmash_modify_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 )
7796 if( !segment_duration || media_time < -1 )
7797 return -1;
7798 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
7799 if( !trak || !trak->edts || !trak->edts->elst || !trak->edts->elst->list )
7800 return -1;
7801 isom_elst_t *elst = trak->edts->elst;
7802 isom_elst_entry_t *data = (isom_elst_entry_t *)lsmash_get_entry_data( elst->list, entry_number );
7803 if( !data )
7804 return -1;
7805 data->segment_duration = segment_duration;
7806 data->media_time = media_time;
7807 data->media_rate = media_rate;
7808 if( !elst->pos || !root->fragment || root->bs->stream == stdout )
7809 return isom_update_tkhd_duration( trak );
7810 /* Rewrite the specified entry.
7811 * Note: we don't update the version of the Edit List Box. */
7812 lsmash_bs_t *bs = root->bs;
7813 FILE *stream = bs->stream;
7814 uint64_t current_pos = lsmash_ftell( stream );
7815 uint64_t entry_pos = elst->pos + ISOM_DEFAULT_LIST_FULLBOX_HEADER_SIZE + ((uint64_t)entry_number - 1) * (elst->version == 1 ? 20 : 12);
7816 lsmash_fseek( stream, entry_pos, SEEK_SET );
7817 if( elst->version )
7819 lsmash_bs_put_be64( bs, data->segment_duration );
7820 lsmash_bs_put_be64( bs, data->media_time );
7822 else
7824 lsmash_bs_put_be32( bs, (uint32_t)data->segment_duration );
7825 lsmash_bs_put_be32( bs, (uint32_t)data->media_time );
7827 lsmash_bs_put_be32( bs, data->media_rate );
7828 int ret = lsmash_bs_write_data( bs );
7829 lsmash_fseek( stream, current_pos, SEEK_SET );
7830 return ret;
7833 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 )
7835 if( media_time < -1 )
7836 return -1;
7837 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
7838 if( !trak || !trak->tkhd )
7839 return -1;
7840 segment_duration = (segment_duration || root->fragment) ? segment_duration
7841 : trak->tkhd->duration ? trak->tkhd->duration
7842 : isom_update_tkhd_duration( trak ) ? 0
7843 : trak->tkhd->duration;
7844 if( isom_add_edts( trak )
7845 || isom_add_elst( trak->edts )
7846 || isom_add_elst_entry( trak->edts->elst, segment_duration, media_time, media_rate ) )
7847 return -1;
7848 return isom_update_tkhd_duration( trak );
7851 /*---- create / modification time fields manipulators ----*/
7853 int lsmash_update_media_modification_time( lsmash_root_t *root, uint32_t track_ID )
7855 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
7856 if( !trak || !trak->mdia || !trak->mdia->mdhd )
7857 return -1;
7858 isom_mdhd_t *mdhd = trak->mdia->mdhd;
7859 mdhd->modification_time = isom_get_current_mp4time();
7860 /* overwrite strange creation_time */
7861 if( mdhd->creation_time > mdhd->modification_time )
7862 mdhd->creation_time = mdhd->modification_time;
7863 return 0;
7866 int lsmash_update_track_modification_time( lsmash_root_t *root, uint32_t track_ID )
7868 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
7869 if( !trak || !trak->tkhd )
7870 return -1;
7871 isom_tkhd_t *tkhd = trak->tkhd;
7872 tkhd->modification_time = isom_get_current_mp4time();
7873 /* overwrite strange creation_time */
7874 if( tkhd->creation_time > tkhd->modification_time )
7875 tkhd->creation_time = tkhd->modification_time;
7876 return 0;
7879 int lsmash_update_movie_modification_time( lsmash_root_t *root )
7881 if( !root || !root->moov || !root->moov->mvhd )
7882 return -1;
7883 isom_mvhd_t *mvhd = root->moov->mvhd;
7884 mvhd->modification_time = isom_get_current_mp4time();
7885 /* overwrite strange creation_time */
7886 if( mvhd->creation_time > mvhd->modification_time )
7887 mvhd->creation_time = mvhd->modification_time;
7888 return 0;
7891 /*---- sample manipulators ----*/
7892 lsmash_sample_t *lsmash_create_sample( uint32_t size )
7894 lsmash_sample_t *sample = malloc( sizeof(lsmash_sample_t) );
7895 if( !sample )
7896 return NULL;
7897 memset( sample, 0, sizeof(lsmash_sample_t) );
7898 if( size )
7900 sample->data = malloc( size );
7901 if( !sample->data )
7903 free( sample );
7904 return NULL;
7906 sample->length = size;
7908 return sample;
7911 int lsmash_sample_alloc( lsmash_sample_t *sample, uint32_t size )
7913 if( !sample )
7914 return -1;
7915 if( !size )
7917 if( sample->data )
7918 free( sample->data );
7919 sample->data = NULL;
7920 sample->length = 0;
7921 return 0;
7923 if( size == sample->length )
7924 return 0;
7925 uint8_t *data;
7926 if( !sample->data )
7927 data = malloc( size );
7928 else
7929 data = realloc( sample->data, size );
7930 if( !data )
7931 return -1;
7932 sample->data = data;
7933 sample->length = size;
7934 return 0;
7937 void lsmash_delete_sample( lsmash_sample_t *sample )
7939 if( !sample )
7940 return;
7941 if( sample->data )
7942 free( sample->data );
7943 free( sample );
7946 static uint32_t isom_add_size( isom_trak_entry_t *trak, uint32_t sample_size )
7948 if( isom_add_stsz_entry( trak->mdia->minf->stbl, sample_size ) )
7949 return 0;
7950 return isom_get_sample_count( trak );
7953 static uint32_t isom_add_dts( isom_trak_entry_t *trak, uint64_t dts )
7955 if( !trak->cache || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list )
7956 return 0;
7957 isom_stbl_t *stbl = trak->mdia->minf->stbl;
7958 isom_stts_t *stts = stbl->stts;
7959 isom_timestamp_t *cache = &trak->cache->timestamp;
7960 if( !stts->list->entry_count )
7962 if( isom_add_stts_entry( stbl, dts ) )
7963 return 0;
7964 cache->dts = dts;
7965 return dts;
7967 if( dts <= cache->dts )
7968 return 0;
7969 uint32_t sample_delta = dts - cache->dts;
7970 isom_stts_entry_t *data = (isom_stts_entry_t *)stts->list->tail->data;
7971 if( data->sample_delta == sample_delta )
7972 ++ data->sample_count;
7973 else if( isom_add_stts_entry( stbl, sample_delta ) )
7974 return 0;
7975 cache->dts = dts;
7976 return sample_delta;
7979 static int isom_add_cts( isom_trak_entry_t *trak, uint64_t cts )
7981 if( !trak->cache )
7982 return -1;
7983 isom_stbl_t *stbl = trak->mdia->minf->stbl;
7984 isom_ctts_t *ctts = stbl->ctts;
7985 isom_timestamp_t *cache = &trak->cache->timestamp;
7986 if( !ctts )
7988 if( cts == cache->dts )
7990 cache->cts = cts;
7991 return 0;
7993 /* Add ctts box and the first ctts entry. */
7994 if( isom_add_ctts( stbl ) || isom_add_ctts_entry( stbl, 0 ) )
7995 return -1;
7996 uint32_t sample_count = isom_get_sample_count( trak );
7997 ctts = stbl->ctts;
7998 isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->head->data;
7999 if( sample_count != 1 )
8001 data->sample_count = isom_get_sample_count( trak ) - 1;
8002 if( isom_add_ctts_entry( stbl, cts - cache->dts ) )
8003 return -1;
8005 else
8006 data->sample_offset = cts;
8007 cache->cts = cts;
8008 return 0;
8010 if( !ctts->list )
8011 return -1;
8012 isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->tail->data;
8013 uint32_t sample_offset = cts - cache->dts;
8014 if( data->sample_offset == sample_offset )
8015 ++ data->sample_count;
8016 else if( isom_add_ctts_entry( stbl, sample_offset ) )
8017 return -1;
8018 cache->cts = cts;
8019 return 0;
8022 static int isom_add_timestamp( isom_trak_entry_t *trak, uint64_t dts, uint64_t cts )
8024 if( cts < dts )
8025 return -1;
8026 uint32_t sample_count = isom_get_sample_count( trak );
8027 uint32_t sample_delta = sample_count > 1 ? isom_add_dts( trak, dts ) : 0;
8028 if( sample_count > 1 && !sample_delta )
8029 return -1;
8030 if( isom_add_cts( trak, cts ) )
8031 return -1;
8032 if( trak->cache->fragment )
8034 isom_cache_t *cache = trak->cache;
8035 cache->fragment->last_duration = sample_delta;
8036 cache->fragment->largest_cts = LSMASH_MAX( cache->timestamp.cts, cache->fragment->largest_cts );
8038 return 0;
8041 static int isom_add_sync_point( isom_trak_entry_t *trak, uint32_t sample_number, lsmash_sample_property_t *prop )
8043 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8044 isom_cache_t *cache = trak->cache;
8045 if( prop->random_access_type != ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC ) /* no null check for prop */
8047 if( !cache->all_sync )
8048 return 0;
8049 if( !stbl->stss && isom_add_stss( stbl ) )
8050 return -1;
8051 if( isom_add_stss_entry( stbl, 1 ) ) /* Declare here the first sample is a sync sample. */
8052 return -1;
8053 cache->all_sync = 0;
8054 return 0;
8056 if( cache->all_sync ) /* We don't need stss box if all samples are sync sample. */
8057 return 0;
8058 if( !stbl->stss )
8060 if( isom_get_sample_count( trak ) == 1 )
8062 cache->all_sync = 1; /* Also the first sample is a sync sample. */
8063 return 0;
8065 if( isom_add_stss( stbl ) )
8066 return -1;
8068 return isom_add_stss_entry( stbl, sample_number );
8071 static int isom_add_partial_sync( isom_trak_entry_t *trak, uint32_t sample_number, lsmash_sample_property_t *prop )
8073 if( !trak->root->qt_compatible )
8074 return 0;
8075 if( prop->random_access_type != QT_SAMPLE_RANDOM_ACCESS_TYPE_PARTIAL_SYNC
8076 && !(prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_RECOVERY && prop->recovery.identifier == prop->recovery.complete) )
8077 return 0;
8078 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8079 if( !stbl->stps && isom_add_stps( stbl ) )
8080 return -1;
8081 return isom_add_stps_entry( stbl, sample_number );
8084 static int isom_add_dependency_type( isom_trak_entry_t *trak, lsmash_sample_property_t *prop )
8086 if( !trak->root->qt_compatible && !trak->root->avc_extensions )
8087 return 0;
8088 uint8_t avc_extensions = trak->root->avc_extensions;
8089 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8090 if( stbl->sdtp )
8091 return isom_add_sdtp_entry( stbl, prop, avc_extensions );
8092 if( !prop->allow_earlier && !prop->leading && !prop->independent && !prop->disposable && !prop->redundant ) /* no null check for prop */
8093 return 0;
8094 if( isom_add_sdtp( stbl ) )
8095 return -1;
8096 uint32_t count = isom_get_sample_count( trak );
8097 /* fill past samples with ISOM_SAMPLE_*_UNKNOWN */
8098 lsmash_sample_property_t null_prop = { 0 };
8099 for( uint32_t i = 1; i < count; i++ )
8100 if( isom_add_sdtp_entry( stbl, &null_prop, avc_extensions ) )
8101 return -1;
8102 return isom_add_sdtp_entry( stbl, prop, avc_extensions );
8105 static int isom_group_random_access( isom_trak_entry_t *trak, lsmash_sample_property_t *prop )
8107 if( trak->root->max_isom_version < 6 )
8108 return 0;
8109 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8110 isom_sbgp_entry_t *sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_RAP );
8111 isom_sgpd_entry_t *sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
8112 if( !sbgp || !sgpd )
8113 return 0;
8114 uint8_t is_rap = prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP
8115 || prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_OPEN_RAP
8116 || prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_UNKNOWN_RAP
8117 || (prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_RECOVERY && prop->recovery.identifier == prop->recovery.complete);
8118 isom_rap_group_t *group = trak->cache->rap;
8119 if( !group )
8121 /* This sample is the first sample, create a grouping cache. */
8122 assert( isom_get_sample_count( trak ) == 1 );
8123 group = malloc( sizeof(isom_rap_group_t) );
8124 if( !group )
8125 return -1;
8126 if( is_rap )
8128 group->random_access = isom_add_rap_group_entry( sgpd );
8129 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count );
8131 else
8133 /* The first sample is not always random access point. */
8134 group->random_access = NULL;
8135 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
8137 if( !group->assignment )
8139 free( group );
8140 return -1;
8142 /* No need checking if group->assignment exists from here. */
8143 group->is_prev_rap = is_rap;
8144 trak->cache->rap = group;
8145 return 0;
8147 if( group->is_prev_rap )
8149 /* OK. here, the previous sample is a menber of 'rap '. */
8150 if( !is_rap )
8152 /* This sample isn't a member of 'rap ' and the previous sample is.
8153 * So we create a new group and set 0 on its group_description_index. */
8154 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
8155 if( !group->assignment )
8157 free( group );
8158 return -1;
8161 else if( prop->random_access_type != ISOM_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP )
8163 /* Create a new group since there is the possibility the next sample is a leading sample.
8164 * This sample is a member of 'rap ', so we set appropriate value on its group_description_index. */
8165 if( group->random_access )
8166 group->random_access->num_leading_samples_known = 1;
8167 group->random_access = isom_add_rap_group_entry( sgpd );
8168 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count );
8169 if( !group->assignment )
8171 free( group );
8172 return -1;
8175 else /* The previous and current sample are a member of 'rap ', and the next sample must not be a leading sample. */
8176 ++ group->assignment->sample_count;
8178 else if( is_rap )
8180 /* This sample is a member of 'rap ' and the previous sample isn't.
8181 * So we create a new group and set appropriate value on its group_description_index. */
8182 if( group->random_access )
8183 group->random_access->num_leading_samples_known = 1;
8184 group->random_access = isom_add_rap_group_entry( sgpd );
8185 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count );
8186 if( !group->assignment )
8188 free( group );
8189 return -1;
8192 else /* The previous and current sample aren't a member of 'rap '. */
8193 ++ group->assignment->sample_count;
8194 /* Obtain the property of the latest random access point group. */
8195 if( !is_rap && group->random_access )
8197 if( prop->leading == ISOM_SAMPLE_LEADING_UNKNOWN )
8199 /* We can no longer know num_leading_samples in this group. */
8200 group->random_access->num_leading_samples_known = 0;
8201 group->random_access = NULL;
8203 else
8205 if( prop->leading == ISOM_SAMPLE_IS_UNDECODABLE_LEADING || prop->leading == ISOM_SAMPLE_IS_DECODABLE_LEADING )
8206 ++ group->random_access->num_leading_samples;
8207 else
8209 /* no more consecutive leading samples in this group */
8210 group->random_access->num_leading_samples_known = 1;
8211 group->random_access = NULL;
8215 group->is_prev_rap = is_rap;
8216 return 0;
8219 static int isom_group_roll_recovery( isom_trak_entry_t *trak, lsmash_sample_property_t *prop )
8221 if( !trak->root->avc_extensions )
8222 return 0;
8223 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8224 isom_sbgp_entry_t *sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_ROLL );
8225 isom_sgpd_entry_t *sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_ROLL );
8226 if( !sbgp || !sgpd )
8227 return 0;
8228 lsmash_entry_list_t *pool = trak->cache->roll.pool;
8229 if( !pool )
8231 pool = lsmash_create_entry_list();
8232 if( !pool )
8233 return -1;
8234 trak->cache->roll.pool = pool;
8236 isom_roll_group_t *group = (isom_roll_group_t *)lsmash_get_entry_data( pool, pool->entry_count );
8237 uint32_t sample_count = isom_get_sample_count( trak );
8238 if( !group || prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_RECOVERY )
8240 if( group )
8241 group->delimited = 1;
8242 else
8243 assert( sample_count == 1 );
8244 /* Create a new group. This group is not 'roll' yet, so we set 0 on its group_description_index. */
8245 group = malloc( sizeof(isom_roll_group_t) );
8246 if( !group )
8247 return -1;
8248 memset( group, 0, sizeof(isom_roll_group_t) );
8249 group->first_sample = sample_count;
8250 group->recovery_point = prop->recovery.complete;
8251 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
8252 if( !group->assignment || lsmash_add_entry( pool, group ) )
8254 free( group );
8255 return -1;
8258 else
8259 ++ group->assignment->sample_count;
8260 /* If encountered a sync sample, all recoveries are completed here. */
8261 if( prop->random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_CLOSED_RAP )
8263 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
8265 group = (isom_roll_group_t *)entry->data;
8266 if( !group )
8267 return -1;
8268 group->described = 1;
8270 return 0;
8272 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
8274 group = (isom_roll_group_t *)entry->data;
8275 if( !group )
8276 return -1;
8277 if( group->described )
8278 continue;
8279 if( prop->recovery.identifier == group->recovery_point )
8281 group->described = 1;
8282 int16_t distance = sample_count - group->first_sample;
8283 /* Add a roll recovery entry only when roll_distance isn't zero since roll_distance = 0 must not be used. */
8284 if( distance )
8286 /* Now, this group is a 'roll'. */
8287 if( !isom_add_roll_group_entry( sgpd, distance ) )
8288 return -1;
8289 group->assignment->group_description_index = sgpd->list->entry_count;
8290 /* All groups before the current group are described. */
8291 lsmash_entry_t *current = entry;
8292 for( entry = pool->head; entry != current; entry = entry->next )
8294 group = (isom_roll_group_t *)entry->data;
8295 if( !group )
8296 return -1;
8297 group->described = 1;
8300 break; /* Avoid evaluating groups, in the pool, having the same identifier for recovery point again. */
8303 /* Remove pooled caches that has become unnecessary. */
8304 for( lsmash_entry_t *entry = pool->head; entry; entry = pool->head )
8306 group = (isom_roll_group_t *)entry->data;
8307 if( !group )
8308 return -1;
8309 if( !group->delimited || !group->described )
8310 break;
8311 if( lsmash_remove_entry_direct( pool, entry, NULL ) )
8312 return -1;
8314 return 0;
8317 /* returns 1 if pooled samples must be flushed. */
8318 /* FIXME: I wonder if this function should have a extra argument which indicates force_to_flush_cached_chunk.
8319 see lsmash_append_sample for detail. */
8320 static int isom_add_chunk( isom_trak_entry_t *trak, lsmash_sample_t *sample )
8322 if( !trak->root || !trak->cache || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale
8323 || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
8324 return -1;
8325 lsmash_root_t *root = trak->root;
8326 isom_chunk_t *current = &trak->cache->chunk;
8327 if( !current->pool )
8329 /* Very initial settings, just once per track */
8330 current->pool = lsmash_create_entry_list();
8331 if( !current->pool )
8332 return -1;
8334 if( !current->pool->entry_count )
8336 /* Cannot decide whether we should flush the current sample or not here yet. */
8337 ++ current->chunk_number;
8338 current->sample_description_index = sample->index;
8339 current->first_dts = sample->dts;
8340 return 0;
8342 if( sample->dts < current->first_dts )
8343 return -1; /* easy error check. */
8344 if( (root->max_chunk_duration >= ((double)(sample->dts - current->first_dts) / trak->mdia->mdhd->timescale))
8345 && (root->max_chunk_size >= current->pool_size + sample->length)
8346 && (current->sample_description_index == sample->index) )
8347 return 0; /* No need to flush current cached chunk, the current sample must be put into that. */
8348 /* NOTE: chunk relative stuff must be pushed into root after a chunk is fully determined with its contents. */
8349 /* now current cached chunk is fixed, actually add chunk relative properties to root accordingly. */
8350 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8351 lsmash_entry_t *last_stsc_entry = stbl->stsc->list->tail;
8352 /* Create a new chunk sequence in this track if needed. */
8353 if( (!last_stsc_entry || current->pool->entry_count != ((isom_stsc_entry_t *)last_stsc_entry->data)->samples_per_chunk)
8354 && isom_add_stsc_entry( stbl, current->chunk_number, current->pool->entry_count, current->sample_description_index ) )
8355 return -1;
8356 /* Add a new chunk offset in this track. */
8357 uint64_t offset = root->size;
8358 if( root->fragment )
8359 offset += ISOM_DEFAULT_BOX_HEADER_SIZE + root->fragment->pool_size;
8360 if( isom_add_stco_entry( stbl, offset ) )
8361 return -1;
8362 /* update cache information */
8363 ++ current->chunk_number;
8364 /* re-initialize cache, using the current sample */
8365 current->sample_description_index = sample->index;
8366 current->first_dts = sample->dts;
8367 /* current->pool must be flushed in isom_append_sample_internal() */
8368 return 1;
8371 static int isom_write_pooled_samples( lsmash_root_t *root, isom_chunk_t *chunk )
8373 if( !root || !root->mdat || !root->bs || !root->bs->stream )
8374 return -1;
8375 uint64_t chunk_size = 0;
8376 for( lsmash_entry_t *entry = chunk->pool->head; entry; entry = entry->next )
8378 lsmash_sample_t *sample = (lsmash_sample_t *)entry->data;
8379 if( !sample || !sample->data )
8380 return -1;
8381 lsmash_bs_put_bytes( root->bs, sample->data, sample->length );
8382 chunk_size += sample->length;
8384 if( lsmash_bs_write_data( root->bs ) )
8385 return -1;
8386 root->mdat->size += chunk_size;
8387 root->size += chunk_size;
8388 lsmash_remove_entries( chunk->pool, lsmash_delete_sample );
8389 chunk->pool_size = 0;
8390 return 0;
8393 static int isom_update_sample_tables( isom_trak_entry_t *trak, lsmash_sample_t *sample )
8395 /* Add a sample_size and increment sample_count. */
8396 uint32_t sample_count = isom_add_size( trak, sample->length );
8397 if( !sample_count )
8398 return -1;
8399 /* Add a decoding timestamp and a composition timestamp. */
8400 if( isom_add_timestamp( trak, sample->dts, sample->cts ) )
8401 return -1;
8402 /* Add a sync point if needed. */
8403 if( isom_add_sync_point( trak, sample_count, &sample->prop ) )
8404 return -1;
8405 /* Add a partial sync point if needed. */
8406 if( isom_add_partial_sync( trak, sample_count, &sample->prop ) )
8407 return -1;
8408 /* Add leading, independent, disposable and redundant information if needed. */
8409 if( isom_add_dependency_type( trak, &sample->prop ) )
8410 return -1;
8411 /* Group samples into random access point type if needed. */
8412 if( isom_group_random_access( trak, &sample->prop ) )
8413 return -1;
8414 /* Group samples into random access recovery point type if needed. */
8415 if( isom_group_roll_recovery( trak, &sample->prop ) )
8416 return -1;
8417 /* Add a chunk if needed. */
8418 return isom_add_chunk( trak, sample );
8421 static void isom_append_fragment_track_run( lsmash_root_t *root, isom_chunk_t *chunk )
8423 if( !chunk->pool || !chunk->pool->head )
8424 return;
8425 isom_fragment_manager_t *fragment = root->fragment;
8426 /* Move samples in the pool of the current track fragment to the pool of the current movie fragment.
8427 * Empty the pool of current track. We don't delete data of samples here. */
8428 if( fragment->pool->tail )
8430 fragment->pool->tail->next = chunk->pool->head;
8431 fragment->pool->tail->next->prev = fragment->pool->tail;
8433 else
8434 fragment->pool->head = chunk->pool->head;
8435 fragment->pool->tail = chunk->pool->tail;
8436 fragment->pool->entry_count += chunk->pool->entry_count;
8437 fragment->pool_size += chunk->pool_size;
8438 chunk->pool_size = 0;
8439 chunk->pool->entry_count = 0;
8440 chunk->pool->head = NULL;
8441 chunk->pool->tail = NULL;
8444 static int isom_output_cached_chunk( isom_trak_entry_t *trak )
8446 lsmash_root_t *root = trak->root;
8447 isom_chunk_t *chunk = &trak->cache->chunk;
8448 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8449 lsmash_entry_t *last_stsc_entry = stbl->stsc->list->tail;
8450 /* Create a new chunk sequence in this track if needed. */
8451 if( (!last_stsc_entry || chunk->pool->entry_count != ((isom_stsc_entry_t *)last_stsc_entry->data)->samples_per_chunk)
8452 && isom_add_stsc_entry( stbl, chunk->chunk_number, chunk->pool->entry_count, chunk->sample_description_index ) )
8453 return -1;
8454 if( root->fragment )
8456 /* Add a new chunk offset in this track. */
8457 if( isom_add_stco_entry( stbl, root->size + ISOM_DEFAULT_BOX_HEADER_SIZE + root->fragment->pool_size ) )
8458 return -1;
8459 isom_append_fragment_track_run( root, chunk );
8460 return 0;
8462 /* Add a new chunk offset in this track. */
8463 if( isom_add_stco_entry( stbl, root->size ) )
8464 return -1;
8465 /* Output pooled samples in this track. */
8466 return isom_write_pooled_samples( root, chunk );
8469 static int isom_append_sample_internal( isom_trak_entry_t *trak, lsmash_sample_t *sample )
8471 int flush = isom_update_sample_tables( trak, sample );
8472 if( flush < 0 )
8473 return -1;
8474 /* flush == 1 means pooled samples must be flushed. */
8475 lsmash_root_t *root = trak->root;
8476 isom_chunk_t *current = &trak->cache->chunk;
8477 if( flush == 1 && isom_write_pooled_samples( root, current ) )
8478 return -1;
8479 /* Arbitration system between tracks with extremely scattering dts.
8480 * Here, we check whether asynchronization between the tracks exceeds the tolerance.
8481 * If a track has too old "first DTS" in its cached chunk than current sample's DTS, then its pooled samples must be flushed.
8482 * We don't consider presentation of media since any edit can pick an arbitrary portion of media in track.
8483 * Note: you needn't read this loop until you grasp the basic handling of chunks. */
8484 double tolerance = root->max_async_tolerance;
8485 for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
8487 isom_trak_entry_t *other = (isom_trak_entry_t *)entry->data;
8488 if( trak == other )
8489 continue;
8490 if( !other || !other->cache || !other->mdia || !other->mdia->mdhd || !other->mdia->mdhd->timescale
8491 || !other->mdia->minf || !other->mdia->minf->stbl || !other->mdia->minf->stbl->stsc || !other->mdia->minf->stbl->stsc->list )
8492 return -1;
8493 isom_chunk_t *chunk = &other->cache->chunk;
8494 if( !chunk->pool || !chunk->pool->entry_count )
8495 continue;
8496 double diff = ((double)sample->dts / trak->mdia->mdhd->timescale)
8497 - ((double)chunk->first_dts / other->mdia->mdhd->timescale);
8498 if( diff > tolerance && isom_output_cached_chunk( other ) )
8499 return -1;
8500 /* Note: we don't flush the cached chunk in the current track and the current sample here
8501 * even if the conditional expression of '-diff > tolerance' meets.
8502 * That's useless because appending a sample to another track would be a good equivalent.
8503 * It's even harmful because it causes excess chunk division by calling
8504 * isom_output_cached_chunk() which always generates a new chunk.
8505 * Anyway some excess chunk division will be there, but rather less without it.
8506 * To completely avoid this, we need to observe at least whether the current sample will be placed
8507 * right next to the previous chunk of the same track or not. */
8509 /* anyway the current sample must be pooled. */
8510 if( lsmash_add_entry( current->pool, sample ) )
8511 return -1;
8512 current->pool_size += sample->length;
8513 return 0;
8516 static int isom_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample )
8518 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
8519 if( !trak || !trak->root || !trak->cache || !trak->mdia
8520 || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale
8521 || !trak->mdia->minf || !trak->mdia->minf->stbl
8522 || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list
8523 || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
8524 return -1;
8525 /* If there is no available Media Data Box to write samples, add and write a new one before any chunk offset is decided. */
8526 if( !root->mdat )
8528 if( isom_new_mdat( root, 0 ) )
8529 return -1;
8530 /* Add the size of the Media Data Box and the placeholder. */
8531 root->size += 2 * ISOM_DEFAULT_BOX_HEADER_SIZE;
8533 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, sample->index );
8534 if( !sample_entry )
8535 return -1;
8536 if( isom_is_lpcm_audio( sample_entry->type ) )
8538 uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket;
8539 uint64_t dts = sample->dts;
8540 uint64_t cts = sample->cts;
8541 /* Append samples splitted into each LPCMFrame. */
8542 for( uint32_t offset = 0; offset < sample->length; offset += frame_size )
8544 lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size );
8545 if( !lpcm_sample )
8546 return -1;
8547 memcpy( lpcm_sample->data, sample->data + offset, frame_size );
8548 lpcm_sample->dts = dts++;
8549 lpcm_sample->cts = cts++;
8550 lpcm_sample->prop = sample->prop;
8551 lpcm_sample->index = sample->index;
8552 if( isom_append_sample_internal( trak, lpcm_sample ) )
8554 lsmash_delete_sample( lpcm_sample );
8555 return -1;
8558 lsmash_delete_sample( sample );
8559 return 0;
8561 return isom_append_sample_internal( trak, sample );
8564 static int isom_output_cache( isom_trak_entry_t *trak )
8566 if( trak->cache->chunk.pool && trak->cache->chunk.pool->entry_count
8567 && isom_output_cached_chunk( trak ) )
8568 return -1;
8569 isom_stbl_t *stbl = trak->mdia->minf->stbl;
8570 if( !stbl->sgpd_list )
8571 return 0;
8572 for( lsmash_entry_t *entry = stbl->sgpd_list->head; entry; entry = entry->next )
8574 isom_sgpd_entry_t *sgpd = (isom_sgpd_entry_t *)entry->data;
8575 if( !sgpd )
8576 return -1;
8577 switch( sgpd->grouping_type )
8579 case ISOM_GROUP_TYPE_RAP :
8581 isom_rap_group_t *group = trak->cache->rap;
8582 if( !group )
8584 if( trak->root->fragment )
8585 continue;
8586 else
8587 return -1;
8589 if( !group->random_access )
8590 continue;
8591 group->random_access->num_leading_samples_known = 1;
8592 break;
8594 case ISOM_GROUP_TYPE_ROLL :
8595 if( !trak->cache->roll.pool )
8597 if( trak->root->fragment )
8598 continue;
8599 else
8600 return -1;
8602 for( lsmash_entry_t *roll_entry = trak->cache->roll.pool->head; roll_entry; roll_entry = roll_entry->next )
8604 isom_roll_group_t *group = (isom_roll_group_t *)roll_entry->data;
8605 if( !group )
8606 return -1;
8607 group->described = 1;
8609 break;
8610 default :
8611 break;
8614 return 0;
8617 static int isom_flush_fragment_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_duration )
8619 isom_traf_entry_t *traf = isom_get_traf( root->fragment->movie, track_ID );
8620 if( !traf )
8621 return 0; /* no samples */
8622 if( !traf->cache || !traf->cache->fragment )
8623 return -1;
8624 if( traf->trun_list && traf->trun_list->entry_count && traf->trun_list->tail && traf->trun_list->tail->data )
8626 /* Media Data Box preceded by Movie Fragment Box could change base_data_offsets in each track fragments later.
8627 * We can't consider this here because the length of Movie Fragment Box is unknown at this step yet. */
8628 isom_trun_entry_t *trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
8629 if( root->fragment->pool_size )
8630 trun->flags |= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT;
8631 trun->data_offset = root->fragment->pool_size;
8633 isom_append_fragment_track_run( root, &traf->cache->chunk );
8634 return isom_set_fragment_last_duration( traf, last_sample_duration );
8637 int lsmash_flush_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta )
8639 if( !root )
8640 return -1;
8641 if( root->fragment && root->fragment->movie )
8642 return isom_flush_fragment_pooled_samples( root, track_ID, last_sample_delta );
8643 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
8644 if( !trak || !trak->cache || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl
8645 || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
8646 return -1;
8647 if( isom_output_cache( trak ) )
8648 return -1;
8649 return lsmash_set_last_sample_delta( root, track_ID, last_sample_delta );
8652 /* This function doesn't update sample_duration of the last sample in the previous movie fragment.
8653 * Instead of this, isom_finish_movie_fragment undertakes this task. */
8654 static int isom_update_fragment_previous_sample_duration( isom_traf_entry_t *traf, isom_trex_entry_t *trex, uint32_t duration )
8656 isom_tfhd_t *tfhd = traf->tfhd;
8657 isom_trun_entry_t *trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
8658 int previous_run_has_previous_sample = 0;
8659 if( trun->sample_count == 1 )
8661 if( traf->trun_list->entry_count == 1 )
8662 return 0; /* The previous track run belongs to the previous movie fragment if it exists. */
8663 if( !traf->trun_list->tail->prev || !traf->trun_list->tail->prev->data )
8664 return -1;
8665 /* OK. The previous sample exists in the previous track run in the same track fragment. */
8666 trun = (isom_trun_entry_t *)traf->trun_list->tail->prev->data;
8667 previous_run_has_previous_sample = 1;
8669 /* Update default_sample_duration of the Track Fragment Header Box
8670 * if this duration is what the first sample in the current track fragment owns. */
8671 if( (trun->sample_count == 2 && traf->trun_list->entry_count == 1)
8672 || (trun->sample_count == 1 && traf->trun_list->entry_count == 2) )
8674 if( duration != trex->default_sample_duration )
8675 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
8676 tfhd->default_sample_duration = duration;
8678 /* Update the previous sample_duration if needed. */
8679 if( duration != tfhd->default_sample_duration )
8680 trun->flags |= ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT;
8681 if( trun->flags )
8683 uint32_t sample_number = trun->sample_count - !previous_run_has_previous_sample;
8684 isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, sample_number );
8685 if( !row )
8686 return -1;
8687 row->sample_duration = duration;
8689 traf->cache->fragment->last_duration = duration;
8690 return 0;
8693 static isom_sample_flags_t isom_generate_fragment_sample_flags( lsmash_sample_t *sample )
8695 isom_sample_flags_t flags;
8696 flags.reserved = 0;
8697 flags.is_leading = sample->prop.leading & 0x3;
8698 flags.sample_depends_on = sample->prop.independent & 0x3;
8699 flags.sample_is_depended_on = sample->prop.disposable & 0x3;
8700 flags.sample_has_redundancy = sample->prop.redundant & 0x3;
8701 flags.sample_padding_value = 0;
8702 flags.sample_is_non_sync_sample = sample->prop.random_access_type != ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
8703 flags.sample_degradation_priority = 0;
8704 return flags;
8707 static int isom_update_fragment_sample_tables( isom_traf_entry_t *traf, lsmash_sample_t *sample )
8709 isom_tfhd_t *tfhd = traf->tfhd;
8710 isom_trex_entry_t *trex = isom_get_trex( traf->root->moov->mvex, tfhd->track_ID );
8711 if( !trex )
8712 return -1;
8713 lsmash_root_t *root = traf->root;
8714 isom_cache_t *cache = traf->cache;
8715 /* Create a new track run if the duration exceeds max_chunk_duration.
8716 * Old one will be appended to the pool of this movie fragment. */
8717 int delimit = (root->max_chunk_duration < ((double)(sample->dts - traf->cache->chunk.first_dts) / lsmash_get_media_timescale( root, tfhd->track_ID )))
8718 || (root->max_chunk_size < (cache->chunk.pool_size + sample->length));
8719 isom_trun_entry_t *trun = NULL;
8720 if( !traf->trun_list || !traf->trun_list->entry_count || delimit )
8722 if( delimit && traf->trun_list && traf->trun_list->entry_count && traf->trun_list->tail && traf->trun_list->tail->data )
8724 /* Media Data Box preceded by Movie Fragment Box could change base data offsets in each track fragments later.
8725 * We can't consider this here because the length of Movie Fragment Box is unknown at this step yet. */
8726 trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
8727 if( root->fragment->pool_size )
8728 trun->flags |= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT;
8729 trun->data_offset = root->fragment->pool_size;
8731 trun = isom_add_trun( traf );
8732 if( !trun )
8733 return -1;
8734 if( !cache->chunk.pool )
8736 /* Very initial settings, just once per track */
8737 cache->chunk.pool = lsmash_create_entry_list();
8738 if( !cache->chunk.pool )
8739 return -1;
8742 else
8744 if( !traf->trun_list->tail || !traf->trun_list->tail->data )
8745 return -1;
8746 trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
8748 uint32_t sample_composition_time_offset = sample->cts - sample->dts;
8749 isom_sample_flags_t sample_flags = isom_generate_fragment_sample_flags( sample );
8750 if( ++trun->sample_count == 1 )
8752 if( traf->trun_list->entry_count == 1 )
8754 /* This track fragment isn't empty-duration-fragment any more. */
8755 tfhd->flags &= ~ISOM_TF_FLAGS_DURATION_IS_EMPTY;
8756 /* Set up sample_description_index in this track fragment. */
8757 if( sample->index != trex->default_sample_description_index )
8758 tfhd->flags |= ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT;
8759 tfhd->sample_description_index = cache->chunk.sample_description_index = sample->index;
8760 /* Set up default_sample_size used in this track fragment. */
8761 tfhd->default_sample_size = sample->length;
8762 /* Set up default_sample_flags used in this track fragment.
8763 * Note: we decide an appropriate default value at the end of this movie fragment. */
8764 tfhd->default_sample_flags = sample_flags;
8765 /* Set up random access information if this sample is random accessible sample.
8766 * We inform only the first sample in each movie fragment. */
8767 if( root->bs->stream != stdout && sample->prop.random_access_type )
8769 isom_tfra_entry_t *tfra = isom_get_tfra( root->mfra, tfhd->track_ID );
8770 if( !tfra )
8772 tfra = isom_add_tfra( root->mfra );
8773 if( !tfra )
8774 return -1;
8775 tfra->track_ID = tfhd->track_ID;
8777 if( !tfra->list )
8779 tfra->list = lsmash_create_entry_list();
8780 if( !tfra->list )
8781 return -1;
8783 isom_tfra_location_time_entry_t *rap = malloc( sizeof(isom_tfra_location_time_entry_t) );
8784 if( !rap )
8785 return -1;
8786 rap->time = sample->cts; /* If this is wrong, blame vague descriptions of 'presentation time' in the spec. */
8787 rap->moof_offset = root->size; /* We place Movie Fragment Box in the head of each movie fragment. */
8788 rap->traf_number = cache->fragment->traf_number;
8789 rap->trun_number = traf->trun_list->entry_count;
8790 rap->sample_number = trun->sample_count;
8791 if( lsmash_add_entry( tfra->list, rap ) )
8792 return -1;
8793 tfra->number_of_entry = tfra->list->entry_count;
8794 int length;
8795 for( length = 1; rap->traf_number >> (length * 8); length++ );
8796 tfra->length_size_of_traf_num = LSMASH_MAX( length - 1, tfra->length_size_of_traf_num );
8797 for( length = 1; rap->traf_number >> (length * 8); length++ );
8798 tfra->length_size_of_trun_num = LSMASH_MAX( length - 1, tfra->length_size_of_trun_num );
8799 for( length = 1; rap->sample_number >> (length * 8); length++ );
8800 tfra->length_size_of_sample_num = LSMASH_MAX( length - 1, tfra->length_size_of_sample_num );
8803 trun->first_sample_flags = sample_flags;
8804 cache->chunk.first_dts = sample->dts;
8806 /* Update the optional rows in the current track run except for sample_duration if needed. */
8807 if( sample->length != tfhd->default_sample_size )
8808 trun->flags |= ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT;
8809 if( memcmp( &sample_flags, &tfhd->default_sample_flags, sizeof(isom_sample_flags_t) ) )
8810 trun->flags |= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT;
8811 if( sample_composition_time_offset )
8812 trun->flags |= ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT;
8813 if( trun->flags )
8815 isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, trun->sample_count );
8816 if( !row )
8817 return -1;
8818 row->sample_size = sample->length;
8819 row->sample_flags = sample_flags;
8820 row->sample_composition_time_offset = sample_composition_time_offset;
8822 /* Set up the previous sample_duration if this sample is not the first sample in the overall movie. */
8823 if( cache->fragment->has_samples )
8825 /* Note: when using for live streaming, it is not good idea to return error (-1) by sample->dts < prev_dts
8826 * since that's trivial for such semi-permanent presentation. */
8827 uint64_t prev_dts = cache->timestamp.dts;
8828 if( sample->dts <= prev_dts || sample->dts > prev_dts + UINT32_MAX )
8829 return -1;
8830 uint32_t sample_duration = sample->dts - prev_dts;
8831 if( isom_update_fragment_previous_sample_duration( traf, trex, sample_duration ) )
8832 return -1;
8834 cache->timestamp.dts = sample->dts;
8835 cache->fragment->largest_cts = LSMASH_MAX( sample->cts, cache->fragment->largest_cts );
8836 return delimit;
8839 static int isom_append_fragment_sample_internal_initial( isom_trak_entry_t *trak, lsmash_sample_t *sample )
8841 int delimit = 0;
8842 /* Update the sample tables of this track fragment.
8843 * If a new chunk was created, append the previous one to the pool of this movie fragment. */
8844 delimit = isom_update_sample_tables( trak, sample );
8845 if( delimit < 0 )
8846 return -1;
8847 else if( delimit == 1 )
8848 isom_append_fragment_track_run( trak->root, &trak->cache->chunk );
8849 /* Add a new sample into the pool of this track fragment. */
8850 if( lsmash_add_entry( trak->cache->chunk.pool, sample ) )
8851 return -1;
8852 trak->cache->chunk.pool_size += sample->length;
8853 trak->cache->fragment->has_samples = 1;
8854 return 0;
8857 static int isom_append_fragment_sample_internal( isom_traf_entry_t *traf, lsmash_sample_t *sample )
8859 int delimit = 0;
8860 /* Update the sample tables of this track fragment.
8861 * If a new track run was created, append the previous one to the pool of this movie fragment. */
8862 delimit = isom_update_fragment_sample_tables( traf, sample );
8863 if( delimit < 0 )
8864 return -1;
8865 else if( delimit == 1 )
8866 isom_append_fragment_track_run( traf->root, &traf->cache->chunk );
8867 /* Add a new sample into the pool of this track fragment. */
8868 if( lsmash_add_entry( traf->cache->chunk.pool, sample ) )
8869 return -1;
8870 traf->cache->chunk.pool_size += sample->length;
8871 traf->cache->fragment->has_samples = 1;
8872 return 0;
8875 static int isom_append_fragment_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample )
8877 isom_fragment_manager_t *fragment = root->fragment;
8878 if( !fragment || !fragment->pool )
8879 return -1;
8880 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
8881 if( !trak || !trak->root || !trak->cache || !trak->cache->fragment || !trak->tkhd || !trak->mdia
8882 || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale
8883 || !trak->mdia->minf || !trak->mdia->minf->stbl
8884 || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list
8885 || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
8886 return -1;
8887 int (*append_sample_func)( void *, lsmash_sample_t * ) = NULL;
8888 void *track_fragment = NULL;
8889 if( !fragment->movie )
8891 append_sample_func = (int (*)( void *, lsmash_sample_t * ))isom_append_fragment_sample_internal_initial;
8892 track_fragment = trak;
8894 else
8896 isom_traf_entry_t *traf = isom_get_traf( fragment->movie, track_ID );
8897 if( !traf )
8899 traf = isom_add_traf( root, fragment->movie );
8900 if( isom_add_tfhd( traf ) )
8901 return -1;
8902 traf->tfhd->flags = ISOM_TF_FLAGS_DURATION_IS_EMPTY; /* no samples for this track fragment yet */
8903 traf->tfhd->track_ID = trak->tkhd->track_ID;
8904 traf->cache = trak->cache;
8905 traf->cache->fragment->traf_number = fragment->movie->traf_list->entry_count;
8907 else if( !traf->root || !traf->root->moov || !traf->root->moov->mvex || !traf->cache || !traf->tfhd )
8908 return -1;
8909 append_sample_func = (int (*)( void *, lsmash_sample_t * ))isom_append_fragment_sample_internal;
8910 track_fragment = traf;
8912 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, sample->index );
8913 if( !sample_entry )
8914 return -1;
8915 if( isom_is_lpcm_audio( sample_entry->type ) )
8917 uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket;
8918 uint64_t dts = sample->dts;
8919 uint64_t cts = sample->cts;
8920 /* Append samples splitted into each LPCMFrame. */
8921 for( uint32_t offset = 0; offset < sample->length; offset += frame_size )
8923 lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size );
8924 if( !lpcm_sample )
8925 return -1;
8926 memcpy( lpcm_sample->data, sample->data + offset, frame_size );
8927 lpcm_sample->dts = dts++;
8928 lpcm_sample->cts = cts++;
8929 lpcm_sample->prop = sample->prop;
8930 lpcm_sample->index = sample->index;
8931 if( append_sample_func( track_fragment, lpcm_sample ) )
8933 lsmash_delete_sample( lpcm_sample );
8934 return -1;
8937 lsmash_delete_sample( sample );
8938 return 0;
8940 return append_sample_func( track_fragment, sample );
8943 int lsmash_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample )
8945 /* We think max_chunk_duration == 0, which means all samples will be cached on memory, should be prevented.
8946 * This means removal of a feature that we used to have, but anyway very alone chunk does not make sense. */
8947 if( !root || !root->bs || !sample || !sample->data || !track_ID
8948 || root->max_chunk_duration == 0 || root->max_async_tolerance == 0 )
8949 return -1;
8950 /* Write File Type Box here if it was not written yet. */
8951 if( !root->file_type_written && isom_write_ftyp( root ) )
8952 return -1;
8953 if( root->fragment && root->fragment->pool )
8954 return isom_append_fragment_sample( root, track_ID, sample );
8955 return isom_append_sample( root, track_ID, sample );
8958 /*---- misc functions ----*/
8960 #define CHAPTER_BUFSIZE 512 /* for chapter handling */
8962 static int isom_get_start_time( char *chap_time, isom_chapter_entry_t *data )
8964 uint64_t hh, mm;
8965 double ss;
8966 if( sscanf( chap_time, "%"SCNu64":%2"SCNu64":%lf", &hh, &mm, &ss ) != 3 )
8967 return -1;
8968 /* check overflow */
8969 if( hh >= 5124095 || mm >= 60 || ss >= 60 )
8970 return -1;
8971 /* 1ns timescale */
8972 data->start_time = (hh * 3600 + mm * 60 + ss) * 1e9;
8973 return 0;
8976 static int isom_lumber_line( char *buff, int bufsize, FILE *chapter )
8978 char *tail;
8979 /* remove newline codes and skip empty line */
8981 if( fgets( buff, bufsize, chapter ) == NULL )
8982 return -1;
8983 tail = &buff[ strlen( buff ) - 1 ];
8984 while( tail >= buff && ( *tail == '\n' || *tail == '\r' ) )
8985 *tail-- = '\0';
8986 }while( tail < buff );
8987 return 0;
8990 static int isom_read_simple_chapter( FILE *chapter, isom_chapter_entry_t *data )
8992 char buff[CHAPTER_BUFSIZE];
8993 int len;
8995 /* get start_time */
8996 if( isom_lumber_line( buff, CHAPTER_BUFSIZE, chapter ) )
8997 return -1;
8998 char *chapter_time = strchr( buff, '=' ); /* find separator */
8999 if( !chapter_time++
9000 || isom_get_start_time( chapter_time, data )
9001 || isom_lumber_line( buff, CHAPTER_BUFSIZE, chapter ) ) /* get chapter_name */
9002 return -1;
9003 char *chapter_name = strchr( buff, '=' ); /* find separator */
9004 if( !chapter_name++ )
9005 return -1;
9006 len = LSMASH_MIN( 255, strlen( chapter_name ) ); /* We support length of chapter_name up to 255 */
9007 data->chapter_name = ( char* )malloc( len + 1 );
9008 if( !data->chapter_name )
9009 return -1;
9010 memcpy( data->chapter_name, chapter_name, len );
9011 data->chapter_name[len] = '\0';
9012 return 0;
9015 static int isom_read_minimum_chapter( FILE *chapter, isom_chapter_entry_t *data )
9017 char buff[CHAPTER_BUFSIZE];
9018 int len;
9020 if( isom_lumber_line( buff, CHAPTER_BUFSIZE, chapter ) /* read newline */
9021 || isom_get_start_time( buff, data ) ) /* get start_time */
9022 return -1;
9023 /* get chapter_name */
9024 char *chapter_name = strchr( buff, ' ' ); /* find separator */
9025 if( !chapter_name++ )
9026 return -1;
9027 len = LSMASH_MIN( 255, strlen( chapter_name ) ); /* We support length of chapter_name up to 255 */
9028 data->chapter_name = ( char* )malloc( len + 1 );
9029 if( !data->chapter_name )
9030 return -1;
9031 memcpy( data->chapter_name, chapter_name, len );
9032 data->chapter_name[len] = '\0';
9033 return 0;
9036 typedef int (*fn_get_chapter_data)( FILE *, isom_chapter_entry_t * );
9038 static fn_get_chapter_data isom_check_chap_line( char *file_name )
9040 char buff[CHAPTER_BUFSIZE];
9041 FILE *fp = fopen( file_name, "rb" );
9042 if( !fp )
9043 return NULL;
9044 fn_get_chapter_data fnc = NULL;
9045 if( fgets( buff, CHAPTER_BUFSIZE, fp ) != NULL )
9047 if( strncmp( buff, "CHAPTER", 7 ) == 0 )
9048 fnc = isom_read_simple_chapter;
9049 else if( isdigit( buff[0] ) && isdigit( buff[1] ) && buff[2] == ':'
9050 && isdigit( buff[3] ) && isdigit( buff[4] ) && buff[5] == ':' )
9051 fnc = isom_read_minimum_chapter;
9053 fclose( fp );
9054 return fnc;
9057 int lsmash_set_tyrant_chapter( lsmash_root_t *root, char *file_name )
9059 /* This function should be called after updating of the latest movie duration. */
9060 if( !root || !root->moov || !root->moov->mvhd || !root->moov->mvhd->timescale || !root->moov->mvhd->duration )
9061 return -1;
9062 /* check each line format */
9063 fn_get_chapter_data fnc = isom_check_chap_line( file_name );
9064 if( !fnc )
9065 return -1;
9066 FILE *chapter = fopen( file_name, "rb" );
9067 if( !chapter )
9068 return -1;
9069 if( isom_add_udta( root, 0 ) || isom_add_chpl( root->moov ) )
9070 goto fail;
9071 isom_chapter_entry_t data;
9072 while( !fnc( chapter, &data ) )
9074 data.start_time = (data.start_time + 50) / 100; /* convert to 100ns unit */
9075 if( data.start_time / 1e7 > (double)root->moov->mvhd->duration / root->moov->mvhd->timescale
9076 || isom_add_chpl_entry( root->moov->udta->chpl, &data ) )
9077 goto fail;
9078 free( data.chapter_name );
9079 data.chapter_name = NULL;
9081 fclose( chapter );
9082 return 0;
9083 fail:
9084 if( data.chapter_name )
9085 free( data.chapter_name );
9086 fclose( chapter );
9087 return -1;
9090 int lsmash_create_reference_chapter_track( lsmash_root_t *root, uint32_t track_ID, char *file_name )
9092 if( !root || (!root->qt_compatible && !root->itunes_audio) || !root->moov || !root->moov->mvhd )
9093 return -1;
9094 FILE *chapter = NULL; /* shut up 'uninitialized' warning */
9095 /* Create a Track Reference Box. */
9096 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
9097 if( !trak || isom_add_tref( trak ) )
9098 return -1;
9099 /* Create a track_ID for a new chapter track. */
9100 uint32_t *id = (uint32_t *)malloc( sizeof(uint32_t) );
9101 if( !id )
9102 return -1;
9103 uint32_t chapter_track_ID = *id = root->moov->mvhd->next_track_ID;
9104 /* Create a Track Reference Type Box. */
9105 isom_tref_type_t *chap = isom_add_track_reference_type( trak->tref, QT_TREF_TYPE_CHAP, 1, id );
9106 if( !chap )
9107 return -1; /* no need to free id */
9108 /* Create a reference chapter track. */
9109 if( chapter_track_ID != lsmash_create_track( root, ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK ) )
9110 return -1;
9111 /* Set track parameters. */
9112 lsmash_track_parameters_t track_param;
9113 lsmash_initialize_track_parameters( &track_param );
9114 track_param.mode = ISOM_TRACK_IN_MOVIE | ISOM_TRACK_IN_PREVIEW;
9115 if( lsmash_set_track_parameters( root, chapter_track_ID, &track_param ) )
9116 goto fail;
9117 /* Set media parameters. */
9118 uint64_t media_timescale = lsmash_get_media_timescale( root, track_ID );
9119 if( !media_timescale )
9120 goto fail;
9121 lsmash_media_parameters_t media_param;
9122 lsmash_initialize_media_parameters( &media_param );
9123 media_param.timescale = media_timescale;
9124 media_param.ISO_language = root->max_3gpp_version >= 6 || root->itunes_audio ? "und" : NULL;
9125 media_param.MAC_language = 0;
9126 if( lsmash_set_media_parameters( root, chapter_track_ID, &media_param ) )
9127 goto fail;
9128 /* Create a sample description. */
9129 uint32_t sample_type = root->max_3gpp_version >= 6 || root->itunes_audio ? ISOM_CODEC_TYPE_TX3G_TEXT : QT_CODEC_TYPE_TEXT_TEXT;
9130 uint32_t sample_entry = lsmash_add_sample_entry( root, chapter_track_ID, sample_type, NULL );
9131 if( !sample_entry )
9132 goto fail;
9133 /* Check each line format. */
9134 fn_get_chapter_data fnc = isom_check_chap_line( file_name );
9135 if( !fnc )
9136 return -1;
9137 /* Open chapter format file. */
9138 chapter = fopen( file_name, "rb" );
9139 if( !chapter )
9140 goto fail;
9141 /* Parse the file and write text samples. */
9142 isom_chapter_entry_t data;
9143 while( !fnc( chapter, &data ) )
9145 /* set start_time */
9146 data.start_time = data.start_time * 1e-9 * media_timescale + 0.5;
9147 /* write a text sample here */
9148 uint16_t name_length = strlen( data.chapter_name );
9149 lsmash_sample_t *sample = lsmash_create_sample( 2 + name_length + 12 * (sample_type == QT_CODEC_TYPE_TEXT_TEXT) );
9150 if( !sample )
9151 goto fail;
9152 sample->data[0] = (name_length >> 8) & 0xff;
9153 sample->data[1] = name_length & 0xff;
9154 memcpy( sample->data + 2, data.chapter_name, name_length );
9155 if( sample_type == QT_CODEC_TYPE_TEXT_TEXT )
9157 /* QuickTime Player requires Text Encoding Attribute Box ('encd') if media language is ISO language codes : undefined.
9158 * Also this box can avoid garbling if the QuickTime text sample is encoded by Unicode characters.
9159 * Note: 3GPP Timed Text supports only UTF-8 or UTF-16, so this box isn't needed. */
9160 static const uint8_t encd[12] =
9162 0x00, 0x00, 0x00, 0x0C, /* size: 12 */
9163 0x65, 0x6E, 0x63, 0x64, /* type: 'encd' */
9164 0x00, 0x00, 0x01, 0x00 /* Unicode Encoding */
9166 memcpy( sample->data + 2 + name_length, encd, 12 );
9168 sample->dts = sample->cts = data.start_time;
9169 sample->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
9170 sample->index = sample_entry;
9171 if( lsmash_append_sample( root, chapter_track_ID, sample ) )
9172 goto fail;
9173 free( data.chapter_name );
9174 data.chapter_name = NULL;
9176 if( lsmash_flush_pooled_samples( root, chapter_track_ID, 0 ) )
9177 goto fail;
9178 trak = isom_get_trak( root, chapter_track_ID );
9179 if( !trak )
9180 goto fail;
9181 fclose( chapter );
9182 trak->is_chapter = 1;
9183 trak->related_track_ID = track_ID;
9184 return 0;
9185 fail:
9186 if( chapter )
9187 fclose( chapter );
9188 if( data.chapter_name )
9189 free( data.chapter_name );
9190 free( chap->track_ID );
9191 chap->track_ID = NULL;
9192 /* Remove the reference chapter track attached at tail of the list. */
9193 lsmash_remove_entry_direct( root->moov->trak_list, root->moov->trak_list->tail, isom_remove_trak );
9194 return -1;
9197 void lsmash_delete_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID )
9199 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
9200 if( !trak )
9201 return;
9202 isom_remove_edts( trak->edts );
9203 trak->edts = NULL;
9206 void lsmash_delete_tyrant_chapter( lsmash_root_t *root )
9208 if( !root || !root->moov || !root->moov->udta )
9209 return;
9210 isom_remove_chpl( root->moov->udta->chpl );
9211 root->moov->udta->chpl = NULL;