timelineeditor: Support importing timecode format v1 and automatic timescale generation.
[L-SMASH.git] / timeline.c
blob85f2accb3e9efc68dffe883d0fda9fcc933e68e5
1 /*****************************************************************************
2 * timeline.c:
3 *****************************************************************************
4 * Copyright (C) 2011 L-SMASH project
6 * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
8 * Permission to use, copy, modify, and/or distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 *****************************************************************************/
21 /* This file is available under an ISC license. */
23 #ifdef LSMASH_DEMUXER_ENABLED
25 #include "internal.h" /* must be placed first */
27 #include <stdlib.h>
28 #include <string.h>
29 #include <inttypes.h>
31 #include "box.h"
32 #include "mp4a.h"
33 #include "mp4sys.h"
36 typedef struct
38 uint64_t data_offset;
39 uint64_t length;
40 void *data;
41 uint32_t number;
42 } isom_portable_chunk_t;
44 typedef struct
46 uint64_t pos;
47 uint32_t duration;
48 uint32_t offset;
49 uint32_t length;
50 uint32_t index;
51 isom_portable_chunk_t *chunk;
52 lsmash_sample_property_t prop;
53 } isom_sample_info_t;
55 typedef struct
57 uint32_t track_ID;
58 uint32_t movie_timescale;
59 uint32_t media_timescale;
60 int32_t ctd_shift; /* shift from composition to decode timeline */
61 uint32_t last_accessed_sample_number;
62 uint32_t last_accessed_chunk_number;
63 uint64_t last_accessed_sample_dts;
64 uint64_t last_accessed_offset;
65 uint64_t last_read_size;
66 void *last_accessed_chunk_data;
67 lsmash_entry_list_t edit_list [1]; /* list of edits */
68 lsmash_entry_list_t description_list[1]; /* list of descriptions */
69 lsmash_entry_list_t chunk_list [1]; /* list of chunks */
70 lsmash_entry_list_t info_list [1]; /* list of sample info */
71 } isom_timeline_t;
73 static isom_timeline_t *isom_get_timeline( lsmash_root_t *root, uint32_t track_ID )
75 if( !track_ID || !root || !root->timeline )
76 return NULL;
77 for( lsmash_entry_t *entry = root->timeline->head; entry; entry = entry->next )
79 isom_timeline_t *timeline = (isom_timeline_t *)entry->data;
80 if( !timeline )
81 return NULL;
82 if( timeline->track_ID == track_ID )
83 return timeline;
85 return NULL;
88 static isom_timeline_t *isom_create_timeline( void )
90 isom_timeline_t *timeline = malloc( sizeof(isom_timeline_t) );
91 if( !timeline )
92 return NULL;
93 timeline->track_ID = 0;
94 timeline->ctd_shift = 0;
95 timeline->last_accessed_sample_number = 0;
96 timeline->last_accessed_chunk_number = 0;
97 timeline->last_accessed_sample_dts = 0;
98 timeline->last_accessed_offset = 0;
99 timeline->last_read_size = 0;
100 timeline->last_accessed_chunk_data = NULL;
101 lsmash_init_entry_list( timeline->edit_list );
102 lsmash_init_entry_list( timeline->description_list );
103 lsmash_init_entry_list( timeline->chunk_list );
104 lsmash_init_entry_list( timeline->info_list );
105 return timeline;
108 static void isom_destruct_timeline_direct( isom_timeline_t *timeline )
110 if( !timeline )
111 return;
112 if( timeline->last_accessed_chunk_data )
113 free( timeline->last_accessed_chunk_data );
114 lsmash_remove_entries( timeline->edit_list, NULL );
115 lsmash_remove_entries( timeline->description_list, isom_remove_sample_description );
116 lsmash_remove_entries( timeline->chunk_list, NULL ); /* chunk data must be already freed. */
117 lsmash_remove_entries( timeline->info_list, NULL );
118 free( timeline );
121 void isom_remove_timelines( lsmash_root_t *root )
123 if( !root || !root->timeline )
124 return;
125 lsmash_remove_list( root->timeline, isom_destruct_timeline_direct );
128 void lsmash_destruct_timeline( lsmash_root_t *root, uint32_t track_ID )
130 if( !track_ID || !root || !root->timeline )
131 return;
132 for( lsmash_entry_t *entry = root->timeline->head; entry; entry = entry->next )
134 isom_timeline_t *timeline = (isom_timeline_t *)entry->data;
135 if( !timeline )
136 continue;
137 if( timeline->track_ID == track_ID )
138 lsmash_remove_entry_direct( root->timeline, entry, isom_destruct_timeline_direct );
142 #if 0
143 static lsmash_video_summary_t *isom_create_video_summary_from_description( isom_visual_entry_t *visual )
145 isom_visual_entry_t *visual;
146 lsmash_video_summary_t *summary = malloc( sizeof(lsmash_video_summary_t) );
147 if( !summary )
148 return NULL;
149 memset( summary, 0, sizeof(lsmash_video_summary_t) );
150 summary->width = visual->width;
151 summary->height = visual->height;
152 if( visual->clap )
154 double cleanApertureWidth = (double)clap->cleanApertureWidthN / cleanApertureWidthD;
155 double cleanApertureHeight = (double)clap->cleanApertureHeightN / cleanApertureHeightD;
156 double horizOff = (double)clap->horizOffN / clap->horizOffD;
157 double vertOff = (double)clap->vertOffN / clap->vertOffD;
158 summary->crop_top = (summary->height - cleanApertureHeight + vertOff) / 2;
159 summary->crop_left = (summary->width - cleanApertureWidth + horizOff) / 2;
160 summary->crop_bottom = (summary->height - cleanApertureHeight - vertOff) / 2;
161 summary->crop_right = (summary->width - cleanApertureWidth - horizOff) / 2;
163 if( visual->pasp )
165 summary->par_h = visual->pasp->hSpacing;
166 summary->par_v = visual->pasp->vSpacing;
168 if( visual->stsl )
169 summary->scaling_method = visual->stsl->scale_method;
170 if( visual->colr )
172 summary->primaries = visual->colr->primaries_index;
173 summary->transfer = visual->colr->transfer_function_index;
174 summary->matrix = visual->colr->matrix_index;
176 return summary;
178 #endif
180 static isom_esds_t *isom_duplicate_esds( isom_box_t *dst_parent, isom_esds_t *src )
182 if( !src || !src->ES )
183 return NULL;
184 isom_esds_t *dst = malloc( sizeof(isom_esds_t) );
185 if( !dst )
186 return NULL;
187 isom_init_box_common( dst, dst_parent, ISOM_BOX_TYPE_ESDS );
188 dst->ES = mp4sys_duplicate_ES_Descriptor( src->ES );
189 if( !dst->ES )
191 free( dst );
192 return NULL;
194 return dst;
197 static int isom_copy_clap( isom_visual_entry_t *dst, isom_visual_entry_t *src )
199 if( !dst )
200 return 0;
201 if( !src || !src->clap )
203 isom_remove_clap( dst->clap );
204 return 0;
206 if( !dst->clap && isom_add_clap( dst ) )
207 return -1;
208 isom_copy_fields( dst, src, clap );
209 return 0;
212 static int isom_copy_pasp( isom_visual_entry_t *dst, isom_visual_entry_t *src )
214 if( !dst )
215 return 0;
216 if( !src || !src->pasp )
218 isom_remove_pasp( dst->pasp );
219 return 0;
221 if( !dst->pasp && isom_add_pasp( dst ) )
222 return -1;
223 isom_copy_fields( dst, src, pasp );
224 return 0;
227 static int isom_copy_colr( isom_visual_entry_t *dst, isom_visual_entry_t *src )
229 if( !dst )
230 return 0;
231 if( !src || !src->colr )
233 isom_remove_colr( dst->colr );
234 return 0;
236 if( !dst->colr && isom_add_colr( dst ) )
237 return -1;
238 isom_copy_fields( dst, src, colr );
239 return 0;
242 static int isom_copy_stsl( isom_visual_entry_t *dst, isom_visual_entry_t *src )
244 if( !dst )
245 return 0;
246 if( !src || !src->stsl )
248 isom_remove_stsl( dst->stsl );
249 return 0;
251 if( !dst->stsl && isom_add_stsl( dst ) )
252 return -1;
253 isom_copy_fields( dst, src, stsl );
254 return 0;
257 static int isom_copy_ps_entries( lsmash_entry_list_t *dst, lsmash_entry_list_t *src )
259 if( !src )
260 return 0;
261 for( lsmash_entry_t *entry = src->head; entry; entry = entry->next )
263 isom_avcC_ps_entry_t *src_ps = (isom_avcC_ps_entry_t *)entry->data;
264 if( !src_ps )
265 return -1;
266 isom_avcC_ps_entry_t *dst_ps = isom_create_ps_entry( src_ps->parameterSetNALUnit, src_ps->parameterSetLength );
267 if( !dst_ps )
268 return -1;
269 if( lsmash_add_entry( dst, dst_ps ) )
271 isom_remove_avcC_ps( dst_ps );
272 return -1;
275 return 0;
278 static int isom_copy_avcC( isom_visual_entry_t *dst, isom_visual_entry_t *src )
280 if( !dst )
281 return 0;
282 isom_remove_avcC( dst->avcC );
283 if( !src || !src->avcC )
284 return 0;
285 if( isom_add_avcC( dst ) )
286 return -1;
287 isom_avcC_t temp = *dst->avcC; /* Hold created lists. */
288 isom_copy_fields( dst, src, avcC );
289 dst->avcC->sequenceParameterSets = temp.sequenceParameterSets;
290 dst->avcC->pictureParameterSets = temp.pictureParameterSets;
291 dst->avcC->sequenceParameterSetExt = temp.sequenceParameterSetExt;
292 if( isom_copy_ps_entries( dst->avcC->sequenceParameterSets, src->avcC->sequenceParameterSets )
293 || isom_copy_ps_entries( dst->avcC->pictureParameterSets, src->avcC->pictureParameterSets )
294 || isom_copy_ps_entries( dst->avcC->sequenceParameterSetExt, src->avcC->sequenceParameterSetExt ) )
295 return -1;
296 return 0;
299 static int isom_copy_btrt( isom_visual_entry_t *dst, isom_visual_entry_t *src )
301 if( !dst )
302 return 0;
303 if( !src || !src->btrt )
305 isom_remove_btrt( dst->btrt );
306 return 0;
308 if( !dst->btrt && isom_add_btrt( dst ) )
309 return -1;
310 isom_copy_fields( dst, src, btrt );
311 return 0;
314 static isom_visual_entry_t *isom_duplicate_visual_description( isom_visual_entry_t *src )
316 isom_visual_entry_t *dst = lsmash_memdup( src, sizeof(isom_visual_entry_t) );
317 if( !dst )
318 return NULL;
319 dst->clap = NULL;
320 dst->pasp = NULL;
321 dst->colr = NULL;
322 dst->stsl = NULL;
323 dst->esds = NULL;
324 dst->avcC = NULL;
325 dst->btrt = NULL;
326 /* Copy children. */
327 dst->esds = isom_duplicate_esds( (isom_box_t *)dst, src->esds );
328 if( (src->esds && !dst->esds) /* Check if copying failed. */
329 || isom_copy_clap( dst, src )
330 || isom_copy_pasp( dst, src )
331 || isom_copy_colr( dst, src )
332 || isom_copy_stsl( dst, src )
333 || isom_copy_avcC( dst, src )
334 || isom_copy_btrt( dst, src ) )
336 isom_remove_sample_description( (isom_sample_entry_t *)dst );
337 return NULL;
339 return dst;
342 static int isom_copy_frma( isom_wave_t *dst, isom_wave_t *src )
344 if( !dst )
345 return 0;
346 if( !src || !src->frma )
348 isom_remove_frma( dst->frma );
349 return 0;
351 if( !dst->frma && isom_add_frma( dst ) )
352 return -1;
353 isom_copy_fields( dst, src, frma );
354 return 0;
357 static int isom_copy_enda( isom_wave_t *dst, isom_wave_t *src )
359 if( !dst )
360 return 0;
361 if( !src || !src->enda )
363 isom_remove_enda( dst->enda );
364 return 0;
366 if( !dst->enda && isom_add_enda( dst ) )
367 return -1;
368 isom_copy_fields( dst, src, enda );
369 return 0;
372 static int isom_copy_mp4a( isom_wave_t *dst, isom_wave_t *src )
374 if( !dst )
375 return 0;
376 if( !src || !src->mp4a )
378 isom_remove_mp4a( dst->mp4a );
379 return 0;
381 if( !dst->mp4a && isom_add_mp4a( dst ) )
382 return -1;
383 isom_copy_fields( dst, src, mp4a );
384 return 0;
387 static int isom_copy_terminator( isom_wave_t *dst, isom_wave_t *src )
389 if( !dst )
390 return 0;
391 if( !src || !src->terminator )
393 isom_remove_terminator( dst->terminator );
394 return 0;
396 if( dst->terminator )
397 return 0;
398 return isom_add_terminator( dst );
401 static int isom_copy_wave( isom_audio_entry_t *dst, isom_audio_entry_t *src )
403 if( !dst )
404 return 0;
405 isom_remove_wave( dst->wave );
406 if( !src || !src->wave )
407 return 0;
408 if( isom_add_wave( dst ) )
409 return -1;
410 if( src->wave->exdata && src->wave->exdata_length )
412 dst->wave->exdata = lsmash_memdup( src->wave->exdata, src->wave->exdata_length );
413 if( !dst->wave->exdata )
414 return -1;
415 dst->wave->exdata_length = src->wave->exdata_length;
417 /* Copy children. */
418 dst->wave->esds = isom_duplicate_esds( (isom_box_t *)dst->wave, src->wave->esds );
419 if( (src->wave->esds && !dst->wave->esds) /* Check if copying failed. */
420 || isom_copy_frma( dst->wave, src->wave )
421 || isom_copy_enda( dst->wave, src->wave )
422 || isom_copy_mp4a( dst->wave, src->wave )
423 || isom_copy_terminator( dst->wave, src->wave ) )
424 return -1;
425 return 0;
428 static int isom_copy_chan( isom_audio_entry_t *dst, isom_audio_entry_t *src )
430 if( !dst )
431 return 0;
432 isom_remove_chan( dst->chan );
433 if( !src || !src->chan )
434 return 0;
435 if( isom_add_chan( dst ) )
436 return -1;
437 dst->chan->channelLayoutTag = src->chan->channelLayoutTag;
438 dst->chan->channelBitmap = src->chan->channelBitmap;
439 dst->chan->numberChannelDescriptions = src->chan->numberChannelDescriptions;
440 if( src->chan->numberChannelDescriptions && src->chan->channelDescriptions )
442 uint32_t numberChannelDescriptions = src->chan->numberChannelDescriptions;
443 dst->chan->channelDescriptions = malloc( numberChannelDescriptions * sizeof(isom_channel_description_t) );
444 if( !dst->chan->channelDescriptions )
445 return -1;
446 for( uint32_t i = 0; i < numberChannelDescriptions; i++ )
448 dst->chan->channelDescriptions[i].channelLabel = src->chan->channelDescriptions[i].channelLabel;
449 dst->chan->channelDescriptions[i].channelFlags = src->chan->channelDescriptions[i].channelFlags;
450 dst->chan->channelDescriptions[i].coordinates[0] = src->chan->channelDescriptions[i].coordinates[0];
451 dst->chan->channelDescriptions[i].coordinates[1] = src->chan->channelDescriptions[i].coordinates[1];
452 dst->chan->channelDescriptions[i].coordinates[2] = src->chan->channelDescriptions[i].coordinates[2];
454 dst->chan->numberChannelDescriptions = src->chan->numberChannelDescriptions;
456 else
458 if( dst->chan->channelDescriptions )
459 free( dst->chan->channelDescriptions );
460 dst->chan->channelDescriptions = NULL;
461 dst->chan->numberChannelDescriptions = 0;
463 return 0;
466 static isom_audio_entry_t *isom_duplicate_audio_description( isom_audio_entry_t *src )
468 isom_audio_entry_t *dst = lsmash_memdup( src, sizeof(isom_audio_entry_t) );
469 if( !dst )
470 return NULL;
471 dst->esds = NULL;
472 dst->wave = NULL;
473 dst->chan = NULL;
474 if( isom_is_lpcm_audio( src->type ) )
476 if( !src->version )
477 dst->constBytesPerAudioPacket = (src->samplesize * src->channelcount) / 8;
478 else if( src->version == 1 )
479 dst->constBytesPerAudioPacket = src->bytesPerFrame;
481 if( src->exdata && src->exdata_length )
483 dst->exdata = lsmash_memdup( src->exdata, src->exdata_length );
484 if( !dst->exdata )
486 isom_remove_sample_description( (isom_sample_entry_t *)dst );
487 return NULL;
489 dst->exdata_length = src->exdata_length;
491 /* Copy children. */
492 dst->esds = isom_duplicate_esds( (isom_box_t *)dst, src->esds );
493 if( (src->esds && !dst->esds) /* Check if copying failed. */
494 || isom_copy_wave( dst, src )
495 || isom_copy_chan( dst, src ) )
497 isom_remove_sample_description( (isom_sample_entry_t *)dst );
498 return NULL;
500 return dst;
503 static int isom_copy_ftab( isom_tx3g_entry_t *dst, isom_tx3g_entry_t *src )
505 if( !dst )
506 return 0;
507 isom_remove_ftab( dst->ftab );
508 if( !src || !src->ftab )
509 return 0;
510 if( isom_add_ftab( dst ) )
511 return -1;
512 if( src->ftab->list )
514 dst->ftab->list = lsmash_create_entry_list();
515 if( !dst->ftab->list )
516 return -1;
517 for( lsmash_entry_t *entry = src->ftab->list->head; entry; entry = entry->next )
519 isom_font_record_t *src_record = (isom_font_record_t *)entry->data;
520 if( !src_record )
521 return -1;
522 isom_font_record_t *dst_record = lsmash_memdup( src_record, sizeof(isom_font_record_t) );
523 dst_record->font_name = lsmash_memdup( src_record->font_name, src_record->font_name_length );
524 if( lsmash_add_entry( dst->ftab->list, dst_record ) )
526 free( dst_record->font_name );
527 free( dst_record );
528 return -1;
532 return 0;
535 static isom_tx3g_entry_t *isom_duplicate_tx3g_description( isom_tx3g_entry_t *src )
537 isom_tx3g_entry_t *dst = lsmash_memdup( src, sizeof(isom_tx3g_entry_t) );
538 if( !dst )
539 return NULL;
540 dst->ftab = NULL;
541 if( isom_copy_ftab( dst, src ) )
543 isom_remove_sample_description( (isom_sample_entry_t *)dst );
544 return NULL;
546 return dst;
549 static isom_text_entry_t *isom_duplicate_text_description( isom_text_entry_t *src )
551 isom_text_entry_t *dst = lsmash_memdup( src, sizeof(isom_text_entry_t) );
552 if( !dst )
553 return NULL;
554 dst->font_name = NULL;
555 dst->font_name_length = 0;
556 if( src->font_name && src->font_name_length )
558 dst->font_name = lsmash_memdup( src->font_name, src->font_name_length );
559 if( !dst->font_name )
561 isom_remove_sample_description( (isom_sample_entry_t *)dst );
562 return NULL;
564 dst->font_name_length = src->font_name_length;
566 return dst;
569 static isom_sample_entry_t *isom_duplicate_description( isom_sample_entry_t *entry, isom_stsd_t *dst_parent )
571 if( !entry )
572 return NULL;
573 void *description = NULL;
574 switch( entry->type )
576 case ISOM_CODEC_TYPE_AVC1_VIDEO :
577 case ISOM_CODEC_TYPE_MP4V_VIDEO :
578 description = isom_duplicate_visual_description( (isom_visual_entry_t *)entry );
579 break;
580 case ISOM_CODEC_TYPE_MP4A_AUDIO :
581 case ISOM_CODEC_TYPE_AC_3_AUDIO :
582 case ISOM_CODEC_TYPE_ALAC_AUDIO :
583 case ISOM_CODEC_TYPE_EC_3_AUDIO :
584 case ISOM_CODEC_TYPE_SAMR_AUDIO :
585 case ISOM_CODEC_TYPE_SAWB_AUDIO :
586 case QT_CODEC_TYPE_23NI_AUDIO :
587 case QT_CODEC_TYPE_NONE_AUDIO :
588 case QT_CODEC_TYPE_LPCM_AUDIO :
589 case QT_CODEC_TYPE_RAW_AUDIO :
590 case QT_CODEC_TYPE_SOWT_AUDIO :
591 case QT_CODEC_TYPE_TWOS_AUDIO :
592 case QT_CODEC_TYPE_FL32_AUDIO :
593 case QT_CODEC_TYPE_FL64_AUDIO :
594 case QT_CODEC_TYPE_IN24_AUDIO :
595 case QT_CODEC_TYPE_IN32_AUDIO :
596 case QT_CODEC_TYPE_NOT_SPECIFIED :
597 description = isom_duplicate_audio_description( (isom_audio_entry_t *)entry );
598 break;
599 case ISOM_CODEC_TYPE_TX3G_TEXT :
600 description = isom_duplicate_tx3g_description( (isom_tx3g_entry_t *)entry );
601 break;
602 case QT_CODEC_TYPE_TEXT_TEXT :
603 description = isom_duplicate_text_description( (isom_text_entry_t *)entry );
604 break;
605 default :
606 return NULL;
608 if( description )
609 ((isom_sample_entry_t *)description)->parent = (isom_box_t *)dst_parent;
610 return (isom_sample_entry_t *)description;
613 #define INCREMENT_SAMPLE_NUMBER_IN_ENTRY( sample_number_in_entry, entry, entry_data ) \
614 if( sample_number_in_entry == entry_data->sample_count ) \
616 sample_number_in_entry = 1; \
617 entry = entry->next; \
619 else \
620 ++sample_number_in_entry
622 int lsmash_construct_timeline( lsmash_root_t *root, uint32_t track_ID )
624 if( !root || !root->moov || !root->moov->mvhd || !root->moov->mvhd->timescale )
625 return -1;
626 /* Get track by track_ID. */
627 isom_trak_entry_t *trak = isom_get_trak( root, track_ID );
628 if( !trak || !trak->mdia || !trak->mdia->mdhd || !trak->mdia->mdhd->timescale || !trak->mdia->minf || !trak->mdia->minf->stbl )
629 return -1;
630 /* Create a timeline list if it doesn't exist. */
631 if( !root->timeline )
633 root->timeline = lsmash_create_entry_list();
634 if( !root->timeline )
635 return -1;
637 /* Create a timeline. */
638 isom_timeline_t *timeline = isom_create_timeline();
639 if( !timeline )
640 return -1;
641 timeline->track_ID = track_ID;
642 timeline->movie_timescale = root->moov->mvhd->timescale;
643 timeline->media_timescale = trak->mdia->mdhd->timescale;
644 /* Preparation for construction. */
645 isom_elst_t *elst = trak->edts ? trak->edts->elst : NULL;
646 isom_stbl_t *stbl = trak->mdia->minf->stbl;
647 isom_stsd_t *stsd = stbl->stsd;
648 isom_stts_t *stts = stbl->stts;
649 isom_ctts_t *ctts = stbl->ctts;
650 isom_stss_t *stss = stbl->stss;
651 isom_stps_t *stps = stbl->stps;
652 isom_sdtp_t *sdtp = stbl->sdtp;
653 isom_stsc_t *stsc = stbl->stsc;
654 isom_stsz_t *stsz = stbl->stsz;
655 isom_stco_t *stco = stbl->stco;
656 isom_sgpd_entry_t *sgpd_roll = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_ROLL );
657 isom_sgpd_entry_t *sgpd_rap = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
658 isom_sbgp_entry_t *sbgp_roll = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_ROLL );
659 isom_sbgp_entry_t *sbgp_rap = isom_get_sample_to_group( stbl, ISOM_GROUP_TYPE_RAP );
660 lsmash_entry_t *elst_entry = elst && elst->list ? elst->list->head : NULL;
661 lsmash_entry_t *stsd_entry = stsd && stsd->list ? stsd->list->head : NULL;
662 lsmash_entry_t *stts_entry = stts && stts->list ? stts->list->head : NULL;
663 lsmash_entry_t *ctts_entry = ctts && ctts->list ? ctts->list->head : NULL;
664 lsmash_entry_t *stss_entry = stss && stss->list ? stss->list->head : NULL;
665 lsmash_entry_t *stps_entry = stps && stps->list ? stps->list->head : NULL;
666 lsmash_entry_t *sdtp_entry = sdtp && sdtp->list ? sdtp->list->head : NULL;
667 lsmash_entry_t *stsz_entry = stsz && stsz->list ? stsz->list->head : NULL;
668 lsmash_entry_t *stsc_entry = stsc && stsc->list ? stsc->list->head : NULL;
669 lsmash_entry_t *stco_entry = stco && stco->list ? stco->list->head : NULL;
670 lsmash_entry_t *sbgp_roll_entry = sbgp_roll && sbgp_roll->list ? sbgp_roll->list->head : NULL;
671 lsmash_entry_t *sbgp_rap_entry = sbgp_rap && sbgp_rap->list ? sbgp_rap->list->head : NULL;
672 lsmash_entry_t *next_stsc_entry = stsc_entry ? stsc_entry->next : NULL;
673 isom_stsc_entry_t *stsc_data = stsc_entry ? (isom_stsc_entry_t *)stsc_entry->data : NULL;
674 isom_sample_entry_t *description = stsd_entry ? (isom_sample_entry_t *)stsd_entry->data : NULL;
675 isom_sample_info_t *info = NULL; /* shut up 'uninitialized' warning */
676 isom_portable_chunk_t *chunk = NULL; /* shut up 'uninitialized' warning */
677 if( !description || !stts_entry || !stsc_entry || !stco_entry || !stco_entry->data )
678 goto fail;
679 chunk = malloc( sizeof(isom_portable_chunk_t) );
680 if( !chunk || lsmash_add_entry( timeline->chunk_list, chunk ) )
681 goto fail;
682 chunk->number = 1;
683 chunk->data = NULL;
684 int all_sync = !stss;
685 int large_presentation = stco->large_presentation;
686 int is_lpcm_audio = isom_is_lpcm_audio( description->type );
687 int iso_sdtp = root->max_isom_version >= 4;
688 int allow_negative_sample_offset = ctts && ((root->max_isom_version >= 4 && ctts->version == 1) || root->qt_compatible);
689 uint32_t sample_number = 1;
690 uint32_t sample_number_in_stts_entry = 1;
691 uint32_t sample_number_in_ctts_entry = 1;
692 uint32_t sample_number_in_sbgp_roll_entry = 1;
693 uint32_t sample_number_in_sbgp_rap_entry = 1;
694 uint32_t sample_number_in_chunk = 1;
695 uint64_t dts = 0;
696 uint64_t cts = 0;
697 uint32_t chunk_number = 1;
698 uint64_t offset_from_chunk = 0;
699 uint64_t data_offset = chunk->data_offset
700 = large_presentation
701 ? ((isom_co64_entry_t *)stco_entry->data)->chunk_offset
702 : ((isom_stco_entry_t *)stco_entry->data)->chunk_offset;
703 uint32_t constant_sample_size = is_lpcm_audio
704 ? ((isom_audio_entry_t *)description)->constBytesPerAudioPacket
705 : stsz->sample_size;
706 /* Copy edits. */
707 while( elst_entry )
709 isom_elst_entry_t *edit = (isom_elst_entry_t *)lsmash_memdup( elst_entry->data, sizeof(isom_elst_entry_t) );
710 if( !edit
711 || lsmash_add_entry( timeline->edit_list, edit ) )
712 goto fail;
713 elst_entry = elst_entry->next;
715 /* Copy sample descriptions. */
716 while( stsd_entry )
718 description = isom_duplicate_description( (isom_sample_entry_t *)stsd_entry->data, NULL );
719 if( !description
720 || lsmash_add_entry( timeline->description_list, description ) )
721 goto fail;
722 stsd_entry = stsd_entry->next;
724 stsd_entry = stsd->list->head;
725 description = (isom_sample_entry_t *)stsd_entry->data;
726 /* Check what the first 2-bits of sample dependency means.
727 * This check is for chimera of ISO Base Media and QTFF. */
728 if( iso_sdtp && sdtp_entry )
730 while( sdtp_entry )
732 isom_sdtp_entry_t *sdtp_data = (isom_sdtp_entry_t *)sdtp_entry->data;
733 if( !sdtp_data )
734 goto fail;
735 if( sdtp_data->is_leading > 1 )
736 break; /* Apparently, it's defined under ISO Base Media. */
737 if( (sdtp_data->is_leading == 1) && (sdtp_data->sample_depends_on == ISOM_SAMPLE_IS_INDEPENDENT) )
739 /* Obviously, it's not defined under ISO Base Media. */
740 iso_sdtp = 0;
741 break;
743 sdtp_entry = sdtp_entry->next;
745 sdtp_entry = sdtp->list->head;
747 /* Construct media timeline. */
748 while( sample_number <= stsz->sample_count )
750 info = malloc( sizeof(isom_sample_info_t) );
751 if( !info )
752 goto fail;
753 memset( info, 0, sizeof(isom_sample_info_t) );
754 /* Get sample duration and sample offset. */
755 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
756 if( !stts_data )
757 goto fail;
758 INCREMENT_SAMPLE_NUMBER_IN_ENTRY( sample_number_in_stts_entry, stts_entry, stts_data );
759 info->duration = stts_data->sample_delta;
760 if( ctts_entry )
762 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
763 if( !ctts_data )
764 goto fail;
765 INCREMENT_SAMPLE_NUMBER_IN_ENTRY( sample_number_in_ctts_entry, ctts_entry, ctts_data );
766 info->offset = ctts_data->sample_offset;
768 else
769 info->offset = 0;
770 if( allow_negative_sample_offset )
772 cts = dts + (int32_t)info->offset;
773 if( (cts + timeline->ctd_shift) < dts )
774 timeline->ctd_shift = dts - cts;
776 else
777 cts = dts + info->offset;
778 dts += info->duration;
779 if( !is_lpcm_audio )
781 /* Check whether sync sample or not. */
782 if( stss_entry )
784 isom_stss_entry_t *stss_data = (isom_stss_entry_t *)stss_entry->data;
785 if( !stss_data )
786 goto fail;
787 if( sample_number == stss_data->sample_number )
789 info->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
790 stss_entry = stss_entry->next;
793 else if( all_sync )
794 info->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
795 /* Check whether partial sync sample or not. */
796 if( stps_entry )
798 isom_stps_entry_t *stps_data = (isom_stps_entry_t *)stps_entry->data;
799 if( !stps_data )
800 goto fail;
801 if( sample_number == stps_data->sample_number )
803 info->prop.random_access_type = QT_SAMPLE_RANDOM_ACCESS_TYPE_PARTIAL_SYNC;
804 stps_entry = stps_entry->next;
807 /* Get sample dependency info. */
808 if( sdtp_entry )
810 isom_sdtp_entry_t *sdtp_data = (isom_sdtp_entry_t *)sdtp_entry->data;
811 if( !sdtp_data )
812 goto fail;
813 if( iso_sdtp )
814 info->prop.leading = sdtp_data->is_leading;
815 else
816 info->prop.allow_earlier = sdtp_data->is_leading;
817 info->prop.independent = sdtp_data->sample_depends_on;
818 info->prop.disposable = sdtp_data->sample_is_depended_on;
819 info->prop.redundant = sdtp_data->sample_has_redundancy;
820 sdtp_entry = sdtp_entry->next;
822 /* Get roll recovery grouping info. */
823 if( sbgp_roll_entry )
825 isom_group_assignment_entry_t *assignment = (isom_group_assignment_entry_t *)sbgp_roll_entry->data;
826 if( !assignment )
827 goto fail;
828 if( sample_number_in_sbgp_roll_entry == 1 && assignment->group_description_index )
830 isom_roll_entry_t *roll_data = (isom_roll_entry_t *)lsmash_get_entry_data( sgpd_roll->list, assignment->group_description_index );
831 if( !roll_data )
832 goto fail;
833 info->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_RECOVERY;
834 info->prop.recovery.complete = sample_number + roll_data->roll_distance;
836 INCREMENT_SAMPLE_NUMBER_IN_ENTRY( sample_number_in_sbgp_roll_entry, sbgp_roll_entry, assignment );
838 info->prop.recovery.identifier = sample_number;
839 /* Get random access point grouping info. */
840 if( sbgp_rap_entry )
842 isom_group_assignment_entry_t *assignment = (isom_group_assignment_entry_t *)sbgp_rap_entry->data;
843 if( !assignment )
844 goto fail;
845 if( assignment->group_description_index && (info->prop.random_access_type == ISOM_SAMPLE_RANDOM_ACCESS_TYPE_NONE) )
847 isom_rap_entry_t *rap_data = (isom_rap_entry_t *)lsmash_get_entry_data( sgpd_rap->list, assignment->group_description_index );
848 if( !rap_data )
849 goto fail;
850 /* If this is not an open RAP, we treat it as an unknown RAP since non-IDR sample could make a closed GOP. */
851 info->prop.random_access_type = (rap_data->num_leading_samples_known && !!rap_data->num_leading_samples)
852 ? ISOM_SAMPLE_RANDOM_ACCESS_TYPE_OPEN_RAP
853 : ISOM_SAMPLE_RANDOM_ACCESS_TYPE_UNKNOWN_RAP;
855 INCREMENT_SAMPLE_NUMBER_IN_ENTRY( sample_number_in_sbgp_rap_entry, sbgp_rap_entry, assignment );
858 else /* All LPCMFrame is sync sample. */
859 info->prop.random_access_type = ISOM_SAMPLE_RANDOM_ACCESS_TYPE_SYNC;
860 /* Get size of sample in the stream. */
861 if( is_lpcm_audio || !stsz_entry )
862 info->length = constant_sample_size;
863 else
865 if( !stsz_entry->data )
866 goto fail;
867 info->length = ((isom_stsz_entry_t *)stsz_entry->data)->entry_size;
868 stsz_entry = stsz_entry->next;
870 /* Get chunk info. */
871 info->pos = data_offset;
872 info->index = stsc_data->sample_description_index;
873 info->chunk = chunk;
874 offset_from_chunk += info->length;
875 if( sample_number_in_chunk == stsc_data->samples_per_chunk )
877 /* Move the next chunk. */
878 sample_number_in_chunk = 1;
879 stco_entry = stco_entry->next;
880 if( stco_entry && stco_entry->data )
881 data_offset = large_presentation
882 ? ((isom_co64_entry_t *)stco_entry->data)->chunk_offset
883 : ((isom_stco_entry_t *)stco_entry->data)->chunk_offset;
884 chunk->length = offset_from_chunk; /* the length of the previous chunk */
885 chunk = malloc( sizeof(isom_portable_chunk_t) );
886 if( !chunk )
887 goto fail;
888 chunk->number = ++chunk_number;
889 chunk->data_offset = data_offset;
890 chunk->data = NULL;
891 offset_from_chunk = 0;
892 if( lsmash_add_entry( timeline->chunk_list, chunk ) )
893 goto fail;
894 if( next_stsc_entry
895 && chunk_number == ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk )
897 stsc_entry = next_stsc_entry;
898 next_stsc_entry = stsc_entry->next;
899 if( !stsc_entry->data )
900 goto fail;
901 stsc_data = (isom_stsc_entry_t *)stsc_entry->data;
902 /* Update sample description. */
903 description = (isom_sample_entry_t *)lsmash_get_entry_data( stsd->list, stsc_data->sample_description_index );
904 is_lpcm_audio = isom_is_lpcm_audio( description->type );
905 if( is_lpcm_audio )
906 constant_sample_size = ((isom_audio_entry_t *)description)->constBytesPerAudioPacket;
909 else
911 ++sample_number_in_chunk;
912 data_offset += info->length;
914 /* OK. Let's add its info. */
915 if( lsmash_add_entry( timeline->info_list, info ) )
916 goto fail;
917 ++sample_number;
919 chunk->length = offset_from_chunk;
920 if( lsmash_add_entry( root->timeline, timeline ) )
922 isom_destruct_timeline_direct( timeline );
923 return -1;
925 return 0;
926 fail:
927 isom_destruct_timeline_direct( timeline );
928 if( info )
929 free( info );
930 if( chunk )
931 free( chunk );
932 return -1;
935 static int isom_get_dts_from_media_timeline_internal( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts )
937 if( !timeline || !sample_number || !dts )
938 return -1;
939 if( sample_number > timeline->info_list->entry_count )
940 return -1;
941 if( sample_number == timeline->last_accessed_sample_number )
942 *dts = timeline->last_accessed_sample_dts;
943 else if( sample_number == 1 )
944 *dts = 0;
945 else if( sample_number == timeline->last_accessed_sample_number + 1 )
947 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, timeline->last_accessed_sample_number );
948 if( !info )
949 return -1;
950 *dts = timeline->last_accessed_sample_dts + info->duration;
952 else if( sample_number == timeline->last_accessed_sample_number - 1 )
954 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, timeline->last_accessed_sample_number - 1 );
955 if( !info )
956 return -1;
957 *dts = timeline->last_accessed_sample_dts - info->duration;
959 else
961 *dts = 0;
962 uint32_t distance = sample_number - 1;
963 lsmash_entry_t *entry;
964 for( entry = timeline->info_list->head; entry; entry = entry->next )
966 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
967 if( !info )
968 return -1;
969 if( distance-- )
970 break;
971 *dts += info->duration;
973 if( !entry )
974 return -1;
976 timeline->last_accessed_sample_dts = *dts;
977 timeline->last_accessed_sample_number = sample_number;
978 return 0;
981 int lsmash_get_dts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *dts )
983 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
984 return isom_get_dts_from_media_timeline_internal( timeline, sample_number, dts );
987 lsmash_sample_t *lsmash_get_sample_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number )
989 uint64_t dts;
990 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
991 if( isom_get_dts_from_media_timeline_internal( timeline, sample_number, &dts ) )
992 return NULL;
993 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
994 if( !info )
995 return NULL;
996 /* Get data of a sample from a chunk. */
997 isom_portable_chunk_t *chunk = info->chunk;
998 lsmash_bs_t *bs = root->bs;
999 if( (timeline->last_accessed_chunk_number != chunk->number)
1000 || (timeline->last_accessed_offset > info->pos)
1001 || (timeline->last_read_size < (info->pos + info->length - timeline->last_accessed_offset)) )
1003 /* Read data of a chunk in the stream. */
1004 uint64_t read_size;
1005 uint64_t seek_pos;
1006 if( root->max_read_size >= chunk->length )
1008 read_size = chunk->length;
1009 seek_pos = chunk->data_offset;
1011 else
1013 read_size = LSMASH_MAX( root->max_read_size, info->length );
1014 seek_pos = info->pos;
1016 lsmash_fseek( bs->stream, seek_pos, SEEK_SET );
1017 lsmash_bs_empty( bs );
1018 if( lsmash_bs_read_data( bs, read_size ) )
1019 return NULL;
1020 chunk->data = lsmash_bs_export_data( bs, NULL );
1021 if( !chunk->data )
1022 return NULL;
1023 lsmash_bs_empty( bs );
1024 if( timeline->last_accessed_chunk_data )
1026 free( timeline->last_accessed_chunk_data );
1027 timeline->last_accessed_chunk_data = NULL;
1029 timeline->last_accessed_chunk_number = chunk->number;
1030 timeline->last_accessed_chunk_data = chunk->data;
1031 timeline->last_accessed_offset = seek_pos;
1032 timeline->last_read_size = read_size;
1034 lsmash_sample_t *sample = lsmash_create_sample( 0 );
1035 if( !sample )
1036 return NULL;
1037 uint64_t offset_from_seek = info->pos - timeline->last_accessed_offset;
1038 sample->data = lsmash_memdup( chunk->data + offset_from_seek, info->length );
1039 if( !sample->data )
1041 lsmash_delete_sample( sample );
1042 return NULL;
1044 /* Get sample info. */
1045 sample->dts = dts;
1046 sample->cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset);
1047 sample->length = info->length;
1048 sample->index = info->index;
1049 sample->prop = info->prop;
1050 return sample;
1053 int lsmash_check_sample_existence_in_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number )
1055 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1056 if( !timeline )
1057 return 0;
1058 return !!lsmash_get_entry_data( timeline->info_list, sample_number );
1061 int lsmash_get_last_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *last_sample_delta )
1063 if( !last_sample_delta )
1064 return -1;
1065 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1066 if( !timeline || !timeline->info_list
1067 || !timeline->info_list->tail || !timeline->info_list->tail->data )
1068 return -1;
1069 *last_sample_delta = ((isom_sample_info_t *)timeline->info_list->tail->data)->duration;
1070 return 0;
1073 int lsmash_copy_timeline_map( lsmash_root_t *dst, uint32_t dst_track_ID, lsmash_root_t *src, uint32_t src_track_ID )
1075 isom_trak_entry_t *dst_trak = isom_get_trak( dst, dst_track_ID );
1076 if( !dst->moov || !dst->moov->mvhd || !dst->moov->mvhd->timescale
1077 || !dst_trak || !dst_trak->mdia || !dst_trak->mdia->mdhd || !dst_trak->mdia->mdhd->timescale
1078 || !dst_trak->mdia->minf || !dst_trak->mdia->minf->stbl )
1079 return -1;
1080 if( dst_trak->edts && dst_trak->edts->elst )
1081 lsmash_remove_entries( dst_trak->edts->elst->list, NULL );
1082 uint32_t src_movie_timescale;
1083 uint32_t src_media_timescale;
1084 int32_t src_ctd_shift; /* Add timeline shift difference between src and dst to each media_time.
1085 * Therefore, call this function as later as possible. */
1086 lsmash_entry_t *src_entry;
1087 isom_trak_entry_t *src_trak = isom_get_trak( src, src_track_ID );
1088 if( !src_trak || !src_trak->edts || !src_trak->edts->elst || !src_trak->edts->elst->list )
1090 /* Get from timeline instead of boxes. */
1091 isom_timeline_t *src_timeline = isom_get_timeline( src, src_track_ID );
1092 if( !src_timeline ||!src_timeline->movie_timescale || !src_timeline->media_timescale || !src_timeline->edit_list )
1093 return -1;
1094 src_movie_timescale = src_timeline->movie_timescale;
1095 src_media_timescale = src_timeline->media_timescale;
1096 src_ctd_shift = src_timeline->ctd_shift;
1097 src_entry = src_timeline->edit_list->head;
1099 else
1101 if( !src->moov || !src->moov->mvhd || !src->moov->mvhd->timescale
1102 || !src_trak->mdia || !src_trak->mdia->mdhd || !src_trak->mdia->mdhd->timescale
1103 || !src_trak->mdia->minf || !src_trak->mdia->minf->stbl )
1104 return -1;
1105 src_movie_timescale = src->moov->mvhd->timescale;
1106 src_media_timescale = src_trak->mdia->mdhd->timescale;
1107 src_ctd_shift = src_trak->mdia->minf->stbl->cslg ? src_trak->mdia->minf->stbl->cslg->compositionToDTSShift : 0;
1108 src_entry = src_trak->edts->elst->list->head;
1110 if( !src_entry )
1111 return 0;
1112 /* Generate edit list if absent in destination. */
1113 if( (!dst_trak->edts && isom_add_edts( dst_trak ))
1114 || (!dst_trak->edts->elst && isom_add_elst( dst_trak->edts )) )
1115 return -1;
1116 uint32_t dst_movie_timescale = dst->moov->mvhd->timescale;
1117 uint32_t dst_media_timescale = dst_trak->mdia->mdhd->timescale;
1118 int32_t dst_ctd_shift = dst_trak->mdia->minf->stbl->cslg ? dst_trak->mdia->minf->stbl->cslg->compositionToDTSShift : 0;
1119 int32_t media_time_shift = src_ctd_shift - dst_ctd_shift;
1120 lsmash_entry_list_t *dst_list = dst_trak->edts->elst->list;
1121 while( src_entry )
1123 isom_elst_entry_t *src_data = (isom_elst_entry_t *)src_entry->data;
1124 if( !src_data )
1125 return -1;
1126 isom_elst_entry_t *dst_data = (isom_elst_entry_t *)malloc( sizeof(isom_elst_entry_t) );
1127 if( !dst_data )
1128 return -1;
1129 dst_data->segment_duration = src_data->segment_duration * ((double)dst_movie_timescale / src_movie_timescale) + 0.5;
1130 dst_data->media_time = (src_data->media_time + media_time_shift) * ((double)dst_media_timescale / src_media_timescale) + 0.5;
1131 dst_data->media_rate = src_data->media_rate;
1132 if( lsmash_add_entry( dst_list, dst_data ) )
1134 free( dst_data );
1135 return -1;
1137 src_entry = src_entry->next;
1139 return 0;
1142 int lsmash_copy_decoder_specific_info( lsmash_root_t *dst, uint32_t dst_track_ID, lsmash_root_t *src, uint32_t src_track_ID )
1144 isom_trak_entry_t *dst_trak = isom_get_trak( dst, dst_track_ID );
1145 if( !dst_trak || !dst_trak->mdia || !dst_trak->mdia->minf || !dst_trak->mdia->minf->stbl
1146 || !dst_trak->mdia->minf->stbl->stsd || !dst_trak->mdia->minf->stbl->stsd->list )
1147 return -1;
1148 isom_stsd_t *dst_stsd = dst_trak->mdia->minf->stbl->stsd;
1149 lsmash_remove_entries( dst_stsd->list, isom_remove_sample_description );
1150 lsmash_entry_t *src_entry = NULL;
1151 isom_trak_entry_t *src_trak = isom_get_trak( src, src_track_ID );
1152 if( !src_trak || !src_trak->mdia || !src_trak->mdia->minf || !src_trak->mdia->minf->stbl
1153 || !src_trak->mdia->minf->stbl->stsd || !src_trak->mdia->minf->stbl->stsd->list )
1155 /* Get source entry from media timeline instead of Sample Description Box. */
1156 isom_timeline_t *src_timeline = isom_get_timeline( src, src_track_ID );
1157 if( !src_timeline || !src_timeline->description_list )
1158 return -1;
1159 src_entry = src_timeline->description_list->head;
1161 else
1162 src_entry = src_trak->mdia->minf->stbl->stsd->list->head;
1163 if( !src_entry )
1164 return -1; /* Required at least one entry. */
1165 while( src_entry )
1167 isom_sample_entry_t *src_data = (isom_sample_entry_t *)src_entry->data;
1168 if( !src_data )
1169 return -1;
1170 isom_sample_entry_t *dst_data = isom_duplicate_description( src_data, dst_stsd );
1171 if( !dst_data )
1172 return -1;
1173 if( lsmash_add_entry( dst_stsd->list, dst_data ) )
1175 isom_remove_sample_description( dst_data );
1176 return -1;
1178 src_entry = src_entry->next;
1180 /* Check if needed Track Aperture Modes. */
1181 if( dst_trak->mdia->minf->vmhd )
1183 isom_tapt_t *tapt = dst_trak->tapt;
1184 isom_visual_entry_t *visual = (isom_visual_entry_t *)dst_stsd->list->head->data;
1185 if( dst_trak->root->qt_compatible /* Track Aperture Modes is only available under QuickTime file format. */
1186 && !visual->stsl /* Sample scaling method might conflict with this feature. */
1187 && visual->clap && visual->pasp /* Check if required boxes exist. */
1188 && tapt && tapt->clef && tapt->prof && tapt->enof /* */
1189 && dst_stsd->list->entry_count == 1 ) /* Multiple sample description might conflict with this. */
1191 uint32_t width = visual->width << 16;
1192 uint32_t height = visual->height << 16;
1193 double clap_width = ((double)visual->clap->cleanApertureWidthN / visual->clap->cleanApertureWidthD) * (1<<16);
1194 double clap_height = ((double)visual->clap->cleanApertureHeightN / visual->clap->cleanApertureHeightD) * (1<<16);
1195 double par = (double)visual->pasp->hSpacing / visual->pasp->vSpacing;
1196 if( par >= 1.0 )
1198 tapt->clef->width = clap_width * par;
1199 tapt->clef->height = clap_height;
1200 tapt->prof->width = width * par;
1201 tapt->prof->height = height;
1203 else
1205 tapt->clef->width = clap_width;
1206 tapt->clef->height = clap_height / par;
1207 tapt->prof->width = width;
1208 tapt->prof->height = height / par;
1210 tapt->enof->width = width;
1211 tapt->enof->height = height;
1213 else
1214 isom_remove_tapt( dst_trak->tapt );
1216 return 0;
1219 int lsmash_set_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list )
1221 if( !ts_list )
1222 return -1;
1223 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1224 if( !timeline )
1225 return -1;
1226 if( ts_list->sample_count != timeline->info_list->entry_count )
1227 return -1; /* Number of samples must be same. */
1228 lsmash_media_ts_t *ts = ts_list->timestamp;
1229 if( ts[0].dts )
1230 return -1; /* DTS must start from value zero. */
1231 /* Update DTSs. */
1232 uint32_t sample_count = ts_list->sample_count;
1233 uint32_t i;
1234 if( timeline->info_list->entry_count > 1 )
1236 i = 1;
1237 lsmash_entry_t *entry = timeline->info_list->head;
1238 isom_sample_info_t *info;
1239 while( i < sample_count )
1241 info = (isom_sample_info_t *)entry->data;
1242 if( !info || (ts[i].dts < ts[i - 1].dts) )
1243 return -1;
1244 info->duration = ts[i].dts - ts[i - 1].dts;
1245 entry = entry->next;
1246 ++i;
1248 if( i > 1 )
1250 if( !entry || !entry->data )
1251 return -1;
1252 /* Copy the previous duration. */
1253 ((isom_sample_info_t *)entry->data)->duration = info->duration;
1255 else
1256 return -1; /* Irregular case: sample_count this timeline has is incorrect. */
1258 else /* still image */
1259 ((isom_sample_info_t *)timeline->info_list->head->data)->duration = UINT32_MAX;
1260 /* Update CTSs.
1261 * ToDo: hint track must not have any sample_offset. */
1262 i = 0;
1263 timeline->ctd_shift = 0;
1264 for( lsmash_entry_t *entry = timeline->info_list->head; entry; entry = entry->next )
1266 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1267 if( (ts[i].cts + timeline->ctd_shift) < ts[i].dts )
1268 timeline->ctd_shift = ts[i].dts - ts[i].cts;
1269 info->offset = ts[i].cts - ts[i].dts;
1270 ++i;
1272 if( timeline->ctd_shift && (!root->qt_compatible || root->max_isom_version < 4) )
1273 return -1; /* Don't allow composition to decode timeline shift. */
1274 return 0;
1277 int lsmash_get_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list )
1279 if( !ts_list )
1280 return -1;
1281 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1282 if( !timeline )
1283 return -1;
1284 uint32_t sample_count = timeline->info_list->entry_count;
1285 if( !sample_count )
1287 ts_list->sample_count = 0;
1288 ts_list->timestamp = NULL;
1289 return 0;
1291 lsmash_media_ts_t *ts = malloc( sample_count * sizeof(lsmash_media_ts_t) );
1292 if( !ts )
1293 return -1;
1294 uint64_t dts = 0;
1295 uint32_t i = 0;
1296 for( lsmash_entry_t *entry = timeline->info_list->head; entry; entry = entry->next )
1298 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1299 if( !info )
1301 free( ts );
1302 return -1;
1304 ts[i].dts = dts;
1305 ts[i].cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset);
1306 dts += info->duration;
1307 ++i;
1309 ts_list->sample_count = sample_count;
1310 ts_list->timestamp = ts;
1311 return 0;
1314 int lsmash_get_media_timeline_shift( lsmash_root_t *root, uint32_t track_ID, int32_t *timeline_shift )
1316 if( !timeline_shift )
1317 return -1;
1318 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1319 if( !timeline )
1320 return -1;
1321 *timeline_shift = timeline->ctd_shift;
1322 return 0;
1325 #endif /* LSMASH_DEMUXER_ENABLED */