importer: Don't treat single frame VC-1 and H.264 stream as still image.
[L-SMASH.git] / isom.c
blob4ea1e430f470e6423965e45cf4d98cd0ec9d8dd0
1 /*****************************************************************************
2 * isom.c:
3 *****************************************************************************
4 * Copyright (C) 2010-2012 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>
30 #include "box.h"
31 #include "isom.h"
32 #include "mp4a.h"
33 #include "mp4sys.h"
34 #include "write.h"
35 #include "description.h"
36 #ifdef LSMASH_DEMUXER_ENABLED
37 #include "read.h"
38 #include "print.h"
39 #include "timeline.h"
40 #endif
43 /*---- ----*/
44 char *isom_4cc2str( uint32_t fourcc )
46 static char str[5];
47 str[0] = (fourcc >> 24) & 0xff;
48 str[1] = (fourcc >> 16) & 0xff;
49 str[2] = (fourcc >> 8) & 0xff;
50 str[3] = fourcc & 0xff;
51 str[4] = 0;
52 return str;
55 isom_trak_entry_t *isom_get_trak( lsmash_root_t *root, uint32_t track_ID )
57 if( !track_ID || !root || !root->moov || !root->moov->trak_list )
58 return NULL;
59 for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
61 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
62 if( !trak || !trak->tkhd )
63 return NULL;
64 if( trak->tkhd->track_ID == track_ID )
65 return trak;
67 return NULL;
70 isom_trex_entry_t *isom_get_trex( isom_mvex_t *mvex, uint32_t track_ID )
72 if( !track_ID || !mvex || !mvex->trex_list )
73 return NULL;
74 for( lsmash_entry_t *entry = mvex->trex_list->head; entry; entry = entry->next )
76 isom_trex_entry_t *trex = (isom_trex_entry_t *)entry->data;
77 if( !trex )
78 return NULL;
79 if( trex->track_ID == track_ID )
80 return trex;
82 return NULL;
85 static isom_traf_entry_t *isom_get_traf( isom_moof_entry_t *moof, uint32_t track_ID )
87 if( !track_ID || !moof || !moof->traf_list )
88 return NULL;
89 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
91 isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
92 if( !traf || !traf->tfhd )
93 return NULL;
94 if( traf->tfhd->track_ID == track_ID )
95 return traf;
97 return NULL;
100 isom_tfra_entry_t *isom_get_tfra( isom_mfra_t *mfra, uint32_t track_ID )
102 if( !track_ID || !mfra || !mfra->tfra_list )
103 return NULL;
104 for( lsmash_entry_t *entry = mfra->tfra_list->head; entry; entry = entry->next )
106 isom_tfra_entry_t *tfra = (isom_tfra_entry_t *)entry->data;
107 if( !tfra )
108 return NULL;
109 if( tfra->track_ID == track_ID )
110 return tfra;
112 return NULL;
115 static int isom_add_elst_entry( isom_elst_t *elst, uint64_t segment_duration, int64_t media_time, int32_t media_rate )
117 isom_elst_entry_t *data = malloc( sizeof(isom_elst_entry_t) );
118 if( !data )
119 return -1;
120 data->segment_duration = segment_duration;
121 data->media_time = media_time;
122 data->media_rate = media_rate;
123 if( lsmash_add_entry( elst->list, data ) )
125 free( data );
126 return -1;
128 if( data->segment_duration > UINT32_MAX || data->media_time > INT32_MAX || data->media_time < INT32_MIN )
129 elst->version = 1;
130 return 0;
133 isom_tref_type_t *isom_add_track_reference_type( isom_tref_t *tref, isom_track_reference_type type, uint32_t ref_count, uint32_t *track_ID )
135 if( !tref || !tref->ref_list )
136 return NULL;
137 isom_tref_type_t *ref = lsmash_malloc_zero( sizeof(isom_tref_type_t) );
138 if( !ref )
139 return NULL;
140 /* Initialize common fields. */
141 ref->root = tref->root;
142 ref->parent = (isom_box_t *)tref;
143 ref->size = 0;
144 ref->type = lsmash_form_iso_box_type( type );
145 /* */
146 ref->ref_count = ref_count;
147 ref->track_ID = track_ID;
148 if( lsmash_add_entry( tref->ref_list, ref ) )
150 free( ref );
151 return NULL;
153 return ref;
156 static int isom_add_dref_entry( isom_dref_t *dref, uint32_t flags, char *name, char *location )
158 if( !dref || !dref->list )
159 return -1;
160 isom_dref_entry_t *data = lsmash_malloc_zero( sizeof(isom_dref_entry_t) );
161 if( !data )
162 return -1;
163 isom_init_box_common( data, dref, name ? ISOM_BOX_TYPE_URN : ISOM_BOX_TYPE_URL );
164 data->flags = flags;
165 if( location )
167 data->location_length = strlen( location ) + 1;
168 data->location = lsmash_memdup( location, data->location_length );
169 if( !data->location )
171 free( data );
172 return -1;
175 if( name )
177 data->name_length = strlen( name ) + 1;
178 data->name = lsmash_memdup( name, data->name_length );
179 if( !data->name )
181 if( data->location )
182 free( data->location );
183 free( data );
184 return -1;
187 if( lsmash_add_entry( dref->list, data ) )
189 if( data->location )
190 free( data->location );
191 if( data->name )
192 free( data->name );
193 free( data );
194 return -1;
196 return 0;
199 isom_avcC_ps_entry_t *isom_create_ps_entry( uint8_t *ps, uint32_t ps_size )
201 isom_avcC_ps_entry_t *entry = malloc( sizeof(isom_avcC_ps_entry_t) );
202 if( !entry )
203 return NULL;
204 entry->parameterSetNALUnit = lsmash_memdup( ps, ps_size );
205 if( !entry->parameterSetNALUnit )
207 free( entry );
208 return NULL;
210 entry->parameterSetLength = ps_size;
211 return entry;
214 void isom_remove_avcC_ps( isom_avcC_ps_entry_t *ps )
216 if( !ps )
217 return;
218 if( ps->parameterSetNALUnit )
219 free( ps->parameterSetNALUnit );
220 free( ps );
223 #if 0
224 static int isom_add_mp4s_entry( isom_stsd_t *stsd )
226 if( !stsd || !stsd->list )
227 return -1;
228 isom_mp4s_entry_t *mp4s = lsmash_malloc_zero( sizeof(isom_mp4s_entry_t) );
229 if( !mp4s )
230 return -1;
231 isom_init_box_common( mp4s, stsd, ISOM_CODEC_TYPE_MP4S_SYSTEM );
232 mp4s->data_reference_index = 1;
233 if( lsmash_add_entry( stsd->list, mp4s ) )
235 free( mp4s );
236 return -1;
238 return 0;
240 #endif
242 int isom_add_frma( isom_wave_t *wave )
244 if( !wave || wave->frma )
245 return -1;
246 isom_create_box( frma, wave, QT_BOX_TYPE_FRMA );
247 wave->frma = frma;
248 return 0;
251 int isom_add_enda( isom_wave_t *wave )
253 if( !wave || wave->enda )
254 return -1;
255 isom_create_box( enda, wave, QT_BOX_TYPE_ENDA );
256 wave->enda = enda;
257 return 0;
260 int isom_add_mp4a( isom_wave_t *wave )
262 if( !wave || wave->mp4a )
263 return -1;
264 isom_create_box( mp4a, wave, QT_BOX_TYPE_MP4A );
265 wave->mp4a = mp4a;
266 return 0;
269 int isom_add_terminator( isom_wave_t *wave )
271 if( !wave || wave->terminator )
272 return -1;
273 isom_create_box( terminator, wave, QT_BOX_TYPE_TERMINATOR );
274 wave->terminator = terminator;
275 return 0;
278 static int isom_add_text_entry( isom_stsd_t *stsd )
280 if( !stsd || !stsd->list )
281 return -1;
282 isom_text_entry_t *text = lsmash_malloc_zero( sizeof(isom_text_entry_t) );
283 if( !text )
284 return -1;
285 isom_init_box_common( text, stsd, QT_CODEC_TYPE_TEXT_TEXT );
286 text->data_reference_index = 1;
287 if( lsmash_add_entry( stsd->list, text ) )
289 free( text );
290 return -1;
292 return 0;
295 int isom_add_ftab( isom_tx3g_entry_t *tx3g )
297 if( !tx3g )
298 return -1;
299 isom_ftab_t *ftab = lsmash_malloc_zero( sizeof(isom_ftab_t) );
300 if( !ftab )
301 return -1;
302 isom_init_box_common( ftab, tx3g, ISOM_BOX_TYPE_FTAB );
303 ftab->list = lsmash_create_entry_list();
304 if( !ftab->list )
306 free( ftab );
307 return -1;
309 tx3g->ftab = ftab;
310 return 0;
313 static int isom_add_tx3g_entry( isom_stsd_t *stsd )
315 if( !stsd || !stsd->list )
316 return -1;
317 isom_tx3g_entry_t *tx3g = lsmash_malloc_zero( sizeof(isom_tx3g_entry_t) );
318 if( !tx3g )
319 return -1;
320 isom_init_box_common( tx3g, stsd, ISOM_CODEC_TYPE_TX3G_TEXT );
321 tx3g->data_reference_index = 1;
322 if( isom_add_ftab( tx3g ) ||
323 lsmash_add_entry( stsd->list, tx3g ) )
325 free( tx3g );
326 return -1;
328 return 0;
331 /* This function returns 0 if failed, sample_entry_number if succeeded. */
332 int lsmash_add_sample_entry( lsmash_root_t *root, uint32_t track_ID, void *summary )
334 if( !summary )
335 return 0;
336 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
337 if( !trak || !trak->root || !trak->root->ftyp || !trak->mdia || !trak->mdia->minf
338 || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list )
339 return 0;
340 isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd;
341 lsmash_entry_list_t *list = stsd->list;
342 int ret = -1;
343 lsmash_codec_type_t sample_type = ((lsmash_summary_t *)summary)->sample_type;
344 if( lsmash_check_codec_type_identical( sample_type, LSMASH_CODEC_TYPE_RAW ) )
346 if( trak->mdia->minf->vmhd )
347 ret = isom_setup_visual_description( stsd, sample_type, (lsmash_video_summary_t *)summary );
348 else if( trak->mdia->minf->smhd )
349 ret = isom_setup_audio_description( stsd, sample_type, (lsmash_audio_summary_t *)summary );
350 return ret ? 0 : list->entry_count;
352 static struct description_setup_table_tag
354 lsmash_codec_type_t type;
355 void *func;
356 } description_setup_table[128] = { { LSMASH_CODEC_TYPE_INITIALIZER, NULL } };
357 if( !description_setup_table[0].func )
359 int i = 0;
360 #define ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( type, func ) \
361 description_setup_table[i++] = (struct description_setup_table_tag){ type, func }
362 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, isom_setup_visual_description );
363 #if 0
364 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO, isom_setup_visual_description );
365 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO, isom_setup_visual_description );
366 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO, isom_setup_visual_description );
367 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO, isom_setup_visual_description );
368 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO, isom_setup_visual_description );
369 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, isom_setup_visual_description );
370 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO, isom_setup_visual_description );
371 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO, isom_setup_visual_description );
372 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO, isom_setup_visual_description );
373 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO, isom_setup_visual_description );
374 #endif
375 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, isom_setup_visual_description );
376 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, isom_setup_visual_description );
377 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, isom_setup_visual_description );
378 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, isom_setup_visual_description );
379 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, isom_setup_visual_description );
380 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, isom_setup_visual_description );
381 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, isom_setup_visual_description );
382 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, isom_setup_visual_description );
383 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, isom_setup_visual_description );
384 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, isom_setup_visual_description );
385 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, isom_setup_visual_description );
386 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, isom_setup_visual_description );
387 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, isom_setup_visual_description );
388 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, isom_setup_visual_description );
389 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, isom_setup_visual_description );
390 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, isom_setup_visual_description );
391 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, isom_setup_visual_description );
392 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, isom_setup_visual_description );
393 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, isom_setup_visual_description );
394 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, isom_setup_visual_description );
395 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, isom_setup_visual_description );
396 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, isom_setup_visual_description );
397 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, isom_setup_visual_description );
398 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, isom_setup_visual_description );
399 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, isom_setup_visual_description );
400 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, isom_setup_visual_description );
401 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, isom_setup_visual_description );
402 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, isom_setup_audio_description );
403 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, isom_setup_audio_description );
404 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, isom_setup_audio_description );
405 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, isom_setup_audio_description );
406 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, isom_setup_audio_description );
407 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, isom_setup_audio_description );
408 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, isom_setup_audio_description );
409 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, isom_setup_audio_description );
410 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, isom_setup_audio_description );
411 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, isom_setup_audio_description );
412 #if 0
413 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO, isom_setup_audio_description );
414 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO, isom_setup_audio_description );
415 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO, isom_setup_audio_description );
416 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO, isom_setup_audio_description );
417 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO, isom_setup_audio_description );
418 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO, isom_setup_audio_description );
419 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_RAW_AUDIO , isom_setup_audio_description );
420 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO, isom_setup_audio_description );
421 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO, isom_setup_audio_description );
422 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO, isom_setup_audio_description );
423 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO, isom_setup_audio_description );
424 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO, isom_setup_audio_description );
425 #endif
426 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MP4A_AUDIO, isom_setup_audio_description );
427 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, isom_setup_audio_description );
428 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, isom_setup_audio_description );
429 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, isom_setup_audio_description );
430 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, isom_setup_audio_description );
431 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, isom_setup_audio_description );
432 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, isom_setup_audio_description );
433 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, isom_setup_audio_description );
434 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, isom_setup_audio_description );
435 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, isom_setup_audio_description );
436 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, isom_setup_audio_description );
437 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT, isom_add_tx3g_entry );
438 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT, isom_add_text_entry );
439 #if 0
440 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4S_SYSTEM, isom_add_mp4s_entry );
441 #endif
443 for( int i = 0; description_setup_table[i].func; i++ )
444 if( lsmash_check_codec_type_identical( sample_type, description_setup_table[i].type ) )
446 if( isom_setup_visual_description == description_setup_table[i].func )
447 ret = isom_setup_visual_description( stsd, sample_type, (lsmash_video_summary_t *)summary );
448 else if( isom_setup_audio_description == description_setup_table[i].func )
449 ret = isom_setup_audio_description( stsd, sample_type, (lsmash_audio_summary_t *)summary );
450 else if( isom_add_tx3g_entry == description_setup_table[i].func )
451 ret = isom_add_tx3g_entry( stsd );
452 else if( isom_add_text_entry == description_setup_table[i].func )
453 ret = isom_add_text_entry( stsd );
454 break;
456 return ret ? 0 : list->entry_count;
459 static int isom_add_stts_entry( isom_stbl_t *stbl, uint32_t sample_delta )
461 if( !stbl || !stbl->stts || !stbl->stts->list )
462 return -1;
463 isom_stts_entry_t *data = malloc( sizeof(isom_stts_entry_t) );
464 if( !data )
465 return -1;
466 data->sample_count = 1;
467 data->sample_delta = sample_delta;
468 if( lsmash_add_entry( stbl->stts->list, data ) )
470 free( data );
471 return -1;
473 return 0;
476 static int isom_add_ctts_entry( isom_stbl_t *stbl, uint32_t sample_offset )
478 if( !stbl || !stbl->ctts || !stbl->ctts->list )
479 return -1;
480 isom_ctts_entry_t *data = malloc( sizeof(isom_ctts_entry_t) );
481 if( !data )
482 return -1;
483 data->sample_count = 1;
484 data->sample_offset = sample_offset;
485 if( lsmash_add_entry( stbl->ctts->list, data ) )
487 free( data );
488 return -1;
490 return 0;
493 static int isom_add_stsc_entry( isom_stbl_t *stbl, uint32_t first_chunk, uint32_t samples_per_chunk, uint32_t sample_description_index )
495 if( !stbl || !stbl->stsc || !stbl->stsc->list )
496 return -1;
497 isom_stsc_entry_t *data = malloc( sizeof(isom_stsc_entry_t) );
498 if( !data )
499 return -1;
500 data->first_chunk = first_chunk;
501 data->samples_per_chunk = samples_per_chunk;
502 data->sample_description_index = sample_description_index;
503 if( lsmash_add_entry( stbl->stsc->list, data ) )
505 free( data );
506 return -1;
508 return 0;
511 static int isom_add_stsz_entry( isom_stbl_t *stbl, uint32_t entry_size )
513 if( !stbl || !stbl->stsz )
514 return -1;
515 isom_stsz_t *stsz = stbl->stsz;
516 /* retrieve initial sample_size */
517 if( !stsz->sample_count )
518 stsz->sample_size = entry_size;
519 /* if it seems constant access_unit size at present, update sample_count only */
520 if( !stsz->list && stsz->sample_size == entry_size )
522 ++ stsz->sample_count;
523 return 0;
525 /* found sample_size varies, create sample_size list */
526 if( !stsz->list )
528 stsz->list = lsmash_create_entry_list();
529 if( !stsz->list )
530 return -1;
531 for( uint32_t i = 0; i < stsz->sample_count; i++ )
533 isom_stsz_entry_t *data = malloc( sizeof(isom_stsz_entry_t) );
534 if( !data )
535 return -1;
536 data->entry_size = stsz->sample_size;
537 if( lsmash_add_entry( stsz->list, data ) )
539 free( data );
540 return -1;
543 stsz->sample_size = 0;
545 isom_stsz_entry_t *data = malloc( sizeof(isom_stsz_entry_t) );
546 if( !data )
547 return -1;
548 data->entry_size = entry_size;
549 if( lsmash_add_entry( stsz->list, data ) )
551 free( data );
552 return -1;
554 ++ stsz->sample_count;
555 return 0;
558 static int isom_add_stss_entry( isom_stbl_t *stbl, uint32_t sample_number )
560 if( !stbl || !stbl->stss || !stbl->stss->list )
561 return -1;
562 isom_stss_entry_t *data = malloc( sizeof(isom_stss_entry_t) );
563 if( !data )
564 return -1;
565 data->sample_number = sample_number;
566 if( lsmash_add_entry( stbl->stss->list, data ) )
568 free( data );
569 return -1;
571 return 0;
574 static int isom_add_stps_entry( isom_stbl_t *stbl, uint32_t sample_number )
576 if( !stbl || !stbl->stps || !stbl->stps->list )
577 return -1;
578 isom_stps_entry_t *data = malloc( sizeof(isom_stps_entry_t) );
579 if( !data )
580 return -1;
581 data->sample_number = sample_number;
582 if( lsmash_add_entry( stbl->stps->list, data ) )
584 free( data );
585 return -1;
587 return 0;
590 static int isom_add_sdtp_entry( isom_box_t *parent, lsmash_sample_property_t *prop, uint8_t avc_extensions )
592 if( !prop || !parent )
593 return -1;
594 isom_sdtp_t *sdtp = NULL;
595 if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) )
596 sdtp = ((isom_stbl_t *)parent)->sdtp;
597 else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) )
598 sdtp = ((isom_traf_entry_t *)parent)->sdtp;
599 else
600 assert( 0 );
601 if( !sdtp || !sdtp->list )
602 return -1;
603 isom_sdtp_entry_t *data = malloc( sizeof(isom_sdtp_entry_t) );
604 if( !data )
605 return -1;
606 /* isom_sdtp_entry_t is smaller than lsmash_sample_property_t. */
607 data->is_leading = (avc_extensions ? prop->leading : prop->allow_earlier) & 0x03;
608 data->sample_depends_on = prop->independent & 0x03;
609 data->sample_is_depended_on = prop->disposable & 0x03;
610 data->sample_has_redundancy = prop->redundant & 0x03;
611 if( lsmash_add_entry( sdtp->list, data ) )
613 free( data );
614 return -1;
616 return 0;
619 static int isom_add_co64( isom_stbl_t *stbl )
621 if( !stbl || stbl->stco )
622 return -1;
623 isom_create_list_box( stco, stbl, ISOM_BOX_TYPE_CO64 );
624 stco->large_presentation = 1;
625 stbl->stco = stco;
626 return 0;
629 static int isom_add_stco( isom_stbl_t *stbl )
631 if( !stbl || stbl->stco )
632 return -1;
633 isom_create_list_box( stco, stbl, ISOM_BOX_TYPE_STCO );
634 stco->large_presentation = 0;
635 stbl->stco = stco;
636 return 0;
639 static int isom_add_co64_entry( isom_stbl_t *stbl, uint64_t chunk_offset )
641 if( !stbl || !stbl->stco || !stbl->stco->list )
642 return -1;
643 isom_co64_entry_t *data = malloc( sizeof(isom_co64_entry_t) );
644 if( !data )
645 return -1;
646 data->chunk_offset = chunk_offset;
647 if( lsmash_add_entry( stbl->stco->list, data ) )
649 free( data );
650 return -1;
652 return 0;
655 static int isom_convert_stco_to_co64( isom_stbl_t* stbl )
657 /* backup stco */
658 isom_stco_t *stco = stbl->stco;
659 stbl->stco = NULL;
660 if( isom_add_co64( stbl ) )
661 return -1;
662 /* move chunk_offset to co64 from stco */
663 for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next )
665 isom_stco_entry_t *data = (isom_stco_entry_t*)entry->data;
666 if( isom_add_co64_entry( stbl, data->chunk_offset ) )
667 return -1;
669 lsmash_remove_list( stco->list, NULL );
670 free( stco );
671 return 0;
674 static int isom_add_stco_entry( isom_stbl_t *stbl, uint64_t chunk_offset )
676 if( !stbl || !stbl->stco || !stbl->stco->list )
677 return -1;
678 if( stbl->stco->large_presentation )
679 return isom_add_co64_entry( stbl, chunk_offset );
680 if( chunk_offset > UINT32_MAX )
682 if( isom_convert_stco_to_co64( stbl ) )
683 return -1;
684 return isom_add_co64_entry( stbl, chunk_offset );
686 isom_stco_entry_t *data = malloc( sizeof(isom_stco_entry_t) );
687 if( !data )
688 return -1;
689 data->chunk_offset = (uint32_t)chunk_offset;
690 if( lsmash_add_entry( stbl->stco->list, data ) )
692 free( data );
693 return -1;
695 return 0;
698 isom_sgpd_entry_t *isom_get_sample_group_description( isom_stbl_t *stbl, uint32_t grouping_type )
700 if( !stbl->sgpd_list )
701 return NULL;
702 for( lsmash_entry_t *entry = stbl->sgpd_list->head; entry; entry = entry->next )
704 isom_sgpd_entry_t *sgpd = (isom_sgpd_entry_t *)entry->data;
705 if( !sgpd || !sgpd->list )
706 return NULL;
707 if( sgpd->grouping_type == grouping_type )
708 return sgpd;
710 return NULL;
713 isom_sbgp_entry_t *isom_get_sample_to_group( isom_stbl_t *stbl, uint32_t grouping_type )
715 if( !stbl->sbgp_list )
716 return NULL;
717 for( lsmash_entry_t *entry = stbl->sbgp_list->head; entry; entry = entry->next )
719 isom_sbgp_entry_t *sbgp = (isom_sbgp_entry_t *)entry->data;
720 if( !sbgp || !sbgp->list )
721 return NULL;
722 if( sbgp->grouping_type == grouping_type )
723 return sbgp;
725 return NULL;
728 static isom_rap_entry_t *isom_add_rap_group_entry( isom_sgpd_entry_t *sgpd )
730 if( !sgpd )
731 return NULL;
732 isom_rap_entry_t *data = malloc( sizeof(isom_rap_entry_t) );
733 if( !data )
734 return NULL;
735 data->description_length = 0;
736 data->num_leading_samples_known = 0;
737 data->num_leading_samples = 0;
738 if( lsmash_add_entry( sgpd->list, data ) )
740 free( data );
741 return NULL;
743 return data;
746 static isom_roll_entry_t *isom_add_roll_group_entry( isom_sgpd_entry_t *sgpd, int16_t roll_distance )
748 if( !sgpd )
749 return NULL;
750 isom_roll_entry_t *data = malloc( sizeof(isom_roll_entry_t) );
751 if( !data )
752 return NULL;
753 data->description_length = 0;
754 data->roll_distance = roll_distance;
755 if( lsmash_add_entry( sgpd->list, data ) )
757 free( data );
758 return NULL;
760 return data;
763 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 )
765 if( !sbgp )
766 return NULL;
767 isom_group_assignment_entry_t *data = malloc( sizeof(isom_group_assignment_entry_t) );
768 if( !data )
769 return NULL;
770 data->sample_count = sample_count;
771 data->group_description_index = group_description_index;
772 if( lsmash_add_entry( sbgp->list, data ) )
774 free( data );
775 return NULL;
777 return data;
780 int isom_add_chpl_entry( isom_chpl_t *chpl, isom_chapter_entry_t *chap_data )
782 if( !chap_data->chapter_name || !chpl || !chpl->list )
783 return -1;
784 isom_chpl_entry_t *data = malloc( sizeof(isom_chpl_entry_t) );
785 if( !data )
786 return -1;
787 data->start_time = chap_data->start_time;
788 data->chapter_name_length = strlen( chap_data->chapter_name );
789 data->chapter_name = (char *)malloc( data->chapter_name_length + 1 );
790 if( !data->chapter_name )
792 free( data );
793 return -1;
795 memcpy( data->chapter_name, chap_data->chapter_name, data->chapter_name_length );
796 data->chapter_name[data->chapter_name_length] = '\0';
797 if( lsmash_add_entry( chpl->list, data ) )
799 free( data->chapter_name );
800 free( data );
801 return -1;
803 return 0;
806 static isom_trex_entry_t *isom_add_trex( isom_mvex_t *mvex )
808 if( !mvex )
809 return NULL;
810 if( !mvex->trex_list )
812 mvex->trex_list = lsmash_create_entry_list();
813 if( !mvex->trex_list )
814 return NULL;
816 isom_trex_entry_t *trex = lsmash_malloc_zero( sizeof(isom_trex_entry_t) );
817 if( !trex )
818 return NULL;
819 isom_init_box_common( trex, mvex, ISOM_BOX_TYPE_TREX );
820 if( lsmash_add_entry( mvex->trex_list, trex ) )
822 free( trex );
823 return NULL;
825 return trex;
828 static isom_trun_entry_t *isom_add_trun( isom_traf_entry_t *traf )
830 if( !traf )
831 return NULL;
832 if( !traf->trun_list )
834 traf->trun_list = lsmash_create_entry_list();
835 if( !traf->trun_list )
836 return NULL;
838 isom_trun_entry_t *trun = lsmash_malloc_zero( sizeof(isom_trun_entry_t) );
839 if( !trun )
840 return NULL;
841 isom_init_box_common( trun, traf, ISOM_BOX_TYPE_TRUN );
842 if( lsmash_add_entry( traf->trun_list, trun ) )
844 free( trun );
845 return NULL;
847 return trun;
850 static isom_traf_entry_t *isom_add_traf( lsmash_root_t *root, isom_moof_entry_t *moof )
852 if( !root || !root->moof_list || !moof )
853 return NULL;
854 if( !moof->traf_list )
856 moof->traf_list = lsmash_create_entry_list();
857 if( !moof->traf_list )
858 return NULL;
860 isom_traf_entry_t *traf = lsmash_malloc_zero( sizeof(isom_traf_entry_t) );
861 if( !traf )
862 return NULL;
863 isom_init_box_common( traf, moof, ISOM_BOX_TYPE_TRAF );
864 isom_cache_t *cache = malloc( sizeof(isom_cache_t) );
865 if( !cache )
867 free( traf );
868 return NULL;
870 memset( cache, 0, sizeof(isom_cache_t) );
871 if( lsmash_add_entry( moof->traf_list, traf ) )
873 free( cache );
874 free( traf );
875 return NULL;
877 traf->cache = cache;
878 return traf;
881 static isom_moof_entry_t *isom_add_moof( lsmash_root_t *root )
883 if( !root )
884 return NULL;
885 if( !root->moof_list )
887 root->moof_list = lsmash_create_entry_list();
888 if( !root->moof_list )
889 return NULL;
891 isom_moof_entry_t *moof = lsmash_malloc_zero( sizeof(isom_moof_entry_t) );
892 if( !moof )
893 return NULL;
894 isom_init_box_common( moof, root, ISOM_BOX_TYPE_MOOF );
895 if( lsmash_add_entry( root->moof_list, moof ) )
897 free( moof );
898 return NULL;
900 return moof;
903 static isom_tfra_entry_t *isom_add_tfra( isom_mfra_t *mfra )
905 if( !mfra )
906 return NULL;
907 if( !mfra->tfra_list )
909 mfra->tfra_list = lsmash_create_entry_list();
910 if( !mfra->tfra_list )
911 return NULL;
913 isom_tfra_entry_t *tfra = lsmash_malloc_zero( sizeof(isom_tfra_entry_t) );
914 if( !tfra )
915 return NULL;
916 isom_init_box_common( tfra, mfra, ISOM_BOX_TYPE_TFRA );
917 if( lsmash_add_entry( mfra->tfra_list, tfra ) )
919 free( tfra );
920 return NULL;
922 return tfra;
925 static int isom_add_ftyp( lsmash_root_t *root )
927 if( root->ftyp )
928 return -1;
929 isom_create_box( ftyp, root, ISOM_BOX_TYPE_FTYP );
930 ftyp->size = ISOM_BASEBOX_COMMON_SIZE + 8;
931 root->ftyp = ftyp;
932 return 0;
935 static int isom_add_moov( lsmash_root_t *root )
937 if( root->moov )
938 return -1;
939 isom_create_box( moov, root, ISOM_BOX_TYPE_MOOV );
940 root->moov = moov;
941 return 0;
944 static int isom_add_mvhd( isom_moov_t *moov )
946 if( !moov || moov->mvhd )
947 return -1;
948 isom_create_box( mvhd, moov, ISOM_BOX_TYPE_MVHD );
949 mvhd->rate = 0x00010000;
950 mvhd->volume = 0x0100;
951 mvhd->matrix[0] = 0x00010000;
952 mvhd->matrix[4] = 0x00010000;
953 mvhd->matrix[8] = 0x40000000;
954 mvhd->next_track_ID = 1;
955 moov->mvhd = mvhd;
956 return 0;
959 static int isom_scan_trak_profileLevelIndication( isom_trak_entry_t *trak, mp4a_audioProfileLevelIndication *audio_pli, mp4sys_visualProfileLevelIndication *visual_pli )
961 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl )
962 return -1;
963 isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd;
964 if( !stsd || !stsd->list || !stsd->list->head )
965 return -1;
966 for( lsmash_entry_t *entry = stsd->list->head; entry; entry = entry->next )
968 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)entry->data;
969 if( !sample_entry )
970 return -1;
971 lsmash_codec_type_t sample_type = (lsmash_codec_type_t)sample_entry->type;
972 if( trak->mdia->minf->vmhd )
974 if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO )
975 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC2_VIDEO )
976 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVCP_VIDEO )
977 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_SVC1_VIDEO )
978 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MVC1_VIDEO )
979 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MVC2_VIDEO ) )
981 /* FIXME: Do we have to arbitrate like audio? */
982 if( *visual_pli == MP4SYS_VISUAL_PLI_NONE_REQUIRED )
983 *visual_pli = MP4SYS_VISUAL_PLI_H264_AVC;
985 else
986 *visual_pli = MP4SYS_VISUAL_PLI_NOT_SPECIFIED;
988 else if( trak->mdia->minf->smhd )
990 if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) )
992 isom_audio_entry_t *audio = (isom_audio_entry_t *)sample_entry;
993 #ifdef LSMASH_DEMUXER_ENABLED
994 isom_esds_t *esds = (isom_esds_t *)isom_get_extension_box( &audio->extensions, ISOM_BOX_TYPE_ESDS );
995 if( !esds || !esds->ES )
996 return -1;
997 if( !lsmash_check_codec_type_identical( audio->summary.sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) )
998 /* This is needed when copying descriptions. */
999 mp4sys_setup_summary_from_DecoderSpecificInfo( &audio->summary, esds->ES );
1000 #endif
1001 *audio_pli = mp4a_max_audioProfileLevelIndication( *audio_pli, mp4a_get_audioProfileLevelIndication( &audio->summary ) );
1003 else
1004 /* NOTE: Audio CODECs other than 'mp4a' does not have appropriate pli. */
1005 *audio_pli = MP4A_AUDIO_PLI_NOT_SPECIFIED;
1007 else
1008 ; /* FIXME: Do we have to set OD_profileLevelIndication? */
1010 return 0;
1013 static int isom_add_iods( isom_moov_t *moov )
1015 if( !moov || !moov->trak_list || moov->iods )
1016 return -1;
1017 isom_create_box( iods, moov, ISOM_BOX_TYPE_IODS );
1018 iods->OD = mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */
1019 if( !iods->OD )
1021 free( iods );
1022 return -1;
1024 mp4a_audioProfileLevelIndication audio_pli = MP4A_AUDIO_PLI_NONE_REQUIRED;
1025 mp4sys_visualProfileLevelIndication visual_pli = MP4SYS_VISUAL_PLI_NONE_REQUIRED;
1026 for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
1028 isom_trak_entry_t* trak = (isom_trak_entry_t*)entry->data;
1029 if( !trak || !trak->tkhd )
1031 free( iods );
1032 return -1;
1034 if( isom_scan_trak_profileLevelIndication( trak, &audio_pli, &visual_pli ) )
1036 free( iods );
1037 return -1;
1039 if( mp4sys_add_ES_ID_Inc( iods->OD, trak->tkhd->track_ID ) )
1041 free( iods );
1042 return -1;
1045 if( mp4sys_to_InitialObjectDescriptor( iods->OD,
1046 0, /* FIXME: I'm not quite sure what the spec says. */
1047 MP4SYS_OD_PLI_NONE_REQUIRED, MP4SYS_SCENE_PLI_NONE_REQUIRED,
1048 audio_pli, visual_pli,
1049 MP4SYS_GRAPHICS_PLI_NONE_REQUIRED ) )
1051 free( iods );
1052 return -1;
1054 moov->iods = iods;
1055 return 0;
1058 static int isom_add_tkhd( isom_trak_entry_t *trak, uint32_t handler_type )
1060 if( !trak || !trak->root || !trak->root->moov || !trak->root->moov->mvhd || !trak->root->moov->trak_list )
1061 return -1;
1062 if( !trak->tkhd )
1064 isom_create_box( tkhd, trak, ISOM_BOX_TYPE_TKHD );
1065 if( handler_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK )
1066 tkhd->volume = 0x0100;
1067 tkhd->matrix[0] = 0x00010000;
1068 tkhd->matrix[4] = 0x00010000;
1069 tkhd->matrix[8] = 0x40000000;
1070 tkhd->duration = 0xffffffff;
1071 tkhd->track_ID = trak->root->moov->mvhd->next_track_ID;
1072 ++ trak->root->moov->mvhd->next_track_ID;
1073 trak->tkhd = tkhd;
1075 return 0;
1078 static int isom_add_clef( isom_tapt_t *tapt )
1080 if( tapt->clef )
1081 return 0;
1082 isom_create_box( clef, tapt, QT_BOX_TYPE_CLEF );
1083 tapt->clef = clef;
1084 return 0;
1087 static int isom_add_prof( isom_tapt_t *tapt )
1089 if( tapt->prof )
1090 return 0;
1091 isom_create_box( prof, tapt, QT_BOX_TYPE_PROF );
1092 tapt->prof = prof;
1093 return 0;
1096 static int isom_add_enof( isom_tapt_t *tapt )
1098 if( tapt->enof )
1099 return 0;
1100 isom_create_box( enof, tapt, QT_BOX_TYPE_ENOF );
1101 tapt->enof = enof;
1102 return 0;
1105 static int isom_add_tapt( isom_trak_entry_t *trak )
1107 if( trak->tapt )
1108 return 0;
1109 isom_create_box( tapt, trak, QT_BOX_TYPE_TAPT );
1110 trak->tapt = tapt;
1111 return 0;
1114 int isom_add_elst( isom_edts_t *edts )
1116 if( edts->elst )
1117 return 0;
1118 isom_create_list_box( elst, edts, ISOM_BOX_TYPE_ELST );
1119 edts->elst = elst;
1120 return 0;
1123 int isom_add_edts( isom_trak_entry_t *trak )
1125 if( trak->edts )
1126 return 0;
1127 isom_create_box( edts, trak, ISOM_BOX_TYPE_EDTS );
1128 trak->edts = edts;
1129 return 0;
1132 int isom_add_tref( isom_trak_entry_t *trak )
1134 if( trak->tref )
1135 return 0;
1136 isom_create_box( tref, trak, ISOM_BOX_TYPE_TREF );
1137 tref->ref_list = lsmash_create_entry_list();
1138 if( !tref->ref_list )
1140 free( tref );
1141 return -1;
1143 trak->tref = tref;
1144 return 0;
1147 static int isom_add_mdhd( isom_mdia_t *mdia, uint16_t default_language )
1149 if( !mdia || mdia->mdhd )
1150 return -1;
1151 isom_create_box( mdhd, mdia, ISOM_BOX_TYPE_MDHD );
1152 mdhd->language = default_language;
1153 mdia->mdhd = mdhd;
1154 return 0;
1157 static int isom_add_mdia( isom_trak_entry_t *trak )
1159 if( !trak || trak->mdia )
1160 return -1;
1161 isom_create_box( mdia, trak, ISOM_BOX_TYPE_MDIA );
1162 trak->mdia = mdia;
1163 return 0;
1166 int isom_add_hdlr( isom_mdia_t *mdia, isom_meta_t *meta, isom_minf_t *minf, uint32_t media_type )
1168 if( (!mdia && !meta && !minf) || (mdia && meta) || (meta && minf) || (minf && mdia) )
1169 return -1; /* Either one must be given. */
1170 if( (mdia && mdia->hdlr) || (meta && meta->hdlr) || (minf && minf->hdlr) )
1171 return -1; /* Selected one must not have hdlr yet. */
1172 isom_box_t *parent = mdia ? (isom_box_t *)mdia : meta ? (isom_box_t *)meta : (isom_box_t *)minf;
1173 isom_create_box( hdlr, parent, ISOM_BOX_TYPE_HDLR );
1174 lsmash_root_t *root = hdlr->root;
1175 uint32_t type = mdia ? (root->qt_compatible ? QT_HANDLER_TYPE_MEDIA : 0) : (meta ? 0 : QT_HANDLER_TYPE_DATA);
1176 uint32_t subtype = media_type;
1177 hdlr->componentType = type;
1178 hdlr->componentSubtype = subtype;
1179 char *type_name = NULL;
1180 char *subtype_name = NULL;
1181 uint8_t type_name_length = 0;
1182 uint8_t subtype_name_length = 0;
1183 if( mdia )
1184 type_name = "Media ";
1185 else if( meta )
1186 type_name = "Metadata ";
1187 else /* if( minf ) */
1188 type_name = "Data ";
1189 type_name_length = strlen( type_name );
1190 struct
1192 uint32_t subtype;
1193 char *subtype_name;
1194 uint8_t subtype_name_length;
1195 } subtype_table[] =
1197 { ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK, "Sound ", 6 },
1198 { ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK, "Video", 6 },
1199 { ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK, "Hint ", 5 },
1200 { ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK, "Metadata ", 9 },
1201 { ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK, "Text ", 5 },
1202 { ISOM_META_HANDLER_TYPE_ITUNES_METADATA, "iTunes ", 7 },
1203 { QT_REFERENCE_HANDLER_TYPE_ALIAS, "Alias ", 6 },
1204 { QT_REFERENCE_HANDLER_TYPE_RESOURCE, "Resource ", 9 },
1205 { QT_REFERENCE_HANDLER_TYPE_URL, "URL ", 4 },
1206 { subtype, "Unknown ", 8 }
1208 for( int i = 0; subtype_table[i].subtype; i++ )
1209 if( subtype == subtype_table[i].subtype )
1211 subtype_name = subtype_table[i].subtype_name;
1212 subtype_name_length = subtype_table[i].subtype_name_length;
1213 break;
1215 uint32_t name_length = 15 + subtype_name_length + type_name_length + root->isom_compatible + root->qt_compatible;
1216 uint8_t *name = malloc( name_length );
1217 if( !name )
1219 free( hdlr );
1220 return -1;
1222 if( root->qt_compatible )
1223 name[0] = name_length & 0xff;
1224 memcpy( name + root->qt_compatible, "L-SMASH ", 8 );
1225 memcpy( name + root->qt_compatible + 8, subtype_name, subtype_name_length );
1226 memcpy( name + root->qt_compatible + 8 + subtype_name_length, type_name, type_name_length );
1227 memcpy( name + root->qt_compatible + 8 + subtype_name_length + type_name_length, "Handler", 7 );
1228 if( root->isom_compatible )
1229 name[name_length - 1] = 0;
1230 hdlr->componentName = name;
1231 hdlr->componentName_length = name_length;
1232 if( mdia )
1233 mdia->hdlr = hdlr;
1234 else if( meta )
1235 meta->hdlr = hdlr;
1236 else
1237 minf->hdlr = hdlr;
1238 return 0;
1241 static int isom_add_minf( isom_mdia_t *mdia )
1243 if( !mdia || mdia->minf )
1244 return -1;
1245 isom_create_box( minf, mdia, ISOM_BOX_TYPE_MINF );
1246 mdia->minf = minf;
1247 return 0;
1250 static int isom_add_vmhd( isom_minf_t *minf )
1252 if( !minf || minf->vmhd )
1253 return -1;
1254 isom_create_box( vmhd, minf, ISOM_BOX_TYPE_VMHD );
1255 vmhd->flags = 0x000001;
1256 minf->vmhd = vmhd;
1257 return 0;
1260 static int isom_add_smhd( isom_minf_t *minf )
1262 if( !minf || minf->smhd )
1263 return -1;
1264 isom_create_box( smhd, minf, ISOM_BOX_TYPE_SMHD );
1265 minf->smhd = smhd;
1266 return 0;
1269 static int isom_add_hmhd( isom_minf_t *minf )
1271 if( !minf || minf->hmhd )
1272 return -1;
1273 isom_create_box( hmhd, minf, ISOM_BOX_TYPE_HMHD );
1274 minf->hmhd = hmhd;
1275 return 0;
1278 static int isom_add_nmhd( isom_minf_t *minf )
1280 if( !minf || minf->nmhd )
1281 return -1;
1282 isom_create_box( nmhd, minf, ISOM_BOX_TYPE_NMHD );
1283 minf->nmhd = nmhd;
1284 return 0;
1287 static int isom_add_gmin( isom_gmhd_t *gmhd )
1289 if( !gmhd || gmhd->gmin )
1290 return -1;
1291 isom_create_box( gmin, gmhd, QT_BOX_TYPE_GMIN );
1292 gmhd->gmin = gmin;
1293 return 0;
1296 static int isom_add_text( isom_gmhd_t *gmhd )
1298 if( !gmhd || gmhd->text )
1299 return -1;
1300 isom_create_box( text, gmhd, QT_BOX_TYPE_TEXT );
1301 text->matrix[0] = 0x00010000;
1302 text->matrix[4] = 0x00010000;
1303 text->matrix[8] = 0x40000000;
1304 gmhd->text = text;
1305 return 0;
1308 static int isom_add_gmhd( isom_minf_t *minf )
1310 if( !minf || minf->gmhd )
1311 return -1;
1312 isom_create_box( gmhd, minf, QT_BOX_TYPE_GMHD );
1313 minf->gmhd = gmhd;
1314 return 0;
1317 static int isom_add_dinf( isom_minf_t *minf )
1319 if( !minf || minf->dinf )
1320 return -1;
1321 isom_create_box( dinf, minf, ISOM_BOX_TYPE_DINF );
1322 minf->dinf = dinf;
1323 return 0;
1326 static int isom_add_dref( isom_dinf_t *dinf )
1328 if( !dinf || dinf->dref )
1329 return -1;
1330 isom_create_list_box( dref, dinf, ISOM_BOX_TYPE_DREF );
1331 dinf->dref = dref;
1332 if( isom_add_dref_entry( dref, 0x000001, NULL, NULL ) )
1333 return -1;
1334 return 0;
1337 static int isom_add_stsd( isom_stbl_t *stbl )
1339 if( !stbl || stbl->stsd )
1340 return -1;
1341 isom_create_list_box( stsd, stbl, ISOM_BOX_TYPE_STSD );
1342 stbl->stsd = stsd;
1343 return 0;
1346 static int isom_add_stts( isom_stbl_t *stbl )
1348 if( !stbl || stbl->stts )
1349 return -1;
1350 isom_create_list_box( stts, stbl, ISOM_BOX_TYPE_STTS );
1351 stbl->stts = stts;
1352 return 0;
1355 static int isom_add_ctts( isom_stbl_t *stbl )
1357 if( !stbl || stbl->ctts )
1358 return -1;
1359 isom_create_list_box( ctts, stbl, ISOM_BOX_TYPE_CTTS );
1360 stbl->ctts = ctts;
1361 return 0;
1364 static int isom_add_cslg( isom_stbl_t *stbl )
1366 if( !stbl || stbl->cslg )
1367 return -1;
1368 isom_create_box( cslg, stbl, ISOM_BOX_TYPE_CSLG );
1369 stbl->cslg = cslg;
1370 return 0;
1373 static int isom_add_stsc( isom_stbl_t *stbl )
1375 if( !stbl || stbl->stsc )
1376 return -1;
1377 isom_create_list_box( stsc, stbl, ISOM_BOX_TYPE_STSC );
1378 stbl->stsc = stsc;
1379 return 0;
1382 static int isom_add_stsz( isom_stbl_t *stbl )
1384 if( !stbl || stbl->stsz )
1385 return -1;
1386 isom_create_box( stsz, stbl, ISOM_BOX_TYPE_STSZ ); /* We don't create a list here. */
1387 stbl->stsz = stsz;
1388 return 0;
1391 static int isom_add_stss( isom_stbl_t *stbl )
1393 if( !stbl || stbl->stss )
1394 return -1;
1395 isom_create_list_box( stss, stbl, ISOM_BOX_TYPE_STSS );
1396 stbl->stss = stss;
1397 return 0;
1400 static int isom_add_stps( isom_stbl_t *stbl )
1402 if( !stbl || stbl->stps )
1403 return -1;
1404 isom_create_list_box( stps, stbl, QT_BOX_TYPE_STPS );
1405 stbl->stps = stps;
1406 return 0;
1409 static int isom_add_sdtp( isom_box_t *parent )
1411 if( !parent )
1412 return -1;
1413 if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) )
1415 isom_stbl_t *stbl = (isom_stbl_t *)parent;
1416 if( stbl->sdtp )
1417 return -1;
1418 isom_create_list_box( sdtp, stbl, ISOM_BOX_TYPE_SDTP );
1419 stbl->sdtp = sdtp;
1421 else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) )
1423 isom_traf_entry_t *traf = (isom_traf_entry_t *)parent;
1424 if( traf->sdtp )
1425 return -1;
1426 isom_create_list_box( sdtp, traf, ISOM_BOX_TYPE_SDTP );
1427 traf->sdtp = sdtp;
1429 else
1430 assert( 0 );
1431 return 0;
1434 static isom_sgpd_entry_t *isom_add_sgpd( isom_stbl_t *stbl, uint32_t grouping_type )
1436 if( !stbl )
1437 return NULL;
1438 if( !stbl->sgpd_list )
1440 stbl->sgpd_list = lsmash_create_entry_list();
1441 if( !stbl->sgpd_list )
1442 return NULL;
1444 isom_sgpd_entry_t *sgpd = lsmash_malloc_zero( sizeof(isom_sgpd_entry_t) );
1445 if( !sgpd )
1446 return NULL;
1447 isom_init_box_common( sgpd, stbl, ISOM_BOX_TYPE_SGPD );
1448 sgpd->list = lsmash_create_entry_list();
1449 if( !sgpd->list || lsmash_add_entry( stbl->sgpd_list, sgpd ) )
1451 free( sgpd );
1452 return NULL;
1454 sgpd->grouping_type = grouping_type;
1455 sgpd->version = 1; /* We use version 1 because it is recommended in the spec. */
1456 switch( grouping_type )
1458 case ISOM_GROUP_TYPE_RAP :
1459 sgpd->default_length = 1;
1460 break;
1461 case ISOM_GROUP_TYPE_ROLL :
1462 sgpd->default_length = 2;
1463 break;
1464 default :
1465 /* We don't consider other grouping types currently. */
1466 break;
1468 return sgpd;
1471 static isom_sbgp_entry_t *isom_add_sbgp( isom_stbl_t *stbl, uint32_t grouping_type )
1473 if( !stbl )
1474 return NULL;
1475 if( !stbl->sbgp_list )
1477 stbl->sbgp_list = lsmash_create_entry_list();
1478 if( !stbl->sbgp_list )
1479 return NULL;
1481 isom_sbgp_entry_t *sbgp = lsmash_malloc_zero( sizeof(isom_sbgp_entry_t) );
1482 if( !sbgp )
1483 return NULL;
1484 isom_init_box_common( sbgp, stbl, ISOM_BOX_TYPE_SBGP );
1485 sbgp->list = lsmash_create_entry_list();
1486 if( !sbgp->list || lsmash_add_entry( stbl->sbgp_list, sbgp ) )
1488 free( sbgp );
1489 return NULL;
1491 sbgp->grouping_type = grouping_type;
1492 return sbgp;
1495 static int isom_add_stbl( isom_minf_t *minf )
1497 if( !minf || minf->stbl )
1498 return -1;
1499 isom_create_box( stbl, minf, ISOM_BOX_TYPE_STBL );
1500 minf->stbl = stbl;
1501 return 0;
1504 int isom_add_chpl( isom_moov_t *moov )
1506 if( !moov || !moov->udta || moov->udta->chpl )
1507 return -1;
1508 isom_create_list_box( chpl, moov->udta, ISOM_BOX_TYPE_CHPL );
1509 chpl->version = 1; /* version = 1 is popular. */
1510 moov->udta->chpl = chpl;
1511 return 0;
1514 int isom_add_metaitem( isom_ilst_t *ilst, lsmash_itunes_metadata_item item )
1516 if( !ilst || !ilst->item_list )
1517 return -1;
1518 lsmash_box_type_t type = lsmash_form_iso_box_type( item );
1519 isom_create_box( metaitem, ilst, type );
1520 if( lsmash_add_entry( ilst->item_list, metaitem ) )
1522 free( metaitem );
1523 return -1;
1525 return 0;
1528 int isom_add_mean( isom_metaitem_t *metaitem )
1530 if( !metaitem || metaitem->mean )
1531 return -1;
1532 isom_create_box( mean, metaitem, ISOM_BOX_TYPE_MEAN );
1533 metaitem->mean = mean;
1534 return 0;
1537 int isom_add_name( isom_metaitem_t *metaitem )
1539 if( !metaitem || metaitem->name )
1540 return -1;
1541 isom_create_box( name, metaitem, ISOM_BOX_TYPE_NAME );
1542 metaitem->name = name;
1543 return 0;
1546 int isom_add_data( isom_metaitem_t *metaitem )
1548 if( !metaitem || metaitem->data )
1549 return -1;
1550 isom_create_box( data, metaitem, ISOM_BOX_TYPE_DATA );
1551 metaitem->data = data;
1552 return 0;
1555 int isom_add_ilst( isom_moov_t *moov )
1557 if( !moov || !moov->udta || !moov->udta->meta || moov->udta->meta->ilst )
1558 return -1;
1559 isom_create_box( ilst, moov->udta->meta, ISOM_BOX_TYPE_ILST );
1560 ilst->item_list = lsmash_create_entry_list();
1561 if( !ilst->item_list )
1563 free( ilst );
1564 return -1;
1566 moov->udta->meta->ilst = ilst;
1567 return 0;
1570 int isom_add_meta( isom_box_t *parent )
1572 if( !parent )
1573 return -1;
1574 isom_create_box( meta, parent, ISOM_BOX_TYPE_META );
1575 if( lsmash_check_box_type_identical( parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) )
1577 lsmash_root_t *root = (lsmash_root_t *)parent;
1578 if( root->meta )
1580 free( meta );
1581 return -1;
1583 root->meta = meta;
1585 else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MOOV ) )
1587 isom_moov_t *moov = (isom_moov_t *)parent;
1588 if( moov->meta )
1590 free( meta );
1591 return -1;
1593 moov->meta = meta;
1595 else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) )
1597 isom_trak_entry_t *trak = (isom_trak_entry_t *)parent;
1598 if( trak->meta )
1600 free( meta );
1601 return -1;
1603 trak->meta = meta;
1605 else
1607 isom_udta_t *udta = (isom_udta_t *)parent;
1608 if( udta->meta )
1610 free( meta );
1611 return -1;
1613 udta->meta = meta;
1615 return 0;
1618 static int isom_add_cprt( isom_udta_t *udta )
1620 if( !udta )
1621 return -1;
1622 if( !udta->cprt_list )
1624 udta->cprt_list = lsmash_create_entry_list();
1625 if( !udta->cprt_list )
1626 return -1;
1628 isom_create_box( cprt, udta, ISOM_BOX_TYPE_CPRT );
1629 if( lsmash_add_entry( udta->cprt_list, cprt ) )
1631 free( cprt );
1632 return -1;
1634 return 0;
1637 int isom_add_udta( lsmash_root_t *root, uint32_t track_ID )
1639 /* track_ID == 0 means the direct addition to moov box */
1640 if( !track_ID )
1642 if( !root || !root->moov )
1643 return -1;
1644 if( root->moov->udta )
1645 return 0;
1646 isom_create_box( udta, root->moov, ISOM_BOX_TYPE_UDTA );
1647 root->moov->udta = udta;
1648 return 0;
1650 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
1651 if( !trak )
1652 return -1;
1653 if( trak->udta )
1654 return 0;
1655 isom_create_box( udta, trak, ISOM_BOX_TYPE_UDTA );
1656 trak->udta = udta;
1657 return 0;
1660 static isom_trak_entry_t *isom_add_trak( lsmash_root_t *root )
1662 if( !root || !root->moov )
1663 return NULL;
1664 isom_moov_t *moov = root->moov;
1665 if( !moov->trak_list )
1667 moov->trak_list = lsmash_create_entry_list();
1668 if( !moov->trak_list )
1669 return NULL;
1671 isom_trak_entry_t *trak = lsmash_malloc_zero( sizeof(isom_trak_entry_t) );
1672 if( !trak )
1673 return NULL;
1674 isom_init_box_common( trak, moov, ISOM_BOX_TYPE_TRAK );
1675 isom_cache_t *cache = lsmash_malloc_zero( sizeof(isom_cache_t) );
1676 if( !cache )
1678 free( trak );
1679 return NULL;
1681 isom_fragment_t *fragment = NULL;
1682 if( root->fragment )
1684 fragment = lsmash_malloc_zero( sizeof(isom_fragment_t) );
1685 if( !fragment )
1687 free( cache );
1688 free( trak );
1689 return NULL;
1691 cache->fragment = fragment;
1693 if( lsmash_add_entry( moov->trak_list, trak ) )
1695 if( fragment )
1696 free( fragment );
1697 free( cache );
1698 free( trak );
1699 return NULL;
1701 trak->cache = cache;
1702 return trak;
1705 static int isom_add_mvex( isom_moov_t *moov )
1707 if( !moov || moov->mvex )
1708 return -1;
1709 isom_create_box( mvex, moov, ISOM_BOX_TYPE_MVEX );
1710 moov->mvex = mvex;
1711 return 0;
1714 static int isom_add_mehd( isom_mvex_t *mvex )
1716 if( !mvex || mvex->mehd )
1717 return -1;
1718 isom_create_box( mehd, mvex, ISOM_BOX_TYPE_MEHD );
1719 mvex->mehd = mehd;
1720 return 0;
1723 static int isom_add_tfhd( isom_traf_entry_t *traf )
1725 if( !traf || traf->tfhd )
1726 return -1;
1727 isom_create_box( tfhd, traf, ISOM_BOX_TYPE_TFHD );
1728 traf->tfhd = tfhd;
1729 return 0;
1732 static int isom_add_tfdt( isom_traf_entry_t *traf )
1734 if( !traf || traf->tfdt )
1735 return -1;
1736 isom_create_box( tfdt, traf, ISOM_BOX_TYPE_TFDT );
1737 traf->tfdt = tfdt;
1738 return 0;
1741 static int isom_add_mfhd( isom_moof_entry_t *moof )
1743 if( !moof || moof->mfhd )
1744 return -1;
1745 isom_create_box( mfhd, moof, ISOM_BOX_TYPE_MFHD );
1746 moof->mfhd = mfhd;
1747 return 0;
1750 static int isom_add_mfra( lsmash_root_t *root )
1752 if( !root || root->mfra )
1753 return -1;
1754 isom_create_box( mfra, root, ISOM_BOX_TYPE_MFRA );
1755 root->mfra = mfra;
1756 return 0;
1759 static int isom_add_mfro( isom_mfra_t *mfra )
1761 if( !mfra || mfra->mfro )
1762 return -1;
1763 isom_create_box( mfro, mfra, ISOM_BOX_TYPE_MFRO );
1764 mfra->mfro = mfro;
1765 return 0;
1768 #define isom_remove_box( box_name, parent_type ) \
1769 do \
1771 parent_type *parent = (parent_type *)box_name->parent; \
1772 free( box_name ); \
1773 if( parent ) \
1774 parent->box_name = NULL; \
1775 } while( 0 )
1777 void isom_remove_unknown_box( isom_unknown_box_t *unknown_box )
1779 if( !unknown_box )
1780 return;
1781 if( unknown_box->unknown_field )
1782 free( unknown_box->unknown_field );
1783 free( unknown_box );
1786 static void isom_remove_ftyp( isom_ftyp_t *ftyp )
1788 if( !ftyp )
1789 return;
1790 if( ftyp->compatible_brands )
1791 free( ftyp->compatible_brands );
1792 isom_remove_box( ftyp, lsmash_root_t );
1795 static void isom_remove_tkhd( isom_tkhd_t *tkhd )
1797 if( !tkhd )
1798 return;
1799 isom_remove_box( tkhd, isom_trak_entry_t );
1802 static void isom_remove_clef( isom_clef_t *clef )
1804 if( !clef )
1805 return;
1806 isom_remove_box( clef, isom_tapt_t );
1809 static void isom_remove_prof( isom_prof_t *prof )
1811 if( !prof )
1812 return;
1813 isom_remove_box( prof, isom_tapt_t );
1816 static void isom_remove_enof( isom_enof_t *enof )
1818 if( !enof )
1819 return;
1820 isom_remove_box( enof, isom_tapt_t );
1823 void isom_remove_tapt( isom_tapt_t *tapt )
1825 if( !tapt )
1826 return;
1827 isom_remove_clef( tapt->clef );
1828 isom_remove_prof( tapt->prof );
1829 isom_remove_enof( tapt->enof );
1830 isom_remove_box( tapt, isom_trak_entry_t );
1833 static void isom_remove_elst( isom_elst_t *elst )
1835 if( !elst )
1836 return;
1837 lsmash_remove_list( elst->list, NULL );
1838 isom_remove_box( elst, isom_edts_t );
1841 static void isom_remove_edts( isom_edts_t *edts )
1843 if( !edts )
1844 return;
1845 isom_remove_elst( edts->elst );
1846 isom_remove_box( edts, isom_trak_entry_t );
1849 void isom_remove_track_reference_type( isom_tref_type_t *ref )
1851 if( !ref )
1852 return;
1853 if( ref->track_ID )
1854 free( ref->track_ID );
1855 free( ref );
1858 void isom_remove_tref( isom_tref_t *tref )
1860 if( !tref )
1861 return;
1862 lsmash_remove_list( tref->ref_list, isom_remove_track_reference_type );
1863 isom_remove_box( tref, isom_trak_entry_t );
1866 static void isom_remove_mdhd( isom_mdhd_t *mdhd )
1868 if( !mdhd )
1869 return;
1870 isom_remove_box( mdhd, isom_mdia_t );
1873 static void isom_remove_vmhd( isom_vmhd_t *vmhd )
1875 if( !vmhd )
1876 return;
1877 isom_remove_box( vmhd, isom_minf_t );
1880 static void isom_remove_smhd( isom_smhd_t *smhd )
1882 if( !smhd )
1883 return;
1884 isom_remove_box( smhd, isom_minf_t );
1887 static void isom_remove_hmhd( isom_hmhd_t *hmhd )
1889 if( !hmhd )
1890 return;
1891 isom_remove_box( hmhd, isom_minf_t );
1894 static void isom_remove_nmhd( isom_nmhd_t *nmhd )
1896 if( !nmhd )
1897 return;
1898 isom_remove_box( nmhd, isom_minf_t );
1901 static void isom_remove_gmin( isom_gmin_t *gmin )
1903 if( !gmin )
1904 return;
1905 isom_remove_box( gmin, isom_gmhd_t );
1908 static void isom_remove_text( isom_text_t *text )
1910 if( !text )
1911 return;
1912 isom_remove_box( text, isom_gmhd_t );
1915 static void isom_remove_gmhd( isom_gmhd_t *gmhd )
1917 if( !gmhd )
1918 return;
1919 isom_remove_gmin( gmhd->gmin );
1920 isom_remove_text( gmhd->text );
1921 isom_remove_box( gmhd, isom_minf_t );
1924 static void isom_remove_hdlr( isom_hdlr_t *hdlr )
1926 if( !hdlr )
1927 return;
1928 if( hdlr->componentName )
1929 free( hdlr->componentName );
1930 if( hdlr->parent )
1932 if( lsmash_check_box_type_identical( hdlr->parent->type, ISOM_BOX_TYPE_MDIA ) )
1933 isom_remove_box( hdlr, isom_mdia_t );
1934 else if( lsmash_check_box_type_identical( hdlr->parent->type, ISOM_BOX_TYPE_META )
1935 || lsmash_check_box_type_identical( hdlr->parent->type, QT_BOX_TYPE_META ) )
1936 isom_remove_box( hdlr, isom_meta_t );
1937 else if( lsmash_check_box_type_identical( hdlr->parent->type, ISOM_BOX_TYPE_MINF ) )
1938 isom_remove_box( hdlr, isom_minf_t );
1939 else
1940 assert( 0 );
1941 return;
1943 free( hdlr );
1946 void isom_remove_clap( isom_clap_t *clap )
1948 if( !clap )
1949 return;
1950 free( clap );
1953 void isom_remove_pasp( isom_pasp_t *pasp )
1955 if( !pasp )
1956 return;
1957 free( pasp );
1960 void isom_remove_glbl( isom_glbl_t *glbl )
1962 if( !glbl )
1963 return;
1964 if( glbl->header_data )
1965 free( glbl->header_data );
1966 free( glbl );
1969 void isom_remove_colr( isom_colr_t *colr )
1971 if( !colr )
1972 return;
1973 free( colr );
1976 void isom_remove_gama( isom_gama_t *gama )
1978 if( !gama )
1979 return;
1980 free( gama );
1983 void isom_remove_fiel( isom_fiel_t *fiel )
1985 if( !fiel )
1986 return;
1987 free( fiel );
1990 void isom_remove_cspc( isom_cspc_t *cspc )
1992 if( !cspc )
1993 return;
1994 free( cspc );
1997 void isom_remove_sgbt( isom_sgbt_t *sgbt )
1999 if( !sgbt )
2000 return;
2001 free( sgbt );
2004 void isom_remove_stsl( isom_stsl_t *stsl )
2006 if( !stsl )
2007 return;
2008 free( stsl );
2011 void isom_remove_esds( isom_esds_t *esds )
2013 if( !esds )
2014 return;
2015 mp4sys_remove_ES_Descriptor( esds->ES );
2016 free( esds );
2019 void isom_remove_avcC( isom_avcC_t *avcC )
2021 if( !avcC )
2022 return;
2023 lsmash_remove_list( avcC->sequenceParameterSets, isom_remove_avcC_ps );
2024 lsmash_remove_list( avcC->pictureParameterSets, isom_remove_avcC_ps );
2025 lsmash_remove_list( avcC->sequenceParameterSetExt, isom_remove_avcC_ps );
2026 free( avcC );
2029 void isom_remove_btrt( isom_btrt_t *btrt )
2031 if( !btrt )
2032 return;
2033 free( btrt );
2036 static void isom_remove_font_record( isom_font_record_t *font_record )
2038 if( !font_record )
2039 return;
2040 if( font_record->font_name )
2041 free( font_record->font_name );
2042 free( font_record );
2045 void isom_remove_ftab( isom_ftab_t *ftab )
2047 if( !ftab )
2048 return;
2049 lsmash_remove_list( ftab->list, isom_remove_font_record );
2050 isom_remove_box( ftab, isom_tx3g_entry_t );
2053 void isom_remove_frma( isom_frma_t *frma )
2055 if( !frma )
2056 return;
2057 isom_remove_box( frma, isom_wave_t );
2060 void isom_remove_enda( isom_enda_t *enda )
2062 if( !enda )
2063 return;
2064 isom_remove_box( enda, isom_wave_t );
2067 void isom_remove_mp4a( isom_mp4a_t *mp4a )
2069 if( !mp4a )
2070 return;
2071 isom_remove_box( mp4a, isom_wave_t );
2074 void isom_remove_terminator( isom_terminator_t *terminator )
2076 if( !terminator )
2077 return;
2078 isom_remove_box( terminator, isom_wave_t );
2081 void isom_remove_wave( isom_wave_t *wave )
2083 if( !wave )
2084 return;
2085 isom_remove_frma( wave->frma );
2086 isom_remove_enda( wave->enda );
2087 isom_remove_mp4a( wave->mp4a );
2088 isom_remove_terminator( wave->terminator );
2089 free( wave );
2092 void isom_remove_chan( isom_chan_t *chan )
2094 if( !chan )
2095 return;
2096 if( chan->channelDescriptions )
2097 free( chan->channelDescriptions );
2098 free( chan );
2101 static void isom_remove_visual_description( isom_sample_entry_t *description )
2103 isom_visual_entry_t *visual = (isom_visual_entry_t *)description;
2104 isom_remove_sample_description_extensions( &visual->extensions );
2105 if( visual->color_table.array )
2106 free( visual->color_table.array );
2107 free( visual );
2110 static void isom_remove_audio_description( isom_sample_entry_t *description )
2112 isom_audio_entry_t *audio = (isom_audio_entry_t *)description;
2113 isom_remove_sample_description_extensions( &audio->extensions );
2114 free( audio );
2117 static void isom_remove_hint_description( isom_sample_entry_t *description )
2119 isom_hint_entry_t *hint = (isom_hint_entry_t *)description;
2120 isom_remove_sample_description_extensions( &hint->extensions );
2121 if( hint->data )
2122 free( hint->data );
2123 free( hint );
2126 static void isom_remove_metadata_description( isom_sample_entry_t *description )
2128 isom_metadata_entry_t *metadata = (isom_metadata_entry_t *)description;
2129 isom_remove_sample_description_extensions( &metadata->extensions );
2130 free( metadata );
2133 static void isom_remove_tx3g_description( isom_sample_entry_t *description )
2135 isom_tx3g_entry_t *tx3g = (isom_tx3g_entry_t *)description;
2136 isom_remove_sample_description_extensions( &tx3g->extensions );
2137 if( tx3g->ftab )
2138 isom_remove_ftab( tx3g->ftab );
2139 free( tx3g );
2142 static void isom_remove_qt_text_description( isom_sample_entry_t *description )
2144 isom_text_entry_t *text = (isom_text_entry_t *)description;
2145 isom_remove_sample_description_extensions( &text->extensions );
2146 if( text->font_name )
2147 free( text->font_name );
2148 free( text );
2151 static void isom_remove_mp4s_description( isom_sample_entry_t *description )
2153 isom_mp4s_entry_t *mp4s = (isom_mp4s_entry_t *)description;
2154 isom_remove_sample_description_extensions( &mp4s->extensions );
2155 free( mp4s );
2158 void isom_remove_sample_description( isom_sample_entry_t *sample )
2160 if( !sample )
2161 return;
2162 lsmash_codec_type_t sample_type = sample->type;
2163 if( lsmash_check_box_type_identical( sample_type, LSMASH_CODEC_TYPE_RAW ) )
2165 if( sample->manager & LSMASH_VIDEO_DESCRIPTION )
2167 isom_remove_visual_description( sample );
2168 return;
2170 else if( sample->manager & LSMASH_AUDIO_DESCRIPTION )
2172 isom_remove_audio_description( sample );
2173 return;
2176 static struct description_remover_table_tag
2178 lsmash_codec_type_t type;
2179 void (*func)( isom_sample_entry_t * );
2180 } description_remover_table[128] = { { LSMASH_CODEC_TYPE_INITIALIZER, NULL } };
2181 if( !description_remover_table[0].func )
2183 /* Initialize the table. */
2184 int i = 0;
2185 #define ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( type, func ) \
2186 description_remover_table[i++] = (struct description_remover_table_tag){ type, func }
2187 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, isom_remove_visual_description );
2188 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO, isom_remove_visual_description );
2189 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO, isom_remove_visual_description );
2190 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO, isom_remove_visual_description );
2191 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO, isom_remove_visual_description );
2192 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO, isom_remove_visual_description );
2193 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, isom_remove_visual_description );
2194 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO, isom_remove_visual_description );
2195 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO, isom_remove_visual_description );
2196 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO, isom_remove_visual_description );
2197 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO, isom_remove_visual_description );
2198 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, isom_remove_visual_description );
2199 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_CFHD_VIDEO, isom_remove_visual_description );
2200 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DV10_VIDEO, isom_remove_visual_description );
2201 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVOO_VIDEO, isom_remove_visual_description );
2202 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVOR_VIDEO, isom_remove_visual_description );
2203 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVTV_VIDEO, isom_remove_visual_description );
2204 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVVT_VIDEO, isom_remove_visual_description );
2205 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_HD10_VIDEO, isom_remove_visual_description );
2206 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_M105_VIDEO, isom_remove_visual_description );
2207 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_PNTG_VIDEO, isom_remove_visual_description );
2208 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ1_VIDEO, isom_remove_visual_description );
2209 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SVQ3_VIDEO, isom_remove_visual_description );
2210 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR0_VIDEO, isom_remove_visual_description );
2211 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR1_VIDEO, isom_remove_visual_description );
2212 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR2_VIDEO, isom_remove_visual_description );
2213 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR3_VIDEO, isom_remove_visual_description );
2214 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SHR4_VIDEO, isom_remove_visual_description );
2215 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_WRLE_VIDEO, isom_remove_visual_description );
2216 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, isom_remove_visual_description );
2217 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, isom_remove_visual_description );
2218 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, isom_remove_visual_description );
2219 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, isom_remove_visual_description );
2220 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, isom_remove_visual_description );
2221 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_CIVD_VIDEO, isom_remove_visual_description );
2222 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DRAC_VIDEO, isom_remove_visual_description );
2223 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, isom_remove_visual_description );
2224 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, isom_remove_visual_description );
2225 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, isom_remove_visual_description );
2226 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, isom_remove_visual_description );
2227 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, isom_remove_visual_description );
2228 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, isom_remove_visual_description );
2229 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, isom_remove_visual_description );
2230 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, isom_remove_visual_description );
2231 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, isom_remove_visual_description );
2232 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, isom_remove_visual_description );
2233 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, isom_remove_visual_description );
2234 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_FLIC_VIDEO, isom_remove_visual_description );
2235 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_GIF_VIDEO, isom_remove_visual_description );
2236 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_H261_VIDEO, isom_remove_visual_description );
2237 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_H263_VIDEO, isom_remove_visual_description );
2238 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_JPEG_VIDEO, isom_remove_visual_description );
2239 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_MJPA_VIDEO, isom_remove_visual_description );
2240 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_MJPB_VIDEO, isom_remove_visual_description );
2241 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_PNG_VIDEO, isom_remove_visual_description );
2242 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_RLE_VIDEO, isom_remove_visual_description );
2243 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_RPZA_VIDEO, isom_remove_visual_description );
2244 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_TGA_VIDEO, isom_remove_visual_description );
2245 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_TIFF_VIDEO, isom_remove_visual_description );
2246 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, isom_remove_visual_description );
2247 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, isom_remove_visual_description );
2248 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, isom_remove_visual_description );
2249 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, isom_remove_visual_description );
2250 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, isom_remove_visual_description );
2251 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, isom_remove_visual_description );
2252 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, isom_remove_visual_description );
2253 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, isom_remove_visual_description );
2254 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, isom_remove_visual_description );
2255 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, isom_remove_visual_description );
2256 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, isom_remove_audio_description );
2257 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, isom_remove_audio_description );
2258 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, isom_remove_audio_description );
2259 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, isom_remove_audio_description );
2260 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, isom_remove_audio_description );
2261 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, isom_remove_audio_description );
2262 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, isom_remove_audio_description );
2263 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, isom_remove_audio_description );
2264 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, isom_remove_audio_description );
2265 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, isom_remove_audio_description );
2266 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, isom_remove_audio_description );
2267 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, isom_remove_audio_description );
2268 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, isom_remove_audio_description );
2269 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, isom_remove_audio_description );
2270 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, isom_remove_audio_description );
2271 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, isom_remove_audio_description );
2272 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, isom_remove_audio_description );
2273 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, isom_remove_audio_description );
2274 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, isom_remove_audio_description );
2275 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, isom_remove_audio_description );
2276 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO, isom_remove_audio_description );
2277 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO, isom_remove_audio_description );
2278 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO, isom_remove_audio_description );
2279 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO, isom_remove_audio_description );
2280 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO, isom_remove_audio_description );
2281 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO, isom_remove_audio_description );
2282 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO, isom_remove_audio_description );
2283 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO, isom_remove_audio_description );
2284 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO, isom_remove_audio_description );
2285 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO, isom_remove_audio_description );
2286 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO, isom_remove_audio_description );
2287 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_FDP_HINT, isom_remove_hint_description );
2288 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_M2TS_HINT, isom_remove_hint_description );
2289 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_PM2T_HINT, isom_remove_hint_description );
2290 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_PRTP_HINT, isom_remove_hint_description );
2291 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_RM2T_HINT, isom_remove_hint_description );
2292 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_RRTP_HINT, isom_remove_hint_description );
2293 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_RSRP_HINT, isom_remove_hint_description );
2294 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_RTP_HINT , isom_remove_hint_description );
2295 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SM2T_HINT, isom_remove_hint_description );
2296 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SRTP_HINT, isom_remove_hint_description );
2297 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_IXSE_META, isom_remove_metadata_description );
2298 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_METT_META, isom_remove_metadata_description );
2299 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_METX_META, isom_remove_metadata_description );
2300 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLIX_META, isom_remove_metadata_description );
2301 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_OKSD_META, isom_remove_metadata_description );
2302 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVCM_META, isom_remove_metadata_description );
2303 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_TEXT_META, isom_remove_metadata_description );
2304 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_URIM_META, isom_remove_metadata_description );
2305 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_XML_META, isom_remove_metadata_description );
2306 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT, isom_remove_tx3g_description );
2307 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT, isom_remove_qt_text_description );
2308 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4S_SYSTEM, isom_remove_mp4s_description );
2309 ADD_DESCRIPTION_REMOVER_TABLE_ELEMENT( LSMASH_CODEC_TYPE_UNSPECIFIED, NULL );
2311 for( int i = 0; description_remover_table[i].func; i++ )
2312 if( lsmash_check_codec_type_identical( sample_type, description_remover_table[i].type ) )
2314 description_remover_table[i].func( sample );
2315 return;
2319 static void isom_remove_stsd( isom_stsd_t *stsd )
2321 if( !stsd )
2322 return;
2323 lsmash_remove_list( stsd->list, isom_remove_sample_description );
2324 isom_remove_box( stsd, isom_stbl_t );
2327 static void isom_remove_stts( isom_stts_t *stts )
2329 if( !stts )
2330 return;
2331 lsmash_remove_list( stts->list, NULL );
2332 isom_remove_box( stts, isom_stbl_t );
2335 static void isom_remove_ctts( isom_ctts_t *ctts )
2337 if( !ctts )
2338 return;
2339 lsmash_remove_list( ctts->list, NULL );
2340 isom_remove_box( ctts, isom_stbl_t );
2343 static void isom_remove_cslg( isom_cslg_t *cslg )
2345 if( !cslg )
2346 return;
2347 isom_remove_box( cslg, isom_stbl_t );
2350 static void isom_remove_stsc( isom_stsc_t *stsc )
2352 if( !stsc )
2353 return;
2354 lsmash_remove_list( stsc->list, NULL );
2355 isom_remove_box( stsc, isom_stbl_t );
2358 static void isom_remove_stsz( isom_stsz_t *stsz )
2360 if( !stsz )
2361 return;
2362 lsmash_remove_list( stsz->list, NULL );
2363 isom_remove_box( stsz, isom_stbl_t );
2366 static void isom_remove_stss( isom_stss_t *stss )
2368 if( !stss )
2369 return;
2370 lsmash_remove_list( stss->list, NULL );
2371 isom_remove_box( stss, isom_stbl_t );
2374 static void isom_remove_stps( isom_stps_t *stps )
2376 if( !stps )
2377 return;
2378 lsmash_remove_list( stps->list, NULL );
2379 isom_remove_box( stps, isom_stbl_t );
2382 static void isom_remove_sdtp( isom_sdtp_t *sdtp )
2384 if( !sdtp )
2385 return;
2386 lsmash_remove_list( sdtp->list, NULL );
2387 if( sdtp->parent )
2389 if( lsmash_check_box_type_identical( sdtp->parent->type, ISOM_BOX_TYPE_STBL ) )
2390 isom_remove_box( sdtp, isom_stbl_t );
2391 else if( lsmash_check_box_type_identical( sdtp->parent->type, ISOM_BOX_TYPE_TRAF ) )
2392 isom_remove_box( sdtp, isom_traf_entry_t );
2393 else
2394 assert( 0 );
2395 return;
2397 free( sdtp );
2400 static void isom_remove_stco( isom_stco_t *stco )
2402 if( !stco )
2403 return;
2404 lsmash_remove_list( stco->list, NULL );
2405 isom_remove_box( stco, isom_stbl_t );
2408 static void isom_remove_sgpd( isom_sgpd_entry_t *sgpd )
2410 if( !sgpd )
2411 return;
2412 lsmash_remove_list( sgpd->list, NULL );
2413 free( sgpd );
2416 static void isom_remove_sbgp( isom_sbgp_entry_t *sbgp )
2418 if( !sbgp )
2419 return;
2420 lsmash_remove_list( sbgp->list, NULL );
2421 free( sbgp );
2424 static void isom_remove_stbl( isom_stbl_t *stbl )
2426 if( !stbl )
2427 return;
2428 isom_remove_stsd( stbl->stsd );
2429 isom_remove_stts( stbl->stts );
2430 isom_remove_ctts( stbl->ctts );
2431 isom_remove_cslg( stbl->cslg );
2432 isom_remove_stsc( stbl->stsc );
2433 isom_remove_stsz( stbl->stsz );
2434 isom_remove_stss( stbl->stss );
2435 isom_remove_stps( stbl->stps );
2436 isom_remove_sdtp( stbl->sdtp );
2437 isom_remove_stco( stbl->stco );
2438 lsmash_remove_list( stbl->sgpd_list, isom_remove_sgpd );
2439 lsmash_remove_list( stbl->sbgp_list, isom_remove_sbgp );
2440 isom_remove_box( stbl, isom_minf_t );
2443 static void isom_remove_dref( isom_dref_t *dref )
2445 if( !dref )
2446 return;
2447 if( !dref->list )
2449 free( dref );
2450 return;
2452 for( lsmash_entry_t *entry = dref->list->head; entry; )
2454 isom_dref_entry_t *data = (isom_dref_entry_t *)entry->data;
2455 if( data )
2457 if( data->name )
2458 free( data->name );
2459 if( data->location )
2460 free( data->location );
2461 free( data );
2463 lsmash_entry_t *next = entry->next;
2464 free( entry );
2465 entry = next;
2467 free( dref->list );
2468 isom_remove_box( dref, isom_dinf_t );
2471 static void isom_remove_dinf( isom_dinf_t *dinf )
2473 if( !dinf )
2474 return;
2475 isom_remove_dref( dinf->dref );
2476 isom_remove_box( dinf, isom_minf_t );
2479 static void isom_remove_minf( isom_minf_t *minf )
2481 if( !minf )
2482 return;
2483 isom_remove_vmhd( minf->vmhd );
2484 isom_remove_smhd( minf->smhd );
2485 isom_remove_hmhd( minf->hmhd );
2486 isom_remove_nmhd( minf->nmhd );
2487 isom_remove_gmhd( minf->gmhd );
2488 isom_remove_hdlr( minf->hdlr );
2489 isom_remove_dinf( minf->dinf );
2490 isom_remove_stbl( minf->stbl );
2491 isom_remove_box( minf, isom_mdia_t );
2494 static void isom_remove_mdia( isom_mdia_t *mdia )
2496 if( !mdia )
2497 return;
2498 isom_remove_mdhd( mdia->mdhd );
2499 isom_remove_minf( mdia->minf );
2500 isom_remove_hdlr( mdia->hdlr );
2501 isom_remove_box( mdia, isom_trak_entry_t );
2504 static void isom_remove_chpl( isom_chpl_t *chpl )
2506 if( !chpl )
2507 return;
2508 if( !chpl->list )
2510 free( chpl );
2511 return;
2513 for( lsmash_entry_t *entry = chpl->list->head; entry; )
2515 isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data;
2516 if( data )
2518 if( data->chapter_name )
2519 free( data->chapter_name );
2520 free( data );
2522 lsmash_entry_t *next = entry->next;
2523 free( entry );
2524 entry = next;
2526 free( chpl->list );
2527 isom_remove_box( chpl, isom_udta_t );
2530 static void isom_remove_keys_entry( isom_keys_entry_t *data )
2532 if( !data )
2533 return;
2534 if( data->key_value )
2535 free( data->key_value );
2536 free( data );
2539 static void isom_remove_keys( isom_keys_t *keys )
2541 if( !keys )
2542 return;
2543 lsmash_remove_list( keys->list, isom_remove_keys_entry );
2544 isom_remove_box( keys, isom_meta_t );
2547 void isom_remove_mean( isom_mean_t *mean )
2549 if( !mean )
2550 return;
2551 if( mean->meaning_string )
2552 free( mean->meaning_string );
2553 isom_remove_box( mean, isom_metaitem_t );
2556 void isom_remove_name( isom_name_t *name )
2558 if( !name )
2559 return;
2560 if( name->name )
2561 free( name->name );
2562 isom_remove_box( name, isom_metaitem_t );
2565 void isom_remove_data( isom_data_t *data )
2567 if( !data )
2568 return;
2569 if( data->value )
2570 free( data->value );
2571 isom_remove_box( data, isom_metaitem_t );
2574 void isom_remove_metaitem( isom_metaitem_t *metaitem )
2576 if( !metaitem )
2577 return;
2578 isom_remove_mean( metaitem->mean );
2579 isom_remove_name( metaitem->name );
2580 isom_remove_data( metaitem->data );
2581 free( metaitem );
2584 void isom_remove_ilst( isom_ilst_t *ilst )
2586 if( !ilst )
2587 return;
2588 lsmash_remove_list( ilst->item_list, isom_remove_metaitem );
2589 isom_remove_box( ilst, isom_meta_t );
2592 static void isom_remove_meta( isom_meta_t *meta )
2594 if( !meta )
2595 return;
2596 isom_remove_hdlr( meta->hdlr );
2597 isom_remove_dinf( meta->dinf );
2598 isom_remove_keys( meta->keys );
2599 isom_remove_ilst( meta->ilst );
2600 if( meta->parent )
2602 if( lsmash_check_box_type_identical( meta->parent->type, LSMASH_BOX_TYPE_UNSPECIFIED ) )
2603 isom_remove_box( meta, lsmash_root_t );
2604 else if( lsmash_check_box_type_identical( meta->parent->type, ISOM_BOX_TYPE_MOOV ) )
2605 isom_remove_box( meta, isom_moov_t );
2606 else if( lsmash_check_box_type_identical( meta->parent->type, ISOM_BOX_TYPE_TRAK ) )
2607 isom_remove_box( meta, isom_trak_entry_t );
2608 else if( lsmash_check_box_type_identical( meta->parent->type, ISOM_BOX_TYPE_UDTA ) )
2609 isom_remove_box( meta, isom_udta_t );
2610 else
2611 assert( 0 );
2612 return;
2614 free( meta );
2617 static void isom_remove_cprt( isom_cprt_t *cprt )
2619 if( !cprt )
2620 return;
2621 if( cprt->notice )
2622 free( cprt->notice );
2623 free( cprt );
2626 static void isom_remove_udta( isom_udta_t *udta )
2628 if( !udta )
2629 return;
2630 isom_remove_chpl( udta->chpl );
2631 isom_remove_meta( udta->meta );
2632 free( udta->WLOC );
2633 free( udta->LOOP );
2634 free( udta->SelO );
2635 free( udta->AllF );
2636 lsmash_remove_list( udta->cprt_list, isom_remove_cprt );
2637 if( udta->parent )
2639 if( lsmash_check_box_type_identical( udta->parent->type, ISOM_BOX_TYPE_MOOV ) )
2640 isom_remove_box( udta, isom_moov_t );
2641 else if( lsmash_check_box_type_identical( udta->parent->type, ISOM_BOX_TYPE_TRAK ) )
2642 isom_remove_box( udta, isom_trak_entry_t );
2643 else
2644 assert( 0 );
2645 return;
2647 free( udta );
2650 static void isom_remove_sample_pool( isom_sample_pool_t *pool );
2652 void isom_remove_trak( isom_trak_entry_t *trak )
2654 if( !trak )
2655 return;
2656 isom_remove_tkhd( trak->tkhd );
2657 isom_remove_tapt( trak->tapt );
2658 isom_remove_edts( trak->edts );
2659 isom_remove_tref( trak->tref );
2660 isom_remove_mdia( trak->mdia );
2661 isom_remove_udta( trak->udta );
2662 isom_remove_meta( trak->meta );
2663 if( trak->cache )
2665 isom_remove_sample_pool( trak->cache->chunk.pool );
2666 lsmash_remove_list( trak->cache->roll.pool, NULL );
2667 if( trak->cache->rap )
2668 free( trak->cache->rap );
2669 free( trak->cache );
2671 free( trak ); /* Note: the list that contains this trak still has the address of the entry. */
2674 static void isom_remove_iods( isom_iods_t *iods )
2676 if( !iods )
2677 return;
2678 mp4sys_remove_ObjectDescriptor( iods->OD );
2679 isom_remove_box( iods, isom_moov_t );
2682 void isom_remove_ctab( isom_ctab_t *ctab )
2684 if( !ctab )
2685 return;
2686 if( ctab->color_table.array )
2687 free( ctab->color_table.array );
2688 if( ctab->parent && lsmash_check_box_type_identical( ctab->parent->type, ISOM_BOX_TYPE_MOOV ) )
2689 isom_remove_box( ctab, isom_moov_t );
2690 else
2691 free( ctab );
2694 static void isom_remove_mehd( isom_mehd_t *mehd )
2696 if( !mehd )
2697 return;
2698 isom_remove_box( mehd, isom_mvex_t );
2701 static void isom_remove_mvex( isom_mvex_t *mvex )
2703 if( !mvex )
2704 return;
2705 isom_remove_mehd( mvex->mehd );
2706 lsmash_remove_list( mvex->trex_list, NULL );
2707 isom_remove_box( mvex, isom_moov_t );
2710 static void isom_remove_moov( lsmash_root_t *root )
2712 if( !root || !root->moov )
2713 return;
2714 isom_moov_t *moov = root->moov;
2715 if( moov->mvhd )
2716 free( moov->mvhd );
2717 isom_remove_iods( moov->iods );
2718 lsmash_remove_list( moov->trak_list, isom_remove_trak );
2719 isom_remove_udta( moov->udta );
2720 isom_remove_ctab( moov->ctab );
2721 isom_remove_meta( moov->meta );
2722 isom_remove_mvex( moov->mvex );
2723 free( moov );
2724 root->moov = NULL;
2727 static void isom_remove_mfhd( isom_mfhd_t *mfhd )
2729 if( !mfhd )
2730 return;
2731 isom_remove_box( mfhd, isom_moof_entry_t );
2734 static void isom_remove_tfhd( isom_tfhd_t *tfhd )
2736 if( !tfhd )
2737 return;
2738 isom_remove_box( tfhd, isom_traf_entry_t );
2741 static void isom_remove_tfdt( isom_tfdt_t *tfdt )
2743 if( !tfdt )
2744 return;
2745 isom_remove_box( tfdt, isom_traf_entry_t );
2748 static void isom_remove_trun( isom_trun_entry_t *trun )
2750 if( !trun )
2751 return;
2752 lsmash_remove_list( trun->optional, NULL );
2753 free( trun ); /* Note: the list that contains this trun still has the address of the entry. */
2756 static void isom_remove_traf( isom_traf_entry_t *traf )
2758 if( !traf )
2759 return;
2760 isom_remove_tfhd( traf->tfhd );
2761 isom_remove_tfdt( traf->tfdt );
2762 lsmash_remove_list( traf->trun_list, isom_remove_trun );
2763 isom_remove_sdtp( traf->sdtp );
2764 free( traf ); /* Note: the list that contains this traf still has the address of the entry. */
2767 static void isom_remove_moof( isom_moof_entry_t *moof )
2769 if( !moof )
2770 return;
2771 isom_remove_mfhd( moof->mfhd );
2772 lsmash_remove_list( moof->traf_list, isom_remove_traf );
2773 free( moof );
2776 static void isom_remove_mdat( isom_mdat_t *mdat )
2778 if( !mdat )
2779 return;
2780 isom_remove_box( mdat, lsmash_root_t );
2783 static void isom_remove_free( isom_free_t *skip )
2785 if( !skip )
2786 return;
2787 if( skip->data )
2788 free( skip->data );
2789 lsmash_root_t *root = (lsmash_root_t *)skip->parent;
2790 free( skip );
2791 root->free = NULL;
2794 static void isom_remove_tfra( isom_tfra_entry_t *tfra )
2796 if( !tfra )
2797 return;
2798 lsmash_remove_list( tfra->list, NULL );
2799 free( tfra );
2802 static void isom_remove_mfro( isom_mfro_t *mfro )
2804 if( !mfro )
2805 return;
2806 isom_remove_box( mfro, isom_mfra_t );
2809 static void isom_remove_mfra( isom_mfra_t *mfra )
2811 if( !mfra )
2812 return;
2813 lsmash_remove_list( mfra->tfra_list, isom_remove_tfra );
2814 isom_remove_mfro( mfra->mfro );
2815 isom_remove_box( mfra, lsmash_root_t );
2818 /* We put a placeholder for 64-bit media data if the media_size of the argument is set to 0.
2819 * If a Media Data Box already exists and we don't pick movie fragments structure,
2820 * write the actual size of the current one and start a new one. */
2821 static int isom_new_mdat( lsmash_root_t *root, uint64_t media_size )
2823 if( !root )
2824 return 0;
2825 if( root->mdat )
2827 /* Write the actual size of the current Media Data Box. */
2828 if( !root->fragment && isom_write_mdat_size( root ) )
2829 return -1;
2831 else
2833 isom_create_box( mdat, root, ISOM_BOX_TYPE_MDAT );
2834 root->mdat = mdat;
2836 /* Start a new Media Data Box. */
2837 return isom_write_mdat_header( root, media_size );
2840 int isom_check_compatibility( lsmash_root_t *root )
2842 if( !root )
2843 return -1;
2844 root->qt_compatible = 0;
2845 /* Check brand to decide mandatory boxes. */
2846 if( !root->ftyp || !root->ftyp->brand_count )
2848 /* No brand declaration means this file is a MP4 version 1 or QuickTime file format. */
2849 if( root->moov && root->moov->iods )
2851 root->mp4_version1 = 1;
2852 root->isom_compatible = 1;
2854 else
2855 root->qt_compatible = 1;
2856 return 0;
2858 for( uint32_t i = 0; i <= root->ftyp->brand_count; i++ )
2860 uint32_t brand = (i == root->ftyp->brand_count ? root->ftyp->major_brand : root->ftyp->compatible_brands[i]);
2861 switch( brand )
2863 case ISOM_BRAND_TYPE_QT :
2864 root->qt_compatible = 1;
2865 break;
2866 case ISOM_BRAND_TYPE_MP41 :
2867 root->mp4_version1 = 1;
2868 break;
2869 case ISOM_BRAND_TYPE_MP42 :
2870 root->mp4_version2 = 1;
2871 break;
2872 case ISOM_BRAND_TYPE_AVC1 :
2873 case ISOM_BRAND_TYPE_ISOM :
2874 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 1 );
2875 break;
2876 case ISOM_BRAND_TYPE_ISO2 :
2877 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 2 );
2878 break;
2879 case ISOM_BRAND_TYPE_ISO3 :
2880 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 3 );
2881 break;
2882 case ISOM_BRAND_TYPE_ISO4 :
2883 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 4 );
2884 break;
2885 case ISOM_BRAND_TYPE_ISO5 :
2886 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 5 );
2887 break;
2888 case ISOM_BRAND_TYPE_ISO6 :
2889 root->max_isom_version = LSMASH_MAX( root->max_isom_version, 6 );
2890 break;
2891 case ISOM_BRAND_TYPE_M4A :
2892 case ISOM_BRAND_TYPE_M4B :
2893 case ISOM_BRAND_TYPE_M4P :
2894 case ISOM_BRAND_TYPE_M4V :
2895 root->itunes_movie = 1;
2896 break;
2897 case ISOM_BRAND_TYPE_3GP4 :
2898 root->max_3gpp_version = LSMASH_MAX( root->max_3gpp_version, 4 );
2899 break;
2900 case ISOM_BRAND_TYPE_3GP5 :
2901 root->max_3gpp_version = LSMASH_MAX( root->max_3gpp_version, 5 );
2902 break;
2903 case ISOM_BRAND_TYPE_3GE6 :
2904 case ISOM_BRAND_TYPE_3GG6 :
2905 case ISOM_BRAND_TYPE_3GP6 :
2906 case ISOM_BRAND_TYPE_3GR6 :
2907 case ISOM_BRAND_TYPE_3GS6 :
2908 root->max_3gpp_version = LSMASH_MAX( root->max_3gpp_version, 6 );
2909 break;
2910 default :
2911 break;
2913 switch( brand )
2915 case ISOM_BRAND_TYPE_AVC1 :
2916 case ISOM_BRAND_TYPE_ISO2 :
2917 case ISOM_BRAND_TYPE_ISO3 :
2918 case ISOM_BRAND_TYPE_ISO4 :
2919 case ISOM_BRAND_TYPE_ISO5 :
2920 case ISOM_BRAND_TYPE_ISO6 :
2921 root->avc_extensions = 1;
2922 break;
2923 default :
2924 break;
2927 root->isom_compatible = !root->qt_compatible || root->mp4_version1 || root->mp4_version2 || root->itunes_movie || root->max_3gpp_version;
2928 return 0;
2931 static uint32_t isom_get_sample_count( isom_trak_entry_t *trak )
2933 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stsz )
2934 return 0;
2935 return trak->mdia->minf->stbl->stsz->sample_count;
2938 static uint64_t isom_get_dts( isom_stts_t *stts, uint32_t sample_number )
2940 if( !stts || !stts->list )
2941 return 0;
2942 uint64_t dts = 0;
2943 uint32_t i = 1;
2944 lsmash_entry_t *entry;
2945 isom_stts_entry_t *data;
2946 for( entry = stts->list->head; entry; entry = entry->next )
2948 data = (isom_stts_entry_t *)entry->data;
2949 if( !data )
2950 return 0;
2951 if( i + data->sample_count > sample_number )
2952 break;
2953 dts += (uint64_t)data->sample_delta * data->sample_count;
2954 i += data->sample_count;
2956 if( !entry )
2957 return 0;
2958 dts += (uint64_t)data->sample_delta * (sample_number - i);
2959 return dts;
2962 #if 0
2963 static uint64_t isom_get_cts( isom_stts_t *stts, isom_ctts_t *ctts, uint32_t sample_number )
2965 if( !stts || !stts->list )
2966 return 0;
2967 if( !ctts )
2968 return isom_get_dts( stts, sample_number );
2969 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. */
2970 lsmash_entry_t *entry;
2971 isom_ctts_entry_t *data;
2972 if( sample_number == 0 )
2973 return 0;
2974 for( entry = ctts->list->head; entry; entry = entry->next )
2976 data = (isom_ctts_entry_t *)entry->data;
2977 if( !data )
2978 return 0;
2979 if( i + data->sample_count > sample_number )
2980 break;
2981 i += data->sample_count;
2983 if( !entry )
2984 return 0;
2985 return isom_get_dts( stts, sample_number ) + data->sample_offset;
2987 #endif
2989 static int isom_replace_last_sample_delta( isom_stbl_t *stbl, uint32_t sample_delta )
2991 if( !stbl || !stbl->stts || !stbl->stts->list || !stbl->stts->list->tail || !stbl->stts->list->tail->data )
2992 return -1;
2993 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stbl->stts->list->tail->data;
2994 if( sample_delta != last_stts_data->sample_delta )
2996 if( last_stts_data->sample_count > 1 )
2998 last_stts_data->sample_count -= 1;
2999 if( isom_add_stts_entry( stbl, sample_delta ) )
3000 return -1;
3002 else
3003 last_stts_data->sample_delta = sample_delta;
3005 return 0;
3008 static int isom_update_mdhd_duration( isom_trak_entry_t *trak, uint32_t last_sample_delta )
3010 if( !trak || !trak->root || !trak->cache || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf
3011 || !trak->mdia->minf->stbl || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list )
3012 return -1;
3013 lsmash_root_t *root = trak->root;
3014 isom_mdhd_t *mdhd = trak->mdia->mdhd;
3015 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3016 isom_stts_t *stts = stbl->stts;
3017 isom_ctts_t *ctts = stbl->ctts;
3018 isom_cslg_t *cslg = stbl->cslg;
3019 mdhd->duration = 0;
3020 uint32_t sample_count = isom_get_sample_count( trak );
3021 if( !sample_count )
3023 /* Return error if non-fragmented movie has no samples. */
3024 if( !root->fragment && !stts->list->entry_count )
3025 return -1;
3026 return 0;
3028 /* Now we have at least 1 sample, so do stts_entry. */
3029 lsmash_entry_t *last_stts = stts->list->tail;
3030 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)last_stts->data;
3031 if( sample_count == 1 )
3032 mdhd->duration = last_stts_data->sample_delta;
3033 /* Now we have at least 2 samples,
3034 * but dunno whether 1 stts_entry which has 2 samples or 2 stts_entry which has 1 samle each. */
3035 else if( !ctts )
3037 /* use dts instead of cts */
3038 mdhd->duration = isom_get_dts( stts, sample_count );
3039 if( last_sample_delta )
3041 mdhd->duration += last_sample_delta;
3042 if( isom_replace_last_sample_delta( stbl, last_sample_delta ) )
3043 return -1;
3045 else if( last_stts_data->sample_count > 1 )
3046 mdhd->duration += last_stts_data->sample_delta; /* no need to update last_stts_data->sample_delta */
3047 else
3049 /* Remove the last entry. */
3050 if( lsmash_remove_entry( stts->list, stts->list->entry_count, NULL ) )
3051 return -1;
3052 /* copy the previous sample_delta. */
3053 ++ ((isom_stts_entry_t *)stts->list->tail->data)->sample_count;
3054 mdhd->duration += ((isom_stts_entry_t *)stts->list->tail->data)->sample_delta;
3057 else
3059 if( !ctts->list || ctts->list->entry_count == 0 )
3060 return -1;
3061 uint64_t dts = 0;
3062 uint64_t max_cts = 0, max2_cts = 0, min_cts = UINT64_MAX;
3063 uint32_t max_offset = 0, min_offset = UINT32_MAX;
3064 int32_t ctd_shift = trak->cache->timestamp.ctd_shift;
3065 uint32_t j, k;
3066 lsmash_entry_t *stts_entry = stts->list->head;
3067 lsmash_entry_t *ctts_entry = ctts->list->head;
3068 j = k = 0;
3069 for( uint32_t i = 0; i < sample_count; i++ )
3071 if( !ctts_entry || !stts_entry )
3072 return -1;
3073 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
3074 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
3075 if( !stts_data || !ctts_data )
3076 return -1;
3077 uint64_t cts;
3078 if( ctd_shift )
3080 /* Anyway, add composition to decode timeline shift for calculating maximum and minimum CTS correctly. */
3081 int32_t sample_offset = (int32_t)ctts_data->sample_offset;
3082 cts = dts + sample_offset + ctd_shift;
3083 max_offset = LSMASH_MAX( (int32_t)max_offset, sample_offset );
3084 min_offset = LSMASH_MIN( (int32_t)min_offset, sample_offset );
3086 else
3088 cts = dts + ctts_data->sample_offset;
3089 max_offset = LSMASH_MAX( max_offset, ctts_data->sample_offset );
3090 min_offset = LSMASH_MIN( min_offset, ctts_data->sample_offset );
3092 min_cts = LSMASH_MIN( min_cts, cts );
3093 if( max_cts < cts )
3095 max2_cts = max_cts;
3096 max_cts = cts;
3098 else if( max2_cts < cts )
3099 max2_cts = cts;
3100 dts += stts_data->sample_delta;
3101 /* If finished sample_count of current entry, move to next. */
3102 if( ++j == ctts_data->sample_count )
3104 ctts_entry = ctts_entry->next;
3105 j = 0;
3107 if( ++k == stts_data->sample_count )
3109 stts_entry = stts_entry->next;
3110 k = 0;
3113 dts -= last_stts_data->sample_delta;
3114 if( root->fragment )
3115 /* Overall presentation is extended exceeding this initial movie.
3116 * So, any players shall display the movie exceeding the durations
3117 * indicated in Movie Header Box, Track Header Boxes and Media Header Boxes.
3118 * Samples up to the duration indicated in Movie Extends Header Box shall be displayed.
3119 * In the absence of Movie Extends Header Box, all samples shall be displayed. */
3120 mdhd->duration += dts + last_sample_delta;
3121 else
3123 if( !last_sample_delta )
3125 /* The spec allows an arbitrary value for the duration of the last sample. So, we pick last-1 sample's. */
3126 last_sample_delta = max_cts - max2_cts;
3128 mdhd->duration = max_cts - min_cts + last_sample_delta;
3129 /* To match dts and media duration, update stts and mdhd relatively. */
3130 if( mdhd->duration > dts )
3131 last_sample_delta = mdhd->duration - dts;
3132 else
3133 mdhd->duration = dts + last_sample_delta; /* media duration must not less than last dts. */
3135 if( isom_replace_last_sample_delta( stbl, last_sample_delta ) )
3136 return -1;
3137 /* Explicit composition information and timeline shifting */
3138 if( cslg || root->qt_compatible || root->max_isom_version >= 4 )
3140 if( ctd_shift )
3142 /* Remove composition to decode timeline shift. */
3143 max_cts -= ctd_shift;
3144 max2_cts -= ctd_shift;
3145 min_cts -= ctd_shift;
3147 int64_t composition_end_time = max_cts + (max_cts - max2_cts);
3148 if( !root->fragment
3149 && ((int32_t)min_offset <= INT32_MAX) && ((int32_t)max_offset <= INT32_MAX)
3150 && ((int64_t)min_cts <= INT32_MAX) && (composition_end_time <= INT32_MAX) )
3152 if( !cslg )
3154 if( isom_add_cslg( trak->mdia->minf->stbl ) )
3155 return -1;
3156 cslg = stbl->cslg;
3158 cslg->compositionToDTSShift = ctd_shift;
3159 cslg->leastDecodeToDisplayDelta = min_offset;
3160 cslg->greatestDecodeToDisplayDelta = max_offset;
3161 cslg->compositionStartTime = min_cts;
3162 cslg->compositionEndTime = composition_end_time;
3164 else
3166 if( cslg )
3167 free( cslg );
3168 stbl->cslg = NULL;
3172 if( mdhd->duration > UINT32_MAX )
3173 mdhd->version = 1;
3174 return 0;
3177 static int isom_update_mvhd_duration( isom_moov_t *moov )
3179 if( !moov || !moov->mvhd )
3180 return -1;
3181 isom_mvhd_t *mvhd = moov->mvhd;
3182 mvhd->duration = 0;
3183 for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
3185 /* We pick maximum track duration as movie duration. */
3186 isom_trak_entry_t *data = (isom_trak_entry_t *)entry->data;
3187 if( !data || !data->tkhd )
3188 return -1;
3189 mvhd->duration = entry != moov->trak_list->head ? LSMASH_MAX( mvhd->duration, data->tkhd->duration ) : data->tkhd->duration;
3191 if( mvhd->duration > UINT32_MAX )
3192 mvhd->version = 1;
3193 return 0;
3196 static int isom_update_tkhd_duration( isom_trak_entry_t *trak )
3198 if( !trak || !trak->tkhd || !trak->root || !trak->root->moov )
3199 return -1;
3200 lsmash_root_t *root = trak->root;
3201 isom_tkhd_t *tkhd = trak->tkhd;
3202 tkhd->duration = 0;
3203 if( root->fragment || !trak->edts || !trak->edts->elst )
3205 /* If this presentation might be extended or this track doesn't have edit list, calculate track duration from media duration. */
3206 if( !trak->mdia || !trak->mdia->mdhd || !root->moov->mvhd || !trak->mdia->mdhd->timescale )
3207 return -1;
3208 if( !trak->mdia->mdhd->duration && isom_update_mdhd_duration( trak, 0 ) )
3209 return -1;
3210 tkhd->duration = trak->mdia->mdhd->duration * ((double)root->moov->mvhd->timescale / trak->mdia->mdhd->timescale);
3212 else
3214 /* If the presentation won't be extended and this track has any edit, then track duration is just the sum of the segment_duartions. */
3215 for( lsmash_entry_t *entry = trak->edts->elst->list->head; entry; entry = entry->next )
3217 isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data;
3218 if( !data )
3219 return -1;
3220 tkhd->duration += data->segment_duration;
3223 if( tkhd->duration > UINT32_MAX )
3224 tkhd->version = 1;
3225 if( !root->fragment && !tkhd->duration )
3226 tkhd->duration = tkhd->version == 1 ? 0xffffffffffffffff : 0xffffffff;
3227 return isom_update_mvhd_duration( root->moov );
3230 int lsmash_update_track_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta )
3232 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
3233 if( !trak )
3234 return -1;
3235 if( isom_update_mdhd_duration( trak, last_sample_delta ) )
3236 return -1;
3237 /* If the presentation won't be extended and this track has any edit, we don't change or update duration in tkhd. */
3238 return (!root->fragment && trak->edts && trak->edts->elst)
3239 ? isom_update_mvhd_duration( root->moov ) /* Only update movie duration. */
3240 : isom_update_tkhd_duration( trak ); /* Also update movie duration internally. */
3243 static inline int isom_increment_sample_number_in_entry( uint32_t *sample_number_in_entry, uint32_t sample_count_in_entry, lsmash_entry_t **entry )
3245 if( *sample_number_in_entry != sample_count_in_entry )
3247 *sample_number_in_entry += 1;
3248 return 0;
3250 /* Precede the next entry. */
3251 *sample_number_in_entry = 1;
3252 if( *entry )
3254 *entry = (*entry)->next;
3255 if( *entry && !(*entry)->data )
3256 return -1;
3258 return 0;
3261 static int isom_calculate_bitrate_description( isom_mdia_t *mdia, uint32_t *bufferSizeDB, uint32_t *maxBitrate, uint32_t *avgBitrate, uint32_t sample_description_index )
3263 isom_stsz_t *stsz = mdia->minf->stbl->stsz;
3264 lsmash_entry_t *stsz_entry = stsz->list ? stsz->list->head : NULL;
3265 lsmash_entry_t *stts_entry = mdia->minf->stbl->stts->list->head;
3266 lsmash_entry_t *stsc_entry = NULL;
3267 lsmash_entry_t *next_stsc_entry = mdia->minf->stbl->stsc->list->head;
3268 isom_stts_entry_t *stts_data = NULL;
3269 isom_stsc_entry_t *stsc_data = NULL;
3270 if( next_stsc_entry && !next_stsc_entry->data )
3271 return -1;
3272 uint32_t rate = 0;
3273 uint64_t dts = 0;
3274 uint32_t time_wnd = 0;
3275 uint32_t timescale = mdia->mdhd->timescale;
3276 uint32_t chunk_number = 0;
3277 uint32_t sample_number_in_stts = 1;
3278 uint32_t sample_number_in_chunk = 1;
3279 *bufferSizeDB = 0;
3280 *maxBitrate = 0;
3281 *avgBitrate = 0;
3282 while( stts_entry )
3284 if( !stsc_data || sample_number_in_chunk == stsc_data->samples_per_chunk )
3286 /* Move the next chunk. */
3287 sample_number_in_chunk = 1;
3288 ++chunk_number;
3289 /* Check if the next entry is broken. */
3290 while( next_stsc_entry && ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk < chunk_number )
3292 /* Just skip broken next entry. */
3293 next_stsc_entry = next_stsc_entry->next;
3294 if( next_stsc_entry && !next_stsc_entry->data )
3295 return -1;
3297 /* Check if the next chunk belongs to the next sequence of chunks. */
3298 if( next_stsc_entry && ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk == chunk_number )
3300 stsc_entry = next_stsc_entry;
3301 next_stsc_entry = next_stsc_entry->next;
3302 if( next_stsc_entry && !next_stsc_entry->data )
3303 return -1;
3304 stsc_data = (isom_stsc_entry_t *)stsc_entry->data;
3305 /* Check if the next contiguous chunks belong to given sample description. */
3306 if( stsc_data->sample_description_index != sample_description_index )
3308 /* Skip chunks which don't belong to given sample description. */
3309 uint32_t number_of_skips = 0;
3310 uint32_t first_chunk = stsc_data->first_chunk;
3311 uint32_t samples_per_chunk = stsc_data->samples_per_chunk;
3312 while( next_stsc_entry )
3314 if( ((isom_stsc_entry_t *)next_stsc_entry->data)->sample_description_index != sample_description_index )
3316 stsc_data = (isom_stsc_entry_t *)next_stsc_entry->data;
3317 number_of_skips += (stsc_data->first_chunk - first_chunk) * samples_per_chunk;
3318 first_chunk = stsc_data->first_chunk;
3319 samples_per_chunk = stsc_data->samples_per_chunk;
3321 else if( ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk <= first_chunk )
3322 ; /* broken entry */
3323 else
3324 break;
3325 /* Just skip the next entry. */
3326 next_stsc_entry = next_stsc_entry->next;
3327 if( next_stsc_entry && !next_stsc_entry->data )
3328 return -1;
3330 if( !next_stsc_entry )
3331 break; /* There is no more chunks which don't belong to given sample description. */
3332 number_of_skips += (((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk - first_chunk) * samples_per_chunk;
3333 for( uint32_t i = 0; i < number_of_skips; i++ )
3335 if( stsz->list )
3337 if( !stsz_entry )
3338 break;
3339 stsz_entry = stsz_entry->next;
3341 if( !stts_entry )
3342 break;
3343 if( isom_increment_sample_number_in_entry( &sample_number_in_stts, ((isom_stts_entry_t *)stts_entry->data)->sample_count, &stts_entry ) )
3344 return -1;
3346 if( (stsz->list && !stsz_entry) || !stts_entry )
3347 break;
3348 chunk_number = stsc_data->first_chunk;
3352 else
3353 ++sample_number_in_chunk;
3354 /* Get current sample's size. */
3355 uint32_t size;
3356 if( stsz->list )
3358 if( !stsz_entry )
3359 break;
3360 isom_stsz_entry_t *stsz_data = (isom_stsz_entry_t *)stsz_entry->data;
3361 if( !stsz_data )
3362 return -1;
3363 size = stsz_data->entry_size;
3364 stsz_entry = stsz_entry->next;
3366 else
3367 size = stsz->sample_size;
3368 /* Get current sample's DTS. */
3369 if( stts_data )
3370 dts += stts_data->sample_delta;
3371 stts_data = (isom_stts_entry_t *)stts_entry->data;
3372 if( !stts_data )
3373 return -1;
3374 isom_increment_sample_number_in_entry( &sample_number_in_stts, stts_data->sample_count, &stts_entry );
3375 /* Calculate bitrate description. */
3376 if( *bufferSizeDB < size )
3377 *bufferSizeDB = size;
3378 *avgBitrate += size;
3379 rate += size;
3380 if( dts > time_wnd + timescale )
3382 if( rate > *maxBitrate )
3383 *maxBitrate = rate;
3384 time_wnd = dts;
3385 rate = 0;
3388 double duration = (double)mdia->mdhd->duration / timescale;
3389 *avgBitrate = (uint32_t)(*avgBitrate / duration);
3390 if( !*maxBitrate )
3391 *maxBitrate = *avgBitrate;
3392 /* Convert to bits per second. */
3393 *maxBitrate *= 8;
3394 *avgBitrate *= 8;
3395 return 0;
3398 static int isom_update_bitrate_description( isom_mdia_t *mdia )
3400 if( !mdia || !mdia->mdhd || !mdia->minf || !mdia->minf->stbl )
3401 return -1;
3402 isom_stbl_t *stbl = mdia->minf->stbl;
3403 if( !stbl->stsd || !stbl->stsd->list
3404 || !stbl->stsz
3405 || !stbl->stsc || !stbl->stsc->list
3406 || !stbl->stts || !stbl->stts->list )
3407 return -1;
3408 uint32_t sample_description_index = 0;
3409 for( lsmash_entry_t *entry = stbl->stsd->list->head; entry; entry = entry->next )
3411 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)entry->data;
3412 if( !sample_entry )
3413 return -1;
3414 ++sample_description_index;
3415 uint32_t bufferSizeDB;
3416 uint32_t maxBitrate;
3417 uint32_t avgBitrate;
3418 /* set bitrate info */
3419 lsmash_codec_type_t sample_type = (lsmash_codec_type_t)sample_entry->type;
3420 if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO )
3421 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC2_VIDEO )
3422 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVCP_VIDEO ) )
3424 isom_visual_entry_t *stsd_data = (isom_visual_entry_t *)sample_entry;
3425 if( !stsd_data )
3426 return -1;
3427 isom_btrt_t *btrt = (isom_btrt_t *)isom_get_extension_box( &stsd_data->extensions, ISOM_BOX_TYPE_BTRT );
3428 if( btrt )
3430 if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) )
3431 return -1;
3432 btrt->bufferSizeDB = bufferSizeDB;
3433 btrt->maxBitrate = maxBitrate;
3434 btrt->avgBitrate = avgBitrate;
3437 else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4V_VIDEO ) )
3439 isom_visual_entry_t *stsd_data = (isom_visual_entry_t *)sample_entry;
3440 if( !stsd_data )
3441 return -1;
3442 isom_esds_t *esds = (isom_esds_t *)isom_get_extension_box( &stsd_data->extensions, ISOM_BOX_TYPE_ESDS );
3443 if( !esds || !esds->ES )
3444 return -1;
3445 if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) )
3446 return -1;
3447 /* FIXME: avgBitrate is 0 only if VBR in proper. */
3448 if( mp4sys_update_DecoderConfigDescriptor( esds->ES, bufferSizeDB, maxBitrate, 0 ) )
3449 return -1;
3451 else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) )
3453 isom_audio_entry_t *stsd_data = (isom_audio_entry_t *)sample_entry;
3454 if( !stsd_data )
3455 return -1;
3456 isom_esds_t *esds = NULL;
3457 if( ((isom_audio_entry_t *)sample_entry)->version )
3459 /* MPEG-4 Audio in QTFF */
3460 isom_wave_t *wave = (isom_wave_t *)isom_get_extension_box( &stsd_data->extensions, QT_BOX_TYPE_WAVE );
3461 if( !wave )
3462 return -1;
3463 esds = (isom_esds_t *)isom_get_extension_box( &wave->extensions, ISOM_BOX_TYPE_ESDS );
3465 else
3466 esds = (isom_esds_t *)isom_get_extension_box( &stsd_data->extensions, ISOM_BOX_TYPE_ESDS );
3467 if( !esds || !esds->ES )
3468 return -1;
3469 if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) )
3470 return -1;
3471 /* FIXME: avgBitrate is 0 only if VBR in proper. */
3472 if( mp4sys_update_DecoderConfigDescriptor( esds->ES, bufferSizeDB, maxBitrate, 0 ) )
3473 return -1;
3475 else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_ALAC_AUDIO )
3476 || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ALAC_AUDIO ) )
3478 isom_audio_entry_t *alac = (isom_audio_entry_t *)sample_entry;
3479 if( !alac )
3480 return -1;
3481 uint8_t *exdata = NULL;
3482 uint32_t exdata_size = 0;
3483 isom_extension_box_t *alac_ext = isom_get_sample_description_extension( &alac->extensions, QT_BOX_TYPE_WAVE );
3484 if( alac_ext )
3486 /* Apple Lossless Audio inside QuickTime file format
3487 * Though average bitrate field we found is always set to 0 apparently,
3488 * we set up maxFrameBytes and avgBitRate fields. */
3489 if( alac_ext->format == EXTENSION_FORMAT_BINARY )
3490 exdata = isom_get_child_box_position( alac_ext->form.binary, alac_ext->size, QT_BOX_TYPE_ALAC, &exdata_size );
3491 else
3493 isom_wave_t *wave = (isom_wave_t *)alac_ext->form.box;
3494 isom_extension_box_t *wave_ext = isom_get_sample_description_extension( &wave->extensions, QT_BOX_TYPE_ALAC );
3495 if( !wave_ext || wave_ext->format != EXTENSION_FORMAT_BINARY )
3496 return -1;
3497 exdata = wave_ext->form.binary;
3498 exdata_size = wave_ext->size;
3501 else
3503 /* Apple Lossless Audio inside ISO Base Media file format */
3504 isom_extension_box_t *ext = isom_get_sample_description_extension( &alac->extensions, ISOM_BOX_TYPE_ALAC );
3505 if( !ext || ext->format != EXTENSION_FORMAT_BINARY )
3506 return -1;
3507 exdata = ext->form.binary;
3508 exdata_size = ext->size;
3510 if( !exdata || exdata_size < 36 )
3511 return -1;
3512 if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) )
3513 return -1;
3514 exdata += 24;
3515 /* maxFrameBytes */
3516 exdata[0] = (bufferSizeDB >> 24) & 0xff;
3517 exdata[1] = (bufferSizeDB >> 16) & 0xff;
3518 exdata[2] = (bufferSizeDB >> 8) & 0xff;
3519 exdata[3] = bufferSizeDB & 0xff;
3520 /* avgBitRate */
3521 exdata[4] = (avgBitrate >> 24) & 0xff;
3522 exdata[5] = (avgBitrate >> 16) & 0xff;
3523 exdata[6] = (avgBitrate >> 8) & 0xff;
3524 exdata[7] = avgBitrate & 0xff;
3526 else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSC_AUDIO )
3527 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSE_AUDIO )
3528 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSH_AUDIO )
3529 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSL_AUDIO ) )
3531 isom_audio_entry_t *dts_audio = (isom_audio_entry_t *)sample_entry;
3532 if( !dts_audio )
3533 return -1;
3534 isom_extension_box_t *ext = isom_get_sample_description_extension( &dts_audio->extensions, ISOM_BOX_TYPE_DDTS );
3535 if( !(ext && ext->format == EXTENSION_FORMAT_BINARY && ext->form.binary && ext->size >= 28) )
3536 return -1;
3537 if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) )
3538 return -1;
3539 if( !stbl->stsz->list )
3540 maxBitrate = avgBitrate;
3541 uint8_t *exdata = ext->form.binary + 12;
3542 exdata[0] = (maxBitrate >> 24) & 0xff;
3543 exdata[1] = (maxBitrate >> 16) & 0xff;
3544 exdata[2] = (maxBitrate >> 8) & 0xff;
3545 exdata[3] = maxBitrate & 0xff;
3546 exdata[4] = (avgBitrate >> 24) & 0xff;
3547 exdata[5] = (avgBitrate >> 16) & 0xff;
3548 exdata[6] = (avgBitrate >> 8) & 0xff;
3549 exdata[7] = avgBitrate & 0xff;
3551 else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_EC_3_AUDIO ) )
3553 isom_audio_entry_t *eac3 = (isom_audio_entry_t *)sample_entry;
3554 if( !eac3 )
3555 return -1;
3556 isom_extension_box_t *ext = isom_get_sample_description_extension( &eac3->extensions, ISOM_BOX_TYPE_DEC3 );
3557 if( !(ext && ext->format == EXTENSION_FORMAT_BINARY && ext->form.binary && ext->size >= 10) )
3558 return -1;
3559 uint16_t bitrate;
3560 if( stbl->stsz->list )
3562 if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) )
3563 return -1;
3564 bitrate = maxBitrate / 1000; /* Use maximum bitrate if VBR. */
3566 else
3567 bitrate = stbl->stsz->sample_size * (eac3->samplerate >> 16) / 192000; /* 192000 == 1536 * 1000 / 8 */
3568 uint8_t *exdata = ext->form.binary + 8;
3569 exdata[0] = (bitrate >> 5) & 0xff;
3570 exdata[1] = (bitrate & 0x1f) << 3;
3573 return sample_description_index ? 0 : -1;
3576 static int isom_check_mandatory_boxes( lsmash_root_t *root )
3578 if( !root )
3579 return -1;
3580 if( !root->moov || !root->moov->mvhd )
3581 return -1;
3582 if( !root->moov->trak_list )
3583 return -1;
3584 /* A movie requires at least one track. */
3585 if( !root->moov->trak_list->head )
3586 return -1;
3587 for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
3589 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
3590 if( !trak
3591 || !trak->tkhd
3592 || !trak->mdia
3593 || !trak->mdia->mdhd
3594 || !trak->mdia->hdlr
3595 || !trak->mdia->minf
3596 || !trak->mdia->minf->dinf
3597 || !trak->mdia->minf->dinf->dref
3598 || !trak->mdia->minf->stbl
3599 || !trak->mdia->minf->stbl->stsd
3600 || !trak->mdia->minf->stbl->stsz
3601 || !trak->mdia->minf->stbl->stts
3602 || !trak->mdia->minf->stbl->stsc
3603 || !trak->mdia->minf->stbl->stco )
3604 return -1;
3605 if( root->qt_compatible && !trak->mdia->minf->hdlr )
3606 return -1;
3607 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3608 if( !stbl->stsd->list || !stbl->stsd->list->head )
3609 return -1;
3610 if( !root->fragment
3611 && (!stbl->stsd->list || !stbl->stsd->list->head
3612 || !stbl->stts->list || !stbl->stts->list->head
3613 || !stbl->stsc->list || !stbl->stsc->list->head
3614 || !stbl->stco->list || !stbl->stco->list->head) )
3615 return -1;
3617 if( !root->fragment )
3618 return 0;
3619 if( !root->moov->mvex || !root->moov->mvex->trex_list )
3620 return -1;
3621 for( lsmash_entry_t *entry = root->moov->mvex->trex_list->head; entry; entry = entry->next )
3622 if( !entry->data ) /* trex */
3623 return -1;
3624 return 0;
3627 static inline uint64_t isom_get_current_mp4time( void )
3629 return (uint64_t)time( NULL ) + ISOM_MAC_EPOCH_OFFSET;
3632 static int isom_set_media_creation_time( isom_trak_entry_t *trak, uint64_t current_mp4time )
3634 if( !trak->mdia || !trak->mdia->mdhd )
3635 return -1;
3636 isom_mdhd_t *mdhd = trak->mdia->mdhd;
3637 if( !mdhd->creation_time )
3638 mdhd->creation_time = mdhd->modification_time = current_mp4time;
3639 return 0;
3642 static int isom_set_track_creation_time( isom_trak_entry_t *trak, uint64_t current_mp4time )
3644 if( !trak || !trak->tkhd )
3645 return -1;
3646 isom_tkhd_t *tkhd = trak->tkhd;
3647 if( !tkhd->creation_time )
3648 tkhd->creation_time = tkhd->modification_time = current_mp4time;
3649 if( isom_set_media_creation_time( trak, current_mp4time ) )
3650 return -1;
3651 return 0;
3654 static int isom_set_movie_creation_time( lsmash_root_t *root )
3656 if( !root || !root->moov || !root->moov->mvhd || !root->moov->trak_list )
3657 return -1;
3658 uint64_t current_mp4time = isom_get_current_mp4time();
3659 for( uint32_t i = 1; i <= root->moov->trak_list->entry_count; i++ )
3660 if( isom_set_track_creation_time( isom_get_trak( root, i ), current_mp4time ) )
3661 return -1;
3662 isom_mvhd_t *mvhd = root->moov->mvhd;
3663 if( !mvhd->creation_time )
3664 mvhd->creation_time = mvhd->modification_time = current_mp4time;
3665 return 0;
3668 #define CHECK_LARGESIZE( x ) \
3669 (x->size) += isom_update_extension_boxes( x ); \
3670 if( (x->size) > UINT32_MAX ) (x->size) += 8
3672 static uint64_t isom_update_extension_boxes( void *box );
3674 static uint64_t isom_update_unknown_box_size( isom_unknown_box_t *unknown_box )
3676 if( !unknown_box )
3677 return 0;
3678 unknown_box->size = ISOM_BASEBOX_COMMON_SIZE + unknown_box->unknown_size;
3679 CHECK_LARGESIZE( unknown_box );
3680 return unknown_box->size;
3683 static uint64_t isom_update_mvhd_size( isom_mvhd_t *mvhd )
3685 if( !mvhd )
3686 return 0;
3687 mvhd->version = 0;
3688 if( mvhd->creation_time > UINT32_MAX || mvhd->modification_time > UINT32_MAX || mvhd->duration > UINT32_MAX )
3689 mvhd->version = 1;
3690 mvhd->size = ISOM_FULLBOX_COMMON_SIZE + 96 + (uint64_t)mvhd->version * 12;
3691 CHECK_LARGESIZE( mvhd );
3692 return mvhd->size;
3695 static uint64_t isom_update_iods_size( isom_iods_t *iods )
3697 if( !iods || !iods->OD )
3698 return 0;
3699 iods->size = ISOM_FULLBOX_COMMON_SIZE + mp4sys_update_ObjectDescriptor_size( iods->OD );
3700 CHECK_LARGESIZE( iods );
3701 return iods->size;
3704 static uint64_t isom_update_ctab_size( isom_ctab_t *ctab )
3706 if( !ctab )
3707 return 0;
3708 ctab->size = ISOM_BASEBOX_COMMON_SIZE + (uint64_t)(1 + ctab->color_table.size + !!ctab->color_table.array) * 8;
3709 CHECK_LARGESIZE( ctab );
3710 return ctab->size;
3713 static uint64_t isom_update_tkhd_size( isom_tkhd_t *tkhd )
3715 if( !tkhd )
3716 return 0;
3717 tkhd->version = 0;
3718 if( tkhd->creation_time > UINT32_MAX || tkhd->modification_time > UINT32_MAX || tkhd->duration > UINT32_MAX )
3719 tkhd->version = 1;
3720 tkhd->size = ISOM_FULLBOX_COMMON_SIZE + 80 + (uint64_t)tkhd->version * 12;
3721 CHECK_LARGESIZE( tkhd );
3722 return tkhd->size;
3725 static uint64_t isom_update_clef_size( isom_clef_t *clef )
3727 if( !clef )
3728 return 0;
3729 clef->size = ISOM_FULLBOX_COMMON_SIZE + 8;
3730 CHECK_LARGESIZE( clef );
3731 return clef->size;
3734 static uint64_t isom_update_prof_size( isom_prof_t *prof )
3736 if( !prof )
3737 return 0;
3738 prof->size = ISOM_FULLBOX_COMMON_SIZE + 8;
3739 CHECK_LARGESIZE( prof );
3740 return prof->size;
3743 static uint64_t isom_update_enof_size( isom_enof_t *enof )
3745 if( !enof )
3746 return 0;
3747 enof->size = ISOM_FULLBOX_COMMON_SIZE + 8;
3748 CHECK_LARGESIZE( enof );
3749 return enof->size;
3752 static uint64_t isom_update_tapt_size( isom_tapt_t *tapt )
3754 if( !tapt )
3755 return 0;
3756 tapt->size = ISOM_BASEBOX_COMMON_SIZE
3757 + isom_update_clef_size( tapt->clef )
3758 + isom_update_prof_size( tapt->prof )
3759 + isom_update_enof_size( tapt->enof );
3760 CHECK_LARGESIZE( tapt );
3761 return tapt->size;
3764 static uint64_t isom_update_elst_size( isom_elst_t *elst )
3766 if( !elst || !elst->list )
3767 return 0;
3768 uint32_t i = 0;
3769 elst->version = 0;
3770 for( lsmash_entry_t *entry = elst->list->head; entry; entry = entry->next, i++ )
3772 isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data;
3773 if( data->segment_duration > UINT32_MAX || data->media_time > INT32_MAX || data->media_time < INT32_MIN )
3774 elst->version = 1;
3776 elst->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)i * ( elst->version ? 20 : 12 );
3777 CHECK_LARGESIZE( elst );
3778 return elst->size;
3781 static uint64_t isom_update_edts_size( isom_edts_t *edts )
3783 if( !edts )
3784 return 0;
3785 edts->size = ISOM_BASEBOX_COMMON_SIZE + isom_update_elst_size( edts->elst );
3786 CHECK_LARGESIZE( edts );
3787 return edts->size;
3790 static uint64_t isom_update_tref_size( isom_tref_t *tref )
3792 if( !tref )
3793 return 0;
3794 tref->size = ISOM_BASEBOX_COMMON_SIZE;
3795 if( tref->ref_list )
3796 for( lsmash_entry_t *entry = tref->ref_list->head; entry; entry = entry->next )
3798 isom_tref_type_t *ref = (isom_tref_type_t *)entry->data;
3799 ref->size = ISOM_BASEBOX_COMMON_SIZE + (uint64_t)ref->ref_count * 4;
3800 CHECK_LARGESIZE( ref );
3801 tref->size += ref->size;
3803 CHECK_LARGESIZE( tref );
3804 return tref->size;
3807 static uint64_t isom_update_mdhd_size( isom_mdhd_t *mdhd )
3809 if( !mdhd )
3810 return 0;
3811 mdhd->version = 0;
3812 if( mdhd->creation_time > UINT32_MAX || mdhd->modification_time > UINT32_MAX || mdhd->duration > UINT32_MAX )
3813 mdhd->version = 1;
3814 mdhd->size = ISOM_FULLBOX_COMMON_SIZE + 20 + (uint64_t)mdhd->version * 12;
3815 CHECK_LARGESIZE( mdhd );
3816 return mdhd->size;
3819 static uint64_t isom_update_hdlr_size( isom_hdlr_t *hdlr )
3821 if( !hdlr )
3822 return 0;
3823 hdlr->size = ISOM_FULLBOX_COMMON_SIZE + 20 + (uint64_t)hdlr->componentName_length;
3824 CHECK_LARGESIZE( hdlr );
3825 return hdlr->size;
3828 static uint64_t isom_update_dref_entry_size( isom_dref_entry_t *urln )
3830 if( !urln )
3831 return 0;
3832 urln->size = ISOM_FULLBOX_COMMON_SIZE + (uint64_t)urln->name_length + urln->location_length;
3833 CHECK_LARGESIZE( urln );
3834 return urln->size;
3837 static uint64_t isom_update_dref_size( isom_dref_t *dref )
3839 if( !dref || !dref->list )
3840 return 0;
3841 dref->size = ISOM_LIST_FULLBOX_COMMON_SIZE;
3842 if( dref->list )
3843 for( lsmash_entry_t *entry = dref->list->head; entry; entry = entry->next )
3845 isom_dref_entry_t *data = (isom_dref_entry_t *)entry->data;
3846 dref->size += isom_update_dref_entry_size( data );
3848 CHECK_LARGESIZE( dref );
3849 return dref->size;
3852 static uint64_t isom_update_dinf_size( isom_dinf_t *dinf )
3854 if( !dinf )
3855 return 0;
3856 dinf->size = ISOM_BASEBOX_COMMON_SIZE + isom_update_dref_size( dinf->dref );
3857 CHECK_LARGESIZE( dinf );
3858 return dinf->size;
3861 static uint64_t isom_update_vmhd_size( isom_vmhd_t *vmhd )
3863 if( !vmhd )
3864 return 0;
3865 vmhd->size = ISOM_FULLBOX_COMMON_SIZE + 8;
3866 CHECK_LARGESIZE( vmhd );
3867 return vmhd->size;
3870 static uint64_t isom_update_smhd_size( isom_smhd_t *smhd )
3872 if( !smhd )
3873 return 0;
3874 smhd->size = ISOM_FULLBOX_COMMON_SIZE + 4;
3875 CHECK_LARGESIZE( smhd );
3876 return smhd->size;
3879 static uint64_t isom_update_hmhd_size( isom_hmhd_t *hmhd )
3881 if( !hmhd )
3882 return 0;
3883 hmhd->size = ISOM_FULLBOX_COMMON_SIZE + 16;
3884 CHECK_LARGESIZE( hmhd );
3885 return hmhd->size;
3888 static uint64_t isom_update_nmhd_size( isom_nmhd_t *nmhd )
3890 if( !nmhd )
3891 return 0;
3892 nmhd->size = ISOM_FULLBOX_COMMON_SIZE;
3893 CHECK_LARGESIZE( nmhd );
3894 return nmhd->size;
3897 static uint64_t isom_update_gmin_size( isom_gmin_t *gmin )
3899 if( !gmin )
3900 return 0;
3901 gmin->size = ISOM_FULLBOX_COMMON_SIZE + 12;
3902 CHECK_LARGESIZE( gmin );
3903 return gmin->size;
3906 static uint64_t isom_update_text_size( isom_text_t *text )
3908 if( !text )
3909 return 0;
3910 text->size = ISOM_BASEBOX_COMMON_SIZE + 36;
3911 CHECK_LARGESIZE( text );
3912 return text->size;
3915 static uint64_t isom_update_gmhd_size( isom_gmhd_t *gmhd )
3917 if( !gmhd )
3918 return 0;
3919 gmhd->size = ISOM_BASEBOX_COMMON_SIZE
3920 + isom_update_gmin_size( gmhd->gmin )
3921 + isom_update_text_size( gmhd->text );
3922 CHECK_LARGESIZE( gmhd);
3923 return gmhd->size;
3926 static uint64_t isom_update_pasp_size( isom_pasp_t *pasp )
3928 if( !pasp )
3929 return 0;
3930 pasp->size = ISOM_BASEBOX_COMMON_SIZE + 8;
3931 CHECK_LARGESIZE( pasp );
3932 return pasp->size;
3935 static uint64_t isom_update_clap_size( isom_clap_t *clap )
3937 if( !clap )
3938 return 0;
3939 clap->size = ISOM_BASEBOX_COMMON_SIZE + 32;
3940 CHECK_LARGESIZE( clap );
3941 return clap->size;
3944 static uint64_t isom_update_glbl_size( isom_glbl_t *glbl )
3946 if( !glbl )
3947 return 0;
3948 glbl->size = ISOM_BASEBOX_COMMON_SIZE + (uint64_t)glbl->header_size;
3949 CHECK_LARGESIZE( glbl );
3950 return glbl->size;
3953 static uint64_t isom_update_colr_size( isom_colr_t *colr )
3955 if( !colr
3956 || (colr->color_parameter_type != ISOM_COLOR_PARAMETER_TYPE_NCLX
3957 && colr->color_parameter_type != QT_COLOR_PARAMETER_TYPE_NCLC) )
3958 return 0;
3959 colr->size = ISOM_BASEBOX_COMMON_SIZE + 10 + (colr->color_parameter_type == ISOM_COLOR_PARAMETER_TYPE_NCLX);
3960 CHECK_LARGESIZE( colr );
3961 return colr->size;
3964 static uint64_t isom_update_gama_size( isom_gama_t *gama )
3966 if( !gama || !gama->parent )
3967 return 0;
3968 /* Note: 'gama' box is superseded by 'colr' box.
3969 * Therefore, writers of QTFF should never write both 'colr' and 'gama' box into an Image Description. */
3970 if( isom_get_extension_box( &((isom_visual_entry_t *)gama->parent)->extensions, QT_BOX_TYPE_COLR ) )
3971 return 0;
3972 gama->size = ISOM_BASEBOX_COMMON_SIZE + 4;
3973 CHECK_LARGESIZE( gama );
3974 return gama->size;
3977 static uint64_t isom_update_fiel_size( isom_fiel_t *fiel )
3979 if( !fiel )
3980 return 0;
3981 fiel->size = ISOM_BASEBOX_COMMON_SIZE + 2;
3982 CHECK_LARGESIZE( fiel );
3983 return fiel->size;
3986 static uint64_t isom_update_cspc_size( isom_cspc_t *cspc )
3988 if( !cspc )
3989 return 0;
3990 cspc->size = ISOM_BASEBOX_COMMON_SIZE + 4;
3991 CHECK_LARGESIZE( cspc );
3992 return cspc->size;
3995 static uint64_t isom_update_sgbt_size( isom_sgbt_t *sgbt )
3997 if( !sgbt )
3998 return 0;
3999 sgbt->size = ISOM_BASEBOX_COMMON_SIZE + 1;
4000 CHECK_LARGESIZE( sgbt );
4001 return sgbt->size;
4004 static uint64_t isom_update_stsl_size( isom_stsl_t *stsl )
4006 if( !stsl )
4007 return 0;
4008 stsl->size = ISOM_FULLBOX_COMMON_SIZE + 6;
4009 CHECK_LARGESIZE( stsl );
4010 return stsl->size;
4013 static uint64_t isom_update_esds_size( isom_esds_t *esds )
4015 if( !esds )
4016 return 0;
4017 esds->size = ISOM_FULLBOX_COMMON_SIZE + mp4sys_update_ES_Descriptor_size( esds->ES );
4018 CHECK_LARGESIZE( esds );
4019 return esds->size;
4022 static uint64_t isom_update_btrt_size( isom_btrt_t *btrt )
4024 if( !btrt )
4025 return 0;
4026 btrt->size = ISOM_BASEBOX_COMMON_SIZE + 12;
4027 CHECK_LARGESIZE( btrt );
4028 return btrt->size;
4031 static uint64_t isom_update_visual_entry_size( isom_sample_entry_t *description )
4033 if( !description )
4034 return 0;
4035 isom_visual_entry_t *visual = (isom_visual_entry_t *)description;
4036 visual->size = ISOM_BASEBOX_COMMON_SIZE + 78;
4037 if( visual->color_table_ID == 0 )
4038 visual->size += (uint64_t)(1 + visual->color_table.size + !!visual->color_table.array) * 8;
4039 CHECK_LARGESIZE( visual );
4040 return visual->size;
4043 #if 0
4044 static uint64_t isom_update_mp4s_entry_size( isom_sample_entry_t *description )
4046 if( !description || !lsmash_check_box_type_identical( description->type, ISOM_CODEC_TYPE_MP4S_SYSTEM ) )
4047 return 0;
4048 isom_mp4s_entry_t *mp4s = (isom_mp4s_entry_t *)description;
4049 mp4s->size = ISOM_BASEBOX_COMMON_SIZE + 8 + isom_update_esds_size( mp4s->esds );
4050 CHECK_LARGESIZE( mp4s );
4051 return mp4s->size;
4053 #endif
4055 static uint64_t isom_update_frma_size( isom_frma_t *frma )
4057 if( !frma )
4058 return 0;
4059 frma->size = ISOM_BASEBOX_COMMON_SIZE + 4;
4060 CHECK_LARGESIZE( frma );
4061 return frma->size;
4064 static uint64_t isom_update_enda_size( isom_enda_t *enda )
4066 if( !enda )
4067 return 0;
4068 enda->size = ISOM_BASEBOX_COMMON_SIZE + 2;
4069 CHECK_LARGESIZE( enda );
4070 return enda->size;
4073 static uint64_t isom_update_mp4a_size( isom_mp4a_t *mp4a )
4075 if( !mp4a )
4076 return 0;
4077 mp4a->size = ISOM_BASEBOX_COMMON_SIZE + 4;
4078 CHECK_LARGESIZE( mp4a );
4079 return mp4a->size;
4082 static uint64_t isom_update_terminator_size( isom_terminator_t *terminator )
4084 if( !terminator )
4085 return 0;
4086 terminator->size = ISOM_BASEBOX_COMMON_SIZE;
4087 CHECK_LARGESIZE( terminator );
4088 return terminator->size;
4091 static uint64_t isom_update_wave_size( isom_wave_t *wave )
4093 if( !wave )
4094 return 0;
4095 wave->size = ISOM_BASEBOX_COMMON_SIZE
4096 + isom_update_frma_size( wave->frma )
4097 + isom_update_enda_size( wave->enda )
4098 + isom_update_mp4a_size( wave->mp4a )
4099 + isom_update_terminator_size( wave->terminator );
4100 CHECK_LARGESIZE( wave );
4101 return wave->size;
4104 static uint64_t isom_update_chan_size( isom_chan_t *chan )
4106 if( !chan )
4107 return 0;
4108 chan->size = ISOM_FULLBOX_COMMON_SIZE + 12 + 20 * (uint64_t)chan->numberChannelDescriptions;
4109 CHECK_LARGESIZE( chan );
4110 return chan->size;
4113 static uint64_t isom_update_audio_entry_size( isom_sample_entry_t *description )
4115 if( !description )
4116 return 0;
4117 isom_audio_entry_t *audio = (isom_audio_entry_t *)description;
4118 audio->size = ISOM_BASEBOX_COMMON_SIZE + 28;
4119 if( audio->version == 1 )
4120 audio->size += 16;
4121 else if( audio->version == 2 )
4122 audio->size += 36;
4123 CHECK_LARGESIZE( audio );
4124 return audio->size;
4127 static uint64_t isom_update_text_entry_size( isom_sample_entry_t *description )
4129 if( !description )
4130 return 0;
4131 isom_text_entry_t *text = (isom_text_entry_t *)description;
4132 text->size = ISOM_BASEBOX_COMMON_SIZE + 51 + (uint64_t)text->font_name_length;
4133 CHECK_LARGESIZE( text );
4134 return text->size;
4137 static uint64_t isom_update_ftab_size( isom_ftab_t *ftab )
4139 if( !ftab || !ftab->list )
4140 return 0;
4141 ftab->size = ISOM_BASEBOX_COMMON_SIZE + 2;
4142 for( lsmash_entry_t *entry = ftab->list->head; entry; entry = entry->next )
4144 isom_font_record_t *data = (isom_font_record_t *)entry->data;
4145 ftab->size += 3 + data->font_name_length;
4147 CHECK_LARGESIZE( ftab );
4148 return ftab->size;
4151 static uint64_t isom_update_tx3g_entry_size( isom_sample_entry_t *description )
4153 if( !description )
4154 return 0;
4155 isom_tx3g_entry_t *tx3g = (isom_tx3g_entry_t *)description;
4156 tx3g->size = ISOM_BASEBOX_COMMON_SIZE + 38 + isom_update_ftab_size( tx3g->ftab );
4157 CHECK_LARGESIZE( tx3g );
4158 return tx3g->size;
4161 static uint64_t isom_update_stsd_size( isom_stsd_t *stsd )
4163 if( !stsd || !stsd->list )
4164 return 0;
4165 uint64_t size = ISOM_LIST_FULLBOX_COMMON_SIZE;
4166 for( lsmash_entry_t *entry = stsd->list->head; entry; entry = entry->next )
4168 isom_sample_entry_t *data = (isom_sample_entry_t *)entry->data;
4169 lsmash_codec_type_t sample_type = (lsmash_codec_type_t)data->type;
4170 if( lsmash_check_codec_type_identical( sample_type, LSMASH_CODEC_TYPE_RAW ) )
4172 if( data->manager & LSMASH_VIDEO_DESCRIPTION )
4173 size += isom_update_visual_entry_size( data );
4174 else if( data->manager & LSMASH_AUDIO_DESCRIPTION )
4175 size += isom_update_audio_entry_size( data );
4176 continue;
4178 static struct description_update_size_table_tag
4180 lsmash_codec_type_t type;
4181 uint64_t (*func)( isom_sample_entry_t * );
4182 } description_update_size_table[128] = { { LSMASH_CODEC_TYPE_INITIALIZER, NULL } };
4183 if( !description_update_size_table[0].func )
4185 /* Initialize the table. */
4186 int i = 0;
4187 #define ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( type, func ) \
4188 description_update_size_table[i++] = (struct description_update_size_table_tag){ type, func }
4189 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, isom_update_visual_entry_size );
4190 #ifdef LSMASH_DEMUXER_ENABLED
4191 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, isom_update_visual_entry_size );
4192 #endif
4193 #if 0
4194 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO, isom_update_visual_entry_size );
4195 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO, isom_update_visual_entry_size );
4196 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO, isom_update_visual_entry_size );
4197 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO, isom_update_visual_entry_size );
4198 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO, isom_update_visual_entry_size );
4199 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO, isom_update_visual_entry_size );
4200 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO, isom_update_visual_entry_size );
4201 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO, isom_update_visual_entry_size );
4202 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO, isom_update_visual_entry_size );
4203 #endif
4204 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, isom_update_visual_entry_size );
4205 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, isom_update_visual_entry_size );
4206 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, isom_update_visual_entry_size );
4207 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, isom_update_visual_entry_size );
4208 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, isom_update_visual_entry_size );
4209 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, isom_update_visual_entry_size );
4210 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, isom_update_visual_entry_size );
4211 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, isom_update_visual_entry_size );
4212 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, isom_update_visual_entry_size );
4213 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, isom_update_visual_entry_size );
4214 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, isom_update_visual_entry_size );
4215 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, isom_update_visual_entry_size );
4216 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, isom_update_visual_entry_size );
4217 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, isom_update_visual_entry_size );
4218 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, isom_update_visual_entry_size );
4219 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, isom_update_visual_entry_size );
4220 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, isom_update_visual_entry_size );
4221 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, isom_update_visual_entry_size );
4222 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, isom_update_visual_entry_size );
4223 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, isom_update_visual_entry_size );
4224 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, isom_update_visual_entry_size );
4225 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, isom_update_visual_entry_size );
4226 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, isom_update_visual_entry_size );
4227 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, isom_update_visual_entry_size );
4228 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, isom_update_visual_entry_size );
4229 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, isom_update_visual_entry_size );
4230 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, isom_update_visual_entry_size );
4231 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, isom_update_audio_entry_size );
4232 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, isom_update_audio_entry_size );
4233 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, isom_update_audio_entry_size );
4234 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, isom_update_audio_entry_size );
4235 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, isom_update_audio_entry_size );
4236 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, isom_update_audio_entry_size );
4237 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, isom_update_audio_entry_size );
4238 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, isom_update_audio_entry_size );
4239 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, isom_update_audio_entry_size );
4240 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, isom_update_audio_entry_size );
4241 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_ALAC_AUDIO, isom_update_audio_entry_size );
4242 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_MP4A_AUDIO, isom_update_audio_entry_size );
4243 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, isom_update_audio_entry_size );
4244 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, isom_update_audio_entry_size );
4245 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, isom_update_audio_entry_size );
4246 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, isom_update_audio_entry_size );
4247 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, isom_update_audio_entry_size );
4248 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, isom_update_audio_entry_size );
4249 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, isom_update_audio_entry_size );
4250 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, isom_update_audio_entry_size );
4251 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, isom_update_audio_entry_size );
4252 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, isom_update_audio_entry_size );
4253 #if 0
4254 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO, isom_update_audio_entry_size );
4255 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO, isom_update_audio_entry_size );
4256 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO, isom_update_audio_entry_size );
4257 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO, isom_update_audio_entry_size );
4258 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO, isom_update_audio_entry_size );
4259 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO, isom_update_audio_entry_size );
4260 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_RAW_AUDIO, isom_update_audio_entry_size );
4261 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO, isom_update_audio_entry_size );
4262 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO, isom_update_audio_entry_size );
4263 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO, isom_update_audio_entry_size );
4264 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO, isom_update_audio_entry_size );
4265 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO, isom_update_audio_entry_size );
4266 #endif
4267 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT, isom_update_tx3g_entry_size );
4268 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT, isom_update_text_entry_size );
4269 #if 0
4270 ADD_DESCRIPTION_UPDATE_SIZE_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4S_SYSTEM, isom_update_mp4s_entry_size );
4271 #endif
4273 for( int i = 0; description_update_size_table[i].func; i++ )
4274 if( lsmash_check_codec_type_identical( sample_type, description_update_size_table[i].type ) )
4276 size += description_update_size_table[i].func( data );
4277 break;
4280 stsd->size = size;
4281 CHECK_LARGESIZE( stsd );
4282 return stsd->size;
4285 static uint64_t isom_update_stts_size( isom_stts_t *stts )
4287 if( !stts || !stts->list )
4288 return 0;
4289 stts->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stts->list->entry_count * 8;
4290 CHECK_LARGESIZE( stts );
4291 return stts->size;
4294 static uint64_t isom_update_ctts_size( isom_ctts_t *ctts )
4296 if( !ctts || !ctts->list )
4297 return 0;
4298 ctts->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)ctts->list->entry_count * 8;
4299 CHECK_LARGESIZE( ctts );
4300 return ctts->size;
4303 static uint64_t isom_update_cslg_size( isom_cslg_t *cslg )
4305 if( !cslg )
4306 return 0;
4307 cslg->size = ISOM_FULLBOX_COMMON_SIZE + 20;
4308 CHECK_LARGESIZE( cslg );
4309 return cslg->size;
4312 static uint64_t isom_update_stsz_size( isom_stsz_t *stsz )
4314 if( !stsz )
4315 return 0;
4316 stsz->size = ISOM_FULLBOX_COMMON_SIZE + 8 + ( stsz->list ? (uint64_t)stsz->list->entry_count * 4 : 0 );
4317 CHECK_LARGESIZE( stsz );
4318 return stsz->size;
4321 static uint64_t isom_update_stss_size( isom_stss_t *stss )
4323 if( !stss || !stss->list )
4324 return 0;
4325 stss->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stss->list->entry_count * 4;
4326 CHECK_LARGESIZE( stss );
4327 return stss->size;
4330 static uint64_t isom_update_stps_size( isom_stps_t *stps )
4332 if( !stps || !stps->list )
4333 return 0;
4334 stps->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stps->list->entry_count * 4;
4335 CHECK_LARGESIZE( stps );
4336 return stps->size;
4339 static uint64_t isom_update_sdtp_size( isom_sdtp_t *sdtp )
4341 if( !sdtp || !sdtp->list )
4342 return 0;
4343 sdtp->size = ISOM_FULLBOX_COMMON_SIZE + (uint64_t)sdtp->list->entry_count;
4344 CHECK_LARGESIZE( sdtp );
4345 return sdtp->size;
4348 static uint64_t isom_update_stsc_size( isom_stsc_t *stsc )
4350 if( !stsc || !stsc->list )
4351 return 0;
4352 stsc->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stsc->list->entry_count * 12;
4353 CHECK_LARGESIZE( stsc );
4354 return stsc->size;
4357 static uint64_t isom_update_stco_size( isom_stco_t *stco )
4359 if( !stco || !stco->list )
4360 return 0;
4361 stco->size = ISOM_LIST_FULLBOX_COMMON_SIZE + (uint64_t)stco->list->entry_count * (stco->large_presentation ? 8 : 4);
4362 CHECK_LARGESIZE( stco );
4363 return stco->size;
4366 static uint64_t isom_update_sbgp_size( isom_sbgp_entry_t *sbgp )
4368 if( !sbgp || !sbgp->list )
4369 return 0;
4370 sbgp->size = ISOM_LIST_FULLBOX_COMMON_SIZE + 4 + (uint64_t)sbgp->list->entry_count * 8;
4371 CHECK_LARGESIZE( sbgp );
4372 return sbgp->size;
4375 static uint64_t isom_update_sgpd_size( isom_sgpd_entry_t *sgpd )
4377 if( !sgpd || !sgpd->list )
4378 return 0;
4379 uint64_t size = ISOM_LIST_FULLBOX_COMMON_SIZE + (1 + (sgpd->version == 1)) * 4;
4380 size += (uint64_t)sgpd->list->entry_count * ((sgpd->version == 1) && !sgpd->default_length) * 4;
4381 switch( sgpd->grouping_type )
4383 case ISOM_GROUP_TYPE_RAP :
4384 size += sgpd->list->entry_count;
4385 break;
4386 case ISOM_GROUP_TYPE_ROLL :
4387 size += (uint64_t)sgpd->list->entry_count * 2;
4388 break;
4389 default :
4390 /* We don't consider other grouping types currently. */
4391 break;
4393 sgpd->size = size;
4394 CHECK_LARGESIZE( sgpd );
4395 return sgpd->size;
4398 static uint64_t isom_update_stbl_size( isom_stbl_t *stbl )
4400 if( !stbl )
4401 return 0;
4402 stbl->size = ISOM_BASEBOX_COMMON_SIZE
4403 + isom_update_stsd_size( stbl->stsd )
4404 + isom_update_stts_size( stbl->stts )
4405 + isom_update_ctts_size( stbl->ctts )
4406 + isom_update_cslg_size( stbl->cslg )
4407 + isom_update_stsz_size( stbl->stsz )
4408 + isom_update_stss_size( stbl->stss )
4409 + isom_update_stps_size( stbl->stps )
4410 + isom_update_sdtp_size( stbl->sdtp )
4411 + isom_update_stsc_size( stbl->stsc )
4412 + isom_update_stco_size( stbl->stco );
4413 if( stbl->sgpd_list )
4414 for( lsmash_entry_t *entry = stbl->sgpd_list->head; entry; entry = entry->next )
4415 stbl->size += isom_update_sgpd_size( (isom_sgpd_entry_t *)entry->data );
4416 if( stbl->sbgp_list )
4417 for( lsmash_entry_t *entry = stbl->sbgp_list->head; entry; entry = entry->next )
4418 stbl->size += isom_update_sbgp_size( (isom_sbgp_entry_t *)entry->data );
4419 CHECK_LARGESIZE( stbl );
4420 return stbl->size;
4423 static uint64_t isom_update_minf_size( isom_minf_t *minf )
4425 if( !minf )
4426 return 0;
4427 minf->size = ISOM_BASEBOX_COMMON_SIZE
4428 + isom_update_vmhd_size( minf->vmhd )
4429 + isom_update_smhd_size( minf->smhd )
4430 + isom_update_hmhd_size( minf->hmhd )
4431 + isom_update_nmhd_size( minf->nmhd )
4432 + isom_update_gmhd_size( minf->gmhd )
4433 + isom_update_hdlr_size( minf->hdlr )
4434 + isom_update_dinf_size( minf->dinf )
4435 + isom_update_stbl_size( minf->stbl );
4436 CHECK_LARGESIZE( minf );
4437 return minf->size;
4440 static uint64_t isom_update_mdia_size( isom_mdia_t *mdia )
4442 if( !mdia )
4443 return 0;
4444 mdia->size = ISOM_BASEBOX_COMMON_SIZE
4445 + isom_update_mdhd_size( mdia->mdhd )
4446 + isom_update_hdlr_size( mdia->hdlr )
4447 + isom_update_minf_size( mdia->minf );
4448 CHECK_LARGESIZE( mdia );
4449 return mdia->size;
4452 static uint64_t isom_update_chpl_size( isom_chpl_t *chpl )
4454 if( !chpl )
4455 return 0;
4456 chpl->size = ISOM_FULLBOX_COMMON_SIZE + 4 * (chpl->version == 1) + 1;
4457 for( lsmash_entry_t *entry = chpl->list->head; entry; entry = entry->next )
4459 isom_chpl_entry_t *data = (isom_chpl_entry_t *)entry->data;
4460 chpl->size += 9 + data->chapter_name_length;
4462 CHECK_LARGESIZE( chpl );
4463 return chpl->size;
4466 static uint64_t isom_update_mean_size( isom_mean_t *mean )
4468 if( !mean )
4469 return 0;
4470 mean->size = ISOM_FULLBOX_COMMON_SIZE + mean->meaning_string_length;
4471 CHECK_LARGESIZE( mean );
4472 return mean->size;
4475 static uint64_t isom_update_name_size( isom_name_t *name )
4477 if( !name )
4478 return 0;
4479 name->size = ISOM_FULLBOX_COMMON_SIZE + name->name_length;
4480 CHECK_LARGESIZE( name );
4481 return name->size;
4484 static uint64_t isom_update_data_size( isom_data_t *data )
4486 if( !data )
4487 return 0;
4488 data->size = ISOM_BASEBOX_COMMON_SIZE + 8 + data->value_length;
4489 CHECK_LARGESIZE( data );
4490 return data->size;
4493 static uint64_t isom_update_metaitem_size( isom_metaitem_t *metaitem )
4495 if( !metaitem )
4496 return 0;
4497 metaitem->size = ISOM_BASEBOX_COMMON_SIZE
4498 + isom_update_mean_size( metaitem->mean )
4499 + isom_update_name_size( metaitem->name )
4500 + isom_update_data_size( metaitem->data );
4501 CHECK_LARGESIZE( metaitem );
4502 return metaitem->size;
4505 static uint64_t isom_update_ilst_size( isom_ilst_t *ilst )
4507 if( !ilst )
4508 return 0;
4509 ilst->size = ISOM_BASEBOX_COMMON_SIZE;
4510 for( lsmash_entry_t *entry = ilst->item_list->head; entry; entry = entry->next )
4511 ilst->size += isom_update_metaitem_size( (isom_metaitem_t *)entry->data );
4512 CHECK_LARGESIZE( ilst );
4513 return ilst->size;
4516 static uint64_t isom_update_meta_size( isom_meta_t *meta )
4518 if( !meta )
4519 return 0;
4520 meta->size = ISOM_FULLBOX_COMMON_SIZE
4521 + isom_update_hdlr_size( meta->hdlr )
4522 + isom_update_dinf_size( meta->dinf )
4523 + isom_update_ilst_size( meta->ilst );
4524 CHECK_LARGESIZE( meta );
4525 return meta->size;
4528 static uint64_t isom_update_cprt_size( isom_cprt_t *cprt )
4530 if( !cprt )
4531 return 0;
4532 cprt->size = ISOM_FULLBOX_COMMON_SIZE + 2 + cprt->notice_length;
4533 CHECK_LARGESIZE( cprt );
4534 return cprt->size;
4537 static uint64_t isom_update_udta_size( isom_udta_t *udta_moov, isom_udta_t *udta_trak )
4539 isom_udta_t *udta = udta_trak ? udta_trak : udta_moov ? udta_moov : NULL;
4540 if( !udta )
4541 return 0;
4542 udta->size = ISOM_BASEBOX_COMMON_SIZE
4543 + (udta_moov ? isom_update_chpl_size( udta->chpl ) : 0)
4544 + isom_update_meta_size( udta->meta );
4545 if( udta->cprt_list )
4546 for( lsmash_entry_t *entry = udta->cprt_list->head; entry; entry = entry->next )
4547 udta->size += isom_update_cprt_size( (isom_cprt_t *)entry->data );
4548 CHECK_LARGESIZE( udta );
4549 return udta->size;
4552 static uint64_t isom_update_trak_entry_size( isom_trak_entry_t *trak )
4554 if( !trak )
4555 return 0;
4556 trak->size = ISOM_BASEBOX_COMMON_SIZE
4557 + isom_update_tkhd_size( trak->tkhd )
4558 + isom_update_tapt_size( trak->tapt )
4559 + isom_update_edts_size( trak->edts )
4560 + isom_update_tref_size( trak->tref )
4561 + isom_update_mdia_size( trak->mdia )
4562 + isom_update_udta_size( NULL, trak->udta )
4563 + isom_update_meta_size( trak->meta );
4564 CHECK_LARGESIZE( trak );
4565 return trak->size;
4568 static uint64_t isom_update_mehd_size( isom_mehd_t *mehd )
4570 if( !mehd )
4571 return 0;
4572 if( mehd->fragment_duration > UINT32_MAX )
4573 mehd->version = 1;
4574 mehd->size = ISOM_FULLBOX_COMMON_SIZE + 4 * (1 + (mehd->version == 1));
4575 CHECK_LARGESIZE( mehd );
4576 return mehd->size;
4579 static uint64_t isom_update_trex_entry_size( isom_trex_entry_t *trex )
4581 if( !trex )
4582 return 0;
4583 trex->size = ISOM_FULLBOX_COMMON_SIZE + 20;
4584 CHECK_LARGESIZE( trex );
4585 return trex->size;
4588 static uint64_t isom_update_mvex_size( isom_mvex_t *mvex )
4590 if( !mvex )
4591 return 0;
4592 mvex->size = ISOM_BASEBOX_COMMON_SIZE;
4593 if( mvex->trex_list )
4594 for( lsmash_entry_t *entry = mvex->trex_list->head; entry; entry = entry->next )
4596 isom_trex_entry_t *trex = (isom_trex_entry_t *)entry->data;
4597 mvex->size += isom_update_trex_entry_size( trex );
4599 if( mvex->root->bs->stream != stdout )
4600 mvex->size += mvex->mehd ? isom_update_mehd_size( mvex->mehd ) : 20; /* 20 bytes is of placeholder. */
4601 CHECK_LARGESIZE( mvex );
4602 return mvex->size;
4605 static int isom_update_moov_size( isom_moov_t *moov )
4607 if( !moov )
4608 return -1;
4609 moov->size = ISOM_BASEBOX_COMMON_SIZE
4610 + isom_update_mvhd_size( moov->mvhd )
4611 + isom_update_iods_size( moov->iods )
4612 + isom_update_udta_size( moov->udta, NULL )
4613 + isom_update_ctab_size( moov->ctab )
4614 + isom_update_meta_size( moov->meta )
4615 + isom_update_mvex_size( moov->mvex );
4616 if( moov->trak_list )
4617 for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
4619 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
4620 moov->size += isom_update_trak_entry_size( trak );
4622 CHECK_LARGESIZE( moov );
4623 return 0;
4626 static uint64_t isom_update_mfhd_size( isom_mfhd_t *mfhd )
4628 if( !mfhd )
4629 return 0;
4630 mfhd->size = ISOM_FULLBOX_COMMON_SIZE + 4;
4631 CHECK_LARGESIZE( mfhd );
4632 return mfhd->size;
4635 static uint64_t isom_update_tfhd_size( isom_tfhd_t *tfhd )
4637 if( !tfhd )
4638 return 0;
4639 tfhd->size = ISOM_FULLBOX_COMMON_SIZE
4641 + 8 * !!( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT )
4642 + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT )
4643 + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT )
4644 + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT )
4645 + 4 * !!( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT );
4646 CHECK_LARGESIZE( tfhd );
4647 return tfhd->size;
4650 static uint64_t isom_update_tfdt_size( isom_tfdt_t *tfdt )
4652 if( !tfdt )
4653 return 0;
4654 tfdt->size = ISOM_FULLBOX_COMMON_SIZE + 4 * (1 + (tfdt->version == 1));
4655 CHECK_LARGESIZE( tfdt );
4656 return tfdt->size;
4659 static uint64_t isom_update_trun_entry_size( isom_trun_entry_t *trun )
4661 if( !trun )
4662 return 0;
4663 trun->size = ISOM_FULLBOX_COMMON_SIZE
4665 + 4 * !!( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT )
4666 + 4 * !!( trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT );
4667 uint64_t row_size = 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT )
4668 + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT )
4669 + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT )
4670 + 4 * !!( trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT );
4671 trun->size += row_size * trun->sample_count;
4672 CHECK_LARGESIZE( trun );
4673 return trun->size;
4676 static uint64_t isom_update_traf_entry_size( isom_traf_entry_t *traf )
4678 if( !traf )
4679 return 0;
4680 traf->size = ISOM_BASEBOX_COMMON_SIZE
4681 + isom_update_tfhd_size( traf->tfhd )
4682 + isom_update_tfdt_size( traf->tfdt )
4683 + isom_update_sdtp_size( traf->sdtp );
4684 if( traf->trun_list )
4685 for( lsmash_entry_t *entry = traf->trun_list->head; entry; entry = entry->next )
4687 isom_trun_entry_t *trun = (isom_trun_entry_t *)entry->data;
4688 traf->size += isom_update_trun_entry_size( trun );
4690 CHECK_LARGESIZE( traf );
4691 return traf->size;
4694 static int isom_update_moof_entry_size( isom_moof_entry_t *moof )
4696 if( !moof )
4697 return -1;
4698 moof->size = ISOM_BASEBOX_COMMON_SIZE + isom_update_mfhd_size( moof->mfhd );
4699 if( moof->traf_list )
4700 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
4702 isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
4703 moof->size += isom_update_traf_entry_size( traf );
4705 CHECK_LARGESIZE( moof );
4706 return 0;
4709 static uint64_t isom_update_tfra_entry_size( isom_tfra_entry_t *tfra )
4711 if( !tfra )
4712 return 0;
4713 tfra->size = ISOM_FULLBOX_COMMON_SIZE + 12;
4714 uint32_t entry_size = 8 * (1 + (tfra->version == 1))
4715 + tfra->length_size_of_traf_num + 1
4716 + tfra->length_size_of_trun_num + 1
4717 + tfra->length_size_of_sample_num + 1;
4718 tfra->size += entry_size * tfra->number_of_entry;
4719 CHECK_LARGESIZE( tfra );
4720 return tfra->size;
4723 static uint64_t isom_update_mfro_size( isom_mfro_t *mfro )
4725 if( !mfro )
4726 return 0;
4727 mfro->size = ISOM_FULLBOX_COMMON_SIZE + 4;
4728 CHECK_LARGESIZE( mfro );
4729 return mfro->size;
4732 static int isom_update_mfra_size( isom_mfra_t *mfra )
4734 if( !mfra )
4735 return -1;
4736 mfra->size = ISOM_BASEBOX_COMMON_SIZE;
4737 if( mfra->tfra_list )
4738 for( lsmash_entry_t *entry = mfra->tfra_list->head; entry; entry = entry->next )
4740 isom_tfra_entry_t *tfra = (isom_tfra_entry_t *)entry->data;
4741 mfra->size += isom_update_tfra_entry_size( tfra );
4743 CHECK_LARGESIZE( mfra );
4744 if( mfra->mfro )
4746 mfra->size += isom_update_mfro_size( mfra->mfro );
4747 mfra->mfro->length = mfra->size;
4749 return 0;
4752 static uint64_t isom_update_extension_boxes( void *box )
4754 assert( box );
4755 uint64_t size = 0;
4756 lsmash_entry_list_t *extensions = &((isom_box_t *)box)->extensions;
4757 for( lsmash_entry_t *entry = extensions->head; entry; entry = entry->next )
4759 isom_extension_box_t *ext = (isom_extension_box_t *)entry->data;
4760 if( !ext )
4761 continue;
4762 if( ext->format == EXTENSION_FORMAT_BINARY )
4764 size += ext->size;
4765 continue;
4767 static struct update_size_table_tag
4769 lsmash_box_type_t type;
4770 uint64_t (*func)( void * );
4771 } update_size_table[32] = { { LSMASH_BOX_TYPE_INITIALIZER, NULL } };
4772 if( !update_size_table[0].func )
4774 /* Initialize the table. */
4775 int i = 0;
4776 #define ADD_UPDATE_SIZE_TABLE_ELEMENT( type, func ) update_size_table[i++] = (struct update_size_table_tag){ type, (uint64_t (*)( void * ))func }
4777 ADD_UPDATE_SIZE_TABLE_ELEMENT( ISOM_BOX_TYPE_ESDS, isom_update_esds_size );
4778 ADD_UPDATE_SIZE_TABLE_ELEMENT( ISOM_BOX_TYPE_BTRT, isom_update_btrt_size );
4779 ADD_UPDATE_SIZE_TABLE_ELEMENT( ISOM_BOX_TYPE_CLAP, isom_update_clap_size );
4780 ADD_UPDATE_SIZE_TABLE_ELEMENT( ISOM_BOX_TYPE_PASP, isom_update_pasp_size );
4781 ADD_UPDATE_SIZE_TABLE_ELEMENT( ISOM_BOX_TYPE_STSL, isom_update_stsl_size );
4782 ADD_UPDATE_SIZE_TABLE_ELEMENT( ISOM_BOX_TYPE_COLR, isom_update_colr_size );
4783 ADD_UPDATE_SIZE_TABLE_ELEMENT( QT_BOX_TYPE_CHAN, isom_update_chan_size );
4784 ADD_UPDATE_SIZE_TABLE_ELEMENT( QT_BOX_TYPE_COLR, isom_update_colr_size );
4785 ADD_UPDATE_SIZE_TABLE_ELEMENT( QT_BOX_TYPE_CSPC, isom_update_cspc_size );
4786 ADD_UPDATE_SIZE_TABLE_ELEMENT( QT_BOX_TYPE_ENDA, isom_update_enda_size );
4787 ADD_UPDATE_SIZE_TABLE_ELEMENT( QT_BOX_TYPE_ESDS, isom_update_esds_size );
4788 ADD_UPDATE_SIZE_TABLE_ELEMENT( QT_BOX_TYPE_FIEL, isom_update_fiel_size );
4789 ADD_UPDATE_SIZE_TABLE_ELEMENT( QT_BOX_TYPE_FRMA, isom_update_frma_size );
4790 ADD_UPDATE_SIZE_TABLE_ELEMENT( QT_BOX_TYPE_GAMA, isom_update_gama_size );
4791 ADD_UPDATE_SIZE_TABLE_ELEMENT( QT_BOX_TYPE_GLBL, isom_update_glbl_size );
4792 ADD_UPDATE_SIZE_TABLE_ELEMENT( QT_BOX_TYPE_SGBT, isom_update_sgbt_size );
4793 ADD_UPDATE_SIZE_TABLE_ELEMENT( QT_BOX_TYPE_WAVE, isom_update_wave_size );
4794 ADD_UPDATE_SIZE_TABLE_ELEMENT( QT_BOX_TYPE_TERMINATOR, isom_update_terminator_size );
4795 ADD_UPDATE_SIZE_TABLE_ELEMENT( LSMASH_BOX_TYPE_UNSPECIFIED, NULL );
4796 #undef ADD_UPDATE_SIZE_TABLE_ELEMENT
4798 uint64_t (*update_size_func)( void * ) = (uint64_t (*)( void * ))isom_update_unknown_box_size;
4799 for( int i = 0; update_size_table[i].func; i++ )
4800 if( lsmash_check_box_type_identical( ext->type, update_size_table[i].type ) )
4802 update_size_func = update_size_table[i].func;
4803 break;
4805 size += update_size_func( ext->form.box );
4807 return size;
4810 /*******************************
4811 public interfaces
4812 *******************************/
4814 /*---- track manipulators ----*/
4816 void lsmash_delete_track( lsmash_root_t *root, uint32_t track_ID )
4818 if( !root || !root->moov || !root->moov->trak_list )
4819 return;
4820 for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
4822 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
4823 if( !trak || !trak->tkhd )
4824 return;
4825 if( trak->tkhd->track_ID == track_ID )
4827 lsmash_remove_entry_direct( root->moov->trak_list, entry, isom_remove_trak );
4828 return;
4833 uint32_t lsmash_create_track( lsmash_root_t *root, lsmash_media_type media_type )
4835 isom_trak_entry_t *trak = isom_add_trak( root );
4836 if( !trak )
4837 return 0;
4838 if( isom_add_tkhd( trak, media_type )
4839 || isom_add_mdia( trak )
4840 || isom_add_mdhd( trak->mdia, root->qt_compatible ? 0 : ISOM_LANGUAGE_CODE_UNDEFINED )
4841 || isom_add_minf( trak->mdia )
4842 || isom_add_stbl( trak->mdia->minf )
4843 || isom_add_dinf( trak->mdia->minf )
4844 || isom_add_dref( trak->mdia->minf->dinf )
4845 || isom_add_stsd( trak->mdia->minf->stbl )
4846 || isom_add_stts( trak->mdia->minf->stbl )
4847 || isom_add_stsc( trak->mdia->minf->stbl )
4848 || isom_add_stco( trak->mdia->minf->stbl )
4849 || isom_add_stsz( trak->mdia->minf->stbl ) )
4850 return 0;
4851 if( isom_add_hdlr( trak->mdia, NULL, NULL, media_type ) )
4852 return 0;
4853 if( root->qt_compatible && isom_add_hdlr( NULL, NULL, trak->mdia->minf, QT_REFERENCE_HANDLER_TYPE_URL ) )
4854 return 0;
4855 switch( media_type )
4857 case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK :
4858 if( isom_add_vmhd( trak->mdia->minf ) )
4859 return 0;
4860 break;
4861 case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK :
4862 if( isom_add_smhd( trak->mdia->minf ) )
4863 return 0;
4864 break;
4865 case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK :
4866 if( isom_add_hmhd( trak->mdia->minf ) )
4867 return 0;
4868 break;
4869 case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK :
4870 if( root->qt_compatible || root->itunes_movie )
4872 if( isom_add_gmhd( trak->mdia->minf )
4873 || isom_add_gmin( trak->mdia->minf->gmhd )
4874 || isom_add_text( trak->mdia->minf->gmhd ) )
4875 return 0;
4877 else
4878 return 0; /* We support only reference text media track for chapter yet. */
4879 break;
4880 default :
4881 if( isom_add_nmhd( trak->mdia->minf ) )
4882 return 0;
4883 break;
4885 return trak->tkhd->track_ID;
4888 uint32_t lsmash_get_track_ID( lsmash_root_t *root, uint32_t track_number )
4890 if( !root || !root->moov )
4891 return 0;
4892 isom_trak_entry_t *trak = (isom_trak_entry_t *)lsmash_get_entry_data( root->moov->trak_list, track_number );
4893 if( !trak || !trak->tkhd )
4894 return 0;
4895 return trak->tkhd->track_ID;
4898 void lsmash_initialize_track_parameters( lsmash_track_parameters_t *param )
4900 memset( param, 0, sizeof(lsmash_track_parameters_t) );
4901 param->audio_volume = 0x0100;
4902 param->matrix[0] = 0x00010000;
4903 param->matrix[4] = 0x00010000;
4904 param->matrix[8] = 0x40000000;
4907 int lsmash_set_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param )
4909 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
4910 if( !trak || !trak->mdia || !trak->mdia->hdlr || !root->moov->mvhd )
4911 return -1;
4912 /* Prepare Track Aperture Modes if required. */
4913 if( root->qt_compatible && param->aperture_modes )
4915 if( !trak->tapt && isom_add_tapt( trak ) )
4916 return -1;
4917 isom_tapt_t *tapt = trak->tapt;
4918 if( (!tapt->clef && isom_add_clef( tapt ))
4919 || (!tapt->prof && isom_add_prof( tapt ))
4920 || (!tapt->enof && isom_add_enof( tapt )) )
4921 return -1;
4923 else
4924 isom_remove_tapt( trak->tapt );
4925 /* Set up Track Header. */
4926 uint32_t media_type = trak->mdia->hdlr->componentSubtype;
4927 isom_tkhd_t *tkhd = trak->tkhd;
4928 tkhd->flags = param->mode;
4929 tkhd->track_ID = param->track_ID ? param->track_ID : tkhd->track_ID;
4930 tkhd->duration = !trak->edts || !trak->edts->elst ? param->duration : tkhd->duration;
4931 /* Template fields
4932 * alternate_group, layer, volume and matrix
4933 * According to 14496-14, these value are all set to defaut values in 14496-12.
4934 * And when a file is read as an MPEG-4 file, these values shall be ignored.
4935 * If a file complies with other specifications, then those fields may have non-default values
4936 * as required by those other specifications. */
4937 if( param->alternate_group )
4939 if( root->qt_compatible || root->itunes_movie || root->max_3gpp_version >= 4 )
4940 tkhd->alternate_group = param->alternate_group;
4941 else
4943 tkhd->alternate_group = 0;
4944 lsmash_log( LSMASH_LOG_WARNING, "alternate_group is specified but not compatible with any of the brands. It won't be set.\n" );
4947 else
4948 tkhd->alternate_group = 0;
4949 if( root->qt_compatible || root->itunes_movie )
4951 tkhd->layer = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->video_layer : 0;
4952 tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? param->audio_volume : 0;
4953 if( media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK )
4954 for( int i = 0; i < 9; i++ )
4955 tkhd->matrix[i] = param->matrix[i];
4956 else
4957 for( int i = 0; i < 9; i++ )
4958 tkhd->matrix[i] = 0;
4960 else
4962 tkhd->layer = 0;
4963 tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? 0x0100 : 0;
4964 tkhd->matrix[0] = 0x00010000;
4965 tkhd->matrix[1] = tkhd->matrix[2] = tkhd->matrix[3] = 0;
4966 tkhd->matrix[4] = 0x00010000;
4967 tkhd->matrix[5] = tkhd->matrix[6] = tkhd->matrix[7] = 0;
4968 tkhd->matrix[8] = 0x40000000;
4970 /* visual presentation size */
4971 tkhd->width = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_width : 0;
4972 tkhd->height = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_height : 0;
4973 /* Update next_track_ID if needed. */
4974 if( root->moov->mvhd->next_track_ID <= tkhd->track_ID )
4975 root->moov->mvhd->next_track_ID = tkhd->track_ID + 1;
4976 return 0;
4979 int lsmash_get_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param )
4981 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
4982 if( !trak )
4983 return -1;
4984 isom_tkhd_t *tkhd = trak->tkhd;
4985 param->mode = tkhd->flags;
4986 param->track_ID = tkhd->track_ID;
4987 param->duration = tkhd->duration;
4988 param->video_layer = tkhd->layer;
4989 param->alternate_group = tkhd->alternate_group;
4990 param->audio_volume = tkhd->volume;
4991 for( int i = 0; i < 9; i++ )
4992 param->matrix[i] = tkhd->matrix[i];
4993 param->display_width = tkhd->width;
4994 param->display_height = tkhd->height;
4995 param->aperture_modes = !!trak->tapt;
4996 return 0;
4999 static int isom_set_media_handler_name( lsmash_root_t *root, uint32_t track_ID, char *handler_name )
5001 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
5002 if( !trak || !trak->mdia || !trak->mdia->hdlr )
5003 return -1;
5004 isom_hdlr_t *hdlr = trak->mdia->hdlr;
5005 uint8_t *name = NULL;
5006 uint32_t name_length = strlen( handler_name ) + root->isom_compatible + root->qt_compatible;
5007 if( root->qt_compatible )
5008 name_length = LSMASH_MIN( name_length, 255 );
5009 if( name_length > hdlr->componentName_length && hdlr->componentName )
5010 name = realloc( hdlr->componentName, name_length );
5011 else if( !hdlr->componentName )
5012 name = malloc( name_length );
5013 else
5014 name = hdlr->componentName;
5015 if( !name )
5016 return -1;
5017 if( root->qt_compatible )
5018 name[0] = name_length & 0xff;
5019 memcpy( name + root->qt_compatible, handler_name, strlen( handler_name ) );
5020 if( root->isom_compatible )
5021 name[name_length - 1] = 0;
5022 hdlr->componentName = name;
5023 hdlr->componentName_length = name_length;
5024 return 0;
5027 static int isom_set_data_handler_name( lsmash_root_t *root, uint32_t track_ID, char *handler_name )
5029 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
5030 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->hdlr )
5031 return -1;
5032 isom_hdlr_t *hdlr = trak->mdia->minf->hdlr;
5033 uint8_t *name = NULL;
5034 uint32_t name_length = strlen( handler_name ) + root->isom_compatible + root->qt_compatible;
5035 if( root->qt_compatible )
5036 name_length = LSMASH_MIN( name_length, 255 );
5037 if( name_length > hdlr->componentName_length && hdlr->componentName )
5038 name = realloc( hdlr->componentName, name_length );
5039 else if( !hdlr->componentName )
5040 name = malloc( name_length );
5041 else
5042 name = hdlr->componentName;
5043 if( !name )
5044 return -1;
5045 if( root->qt_compatible )
5046 name[0] = name_length & 0xff;
5047 memcpy( name + root->qt_compatible, handler_name, strlen( handler_name ) );
5048 if( root->isom_compatible )
5049 name[name_length - 1] = 0;
5050 hdlr->componentName = name;
5051 hdlr->componentName_length = name_length;
5052 return 0;
5055 uint32_t lsmash_get_media_timescale( lsmash_root_t *root, uint32_t track_ID )
5057 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
5058 if( !trak || !trak->mdia || !trak->mdia->mdhd )
5059 return 0;
5060 return trak->mdia->mdhd->timescale;
5063 uint64_t lsmash_get_media_duration( lsmash_root_t *root, uint32_t track_ID )
5065 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
5066 if( !trak || !trak->mdia || !trak->mdia->mdhd )
5067 return 0;
5068 return trak->mdia->mdhd->duration;
5071 uint64_t lsmash_get_track_duration( lsmash_root_t *root, uint32_t track_ID )
5073 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
5074 if( !trak || !trak->tkhd )
5075 return 0;
5076 return trak->tkhd->duration;
5079 uint32_t lsmash_get_last_sample_delta( lsmash_root_t *root, uint32_t track_ID )
5081 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
5082 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl
5083 || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list
5084 || !trak->mdia->minf->stbl->stts->list->tail || !trak->mdia->minf->stbl->stts->list->tail->data )
5085 return 0;
5086 return ((isom_stts_entry_t *)trak->mdia->minf->stbl->stts->list->tail->data)->sample_delta;
5089 uint32_t lsmash_get_start_time_offset( lsmash_root_t *root, uint32_t track_ID )
5091 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
5092 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl
5093 || !trak->mdia->minf->stbl->ctts || !trak->mdia->minf->stbl->ctts->list
5094 || !trak->mdia->minf->stbl->ctts->list->head || !trak->mdia->minf->stbl->ctts->list->head->data )
5095 return 0;
5096 return ((isom_ctts_entry_t *)trak->mdia->minf->stbl->ctts->list->head->data)->sample_offset;
5099 uint32_t lsmash_get_composition_to_decode_shift( lsmash_root_t *root, uint32_t track_ID )
5101 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
5102 if( !trak || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl )
5103 return 0;
5104 uint32_t sample_count = isom_get_sample_count( trak );
5105 if( sample_count == 0 )
5106 return 0;
5107 isom_stbl_t *stbl = trak->mdia->minf->stbl;
5108 if( !stbl->stts || !stbl->stts->list || !stbl->ctts || !stbl->ctts->list )
5109 return 0;
5110 if( !(root->max_isom_version >= 4 && stbl->ctts->version == 1) && !root->qt_compatible )
5111 return 0; /* This movie shall not have composition to decode timeline shift. */
5112 lsmash_entry_t *stts_entry = stbl->stts->list->head;
5113 lsmash_entry_t *ctts_entry = stbl->ctts->list->head;
5114 if( !stts_entry || !ctts_entry )
5115 return 0;
5116 uint64_t dts = 0;
5117 uint64_t cts = 0;
5118 uint32_t ctd_shift = 0;
5119 uint32_t i = 0;
5120 uint32_t j = 0;
5121 for( uint32_t k = 0; k < sample_count; i++ )
5123 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
5124 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
5125 if( !stts_data || !ctts_data )
5126 return 0;
5127 cts = dts + (int32_t)ctts_data->sample_offset;
5128 if( dts > cts + ctd_shift )
5129 ctd_shift = dts - cts;
5130 dts += stts_data->sample_delta;
5131 if( ++i == stts_data->sample_count )
5133 stts_entry = stts_entry->next;
5134 if( !stts_entry )
5135 return 0;
5136 i = 0;
5138 if( ++j == ctts_data->sample_count )
5140 ctts_entry = ctts_entry->next;
5141 if( !ctts_entry )
5142 return 0;
5143 j = 0;
5146 return ctd_shift;
5149 uint16_t lsmash_pack_iso_language( char *iso_language )
5151 if( !iso_language || strlen( iso_language ) != 3 )
5152 return 0;
5153 return (uint16_t)LSMASH_PACK_ISO_LANGUAGE( iso_language[0], iso_language[1], iso_language[2] );
5156 static int isom_iso2mac_language( uint16_t ISO_language, uint16_t *MAC_language )
5158 if( !MAC_language )
5159 return -1;
5160 int i = 0;
5161 for( ; isom_languages[i].iso_name; i++ )
5162 if( ISO_language == isom_languages[i].iso_name )
5163 break;
5164 if( !isom_languages[i].iso_name )
5165 return -1;
5166 *MAC_language = isom_languages[i].mac_value;
5167 return 0;
5170 static int isom_mac2iso_language( uint16_t MAC_language, uint16_t *ISO_language )
5172 if( !ISO_language )
5173 return -1;
5174 int i = 0;
5175 for( ; isom_languages[i].iso_name; i++ )
5176 if( MAC_language == isom_languages[i].mac_value )
5177 break;
5178 *ISO_language = isom_languages[i].iso_name ? isom_languages[i].iso_name : ISOM_LANGUAGE_CODE_UNDEFINED;
5179 return 0;
5182 static int isom_set_media_language( lsmash_root_t *root, uint32_t track_ID, uint16_t ISO_language, uint16_t MAC_language )
5184 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
5185 if( !trak || !trak->mdia || !trak->mdia->mdhd )
5186 return -1;
5187 uint16_t language = 0;
5188 if( root->isom_compatible )
5190 if( ISO_language )
5191 language = ISO_language;
5192 else if( MAC_language )
5194 if( isom_mac2iso_language( MAC_language, &language ) )
5195 return -1;
5197 else
5198 language = ISOM_LANGUAGE_CODE_UNDEFINED;
5200 else if( root->qt_compatible )
5202 if( ISO_language )
5204 if( isom_iso2mac_language( ISO_language, &language ) )
5205 return -1;
5207 else
5208 language = MAC_language;
5210 else
5211 return -1;
5212 trak->mdia->mdhd->language = language;
5213 return 0;
5216 static int isom_create_grouping( isom_trak_entry_t *trak, isom_grouping_type grouping_type )
5218 lsmash_root_t *root = trak->root;
5219 switch( grouping_type )
5221 case ISOM_GROUP_TYPE_RAP :
5222 assert( root->max_isom_version >= 6 );
5223 break;
5224 case ISOM_GROUP_TYPE_ROLL :
5225 assert( root->avc_extensions || root->qt_compatible );
5226 break;
5227 default :
5228 assert( 0 );
5229 break;
5231 if( !isom_add_sgpd( trak->mdia->minf->stbl, grouping_type )
5232 || !isom_add_sbgp( trak->mdia->minf->stbl, grouping_type ) )
5233 return -1;
5234 return 0;
5237 void lsmash_initialize_media_parameters( lsmash_media_parameters_t *param )
5239 memset( param, 0, sizeof(lsmash_media_parameters_t) );
5240 param->timescale = 1;
5243 int lsmash_set_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param )
5245 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
5246 if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl )
5247 return -1;
5248 trak->mdia->mdhd->timescale = param->timescale;
5249 if( isom_set_media_language( root, track_ID, param->ISO_language, param->MAC_language ) )
5250 return -1;
5251 if( param->media_handler_name
5252 && isom_set_media_handler_name( root, track_ID, param->media_handler_name ) )
5253 return -1;
5254 if( root->qt_compatible && param->data_handler_name
5255 && isom_set_data_handler_name( root, track_ID, param->data_handler_name ) )
5256 return -1;
5257 if( (root->avc_extensions || root->qt_compatible) && param->roll_grouping
5258 && isom_create_grouping( trak, ISOM_GROUP_TYPE_ROLL ) )
5259 return -1;
5260 if( (root->max_isom_version >= 6) && param->rap_grouping
5261 && isom_create_grouping( trak, ISOM_GROUP_TYPE_RAP ) )
5262 return -1;
5263 return 0;
5266 int lsmash_get_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param )
5268 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
5269 if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->hdlr
5270 || !trak->mdia->minf || !trak->mdia->minf->stbl )
5271 return -1;
5272 isom_mdhd_t *mdhd = trak->mdia->mdhd;
5273 isom_stbl_t *stbl = trak->mdia->minf->stbl;
5274 isom_sbgp_entry_t *sbgp;
5275 isom_sgpd_entry_t *sgpd;
5276 param->timescale = mdhd->timescale;
5277 param->handler_type = trak->mdia->hdlr->componentSubtype;
5278 param->duration = mdhd->duration;
5279 /* Whether sample grouping present. */
5280 sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_ROLL );
5281 sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_ROLL );
5282 param->roll_grouping = sbgp && sgpd;
5283 sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_RAP );
5284 sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
5285 param->rap_grouping = sbgp && sgpd;
5286 /* Get media language. */
5287 if( mdhd->language >= 0x800 )
5289 param->MAC_language = 0;
5290 param->ISO_language = mdhd->language;
5292 else
5294 param->MAC_language = mdhd->language;
5295 param->ISO_language = 0;
5297 /* Get handler name(s). */
5298 isom_hdlr_t *hdlr = trak->mdia->hdlr;
5299 int length = LSMASH_MIN( 255, hdlr->componentName_length );
5300 if( length )
5302 memcpy( param->media_handler_name_shadow, hdlr->componentName + root->qt_compatible, length );
5303 param->media_handler_name_shadow[length - 2 + root->isom_compatible + root->qt_compatible] = '\0';
5304 param->media_handler_name = param->media_handler_name_shadow;
5306 else
5308 param->media_handler_name = NULL;
5309 memset( param->media_handler_name_shadow, 0, sizeof(param->media_handler_name_shadow) );
5311 if( trak->mdia->minf->hdlr )
5313 hdlr = trak->mdia->minf->hdlr;
5314 length = LSMASH_MIN( 255, hdlr->componentName_length );
5315 if( length )
5317 memcpy( param->data_handler_name_shadow, hdlr->componentName + root->qt_compatible, length );
5318 param->data_handler_name_shadow[length - 2 + root->isom_compatible + root->qt_compatible] = '\0';
5319 param->data_handler_name = param->data_handler_name_shadow;
5321 else
5323 param->data_handler_name = NULL;
5324 memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) );
5327 else
5329 param->data_handler_name = NULL;
5330 memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) );
5332 return 0;
5335 /*---- movie manipulators ----*/
5337 lsmash_root_t *lsmash_open_movie( const char *filename, lsmash_file_mode mode )
5339 if( !filename )
5340 return NULL;
5341 char open_mode[4] = { 0 };
5342 if( mode & LSMASH_FILE_MODE_WRITE )
5343 memcpy( open_mode, "w+b", 4 );
5344 #ifdef LSMASH_DEMUXER_ENABLED
5345 else if( mode & LSMASH_FILE_MODE_READ )
5346 memcpy( open_mode, "rb", 3 );
5347 #endif
5348 if( !open_mode[0] )
5349 return NULL;
5350 lsmash_root_t *root = lsmash_malloc_zero( sizeof(lsmash_root_t) );
5351 if( !root )
5352 return NULL;
5353 root->root = root;
5354 root->bs = lsmash_malloc_zero( sizeof(lsmash_bs_t) );
5355 if( !root->bs )
5356 goto fail;
5357 if( !strcmp( filename, "-" ) )
5359 if( mode & LSMASH_FILE_MODE_READ )
5360 root->bs->stream = stdin;
5361 else if( (mode & LSMASH_FILE_MODE_WRITE) && (mode & LSMASH_FILE_MODE_FRAGMENTED) )
5362 root->bs->stream = stdout;
5364 else
5365 root->bs->stream = fopen( filename, open_mode );
5366 if( !root->bs->stream )
5367 goto fail;
5368 root->flags = mode;
5369 if( mode & LSMASH_FILE_MODE_WRITE )
5371 if( isom_add_moov( root ) || isom_add_mvhd( root->moov ) )
5372 goto fail;
5373 root->qt_compatible = 1; /* QTFF is default file format. */
5375 #ifdef LSMASH_DEMUXER_ENABLED
5376 if( (mode & (LSMASH_FILE_MODE_READ | LSMASH_FILE_MODE_DUMP)) )
5378 if( isom_read_root( root ) )
5379 goto fail;
5380 root->max_read_size = 4 * 1024 * 1024;
5382 #endif
5383 if( mode & LSMASH_FILE_MODE_FRAGMENTED )
5385 root->fragment = lsmash_malloc_zero( sizeof(isom_fragment_manager_t) );
5386 if( !root->fragment )
5387 goto fail;
5388 root->fragment->pool = lsmash_create_entry_list();
5389 if( !root->fragment->pool )
5390 goto fail;
5392 return root;
5393 fail:
5394 lsmash_destroy_root( root );
5395 return NULL;
5398 static int isom_finish_fragment_movie( lsmash_root_t *root );
5400 /* A movie fragment cannot switch a sample description to another.
5401 * So you must call this function before switching sample descriptions. */
5402 int lsmash_create_fragment_movie( lsmash_root_t *root )
5404 if( !root || !root->bs || !root->fragment || !root->moov || !root->moov->trak_list )
5405 return -1;
5406 /* Finish the previous movie fragment before starting a new one. */
5407 if( isom_finish_fragment_movie( root ) )
5408 return -1;
5409 /* We always hold only one movie fragment except for the initial movie (a pair of moov and mdat). */
5410 if( root->fragment->movie && root->moof_list->entry_count != 1 )
5411 return -1;
5412 isom_moof_entry_t *moof = isom_add_moof( root );
5413 if( isom_add_mfhd( moof ) )
5414 return -1;
5415 root->fragment->movie = moof;
5416 moof->mfhd->sequence_number = ++ root->fragment->fragment_count;
5417 if( root->moof_list->entry_count == 1 )
5418 return 0;
5419 /* Remove the previous movie fragment. */
5420 return lsmash_remove_entry( root->moof_list, 1, isom_remove_moof );
5423 static int isom_set_brands( lsmash_root_t *root, lsmash_brand_type major_brand, uint32_t minor_version, lsmash_brand_type *brands, uint32_t brand_count )
5425 if( brand_count > 50 )
5426 return -1; /* We support setting brands up to 50. */
5427 if( !brand_count )
5429 /* Absence of File Type Box means this file is a QuickTime or MP4 version 1 format file. */
5430 if( root->ftyp )
5432 if( root->ftyp->compatible_brands )
5433 free( root->ftyp->compatible_brands );
5434 free( root->ftyp );
5435 root->ftyp = NULL;
5437 return 0;
5439 if( !root->ftyp && isom_add_ftyp( root ) )
5440 return -1;
5441 isom_ftyp_t *ftyp = root->ftyp;
5442 ftyp->major_brand = major_brand;
5443 ftyp->minor_version = minor_version;
5444 lsmash_brand_type *compatible_brands;
5445 if( !ftyp->compatible_brands )
5446 compatible_brands = malloc( brand_count * sizeof(uint32_t) );
5447 else
5448 compatible_brands = realloc( ftyp->compatible_brands, brand_count * sizeof(uint32_t) );
5449 if( !compatible_brands )
5450 return -1;
5451 ftyp->compatible_brands = compatible_brands;
5452 for( uint32_t i = 0; i < brand_count; i++ )
5454 ftyp->compatible_brands[i] = brands[i];
5455 ftyp->size += 4;
5457 ftyp->brand_count = brand_count;
5458 return isom_check_compatibility( root );
5461 void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t *param )
5463 memset( param, 0, sizeof(lsmash_movie_parameters_t) );
5464 param->max_chunk_duration = 0.5;
5465 param->max_async_tolerance = 2.0;
5466 param->max_chunk_size = 4 * 1024 * 1024;
5467 param->max_read_size = 4 * 1024 * 1024;
5468 param->timescale = 600;
5469 param->playback_rate = 0x00010000;
5470 param->playback_volume = 0x0100;
5473 int lsmash_set_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param )
5475 if( !root || !root->moov || !root->moov->mvhd
5476 || isom_set_brands( root, param->major_brand, param->minor_version, param->brands, param->number_of_brands ) )
5477 return -1;
5478 isom_mvhd_t *mvhd = root->moov->mvhd;
5479 root->max_chunk_duration = param->max_chunk_duration;
5480 root->max_async_tolerance = LSMASH_MAX( param->max_async_tolerance, 2 * param->max_chunk_duration );
5481 root->max_chunk_size = param->max_chunk_size;
5482 root->max_read_size = param->max_read_size;
5483 mvhd->timescale = param->timescale;
5484 if( root->qt_compatible || root->itunes_movie )
5486 mvhd->rate = param->playback_rate;
5487 mvhd->volume = param->playback_volume;
5488 mvhd->previewTime = param->preview_time;
5489 mvhd->previewDuration = param->preview_duration;
5490 mvhd->posterTime = param->poster_time;
5492 else
5494 mvhd->rate = 0x00010000;
5495 mvhd->volume = 0x0100;
5496 mvhd->previewTime = 0;
5497 mvhd->previewDuration = 0;
5498 mvhd->posterTime = 0;
5500 return 0;
5503 int lsmash_get_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param )
5505 if( !root || !root->moov || !root->moov->mvhd )
5506 return -1;
5507 isom_mvhd_t *mvhd = root->moov->mvhd;
5508 if( root->ftyp )
5510 isom_ftyp_t *ftyp = root->ftyp;
5511 uint32_t brand_count = LSMASH_MIN( ftyp->brand_count, 50 ); /* brands up to 50 */
5512 for( uint32_t i = 0; i < brand_count; i++ )
5513 param->brands_shadow[i] = ftyp->compatible_brands[i];
5514 param->major_brand = ftyp->major_brand;
5515 param->brands = param->brands_shadow;
5516 param->number_of_brands = brand_count;
5517 param->minor_version = ftyp->minor_version;
5519 param->max_chunk_duration = root->max_chunk_duration;
5520 param->max_async_tolerance = root->max_async_tolerance;
5521 param->max_chunk_size = root->max_chunk_size;
5522 param->max_read_size = root->max_read_size;
5523 param->timescale = mvhd->timescale;
5524 param->duration = mvhd->duration;
5525 param->playback_rate = mvhd->rate;
5526 param->playback_volume = mvhd->volume;
5527 param->preview_time = mvhd->previewTime;
5528 param->preview_duration = mvhd->previewDuration;
5529 param->poster_time = mvhd->posterTime;
5530 param->number_of_tracks = root->moov->trak_list ? root->moov->trak_list->entry_count : 0;
5531 return 0;
5534 uint32_t lsmash_get_movie_timescale( lsmash_root_t *root )
5536 if( !root || !root->moov || !root->moov->mvhd )
5537 return 0;
5538 return root->moov->mvhd->timescale;
5541 int lsmash_set_free( lsmash_root_t *root, uint8_t *data, uint64_t data_length )
5543 if( !root || !root->free || !data || !data_length )
5544 return -1;
5545 isom_free_t *skip = root->free;
5546 uint8_t *tmp = NULL;
5547 if( !skip->data )
5548 tmp = malloc( data_length );
5549 else if( skip->length < data_length )
5550 tmp = realloc( skip->data, data_length );
5551 if( !tmp )
5552 return -1;
5553 memcpy( tmp, data, data_length );
5554 skip->data = tmp;
5555 skip->length = data_length;
5556 return 0;
5559 int lsmash_add_free( lsmash_root_t *root, uint8_t *data, uint64_t data_length )
5561 if( !root )
5562 return -1;
5563 if( !root->free )
5565 isom_create_box( skip, root, ISOM_BOX_TYPE_FREE );
5566 root->free = skip;
5568 if( data && data_length )
5569 return lsmash_set_free( root, data, data_length );
5570 return 0;
5573 int lsmash_create_object_descriptor( lsmash_root_t *root )
5575 if( !root )
5576 return -1;
5577 /* Return error if this file is not compatible with MP4 file format. */
5578 if( !root->mp4_version1 && !root->mp4_version2 )
5579 return -1;
5580 return isom_add_iods( root->moov );
5583 /*---- finishing functions ----*/
5585 static int isom_set_fragment_overall_duration( lsmash_root_t *root )
5587 isom_mvex_t *mvex = root->moov->mvex;
5588 if( isom_add_mehd( mvex ) )
5589 return -1;
5590 /* Get the longest duration of the tracks. */
5591 uint64_t longest_duration = 0;
5592 for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
5594 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
5595 if( !trak || !trak->cache || !trak->cache->fragment || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale )
5596 return -1;
5597 uint64_t duration;
5598 if( !trak->edts || !trak->edts->elst || !trak->edts->elst->list )
5600 duration = trak->cache->fragment->largest_cts + trak->cache->fragment->last_duration;
5601 duration = (uint64_t)(((double)duration / trak->mdia->mdhd->timescale) * root->moov->mvhd->timescale);
5603 else
5605 duration = 0;
5606 for( lsmash_entry_t *elst_entry = trak->edts->elst->list->head; elst_entry; elst_entry = elst_entry->next )
5608 isom_elst_entry_t *data = (isom_elst_entry_t *)elst_entry->data;
5609 if( !data )
5610 return -1;
5611 duration += data->segment_duration;
5614 longest_duration = LSMASH_MAX( duration, longest_duration );
5616 mvex->mehd->fragment_duration = longest_duration;
5617 mvex->mehd->version = 1;
5618 isom_update_mehd_size( mvex->mehd );
5619 /* Write Movie Extends Header Box here. */
5620 lsmash_bs_t *bs = root->bs;
5621 FILE *stream = bs->stream;
5622 uint64_t current_pos = lsmash_ftell( stream );
5623 lsmash_fseek( stream, mvex->placeholder_pos, SEEK_SET );
5624 int ret = isom_write_mehd( bs, mvex->mehd );
5625 if( !ret )
5626 ret = lsmash_bs_write_data( bs );
5627 lsmash_fseek( stream, current_pos, SEEK_SET );
5628 return ret;
5631 static int isom_write_fragment_random_access_info( lsmash_root_t *root )
5633 if( !root->moov->mvex || !root->moov->mvex->trex_list )
5634 return 0;
5635 /* Reconstruct the Movie Fragment Random Access Box.
5636 * All 'time' field in the Track Fragment Random Access Boxes shall reflect edit list. */
5637 uint32_t movie_timescale = lsmash_get_movie_timescale( root );
5638 if( movie_timescale == 0 )
5639 return -1; /* Division by zero will occur. */
5640 for( lsmash_entry_t *trex_entry = root->moov->mvex->trex_list->head; trex_entry; trex_entry = trex_entry->next )
5642 isom_trex_entry_t *trex = (isom_trex_entry_t *)trex_entry->data;
5643 if( !trex )
5644 return -1;
5645 /* Get the edit list of the track associated with the trex->track_ID.
5646 * If failed or absent, implicit timeline mapping edit is used, and skip this operation for the track. */
5647 isom_trak_entry_t *trak = isom_get_trak( root, trex->track_ID );
5648 if( !trak )
5649 return -1;
5650 if( !trak->edts || !trak->edts->elst || !trak->edts->elst->list
5651 || !trak->edts->elst->list->head || !trak->edts->elst->list->head->data )
5652 continue;
5653 isom_elst_t *elst = trak->edts->elst;
5654 /* Get the Track Fragment Random Access Boxes of the track associated with the trex->track_ID.
5655 * If failed or absent, skip reconstructing the Track Fragment Random Access Box of the track. */
5656 isom_tfra_entry_t *tfra = isom_get_tfra( root->mfra, trex->track_ID );
5657 if( !tfra )
5658 continue;
5659 /* Reconstruct the Track Fragment Random Access Box. */
5660 lsmash_entry_t *edit_entry = elst->list->head;
5661 isom_elst_entry_t *edit = edit_entry->data;
5662 uint64_t edit_offset = 0; /* units in media timescale */
5663 uint32_t media_timescale = lsmash_get_media_timescale( root, trex->track_ID );
5664 for( lsmash_entry_t *rap_entry = tfra->list->head; rap_entry; )
5666 isom_tfra_location_time_entry_t *rap = (isom_tfra_location_time_entry_t *)rap_entry->data;
5667 if( !rap )
5669 /* Irregular case. Drop this entry. */
5670 lsmash_entry_t *next = rap_entry->next;
5671 lsmash_remove_entry_direct( tfra->list, rap_entry, NULL );
5672 rap_entry = next;
5673 continue;
5675 uint64_t composition_time = rap->time;
5676 /* Skip edits that doesn't need the current sync sample indicated in the Track Fragment Random Access Box. */
5677 while( edit )
5679 uint64_t segment_duration = ((edit->segment_duration - 1) / movie_timescale + 1) * media_timescale;
5680 if( edit->media_time != ISOM_EDIT_MODE_EMPTY
5681 && composition_time < edit->media_time + segment_duration )
5682 break; /* This Timeline Mapping Edit might require the current sync sample.
5683 * Note: this condition doesn't cover all cases.
5684 * For instance, matching the both following conditions
5685 * 1. A sync sample isn't in the presentation.
5686 * 2. The other samples, which precede it in the composition timeline, is in the presentation. */
5687 edit_offset += segment_duration;
5688 edit_entry = edit_entry->next;
5689 if( !edit_entry )
5691 /* No more presentation. */
5692 edit = NULL;
5693 break;
5695 edit = edit_entry->data;
5697 if( !edit )
5699 /* No more presentation.
5700 * Drop the rest of sync samples since they are generally absent in the whole presentation.
5701 * Though the exceptions are sync samples with earlier composition time, we ignore them. (SAP type 2: TEPT = TDEC = TSAP < TPTF)
5702 * To support this exception, we need sorting entries of the list by composition times. */
5703 for( ; rap_entry; rap_entry = rap_entry->next )
5704 lsmash_remove_entry_direct( tfra->list, rap_entry, NULL );
5705 break;
5707 /* If the sync sample isn't in the presentation,
5708 * we pick the earliest presentation time of the current edit as its presentation time. */
5709 rap->time = edit_offset;
5710 if( composition_time >= edit->media_time )
5711 rap->time += composition_time - edit->media_time;
5712 rap_entry = rap_entry->next;
5715 /* Decide the size of the Movie Fragment Random Access Box. */
5716 if( isom_update_mfra_size( root->mfra ) )
5717 return -1;
5718 /* Write the Movie Fragment Random Access Box. */
5719 return isom_write_mfra( root->bs, root->mfra );
5722 int lsmash_finish_movie( lsmash_root_t *root, lsmash_adhoc_remux_t* remux )
5724 if( !root || !root->bs || !root->moov || !root->moov->trak_list )
5725 return -1;
5726 if( root->fragment )
5728 /* Output the final movie fragment. */
5729 if( isom_finish_fragment_movie( root ) )
5730 return -1;
5731 if( root->bs->stream == stdout )
5732 return 0;
5733 /* Write the overall random access information at the tail of the movie. */
5734 if( isom_write_fragment_random_access_info( root ) )
5735 return -1;
5736 /* Set overall duration of the movie. */
5737 return isom_set_fragment_overall_duration( root );
5739 isom_moov_t *moov = root->moov;
5740 for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
5742 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
5743 if( !trak || !trak->cache || !trak->tkhd || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl )
5744 return -1;
5745 uint32_t track_ID = trak->tkhd->track_ID;
5746 uint32_t related_track_ID = trak->related_track_ID;
5747 /* Disable the track if the track is a track reference chapter. */
5748 if( trak->is_chapter )
5749 trak->tkhd->flags &= ~ISOM_TRACK_ENABLED;
5750 if( trak->is_chapter && related_track_ID )
5752 /* In order that the track duration of the chapter track doesn't exceed that of the related track. */
5753 lsmash_edit_t edit;
5754 edit.duration = LSMASH_MIN( trak->tkhd->duration, lsmash_get_track_duration( root, related_track_ID ) );
5755 edit.start_time = 0;
5756 edit.rate = ISOM_EDIT_MODE_NORMAL;
5757 if( lsmash_create_explicit_timeline_map( root, track_ID, edit ) )
5758 return -1;
5760 /* Add stss box if any samples aren't sync sample. */
5761 isom_stbl_t *stbl = trak->mdia->minf->stbl;
5762 if( !trak->cache->all_sync && !stbl->stss && isom_add_stss( stbl ) )
5763 return -1;
5764 if( isom_update_bitrate_description( trak->mdia ) )
5765 return -1;
5767 if( root->mp4_version1 == 1 && isom_add_iods( moov ) )
5768 return -1;
5769 if( isom_check_mandatory_boxes( root )
5770 || isom_set_movie_creation_time( root )
5771 || isom_update_moov_size( moov )
5772 || isom_write_mdat_size( root ) )
5773 return -1;
5775 lsmash_bs_t *bs = root->bs;
5776 uint64_t meta_size = root->meta ? root->meta->size : 0;
5777 if( !remux )
5779 if( isom_write_moov( root )
5780 || isom_write_meta( bs, root->meta ) )
5781 return -1;
5782 root->size += moov->size + meta_size;
5783 return 0;
5786 /* stco->co64 conversion, depending on last chunk's offset */
5787 for( lsmash_entry_t* entry = moov->trak_list->head; entry; )
5789 isom_trak_entry_t* trak = (isom_trak_entry_t*)entry->data;
5790 isom_stco_t* stco = trak->mdia->minf->stbl->stco;
5791 if( !stco->list->tail )
5792 return -1;
5793 if( stco->large_presentation
5794 || (((isom_stco_entry_t*)stco->list->tail->data)->chunk_offset + moov->size + meta_size) <= UINT32_MAX )
5796 entry = entry->next;
5797 continue; /* no need to convert stco into co64 */
5799 /* stco->co64 conversion */
5800 if( isom_convert_stco_to_co64( trak->mdia->minf->stbl )
5801 || isom_update_moov_size( moov ) )
5802 return -1;
5803 entry = moov->trak_list->head; /* whenever any conversion, re-check all traks */
5806 /* now the amount of offset is fixed. */
5807 uint64_t mtf_size = moov->size + meta_size; /* sum of size of boxes moved to front */
5809 /* buffer size must be at least mtf_size * 2 */
5810 remux->buffer_size = LSMASH_MAX( remux->buffer_size, mtf_size * 2 );
5812 uint8_t* buf[2];
5813 if( (buf[0] = (uint8_t*)malloc( remux->buffer_size )) == NULL )
5814 return -1; /* NOTE: i think we still can fallback to "return isom_write_moov( root );" here. */
5815 uint64_t size = remux->buffer_size / 2;
5816 buf[1] = buf[0] + size; /* split to 2 buffers */
5818 /* now the amount of offset is fixed. apply that to stco/co64 */
5819 for( lsmash_entry_t* entry = moov->trak_list->head; entry; entry = entry->next )
5821 isom_stco_t* stco = ((isom_trak_entry_t*)entry->data)->mdia->minf->stbl->stco;
5822 if( stco->large_presentation )
5823 for( lsmash_entry_t* co64_entry = stco->list->head ; co64_entry ; co64_entry = co64_entry->next )
5824 ((isom_co64_entry_t*)co64_entry->data)->chunk_offset += mtf_size;
5825 else
5826 for( lsmash_entry_t* stco_entry = stco->list->head ; stco_entry ; stco_entry = stco_entry->next )
5827 ((isom_stco_entry_t*)stco_entry->data)->chunk_offset += mtf_size;
5830 FILE *stream = bs->stream;
5831 isom_mdat_t *mdat = root->mdat;
5832 uint64_t total = root->size + mtf_size;
5833 uint64_t readnum;
5834 /* backup starting area of mdat and write moov + meta there instead */
5835 if( lsmash_fseek( stream, mdat->placeholder_pos, SEEK_SET ) )
5836 goto fail;
5837 readnum = fread( buf[0], 1, size, stream );
5838 uint64_t read_pos = lsmash_ftell( stream );
5840 /* write moov + meta there instead */
5841 if( lsmash_fseek( stream, mdat->placeholder_pos, SEEK_SET )
5842 || isom_write_moov( root )
5843 || isom_write_meta( bs, root->meta ) )
5844 goto fail;
5845 uint64_t write_pos = lsmash_ftell( stream );
5847 mdat->placeholder_pos += mtf_size; /* update placeholder */
5849 /* copy-pastan */
5850 int buf_switch = 1;
5851 while( readnum == size )
5853 if( lsmash_fseek( stream, read_pos, SEEK_SET ) )
5854 goto fail;
5855 readnum = fread( buf[buf_switch], 1, size, stream );
5856 read_pos = lsmash_ftell( stream );
5858 buf_switch ^= 0x1;
5860 if( lsmash_fseek( stream, write_pos, SEEK_SET )
5861 || fwrite( buf[buf_switch], 1, size, stream ) != size )
5862 goto fail;
5863 write_pos = lsmash_ftell( stream );
5864 if( remux->func ) remux->func( remux->param, write_pos, total ); // FIXME:
5866 if( fwrite( buf[buf_switch^0x1], 1, readnum, stream ) != readnum )
5867 goto fail;
5868 if( remux->func ) remux->func( remux->param, total, total ); // FIXME:
5870 root->size += mtf_size;
5871 free( buf[0] );
5872 return 0;
5874 fail:
5875 free( buf[0] );
5876 return -1;
5879 #define GET_MOST_USED( box_name, index, flag_name ) \
5880 if( most_used[index] < stats.flag_name[i] ) \
5882 most_used[index] = stats.flag_name[i]; \
5883 box_name->default_sample_flags.flag_name = i; \
5886 static int isom_create_fragment_overall_default_settings( lsmash_root_t *root )
5888 if( isom_add_mvex( root->moov ) )
5889 return -1;
5890 for( lsmash_entry_t *trak_entry = root->moov->trak_list->head; trak_entry; trak_entry = trak_entry->next )
5892 isom_trak_entry_t *trak = (isom_trak_entry_t *)trak_entry->data;
5893 if( !trak || !trak->cache || !trak->tkhd || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl )
5894 return -1;
5895 isom_stbl_t *stbl = trak->mdia->minf->stbl;
5896 if( !stbl->stts || !stbl->stts->list || !stbl->stsz
5897 || (stbl->stts->list->tail && !stbl->stts->list->tail->data)
5898 || (stbl->stsz->list && stbl->stsz->list->head && !stbl->stsz->list->head->data) )
5899 return -1;
5900 isom_trex_entry_t *trex = isom_add_trex( root->moov->mvex );
5901 if( !trex )
5902 return -1;
5903 trex->track_ID = trak->tkhd->track_ID;
5904 /* Set up defaults. */
5905 trex->default_sample_description_index = trak->cache->chunk.sample_description_index ? trak->cache->chunk.sample_description_index : 1;
5906 trex->default_sample_duration = stbl->stts->list->tail ? ((isom_stts_entry_t *)stbl->stts->list->tail->data)->sample_delta : 1;
5907 trex->default_sample_size = !stbl->stsz->list
5908 ? stbl->stsz->sample_size : stbl->stsz->list->head
5909 ? ((isom_stsz_entry_t *)stbl->stsz->list->head->data)->entry_size : 0;
5910 if( stbl->sdtp && stbl->sdtp->list )
5912 struct sample_flags_stats_t
5914 uint32_t is_leading [4];
5915 uint32_t sample_depends_on [4];
5916 uint32_t sample_is_depended_on[4];
5917 uint32_t sample_has_redundancy[4];
5918 } stats = { { 0 }, { 0 }, { 0 }, { 0 } };
5919 for( lsmash_entry_t *sdtp_entry = stbl->sdtp->list->head; sdtp_entry; sdtp_entry = sdtp_entry->next )
5921 isom_sdtp_entry_t *data = (isom_sdtp_entry_t *)sdtp_entry->data;
5922 if( !data )
5923 return -1;
5924 ++ stats.is_leading [ data->is_leading ];
5925 ++ stats.sample_depends_on [ data->sample_depends_on ];
5926 ++ stats.sample_is_depended_on[ data->sample_is_depended_on ];
5927 ++ stats.sample_has_redundancy[ data->sample_has_redundancy ];
5929 uint32_t most_used[4] = { 0, 0, 0, 0 };
5930 for( int i = 0; i < 4; i++ )
5932 GET_MOST_USED( trex, 0, is_leading );
5933 GET_MOST_USED( trex, 1, sample_depends_on );
5934 GET_MOST_USED( trex, 2, sample_is_depended_on );
5935 GET_MOST_USED( trex, 3, sample_has_redundancy );
5938 trex->default_sample_flags.sample_is_non_sync_sample = !trak->cache->all_sync;
5940 return 0;
5943 static int isom_prepare_random_access_info( lsmash_root_t *root )
5945 if( root->bs->stream == stdout )
5946 return 0;
5947 if( isom_add_mfra( root )
5948 || isom_add_mfro( root->mfra ) )
5949 return -1;
5950 return 0;
5953 static int isom_output_fragment_media_data( lsmash_root_t *root )
5955 isom_fragment_manager_t *fragment = root->fragment;
5956 if( !fragment->pool->entry_count )
5958 /* no need to write media data */
5959 lsmash_remove_entries( fragment->pool, lsmash_delete_sample );
5960 fragment->pool_size = 0;
5961 return 0;
5963 /* If there is no available Media Data Box to write samples, add and write a new one. */
5964 if( isom_new_mdat( root, fragment->pool_size ) )
5965 return -1;
5966 /* Write samples in the current movie fragment. */
5967 for( lsmash_entry_t* entry = fragment->pool->head; entry; entry = entry->next )
5969 isom_sample_pool_t *pool = (isom_sample_pool_t *)entry->data;
5970 if( !pool )
5971 return -1;
5972 lsmash_bs_put_bytes( root->bs, pool->size, pool->data );
5974 if( lsmash_bs_write_data( root->bs ) )
5975 return -1;
5976 root->size += root->mdat->size;
5977 lsmash_remove_entries( fragment->pool, isom_remove_sample_pool );
5978 fragment->pool_size = 0;
5979 return 0;
5982 static int isom_finish_fragment_initial_movie( lsmash_root_t *root )
5984 if( !root->moov || !root->moov->trak_list )
5985 return -1;
5986 isom_moov_t *moov = root->moov;
5987 for( lsmash_entry_t *entry = moov->trak_list->head; entry; entry = entry->next )
5989 isom_trak_entry_t *trak = (isom_trak_entry_t *)entry->data;
5990 if( !trak || !trak->cache || !trak->tkhd || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl )
5991 return -1;
5992 if( isom_get_sample_count( trak ) )
5994 /* Add stss box if any samples aren't sync sample. */
5995 isom_stbl_t *stbl = trak->mdia->minf->stbl;
5996 if( !trak->cache->all_sync && !stbl->stss && isom_add_stss( stbl ) )
5997 return -1;
5999 else
6000 trak->tkhd->duration = 0;
6001 if( isom_update_bitrate_description( trak->mdia ) )
6002 return -1;
6004 if( root->mp4_version1 == 1 && isom_add_iods( moov ) )
6005 return -1;
6006 if( isom_create_fragment_overall_default_settings( root )
6007 || isom_prepare_random_access_info( root )
6008 || isom_check_mandatory_boxes( root )
6009 || isom_set_movie_creation_time( root )
6010 || isom_update_moov_size( moov ) )
6011 return -1;
6012 /* stco->co64 conversion, depending on last chunk's offset */
6013 uint64_t meta_size = root->meta ? root->meta->size : 0;
6014 for( lsmash_entry_t* entry = moov->trak_list->head; entry; )
6016 isom_trak_entry_t* trak = (isom_trak_entry_t*)entry->data;
6017 isom_stco_t* stco = trak->mdia->minf->stbl->stco;
6018 if( !stco->list->tail /* no samples */
6019 || stco->large_presentation
6020 || (((isom_stco_entry_t*)stco->list->tail->data)->chunk_offset + moov->size + meta_size) <= UINT32_MAX )
6022 entry = entry->next;
6023 continue; /* no need to convert stco into co64 */
6025 /* stco->co64 conversion */
6026 if( isom_convert_stco_to_co64( trak->mdia->minf->stbl )
6027 || isom_update_moov_size( moov ) )
6028 return -1;
6029 entry = moov->trak_list->head; /* whenever any conversion, re-check all traks */
6031 /* Now, the amount of offset is fixed. Apply that to stco/co64. */
6032 uint64_t preceding_size = moov->size + meta_size;
6033 for( lsmash_entry_t* entry = moov->trak_list->head; entry; entry = entry->next )
6035 isom_stco_t* stco = ((isom_trak_entry_t*)entry->data)->mdia->minf->stbl->stco;
6036 if( stco->large_presentation )
6037 for( lsmash_entry_t* co64_entry = stco->list->head ; co64_entry ; co64_entry = co64_entry->next )
6038 ((isom_co64_entry_t*)co64_entry->data)->chunk_offset += preceding_size;
6039 else
6040 for( lsmash_entry_t* stco_entry = stco->list->head ; stco_entry ; stco_entry = stco_entry->next )
6041 ((isom_stco_entry_t*)stco_entry->data)->chunk_offset += preceding_size;
6043 /* Write File Type Box here if it was not written yet. */
6044 if( !root->file_type_written && isom_write_ftyp( root ) )
6045 return -1;
6046 /* Write Movie Box. */
6047 if( isom_write_moov( root )
6048 || isom_write_meta( root->bs, root->meta ) )
6049 return -1;
6050 root->size += preceding_size;
6051 /* Output samples. */
6052 return isom_output_fragment_media_data( root );
6055 /* Return 1 if there is diffrence, otherwise return 0. */
6056 static int isom_compare_sample_flags( isom_sample_flags_t *a, isom_sample_flags_t *b )
6058 return (a->reserved != b->reserved)
6059 || (a->is_leading != b->is_leading)
6060 || (a->sample_depends_on != b->sample_depends_on)
6061 || (a->sample_is_depended_on != b->sample_is_depended_on)
6062 || (a->sample_has_redundancy != b->sample_has_redundancy)
6063 || (a->sample_padding_value != b->sample_padding_value)
6064 || (a->sample_is_non_sync_sample != b->sample_is_non_sync_sample)
6065 || (a->sample_degradation_priority != b->sample_degradation_priority);
6068 static int isom_finish_fragment_movie( lsmash_root_t *root )
6070 if( !root->moov || !root->moov->trak_list || !root->fragment || !root->fragment->pool )
6071 return -1;
6072 isom_moof_entry_t *moof = root->fragment->movie;
6073 if( !moof )
6074 return isom_finish_fragment_initial_movie( root );
6075 /* Calculate appropriate default_sample_flags of each Track Fragment Header Box.
6076 * And check whether that default_sample_flags is useful or not. */
6077 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
6079 isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
6080 if( !traf || !traf->tfhd || !traf->root || !traf->root->moov || !traf->root->moov->mvex )
6081 return -1;
6082 isom_tfhd_t *tfhd = traf->tfhd;
6083 isom_trex_entry_t *trex = isom_get_trex( root->moov->mvex, tfhd->track_ID );
6084 if( !trex )
6085 return -1;
6086 struct sample_flags_stats_t
6088 uint32_t is_leading [4];
6089 uint32_t sample_depends_on [4];
6090 uint32_t sample_is_depended_on [4];
6091 uint32_t sample_has_redundancy [4];
6092 uint32_t sample_is_non_sync_sample[2];
6093 } stats = { { 0 }, { 0 }, { 0 }, { 0 }, { 0 } };
6094 for( lsmash_entry_t *trun_entry = traf->trun_list->head; trun_entry; trun_entry = trun_entry->next )
6096 isom_trun_entry_t *trun = (isom_trun_entry_t *)trun_entry->data;
6097 if( !trun || !trun->sample_count )
6098 return -1;
6099 isom_sample_flags_t *sample_flags;
6100 if( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT )
6102 if( !trun->optional )
6103 return -1;
6104 for( lsmash_entry_t *optional_entry = trun->optional->head; optional_entry; optional_entry = optional_entry->next )
6106 isom_trun_optional_row_t *row = (isom_trun_optional_row_t *)optional_entry->data;
6107 if( !row )
6108 return -1;
6109 sample_flags = &row->sample_flags;
6110 ++ stats.is_leading [ sample_flags->is_leading ];
6111 ++ stats.sample_depends_on [ sample_flags->sample_depends_on ];
6112 ++ stats.sample_is_depended_on [ sample_flags->sample_is_depended_on ];
6113 ++ stats.sample_has_redundancy [ sample_flags->sample_has_redundancy ];
6114 ++ stats.sample_is_non_sync_sample[ sample_flags->sample_is_non_sync_sample ];
6117 else
6119 sample_flags = &tfhd->default_sample_flags;
6120 stats.is_leading [ sample_flags->is_leading ] += trun->sample_count;
6121 stats.sample_depends_on [ sample_flags->sample_depends_on ] += trun->sample_count;
6122 stats.sample_is_depended_on [ sample_flags->sample_is_depended_on ] += trun->sample_count;
6123 stats.sample_has_redundancy [ sample_flags->sample_has_redundancy ] += trun->sample_count;
6124 stats.sample_is_non_sync_sample[ sample_flags->sample_is_non_sync_sample ] += trun->sample_count;
6127 uint32_t most_used[5] = { 0, 0, 0, 0, 0 };
6128 for( int i = 0; i < 4; i++ )
6130 GET_MOST_USED( tfhd, 0, is_leading );
6131 GET_MOST_USED( tfhd, 1, sample_depends_on );
6132 GET_MOST_USED( tfhd, 2, sample_is_depended_on );
6133 GET_MOST_USED( tfhd, 3, sample_has_redundancy );
6134 if( i < 2 )
6135 GET_MOST_USED( tfhd, 4, sample_is_non_sync_sample );
6137 int useful_default_sample_duration = 0;
6138 int useful_default_sample_size = 0;
6139 for( lsmash_entry_t *trun_entry = traf->trun_list->head; trun_entry; trun_entry = trun_entry->next )
6141 isom_trun_entry_t *trun = (isom_trun_entry_t *)trun_entry->data;
6142 if( !(trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT) )
6143 useful_default_sample_duration = 1;
6144 if( !(trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT) )
6145 useful_default_sample_size = 1;
6146 int useful_first_sample_flags = 1;
6147 int useful_default_sample_flags = 1;
6148 if( trun->sample_count == 1 )
6150 /* It is enough to check only if first_sample_flags equals default_sample_flags or not.
6151 * If it is equal, just use default_sample_flags.
6152 * If not, just use first_sample_flags of this run. */
6153 if( !isom_compare_sample_flags( &trun->first_sample_flags, &tfhd->default_sample_flags ) )
6154 useful_first_sample_flags = 0;
6156 else if( trun->optional && trun->optional->head )
6158 lsmash_entry_t *optional_entry = trun->optional->head->next;
6159 isom_trun_optional_row_t *row = (isom_trun_optional_row_t *)optional_entry->data;
6160 isom_sample_flags_t representative_sample_flags = row->sample_flags;
6161 if( isom_compare_sample_flags( &tfhd->default_sample_flags, &representative_sample_flags ) )
6162 useful_default_sample_flags = 0;
6163 if( !isom_compare_sample_flags( &trun->first_sample_flags, &representative_sample_flags ) )
6164 useful_first_sample_flags = 0;
6165 if( useful_default_sample_flags )
6166 for( optional_entry = optional_entry->next; optional_entry; optional_entry = optional_entry->next )
6168 row = (isom_trun_optional_row_t *)optional_entry->data;
6169 if( isom_compare_sample_flags( &representative_sample_flags, &row->sample_flags ) )
6171 useful_default_sample_flags = 0;
6172 break;
6176 if( useful_default_sample_flags )
6178 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT;
6179 trun->flags &= ~ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT;
6181 else
6183 useful_first_sample_flags = 0;
6184 trun->flags |= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT;
6186 if( useful_first_sample_flags )
6187 trun->flags |= ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT;
6189 if( useful_default_sample_duration && tfhd->default_sample_duration != trex->default_sample_duration )
6190 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
6191 else
6192 tfhd->default_sample_duration = trex->default_sample_duration; /* This might be redundant, but is to be more natural. */
6193 if( useful_default_sample_size && tfhd->default_sample_size != trex->default_sample_size )
6194 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT;
6195 else
6196 tfhd->default_sample_size = trex->default_sample_size; /* This might be redundant, but is to be more natural. */
6197 if( !(tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT) )
6198 tfhd->default_sample_flags = trex->default_sample_flags; /* This might be redundant, but is to be more natural. */
6199 else if( !isom_compare_sample_flags( &tfhd->default_sample_flags, &trex->default_sample_flags ) )
6200 tfhd->flags &= ~ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT;
6202 /* When using for live streaming, setting explicit base_data_offset is not preferable.
6203 * However, it's OK because we haven't supported this yet.
6204 * Implicit base_data_offsets that originate in the first byte of each Movie Fragment Box will be implemented
6205 * by the feature of ISO Base Media File Format version 5 or later.
6206 * Media Data Box starts immediately after Movie Fragment Box. */
6207 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
6209 isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
6210 traf->tfhd->flags |= ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT;
6212 /* Consider the update of tf_flags here. */
6213 if( isom_update_moof_entry_size( moof ) )
6214 return -1;
6215 /* Now, we can calculate offsets in the current movie fragment, so do it. */
6216 for( lsmash_entry_t *entry = moof->traf_list->head; entry; entry = entry->next )
6218 isom_traf_entry_t *traf = (isom_traf_entry_t *)entry->data;
6219 traf->tfhd->base_data_offset = root->size + moof->size + ISOM_BASEBOX_COMMON_SIZE;
6221 if( isom_write_moof( root->bs, moof ) )
6222 return -1;
6223 root->size += moof->size;
6224 /* Output samples. */
6225 return isom_output_fragment_media_data( root );
6228 #undef GET_MOST_USED
6230 static isom_trun_optional_row_t *isom_request_trun_optional_row( isom_trun_entry_t *trun, isom_tfhd_t *tfhd, uint32_t sample_number )
6232 isom_trun_optional_row_t *row = NULL;
6233 if( !trun->optional )
6235 trun->optional = lsmash_create_entry_list();
6236 if( !trun->optional )
6237 return NULL;
6239 if( trun->optional->entry_count < sample_number )
6241 while( trun->optional->entry_count < sample_number )
6243 row = malloc( sizeof(isom_trun_optional_row_t) );
6244 if( !row )
6245 return NULL;
6246 /* Copy from default. */
6247 row->sample_duration = tfhd->default_sample_duration;
6248 row->sample_size = tfhd->default_sample_size;
6249 row->sample_flags = tfhd->default_sample_flags;
6250 row->sample_composition_time_offset = 0;
6251 if( lsmash_add_entry( trun->optional, row ) )
6253 free( row );
6254 return NULL;
6257 return row;
6259 uint32_t i = 0;
6260 for( lsmash_entry_t *entry = trun->optional->head; entry; entry = entry->next )
6262 row = (isom_trun_optional_row_t *)entry->data;
6263 if( !row )
6264 return NULL;
6265 if( ++i == sample_number )
6266 return row;
6268 return NULL;
6271 int lsmash_create_fragment_empty_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t duration )
6273 if( !root || !root->fragment || !root->fragment->movie || !root->moov )
6274 return -1;
6275 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6276 if( !trak || !trak->tkhd )
6277 return -1;
6278 isom_trex_entry_t *trex = isom_get_trex( root->moov->mvex, track_ID );
6279 if( !trex )
6280 return -1;
6281 isom_moof_entry_t *moof = root->fragment->movie;
6282 isom_traf_entry_t *traf = isom_get_traf( moof, track_ID );
6283 if( traf )
6284 return -1;
6285 traf = isom_add_traf( root, moof );
6286 if( isom_add_tfhd( traf ) )
6287 return -1;
6288 isom_tfhd_t *tfhd = traf->tfhd;
6289 tfhd->flags = ISOM_TF_FLAGS_DURATION_IS_EMPTY; /* no samples for this track fragment yet */
6290 tfhd->track_ID = trak->tkhd->track_ID;
6291 tfhd->default_sample_duration = duration;
6292 if( duration != trex->default_sample_duration )
6293 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
6294 traf->cache = trak->cache;
6295 traf->cache->fragment->traf_number = moof->traf_list->entry_count;
6296 traf->cache->fragment->last_duration += duration; /* The duration of the last sample includes this empty-duration. */
6297 return 0;
6300 static int isom_set_fragment_last_duration( isom_traf_entry_t *traf, uint32_t last_duration )
6302 isom_tfhd_t *tfhd = traf->tfhd;
6303 if( !traf->trun_list || !traf->trun_list->tail || !traf->trun_list->tail->data )
6305 /* There are no track runs in this track fragment, so it is a empty-duration. */
6306 isom_trex_entry_t *trex = isom_get_trex( traf->root->moov->mvex, tfhd->track_ID );
6307 if( !trex )
6308 return -1;
6309 tfhd->flags |= ISOM_TF_FLAGS_DURATION_IS_EMPTY;
6310 if( last_duration != trex->default_sample_duration )
6311 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
6312 tfhd->default_sample_duration = last_duration;
6313 traf->cache->fragment->last_duration = last_duration;
6314 return 0;
6316 /* Update the last sample_duration if needed. */
6317 isom_trun_entry_t *trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
6318 if( trun->sample_count == 1 && traf->trun_list->entry_count == 1 )
6320 isom_trex_entry_t *trex = isom_get_trex( traf->root->moov->mvex, tfhd->track_ID );
6321 if( !trex )
6322 return -1;
6323 if( last_duration != trex->default_sample_duration )
6324 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
6325 tfhd->default_sample_duration = last_duration;
6327 else if( last_duration != tfhd->default_sample_duration )
6328 trun->flags |= ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT;
6329 if( trun->flags )
6331 isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, trun->sample_count );
6332 if( !row )
6333 return -1;
6334 row->sample_duration = last_duration;
6336 traf->cache->fragment->last_duration = last_duration;
6337 return 0;
6340 int lsmash_set_last_sample_delta( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_delta )
6342 if( !root || !track_ID )
6343 return -1;
6344 if( root->fragment && root->fragment->movie )
6346 isom_traf_entry_t *traf = isom_get_traf( root->fragment->movie, track_ID );
6347 if( !traf || !traf->cache || !traf->tfhd || !traf->trun_list )
6348 return -1;
6349 return isom_set_fragment_last_duration( traf, sample_delta );
6351 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6352 if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->minf || !trak->mdia->minf->stbl
6353 || !trak->mdia->minf->stbl->stsz || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list )
6354 return -1;
6355 isom_stbl_t *stbl = trak->mdia->minf->stbl;
6356 isom_stts_t *stts = stbl->stts;
6357 uint32_t sample_count = isom_get_sample_count( trak );
6358 if( !stts->list->tail )
6360 if( !sample_count )
6361 return 0; /* no samples */
6362 if( sample_count > 1 )
6363 return -1; /* irregular sample_count */
6364 if( isom_add_stts_entry( stbl, sample_delta ) )
6365 return -1;
6366 return lsmash_update_track_duration( root, track_ID, 0 );
6368 uint32_t i = 0;
6369 for( lsmash_entry_t *entry = stts->list->head; entry; entry = entry->next )
6370 i += ((isom_stts_entry_t *)entry->data)->sample_count;
6371 if( sample_count < i )
6372 return -1;
6373 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stts->list->tail->data;
6374 if( !last_stts_data )
6375 return -1;
6376 if( sample_count > i )
6378 if( sample_count - i > 1 )
6379 return -1;
6380 /* Add a sample_delta. */
6381 if( sample_delta == last_stts_data->sample_delta )
6382 ++ last_stts_data->sample_count;
6383 else if( isom_add_stts_entry( stbl, sample_delta ) )
6384 return -1;
6386 else if( sample_count == i && isom_replace_last_sample_delta( stbl, sample_delta ) )
6387 return -1;
6388 return lsmash_update_track_duration( root, track_ID, sample_delta );
6391 void lsmash_discard_boxes( lsmash_root_t *root )
6393 if( !root )
6394 return;
6395 isom_remove_ftyp( root->ftyp );
6396 isom_remove_moov( root );
6397 lsmash_remove_list( root->moof_list, isom_remove_moof );
6398 isom_remove_mdat( root->mdat );
6399 isom_remove_free( root->free );
6400 isom_remove_meta( root->meta );
6401 isom_remove_mfra( root->mfra );
6402 root->ftyp = NULL;
6403 root->moov = NULL;
6404 root->moof_list = NULL;
6405 root->mdat = NULL;
6406 root->free = NULL;
6407 root->mfra = NULL;
6410 void lsmash_destroy_root( lsmash_root_t *root )
6412 if( !root )
6413 return;
6414 #ifdef LSMASH_DEMUXER_ENABLED
6415 isom_remove_print_funcs( root );
6416 isom_remove_timelines( root );
6417 #endif
6418 lsmash_discard_boxes( root );
6419 if( root->bs )
6421 if( root->bs->stream )
6422 fclose( root->bs->stream );
6423 if( root->bs->data )
6424 free( root->bs->data );
6425 free( root->bs );
6427 if( root->fragment )
6429 lsmash_remove_list( root->fragment->pool, lsmash_delete_sample );
6430 free( root->fragment );
6432 free( root );
6435 /*---- timeline manipulator ----*/
6437 int lsmash_modify_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t edit_number, lsmash_edit_t edit )
6439 if( !edit.duration || edit.start_time < -1 )
6440 return -1;
6441 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6442 if( !trak || !trak->edts || !trak->edts->elst || !trak->edts->elst->list )
6443 return -1;
6444 isom_elst_t *elst = trak->edts->elst;
6445 isom_elst_entry_t *data = (isom_elst_entry_t *)lsmash_get_entry_data( elst->list, edit_number );
6446 if( !data )
6447 return -1;
6448 data->segment_duration = edit.duration;
6449 data->media_time = edit.start_time;
6450 data->media_rate = edit.rate;
6451 if( !elst->pos || !root->fragment || root->bs->stream == stdout )
6452 return isom_update_tkhd_duration( trak );
6453 /* Rewrite the specified entry.
6454 * Note: we don't update the version of the Edit List Box. */
6455 lsmash_bs_t *bs = root->bs;
6456 FILE *stream = bs->stream;
6457 uint64_t current_pos = lsmash_ftell( stream );
6458 uint64_t entry_pos = elst->pos + ISOM_LIST_FULLBOX_COMMON_SIZE + ((uint64_t)edit_number - 1) * (elst->version == 1 ? 20 : 12);
6459 lsmash_fseek( stream, entry_pos, SEEK_SET );
6460 if( elst->version )
6462 lsmash_bs_put_be64( bs, data->segment_duration );
6463 lsmash_bs_put_be64( bs, data->media_time );
6465 else
6467 lsmash_bs_put_be32( bs, (uint32_t)LSMASH_MIN( data->segment_duration, UINT32_MAX ) );
6468 lsmash_bs_put_be32( bs, (uint32_t)data->media_time );
6470 lsmash_bs_put_be32( bs, data->media_rate );
6471 int ret = lsmash_bs_write_data( bs );
6472 lsmash_fseek( stream, current_pos, SEEK_SET );
6473 return ret;
6476 int lsmash_create_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, lsmash_edit_t edit )
6478 if( edit.start_time < -1 )
6479 return -1;
6480 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6481 if( !trak || !trak->tkhd )
6482 return -1;
6483 edit.duration = (edit.duration || root->fragment) ? edit.duration
6484 : trak->tkhd->duration ? trak->tkhd->duration
6485 : isom_update_tkhd_duration( trak ) ? 0
6486 : trak->tkhd->duration;
6487 if( isom_add_edts( trak )
6488 || isom_add_elst( trak->edts )
6489 || isom_add_elst_entry( trak->edts->elst, edit.duration, edit.start_time, edit.rate ) )
6490 return -1;
6491 return isom_update_tkhd_duration( trak );
6494 int lsmash_get_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t edit_number, lsmash_edit_t *edit )
6496 if( !edit )
6497 return -1;
6498 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6499 if( !trak )
6500 return -1;
6501 if( !trak->edts || !trak->edts->elst )
6503 /* no edits */
6504 edit->duration = 0;
6505 edit->start_time = 0;
6506 edit->rate = 0;
6507 return 0;
6509 isom_elst_entry_t *elst = (isom_elst_entry_t *)lsmash_get_entry_data( trak->edts->elst->list, edit_number );
6510 if( !elst )
6511 return -1;
6512 edit->duration = elst->segment_duration;
6513 edit->start_time = elst->media_time;
6514 edit->rate = elst->media_rate;
6515 return 0;
6518 uint32_t lsmash_count_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID )
6520 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6521 if( !trak || !trak->edts || !trak->edts->elst || !trak->edts->elst->list )
6522 return 0;
6523 return trak->edts->elst->list->entry_count;
6526 /*---- create / modification time fields manipulators ----*/
6528 int lsmash_update_media_modification_time( lsmash_root_t *root, uint32_t track_ID )
6530 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6531 if( !trak || !trak->mdia || !trak->mdia->mdhd )
6532 return -1;
6533 isom_mdhd_t *mdhd = trak->mdia->mdhd;
6534 mdhd->modification_time = isom_get_current_mp4time();
6535 /* overwrite strange creation_time */
6536 if( mdhd->creation_time > mdhd->modification_time )
6537 mdhd->creation_time = mdhd->modification_time;
6538 return 0;
6541 int lsmash_update_track_modification_time( lsmash_root_t *root, uint32_t track_ID )
6543 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
6544 if( !trak || !trak->tkhd )
6545 return -1;
6546 isom_tkhd_t *tkhd = trak->tkhd;
6547 tkhd->modification_time = isom_get_current_mp4time();
6548 /* overwrite strange creation_time */
6549 if( tkhd->creation_time > tkhd->modification_time )
6550 tkhd->creation_time = tkhd->modification_time;
6551 return 0;
6554 int lsmash_update_movie_modification_time( lsmash_root_t *root )
6556 if( !root || !root->moov || !root->moov->mvhd )
6557 return -1;
6558 isom_mvhd_t *mvhd = root->moov->mvhd;
6559 mvhd->modification_time = isom_get_current_mp4time();
6560 /* overwrite strange creation_time */
6561 if( mvhd->creation_time > mvhd->modification_time )
6562 mvhd->creation_time = mvhd->modification_time;
6563 return 0;
6566 /*---- sample manipulators ----*/
6567 lsmash_sample_t *lsmash_create_sample( uint32_t size )
6569 lsmash_sample_t *sample = lsmash_malloc_zero( sizeof(lsmash_sample_t) );
6570 if( !sample )
6571 return NULL;
6572 if( !size )
6573 return sample;
6574 sample->data = malloc( size );
6575 if( !sample->data )
6577 free( sample );
6578 return NULL;
6580 sample->length = size;
6581 return sample;
6584 int lsmash_sample_alloc( lsmash_sample_t *sample, uint32_t size )
6586 if( !sample )
6587 return -1;
6588 if( !size )
6590 if( sample->data )
6591 free( sample->data );
6592 sample->data = NULL;
6593 sample->length = 0;
6594 return 0;
6596 if( size == sample->length )
6597 return 0;
6598 uint8_t *data;
6599 if( !sample->data )
6600 data = malloc( size );
6601 else
6602 data = realloc( sample->data, size );
6603 if( !data )
6604 return -1;
6605 sample->data = data;
6606 sample->length = size;
6607 return 0;
6610 void lsmash_delete_sample( lsmash_sample_t *sample )
6612 if( !sample )
6613 return;
6614 if( sample->data )
6615 free( sample->data );
6616 free( sample );
6619 isom_sample_pool_t *isom_create_sample_pool( uint64_t size )
6621 isom_sample_pool_t *pool = lsmash_malloc_zero( sizeof(isom_sample_pool_t) );
6622 if( !pool )
6623 return NULL;
6624 if( size == 0 )
6625 return pool;
6626 pool->data = malloc( size );
6627 if( !pool->data )
6629 free( pool );
6630 return NULL;
6632 pool->alloc = size;
6633 return pool;
6636 static void isom_remove_sample_pool( isom_sample_pool_t *pool )
6638 if( !pool )
6639 return;
6640 if( pool->data )
6641 free( pool->data );
6642 free( pool );
6645 static uint32_t isom_add_size( isom_trak_entry_t *trak, uint32_t sample_size )
6647 if( isom_add_stsz_entry( trak->mdia->minf->stbl, sample_size ) )
6648 return 0;
6649 return isom_get_sample_count( trak );
6652 static uint32_t isom_add_dts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t dts )
6654 isom_stts_t *stts = stbl->stts;
6655 if( !stts->list->entry_count )
6657 if( isom_add_stts_entry( stbl, dts ) )
6658 return 0;
6659 cache->dts = dts;
6660 return dts;
6662 if( dts <= cache->dts )
6663 return 0;
6664 uint32_t sample_delta = dts - cache->dts;
6665 isom_stts_entry_t *data = (isom_stts_entry_t *)stts->list->tail->data;
6666 if( data->sample_delta == sample_delta )
6667 ++ data->sample_count;
6668 else if( isom_add_stts_entry( stbl, sample_delta ) )
6669 return 0;
6670 cache->dts = dts;
6671 return sample_delta;
6674 static int isom_add_cts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t cts )
6676 isom_ctts_t *ctts = stbl->ctts;
6677 if( !ctts )
6679 if( cts == cache->dts )
6681 cache->cts = cts;
6682 return 0;
6684 /* Add ctts box and the first ctts entry. */
6685 if( isom_add_ctts( stbl ) || isom_add_ctts_entry( stbl, 0 ) )
6686 return -1;
6687 ctts = stbl->ctts;
6688 isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->head->data;
6689 uint32_t sample_count = stbl->stsz->sample_count;
6690 if( sample_count != 1 )
6692 data->sample_count = sample_count - 1;
6693 if( isom_add_ctts_entry( stbl, cts - cache->dts ) )
6694 return -1;
6696 else
6697 data->sample_offset = cts;
6698 cache->cts = cts;
6699 return 0;
6701 if( !ctts->list )
6702 return -1;
6703 isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->tail->data;
6704 uint32_t sample_offset = cts - cache->dts;
6705 if( data->sample_offset == sample_offset )
6706 ++ data->sample_count;
6707 else if( isom_add_ctts_entry( stbl, sample_offset ) )
6708 return -1;
6709 cache->cts = cts;
6710 return 0;
6713 static int isom_add_timestamp( isom_trak_entry_t *trak, uint64_t dts, uint64_t cts )
6715 if( !trak->cache || !trak->mdia->minf->stbl->stts || !trak->mdia->minf->stbl->stts->list )
6716 return -1;
6717 lsmash_root_t *root = trak->root;
6718 if( root->isom_compatible && root->qt_compatible && (cts - dts) > INT32_MAX )
6719 return -1; /* sample_offset is not compatible. */
6720 isom_stbl_t *stbl = trak->mdia->minf->stbl;
6721 isom_timestamp_t *ts_cache = &trak->cache->timestamp;
6722 uint32_t sample_count = isom_get_sample_count( trak );
6723 uint32_t sample_delta = sample_count > 1 ? isom_add_dts( stbl, ts_cache, dts ) : 0;
6724 if( sample_count > 1 && !sample_delta )
6725 return -1;
6726 if( isom_add_cts( stbl, ts_cache, cts ) )
6727 return -1;
6728 if( (cts + ts_cache->ctd_shift) < dts )
6730 if( (root->max_isom_version < 4 && !root->qt_compatible) /* Negative sample offset is not supported. */
6731 || (root->max_isom_version >= 4 && trak->root->qt_compatible) /* ctts version 1 is not defined in QTFF. */
6732 || ((dts - cts) > INT32_MAX) ) /* Overflow */
6733 return -1;
6734 ts_cache->ctd_shift = dts - cts;
6735 if( stbl->ctts->version == 0 && !trak->root->qt_compatible )
6736 stbl->ctts->version = 1;
6738 if( trak->cache->fragment )
6740 isom_fragment_t *fragment_cache = trak->cache->fragment;
6741 fragment_cache->last_duration = sample_delta;
6742 fragment_cache->largest_cts = LSMASH_MAX( ts_cache->cts, fragment_cache->largest_cts );
6744 return 0;
6747 static int isom_add_sync_point( isom_trak_entry_t *trak, uint32_t sample_number, lsmash_sample_property_t *prop )
6749 isom_stbl_t *stbl = trak->mdia->minf->stbl;
6750 isom_cache_t *cache = trak->cache;
6751 if( !(prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC) ) /* no null check for prop */
6753 if( !cache->all_sync )
6754 return 0;
6755 if( !stbl->stss && isom_add_stss( stbl ) )
6756 return -1;
6757 if( isom_add_stss_entry( stbl, 1 ) ) /* Declare here the first sample is a sync sample. */
6758 return -1;
6759 cache->all_sync = 0;
6760 return 0;
6762 if( cache->all_sync ) /* We don't need stss box if all samples are sync sample. */
6763 return 0;
6764 if( !stbl->stss )
6766 if( isom_get_sample_count( trak ) == 1 )
6768 cache->all_sync = 1; /* Also the first sample is a sync sample. */
6769 return 0;
6771 if( isom_add_stss( stbl ) )
6772 return -1;
6774 return isom_add_stss_entry( stbl, sample_number );
6777 static int isom_add_partial_sync( isom_trak_entry_t *trak, uint32_t sample_number, lsmash_sample_property_t *prop )
6779 if( !trak->root->qt_compatible )
6780 return 0;
6781 if( !(prop->ra_flags & QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC)
6782 && !(LSMASH_IS_POST_ROLL_START( prop->ra_flags ) && prop->post_roll.identifier == prop->post_roll.complete) )
6783 return 0;
6784 /* This sample is a partial sync sample. */
6785 isom_stbl_t *stbl = trak->mdia->minf->stbl;
6786 if( !stbl->stps && isom_add_stps( stbl ) )
6787 return -1;
6788 return isom_add_stps_entry( stbl, sample_number );
6791 static int isom_add_dependency_type( isom_trak_entry_t *trak, lsmash_sample_property_t *prop )
6793 if( !trak->root->qt_compatible && !trak->root->avc_extensions )
6794 return 0;
6795 uint8_t avc_extensions = trak->root->avc_extensions;
6796 isom_stbl_t *stbl = trak->mdia->minf->stbl;
6797 if( stbl->sdtp )
6798 return isom_add_sdtp_entry( (isom_box_t *)stbl, prop, avc_extensions );
6799 if( !prop->allow_earlier && !prop->leading && !prop->independent && !prop->disposable && !prop->redundant ) /* no null check for prop */
6800 return 0;
6801 if( isom_add_sdtp( (isom_box_t *)stbl ) )
6802 return -1;
6803 uint32_t count = isom_get_sample_count( trak );
6804 /* fill past samples with ISOM_SAMPLE_*_UNKNOWN */
6805 lsmash_sample_property_t null_prop = { 0 };
6806 for( uint32_t i = 1; i < count; i++ )
6807 if( isom_add_sdtp_entry( (isom_box_t *)stbl, &null_prop, avc_extensions ) )
6808 return -1;
6809 return isom_add_sdtp_entry( (isom_box_t *)stbl, prop, avc_extensions );
6812 static int isom_rap_grouping_established( isom_rap_group_t *group, int num_leading_samples_known, isom_sgpd_entry_t *sgpd )
6814 isom_rap_entry_t *rap = group->random_access;
6815 if( !rap )
6816 return 0;
6817 assert( rap == (isom_rap_entry_t *)sgpd->list->tail->data );
6818 rap->num_leading_samples_known = num_leading_samples_known;
6819 /* Avoid duplication of sample group descriptions. */
6820 uint32_t group_description_index = 1;
6821 for( lsmash_entry_t *entry = sgpd->list->head; entry != sgpd->list->tail; entry = entry->next )
6823 isom_rap_entry_t *data = (isom_rap_entry_t *)entry->data;
6824 if( !data )
6825 return -1;
6826 if( rap->num_leading_samples_known == data->num_leading_samples_known
6827 && rap->num_leading_samples == data->num_leading_samples )
6829 /* The same description already exists.
6830 * Remove the latest random access entry. */
6831 lsmash_remove_entry_direct( sgpd->list, sgpd->list->tail, NULL );
6832 /* Replace assigned group_description_index with the one corresponding the same description. */
6833 if( group->assignment->group_description_index == 0 )
6835 if( group->prev_assignment )
6836 group->prev_assignment->group_description_index = group_description_index;
6838 else
6839 group->assignment->group_description_index = group_description_index;
6840 break;
6842 ++group_description_index;
6844 group->random_access = NULL;
6845 return 0;
6848 static int isom_group_random_access( isom_trak_entry_t *trak, lsmash_sample_property_t *prop )
6850 if( trak->root->max_isom_version < 6 )
6851 return 0;
6852 isom_stbl_t *stbl = trak->mdia->minf->stbl;
6853 isom_sbgp_entry_t *sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_RAP );
6854 isom_sgpd_entry_t *sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
6855 if( !sbgp || !sgpd )
6856 return 0;
6857 uint8_t is_rap = (prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC)
6858 || (prop->ra_flags & QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC)
6859 || (prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP)
6860 || (LSMASH_IS_POST_ROLL_START( prop->ra_flags ) && prop->post_roll.identifier == prop->post_roll.complete);
6861 isom_rap_group_t *group = trak->cache->rap;
6862 if( !group )
6864 /* This sample is the first sample, create a grouping cache. */
6865 assert( isom_get_sample_count( trak ) == 1 );
6866 group = malloc( sizeof(isom_rap_group_t) );
6867 if( !group )
6868 return -1;
6869 if( is_rap )
6871 group->random_access = isom_add_rap_group_entry( sgpd );
6872 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count );
6874 else
6876 /* The first sample is not always random access point. */
6877 group->random_access = NULL;
6878 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
6880 if( !group->assignment )
6882 free( group );
6883 return -1;
6885 group->prev_assignment = NULL;
6886 group->is_prev_rap = is_rap;
6887 trak->cache->rap = group;
6888 return 0;
6890 if( group->is_prev_rap )
6892 /* OK. here, the previous sample is a menber of 'rap '. */
6893 if( !is_rap )
6895 /* This sample isn't a member of 'rap ' and the previous sample is.
6896 * So we create a new group and set 0 on its group_description_index. */
6897 group->prev_assignment = group->assignment;
6898 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
6899 if( !group->assignment )
6901 free( group );
6902 return -1;
6905 else if( !LSMASH_IS_CLOSED_RAP( prop->ra_flags ) )
6907 /* Create a new group since there is the possibility the next sample is a leading sample.
6908 * This sample is a member of 'rap ', so we set appropriate value on its group_description_index. */
6909 if( isom_rap_grouping_established( group, 1, sgpd ) )
6910 return -1;
6911 group->random_access = isom_add_rap_group_entry( sgpd );
6912 group->prev_assignment = group->assignment;
6913 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count );
6914 if( !group->assignment )
6916 free( group );
6917 return -1;
6920 else /* The previous and current sample are a member of 'rap ', and the next sample must not be a leading sample. */
6921 ++ group->assignment->sample_count;
6923 else if( is_rap )
6925 /* This sample is a member of 'rap ' and the previous sample isn't.
6926 * So we create a new group and set appropriate value on its group_description_index. */
6927 if( isom_rap_grouping_established( group, 1, sgpd ) )
6928 return -1;
6929 group->random_access = isom_add_rap_group_entry( sgpd );
6930 group->prev_assignment = group->assignment;
6931 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count );
6932 if( !group->assignment )
6934 free( group );
6935 return -1;
6938 else /* The previous and current sample aren't a member of 'rap '. */
6939 ++ group->assignment->sample_count;
6940 /* Obtain the property of the latest random access point group. */
6941 if( !is_rap && group->random_access )
6943 if( prop->leading == ISOM_SAMPLE_LEADING_UNKNOWN )
6945 /* We can no longer know num_leading_samples in this group. */
6946 if( isom_rap_grouping_established( group, 0, sgpd ) )
6947 return -1;
6949 else
6951 if( prop->leading == ISOM_SAMPLE_IS_UNDECODABLE_LEADING || prop->leading == ISOM_SAMPLE_IS_DECODABLE_LEADING )
6952 ++ group->random_access->num_leading_samples;
6953 /* no more consecutive leading samples in this group */
6954 else if( isom_rap_grouping_established( group, 1, sgpd ) )
6955 return -1;
6958 group->is_prev_rap = is_rap;
6959 return 0;
6962 static int isom_roll_grouping_established( isom_roll_group_t *group, int16_t roll_distance, isom_sgpd_entry_t *sgpd )
6964 /* Avoid duplication of sample group descriptions. */
6965 uint32_t group_description_index = 1;
6966 for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next )
6968 isom_roll_entry_t *data = (isom_roll_entry_t *)entry->data;
6969 if( !data )
6970 return -1;
6971 if( roll_distance == data->roll_distance )
6973 /* The same description already exists.
6974 * Set the group_description_index corresponding the same description. */
6975 group->assignment->group_description_index = group_description_index;
6976 group->described = 1;
6977 return 0;
6979 ++group_description_index;
6981 /* Add a new roll recovery description. */
6982 if( !isom_add_roll_group_entry( sgpd, roll_distance ) )
6983 return -1;
6984 group->assignment->group_description_index = sgpd->list->entry_count;
6985 group->described = 1;
6986 return 0;
6989 static int isom_deduplicate_roll_group( isom_sbgp_entry_t *sbgp, lsmash_entry_list_t *pool )
6991 /* Deduplication */
6992 uint32_t current_group_number = sbgp->list->entry_count - pool->entry_count + 1;
6993 isom_group_assignment_entry_t *prev_assignment = (isom_group_assignment_entry_t *)lsmash_get_entry_data( sbgp->list, current_group_number - 1 );
6994 for( lsmash_entry_t *entry = pool->head; entry; )
6996 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
6997 if( !group || !group->assignment )
6998 return -1;
6999 if( !group->delimited || !group->described )
7000 return 0;
7001 if( prev_assignment && prev_assignment->group_description_index == group->assignment->group_description_index )
7003 /* Merge the current group with the previous. */
7004 lsmash_entry_t *next_entry = entry->next;
7005 prev_assignment->sample_count += group->assignment->sample_count;
7006 if( lsmash_remove_entry( sbgp->list, current_group_number, NULL )
7007 || lsmash_remove_entry_direct( pool, entry, NULL ) )
7008 return -1;
7009 entry = next_entry;
7011 else
7013 entry = entry->next;
7014 prev_assignment = group->assignment;
7015 ++current_group_number;
7018 return 0;
7021 /* Remove pooled caches that has become unnecessary. */
7022 static int isom_clean_roll_pool( isom_sbgp_entry_t *sbgp, lsmash_entry_list_t *pool )
7024 for( lsmash_entry_t *entry = pool->head; entry; entry = pool->head )
7026 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
7027 if( !group )
7028 return -1;
7029 if( !group->delimited || !group->described )
7030 return 0;
7031 if( lsmash_remove_entry_direct( pool, entry, NULL ) )
7032 return -1;
7034 return 0;
7037 static int isom_flush_roll_pool( isom_sbgp_entry_t *sbgp, lsmash_entry_list_t *pool )
7039 if( isom_deduplicate_roll_group( sbgp, pool ) )
7040 return -1;
7041 return isom_clean_roll_pool( sbgp, pool );
7044 static int isom_all_recovery_described( isom_sbgp_entry_t *sbgp, lsmash_entry_list_t *pool )
7046 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
7048 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
7049 if( !group )
7050 return -1;
7051 group->described = 1;
7053 return isom_flush_roll_pool( sbgp, pool );
7056 static int isom_group_roll_recovery( isom_trak_entry_t *trak, lsmash_sample_property_t *prop )
7058 if( !trak->root->avc_extensions && !trak->root->qt_compatible )
7059 return 0;
7060 isom_stbl_t *stbl = trak->mdia->minf->stbl;
7061 isom_sbgp_entry_t *sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_ROLL );
7062 isom_sgpd_entry_t *sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_ROLL );
7063 if( !sbgp || !sgpd )
7064 return 0;
7065 lsmash_entry_list_t *pool = trak->cache->roll.pool;
7066 if( !pool )
7068 pool = lsmash_create_entry_list();
7069 if( !pool )
7070 return -1;
7071 trak->cache->roll.pool = pool;
7073 isom_roll_group_t *group = (isom_roll_group_t *)lsmash_get_entry_data( pool, pool->entry_count );
7074 uint32_t sample_count = isom_get_sample_count( trak );
7075 int is_recovery_start = LSMASH_IS_POST_ROLL_START( prop->ra_flags );
7076 int valid_pre_roll = !is_recovery_start && (prop->ra_flags != ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE)
7077 && (prop->pre_roll.distance > 0) && (prop->pre_roll.distance <= -INT16_MIN);
7078 int new_group = !group || is_recovery_start || (group->prev_is_recovery_start != is_recovery_start);
7079 if( !new_group )
7081 /* Check pre-roll distance. */
7082 if( !group->assignment )
7083 return -1;
7084 isom_roll_entry_t *prev_roll = (isom_roll_entry_t *)lsmash_get_entry_data( sgpd->list, group->assignment->group_description_index );
7085 if( !prev_roll )
7086 new_group = valid_pre_roll;
7087 else if( !valid_pre_roll || (prop->pre_roll.distance != -prev_roll->roll_distance) )
7088 /* Pre-roll distance is different from the previous. */
7089 new_group = 1;
7091 if( new_group )
7093 if( group )
7094 group->delimited = 1;
7095 else
7096 assert( sample_count == 1 );
7097 /* Create a new group. */
7098 group = lsmash_malloc_zero( sizeof(isom_roll_group_t) );
7099 if( !group )
7100 return -1;
7101 group->prev_is_recovery_start = is_recovery_start;
7102 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
7103 if( !group->assignment || lsmash_add_entry( pool, group ) )
7105 free( group );
7106 return -1;
7108 if( is_recovery_start )
7110 /* a member of non-roll or post-roll group */
7111 group->first_sample = sample_count;
7112 group->recovery_point = prop->post_roll.complete;
7114 else
7116 if( valid_pre_roll )
7118 /* a member of pre-roll group */
7119 if( isom_roll_grouping_established( group, -prop->pre_roll.distance, sgpd ) )
7120 return -1;
7122 else
7123 /* a member of non-roll group */
7124 group->described = 1;
7127 else
7129 group->prev_is_recovery_start = is_recovery_start;
7130 ++ group->assignment->sample_count;
7132 /* If encountered a sync sample, all recovery is completed here. */
7133 if( prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC )
7134 return isom_all_recovery_described( sbgp, pool );
7135 /* Check whether this sample is a random access recovery point or not. */
7136 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
7138 group = (isom_roll_group_t *)entry->data;
7139 if( !group )
7140 return -1;
7141 if( group->described )
7142 continue;
7143 if( prop->post_roll.identifier == group->recovery_point )
7145 int16_t distance = sample_count - group->first_sample;
7146 /* Add a roll recovery entry only when roll_distance isn't zero since roll_distance = 0 must not be used. */
7147 if( distance )
7149 /* Now, this group is a 'roll'. */
7150 if( isom_roll_grouping_established( group, distance, sgpd ) )
7151 return -1;
7152 /* All groups before the current group are described. */
7153 lsmash_entry_t *current = entry;
7154 for( entry = pool->head; entry != current; entry = entry->next )
7156 group = (isom_roll_group_t *)entry->data;
7157 if( !group )
7158 return -1;
7159 group->described = 1;
7162 else
7163 group->described = 1;
7164 break; /* Avoid evaluating groups, in the pool, having the same identifier for recovery point again. */
7167 return isom_flush_roll_pool( sbgp, pool );
7170 /* returns 1 if pooled samples must be flushed. */
7171 /* FIXME: I wonder if this function should have a extra argument which indicates force_to_flush_cached_chunk.
7172 see lsmash_append_sample for detail. */
7173 static int isom_add_chunk( isom_trak_entry_t *trak, lsmash_sample_t *sample )
7175 if( !trak->root || !trak->cache || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale
7176 || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
7177 return -1;
7178 lsmash_root_t *root = trak->root;
7179 isom_chunk_t *current = &trak->cache->chunk;
7180 if( !current->pool )
7182 /* Very initial settings, just once per track */
7183 current->pool = isom_create_sample_pool( 0 );
7184 if( !current->pool )
7185 return -1;
7187 if( !current->pool->sample_count )
7189 /* Cannot decide whether we should flush the current sample or not here yet. */
7190 ++ current->chunk_number;
7191 current->sample_description_index = sample->index;
7192 current->first_dts = sample->dts;
7193 return 0;
7195 if( sample->dts < current->first_dts )
7196 return -1; /* easy error check. */
7197 if( (root->max_chunk_duration >= ((double)(sample->dts - current->first_dts) / trak->mdia->mdhd->timescale))
7198 && (root->max_chunk_size >= current->pool->size + sample->length)
7199 && (current->sample_description_index == sample->index) )
7200 return 0; /* No need to flush current cached chunk, the current sample must be put into that. */
7201 /* NOTE: chunk relative stuff must be pushed into root after a chunk is fully determined with its contents. */
7202 /* now current cached chunk is fixed, actually add chunk relative properties to root accordingly. */
7203 isom_stbl_t *stbl = trak->mdia->minf->stbl;
7204 isom_stsc_entry_t *last_stsc_data = stbl->stsc->list->tail ? (isom_stsc_entry_t *)stbl->stsc->list->tail->data : NULL;
7205 /* Create a new chunk sequence in this track if needed. */
7206 if( (!last_stsc_data
7207 || current->pool->sample_count != last_stsc_data->samples_per_chunk
7208 || current->sample_description_index != last_stsc_data->sample_description_index)
7209 && isom_add_stsc_entry( stbl, current->chunk_number, current->pool->sample_count, current->sample_description_index ) )
7210 return -1;
7211 /* Add a new chunk offset in this track. */
7212 uint64_t offset = root->size;
7213 if( root->fragment )
7214 offset += ISOM_BASEBOX_COMMON_SIZE + root->fragment->pool_size;
7215 if( isom_add_stco_entry( stbl, offset ) )
7216 return -1;
7217 /* update cache information */
7218 ++ current->chunk_number;
7219 /* re-initialize cache, using the current sample */
7220 current->sample_description_index = sample->index;
7221 current->first_dts = sample->dts;
7222 /* current->pool must be flushed in isom_append_sample_internal() */
7223 return 1;
7226 static int isom_write_pooled_samples( lsmash_root_t *root, isom_sample_pool_t *pool )
7228 if( !root || !root->mdat || !root->bs || !root->bs->stream )
7229 return -1;
7230 lsmash_bs_put_bytes( root->bs, pool->size, pool->data );
7231 if( lsmash_bs_write_data( root->bs ) )
7232 return -1;
7233 root->mdat->size += pool->size;
7234 root->size += pool->size;
7235 pool->sample_count = 0;
7236 pool->size = 0;
7237 return 0;
7240 static int isom_update_sample_tables( isom_trak_entry_t *trak, lsmash_sample_t *sample )
7242 /* Add a sample_size and increment sample_count. */
7243 uint32_t sample_count = isom_add_size( trak, sample->length );
7244 if( !sample_count )
7245 return -1;
7246 /* Add a decoding timestamp and a composition timestamp. */
7247 if( isom_add_timestamp( trak, sample->dts, sample->cts ) )
7248 return -1;
7249 /* Add a sync point if needed. */
7250 if( isom_add_sync_point( trak, sample_count, &sample->prop ) )
7251 return -1;
7252 /* Add a partial sync point if needed. */
7253 if( isom_add_partial_sync( trak, sample_count, &sample->prop ) )
7254 return -1;
7255 /* Add leading, independent, disposable and redundant information if needed. */
7256 if( isom_add_dependency_type( trak, &sample->prop ) )
7257 return -1;
7258 /* Group samples into random access point type if needed. */
7259 if( isom_group_random_access( trak, &sample->prop ) )
7260 return -1;
7261 /* Group samples into random access recovery point type if needed. */
7262 if( isom_group_roll_recovery( trak, &sample->prop ) )
7263 return -1;
7264 /* Add a chunk if needed. */
7265 return isom_add_chunk( trak, sample );
7268 static int isom_append_fragment_track_run( lsmash_root_t *root, isom_chunk_t *chunk )
7270 if( !chunk->pool || !chunk->pool->size )
7271 return 0;
7272 isom_fragment_manager_t *fragment = root->fragment;
7273 /* Move data in the pool of the current track fragment to the pool of the current movie fragment.
7274 * Empty the pool of current track. We don't delete data of samples here. */
7275 if( lsmash_add_entry( fragment->pool, chunk->pool ) )
7276 return -1;
7277 fragment->pool->entry_count += chunk->pool->sample_count;
7278 fragment->pool_size += chunk->pool->size;
7279 chunk->pool = isom_create_sample_pool( chunk->pool->size );
7280 return chunk->pool ? 0 : -1;
7283 static int isom_output_cached_chunk( isom_trak_entry_t *trak )
7285 lsmash_root_t *root = trak->root;
7286 isom_chunk_t *chunk = &trak->cache->chunk;
7287 isom_stbl_t *stbl = trak->mdia->minf->stbl;
7288 isom_stsc_entry_t *last_stsc_data = stbl->stsc->list->tail ? (isom_stsc_entry_t *)stbl->stsc->list->tail->data : NULL;
7289 /* Create a new chunk sequence in this track if needed. */
7290 if( (!last_stsc_data
7291 || chunk->pool->sample_count != last_stsc_data->samples_per_chunk
7292 || chunk->sample_description_index != last_stsc_data->sample_description_index)
7293 && isom_add_stsc_entry( stbl, chunk->chunk_number, chunk->pool->sample_count, chunk->sample_description_index ) )
7294 return -1;
7295 if( root->fragment )
7297 /* Add a new chunk offset in this track. */
7298 if( isom_add_stco_entry( stbl, root->size + ISOM_BASEBOX_COMMON_SIZE + root->fragment->pool_size ) )
7299 return -1;
7300 return isom_append_fragment_track_run( root, chunk );
7302 /* Add a new chunk offset in this track. */
7303 if( isom_add_stco_entry( stbl, root->size ) )
7304 return -1;
7305 /* Output pooled samples in this track. */
7306 return isom_write_pooled_samples( root, chunk->pool );
7309 static int isom_pool_sample( isom_sample_pool_t *pool, lsmash_sample_t *sample )
7311 uint64_t pool_size = pool->size + sample->length;
7312 if( pool->alloc < pool_size )
7314 uint8_t *data;
7315 uint64_t alloc = pool_size + (1<<16);
7316 if( !pool->data )
7317 data = malloc( alloc );
7318 else
7319 data = realloc( pool->data, alloc );
7320 if( !data )
7321 return -1;
7322 pool->data = data;
7323 pool->alloc = alloc;
7325 memcpy( pool->data + pool->size, sample->data, sample->length );
7326 pool->size = pool_size;
7327 pool->sample_count += 1;
7328 lsmash_delete_sample( sample );
7329 return 0;
7332 static int isom_append_sample_internal( isom_trak_entry_t *trak, lsmash_sample_t *sample )
7334 int flush = isom_update_sample_tables( trak, sample );
7335 if( flush < 0 )
7336 return -1;
7337 /* flush == 1 means pooled samples must be flushed. */
7338 lsmash_root_t *root = trak->root;
7339 isom_sample_pool_t *current_pool = trak->cache->chunk.pool;
7340 if( flush == 1 && isom_write_pooled_samples( root, current_pool ) )
7341 return -1;
7342 /* Arbitration system between tracks with extremely scattering dts.
7343 * Here, we check whether asynchronization between the tracks exceeds the tolerance.
7344 * If a track has too old "first DTS" in its cached chunk than current sample's DTS, then its pooled samples must be flushed.
7345 * We don't consider presentation of media since any edit can pick an arbitrary portion of media in track.
7346 * Note: you needn't read this loop until you grasp the basic handling of chunks. */
7347 double tolerance = root->max_async_tolerance;
7348 for( lsmash_entry_t *entry = root->moov->trak_list->head; entry; entry = entry->next )
7350 isom_trak_entry_t *other = (isom_trak_entry_t *)entry->data;
7351 if( trak == other )
7352 continue;
7353 if( !other || !other->cache || !other->mdia || !other->mdia->mdhd || !other->mdia->mdhd->timescale
7354 || !other->mdia->minf || !other->mdia->minf->stbl || !other->mdia->minf->stbl->stsc || !other->mdia->minf->stbl->stsc->list )
7355 return -1;
7356 isom_chunk_t *chunk = &other->cache->chunk;
7357 if( !chunk->pool || !chunk->pool->sample_count )
7358 continue;
7359 double diff = ((double)sample->dts / trak->mdia->mdhd->timescale)
7360 - ((double)chunk->first_dts / other->mdia->mdhd->timescale);
7361 if( diff > tolerance && isom_output_cached_chunk( other ) )
7362 return -1;
7363 /* Note: we don't flush the cached chunk in the current track and the current sample here
7364 * even if the conditional expression of '-diff > tolerance' meets.
7365 * That's useless because appending a sample to another track would be a good equivalent.
7366 * It's even harmful because it causes excess chunk division by calling
7367 * isom_output_cached_chunk() which always generates a new chunk.
7368 * Anyway some excess chunk division will be there, but rather less without it.
7369 * To completely avoid this, we need to observe at least whether the current sample will be placed
7370 * right next to the previous chunk of the same track or not. */
7372 /* anyway the current sample must be pooled. */
7373 return isom_pool_sample( current_pool, sample );
7376 static int isom_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample )
7378 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
7379 if( !trak || !trak->root || !trak->cache || !trak->mdia
7380 || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale
7381 || !trak->mdia->minf || !trak->mdia->minf->stbl
7382 || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list
7383 || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
7384 return -1;
7385 /* If there is no available Media Data Box to write samples, add and write a new one before any chunk offset is decided. */
7386 if( !root->mdat )
7388 if( isom_new_mdat( root, 0 ) )
7389 return -1;
7390 /* Add the size of the Media Data Box and the placeholder. */
7391 root->size += 2 * ISOM_BASEBOX_COMMON_SIZE;
7393 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, sample->index );
7394 if( !sample_entry )
7395 return -1;
7396 if( isom_is_lpcm_audio( sample_entry ) )
7398 uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket;
7399 if( sample->length == frame_size )
7400 return isom_append_sample_internal( trak, sample );
7401 else if( sample->length < frame_size )
7402 return -1;
7403 /* Append samples splitted into each LPCMFrame. */
7404 uint64_t dts = sample->dts;
7405 uint64_t cts = sample->cts;
7406 for( uint32_t offset = 0; offset < sample->length; offset += frame_size )
7408 lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size );
7409 if( !lpcm_sample )
7410 return -1;
7411 memcpy( lpcm_sample->data, sample->data + offset, frame_size );
7412 lpcm_sample->dts = dts++;
7413 lpcm_sample->cts = cts++;
7414 lpcm_sample->prop = sample->prop;
7415 lpcm_sample->index = sample->index;
7416 if( isom_append_sample_internal( trak, lpcm_sample ) )
7418 lsmash_delete_sample( lpcm_sample );
7419 return -1;
7422 lsmash_delete_sample( sample );
7423 return 0;
7425 return isom_append_sample_internal( trak, sample );
7428 static int isom_output_cache( isom_trak_entry_t *trak )
7430 if( trak->cache->chunk.pool && trak->cache->chunk.pool->sample_count
7431 && isom_output_cached_chunk( trak ) )
7432 return -1;
7433 isom_stbl_t *stbl = trak->mdia->minf->stbl;
7434 if( !stbl->sgpd_list )
7435 return 0;
7436 for( lsmash_entry_t *entry = stbl->sgpd_list->head; entry; entry = entry->next )
7438 isom_sgpd_entry_t *sgpd = (isom_sgpd_entry_t *)entry->data;
7439 if( !sgpd )
7440 return -1;
7441 switch( sgpd->grouping_type )
7443 case ISOM_GROUP_TYPE_RAP :
7445 isom_rap_group_t *group = trak->cache->rap;
7446 if( !group )
7448 if( trak->root->fragment )
7449 continue;
7450 else
7451 return -1;
7453 if( !group->random_access )
7454 continue;
7455 group->random_access->num_leading_samples_known = 1;
7456 break;
7458 case ISOM_GROUP_TYPE_ROLL :
7459 if( !trak->cache->roll.pool )
7461 if( trak->root->fragment )
7462 continue;
7463 else
7464 return -1;
7466 for( lsmash_entry_t *roll_entry = trak->cache->roll.pool->head; roll_entry; roll_entry = roll_entry->next )
7468 isom_roll_group_t *group = (isom_roll_group_t *)roll_entry->data;
7469 if( !group )
7470 return -1;
7471 group->described = 1;
7472 group->delimited = 1;
7474 isom_sbgp_entry_t *sbgp = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_ROLL );
7475 if( isom_flush_roll_pool( sbgp, trak->cache->roll.pool ) )
7476 return -1;
7477 break;
7478 default :
7479 break;
7482 return 0;
7485 static int isom_flush_fragment_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_duration )
7487 isom_traf_entry_t *traf = isom_get_traf( root->fragment->movie, track_ID );
7488 if( !traf )
7489 return 0; /* no samples */
7490 if( !traf->cache || !traf->cache->fragment )
7491 return -1;
7492 if( traf->trun_list && traf->trun_list->entry_count && traf->trun_list->tail && traf->trun_list->tail->data )
7494 /* Media Data Box preceded by Movie Fragment Box could change base_data_offsets in each track fragments later.
7495 * We can't consider this here because the length of Movie Fragment Box is unknown at this step yet. */
7496 isom_trun_entry_t *trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
7497 if( root->fragment->pool_size )
7498 trun->flags |= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT;
7499 trun->data_offset = root->fragment->pool_size;
7501 if( isom_append_fragment_track_run( root, &traf->cache->chunk ) )
7502 return -1;
7503 return isom_set_fragment_last_duration( traf, last_sample_duration );
7506 int lsmash_flush_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta )
7508 if( !root )
7509 return -1;
7510 if( root->fragment && root->fragment->movie )
7511 return isom_flush_fragment_pooled_samples( root, track_ID, last_sample_delta );
7512 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
7513 if( !trak || !trak->cache || !trak->mdia || !trak->mdia->minf || !trak->mdia->minf->stbl
7514 || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
7515 return -1;
7516 if( isom_output_cache( trak ) )
7517 return -1;
7518 return lsmash_set_last_sample_delta( root, track_ID, last_sample_delta );
7521 /* This function doesn't update sample_duration of the last sample in the previous movie fragment.
7522 * Instead of this, isom_finish_movie_fragment undertakes this task. */
7523 static int isom_update_fragment_previous_sample_duration( isom_traf_entry_t *traf, isom_trex_entry_t *trex, uint32_t duration )
7525 isom_tfhd_t *tfhd = traf->tfhd;
7526 isom_trun_entry_t *trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
7527 int previous_run_has_previous_sample = 0;
7528 if( trun->sample_count == 1 )
7530 if( traf->trun_list->entry_count == 1 )
7531 return 0; /* The previous track run belongs to the previous movie fragment if it exists. */
7532 if( !traf->trun_list->tail->prev || !traf->trun_list->tail->prev->data )
7533 return -1;
7534 /* OK. The previous sample exists in the previous track run in the same track fragment. */
7535 trun = (isom_trun_entry_t *)traf->trun_list->tail->prev->data;
7536 previous_run_has_previous_sample = 1;
7538 /* Update default_sample_duration of the Track Fragment Header Box
7539 * if this duration is what the first sample in the current track fragment owns. */
7540 if( (trun->sample_count == 2 && traf->trun_list->entry_count == 1)
7541 || (trun->sample_count == 1 && traf->trun_list->entry_count == 2) )
7543 if( duration != trex->default_sample_duration )
7544 tfhd->flags |= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT;
7545 tfhd->default_sample_duration = duration;
7547 /* Update the previous sample_duration if needed. */
7548 if( duration != tfhd->default_sample_duration )
7549 trun->flags |= ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT;
7550 if( trun->flags )
7552 uint32_t sample_number = trun->sample_count - !previous_run_has_previous_sample;
7553 isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, sample_number );
7554 if( !row )
7555 return -1;
7556 row->sample_duration = duration;
7558 traf->cache->fragment->last_duration = duration;
7559 return 0;
7562 static isom_sample_flags_t isom_generate_fragment_sample_flags( lsmash_sample_t *sample )
7564 isom_sample_flags_t flags;
7565 flags.reserved = 0;
7566 flags.is_leading = sample->prop.leading & 0x3;
7567 flags.sample_depends_on = sample->prop.independent & 0x3;
7568 flags.sample_is_depended_on = sample->prop.disposable & 0x3;
7569 flags.sample_has_redundancy = sample->prop.redundant & 0x3;
7570 flags.sample_padding_value = 0;
7571 flags.sample_is_non_sync_sample = !(sample->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC);
7572 flags.sample_degradation_priority = 0;
7573 return flags;
7576 static int isom_update_fragment_sample_tables( isom_traf_entry_t *traf, lsmash_sample_t *sample )
7578 isom_tfhd_t *tfhd = traf->tfhd;
7579 isom_trex_entry_t *trex = isom_get_trex( traf->root->moov->mvex, tfhd->track_ID );
7580 if( !trex )
7581 return -1;
7582 lsmash_root_t *root = traf->root;
7583 isom_cache_t *cache = traf->cache;
7584 isom_chunk_t *current = &cache->chunk;
7585 if( !current->pool )
7587 /* Very initial settings, just once per track */
7588 current->pool = isom_create_sample_pool( 0 );
7589 if( !current->pool )
7590 return -1;
7592 /* Create a new track run if the duration exceeds max_chunk_duration.
7593 * Old one will be appended to the pool of this movie fragment. */
7594 int delimit = (root->max_chunk_duration < ((double)(sample->dts - current->first_dts) / lsmash_get_media_timescale( root, tfhd->track_ID )))
7595 || (root->max_chunk_size < (current->pool->size + sample->length));
7596 isom_trun_entry_t *trun = NULL;
7597 if( !traf->trun_list || !traf->trun_list->entry_count || delimit )
7599 if( delimit && traf->trun_list && traf->trun_list->entry_count && traf->trun_list->tail && traf->trun_list->tail->data )
7601 /* Media Data Box preceded by Movie Fragment Box could change base data offsets in each track fragments later.
7602 * We can't consider this here because the length of Movie Fragment Box is unknown at this step yet. */
7603 trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
7604 if( root->fragment->pool_size )
7605 trun->flags |= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT;
7606 trun->data_offset = root->fragment->pool_size;
7608 trun = isom_add_trun( traf );
7609 if( !trun )
7610 return -1;
7612 else
7614 if( !traf->trun_list->tail || !traf->trun_list->tail->data )
7615 return -1;
7616 trun = (isom_trun_entry_t *)traf->trun_list->tail->data;
7618 isom_sample_flags_t sample_flags = isom_generate_fragment_sample_flags( sample );
7619 if( ++trun->sample_count == 1 )
7621 if( traf->trun_list->entry_count == 1 )
7623 /* This track fragment isn't empty-duration-fragment any more. */
7624 tfhd->flags &= ~ISOM_TF_FLAGS_DURATION_IS_EMPTY;
7625 /* Set up sample_description_index in this track fragment. */
7626 if( sample->index != trex->default_sample_description_index )
7627 tfhd->flags |= ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT;
7628 tfhd->sample_description_index = current->sample_description_index = sample->index;
7629 /* Set up default_sample_size used in this track fragment. */
7630 tfhd->default_sample_size = sample->length;
7631 /* Set up default_sample_flags used in this track fragment.
7632 * Note: we decide an appropriate default value at the end of this movie fragment. */
7633 tfhd->default_sample_flags = sample_flags;
7634 /* Set up random access information if this sample is a sync sample.
7635 * We inform only the first sample in each movie fragment. */
7636 if( root->bs->stream != stdout && (sample->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC) )
7638 isom_tfra_entry_t *tfra = isom_get_tfra( root->mfra, tfhd->track_ID );
7639 if( !tfra )
7641 tfra = isom_add_tfra( root->mfra );
7642 if( !tfra )
7643 return -1;
7644 tfra->track_ID = tfhd->track_ID;
7646 if( !tfra->list )
7648 tfra->list = lsmash_create_entry_list();
7649 if( !tfra->list )
7650 return -1;
7652 isom_tfra_location_time_entry_t *rap = malloc( sizeof(isom_tfra_location_time_entry_t) );
7653 if( !rap )
7654 return -1;
7655 rap->time = sample->cts; /* Set composition timestamp temporally.
7656 * At the end of the whole movie, this will be reset as presentation time. */
7657 rap->moof_offset = root->size; /* We place Movie Fragment Box in the head of each movie fragment. */
7658 rap->traf_number = cache->fragment->traf_number;
7659 rap->trun_number = traf->trun_list->entry_count;
7660 rap->sample_number = trun->sample_count;
7661 if( lsmash_add_entry( tfra->list, rap ) )
7663 free( rap );
7664 return -1;
7666 tfra->number_of_entry = tfra->list->entry_count;
7667 int length;
7668 for( length = 1; rap->traf_number >> (length * 8); length++ );
7669 tfra->length_size_of_traf_num = LSMASH_MAX( length - 1, tfra->length_size_of_traf_num );
7670 for( length = 1; rap->traf_number >> (length * 8); length++ );
7671 tfra->length_size_of_trun_num = LSMASH_MAX( length - 1, tfra->length_size_of_trun_num );
7672 for( length = 1; rap->sample_number >> (length * 8); length++ );
7673 tfra->length_size_of_sample_num = LSMASH_MAX( length - 1, tfra->length_size_of_sample_num );
7675 /* Set up the base media decode time of this track fragment.
7676 * This feature is available under ISO Base Media version 6 or later. */
7677 if( root->max_isom_version >= 6 )
7679 assert( !traf->tfdt );
7680 if( isom_add_tfdt( traf ) )
7681 return -1;
7682 if( sample->dts > UINT32_MAX )
7683 traf->tfdt->version = 1;
7684 traf->tfdt->baseMediaDecodeTime = sample->dts;
7687 trun->first_sample_flags = sample_flags;
7688 current->first_dts = sample->dts;
7690 /* Update the optional rows in the current track run except for sample_duration if needed. */
7691 if( sample->length != tfhd->default_sample_size )
7692 trun->flags |= ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT;
7693 if( isom_compare_sample_flags( &sample_flags, &tfhd->default_sample_flags ) )
7694 trun->flags |= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT;
7695 uint32_t sample_composition_time_offset = sample->cts - sample->dts;
7696 if( sample_composition_time_offset )
7698 trun->flags |= ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT;
7699 /* Check if negative composition time offset is present. */
7700 isom_timestamp_t *ts_cache = &cache->timestamp;
7701 if( (sample->cts + ts_cache->ctd_shift) < sample->dts )
7703 if( root->max_isom_version < 6 )
7704 return -1; /* Negative composition time offset is not supported. */
7705 if( (sample->dts - sample->cts) > INT32_MAX )
7706 return -1; /* Overflow */
7707 ts_cache->ctd_shift = sample->dts - sample->cts;
7708 if( trun->version == 0 && root->max_isom_version >= 6 )
7709 trun->version = 1;
7712 if( trun->flags )
7714 isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, trun->sample_count );
7715 if( !row )
7716 return -1;
7717 row->sample_size = sample->length;
7718 row->sample_flags = sample_flags;
7719 row->sample_composition_time_offset = sample_composition_time_offset;
7721 /* Set up the previous sample_duration if this sample is not the first sample in the overall movie. */
7722 if( cache->fragment->has_samples )
7724 /* Note: when using for live streaming, it is not good idea to return error (-1) by sample->dts < prev_dts
7725 * since that's trivial for such semi-permanent presentation. */
7726 uint64_t prev_dts = cache->timestamp.dts;
7727 if( sample->dts <= prev_dts || sample->dts > prev_dts + UINT32_MAX )
7728 return -1;
7729 uint32_t sample_duration = sample->dts - prev_dts;
7730 if( isom_update_fragment_previous_sample_duration( traf, trex, sample_duration ) )
7731 return -1;
7733 cache->timestamp.dts = sample->dts;
7734 cache->fragment->largest_cts = LSMASH_MAX( sample->cts, cache->fragment->largest_cts );
7735 return delimit;
7738 static int isom_append_fragment_sample_internal_initial( isom_trak_entry_t *trak, lsmash_sample_t *sample )
7740 int delimit = 0;
7741 /* Update the sample tables of this track fragment.
7742 * If a new chunk was created, append the previous one to the pool of this movie fragment. */
7743 delimit = isom_update_sample_tables( trak, sample );
7744 if( delimit < 0 )
7745 return -1;
7746 else if( delimit == 1 )
7747 isom_append_fragment_track_run( trak->root, &trak->cache->chunk );
7748 /* Add a new sample into the pool of this track fragment. */
7749 if( isom_pool_sample( trak->cache->chunk.pool, sample ) )
7750 return -1;
7751 trak->cache->fragment->has_samples = 1;
7752 return 0;
7755 static int isom_append_fragment_sample_internal( isom_traf_entry_t *traf, lsmash_sample_t *sample )
7757 int delimit = 0;
7758 /* Update the sample tables of this track fragment.
7759 * If a new track run was created, append the previous one to the pool of this movie fragment. */
7760 delimit = isom_update_fragment_sample_tables( traf, sample );
7761 if( delimit < 0 )
7762 return -1;
7763 else if( delimit == 1 )
7764 isom_append_fragment_track_run( traf->root, &traf->cache->chunk );
7765 /* Add a new sample into the pool of this track fragment. */
7766 if( isom_pool_sample( traf->cache->chunk.pool, sample ) )
7767 return -1;
7768 traf->cache->fragment->has_samples = 1;
7769 return 0;
7772 static int isom_append_fragment_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample )
7774 isom_fragment_manager_t *fragment = root->fragment;
7775 if( !fragment || !fragment->pool )
7776 return -1;
7777 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
7778 if( !trak || !trak->root || !trak->cache || !trak->cache->fragment || !trak->tkhd || !trak->mdia
7779 || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale
7780 || !trak->mdia->minf || !trak->mdia->minf->stbl
7781 || !trak->mdia->minf->stbl->stsd || !trak->mdia->minf->stbl->stsd->list
7782 || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
7783 return -1;
7784 int (*append_sample_func)( void *, lsmash_sample_t * ) = NULL;
7785 void *track_fragment = NULL;
7786 if( !fragment->movie )
7788 append_sample_func = (int (*)( void *, lsmash_sample_t * ))isom_append_fragment_sample_internal_initial;
7789 track_fragment = trak;
7791 else
7793 isom_traf_entry_t *traf = isom_get_traf( fragment->movie, track_ID );
7794 if( !traf )
7796 traf = isom_add_traf( root, fragment->movie );
7797 if( isom_add_tfhd( traf ) )
7798 return -1;
7799 traf->tfhd->flags = ISOM_TF_FLAGS_DURATION_IS_EMPTY; /* no samples for this track fragment yet */
7800 traf->tfhd->track_ID = trak->tkhd->track_ID;
7801 traf->cache = trak->cache;
7802 traf->cache->fragment->traf_number = fragment->movie->traf_list->entry_count;
7804 else if( !traf->root || !traf->root->moov || !traf->root->moov->mvex || !traf->cache || !traf->tfhd )
7805 return -1;
7806 append_sample_func = (int (*)( void *, lsmash_sample_t * ))isom_append_fragment_sample_internal;
7807 track_fragment = traf;
7809 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( trak->mdia->minf->stbl->stsd->list, sample->index );
7810 if( !sample_entry )
7811 return -1;
7812 if( isom_is_lpcm_audio( sample_entry ) )
7814 uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket;
7815 if( sample->length == frame_size )
7816 return append_sample_func( track_fragment, sample );
7817 else if( sample->length < frame_size )
7818 return -1;
7819 /* Append samples splitted into each LPCMFrame. */
7820 uint64_t dts = sample->dts;
7821 uint64_t cts = sample->cts;
7822 for( uint32_t offset = 0; offset < sample->length; offset += frame_size )
7824 lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size );
7825 if( !lpcm_sample )
7826 return -1;
7827 memcpy( lpcm_sample->data, sample->data + offset, frame_size );
7828 lpcm_sample->dts = dts++;
7829 lpcm_sample->cts = cts++;
7830 lpcm_sample->prop = sample->prop;
7831 lpcm_sample->index = sample->index;
7832 if( append_sample_func( track_fragment, lpcm_sample ) )
7834 lsmash_delete_sample( lpcm_sample );
7835 return -1;
7838 lsmash_delete_sample( sample );
7839 return 0;
7841 return append_sample_func( track_fragment, sample );
7844 int lsmash_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample )
7846 /* We think max_chunk_duration == 0, which means all samples will be cached on memory, should be prevented.
7847 * This means removal of a feature that we used to have, but anyway very alone chunk does not make sense. */
7848 if( !root || !root->bs || !sample || !sample->data || !track_ID
7849 || root->max_chunk_duration == 0 || root->max_async_tolerance == 0 )
7850 return -1;
7851 /* Write File Type Box here if it was not written yet. */
7852 if( !root->file_type_written && isom_write_ftyp( root ) )
7853 return -1;
7854 if( root->fragment && root->fragment->pool )
7855 return isom_append_fragment_sample( root, track_ID, sample );
7856 return isom_append_sample( root, track_ID, sample );
7859 /*---- misc functions ----*/
7861 int lsmash_delete_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID )
7863 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
7864 if( !trak )
7865 return -1;
7866 isom_remove_edts( trak->edts );
7867 trak->edts = NULL;
7868 return isom_update_tkhd_duration( trak );
7871 void lsmash_delete_tyrant_chapter( lsmash_root_t *root )
7873 if( !root || !root->moov || !root->moov->udta )
7874 return;
7875 isom_remove_chpl( root->moov->udta->chpl );
7876 root->moov->udta->chpl = NULL;
7879 int lsmash_set_copyright( lsmash_root_t *root, uint32_t track_ID, uint16_t ISO_language, char *notice )
7881 if( !root || !root->moov || !root->isom_compatible || (ISO_language && ISO_language < 0x800) || !notice )
7882 return -1;
7883 isom_udta_t *udta;
7884 if( track_ID )
7886 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
7887 if( !trak || (!trak->udta && isom_add_udta( root, track_ID )) )
7888 return -1;
7889 udta = trak->udta;
7891 else
7893 if( !root->moov->udta && isom_add_udta( root, 0 ) )
7894 return -1;
7895 udta = root->moov->udta;
7897 assert( udta );
7898 if( udta->cprt_list )
7899 for( lsmash_entry_t *entry = udta->cprt_list->head; entry; entry = entry->next )
7901 isom_cprt_t *cprt = (isom_cprt_t *)entry->data;
7902 if( !cprt || cprt->language == ISO_language )
7903 return -1;
7905 if( isom_add_cprt( udta ) )
7906 return -1;
7907 isom_cprt_t *cprt = (isom_cprt_t *)udta->cprt_list->tail->data;
7908 cprt->language = ISO_language;
7909 cprt->notice_length = strlen( notice ) + 1;
7910 cprt->notice = lsmash_memdup( notice, cprt->notice_length );
7911 return 0;