list: Decide the entry eliminator of list at its initialization.
[L-SMASH.git] / core / timeline.c
blobb64b75591dea510f43c3406296fd2d1f1ad4652a
1 /*****************************************************************************
2 * timeline.c
3 *****************************************************************************
4 * Copyright (C) 2011-2017 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 #include "common/internal.h" /* must be placed first */
25 #include <stdlib.h>
26 #include <string.h>
27 #include <inttypes.h>
29 #include "box.h"
30 #include "timeline.h"
32 #include "codecs/mp4a.h"
33 #include "codecs/mp4sys.h"
34 #include "codecs/description.h"
36 #include "importer/importer.h"
38 #define NO_RANDOM_ACCESS_POINT 0xffffffff
40 typedef struct
42 uint64_t pos;
43 uint32_t duration;
44 uint32_t offset;
45 uint32_t length;
46 uint32_t index;
47 isom_portable_chunk_t *chunk;
48 lsmash_sample_property_t prop;
49 } isom_sample_info_t;
51 static const lsmash_class_t lsmash_timeline_class =
53 "timeline"
56 struct isom_timeline_tag
58 const lsmash_class_t *class;
59 uint32_t track_ID;
60 uint32_t movie_timescale;
61 uint32_t media_timescale;
62 uint32_t sample_count;
63 uint32_t max_sample_size;
64 uint32_t ctd_shift; /* shift from composition to decode timeline */
65 uint64_t media_duration;
66 uint64_t track_duration;
67 uint32_t last_accessed_sample_number;
68 uint64_t last_accessed_sample_dts;
69 uint32_t last_accessed_lpcm_bunch_number;
70 uint32_t last_accessed_lpcm_bunch_duration;
71 uint32_t last_accessed_lpcm_bunch_sample_count;
72 uint32_t last_accessed_lpcm_bunch_first_sample_number;
73 uint64_t last_accessed_lpcm_bunch_dts;
74 lsmash_entry_list_t edit_list [1]; /* list of edits */
75 lsmash_entry_list_t chunk_list[1]; /* list of chunks */
76 lsmash_entry_list_t info_list [1]; /* list of sample info */
77 lsmash_entry_list_t bunch_list[1]; /* list of LPCM bunch */
78 int (*get_dts)( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts );
79 int (*get_cts)( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts );
80 int (*get_sample_duration)( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration );
81 lsmash_sample_t *(*get_sample)( isom_timeline_t *timeline, uint32_t sample_number );
82 int (*get_sample_info)( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample );
83 int (*get_sample_property)( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop );
84 int (*check_sample_existence)( isom_timeline_t *timeline, uint32_t sample_number );
87 isom_timeline_t *isom_get_timeline( lsmash_root_t *root, uint32_t track_ID )
89 if( isom_check_initializer_present( root ) < 0
90 || track_ID == 0
91 || !root->file->timeline )
92 return NULL;
93 for( lsmash_entry_t *entry = root->file->timeline->head; entry; entry = entry->next )
95 isom_timeline_t *timeline = (isom_timeline_t *)entry->data;
96 if( !timeline )
97 return NULL;
98 if( timeline->track_ID == track_ID )
99 return timeline;
101 return NULL;
104 isom_timeline_t *isom_timeline_create( void )
106 isom_timeline_t *timeline = lsmash_malloc_zero( sizeof(isom_timeline_t) );
107 if( !timeline )
108 return NULL;
109 timeline->class = &lsmash_timeline_class;
110 lsmash_list_init_simple( timeline->edit_list );
111 lsmash_list_init_simple( timeline->chunk_list );
112 lsmash_list_init_simple( timeline->info_list );
113 lsmash_list_init_simple( timeline->bunch_list );
114 return timeline;
117 void isom_timeline_destroy( isom_timeline_t *timeline )
119 if( !timeline )
120 return;
121 lsmash_list_remove_entries( timeline->edit_list );
122 lsmash_list_remove_entries( timeline->chunk_list ); /* chunk data must be already freed. */
123 lsmash_list_remove_entries( timeline->info_list );
124 lsmash_list_remove_entries( timeline->bunch_list );
125 lsmash_free( timeline );
128 void isom_remove_timelines( lsmash_file_t *file )
130 if( LSMASH_IS_NON_EXISTING_BOX( file ) || !file->timeline )
131 return;
132 lsmash_list_destroy( file->timeline );
135 void lsmash_destruct_timeline( lsmash_root_t *root, uint32_t track_ID )
137 if( LSMASH_IS_NON_EXISTING_BOX( root )
138 || track_ID == 0
139 || !root->file->timeline )
140 return;
141 for( lsmash_entry_t *entry = root->file->timeline->head; entry; entry = entry->next )
143 isom_timeline_t *timeline = (isom_timeline_t *)entry->data;
144 if( !timeline )
145 continue;
146 if( timeline->track_ID == track_ID )
148 lsmash_list_remove_entry_direct( root->file->timeline, entry );
149 break;
154 int isom_timeline_set_track_ID
156 isom_timeline_t *timeline,
157 uint32_t track_ID
160 if( !timeline || track_ID == 0 )
161 return LSMASH_ERR_FUNCTION_PARAM;
162 timeline->track_ID = track_ID;
163 return 0;
166 int isom_timeline_set_movie_timescale
168 isom_timeline_t *timeline,
169 uint32_t movie_timescale
172 if( !timeline || movie_timescale == 0 )
173 return LSMASH_ERR_FUNCTION_PARAM;
174 timeline->movie_timescale = movie_timescale;
175 return 0;
178 int isom_timeline_set_media_timescale
180 isom_timeline_t *timeline,
181 uint32_t media_timescale
184 if( !timeline || media_timescale == 0 )
185 return LSMASH_ERR_FUNCTION_PARAM;
186 timeline->media_timescale = media_timescale;
187 return 0;
190 int isom_timeline_set_sample_count
192 isom_timeline_t *timeline,
193 uint32_t sample_count
196 if( !timeline || sample_count == 0 )
197 return LSMASH_ERR_FUNCTION_PARAM;
198 timeline->sample_count = sample_count;
199 return 0;
202 int isom_timeline_set_max_sample_size
204 isom_timeline_t *timeline,
205 uint32_t max_sample_size
208 if( !timeline || max_sample_size == 0 )
209 return LSMASH_ERR_FUNCTION_PARAM;
210 timeline->max_sample_size = max_sample_size;
211 return 0;
214 int isom_timeline_set_media_duration
216 isom_timeline_t *timeline,
217 uint32_t media_duration
220 if( !timeline || media_duration == 0 )
221 return LSMASH_ERR_FUNCTION_PARAM;
222 timeline->media_duration = media_duration;
223 return 0;
226 int isom_timeline_set_track_duration
228 isom_timeline_t *timeline,
229 uint32_t track_duration
232 if( !timeline || track_duration == 0 )
233 return LSMASH_ERR_FUNCTION_PARAM;
234 timeline->track_duration = track_duration;
235 return 0;
238 static void isom_get_qt_fixed_comp_audio_sample_quants
240 isom_timeline_t *timeline,
241 isom_sample_entry_t *description,
242 uint32_t *samples_per_packet,
243 uint32_t *constant_sample_size
246 isom_audio_entry_t *audio = (isom_audio_entry_t *)description;
247 if( audio->version == 0 )
249 uint32_t dummy;
250 if( !isom_get_implicit_qt_fixed_comp_audio_sample_quants( audio, samples_per_packet, constant_sample_size, &dummy ) )
252 /* LPCM */
253 if( !isom_is_lpcm_audio( audio ) )
254 lsmash_log( timeline, LSMASH_LOG_WARNING, "unsupported implicit sample table!\n" );
255 *samples_per_packet = 1;
256 *constant_sample_size = (audio->samplesize * audio->channelcount) / 8;
259 else if( audio->version == 1 )
261 *samples_per_packet = audio->samplesPerPacket;
262 *constant_sample_size = audio->bytesPerFrame;
264 else /* if( audio->version == 2 ) */
266 *samples_per_packet = audio->constLPCMFramesPerAudioPacket;
267 *constant_sample_size = audio->constBytesPerAudioPacket;
271 static int isom_is_qt_fixed_compressed_audio
273 isom_sample_entry_t *description
276 if( (description->manager & LSMASH_VIDEO_DESCRIPTION) || !isom_is_qt_audio( description->type ) )
277 return 0;
278 /* LPCM is a special case of fixed compression. */
279 return (((isom_audio_entry_t *)description)->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION);
282 static int isom_add_sample_info_entry( isom_timeline_t *timeline, isom_sample_info_t *src_info )
284 isom_sample_info_t *dst_info = lsmash_malloc( sizeof(isom_sample_info_t) );
285 if( !dst_info )
286 return LSMASH_ERR_MEMORY_ALLOC;
287 if( lsmash_list_add_entry( timeline->info_list, dst_info ) < 0 )
289 lsmash_free( dst_info );
290 return LSMASH_ERR_MEMORY_ALLOC;
292 *dst_info = *src_info;
293 return 0;
296 int isom_add_lpcm_bunch_entry( isom_timeline_t *timeline, isom_lpcm_bunch_t *src_bunch )
298 isom_lpcm_bunch_t *dst_bunch = lsmash_malloc( sizeof(isom_lpcm_bunch_t) );
299 if( !dst_bunch )
300 return LSMASH_ERR_MEMORY_ALLOC;
301 if( lsmash_list_add_entry( timeline->bunch_list, dst_bunch ) < 0 )
303 lsmash_free( dst_bunch );
304 return LSMASH_ERR_MEMORY_ALLOC;
306 *dst_bunch = *src_bunch;
307 return 0;
310 static int isom_add_portable_chunk_entry( isom_timeline_t *timeline, isom_portable_chunk_t *src_chunk )
312 isom_portable_chunk_t *dst_chunk = lsmash_malloc( sizeof(isom_portable_chunk_t) );
313 if( !dst_chunk )
314 return LSMASH_ERR_MEMORY_ALLOC;
315 if( lsmash_list_add_entry( timeline->chunk_list, dst_chunk ) < 0 )
317 lsmash_free( dst_chunk );
318 return LSMASH_ERR_MEMORY_ALLOC;
320 *dst_chunk = *src_chunk;
321 return 0;
324 static int isom_compare_lpcm_sample_info( isom_lpcm_bunch_t *bunch, isom_sample_info_t *info )
326 return info->duration != bunch->duration
327 || info->offset != bunch->offset
328 || info->length != bunch->length
329 || info->index != bunch->index
330 || info->chunk != bunch->chunk;
333 static void isom_update_bunch( isom_lpcm_bunch_t *bunch, isom_sample_info_t *info )
335 bunch->pos = info->pos;
336 bunch->duration = info->duration;
337 bunch->offset = info->offset;
338 bunch->length = info->length;
339 bunch->index = info->index;
340 bunch->chunk = info->chunk;
341 bunch->prop = info->prop;
342 bunch->sample_count = 1;
345 static isom_lpcm_bunch_t *isom_get_bunch( isom_timeline_t *timeline, uint32_t sample_number )
347 if( sample_number >= timeline->last_accessed_lpcm_bunch_first_sample_number
348 && sample_number < timeline->last_accessed_lpcm_bunch_first_sample_number + timeline->last_accessed_lpcm_bunch_sample_count )
349 /* Get from the last accessed LPCM bunch. */
350 return (isom_lpcm_bunch_t *)lsmash_list_get_entry_data( timeline->bunch_list, timeline->last_accessed_lpcm_bunch_number );
351 uint32_t first_sample_number_in_next_bunch;
352 uint32_t bunch_number = 1;
353 uint64_t bunch_dts;
354 if( timeline->last_accessed_lpcm_bunch_first_sample_number
355 && timeline->last_accessed_lpcm_bunch_first_sample_number <= sample_number )
357 first_sample_number_in_next_bunch = timeline->last_accessed_lpcm_bunch_first_sample_number + timeline->last_accessed_lpcm_bunch_sample_count;
358 bunch_number += timeline->last_accessed_lpcm_bunch_number;
359 bunch_dts = timeline->last_accessed_lpcm_bunch_dts
360 + timeline->last_accessed_lpcm_bunch_duration * timeline->last_accessed_lpcm_bunch_sample_count;
362 else
364 /* Seek from the first LPCM bunch. */
365 first_sample_number_in_next_bunch = 1;
366 bunch_dts = 0;
368 isom_lpcm_bunch_t *bunch = (isom_lpcm_bunch_t *)lsmash_list_get_entry_data( timeline->bunch_list, bunch_number++ );
369 if( !bunch )
370 return NULL;
371 first_sample_number_in_next_bunch += bunch->sample_count;
372 while( sample_number >= first_sample_number_in_next_bunch )
374 bunch_dts += bunch->duration * bunch->sample_count;
375 bunch = (isom_lpcm_bunch_t *)lsmash_list_get_entry_data( timeline->bunch_list, bunch_number++ );
376 if( !bunch )
377 return NULL;
378 first_sample_number_in_next_bunch += bunch->sample_count;
380 timeline->last_accessed_lpcm_bunch_dts = bunch_dts;
381 timeline->last_accessed_lpcm_bunch_number = bunch_number - 1;
382 timeline->last_accessed_lpcm_bunch_duration = bunch->duration;
383 timeline->last_accessed_lpcm_bunch_sample_count = bunch->sample_count;
384 timeline->last_accessed_lpcm_bunch_first_sample_number = first_sample_number_in_next_bunch - bunch->sample_count;
385 return bunch;
388 static int isom_get_dts_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts )
390 if( sample_number == timeline->last_accessed_sample_number )
391 *dts = timeline->last_accessed_sample_dts;
392 else if( sample_number == 1 )
393 *dts = 0;
394 else if( sample_number == timeline->last_accessed_sample_number + 1 )
396 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_list_get_entry_data( timeline->info_list, timeline->last_accessed_sample_number );
397 if( !info )
398 return LSMASH_ERR_NAMELESS;
399 *dts = timeline->last_accessed_sample_dts + info->duration;
401 else if( sample_number == timeline->last_accessed_sample_number - 1 )
403 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_list_get_entry_data( timeline->info_list, timeline->last_accessed_sample_number - 1 );
404 if( !info )
405 return LSMASH_ERR_NAMELESS;
406 *dts = timeline->last_accessed_sample_dts - info->duration;
408 else
410 *dts = 0;
411 uint32_t distance = sample_number - 1;
412 lsmash_entry_t *entry;
413 for( entry = timeline->info_list->head; entry; entry = entry->next )
415 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
416 if( !info )
417 return LSMASH_ERR_NAMELESS;
418 if( distance-- == 0 )
419 break;
420 *dts += info->duration;
422 if( !entry )
423 return LSMASH_ERR_NAMELESS;
425 /* Note: last_accessed_sample_number is always updated together with last_accessed_sample_dts, and vice versa. */
426 timeline->last_accessed_sample_dts = *dts;
427 timeline->last_accessed_sample_number = sample_number;
428 return 0;
431 static int isom_get_cts_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts )
433 int ret = isom_get_dts_from_info_list( timeline, sample_number, cts );
434 if( ret < 0 )
435 return ret;
436 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_list_get_entry_data( timeline->info_list, sample_number );
437 if( !info )
438 return LSMASH_ERR_NAMELESS;
439 *cts = isom_make_cts( *cts, info->offset, timeline->ctd_shift );
440 return 0;
443 static int isom_get_dts_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts )
445 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
446 if( !bunch )
447 return LSMASH_ERR_NAMELESS;
448 *dts = timeline->last_accessed_lpcm_bunch_dts + (sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number) * bunch->duration;
449 return 0;
452 static int isom_get_cts_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts )
454 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
455 if( !bunch )
456 return LSMASH_ERR_NAMELESS;
457 *cts = timeline->last_accessed_lpcm_bunch_dts + (sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number) * bunch->duration + bunch->offset;
458 return 0;
461 static int isom_get_sample_duration_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration )
463 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_list_get_entry_data( timeline->info_list, sample_number );
464 if( !info )
465 return LSMASH_ERR_NAMELESS;
466 *sample_duration = info->duration;
467 return 0;
470 static int isom_get_sample_duration_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration )
472 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
473 if( !bunch )
474 return LSMASH_ERR_NAMELESS;
475 *sample_duration = bunch->duration;
476 return 0;
479 static int isom_check_sample_existence_in_info_list( isom_timeline_t *timeline, uint32_t sample_number )
481 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_list_get_entry_data( timeline->info_list, sample_number );
482 if( !info || !info->chunk )
483 return 0;
484 return !!info->chunk->file;
487 static int isom_check_sample_existence_in_bunch_list( isom_timeline_t *timeline, uint32_t sample_number )
489 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
490 if( !bunch || !bunch->chunk )
491 return 0;
492 return !!bunch->chunk->file;
495 static lsmash_sample_t *isom_read_sample_data_from_stream
497 lsmash_file_t *file,
498 isom_timeline_t *timeline,
499 uint32_t sample_length,
500 uint64_t sample_pos
503 if( !file )
504 return NULL;
505 lsmash_sample_t *sample = lsmash_create_sample( 0 );
506 if( !sample )
507 return NULL;
508 lsmash_bs_t *bs = file->bs;
509 lsmash_bs_read_seek( bs, sample_pos, SEEK_SET );
510 sample->data = lsmash_bs_get_bytes( bs, sample_length );
511 if( !sample->data )
513 lsmash_delete_sample( sample );
514 return NULL;
516 return sample;
519 static lsmash_sample_t *isom_get_lpcm_sample_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number )
521 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
522 if( !bunch
523 || !bunch->chunk )
524 return NULL;
525 /* Get data of a sample from the stream. */
526 uint64_t sample_number_offset = sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number;
527 uint64_t sample_pos = bunch->pos + sample_number_offset * bunch->length;
528 lsmash_sample_t *sample = isom_read_sample_data_from_stream( bunch->chunk->file, timeline, bunch->length, sample_pos );
529 if( !sample )
530 return NULL;
531 /* Get sample info. */
532 sample->dts = timeline->last_accessed_lpcm_bunch_dts + sample_number_offset * bunch->duration;
533 sample->cts = isom_make_cts( sample->dts, bunch->offset, timeline->ctd_shift );
534 sample->pos = sample_pos;
535 sample->length = bunch->length;
536 sample->index = bunch->index;
537 sample->prop = bunch->prop;
538 return sample;
541 static lsmash_sample_t *isom_get_sample_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number )
543 uint64_t dts;
544 if( isom_get_dts_from_info_list( timeline, sample_number, &dts ) < 0 )
545 return NULL;
546 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_list_get_entry_data( timeline->info_list, sample_number );
547 if( !info
548 || !info->chunk )
549 return NULL;
550 /* Get data of a sample from the stream. */
551 lsmash_sample_t *sample = isom_read_sample_data_from_stream( info->chunk->file, timeline, info->length, info->pos );
552 if( !sample )
553 return NULL;
554 /* Get sample info. */
555 sample->dts = dts;
556 sample->cts = isom_make_cts( dts, info->offset, timeline->ctd_shift );
557 sample->pos = info->pos;
558 sample->length = info->length;
559 sample->index = info->index;
560 sample->prop = info->prop;
561 return sample;
564 static int isom_get_lpcm_sample_info_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample )
566 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
567 if( !bunch )
568 return LSMASH_ERR_NAMELESS;
569 uint64_t sample_number_offset = sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number;
570 sample->dts = timeline->last_accessed_lpcm_bunch_dts + sample_number_offset * bunch->duration;
571 sample->cts = isom_make_cts( sample->dts, bunch->offset, timeline->ctd_shift );
572 sample->pos = bunch->pos + sample_number_offset * bunch->length;
573 sample->length = bunch->length;
574 sample->index = bunch->index;
575 sample->prop = bunch->prop;
576 return 0;
579 static int isom_get_sample_info_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample )
581 uint64_t dts;
582 int ret = isom_get_dts_from_info_list( timeline, sample_number, &dts );
583 if( ret < 0 )
584 return ret;
585 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_list_get_entry_data( timeline->info_list, sample_number );
586 if( !info )
587 return LSMASH_ERR_NAMELESS;
588 sample->dts = dts;
589 sample->cts = isom_make_cts( dts, info->offset, timeline->ctd_shift );
590 sample->pos = info->pos;
591 sample->length = info->length;
592 sample->index = info->index;
593 sample->prop = info->prop;
594 return 0;
597 static int isom_get_lpcm_sample_property_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop )
599 memset( prop, 0, sizeof(lsmash_sample_property_t) );
600 prop->ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
601 return 0;
604 static int isom_get_sample_property_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop )
606 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_list_get_entry_data( timeline->info_list, sample_number );
607 if( !info )
608 return LSMASH_ERR_NAMELESS;
609 *prop = info->prop;
610 return 0;
613 static void isom_timeline_set_sample_getter_funcs
615 isom_timeline_t *timeline
618 timeline->get_dts = isom_get_dts_from_info_list;
619 timeline->get_cts = isom_get_cts_from_info_list;
620 timeline->get_sample_duration = isom_get_sample_duration_from_info_list;
621 timeline->check_sample_existence = isom_check_sample_existence_in_info_list;
622 timeline->get_sample = isom_get_sample_from_media_timeline;
623 timeline->get_sample_info = isom_get_sample_info_from_media_timeline;
624 timeline->get_sample_property = isom_get_sample_property_from_media_timeline;
627 void isom_timeline_set_lpcm_sample_getter_funcs
629 isom_timeline_t *timeline
632 timeline->get_dts = isom_get_dts_from_bunch_list;
633 timeline->get_cts = isom_get_cts_from_bunch_list;
634 timeline->get_sample_duration = isom_get_sample_duration_from_bunch_list;
635 timeline->check_sample_existence = isom_check_sample_existence_in_bunch_list;
636 timeline->get_sample = isom_get_lpcm_sample_from_media_timeline;
637 timeline->get_sample_info = isom_get_lpcm_sample_info_from_media_timeline;
638 timeline->get_sample_property = isom_get_lpcm_sample_property_from_media_timeline;
641 static inline void isom_increment_sample_number_in_entry
643 uint32_t *sample_number_in_entry,
644 lsmash_entry_t **entry,
645 uint32_t sample_count
648 if( *sample_number_in_entry == sample_count )
650 *sample_number_in_entry = 1;
651 *entry = (*entry)->next;
653 else
654 *sample_number_in_entry += 1;
657 static inline isom_sgpd_t *isom_select_appropriate_sgpd
659 isom_sgpd_t *sgpd,
660 isom_sgpd_t *sgpd_frag,
661 uint32_t *group_description_index
664 if( LSMASH_IS_EXISTING_BOX( sgpd_frag ) && *group_description_index >= 0x10000 )
666 /* The specification doesn't define 0x10000 explicitly, however says that there must be fewer than
667 * 65536 group definitions for this track and grouping type in the sample table in the Movie Box.
668 * So, we assume 0x10000 is equivalent to 0. */
669 *group_description_index -= 0x10000;
670 return sgpd_frag;
672 else
673 return sgpd;
676 static int isom_get_roll_recovery_grouping_info
678 isom_timeline_t *timeline,
679 lsmash_entry_t **sbgp_roll_entry,
680 isom_sgpd_t *sgpd_roll,
681 isom_sgpd_t *sgpd_frag_roll,
682 uint32_t *sample_number_in_sbgp_roll_entry,
683 isom_sample_info_t *info,
684 uint32_t sample_number
687 isom_group_assignment_entry_t *assignment = (isom_group_assignment_entry_t *)(*sbgp_roll_entry)->data;
688 if( !assignment )
689 return LSMASH_ERR_NAMELESS;
690 if( assignment->group_description_index )
692 uint32_t group_description_index = assignment->group_description_index;
693 isom_sgpd_t *sgpd = isom_select_appropriate_sgpd( sgpd_roll, sgpd_frag_roll, &group_description_index );
694 isom_roll_entry_t *roll_data = (isom_roll_entry_t *)lsmash_list_get_entry_data( sgpd->list, group_description_index );
695 if( roll_data )
697 if( roll_data->roll_distance > 0 )
699 /* post-roll */
700 info->prop.post_roll.complete = sample_number + roll_data->roll_distance;
701 if( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
702 info->prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_POST_ROLL_START;
704 else if( roll_data->roll_distance < 0 )
706 /* pre-roll */
707 info->prop.pre_roll.distance = -roll_data->roll_distance;
708 if( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
709 info->prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_PRE_ROLL_END;
712 else if( *sample_number_in_sbgp_roll_entry == 1 && group_description_index )
713 lsmash_log( timeline, LSMASH_LOG_WARNING, "a description of roll recoveries is not found in the Sample Group Description Box.\n" );
715 isom_increment_sample_number_in_entry( sample_number_in_sbgp_roll_entry, sbgp_roll_entry, assignment->sample_count );
716 return 0;
719 static int isom_get_random_access_point_grouping_info
721 isom_timeline_t *timeline,
722 lsmash_entry_t **sbgp_rap_entry,
723 isom_sgpd_t *sgpd_rap,
724 isom_sgpd_t *sgpd_frag_rap,
725 uint32_t *sample_number_in_sbgp_rap_entry,
726 isom_sample_info_t *info,
727 uint32_t *distance
730 isom_group_assignment_entry_t *assignment = (isom_group_assignment_entry_t *)(*sbgp_rap_entry)->data;
731 if( !assignment )
732 return LSMASH_ERR_NAMELESS;
733 if( assignment->group_description_index && (info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE) )
735 uint32_t group_description_index = assignment->group_description_index;
736 isom_sgpd_t *sgpd = isom_select_appropriate_sgpd( sgpd_rap, sgpd_frag_rap, &group_description_index );
737 isom_rap_entry_t *rap_data = (isom_rap_entry_t *)lsmash_list_get_entry_data( sgpd->list, group_description_index );
738 if( rap_data )
740 /* If this is not an open RAP, we treat it as an unknown RAP since non-IDR sample could make a closed GOP. */
741 info->prop.ra_flags |= (rap_data->num_leading_samples_known && !!rap_data->num_leading_samples)
742 ? ISOM_SAMPLE_RANDOM_ACCESS_FLAG_OPEN_RAP
743 : ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP;
744 *distance = 0;
746 else if( *sample_number_in_sbgp_rap_entry == 1 && group_description_index )
747 lsmash_log( timeline, LSMASH_LOG_WARNING, "a description of random access points is not found in the Sample Group Description Box.\n" );
749 isom_increment_sample_number_in_entry( sample_number_in_sbgp_rap_entry, sbgp_rap_entry, assignment->sample_count );
750 return 0;
753 int isom_timeline_construct( lsmash_root_t *root, uint32_t track_ID )
755 if( isom_check_initializer_present( root ) < 0 )
756 return LSMASH_ERR_FUNCTION_PARAM;
757 lsmash_file_t *file = root->file;
758 if( LSMASH_IS_NON_EXISTING_BOX( file->moov->mvhd )
759 || file->moov->mvhd->timescale == 0 )
760 return LSMASH_ERR_INVALID_DATA;
761 /* Get track by track_ID. */
762 isom_trak_t *trak = isom_get_trak( file, track_ID );
763 if( LSMASH_IS_NON_EXISTING_BOX( trak->tkhd )
764 || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->mdhd )
765 || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl->stco )
766 || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl->stsd )
767 || (LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl->stsz ) && LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl->stz2 ))
768 || trak->mdia->mdhd->timescale == 0 )
769 return LSMASH_ERR_INVALID_DATA;
770 /* Create a timeline list if it doesn't exist. */
771 if( !file->timeline )
773 file->timeline = lsmash_list_create( isom_timeline_destroy );
774 if( !file->timeline )
775 return LSMASH_ERR_MEMORY_ALLOC;
777 /* Create a timeline. */
778 isom_timeline_t *timeline = isom_timeline_create();
779 if( !timeline )
780 return LSMASH_ERR_MEMORY_ALLOC;
781 timeline->track_ID = track_ID;
782 timeline->movie_timescale = file->moov->mvhd->timescale;
783 timeline->media_timescale = trak->mdia->mdhd->timescale;
784 timeline->track_duration = trak->tkhd->duration;
785 /* Preparation for construction. */
786 isom_elst_t *elst = trak->edts->elst;
787 isom_minf_t *minf = trak->mdia->minf;
788 isom_dref_t *dref = minf->dinf->dref;
789 isom_stbl_t *stbl = minf->stbl;
790 isom_stsd_t *stsd = stbl->stsd;
791 isom_stts_t *stts = stbl->stts;
792 isom_ctts_t *ctts = stbl->ctts;
793 isom_stss_t *stss = stbl->stss;
794 isom_stps_t *stps = stbl->stps;
795 isom_sdtp_t *sdtp = stbl->sdtp;
796 isom_stsc_t *stsc = stbl->stsc;
797 isom_stsz_t *stsz = stbl->stsz;
798 isom_stz2_t *stz2 = stbl->stz2;
799 isom_stco_t *stco = stbl->stco;
800 isom_sgpd_t *sgpd_rap = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
801 isom_sbgp_t *sbgp_rap = isom_get_sample_to_group ( stbl, ISOM_GROUP_TYPE_RAP );
802 isom_sgpd_t *sgpd_roll = isom_get_roll_recovery_sample_group_description( &stbl->sgpd_list );
803 isom_sbgp_t *sbgp_roll = isom_get_roll_recovery_sample_to_group ( &stbl->sbgp_list );
804 lsmash_entry_t *elst_entry = elst->list ? elst->list->head : NULL;
805 lsmash_entry_t *stts_entry = stts->list ? stts->list->head : NULL;
806 lsmash_entry_t *ctts_entry = ctts->list ? ctts->list->head : NULL;
807 lsmash_entry_t *stss_entry = stss->list ? stss->list->head : NULL;
808 lsmash_entry_t *stps_entry = stps->list ? stps->list->head : NULL;
809 lsmash_entry_t *sdtp_entry = sdtp->list ? sdtp->list->head : NULL;
810 lsmash_entry_t *stsz_entry = LSMASH_IS_EXISTING_BOX( stsz ) ? (stsz->list ? stsz->list->head : NULL)
811 : (stz2->list ? stz2->list->head : NULL);
812 lsmash_entry_t *stsc_entry = stsc->list ? stsc->list->head : NULL;
813 lsmash_entry_t *stco_entry = stco->list ? stco->list->head : NULL;
814 lsmash_entry_t *sbgp_roll_entry = sbgp_roll->list ? sbgp_roll->list->head : NULL;
815 lsmash_entry_t *sbgp_rap_entry = sbgp_rap->list ? sbgp_rap->list->head : NULL;
816 lsmash_entry_t *next_stsc_entry = stsc_entry ? stsc_entry->next : NULL;
817 isom_stsc_entry_t *stsc_data = stsc_entry ? (isom_stsc_entry_t *)stsc_entry->data : NULL;
818 int err = LSMASH_ERR_INVALID_DATA;
819 int movie_fragments_present = (LSMASH_IS_EXISTING_BOX( file->moov->mvex ) && file->moof_list.head);
820 if( !movie_fragments_present && (!stts_entry || !stsc_entry || !stco_entry || !stco_entry->data || (next_stsc_entry && !next_stsc_entry->data)) )
821 goto fail;
822 isom_sample_entry_t *description = (isom_sample_entry_t *)lsmash_list_get_entry_data( &stsd->list, stsc_data ? stsc_data->sample_description_index : 1 );
823 if( LSMASH_IS_NON_EXISTING_BOX( description ) )
824 goto fail;
825 lsmash_entry_list_t *dref_list = LSMASH_IS_EXISTING_BOX( dref ) ? &dref->list : NULL;
826 isom_dref_entry_t *dref_entry = (isom_dref_entry_t *)lsmash_list_get_entry_data( dref_list, description->data_reference_index );
827 int all_sync = LSMASH_IS_NON_EXISTING_BOX( stss );
828 int large_presentation = stco->large_presentation || lsmash_check_box_type_identical( stco->type, ISOM_BOX_TYPE_CO64 );
829 int is_lpcm_audio = isom_is_lpcm_audio( description );
830 int is_qt_fixed_comp_audio = isom_is_qt_fixed_compressed_audio( description );
831 int iso_sdtp = file->max_isom_version >= 2 || file->avc_extensions;
832 int allow_negative_sample_offset = ctts && ((file->max_isom_version >= 4 && ctts->version == 1) || file->qt_compatible);
833 uint32_t sample_number_in_stts_entry = 1;
834 uint32_t sample_number_in_ctts_entry = 1;
835 uint32_t sample_number_in_sbgp_roll_entry = 1;
836 uint32_t sample_number_in_sbgp_rap_entry = 1;
837 uint64_t dts = 0;
838 uint32_t chunk_number = 1;
839 uint64_t offset_from_chunk = 0;
840 uint64_t data_offset = stco_entry && stco_entry->data
841 ? large_presentation
842 ? ((isom_co64_entry_t *)stco_entry->data)->chunk_offset
843 : ((isom_stco_entry_t *)stco_entry->data)->chunk_offset
844 : 0;
845 uint32_t initial_movie_sample_count = LSMASH_IS_EXISTING_BOX( stsz ) ? stsz->sample_count : stz2->sample_count;
846 uint32_t samples_per_packet;
847 uint32_t constant_sample_size;
848 if( is_qt_fixed_comp_audio )
849 isom_get_qt_fixed_comp_audio_sample_quants( timeline, description, &samples_per_packet, &constant_sample_size );
850 else
852 samples_per_packet = 1;
853 constant_sample_size = stsz ? stsz->sample_size : 0;
855 uint32_t sample_number = samples_per_packet;
856 uint32_t sample_number_in_chunk = samples_per_packet;
857 /* Copy edits. */
858 while( elst_entry )
860 isom_elst_entry_t *edit = (isom_elst_entry_t *)lsmash_memdup( elst_entry->data, sizeof(isom_elst_entry_t) );
861 if( !edit
862 || lsmash_list_add_entry( timeline->edit_list, edit ) < 0 )
864 err = LSMASH_ERR_MEMORY_ALLOC;
865 goto fail;
867 elst_entry = elst_entry->next;
869 /* Check what the first 2-bits of sample dependency means.
870 * This check is for chimera of ISO Base Media and QTFF. */
871 if( iso_sdtp && sdtp_entry )
873 while( sdtp_entry )
875 isom_sdtp_entry_t *sdtp_data = (isom_sdtp_entry_t *)sdtp_entry->data;
876 if( !sdtp_data )
877 goto fail;
878 if( sdtp_data->is_leading > 1 )
879 break; /* Apparently, it's defined under ISO Base Media. */
880 if( (sdtp_data->is_leading == 1) && (sdtp_data->sample_depends_on == ISOM_SAMPLE_IS_INDEPENDENT) )
882 /* Obviously, it's not defined under ISO Base Media. */
883 iso_sdtp = 0;
884 break;
886 sdtp_entry = sdtp_entry->next;
888 sdtp_entry = sdtp->list->head;
890 /**--- Construct media timeline. ---**/
891 isom_portable_chunk_t chunk;
892 chunk.data_offset = data_offset;
893 chunk.length = 0;
894 chunk.number = chunk_number;
895 chunk.file = (!dref_entry || LSMASH_IS_NON_EXISTING_BOX( dref_entry->ref_file )) ? NULL : dref_entry->ref_file;
896 if( (err = isom_add_portable_chunk_entry( timeline, &chunk )) < 0 )
897 goto fail;
898 uint32_t distance = NO_RANDOM_ACCESS_POINT;
899 uint32_t last_duration = UINT32_MAX;
900 uint32_t packet_number = 1;
901 isom_lpcm_bunch_t bunch = { 0 };
902 while( sample_number <= initial_movie_sample_count )
904 isom_sample_info_t info = { 0 };
905 /* Get sample duration and sample offset. */
906 for( uint32_t i = 0; i < samples_per_packet; i++ )
908 /* sample duration */
909 if( stts_entry )
911 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
912 if( !stts_data )
913 goto fail;
914 isom_increment_sample_number_in_entry( &sample_number_in_stts_entry, &stts_entry, stts_data->sample_count );
915 last_duration = stts_data->sample_delta;
917 info.duration += last_duration;
918 dts += last_duration;
919 /* sample offset */
920 uint32_t sample_offset;
921 if( ctts_entry )
923 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
924 if( !ctts_data )
925 goto fail;
926 isom_increment_sample_number_in_entry( &sample_number_in_ctts_entry, &ctts_entry, ctts_data->sample_count );
927 sample_offset = ctts_data->sample_offset;
928 if( allow_negative_sample_offset && sample_offset != ISOM_NON_OUTPUT_SAMPLE_OFFSET )
930 uint64_t cts = dts + (int32_t)sample_offset;
931 if( (cts + timeline->ctd_shift) < dts )
932 timeline->ctd_shift = dts - cts;
935 else
936 sample_offset = 0;
937 if( i == 0 )
938 info.offset = sample_offset;
940 timeline->media_duration += info.duration;
941 if( !is_qt_fixed_comp_audio )
943 /* Check whether sync sample or not. */
944 if( stss_entry )
946 isom_stss_entry_t *stss_data = (isom_stss_entry_t *)stss_entry->data;
947 if( !stss_data )
948 goto fail;
949 if( sample_number == stss_data->sample_number )
951 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
952 stss_entry = stss_entry->next;
953 distance = 0;
956 else if( all_sync )
957 /* Don't reset distance as 0 since MDCT-based audio frames need pre-roll for correct presentation
958 * though all of them could be marked as a sync sample. */
959 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
960 /* Check whether partial sync sample or not. */
961 if( stps_entry )
963 isom_stps_entry_t *stps_data = (isom_stps_entry_t *)stps_entry->data;
964 if( !stps_data )
965 goto fail;
966 if( sample_number == stps_data->sample_number )
968 info.prop.ra_flags |= QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC | QT_SAMPLE_RANDOM_ACCESS_FLAG_RAP;
969 stps_entry = stps_entry->next;
970 distance = 0;
973 /* Get sample dependency info. */
974 if( sdtp_entry )
976 isom_sdtp_entry_t *sdtp_data = (isom_sdtp_entry_t *)sdtp_entry->data;
977 if( !sdtp_data )
978 goto fail;
979 if( iso_sdtp )
980 info.prop.leading = sdtp_data->is_leading;
981 else
982 info.prop.allow_earlier = sdtp_data->is_leading;
983 info.prop.independent = sdtp_data->sample_depends_on;
984 info.prop.disposable = sdtp_data->sample_is_depended_on;
985 info.prop.redundant = sdtp_data->sample_has_redundancy;
986 sdtp_entry = sdtp_entry->next;
988 /* Get roll recovery grouping info. */
989 if( sbgp_roll_entry
990 && isom_get_roll_recovery_grouping_info( timeline,
991 &sbgp_roll_entry, sgpd_roll, NULL,
992 &sample_number_in_sbgp_roll_entry,
993 &info, sample_number ) < 0 )
994 goto fail;
995 info.prop.post_roll.identifier = sample_number;
996 /* Get random access point grouping info. */
997 if( sbgp_rap_entry
998 && isom_get_random_access_point_grouping_info( timeline,
999 &sbgp_rap_entry, sgpd_rap, NULL,
1000 &sample_number_in_sbgp_rap_entry,
1001 &info, &distance ) < 0 )
1002 goto fail;
1003 /* Set up distance from the previous random access point. */
1004 if( distance != NO_RANDOM_ACCESS_POINT )
1006 if( info.prop.pre_roll.distance == 0 )
1007 info.prop.pre_roll.distance = distance;
1008 ++distance;
1011 else
1012 /* All uncompressed and non-variable compressed audio frame is a sync sample. */
1013 info.prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1014 /* Get size of sample in the stream. */
1015 if( is_qt_fixed_comp_audio || !stsz_entry )
1016 info.length = constant_sample_size;
1017 else
1019 if( !stsz_entry->data )
1020 goto fail;
1021 info.length = ((isom_stsz_entry_t *)stsz_entry->data)->entry_size;
1022 stsz_entry = stsz_entry->next;
1024 timeline->max_sample_size = LSMASH_MAX( timeline->max_sample_size, info.length );
1025 /* Get chunk info. */
1026 info.pos = data_offset;
1027 info.index = stsc_data->sample_description_index;
1028 info.chunk = (isom_portable_chunk_t *)timeline->chunk_list->tail->data;
1029 offset_from_chunk += info.length;
1030 if( sample_number_in_chunk == stsc_data->samples_per_chunk )
1032 /* Set the length of the last chunk. */
1033 if( info.chunk )
1034 info.chunk->length = offset_from_chunk;
1035 /* Move the next chunk. */
1036 if( stco_entry )
1037 stco_entry = stco_entry->next;
1038 if( stco_entry
1039 && stco_entry->data )
1040 data_offset = large_presentation
1041 ? ((isom_co64_entry_t *)stco_entry->data)->chunk_offset
1042 : ((isom_stco_entry_t *)stco_entry->data)->chunk_offset;
1043 chunk.data_offset = data_offset;
1044 chunk.length = 0;
1045 chunk.number = ++chunk_number;
1046 offset_from_chunk = 0;
1047 /* Check if the next entry is broken. */
1048 while( next_stsc_entry && chunk_number > ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk )
1050 /* Just skip broken next entry. */
1051 lsmash_log( timeline, LSMASH_LOG_WARNING, "ignore broken entry in Sample To Chunk Box.\n" );
1052 lsmash_log( timeline, LSMASH_LOG_WARNING, "timeline might be corrupted.\n" );
1053 next_stsc_entry = next_stsc_entry->next;
1054 if( next_stsc_entry
1055 && !next_stsc_entry->data )
1056 goto fail;
1058 /* Check if the next chunk belongs to the next sequence of chunks. */
1059 if( next_stsc_entry && chunk_number == ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk )
1061 stsc_entry = next_stsc_entry;
1062 next_stsc_entry = next_stsc_entry->next;
1063 if( next_stsc_entry
1064 && !next_stsc_entry->data )
1065 goto fail;
1066 stsc_data = (isom_stsc_entry_t *)stsc_entry->data;
1067 /* Update sample description. */
1068 description = (isom_sample_entry_t *)lsmash_list_get_entry_data( &stsd->list, stsc_data->sample_description_index );
1069 is_lpcm_audio = LSMASH_IS_EXISTING_BOX( description ) ? isom_is_lpcm_audio( description ) : 0;
1070 is_qt_fixed_comp_audio = LSMASH_IS_EXISTING_BOX( description ) ? isom_is_qt_fixed_compressed_audio( description ) : 0;
1071 if( is_qt_fixed_comp_audio )
1072 isom_get_qt_fixed_comp_audio_sample_quants( timeline, description, &samples_per_packet, &constant_sample_size );
1073 else
1075 samples_per_packet = 1;
1076 constant_sample_size = stsz ? stsz->sample_size : 0;
1078 /* Reference media data. */
1079 dref_entry = (isom_dref_entry_t *)lsmash_list_get_entry_data( dref_list, LSMASH_IS_EXISTING_BOX( description ) ? description->data_reference_index : 0 );
1080 chunk.file = (!dref_entry || LSMASH_IS_NON_EXISTING_BOX( dref_entry->ref_file )) ? NULL : dref_entry->ref_file;
1082 sample_number_in_chunk = samples_per_packet;
1083 if( (err = isom_add_portable_chunk_entry( timeline, &chunk )) < 0 )
1084 goto fail;
1086 else
1088 data_offset += info.length;
1089 sample_number_in_chunk += samples_per_packet;
1091 /* OK. Let's add its info. */
1092 if( is_lpcm_audio )
1094 if( sample_number == samples_per_packet )
1095 isom_update_bunch( &bunch, &info );
1096 else if( isom_compare_lpcm_sample_info( &bunch, &info ) )
1098 if( (err = isom_add_lpcm_bunch_entry( timeline, &bunch )) < 0 )
1099 goto fail;
1100 isom_update_bunch( &bunch, &info );
1102 else
1103 ++ bunch.sample_count;
1105 else if( (err = isom_add_sample_info_entry( timeline, &info )) < 0 )
1106 goto fail;
1107 if( timeline->info_list->entry_count && timeline->bunch_list->entry_count )
1109 lsmash_log( timeline, LSMASH_LOG_ERROR, "LPCM + non-LPCM track is not supported.\n" );
1110 err = LSMASH_ERR_PATCH_WELCOME;
1111 goto fail;
1113 sample_number += samples_per_packet;
1114 packet_number += 1;
1116 isom_portable_chunk_t *last_chunk = lsmash_list_get_entry_data( timeline->chunk_list, timeline->chunk_list->entry_count );
1117 if( last_chunk )
1119 if( offset_from_chunk )
1120 last_chunk->length = offset_from_chunk;
1121 else
1123 /* Remove the last invalid chunk. */
1124 lsmash_list_remove_entry( timeline->chunk_list, timeline->chunk_list->entry_count );
1125 --chunk_number;
1128 uint32_t sample_count = packet_number - 1;
1129 if( movie_fragments_present )
1131 isom_tfra_t *tfra = isom_get_tfra( file->mfra, track_ID );
1132 lsmash_entry_t *tfra_entry = tfra->list ? tfra->list->head : NULL;
1133 isom_tfra_location_time_entry_t *rap = tfra_entry ? (isom_tfra_location_time_entry_t *)tfra_entry->data : NULL;
1134 chunk.data_offset = 0;
1135 chunk.length = 0;
1136 /* Movie fragments */
1137 for( lsmash_entry_t *moof_entry = file->moof_list.head; moof_entry; moof_entry = moof_entry->next )
1139 isom_moof_t *moof = (isom_moof_t *)moof_entry->data;
1140 if( LSMASH_IS_NON_EXISTING_BOX( moof ) )
1141 goto fail;
1142 uint64_t last_sample_end_pos = 0;
1143 /* Track fragments */
1144 uint32_t traf_number = 1;
1145 for( lsmash_entry_t *traf_entry = moof->traf_list.head; traf_entry; traf_entry = traf_entry->next )
1147 isom_traf_t *traf = (isom_traf_t *)traf_entry->data;
1148 isom_tfhd_t *tfhd = traf->tfhd;
1149 isom_trex_t *trex = isom_get_trex( file->moov->mvex, tfhd->track_ID );
1150 if( LSMASH_IS_NON_EXISTING_BOX( trex ) )
1151 goto fail;
1152 /* Ignore ISOM_TF_FLAGS_DURATION_IS_EMPTY flag even if set. */
1153 if( !traf->trun_list.head )
1155 ++traf_number;
1156 continue;
1158 /* Get base_data_offset. */
1159 uint64_t base_data_offset;
1160 if( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT )
1161 base_data_offset = tfhd->base_data_offset;
1162 else if( (tfhd->flags & ISOM_TF_FLAGS_DEFAULT_BASE_IS_MOOF) || traf_entry == moof->traf_list.head )
1163 base_data_offset = moof->pos;
1164 else
1165 base_data_offset = last_sample_end_pos;
1166 /* sample grouping */
1167 isom_sgpd_t *sgpd_frag_rap;
1168 isom_sgpd_t *sgpd_frag_roll;
1169 sgpd_frag_rap = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_RAP );
1170 sbgp_rap = isom_get_fragment_sample_to_group ( traf, ISOM_GROUP_TYPE_RAP );
1171 sbgp_rap_entry = sbgp_rap->list ? sbgp_rap->list->head : NULL;
1172 sgpd_frag_roll = isom_get_roll_recovery_sample_group_description( &traf->sgpd_list );
1173 sbgp_roll = isom_get_roll_recovery_sample_to_group ( &traf->sbgp_list );
1174 sbgp_roll_entry = sbgp_roll->list ? sbgp_roll->list->head : NULL;
1175 int need_data_offset_only = (tfhd->track_ID != track_ID);
1176 /* Track runs */
1177 uint32_t trun_number = 1;
1178 for( lsmash_entry_t *trun_entry = traf->trun_list.head; trun_entry; trun_entry = trun_entry->next )
1180 isom_trun_t *trun = (isom_trun_t *)trun_entry->data;
1181 if( LSMASH_IS_NON_EXISTING_BOX( trun ) )
1182 goto fail;
1183 if( trun->sample_count == 0 )
1185 ++trun_number;
1186 continue;
1188 /* Get data_offset. */
1189 if( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT )
1190 data_offset = trun->data_offset + base_data_offset;
1191 else if( trun_entry == traf->trun_list.head )
1192 data_offset = base_data_offset;
1193 else
1194 data_offset = last_sample_end_pos;
1195 /* */
1196 uint32_t sample_description_index = 0;
1197 isom_sdtp_entry_t *sdtp_data = NULL;
1198 if( !need_data_offset_only )
1200 /* Get sample_description_index of this track fragment. */
1201 if( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT )
1202 sample_description_index = tfhd->sample_description_index;
1203 else
1204 sample_description_index = trex->default_sample_description_index;
1205 description = (isom_sample_entry_t *)lsmash_list_get_entry_data( &stsd->list, sample_description_index );
1206 is_lpcm_audio = LSMASH_IS_EXISTING_BOX( description ) ? isom_is_lpcm_audio( description ) : 0;
1207 /* Reference media data. */
1208 dref_entry = (isom_dref_entry_t *)lsmash_list_get_entry_data( dref_list, LSMASH_IS_EXISTING_BOX( description ) ? description->data_reference_index : 0 );
1209 lsmash_file_t *ref_file = (!dref_entry || LSMASH_IS_NON_EXISTING_BOX( dref_entry->ref_file )) ? NULL : dref_entry->ref_file;
1210 /* Each track run can be considered as a chunk.
1211 * Here, we consider physically consecutive track runs as one chunk. */
1212 if( chunk.data_offset + chunk.length != data_offset || chunk.file != ref_file )
1214 chunk.data_offset = data_offset;
1215 chunk.length = 0;
1216 chunk.number = ++chunk_number;
1217 chunk.file = ref_file;
1218 if( (err = isom_add_portable_chunk_entry( timeline, &chunk )) < 0 )
1219 goto fail;
1221 /* Get dependency info for this track fragment. */
1222 sdtp_entry = traf->sdtp->list ? traf->sdtp->list->head : NULL;
1223 sdtp_data = sdtp_entry && sdtp_entry->data ? (isom_sdtp_entry_t *)sdtp_entry->data : NULL;
1225 /* Get info of each sample. */
1226 lsmash_entry_t *row_entry = trun->optional && trun->optional->head ? trun->optional->head : NULL;
1227 sample_number = 1;
1228 while( sample_number <= trun->sample_count )
1230 isom_sample_info_t info = { 0 };
1231 isom_trun_optional_row_t *row = row_entry && row_entry->data ? (isom_trun_optional_row_t *)row_entry->data : NULL;
1232 /* Get sample_size */
1233 if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT) )
1234 info.length = row->sample_size;
1235 else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT )
1236 info.length = tfhd->default_sample_size;
1237 else
1238 info.length = trex->default_sample_size;
1239 if( !need_data_offset_only )
1241 info.pos = data_offset;
1242 info.index = sample_description_index;
1243 info.chunk = (isom_portable_chunk_t *)timeline->chunk_list->tail->data;
1244 info.chunk->length += info.length;
1245 /* Get sample_duration. */
1246 if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT) )
1247 info.duration = row->sample_duration;
1248 else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT )
1249 info.duration = tfhd->default_sample_duration;
1250 else
1251 info.duration = trex->default_sample_duration;
1252 /* Get composition time offset. */
1253 if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT) )
1255 info.offset = row->sample_composition_time_offset;
1256 /* Check composition to decode timeline shift. */
1257 if( file->max_isom_version >= 6 && trun->version != 0 && info.offset != ISOM_NON_OUTPUT_SAMPLE_OFFSET )
1259 uint64_t cts = dts + (int32_t)info.offset;
1260 if( (cts + timeline->ctd_shift) < dts )
1261 timeline->ctd_shift = dts - cts;
1264 else
1265 info.offset = 0;
1266 dts += info.duration;
1267 /* Update media duration and maximun sample size. */
1268 timeline->media_duration += info.duration;
1269 timeline->max_sample_size = LSMASH_MAX( timeline->max_sample_size, info.length );
1270 if( !is_lpcm_audio )
1272 /* Get sample_flags. */
1273 isom_sample_flags_t sample_flags;
1274 if( sample_number == 1 && (trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT) )
1275 sample_flags = trun->first_sample_flags;
1276 else if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT) )
1277 sample_flags = row->sample_flags;
1278 else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT )
1279 sample_flags = tfhd->default_sample_flags;
1280 else
1281 sample_flags = trex->default_sample_flags;
1282 if( sdtp_data )
1284 /* Independent and Disposable Samples Box overrides the information from sample_flags.
1285 * There is no description in the specification about this, but the intention should be such a thing.
1286 * The ground is that sample_flags is placed in media layer
1287 * while Independent and Disposable Samples Box is placed in track or presentation layer. */
1288 info.prop.leading = sdtp_data->is_leading;
1289 info.prop.independent = sdtp_data->sample_depends_on;
1290 info.prop.disposable = sdtp_data->sample_is_depended_on;
1291 info.prop.redundant = sdtp_data->sample_has_redundancy;
1292 if( sdtp_entry )
1293 sdtp_entry = sdtp_entry->next;
1294 sdtp_data = sdtp_entry ? (isom_sdtp_entry_t *)sdtp_entry->data : NULL;
1296 else
1298 info.prop.leading = sample_flags.is_leading;
1299 info.prop.independent = sample_flags.sample_depends_on;
1300 info.prop.disposable = sample_flags.sample_is_depended_on;
1301 info.prop.redundant = sample_flags.sample_has_redundancy;
1303 /* Check this sample is a sync sample or not.
1304 * Note: all sync sample shall be independent. */
1305 if( !sample_flags.sample_is_non_sync_sample
1306 && info.prop.independent != ISOM_SAMPLE_IS_NOT_INDEPENDENT )
1308 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1309 distance = 0;
1311 /* Get roll recovery grouping info. */
1312 uint32_t roll_id = sample_count + sample_number;
1313 if( sbgp_roll_entry
1314 && isom_get_roll_recovery_grouping_info( timeline,
1315 &sbgp_roll_entry, sgpd_roll, sgpd_frag_roll,
1316 &sample_number_in_sbgp_roll_entry,
1317 &info, roll_id ) < 0 )
1318 goto fail;
1319 info.prop.post_roll.identifier = roll_id;
1320 /* Get random access point grouping info. */
1321 if( sbgp_rap_entry
1322 && isom_get_random_access_point_grouping_info( timeline,
1323 &sbgp_rap_entry, sgpd_rap, sgpd_frag_rap,
1324 &sample_number_in_sbgp_rap_entry,
1325 &info, &distance ) < 0 )
1326 goto fail;
1327 /* Get the location of the sync sample from 'tfra' if it is not set up yet.
1328 * Note: there is no guarantee that its entries are placed in a specific order. */
1329 if( LSMASH_IS_EXISTING_BOX( tfra ) )
1331 if( tfra->number_of_entry == 0
1332 && info.prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1333 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1334 if( rap
1335 && rap->moof_offset == moof->pos
1336 && rap->traf_number == traf_number
1337 && rap->trun_number == trun_number
1338 && rap->sample_number == sample_number )
1340 if( info.prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1341 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1342 if( tfra_entry )
1343 tfra_entry = tfra_entry->next;
1344 rap = tfra_entry ? (isom_tfra_location_time_entry_t *)tfra_entry->data : NULL;
1347 /* Set up distance from the previous random access point. */
1348 if( distance != NO_RANDOM_ACCESS_POINT )
1350 if( info.prop.pre_roll.distance == 0 )
1351 info.prop.pre_roll.distance = distance;
1352 ++distance;
1354 /* OK. Let's add its info. */
1355 if( (err = isom_add_sample_info_entry( timeline, &info )) < 0 )
1356 goto fail;
1358 else
1360 /* All LPCMFrame is a sync sample. */
1361 info.prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1362 /* OK. Let's add its info. */
1363 if( sample_count == 0 && sample_number == 1 )
1364 isom_update_bunch( &bunch, &info );
1365 else if( isom_compare_lpcm_sample_info( &bunch, &info ) )
1367 if( (err = isom_add_lpcm_bunch_entry( timeline, &bunch )) < 0 )
1368 goto fail;
1369 isom_update_bunch( &bunch, &info );
1371 else
1372 ++ bunch.sample_count;
1374 if( timeline-> info_list->entry_count
1375 && timeline->bunch_list->entry_count )
1377 lsmash_log( timeline, LSMASH_LOG_ERROR, "LPCM + non-LPCM track is not supported.\n" );
1378 err = LSMASH_ERR_PATCH_WELCOME;
1379 goto fail;
1382 data_offset += info.length;
1383 last_sample_end_pos = data_offset;
1384 if( row_entry )
1385 row_entry = row_entry->next;
1386 ++sample_number;
1388 if( !need_data_offset_only )
1389 sample_count += sample_number - 1;
1390 ++trun_number;
1391 } /* Track runs */
1392 ++traf_number;
1393 } /* Track fragments */
1394 } /* Movie fragments */
1396 else if( timeline->chunk_list->entry_count == 0 )
1397 goto fail; /* No samples in this track. */
1398 if( bunch.sample_count && (err = isom_add_lpcm_bunch_entry( timeline, &bunch )) < 0 )
1399 goto fail;
1400 if( (err = lsmash_list_add_entry( file->timeline, timeline )) < 0 )
1401 goto fail;
1402 /* Finish timeline construction. */
1403 timeline->sample_count = sample_count;
1404 if( timeline->info_list->entry_count )
1405 isom_timeline_set_sample_getter_funcs( timeline );
1406 else
1407 isom_timeline_set_lpcm_sample_getter_funcs( timeline );
1408 return 0;
1409 fail:
1410 isom_timeline_destroy( timeline );
1411 return err;
1414 int lsmash_construct_timeline( lsmash_root_t *root, uint32_t track_ID )
1416 if( LSMASH_IS_NON_EXISTING_BOX( root )
1417 || LSMASH_IS_NON_EXISTING_BOX( root->file )
1418 || track_ID == 0 )
1419 return LSMASH_ERR_FUNCTION_PARAM;
1420 uint32_t track_number;
1421 if( LSMASH_IS_EXISTING_BOX( root->file->initializer ) )
1423 if( LSMASH_IS_NON_EXISTING_BOX( root->file->initializer->moov ) )
1424 return LSMASH_ERR_INVALID_DATA;
1425 track_number = 1;
1426 int track_found = 0;
1427 for( lsmash_entry_t *entry = root->file->initializer->moov->trak_list.head; entry; entry = entry->next )
1429 isom_trak_t *trak = (isom_trak_t *)entry->data;
1430 if( LSMASH_IS_NON_EXISTING_BOX( trak )
1431 || LSMASH_IS_NON_EXISTING_BOX( trak->tkhd ) )
1432 continue;
1433 if( trak->tkhd->track_ID == track_ID )
1435 track_found = 1;
1436 break;
1438 ++track_number;
1440 if( !track_found )
1441 return LSMASH_ERR_NAMELESS;
1443 else
1444 track_number = track_ID;
1445 return lsmash_importer_construct_timeline( root->file->importer, track_number );
1448 int lsmash_get_dts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *dts )
1450 if( !sample_number || !dts )
1451 return LSMASH_ERR_FUNCTION_PARAM;
1452 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1453 if( !timeline || sample_number > timeline->sample_count )
1454 return LSMASH_ERR_NAMELESS;
1455 return timeline->get_dts( timeline, sample_number, dts );
1458 int lsmash_get_cts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *cts )
1460 if( !sample_number || !cts )
1461 return LSMASH_ERR_FUNCTION_PARAM;
1462 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1463 if( !timeline || sample_number > timeline->sample_count )
1464 return LSMASH_ERR_NAMELESS;
1465 return timeline->get_cts( timeline, sample_number, cts );
1468 lsmash_sample_t *lsmash_get_sample_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number )
1470 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1471 return timeline ? timeline->get_sample( timeline, sample_number ) : NULL;
1474 int lsmash_get_sample_info_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, lsmash_sample_t *sample )
1476 if( !sample )
1477 return LSMASH_ERR_FUNCTION_PARAM;
1478 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1479 return timeline ? timeline->get_sample_info( timeline, sample_number, sample ) : -1;
1482 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 )
1484 if( !prop )
1485 return LSMASH_ERR_FUNCTION_PARAM;
1486 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1487 return timeline ? timeline->get_sample_property( timeline, sample_number, prop ) : -1;
1490 int lsmash_get_composition_to_decode_shift_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *ctd_shift )
1492 if( !ctd_shift )
1493 return LSMASH_ERR_FUNCTION_PARAM;
1494 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1495 if( !timeline )
1496 return LSMASH_ERR_NAMELESS;
1497 *ctd_shift = timeline->ctd_shift;
1498 return 0;
1501 static int isom_get_closest_past_random_accessible_point_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number )
1503 lsmash_entry_t *entry = lsmash_list_get_entry( timeline->info_list, sample_number-- );
1504 if( !entry
1505 || !entry->data )
1506 return LSMASH_ERR_NAMELESS;
1507 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1508 while( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1510 entry = entry->prev;
1511 if( !entry
1512 || !entry->data )
1513 return LSMASH_ERR_NAMELESS;
1514 info = (isom_sample_info_t *)entry->data;
1515 --sample_number;
1517 *rap_number = sample_number + 1;
1518 return 0;
1521 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 )
1523 lsmash_entry_t *entry = lsmash_list_get_entry( timeline->info_list, sample_number++ );
1524 if( !entry
1525 || !entry->data )
1526 return LSMASH_ERR_NAMELESS;
1527 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1528 while( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1530 entry = entry->next;
1531 if( !entry
1532 || !entry->data )
1533 return LSMASH_ERR_NAMELESS;
1534 info = (isom_sample_info_t *)entry->data;
1535 ++sample_number;
1537 *rap_number = sample_number - 1;
1538 return 0;
1541 static int isom_get_closest_random_accessible_point_from_media_timeline_internal( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number )
1543 if( !timeline )
1544 return LSMASH_ERR_NAMELESS;
1545 int ret;
1546 if( (ret = isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, sample_number, rap_number )) < 0
1547 && (ret = isom_get_closest_future_random_accessible_point_from_media_timeline( timeline, sample_number + 1, rap_number )) < 0 )
1548 return ret;
1549 return 0;
1552 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 )
1554 if( sample_number == 0 || !rap_number )
1555 return LSMASH_ERR_FUNCTION_PARAM;
1556 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1557 if( !timeline )
1558 return LSMASH_ERR_NAMELESS;
1559 if( timeline->info_list->entry_count == 0 )
1561 *rap_number = sample_number; /* All LPCM is sync sample. */
1562 return 0;
1564 return isom_get_closest_random_accessible_point_from_media_timeline_internal( timeline, sample_number, rap_number );
1567 int lsmash_get_closest_random_accessible_point_detail_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number,
1568 uint32_t *rap_number, lsmash_random_access_flag *ra_flags, uint32_t *leading, uint32_t *distance )
1570 if( sample_number == 0 )
1571 return LSMASH_ERR_FUNCTION_PARAM;
1572 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1573 if( !timeline )
1574 return LSMASH_ERR_NAMELESS;
1575 if( timeline->info_list->entry_count == 0 )
1577 /* All LPCM is sync sample. */
1578 *rap_number = sample_number;
1579 if( ra_flags )
1580 *ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1581 if( leading )
1582 *leading = 0;
1583 if( distance )
1584 *distance = 0;
1585 return 0;
1587 int ret = isom_get_closest_random_accessible_point_from_media_timeline_internal( timeline, sample_number, rap_number );
1588 if( ret < 0 )
1589 return ret;
1590 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_list_get_entry_data( timeline->info_list, *rap_number );
1591 if( !info )
1592 return LSMASH_ERR_NAMELESS;
1593 if( ra_flags )
1594 *ra_flags = info->prop.ra_flags;
1595 if( leading )
1596 *leading = 0;
1597 if( distance )
1598 *distance = 0;
1599 if( sample_number < *rap_number )
1600 /* Impossible to desire to decode the sample of given number correctly. */
1601 return 0;
1602 else if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) )
1604 if( leading )
1606 /* Count leading samples. */
1607 uint32_t current_sample_number = *rap_number + 1;
1608 uint64_t dts;
1609 if( (ret = isom_get_dts_from_info_list( timeline, *rap_number, &dts )) < 0 )
1610 return ret;
1611 uint64_t rap_cts = isom_make_cts_adjust( dts, info->offset, timeline->ctd_shift );
1614 dts += info->duration;
1615 if( rap_cts <= dts )
1616 break; /* leading samples of this random accessible point must not be present more. */
1617 info = (isom_sample_info_t *)lsmash_list_get_entry_data( timeline->info_list, current_sample_number++ );
1618 if( !info )
1619 break;
1620 uint64_t cts = isom_make_cts_adjust( dts, info->offset, timeline->ctd_shift );
1621 if( rap_cts != LSMASH_TIMESTAMP_UNDEFINED && rap_cts > cts )
1622 ++ *leading;
1623 } while( 1 );
1625 if( !distance || sample_number == *rap_number )
1626 return 0;
1627 /* Measure distance from the first closest non-recovery random accessible point to the second. */
1628 uint32_t prev_rap_number = *rap_number;
1631 if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) < 0 )
1632 /* The previous random accessible point is not present. */
1633 return 0;
1634 info = (isom_sample_info_t *)lsmash_list_get_entry_data( timeline->info_list, prev_rap_number );
1635 if( !info )
1636 return LSMASH_ERR_NAMELESS;
1637 if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) )
1639 /* Decode shall already complete at the first closest non-recovery random accessible point if starting to decode from the second. */
1640 *distance = *rap_number - prev_rap_number;
1641 return 0;
1643 } while( 1 );
1645 if( !distance )
1646 return 0;
1647 /* Calculate roll-distance. */
1648 if( info->prop.pre_roll.distance )
1650 /* Pre-roll recovery */
1651 uint32_t prev_rap_number = *rap_number;
1654 if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) < 0
1655 && *rap_number < info->prop.pre_roll.distance )
1657 /* The previous random accessible point is not present.
1658 * And sample of given number might be not able to decoded correctly. */
1659 *distance = 0;
1660 return 0;
1662 if( prev_rap_number + info->prop.pre_roll.distance <= *rap_number )
1665 * |<---- pre-roll distance ---->|
1666 * |<--------- distance -------->|
1667 * media +++++++++++++++++++++++++ *** +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1668 * ^ ^ ^ ^
1669 * random accessible point starting point random accessible point given sample
1670 * (complete)
1672 *distance = info->prop.pre_roll.distance;
1673 return 0;
1675 else if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) )
1678 * |<------------ pre-roll distance ------------------>|
1679 * |<------ distance ------->|
1680 * media ++++++++++++++++ *** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1681 * ^ ^ ^ ^
1682 * random accessible point random accessible point given sample
1683 * (starting point) (complete)
1685 *distance = *rap_number - prev_rap_number;
1686 return 0;
1688 } while( 1 );
1690 /* Post-roll recovery */
1691 if( sample_number >= info->prop.post_roll.complete )
1693 * |<----- post-roll distance ----->|
1694 * (distance = 0)
1695 * media +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1696 * ^ ^ ^
1697 * random accessible point complete given sample
1698 * (starting point)
1700 return 0;
1701 uint32_t prev_rap_number = *rap_number;
1704 if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) < 0 )
1705 /* The previous random accessible point is not present. */
1706 return 0;
1707 info = (isom_sample_info_t *)lsmash_list_get_entry_data( timeline->info_list, prev_rap_number );
1708 if( !info )
1709 return LSMASH_ERR_NAMELESS;
1710 if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) || sample_number >= info->prop.post_roll.complete )
1712 *distance = *rap_number - prev_rap_number;
1713 return 0;
1715 } while( 1 );
1718 int lsmash_check_sample_existence_in_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number )
1720 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1721 return timeline ? timeline->check_sample_existence( timeline, sample_number ) : 0;
1724 int lsmash_get_last_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *last_sample_delta )
1726 if( !last_sample_delta )
1727 return LSMASH_ERR_FUNCTION_PARAM;
1728 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1729 return timeline ? timeline->get_sample_duration( timeline, timeline->sample_count, last_sample_delta ) : -1;
1732 int lsmash_get_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint32_t *sample_delta )
1734 if( !sample_delta )
1735 return LSMASH_ERR_FUNCTION_PARAM;
1736 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1737 return timeline ? timeline->get_sample_duration( timeline, sample_number, sample_delta ) : -1;
1740 uint32_t lsmash_get_sample_count_in_media_timeline( lsmash_root_t *root, uint32_t track_ID )
1742 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1743 if( !timeline )
1744 return 0;
1745 return timeline->sample_count;
1748 uint32_t lsmash_get_max_sample_size_in_media_timeline( lsmash_root_t *root, uint32_t track_ID )
1750 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1751 if( !timeline )
1752 return 0;
1753 return timeline->max_sample_size;
1756 uint64_t lsmash_get_media_duration_from_media_timeline( lsmash_root_t *root, uint32_t track_ID )
1758 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1759 if( !timeline )
1760 return 0;
1761 return timeline->media_duration;
1764 isom_elst_entry_t *isom_timelime_get_explicit_timeline_map
1766 lsmash_root_t *root,
1767 uint32_t track_ID,
1768 uint32_t edit_number
1771 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1772 if( !timeline )
1773 return NULL;
1774 return lsmash_list_get_entry_data( timeline->edit_list, edit_number );
1777 uint32_t isom_timelime_count_explicit_timeline_map
1779 lsmash_root_t *root,
1780 uint32_t track_ID
1783 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1784 if( !timeline )
1785 return 0;
1786 return timeline->edit_list->entry_count;
1789 int lsmash_copy_timeline_map( lsmash_root_t *dst, uint32_t dst_track_ID, lsmash_root_t *src, uint32_t src_track_ID )
1791 if( isom_check_initializer_present( dst ) < 0
1792 || isom_check_initializer_present( src ) < 0 )
1793 return LSMASH_ERR_FUNCTION_PARAM;
1794 lsmash_file_t *dst_file = dst->file->initializer;
1795 isom_trak_t *dst_trak = isom_get_trak( dst_file, dst_track_ID );
1796 if( LSMASH_IS_NON_EXISTING_BOX( dst_file->moov->mvhd )
1797 || LSMASH_IS_NON_EXISTING_BOX( dst_trak->mdia->mdhd )
1798 || LSMASH_IS_NON_EXISTING_BOX( dst_trak->mdia->minf->stbl )
1799 || dst_file->moov->mvhd->timescale == 0
1800 || dst_trak->mdia->mdhd->timescale == 0 )
1801 return LSMASH_ERR_NAMELESS;
1802 if( LSMASH_IS_EXISTING_BOX( dst_trak->edts->elst ) )
1803 lsmash_list_remove_entries( dst_trak->edts->elst->list );
1804 uint32_t src_movie_timescale;
1805 uint32_t src_media_timescale;
1806 uint64_t src_track_duration;
1807 uint64_t src_media_duration;
1808 int32_t src_ctd_shift; /* Add timeline shift difference between src and dst to each media_time.
1809 * Therefore, call this function as later as possible. */
1810 lsmash_entry_t *src_entry = NULL;
1811 lsmash_file_t *src_file = src->file->initializer;
1812 isom_trak_t *src_trak = isom_get_trak( src_file, src_track_ID );
1813 int src_fragmented = !!(src_file->flags & LSMASH_FILE_MODE_FRAGMENTED);
1814 if( !src_trak->edts->elst->list
1815 || src_fragmented )
1817 /* Get from constructed timeline instead of boxes. */
1818 isom_timeline_t *src_timeline = isom_get_timeline( src, src_track_ID );
1819 if( src_timeline
1820 && src_timeline->movie_timescale
1821 && src_timeline->media_timescale )
1823 src_entry = src_timeline->edit_list->head;
1824 if( !src_entry )
1825 return 0;
1826 src_movie_timescale = src_timeline->movie_timescale;
1827 src_media_timescale = src_timeline->media_timescale;
1828 src_track_duration = src_timeline->track_duration;
1829 src_media_duration = src_timeline->media_duration;
1830 src_ctd_shift = src_timeline->ctd_shift;
1832 else if( !src_fragmented )
1833 return LSMASH_ERR_NAMELESS;
1835 if( !src_entry )
1837 if( LSMASH_IS_NON_EXISTING_BOX( src_file->moov->mvhd )
1838 || LSMASH_IS_NON_EXISTING_BOX( src_trak->tkhd )
1839 || LSMASH_IS_NON_EXISTING_BOX( src_trak->mdia->mdhd )
1840 || LSMASH_IS_NON_EXISTING_BOX( src_trak->mdia->minf->stbl )
1841 || src_file->moov->mvhd->timescale == 0
1842 || src_trak->mdia->mdhd->timescale == 0 )
1843 return LSMASH_ERR_NAMELESS;
1844 if( !src_trak->edts->elst->list
1845 || !src_trak->edts->elst->list->head )
1846 return 0;
1847 src_entry = src_trak->edts->elst->list->head;
1848 src_movie_timescale = src_file->moov->mvhd->timescale;
1849 src_media_timescale = src_trak->mdia->mdhd->timescale;
1850 src_track_duration = src_trak->tkhd->duration;
1851 src_media_duration = src_trak->mdia->mdhd->duration;
1852 src_ctd_shift = src_trak->mdia->minf->stbl->cslg->compositionToDTSShift;
1854 /* Generate the edit list if absent in the destination. */
1855 if( (LSMASH_IS_NON_EXISTING_BOX( dst_trak->edts ) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_edts( dst_trak ) ))
1856 || (LSMASH_IS_NON_EXISTING_BOX( dst_trak->edts->elst ) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_elst( dst_trak->edts ) )) )
1857 return LSMASH_ERR_NAMELESS;
1858 uint32_t dst_movie_timescale = dst_file->moov->mvhd->timescale;
1859 uint32_t dst_media_timescale = dst_trak->mdia->mdhd->timescale;
1860 int32_t dst_ctd_shift = dst_trak->mdia->minf->stbl->cslg->compositionToDTSShift;
1861 int32_t media_time_shift = src_ctd_shift - dst_ctd_shift;
1862 lsmash_entry_list_t *dst_list = dst_trak->edts->elst->list;
1863 while( src_entry )
1865 isom_elst_entry_t *src_data = (isom_elst_entry_t *)src_entry->data;
1866 if( !src_data )
1867 return LSMASH_ERR_NAMELESS;
1868 isom_elst_entry_t *dst_data = (isom_elst_entry_t *)lsmash_malloc( sizeof(isom_elst_entry_t) );
1869 if( !dst_data )
1870 return LSMASH_ERR_MEMORY_ALLOC;
1871 uint64_t segment_duration;
1872 if( src_data->segment_duration == 0 && !dst_file->fragment )
1873 /* The implicit duration edit is not suitable for non-fragmented movie file.
1874 * Set an appropriate duration from the source track. */
1875 segment_duration = src_fragmented
1876 ? (uint64_t)(src_media_duration * ((double)src_movie_timescale / src_media_timescale))
1877 : src_track_duration;
1878 else
1879 segment_duration = src_data->segment_duration;
1880 dst_data->segment_duration = segment_duration * ((double)dst_movie_timescale / src_movie_timescale) + 0.5;
1881 dst_data->media_rate = src_data->media_rate;
1882 if( src_data->media_time != ISOM_EDIT_MODE_EMPTY )
1883 dst_data->media_time = (src_data->media_time + media_time_shift) * ((double)dst_media_timescale / src_media_timescale) + 0.5;
1884 else
1885 dst_data->media_time = ISOM_EDIT_MODE_EMPTY;
1886 if( lsmash_list_add_entry( dst_list, dst_data ) < 0 )
1888 lsmash_free( dst_data );
1889 return LSMASH_ERR_MEMORY_ALLOC;
1891 src_entry = src_entry->next;
1893 return 0;
1896 int lsmash_set_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list )
1898 if( LSMASH_IS_NON_EXISTING_BOX( root )
1899 || LSMASH_IS_NON_EXISTING_BOX( root->file )
1900 || !ts_list )
1901 return -1;
1902 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1903 if( !timeline )
1904 return LSMASH_ERR_NAMELESS;
1905 if( timeline->info_list->entry_count == 0 )
1907 lsmash_log( timeline, LSMASH_LOG_ERROR, "Changing timestamps of LPCM track is not supported.\n" );
1908 return LSMASH_ERR_PATCH_WELCOME;
1910 if( ts_list->sample_count != timeline->info_list->entry_count )
1911 return LSMASH_ERR_INVALID_DATA; /* Number of samples must be same. */
1912 lsmash_media_ts_t *ts = ts_list->timestamp;
1913 if( ts[0].dts )
1914 return LSMASH_ERR_INVALID_DATA; /* DTS must start from value zero. */
1915 /* Update DTSs. */
1916 uint32_t sample_count = ts_list->sample_count;
1917 uint32_t i;
1918 if( timeline->info_list->entry_count > 1 )
1920 i = 1;
1921 lsmash_entry_t *entry = timeline->info_list->head;
1922 isom_sample_info_t *info = NULL;
1923 while( i < sample_count )
1925 info = (isom_sample_info_t *)entry->data;
1926 if( !info || (ts[i].dts < ts[i - 1].dts) )
1927 return LSMASH_ERR_INVALID_DATA;
1928 info->duration = ts[i].dts - ts[i - 1].dts;
1929 entry = entry->next;
1930 ++i;
1932 if( i > 1 )
1934 if( !entry
1935 || !entry->data )
1936 return LSMASH_ERR_INVALID_DATA;
1937 /* Copy the previous duration. */
1938 ((isom_sample_info_t *)entry->data)->duration = info->duration;
1940 else
1941 return LSMASH_ERR_INVALID_DATA; /* Irregular case: sample_count this timeline has is incorrect. */
1943 else /* still image */
1944 ((isom_sample_info_t *)timeline->info_list->head->data)->duration = UINT32_MAX;
1945 /* Update CTSs.
1946 * ToDo: hint track must not have any sample_offset. */
1947 i = 0;
1948 timeline->ctd_shift = 0;
1949 for( lsmash_entry_t *entry = timeline->info_list->head; entry; entry = entry->next )
1951 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1952 if( ts[i].cts != LSMASH_TIMESTAMP_UNDEFINED )
1954 if( (ts[i].cts + timeline->ctd_shift) < ts[i].dts )
1955 timeline->ctd_shift = ts[i].dts - ts[i].cts;
1956 info->offset = ts[i].cts - ts[i].dts;
1958 else
1959 info->offset = ISOM_NON_OUTPUT_SAMPLE_OFFSET;
1960 ++i;
1962 if( timeline->ctd_shift && (!root->file->qt_compatible || root->file->max_isom_version < 4) )
1963 return LSMASH_ERR_INVALID_DATA; /* Don't allow composition to decode timeline shift. */
1964 return 0;
1967 int lsmash_get_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list )
1969 if( !ts_list )
1970 return LSMASH_ERR_FUNCTION_PARAM;
1971 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1972 if( !timeline )
1973 return LSMASH_ERR_NAMELESS;
1974 uint32_t sample_count = timeline->info_list->entry_count;
1975 if( sample_count == 0 )
1977 ts_list->sample_count = 0;
1978 ts_list->timestamp = NULL;
1979 return 0;
1981 lsmash_media_ts_t *ts = lsmash_malloc( sample_count * sizeof(lsmash_media_ts_t) );
1982 if( !ts )
1983 return LSMASH_ERR_MEMORY_ALLOC;
1984 uint64_t dts = 0;
1985 uint32_t i = 0;
1986 if( timeline->info_list->entry_count )
1987 for( lsmash_entry_t *entry = timeline->info_list->head; entry; entry = entry->next )
1989 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1990 if( !info )
1992 lsmash_free( ts );
1993 return LSMASH_ERR_NAMELESS;
1995 ts[i].dts = dts;
1996 ts[i].cts = isom_make_cts( dts, info->offset, timeline->ctd_shift );
1997 dts += info->duration;
1998 ++i;
2000 else
2001 for( lsmash_entry_t *entry = timeline->bunch_list->head; entry; entry = entry->next )
2003 isom_lpcm_bunch_t *bunch = (isom_lpcm_bunch_t *)entry->data;
2004 if( !bunch )
2006 lsmash_free( ts );
2007 return LSMASH_ERR_NAMELESS;
2009 for( uint32_t j = 0; j < bunch->sample_count; j++ )
2011 ts[i].dts = dts;
2012 ts[i].cts = isom_make_cts( dts, bunch->offset, timeline->ctd_shift );
2013 dts += bunch->duration;
2014 ++i;
2017 ts_list->sample_count = sample_count;
2018 ts_list->timestamp = ts;
2019 return 0;
2022 void lsmash_delete_media_timestamps( lsmash_media_ts_list_t *ts_list )
2024 if( !ts_list )
2025 return;
2026 lsmash_freep( &ts_list->timestamp );
2027 ts_list->sample_count = 0;
2030 static int isom_compare_dts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b )
2032 int64_t diff = (int64_t)(a->dts - b->dts);
2033 return diff > 0 ? 1 : (diff == 0 ? 0 : -1);
2036 void lsmash_sort_timestamps_decoding_order( lsmash_media_ts_list_t *ts_list )
2038 if( !ts_list )
2039 return;
2040 qsort( ts_list->timestamp, ts_list->sample_count, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))isom_compare_dts );
2043 static int isom_compare_cts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b )
2045 int64_t diff = (int64_t)(a->cts - b->cts);
2046 return diff > 0 ? 1 : (diff == 0 ? 0 : -1);
2049 void lsmash_sort_timestamps_composition_order( lsmash_media_ts_list_t *ts_list )
2051 if( !ts_list )
2052 return;
2053 qsort( ts_list->timestamp, ts_list->sample_count, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))isom_compare_cts );
2056 int lsmash_get_max_sample_delay( lsmash_media_ts_list_t *ts_list, uint32_t *max_sample_delay )
2058 if( !ts_list || !max_sample_delay )
2059 return LSMASH_ERR_FUNCTION_PARAM;
2060 lsmash_media_ts_t *orig_ts = ts_list->timestamp;
2061 lsmash_media_ts_t *ts = lsmash_malloc( ts_list->sample_count * sizeof(lsmash_media_ts_t) );
2062 if( !ts )
2063 return LSMASH_ERR_MEMORY_ALLOC;
2064 ts_list->timestamp = ts;
2065 *max_sample_delay = 0;
2066 for( uint32_t i = 0; i < ts_list->sample_count; i++ )
2068 ts[i].cts = orig_ts[i].cts; /* for sorting */
2069 ts[i].dts = i;
2071 lsmash_sort_timestamps_composition_order( ts_list );
2072 for( uint32_t i = 0; i < ts_list->sample_count; i++ )
2073 if( i < ts[i].dts )
2075 uint32_t sample_delay = ts[i].dts - i;
2076 *max_sample_delay = LSMASH_MAX( *max_sample_delay, sample_delay );
2078 lsmash_free( ts );
2079 ts_list->timestamp = orig_ts;
2080 return 0;