timeline: Avoid NULL dereference if Data Reference Box is absent.
[L-SMASH.git] / core / timeline.c
blob4f5e3957c9e9ba07f666ead1247103b4aeebc3d3
1 /*****************************************************************************
2 * timeline.c:
3 *****************************************************************************
4 * Copyright (C) 2011-2015 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_init_entry_list( timeline->edit_list );
111 lsmash_init_entry_list( timeline->chunk_list );
112 lsmash_init_entry_list( timeline->info_list );
113 lsmash_init_entry_list( timeline->bunch_list );
114 return timeline;
117 void isom_timeline_destroy( isom_timeline_t *timeline )
119 if( !timeline )
120 return;
121 lsmash_remove_entries( timeline->edit_list, NULL );
122 lsmash_remove_entries( timeline->chunk_list, NULL ); /* chunk data must be already freed. */
123 lsmash_remove_entries( timeline->info_list, NULL );
124 lsmash_remove_entries( timeline->bunch_list, NULL );
125 lsmash_free( timeline );
128 void isom_remove_timelines( lsmash_file_t *file )
130 if( !file
131 || !file->timeline )
132 return;
133 lsmash_remove_list( file->timeline, isom_timeline_destroy );
136 void lsmash_destruct_timeline( lsmash_root_t *root, uint32_t track_ID )
138 if( track_ID == 0
139 || !root
140 || !root->file
141 || !root->file->timeline )
142 return;
143 for( lsmash_entry_t *entry = root->file->timeline->head; entry; entry = entry->next )
145 isom_timeline_t *timeline = (isom_timeline_t *)entry->data;
146 if( !timeline )
147 continue;
148 if( timeline->track_ID == track_ID )
150 lsmash_remove_entry_direct( root->file->timeline, entry, isom_timeline_destroy );
151 break;
156 int isom_timeline_set_track_ID
158 isom_timeline_t *timeline,
159 uint32_t track_ID
162 if( !timeline || track_ID == 0 )
163 return LSMASH_ERR_FUNCTION_PARAM;
164 timeline->track_ID = track_ID;
165 return 0;
168 int isom_timeline_set_movie_timescale
170 isom_timeline_t *timeline,
171 uint32_t movie_timescale
174 if( !timeline || movie_timescale == 0 )
175 return LSMASH_ERR_FUNCTION_PARAM;
176 timeline->movie_timescale = movie_timescale;
177 return 0;
180 int isom_timeline_set_media_timescale
182 isom_timeline_t *timeline,
183 uint32_t media_timescale
186 if( !timeline || media_timescale == 0 )
187 return LSMASH_ERR_FUNCTION_PARAM;
188 timeline->media_timescale = media_timescale;
189 return 0;
192 int isom_timeline_set_sample_count
194 isom_timeline_t *timeline,
195 uint32_t sample_count
198 if( !timeline || sample_count == 0 )
199 return LSMASH_ERR_FUNCTION_PARAM;
200 timeline->sample_count = sample_count;
201 return 0;
204 int isom_timeline_set_max_sample_size
206 isom_timeline_t *timeline,
207 uint32_t max_sample_size
210 if( !timeline || max_sample_size == 0 )
211 return LSMASH_ERR_FUNCTION_PARAM;
212 timeline->max_sample_size = max_sample_size;
213 return 0;
216 int isom_timeline_set_media_duration
218 isom_timeline_t *timeline,
219 uint32_t media_duration
222 if( !timeline || media_duration == 0 )
223 return LSMASH_ERR_FUNCTION_PARAM;
224 timeline->media_duration = media_duration;
225 return 0;
228 int isom_timeline_set_track_duration
230 isom_timeline_t *timeline,
231 uint32_t track_duration
234 if( !timeline || track_duration == 0 )
235 return LSMASH_ERR_FUNCTION_PARAM;
236 timeline->track_duration = track_duration;
237 return 0;
240 static void isom_get_qt_fixed_comp_audio_sample_quants
242 isom_timeline_t *timeline,
243 isom_sample_entry_t *description,
244 uint32_t *samples_per_packet,
245 uint32_t *constant_sample_size
248 isom_audio_entry_t *audio = (isom_audio_entry_t *)description;
249 if( audio->version == 0 )
251 uint32_t dummy;
252 if( !isom_get_implicit_qt_fixed_comp_audio_sample_quants( audio, samples_per_packet, constant_sample_size, &dummy ) )
254 /* LPCM */
255 if( !isom_is_lpcm_audio( audio ) )
256 lsmash_log( timeline, LSMASH_LOG_WARNING, "unsupported implicit sample table!\n" );
257 *samples_per_packet = 1;
258 *constant_sample_size = (audio->samplesize * audio->channelcount) / 8;
261 else if( audio->version == 1 )
263 *samples_per_packet = audio->samplesPerPacket;
264 *constant_sample_size = audio->bytesPerFrame;
266 else /* if( audio->version == 2 ) */
268 *samples_per_packet = audio->constLPCMFramesPerAudioPacket;
269 *constant_sample_size = audio->constBytesPerAudioPacket;
273 static int isom_is_qt_fixed_compressed_audio
275 isom_sample_entry_t *description
278 if( (description->manager & LSMASH_VIDEO_DESCRIPTION) || !isom_is_qt_audio( description->type ) )
279 return 0;
280 /* LPCM is a special case of fixed compression. */
281 return (((isom_audio_entry_t *)description)->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION);
284 static int isom_add_sample_info_entry( isom_timeline_t *timeline, isom_sample_info_t *src_info )
286 isom_sample_info_t *dst_info = lsmash_malloc( sizeof(isom_sample_info_t) );
287 if( !dst_info )
288 return LSMASH_ERR_MEMORY_ALLOC;
289 if( lsmash_add_entry( timeline->info_list, dst_info ) < 0 )
291 lsmash_free( dst_info );
292 return LSMASH_ERR_MEMORY_ALLOC;
294 *dst_info = *src_info;
295 return 0;
298 int isom_add_lpcm_bunch_entry( isom_timeline_t *timeline, isom_lpcm_bunch_t *src_bunch )
300 isom_lpcm_bunch_t *dst_bunch = lsmash_malloc( sizeof(isom_lpcm_bunch_t) );
301 if( !dst_bunch )
302 return LSMASH_ERR_MEMORY_ALLOC;
303 if( lsmash_add_entry( timeline->bunch_list, dst_bunch ) < 0 )
305 lsmash_free( dst_bunch );
306 return LSMASH_ERR_MEMORY_ALLOC;
308 *dst_bunch = *src_bunch;
309 return 0;
312 static int isom_add_portable_chunk_entry( isom_timeline_t *timeline, isom_portable_chunk_t *src_chunk )
314 isom_portable_chunk_t *dst_chunk = lsmash_malloc( sizeof(isom_portable_chunk_t) );
315 if( !dst_chunk )
316 return LSMASH_ERR_MEMORY_ALLOC;
317 if( lsmash_add_entry( timeline->chunk_list, dst_chunk ) < 0 )
319 lsmash_free( dst_chunk );
320 return LSMASH_ERR_MEMORY_ALLOC;
322 *dst_chunk = *src_chunk;
323 return 0;
326 static int isom_compare_lpcm_sample_info( isom_lpcm_bunch_t *bunch, isom_sample_info_t *info )
328 return info->duration != bunch->duration
329 || info->offset != bunch->offset
330 || info->length != bunch->length
331 || info->index != bunch->index
332 || info->chunk != bunch->chunk;
335 static void isom_update_bunch( isom_lpcm_bunch_t *bunch, isom_sample_info_t *info )
337 bunch->pos = info->pos;
338 bunch->duration = info->duration;
339 bunch->offset = info->offset;
340 bunch->length = info->length;
341 bunch->index = info->index;
342 bunch->chunk = info->chunk;
343 bunch->prop = info->prop;
344 bunch->sample_count = 1;
347 static isom_lpcm_bunch_t *isom_get_bunch( isom_timeline_t *timeline, uint32_t sample_number )
349 if( sample_number >= timeline->last_accessed_lpcm_bunch_first_sample_number
350 && sample_number < timeline->last_accessed_lpcm_bunch_first_sample_number + timeline->last_accessed_lpcm_bunch_sample_count )
351 /* Get from the last accessed LPCM bunch. */
352 return (isom_lpcm_bunch_t *)lsmash_get_entry_data( timeline->bunch_list, timeline->last_accessed_lpcm_bunch_number );
353 uint32_t first_sample_number_in_next_bunch;
354 uint32_t bunch_number = 1;
355 uint64_t bunch_dts;
356 if( timeline->last_accessed_lpcm_bunch_first_sample_number
357 && timeline->last_accessed_lpcm_bunch_first_sample_number <= sample_number )
359 first_sample_number_in_next_bunch = timeline->last_accessed_lpcm_bunch_first_sample_number + timeline->last_accessed_lpcm_bunch_sample_count;
360 bunch_number += timeline->last_accessed_lpcm_bunch_number;
361 bunch_dts = timeline->last_accessed_lpcm_bunch_dts
362 + timeline->last_accessed_lpcm_bunch_duration * timeline->last_accessed_lpcm_bunch_sample_count;
364 else
366 /* Seek from the first LPCM bunch. */
367 first_sample_number_in_next_bunch = 1;
368 bunch_dts = 0;
370 isom_lpcm_bunch_t *bunch = (isom_lpcm_bunch_t *)lsmash_get_entry_data( timeline->bunch_list, bunch_number++ );
371 if( !bunch )
372 return NULL;
373 first_sample_number_in_next_bunch += bunch->sample_count;
374 while( sample_number >= first_sample_number_in_next_bunch )
376 bunch_dts += bunch->duration * bunch->sample_count;
377 bunch = (isom_lpcm_bunch_t *)lsmash_get_entry_data( timeline->bunch_list, bunch_number++ );
378 if( !bunch )
379 return NULL;
380 first_sample_number_in_next_bunch += bunch->sample_count;
382 timeline->last_accessed_lpcm_bunch_dts = bunch_dts;
383 timeline->last_accessed_lpcm_bunch_number = bunch_number - 1;
384 timeline->last_accessed_lpcm_bunch_duration = bunch->duration;
385 timeline->last_accessed_lpcm_bunch_sample_count = bunch->sample_count;
386 timeline->last_accessed_lpcm_bunch_first_sample_number = first_sample_number_in_next_bunch - bunch->sample_count;
387 return bunch;
390 static int isom_get_dts_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts )
392 if( sample_number == timeline->last_accessed_sample_number )
393 *dts = timeline->last_accessed_sample_dts;
394 else if( sample_number == 1 )
395 *dts = 0;
396 else if( sample_number == timeline->last_accessed_sample_number + 1 )
398 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, timeline->last_accessed_sample_number );
399 if( !info )
400 return LSMASH_ERR_NAMELESS;
401 *dts = timeline->last_accessed_sample_dts + info->duration;
403 else if( sample_number == timeline->last_accessed_sample_number - 1 )
405 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, timeline->last_accessed_sample_number - 1 );
406 if( !info )
407 return LSMASH_ERR_NAMELESS;
408 *dts = timeline->last_accessed_sample_dts - info->duration;
410 else
412 *dts = 0;
413 uint32_t distance = sample_number - 1;
414 lsmash_entry_t *entry;
415 for( entry = timeline->info_list->head; entry; entry = entry->next )
417 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
418 if( !info )
419 return LSMASH_ERR_NAMELESS;
420 if( distance-- == 0 )
421 break;
422 *dts += info->duration;
424 if( !entry )
425 return LSMASH_ERR_NAMELESS;
427 /* Note: last_accessed_sample_number is always updated together with last_accessed_sample_dts, and vice versa. */
428 timeline->last_accessed_sample_dts = *dts;
429 timeline->last_accessed_sample_number = sample_number;
430 return 0;
433 static int isom_get_cts_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts )
435 int ret = isom_get_dts_from_info_list( timeline, sample_number, cts );
436 if( ret < 0 )
437 return ret;
438 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
439 if( !info )
440 return LSMASH_ERR_NAMELESS;
441 *cts = timeline->ctd_shift ? (*cts + (int32_t)info->offset) : (*cts + info->offset);
442 return 0;
445 static int isom_get_dts_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts )
447 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
448 if( !bunch )
449 return LSMASH_ERR_NAMELESS;
450 *dts = timeline->last_accessed_lpcm_bunch_dts + (sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number) * bunch->duration;
451 return 0;
454 static int isom_get_cts_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts )
456 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
457 if( !bunch )
458 return LSMASH_ERR_NAMELESS;
459 *cts = timeline->last_accessed_lpcm_bunch_dts + (sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number) * bunch->duration + bunch->offset;
460 return 0;
463 static int isom_get_sample_duration_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration )
465 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
466 if( !info )
467 return LSMASH_ERR_NAMELESS;
468 *sample_duration = info->duration;
469 return 0;
472 static int isom_get_sample_duration_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration )
474 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
475 if( !bunch )
476 return LSMASH_ERR_NAMELESS;
477 *sample_duration = bunch->duration;
478 return 0;
481 static int isom_check_sample_existence_in_info_list( isom_timeline_t *timeline, uint32_t sample_number )
483 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
484 if( !info || !info->chunk )
485 return 0;
486 return !!info->chunk->file;
489 static int isom_check_sample_existence_in_bunch_list( isom_timeline_t *timeline, uint32_t sample_number )
491 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
492 if( !bunch || !bunch->chunk )
493 return 0;
494 return !!bunch->chunk->file;
497 static lsmash_sample_t *isom_read_sample_data_from_stream
499 lsmash_file_t *file,
500 isom_timeline_t *timeline,
501 uint32_t sample_length,
502 uint64_t sample_pos
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 = timeline->ctd_shift ? (sample->dts + (int32_t)bunch->offset) : (sample->dts + bunch->offset);
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_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 = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset);
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 = timeline->ctd_shift ? (sample->dts + (int32_t)bunch->offset) : (sample->dts + bunch->offset);
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_get_entry_data( timeline->info_list, sample_number );
586 if( !info )
587 return LSMASH_ERR_NAMELESS;
588 sample->dts = dts;
589 sample->cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset);
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_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( 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_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_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( !file->moov
759 || !file->moov->mvhd
760 || file->moov->mvhd->timescale == 0 )
761 return LSMASH_ERR_INVALID_DATA;
762 /* Get track by track_ID. */
763 isom_trak_t *trak = isom_get_trak( file, track_ID );
764 if( !trak
765 || !trak->tkhd
766 || !trak->mdia
767 || !trak->mdia->mdhd
768 || trak->mdia->mdhd->timescale == 0
769 || !trak->mdia->minf
770 || !trak->mdia->minf->stbl )
771 return LSMASH_ERR_INVALID_DATA;
772 /* Create a timeline list if it doesn't exist. */
773 if( !file->timeline )
775 file->timeline = lsmash_create_entry_list();
776 if( !file->timeline )
777 return LSMASH_ERR_MEMORY_ALLOC;
779 /* Create a timeline. */
780 isom_timeline_t *timeline = isom_timeline_create();
781 if( !timeline )
782 return LSMASH_ERR_MEMORY_ALLOC;
783 timeline->track_ID = track_ID;
784 timeline->movie_timescale = file->moov->mvhd->timescale;
785 timeline->media_timescale = trak->mdia->mdhd->timescale;
786 timeline->track_duration = trak->tkhd->duration;
787 /* Preparation for construction. */
788 isom_elst_t *elst = trak->edts ? trak->edts->elst : NULL;
789 isom_minf_t *minf = trak->mdia->minf;
790 isom_dref_t *dref = minf->dinf ? minf->dinf->dref : NULL;
791 isom_stbl_t *stbl = minf->stbl;
792 isom_stsd_t *stsd = stbl->stsd;
793 isom_stts_t *stts = stbl->stts;
794 isom_ctts_t *ctts = stbl->ctts;
795 isom_stss_t *stss = stbl->stss;
796 isom_stps_t *stps = stbl->stps;
797 isom_sdtp_t *sdtp = stbl->sdtp;
798 isom_stsc_t *stsc = stbl->stsc;
799 isom_stsz_t *stsz = stbl->stsz;
800 isom_stco_t *stco = stbl->stco;
801 isom_sgpd_t *sgpd_rap = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
802 isom_sbgp_t *sbgp_rap = isom_get_sample_to_group ( stbl, ISOM_GROUP_TYPE_RAP );
803 isom_sgpd_t *sgpd_roll = isom_get_roll_recovery_sample_group_description( &stbl->sgpd_list );
804 isom_sbgp_t *sbgp_roll = isom_get_roll_recovery_sample_to_group ( &stbl->sbgp_list );
805 lsmash_entry_t *elst_entry = elst && elst->list ? elst->list->head : NULL;
806 lsmash_entry_t *stts_entry = stts && stts->list ? stts->list->head : NULL;
807 lsmash_entry_t *ctts_entry = ctts && ctts->list ? ctts->list->head : NULL;
808 lsmash_entry_t *stss_entry = stss && stss->list ? stss->list->head : NULL;
809 lsmash_entry_t *stps_entry = stps && stps->list ? stps->list->head : NULL;
810 lsmash_entry_t *sdtp_entry = sdtp && sdtp->list ? sdtp->list->head : NULL;
811 lsmash_entry_t *stsz_entry = stsz && stsz->list ? stsz->list->head : NULL;
812 lsmash_entry_t *stsc_entry = stsc && stsc->list ? stsc->list->head : NULL;
813 lsmash_entry_t *stco_entry = stco && stco->list ? stco->list->head : NULL;
814 lsmash_entry_t *sbgp_roll_entry = sbgp_roll && sbgp_roll->list ? sbgp_roll->list->head : NULL;
815 lsmash_entry_t *sbgp_rap_entry = sbgp_rap && 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 = (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_get_entry_data( &stsd->list, stsc_data ? stsc_data->sample_description_index : 1 );
823 if( !description )
824 goto fail;
825 lsmash_entry_list_t *dref_list = dref ? &dref->list : NULL;
826 isom_dref_entry_t *dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( dref_list, description->data_reference_index );
827 int all_sync = !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 samples_per_packet;
846 uint32_t constant_sample_size;
847 if( is_qt_fixed_comp_audio )
848 isom_get_qt_fixed_comp_audio_sample_quants( timeline, description, &samples_per_packet, &constant_sample_size );
849 else
851 samples_per_packet = 1;
852 constant_sample_size = stsz->sample_size;
854 uint32_t sample_number = samples_per_packet;
855 uint32_t sample_number_in_chunk = samples_per_packet;
856 /* Copy edits. */
857 while( elst_entry )
859 isom_elst_entry_t *edit = (isom_elst_entry_t *)lsmash_memdup( elst_entry->data, sizeof(isom_elst_entry_t) );
860 if( !edit
861 || lsmash_add_entry( timeline->edit_list, edit ) < 0 )
863 err = LSMASH_ERR_MEMORY_ALLOC;
864 goto fail;
866 elst_entry = elst_entry->next;
868 /* Check what the first 2-bits of sample dependency means.
869 * This check is for chimera of ISO Base Media and QTFF. */
870 if( iso_sdtp && sdtp_entry )
872 while( sdtp_entry )
874 isom_sdtp_entry_t *sdtp_data = (isom_sdtp_entry_t *)sdtp_entry->data;
875 if( !sdtp_data )
876 goto fail;
877 if( sdtp_data->is_leading > 1 )
878 break; /* Apparently, it's defined under ISO Base Media. */
879 if( (sdtp_data->is_leading == 1) && (sdtp_data->sample_depends_on == ISOM_SAMPLE_IS_INDEPENDENT) )
881 /* Obviously, it's not defined under ISO Base Media. */
882 iso_sdtp = 0;
883 break;
885 sdtp_entry = sdtp_entry->next;
887 sdtp_entry = sdtp->list->head;
889 /**--- Construct media timeline. ---**/
890 isom_portable_chunk_t chunk;
891 chunk.data_offset = data_offset;
892 chunk.length = 0;
893 chunk.number = chunk_number;
894 chunk.file = (!dref_entry || !dref_entry->ref_file) ? NULL : dref_entry->ref_file;
895 if( (err = isom_add_portable_chunk_entry( timeline, &chunk )) < 0 )
896 goto fail;
897 uint32_t distance = NO_RANDOM_ACCESS_POINT;
898 uint32_t last_duration = UINT32_MAX;
899 uint32_t packet_number = 1;
900 isom_lpcm_bunch_t bunch = { 0 };
901 while( sample_number <= stsz->sample_count )
903 isom_sample_info_t info = { 0 };
904 /* Get sample duration and sample offset. */
905 for( uint32_t i = 0; i < samples_per_packet; i++ )
907 /* sample duration */
908 if( stts_entry )
910 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
911 if( !stts_data )
912 goto fail;
913 isom_increment_sample_number_in_entry( &sample_number_in_stts_entry, &stts_entry, stts_data->sample_count );
914 last_duration = stts_data->sample_delta;
916 info.duration += last_duration;
917 dts += last_duration;
918 /* sample offset */
919 uint32_t sample_offset;
920 if( ctts_entry )
922 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
923 if( !ctts_data )
924 goto fail;
925 isom_increment_sample_number_in_entry( &sample_number_in_ctts_entry, &ctts_entry, ctts_data->sample_count );
926 sample_offset = ctts_data->sample_offset;
927 if( allow_negative_sample_offset )
929 uint64_t cts = dts + (int32_t)sample_offset;
930 if( (cts + timeline->ctd_shift) < dts )
931 timeline->ctd_shift = dts - cts;
934 else
935 sample_offset = 0;
936 if( i == 0 )
937 info.offset = sample_offset;
939 timeline->media_duration += info.duration;
940 if( !is_qt_fixed_comp_audio )
942 /* Check whether sync sample or not. */
943 if( stss_entry )
945 isom_stss_entry_t *stss_data = (isom_stss_entry_t *)stss_entry->data;
946 if( !stss_data )
947 goto fail;
948 if( sample_number == stss_data->sample_number )
950 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
951 stss_entry = stss_entry->next;
952 distance = 0;
955 else if( all_sync )
956 /* Don't reset distance as 0 since MDCT-based audio frames need pre-roll for correct presentation
957 * though all of them could be marked as a sync sample. */
958 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
959 /* Check whether partial sync sample or not. */
960 if( stps_entry )
962 isom_stps_entry_t *stps_data = (isom_stps_entry_t *)stps_entry->data;
963 if( !stps_data )
964 goto fail;
965 if( sample_number == stps_data->sample_number )
967 info.prop.ra_flags |= QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC | QT_SAMPLE_RANDOM_ACCESS_FLAG_RAP;
968 stps_entry = stps_entry->next;
969 distance = 0;
972 /* Get sample dependency info. */
973 if( sdtp_entry )
975 isom_sdtp_entry_t *sdtp_data = (isom_sdtp_entry_t *)sdtp_entry->data;
976 if( !sdtp_data )
977 goto fail;
978 if( iso_sdtp )
979 info.prop.leading = sdtp_data->is_leading;
980 else
981 info.prop.allow_earlier = sdtp_data->is_leading;
982 info.prop.independent = sdtp_data->sample_depends_on;
983 info.prop.disposable = sdtp_data->sample_is_depended_on;
984 info.prop.redundant = sdtp_data->sample_has_redundancy;
985 sdtp_entry = sdtp_entry->next;
987 /* Get roll recovery grouping info. */
988 if( sbgp_roll_entry
989 && isom_get_roll_recovery_grouping_info( timeline,
990 &sbgp_roll_entry, sgpd_roll, NULL,
991 &sample_number_in_sbgp_roll_entry,
992 &info, sample_number ) < 0 )
993 goto fail;
994 info.prop.post_roll.identifier = sample_number;
995 /* Get random access point grouping info. */
996 if( sbgp_rap_entry
997 && isom_get_random_access_point_grouping_info( timeline,
998 &sbgp_rap_entry, sgpd_rap, NULL,
999 &sample_number_in_sbgp_rap_entry,
1000 &info, &distance ) < 0 )
1001 goto fail;
1002 /* Set up distance from the previous random access point. */
1003 if( distance != NO_RANDOM_ACCESS_POINT )
1005 if( info.prop.pre_roll.distance == 0 )
1006 info.prop.pre_roll.distance = distance;
1007 ++distance;
1010 else
1011 /* All uncompressed and non-variable compressed audio frame is a sync sample. */
1012 info.prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1013 /* Get size of sample in the stream. */
1014 if( is_qt_fixed_comp_audio || !stsz_entry )
1015 info.length = constant_sample_size;
1016 else
1018 if( !stsz_entry->data )
1019 goto fail;
1020 info.length = ((isom_stsz_entry_t *)stsz_entry->data)->entry_size;
1021 stsz_entry = stsz_entry->next;
1023 timeline->max_sample_size = LSMASH_MAX( timeline->max_sample_size, info.length );
1024 /* Get chunk info. */
1025 info.pos = data_offset;
1026 info.index = stsc_data->sample_description_index;
1027 info.chunk = (isom_portable_chunk_t *)timeline->chunk_list->tail->data;
1028 offset_from_chunk += info.length;
1029 if( sample_number_in_chunk == stsc_data->samples_per_chunk )
1031 /* Set the length of the last chunk. */
1032 if( info.chunk )
1033 info.chunk->length = offset_from_chunk;
1034 /* Move the next chunk. */
1035 if( stco_entry )
1036 stco_entry = stco_entry->next;
1037 if( stco_entry
1038 && stco_entry->data )
1039 data_offset = large_presentation
1040 ? ((isom_co64_entry_t *)stco_entry->data)->chunk_offset
1041 : ((isom_stco_entry_t *)stco_entry->data)->chunk_offset;
1042 chunk.data_offset = data_offset;
1043 chunk.length = 0;
1044 chunk.number = ++chunk_number;
1045 offset_from_chunk = 0;
1046 /* Check if the next entry is broken. */
1047 while( next_stsc_entry && chunk_number > ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk )
1049 /* Just skip broken next entry. */
1050 lsmash_log( timeline, LSMASH_LOG_WARNING, "ignore broken entry in Sample To Chunk Box.\n" );
1051 lsmash_log( timeline, LSMASH_LOG_WARNING, "timeline might be corrupted.\n" );
1052 next_stsc_entry = next_stsc_entry->next;
1053 if( next_stsc_entry
1054 && !next_stsc_entry->data )
1055 goto fail;
1057 /* Check if the next chunk belongs to the next sequence of chunks. */
1058 if( next_stsc_entry && chunk_number == ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk )
1060 stsc_entry = next_stsc_entry;
1061 next_stsc_entry = next_stsc_entry->next;
1062 if( next_stsc_entry
1063 && !next_stsc_entry->data )
1064 goto fail;
1065 stsc_data = (isom_stsc_entry_t *)stsc_entry->data;
1066 /* Update sample description. */
1067 description = (isom_sample_entry_t *)lsmash_get_entry_data( &stsd->list, stsc_data->sample_description_index );
1068 is_lpcm_audio = description ? isom_is_lpcm_audio( description ) : 0;
1069 is_qt_fixed_comp_audio = description ? isom_is_qt_fixed_compressed_audio( description ) : 0;
1070 if( is_qt_fixed_comp_audio )
1071 isom_get_qt_fixed_comp_audio_sample_quants( timeline, description, &samples_per_packet, &constant_sample_size );
1072 else
1074 samples_per_packet = 1;
1075 constant_sample_size = stsz->sample_size;
1077 /* Reference media data. */
1078 dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( dref_list, description ? description->data_reference_index : 0 );
1079 chunk.file = (!dref_entry || !dref_entry->ref_file) ? NULL : dref_entry->ref_file;
1081 sample_number_in_chunk = samples_per_packet;
1082 if( (err = isom_add_portable_chunk_entry( timeline, &chunk )) < 0 )
1083 goto fail;
1085 else
1087 data_offset += info.length;
1088 sample_number_in_chunk += samples_per_packet;
1090 /* OK. Let's add its info. */
1091 if( is_lpcm_audio )
1093 if( sample_number == samples_per_packet )
1094 isom_update_bunch( &bunch, &info );
1095 else if( isom_compare_lpcm_sample_info( &bunch, &info ) )
1097 if( (err = isom_add_lpcm_bunch_entry( timeline, &bunch )) < 0 )
1098 goto fail;
1099 isom_update_bunch( &bunch, &info );
1101 else
1102 ++ bunch.sample_count;
1104 else if( (err = isom_add_sample_info_entry( timeline, &info )) < 0 )
1105 goto fail;
1106 if( timeline->info_list->entry_count && timeline->bunch_list->entry_count )
1108 lsmash_log( timeline, LSMASH_LOG_ERROR, "LPCM + non-LPCM track is not supported.\n" );
1109 err = LSMASH_ERR_PATCH_WELCOME;
1110 goto fail;
1112 sample_number += samples_per_packet;
1113 packet_number += 1;
1115 isom_portable_chunk_t *last_chunk = lsmash_get_entry_data( timeline->chunk_list, timeline->chunk_list->entry_count );
1116 if( last_chunk )
1118 if( offset_from_chunk )
1119 last_chunk->length = offset_from_chunk;
1120 else
1122 /* Remove the last invalid chunk. */
1123 lsmash_remove_entry( timeline->chunk_list, timeline->chunk_list->entry_count, NULL );
1124 --chunk_number;
1127 uint32_t sample_count = packet_number - 1;
1128 if( movie_fragments_present )
1130 isom_tfra_t *tfra = isom_get_tfra( file->mfra, track_ID );
1131 lsmash_entry_t *tfra_entry = tfra && tfra->list ? tfra->list->head : NULL;
1132 isom_tfra_location_time_entry_t *rap = tfra_entry ? (isom_tfra_location_time_entry_t *)tfra_entry->data : NULL;
1133 chunk.data_offset = 0;
1134 chunk.length = 0;
1135 /* Movie fragments */
1136 for( lsmash_entry_t *moof_entry = file->moof_list.head; moof_entry; moof_entry = moof_entry->next )
1138 isom_moof_t *moof = (isom_moof_t *)moof_entry->data;
1139 if( !moof )
1140 goto fail;
1141 uint64_t last_sample_end_pos = 0;
1142 /* Track fragments */
1143 uint32_t traf_number = 1;
1144 for( lsmash_entry_t *traf_entry = moof->traf_list.head; traf_entry; traf_entry = traf_entry->next )
1146 isom_traf_t *traf = (isom_traf_t *)traf_entry->data;
1147 if( !traf )
1148 goto fail;
1149 isom_tfhd_t *tfhd = traf->tfhd;
1150 if( !tfhd )
1151 goto fail;
1152 isom_trex_t *trex = isom_get_trex( file->moov->mvex, tfhd->track_ID );
1153 if( !trex )
1154 goto fail;
1155 /* Ignore ISOM_TF_FLAGS_DURATION_IS_EMPTY flag even if set. */
1156 if( !traf->trun_list.head )
1158 ++traf_number;
1159 continue;
1161 /* Get base_data_offset. */
1162 uint64_t base_data_offset;
1163 if( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT )
1164 base_data_offset = tfhd->base_data_offset;
1165 else if( (tfhd->flags & ISOM_TF_FLAGS_DEFAULT_BASE_IS_MOOF) || traf_entry == moof->traf_list.head )
1166 base_data_offset = moof->pos;
1167 else
1168 base_data_offset = last_sample_end_pos;
1169 /* sample grouping */
1170 isom_sgpd_t *sgpd_frag_rap;
1171 isom_sgpd_t *sgpd_frag_roll;
1172 sgpd_frag_rap = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_RAP );
1173 sbgp_rap = isom_get_fragment_sample_to_group ( traf, ISOM_GROUP_TYPE_RAP );
1174 sbgp_rap_entry = sbgp_rap && sbgp_rap->list ? sbgp_rap->list->head : NULL;
1175 sgpd_frag_roll = isom_get_roll_recovery_sample_group_description( &traf->sgpd_list );
1176 sbgp_roll = isom_get_roll_recovery_sample_to_group ( &traf->sbgp_list );
1177 sbgp_roll_entry = sbgp_roll && sbgp_roll->list ? sbgp_roll->list->head : NULL;
1178 int need_data_offset_only = (tfhd->track_ID != track_ID);
1179 /* Track runs */
1180 uint32_t trun_number = 1;
1181 for( lsmash_entry_t *trun_entry = traf->trun_list.head; trun_entry; trun_entry = trun_entry->next )
1183 isom_trun_t *trun = (isom_trun_t *)trun_entry->data;
1184 if( !trun )
1185 goto fail;
1186 if( trun->sample_count == 0 )
1188 ++trun_number;
1189 continue;
1191 /* Get data_offset. */
1192 if( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT )
1193 data_offset = trun->data_offset + base_data_offset;
1194 else if( trun_entry == traf->trun_list.head )
1195 data_offset = base_data_offset;
1196 else
1197 data_offset = last_sample_end_pos;
1198 /* */
1199 uint32_t sample_description_index = 0;
1200 isom_sdtp_entry_t *sdtp_data = NULL;
1201 if( !need_data_offset_only )
1203 /* Get sample_description_index of this track fragment. */
1204 if( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT )
1205 sample_description_index = tfhd->sample_description_index;
1206 else
1207 sample_description_index = trex->default_sample_description_index;
1208 description = (isom_sample_entry_t *)lsmash_get_entry_data( &stsd->list, sample_description_index );
1209 is_lpcm_audio = description ? isom_is_lpcm_audio( description ) : 0;
1210 /* Reference media data. */
1211 dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( dref_list, description ? description->data_reference_index : 0 );
1212 lsmash_file_t *ref_file = (!dref_entry || !dref_entry->ref_file) ? NULL : dref_entry->ref_file;
1213 /* Each track run can be considered as a chunk.
1214 * Here, we consider physically consecutive track runs as one chunk. */
1215 if( chunk.data_offset + chunk.length != data_offset || chunk.file != ref_file )
1217 chunk.data_offset = data_offset;
1218 chunk.length = 0;
1219 chunk.number = ++chunk_number;
1220 chunk.file = ref_file;
1221 if( (err = isom_add_portable_chunk_entry( timeline, &chunk )) < 0 )
1222 goto fail;
1224 /* Get dependency info for this track fragment. */
1225 sdtp_entry = traf->sdtp && traf->sdtp->list ? traf->sdtp->list->head : NULL;
1226 sdtp_data = sdtp_entry && sdtp_entry->data ? (isom_sdtp_entry_t *)sdtp_entry->data : NULL;
1228 /* Get info of each sample. */
1229 lsmash_entry_t *row_entry = trun->optional && trun->optional->head ? trun->optional->head : NULL;
1230 sample_number = 1;
1231 while( sample_number <= trun->sample_count )
1233 isom_sample_info_t info = { 0 };
1234 isom_trun_optional_row_t *row = row_entry && row_entry->data ? (isom_trun_optional_row_t *)row_entry->data : NULL;
1235 /* Get sample_size */
1236 if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT) )
1237 info.length = row->sample_size;
1238 else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT )
1239 info.length = tfhd->default_sample_size;
1240 else
1241 info.length = trex->default_sample_size;
1242 if( !need_data_offset_only )
1244 info.pos = data_offset;
1245 info.index = sample_description_index;
1246 info.chunk = (isom_portable_chunk_t *)timeline->chunk_list->tail->data;
1247 info.chunk->length += info.length;
1248 /* Get sample_duration. */
1249 if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT) )
1250 info.duration = row->sample_duration;
1251 else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT )
1252 info.duration = tfhd->default_sample_duration;
1253 else
1254 info.duration = trex->default_sample_duration;
1255 /* Get composition time offset. */
1256 if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT) )
1258 info.offset = row->sample_composition_time_offset;
1259 /* Check composition to decode timeline shift. */
1260 if( file->max_isom_version >= 6 && trun->version != 0 )
1262 uint64_t cts = dts + (int32_t)info.offset;
1263 if( (cts + timeline->ctd_shift) < dts )
1264 timeline->ctd_shift = dts - cts;
1267 else
1268 info.offset = 0;
1269 dts += info.duration;
1270 /* Update media duration and maximun sample size. */
1271 timeline->media_duration += info.duration;
1272 timeline->max_sample_size = LSMASH_MAX( timeline->max_sample_size, info.length );
1273 if( !is_lpcm_audio )
1275 /* Get sample_flags. */
1276 isom_sample_flags_t sample_flags;
1277 if( sample_number == 1 && (trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT) )
1278 sample_flags = trun->first_sample_flags;
1279 else if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT) )
1280 sample_flags = row->sample_flags;
1281 else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT )
1282 sample_flags = tfhd->default_sample_flags;
1283 else
1284 sample_flags = trex->default_sample_flags;
1285 if( sdtp_data )
1287 /* Independent and Disposable Samples Box overrides the information from sample_flags.
1288 * There is no description in the specification about this, but the intention should be such a thing.
1289 * The ground is that sample_flags is placed in media layer
1290 * while Independent and Disposable Samples Box is placed in track or presentation layer. */
1291 info.prop.leading = sdtp_data->is_leading;
1292 info.prop.independent = sdtp_data->sample_depends_on;
1293 info.prop.disposable = sdtp_data->sample_is_depended_on;
1294 info.prop.redundant = sdtp_data->sample_has_redundancy;
1295 if( sdtp_entry )
1296 sdtp_entry = sdtp_entry->next;
1297 sdtp_data = sdtp_entry ? (isom_sdtp_entry_t *)sdtp_entry->data : NULL;
1299 else
1301 info.prop.leading = sample_flags.is_leading;
1302 info.prop.independent = sample_flags.sample_depends_on;
1303 info.prop.disposable = sample_flags.sample_is_depended_on;
1304 info.prop.redundant = sample_flags.sample_has_redundancy;
1306 /* Check this sample is a sync sample or not.
1307 * Note: all sync sample shall be independent. */
1308 if( !sample_flags.sample_is_non_sync_sample
1309 && info.prop.independent != ISOM_SAMPLE_IS_NOT_INDEPENDENT )
1311 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1312 distance = 0;
1314 /* Get roll recovery grouping info. */
1315 uint32_t roll_id = sample_count + sample_number;
1316 if( sbgp_roll_entry
1317 && isom_get_roll_recovery_grouping_info( timeline,
1318 &sbgp_roll_entry, sgpd_roll, sgpd_frag_roll,
1319 &sample_number_in_sbgp_roll_entry,
1320 &info, roll_id ) < 0 )
1321 goto fail;
1322 info.prop.post_roll.identifier = roll_id;
1323 /* Get random access point grouping info. */
1324 if( sbgp_rap_entry
1325 && isom_get_random_access_point_grouping_info( timeline,
1326 &sbgp_rap_entry, sgpd_rap, sgpd_frag_rap,
1327 &sample_number_in_sbgp_rap_entry,
1328 &info, &distance ) < 0 )
1329 goto fail;
1330 /* Get the location of the sync sample from 'tfra' if it is not set up yet.
1331 * Note: there is no guarantee that its entries are placed in a specific order. */
1332 if( tfra )
1334 if( tfra->number_of_entry == 0
1335 && info.prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1336 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1337 if( rap
1338 && rap->moof_offset == moof->pos
1339 && rap->traf_number == traf_number
1340 && rap->trun_number == trun_number
1341 && rap->sample_number == sample_number )
1343 if( info.prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1344 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1345 if( tfra_entry )
1346 tfra_entry = tfra_entry->next;
1347 rap = tfra_entry ? (isom_tfra_location_time_entry_t *)tfra_entry->data : NULL;
1350 /* Set up distance from the previous random access point. */
1351 if( distance != NO_RANDOM_ACCESS_POINT )
1353 if( info.prop.pre_roll.distance == 0 )
1354 info.prop.pre_roll.distance = distance;
1355 ++distance;
1357 /* OK. Let's add its info. */
1358 if( (err = isom_add_sample_info_entry( timeline, &info )) < 0 )
1359 goto fail;
1361 else
1363 /* All LPCMFrame is a sync sample. */
1364 info.prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1365 /* OK. Let's add its info. */
1366 if( sample_count == 0 && sample_number == 1 )
1367 isom_update_bunch( &bunch, &info );
1368 else if( isom_compare_lpcm_sample_info( &bunch, &info ) )
1370 if( (err = isom_add_lpcm_bunch_entry( timeline, &bunch )) < 0 )
1371 goto fail;
1372 isom_update_bunch( &bunch, &info );
1374 else
1375 ++ bunch.sample_count;
1377 if( timeline-> info_list->entry_count
1378 && timeline->bunch_list->entry_count )
1380 lsmash_log( timeline, LSMASH_LOG_ERROR, "LPCM + non-LPCM track is not supported.\n" );
1381 err = LSMASH_ERR_PATCH_WELCOME;
1382 goto fail;
1385 data_offset += info.length;
1386 last_sample_end_pos = data_offset;
1387 if( row_entry )
1388 row_entry = row_entry->next;
1389 ++sample_number;
1391 if( !need_data_offset_only )
1392 sample_count += sample_number - 1;
1393 ++trun_number;
1394 } /* Track runs */
1395 ++traf_number;
1396 } /* Track fragments */
1397 } /* Movie fragments */
1399 else if( timeline->chunk_list->entry_count == 0 )
1400 goto fail; /* No samples in this track. */
1401 if( bunch.sample_count && (err = isom_add_lpcm_bunch_entry( timeline, &bunch )) < 0 )
1402 goto fail;
1403 if( (err = lsmash_add_entry( file->timeline, timeline )) < 0 )
1404 goto fail;
1405 /* Finish timeline construction. */
1406 timeline->sample_count = sample_count;
1407 if( timeline->info_list->entry_count )
1408 isom_timeline_set_sample_getter_funcs( timeline );
1409 else
1410 isom_timeline_set_lpcm_sample_getter_funcs( timeline );
1411 return 0;
1412 fail:
1413 isom_timeline_destroy( timeline );
1414 return err;
1417 int lsmash_construct_timeline( lsmash_root_t *root, uint32_t track_ID )
1419 if( !root
1420 || !root->file
1421 || track_ID == 0 )
1422 return LSMASH_ERR_FUNCTION_PARAM;
1423 uint32_t track_number;
1424 if( root->file->initializer )
1426 if( !root->file->initializer->moov )
1427 return LSMASH_ERR_INVALID_DATA;
1428 track_number = 1;
1429 int track_found = 0;
1430 for( lsmash_entry_t *entry = root->file->initializer->moov->trak_list.head; entry; entry = entry->next )
1432 isom_trak_t *trak = (isom_trak_t *)entry->data;
1433 if( !trak
1434 || !trak->tkhd )
1435 continue;
1436 if( trak->tkhd->track_ID == track_ID )
1438 track_found = 1;
1439 break;
1441 ++track_number;
1443 if( !track_found )
1444 return LSMASH_ERR_NAMELESS;
1446 else
1447 track_number = track_ID;
1448 return lsmash_importer_construct_timeline( root->file->importer, track_number );
1451 int lsmash_get_dts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *dts )
1453 if( !sample_number || !dts )
1454 return LSMASH_ERR_FUNCTION_PARAM;
1455 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1456 if( !timeline || sample_number > timeline->sample_count )
1457 return LSMASH_ERR_NAMELESS;
1458 return timeline->get_dts( timeline, sample_number, dts );
1461 int lsmash_get_cts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *cts )
1463 if( !sample_number || !cts )
1464 return LSMASH_ERR_FUNCTION_PARAM;
1465 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1466 if( !timeline || sample_number > timeline->sample_count )
1467 return LSMASH_ERR_NAMELESS;
1468 return timeline->get_cts( timeline, sample_number, cts );
1471 lsmash_sample_t *lsmash_get_sample_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number )
1473 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1474 return timeline ? timeline->get_sample( timeline, sample_number ) : NULL;
1477 int lsmash_get_sample_info_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, lsmash_sample_t *sample )
1479 if( !sample )
1480 return LSMASH_ERR_FUNCTION_PARAM;
1481 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1482 return timeline ? timeline->get_sample_info( timeline, sample_number, sample ) : -1;
1485 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 )
1487 if( !prop )
1488 return LSMASH_ERR_FUNCTION_PARAM;
1489 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1490 return timeline ? timeline->get_sample_property( timeline, sample_number, prop ) : -1;
1493 int lsmash_get_composition_to_decode_shift_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *ctd_shift )
1495 if( !ctd_shift )
1496 return LSMASH_ERR_FUNCTION_PARAM;
1497 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1498 if( !timeline )
1499 return LSMASH_ERR_NAMELESS;
1500 *ctd_shift = timeline->ctd_shift;
1501 return 0;
1504 static int isom_get_closest_past_random_accessible_point_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number )
1506 lsmash_entry_t *entry = lsmash_get_entry( timeline->info_list, sample_number-- );
1507 if( !entry
1508 || !entry->data )
1509 return LSMASH_ERR_NAMELESS;
1510 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1511 while( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1513 entry = entry->prev;
1514 if( !entry
1515 || !entry->data )
1516 return LSMASH_ERR_NAMELESS;
1517 info = (isom_sample_info_t *)entry->data;
1518 --sample_number;
1520 *rap_number = sample_number + 1;
1521 return 0;
1524 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 )
1526 lsmash_entry_t *entry = lsmash_get_entry( timeline->info_list, sample_number++ );
1527 if( !entry
1528 || !entry->data )
1529 return LSMASH_ERR_NAMELESS;
1530 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1531 while( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1533 entry = entry->next;
1534 if( !entry
1535 || !entry->data )
1536 return LSMASH_ERR_NAMELESS;
1537 info = (isom_sample_info_t *)entry->data;
1538 ++sample_number;
1540 *rap_number = sample_number - 1;
1541 return 0;
1544 static int isom_get_closest_random_accessible_point_from_media_timeline_internal( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number )
1546 if( !timeline )
1547 return LSMASH_ERR_NAMELESS;
1548 int ret;
1549 if( (ret = isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, sample_number, rap_number )) < 0
1550 && (ret = isom_get_closest_future_random_accessible_point_from_media_timeline( timeline, sample_number + 1, rap_number )) < 0 )
1551 return ret;
1552 return 0;
1555 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 )
1557 if( sample_number == 0 || !rap_number )
1558 return LSMASH_ERR_FUNCTION_PARAM;
1559 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1560 if( !timeline )
1561 return LSMASH_ERR_NAMELESS;
1562 if( timeline->info_list->entry_count == 0 )
1564 *rap_number = sample_number; /* All LPCM is sync sample. */
1565 return 0;
1567 return isom_get_closest_random_accessible_point_from_media_timeline_internal( timeline, sample_number, rap_number );
1570 int lsmash_get_closest_random_accessible_point_detail_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number,
1571 uint32_t *rap_number, lsmash_random_access_flag *ra_flags, uint32_t *leading, uint32_t *distance )
1573 if( sample_number == 0 )
1574 return LSMASH_ERR_FUNCTION_PARAM;
1575 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1576 if( !timeline )
1577 return LSMASH_ERR_NAMELESS;
1578 if( timeline->info_list->entry_count == 0 )
1580 /* All LPCM is sync sample. */
1581 *rap_number = sample_number;
1582 if( ra_flags )
1583 *ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1584 if( leading )
1585 *leading = 0;
1586 if( distance )
1587 *distance = 0;
1588 return 0;
1590 int ret = isom_get_closest_random_accessible_point_from_media_timeline_internal( timeline, sample_number, rap_number );
1591 if( ret < 0 )
1592 return ret;
1593 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, *rap_number );
1594 if( !info )
1595 return LSMASH_ERR_NAMELESS;
1596 if( ra_flags )
1597 *ra_flags = info->prop.ra_flags;
1598 if( leading )
1599 *leading = 0;
1600 if( distance )
1601 *distance = 0;
1602 if( sample_number < *rap_number )
1603 /* Impossible to desire to decode the sample of given number correctly. */
1604 return 0;
1605 else if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) )
1607 if( leading )
1609 /* Count leading samples. */
1610 uint32_t current_sample_number = *rap_number + 1;
1611 uint64_t dts;
1612 if( (ret = isom_get_dts_from_info_list( timeline, *rap_number, &dts )) < 0 )
1613 return ret;
1614 uint64_t rap_cts = timeline->ctd_shift ? (dts + (int32_t)info->offset + timeline->ctd_shift) : (dts + info->offset);
1617 dts += info->duration;
1618 if( rap_cts <= dts )
1619 break; /* leading samples of this random accessible point must not be present more. */
1620 info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, current_sample_number++ );
1621 if( !info )
1622 break;
1623 uint64_t cts = timeline->ctd_shift ? (dts + (int32_t)info->offset + timeline->ctd_shift) : (dts + info->offset);
1624 if( rap_cts > cts )
1625 ++ *leading;
1626 } while( 1 );
1628 if( !distance || sample_number == *rap_number )
1629 return 0;
1630 /* Measure distance from the first closest non-recovery random accessible point to the second. */
1631 uint32_t prev_rap_number = *rap_number;
1634 if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) < 0 )
1635 /* The previous random accessible point is not present. */
1636 return 0;
1637 info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, prev_rap_number );
1638 if( !info )
1639 return LSMASH_ERR_NAMELESS;
1640 if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) )
1642 /* Decode shall already complete at the first closest non-recovery random accessible point if starting to decode from the second. */
1643 *distance = *rap_number - prev_rap_number;
1644 return 0;
1646 } while( 1 );
1648 if( !distance )
1649 return 0;
1650 /* Calculate roll-distance. */
1651 if( info->prop.pre_roll.distance )
1653 /* Pre-roll recovery */
1654 uint32_t prev_rap_number = *rap_number;
1657 if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) < 0
1658 && *rap_number < info->prop.pre_roll.distance )
1660 /* The previous random accessible point is not present.
1661 * And sample of given number might be not able to decoded correctly. */
1662 *distance = 0;
1663 return 0;
1665 if( prev_rap_number + info->prop.pre_roll.distance <= *rap_number )
1668 * |<---- pre-roll distance ---->|
1669 * |<--------- distance -------->|
1670 * media +++++++++++++++++++++++++ *** +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1671 * ^ ^ ^ ^
1672 * random accessible point starting point random accessible point given sample
1673 * (complete)
1675 *distance = info->prop.pre_roll.distance;
1676 return 0;
1678 else if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) )
1681 * |<------------ pre-roll distance ------------------>|
1682 * |<------ distance ------->|
1683 * media ++++++++++++++++ *** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1684 * ^ ^ ^ ^
1685 * random accessible point random accessible point given sample
1686 * (starting point) (complete)
1688 *distance = *rap_number - prev_rap_number;
1689 return 0;
1691 } while( 1 );
1693 /* Post-roll recovery */
1694 if( sample_number >= info->prop.post_roll.complete )
1696 * |<----- post-roll distance ----->|
1697 * (distance = 0)
1698 * media +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1699 * ^ ^ ^
1700 * random accessible point complete given sample
1701 * (starting point)
1703 return 0;
1704 uint32_t prev_rap_number = *rap_number;
1707 if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) < 0 )
1708 /* The previous random accessible point is not present. */
1709 return 0;
1710 info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, prev_rap_number );
1711 if( !info )
1712 return LSMASH_ERR_NAMELESS;
1713 if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) || sample_number >= info->prop.post_roll.complete )
1715 *distance = *rap_number - prev_rap_number;
1716 return 0;
1718 } while( 1 );
1721 int lsmash_check_sample_existence_in_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number )
1723 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1724 return timeline ? timeline->check_sample_existence( timeline, sample_number ) : 0;
1727 int lsmash_get_last_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *last_sample_delta )
1729 if( !last_sample_delta )
1730 return LSMASH_ERR_FUNCTION_PARAM;
1731 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1732 return timeline ? timeline->get_sample_duration( timeline, timeline->sample_count, last_sample_delta ) : -1;
1735 int lsmash_get_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint32_t *sample_delta )
1737 if( !sample_delta )
1738 return LSMASH_ERR_FUNCTION_PARAM;
1739 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1740 return timeline ? timeline->get_sample_duration( timeline, sample_number, sample_delta ) : -1;
1743 uint32_t lsmash_get_sample_count_in_media_timeline( lsmash_root_t *root, uint32_t track_ID )
1745 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1746 if( !timeline )
1747 return 0;
1748 return timeline->sample_count;
1751 uint32_t lsmash_get_max_sample_size_in_media_timeline( lsmash_root_t *root, uint32_t track_ID )
1753 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1754 if( !timeline )
1755 return 0;
1756 return timeline->max_sample_size;
1759 uint64_t lsmash_get_media_duration_from_media_timeline( lsmash_root_t *root, uint32_t track_ID )
1761 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1762 if( !timeline )
1763 return 0;
1764 return timeline->media_duration;
1767 isom_elst_entry_t *isom_timelime_get_explicit_timeline_map
1769 lsmash_root_t *root,
1770 uint32_t track_ID,
1771 uint32_t edit_number
1774 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1775 if( !timeline )
1776 return NULL;
1777 return lsmash_get_entry_data( timeline->edit_list, edit_number );
1780 uint32_t isom_timelime_count_explicit_timeline_map
1782 lsmash_root_t *root,
1783 uint32_t track_ID
1786 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1787 if( !timeline )
1788 return 0;
1789 return timeline->edit_list->entry_count;
1792 int lsmash_copy_timeline_map( lsmash_root_t *dst, uint32_t dst_track_ID, lsmash_root_t *src, uint32_t src_track_ID )
1794 if( isom_check_initializer_present( dst ) < 0
1795 || isom_check_initializer_present( src ) < 0 )
1796 return LSMASH_ERR_FUNCTION_PARAM;
1797 lsmash_file_t *dst_file = dst->file->initializer;
1798 isom_trak_t *dst_trak = isom_get_trak( dst_file, dst_track_ID );
1799 if( !dst_file->moov
1800 || !dst_file->moov->mvhd
1801 || dst_file->moov->mvhd->timescale == 0
1802 || !dst_trak
1803 || !dst_trak->mdia
1804 || !dst_trak->mdia->mdhd
1805 || dst_trak->mdia->mdhd->timescale == 0
1806 || !dst_trak->mdia->minf
1807 || !dst_trak->mdia->minf->stbl )
1808 return LSMASH_ERR_NAMELESS;
1809 if( dst_trak->edts
1810 && dst_trak->edts->elst )
1811 lsmash_remove_entries( dst_trak->edts->elst->list, NULL );
1812 uint32_t src_movie_timescale;
1813 uint32_t src_media_timescale;
1814 uint64_t src_track_duration;
1815 uint64_t src_media_duration;
1816 int32_t src_ctd_shift; /* Add timeline shift difference between src and dst to each media_time.
1817 * Therefore, call this function as later as possible. */
1818 lsmash_entry_t *src_entry = NULL;
1819 lsmash_file_t *src_file = src->file->initializer;
1820 isom_trak_t *src_trak = isom_get_trak( src_file, src_track_ID );
1821 int src_fragmented = !!(src_file->flags & LSMASH_FILE_MODE_FRAGMENTED);
1822 if( !src_trak
1823 || !src_trak->edts
1824 || !src_trak->edts->elst
1825 || !src_trak->edts->elst->list
1826 || src_fragmented )
1828 /* Get from constructed timeline instead of boxes. */
1829 isom_timeline_t *src_timeline = isom_get_timeline( src, src_track_ID );
1830 if( src_timeline
1831 && src_timeline->movie_timescale
1832 && src_timeline->media_timescale )
1834 src_entry = src_timeline->edit_list->head;
1835 if( !src_entry )
1836 return 0;
1837 src_movie_timescale = src_timeline->movie_timescale;
1838 src_media_timescale = src_timeline->media_timescale;
1839 src_track_duration = src_timeline->track_duration;
1840 src_media_duration = src_timeline->media_duration;
1841 src_ctd_shift = src_timeline->ctd_shift;
1843 else if( !src_fragmented )
1844 return LSMASH_ERR_NAMELESS;
1846 if( !src_entry )
1848 if( !src_file->moov
1849 || !src_file->moov->mvhd
1850 || src_file->moov->mvhd->timescale == 0
1851 || !src_trak
1852 || !src_trak->tkhd
1853 || !src_trak->mdia
1854 || !src_trak->mdia->mdhd
1855 || src_trak->mdia->mdhd->timescale == 0
1856 || !src_trak->mdia->minf
1857 || !src_trak->mdia->minf->stbl )
1858 return LSMASH_ERR_NAMELESS;
1859 if( !src_trak->edts
1860 || !src_trak->edts->elst
1861 || !src_trak->edts->elst->list
1862 || !src_trak->edts->elst->list->head )
1863 return 0;
1864 src_entry = src_trak->edts->elst->list->head;
1865 src_movie_timescale = src_file->moov->mvhd->timescale;
1866 src_media_timescale = src_trak->mdia->mdhd->timescale;
1867 src_track_duration = src_trak->tkhd->duration;
1868 src_media_duration = src_trak->mdia->mdhd->duration;
1869 src_ctd_shift = src_trak->mdia->minf->stbl->cslg ? src_trak->mdia->minf->stbl->cslg->compositionToDTSShift : 0;
1871 /* Generate the edit list if absent in the destination. */
1872 if( (!dst_trak->edts && !isom_add_edts( dst_trak ))
1873 || (!dst_trak->edts->elst && !isom_add_elst( dst_trak->edts )) )
1874 return LSMASH_ERR_NAMELESS;
1875 uint32_t dst_movie_timescale = dst_file->moov->mvhd->timescale;
1876 uint32_t dst_media_timescale = dst_trak->mdia->mdhd->timescale;
1877 int32_t dst_ctd_shift = dst_trak->mdia->minf->stbl->cslg ? dst_trak->mdia->minf->stbl->cslg->compositionToDTSShift : 0;
1878 int32_t media_time_shift = src_ctd_shift - dst_ctd_shift;
1879 lsmash_entry_list_t *dst_list = dst_trak->edts->elst->list;
1880 while( src_entry )
1882 isom_elst_entry_t *src_data = (isom_elst_entry_t *)src_entry->data;
1883 if( !src_data )
1884 return LSMASH_ERR_NAMELESS;
1885 isom_elst_entry_t *dst_data = (isom_elst_entry_t *)lsmash_malloc( sizeof(isom_elst_entry_t) );
1886 if( !dst_data )
1887 return LSMASH_ERR_MEMORY_ALLOC;
1888 uint64_t segment_duration;
1889 if( src_data->segment_duration == 0 && !dst_file->fragment )
1890 /* The implicit duration edit is not suitable for non-fragmented movie file.
1891 * Set an appropriate duration from the source track. */
1892 segment_duration = src_fragmented
1893 ? (uint64_t)(src_media_duration * ((double)src_movie_timescale / src_media_timescale))
1894 : src_track_duration;
1895 else
1896 segment_duration = src_data->segment_duration;
1897 dst_data->segment_duration = segment_duration * ((double)dst_movie_timescale / src_movie_timescale) + 0.5;
1898 dst_data->media_rate = src_data->media_rate;
1899 if( src_data->media_time != ISOM_EDIT_MODE_EMPTY )
1900 dst_data->media_time = (src_data->media_time + media_time_shift) * ((double)dst_media_timescale / src_media_timescale) + 0.5;
1901 else
1902 dst_data->media_time = ISOM_EDIT_MODE_EMPTY;
1903 if( lsmash_add_entry( dst_list, dst_data ) < 0 )
1905 lsmash_free( dst_data );
1906 return LSMASH_ERR_MEMORY_ALLOC;
1908 src_entry = src_entry->next;
1910 return 0;
1913 int lsmash_set_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list )
1915 if( !root || !root->file || !ts_list )
1916 return -1;
1917 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1918 if( !timeline )
1919 return LSMASH_ERR_NAMELESS;
1920 if( timeline->info_list->entry_count == 0 )
1922 lsmash_log( timeline, LSMASH_LOG_ERROR, "Changing timestamps of LPCM track is not supported.\n" );
1923 return LSMASH_ERR_PATCH_WELCOME;
1925 if( ts_list->sample_count != timeline->info_list->entry_count )
1926 return LSMASH_ERR_INVALID_DATA; /* Number of samples must be same. */
1927 lsmash_media_ts_t *ts = ts_list->timestamp;
1928 if( ts[0].dts )
1929 return LSMASH_ERR_INVALID_DATA; /* DTS must start from value zero. */
1930 /* Update DTSs. */
1931 uint32_t sample_count = ts_list->sample_count;
1932 uint32_t i;
1933 if( timeline->info_list->entry_count > 1 )
1935 i = 1;
1936 lsmash_entry_t *entry = timeline->info_list->head;
1937 isom_sample_info_t *info = NULL;
1938 while( i < sample_count )
1940 info = (isom_sample_info_t *)entry->data;
1941 if( !info || (ts[i].dts < ts[i - 1].dts) )
1942 return LSMASH_ERR_INVALID_DATA;
1943 info->duration = ts[i].dts - ts[i - 1].dts;
1944 entry = entry->next;
1945 ++i;
1947 if( i > 1 )
1949 if( !entry
1950 || !entry->data )
1951 return LSMASH_ERR_INVALID_DATA;
1952 /* Copy the previous duration. */
1953 ((isom_sample_info_t *)entry->data)->duration = info->duration;
1955 else
1956 return LSMASH_ERR_INVALID_DATA; /* Irregular case: sample_count this timeline has is incorrect. */
1958 else /* still image */
1959 ((isom_sample_info_t *)timeline->info_list->head->data)->duration = UINT32_MAX;
1960 /* Update CTSs.
1961 * ToDo: hint track must not have any sample_offset. */
1962 i = 0;
1963 timeline->ctd_shift = 0;
1964 for( lsmash_entry_t *entry = timeline->info_list->head; entry; entry = entry->next )
1966 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1967 if( (ts[i].cts + timeline->ctd_shift) < ts[i].dts )
1968 timeline->ctd_shift = ts[i].dts - ts[i].cts;
1969 info->offset = ts[i].cts - ts[i].dts;
1970 ++i;
1972 if( timeline->ctd_shift && (!root->file->qt_compatible || root->file->max_isom_version < 4) )
1973 return LSMASH_ERR_INVALID_DATA; /* Don't allow composition to decode timeline shift. */
1974 return 0;
1977 int lsmash_get_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list )
1979 if( !ts_list )
1980 return LSMASH_ERR_FUNCTION_PARAM;
1981 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1982 if( !timeline )
1983 return LSMASH_ERR_NAMELESS;
1984 uint32_t sample_count = timeline->info_list->entry_count;
1985 if( !sample_count )
1987 ts_list->sample_count = 0;
1988 ts_list->timestamp = NULL;
1989 return 0;
1991 lsmash_media_ts_t *ts = lsmash_malloc( sample_count * sizeof(lsmash_media_ts_t) );
1992 if( !ts )
1993 return LSMASH_ERR_MEMORY_ALLOC;
1994 uint64_t dts = 0;
1995 uint32_t i = 0;
1996 if( timeline->info_list->entry_count )
1997 for( lsmash_entry_t *entry = timeline->info_list->head; entry; entry = entry->next )
1999 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
2000 if( !info )
2002 lsmash_free( ts );
2003 return LSMASH_ERR_NAMELESS;
2005 ts[i].dts = dts;
2006 ts[i].cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset);
2007 dts += info->duration;
2008 ++i;
2010 else
2011 for( lsmash_entry_t *entry = timeline->bunch_list->head; entry; entry = entry->next )
2013 isom_lpcm_bunch_t *bunch = (isom_lpcm_bunch_t *)entry->data;
2014 if( !bunch )
2016 lsmash_free( ts );
2017 return LSMASH_ERR_NAMELESS;
2019 for( uint32_t j = 0; j < bunch->sample_count; j++ )
2021 ts[i].dts = dts;
2022 ts[i].cts = timeline->ctd_shift ? (dts + (int32_t)bunch->offset) : (dts + bunch->offset);
2023 dts += bunch->duration;
2024 ++i;
2027 ts_list->sample_count = sample_count;
2028 ts_list->timestamp = ts;
2029 return 0;
2032 void lsmash_delete_media_timestamps( lsmash_media_ts_list_t *ts_list )
2034 if( !ts_list )
2035 return;
2036 lsmash_freep( &ts_list->timestamp );
2037 ts_list->sample_count = 0;
2040 static int isom_compare_dts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b )
2042 int64_t diff = (int64_t)(a->dts - b->dts);
2043 return diff > 0 ? 1 : (diff == 0 ? 0 : -1);
2046 void lsmash_sort_timestamps_decoding_order( lsmash_media_ts_list_t *ts_list )
2048 if( !ts_list )
2049 return;
2050 qsort( ts_list->timestamp, ts_list->sample_count, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))isom_compare_dts );
2053 static int isom_compare_cts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b )
2055 int64_t diff = (int64_t)(a->cts - b->cts);
2056 return diff > 0 ? 1 : (diff == 0 ? 0 : -1);
2059 void lsmash_sort_timestamps_composition_order( lsmash_media_ts_list_t *ts_list )
2061 if( !ts_list )
2062 return;
2063 qsort( ts_list->timestamp, ts_list->sample_count, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))isom_compare_cts );
2066 int lsmash_get_max_sample_delay( lsmash_media_ts_list_t *ts_list, uint32_t *max_sample_delay )
2068 if( !ts_list || !max_sample_delay )
2069 return LSMASH_ERR_FUNCTION_PARAM;
2070 lsmash_media_ts_t *orig_ts = ts_list->timestamp;
2071 lsmash_media_ts_t *ts = lsmash_malloc( ts_list->sample_count * sizeof(lsmash_media_ts_t) );
2072 if( !ts )
2073 return LSMASH_ERR_MEMORY_ALLOC;
2074 ts_list->timestamp = ts;
2075 *max_sample_delay = 0;
2076 for( uint32_t i = 0; i < ts_list->sample_count; i++ )
2078 ts[i].cts = orig_ts[i].cts; /* for sorting */
2079 ts[i].dts = i;
2081 lsmash_sort_timestamps_composition_order( ts_list );
2082 for( uint32_t i = 0; i < ts_list->sample_count; i++ )
2083 if( i < ts[i].dts )
2085 uint32_t sample_delay = ts[i].dts - i;
2086 *max_sample_delay = LSMASH_MAX( *max_sample_delay, sample_delay );
2088 lsmash_free( ts );
2089 ts_list->timestamp = orig_ts;
2090 return 0;