MSVC: add missing source files.
[L-SMASH.git] / core / isom.c
blob857af678018a0d8bf66fcf15389321a84c196a92
1 /*****************************************************************************
2 * isom.c:
3 *****************************************************************************
4 * Copyright (C) 2010-2015 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 "common/internal.h" /* must be placed first */
26 #include <stdlib.h>
27 #include <string.h>
28 #include <inttypes.h>
30 #include "box.h"
31 #include "file.h"
32 #include "fragment.h"
33 #include "read.h"
34 #include "timeline.h"
35 #include "write.h"
37 #include "codecs/mp4a.h"
38 #include "codecs/mp4sys.h"
39 #include "codecs/description.h"
41 /*---- ----*/
42 int isom_check_initializer_present( lsmash_root_t *root )
44 if( !root
45 || !root->file
46 || !root->file->initializer )
47 return LSMASH_ERR_NAMELESS;
48 return 0;
51 isom_trak_t *isom_get_trak( lsmash_file_t *file, uint32_t track_ID )
53 if( track_ID == 0
54 || !file
55 || file != file->initializer
56 || !file->moov )
57 return NULL;
58 for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next )
60 isom_trak_t *trak = (isom_trak_t *)entry->data;
61 if( !trak
62 || !trak->tkhd )
63 return NULL;
64 if( trak->tkhd->track_ID == track_ID )
65 return trak;
67 return NULL;
70 isom_trex_t *isom_get_trex( isom_mvex_t *mvex, uint32_t track_ID )
72 if( track_ID == 0 || !mvex )
73 return NULL;
74 for( lsmash_entry_t *entry = mvex->trex_list.head; entry; entry = entry->next )
76 isom_trex_t *trex = (isom_trex_t *)entry->data;
77 if( !trex )
78 return NULL;
79 if( trex->track_ID == track_ID )
80 return trex;
82 return NULL;
85 isom_traf_t *isom_get_traf( isom_moof_t *moof, uint32_t track_ID )
87 if( track_ID == 0 || !moof )
88 return NULL;
89 for( lsmash_entry_t *entry = moof->traf_list.head; entry; entry = entry->next )
91 isom_traf_t *traf = (isom_traf_t *)entry->data;
92 if( !traf
93 || !traf->tfhd )
94 return NULL;
95 if( traf->tfhd->track_ID == track_ID )
96 return traf;
98 return NULL;
101 isom_tfra_t *isom_get_tfra( isom_mfra_t *mfra, uint32_t track_ID )
103 if( track_ID == 0 || !mfra )
104 return NULL;
105 for( lsmash_entry_t *entry = mfra->tfra_list.head; entry; entry = entry->next )
107 isom_tfra_t *tfra = (isom_tfra_t *)entry->data;
108 if( !tfra )
109 return NULL;
110 if( tfra->track_ID == track_ID )
111 return tfra;
113 return NULL;
116 static int isom_add_elst_entry( isom_elst_t *elst, uint64_t segment_duration, int64_t media_time, int32_t media_rate )
118 assert( elst->file );
119 isom_elst_entry_t *data = lsmash_malloc( sizeof(isom_elst_entry_t) );
120 if( !data )
121 return LSMASH_ERR_MEMORY_ALLOC;
122 data->segment_duration = segment_duration;
123 data->media_time = media_time;
124 data->media_rate = media_rate;
125 if( lsmash_add_entry( elst->list, data ) < 0 )
127 lsmash_free( data );
128 return LSMASH_ERR_MEMORY_ALLOC;
130 if( !elst->file->undefined_64_ver
131 && (data->segment_duration > UINT32_MAX
132 || data->media_time > INT32_MAX
133 || data->media_time < INT32_MIN) )
134 elst->version = 1;
135 return 0;
138 /* This function returns 0 if failed, sample_entry_number if succeeded. */
139 int lsmash_add_sample_entry( lsmash_root_t *root, uint32_t track_ID, void *summary )
141 if( !root || !summary
142 || ((lsmash_summary_t *)summary)->data_ref_index == 0
143 || ((lsmash_summary_t *)summary)->data_ref_index > UINT16_MAX )
144 return 0;
145 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
146 if( !trak
147 || !trak->file
148 || !trak->mdia
149 || !trak->mdia->hdlr
150 || !trak->mdia->minf
151 || !trak->mdia->minf->stbl
152 || !trak->mdia->minf->stbl->stsd )
153 return 0;
154 isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd;
155 lsmash_media_type media_type = trak->mdia->hdlr->componentSubtype;
156 if( isom_setup_sample_description( stsd, media_type, (lsmash_summary_t *)summary ) < 0 )
157 return 0;
158 else
159 return stsd->list.entry_count;
162 static int isom_add_stts_entry( isom_stbl_t *stbl, uint32_t sample_delta )
164 if( !stbl
165 || !stbl->stts
166 || !stbl->stts->list )
167 return LSMASH_ERR_NAMELESS;
168 isom_stts_entry_t *data = lsmash_malloc( sizeof(isom_stts_entry_t) );
169 if( !data )
170 return LSMASH_ERR_MEMORY_ALLOC;
171 data->sample_count = 1;
172 data->sample_delta = sample_delta;
173 if( lsmash_add_entry( stbl->stts->list, data ) < 0 )
175 lsmash_free( data );
176 return LSMASH_ERR_MEMORY_ALLOC;
178 return 0;
181 static int isom_add_ctts_entry( isom_stbl_t *stbl, uint32_t sample_offset )
183 if( !stbl
184 || !stbl->ctts
185 || !stbl->ctts->list )
186 return LSMASH_ERR_NAMELESS;
187 isom_ctts_entry_t *data = lsmash_malloc( sizeof(isom_ctts_entry_t) );
188 if( !data )
189 return LSMASH_ERR_MEMORY_ALLOC;
190 data->sample_count = 1;
191 data->sample_offset = sample_offset;
192 if( lsmash_add_entry( stbl->ctts->list, data ) < 0 )
194 lsmash_free( data );
195 return LSMASH_ERR_MEMORY_ALLOC;
197 return 0;
200 static int isom_add_stsc_entry( isom_stbl_t *stbl, uint32_t first_chunk, uint32_t samples_per_chunk, uint32_t sample_description_index )
202 if( !stbl
203 || !stbl->stsc
204 || !stbl->stsc->list )
205 return LSMASH_ERR_NAMELESS;
206 isom_stsc_entry_t *data = lsmash_malloc( sizeof(isom_stsc_entry_t) );
207 if( !data )
208 return LSMASH_ERR_MEMORY_ALLOC;
209 data->first_chunk = first_chunk;
210 data->samples_per_chunk = samples_per_chunk;
211 data->sample_description_index = sample_description_index;
212 if( lsmash_add_entry( stbl->stsc->list, data ) < 0 )
214 lsmash_free( data );
215 return LSMASH_ERR_MEMORY_ALLOC;
217 return 0;
220 static int isom_add_stsz_entry( isom_stbl_t *stbl, uint32_t entry_size )
222 if( !stbl
223 || !stbl->stsz )
224 return LSMASH_ERR_NAMELESS;
225 isom_stsz_t *stsz = stbl->stsz;
226 /* retrieve initial sample_size */
227 if( stsz->sample_count == 0 )
228 stsz->sample_size = entry_size;
229 /* if it seems constant sample size at present, update sample_count only */
230 if( !stsz->list && stsz->sample_size == entry_size )
232 ++ stsz->sample_count;
233 return 0;
235 /* found sample_size varies, create sample_size list */
236 if( !stsz->list )
238 stsz->list = lsmash_create_entry_list();
239 if( !stsz->list )
240 return LSMASH_ERR_MEMORY_ALLOC;
241 for( uint32_t i = 0; i < stsz->sample_count; i++ )
243 isom_stsz_entry_t *data = lsmash_malloc( sizeof(isom_stsz_entry_t) );
244 if( !data )
245 return LSMASH_ERR_MEMORY_ALLOC;
246 data->entry_size = stsz->sample_size;
247 if( lsmash_add_entry( stsz->list, data ) < 0 )
249 lsmash_free( data );
250 return LSMASH_ERR_MEMORY_ALLOC;
253 stsz->sample_size = 0;
255 isom_stsz_entry_t *data = lsmash_malloc( sizeof(isom_stsz_entry_t) );
256 if( !data )
257 return LSMASH_ERR_MEMORY_ALLOC;
258 data->entry_size = entry_size;
259 if( lsmash_add_entry( stsz->list, data ) < 0 )
261 lsmash_free( data );
262 return LSMASH_ERR_MEMORY_ALLOC;
264 ++ stsz->sample_count;
265 return 0;
268 static int isom_add_stss_entry( isom_stbl_t *stbl, uint32_t sample_number )
270 if( !stbl
271 || !stbl->stss
272 || !stbl->stss->list )
273 return LSMASH_ERR_NAMELESS;
274 isom_stss_entry_t *data = lsmash_malloc( sizeof(isom_stss_entry_t) );
275 if( !data )
276 return LSMASH_ERR_MEMORY_ALLOC;
277 data->sample_number = sample_number;
278 if( lsmash_add_entry( stbl->stss->list, data ) < 0 )
280 lsmash_free( data );
281 return LSMASH_ERR_MEMORY_ALLOC;
283 return 0;
286 static int isom_add_stps_entry( isom_stbl_t *stbl, uint32_t sample_number )
288 if( !stbl
289 || !stbl->stps
290 || !stbl->stps->list )
291 return LSMASH_ERR_NAMELESS;
292 isom_stps_entry_t *data = lsmash_malloc( sizeof(isom_stps_entry_t) );
293 if( !data )
294 return LSMASH_ERR_MEMORY_ALLOC;
295 data->sample_number = sample_number;
296 if( lsmash_add_entry( stbl->stps->list, data ) < 0 )
298 lsmash_free( data );
299 return LSMASH_ERR_MEMORY_ALLOC;
301 return 0;
304 /* Between ISOBMFF and QTFF, the most significant 2-bit has different meaning.
305 * For the maximum compatibility, we set 0 to the most significant 2-bit when compatible with
306 * both ISOBMFF with AVCFF extensions and QTFF.
307 * compatibility == 0 -> neither AVCFF extensions nor QTFF compatible
308 * compatibility == 1 -> AVCFF extensions compatible
309 * compatibility == 2 -> QTFF compatible
310 * compatibility == 3 -> both AVCFF extensions and QTFF compatible */
311 static int isom_add_sdtp_entry( isom_box_t *parent, lsmash_sample_property_t *prop, int compatibility )
313 if( !prop || !parent )
314 return LSMASH_ERR_NAMELESS;
315 isom_sdtp_t *sdtp = NULL;
316 if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) )
317 sdtp = ((isom_stbl_t *)parent)->sdtp;
318 else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) )
319 sdtp = ((isom_traf_t *)parent)->sdtp;
320 else
321 assert( 0 );
322 if( !sdtp
323 || !sdtp->list )
324 return LSMASH_ERR_NAMELESS;
325 isom_sdtp_entry_t *data = lsmash_malloc( sizeof(isom_sdtp_entry_t) );
326 if( !data )
327 return LSMASH_ERR_MEMORY_ALLOC;
328 if( compatibility == 1 )
329 data->is_leading = prop->leading & 0x03;
330 else if( compatibility == 2 )
331 data->is_leading = prop->allow_earlier & 0x03;
332 else
334 data->is_leading = 0;
335 assert( compatibility == 3 );
337 data->sample_depends_on = prop->independent & 0x03;
338 data->sample_is_depended_on = prop->disposable & 0x03;
339 data->sample_has_redundancy = prop->redundant & 0x03;
340 if( lsmash_add_entry( sdtp->list, data ) < 0 )
342 lsmash_free( data );
343 return LSMASH_ERR_MEMORY_ALLOC;
345 return 0;
348 static int isom_add_co64_entry( isom_stbl_t *stbl, uint64_t chunk_offset )
350 if( !stbl
351 || !stbl->stco
352 || !stbl->stco->list )
353 return LSMASH_ERR_NAMELESS;
354 isom_co64_entry_t *data = lsmash_malloc( sizeof(isom_co64_entry_t) );
355 if( !data )
356 return LSMASH_ERR_MEMORY_ALLOC;
357 data->chunk_offset = chunk_offset;
358 if( lsmash_add_entry( stbl->stco->list, data ) < 0 )
360 lsmash_free( data );
361 return LSMASH_ERR_MEMORY_ALLOC;
363 return 0;
366 static int isom_convert_stco_to_co64( isom_stbl_t *stbl )
368 /* backup stco */
369 int err = 0;
370 isom_stco_t *stco = stbl->stco;
371 stbl->stco = NULL;
372 if( !isom_add_co64( stbl ) )
374 err = LSMASH_ERR_NAMELESS;
375 goto fail;
377 /* move chunk_offset to co64 from stco */
378 for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next )
380 isom_stco_entry_t *data = (isom_stco_entry_t*)entry->data;
381 if( (err = isom_add_co64_entry( stbl, data->chunk_offset )) < 0 )
382 goto fail;
384 fail:
385 isom_remove_box_by_itself( stco );
386 return err;
389 static int isom_add_stco_entry( isom_stbl_t *stbl, uint64_t chunk_offset )
391 if( !stbl
392 || !stbl->stco
393 || !stbl->stco->list )
394 return LSMASH_ERR_NAMELESS;
395 if( stbl->stco->large_presentation )
396 return isom_add_co64_entry( stbl, chunk_offset );
397 if( chunk_offset > UINT32_MAX )
399 int err = isom_convert_stco_to_co64( stbl );
400 if( err < 0 )
401 return err;
402 return isom_add_co64_entry( stbl, chunk_offset );
404 isom_stco_entry_t *data = lsmash_malloc( sizeof(isom_stco_entry_t) );
405 if( !data )
406 return LSMASH_ERR_MEMORY_ALLOC;
407 data->chunk_offset = (uint32_t)chunk_offset;
408 if( lsmash_add_entry( stbl->stco->list, data ) < 0 )
410 lsmash_free( data );
411 return LSMASH_ERR_MEMORY_ALLOC;
413 return 0;
416 static isom_sgpd_t *isom_get_sample_group_description_common( lsmash_entry_list_t *list, uint32_t grouping_type )
418 for( lsmash_entry_t *entry = list->head; entry; entry = entry->next )
420 isom_sgpd_t *sgpd = (isom_sgpd_t *)entry->data;
421 if( !sgpd
422 || !sgpd->list )
423 return NULL;
424 if( sgpd->grouping_type == grouping_type )
425 return sgpd;
427 return NULL;
430 static isom_sbgp_t *isom_get_sample_to_group_common( lsmash_entry_list_t *list, uint32_t grouping_type )
432 for( lsmash_entry_t *entry = list->head; entry; entry = entry->next )
434 isom_sbgp_t *sbgp = (isom_sbgp_t *)entry->data;
435 if( !sbgp
436 || !sbgp->list )
437 return NULL;
438 if( sbgp->grouping_type == grouping_type )
439 return sbgp;
441 return NULL;
444 isom_sgpd_t *isom_get_sample_group_description( isom_stbl_t *stbl, uint32_t grouping_type )
446 return isom_get_sample_group_description_common( &stbl->sgpd_list, grouping_type );
449 isom_sbgp_t *isom_get_sample_to_group( isom_stbl_t *stbl, uint32_t grouping_type )
451 return isom_get_sample_to_group_common( &stbl->sbgp_list, grouping_type );
454 isom_sgpd_t *isom_get_roll_recovery_sample_group_description( lsmash_entry_list_t *list )
456 isom_sgpd_t *sgpd;
457 if( (sgpd = isom_get_sample_group_description_common( list, ISOM_GROUP_TYPE_ROLL ))
458 || (sgpd = isom_get_sample_group_description_common( list, ISOM_GROUP_TYPE_PROL )) )
459 return sgpd;
460 return NULL;
463 isom_sbgp_t *isom_get_roll_recovery_sample_to_group( lsmash_entry_list_t *list )
465 isom_sbgp_t *sbgp;
466 if( (sbgp = isom_get_sample_to_group_common( list, ISOM_GROUP_TYPE_ROLL ))
467 || (sbgp = isom_get_sample_to_group_common( list, ISOM_GROUP_TYPE_PROL )) )
468 return sbgp;
469 return NULL;
472 isom_sgpd_t *isom_get_fragment_sample_group_description( isom_traf_t *traf, uint32_t grouping_type )
474 return isom_get_sample_group_description_common( &traf->sgpd_list, grouping_type );
477 isom_sbgp_t *isom_get_fragment_sample_to_group( isom_traf_t *traf, uint32_t grouping_type )
479 return isom_get_sample_to_group_common( &traf->sbgp_list, grouping_type );
482 static isom_rap_entry_t *isom_add_rap_group_entry( isom_sgpd_t *sgpd )
484 if( !sgpd )
485 return NULL;
486 isom_rap_entry_t *data = lsmash_malloc( sizeof(isom_rap_entry_t) );
487 if( !data )
488 return NULL;
489 data->description_length = 0;
490 data->num_leading_samples_known = 0;
491 data->num_leading_samples = 0;
492 if( lsmash_add_entry( sgpd->list, data ) < 0 )
494 lsmash_free( data );
495 return NULL;
497 return data;
500 static isom_roll_entry_t *isom_add_roll_group_entry( isom_sgpd_t *sgpd, int16_t roll_distance )
502 if( !sgpd )
503 return NULL;
504 isom_roll_entry_t *data = lsmash_malloc( sizeof(isom_roll_entry_t) );
505 if( !data )
506 return NULL;
507 data->description_length = 0;
508 data->roll_distance = roll_distance;
509 if( lsmash_add_entry( sgpd->list, data ) < 0 )
511 lsmash_free( data );
512 return NULL;
514 return data;
517 static isom_group_assignment_entry_t *isom_add_group_assignment_entry( isom_sbgp_t *sbgp, uint32_t sample_count, uint32_t group_description_index )
519 if( !sbgp )
520 return NULL;
521 isom_group_assignment_entry_t *data = lsmash_malloc( sizeof(isom_group_assignment_entry_t) );
522 if( !data )
523 return NULL;
524 data->sample_count = sample_count;
525 data->group_description_index = group_description_index;
526 if( lsmash_add_entry( sbgp->list, data ) < 0 )
528 lsmash_free( data );
529 return NULL;
531 return data;
534 static uint32_t isom_get_sample_count_from_sample_table( isom_stbl_t *stbl )
536 if( stbl->stsz )
537 return stbl->stsz->sample_count;
538 else if( stbl->stz2 )
539 return stbl->stz2->sample_count;
540 else
541 return 0;
544 uint32_t isom_get_sample_count( isom_trak_t *trak )
546 if( !trak
547 || !trak->mdia
548 || !trak->mdia->minf
549 || !trak->mdia->minf->stbl )
550 return 0;
551 return isom_get_sample_count_from_sample_table( trak->mdia->minf->stbl );
554 static uint64_t isom_get_dts( isom_stts_t *stts, uint32_t sample_number )
556 if( !stts
557 || !stts->list )
558 return 0;
559 uint64_t dts = 0;
560 uint32_t i = 1;
561 lsmash_entry_t *entry;
562 isom_stts_entry_t *data = NULL;
563 for( entry = stts->list->head; entry; entry = entry->next )
565 data = (isom_stts_entry_t *)entry->data;
566 if( !data )
567 return 0;
568 if( i + data->sample_count > sample_number )
569 break;
570 dts += (uint64_t)data->sample_delta * data->sample_count;
571 i += data->sample_count;
573 if( !entry )
574 return 0;
575 dts += (uint64_t)data->sample_delta * (sample_number - i);
576 return dts;
579 #if 0
580 static uint64_t isom_get_cts( isom_stts_t *stts, isom_ctts_t *ctts, uint32_t sample_number )
582 if( !stts
583 || !stts->list )
584 return 0;
585 if( !ctts )
586 return isom_get_dts( stts, sample_number );
587 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. */
588 lsmash_entry_t *entry;
589 isom_ctts_entry_t *data = NULL;
590 if( sample_number == 0 )
591 return 0;
592 for( entry = ctts->list->head; entry; entry = entry->next )
594 data = (isom_ctts_entry_t *)entry->data;
595 if( !data )
596 return 0;
597 if( i + data->sample_count > sample_number )
598 break;
599 i += data->sample_count;
601 if( !entry )
602 return 0;
603 return isom_get_dts( stts, sample_number ) + data->sample_offset;
605 #endif
607 static int isom_replace_last_sample_delta( isom_stbl_t *stbl, uint32_t sample_delta )
609 if( !stbl
610 || !stbl->stts
611 || !stbl->stts->list
612 || !stbl->stts->list->tail
613 || !stbl->stts->list->tail->data )
614 return LSMASH_ERR_NAMELESS;
615 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stbl->stts->list->tail->data;
616 if( sample_delta != last_stts_data->sample_delta )
618 if( last_stts_data->sample_count > 1 )
620 last_stts_data->sample_count -= 1;
621 int err = isom_add_stts_entry( stbl, sample_delta );
622 if( err < 0 )
623 return err;
625 else
626 last_stts_data->sample_delta = sample_delta;
628 return 0;
631 static int isom_update_mdhd_duration( isom_trak_t *trak, uint32_t last_sample_delta )
633 if( !trak
634 || !trak->file
635 || !trak->cache
636 || !trak->mdia
637 || !trak->mdia->mdhd
638 || !trak->mdia->minf
639 || !trak->mdia->minf->stbl
640 || !trak->mdia->minf->stbl->stts
641 || !trak->mdia->minf->stbl->stts->list )
642 return LSMASH_ERR_INVALID_DATA;
643 lsmash_file_t *file = trak->file;
644 isom_mdhd_t *mdhd = trak->mdia->mdhd;
645 isom_stbl_t *stbl = trak->mdia->minf->stbl;
646 isom_stts_t *stts = stbl->stts;
647 isom_ctts_t *ctts = stbl->ctts;
648 isom_cslg_t *cslg = stbl->cslg;
649 mdhd->duration = 0;
650 uint32_t sample_count = isom_get_sample_count( trak );
651 if( sample_count == 0 )
653 /* Return error if non-fragmented movie has no samples. */
654 if( !file->fragment && !stts->list->entry_count )
655 return LSMASH_ERR_INVALID_DATA;
656 return 0;
658 /* Now we have at least 1 sample, so do stts_entry. */
659 lsmash_entry_t *last_stts = stts->list->tail;
660 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)last_stts->data;
661 if( sample_count == 1 )
662 mdhd->duration = last_stts_data->sample_delta;
663 /* Now we have at least 2 samples,
664 * but dunno whether 1 stts_entry which has 2 samples or 2 stts_entry which has 1 samle each. */
665 else if( !ctts )
667 /* use dts instead of cts */
668 mdhd->duration = isom_get_dts( stts, sample_count );
669 int err;
670 if( last_sample_delta )
672 mdhd->duration += last_sample_delta;
673 if( (err = isom_replace_last_sample_delta( stbl, last_sample_delta )) < 0 )
674 return err;
676 else if( last_stts_data->sample_count > 1 )
677 mdhd->duration += last_stts_data->sample_delta; /* no need to update last_stts_data->sample_delta */
678 else
680 /* Remove the last entry. */
681 if( (err = lsmash_remove_entry_tail( stts->list, NULL )) < 0 )
682 return err;
683 /* copy the previous sample_delta. */
684 ++ ((isom_stts_entry_t *)stts->list->tail->data)->sample_count;
685 mdhd->duration += ((isom_stts_entry_t *)stts->list->tail->data)->sample_delta;
688 else
690 if( !ctts->list
691 || ctts->list->entry_count == 0 )
692 return LSMASH_ERR_INVALID_DATA;
693 uint64_t dts = 0;
694 uint64_t max_cts = 0;
695 uint64_t max2_cts = 0;
696 uint64_t min_cts = UINT64_MAX;
697 int64_t max_offset = 0;
698 int64_t min_offset = UINT32_MAX;
699 int32_t ctd_shift = trak->cache->timestamp.ctd_shift;
700 uint32_t j = 0;
701 uint32_t k = 0;
702 lsmash_entry_t *stts_entry = stts->list->head;
703 lsmash_entry_t *ctts_entry = ctts->list->head;
704 for( uint32_t i = 0; i < sample_count; i++ )
706 if( !ctts_entry || !stts_entry )
707 return LSMASH_ERR_INVALID_DATA;
708 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
709 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
710 if( !stts_data || !ctts_data )
711 return LSMASH_ERR_INVALID_DATA;
712 uint64_t cts;
713 if( ctd_shift )
715 /* Anyway, add composition to decode timeline shift for calculating maximum and minimum CTS correctly. */
716 int64_t sample_offset = (int32_t)ctts_data->sample_offset;
717 cts = dts + sample_offset + ctd_shift;
718 max_offset = LSMASH_MAX( max_offset, sample_offset );
719 min_offset = LSMASH_MIN( min_offset, sample_offset );
721 else
723 cts = dts + ctts_data->sample_offset;
724 max_offset = LSMASH_MAX( max_offset, ctts_data->sample_offset );
725 min_offset = LSMASH_MIN( min_offset, ctts_data->sample_offset );
727 min_cts = LSMASH_MIN( min_cts, cts );
728 if( max_cts < cts )
730 max2_cts = max_cts;
731 max_cts = cts;
733 else if( max2_cts < cts )
734 max2_cts = cts;
735 dts += stts_data->sample_delta;
736 /* If finished sample_count of current entry, move to next. */
737 if( ++j == ctts_data->sample_count )
739 ctts_entry = ctts_entry->next;
740 j = 0;
742 if( ++k == stts_data->sample_count )
744 stts_entry = stts_entry->next;
745 k = 0;
748 dts -= last_stts_data->sample_delta;
749 if( file->fragment )
750 /* Overall presentation is extended exceeding this initial movie.
751 * So, any players shall display the movie exceeding the durations
752 * indicated in Movie Header Box, Track Header Boxes and Media Header Boxes.
753 * Samples up to the duration indicated in Movie Extends Header Box shall be displayed.
754 * In the absence of Movie Extends Header Box, all samples shall be displayed. */
755 mdhd->duration += dts + last_sample_delta;
756 else
758 if( !last_sample_delta )
760 /* The spec allows an arbitrary value for the duration of the last sample. So, we pick last-1 sample's. */
761 last_sample_delta = max_cts - max2_cts;
763 mdhd->duration = max_cts - min_cts + last_sample_delta;
764 /* To match dts and media duration, update stts and mdhd relatively. */
765 if( mdhd->duration > dts )
766 last_sample_delta = mdhd->duration - dts;
767 else
768 mdhd->duration = dts + last_sample_delta; /* media duration must not less than last dts. */
770 int err = isom_replace_last_sample_delta( stbl, last_sample_delta );
771 if( err < 0 )
772 return err;
773 /* Explicit composition information and timeline shifting */
774 if( cslg || file->qt_compatible || file->max_isom_version >= 4 )
776 if( ctd_shift )
778 /* Remove composition to decode timeline shift. */
779 max_cts -= ctd_shift;
780 max2_cts -= ctd_shift;
781 min_cts -= ctd_shift;
783 int64_t composition_end_time = max_cts + (max_cts - max2_cts);
784 if( !file->fragment
785 && (min_offset <= INT32_MAX) && (min_offset >= INT32_MIN)
786 && (max_offset <= INT32_MAX) && (max_offset >= INT32_MIN)
787 && ((int64_t)min_cts <= INT32_MAX) && (composition_end_time <= INT32_MAX) )
789 if( !cslg )
791 if( !isom_add_cslg( trak->mdia->minf->stbl ) )
792 return LSMASH_ERR_NAMELESS;
793 cslg = stbl->cslg;
795 cslg->compositionToDTSShift = ctd_shift;
796 cslg->leastDecodeToDisplayDelta = min_offset;
797 cslg->greatestDecodeToDisplayDelta = max_offset;
798 cslg->compositionStartTime = min_cts;
799 cslg->compositionEndTime = composition_end_time;
801 else
802 isom_remove_box_by_itself( cslg );
805 if( mdhd->duration > UINT32_MAX && !file->undefined_64_ver )
806 mdhd->version = 1;
807 return 0;
810 static int isom_update_mvhd_duration( isom_moov_t *moov )
812 if( !moov
813 || !moov->mvhd
814 || !moov->mvhd->file )
815 return LSMASH_ERR_INVALID_DATA;
816 isom_mvhd_t *mvhd = moov->mvhd;
817 mvhd->duration = 0;
818 for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next )
820 /* We pick maximum track duration as movie duration. */
821 isom_trak_t *data = (isom_trak_t *)entry->data;
822 if( !data
823 || !data->tkhd )
824 return LSMASH_ERR_INVALID_DATA;
825 mvhd->duration = entry != moov->trak_list.head
826 ? LSMASH_MAX( mvhd->duration, data->tkhd->duration )
827 : data->tkhd->duration;
829 if( mvhd->duration > UINT32_MAX && !mvhd->file->undefined_64_ver )
830 mvhd->version = 1;
831 return 0;
834 int isom_update_tkhd_duration( isom_trak_t *trak )
836 if( !trak
837 || !trak->tkhd
838 || !trak->file
839 || !trak->file->moov )
840 return LSMASH_ERR_INVALID_DATA;
841 lsmash_file_t *file = trak->file;
842 isom_tkhd_t *tkhd = trak->tkhd;
843 tkhd->duration = 0;
844 if( file->fragment
845 || !trak->edts
846 || !trak->edts->elst )
848 /* If this presentation might be extended or this track doesn't have edit list, calculate track duration from media duration. */
849 if( !trak->mdia
850 || !trak->mdia->mdhd
851 || !file->moov->mvhd
852 || trak->mdia->mdhd->timescale == 0 )
853 return LSMASH_ERR_INVALID_DATA;
854 if( trak->mdia->mdhd->duration == 0 )
856 int err = isom_update_mdhd_duration( trak, 0 );
857 if( err < 0 )
858 return err;
860 tkhd->duration = trak->mdia->mdhd->duration * ((double)file->moov->mvhd->timescale / trak->mdia->mdhd->timescale);
862 else
864 /* If the presentation won't be extended and this track has any edit, then track duration is just the sum of the segment_duartions. */
865 for( lsmash_entry_t *entry = trak->edts->elst->list->head; entry; entry = entry->next )
867 isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data;
868 if( !data )
869 return LSMASH_ERR_INVALID_DATA;
870 tkhd->duration += data->segment_duration;
873 if( tkhd->duration > UINT32_MAX && !file->undefined_64_ver )
874 tkhd->version = 1;
875 if( !file->fragment && tkhd->duration == 0 )
876 tkhd->duration = tkhd->version == 1 ? 0xffffffffffffffff : 0xffffffff;
877 return isom_update_mvhd_duration( file->moov );
880 int lsmash_update_track_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta )
882 if( isom_check_initializer_present( root ) < 0 )
883 return LSMASH_ERR_FUNCTION_PARAM;
884 lsmash_file_t *file = root->file;
885 isom_trak_t *trak = isom_get_trak( file, track_ID );
886 if( !trak )
887 return LSMASH_ERR_NAMELESS;
888 int err = isom_update_mdhd_duration( trak, last_sample_delta );
889 if( err < 0 )
890 return err;
891 /* If the presentation won't be extended and this track has any edit, we don't change or update duration in tkhd. */
892 if( !file->fragment && trak->edts && trak->edts->elst )
893 err = isom_update_mvhd_duration( file->moov ); /* Only update movie duration. */
894 else
895 err = isom_update_tkhd_duration( trak ); /* Also update movie duration internally. */
896 return err;
899 static inline int isom_increment_sample_number_in_entry
901 uint32_t *sample_number_in_entry,
902 uint32_t sample_count_in_entry,
903 lsmash_entry_t **entry
906 if( *sample_number_in_entry != sample_count_in_entry )
908 *sample_number_in_entry += 1;
909 return 0;
911 /* Precede the next entry. */
912 *sample_number_in_entry = 1;
913 if( *entry )
915 *entry = (*entry)->next;
916 if( *entry && !(*entry)->data )
917 return LSMASH_ERR_INVALID_DATA;
919 return 0;
922 int isom_calculate_bitrate_description
924 isom_stbl_t *stbl,
925 isom_mdhd_t *mdhd,
926 uint32_t *bufferSizeDB,
927 uint32_t *maxBitrate,
928 uint32_t *avgBitrate,
929 uint32_t sample_description_index
932 isom_stsz_t *stsz = stbl->stsz;
933 lsmash_entry_list_t *stsz_list = stsz ? stsz->list : stbl->stz2->list;
934 lsmash_entry_t *stsz_entry = stsz_list ? stsz_list->head : NULL;
935 lsmash_entry_t *stts_entry = stbl->stts->list->head;
936 lsmash_entry_t *stsc_entry = NULL;
937 lsmash_entry_t *next_stsc_entry = stbl->stsc->list->head;
938 isom_stts_entry_t *stts_data = NULL;
939 isom_stsc_entry_t *stsc_data = NULL;
940 if( next_stsc_entry && !next_stsc_entry->data )
941 return LSMASH_ERR_INVALID_DATA;
942 uint32_t rate = 0;
943 uint64_t dts = 0;
944 uint32_t time_wnd = 0;
945 uint32_t chunk_number = 0;
946 uint32_t sample_number_in_stts = 1;
947 uint32_t sample_number_in_chunk = 1;
948 uint32_t constant_sample_size = stsz ? stsz->sample_size : 0;
949 *bufferSizeDB = 0;
950 *maxBitrate = 0;
951 *avgBitrate = 0;
952 while( stts_entry )
954 int err;
955 if( !stsc_data || sample_number_in_chunk == stsc_data->samples_per_chunk )
957 /* Move the next chunk. */
958 sample_number_in_chunk = 1;
959 ++chunk_number;
960 /* Check if the next entry is broken. */
961 while( next_stsc_entry && ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk < chunk_number )
963 /* Just skip broken next entry. */
964 next_stsc_entry = next_stsc_entry->next;
965 if( next_stsc_entry && !next_stsc_entry->data )
966 return LSMASH_ERR_INVALID_DATA;
968 /* Check if the next chunk belongs to the next sequence of chunks. */
969 if( next_stsc_entry && ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk == chunk_number )
971 stsc_entry = next_stsc_entry;
972 next_stsc_entry = next_stsc_entry->next;
973 if( next_stsc_entry && !next_stsc_entry->data )
974 return LSMASH_ERR_INVALID_DATA;
975 stsc_data = (isom_stsc_entry_t *)stsc_entry->data;
976 /* Check if the next contiguous chunks belong to given sample description. */
977 if( stsc_data->sample_description_index != sample_description_index )
979 /* Skip chunks which don't belong to given sample description. */
980 uint32_t number_of_skips = 0;
981 uint32_t first_chunk = stsc_data->first_chunk;
982 uint32_t samples_per_chunk = stsc_data->samples_per_chunk;
983 while( next_stsc_entry )
985 if( ((isom_stsc_entry_t *)next_stsc_entry->data)->sample_description_index != sample_description_index )
987 stsc_data = (isom_stsc_entry_t *)next_stsc_entry->data;
988 number_of_skips += (stsc_data->first_chunk - first_chunk) * samples_per_chunk;
989 first_chunk = stsc_data->first_chunk;
990 samples_per_chunk = stsc_data->samples_per_chunk;
992 else if( ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk <= first_chunk )
993 ; /* broken entry */
994 else
995 break;
996 /* Just skip the next entry. */
997 next_stsc_entry = next_stsc_entry->next;
998 if( next_stsc_entry && !next_stsc_entry->data )
999 return LSMASH_ERR_INVALID_DATA;
1001 if( !next_stsc_entry )
1002 break; /* There is no more chunks which don't belong to given sample description. */
1003 number_of_skips += (((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk - first_chunk) * samples_per_chunk;
1004 for( uint32_t i = 0; i < number_of_skips; i++ )
1006 if( stsz_list )
1008 if( !stsz_entry )
1009 break;
1010 stsz_entry = stsz_entry->next;
1012 if( !stts_entry )
1013 break;
1014 if( (err = isom_increment_sample_number_in_entry( &sample_number_in_stts,
1015 ((isom_stts_entry_t *)stts_entry->data)->sample_count,
1016 &stts_entry )) < 0 )
1017 return err;
1019 if( (stsz_list && !stsz_entry) || !stts_entry )
1020 break;
1021 chunk_number = stsc_data->first_chunk;
1025 else
1026 ++sample_number_in_chunk;
1027 /* Get current sample's size. */
1028 uint32_t size;
1029 if( stsz_list )
1031 if( !stsz_entry )
1032 break;
1033 isom_stsz_entry_t *stsz_data = (isom_stsz_entry_t *)stsz_entry->data;
1034 if( !stsz_data )
1035 return LSMASH_ERR_INVALID_DATA;
1036 size = stsz_data->entry_size;
1037 stsz_entry = stsz_entry->next;
1039 else
1040 size = constant_sample_size;
1041 /* Get current sample's DTS. */
1042 if( stts_data )
1043 dts += stts_data->sample_delta;
1044 stts_data = (isom_stts_entry_t *)stts_entry->data;
1045 if( !stts_data )
1046 return LSMASH_ERR_INVALID_DATA;
1047 if( (err = isom_increment_sample_number_in_entry( &sample_number_in_stts, stts_data->sample_count, &stts_entry )) < 0 )
1048 return err;
1049 /* Calculate bitrate description. */
1050 if( *bufferSizeDB < size )
1051 *bufferSizeDB = size;
1052 *avgBitrate += size;
1053 rate += size;
1054 if( dts > time_wnd + mdhd->timescale )
1056 if( rate > *maxBitrate )
1057 *maxBitrate = rate;
1058 time_wnd = dts;
1059 rate = 0;
1062 double duration = (double)mdhd->duration / mdhd->timescale;
1063 *avgBitrate = (uint32_t)(*avgBitrate / duration);
1064 if( *maxBitrate == 0 )
1065 *maxBitrate = *avgBitrate;
1066 /* Convert to bits per second. */
1067 *maxBitrate *= 8;
1068 *avgBitrate *= 8;
1069 return 0;
1072 int isom_is_variable_size( isom_stbl_t *stbl )
1074 if( (stbl->stz2 && stbl->stz2->sample_count > 1)
1075 || (stbl->stsz && stbl->stsz->sample_count > 1 && stbl->stsz->sample_size == 0) )
1076 return 1;
1077 else
1078 return 0;
1081 uint32_t isom_get_first_sample_size( isom_stbl_t *stbl )
1083 if( stbl->stsz )
1085 /* 'stsz' */
1086 if( stbl->stsz->sample_size )
1087 return stbl->stsz->sample_size;
1088 else if( stbl->stsz->list && stbl->stsz->list->head && stbl->stsz->list->head->data )
1089 return ((isom_stsz_entry_t *)stbl->stsz->list->head->data)->entry_size;
1090 else
1091 return 0;
1093 else if( stbl->stz2 )
1095 /* stz2 */
1096 if( stbl->stz2->list && stbl->stz2->list->head && stbl->stz2->list->head->data )
1097 return ((isom_stsz_entry_t *)stbl->stz2->list->head->data)->entry_size;
1098 else
1099 return 0;
1101 else
1102 return 0;
1105 int isom_update_bitrate_description( isom_mdia_t *mdia )
1107 if( !mdia
1108 || !mdia->mdhd
1109 || !mdia->minf
1110 || !mdia->minf->stbl )
1111 return LSMASH_ERR_INVALID_DATA;
1112 isom_stbl_t *stbl = mdia->minf->stbl;
1113 if( !stbl->stsd
1114 || (!stbl->stsz && !stbl->stz2)
1115 || !stbl->stsc || !stbl->stsc->list
1116 || !stbl->stts || !stbl->stts->list )
1117 return LSMASH_ERR_INVALID_DATA;
1118 uint32_t sample_description_index = 0;
1119 for( lsmash_entry_t *entry = stbl->stsd->list.head; entry; entry = entry->next )
1121 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)entry->data;
1122 if( !sample_entry )
1123 return LSMASH_ERR_INVALID_DATA;
1124 ++sample_description_index;
1125 isom_bitrate_updater_t func_update_bitrate = isom_get_bitrate_updater( sample_entry );
1126 if( func_update_bitrate )
1128 int err = func_update_bitrate( stbl, mdia->mdhd, sample_description_index );
1129 if( err < 0 )
1130 return err;
1133 return sample_description_index ? 0 : LSMASH_ERR_INVALID_DATA;
1136 static inline uint64_t isom_get_current_mp4time( void )
1138 return (uint64_t)time( NULL ) + ISOM_MAC_EPOCH_OFFSET;
1141 static int isom_set_media_creation_time( isom_trak_t *trak, uint64_t current_mp4time )
1143 if( !trak->mdia
1144 || !trak->mdia->mdhd )
1145 return LSMASH_ERR_NAMELESS;
1146 isom_mdhd_t *mdhd = trak->mdia->mdhd;
1147 if( mdhd->creation_time == 0 )
1148 mdhd->creation_time = mdhd->modification_time = current_mp4time;
1149 return 0;
1152 static int isom_set_track_creation_time( isom_trak_t *trak, uint64_t current_mp4time )
1154 if( !trak
1155 || !trak->tkhd )
1156 return LSMASH_ERR_NAMELESS;
1157 isom_tkhd_t *tkhd = trak->tkhd;
1158 if( tkhd->creation_time == 0 )
1159 tkhd->creation_time = tkhd->modification_time = current_mp4time;
1160 return isom_set_media_creation_time( trak, current_mp4time );
1163 static int isom_set_movie_creation_time( lsmash_file_t *file )
1165 if( !file
1166 || !file->moov
1167 || !file->moov->mvhd )
1168 return LSMASH_ERR_NAMELESS;
1169 uint64_t current_mp4time = isom_get_current_mp4time();
1170 for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next )
1172 int err = isom_set_track_creation_time( (isom_trak_t *)entry->data, current_mp4time );
1173 if( err < 0 )
1174 return err;
1176 isom_mvhd_t *mvhd = file->moov->mvhd;
1177 if( mvhd->creation_time == 0 )
1178 mvhd->creation_time = mvhd->modification_time = current_mp4time;
1179 return 0;
1182 int isom_setup_handler_reference( isom_hdlr_t *hdlr, uint32_t media_type )
1184 isom_box_t *parent = hdlr->parent;
1185 lsmash_file_t *file = hdlr->file;
1186 if( !parent || !file )
1187 return LSMASH_ERR_NAMELESS;
1188 isom_mdia_t *mdia = lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MDIA ) ? (isom_mdia_t *)parent : NULL;
1189 isom_meta_t *meta = lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) ? (isom_meta_t *)parent
1190 : lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) ? (isom_meta_t *)parent : NULL;
1191 uint32_t type = mdia ? (file->qt_compatible ? QT_HANDLER_TYPE_MEDIA : 0) : (meta ? 0 : QT_HANDLER_TYPE_DATA);
1192 uint32_t subtype = media_type;
1193 hdlr->componentType = type;
1194 hdlr->componentSubtype = subtype;
1195 char *type_name = NULL;
1196 char *subtype_name = NULL;
1197 uint8_t type_name_length = 0;
1198 uint8_t subtype_name_length = 0;
1199 if( mdia )
1200 type_name = "Media ";
1201 else if( meta )
1202 type_name = "Metadata ";
1203 else /* if( minf ) */
1204 type_name = "Data ";
1205 type_name_length = strlen( type_name );
1206 struct
1208 uint32_t subtype;
1209 char *subtype_name;
1210 uint8_t subtype_name_length;
1211 } subtype_table[] =
1213 { ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK, "Sound ", 6 },
1214 { ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK, "Video ", 6 },
1215 { ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK, "Hint ", 5 },
1216 { ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK, "Metadata ", 9 },
1217 { ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK, "Text ", 5 },
1218 { ISOM_META_HANDLER_TYPE_ITUNES_METADATA, "iTunes ", 7 },
1219 { QT_REFERENCE_HANDLER_TYPE_ALIAS, "Alias ", 6 },
1220 { QT_REFERENCE_HANDLER_TYPE_RESOURCE, "Resource ", 9 },
1221 { QT_REFERENCE_HANDLER_TYPE_URL, "URL ", 4 },
1222 { subtype, "Unknown ", 8 }
1224 for( int i = 0; subtype_table[i].subtype; i++ )
1225 if( subtype == subtype_table[i].subtype )
1227 subtype_name = subtype_table[i].subtype_name;
1228 subtype_name_length = subtype_table[i].subtype_name_length;
1229 break;
1231 uint32_t name_length = 15 + subtype_name_length + type_name_length + file->isom_compatible + file->qt_compatible;
1232 uint8_t *name = lsmash_malloc( name_length );
1233 if( !name )
1234 return LSMASH_ERR_MEMORY_ALLOC;
1235 if( file->qt_compatible )
1236 name[0] = name_length & 0xff;
1237 memcpy( name + file->qt_compatible, "L-SMASH ", 8 );
1238 memcpy( name + file->qt_compatible + 8, subtype_name, subtype_name_length );
1239 memcpy( name + file->qt_compatible + 8 + subtype_name_length, type_name, type_name_length );
1240 memcpy( name + file->qt_compatible + 8 + subtype_name_length + type_name_length, "Handler", 7 );
1241 if( file->isom_compatible )
1242 name[name_length - 1] = 0;
1243 hdlr->componentName = name;
1244 hdlr->componentName_length = name_length;
1245 return 0;
1248 isom_trak_t *isom_track_create( lsmash_file_t *file, lsmash_media_type media_type )
1250 /* Don't allow to create a new track if the initial movie is already written. */
1251 if( (file->fragment && file->fragment->movie)
1252 || (file->moov && (file->moov->manager & LSMASH_WRITTEN_BOX)) )
1253 return NULL;
1254 isom_trak_t *trak = isom_add_trak( file->moov );
1255 if( !trak
1256 || !trak->file
1257 || !trak->file->moov
1258 || !trak->file->moov->mvhd )
1259 goto fail;
1260 if( !isom_add_tkhd( trak )
1261 || !isom_add_mdia( trak )
1262 || !isom_add_mdhd( trak->mdia )
1263 || !isom_add_minf( trak->mdia )
1264 || !isom_add_dinf( trak->mdia->minf )
1265 || !isom_add_dref( trak->mdia->minf->dinf )
1266 || !isom_add_stbl( trak->mdia->minf )
1267 || !isom_add_stsd( trak->mdia->minf->stbl )
1268 || !isom_add_stts( trak->mdia->minf->stbl )
1269 || !isom_add_stsc( trak->mdia->minf->stbl )
1270 || !isom_add_stco( trak->mdia->minf->stbl )
1271 || !isom_add_stsz( trak->mdia->minf->stbl ) )
1272 goto fail;
1273 if( !isom_add_hdlr( trak->mdia )
1274 || isom_setup_handler_reference( trak->mdia->hdlr, media_type ) < 0 )
1275 goto fail;
1276 if( file->qt_compatible )
1278 if( !isom_add_hdlr( trak->mdia->minf )
1279 || isom_setup_handler_reference( trak->mdia->minf->hdlr, QT_REFERENCE_HANDLER_TYPE_URL ) < 0 )
1280 goto fail;
1282 switch( media_type )
1284 case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK :
1285 if( !isom_add_vmhd( trak->mdia->minf ) )
1286 goto fail;
1287 trak->mdia->minf->vmhd->flags = 0x000001;
1288 break;
1289 case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK :
1290 if( !isom_add_smhd( trak->mdia->minf ) )
1291 goto fail;
1292 trak->cache->is_audio = 1;
1293 break;
1294 case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK :
1295 if( !isom_add_hmhd( trak->mdia->minf ) )
1296 goto fail;
1297 break;
1298 case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK :
1299 if( file->qt_compatible || file->itunes_movie )
1301 if( !isom_add_gmhd( trak->mdia->minf )
1302 || !isom_add_gmin( trak->mdia->minf->gmhd )
1303 || !isom_add_text( trak->mdia->minf->gmhd ) )
1304 return 0;
1305 /* Default Text Media Information Box. */
1307 isom_text_t *text = trak->mdia->minf->gmhd->text;
1308 text->matrix[0] = 0x00010000;
1309 text->matrix[4] = 0x00010000;
1310 text->matrix[8] = 0x40000000;
1313 else
1314 goto fail; /* We support only reference text media track for chapter yet. */
1315 break;
1316 default :
1317 if( !isom_add_nmhd( trak->mdia->minf ) )
1318 goto fail;
1319 break;
1321 /* Default Track Header Box. */
1323 isom_tkhd_t *tkhd = trak->tkhd;
1324 if( media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK )
1325 tkhd->volume = 0x0100;
1326 tkhd->matrix[0] = 0x00010000;
1327 tkhd->matrix[4] = 0x00010000;
1328 tkhd->matrix[8] = 0x40000000;
1329 tkhd->duration = 0xffffffff;
1330 tkhd->track_ID = trak->file->moov->mvhd->next_track_ID ++;
1332 trak->mdia->mdhd->language = file->qt_compatible ? 0 : ISOM_LANGUAGE_CODE_UNDEFINED;
1333 return trak;
1334 fail:
1335 isom_remove_box_by_itself( trak );
1336 return NULL;
1339 isom_moov_t *isom_movie_create( lsmash_file_t *file )
1341 isom_moov_t *moov = isom_add_moov( file );
1342 isom_mvhd_t *mvhd = isom_add_mvhd( moov );
1343 if( !mvhd )
1345 isom_remove_box_by_itself( moov );
1346 return NULL;
1348 /* Default Movie Header Box. */
1349 mvhd->rate = 0x00010000;
1350 mvhd->volume = 0x0100;
1351 mvhd->matrix[0] = 0x00010000;
1352 mvhd->matrix[4] = 0x00010000;
1353 mvhd->matrix[8] = 0x40000000;
1354 mvhd->next_track_ID = 1;
1355 file->initializer = file;
1356 return moov;
1359 /*******************************
1360 public interfaces
1361 *******************************/
1363 /*---- track manipulators ----*/
1365 void lsmash_delete_track( lsmash_root_t *root, uint32_t track_ID )
1367 if( isom_check_initializer_present( root ) < 0
1368 || !root->file->initializer->moov )
1369 return;
1370 for( lsmash_entry_t *entry = root->file->initializer->moov->trak_list.head; entry; entry = entry->next )
1372 isom_trak_t *trak = (isom_trak_t *)entry->data;
1373 if( !trak
1374 || !trak->tkhd )
1375 return;
1376 if( trak->tkhd->track_ID == track_ID )
1378 isom_remove_box_by_itself( trak );
1379 return;
1384 uint32_t lsmash_create_track( lsmash_root_t *root, lsmash_media_type media_type )
1386 if( isom_check_initializer_present( root ) < 0 )
1387 return 0;
1388 isom_trak_t *trak = isom_track_create( root->file, media_type );
1389 if( !trak
1390 || !trak->tkhd )
1391 return 0;
1392 return trak->tkhd->track_ID;
1395 uint32_t lsmash_get_track_ID( lsmash_root_t *root, uint32_t track_number )
1397 if( isom_check_initializer_present( root ) < 0
1398 || !root->file->initializer->moov )
1399 return 0;
1400 isom_trak_t *trak = (isom_trak_t *)lsmash_get_entry_data( &root->file->initializer->moov->trak_list, track_number );
1401 if( !trak
1402 || !trak->tkhd )
1403 return 0;
1404 return trak->tkhd->track_ID;
1407 void lsmash_initialize_track_parameters( lsmash_track_parameters_t *param )
1409 memset( param, 0, sizeof(lsmash_track_parameters_t) );
1410 param->audio_volume = 0x0100;
1411 param->matrix[0] = 0x00010000;
1412 param->matrix[4] = 0x00010000;
1413 param->matrix[8] = 0x40000000;
1416 int lsmash_set_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param )
1418 if( isom_check_initializer_present( root ) < 0 )
1419 return LSMASH_ERR_FUNCTION_PARAM;
1420 lsmash_file_t *file = root->file;
1421 isom_trak_t *trak = isom_get_trak( file, track_ID );
1422 if( !trak
1423 || !trak->mdia
1424 || !trak->mdia->hdlr
1425 || !file->moov->mvhd )
1426 return LSMASH_ERR_NAMELESS;
1427 /* Prepare Track Aperture Modes if required. */
1428 if( file->qt_compatible && param->aperture_modes )
1430 if( !trak->tapt && !isom_add_tapt( trak ) )
1431 return LSMASH_ERR_NAMELESS;
1432 isom_tapt_t *tapt = trak->tapt;
1433 if( (!tapt->clef && !isom_add_clef( tapt ))
1434 || (!tapt->prof && !isom_add_prof( tapt ))
1435 || (!tapt->enof && !isom_add_enof( tapt )) )
1436 return LSMASH_ERR_NAMELESS;
1438 else
1439 isom_remove_box_by_itself( trak->tapt );
1440 /* Set up Track Header. */
1441 uint32_t media_type = trak->mdia->hdlr->componentSubtype;
1442 isom_tkhd_t *tkhd = trak->tkhd;
1443 tkhd->flags = param->mode;
1444 tkhd->track_ID = param->track_ID ? param->track_ID : tkhd->track_ID;
1445 tkhd->duration = !trak->edts || !trak->edts->elst ? param->duration : tkhd->duration;
1446 /* Template fields
1447 * alternate_group, layer, volume and matrix
1448 * According to 14496-14, these value are all set to defaut values in 14496-12.
1449 * And when a file is read as an MPEG-4 file, these values shall be ignored.
1450 * If a file complies with other specifications, then those fields may have non-default values
1451 * as required by those other specifications. */
1452 if( param->alternate_group )
1454 if( file->qt_compatible || file->itunes_movie || file->max_3gpp_version >= 4 )
1455 tkhd->alternate_group = param->alternate_group;
1456 else
1458 tkhd->alternate_group = 0;
1459 lsmash_log( NULL, LSMASH_LOG_WARNING,
1460 "alternate_group is specified but not compatible with any of the brands. It won't be set.\n" );
1463 else
1464 tkhd->alternate_group = 0;
1465 if( file->qt_compatible || file->itunes_movie )
1467 tkhd->layer = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->video_layer : 0;
1468 tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? param->audio_volume : 0;
1469 if( media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK )
1470 for( int i = 0; i < 9; i++ )
1471 tkhd->matrix[i] = param->matrix[i];
1472 else
1473 for( int i = 0; i < 9; i++ )
1474 tkhd->matrix[i] = 0;
1476 else
1478 tkhd->layer = 0;
1479 tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? 0x0100 : 0;
1480 tkhd->matrix[0] = 0x00010000;
1481 tkhd->matrix[1] = 0;
1482 tkhd->matrix[2] = 0;
1483 tkhd->matrix[3] = 0;
1484 tkhd->matrix[4] = 0x00010000;
1485 tkhd->matrix[5] = 0;
1486 tkhd->matrix[6] = 0;
1487 tkhd->matrix[7] = 0;
1488 tkhd->matrix[8] = 0x40000000;
1490 /* visual presentation size */
1491 tkhd->width = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_width : 0;
1492 tkhd->height = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_height : 0;
1493 /* Update next_track_ID if needed. */
1494 if( file->moov->mvhd->next_track_ID <= tkhd->track_ID )
1495 file->moov->mvhd->next_track_ID = tkhd->track_ID + 1;
1496 return 0;
1499 int lsmash_get_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param )
1501 if( isom_check_initializer_present( root ) < 0 )
1502 return LSMASH_ERR_FUNCTION_PARAM;
1503 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1504 if( !trak )
1505 return LSMASH_ERR_NAMELESS;
1506 isom_tkhd_t *tkhd = trak->tkhd;
1507 param->mode = tkhd->flags;
1508 param->track_ID = tkhd->track_ID;
1509 param->duration = tkhd->duration;
1510 param->video_layer = tkhd->layer;
1511 param->alternate_group = tkhd->alternate_group;
1512 param->audio_volume = tkhd->volume;
1513 for( int i = 0; i < 9; i++ )
1514 param->matrix[i] = tkhd->matrix[i];
1515 param->display_width = tkhd->width;
1516 param->display_height = tkhd->height;
1517 param->aperture_modes = !!trak->tapt;
1518 return 0;
1521 static inline int check_dref_presence( isom_trak_t *trak )
1523 if( !trak
1524 || !trak->mdia
1525 || !trak->mdia->minf
1526 || !trak->mdia->minf->dinf
1527 || !trak->mdia->minf->dinf->dref )
1528 return LSMASH_ERR_NAMELESS;
1529 return 0;
1532 uint32_t lsmash_count_data_reference
1534 lsmash_root_t *root,
1535 uint32_t track_ID
1538 if( isom_check_initializer_present( root ) < 0 )
1539 return 0;
1540 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1541 if( check_dref_presence( trak ) < 0 )
1542 return 0;
1543 return trak->mdia->minf->dinf->dref->list.entry_count;
1546 int lsmash_get_data_reference
1548 lsmash_root_t *root,
1549 uint32_t track_ID,
1550 lsmash_data_reference_t *data_ref
1553 if( isom_check_initializer_present( root ) < 0 || !data_ref )
1554 return LSMASH_ERR_FUNCTION_PARAM;
1555 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1556 if( check_dref_presence( trak ) < 0 )
1557 return LSMASH_ERR_NAMELESS;
1558 isom_dref_entry_t *url = lsmash_get_entry_data( &trak->mdia->minf->dinf->dref->list, data_ref->index );
1559 if( !url )
1560 return LSMASH_ERR_NAMELESS;
1561 if( !(url->flags & 0x000001) && url->location )
1563 int length = strlen( url->location );
1564 char *location = lsmash_malloc( length + 1 );
1565 if( !location )
1566 return LSMASH_ERR_MEMORY_ALLOC;
1567 memcpy( location, url->location, length );
1568 location[length] = '\0';
1569 data_ref->location = location;
1571 else
1572 data_ref->location = NULL;
1573 return 0;
1576 void lsmash_cleanup_data_reference
1578 lsmash_data_reference_t *data_ref
1581 if( !data_ref )
1582 return;
1583 lsmash_freep( &data_ref->location );
1586 int lsmash_create_data_reference
1588 lsmash_root_t *root,
1589 uint32_t track_ID,
1590 lsmash_data_reference_t *data_ref,
1591 lsmash_file_t *file
1594 /* At present, we don't support external data references for movie fragments.
1595 * Note that, for external media data, default-base-is-moof is meaningless since relative
1596 * offsets from Movie Fragment Boxes make no sense.
1597 * In the future, the condition of !(file->flags & LSMASH_FILE_MODE_WRITE) may be removed
1598 * for the implementation which does not write actually and does reference read-only file. */
1599 if( !root || !file || file->root != root
1600 || (!(file->flags & LSMASH_FILE_MODE_MEDIA) && !(file->flags & LSMASH_FILE_MODE_INITIALIZATION))
1601 || !(file->flags & LSMASH_FILE_MODE_WRITE)
1602 || (root->file != file && ((file->flags & LSMASH_FILE_MODE_FRAGMENTED) || file->fragment))
1603 || !data_ref )
1604 return LSMASH_ERR_FUNCTION_PARAM;
1605 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
1606 if( check_dref_presence( trak ) < 0 )
1607 return LSMASH_ERR_NAMELESS;
1608 isom_dref_entry_t *url = isom_add_dref_entry( trak->mdia->minf->dinf->dref, ISOM_BOX_TYPE_URL );
1609 if( !url )
1610 return LSMASH_ERR_NAMELESS;
1611 if( !data_ref->location || root->file == file )
1613 /* Media data is in the same file. */
1614 url->flags = 0x000001;
1615 url->ref_file = root->file;
1617 else
1619 /* Set the location of the file. */
1620 int length = strlen( data_ref->location );
1621 url->location = lsmash_malloc( length + 1 );
1622 if( !url->location )
1624 isom_remove_box_by_itself( url );
1625 return LSMASH_ERR_MEMORY_ALLOC;
1627 memcpy( url->location, data_ref->location, length );
1628 url->location[length] = '\0';
1629 url->location_length = length + 1;
1630 url->ref_file = file;
1632 data_ref->index = trak->mdia->minf->dinf->dref->list.entry_count;
1633 return 0;
1636 int lsmash_assign_data_reference
1638 lsmash_root_t *root,
1639 uint32_t track_ID,
1640 uint32_t data_ref_index,
1641 lsmash_file_t *file
1644 if( isom_check_initializer_present( root ) < 0
1645 || !file || file->root != root
1646 || !(file->flags & LSMASH_FILE_MODE_MEDIA)
1647 || !(file->flags & LSMASH_FILE_MODE_READ)
1648 || data_ref_index == 0 )
1649 return LSMASH_ERR_FUNCTION_PARAM;
1650 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1651 if( check_dref_presence( trak ) < 0 )
1652 return LSMASH_ERR_NAMELESS;
1653 isom_dref_entry_t *url = (isom_dref_entry_t *)lsmash_get_entry_data( &trak->mdia->minf->dinf->dref->list, data_ref_index );
1654 if( !url )
1655 return LSMASH_ERR_NAMELESS;
1656 if( !(url->flags & 0x000001) )
1657 /* Reference an external media data. */
1658 url->ref_file = file;
1659 return 0;
1662 static int isom_set_media_handler_name( lsmash_file_t *file, uint32_t track_ID, char *handler_name )
1664 isom_trak_t *trak = isom_get_trak( file, track_ID );
1665 if( !trak
1666 || !trak->mdia
1667 || !trak->mdia->hdlr )
1668 return LSMASH_ERR_NAMELESS;
1669 isom_hdlr_t *hdlr = trak->mdia->hdlr;
1670 uint8_t *name = NULL;
1671 uint32_t name_length = strlen( handler_name ) + file->isom_compatible + file->qt_compatible;
1672 if( file->qt_compatible )
1673 name_length = LSMASH_MIN( name_length, 255 );
1674 if( name_length > hdlr->componentName_length && hdlr->componentName )
1675 name = lsmash_realloc( hdlr->componentName, name_length );
1676 else if( !hdlr->componentName )
1677 name = lsmash_malloc( name_length );
1678 else
1679 name = hdlr->componentName;
1680 if( !name )
1681 return LSMASH_ERR_MEMORY_ALLOC;
1682 if( file->qt_compatible )
1683 name[0] = name_length & 0xff;
1684 memcpy( name + file->qt_compatible, handler_name, strlen( handler_name ) );
1685 if( file->isom_compatible )
1686 name[name_length - 1] = 0;
1687 hdlr->componentName = name;
1688 hdlr->componentName_length = name_length;
1689 return 0;
1692 static int isom_set_data_handler_name( lsmash_file_t *file, uint32_t track_ID, char *handler_name )
1694 isom_trak_t *trak = isom_get_trak( file, track_ID );
1695 if( !trak
1696 || !trak->mdia
1697 || !trak->mdia->minf
1698 || !trak->mdia->minf->hdlr )
1699 return LSMASH_ERR_NAMELESS;
1700 isom_hdlr_t *hdlr = trak->mdia->minf->hdlr;
1701 uint8_t *name = NULL;
1702 uint32_t name_length = strlen( handler_name ) + file->isom_compatible + file->qt_compatible;
1703 if( file->qt_compatible )
1704 name_length = LSMASH_MIN( name_length, 255 );
1705 if( name_length > hdlr->componentName_length && hdlr->componentName )
1706 name = lsmash_realloc( hdlr->componentName, name_length );
1707 else if( !hdlr->componentName )
1708 name = lsmash_malloc( name_length );
1709 else
1710 name = hdlr->componentName;
1711 if( !name )
1712 return LSMASH_ERR_MEMORY_ALLOC;
1713 if( file->qt_compatible )
1714 name[0] = name_length & 0xff;
1715 memcpy( name + file->qt_compatible, handler_name, strlen( handler_name ) );
1716 if( file->isom_compatible )
1717 name[name_length - 1] = 0;
1718 hdlr->componentName = name;
1719 hdlr->componentName_length = name_length;
1720 return 0;
1723 uint32_t lsmash_get_media_timescale( lsmash_root_t *root, uint32_t track_ID )
1725 if( isom_check_initializer_present( root ) < 0 )
1726 return 0;
1727 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1728 if( !trak
1729 || !trak->mdia
1730 || !trak->mdia->mdhd )
1731 return 0;
1732 return trak->mdia->mdhd->timescale;
1735 uint64_t lsmash_get_media_duration( lsmash_root_t *root, uint32_t track_ID )
1737 if( isom_check_initializer_present( root ) < 0 )
1738 return 0;
1739 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1740 if( !trak
1741 || !trak->mdia
1742 || !trak->mdia->mdhd )
1743 return 0;
1744 return trak->mdia->mdhd->duration;
1747 uint64_t lsmash_get_track_duration( lsmash_root_t *root, uint32_t track_ID )
1749 if( isom_check_initializer_present( root ) < 0 )
1750 return 0;
1751 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1752 if( !trak
1753 || !trak->tkhd )
1754 return 0;
1755 return trak->tkhd->duration;
1758 uint32_t lsmash_get_last_sample_delta( lsmash_root_t *root, uint32_t track_ID )
1760 if( isom_check_initializer_present( root ) < 0 )
1761 return 0;
1762 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
1763 if( !trak
1764 || !trak->mdia
1765 || !trak->mdia->minf
1766 || !trak->mdia->minf->stbl
1767 || !trak->mdia->minf->stbl->stts
1768 || !trak->mdia->minf->stbl->stts->list
1769 || !trak->mdia->minf->stbl->stts->list->tail
1770 || !trak->mdia->minf->stbl->stts->list->tail->data )
1771 return 0;
1772 return ((isom_stts_entry_t *)trak->mdia->minf->stbl->stts->list->tail->data)->sample_delta;
1775 uint32_t lsmash_get_start_time_offset( lsmash_root_t *root, uint32_t track_ID )
1777 if( isom_check_initializer_present( root ) < 0 )
1778 return 0;
1779 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
1780 if( !trak
1781 || !trak->mdia
1782 || !trak->mdia->minf
1783 || !trak->mdia->minf->stbl
1784 || !trak->mdia->minf->stbl->ctts
1785 || !trak->mdia->minf->stbl->ctts->list
1786 || !trak->mdia->minf->stbl->ctts->list->head
1787 || !trak->mdia->minf->stbl->ctts->list->head->data )
1788 return 0;
1789 return ((isom_ctts_entry_t *)trak->mdia->minf->stbl->ctts->list->head->data)->sample_offset;
1792 uint32_t lsmash_get_composition_to_decode_shift( lsmash_root_t *root, uint32_t track_ID )
1794 if( isom_check_initializer_present( root ) < 0 )
1795 return 0;
1796 lsmash_file_t *file = root->file->initializer;
1797 isom_trak_t *trak = isom_get_trak( file, track_ID );
1798 if( !trak
1799 || !trak->mdia
1800 || !trak->mdia->minf
1801 || !trak->mdia->minf->stbl )
1802 return 0;
1803 uint32_t sample_count = isom_get_sample_count( trak );
1804 if( sample_count == 0 )
1805 return 0;
1806 isom_stbl_t *stbl = trak->mdia->minf->stbl;
1807 if( !stbl->stts || !stbl->stts->list
1808 || !stbl->ctts || !stbl->ctts->list )
1809 return 0;
1810 if( !(file->max_isom_version >= 4 && stbl->ctts->version == 1) && !file->qt_compatible )
1811 return 0; /* This movie shall not have composition to decode timeline shift. */
1812 lsmash_entry_t *stts_entry = stbl->stts->list->head;
1813 lsmash_entry_t *ctts_entry = stbl->ctts->list->head;
1814 if( !stts_entry || !ctts_entry )
1815 return 0;
1816 uint64_t dts = 0;
1817 uint64_t cts = 0;
1818 uint32_t ctd_shift = 0;
1819 uint32_t i = 0;
1820 uint32_t j = 0;
1821 for( uint32_t k = 0; k < sample_count; k++ )
1823 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
1824 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
1825 if( !stts_data || !ctts_data )
1826 return 0;
1827 cts = dts + (int32_t)ctts_data->sample_offset;
1828 if( dts > cts + ctd_shift )
1829 ctd_shift = dts - cts;
1830 dts += stts_data->sample_delta;
1831 if( ++i == stts_data->sample_count )
1833 stts_entry = stts_entry->next;
1834 if( !stts_entry )
1835 return 0;
1836 i = 0;
1838 if( ++j == ctts_data->sample_count )
1840 ctts_entry = ctts_entry->next;
1841 if( !ctts_entry )
1842 return 0;
1843 j = 0;
1846 return ctd_shift;
1849 uint16_t lsmash_pack_iso_language( char *iso_language )
1851 if( !iso_language || strlen( iso_language ) != 3 )
1852 return 0;
1853 return (uint16_t)LSMASH_PACK_ISO_LANGUAGE( iso_language[0], iso_language[1], iso_language[2] );
1856 static int isom_iso2mac_language( uint16_t ISO_language, uint16_t *MAC_language )
1858 assert( MAC_language );
1859 int i = 0;
1860 for( ; isom_languages[i].iso_name; i++ )
1861 if( ISO_language == isom_languages[i].iso_name )
1862 break;
1863 if( !isom_languages[i].iso_name )
1864 return LSMASH_ERR_NAMELESS;
1865 *MAC_language = isom_languages[i].mac_value;
1866 return 0;
1869 static int isom_mac2iso_language( uint16_t MAC_language, uint16_t *ISO_language )
1871 assert( ISO_language );
1872 int i = 0;
1873 for( ; isom_languages[i].iso_name; i++ )
1874 if( MAC_language == isom_languages[i].mac_value )
1875 break;
1876 *ISO_language = isom_languages[i].iso_name ? isom_languages[i].iso_name : ISOM_LANGUAGE_CODE_UNDEFINED;
1877 return 0;
1880 static int isom_set_media_language( lsmash_file_t *file, uint32_t track_ID, uint16_t ISO_language, uint16_t MAC_language )
1882 isom_trak_t *trak = isom_get_trak( file, track_ID );
1883 if( !trak
1884 || !trak->mdia
1885 || !trak->mdia->mdhd )
1886 return LSMASH_ERR_NAMELESS;
1887 uint16_t language = 0;
1888 if( file->isom_compatible )
1890 if( ISO_language )
1891 language = ISO_language;
1892 else if( MAC_language )
1894 int err = isom_mac2iso_language( MAC_language, &language );
1895 if( err )
1896 return err;
1898 else
1899 language = ISOM_LANGUAGE_CODE_UNDEFINED;
1901 else if( file->qt_compatible )
1903 if( ISO_language )
1905 int err = isom_iso2mac_language( ISO_language, &language );
1906 if( err )
1907 return err;
1909 else
1910 language = MAC_language;
1912 else
1913 return LSMASH_ERR_INVALID_DATA;
1914 trak->mdia->mdhd->language = language;
1915 return 0;
1918 int isom_add_sample_grouping( isom_box_t *parent, isom_grouping_type grouping_type )
1920 isom_sgpd_t *sgpd;
1921 isom_sbgp_t *sbgp;
1922 if( NULL == (sgpd = isom_add_sgpd( parent ))
1923 || NULL == (sbgp = isom_add_sbgp( parent )) )
1924 return LSMASH_ERR_NAMELESS;
1925 sbgp->grouping_type = grouping_type;
1926 sgpd->grouping_type = grouping_type;
1927 sgpd->version = 1; /* We use version 1 for Sample Group Description Box because it is recommended in the spec. */
1928 switch( grouping_type )
1930 case ISOM_GROUP_TYPE_RAP :
1931 sgpd->default_length = 1;
1932 break;
1933 case ISOM_GROUP_TYPE_ROLL :
1934 case ISOM_GROUP_TYPE_PROL :
1935 sgpd->default_length = 2;
1936 break;
1937 default :
1938 /* We don't consider other grouping types currently. */
1939 break;
1941 return 0;
1944 static int isom_create_sample_grouping( isom_trak_t *trak, isom_grouping_type grouping_type )
1946 lsmash_file_t *file = trak->file;
1947 switch( grouping_type )
1949 case ISOM_GROUP_TYPE_RAP :
1950 assert( file->max_isom_version >= 6 );
1951 break;
1952 case ISOM_GROUP_TYPE_ROLL :
1953 case ISOM_GROUP_TYPE_PROL :
1954 assert( file->avc_extensions || file->qt_compatible );
1955 break;
1956 default :
1957 assert( 0 );
1958 break;
1960 int err = isom_add_sample_grouping( (isom_box_t *)trak->mdia->minf->stbl, grouping_type );
1961 if( err < 0 )
1962 return err;
1963 if( trak->cache->fragment && file->max_isom_version >= 6 )
1964 switch( grouping_type )
1966 case ISOM_GROUP_TYPE_RAP :
1967 trak->cache->fragment->rap_grouping = 1;
1968 break;
1969 case ISOM_GROUP_TYPE_ROLL :
1970 case ISOM_GROUP_TYPE_PROL :
1971 trak->cache->fragment->roll_grouping = 1;
1972 break;
1973 default :
1974 /* We don't consider other grouping types currently. */
1975 break;
1977 return 0;
1980 static int isom_compress_sample_size_table( isom_stbl_t *stbl )
1982 if( stbl->file->max_3gpp_version )
1983 /* 3GPP: Limitations to the ISO base media file format
1984 * - compact sample sizes ('stz2') shall not be used for tracks containing H.263, MPEG-4 video, AMR, AMR-WB, AAC or Timed text.
1985 * Note that 'mp4a' check is incomplete here since this restriction is not applied to Enhanced aacPlus audio (HE-AAC v2). */
1986 for( lsmash_entry_t *entry = stbl->stsd->list.head; entry; entry = entry->next )
1988 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)entry->data;
1989 if( !sample_entry )
1990 return LSMASH_ERR_INVALID_DATA;
1991 lsmash_codec_type_t sample_type = sample_entry->type;
1992 if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_S263_VIDEO )
1993 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4V_VIDEO )
1994 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO )
1995 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_SAMR_AUDIO )
1996 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_SAWB_AUDIO )
1997 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_TX3G_TEXT ) )
1998 return 0;
2000 if( stbl->stsz && isom_is_variable_size( stbl ) )
2002 int max_num_bits = 0;
2003 for( lsmash_entry_t *entry = stbl->stsz->list->head; entry; entry = entry->next )
2005 isom_stsz_entry_t *data = (isom_stsz_entry_t *)entry->data;
2006 if( !data )
2007 return LSMASH_ERR_INVALID_DATA;
2008 int num_bits;
2009 for( num_bits = 1; data->entry_size >> num_bits; num_bits++ );
2010 if( max_num_bits < num_bits )
2012 max_num_bits = num_bits;
2013 if( max_num_bits > 16 )
2014 return 0; /* not compressible */
2017 if( max_num_bits <= 16 && isom_add_stz2( stbl ) )
2019 /* The sample size table can be compressed by using 'stz2'. */
2020 isom_stsz_t *stsz = stbl->stsz;
2021 isom_stz2_t *stz2 = stbl->stz2;
2022 stz2->sample_count = stsz->sample_count;
2023 if( max_num_bits <= 4 )
2024 stz2->field_size = 4;
2025 else if( max_num_bits <= 8 )
2026 stz2->field_size = 8;
2027 else
2028 stz2->field_size = 16;
2029 lsmash_move_entries( stz2->list, stsz->list );
2030 isom_remove_box_by_itself( stsz );
2033 return 0;
2036 static int isom_add_dependency_type( isom_stbl_t *stbl, lsmash_file_t *file, lsmash_sample_property_t *prop )
2038 if( !file->qt_compatible && !file->avc_extensions )
2039 return 0;
2040 int compatibility = file->avc_extensions && file->qt_compatible ? 3
2041 : file->qt_compatible ? 2
2042 : file->avc_extensions ? 1
2043 : 0;
2044 if( stbl->sdtp )
2045 return isom_add_sdtp_entry( (isom_box_t *)stbl, prop, compatibility );
2046 /* no null check for prop */
2047 if( !prop->allow_earlier
2048 && !prop->leading
2049 && !prop->independent
2050 && !prop->disposable
2051 && !prop->redundant )
2052 return 0;
2053 if( !isom_add_sdtp( (isom_box_t *)stbl ) )
2054 return LSMASH_ERR_NAMELESS;
2055 uint32_t count = isom_get_sample_count_from_sample_table( stbl );
2056 /* fill past samples with ISOM_SAMPLE_*_UNKNOWN */
2057 lsmash_sample_property_t null_prop = { 0 };
2058 for( uint32_t i = 1; i < count; i++ )
2060 int err = isom_add_sdtp_entry( (isom_box_t *)stbl, &null_prop, compatibility );
2061 if( err < 0 )
2062 return err;
2064 return isom_add_sdtp_entry( (isom_box_t *)stbl, prop, compatibility );
2067 void lsmash_initialize_media_parameters( lsmash_media_parameters_t *param )
2069 memset( param, 0, sizeof(lsmash_media_parameters_t) );
2070 param->timescale = 1;
2073 int lsmash_set_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param )
2075 if( isom_check_initializer_present( root ) < 0 )
2076 return LSMASH_ERR_FUNCTION_PARAM;
2077 lsmash_file_t *file = root->file;
2078 isom_trak_t *trak = isom_get_trak( file, track_ID );
2079 if( !trak
2080 || !trak->mdia
2081 || !trak->mdia->mdhd
2082 || !trak->mdia->minf
2083 || !trak->mdia->minf->stbl )
2084 return LSMASH_ERR_NAMELESS;
2085 trak->mdia->mdhd->timescale = param->timescale;
2086 int err = isom_set_media_language( file, track_ID, param->ISO_language, param->MAC_language );
2087 if( err < 0 )
2088 return err;
2089 if( param->media_handler_name
2090 && (err = isom_set_media_handler_name( file, track_ID, param->media_handler_name )) < 0 )
2091 return err;
2092 if( file->qt_compatible && param->data_handler_name
2093 && (err = isom_set_data_handler_name( file, track_ID, param->data_handler_name )) < 0 )
2094 return err;
2095 if( (file->avc_extensions || file->qt_compatible) && param->roll_grouping
2096 && (err = isom_create_sample_grouping( trak, ISOM_GROUP_TYPE_ROLL )) < 0 )
2097 return err;
2098 if( (file->max_isom_version >= 6) && param->rap_grouping
2099 && (err = isom_create_sample_grouping( trak, ISOM_GROUP_TYPE_RAP )) < 0 )
2100 return err;
2101 if( !file->qt_compatible && param->compact_sample_size_table )
2102 trak->mdia->minf->stbl->compress_sample_size_table = isom_compress_sample_size_table;
2103 if( !param->no_sample_dependency_table )
2104 trak->mdia->minf->stbl->add_dependency_type = isom_add_dependency_type;
2105 return 0;
2108 static uint32_t get_actual_handler_name_length( isom_hdlr_t *hdlr, lsmash_file_t *file )
2110 if( hdlr->componentName_length == 0 )
2111 return 0;
2112 uint32_t length;
2113 uint8_t *name;
2114 if( file->qt_compatible )
2116 length = LSMASH_MIN( hdlr->componentName[0], hdlr->componentName_length - 1 );
2117 if( !file->isom_compatible )
2118 return length;
2119 name = &hdlr->componentName[1];
2121 else
2123 length = hdlr->componentName_length;
2124 name = hdlr->componentName;
2126 /* Considering fool-proof such as not terminated by '\0'. */
2127 uint32_t i = 0;
2128 while( i < length && name[i] )
2129 ++i;
2130 return i;
2133 int lsmash_get_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param )
2135 if( isom_check_initializer_present( root ) < 0 )
2136 return LSMASH_ERR_FUNCTION_PARAM;
2137 lsmash_file_t *file = root->file->initializer;
2138 isom_trak_t *trak = isom_get_trak( file, track_ID );
2139 if( !trak
2140 || !trak->mdia
2141 || !trak->mdia->mdhd
2142 || !trak->mdia->hdlr
2143 || !trak->mdia->minf
2144 || !trak->mdia->minf->stbl )
2145 return LSMASH_ERR_NAMELESS;
2146 isom_mdhd_t *mdhd = trak->mdia->mdhd;
2147 isom_stbl_t *stbl = trak->mdia->minf->stbl;
2148 param->timescale = mdhd->timescale;
2149 param->handler_type = trak->mdia->hdlr->componentSubtype;
2150 param->duration = mdhd->duration;
2151 /* Whether sample grouping present. */
2153 isom_sbgp_t *sbgp;
2154 isom_sgpd_t *sgpd;
2155 sbgp = isom_get_sample_to_group ( stbl, ISOM_GROUP_TYPE_RAP );
2156 sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
2157 param->rap_grouping = sbgp && sgpd;
2158 sbgp = isom_get_roll_recovery_sample_to_group ( &stbl->sbgp_list );
2159 sgpd = isom_get_roll_recovery_sample_group_description( &stbl->sgpd_list );
2160 param->roll_grouping = sbgp && sgpd;
2162 /* Get media language. */
2163 if( mdhd->language >= 0x800 )
2165 param->MAC_language = 0;
2166 param->ISO_language = mdhd->language;
2168 else
2170 param->MAC_language = mdhd->language;
2171 param->ISO_language = 0;
2173 /* Get handler name(s). */
2174 isom_hdlr_t *hdlr = trak->mdia->hdlr;
2175 uint32_t actual_length = get_actual_handler_name_length( hdlr, file );
2176 uint32_t length = LSMASH_MIN( 255, actual_length );
2177 if( length )
2179 memcpy( param->media_handler_name_shadow, hdlr->componentName + file->qt_compatible, length );
2180 param->media_handler_name_shadow[length] = '\0';
2181 param->media_handler_name = param->media_handler_name_shadow;
2183 else
2185 param->media_handler_name = NULL;
2186 memset( param->media_handler_name_shadow, 0, sizeof(param->media_handler_name_shadow) );
2188 if( trak->mdia->minf->hdlr )
2190 hdlr = trak->mdia->minf->hdlr;
2191 actual_length = get_actual_handler_name_length( hdlr, file );
2192 length = LSMASH_MIN( 255, actual_length );
2193 if( length )
2195 memcpy( param->data_handler_name_shadow, hdlr->componentName + file->qt_compatible, length );
2196 param->data_handler_name_shadow[length] = '\0';
2197 param->data_handler_name = param->data_handler_name_shadow;
2199 else
2201 param->data_handler_name = NULL;
2202 memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) );
2205 else
2207 param->data_handler_name = NULL;
2208 memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) );
2210 param->compact_sample_size_table = !!stbl->stz2;
2211 param->no_sample_dependency_table = !!stbl->sdtp;
2212 param->reserved[0] = param->reserved[1] = 0;
2213 return 0;
2216 /*---- movie manipulators ----*/
2218 void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t *param )
2220 memset( param, 0, sizeof(lsmash_movie_parameters_t) );
2221 param->timescale = 600;
2222 param->playback_rate = 0x00010000;
2223 param->playback_volume = 0x0100;
2226 int lsmash_set_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param )
2228 if( !root )
2229 return LSMASH_ERR_FUNCTION_PARAM;
2230 lsmash_file_t *file = root->file;
2231 if( !file
2232 || !file->moov
2233 || !file->moov->mvhd )
2234 return LSMASH_ERR_NAMELESS;
2235 isom_mvhd_t *mvhd = file->moov->mvhd;
2236 mvhd->timescale = param->timescale;
2237 if( file->qt_compatible || file->itunes_movie )
2239 mvhd->rate = param->playback_rate;
2240 mvhd->volume = param->playback_volume;
2241 mvhd->previewTime = param->preview_time;
2242 mvhd->previewDuration = param->preview_duration;
2243 mvhd->posterTime = param->poster_time;
2245 else
2247 mvhd->rate = 0x00010000;
2248 mvhd->volume = 0x0100;
2249 mvhd->previewTime = 0;
2250 mvhd->previewDuration = 0;
2251 mvhd->posterTime = 0;
2253 return 0;
2256 int lsmash_get_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param )
2258 if( isom_check_initializer_present( root ) < 0 )
2259 return LSMASH_ERR_FUNCTION_PARAM;
2260 lsmash_file_t *file = root->file->initializer;
2261 if( !file->moov
2262 || !file->moov->mvhd )
2263 return LSMASH_ERR_NAMELESS;
2264 isom_mvhd_t *mvhd = file->moov->mvhd;
2265 param->timescale = mvhd->timescale;
2266 param->duration = mvhd->duration;
2267 param->playback_rate = mvhd->rate;
2268 param->playback_volume = mvhd->volume;
2269 param->preview_time = mvhd->previewTime;
2270 param->preview_duration = mvhd->previewDuration;
2271 param->poster_time = mvhd->posterTime;
2272 param->number_of_tracks = file->moov->trak_list.entry_count;
2273 return 0;
2276 uint32_t lsmash_get_movie_timescale( lsmash_root_t *root )
2278 if( isom_check_initializer_present( root ) < 0
2279 || !root->file->initializer->moov
2280 || !root->file->initializer->moov->mvhd )
2281 return 0;
2282 return root->file->initializer->moov->mvhd->timescale;
2285 static int isom_scan_trak_profileLevelIndication
2287 isom_trak_t *trak,
2288 mp4a_audioProfileLevelIndication *audio_pli,
2289 mp4sys_visualProfileLevelIndication *visual_pli
2292 if( !trak
2293 || !trak->mdia
2294 || !trak->mdia->minf
2295 || !trak->mdia->minf->stbl )
2296 return LSMASH_ERR_INVALID_DATA;
2297 isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd;
2298 if( !stsd
2299 || !stsd->list.head )
2300 return LSMASH_ERR_INVALID_DATA;
2301 for( lsmash_entry_t *entry = stsd->list.head; entry; entry = entry->next )
2303 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)entry->data;
2304 if( !sample_entry )
2305 return LSMASH_ERR_INVALID_DATA;
2306 lsmash_codec_type_t sample_type = sample_entry->type;
2307 if( trak->mdia->minf->vmhd )
2309 if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO )
2310 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC2_VIDEO )
2311 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC3_VIDEO )
2312 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC4_VIDEO )
2313 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVCP_VIDEO )
2314 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_SVC1_VIDEO )
2315 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MVC1_VIDEO )
2316 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MVC2_VIDEO ) )
2318 /* FIXME: Do we have to arbitrate like audio? */
2319 if( *visual_pli == MP4SYS_VISUAL_PLI_NONE_REQUIRED )
2320 *visual_pli = MP4SYS_VISUAL_PLI_H264_AVC;
2322 else
2323 *visual_pli = MP4SYS_VISUAL_PLI_NOT_SPECIFIED;
2325 else if( trak->mdia->minf->smhd )
2327 if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) )
2329 isom_audio_entry_t *audio = (isom_audio_entry_t *)sample_entry;
2330 isom_esds_t *esds = (isom_esds_t *)isom_get_extension_box_format( &audio->extensions, ISOM_BOX_TYPE_ESDS );
2331 if( !esds || !esds->ES )
2332 return LSMASH_ERR_INVALID_DATA;
2333 lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO );
2334 if( !summary )
2335 continue;
2336 if( mp4sys_setup_summary_from_DecoderSpecificInfo( summary, esds->ES ) < 0 )
2337 *audio_pli = MP4A_AUDIO_PLI_NOT_SPECIFIED;
2338 else
2339 *audio_pli = mp4a_max_audioProfileLevelIndication( *audio_pli, mp4a_get_audioProfileLevelIndication( summary ) );
2340 lsmash_cleanup_summary( (lsmash_summary_t *)summary );
2342 else
2343 /* NOTE: Audio CODECs other than 'mp4a' does not have appropriate pli. */
2344 *audio_pli = MP4A_AUDIO_PLI_NOT_SPECIFIED;
2346 else
2347 ; /* FIXME: Do we have to set OD_profileLevelIndication? */
2349 return 0;
2352 int isom_setup_iods( isom_moov_t *moov )
2354 if( !moov->iods && !isom_add_iods( moov ) )
2355 return LSMASH_ERR_NAMELESS;
2356 isom_iods_t *iods = moov->iods;
2357 int err = LSMASH_ERR_NAMELESS;
2358 iods->OD = mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */
2359 if( !iods->OD )
2360 goto fail;
2361 mp4a_audioProfileLevelIndication audio_pli = MP4A_AUDIO_PLI_NONE_REQUIRED;
2362 mp4sys_visualProfileLevelIndication visual_pli = MP4SYS_VISUAL_PLI_NONE_REQUIRED;
2363 for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next )
2365 isom_trak_t *trak = (isom_trak_t *)entry->data;
2366 if( !trak
2367 || !trak->tkhd )
2368 goto fail;
2369 if( (err = isom_scan_trak_profileLevelIndication( trak, &audio_pli, &visual_pli )) < 0 )
2370 goto fail;
2371 if( (err = mp4sys_create_ES_ID_Inc( iods->OD, trak->tkhd->track_ID )) < 0 )
2372 goto fail;
2374 if( (err = mp4sys_to_InitialObjectDescriptor( iods->OD,
2375 0, /* FIXME: I'm not quite sure what the spec says. */
2376 MP4SYS_OD_PLI_NONE_REQUIRED, MP4SYS_SCENE_PLI_NONE_REQUIRED,
2377 audio_pli, visual_pli,
2378 MP4SYS_GRAPHICS_PLI_NONE_REQUIRED )) < 0 )
2379 goto fail;
2380 return 0;
2381 fail:
2382 isom_remove_box_by_itself( iods );
2383 return err;
2386 int lsmash_create_object_descriptor( lsmash_root_t *root )
2388 if( isom_check_initializer_present( root ) < 0 )
2389 return LSMASH_ERR_FUNCTION_PARAM;
2390 lsmash_file_t *file = root->file;
2391 /* Return error if this file is not compatible with MP4 file format. */
2392 if( !file->mp4_version1
2393 && !file->mp4_version2 )
2394 return LSMASH_ERR_FUNCTION_PARAM;
2395 return isom_setup_iods( file->moov );
2398 /*---- finishing functions ----*/
2400 int isom_complement_data_reference( isom_minf_t *minf )
2402 if( !minf->dinf
2403 || !minf->dinf->dref )
2404 return LSMASH_ERR_INVALID_DATA;
2405 /* Complement data referece if absent. */
2406 if( !minf->dinf->dref->list.head )
2408 isom_dref_entry_t *url = isom_add_dref_entry( minf->dinf->dref, ISOM_BOX_TYPE_URL );
2409 if( !url )
2410 return LSMASH_ERR_NAMELESS;
2411 url->flags = 0x000001; /* Media data is in the same file. */
2413 return 0;
2416 static lsmash_file_t *isom_get_written_media_file
2418 isom_trak_t *trak,
2419 uint32_t sample_description_index
2422 isom_minf_t *minf = trak->mdia->minf;
2423 isom_sample_entry_t *description = (isom_sample_entry_t *)lsmash_get_entry_data( &minf->stbl->stsd->list, sample_description_index );
2424 isom_dref_entry_t *dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( &minf->dinf->dref->list, description ? description->data_reference_index : 1 );
2425 lsmash_file_t *file = (!dref_entry || !dref_entry->ref_file) ? trak->file : dref_entry->ref_file;
2426 if( !(file->flags & LSMASH_FILE_MODE_MEDIA)
2427 || !(file->flags & LSMASH_FILE_MODE_WRITE) )
2428 return trak->file;
2429 return file;
2432 int isom_check_large_offset_requirement
2434 isom_moov_t *moov,
2435 uint64_t meta_size
2438 for( lsmash_entry_t *entry = moov->trak_list.head; entry; )
2440 isom_trak_t *trak = (isom_trak_t *)entry->data;
2441 isom_stco_t *stco = trak->mdia->minf->stbl->stco;
2442 if( !stco->list->tail /* no samples */
2443 || stco->large_presentation
2444 || (((isom_stco_entry_t *)stco->list->tail->data)->chunk_offset + moov->size + meta_size) <= UINT32_MAX )
2446 entry = entry->next;
2447 continue; /* no need to convert stco into co64 */
2449 /* stco->co64 conversion */
2450 int err = isom_convert_stco_to_co64( trak->mdia->minf->stbl );
2451 if( err < 0 )
2452 return err;
2453 if( isom_update_box_size( moov ) == 0 )
2454 return LSMASH_ERR_INVALID_DATA;
2455 entry = moov->trak_list.head; /* whenever any conversion, re-check all traks */
2457 return 0;
2460 void isom_add_preceding_box_size
2462 isom_moov_t *moov,
2463 uint64_t preceding_size
2466 for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next )
2468 /* Apply to the chunks in the same file. */
2469 isom_trak_t *trak = (isom_trak_t *)entry->data;
2470 isom_stsc_t *stsc = trak->mdia->minf->stbl->stsc;
2471 isom_stco_t *stco = trak->mdia->minf->stbl->stco;
2472 lsmash_entry_t *stsc_entry = stsc->list->head;
2473 isom_stsc_entry_t *stsc_data = stsc_entry ? (isom_stsc_entry_t *)stsc_entry->data : NULL;
2474 uint32_t chunk_number = 1;
2475 for( lsmash_entry_t *stco_entry = stco->list->head; stco_entry; )
2477 if( stsc_data
2478 && stsc_data->first_chunk == chunk_number )
2480 lsmash_file_t *ref_file = isom_get_written_media_file( trak, stsc_data->sample_description_index );
2481 stsc_entry = stsc_entry->next;
2482 stsc_data = stsc_entry ? (isom_stsc_entry_t *)stsc_entry->data : NULL;
2483 if( ref_file != trak->file )
2485 /* The chunks are not contained in the same file. Skip applying the offset.
2486 * If no more stsc entries, the rest of the chunks is not contained in the same file. */
2487 if( !stsc_entry || !stsc_data )
2488 break;
2489 while( stco_entry && chunk_number < stsc_data->first_chunk )
2491 stco_entry = stco_entry->next;
2492 ++chunk_number;
2494 continue;
2497 if( stco->large_presentation )
2498 ((isom_co64_entry_t *)stco_entry->data)->chunk_offset += preceding_size;
2499 else
2500 ((isom_stco_entry_t *)stco_entry->data)->chunk_offset += preceding_size;
2501 stco_entry = stco_entry->next;
2502 ++chunk_number;
2507 int isom_establish_movie( lsmash_file_t *file )
2509 assert( file == file->initializer );
2510 int err;
2511 if( (err = isom_check_mandatory_boxes( file )) < 0
2512 || (err = isom_set_movie_creation_time( file )) < 0 )
2513 return err;
2514 if( isom_update_box_size( file->moov ) == 0 )
2515 return LSMASH_ERR_INVALID_DATA;
2516 return 0;
2519 int lsmash_finish_movie
2521 lsmash_root_t *root,
2522 lsmash_adhoc_remux_t *remux
2525 if( isom_check_initializer_present( root ) < 0 )
2526 return LSMASH_ERR_FUNCTION_PARAM;
2527 lsmash_file_t *file = root->file;
2528 if( !file
2529 || !file->bs
2530 || !file->initializer->moov )
2531 return LSMASH_ERR_INVALID_DATA;
2532 if( file->fragment )
2533 return isom_finish_final_fragment_movie( file, remux );
2534 if( file != file->initializer )
2535 return LSMASH_ERR_INVALID_DATA;
2536 int err;
2537 isom_moov_t *moov = file->moov;
2538 for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next )
2540 isom_trak_t *trak = (isom_trak_t *)entry->data;
2541 if( !trak
2542 || !trak->cache
2543 || !trak->tkhd
2544 || !trak->mdia
2545 || !trak->mdia->minf
2546 || !trak->mdia->minf->stbl
2547 || !trak->mdia->minf->stbl->stsd
2548 || !trak->mdia->minf->stbl->stsd->list.head
2549 || !trak->mdia->minf->stbl->stsd->list.head->data
2550 || !trak->mdia->minf->stbl->stco
2551 || !trak->mdia->minf->stbl->stco->list
2552 || !trak->mdia->minf->stbl->stco->list->tail )
2553 return LSMASH_ERR_INVALID_DATA;
2554 if( (err = isom_complement_data_reference( trak->mdia->minf )) < 0 )
2555 return err;
2556 uint32_t track_ID = trak->tkhd->track_ID;
2557 uint32_t related_track_ID = trak->related_track_ID;
2558 /* Disable the track if the track is a track reference chapter. */
2559 if( trak->is_chapter )
2560 trak->tkhd->flags &= ~ISOM_TRACK_ENABLED;
2561 if( trak->is_chapter && related_track_ID )
2563 /* In order that the track duration of the chapter track doesn't exceed that of the related track. */
2564 lsmash_edit_t edit;
2565 edit.duration = LSMASH_MIN( trak->tkhd->duration, lsmash_get_track_duration( root, related_track_ID ) );
2566 edit.start_time = 0;
2567 edit.rate = ISOM_EDIT_MODE_NORMAL;
2568 if( (err = lsmash_create_explicit_timeline_map( root, track_ID, edit )) < 0 )
2569 return err;
2571 isom_stbl_t *stbl = trak->mdia->minf->stbl;
2572 /* Compress sample size table. */
2573 if( stbl->compress_sample_size_table
2574 && (err = stbl->compress_sample_size_table( stbl )) < 0 )
2575 return err;
2576 /* Add stss box if any samples aren't sync sample. */
2577 if( !trak->cache->all_sync && !stbl->stss && !isom_add_stss( stbl ) )
2578 return LSMASH_ERR_NAMELESS;
2579 if( (err = isom_update_tkhd_duration( trak )) < 0
2580 || (err = isom_update_bitrate_description( trak->mdia )) < 0 )
2581 return err;
2583 if( file->mp4_version1 == 1 && (err = isom_setup_iods( moov )) < 0 )
2584 return err;
2585 if( (err = isom_establish_movie( file )) < 0 )
2586 return err;
2587 /* Write the size of Media Data Box here. */
2588 lsmash_bs_t *bs = file->bs;
2589 file->mdat->manager &= ~LSMASH_INCOMPLETE_BOX;
2590 if( (err = isom_write_box( bs, (isom_box_t *)file->mdat )) < 0 )
2591 return err;
2592 /* Write the Movie Box and a Meta Box if no optimization for progressive download. */
2593 uint64_t meta_size = file->meta ? file->meta->size : 0;
2594 if( !remux )
2596 if( (err = isom_write_box( bs, (isom_box_t *)file->moov )) < 0
2597 || (err = isom_write_box( bs, (isom_box_t *)file->meta )) < 0 )
2598 return err;
2599 file->size += moov->size + meta_size;
2600 return 0;
2602 /* stco->co64 conversion, depending on last chunk's offset */
2603 if( (err = isom_check_large_offset_requirement( moov, meta_size )) < 0 )
2604 return err;
2605 /* now the amount of offset is fixed. */
2606 uint64_t mtf_size = moov->size + meta_size; /* sum of size of boxes moved to front */
2607 /* buffer size must be at least mtf_size * 2 */
2608 remux->buffer_size = LSMASH_MAX( remux->buffer_size, mtf_size * 2 );
2609 /* Split to 2 buffers. */
2610 uint8_t *buf[2] = { NULL, NULL };
2611 if( (buf[0] = (uint8_t*)lsmash_malloc( remux->buffer_size )) == NULL )
2612 return LSMASH_ERR_MEMORY_ALLOC; /* NOTE: I think we still can fallback to "return isom_write_moov();" here. */
2613 size_t size = remux->buffer_size / 2;
2614 buf[1] = buf[0] + size;
2615 /* Now, the amount of the offset is fixed. apply it to stco/co64 */
2616 isom_add_preceding_box_size( moov, mtf_size );
2617 /* Backup starting area of mdat and write moov + meta there instead. */
2618 isom_mdat_t *mdat = file->mdat;
2619 uint64_t total = file->size + mtf_size;
2620 uint64_t placeholder_pos = file->free ? file->free->pos : mdat->pos;
2621 if( (err = lsmash_bs_write_seek( bs, placeholder_pos, SEEK_SET )) < 0 )
2622 goto fail;
2623 size_t read_num = size;
2624 lsmash_bs_read_data( bs, buf[0], &read_num );
2625 uint64_t read_pos = bs->offset;
2626 /* Write moov + meta there instead. */
2627 if( (err = lsmash_bs_write_seek( bs, placeholder_pos, SEEK_SET )) < 0
2628 || (err = isom_write_box( bs, (isom_box_t *)file->moov )) < 0
2629 || (err = isom_write_box( bs, (isom_box_t *)file->meta )) < 0 )
2630 goto fail;
2631 uint64_t write_pos = bs->offset;
2632 /* Update the positions */
2633 mdat->pos += mtf_size;
2634 if( file->free )
2635 file->free->pos += mtf_size;
2636 /* Move Media Data Box. */
2637 if( (err = isom_rearrange_data( file, remux, buf, read_num, size, read_pos, write_pos, total )) < 0 )
2638 goto fail;
2639 file->size += mtf_size;
2640 lsmash_free( buf[0] );
2641 return 0;
2642 fail:
2643 lsmash_free( buf[0] );
2644 return err;
2647 int lsmash_set_last_sample_delta( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_delta )
2649 if( isom_check_initializer_present( root ) < 0 || track_ID == 0 )
2650 return LSMASH_ERR_FUNCTION_PARAM;
2651 lsmash_file_t *file = root->file;
2652 if( file->fragment
2653 && file->fragment->movie )
2655 isom_traf_t *traf = isom_get_traf( file->fragment->movie, track_ID );
2656 if( !traf
2657 || !traf->cache
2658 || !traf->tfhd )
2659 return LSMASH_ERR_NAMELESS;
2660 return isom_set_fragment_last_duration( traf, sample_delta );
2662 if( file != file->initializer )
2663 return LSMASH_ERR_INVALID_DATA;
2664 isom_trak_t *trak = isom_get_trak( file, track_ID );
2665 if( !trak
2666 || !trak->cache
2667 || !trak->mdia
2668 || !trak->mdia->mdhd
2669 || !trak->mdia->minf
2670 || !trak->mdia->minf->stbl
2671 || !trak->mdia->minf->stbl->stsd
2672 || (!trak->mdia->minf->stbl->stsz && !trak->mdia->minf->stbl->stz2)
2673 || !trak->mdia->minf->stbl->stts
2674 || !trak->mdia->minf->stbl->stts->list )
2675 return LSMASH_ERR_NAMELESS;
2676 isom_stbl_t *stbl = trak->mdia->minf->stbl;
2677 isom_stts_t *stts = stbl->stts;
2678 uint32_t sample_count = isom_get_sample_count( trak );
2679 int err;
2680 if( !stts->list->tail )
2682 if( !sample_count )
2683 return 0; /* no samples */
2684 if( sample_count > 1 )
2685 return LSMASH_ERR_INVALID_DATA; /* irregular sample_count */
2686 /* Set the duration of the first sample.
2687 * This duration is also the duration of the last sample. */
2688 if( (err = isom_add_stts_entry( stbl, sample_delta )) < 0 )
2689 return err;
2690 return lsmash_update_track_duration( root, track_ID, 0 );
2692 uint32_t i = 0;
2693 for( lsmash_entry_t *entry = stts->list->head; entry; entry = entry->next )
2694 i += ((isom_stts_entry_t *)entry->data)->sample_count;
2695 if( sample_count < i )
2696 return LSMASH_ERR_INVALID_DATA;
2697 int no_last = (sample_count > i);
2698 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stts->list->tail->data;
2699 if( !last_stts_data )
2700 return LSMASH_ERR_INVALID_DATA;
2701 /* Consider QuikcTime fixed compression audio. */
2702 isom_audio_entry_t *audio = (isom_audio_entry_t *)lsmash_get_entry_data( &trak->mdia->minf->stbl->stsd->list,
2703 trak->cache->chunk.sample_description_index );
2704 if( !audio )
2705 return LSMASH_ERR_INVALID_DATA;
2706 if( (audio->manager & LSMASH_AUDIO_DESCRIPTION)
2707 && (audio->manager & LSMASH_QTFF_BASE)
2708 && (audio->version == 1)
2709 && (audio->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION) )
2711 if( audio->samplesPerPacket == 0 )
2712 return LSMASH_ERR_INVALID_DATA;
2713 int exclude_last_sample = no_last ? 0 : 1;
2714 uint32_t j = audio->samplesPerPacket;
2715 for( lsmash_entry_t *entry = stts->list->tail; entry && j > 1; entry = entry->prev )
2717 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)entry->data;
2718 if( !stts_data )
2719 return LSMASH_ERR_INVALID_DATA;
2720 for( uint32_t k = exclude_last_sample; k < stts_data->sample_count && j > 1; k++ )
2722 sample_delta -= stts_data->sample_delta;
2723 --j;
2725 exclude_last_sample = 0;
2728 /* Set sample_delta. */
2729 if( no_last )
2731 /* The duration of the last sample is not set yet. */
2732 if( sample_count - i > 1 )
2733 return LSMASH_ERR_INVALID_DATA;
2734 /* Add a sample_delta. */
2735 if( sample_delta == last_stts_data->sample_delta )
2736 ++ last_stts_data->sample_count;
2737 else if( (err = isom_add_stts_entry( stbl, sample_delta )) < 0 )
2738 return err;
2740 /* The duration of the last sample is already set. Replace it with a new one. */
2741 else if( (err = isom_replace_last_sample_delta( stbl, sample_delta )) < 0 )
2742 return err;
2743 return lsmash_update_track_duration( root, track_ID, sample_delta );
2746 /*---- timeline manipulator ----*/
2748 int lsmash_modify_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t edit_number, lsmash_edit_t edit )
2750 if( isom_check_initializer_present( root ) < 0
2751 || edit.start_time < -1 )
2752 return LSMASH_ERR_FUNCTION_PARAM;
2753 lsmash_file_t *file = root->file->initializer;
2754 isom_trak_t *trak = isom_get_trak( file, track_ID );
2755 if( !trak
2756 || !trak->edts
2757 || !trak->edts->elst
2758 || !trak->edts->elst->list )
2759 return LSMASH_ERR_NAMELESS;
2760 isom_elst_t *elst = trak->edts->elst;
2761 isom_elst_entry_t *data = (isom_elst_entry_t *)lsmash_get_entry_data( elst->list, edit_number );
2762 if( !data )
2763 return LSMASH_ERR_NAMELESS;
2764 data->segment_duration = edit.duration;
2765 data->media_time = edit.start_time;
2766 data->media_rate = edit.rate;
2767 if( elst->pos == 0 || !file->fragment || file->bs->unseekable )
2768 return isom_update_tkhd_duration( trak );
2769 /* Rewrite the specified entry.
2770 * Note: we don't update the version of the Edit List Box. */
2771 lsmash_bs_t *bs = file->bs;
2772 uint64_t current_pos = bs->offset;
2773 uint64_t entry_pos = elst->pos + ISOM_LIST_FULLBOX_COMMON_SIZE + ((uint64_t)edit_number - 1) * (elst->version == 1 ? 20 : 12);
2774 lsmash_bs_write_seek( bs, entry_pos, SEEK_SET );
2775 if( elst->version )
2777 lsmash_bs_put_be64( bs, data->segment_duration );
2778 lsmash_bs_put_be64( bs, data->media_time );
2780 else
2782 lsmash_bs_put_be32( bs, (uint32_t)LSMASH_MIN( data->segment_duration, UINT32_MAX ) );
2783 lsmash_bs_put_be32( bs, (uint32_t)data->media_time );
2785 lsmash_bs_put_be32( bs, data->media_rate );
2786 int ret = lsmash_bs_flush_buffer( bs );
2787 lsmash_bs_write_seek( bs, current_pos, SEEK_SET );
2788 return ret;
2791 int lsmash_create_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, lsmash_edit_t edit )
2793 if( isom_check_initializer_present( root ) < 0 || edit.start_time < -1 )
2794 return LSMASH_ERR_FUNCTION_PARAM;
2795 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
2796 if( !trak
2797 || !trak->tkhd )
2798 return LSMASH_ERR_NAMELESS;
2799 edit.duration = (edit.duration || root->file->fragment) ? edit.duration
2800 : trak->tkhd->duration ? trak->tkhd->duration
2801 : isom_update_tkhd_duration( trak ) < 0 ? 0
2802 : trak->tkhd->duration;
2803 if( (!trak->edts && !isom_add_edts( trak ))
2804 || (!trak->edts->elst && !isom_add_elst( trak->edts )) )
2805 return LSMASH_ERR_NAMELESS;
2806 int err = isom_add_elst_entry( trak->edts->elst, edit.duration, edit.start_time, edit.rate );
2807 if( err < 0 )
2808 return err;
2809 return isom_update_tkhd_duration( trak );
2812 int lsmash_get_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t edit_number, lsmash_edit_t *edit )
2814 if( isom_check_initializer_present( root ) < 0 || !edit )
2815 return LSMASH_ERR_FUNCTION_PARAM;
2816 isom_elst_entry_t *data;
2817 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
2818 if( !trak )
2819 data = isom_timelime_get_explicit_timeline_map( root, track_ID, edit_number );
2820 else
2822 if( !trak->edts
2823 || !trak->edts->elst )
2825 /* no edits */
2826 edit->duration = 0;
2827 edit->start_time = 0;
2828 edit->rate = 0;
2829 return 0;
2831 data = (isom_elst_entry_t *)lsmash_get_entry_data( trak->edts->elst->list, edit_number );
2833 if( !data )
2834 return LSMASH_ERR_NAMELESS;
2835 edit->duration = data->segment_duration;
2836 edit->start_time = data->media_time;
2837 edit->rate = data->media_rate;
2838 return 0;
2841 uint32_t lsmash_count_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID )
2843 if( isom_check_initializer_present( root ) < 0 )
2844 return LSMASH_ERR_FUNCTION_PARAM;
2845 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
2846 if( !trak )
2847 return isom_timelime_count_explicit_timeline_map( root, track_ID );
2848 else
2850 if( !trak->edts
2851 || !trak->edts->elst
2852 || !trak->edts->elst->list )
2853 return 0;
2854 return trak->edts->elst->list->entry_count;
2858 /*---- create / modification time fields manipulators ----*/
2860 int lsmash_update_media_modification_time( lsmash_root_t *root, uint32_t track_ID )
2862 if( isom_check_initializer_present( root ) < 0 )
2863 return LSMASH_ERR_FUNCTION_PARAM;
2864 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
2865 if( !trak
2866 || !trak->mdia
2867 || !trak->mdia->mdhd )
2868 return LSMASH_ERR_NAMELESS;
2869 isom_mdhd_t *mdhd = trak->mdia->mdhd;
2870 mdhd->modification_time = isom_get_current_mp4time();
2871 /* overwrite strange creation_time */
2872 if( mdhd->creation_time > mdhd->modification_time )
2873 mdhd->creation_time = mdhd->modification_time;
2874 return 0;
2877 int lsmash_update_track_modification_time( lsmash_root_t *root, uint32_t track_ID )
2879 if( isom_check_initializer_present( root ) < 0 )
2880 return LSMASH_ERR_FUNCTION_PARAM;
2881 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
2882 if( !trak
2883 || !trak->tkhd )
2884 return LSMASH_ERR_NAMELESS;
2885 isom_tkhd_t *tkhd = trak->tkhd;
2886 tkhd->modification_time = isom_get_current_mp4time();
2887 /* overwrite strange creation_time */
2888 if( tkhd->creation_time > tkhd->modification_time )
2889 tkhd->creation_time = tkhd->modification_time;
2890 return 0;
2893 int lsmash_update_movie_modification_time( lsmash_root_t *root )
2895 if( isom_check_initializer_present( root ) < 0 )
2896 return LSMASH_ERR_FUNCTION_PARAM;
2897 lsmash_file_t *file = root->file->initializer;
2898 if( !file->moov
2899 || !file->moov->mvhd )
2900 return LSMASH_ERR_INVALID_DATA;
2901 isom_mvhd_t *mvhd = file->moov->mvhd;
2902 mvhd->modification_time = isom_get_current_mp4time();
2903 /* overwrite strange creation_time */
2904 if( mvhd->creation_time > mvhd->modification_time )
2905 mvhd->creation_time = mvhd->modification_time;
2906 return 0;
2909 /*---- sample manipulators ----*/
2910 lsmash_sample_t *lsmash_create_sample( uint32_t size )
2912 lsmash_sample_t *sample = lsmash_malloc_zero( sizeof(lsmash_sample_t) );
2913 if( !sample )
2914 return NULL;
2915 if( size == 0 )
2916 return sample;
2917 sample->data = lsmash_malloc( size );
2918 if( !sample->data )
2920 lsmash_free( sample );
2921 return NULL;
2923 sample->length = size;
2924 return sample;
2927 int lsmash_sample_alloc( lsmash_sample_t *sample, uint32_t size )
2929 if( !sample )
2930 return LSMASH_ERR_FUNCTION_PARAM;
2931 if( size == 0 )
2933 lsmash_free( sample->data );
2934 sample->data = NULL;
2935 sample->length = 0;
2936 return 0;
2938 if( size == sample->length )
2939 return 0;
2940 uint8_t *data;
2941 if( !sample->data )
2942 data = lsmash_malloc( size );
2943 else
2944 data = lsmash_realloc( sample->data, size );
2945 if( !data )
2946 return LSMASH_ERR_MEMORY_ALLOC;
2947 sample->data = data;
2948 sample->length = size;
2949 return 0;
2952 void lsmash_delete_sample( lsmash_sample_t *sample )
2954 if( !sample )
2955 return;
2956 lsmash_free( sample->data );
2957 lsmash_free( sample );
2960 isom_sample_pool_t *isom_create_sample_pool( uint64_t size )
2962 isom_sample_pool_t *pool = lsmash_malloc_zero( sizeof(isom_sample_pool_t) );
2963 if( !pool )
2964 return NULL;
2965 if( size == 0 )
2966 return pool;
2967 pool->data = lsmash_malloc( size );
2968 if( !pool->data )
2970 lsmash_free( pool );
2971 return NULL;
2973 pool->alloc = size;
2974 return pool;
2977 void isom_remove_sample_pool( isom_sample_pool_t *pool )
2979 if( !pool )
2980 return;
2981 lsmash_free( pool->data );
2982 lsmash_free( pool );
2985 static uint32_t isom_add_size( isom_stbl_t *stbl, uint32_t sample_size )
2987 if( isom_add_stsz_entry( stbl, sample_size ) < 0 )
2988 return 0;
2989 return isom_get_sample_count_from_sample_table( stbl );
2992 static uint32_t isom_add_dts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t dts )
2994 isom_stts_t *stts = stbl->stts;
2995 if( stts->list->entry_count == 0 )
2997 if( isom_add_stts_entry( stbl, dts ) < 0 )
2998 return 0;
2999 cache->dts = dts;
3000 return dts;
3002 if( dts <= cache->dts )
3003 return 0;
3004 uint32_t sample_delta = dts - cache->dts;
3005 isom_stts_entry_t *data = (isom_stts_entry_t *)stts->list->tail->data;
3006 if( data->sample_delta == sample_delta )
3007 ++ data->sample_count;
3008 else if( isom_add_stts_entry( stbl, sample_delta ) < 0 )
3009 return 0;
3010 cache->dts = dts;
3011 return sample_delta;
3014 static int isom_add_cts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t cts )
3016 int err;
3017 isom_ctts_t *ctts = stbl->ctts;
3018 if( !ctts )
3020 if( cts == cache->dts )
3022 cache->cts = cts;
3023 return 0;
3025 /* Add ctts box and the first ctts entry. */
3026 if( !isom_add_ctts( stbl ) )
3027 return LSMASH_ERR_NAMELESS;
3028 if( (err = isom_add_ctts_entry( stbl, 0 )) < 0 )
3029 return err;
3030 ctts = stbl->ctts;
3031 isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->head->data;
3032 uint32_t sample_count = stbl->stsz ? stbl->stsz->sample_count : stbl->stz2->sample_count;
3033 if( sample_count != 1 )
3035 data->sample_count = sample_count - 1;
3036 if( (err = isom_add_ctts_entry( stbl, cts - cache->dts )) < 0 )
3037 return err;
3039 else
3040 data->sample_offset = cts;
3041 cache->cts = cts;
3042 return 0;
3044 if( !ctts->list )
3045 return LSMASH_ERR_INVALID_DATA;
3046 isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->tail->data;
3047 uint32_t sample_offset = cts - cache->dts;
3048 if( data->sample_offset == sample_offset )
3049 ++ data->sample_count;
3050 else if( (err = isom_add_ctts_entry( stbl, sample_offset )) < 0 )
3051 return err;
3052 cache->cts = cts;
3053 return 0;
3056 static int isom_add_timestamp( isom_stbl_t *stbl, isom_cache_t *cache, lsmash_file_t *file, uint64_t dts, uint64_t cts )
3058 if( !cache
3059 || !stbl->stts
3060 || !stbl->stts->list )
3061 return LSMASH_ERR_INVALID_DATA;
3062 if( file->isom_compatible && file->qt_compatible && (cts - dts) > INT32_MAX )
3063 return LSMASH_ERR_INVALID_DATA; /* sample_offset is not compatible. */
3064 isom_timestamp_t *ts_cache = &cache->timestamp;
3065 uint32_t sample_count = isom_get_sample_count_from_sample_table( stbl );
3066 uint32_t sample_delta = sample_count > 1 ? isom_add_dts( stbl, ts_cache, dts ) : 0;
3067 if( sample_count > 1 && sample_delta == 0 )
3068 return LSMASH_ERR_INVALID_DATA;
3069 int err = isom_add_cts( stbl, ts_cache, cts );
3070 if( err < 0 )
3071 return err;
3072 if( (cts + ts_cache->ctd_shift) < dts )
3074 if( (file->max_isom_version < 4 && !file->qt_compatible) /* Negative sample offset is not supported. */
3075 || (file->max_isom_version >= 4 && file->qt_compatible) /* ctts version 1 is not defined in QTFF. */
3076 || ((dts - cts) > INT32_MAX) ) /* Overflow */
3077 return LSMASH_ERR_INVALID_DATA;
3078 ts_cache->ctd_shift = dts - cts;
3079 if( stbl->ctts->version == 0 && !file->qt_compatible )
3080 stbl->ctts->version = 1;
3082 if( cache->fragment )
3084 isom_fragment_t *fragment_cache = cache->fragment;
3085 fragment_cache->last_duration = sample_delta;
3086 fragment_cache->largest_cts = LSMASH_MAX( ts_cache->cts, fragment_cache->largest_cts );
3088 return 0;
3091 static int isom_add_sync_point( isom_stbl_t *stbl, isom_cache_t *cache, uint32_t sample_number, lsmash_sample_property_t *prop )
3093 if( !(prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC) ) /* no null check for prop */
3095 if( !cache->all_sync )
3096 return 0;
3097 if( !stbl->stss && !isom_add_stss( stbl ) )
3098 return LSMASH_ERR_NAMELESS;
3099 int err = isom_add_stss_entry( stbl, 1 );
3100 if( err < 0 ) /* Declare here the first sample is a sync sample. */
3101 return err;
3102 cache->all_sync = 0;
3103 return 0;
3105 if( cache->all_sync ) /* We don't need stss box if all samples are sync sample. */
3106 return 0;
3107 if( !stbl->stss )
3109 if( isom_get_sample_count_from_sample_table( stbl ) == 1 )
3111 cache->all_sync = 1; /* Also the first sample is a sync sample. */
3112 return 0;
3114 if( !isom_add_stss( stbl ) )
3115 return LSMASH_ERR_NAMELESS;
3117 return isom_add_stss_entry( stbl, sample_number );
3120 static int isom_add_partial_sync( isom_stbl_t *stbl, lsmash_file_t *file, uint32_t sample_number, lsmash_sample_property_t *prop )
3122 if( !file->qt_compatible )
3123 return 0;
3124 if( !(prop->ra_flags & QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC) )
3125 return 0;
3126 /* This sample is a partial sync sample. */
3127 if( !stbl->stps && !isom_add_stps( stbl ) )
3128 return LSMASH_ERR_NAMELESS;
3129 return isom_add_stps_entry( stbl, sample_number );
3132 int isom_rap_grouping_established( isom_rap_group_t *group, int num_leading_samples_known, isom_sgpd_t *sgpd, int is_fragment )
3134 isom_rap_entry_t *rap = group->random_access;
3135 if( !rap )
3136 return 0;
3137 assert( rap == (isom_rap_entry_t *)sgpd->list->tail->data );
3138 rap->num_leading_samples_known = num_leading_samples_known;
3139 /* Avoid duplication of sample group descriptions. */
3140 uint32_t group_description_index = is_fragment ? 0x10001 : 1;
3141 for( lsmash_entry_t *entry = sgpd->list->head; entry != sgpd->list->tail; entry = entry->next )
3143 isom_rap_entry_t *data = (isom_rap_entry_t *)entry->data;
3144 if( !data )
3145 return LSMASH_ERR_INVALID_DATA;
3146 if( rap->num_leading_samples_known == data->num_leading_samples_known
3147 && rap->num_leading_samples == data->num_leading_samples )
3149 /* The same description already exists.
3150 * Remove the latest random access entry. */
3151 lsmash_remove_entry_tail( sgpd->list, NULL );
3152 /* Replace assigned group_description_index with the one corresponding the same description. */
3153 if( group->assignment->group_description_index == 0 )
3155 /* We don't create consecutive sample groups not assigned to 'rap '.
3156 * So the previous sample group shall be a group of 'rap ' if any. */
3157 if( group->prev_assignment )
3159 assert( group->prev_assignment->group_description_index );
3160 group->prev_assignment->group_description_index = group_description_index;
3163 else
3164 group->assignment->group_description_index = group_description_index;
3165 break;
3167 ++group_description_index;
3169 group->random_access = NULL;
3170 return 0;
3173 int isom_group_random_access( isom_box_t *parent, isom_cache_t *cache, lsmash_sample_t *sample )
3175 if( parent->file->max_isom_version < 6 )
3176 return 0;
3177 isom_sbgp_t *sbgp;
3178 isom_sgpd_t *sgpd;
3179 uint32_t sample_count;
3180 int is_fragment;
3181 if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) )
3183 isom_stbl_t *stbl = (isom_stbl_t *)parent;
3184 sbgp = isom_get_sample_to_group ( stbl, ISOM_GROUP_TYPE_RAP );
3185 sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
3186 sample_count = isom_get_sample_count_from_sample_table( stbl );
3187 is_fragment = 0;
3189 else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) )
3191 isom_traf_t *traf = (isom_traf_t *)parent;
3192 sbgp = isom_get_fragment_sample_to_group ( traf, ISOM_GROUP_TYPE_RAP );
3193 sgpd = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_RAP );
3194 sample_count = cache->fragment->sample_count + 1;
3195 is_fragment = 1;
3197 else
3199 assert( 0 );
3200 sbgp = NULL;
3201 sgpd = NULL;
3202 /* redundant initializations to suppress warnings from unclever compilers */
3203 sample_count = 0;
3204 is_fragment = 0;
3206 if( !sbgp || !sgpd )
3207 return 0;
3208 lsmash_sample_property_t *prop = &sample->prop;
3209 uint8_t is_rap = (prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC)
3210 || (prop->ra_flags & QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC)
3211 || (prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP)
3212 || (LSMASH_IS_POST_ROLL_START( prop->ra_flags ) && prop->post_roll.identifier == prop->post_roll.complete);
3213 isom_rap_group_t *group = cache->rap;
3214 if( !group )
3216 /* This sample is the first sample, create a grouping cache. */
3217 assert( sample_count == 1 );
3218 group = lsmash_malloc( sizeof(isom_rap_group_t) );
3219 if( !group )
3220 return LSMASH_ERR_MEMORY_ALLOC;
3221 if( is_rap )
3223 group->random_access = isom_add_rap_group_entry( sgpd );
3224 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count + (is_fragment ? 0x10000 : 0) );
3226 else
3228 /* The first sample is not always a random access point. */
3229 group->random_access = NULL;
3230 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
3232 if( !group->assignment )
3234 lsmash_free( group );
3235 return LSMASH_ERR_MEMORY_ALLOC;
3237 group->prev_assignment = NULL;
3238 group->is_prev_rap = is_rap;
3239 cache->rap = group;
3240 return 0;
3242 int err;
3243 if( group->is_prev_rap )
3245 /* OK. here, the previous sample is a menber of 'rap '. */
3246 if( !is_rap )
3248 /* This sample isn't a member of 'rap ' and the previous sample is.
3249 * So we create a new group and set 0 on its group_description_index. */
3250 group->prev_assignment = group->assignment;
3251 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
3252 if( !group->assignment )
3254 lsmash_free( group );
3255 return LSMASH_ERR_MEMORY_ALLOC;
3258 else if( !LSMASH_IS_CLOSED_RAP( prop->ra_flags ) )
3260 /* Create a new group since there is the possibility the next sample is a leading sample.
3261 * This sample is a member of 'rap ', so we set appropriate value on its group_description_index. */
3262 if( (err = isom_rap_grouping_established( group, 1, sgpd, is_fragment )) < 0 )
3263 return err;
3264 group->random_access = isom_add_rap_group_entry( sgpd );
3265 group->prev_assignment = group->assignment;
3266 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count + (is_fragment ? 0x10000 : 0) );
3267 if( !group->assignment )
3269 lsmash_free( group );
3270 return LSMASH_ERR_MEMORY_ALLOC;
3273 else /* The previous and current sample are a member of 'rap ', and the next sample must not be a leading sample. */
3274 ++ group->assignment->sample_count;
3276 else if( is_rap )
3278 /* This sample is a member of 'rap ' and the previous sample isn't.
3279 * So we create a new group and set appropriate value on its group_description_index. */
3280 if( (err = isom_rap_grouping_established( group, 1, sgpd, is_fragment )) < 0 )
3281 return err;
3282 group->random_access = isom_add_rap_group_entry( sgpd );
3283 group->prev_assignment = group->assignment;
3284 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count + (is_fragment ? 0x10000 : 0) );
3285 if( !group->assignment )
3287 lsmash_free( group );
3288 return LSMASH_ERR_MEMORY_ALLOC;
3291 else /* The previous and current sample aren't a member of 'rap '. */
3292 ++ group->assignment->sample_count;
3293 /* Obtain the property of the latest random access point group. */
3294 if( !is_rap && group->random_access )
3296 if( prop->leading == ISOM_SAMPLE_LEADING_UNKNOWN )
3298 /* We can no longer know num_leading_samples in this group. */
3299 if( (err = isom_rap_grouping_established( group, 0, sgpd, is_fragment )) < 0 )
3300 return err;
3302 else
3304 if( prop->leading == ISOM_SAMPLE_IS_UNDECODABLE_LEADING
3305 || prop->leading == ISOM_SAMPLE_IS_DECODABLE_LEADING )
3306 ++ group->random_access->num_leading_samples;
3307 /* no more consecutive leading samples in this group */
3308 else if( (err = isom_rap_grouping_established( group, 1, sgpd, is_fragment )) < 0 )
3309 return err;
3312 group->is_prev_rap = is_rap;
3313 return 0;
3316 static int isom_roll_grouping_established( isom_roll_group_t *group )
3318 /* Avoid duplication of sample group descriptions. */
3319 isom_sgpd_t *sgpd = group->sgpd;
3320 uint32_t group_description_index = group->is_fragment ? 0x10001 : 1;
3321 for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next )
3323 isom_roll_entry_t *data = (isom_roll_entry_t *)entry->data;
3324 if( !data )
3325 return LSMASH_ERR_INVALID_DATA;
3326 if( group->roll_distance == data->roll_distance )
3328 /* The same description already exists.
3329 * Set the group_description_index corresponding the same description. */
3330 group->assignment->group_description_index = group_description_index;
3331 return 0;
3333 ++group_description_index;
3335 /* Add a new roll recovery description. */
3336 if( !isom_add_roll_group_entry( sgpd, group->roll_distance ) )
3337 return LSMASH_ERR_MEMORY_ALLOC;
3338 group->assignment->group_description_index = sgpd->list->entry_count + (group->is_fragment ? 0x10000 : 0);
3339 return 0;
3342 static int isom_deduplicate_roll_group( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool )
3344 /* Deduplication */
3345 uint32_t current_group_number = sbgp->list->entry_count - pool->entry_count + 1;
3346 isom_group_assignment_entry_t *prev_assignment = (isom_group_assignment_entry_t *)lsmash_get_entry_data( sbgp->list, current_group_number - 1 );
3347 for( lsmash_entry_t *entry = pool->head; entry; )
3349 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3350 if( !group
3351 || !group->assignment )
3352 return LSMASH_ERR_INVALID_DATA;
3353 if( !group->delimited || group->described != ROLL_DISTANCE_DETERMINED )
3354 return 0;
3355 if( prev_assignment && prev_assignment->group_description_index == group->assignment->group_description_index )
3357 /* Merge the current group with the previous. */
3358 lsmash_entry_t *next_entry = entry->next;
3359 prev_assignment->sample_count += group->assignment->sample_count;
3360 int err;
3361 if( (err = lsmash_remove_entry( sbgp->list, current_group_number, NULL )) < 0
3362 || (err = lsmash_remove_entry_direct( pool, entry, NULL )) < 0 )
3363 return err;
3364 entry = next_entry;
3366 else
3368 entry = entry->next;
3369 prev_assignment = group->assignment;
3370 ++current_group_number;
3373 return 0;
3376 /* Remove pooled caches that has become unnecessary. */
3377 static int isom_clean_roll_pool( lsmash_entry_list_t *pool )
3379 for( lsmash_entry_t *entry = pool->head; entry; entry = pool->head )
3381 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3382 if( !group )
3383 return LSMASH_ERR_INVALID_DATA;
3384 if( !group->delimited || group->described != ROLL_DISTANCE_DETERMINED )
3385 return 0;
3386 int err = lsmash_remove_entry_direct( pool, entry, NULL );
3387 if( err < 0 )
3388 return err;
3390 return 0;
3393 static int isom_flush_roll_pool( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool )
3395 int err;
3396 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
3398 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3399 if( !group )
3400 return LSMASH_ERR_INVALID_DATA;
3401 if( group->delimited
3402 && group->described == ROLL_DISTANCE_DETERMINED
3403 && group->roll_distance != 0
3404 && (err = isom_roll_grouping_established( group )) < 0 )
3405 return err;
3407 if( (err = isom_deduplicate_roll_group( sbgp, pool )) < 0 )
3408 return err;
3409 return isom_clean_roll_pool( pool );
3412 static int isom_all_recovery_described( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool )
3414 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
3416 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3417 if( !group )
3418 return LSMASH_ERR_INVALID_DATA;
3419 group->described = ROLL_DISTANCE_DETERMINED;
3421 return isom_flush_roll_pool( sbgp, pool );
3424 int isom_all_recovery_completed( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool )
3426 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
3428 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3429 if( !group )
3430 return LSMASH_ERR_INVALID_DATA;
3431 group->described = ROLL_DISTANCE_DETERMINED;
3432 group->delimited = 1;
3434 return isom_flush_roll_pool( sbgp, pool );
3437 static isom_roll_entry_t *isom_get_roll_description
3439 isom_roll_group_t *group
3442 uint32_t group_description_index = group->assignment->group_description_index;
3443 if( group_description_index && group->is_fragment )
3445 assert( group_description_index > 0x10000 );
3446 group_description_index -= 0x10000;
3448 return (isom_roll_entry_t *)lsmash_get_entry_data( group->sgpd->list, group_description_index );
3451 int isom_group_roll_recovery( isom_box_t *parent, isom_cache_t *cache, lsmash_sample_t *sample )
3453 if( !parent->file->avc_extensions
3454 && !parent->file->qt_compatible )
3455 return 0;
3456 uint32_t sample_count;
3457 int is_fragment;
3458 lsmash_entry_list_t *sbgp_list;
3459 lsmash_entry_list_t *sgpd_list;
3460 if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) )
3462 isom_stbl_t *stbl = (isom_stbl_t *)parent;
3463 sbgp_list = &stbl->sbgp_list;
3464 sgpd_list = &stbl->sgpd_list;
3465 sample_count = isom_get_sample_count_from_sample_table( stbl );
3466 is_fragment = 0;
3468 else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) )
3470 if( parent->file->max_isom_version < 6 )
3471 return 0;
3472 isom_traf_t *traf = (isom_traf_t *)parent;
3473 sbgp_list = &traf->sbgp_list;
3474 sgpd_list = &traf->sgpd_list;
3475 sample_count = cache->fragment->sample_count + 1;
3476 is_fragment = 1;
3478 else
3480 assert( 0 );
3481 return LSMASH_ERR_INVALID_DATA;
3483 isom_sbgp_t *sbgp = isom_get_roll_recovery_sample_to_group ( sbgp_list );
3484 isom_sgpd_t *sgpd = isom_get_roll_recovery_sample_group_description( sgpd_list );
3485 if( !sbgp || !sgpd || sbgp->grouping_type != sgpd->grouping_type )
3486 return 0;
3487 /* Check if 'roll' -> 'prol' conversion is needed. */
3488 if( cache->is_audio
3489 && sbgp->grouping_type == ISOM_GROUP_TYPE_ROLL
3490 && !(sample->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC) )
3492 /* Since not every samples is a sync sample, change grouping_type into 'prol'. */
3493 sbgp->grouping_type = ISOM_GROUP_TYPE_PROL;
3494 sgpd->grouping_type = ISOM_GROUP_TYPE_PROL;
3496 lsmash_entry_list_t *pool = cache->roll.pool;
3497 if( !pool )
3499 pool = lsmash_create_entry_list();
3500 if( !pool )
3501 return LSMASH_ERR_MEMORY_ALLOC;
3502 cache->roll.pool = pool;
3504 lsmash_sample_property_t *prop = &sample->prop;
3505 isom_roll_group_t *group = (isom_roll_group_t *)lsmash_get_entry_data( pool, pool->entry_count );
3506 int is_recovery_start = LSMASH_IS_POST_ROLL_START( prop->ra_flags );
3507 int valid_pre_roll = !is_recovery_start
3508 && (prop->ra_flags != ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE)
3509 && (prop->pre_roll.distance > 0)
3510 && (prop->pre_roll.distance <= -INT16_MIN);
3511 int new_group = !group || is_recovery_start || (group->prev_is_recovery_start != is_recovery_start);
3512 if( !new_group )
3514 /* Check pre-roll distance. */
3515 assert( group->assignment && group->sgpd );
3516 isom_roll_entry_t *prev_roll = isom_get_roll_description( group );
3517 if( !prev_roll )
3518 new_group = valid_pre_roll;
3519 else if( !valid_pre_roll || (prop->pre_roll.distance != -prev_roll->roll_distance) )
3520 /* Pre-roll distance is different from the previous. */
3521 new_group = 1;
3523 if( new_group )
3525 if( group )
3526 group->delimited = 1;
3527 else
3528 assert( sample_count == 1 );
3529 /* Create a new group. */
3530 group = lsmash_malloc_zero( sizeof(isom_roll_group_t) );
3531 if( !group )
3532 return LSMASH_ERR_MEMORY_ALLOC;
3533 group->sgpd = sgpd;
3534 group->prev_is_recovery_start = is_recovery_start;
3535 group->is_fragment = is_fragment;
3536 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
3537 if( !group->assignment || lsmash_add_entry( pool, group ) < 0 )
3539 lsmash_free( group );
3540 return LSMASH_ERR_MEMORY_ALLOC;
3542 if( is_recovery_start )
3544 /* a member of non-roll or post-roll group */
3545 group->first_sample = sample_count;
3546 group->recovery_point = prop->post_roll.complete;
3548 else
3550 group->described = ROLL_DISTANCE_DETERMINED;
3551 if( valid_pre_roll )
3553 /* a member of pre-roll group */
3554 group->roll_distance = -(signed)prop->pre_roll.distance;
3555 int err = isom_roll_grouping_established( group );
3556 if( err < 0 )
3557 return err;
3559 else
3560 /* a member of non-roll group */
3561 group->roll_distance = 0;
3564 else
3566 group->prev_is_recovery_start = is_recovery_start;
3567 group->assignment->sample_count += 1;
3569 /* If encountered a RAP, all recovery is completed here. */
3570 if( prop->ra_flags & (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
3571 | ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
3572 | QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC) )
3573 return isom_all_recovery_described( sbgp, pool );
3574 /* Check whether this sample is a random access recovery point or not. */
3575 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
3577 group = (isom_roll_group_t *)entry->data;
3578 if( !group )
3579 return LSMASH_ERR_INVALID_DATA;
3580 if( group->described == ROLL_DISTANCE_DETERMINED )
3581 continue;
3582 if( group->described == ROLL_DISTANCE_INITIALIZED )
3584 /* Let's consider the following picture sequence.
3585 * coded order : P[0] P[1] P[2] P[3] P[4] P[5]
3586 * DTS : 0 1 2 3 4 5
3587 * CTS : 2 4 3 6 7 5
3588 * Here, P[0] conveys a recovery point SEI and P[3] is the recovery point.
3589 * Correctness of decoded pictures is specified by recovery point in output order for both AVC and HEVC.
3590 * Therefore, as follows,
3591 * output order : P[0] P[2] P[1] P[5]|P[3] P[4]
3592 * ---(incorrect?)--->|
3593 * there is no guarantee that P[5] is decoded and output correctly.
3594 * From this, it can be said that the roll_distance of this sequence is equal to 5. */
3595 isom_roll_entry_t *post_roll = isom_get_roll_description( group );
3596 if( post_roll && post_roll->roll_distance > 0 )
3598 if( group->rp_cts > sample->cts )
3599 /* Updated roll_distance for composition reordering. */
3600 post_roll->roll_distance = sample_count - group->first_sample;
3601 if( ++ group->wait_and_see_count >= MAX_ROLL_WAIT_AND_SEE_COUNT )
3602 group->described = ROLL_DISTANCE_DETERMINED;
3605 else if( prop->post_roll.identifier == group->recovery_point )
3607 int16_t distance = sample_count - group->first_sample;
3608 group->rp_cts = sample->cts;
3609 group->roll_distance = distance;
3610 /* Add a roll recovery entry only when roll_distance isn't zero since roll_distance = 0 must not be used. */
3611 if( distance )
3613 /* Now, this group is a 'roll'.
3614 * The roll_distance may be updated later because of composition reordering. */
3615 group->described = ROLL_DISTANCE_INITIALIZED;
3616 group->wait_and_see_count = 0;
3617 /* All groups with uninitialized roll_distance before the current group are described. */
3618 lsmash_entry_t *current = entry;
3619 for( entry = pool->head; entry != current; entry = entry->next )
3621 group = (isom_roll_group_t *)entry->data;
3622 if( !group || group->described != ROLL_DISTANCE_INITIALIZED )
3623 continue;
3624 group->described = ROLL_DISTANCE_DETERMINED;
3626 /* Cache the CTS of the first recovery point in a subsegment. */
3627 if( cache->fragment
3628 && cache->fragment->subsegment.first_rp_number == 0 )
3630 cache->fragment->subsegment.first_rp_number = sample_count;
3631 cache->fragment->subsegment.first_rp_cts = sample->cts;
3632 cache->fragment->subsegment.first_ed_cts = sample->cts;
3633 cache->fragment->subsegment.decodable = 1;
3636 else
3637 /* Random Accessible Point */
3638 return isom_all_recovery_described( sbgp, pool );
3641 return isom_flush_roll_pool( sbgp, pool );
3644 static int isom_update_chunk_tables
3646 isom_stbl_t *stbl,
3647 lsmash_file_t *media_file,
3648 isom_chunk_t *current
3651 isom_stsc_entry_t *last_stsc_data = stbl->stsc->list->tail ? (isom_stsc_entry_t *)stbl->stsc->list->tail->data : NULL;
3652 /* Create a new chunk sequence in this track if needed. */
3653 int err;
3654 if( (!last_stsc_data
3655 || current->pool->sample_count != last_stsc_data->samples_per_chunk
3656 || current->sample_description_index != last_stsc_data->sample_description_index)
3657 && (err = isom_add_stsc_entry( stbl, current->chunk_number,
3658 current->pool->sample_count,
3659 current->sample_description_index )) < 0 )
3660 return err;
3661 /* Add a new chunk offset in this track. */
3662 uint64_t offset = media_file->size;
3663 if( media_file->fragment )
3664 offset += ISOM_BASEBOX_COMMON_SIZE + media_file->fragment->pool_size;
3665 return isom_add_stco_entry( stbl, offset );
3668 /* This function decides to put a give sample on the current chunk or the next new one.
3669 * Returns 1 if pooled samples must be flushed.
3670 * FIXME: I wonder if this function should have a extra argument which indicates force_to_flush_cached_chunk.
3671 * see lsmash_append_sample for detail. */
3672 static int isom_add_sample_to_chunk
3674 isom_trak_t *trak,
3675 lsmash_sample_t *sample
3678 if( !trak->file
3679 || !trak->cache
3680 || !trak->mdia->mdhd
3681 || trak->mdia->mdhd->timescale == 0
3682 || !trak->mdia->minf->dinf
3683 || !trak->mdia->minf->dinf->dref
3684 || !trak->mdia->minf->stbl->stsd
3685 || !trak->mdia->minf->stbl->stsc
3686 || !trak->mdia->minf->stbl->stsc->list )
3687 return LSMASH_ERR_INVALID_DATA;
3688 isom_chunk_t *current = &trak->cache->chunk;
3689 if( !current->pool )
3691 /* Very initial settings, just once per track */
3692 current->pool = isom_create_sample_pool( 0 );
3693 if( !current->pool )
3694 return LSMASH_ERR_MEMORY_ALLOC;
3696 if( current->pool->sample_count == 0 )
3698 /* Cannot decide whether we should flush the current sample or not here yet. */
3699 current->chunk_number += 1;
3700 current->sample_description_index = sample->index;
3701 current->first_dts = sample->dts;
3702 return 0;
3704 if( sample->dts < current->first_dts )
3705 return LSMASH_ERR_INVALID_DATA; /* easy error check. */
3706 lsmash_file_t *media_file = isom_get_written_media_file( trak, current->sample_description_index );
3707 if( (current->sample_description_index == sample->index)
3708 && (media_file->max_chunk_duration >= ((double)(sample->dts - current->first_dts) / trak->mdia->mdhd->timescale))
3709 && (media_file->max_chunk_size >= current->pool->size + sample->length) )
3710 return 0; /* No need to flush current cached chunk, the current sample must be put into that. */
3711 /* NOTE: chunk relative stuff must be pushed into file after a chunk is fully determined with its contents.
3712 * Now the current cached chunk is fixed, actually add the chunk relative properties to its file accordingly. */
3713 int err = isom_update_chunk_tables( trak->mdia->minf->stbl, media_file, current );
3714 if( err < 0 )
3715 return err;
3716 /* Update and re-initialize cache, using the current sample */
3717 current->chunk_number += 1;
3718 current->sample_description_index = sample->index;
3719 current->first_dts = sample->dts;
3720 /* current->pool must be flushed in isom_append_sample_internal() */
3721 return 1;
3724 static int isom_write_pooled_samples( lsmash_file_t *file, isom_sample_pool_t *pool )
3726 if( !file
3727 || !file->bs
3728 || !file->bs->stream
3729 || !(file->flags & LSMASH_FILE_MODE_WRITE)
3730 || !(file->flags & LSMASH_FILE_MODE_MEDIA)
3731 || ((file->flags & LSMASH_FILE_MODE_BOX) && !file->mdat) )
3732 return LSMASH_ERR_INVALID_DATA;
3733 lsmash_bs_put_bytes( file->bs, pool->size, pool->data );
3734 int err = lsmash_bs_flush_buffer( file->bs );
3735 if( err < 0 )
3736 return err;
3737 if( file->mdat )
3738 file->mdat->media_size += pool->size;
3739 file->size += pool->size;
3740 pool->sample_count = 0;
3741 pool->size = 0;
3742 return 0;
3745 int isom_update_sample_tables
3747 isom_trak_t *trak,
3748 lsmash_sample_t *sample,
3749 uint32_t *samples_per_packet,
3750 isom_sample_entry_t *sample_entry
3753 int err;
3754 isom_audio_entry_t *audio = (isom_audio_entry_t *)sample_entry;
3755 if( (audio->manager & LSMASH_AUDIO_DESCRIPTION)
3756 && (audio->manager & LSMASH_QTFF_BASE)
3757 && (audio->version == 1)
3758 && (audio->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION) )
3760 /* Add entries of the sample table for each uncompressed sample. */
3761 uint64_t sample_duration = trak->mdia->mdhd->timescale / (audio->samplerate >> 16);
3762 if( audio->samplesPerPacket == 0 || sample_duration == 0 )
3763 return LSMASH_ERR_INVALID_DATA;
3764 uint64_t sample_dts = sample->dts;
3765 uint64_t sample_cts = sample->cts;
3766 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3767 for( uint32_t i = 0; i < audio->samplesPerPacket; i++ )
3769 /* Add a size of uncomressed audio and increment sample_count.
3770 * This points to individual uncompressed audio samples, each one byte in size, within the compressed frames. */
3771 uint32_t sample_count = isom_add_size( stbl, 1 );
3772 if( sample_count == 0 )
3773 return LSMASH_ERR_NAMELESS;
3774 /* Add a decoding timestamp and a composition timestamp. */
3775 if( (err = isom_add_timestamp( stbl, trak->cache, trak->file, sample_dts, sample_cts )) < 0 )
3776 return err;
3777 sample_dts += sample_duration;
3778 sample_cts += sample_duration;
3780 *samples_per_packet = audio->samplesPerPacket;
3782 else
3784 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3785 /* Add a sample_size and increment sample_count. */
3786 uint32_t sample_count = isom_add_size( stbl, sample->length );
3787 if( sample_count == 0 )
3788 return LSMASH_ERR_NAMELESS;
3789 /* Add a decoding timestamp and a composition timestamp. */
3790 if( (err = isom_add_timestamp( stbl, trak->cache, trak->file, sample->dts, sample->cts )) < 0 )
3791 return err;
3792 /* Add a sync point if needed. */
3793 if( (err = isom_add_sync_point( stbl, trak->cache, sample_count, &sample->prop )) < 0 )
3794 return err;
3795 /* Add a partial sync point if needed. */
3796 if( (err = isom_add_partial_sync( stbl, trak->file, sample_count, &sample->prop )) < 0 )
3797 return err;
3798 /* Add leading, independent, disposable and redundant information if needed. */
3799 if( stbl->add_dependency_type
3800 && (err = stbl->add_dependency_type( stbl, trak->file, &sample->prop )) < 0 )
3801 return err;
3802 /* Group samples into random access point type if needed. */
3803 if( (err = isom_group_random_access( (isom_box_t *)stbl, trak->cache, sample )) < 0 )
3804 return err;
3805 /* Group samples into random access recovery point type if needed. */
3806 if( (err = isom_group_roll_recovery( (isom_box_t *)stbl, trak->cache, sample )) < 0 )
3807 return err;
3808 *samples_per_packet = 1;
3810 /* Add a chunk if needed. */
3811 return isom_add_sample_to_chunk( trak, sample );
3814 static int isom_output_cached_chunk( isom_trak_t *trak )
3816 isom_chunk_t *chunk = &trak->cache->chunk;
3817 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3818 isom_stsc_entry_t *last_stsc_data = stbl->stsc->list->tail ? (isom_stsc_entry_t *)stbl->stsc->list->tail->data : NULL;
3819 /* Create a new chunk sequence in this track if needed. */
3820 int err;
3821 if( (!last_stsc_data
3822 || chunk->pool->sample_count != last_stsc_data->samples_per_chunk
3823 || chunk->sample_description_index != last_stsc_data->sample_description_index)
3824 && (err = isom_add_stsc_entry( stbl, chunk->chunk_number,
3825 chunk->pool->sample_count,
3826 chunk->sample_description_index )) < 0 )
3827 return err;
3828 lsmash_file_t *file = isom_get_written_media_file( trak, chunk->sample_description_index );
3829 if( file->fragment )
3831 /* Add a new chunk offset in this track. */
3832 if( (err = isom_add_stco_entry( stbl, file->size + ISOM_BASEBOX_COMMON_SIZE + file->fragment->pool_size )) < 0 )
3833 return err;
3834 return isom_append_fragment_track_run( file, chunk );
3836 /* Add a new chunk offset in this track. */
3837 if( (err = isom_add_stco_entry( stbl, file->size )) < 0 )
3838 return err;
3839 /* Output pooled samples in this track. */
3840 return isom_write_pooled_samples( file, chunk->pool );
3843 int isom_pool_sample( isom_sample_pool_t *pool, lsmash_sample_t *sample, uint32_t samples_per_packet )
3845 uint64_t pool_size = pool->size + sample->length;
3846 if( pool->alloc < pool_size )
3848 uint8_t *data;
3849 uint64_t alloc = pool_size + (1<<16);
3850 if( !pool->data )
3851 data = lsmash_malloc( alloc );
3852 else
3853 data = lsmash_realloc( pool->data, alloc );
3854 if( !data )
3855 return LSMASH_ERR_MEMORY_ALLOC;
3856 pool->data = data;
3857 pool->alloc = alloc;
3859 memcpy( pool->data + pool->size, sample->data, sample->length );
3860 pool->size = pool_size;
3861 pool->sample_count += samples_per_packet;
3862 lsmash_delete_sample( sample );
3863 return 0;
3866 static int isom_append_sample_internal
3868 isom_trak_t *trak,
3869 lsmash_sample_t *sample,
3870 isom_sample_entry_t *sample_entry
3873 uint32_t samples_per_packet;
3874 int ret = isom_update_sample_tables( trak, sample, &samples_per_packet, sample_entry );
3875 if( ret < 0 )
3876 return ret;
3877 /* ret == 1 means pooled samples must be flushed. */
3878 isom_sample_pool_t *current_pool = trak->cache->chunk.pool;
3879 if( ret == 1 )
3881 /* The sample_description_index in the cache is one of the next written chunk.
3882 * Therefore, it cannot be referenced here. */
3883 lsmash_entry_list_t *stsc_list = trak->mdia->minf->stbl->stsc->list;
3884 isom_stsc_entry_t *last_stsc_data = (isom_stsc_entry_t *)stsc_list->tail->data;
3885 lsmash_file_t *file = isom_get_written_media_file( trak, last_stsc_data->sample_description_index );
3886 if( (ret = isom_write_pooled_samples( file, current_pool )) < 0 )
3887 return ret;
3889 /* Arbitration system between tracks with extremely scattering dts.
3890 * Here, we check whether asynchronization between the tracks exceeds the tolerance.
3891 * If a track has too old "first DTS" in its cached chunk than current sample's DTS, then its pooled samples must be flushed.
3892 * We don't consider presentation of media since any edit can pick an arbitrary portion of media in track.
3893 * Note: you needn't read this loop until you grasp the basic handling of chunks. */
3894 lsmash_file_t *file = trak->file;
3895 double tolerance = file->max_async_tolerance;
3896 for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next )
3898 isom_trak_t *other = (isom_trak_t *)entry->data;
3899 if( trak == other )
3900 continue;
3901 if( !other
3902 || !other->cache
3903 || !other->mdia
3904 || !other->mdia->mdhd
3905 || other->mdia->mdhd->timescale == 0
3906 || !other->mdia->minf
3907 || !other->mdia->minf->stbl
3908 || !other->mdia->minf->stbl->stsc
3909 || !other->mdia->minf->stbl->stsc->list )
3910 return LSMASH_ERR_INVALID_DATA;
3911 isom_chunk_t *chunk = &other->cache->chunk;
3912 if( !chunk->pool || chunk->pool->sample_count == 0 )
3913 continue;
3914 double diff = ((double)sample->dts / trak->mdia->mdhd->timescale)
3915 - ((double)chunk->first_dts / other->mdia->mdhd->timescale);
3916 if( diff > tolerance && (ret = isom_output_cached_chunk( other )) < 0 )
3917 return ret;
3918 /* Note: we don't flush the cached chunk in the current track and the current sample here
3919 * even if the conditional expression of '-diff > tolerance' meets.
3920 * That's useless because appending a sample to another track would be a good equivalent.
3921 * It's even harmful because it causes excess chunk division by calling
3922 * isom_output_cached_chunk() which always generates a new chunk.
3923 * Anyway some excess chunk division will be there, but rather less without it.
3924 * To completely avoid this, we need to observe at least whether the current sample will be placed
3925 * right next to the previous chunk of the same track or not. */
3927 /* anyway the current sample must be pooled. */
3928 return isom_pool_sample( current_pool, sample, samples_per_packet );
3931 int isom_append_sample_by_type
3933 void *track,
3934 lsmash_sample_t *sample,
3935 isom_sample_entry_t *sample_entry,
3936 int (*func_append_sample)( void *, lsmash_sample_t *, isom_sample_entry_t * )
3939 if( isom_is_lpcm_audio( sample_entry ) )
3941 uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket;
3942 if( sample->length == frame_size )
3943 return func_append_sample( track, sample, sample_entry );
3944 else if( sample->length < frame_size )
3945 return LSMASH_ERR_INVALID_DATA;
3946 /* Append samples splitted into each LPCMFrame. */
3947 uint64_t dts = sample->dts;
3948 uint64_t cts = sample->cts;
3949 for( uint32_t offset = 0; offset < sample->length; offset += frame_size )
3951 lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size );
3952 if( !lpcm_sample )
3953 return LSMASH_ERR_MEMORY_ALLOC;
3954 memcpy( lpcm_sample->data, sample->data + offset, frame_size );
3955 lpcm_sample->dts = dts++;
3956 lpcm_sample->cts = cts++;
3957 lpcm_sample->prop = sample->prop;
3958 lpcm_sample->index = sample->index;
3959 int err = func_append_sample( track, lpcm_sample, sample_entry );
3960 if( err < 0 )
3962 lsmash_delete_sample( lpcm_sample );
3963 return err;
3966 lsmash_delete_sample( sample );
3967 return 0;
3969 return func_append_sample( track, sample, sample_entry );
3972 /* This function is for non-fragmented movie. */
3973 static int isom_append_sample
3975 lsmash_file_t *file,
3976 isom_trak_t *trak,
3977 lsmash_sample_t *sample,
3978 isom_sample_entry_t *sample_entry
3981 /* If there is no available Media Data Box to write samples, add and write a new one before any chunk offset is decided. */
3982 int err;
3983 if( !file->mdat )
3985 if( !isom_add_mdat( file ) )
3986 return LSMASH_ERR_NAMELESS;
3987 file->mdat->manager |= LSMASH_PLACEHOLDER;
3988 if( (err = isom_write_box( file->bs, (isom_box_t *)file->mdat )) < 0 )
3989 return err;
3990 assert( file->free );
3991 file->size += file->free->size + file->mdat->size;
3993 return isom_append_sample_by_type( trak, sample, sample_entry, (int (*)( void *, lsmash_sample_t *, isom_sample_entry_t * ))isom_append_sample_internal );
3996 static int isom_output_cache( isom_trak_t *trak )
3998 int err;
3999 isom_cache_t *cache = trak->cache;
4000 if( cache->chunk.pool
4001 && cache->chunk.pool->sample_count
4002 && (err = isom_output_cached_chunk( trak )) < 0 )
4003 return err;
4004 isom_stbl_t *stbl = trak->mdia->minf->stbl;
4005 for( lsmash_entry_t *entry = stbl->sgpd_list.head; entry; entry = entry->next )
4007 isom_sgpd_t *sgpd = (isom_sgpd_t *)entry->data;
4008 if( !sgpd )
4009 return LSMASH_ERR_INVALID_DATA;
4010 switch( sgpd->grouping_type )
4012 case ISOM_GROUP_TYPE_RAP :
4014 isom_rap_group_t *group = cache->rap;
4015 if( !group )
4017 if( stbl->file->fragment )
4018 continue;
4019 else
4020 return LSMASH_ERR_NAMELESS;
4022 if( !group->random_access )
4023 continue;
4024 group->random_access->num_leading_samples_known = 1;
4025 break;
4027 case ISOM_GROUP_TYPE_ROLL :
4028 case ISOM_GROUP_TYPE_PROL :
4029 if( !cache->roll.pool )
4031 if( stbl->file->fragment )
4032 continue;
4033 else
4034 return LSMASH_ERR_NAMELESS;
4036 isom_sbgp_t *sbgp = isom_get_roll_recovery_sample_to_group( &stbl->sbgp_list );
4037 if( !sbgp )
4038 return LSMASH_ERR_NAMELESS;
4039 if( (err = isom_all_recovery_completed( sbgp, cache->roll.pool )) < 0 )
4040 return err;
4041 break;
4042 default :
4043 break;
4046 return 0;
4049 int lsmash_flush_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta )
4051 if( isom_check_initializer_present( root ) < 0 )
4052 return LSMASH_ERR_FUNCTION_PARAM;
4053 lsmash_file_t *file = root->file;
4054 if( file->fragment
4055 && file->fragment->movie )
4056 return isom_flush_fragment_pooled_samples( file, track_ID, last_sample_delta );
4057 if( file != file->initializer )
4058 return LSMASH_ERR_INVALID_DATA;
4059 isom_trak_t *trak = isom_get_trak( file, track_ID );
4060 if( !trak
4061 || !trak->cache
4062 || !trak->mdia
4063 || !trak->mdia->minf
4064 || !trak->mdia->minf->stbl
4065 || !trak->mdia->minf->stbl->stsc
4066 || !trak->mdia->minf->stbl->stsc->list )
4067 return LSMASH_ERR_NAMELESS;
4068 int err = isom_output_cache( trak );
4069 if( err < 0 )
4070 return err;
4071 return lsmash_set_last_sample_delta( root, track_ID, last_sample_delta );
4074 int lsmash_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample )
4076 if( isom_check_initializer_present( root ) < 0
4077 || track_ID == 0
4078 || !sample
4079 || !sample->data )
4080 return LSMASH_ERR_FUNCTION_PARAM;
4081 lsmash_file_t *file = root->file;
4082 /* We think max_chunk_duration == 0, which means all samples will be cached on memory, should be prevented.
4083 * This means removal of a feature that we used to have, but anyway very alone chunk does not make sense. */
4084 if( !file
4085 || !file->bs
4086 || !(file->flags & LSMASH_FILE_MODE_BOX)
4087 || file->max_chunk_duration == 0
4088 || file->max_async_tolerance == 0 )
4089 return LSMASH_ERR_NAMELESS;
4090 /* Write File Type Box here if it was not written yet. */
4091 if( file->flags & LSMASH_FILE_MODE_INITIALIZATION )
4093 if( file->ftyp && !(file->ftyp->manager & LSMASH_WRITTEN_BOX) )
4095 int err = isom_write_box( file->bs, (isom_box_t *)file->ftyp );
4096 if( err < 0 )
4097 return err;
4098 file->size += file->ftyp->size;
4101 /* Get a sample initializer. */
4102 isom_trak_t *trak = isom_get_trak( file->initializer, track_ID );
4103 if( !trak
4104 || !trak->file
4105 || !trak->cache
4106 || !trak->tkhd
4107 || !trak->mdia
4108 || !trak->mdia->mdhd
4109 || trak->mdia->mdhd->timescale == 0
4110 || !trak->mdia->minf
4111 || !trak->mdia->minf->stbl
4112 || !trak->mdia->minf->stbl->stsd
4113 || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
4114 return LSMASH_ERR_NAMELESS;
4115 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( &trak->mdia->minf->stbl->stsd->list, sample->index );
4116 if( !sample_entry )
4117 return LSMASH_ERR_NAMELESS;
4118 /* Append a sample. */
4119 if( (file->flags & LSMASH_FILE_MODE_FRAGMENTED)
4120 && file->fragment
4121 && file->fragment->pool )
4122 return isom_append_fragment_sample( file, trak, sample, sample_entry );
4123 if( file != file->initializer )
4124 return LSMASH_ERR_INVALID_DATA;
4125 return isom_append_sample( file, trak, sample, sample_entry );
4128 /*---- misc functions ----*/
4130 int lsmash_delete_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID )
4132 if( isom_check_initializer_present( root ) < 0 )
4133 return LSMASH_ERR_FUNCTION_PARAM;
4134 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
4135 if( !trak )
4136 return LSMASH_ERR_NAMELESS;
4137 isom_remove_box_by_itself( trak->edts );
4138 return isom_update_tkhd_duration( trak );
4141 void lsmash_delete_tyrant_chapter( lsmash_root_t *root )
4143 if( isom_check_initializer_present( root ) < 0
4144 || !root->file->initializer->moov
4145 || !root->file->initializer->moov->udta )
4146 return;
4147 isom_remove_box_by_itself( root->file->moov->udta->chpl );
4150 int lsmash_set_copyright( lsmash_root_t *root, uint32_t track_ID, uint16_t ISO_language, char *notice )
4152 if( isom_check_initializer_present( root ) < 0
4153 || (ISO_language && ISO_language < 0x800)
4154 || !notice )
4155 return LSMASH_ERR_FUNCTION_PARAM;
4156 lsmash_file_t *file = root->file;
4157 if( !file->moov
4158 || !file->isom_compatible )
4159 return LSMASH_ERR_NAMELESS;
4160 isom_udta_t *udta;
4161 if( track_ID )
4163 isom_trak_t *trak = isom_get_trak( file, track_ID );
4164 if( !trak || (!trak->udta && !isom_add_udta( trak )) )
4165 return LSMASH_ERR_NAMELESS;
4166 udta = trak->udta;
4168 else
4170 if( !file->moov->udta && !isom_add_udta( file->moov ) )
4171 return LSMASH_ERR_NAMELESS;
4172 udta = file->moov->udta;
4174 assert( udta );
4175 for( lsmash_entry_t *entry = udta->cprt_list.head; entry; entry = entry->next )
4177 isom_cprt_t *cprt = (isom_cprt_t *)entry->data;
4178 if( !cprt || cprt->language == ISO_language )
4179 return LSMASH_ERR_NAMELESS;
4181 if( !isom_add_cprt( udta ) )
4182 return LSMASH_ERR_NAMELESS;
4183 isom_cprt_t *cprt = (isom_cprt_t *)udta->cprt_list.tail->data;
4184 cprt->language = ISO_language;
4185 cprt->notice_length = strlen( notice ) + 1;
4186 cprt->notice = lsmash_memdup( notice, cprt->notice_length );
4187 return 0;