fragment: Get 'tfra' box only when 'mfra' box is present.
[L-SMASH.git] / core / timeline.c
blob1e8de446a0eb96ea08605807a6d49389d57cf489
1 /*****************************************************************************
2 * timeline.c:
3 *****************************************************************************
4 * Copyright (C) 2011-2014 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 "common/internal.h" /* must be placed first */
27 #include <stdlib.h>
28 #include <string.h>
29 #include <inttypes.h>
31 #include "box.h"
33 #include "codecs/mp4a.h"
34 #include "codecs/mp4sys.h"
35 #include "codecs/description.h"
37 #define NO_RANDOM_ACCESS_POINT 0xffffffff
39 typedef struct
41 uint64_t data_offset;
42 uint64_t length;
43 uint32_t number; /* useless at present */
44 lsmash_file_t *file;
45 } isom_portable_chunk_t;
47 typedef struct
49 uint64_t pos;
50 uint32_t duration;
51 uint32_t offset;
52 uint32_t length;
53 uint32_t index;
54 isom_portable_chunk_t *chunk;
55 lsmash_sample_property_t prop;
56 } isom_sample_info_t;
58 typedef struct
60 uint64_t pos; /* position of the first sample in this bunch */
61 uint32_t duration; /* duration in media timescale each sample has */
62 uint32_t offset; /* offset between composition time and decoding time each sample has */
63 uint32_t length; /* data size each sample has */
64 uint32_t index; /* sample_description_index applied to each sample */
65 isom_portable_chunk_t *chunk; /* chunk samples belong to */
66 lsmash_sample_property_t prop; /* property applied to each sample */
67 uint32_t sample_count; /* number of samples in this bunch */
68 } isom_lpcm_bunch_t;
70 static const lsmash_class_t lsmash_timeline_class =
72 "timeline"
75 typedef struct isom_timeline_tag isom_timeline_t;
77 struct isom_timeline_tag
79 const lsmash_class_t *class;
80 uint32_t track_ID;
81 uint32_t movie_timescale;
82 uint32_t media_timescale;
83 uint32_t sample_count;
84 uint32_t max_sample_size;
85 uint32_t ctd_shift; /* shift from composition to decode timeline */
86 uint64_t media_duration;
87 uint64_t track_duration;
88 uint32_t last_accessed_sample_number;
89 uint64_t last_accessed_sample_dts;
90 uint32_t last_accessed_lpcm_bunch_number;
91 uint32_t last_accessed_lpcm_bunch_duration;
92 uint32_t last_accessed_lpcm_bunch_sample_count;
93 uint32_t last_accessed_lpcm_bunch_first_sample_number;
94 uint64_t last_accessed_lpcm_bunch_dts;
95 lsmash_entry_list_t edit_list [1]; /* list of edits */
96 lsmash_entry_list_t chunk_list[1]; /* list of chunks */
97 lsmash_entry_list_t info_list [1]; /* list of sample info */
98 lsmash_entry_list_t bunch_list[1]; /* list of LPCM bunch */
99 int (*get_dts)( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts );
100 int (*get_cts)( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts );
101 int (*get_sample_duration)( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration );
102 lsmash_sample_t *(*get_sample)( isom_timeline_t *timeline, uint32_t sample_number );
103 int (*get_sample_info)( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample );
104 int (*get_sample_property)( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop );
105 int (*check_sample_existence)( isom_timeline_t *timeline, uint32_t sample_number );
108 static isom_timeline_t *isom_get_timeline( lsmash_root_t *root, uint32_t track_ID )
110 if( !track_ID || !root || !root->file || !root->file->timeline )
111 return NULL;
112 for( lsmash_entry_t *entry = root->file->timeline->head; entry; entry = entry->next )
114 isom_timeline_t *timeline = (isom_timeline_t *)entry->data;
115 if( !timeline )
116 return NULL;
117 if( timeline->track_ID == track_ID )
118 return timeline;
120 return NULL;
123 static isom_timeline_t *isom_create_timeline( void )
125 isom_timeline_t *timeline = lsmash_malloc_zero( sizeof(isom_timeline_t) );
126 if( !timeline )
127 return NULL;
128 timeline->class = &lsmash_timeline_class;
129 lsmash_init_entry_list( timeline->edit_list );
130 lsmash_init_entry_list( timeline->chunk_list );
131 lsmash_init_entry_list( timeline->info_list );
132 lsmash_init_entry_list( timeline->bunch_list );
133 return timeline;
136 static void isom_destruct_timeline_direct( isom_timeline_t *timeline )
138 if( !timeline )
139 return;
140 lsmash_remove_entries( timeline->edit_list, NULL );
141 lsmash_remove_entries( timeline->chunk_list, NULL ); /* chunk data must be already freed. */
142 lsmash_remove_entries( timeline->info_list, NULL );
143 lsmash_remove_entries( timeline->bunch_list, NULL );
144 lsmash_free( timeline );
147 void isom_remove_timelines( lsmash_file_t *file )
149 if( !file
150 || !file->timeline )
151 return;
152 lsmash_remove_list( file->timeline, isom_destruct_timeline_direct );
155 void lsmash_destruct_timeline( lsmash_root_t *root, uint32_t track_ID )
157 if( track_ID == 0
158 || !root
159 || !root->file
160 || !root->file->timeline )
161 return;
162 for( lsmash_entry_t *entry = root->file->timeline->head; entry; entry = entry->next )
164 isom_timeline_t *timeline = (isom_timeline_t *)entry->data;
165 if( !timeline )
166 continue;
167 if( timeline->track_ID == track_ID )
169 lsmash_remove_entry_direct( root->file->timeline, entry, isom_destruct_timeline_direct );
170 break;
175 static void isom_get_qt_fixed_comp_audio_sample_quants
177 isom_timeline_t *timeline,
178 isom_sample_entry_t *description,
179 uint32_t *samples_per_packet,
180 uint32_t *constant_sample_size
183 isom_audio_entry_t *audio = (isom_audio_entry_t *)description;
184 if( audio->version == 0 )
186 uint32_t dummy;
187 if( !isom_get_implicit_qt_fixed_comp_audio_sample_quants( audio, samples_per_packet, constant_sample_size, &dummy ) )
189 /* LPCM */
190 if( !isom_is_lpcm_audio( audio ) )
191 lsmash_log( timeline, LSMASH_LOG_WARNING, "unsupported implicit sample table!\n" );
192 *samples_per_packet = 1;
193 *constant_sample_size = (audio->samplesize * audio->channelcount) / 8;
196 else if( audio->version == 1 )
198 *samples_per_packet = audio->samplesPerPacket;
199 *constant_sample_size = audio->bytesPerFrame;
201 else /* if( audio->version == 2 ) */
203 *samples_per_packet = audio->constLPCMFramesPerAudioPacket;
204 *constant_sample_size = audio->constBytesPerAudioPacket;
208 static int isom_is_qt_fixed_compressed_audio
210 isom_sample_entry_t *description
213 if( (description->manager & LSMASH_VIDEO_DESCRIPTION) || !isom_is_qt_audio( description->type ) )
214 return 0;
215 /* LPCM is a special case of fixed compression. */
216 return (((isom_audio_entry_t *)description)->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION);
219 static int isom_add_sample_info_entry( isom_timeline_t *timeline, isom_sample_info_t *src_info )
221 isom_sample_info_t *dst_info = lsmash_malloc( sizeof(isom_sample_info_t) );
222 if( !dst_info )
223 return -1;
224 if( lsmash_add_entry( timeline->info_list, dst_info ) )
226 lsmash_free( dst_info );
227 return -1;
229 *dst_info = *src_info;
230 return 0;
233 static int isom_add_lpcm_bunch_entry( isom_timeline_t *timeline, isom_lpcm_bunch_t *src_bunch )
235 isom_lpcm_bunch_t *dst_bunch = lsmash_malloc( sizeof(isom_lpcm_bunch_t) );
236 if( !dst_bunch )
237 return -1;
238 if( lsmash_add_entry( timeline->bunch_list, dst_bunch ) )
240 lsmash_free( dst_bunch );
241 return -1;
243 *dst_bunch = *src_bunch;
244 return 0;
247 static int isom_add_portable_chunk_entry( isom_timeline_t *timeline, isom_portable_chunk_t *src_chunk )
249 isom_portable_chunk_t *dst_chunk = lsmash_malloc( sizeof(isom_portable_chunk_t) );
250 if( !dst_chunk )
251 return -1;
252 if( lsmash_add_entry( timeline->chunk_list, dst_chunk ) )
254 lsmash_free( dst_chunk );
255 return -1;
257 *dst_chunk = *src_chunk;
258 return 0;
261 static int isom_compare_lpcm_sample_info( isom_lpcm_bunch_t *bunch, isom_sample_info_t *info )
263 return info->duration != bunch->duration
264 || info->offset != bunch->offset
265 || info->length != bunch->length
266 || info->index != bunch->index
267 || info->chunk != bunch->chunk;
270 static void isom_update_bunch( isom_lpcm_bunch_t *bunch, isom_sample_info_t *info )
272 bunch->pos = info->pos;
273 bunch->duration = info->duration;
274 bunch->offset = info->offset;
275 bunch->length = info->length;
276 bunch->index = info->index;
277 bunch->chunk = info->chunk;
278 bunch->prop = info->prop;
279 bunch->sample_count = 1;
282 static isom_lpcm_bunch_t *isom_get_bunch( isom_timeline_t *timeline, uint32_t sample_number )
284 if( sample_number >= timeline->last_accessed_lpcm_bunch_first_sample_number
285 && sample_number < timeline->last_accessed_lpcm_bunch_first_sample_number + timeline->last_accessed_lpcm_bunch_sample_count )
286 /* Get from the last accessed LPCM bunch. */
287 return (isom_lpcm_bunch_t *)lsmash_get_entry_data( timeline->bunch_list, timeline->last_accessed_lpcm_bunch_number );
288 uint32_t first_sample_number_in_next_bunch;
289 uint32_t bunch_number = 1;
290 uint64_t bunch_dts;
291 if( timeline->last_accessed_lpcm_bunch_first_sample_number
292 && timeline->last_accessed_lpcm_bunch_first_sample_number <= sample_number )
294 first_sample_number_in_next_bunch = timeline->last_accessed_lpcm_bunch_first_sample_number + timeline->last_accessed_lpcm_bunch_sample_count;
295 bunch_number += timeline->last_accessed_lpcm_bunch_number;
296 bunch_dts = timeline->last_accessed_lpcm_bunch_dts
297 + timeline->last_accessed_lpcm_bunch_duration * timeline->last_accessed_lpcm_bunch_sample_count;
299 else
301 /* Seek from the first LPCM bunch. */
302 first_sample_number_in_next_bunch = 1;
303 bunch_dts = 0;
305 isom_lpcm_bunch_t *bunch = (isom_lpcm_bunch_t *)lsmash_get_entry_data( timeline->bunch_list, bunch_number++ );
306 if( !bunch )
307 return NULL;
308 first_sample_number_in_next_bunch += bunch->sample_count;
309 while( sample_number >= first_sample_number_in_next_bunch )
311 bunch_dts += bunch->duration * bunch->sample_count;
312 bunch = (isom_lpcm_bunch_t *)lsmash_get_entry_data( timeline->bunch_list, bunch_number++ );
313 if( !bunch )
314 return NULL;
315 first_sample_number_in_next_bunch += bunch->sample_count;
317 timeline->last_accessed_lpcm_bunch_dts = bunch_dts;
318 timeline->last_accessed_lpcm_bunch_number = bunch_number - 1;
319 timeline->last_accessed_lpcm_bunch_duration = bunch->duration;
320 timeline->last_accessed_lpcm_bunch_sample_count = bunch->sample_count;
321 timeline->last_accessed_lpcm_bunch_first_sample_number = first_sample_number_in_next_bunch - bunch->sample_count;
322 return bunch;
325 static int isom_get_dts_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts )
327 if( sample_number == timeline->last_accessed_sample_number )
328 *dts = timeline->last_accessed_sample_dts;
329 else if( sample_number == 1 )
330 *dts = 0;
331 else if( sample_number == timeline->last_accessed_sample_number + 1 )
333 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, timeline->last_accessed_sample_number );
334 if( !info )
335 return -1;
336 *dts = timeline->last_accessed_sample_dts + info->duration;
338 else if( sample_number == timeline->last_accessed_sample_number - 1 )
340 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, timeline->last_accessed_sample_number - 1 );
341 if( !info )
342 return -1;
343 *dts = timeline->last_accessed_sample_dts - info->duration;
345 else
347 *dts = 0;
348 uint32_t distance = sample_number - 1;
349 lsmash_entry_t *entry;
350 for( entry = timeline->info_list->head; entry; entry = entry->next )
352 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
353 if( !info )
354 return -1;
355 if( distance-- == 0 )
356 break;
357 *dts += info->duration;
359 if( !entry )
360 return -1;
362 /* Note: last_accessed_sample_number is always updated together with last_accessed_sample_dts, and vice versa. */
363 timeline->last_accessed_sample_dts = *dts;
364 timeline->last_accessed_sample_number = sample_number;
365 return 0;
368 static int isom_get_cts_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts )
370 if( isom_get_dts_from_info_list( timeline, sample_number, cts ) )
371 return -1;
372 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
373 if( !info )
374 return -1;
375 *cts = timeline->ctd_shift ? (*cts + (int32_t)info->offset) : (*cts + info->offset);
376 return 0;
379 static int isom_get_dts_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts )
381 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
382 if( !bunch )
383 return -1;
384 *dts = timeline->last_accessed_lpcm_bunch_dts + (sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number) * bunch->duration;
385 return 0;
388 static int isom_get_cts_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts )
390 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
391 if( !bunch )
392 return -1;
393 *cts = timeline->last_accessed_lpcm_bunch_dts + (sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number) * bunch->duration + bunch->offset;
394 return 0;
397 static int isom_get_sample_duration_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration )
399 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
400 if( !info )
401 return -1;
402 *sample_duration = info->duration;
403 return 0;
406 static int isom_get_sample_duration_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration )
408 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
409 if( !bunch )
410 return -1;
411 *sample_duration = bunch->duration;
412 return 0;
415 static int isom_check_sample_existence_in_info_list( isom_timeline_t *timeline, uint32_t sample_number )
417 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
418 if( !info || !info->chunk )
419 return 0;
420 return !!info->chunk->file;
423 static int isom_check_sample_existence_in_bunch_list( isom_timeline_t *timeline, uint32_t sample_number )
425 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
426 if( !bunch || !bunch->chunk )
427 return 0;
428 return !!bunch->chunk->file;
431 static lsmash_sample_t *isom_read_sample_data_from_stream
433 lsmash_file_t *file,
434 isom_timeline_t *timeline,
435 uint32_t sample_length,
436 uint64_t sample_pos
439 lsmash_sample_t *sample = lsmash_create_sample( 0 );
440 if( !sample )
441 return NULL;
442 lsmash_bs_t *bs = file->bs;
443 lsmash_bs_read_seek( bs, sample_pos, SEEK_SET );
444 sample->data = lsmash_bs_get_bytes( bs, sample_length );
445 if( !sample->data )
447 lsmash_delete_sample( sample );
448 return NULL;
450 return sample;
453 static lsmash_sample_t *isom_get_lpcm_sample_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number )
455 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
456 if( !bunch
457 || !bunch->chunk )
458 return NULL;
459 /* Get data of a sample from the stream. */
460 uint64_t sample_number_offset = sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number;
461 uint64_t sample_pos = bunch->pos + sample_number_offset * bunch->length;
462 lsmash_sample_t *sample = isom_read_sample_data_from_stream( bunch->chunk->file, timeline, bunch->length, sample_pos );
463 if( !sample )
464 return NULL;
465 /* Get sample info. */
466 sample->dts = timeline->last_accessed_lpcm_bunch_dts + sample_number_offset * bunch->duration;
467 sample->cts = timeline->ctd_shift ? (sample->dts + (int32_t)bunch->offset) : (sample->dts + bunch->offset);
468 sample->pos = sample_pos;
469 sample->length = bunch->length;
470 sample->index = bunch->index;
471 sample->prop = bunch->prop;
472 return sample;
475 static lsmash_sample_t *isom_get_sample_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number )
477 uint64_t dts;
478 if( isom_get_dts_from_info_list( timeline, sample_number, &dts ) )
479 return NULL;
480 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
481 if( !info
482 || !info->chunk )
483 return NULL;
484 /* Get data of a sample from the stream. */
485 lsmash_sample_t *sample = isom_read_sample_data_from_stream( info->chunk->file, timeline, info->length, info->pos );
486 if( !sample )
487 return NULL;
488 /* Get sample info. */
489 sample->dts = dts;
490 sample->cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset);
491 sample->pos = info->pos;
492 sample->length = info->length;
493 sample->index = info->index;
494 sample->prop = info->prop;
495 return sample;
498 static int isom_get_lpcm_sample_info_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample )
500 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
501 if( !bunch )
502 return -1;
503 uint64_t sample_number_offset = sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number;
504 sample->dts = timeline->last_accessed_lpcm_bunch_dts + sample_number_offset * bunch->duration;
505 sample->cts = timeline->ctd_shift ? (sample->dts + (int32_t)bunch->offset) : (sample->dts + bunch->offset);
506 sample->pos = bunch->pos + sample_number_offset * bunch->length;
507 sample->length = bunch->length;
508 sample->index = bunch->index;
509 sample->prop = bunch->prop;
510 return 0;
513 static int isom_get_sample_info_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample )
515 uint64_t dts;
516 if( isom_get_dts_from_info_list( timeline, sample_number, &dts ) )
517 return -1;
518 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
519 if( !info )
520 return -1;
521 sample->dts = dts;
522 sample->cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset);
523 sample->pos = info->pos;
524 sample->length = info->length;
525 sample->index = info->index;
526 sample->prop = info->prop;
527 return 0;
530 static int isom_get_lpcm_sample_property_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop )
532 memset( prop, 0, sizeof(lsmash_sample_property_t) );
533 prop->ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
534 return 0;
537 static int isom_get_sample_property_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop )
539 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
540 if( !info )
541 return -1;
542 *prop = info->prop;
543 return 0;
546 static inline void isom_increment_sample_number_in_entry
548 uint32_t *sample_number_in_entry,
549 lsmash_entry_t **entry,
550 uint32_t sample_count
553 if( *sample_number_in_entry == sample_count )
555 *sample_number_in_entry = 1;
556 *entry = (*entry)->next;
558 else
559 *sample_number_in_entry += 1;
562 static inline isom_sgpd_t *isom_select_appropriate_sgpd
564 isom_sgpd_t *sgpd,
565 isom_sgpd_t *sgpd_frag,
566 uint32_t *group_description_index
569 if( sgpd_frag && *group_description_index >= 0x10000 )
571 /* The specification doesn't define 0x10000 explicitly, however says that there must be fewer than
572 * 65536 group definitions for this track and grouping type in the sample table in the Movie Box.
573 * So, we assume 0x10000 is equivalent to 0. */
574 *group_description_index -= 0x10000;
575 return sgpd_frag;
577 else
578 return sgpd;
581 static int isom_get_roll_recovery_grouping_info
583 isom_timeline_t *timeline,
584 lsmash_entry_t **sbgp_roll_entry,
585 isom_sgpd_t *sgpd_roll,
586 isom_sgpd_t *sgpd_frag_roll,
587 uint32_t *sample_number_in_sbgp_roll_entry,
588 isom_sample_info_t *info,
589 uint32_t sample_number
592 isom_group_assignment_entry_t *assignment = (isom_group_assignment_entry_t *)(*sbgp_roll_entry)->data;
593 if( !assignment )
594 return -1;
595 if( assignment->group_description_index )
597 uint32_t group_description_index = assignment->group_description_index;
598 isom_sgpd_t *sgpd = isom_select_appropriate_sgpd( sgpd_roll, sgpd_frag_roll, &group_description_index );
599 isom_roll_entry_t *roll_data = (isom_roll_entry_t *)lsmash_get_entry_data( sgpd->list, group_description_index );
600 if( roll_data )
602 if( roll_data->roll_distance > 0 )
604 /* post-roll */
605 info->prop.post_roll.complete = sample_number + roll_data->roll_distance;
606 if( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
607 info->prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_POST_ROLL_START;
609 else if( roll_data->roll_distance < 0 )
611 /* pre-roll */
612 info->prop.pre_roll.distance = -roll_data->roll_distance;
613 if( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
614 info->prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_PRE_ROLL_END;
617 else if( *sample_number_in_sbgp_roll_entry == 1 && group_description_index )
618 lsmash_log( timeline, LSMASH_LOG_WARNING, "a description of roll recoveries is not found in the Sample Group Description Box.\n" );
620 isom_increment_sample_number_in_entry( sample_number_in_sbgp_roll_entry, sbgp_roll_entry, assignment->sample_count );
621 return 0;
624 static int isom_get_random_access_point_grouping_info
626 isom_timeline_t *timeline,
627 lsmash_entry_t **sbgp_rap_entry,
628 isom_sgpd_t *sgpd_rap,
629 isom_sgpd_t *sgpd_frag_rap,
630 uint32_t *sample_number_in_sbgp_rap_entry,
631 isom_sample_info_t *info,
632 uint32_t *distance
635 isom_group_assignment_entry_t *assignment = (isom_group_assignment_entry_t *)(*sbgp_rap_entry)->data;
636 if( !assignment )
637 return -1;
638 if( assignment->group_description_index && (info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE) )
640 uint32_t group_description_index = assignment->group_description_index;
641 isom_sgpd_t *sgpd = isom_select_appropriate_sgpd( sgpd_rap, sgpd_frag_rap, &group_description_index );
642 isom_rap_entry_t *rap_data = (isom_rap_entry_t *)lsmash_get_entry_data( sgpd->list, group_description_index );
643 if( rap_data )
645 /* If this is not an open RAP, we treat it as an unknown RAP since non-IDR sample could make a closed GOP. */
646 info->prop.ra_flags |= (rap_data->num_leading_samples_known && !!rap_data->num_leading_samples)
647 ? ISOM_SAMPLE_RANDOM_ACCESS_FLAG_OPEN_RAP
648 : ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP;
649 *distance = 0;
651 else if( *sample_number_in_sbgp_rap_entry == 1 && group_description_index )
652 lsmash_log( timeline, LSMASH_LOG_WARNING, "a description of random access points is not found in the Sample Group Description Box.\n" );
654 isom_increment_sample_number_in_entry( sample_number_in_sbgp_rap_entry, sbgp_rap_entry, assignment->sample_count );
655 return 0;
658 int lsmash_construct_timeline( lsmash_root_t *root, uint32_t track_ID )
660 if( !root )
661 return -1;
662 lsmash_file_t *file = root->file;
663 if( !file
664 || !file->moov
665 || !file->moov->mvhd
666 || file->moov->mvhd->timescale == 0 )
667 return -1;
668 /* Get track by track_ID. */
669 isom_trak_t *trak = isom_get_trak( file, track_ID );
670 if( !trak
671 || !trak->tkhd
672 || !trak->mdia
673 || !trak->mdia->mdhd
674 || trak->mdia->mdhd->timescale == 0
675 || !trak->mdia->minf
676 || !trak->mdia->minf->stbl )
677 return -1;
678 /* Create a timeline list if it doesn't exist. */
679 if( !file->timeline )
681 file->timeline = lsmash_create_entry_list();
682 if( !file->timeline )
683 return -1;
685 /* Create a timeline. */
686 isom_timeline_t *timeline = isom_create_timeline();
687 if( !timeline )
688 return -1;
689 timeline->track_ID = track_ID;
690 timeline->movie_timescale = file->moov->mvhd->timescale;
691 timeline->media_timescale = trak->mdia->mdhd->timescale;
692 timeline->track_duration = trak->tkhd->duration;
693 /* Preparation for construction. */
694 isom_elst_t *elst = trak->edts ? trak->edts->elst : NULL;
695 isom_minf_t *minf = trak->mdia->minf;
696 isom_dref_t *dref = minf->dinf->dref;
697 isom_stbl_t *stbl = minf->stbl;
698 isom_stsd_t *stsd = stbl->stsd;
699 isom_stts_t *stts = stbl->stts;
700 isom_ctts_t *ctts = stbl->ctts;
701 isom_stss_t *stss = stbl->stss;
702 isom_stps_t *stps = stbl->stps;
703 isom_sdtp_t *sdtp = stbl->sdtp;
704 isom_stsc_t *stsc = stbl->stsc;
705 isom_stsz_t *stsz = stbl->stsz;
706 isom_stco_t *stco = stbl->stco;
707 isom_sgpd_t *sgpd_rap = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
708 isom_sbgp_t *sbgp_rap = isom_get_sample_to_group ( stbl, ISOM_GROUP_TYPE_RAP );
709 isom_sgpd_t *sgpd_roll = isom_get_roll_recovery_sample_group_description( &stbl->sgpd_list );
710 isom_sbgp_t *sbgp_roll = isom_get_roll_recovery_sample_to_group ( &stbl->sbgp_list );
711 lsmash_entry_t *elst_entry = elst && elst->list ? elst->list->head : NULL;
712 lsmash_entry_t *stts_entry = stts && stts->list ? stts->list->head : NULL;
713 lsmash_entry_t *ctts_entry = ctts && ctts->list ? ctts->list->head : NULL;
714 lsmash_entry_t *stss_entry = stss && stss->list ? stss->list->head : NULL;
715 lsmash_entry_t *stps_entry = stps && stps->list ? stps->list->head : NULL;
716 lsmash_entry_t *sdtp_entry = sdtp && sdtp->list ? sdtp->list->head : NULL;
717 lsmash_entry_t *stsz_entry = stsz && stsz->list ? stsz->list->head : NULL;
718 lsmash_entry_t *stsc_entry = stsc && stsc->list ? stsc->list->head : NULL;
719 lsmash_entry_t *stco_entry = stco && stco->list ? stco->list->head : NULL;
720 lsmash_entry_t *sbgp_roll_entry = sbgp_roll && sbgp_roll->list ? sbgp_roll->list->head : NULL;
721 lsmash_entry_t *sbgp_rap_entry = sbgp_rap && sbgp_rap->list ? sbgp_rap->list->head : NULL;
722 lsmash_entry_t *next_stsc_entry = stsc_entry ? stsc_entry->next : NULL;
723 isom_stsc_entry_t *stsc_data = stsc_entry ? (isom_stsc_entry_t *)stsc_entry->data : NULL;
724 int movie_framemts_present = (file->moov->mvex && file->moof_list.head);
725 if( !movie_framemts_present && (!stts_entry || !stsc_entry || !stco_entry || !stco_entry->data || (next_stsc_entry && !next_stsc_entry->data)) )
726 goto fail;
727 isom_sample_entry_t *description = (isom_sample_entry_t *)lsmash_get_entry_data( &stsd->list, stsc_data ? stsc_data->sample_description_index : 1 );
728 if( !description )
729 goto fail;
730 isom_dref_entry_t *dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( &dref->list, description->data_reference_index );
731 int all_sync = !stss;
732 int large_presentation = stco->large_presentation || lsmash_check_box_type_identical( stco->type, ISOM_BOX_TYPE_CO64 );
733 int is_lpcm_audio = isom_is_lpcm_audio( description );
734 int is_qt_fixed_comp_audio = isom_is_qt_fixed_compressed_audio( description );
735 int iso_sdtp = file->max_isom_version >= 2 || file->avc_extensions;
736 int allow_negative_sample_offset = ctts && ((file->max_isom_version >= 4 && ctts->version == 1) || file->qt_compatible);
737 uint32_t sample_number_in_stts_entry = 1;
738 uint32_t sample_number_in_ctts_entry = 1;
739 uint32_t sample_number_in_sbgp_roll_entry = 1;
740 uint32_t sample_number_in_sbgp_rap_entry = 1;
741 uint64_t dts = 0;
742 uint32_t chunk_number = 1;
743 uint64_t offset_from_chunk = 0;
744 uint64_t data_offset = stco_entry && stco_entry->data
745 ? large_presentation
746 ? ((isom_co64_entry_t *)stco_entry->data)->chunk_offset
747 : ((isom_stco_entry_t *)stco_entry->data)->chunk_offset
748 : 0;
749 uint32_t samples_per_packet;
750 uint32_t constant_sample_size;
751 if( is_qt_fixed_comp_audio )
752 isom_get_qt_fixed_comp_audio_sample_quants( timeline, description, &samples_per_packet, &constant_sample_size );
753 else
755 samples_per_packet = 1;
756 constant_sample_size = stsz->sample_size;
758 uint32_t sample_number = samples_per_packet;
759 uint32_t sample_number_in_chunk = samples_per_packet;
760 /* Copy edits. */
761 while( elst_entry )
763 isom_elst_entry_t *edit = (isom_elst_entry_t *)lsmash_memdup( elst_entry->data, sizeof(isom_elst_entry_t) );
764 if( !edit
765 || lsmash_add_entry( timeline->edit_list, edit ) )
766 goto fail;
767 elst_entry = elst_entry->next;
769 /* Check what the first 2-bits of sample dependency means.
770 * This check is for chimera of ISO Base Media and QTFF. */
771 if( iso_sdtp && sdtp_entry )
773 while( sdtp_entry )
775 isom_sdtp_entry_t *sdtp_data = (isom_sdtp_entry_t *)sdtp_entry->data;
776 if( !sdtp_data )
777 goto fail;
778 if( sdtp_data->is_leading > 1 )
779 break; /* Apparently, it's defined under ISO Base Media. */
780 if( (sdtp_data->is_leading == 1) && (sdtp_data->sample_depends_on == ISOM_SAMPLE_IS_INDEPENDENT) )
782 /* Obviously, it's not defined under ISO Base Media. */
783 iso_sdtp = 0;
784 break;
786 sdtp_entry = sdtp_entry->next;
788 sdtp_entry = sdtp->list->head;
790 /**--- Construct media timeline. ---**/
791 isom_portable_chunk_t chunk;
792 chunk.data_offset = data_offset;
793 chunk.length = 0;
794 chunk.number = chunk_number;
795 chunk.file = (!dref_entry || !dref_entry->ref_file) ? NULL : dref_entry->ref_file;
796 if( isom_add_portable_chunk_entry( timeline, &chunk ) < 0 )
797 goto fail;
798 uint32_t distance = NO_RANDOM_ACCESS_POINT;
799 uint32_t last_duration = UINT32_MAX;
800 uint32_t packet_number = 1;
801 isom_lpcm_bunch_t bunch = { 0 };
802 while( sample_number <= stsz->sample_count )
804 isom_sample_info_t info = { 0 };
805 /* Get sample duration and sample offset. */
806 for( uint32_t i = 0; i < samples_per_packet; i++ )
808 /* sample duration */
809 if( stts_entry )
811 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
812 if( !stts_data )
813 goto fail;
814 isom_increment_sample_number_in_entry( &sample_number_in_stts_entry, &stts_entry, stts_data->sample_count );
815 last_duration = stts_data->sample_delta;
817 info.duration += last_duration;
818 dts += last_duration;
819 /* sample offset */
820 uint32_t sample_offset;
821 if( ctts_entry )
823 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
824 if( !ctts_data )
825 goto fail;
826 isom_increment_sample_number_in_entry( &sample_number_in_ctts_entry, &ctts_entry, ctts_data->sample_count );
827 sample_offset = ctts_data->sample_offset;
828 if( allow_negative_sample_offset )
830 uint64_t cts = dts + (int32_t)sample_offset;
831 if( (cts + timeline->ctd_shift) < dts )
832 timeline->ctd_shift = dts - cts;
835 else
836 sample_offset = 0;
837 if( i == 0 )
838 info.offset = sample_offset;
840 timeline->media_duration += info.duration;
841 if( !is_qt_fixed_comp_audio )
843 /* Check whether sync sample or not. */
844 if( stss_entry )
846 isom_stss_entry_t *stss_data = (isom_stss_entry_t *)stss_entry->data;
847 if( !stss_data )
848 goto fail;
849 if( sample_number == stss_data->sample_number )
851 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
852 stss_entry = stss_entry->next;
853 distance = 0;
856 else if( all_sync )
857 /* Don't reset distance as 0 since MDCT-based audio frames need pre-roll for correct presentation
858 * though all of them could be marked as a sync sample. */
859 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
860 /* Check whether partial sync sample or not. */
861 if( stps_entry )
863 isom_stps_entry_t *stps_data = (isom_stps_entry_t *)stps_entry->data;
864 if( !stps_data )
865 goto fail;
866 if( sample_number == stps_data->sample_number )
868 info.prop.ra_flags |= QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC | QT_SAMPLE_RANDOM_ACCESS_FLAG_RAP;
869 stps_entry = stps_entry->next;
870 distance = 0;
873 /* Get sample dependency info. */
874 if( sdtp_entry )
876 isom_sdtp_entry_t *sdtp_data = (isom_sdtp_entry_t *)sdtp_entry->data;
877 if( !sdtp_data )
878 goto fail;
879 if( iso_sdtp )
880 info.prop.leading = sdtp_data->is_leading;
881 else
882 info.prop.allow_earlier = sdtp_data->is_leading;
883 info.prop.independent = sdtp_data->sample_depends_on;
884 info.prop.disposable = sdtp_data->sample_is_depended_on;
885 info.prop.redundant = sdtp_data->sample_has_redundancy;
886 sdtp_entry = sdtp_entry->next;
888 /* Get roll recovery grouping info. */
889 if( sbgp_roll_entry
890 && isom_get_roll_recovery_grouping_info( timeline,
891 &sbgp_roll_entry, sgpd_roll, NULL,
892 &sample_number_in_sbgp_roll_entry,
893 &info, sample_number ) < 0 )
894 goto fail;
895 info.prop.post_roll.identifier = sample_number;
896 /* Get random access point grouping info. */
897 if( sbgp_rap_entry
898 && isom_get_random_access_point_grouping_info( timeline,
899 &sbgp_rap_entry, sgpd_rap, NULL,
900 &sample_number_in_sbgp_rap_entry,
901 &info, &distance ) < 0 )
902 goto fail;
903 /* Set up distance from the previous random access point. */
904 if( distance != NO_RANDOM_ACCESS_POINT )
906 if( info.prop.pre_roll.distance == 0 )
907 info.prop.pre_roll.distance = distance;
908 ++distance;
911 else
912 /* All uncompressed and non-variable compressed audio frame is a sync sample. */
913 info.prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
914 /* Get size of sample in the stream. */
915 if( is_qt_fixed_comp_audio || !stsz_entry )
916 info.length = constant_sample_size;
917 else
919 if( !stsz_entry->data )
920 goto fail;
921 info.length = ((isom_stsz_entry_t *)stsz_entry->data)->entry_size;
922 stsz_entry = stsz_entry->next;
924 timeline->max_sample_size = LSMASH_MAX( timeline->max_sample_size, info.length );
925 /* Get chunk info. */
926 info.pos = data_offset;
927 info.index = stsc_data->sample_description_index;
928 info.chunk = (isom_portable_chunk_t *)timeline->chunk_list->tail->data;
929 offset_from_chunk += info.length;
930 if( sample_number_in_chunk == stsc_data->samples_per_chunk )
932 /* Set the length of the last chunk. */
933 if( info.chunk )
934 info.chunk->length = offset_from_chunk;
935 /* Move the next chunk. */
936 if( stco_entry )
937 stco_entry = stco_entry->next;
938 if( stco_entry
939 && stco_entry->data )
940 data_offset = large_presentation
941 ? ((isom_co64_entry_t *)stco_entry->data)->chunk_offset
942 : ((isom_stco_entry_t *)stco_entry->data)->chunk_offset;
943 chunk.data_offset = data_offset;
944 chunk.length = 0;
945 chunk.number = ++chunk_number;
946 offset_from_chunk = 0;
947 /* Check if the next entry is broken. */
948 while( next_stsc_entry && chunk_number > ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk )
950 /* Just skip broken next entry. */
951 lsmash_log( timeline, LSMASH_LOG_WARNING, "ignore broken entry in Sample To Chunk Box.\n" );
952 lsmash_log( timeline, LSMASH_LOG_WARNING, "timeline might be corrupted.\n" );
953 next_stsc_entry = next_stsc_entry->next;
954 if( next_stsc_entry
955 && !next_stsc_entry->data )
956 goto fail;
958 /* Check if the next chunk belongs to the next sequence of chunks. */
959 if( next_stsc_entry && chunk_number == ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk )
961 stsc_entry = next_stsc_entry;
962 next_stsc_entry = next_stsc_entry->next;
963 if( next_stsc_entry
964 && !next_stsc_entry->data )
965 goto fail;
966 stsc_data = (isom_stsc_entry_t *)stsc_entry->data;
967 /* Update sample description. */
968 description = (isom_sample_entry_t *)lsmash_get_entry_data( &stsd->list, stsc_data->sample_description_index );
969 is_lpcm_audio = isom_is_lpcm_audio( description );
970 is_qt_fixed_comp_audio = isom_is_qt_fixed_compressed_audio( description );
971 if( is_qt_fixed_comp_audio )
972 isom_get_qt_fixed_comp_audio_sample_quants( timeline, description, &samples_per_packet, &constant_sample_size );
973 else
975 samples_per_packet = 1;
976 constant_sample_size = stsz->sample_size;
978 /* Reference media data. */
979 dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( &dref->list, description->data_reference_index );
980 chunk.file = (!dref_entry || !dref_entry->ref_file) ? NULL : dref_entry->ref_file;
982 sample_number_in_chunk = samples_per_packet;
983 if( isom_add_portable_chunk_entry( timeline, &chunk ) < 0 )
984 goto fail;
986 else
988 data_offset += info.length;
989 sample_number_in_chunk += samples_per_packet;
991 /* OK. Let's add its info. */
992 if( is_lpcm_audio )
994 if( sample_number == samples_per_packet )
995 isom_update_bunch( &bunch, &info );
996 else if( isom_compare_lpcm_sample_info( &bunch, &info ) )
998 if( isom_add_lpcm_bunch_entry( timeline, &bunch ) )
999 goto fail;
1000 isom_update_bunch( &bunch, &info );
1002 else
1003 ++ bunch.sample_count;
1005 else if( isom_add_sample_info_entry( timeline, &info ) )
1006 goto fail;
1007 if( timeline->info_list->entry_count && timeline->bunch_list->entry_count )
1009 lsmash_log( timeline, LSMASH_LOG_ERROR, "LPCM + non-LPCM track is not supported.\n" );
1010 goto fail;
1012 sample_number += samples_per_packet;
1013 packet_number += 1;
1015 isom_portable_chunk_t *last_chunk = lsmash_get_entry_data( timeline->chunk_list, timeline->chunk_list->entry_count );
1016 if( last_chunk )
1018 if( offset_from_chunk )
1019 last_chunk->length = offset_from_chunk;
1020 else
1022 /* Remove the last invalid chunk. */
1023 lsmash_remove_entry( timeline->chunk_list, timeline->chunk_list->entry_count, NULL );
1024 --chunk_number;
1027 uint32_t sample_count = packet_number - 1;
1028 if( movie_framemts_present )
1030 isom_tfra_t *tfra = isom_get_tfra( file->mfra, track_ID );
1031 lsmash_entry_t *tfra_entry = tfra && tfra->list ? tfra->list->head : NULL;
1032 isom_tfra_location_time_entry_t *rap = tfra_entry ? (isom_tfra_location_time_entry_t *)tfra_entry->data : NULL;
1033 chunk.data_offset = 0;
1034 chunk.length = 0;
1035 /* Movie fragments */
1036 for( lsmash_entry_t *moof_entry = file->moof_list.head; moof_entry; moof_entry = moof_entry->next )
1038 isom_moof_t *moof = (isom_moof_t *)moof_entry->data;
1039 if( !moof )
1040 goto fail;
1041 uint64_t last_sample_end_pos = 0;
1042 /* Track fragments */
1043 uint32_t traf_number = 1;
1044 for( lsmash_entry_t *traf_entry = moof->traf_list.head; traf_entry; traf_entry = traf_entry->next )
1046 isom_traf_t *traf = (isom_traf_t *)traf_entry->data;
1047 if( !traf )
1048 goto fail;
1049 isom_tfhd_t *tfhd = traf->tfhd;
1050 if( !tfhd )
1051 goto fail;
1052 isom_trex_t *trex = isom_get_trex( file->moov->mvex, tfhd->track_ID );
1053 if( !trex )
1054 goto fail;
1055 /* Ignore ISOM_TF_FLAGS_DURATION_IS_EMPTY flag even if set. */
1056 if( !traf->trun_list.head )
1058 ++traf_number;
1059 continue;
1061 /* Get base_data_offset. */
1062 uint64_t base_data_offset;
1063 if( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT )
1064 base_data_offset = tfhd->base_data_offset;
1065 else if( (tfhd->flags & ISOM_TF_FLAGS_DEFAULT_BASE_IS_MOOF) || traf_entry == moof->traf_list.head )
1066 base_data_offset = moof->pos;
1067 else
1068 base_data_offset = last_sample_end_pos;
1069 /* sample grouping */
1070 isom_sgpd_t *sgpd_frag_rap;
1071 isom_sgpd_t *sgpd_frag_roll;
1072 sgpd_frag_rap = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_RAP );
1073 sbgp_rap = isom_get_fragment_sample_to_group ( traf, ISOM_GROUP_TYPE_RAP );
1074 sbgp_rap_entry = sbgp_rap && sbgp_rap->list ? sbgp_rap->list->head : NULL;
1075 sgpd_frag_roll = isom_get_roll_recovery_sample_group_description( &traf->sgpd_list );
1076 sbgp_roll = isom_get_roll_recovery_sample_to_group ( &traf->sbgp_list );
1077 sbgp_roll_entry = sbgp_roll && sbgp_roll->list ? sbgp_roll->list->head : NULL;
1078 int need_data_offset_only = (tfhd->track_ID != track_ID);
1079 /* Track runs */
1080 uint32_t trun_number = 1;
1081 for( lsmash_entry_t *trun_entry = traf->trun_list.head; trun_entry; trun_entry = trun_entry->next )
1083 isom_trun_t *trun = (isom_trun_t *)trun_entry->data;
1084 if( !trun )
1085 goto fail;
1086 if( trun->sample_count == 0 )
1088 ++trun_number;
1089 continue;
1091 /* Get data_offset. */
1092 if( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT )
1093 data_offset = trun->data_offset + base_data_offset;
1094 else if( trun_entry == traf->trun_list.head )
1095 data_offset = base_data_offset;
1096 else
1097 data_offset = last_sample_end_pos;
1098 /* */
1099 uint32_t sample_description_index = 0;
1100 isom_sdtp_entry_t *sdtp_data = NULL;
1101 if( !need_data_offset_only )
1103 /* Get sample_description_index of this track fragment. */
1104 if( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT )
1105 sample_description_index = tfhd->sample_description_index;
1106 else
1107 sample_description_index = trex->default_sample_description_index;
1108 description = (isom_sample_entry_t *)lsmash_get_entry_data( &stsd->list, sample_description_index );
1109 is_lpcm_audio = isom_is_lpcm_audio( description );
1110 /* Reference media data. */
1111 dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( &dref->list, description->data_reference_index );
1112 lsmash_file_t *ref_file = (!dref_entry || !dref_entry->ref_file) ? NULL : dref_entry->ref_file;
1113 /* Each track run can be considered as a chunk.
1114 * Here, we consider physically consecutive track runs as one chunk. */
1115 if( chunk.data_offset + chunk.length != data_offset || chunk.file != ref_file )
1117 chunk.data_offset = data_offset;
1118 chunk.length = 0;
1119 chunk.number = ++chunk_number;
1120 chunk.file = ref_file;
1121 if( isom_add_portable_chunk_entry( timeline, &chunk ) < 0 )
1122 goto fail;
1124 /* Get dependency info for this track fragment. */
1125 sdtp_entry = traf->sdtp && traf->sdtp->list ? traf->sdtp->list->head : NULL;
1126 sdtp_data = sdtp_entry && sdtp_entry->data ? (isom_sdtp_entry_t *)sdtp_entry->data : NULL;
1128 /* Get info of each sample. */
1129 lsmash_entry_t *row_entry = trun->optional && trun->optional->head ? trun->optional->head : NULL;
1130 sample_number = 1;
1131 while( sample_number <= trun->sample_count )
1133 isom_sample_info_t info = { 0 };
1134 isom_trun_optional_row_t *row = row_entry && row_entry->data ? (isom_trun_optional_row_t *)row_entry->data : NULL;
1135 /* Get sample_size */
1136 if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT) )
1137 info.length = row->sample_size;
1138 else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT )
1139 info.length = tfhd->default_sample_size;
1140 else
1141 info.length = trex->default_sample_size;
1142 if( !need_data_offset_only )
1144 info.pos = data_offset;
1145 info.index = sample_description_index;
1146 info.chunk = (isom_portable_chunk_t *)timeline->chunk_list->tail->data;
1147 info.chunk->length += info.length;
1148 /* Get sample_duration. */
1149 if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT) )
1150 info.duration = row->sample_duration;
1151 else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT )
1152 info.duration = tfhd->default_sample_duration;
1153 else
1154 info.duration = trex->default_sample_duration;
1155 /* Get composition time offset. */
1156 if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT) )
1158 info.offset = row->sample_composition_time_offset;
1159 /* Check composition to decode timeline shift. */
1160 if( file->max_isom_version >= 6 && trun->version != 0 )
1162 uint64_t cts = dts + (int32_t)info.offset;
1163 if( (cts + timeline->ctd_shift) < dts )
1164 timeline->ctd_shift = dts - cts;
1167 else
1168 info.offset = 0;
1169 dts += info.duration;
1170 /* Update media duration and maximun sample size. */
1171 timeline->media_duration += info.duration;
1172 timeline->max_sample_size = LSMASH_MAX( timeline->max_sample_size, info.length );
1173 if( !is_lpcm_audio )
1175 /* Get sample_flags. */
1176 isom_sample_flags_t sample_flags;
1177 if( sample_number == 1 && (trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT) )
1178 sample_flags = trun->first_sample_flags;
1179 else if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT) )
1180 sample_flags = row->sample_flags;
1181 else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT )
1182 sample_flags = tfhd->default_sample_flags;
1183 else
1184 sample_flags = trex->default_sample_flags;
1185 if( sdtp_data )
1187 /* Independent and Disposable Samples Box overrides the information from sample_flags.
1188 * There is no description in the specification about this, but the intention should be such a thing.
1189 * The ground is that sample_flags is placed in media layer
1190 * while Independent and Disposable Samples Box is placed in track or presentation layer. */
1191 info.prop.leading = sdtp_data->is_leading;
1192 info.prop.independent = sdtp_data->sample_depends_on;
1193 info.prop.disposable = sdtp_data->sample_is_depended_on;
1194 info.prop.redundant = sdtp_data->sample_has_redundancy;
1195 if( sdtp_entry )
1196 sdtp_entry = sdtp_entry->next;
1197 sdtp_data = sdtp_entry ? (isom_sdtp_entry_t *)sdtp_entry->data : NULL;
1199 else
1201 info.prop.leading = sample_flags.is_leading;
1202 info.prop.independent = sample_flags.sample_depends_on;
1203 info.prop.disposable = sample_flags.sample_is_depended_on;
1204 info.prop.redundant = sample_flags.sample_has_redundancy;
1206 /* Check this sample is a sync sample or not.
1207 * Note: all sync sample shall be independent. */
1208 if( !sample_flags.sample_is_non_sync_sample
1209 && info.prop.independent != ISOM_SAMPLE_IS_NOT_INDEPENDENT )
1211 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1212 distance = 0;
1214 /* Get roll recovery grouping info. */
1215 uint32_t roll_id = sample_count + sample_number;
1216 if( sbgp_roll_entry
1217 && isom_get_roll_recovery_grouping_info( timeline,
1218 &sbgp_roll_entry, sgpd_roll, sgpd_frag_roll,
1219 &sample_number_in_sbgp_roll_entry,
1220 &info, roll_id ) < 0 )
1221 goto fail;
1222 info.prop.post_roll.identifier = roll_id;
1223 /* Get random access point grouping info. */
1224 if( sbgp_rap_entry
1225 && isom_get_random_access_point_grouping_info( timeline,
1226 &sbgp_rap_entry, sgpd_rap, sgpd_frag_rap,
1227 &sample_number_in_sbgp_rap_entry,
1228 &info, &distance ) < 0 )
1229 goto fail;
1230 /* Get the location of the sync sample from 'tfra' if it is not set up yet.
1231 * Note: there is no guarantee that its entries are placed in a specific order. */
1232 if( tfra )
1234 if( tfra->number_of_entry == 0
1235 && info.prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1236 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1237 if( rap
1238 && rap->moof_offset == moof->pos
1239 && rap->traf_number == traf_number
1240 && rap->trun_number == trun_number
1241 && rap->sample_number == sample_number )
1243 if( info.prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1244 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1245 if( tfra_entry )
1246 tfra_entry = tfra_entry->next;
1247 rap = tfra_entry ? (isom_tfra_location_time_entry_t *)tfra_entry->data : NULL;
1250 /* Set up distance from the previous random access point. */
1251 if( distance != NO_RANDOM_ACCESS_POINT )
1253 if( info.prop.pre_roll.distance == 0 )
1254 info.prop.pre_roll.distance = distance;
1255 ++distance;
1257 /* OK. Let's add its info. */
1258 if( isom_add_sample_info_entry( timeline, &info ) )
1259 goto fail;
1261 else
1263 /* All LPCMFrame is a sync sample. */
1264 info.prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1265 /* OK. Let's add its info. */
1266 if( sample_count == 0 && sample_number == 1 )
1267 isom_update_bunch( &bunch, &info );
1268 else if( isom_compare_lpcm_sample_info( &bunch, &info ) )
1270 if( isom_add_lpcm_bunch_entry( timeline, &bunch ) )
1271 goto fail;
1272 isom_update_bunch( &bunch, &info );
1274 else
1275 ++ bunch.sample_count;
1277 if( timeline-> info_list->entry_count
1278 && timeline->bunch_list->entry_count )
1280 lsmash_log( timeline, LSMASH_LOG_ERROR, "LPCM + non-LPCM track is not supported.\n" );
1281 goto fail;
1284 data_offset += info.length;
1285 last_sample_end_pos = data_offset;
1286 if( row_entry )
1287 row_entry = row_entry->next;
1288 ++sample_number;
1290 if( !need_data_offset_only )
1291 sample_count += sample_number - 1;
1292 ++trun_number;
1293 } /* Track runs */
1294 ++traf_number;
1295 } /* Track fragments */
1296 } /* Movie fragments */
1298 else if( timeline->chunk_list->entry_count == 0 )
1299 goto fail; /* No samples in this track. */
1300 if( bunch.sample_count && isom_add_lpcm_bunch_entry( timeline, &bunch ) )
1301 goto fail;
1302 if( lsmash_add_entry( file->timeline, timeline ) )
1303 goto fail;
1304 /* Finish timeline construction. */
1305 timeline->sample_count = sample_count;
1306 if( timeline->info_list->entry_count )
1308 timeline->get_dts = isom_get_dts_from_info_list;
1309 timeline->get_cts = isom_get_cts_from_info_list;
1310 timeline->get_sample_duration = isom_get_sample_duration_from_info_list;
1311 timeline->check_sample_existence = isom_check_sample_existence_in_info_list;
1312 timeline->get_sample = isom_get_sample_from_media_timeline;
1313 timeline->get_sample_info = isom_get_sample_info_from_media_timeline;
1314 timeline->get_sample_property = isom_get_sample_property_from_media_timeline;
1316 else
1318 timeline->get_dts = isom_get_dts_from_bunch_list;
1319 timeline->get_cts = isom_get_cts_from_bunch_list;
1320 timeline->get_sample_duration = isom_get_sample_duration_from_bunch_list;
1321 timeline->check_sample_existence = isom_check_sample_existence_in_bunch_list;
1322 timeline->get_sample = isom_get_lpcm_sample_from_media_timeline;
1323 timeline->get_sample_info = isom_get_lpcm_sample_info_from_media_timeline;
1324 timeline->get_sample_property = isom_get_lpcm_sample_property_from_media_timeline;
1326 return 0;
1327 fail:
1328 isom_destruct_timeline_direct( timeline );
1329 return -1;
1332 int lsmash_get_dts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *dts )
1334 if( !sample_number || !dts )
1335 return -1;
1336 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1337 if( !timeline || sample_number > timeline->sample_count )
1338 return -1;
1339 return timeline->get_dts( timeline, sample_number, dts );
1342 int lsmash_get_cts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *cts )
1344 if( !sample_number || !cts )
1345 return -1;
1346 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1347 if( !timeline || sample_number > timeline->sample_count )
1348 return -1;
1349 return timeline->get_cts( timeline, sample_number, cts );
1352 lsmash_sample_t *lsmash_get_sample_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number )
1354 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1355 return timeline ? timeline->get_sample( timeline, sample_number ) : NULL;
1358 int lsmash_get_sample_info_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, lsmash_sample_t *sample )
1360 if( !sample )
1361 return -1;
1362 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1363 return timeline ? timeline->get_sample_info( timeline, sample_number, sample ) : -1;
1366 int lsmash_get_sample_property_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, lsmash_sample_property_t *prop )
1368 if( !prop )
1369 return -1;
1370 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1371 return timeline ? timeline->get_sample_property( timeline, sample_number, prop ) : -1;
1374 int lsmash_get_composition_to_decode_shift_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *ctd_shift )
1376 if( !ctd_shift )
1377 return -1;
1378 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1379 if( !timeline )
1380 return -1;
1381 *ctd_shift = timeline->ctd_shift;
1382 return 0;
1385 static inline int isom_get_closest_past_random_accessible_point_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number )
1387 lsmash_entry_t *entry = lsmash_get_entry( timeline->info_list, sample_number-- );
1388 if( !entry
1389 || !entry->data )
1390 return -1;
1391 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1392 while( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1394 entry = entry->prev;
1395 if( !entry
1396 || !entry->data )
1397 return -1;
1398 info = (isom_sample_info_t *)entry->data;
1399 --sample_number;
1401 *rap_number = sample_number + 1;
1402 return 0;
1405 static inline int isom_get_closest_future_random_accessible_point_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number )
1407 lsmash_entry_t *entry = lsmash_get_entry( timeline->info_list, sample_number++ );
1408 if( !entry
1409 || !entry->data )
1410 return -1;
1411 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1412 while( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1414 entry = entry->next;
1415 if( !entry
1416 || !entry->data )
1417 return -1;
1418 info = (isom_sample_info_t *)entry->data;
1419 ++sample_number;
1421 *rap_number = sample_number - 1;
1422 return 0;
1425 static int isom_get_closest_random_accessible_point_from_media_timeline_internal( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number )
1427 if( !timeline )
1428 return -1;
1429 if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, sample_number, rap_number )
1430 && isom_get_closest_future_random_accessible_point_from_media_timeline( timeline, sample_number + 1, rap_number ) )
1431 return -1;
1432 return 0;
1435 int lsmash_get_closest_random_accessible_point_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint32_t *rap_number )
1437 if( sample_number == 0 || !rap_number )
1438 return -1;
1439 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1440 if( timeline->info_list->entry_count == 0 )
1442 *rap_number = sample_number; /* All LPCM is sync sample. */
1443 return 0;
1445 return isom_get_closest_random_accessible_point_from_media_timeline_internal( timeline, sample_number, rap_number );
1448 int lsmash_get_closest_random_accessible_point_detail_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number,
1449 uint32_t *rap_number, lsmash_random_access_flag *ra_flags, uint32_t *leading, uint32_t *distance )
1451 if( sample_number == 0 )
1452 return -1;
1453 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1454 if( timeline->info_list->entry_count == 0 )
1456 /* All LPCM is sync sample. */
1457 *rap_number = sample_number;
1458 if( ra_flags )
1459 *ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1460 if( leading )
1461 *leading = 0;
1462 if( distance )
1463 *distance = 0;
1464 return 0;
1466 if( isom_get_closest_random_accessible_point_from_media_timeline_internal( timeline, sample_number, rap_number ) )
1467 return -1;
1468 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, *rap_number );
1469 if( !info )
1470 return -1;
1471 if( ra_flags )
1472 *ra_flags = info->prop.ra_flags;
1473 if( leading )
1474 *leading = 0;
1475 if( distance )
1476 *distance = 0;
1477 if( sample_number < *rap_number )
1478 /* Impossible to desire to decode the sample of given number correctly. */
1479 return 0;
1480 else if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) )
1482 if( leading )
1484 /* Count leading samples. */
1485 uint32_t current_sample_number = *rap_number + 1;
1486 uint64_t dts;
1487 if( isom_get_dts_from_info_list( timeline, *rap_number, &dts ) )
1488 return -1;
1489 uint64_t rap_cts = timeline->ctd_shift ? (dts + (int32_t)info->offset + timeline->ctd_shift) : (dts + info->offset);
1492 dts += info->duration;
1493 if( rap_cts <= dts )
1494 break; /* leading samples of this random accessible point must not be present more. */
1495 info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, current_sample_number++ );
1496 if( !info )
1497 break;
1498 uint64_t cts = timeline->ctd_shift ? (dts + (int32_t)info->offset + timeline->ctd_shift) : (dts + info->offset);
1499 if( rap_cts > cts )
1500 ++ *leading;
1501 } while( 1 );
1503 if( !distance || sample_number == *rap_number )
1504 return 0;
1505 /* Measure distance from the first closest non-recovery random accessible point to the second. */
1506 uint32_t prev_rap_number = *rap_number;
1509 if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) )
1510 /* The previous random accessible point is not present. */
1511 return 0;
1512 info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, prev_rap_number );
1513 if( !info )
1514 return -1;
1515 if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) )
1517 /* Decode shall already complete at the first closest non-recovery random accessible point if starting to decode from the second. */
1518 *distance = *rap_number - prev_rap_number;
1519 return 0;
1521 } while( 1 );
1523 if( !distance )
1524 return 0;
1525 /* Calculate roll-distance. */
1526 if( info->prop.pre_roll.distance )
1528 /* Pre-roll recovery */
1529 uint32_t prev_rap_number = *rap_number;
1532 if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number )
1533 && *rap_number < info->prop.pre_roll.distance )
1535 /* The previous random accessible point is not present.
1536 * And sample of given number might be not able to decoded correctly. */
1537 *distance = 0;
1538 return 0;
1540 if( prev_rap_number + info->prop.pre_roll.distance <= *rap_number )
1543 * |<---- pre-roll distance ---->|
1544 * |<--------- distance -------->|
1545 * media +++++++++++++++++++++++++ *** +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1546 * ^ ^ ^ ^
1547 * random accessible point starting point random accessible point given sample
1548 * (complete)
1550 *distance = info->prop.pre_roll.distance;
1551 return 0;
1553 else if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) )
1556 * |<------------ pre-roll distance ------------------>|
1557 * |<------ distance ------->|
1558 * media ++++++++++++++++ *** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1559 * ^ ^ ^ ^
1560 * random accessible point random accessible point given sample
1561 * (starting point) (complete)
1563 *distance = *rap_number - prev_rap_number;
1564 return 0;
1566 } while( 1 );
1568 /* Post-roll recovery */
1569 if( sample_number >= info->prop.post_roll.complete )
1571 * |<----- post-roll distance ----->|
1572 * (distance = 0)
1573 * media +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1574 * ^ ^ ^
1575 * random accessible point complete given sample
1576 * (starting point)
1578 return 0;
1579 uint32_t prev_rap_number = *rap_number;
1582 if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) )
1583 /* The previous random accessible point is not present. */
1584 return 0;
1585 info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, prev_rap_number );
1586 if( !info )
1587 return -1;
1588 if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) || sample_number >= info->prop.post_roll.complete )
1590 *distance = *rap_number - prev_rap_number;
1591 return 0;
1593 } while( 1 );
1596 int lsmash_check_sample_existence_in_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number )
1598 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1599 return timeline ? timeline->check_sample_existence( timeline, sample_number ) : 0;
1602 int lsmash_get_last_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *last_sample_delta )
1604 if( !last_sample_delta )
1605 return -1;
1606 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1607 return timeline ? timeline->get_sample_duration( timeline, timeline->sample_count, last_sample_delta ) : -1;
1610 int lsmash_get_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint32_t *sample_delta )
1612 if( !sample_delta )
1613 return -1;
1614 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1615 return timeline ? timeline->get_sample_duration( timeline, sample_number, sample_delta ) : -1;
1618 uint32_t lsmash_get_sample_count_in_media_timeline( lsmash_root_t *root, uint32_t track_ID )
1620 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1621 if( !timeline )
1622 return 0;
1623 return timeline->sample_count;
1626 uint32_t lsmash_get_max_sample_size_in_media_timeline( lsmash_root_t *root, uint32_t track_ID )
1628 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1629 if( !timeline )
1630 return 0;
1631 return timeline->max_sample_size;
1634 uint64_t lsmash_get_media_duration_from_media_timeline( lsmash_root_t *root, uint32_t track_ID )
1636 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1637 if( !timeline )
1638 return 0;
1639 return timeline->media_duration;
1642 int lsmash_copy_timeline_map( lsmash_root_t *dst, uint32_t dst_track_ID, lsmash_root_t *src, uint32_t src_track_ID )
1644 if( !dst || !src )
1645 return -1;
1646 lsmash_file_t *dst_file = dst->file;
1647 isom_trak_t *dst_trak = isom_get_trak( dst_file, dst_track_ID );
1648 if( !dst_file->moov
1649 || !dst_file->moov->mvhd
1650 || dst_file->moov->mvhd->timescale == 0
1651 || !dst_trak
1652 || !dst_trak->mdia
1653 || !dst_trak->mdia->mdhd
1654 || dst_trak->mdia->mdhd->timescale == 0
1655 || !dst_trak->mdia->minf
1656 || !dst_trak->mdia->minf->stbl )
1657 return -1;
1658 if( dst_trak->edts
1659 && dst_trak->edts->elst )
1660 lsmash_remove_entries( dst_trak->edts->elst->list, NULL );
1661 uint32_t src_movie_timescale;
1662 uint32_t src_media_timescale;
1663 uint64_t src_track_duration;
1664 uint64_t src_media_duration;
1665 int32_t src_ctd_shift; /* Add timeline shift difference between src and dst to each media_time.
1666 * Therefore, call this function as later as possible. */
1667 lsmash_entry_t *src_entry;
1668 lsmash_file_t *src_file = src->file;
1669 isom_trak_t *src_trak = isom_get_trak( src_file, src_track_ID );
1670 if( !src_trak
1671 || !src_trak->edts
1672 || !src_trak->edts->elst
1673 || !src_trak->edts->elst->list )
1675 /* Get from timeline instead of boxes. */
1676 isom_timeline_t *src_timeline = isom_get_timeline( src, src_track_ID );
1677 if( !src_timeline
1678 || src_timeline->movie_timescale == 0
1679 || src_timeline->media_timescale == 0
1680 || !src_timeline->edit_list )
1681 return -1;
1682 src_movie_timescale = src_timeline->movie_timescale;
1683 src_media_timescale = src_timeline->media_timescale;
1684 src_track_duration = src_timeline->track_duration;
1685 src_media_duration = src_timeline->media_duration;
1686 src_ctd_shift = src_timeline->ctd_shift;
1687 src_entry = src_timeline->edit_list->head;
1689 else
1691 if( !src_file->moov
1692 || !src_file->moov->mvhd
1693 || src_file->moov->mvhd->timescale == 0
1694 || !src_trak->tkhd
1695 || !src_trak->mdia
1696 || !src_trak->mdia->mdhd
1697 || src_trak->mdia->mdhd->timescale == 0
1698 || !src_trak->mdia->minf
1699 || !src_trak->mdia->minf->stbl )
1700 return -1;
1701 src_movie_timescale = src_file->moov->mvhd->timescale;
1702 src_media_timescale = src_trak->mdia->mdhd->timescale;
1703 src_track_duration = src_trak->tkhd->duration;
1704 src_media_duration = src_trak->mdia->mdhd->duration;
1705 src_ctd_shift = src_trak->mdia->minf->stbl->cslg ? src_trak->mdia->minf->stbl->cslg->compositionToDTSShift : 0;
1706 src_entry = src_trak->edts->elst->list->head;
1708 if( !src_entry )
1709 return 0;
1710 /* Generate edit list if absent in destination. */
1711 if( (!dst_trak->edts && !isom_add_edts( dst_trak ))
1712 || (!dst_trak->edts->elst && !isom_add_elst( dst_trak->edts )) )
1713 return -1;
1714 uint32_t dst_movie_timescale = dst_file->moov->mvhd->timescale;
1715 uint32_t dst_media_timescale = dst_trak->mdia->mdhd->timescale;
1716 int32_t dst_ctd_shift = dst_trak->mdia->minf->stbl->cslg ? dst_trak->mdia->minf->stbl->cslg->compositionToDTSShift : 0;
1717 int32_t media_time_shift = src_ctd_shift - dst_ctd_shift;
1718 lsmash_entry_list_t *dst_list = dst_trak->edts->elst->list;
1719 lsmash_entry_t *src_head = src_entry;
1720 while( src_entry )
1722 isom_elst_entry_t *src_data = (isom_elst_entry_t *)src_entry->data;
1723 if( !src_data )
1724 return -1;
1725 uint64_t segment_duration;
1726 if( src_data->segment_duration == 0 && !dst_file->fragment )
1728 /* The 0-duration edit makes no sence for non-fragmented movie file. */
1729 if( src_entry == src_head )
1730 /* Set an appropriate duration from the source track. */
1731 segment_duration = src_track_duration
1732 ? src_track_duration
1733 : src_media_duration * ((double)src_movie_timescale / src_media_timescale);
1734 else
1735 /* Two or more 0-duration edits make no sence. Just skip them. */
1736 continue;
1738 else
1739 segment_duration = src_data->segment_duration;
1740 isom_elst_entry_t *dst_data = (isom_elst_entry_t *)lsmash_malloc( sizeof(isom_elst_entry_t) );
1741 if( !dst_data )
1742 return -1;
1743 dst_data->segment_duration = segment_duration * ((double)dst_movie_timescale / src_movie_timescale) + 0.5;
1744 dst_data->media_rate = src_data->media_rate;
1745 if( src_data->media_time != ISOM_EDIT_MODE_EMPTY )
1746 dst_data->media_time = (src_data->media_time + media_time_shift) * ((double)dst_media_timescale / src_media_timescale) + 0.5;
1747 else
1748 dst_data->media_time = ISOM_EDIT_MODE_EMPTY;
1749 if( lsmash_add_entry( dst_list, dst_data ) < 0 )
1751 lsmash_free( dst_data );
1752 return -1;
1754 src_entry = src_entry->next;
1756 return 0;
1759 int lsmash_set_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list )
1761 if( !root || !root->file || !ts_list )
1762 return -1;
1763 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1764 if( !timeline )
1765 return -1;
1766 if( timeline->info_list->entry_count == 0 )
1768 lsmash_log( timeline, LSMASH_LOG_ERROR, "Changing timestamps of LPCM track is not supported.\n" );
1769 return -1;
1771 if( ts_list->sample_count != timeline->info_list->entry_count )
1772 return -1; /* Number of samples must be same. */
1773 lsmash_media_ts_t *ts = ts_list->timestamp;
1774 if( ts[0].dts )
1775 return -1; /* DTS must start from value zero. */
1776 /* Update DTSs. */
1777 uint32_t sample_count = ts_list->sample_count;
1778 uint32_t i;
1779 if( timeline->info_list->entry_count > 1 )
1781 i = 1;
1782 lsmash_entry_t *entry = timeline->info_list->head;
1783 isom_sample_info_t *info;
1784 while( i < sample_count )
1786 info = (isom_sample_info_t *)entry->data;
1787 if( !info || (ts[i].dts < ts[i - 1].dts) )
1788 return -1;
1789 info->duration = ts[i].dts - ts[i - 1].dts;
1790 entry = entry->next;
1791 ++i;
1793 if( i > 1 )
1795 if( !entry
1796 || !entry->data )
1797 return -1;
1798 /* Copy the previous duration. */
1799 ((isom_sample_info_t *)entry->data)->duration = info->duration;
1801 else
1802 return -1; /* Irregular case: sample_count this timeline has is incorrect. */
1804 else /* still image */
1805 ((isom_sample_info_t *)timeline->info_list->head->data)->duration = UINT32_MAX;
1806 /* Update CTSs.
1807 * ToDo: hint track must not have any sample_offset. */
1808 i = 0;
1809 timeline->ctd_shift = 0;
1810 for( lsmash_entry_t *entry = timeline->info_list->head; entry; entry = entry->next )
1812 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1813 if( (ts[i].cts + timeline->ctd_shift) < ts[i].dts )
1814 timeline->ctd_shift = ts[i].dts - ts[i].cts;
1815 info->offset = ts[i].cts - ts[i].dts;
1816 ++i;
1818 if( timeline->ctd_shift && (!root->file->qt_compatible || root->file->max_isom_version < 4) )
1819 return -1; /* Don't allow composition to decode timeline shift. */
1820 return 0;
1823 int lsmash_get_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list )
1825 if( !ts_list )
1826 return -1;
1827 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1828 if( !timeline )
1829 return -1;
1830 uint32_t sample_count = timeline->info_list->entry_count;
1831 if( !sample_count )
1833 ts_list->sample_count = 0;
1834 ts_list->timestamp = NULL;
1835 return 0;
1837 lsmash_media_ts_t *ts = lsmash_malloc( sample_count * sizeof(lsmash_media_ts_t) );
1838 if( !ts )
1839 return -1;
1840 uint64_t dts = 0;
1841 uint32_t i = 0;
1842 if( timeline->info_list->entry_count )
1843 for( lsmash_entry_t *entry = timeline->info_list->head; entry; entry = entry->next )
1845 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1846 if( !info )
1848 lsmash_free( ts );
1849 return -1;
1851 ts[i].dts = dts;
1852 ts[i].cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset);
1853 dts += info->duration;
1854 ++i;
1856 else
1857 for( lsmash_entry_t *entry = timeline->bunch_list->head; entry; entry = entry->next )
1859 isom_lpcm_bunch_t *bunch = (isom_lpcm_bunch_t *)entry->data;
1860 if( !bunch )
1862 lsmash_free( ts );
1863 return -1;
1865 for( uint32_t j = 0; j < bunch->sample_count; j++ )
1867 ts[i].dts = dts;
1868 ts[i].cts = timeline->ctd_shift ? (dts + (int32_t)bunch->offset) : (dts + bunch->offset);
1869 dts += bunch->duration;
1870 ++i;
1873 ts_list->sample_count = sample_count;
1874 ts_list->timestamp = ts;
1875 return 0;
1878 void lsmash_delete_media_timestamps( lsmash_media_ts_list_t *ts_list )
1880 if( !ts_list )
1881 return;
1882 if( ts_list->timestamp )
1884 lsmash_free( ts_list->timestamp );
1885 ts_list->timestamp = NULL;
1887 ts_list->sample_count = 0;
1890 static int isom_compare_dts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b )
1892 int64_t diff = (int64_t)(a->dts - b->dts);
1893 return diff > 0 ? 1 : (diff == 0 ? 0 : -1);
1896 void lsmash_sort_timestamps_decoding_order( lsmash_media_ts_list_t *ts_list )
1898 if( !ts_list )
1899 return;
1900 qsort( ts_list->timestamp, ts_list->sample_count, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))isom_compare_dts );
1903 static int isom_compare_cts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b )
1905 int64_t diff = (int64_t)(a->cts - b->cts);
1906 return diff > 0 ? 1 : (diff == 0 ? 0 : -1);
1909 void lsmash_sort_timestamps_composition_order( lsmash_media_ts_list_t *ts_list )
1911 if( !ts_list )
1912 return;
1913 qsort( ts_list->timestamp, ts_list->sample_count, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))isom_compare_cts );
1916 int lsmash_get_max_sample_delay( lsmash_media_ts_list_t *ts_list, uint32_t *max_sample_delay )
1918 if( !ts_list || !max_sample_delay )
1919 return -1;
1920 lsmash_media_ts_t *orig_ts = ts_list->timestamp;
1921 lsmash_media_ts_t *ts = lsmash_malloc( ts_list->sample_count * sizeof(lsmash_media_ts_t) );
1922 if( !ts )
1923 return -1;
1924 ts_list->timestamp = ts;
1925 *max_sample_delay = 0;
1926 for( uint32_t i = 0; i < ts_list->sample_count; i++ )
1928 ts[i].cts = orig_ts[i].cts; /* for sorting */
1929 ts[i].dts = i;
1931 lsmash_sort_timestamps_composition_order( ts_list );
1932 for( uint32_t i = 0; i < ts_list->sample_count; i++ )
1933 if( i < ts[i].dts )
1935 uint32_t sample_delay = ts[i].dts - i;
1936 *max_sample_delay = LSMASH_MAX( *max_sample_delay, sample_delay );
1938 lsmash_free( ts );
1939 ts_list->timestamp = orig_ts;
1940 return 0;
1943 #endif /* LSMASH_DEMUXER_ENABLED */