Update copyright dates.
[L-SMASH.git] / core / timeline.c
blobd5efaf2bf1329bf929c71a01b5b7d03e02776be2
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_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 if( !file )
506 return NULL;
507 lsmash_sample_t *sample = lsmash_create_sample( 0 );
508 if( !sample )
509 return NULL;
510 lsmash_bs_t *bs = file->bs;
511 lsmash_bs_read_seek( bs, sample_pos, SEEK_SET );
512 sample->data = lsmash_bs_get_bytes( bs, sample_length );
513 if( !sample->data )
515 lsmash_delete_sample( sample );
516 return NULL;
518 return sample;
521 static lsmash_sample_t *isom_get_lpcm_sample_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number )
523 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
524 if( !bunch
525 || !bunch->chunk )
526 return NULL;
527 /* Get data of a sample from the stream. */
528 uint64_t sample_number_offset = sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number;
529 uint64_t sample_pos = bunch->pos + sample_number_offset * bunch->length;
530 lsmash_sample_t *sample = isom_read_sample_data_from_stream( bunch->chunk->file, timeline, bunch->length, sample_pos );
531 if( !sample )
532 return NULL;
533 /* Get sample info. */
534 sample->dts = timeline->last_accessed_lpcm_bunch_dts + sample_number_offset * bunch->duration;
535 sample->cts = timeline->ctd_shift ? (sample->dts + (int32_t)bunch->offset) : (sample->dts + bunch->offset);
536 sample->pos = sample_pos;
537 sample->length = bunch->length;
538 sample->index = bunch->index;
539 sample->prop = bunch->prop;
540 return sample;
543 static lsmash_sample_t *isom_get_sample_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number )
545 uint64_t dts;
546 if( isom_get_dts_from_info_list( timeline, sample_number, &dts ) < 0 )
547 return NULL;
548 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
549 if( !info
550 || !info->chunk )
551 return NULL;
552 /* Get data of a sample from the stream. */
553 lsmash_sample_t *sample = isom_read_sample_data_from_stream( info->chunk->file, timeline, info->length, info->pos );
554 if( !sample )
555 return NULL;
556 /* Get sample info. */
557 sample->dts = dts;
558 sample->cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset);
559 sample->pos = info->pos;
560 sample->length = info->length;
561 sample->index = info->index;
562 sample->prop = info->prop;
563 return sample;
566 static int isom_get_lpcm_sample_info_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample )
568 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
569 if( !bunch )
570 return LSMASH_ERR_NAMELESS;
571 uint64_t sample_number_offset = sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number;
572 sample->dts = timeline->last_accessed_lpcm_bunch_dts + sample_number_offset * bunch->duration;
573 sample->cts = timeline->ctd_shift ? (sample->dts + (int32_t)bunch->offset) : (sample->dts + bunch->offset);
574 sample->pos = bunch->pos + sample_number_offset * bunch->length;
575 sample->length = bunch->length;
576 sample->index = bunch->index;
577 sample->prop = bunch->prop;
578 return 0;
581 static int isom_get_sample_info_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample )
583 uint64_t dts;
584 int ret = isom_get_dts_from_info_list( timeline, sample_number, &dts );
585 if( ret < 0 )
586 return ret;
587 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
588 if( !info )
589 return LSMASH_ERR_NAMELESS;
590 sample->dts = dts;
591 sample->cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset);
592 sample->pos = info->pos;
593 sample->length = info->length;
594 sample->index = info->index;
595 sample->prop = info->prop;
596 return 0;
599 static int isom_get_lpcm_sample_property_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop )
601 memset( prop, 0, sizeof(lsmash_sample_property_t) );
602 prop->ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
603 return 0;
606 static int isom_get_sample_property_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop )
608 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
609 if( !info )
610 return LSMASH_ERR_NAMELESS;
611 *prop = info->prop;
612 return 0;
615 static void isom_timeline_set_sample_getter_funcs
617 isom_timeline_t *timeline
620 timeline->get_dts = isom_get_dts_from_info_list;
621 timeline->get_cts = isom_get_cts_from_info_list;
622 timeline->get_sample_duration = isom_get_sample_duration_from_info_list;
623 timeline->check_sample_existence = isom_check_sample_existence_in_info_list;
624 timeline->get_sample = isom_get_sample_from_media_timeline;
625 timeline->get_sample_info = isom_get_sample_info_from_media_timeline;
626 timeline->get_sample_property = isom_get_sample_property_from_media_timeline;
629 void isom_timeline_set_lpcm_sample_getter_funcs
631 isom_timeline_t *timeline
634 timeline->get_dts = isom_get_dts_from_bunch_list;
635 timeline->get_cts = isom_get_cts_from_bunch_list;
636 timeline->get_sample_duration = isom_get_sample_duration_from_bunch_list;
637 timeline->check_sample_existence = isom_check_sample_existence_in_bunch_list;
638 timeline->get_sample = isom_get_lpcm_sample_from_media_timeline;
639 timeline->get_sample_info = isom_get_lpcm_sample_info_from_media_timeline;
640 timeline->get_sample_property = isom_get_lpcm_sample_property_from_media_timeline;
643 static inline void isom_increment_sample_number_in_entry
645 uint32_t *sample_number_in_entry,
646 lsmash_entry_t **entry,
647 uint32_t sample_count
650 if( *sample_number_in_entry == sample_count )
652 *sample_number_in_entry = 1;
653 *entry = (*entry)->next;
655 else
656 *sample_number_in_entry += 1;
659 static inline isom_sgpd_t *isom_select_appropriate_sgpd
661 isom_sgpd_t *sgpd,
662 isom_sgpd_t *sgpd_frag,
663 uint32_t *group_description_index
666 if( sgpd_frag && *group_description_index >= 0x10000 )
668 /* The specification doesn't define 0x10000 explicitly, however says that there must be fewer than
669 * 65536 group definitions for this track and grouping type in the sample table in the Movie Box.
670 * So, we assume 0x10000 is equivalent to 0. */
671 *group_description_index -= 0x10000;
672 return sgpd_frag;
674 else
675 return sgpd;
678 static int isom_get_roll_recovery_grouping_info
680 isom_timeline_t *timeline,
681 lsmash_entry_t **sbgp_roll_entry,
682 isom_sgpd_t *sgpd_roll,
683 isom_sgpd_t *sgpd_frag_roll,
684 uint32_t *sample_number_in_sbgp_roll_entry,
685 isom_sample_info_t *info,
686 uint32_t sample_number
689 isom_group_assignment_entry_t *assignment = (isom_group_assignment_entry_t *)(*sbgp_roll_entry)->data;
690 if( !assignment )
691 return LSMASH_ERR_NAMELESS;
692 if( assignment->group_description_index )
694 uint32_t group_description_index = assignment->group_description_index;
695 isom_sgpd_t *sgpd = isom_select_appropriate_sgpd( sgpd_roll, sgpd_frag_roll, &group_description_index );
696 isom_roll_entry_t *roll_data = (isom_roll_entry_t *)lsmash_get_entry_data( sgpd->list, group_description_index );
697 if( roll_data )
699 if( roll_data->roll_distance > 0 )
701 /* post-roll */
702 info->prop.post_roll.complete = sample_number + roll_data->roll_distance;
703 if( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
704 info->prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_POST_ROLL_START;
706 else if( roll_data->roll_distance < 0 )
708 /* pre-roll */
709 info->prop.pre_roll.distance = -roll_data->roll_distance;
710 if( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
711 info->prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_PRE_ROLL_END;
714 else if( *sample_number_in_sbgp_roll_entry == 1 && group_description_index )
715 lsmash_log( timeline, LSMASH_LOG_WARNING, "a description of roll recoveries is not found in the Sample Group Description Box.\n" );
717 isom_increment_sample_number_in_entry( sample_number_in_sbgp_roll_entry, sbgp_roll_entry, assignment->sample_count );
718 return 0;
721 static int isom_get_random_access_point_grouping_info
723 isom_timeline_t *timeline,
724 lsmash_entry_t **sbgp_rap_entry,
725 isom_sgpd_t *sgpd_rap,
726 isom_sgpd_t *sgpd_frag_rap,
727 uint32_t *sample_number_in_sbgp_rap_entry,
728 isom_sample_info_t *info,
729 uint32_t *distance
732 isom_group_assignment_entry_t *assignment = (isom_group_assignment_entry_t *)(*sbgp_rap_entry)->data;
733 if( !assignment )
734 return LSMASH_ERR_NAMELESS;
735 if( assignment->group_description_index && (info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE) )
737 uint32_t group_description_index = assignment->group_description_index;
738 isom_sgpd_t *sgpd = isom_select_appropriate_sgpd( sgpd_rap, sgpd_frag_rap, &group_description_index );
739 isom_rap_entry_t *rap_data = (isom_rap_entry_t *)lsmash_get_entry_data( sgpd->list, group_description_index );
740 if( rap_data )
742 /* If this is not an open RAP, we treat it as an unknown RAP since non-IDR sample could make a closed GOP. */
743 info->prop.ra_flags |= (rap_data->num_leading_samples_known && !!rap_data->num_leading_samples)
744 ? ISOM_SAMPLE_RANDOM_ACCESS_FLAG_OPEN_RAP
745 : ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP;
746 *distance = 0;
748 else if( *sample_number_in_sbgp_rap_entry == 1 && group_description_index )
749 lsmash_log( timeline, LSMASH_LOG_WARNING, "a description of random access points is not found in the Sample Group Description Box.\n" );
751 isom_increment_sample_number_in_entry( sample_number_in_sbgp_rap_entry, sbgp_rap_entry, assignment->sample_count );
752 return 0;
755 int isom_timeline_construct( lsmash_root_t *root, uint32_t track_ID )
757 if( isom_check_initializer_present( root ) < 0 )
758 return LSMASH_ERR_FUNCTION_PARAM;
759 lsmash_file_t *file = root->file;
760 if( !file->moov
761 || !file->moov->mvhd
762 || file->moov->mvhd->timescale == 0 )
763 return LSMASH_ERR_INVALID_DATA;
764 /* Get track by track_ID. */
765 isom_trak_t *trak = isom_get_trak( file, track_ID );
766 if( !trak
767 || !trak->tkhd
768 || !trak->mdia
769 || !trak->mdia->mdhd
770 || trak->mdia->mdhd->timescale == 0
771 || !trak->mdia->minf
772 || !trak->mdia->minf->stbl
773 || !trak->mdia->minf->stbl->stco
774 || !trak->mdia->minf->stbl->stsd
775 || (!trak->mdia->minf->stbl->stsz && !trak->mdia->minf->stbl->stz2) )
776 return LSMASH_ERR_INVALID_DATA;
777 /* Create a timeline list if it doesn't exist. */
778 if( !file->timeline )
780 file->timeline = lsmash_create_entry_list();
781 if( !file->timeline )
782 return LSMASH_ERR_MEMORY_ALLOC;
784 /* Create a timeline. */
785 isom_timeline_t *timeline = isom_timeline_create();
786 if( !timeline )
787 return LSMASH_ERR_MEMORY_ALLOC;
788 timeline->track_ID = track_ID;
789 timeline->movie_timescale = file->moov->mvhd->timescale;
790 timeline->media_timescale = trak->mdia->mdhd->timescale;
791 timeline->track_duration = trak->tkhd->duration;
792 /* Preparation for construction. */
793 isom_elst_t *elst = trak->edts ? trak->edts->elst : NULL;
794 isom_minf_t *minf = trak->mdia->minf;
795 isom_dref_t *dref = minf->dinf ? minf->dinf->dref : NULL;
796 isom_stbl_t *stbl = minf->stbl;
797 isom_stsd_t *stsd = stbl->stsd;
798 isom_stts_t *stts = stbl->stts;
799 isom_ctts_t *ctts = stbl->ctts;
800 isom_stss_t *stss = stbl->stss;
801 isom_stps_t *stps = stbl->stps;
802 isom_sdtp_t *sdtp = stbl->sdtp;
803 isom_stsc_t *stsc = stbl->stsc;
804 isom_stsz_t *stsz = stbl->stsz;
805 isom_stz2_t *stz2 = stbl->stz2;
806 isom_stco_t *stco = stbl->stco;
807 isom_sgpd_t *sgpd_rap = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
808 isom_sbgp_t *sbgp_rap = isom_get_sample_to_group ( stbl, ISOM_GROUP_TYPE_RAP );
809 isom_sgpd_t *sgpd_roll = isom_get_roll_recovery_sample_group_description( &stbl->sgpd_list );
810 isom_sbgp_t *sbgp_roll = isom_get_roll_recovery_sample_to_group ( &stbl->sbgp_list );
811 lsmash_entry_t *elst_entry = elst && elst->list ? elst->list->head : NULL;
812 lsmash_entry_t *stts_entry = stts && stts->list ? stts->list->head : NULL;
813 lsmash_entry_t *ctts_entry = ctts && ctts->list ? ctts->list->head : NULL;
814 lsmash_entry_t *stss_entry = stss && stss->list ? stss->list->head : NULL;
815 lsmash_entry_t *stps_entry = stps && stps->list ? stps->list->head : NULL;
816 lsmash_entry_t *sdtp_entry = sdtp && sdtp->list ? sdtp->list->head : NULL;
817 lsmash_entry_t *stsz_entry = stsz ? (stsz->list ? stsz->list->head : NULL) : (stz2->list ? stz2->list->head : NULL);
818 lsmash_entry_t *stsc_entry = stsc && stsc->list ? stsc->list->head : NULL;
819 lsmash_entry_t *stco_entry = stco->list ? stco->list->head : NULL;
820 lsmash_entry_t *sbgp_roll_entry = sbgp_roll && sbgp_roll->list ? sbgp_roll->list->head : NULL;
821 lsmash_entry_t *sbgp_rap_entry = sbgp_rap && sbgp_rap->list ? sbgp_rap->list->head : NULL;
822 lsmash_entry_t *next_stsc_entry = stsc_entry ? stsc_entry->next : NULL;
823 isom_stsc_entry_t *stsc_data = stsc_entry ? (isom_stsc_entry_t *)stsc_entry->data : NULL;
824 int err = LSMASH_ERR_INVALID_DATA;
825 int movie_fragments_present = (file->moov->mvex && file->moof_list.head);
826 if( !movie_fragments_present && (!stts_entry || !stsc_entry || !stco_entry || !stco_entry->data || (next_stsc_entry && !next_stsc_entry->data)) )
827 goto fail;
828 isom_sample_entry_t *description = (isom_sample_entry_t *)lsmash_get_entry_data( &stsd->list, stsc_data ? stsc_data->sample_description_index : 1 );
829 if( !description )
830 goto fail;
831 lsmash_entry_list_t *dref_list = dref ? &dref->list : NULL;
832 isom_dref_entry_t *dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( dref_list, description->data_reference_index );
833 int all_sync = !stss;
834 int large_presentation = stco->large_presentation || lsmash_check_box_type_identical( stco->type, ISOM_BOX_TYPE_CO64 );
835 int is_lpcm_audio = isom_is_lpcm_audio( description );
836 int is_qt_fixed_comp_audio = isom_is_qt_fixed_compressed_audio( description );
837 int iso_sdtp = file->max_isom_version >= 2 || file->avc_extensions;
838 int allow_negative_sample_offset = ctts && ((file->max_isom_version >= 4 && ctts->version == 1) || file->qt_compatible);
839 uint32_t sample_number_in_stts_entry = 1;
840 uint32_t sample_number_in_ctts_entry = 1;
841 uint32_t sample_number_in_sbgp_roll_entry = 1;
842 uint32_t sample_number_in_sbgp_rap_entry = 1;
843 uint64_t dts = 0;
844 uint32_t chunk_number = 1;
845 uint64_t offset_from_chunk = 0;
846 uint64_t data_offset = stco_entry && stco_entry->data
847 ? large_presentation
848 ? ((isom_co64_entry_t *)stco_entry->data)->chunk_offset
849 : ((isom_stco_entry_t *)stco_entry->data)->chunk_offset
850 : 0;
851 uint32_t initial_movie_sample_count = stsz ? stsz->sample_count : stz2->sample_count;
852 uint32_t samples_per_packet;
853 uint32_t constant_sample_size;
854 if( is_qt_fixed_comp_audio )
855 isom_get_qt_fixed_comp_audio_sample_quants( timeline, description, &samples_per_packet, &constant_sample_size );
856 else
858 samples_per_packet = 1;
859 constant_sample_size = stsz ? stsz->sample_size : 0;
861 uint32_t sample_number = samples_per_packet;
862 uint32_t sample_number_in_chunk = samples_per_packet;
863 /* Copy edits. */
864 while( elst_entry )
866 isom_elst_entry_t *edit = (isom_elst_entry_t *)lsmash_memdup( elst_entry->data, sizeof(isom_elst_entry_t) );
867 if( !edit
868 || lsmash_add_entry( timeline->edit_list, edit ) < 0 )
870 err = LSMASH_ERR_MEMORY_ALLOC;
871 goto fail;
873 elst_entry = elst_entry->next;
875 /* Check what the first 2-bits of sample dependency means.
876 * This check is for chimera of ISO Base Media and QTFF. */
877 if( iso_sdtp && sdtp_entry )
879 while( sdtp_entry )
881 isom_sdtp_entry_t *sdtp_data = (isom_sdtp_entry_t *)sdtp_entry->data;
882 if( !sdtp_data )
883 goto fail;
884 if( sdtp_data->is_leading > 1 )
885 break; /* Apparently, it's defined under ISO Base Media. */
886 if( (sdtp_data->is_leading == 1) && (sdtp_data->sample_depends_on == ISOM_SAMPLE_IS_INDEPENDENT) )
888 /* Obviously, it's not defined under ISO Base Media. */
889 iso_sdtp = 0;
890 break;
892 sdtp_entry = sdtp_entry->next;
894 sdtp_entry = sdtp->list->head;
896 /**--- Construct media timeline. ---**/
897 isom_portable_chunk_t chunk;
898 chunk.data_offset = data_offset;
899 chunk.length = 0;
900 chunk.number = chunk_number;
901 chunk.file = (!dref_entry || !dref_entry->ref_file) ? NULL : dref_entry->ref_file;
902 if( (err = isom_add_portable_chunk_entry( timeline, &chunk )) < 0 )
903 goto fail;
904 uint32_t distance = NO_RANDOM_ACCESS_POINT;
905 uint32_t last_duration = UINT32_MAX;
906 uint32_t packet_number = 1;
907 isom_lpcm_bunch_t bunch = { 0 };
908 while( sample_number <= initial_movie_sample_count )
910 isom_sample_info_t info = { 0 };
911 /* Get sample duration and sample offset. */
912 for( uint32_t i = 0; i < samples_per_packet; i++ )
914 /* sample duration */
915 if( stts_entry )
917 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
918 if( !stts_data )
919 goto fail;
920 isom_increment_sample_number_in_entry( &sample_number_in_stts_entry, &stts_entry, stts_data->sample_count );
921 last_duration = stts_data->sample_delta;
923 info.duration += last_duration;
924 dts += last_duration;
925 /* sample offset */
926 uint32_t sample_offset;
927 if( ctts_entry )
929 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
930 if( !ctts_data )
931 goto fail;
932 isom_increment_sample_number_in_entry( &sample_number_in_ctts_entry, &ctts_entry, ctts_data->sample_count );
933 sample_offset = ctts_data->sample_offset;
934 if( allow_negative_sample_offset )
936 uint64_t cts = dts + (int32_t)sample_offset;
937 if( (cts + timeline->ctd_shift) < dts )
938 timeline->ctd_shift = dts - cts;
941 else
942 sample_offset = 0;
943 if( i == 0 )
944 info.offset = sample_offset;
946 timeline->media_duration += info.duration;
947 if( !is_qt_fixed_comp_audio )
949 /* Check whether sync sample or not. */
950 if( stss_entry )
952 isom_stss_entry_t *stss_data = (isom_stss_entry_t *)stss_entry->data;
953 if( !stss_data )
954 goto fail;
955 if( sample_number == stss_data->sample_number )
957 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
958 stss_entry = stss_entry->next;
959 distance = 0;
962 else if( all_sync )
963 /* Don't reset distance as 0 since MDCT-based audio frames need pre-roll for correct presentation
964 * though all of them could be marked as a sync sample. */
965 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
966 /* Check whether partial sync sample or not. */
967 if( stps_entry )
969 isom_stps_entry_t *stps_data = (isom_stps_entry_t *)stps_entry->data;
970 if( !stps_data )
971 goto fail;
972 if( sample_number == stps_data->sample_number )
974 info.prop.ra_flags |= QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC | QT_SAMPLE_RANDOM_ACCESS_FLAG_RAP;
975 stps_entry = stps_entry->next;
976 distance = 0;
979 /* Get sample dependency info. */
980 if( sdtp_entry )
982 isom_sdtp_entry_t *sdtp_data = (isom_sdtp_entry_t *)sdtp_entry->data;
983 if( !sdtp_data )
984 goto fail;
985 if( iso_sdtp )
986 info.prop.leading = sdtp_data->is_leading;
987 else
988 info.prop.allow_earlier = sdtp_data->is_leading;
989 info.prop.independent = sdtp_data->sample_depends_on;
990 info.prop.disposable = sdtp_data->sample_is_depended_on;
991 info.prop.redundant = sdtp_data->sample_has_redundancy;
992 sdtp_entry = sdtp_entry->next;
994 /* Get roll recovery grouping info. */
995 if( sbgp_roll_entry
996 && isom_get_roll_recovery_grouping_info( timeline,
997 &sbgp_roll_entry, sgpd_roll, NULL,
998 &sample_number_in_sbgp_roll_entry,
999 &info, sample_number ) < 0 )
1000 goto fail;
1001 info.prop.post_roll.identifier = sample_number;
1002 /* Get random access point grouping info. */
1003 if( sbgp_rap_entry
1004 && isom_get_random_access_point_grouping_info( timeline,
1005 &sbgp_rap_entry, sgpd_rap, NULL,
1006 &sample_number_in_sbgp_rap_entry,
1007 &info, &distance ) < 0 )
1008 goto fail;
1009 /* Set up distance from the previous random access point. */
1010 if( distance != NO_RANDOM_ACCESS_POINT )
1012 if( info.prop.pre_roll.distance == 0 )
1013 info.prop.pre_roll.distance = distance;
1014 ++distance;
1017 else
1018 /* All uncompressed and non-variable compressed audio frame is a sync sample. */
1019 info.prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1020 /* Get size of sample in the stream. */
1021 if( is_qt_fixed_comp_audio || !stsz_entry )
1022 info.length = constant_sample_size;
1023 else
1025 if( !stsz_entry->data )
1026 goto fail;
1027 info.length = ((isom_stsz_entry_t *)stsz_entry->data)->entry_size;
1028 stsz_entry = stsz_entry->next;
1030 timeline->max_sample_size = LSMASH_MAX( timeline->max_sample_size, info.length );
1031 /* Get chunk info. */
1032 info.pos = data_offset;
1033 info.index = stsc_data->sample_description_index;
1034 info.chunk = (isom_portable_chunk_t *)timeline->chunk_list->tail->data;
1035 offset_from_chunk += info.length;
1036 if( sample_number_in_chunk == stsc_data->samples_per_chunk )
1038 /* Set the length of the last chunk. */
1039 if( info.chunk )
1040 info.chunk->length = offset_from_chunk;
1041 /* Move the next chunk. */
1042 if( stco_entry )
1043 stco_entry = stco_entry->next;
1044 if( stco_entry
1045 && stco_entry->data )
1046 data_offset = large_presentation
1047 ? ((isom_co64_entry_t *)stco_entry->data)->chunk_offset
1048 : ((isom_stco_entry_t *)stco_entry->data)->chunk_offset;
1049 chunk.data_offset = data_offset;
1050 chunk.length = 0;
1051 chunk.number = ++chunk_number;
1052 offset_from_chunk = 0;
1053 /* Check if the next entry is broken. */
1054 while( next_stsc_entry && chunk_number > ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk )
1056 /* Just skip broken next entry. */
1057 lsmash_log( timeline, LSMASH_LOG_WARNING, "ignore broken entry in Sample To Chunk Box.\n" );
1058 lsmash_log( timeline, LSMASH_LOG_WARNING, "timeline might be corrupted.\n" );
1059 next_stsc_entry = next_stsc_entry->next;
1060 if( next_stsc_entry
1061 && !next_stsc_entry->data )
1062 goto fail;
1064 /* Check if the next chunk belongs to the next sequence of chunks. */
1065 if( next_stsc_entry && chunk_number == ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk )
1067 stsc_entry = next_stsc_entry;
1068 next_stsc_entry = next_stsc_entry->next;
1069 if( next_stsc_entry
1070 && !next_stsc_entry->data )
1071 goto fail;
1072 stsc_data = (isom_stsc_entry_t *)stsc_entry->data;
1073 /* Update sample description. */
1074 description = (isom_sample_entry_t *)lsmash_get_entry_data( &stsd->list, stsc_data->sample_description_index );
1075 is_lpcm_audio = description ? isom_is_lpcm_audio( description ) : 0;
1076 is_qt_fixed_comp_audio = description ? isom_is_qt_fixed_compressed_audio( description ) : 0;
1077 if( is_qt_fixed_comp_audio )
1078 isom_get_qt_fixed_comp_audio_sample_quants( timeline, description, &samples_per_packet, &constant_sample_size );
1079 else
1081 samples_per_packet = 1;
1082 constant_sample_size = stsz ? stsz->sample_size : 0;
1084 /* Reference media data. */
1085 dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( dref_list, description ? description->data_reference_index : 0 );
1086 chunk.file = (!dref_entry || !dref_entry->ref_file) ? NULL : dref_entry->ref_file;
1088 sample_number_in_chunk = samples_per_packet;
1089 if( (err = isom_add_portable_chunk_entry( timeline, &chunk )) < 0 )
1090 goto fail;
1092 else
1094 data_offset += info.length;
1095 sample_number_in_chunk += samples_per_packet;
1097 /* OK. Let's add its info. */
1098 if( is_lpcm_audio )
1100 if( sample_number == samples_per_packet )
1101 isom_update_bunch( &bunch, &info );
1102 else if( isom_compare_lpcm_sample_info( &bunch, &info ) )
1104 if( (err = isom_add_lpcm_bunch_entry( timeline, &bunch )) < 0 )
1105 goto fail;
1106 isom_update_bunch( &bunch, &info );
1108 else
1109 ++ bunch.sample_count;
1111 else if( (err = isom_add_sample_info_entry( timeline, &info )) < 0 )
1112 goto fail;
1113 if( timeline->info_list->entry_count && timeline->bunch_list->entry_count )
1115 lsmash_log( timeline, LSMASH_LOG_ERROR, "LPCM + non-LPCM track is not supported.\n" );
1116 err = LSMASH_ERR_PATCH_WELCOME;
1117 goto fail;
1119 sample_number += samples_per_packet;
1120 packet_number += 1;
1122 isom_portable_chunk_t *last_chunk = lsmash_get_entry_data( timeline->chunk_list, timeline->chunk_list->entry_count );
1123 if( last_chunk )
1125 if( offset_from_chunk )
1126 last_chunk->length = offset_from_chunk;
1127 else
1129 /* Remove the last invalid chunk. */
1130 lsmash_remove_entry( timeline->chunk_list, timeline->chunk_list->entry_count, NULL );
1131 --chunk_number;
1134 uint32_t sample_count = packet_number - 1;
1135 if( movie_fragments_present )
1137 isom_tfra_t *tfra = isom_get_tfra( file->mfra, track_ID );
1138 lsmash_entry_t *tfra_entry = tfra && tfra->list ? tfra->list->head : NULL;
1139 isom_tfra_location_time_entry_t *rap = tfra_entry ? (isom_tfra_location_time_entry_t *)tfra_entry->data : NULL;
1140 chunk.data_offset = 0;
1141 chunk.length = 0;
1142 /* Movie fragments */
1143 for( lsmash_entry_t *moof_entry = file->moof_list.head; moof_entry; moof_entry = moof_entry->next )
1145 isom_moof_t *moof = (isom_moof_t *)moof_entry->data;
1146 if( !moof )
1147 goto fail;
1148 uint64_t last_sample_end_pos = 0;
1149 /* Track fragments */
1150 uint32_t traf_number = 1;
1151 for( lsmash_entry_t *traf_entry = moof->traf_list.head; traf_entry; traf_entry = traf_entry->next )
1153 isom_traf_t *traf = (isom_traf_t *)traf_entry->data;
1154 if( !traf )
1155 goto fail;
1156 isom_tfhd_t *tfhd = traf->tfhd;
1157 if( !tfhd )
1158 goto fail;
1159 isom_trex_t *trex = isom_get_trex( file->moov->mvex, tfhd->track_ID );
1160 if( !trex )
1161 goto fail;
1162 /* Ignore ISOM_TF_FLAGS_DURATION_IS_EMPTY flag even if set. */
1163 if( !traf->trun_list.head )
1165 ++traf_number;
1166 continue;
1168 /* Get base_data_offset. */
1169 uint64_t base_data_offset;
1170 if( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT )
1171 base_data_offset = tfhd->base_data_offset;
1172 else if( (tfhd->flags & ISOM_TF_FLAGS_DEFAULT_BASE_IS_MOOF) || traf_entry == moof->traf_list.head )
1173 base_data_offset = moof->pos;
1174 else
1175 base_data_offset = last_sample_end_pos;
1176 /* sample grouping */
1177 isom_sgpd_t *sgpd_frag_rap;
1178 isom_sgpd_t *sgpd_frag_roll;
1179 sgpd_frag_rap = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_RAP );
1180 sbgp_rap = isom_get_fragment_sample_to_group ( traf, ISOM_GROUP_TYPE_RAP );
1181 sbgp_rap_entry = sbgp_rap && sbgp_rap->list ? sbgp_rap->list->head : NULL;
1182 sgpd_frag_roll = isom_get_roll_recovery_sample_group_description( &traf->sgpd_list );
1183 sbgp_roll = isom_get_roll_recovery_sample_to_group ( &traf->sbgp_list );
1184 sbgp_roll_entry = sbgp_roll && sbgp_roll->list ? sbgp_roll->list->head : NULL;
1185 int need_data_offset_only = (tfhd->track_ID != track_ID);
1186 /* Track runs */
1187 uint32_t trun_number = 1;
1188 for( lsmash_entry_t *trun_entry = traf->trun_list.head; trun_entry; trun_entry = trun_entry->next )
1190 isom_trun_t *trun = (isom_trun_t *)trun_entry->data;
1191 if( !trun )
1192 goto fail;
1193 if( trun->sample_count == 0 )
1195 ++trun_number;
1196 continue;
1198 /* Get data_offset. */
1199 if( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT )
1200 data_offset = trun->data_offset + base_data_offset;
1201 else if( trun_entry == traf->trun_list.head )
1202 data_offset = base_data_offset;
1203 else
1204 data_offset = last_sample_end_pos;
1205 /* */
1206 uint32_t sample_description_index = 0;
1207 isom_sdtp_entry_t *sdtp_data = NULL;
1208 if( !need_data_offset_only )
1210 /* Get sample_description_index of this track fragment. */
1211 if( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT )
1212 sample_description_index = tfhd->sample_description_index;
1213 else
1214 sample_description_index = trex->default_sample_description_index;
1215 description = (isom_sample_entry_t *)lsmash_get_entry_data( &stsd->list, sample_description_index );
1216 is_lpcm_audio = description ? isom_is_lpcm_audio( description ) : 0;
1217 /* Reference media data. */
1218 dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( dref_list, description ? description->data_reference_index : 0 );
1219 lsmash_file_t *ref_file = (!dref_entry || !dref_entry->ref_file) ? NULL : dref_entry->ref_file;
1220 /* Each track run can be considered as a chunk.
1221 * Here, we consider physically consecutive track runs as one chunk. */
1222 if( chunk.data_offset + chunk.length != data_offset || chunk.file != ref_file )
1224 chunk.data_offset = data_offset;
1225 chunk.length = 0;
1226 chunk.number = ++chunk_number;
1227 chunk.file = ref_file;
1228 if( (err = isom_add_portable_chunk_entry( timeline, &chunk )) < 0 )
1229 goto fail;
1231 /* Get dependency info for this track fragment. */
1232 sdtp_entry = traf->sdtp && traf->sdtp->list ? traf->sdtp->list->head : NULL;
1233 sdtp_data = sdtp_entry && sdtp_entry->data ? (isom_sdtp_entry_t *)sdtp_entry->data : NULL;
1235 /* Get info of each sample. */
1236 lsmash_entry_t *row_entry = trun->optional && trun->optional->head ? trun->optional->head : NULL;
1237 sample_number = 1;
1238 while( sample_number <= trun->sample_count )
1240 isom_sample_info_t info = { 0 };
1241 isom_trun_optional_row_t *row = row_entry && row_entry->data ? (isom_trun_optional_row_t *)row_entry->data : NULL;
1242 /* Get sample_size */
1243 if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT) )
1244 info.length = row->sample_size;
1245 else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT )
1246 info.length = tfhd->default_sample_size;
1247 else
1248 info.length = trex->default_sample_size;
1249 if( !need_data_offset_only )
1251 info.pos = data_offset;
1252 info.index = sample_description_index;
1253 info.chunk = (isom_portable_chunk_t *)timeline->chunk_list->tail->data;
1254 info.chunk->length += info.length;
1255 /* Get sample_duration. */
1256 if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT) )
1257 info.duration = row->sample_duration;
1258 else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT )
1259 info.duration = tfhd->default_sample_duration;
1260 else
1261 info.duration = trex->default_sample_duration;
1262 /* Get composition time offset. */
1263 if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT) )
1265 info.offset = row->sample_composition_time_offset;
1266 /* Check composition to decode timeline shift. */
1267 if( file->max_isom_version >= 6 && trun->version != 0 )
1269 uint64_t cts = dts + (int32_t)info.offset;
1270 if( (cts + timeline->ctd_shift) < dts )
1271 timeline->ctd_shift = dts - cts;
1274 else
1275 info.offset = 0;
1276 dts += info.duration;
1277 /* Update media duration and maximun sample size. */
1278 timeline->media_duration += info.duration;
1279 timeline->max_sample_size = LSMASH_MAX( timeline->max_sample_size, info.length );
1280 if( !is_lpcm_audio )
1282 /* Get sample_flags. */
1283 isom_sample_flags_t sample_flags;
1284 if( sample_number == 1 && (trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT) )
1285 sample_flags = trun->first_sample_flags;
1286 else if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT) )
1287 sample_flags = row->sample_flags;
1288 else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT )
1289 sample_flags = tfhd->default_sample_flags;
1290 else
1291 sample_flags = trex->default_sample_flags;
1292 if( sdtp_data )
1294 /* Independent and Disposable Samples Box overrides the information from sample_flags.
1295 * There is no description in the specification about this, but the intention should be such a thing.
1296 * The ground is that sample_flags is placed in media layer
1297 * while Independent and Disposable Samples Box is placed in track or presentation layer. */
1298 info.prop.leading = sdtp_data->is_leading;
1299 info.prop.independent = sdtp_data->sample_depends_on;
1300 info.prop.disposable = sdtp_data->sample_is_depended_on;
1301 info.prop.redundant = sdtp_data->sample_has_redundancy;
1302 if( sdtp_entry )
1303 sdtp_entry = sdtp_entry->next;
1304 sdtp_data = sdtp_entry ? (isom_sdtp_entry_t *)sdtp_entry->data : NULL;
1306 else
1308 info.prop.leading = sample_flags.is_leading;
1309 info.prop.independent = sample_flags.sample_depends_on;
1310 info.prop.disposable = sample_flags.sample_is_depended_on;
1311 info.prop.redundant = sample_flags.sample_has_redundancy;
1313 /* Check this sample is a sync sample or not.
1314 * Note: all sync sample shall be independent. */
1315 if( !sample_flags.sample_is_non_sync_sample
1316 && info.prop.independent != ISOM_SAMPLE_IS_NOT_INDEPENDENT )
1318 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1319 distance = 0;
1321 /* Get roll recovery grouping info. */
1322 uint32_t roll_id = sample_count + sample_number;
1323 if( sbgp_roll_entry
1324 && isom_get_roll_recovery_grouping_info( timeline,
1325 &sbgp_roll_entry, sgpd_roll, sgpd_frag_roll,
1326 &sample_number_in_sbgp_roll_entry,
1327 &info, roll_id ) < 0 )
1328 goto fail;
1329 info.prop.post_roll.identifier = roll_id;
1330 /* Get random access point grouping info. */
1331 if( sbgp_rap_entry
1332 && isom_get_random_access_point_grouping_info( timeline,
1333 &sbgp_rap_entry, sgpd_rap, sgpd_frag_rap,
1334 &sample_number_in_sbgp_rap_entry,
1335 &info, &distance ) < 0 )
1336 goto fail;
1337 /* Get the location of the sync sample from 'tfra' if it is not set up yet.
1338 * Note: there is no guarantee that its entries are placed in a specific order. */
1339 if( tfra )
1341 if( tfra->number_of_entry == 0
1342 && info.prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1343 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1344 if( rap
1345 && rap->moof_offset == moof->pos
1346 && rap->traf_number == traf_number
1347 && rap->trun_number == trun_number
1348 && rap->sample_number == sample_number )
1350 if( info.prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1351 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1352 if( tfra_entry )
1353 tfra_entry = tfra_entry->next;
1354 rap = tfra_entry ? (isom_tfra_location_time_entry_t *)tfra_entry->data : NULL;
1357 /* Set up distance from the previous random access point. */
1358 if( distance != NO_RANDOM_ACCESS_POINT )
1360 if( info.prop.pre_roll.distance == 0 )
1361 info.prop.pre_roll.distance = distance;
1362 ++distance;
1364 /* OK. Let's add its info. */
1365 if( (err = isom_add_sample_info_entry( timeline, &info )) < 0 )
1366 goto fail;
1368 else
1370 /* All LPCMFrame is a sync sample. */
1371 info.prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1372 /* OK. Let's add its info. */
1373 if( sample_count == 0 && sample_number == 1 )
1374 isom_update_bunch( &bunch, &info );
1375 else if( isom_compare_lpcm_sample_info( &bunch, &info ) )
1377 if( (err = isom_add_lpcm_bunch_entry( timeline, &bunch )) < 0 )
1378 goto fail;
1379 isom_update_bunch( &bunch, &info );
1381 else
1382 ++ bunch.sample_count;
1384 if( timeline-> info_list->entry_count
1385 && timeline->bunch_list->entry_count )
1387 lsmash_log( timeline, LSMASH_LOG_ERROR, "LPCM + non-LPCM track is not supported.\n" );
1388 err = LSMASH_ERR_PATCH_WELCOME;
1389 goto fail;
1392 data_offset += info.length;
1393 last_sample_end_pos = data_offset;
1394 if( row_entry )
1395 row_entry = row_entry->next;
1396 ++sample_number;
1398 if( !need_data_offset_only )
1399 sample_count += sample_number - 1;
1400 ++trun_number;
1401 } /* Track runs */
1402 ++traf_number;
1403 } /* Track fragments */
1404 } /* Movie fragments */
1406 else if( timeline->chunk_list->entry_count == 0 )
1407 goto fail; /* No samples in this track. */
1408 if( bunch.sample_count && (err = isom_add_lpcm_bunch_entry( timeline, &bunch )) < 0 )
1409 goto fail;
1410 if( (err = lsmash_add_entry( file->timeline, timeline )) < 0 )
1411 goto fail;
1412 /* Finish timeline construction. */
1413 timeline->sample_count = sample_count;
1414 if( timeline->info_list->entry_count )
1415 isom_timeline_set_sample_getter_funcs( timeline );
1416 else
1417 isom_timeline_set_lpcm_sample_getter_funcs( timeline );
1418 return 0;
1419 fail:
1420 isom_timeline_destroy( timeline );
1421 return err;
1424 int lsmash_construct_timeline( lsmash_root_t *root, uint32_t track_ID )
1426 if( !root
1427 || !root->file
1428 || track_ID == 0 )
1429 return LSMASH_ERR_FUNCTION_PARAM;
1430 uint32_t track_number;
1431 if( root->file->initializer )
1433 if( !root->file->initializer->moov )
1434 return LSMASH_ERR_INVALID_DATA;
1435 track_number = 1;
1436 int track_found = 0;
1437 for( lsmash_entry_t *entry = root->file->initializer->moov->trak_list.head; entry; entry = entry->next )
1439 isom_trak_t *trak = (isom_trak_t *)entry->data;
1440 if( !trak
1441 || !trak->tkhd )
1442 continue;
1443 if( trak->tkhd->track_ID == track_ID )
1445 track_found = 1;
1446 break;
1448 ++track_number;
1450 if( !track_found )
1451 return LSMASH_ERR_NAMELESS;
1453 else
1454 track_number = track_ID;
1455 return lsmash_importer_construct_timeline( root->file->importer, track_number );
1458 int lsmash_get_dts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *dts )
1460 if( !sample_number || !dts )
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_dts( timeline, sample_number, dts );
1468 int lsmash_get_cts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *cts )
1470 if( !sample_number || !cts )
1471 return LSMASH_ERR_FUNCTION_PARAM;
1472 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1473 if( !timeline || sample_number > timeline->sample_count )
1474 return LSMASH_ERR_NAMELESS;
1475 return timeline->get_cts( timeline, sample_number, cts );
1478 lsmash_sample_t *lsmash_get_sample_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number )
1480 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1481 return timeline ? timeline->get_sample( timeline, sample_number ) : NULL;
1484 int lsmash_get_sample_info_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, lsmash_sample_t *sample )
1486 if( !sample )
1487 return LSMASH_ERR_FUNCTION_PARAM;
1488 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1489 return timeline ? timeline->get_sample_info( timeline, sample_number, sample ) : -1;
1492 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 )
1494 if( !prop )
1495 return LSMASH_ERR_FUNCTION_PARAM;
1496 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1497 return timeline ? timeline->get_sample_property( timeline, sample_number, prop ) : -1;
1500 int lsmash_get_composition_to_decode_shift_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *ctd_shift )
1502 if( !ctd_shift )
1503 return LSMASH_ERR_FUNCTION_PARAM;
1504 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1505 if( !timeline )
1506 return LSMASH_ERR_NAMELESS;
1507 *ctd_shift = timeline->ctd_shift;
1508 return 0;
1511 static int isom_get_closest_past_random_accessible_point_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number )
1513 lsmash_entry_t *entry = lsmash_get_entry( timeline->info_list, sample_number-- );
1514 if( !entry
1515 || !entry->data )
1516 return LSMASH_ERR_NAMELESS;
1517 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1518 while( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1520 entry = entry->prev;
1521 if( !entry
1522 || !entry->data )
1523 return LSMASH_ERR_NAMELESS;
1524 info = (isom_sample_info_t *)entry->data;
1525 --sample_number;
1527 *rap_number = sample_number + 1;
1528 return 0;
1531 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 )
1533 lsmash_entry_t *entry = lsmash_get_entry( timeline->info_list, sample_number++ );
1534 if( !entry
1535 || !entry->data )
1536 return LSMASH_ERR_NAMELESS;
1537 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1538 while( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1540 entry = entry->next;
1541 if( !entry
1542 || !entry->data )
1543 return LSMASH_ERR_NAMELESS;
1544 info = (isom_sample_info_t *)entry->data;
1545 ++sample_number;
1547 *rap_number = sample_number - 1;
1548 return 0;
1551 static int isom_get_closest_random_accessible_point_from_media_timeline_internal( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number )
1553 if( !timeline )
1554 return LSMASH_ERR_NAMELESS;
1555 int ret;
1556 if( (ret = isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, sample_number, rap_number )) < 0
1557 && (ret = isom_get_closest_future_random_accessible_point_from_media_timeline( timeline, sample_number + 1, rap_number )) < 0 )
1558 return ret;
1559 return 0;
1562 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 )
1564 if( sample_number == 0 || !rap_number )
1565 return LSMASH_ERR_FUNCTION_PARAM;
1566 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1567 if( !timeline )
1568 return LSMASH_ERR_NAMELESS;
1569 if( timeline->info_list->entry_count == 0 )
1571 *rap_number = sample_number; /* All LPCM is sync sample. */
1572 return 0;
1574 return isom_get_closest_random_accessible_point_from_media_timeline_internal( timeline, sample_number, rap_number );
1577 int lsmash_get_closest_random_accessible_point_detail_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number,
1578 uint32_t *rap_number, lsmash_random_access_flag *ra_flags, uint32_t *leading, uint32_t *distance )
1580 if( sample_number == 0 )
1581 return LSMASH_ERR_FUNCTION_PARAM;
1582 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1583 if( !timeline )
1584 return LSMASH_ERR_NAMELESS;
1585 if( timeline->info_list->entry_count == 0 )
1587 /* All LPCM is sync sample. */
1588 *rap_number = sample_number;
1589 if( ra_flags )
1590 *ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1591 if( leading )
1592 *leading = 0;
1593 if( distance )
1594 *distance = 0;
1595 return 0;
1597 int ret = isom_get_closest_random_accessible_point_from_media_timeline_internal( timeline, sample_number, rap_number );
1598 if( ret < 0 )
1599 return ret;
1600 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, *rap_number );
1601 if( !info )
1602 return LSMASH_ERR_NAMELESS;
1603 if( ra_flags )
1604 *ra_flags = info->prop.ra_flags;
1605 if( leading )
1606 *leading = 0;
1607 if( distance )
1608 *distance = 0;
1609 if( sample_number < *rap_number )
1610 /* Impossible to desire to decode the sample of given number correctly. */
1611 return 0;
1612 else if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) )
1614 if( leading )
1616 /* Count leading samples. */
1617 uint32_t current_sample_number = *rap_number + 1;
1618 uint64_t dts;
1619 if( (ret = isom_get_dts_from_info_list( timeline, *rap_number, &dts )) < 0 )
1620 return ret;
1621 uint64_t rap_cts = timeline->ctd_shift ? (dts + (int32_t)info->offset + timeline->ctd_shift) : (dts + info->offset);
1624 dts += info->duration;
1625 if( rap_cts <= dts )
1626 break; /* leading samples of this random accessible point must not be present more. */
1627 info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, current_sample_number++ );
1628 if( !info )
1629 break;
1630 uint64_t cts = timeline->ctd_shift ? (dts + (int32_t)info->offset + timeline->ctd_shift) : (dts + info->offset);
1631 if( rap_cts > cts )
1632 ++ *leading;
1633 } while( 1 );
1635 if( !distance || sample_number == *rap_number )
1636 return 0;
1637 /* Measure distance from the first closest non-recovery random accessible point to the second. */
1638 uint32_t prev_rap_number = *rap_number;
1641 if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) < 0 )
1642 /* The previous random accessible point is not present. */
1643 return 0;
1644 info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, prev_rap_number );
1645 if( !info )
1646 return LSMASH_ERR_NAMELESS;
1647 if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) )
1649 /* Decode shall already complete at the first closest non-recovery random accessible point if starting to decode from the second. */
1650 *distance = *rap_number - prev_rap_number;
1651 return 0;
1653 } while( 1 );
1655 if( !distance )
1656 return 0;
1657 /* Calculate roll-distance. */
1658 if( info->prop.pre_roll.distance )
1660 /* Pre-roll recovery */
1661 uint32_t prev_rap_number = *rap_number;
1664 if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) < 0
1665 && *rap_number < info->prop.pre_roll.distance )
1667 /* The previous random accessible point is not present.
1668 * And sample of given number might be not able to decoded correctly. */
1669 *distance = 0;
1670 return 0;
1672 if( prev_rap_number + info->prop.pre_roll.distance <= *rap_number )
1675 * |<---- pre-roll distance ---->|
1676 * |<--------- distance -------->|
1677 * media +++++++++++++++++++++++++ *** +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1678 * ^ ^ ^ ^
1679 * random accessible point starting point random accessible point given sample
1680 * (complete)
1682 *distance = info->prop.pre_roll.distance;
1683 return 0;
1685 else if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) )
1688 * |<------------ pre-roll distance ------------------>|
1689 * |<------ distance ------->|
1690 * media ++++++++++++++++ *** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1691 * ^ ^ ^ ^
1692 * random accessible point random accessible point given sample
1693 * (starting point) (complete)
1695 *distance = *rap_number - prev_rap_number;
1696 return 0;
1698 } while( 1 );
1700 /* Post-roll recovery */
1701 if( sample_number >= info->prop.post_roll.complete )
1703 * |<----- post-roll distance ----->|
1704 * (distance = 0)
1705 * media +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1706 * ^ ^ ^
1707 * random accessible point complete given sample
1708 * (starting point)
1710 return 0;
1711 uint32_t prev_rap_number = *rap_number;
1714 if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) < 0 )
1715 /* The previous random accessible point is not present. */
1716 return 0;
1717 info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, prev_rap_number );
1718 if( !info )
1719 return LSMASH_ERR_NAMELESS;
1720 if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) || sample_number >= info->prop.post_roll.complete )
1722 *distance = *rap_number - prev_rap_number;
1723 return 0;
1725 } while( 1 );
1728 int lsmash_check_sample_existence_in_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number )
1730 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1731 return timeline ? timeline->check_sample_existence( timeline, sample_number ) : 0;
1734 int lsmash_get_last_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *last_sample_delta )
1736 if( !last_sample_delta )
1737 return LSMASH_ERR_FUNCTION_PARAM;
1738 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1739 return timeline ? timeline->get_sample_duration( timeline, timeline->sample_count, last_sample_delta ) : -1;
1742 int lsmash_get_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint32_t *sample_delta )
1744 if( !sample_delta )
1745 return LSMASH_ERR_FUNCTION_PARAM;
1746 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1747 return timeline ? timeline->get_sample_duration( timeline, sample_number, sample_delta ) : -1;
1750 uint32_t lsmash_get_sample_count_in_media_timeline( lsmash_root_t *root, uint32_t track_ID )
1752 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1753 if( !timeline )
1754 return 0;
1755 return timeline->sample_count;
1758 uint32_t lsmash_get_max_sample_size_in_media_timeline( lsmash_root_t *root, uint32_t track_ID )
1760 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1761 if( !timeline )
1762 return 0;
1763 return timeline->max_sample_size;
1766 uint64_t lsmash_get_media_duration_from_media_timeline( lsmash_root_t *root, uint32_t track_ID )
1768 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1769 if( !timeline )
1770 return 0;
1771 return timeline->media_duration;
1774 isom_elst_entry_t *isom_timelime_get_explicit_timeline_map
1776 lsmash_root_t *root,
1777 uint32_t track_ID,
1778 uint32_t edit_number
1781 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1782 if( !timeline )
1783 return NULL;
1784 return lsmash_get_entry_data( timeline->edit_list, edit_number );
1787 uint32_t isom_timelime_count_explicit_timeline_map
1789 lsmash_root_t *root,
1790 uint32_t track_ID
1793 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1794 if( !timeline )
1795 return 0;
1796 return timeline->edit_list->entry_count;
1799 int lsmash_copy_timeline_map( lsmash_root_t *dst, uint32_t dst_track_ID, lsmash_root_t *src, uint32_t src_track_ID )
1801 if( isom_check_initializer_present( dst ) < 0
1802 || isom_check_initializer_present( src ) < 0 )
1803 return LSMASH_ERR_FUNCTION_PARAM;
1804 lsmash_file_t *dst_file = dst->file->initializer;
1805 isom_trak_t *dst_trak = isom_get_trak( dst_file, dst_track_ID );
1806 if( !dst_file->moov
1807 || !dst_file->moov->mvhd
1808 || dst_file->moov->mvhd->timescale == 0
1809 || !dst_trak
1810 || !dst_trak->mdia
1811 || !dst_trak->mdia->mdhd
1812 || dst_trak->mdia->mdhd->timescale == 0
1813 || !dst_trak->mdia->minf
1814 || !dst_trak->mdia->minf->stbl )
1815 return LSMASH_ERR_NAMELESS;
1816 if( dst_trak->edts
1817 && dst_trak->edts->elst )
1818 lsmash_remove_entries( dst_trak->edts->elst->list, NULL );
1819 uint32_t src_movie_timescale;
1820 uint32_t src_media_timescale;
1821 uint64_t src_track_duration;
1822 uint64_t src_media_duration;
1823 int32_t src_ctd_shift; /* Add timeline shift difference between src and dst to each media_time.
1824 * Therefore, call this function as later as possible. */
1825 lsmash_entry_t *src_entry = NULL;
1826 lsmash_file_t *src_file = src->file->initializer;
1827 isom_trak_t *src_trak = isom_get_trak( src_file, src_track_ID );
1828 int src_fragmented = !!(src_file->flags & LSMASH_FILE_MODE_FRAGMENTED);
1829 if( !src_trak
1830 || !src_trak->edts
1831 || !src_trak->edts->elst
1832 || !src_trak->edts->elst->list
1833 || src_fragmented )
1835 /* Get from constructed timeline instead of boxes. */
1836 isom_timeline_t *src_timeline = isom_get_timeline( src, src_track_ID );
1837 if( src_timeline
1838 && src_timeline->movie_timescale
1839 && src_timeline->media_timescale )
1841 src_entry = src_timeline->edit_list->head;
1842 if( !src_entry )
1843 return 0;
1844 src_movie_timescale = src_timeline->movie_timescale;
1845 src_media_timescale = src_timeline->media_timescale;
1846 src_track_duration = src_timeline->track_duration;
1847 src_media_duration = src_timeline->media_duration;
1848 src_ctd_shift = src_timeline->ctd_shift;
1850 else if( !src_fragmented )
1851 return LSMASH_ERR_NAMELESS;
1853 if( !src_entry )
1855 if( !src_file->moov
1856 || !src_file->moov->mvhd
1857 || src_file->moov->mvhd->timescale == 0
1858 || !src_trak
1859 || !src_trak->tkhd
1860 || !src_trak->mdia
1861 || !src_trak->mdia->mdhd
1862 || src_trak->mdia->mdhd->timescale == 0
1863 || !src_trak->mdia->minf
1864 || !src_trak->mdia->minf->stbl )
1865 return LSMASH_ERR_NAMELESS;
1866 if( !src_trak->edts
1867 || !src_trak->edts->elst
1868 || !src_trak->edts->elst->list
1869 || !src_trak->edts->elst->list->head )
1870 return 0;
1871 src_entry = src_trak->edts->elst->list->head;
1872 src_movie_timescale = src_file->moov->mvhd->timescale;
1873 src_media_timescale = src_trak->mdia->mdhd->timescale;
1874 src_track_duration = src_trak->tkhd->duration;
1875 src_media_duration = src_trak->mdia->mdhd->duration;
1876 src_ctd_shift = src_trak->mdia->minf->stbl->cslg ? src_trak->mdia->minf->stbl->cslg->compositionToDTSShift : 0;
1878 /* Generate the edit list if absent in the destination. */
1879 if( (!dst_trak->edts && !isom_add_edts( dst_trak ))
1880 || (!dst_trak->edts->elst && !isom_add_elst( dst_trak->edts )) )
1881 return LSMASH_ERR_NAMELESS;
1882 uint32_t dst_movie_timescale = dst_file->moov->mvhd->timescale;
1883 uint32_t dst_media_timescale = dst_trak->mdia->mdhd->timescale;
1884 int32_t dst_ctd_shift = dst_trak->mdia->minf->stbl->cslg ? dst_trak->mdia->minf->stbl->cslg->compositionToDTSShift : 0;
1885 int32_t media_time_shift = src_ctd_shift - dst_ctd_shift;
1886 lsmash_entry_list_t *dst_list = dst_trak->edts->elst->list;
1887 while( src_entry )
1889 isom_elst_entry_t *src_data = (isom_elst_entry_t *)src_entry->data;
1890 if( !src_data )
1891 return LSMASH_ERR_NAMELESS;
1892 isom_elst_entry_t *dst_data = (isom_elst_entry_t *)lsmash_malloc( sizeof(isom_elst_entry_t) );
1893 if( !dst_data )
1894 return LSMASH_ERR_MEMORY_ALLOC;
1895 uint64_t segment_duration;
1896 if( src_data->segment_duration == 0 && !dst_file->fragment )
1897 /* The implicit duration edit is not suitable for non-fragmented movie file.
1898 * Set an appropriate duration from the source track. */
1899 segment_duration = src_fragmented
1900 ? (uint64_t)(src_media_duration * ((double)src_movie_timescale / src_media_timescale))
1901 : src_track_duration;
1902 else
1903 segment_duration = src_data->segment_duration;
1904 dst_data->segment_duration = segment_duration * ((double)dst_movie_timescale / src_movie_timescale) + 0.5;
1905 dst_data->media_rate = src_data->media_rate;
1906 if( src_data->media_time != ISOM_EDIT_MODE_EMPTY )
1907 dst_data->media_time = (src_data->media_time + media_time_shift) * ((double)dst_media_timescale / src_media_timescale) + 0.5;
1908 else
1909 dst_data->media_time = ISOM_EDIT_MODE_EMPTY;
1910 if( lsmash_add_entry( dst_list, dst_data ) < 0 )
1912 lsmash_free( dst_data );
1913 return LSMASH_ERR_MEMORY_ALLOC;
1915 src_entry = src_entry->next;
1917 return 0;
1920 int lsmash_set_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list )
1922 if( !root || !root->file || !ts_list )
1923 return -1;
1924 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1925 if( !timeline )
1926 return LSMASH_ERR_NAMELESS;
1927 if( timeline->info_list->entry_count == 0 )
1929 lsmash_log( timeline, LSMASH_LOG_ERROR, "Changing timestamps of LPCM track is not supported.\n" );
1930 return LSMASH_ERR_PATCH_WELCOME;
1932 if( ts_list->sample_count != timeline->info_list->entry_count )
1933 return LSMASH_ERR_INVALID_DATA; /* Number of samples must be same. */
1934 lsmash_media_ts_t *ts = ts_list->timestamp;
1935 if( ts[0].dts )
1936 return LSMASH_ERR_INVALID_DATA; /* DTS must start from value zero. */
1937 /* Update DTSs. */
1938 uint32_t sample_count = ts_list->sample_count;
1939 uint32_t i;
1940 if( timeline->info_list->entry_count > 1 )
1942 i = 1;
1943 lsmash_entry_t *entry = timeline->info_list->head;
1944 isom_sample_info_t *info = NULL;
1945 while( i < sample_count )
1947 info = (isom_sample_info_t *)entry->data;
1948 if( !info || (ts[i].dts < ts[i - 1].dts) )
1949 return LSMASH_ERR_INVALID_DATA;
1950 info->duration = ts[i].dts - ts[i - 1].dts;
1951 entry = entry->next;
1952 ++i;
1954 if( i > 1 )
1956 if( !entry
1957 || !entry->data )
1958 return LSMASH_ERR_INVALID_DATA;
1959 /* Copy the previous duration. */
1960 ((isom_sample_info_t *)entry->data)->duration = info->duration;
1962 else
1963 return LSMASH_ERR_INVALID_DATA; /* Irregular case: sample_count this timeline has is incorrect. */
1965 else /* still image */
1966 ((isom_sample_info_t *)timeline->info_list->head->data)->duration = UINT32_MAX;
1967 /* Update CTSs.
1968 * ToDo: hint track must not have any sample_offset. */
1969 i = 0;
1970 timeline->ctd_shift = 0;
1971 for( lsmash_entry_t *entry = timeline->info_list->head; entry; entry = entry->next )
1973 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1974 if( (ts[i].cts + timeline->ctd_shift) < ts[i].dts )
1975 timeline->ctd_shift = ts[i].dts - ts[i].cts;
1976 info->offset = ts[i].cts - ts[i].dts;
1977 ++i;
1979 if( timeline->ctd_shift && (!root->file->qt_compatible || root->file->max_isom_version < 4) )
1980 return LSMASH_ERR_INVALID_DATA; /* Don't allow composition to decode timeline shift. */
1981 return 0;
1984 int lsmash_get_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list )
1986 if( !ts_list )
1987 return LSMASH_ERR_FUNCTION_PARAM;
1988 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1989 if( !timeline )
1990 return LSMASH_ERR_NAMELESS;
1991 uint32_t sample_count = timeline->info_list->entry_count;
1992 if( !sample_count )
1994 ts_list->sample_count = 0;
1995 ts_list->timestamp = NULL;
1996 return 0;
1998 lsmash_media_ts_t *ts = lsmash_malloc( sample_count * sizeof(lsmash_media_ts_t) );
1999 if( !ts )
2000 return LSMASH_ERR_MEMORY_ALLOC;
2001 uint64_t dts = 0;
2002 uint32_t i = 0;
2003 if( timeline->info_list->entry_count )
2004 for( lsmash_entry_t *entry = timeline->info_list->head; entry; entry = entry->next )
2006 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
2007 if( !info )
2009 lsmash_free( ts );
2010 return LSMASH_ERR_NAMELESS;
2012 ts[i].dts = dts;
2013 ts[i].cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset);
2014 dts += info->duration;
2015 ++i;
2017 else
2018 for( lsmash_entry_t *entry = timeline->bunch_list->head; entry; entry = entry->next )
2020 isom_lpcm_bunch_t *bunch = (isom_lpcm_bunch_t *)entry->data;
2021 if( !bunch )
2023 lsmash_free( ts );
2024 return LSMASH_ERR_NAMELESS;
2026 for( uint32_t j = 0; j < bunch->sample_count; j++ )
2028 ts[i].dts = dts;
2029 ts[i].cts = timeline->ctd_shift ? (dts + (int32_t)bunch->offset) : (dts + bunch->offset);
2030 dts += bunch->duration;
2031 ++i;
2034 ts_list->sample_count = sample_count;
2035 ts_list->timestamp = ts;
2036 return 0;
2039 void lsmash_delete_media_timestamps( lsmash_media_ts_list_t *ts_list )
2041 if( !ts_list )
2042 return;
2043 lsmash_freep( &ts_list->timestamp );
2044 ts_list->sample_count = 0;
2047 static int isom_compare_dts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b )
2049 int64_t diff = (int64_t)(a->dts - b->dts);
2050 return diff > 0 ? 1 : (diff == 0 ? 0 : -1);
2053 void lsmash_sort_timestamps_decoding_order( lsmash_media_ts_list_t *ts_list )
2055 if( !ts_list )
2056 return;
2057 qsort( ts_list->timestamp, ts_list->sample_count, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))isom_compare_dts );
2060 static int isom_compare_cts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b )
2062 int64_t diff = (int64_t)(a->cts - b->cts);
2063 return diff > 0 ? 1 : (diff == 0 ? 0 : -1);
2066 void lsmash_sort_timestamps_composition_order( lsmash_media_ts_list_t *ts_list )
2068 if( !ts_list )
2069 return;
2070 qsort( ts_list->timestamp, ts_list->sample_count, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))isom_compare_cts );
2073 int lsmash_get_max_sample_delay( lsmash_media_ts_list_t *ts_list, uint32_t *max_sample_delay )
2075 if( !ts_list || !max_sample_delay )
2076 return LSMASH_ERR_FUNCTION_PARAM;
2077 lsmash_media_ts_t *orig_ts = ts_list->timestamp;
2078 lsmash_media_ts_t *ts = lsmash_malloc( ts_list->sample_count * sizeof(lsmash_media_ts_t) );
2079 if( !ts )
2080 return LSMASH_ERR_MEMORY_ALLOC;
2081 ts_list->timestamp = ts;
2082 *max_sample_delay = 0;
2083 for( uint32_t i = 0; i < ts_list->sample_count; i++ )
2085 ts[i].cts = orig_ts[i].cts; /* for sorting */
2086 ts[i].dts = i;
2088 lsmash_sort_timestamps_composition_order( ts_list );
2089 for( uint32_t i = 0; i < ts_list->sample_count; i++ )
2090 if( i < ts[i].dts )
2092 uint32_t sample_delay = ts[i].dts - i;
2093 *max_sample_delay = LSMASH_MAX( *max_sample_delay, sample_delay );
2095 lsmash_free( ts );
2096 ts_list->timestamp = orig_ts;
2097 return 0;