isom: Utilize meaningful error values.
[L-SMASH.git] / core / timeline.c
blobcb75c7ea05f3fa0d6ea18ed3489fc8542962286a
1 /*****************************************************************************
2 * timeline.c:
3 *****************************************************************************
4 * Copyright (C) 2011-2014 L-SMASH project
6 * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
8 * Permission to use, copy, modify, and/or distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 *****************************************************************************/
21 /* This file is available under an ISC license. */
23 #ifdef LSMASH_DEMUXER_ENABLED
25 #include "common/internal.h" /* must be placed first */
27 #include <stdlib.h>
28 #include <string.h>
29 #include <inttypes.h>
31 #include "box.h"
33 #include "codecs/mp4a.h"
34 #include "codecs/mp4sys.h"
35 #include "codecs/description.h"
37 #define NO_RANDOM_ACCESS_POINT 0xffffffff
39 typedef struct
41 uint64_t data_offset;
42 uint64_t length;
43 uint32_t number; /* useless at present */
44 lsmash_file_t *file;
45 } isom_portable_chunk_t;
47 typedef struct
49 uint64_t pos;
50 uint32_t duration;
51 uint32_t offset;
52 uint32_t length;
53 uint32_t index;
54 isom_portable_chunk_t *chunk;
55 lsmash_sample_property_t prop;
56 } isom_sample_info_t;
58 typedef struct
60 uint64_t pos; /* position of the first sample in this bunch */
61 uint32_t duration; /* duration in media timescale each sample has */
62 uint32_t offset; /* offset between composition time and decoding time each sample has */
63 uint32_t length; /* data size each sample has */
64 uint32_t index; /* sample_description_index applied to each sample */
65 isom_portable_chunk_t *chunk; /* chunk samples belong to */
66 lsmash_sample_property_t prop; /* property applied to each sample */
67 uint32_t sample_count; /* number of samples in this bunch */
68 } isom_lpcm_bunch_t;
70 static const lsmash_class_t lsmash_timeline_class =
72 "timeline"
75 typedef struct isom_timeline_tag isom_timeline_t;
77 struct isom_timeline_tag
79 const lsmash_class_t *class;
80 uint32_t track_ID;
81 uint32_t movie_timescale;
82 uint32_t media_timescale;
83 uint32_t sample_count;
84 uint32_t max_sample_size;
85 uint32_t ctd_shift; /* shift from composition to decode timeline */
86 uint64_t media_duration;
87 uint64_t track_duration;
88 uint32_t last_accessed_sample_number;
89 uint64_t last_accessed_sample_dts;
90 uint32_t last_accessed_lpcm_bunch_number;
91 uint32_t last_accessed_lpcm_bunch_duration;
92 uint32_t last_accessed_lpcm_bunch_sample_count;
93 uint32_t last_accessed_lpcm_bunch_first_sample_number;
94 uint64_t last_accessed_lpcm_bunch_dts;
95 lsmash_entry_list_t edit_list [1]; /* list of edits */
96 lsmash_entry_list_t chunk_list[1]; /* list of chunks */
97 lsmash_entry_list_t info_list [1]; /* list of sample info */
98 lsmash_entry_list_t bunch_list[1]; /* list of LPCM bunch */
99 int (*get_dts)( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts );
100 int (*get_cts)( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts );
101 int (*get_sample_duration)( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration );
102 lsmash_sample_t *(*get_sample)( isom_timeline_t *timeline, uint32_t sample_number );
103 int (*get_sample_info)( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample );
104 int (*get_sample_property)( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop );
105 int (*check_sample_existence)( isom_timeline_t *timeline, uint32_t sample_number );
108 static isom_timeline_t *isom_get_timeline( lsmash_root_t *root, uint32_t track_ID )
110 if( isom_check_initializer_present( root ) < 0
111 || track_ID == 0
112 || !root->file->timeline )
113 return NULL;
114 for( lsmash_entry_t *entry = root->file->timeline->head; entry; entry = entry->next )
116 isom_timeline_t *timeline = (isom_timeline_t *)entry->data;
117 if( !timeline )
118 return NULL;
119 if( timeline->track_ID == track_ID )
120 return timeline;
122 return NULL;
125 static isom_timeline_t *isom_create_timeline( void )
127 isom_timeline_t *timeline = lsmash_malloc_zero( sizeof(isom_timeline_t) );
128 if( !timeline )
129 return NULL;
130 timeline->class = &lsmash_timeline_class;
131 lsmash_init_entry_list( timeline->edit_list );
132 lsmash_init_entry_list( timeline->chunk_list );
133 lsmash_init_entry_list( timeline->info_list );
134 lsmash_init_entry_list( timeline->bunch_list );
135 return timeline;
138 static void isom_destruct_timeline_direct( isom_timeline_t *timeline )
140 if( !timeline )
141 return;
142 lsmash_remove_entries( timeline->edit_list, NULL );
143 lsmash_remove_entries( timeline->chunk_list, NULL ); /* chunk data must be already freed. */
144 lsmash_remove_entries( timeline->info_list, NULL );
145 lsmash_remove_entries( timeline->bunch_list, NULL );
146 lsmash_free( timeline );
149 void isom_remove_timelines( lsmash_file_t *file )
151 if( !file
152 || !file->timeline )
153 return;
154 lsmash_remove_list( file->timeline, isom_destruct_timeline_direct );
157 void lsmash_destruct_timeline( lsmash_root_t *root, uint32_t track_ID )
159 if( track_ID == 0
160 || !root
161 || !root->file
162 || !root->file->timeline )
163 return;
164 for( lsmash_entry_t *entry = root->file->timeline->head; entry; entry = entry->next )
166 isom_timeline_t *timeline = (isom_timeline_t *)entry->data;
167 if( !timeline )
168 continue;
169 if( timeline->track_ID == track_ID )
171 lsmash_remove_entry_direct( root->file->timeline, entry, isom_destruct_timeline_direct );
172 break;
177 static void isom_get_qt_fixed_comp_audio_sample_quants
179 isom_timeline_t *timeline,
180 isom_sample_entry_t *description,
181 uint32_t *samples_per_packet,
182 uint32_t *constant_sample_size
185 isom_audio_entry_t *audio = (isom_audio_entry_t *)description;
186 if( audio->version == 0 )
188 uint32_t dummy;
189 if( !isom_get_implicit_qt_fixed_comp_audio_sample_quants( audio, samples_per_packet, constant_sample_size, &dummy ) )
191 /* LPCM */
192 if( !isom_is_lpcm_audio( audio ) )
193 lsmash_log( timeline, LSMASH_LOG_WARNING, "unsupported implicit sample table!\n" );
194 *samples_per_packet = 1;
195 *constant_sample_size = (audio->samplesize * audio->channelcount) / 8;
198 else if( audio->version == 1 )
200 *samples_per_packet = audio->samplesPerPacket;
201 *constant_sample_size = audio->bytesPerFrame;
203 else /* if( audio->version == 2 ) */
205 *samples_per_packet = audio->constLPCMFramesPerAudioPacket;
206 *constant_sample_size = audio->constBytesPerAudioPacket;
210 static int isom_is_qt_fixed_compressed_audio
212 isom_sample_entry_t *description
215 if( (description->manager & LSMASH_VIDEO_DESCRIPTION) || !isom_is_qt_audio( description->type ) )
216 return 0;
217 /* LPCM is a special case of fixed compression. */
218 return (((isom_audio_entry_t *)description)->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION);
221 static int isom_add_sample_info_entry( isom_timeline_t *timeline, isom_sample_info_t *src_info )
223 isom_sample_info_t *dst_info = lsmash_malloc( sizeof(isom_sample_info_t) );
224 if( !dst_info )
225 return LSMASH_ERR_MEMORY_ALLOC;
226 if( lsmash_add_entry( timeline->info_list, dst_info ) < 0 )
228 lsmash_free( dst_info );
229 return LSMASH_ERR_MEMORY_ALLOC;
231 *dst_info = *src_info;
232 return 0;
235 static int isom_add_lpcm_bunch_entry( isom_timeline_t *timeline, isom_lpcm_bunch_t *src_bunch )
237 isom_lpcm_bunch_t *dst_bunch = lsmash_malloc( sizeof(isom_lpcm_bunch_t) );
238 if( !dst_bunch )
239 return LSMASH_ERR_MEMORY_ALLOC;
240 if( lsmash_add_entry( timeline->bunch_list, dst_bunch ) < 0 )
242 lsmash_free( dst_bunch );
243 return LSMASH_ERR_MEMORY_ALLOC;
245 *dst_bunch = *src_bunch;
246 return 0;
249 static int isom_add_portable_chunk_entry( isom_timeline_t *timeline, isom_portable_chunk_t *src_chunk )
251 isom_portable_chunk_t *dst_chunk = lsmash_malloc( sizeof(isom_portable_chunk_t) );
252 if( !dst_chunk )
253 return LSMASH_ERR_MEMORY_ALLOC;
254 if( lsmash_add_entry( timeline->chunk_list, dst_chunk ) < 0 )
256 lsmash_free( dst_chunk );
257 return LSMASH_ERR_MEMORY_ALLOC;
259 *dst_chunk = *src_chunk;
260 return 0;
263 static int isom_compare_lpcm_sample_info( isom_lpcm_bunch_t *bunch, isom_sample_info_t *info )
265 return info->duration != bunch->duration
266 || info->offset != bunch->offset
267 || info->length != bunch->length
268 || info->index != bunch->index
269 || info->chunk != bunch->chunk;
272 static void isom_update_bunch( isom_lpcm_bunch_t *bunch, isom_sample_info_t *info )
274 bunch->pos = info->pos;
275 bunch->duration = info->duration;
276 bunch->offset = info->offset;
277 bunch->length = info->length;
278 bunch->index = info->index;
279 bunch->chunk = info->chunk;
280 bunch->prop = info->prop;
281 bunch->sample_count = 1;
284 static isom_lpcm_bunch_t *isom_get_bunch( isom_timeline_t *timeline, uint32_t sample_number )
286 if( sample_number >= timeline->last_accessed_lpcm_bunch_first_sample_number
287 && sample_number < timeline->last_accessed_lpcm_bunch_first_sample_number + timeline->last_accessed_lpcm_bunch_sample_count )
288 /* Get from the last accessed LPCM bunch. */
289 return (isom_lpcm_bunch_t *)lsmash_get_entry_data( timeline->bunch_list, timeline->last_accessed_lpcm_bunch_number );
290 uint32_t first_sample_number_in_next_bunch;
291 uint32_t bunch_number = 1;
292 uint64_t bunch_dts;
293 if( timeline->last_accessed_lpcm_bunch_first_sample_number
294 && timeline->last_accessed_lpcm_bunch_first_sample_number <= sample_number )
296 first_sample_number_in_next_bunch = timeline->last_accessed_lpcm_bunch_first_sample_number + timeline->last_accessed_lpcm_bunch_sample_count;
297 bunch_number += timeline->last_accessed_lpcm_bunch_number;
298 bunch_dts = timeline->last_accessed_lpcm_bunch_dts
299 + timeline->last_accessed_lpcm_bunch_duration * timeline->last_accessed_lpcm_bunch_sample_count;
301 else
303 /* Seek from the first LPCM bunch. */
304 first_sample_number_in_next_bunch = 1;
305 bunch_dts = 0;
307 isom_lpcm_bunch_t *bunch = (isom_lpcm_bunch_t *)lsmash_get_entry_data( timeline->bunch_list, bunch_number++ );
308 if( !bunch )
309 return NULL;
310 first_sample_number_in_next_bunch += bunch->sample_count;
311 while( sample_number >= first_sample_number_in_next_bunch )
313 bunch_dts += bunch->duration * bunch->sample_count;
314 bunch = (isom_lpcm_bunch_t *)lsmash_get_entry_data( timeline->bunch_list, bunch_number++ );
315 if( !bunch )
316 return NULL;
317 first_sample_number_in_next_bunch += bunch->sample_count;
319 timeline->last_accessed_lpcm_bunch_dts = bunch_dts;
320 timeline->last_accessed_lpcm_bunch_number = bunch_number - 1;
321 timeline->last_accessed_lpcm_bunch_duration = bunch->duration;
322 timeline->last_accessed_lpcm_bunch_sample_count = bunch->sample_count;
323 timeline->last_accessed_lpcm_bunch_first_sample_number = first_sample_number_in_next_bunch - bunch->sample_count;
324 return bunch;
327 static int isom_get_dts_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts )
329 if( sample_number == timeline->last_accessed_sample_number )
330 *dts = timeline->last_accessed_sample_dts;
331 else if( sample_number == 1 )
332 *dts = 0;
333 else if( sample_number == timeline->last_accessed_sample_number + 1 )
335 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, timeline->last_accessed_sample_number );
336 if( !info )
337 return LSMASH_ERR_NAMELESS;
338 *dts = timeline->last_accessed_sample_dts + info->duration;
340 else if( sample_number == timeline->last_accessed_sample_number - 1 )
342 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, timeline->last_accessed_sample_number - 1 );
343 if( !info )
344 return LSMASH_ERR_NAMELESS;
345 *dts = timeline->last_accessed_sample_dts - info->duration;
347 else
349 *dts = 0;
350 uint32_t distance = sample_number - 1;
351 lsmash_entry_t *entry;
352 for( entry = timeline->info_list->head; entry; entry = entry->next )
354 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
355 if( !info )
356 return LSMASH_ERR_NAMELESS;
357 if( distance-- == 0 )
358 break;
359 *dts += info->duration;
361 if( !entry )
362 return LSMASH_ERR_NAMELESS;
364 /* Note: last_accessed_sample_number is always updated together with last_accessed_sample_dts, and vice versa. */
365 timeline->last_accessed_sample_dts = *dts;
366 timeline->last_accessed_sample_number = sample_number;
367 return 0;
370 static int isom_get_cts_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts )
372 int ret = isom_get_dts_from_info_list( timeline, sample_number, cts );
373 if( ret < 0 )
374 return ret;
375 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
376 if( !info )
377 return LSMASH_ERR_NAMELESS;
378 *cts = timeline->ctd_shift ? (*cts + (int32_t)info->offset) : (*cts + info->offset);
379 return 0;
382 static int isom_get_dts_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *dts )
384 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
385 if( !bunch )
386 return LSMASH_ERR_NAMELESS;
387 *dts = timeline->last_accessed_lpcm_bunch_dts + (sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number) * bunch->duration;
388 return 0;
391 static int isom_get_cts_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint64_t *cts )
393 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
394 if( !bunch )
395 return LSMASH_ERR_NAMELESS;
396 *cts = timeline->last_accessed_lpcm_bunch_dts + (sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number) * bunch->duration + bunch->offset;
397 return 0;
400 static int isom_get_sample_duration_from_info_list( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration )
402 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
403 if( !info )
404 return LSMASH_ERR_NAMELESS;
405 *sample_duration = info->duration;
406 return 0;
409 static int isom_get_sample_duration_from_bunch_list( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *sample_duration )
411 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
412 if( !bunch )
413 return LSMASH_ERR_NAMELESS;
414 *sample_duration = bunch->duration;
415 return 0;
418 static int isom_check_sample_existence_in_info_list( isom_timeline_t *timeline, uint32_t sample_number )
420 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
421 if( !info || !info->chunk )
422 return 0;
423 return !!info->chunk->file;
426 static int isom_check_sample_existence_in_bunch_list( isom_timeline_t *timeline, uint32_t sample_number )
428 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
429 if( !bunch || !bunch->chunk )
430 return 0;
431 return !!bunch->chunk->file;
434 static lsmash_sample_t *isom_read_sample_data_from_stream
436 lsmash_file_t *file,
437 isom_timeline_t *timeline,
438 uint32_t sample_length,
439 uint64_t sample_pos
442 lsmash_sample_t *sample = lsmash_create_sample( 0 );
443 if( !sample )
444 return NULL;
445 lsmash_bs_t *bs = file->bs;
446 lsmash_bs_read_seek( bs, sample_pos, SEEK_SET );
447 sample->data = lsmash_bs_get_bytes( bs, sample_length );
448 if( !sample->data )
450 lsmash_delete_sample( sample );
451 return NULL;
453 return sample;
456 static lsmash_sample_t *isom_get_lpcm_sample_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number )
458 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
459 if( !bunch
460 || !bunch->chunk )
461 return NULL;
462 /* Get data of a sample from the stream. */
463 uint64_t sample_number_offset = sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number;
464 uint64_t sample_pos = bunch->pos + sample_number_offset * bunch->length;
465 lsmash_sample_t *sample = isom_read_sample_data_from_stream( bunch->chunk->file, timeline, bunch->length, sample_pos );
466 if( !sample )
467 return NULL;
468 /* Get sample info. */
469 sample->dts = timeline->last_accessed_lpcm_bunch_dts + sample_number_offset * bunch->duration;
470 sample->cts = timeline->ctd_shift ? (sample->dts + (int32_t)bunch->offset) : (sample->dts + bunch->offset);
471 sample->pos = sample_pos;
472 sample->length = bunch->length;
473 sample->index = bunch->index;
474 sample->prop = bunch->prop;
475 return sample;
478 static lsmash_sample_t *isom_get_sample_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number )
480 uint64_t dts;
481 if( isom_get_dts_from_info_list( timeline, sample_number, &dts ) < 0 )
482 return NULL;
483 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
484 if( !info
485 || !info->chunk )
486 return NULL;
487 /* Get data of a sample from the stream. */
488 lsmash_sample_t *sample = isom_read_sample_data_from_stream( info->chunk->file, timeline, info->length, info->pos );
489 if( !sample )
490 return NULL;
491 /* Get sample info. */
492 sample->dts = dts;
493 sample->cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset);
494 sample->pos = info->pos;
495 sample->length = info->length;
496 sample->index = info->index;
497 sample->prop = info->prop;
498 return sample;
501 static int isom_get_lpcm_sample_info_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample )
503 isom_lpcm_bunch_t *bunch = isom_get_bunch( timeline, sample_number );
504 if( !bunch )
505 return LSMASH_ERR_NAMELESS;
506 uint64_t sample_number_offset = sample_number - timeline->last_accessed_lpcm_bunch_first_sample_number;
507 sample->dts = timeline->last_accessed_lpcm_bunch_dts + sample_number_offset * bunch->duration;
508 sample->cts = timeline->ctd_shift ? (sample->dts + (int32_t)bunch->offset) : (sample->dts + bunch->offset);
509 sample->pos = bunch->pos + sample_number_offset * bunch->length;
510 sample->length = bunch->length;
511 sample->index = bunch->index;
512 sample->prop = bunch->prop;
513 return 0;
516 static int isom_get_sample_info_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_t *sample )
518 uint64_t dts;
519 int ret = isom_get_dts_from_info_list( timeline, sample_number, &dts );
520 if( ret < 0 )
521 return ret;
522 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
523 if( !info )
524 return LSMASH_ERR_NAMELESS;
525 sample->dts = dts;
526 sample->cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset);
527 sample->pos = info->pos;
528 sample->length = info->length;
529 sample->index = info->index;
530 sample->prop = info->prop;
531 return 0;
534 static int isom_get_lpcm_sample_property_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop )
536 memset( prop, 0, sizeof(lsmash_sample_property_t) );
537 prop->ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
538 return 0;
541 static int isom_get_sample_property_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, lsmash_sample_property_t *prop )
543 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, sample_number );
544 if( !info )
545 return LSMASH_ERR_NAMELESS;
546 *prop = info->prop;
547 return 0;
550 static inline void isom_increment_sample_number_in_entry
552 uint32_t *sample_number_in_entry,
553 lsmash_entry_t **entry,
554 uint32_t sample_count
557 if( *sample_number_in_entry == sample_count )
559 *sample_number_in_entry = 1;
560 *entry = (*entry)->next;
562 else
563 *sample_number_in_entry += 1;
566 static inline isom_sgpd_t *isom_select_appropriate_sgpd
568 isom_sgpd_t *sgpd,
569 isom_sgpd_t *sgpd_frag,
570 uint32_t *group_description_index
573 if( sgpd_frag && *group_description_index >= 0x10000 )
575 /* The specification doesn't define 0x10000 explicitly, however says that there must be fewer than
576 * 65536 group definitions for this track and grouping type in the sample table in the Movie Box.
577 * So, we assume 0x10000 is equivalent to 0. */
578 *group_description_index -= 0x10000;
579 return sgpd_frag;
581 else
582 return sgpd;
585 static int isom_get_roll_recovery_grouping_info
587 isom_timeline_t *timeline,
588 lsmash_entry_t **sbgp_roll_entry,
589 isom_sgpd_t *sgpd_roll,
590 isom_sgpd_t *sgpd_frag_roll,
591 uint32_t *sample_number_in_sbgp_roll_entry,
592 isom_sample_info_t *info,
593 uint32_t sample_number
596 isom_group_assignment_entry_t *assignment = (isom_group_assignment_entry_t *)(*sbgp_roll_entry)->data;
597 if( !assignment )
598 return LSMASH_ERR_NAMELESS;
599 if( assignment->group_description_index )
601 uint32_t group_description_index = assignment->group_description_index;
602 isom_sgpd_t *sgpd = isom_select_appropriate_sgpd( sgpd_roll, sgpd_frag_roll, &group_description_index );
603 isom_roll_entry_t *roll_data = (isom_roll_entry_t *)lsmash_get_entry_data( sgpd->list, group_description_index );
604 if( roll_data )
606 if( roll_data->roll_distance > 0 )
608 /* post-roll */
609 info->prop.post_roll.complete = sample_number + roll_data->roll_distance;
610 if( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
611 info->prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_POST_ROLL_START;
613 else if( roll_data->roll_distance < 0 )
615 /* pre-roll */
616 info->prop.pre_roll.distance = -roll_data->roll_distance;
617 if( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
618 info->prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_PRE_ROLL_END;
621 else if( *sample_number_in_sbgp_roll_entry == 1 && group_description_index )
622 lsmash_log( timeline, LSMASH_LOG_WARNING, "a description of roll recoveries is not found in the Sample Group Description Box.\n" );
624 isom_increment_sample_number_in_entry( sample_number_in_sbgp_roll_entry, sbgp_roll_entry, assignment->sample_count );
625 return 0;
628 static int isom_get_random_access_point_grouping_info
630 isom_timeline_t *timeline,
631 lsmash_entry_t **sbgp_rap_entry,
632 isom_sgpd_t *sgpd_rap,
633 isom_sgpd_t *sgpd_frag_rap,
634 uint32_t *sample_number_in_sbgp_rap_entry,
635 isom_sample_info_t *info,
636 uint32_t *distance
639 isom_group_assignment_entry_t *assignment = (isom_group_assignment_entry_t *)(*sbgp_rap_entry)->data;
640 if( !assignment )
641 return LSMASH_ERR_NAMELESS;
642 if( assignment->group_description_index && (info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE) )
644 uint32_t group_description_index = assignment->group_description_index;
645 isom_sgpd_t *sgpd = isom_select_appropriate_sgpd( sgpd_rap, sgpd_frag_rap, &group_description_index );
646 isom_rap_entry_t *rap_data = (isom_rap_entry_t *)lsmash_get_entry_data( sgpd->list, group_description_index );
647 if( rap_data )
649 /* If this is not an open RAP, we treat it as an unknown RAP since non-IDR sample could make a closed GOP. */
650 info->prop.ra_flags |= (rap_data->num_leading_samples_known && !!rap_data->num_leading_samples)
651 ? ISOM_SAMPLE_RANDOM_ACCESS_FLAG_OPEN_RAP
652 : ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP;
653 *distance = 0;
655 else if( *sample_number_in_sbgp_rap_entry == 1 && group_description_index )
656 lsmash_log( timeline, LSMASH_LOG_WARNING, "a description of random access points is not found in the Sample Group Description Box.\n" );
658 isom_increment_sample_number_in_entry( sample_number_in_sbgp_rap_entry, sbgp_rap_entry, assignment->sample_count );
659 return 0;
662 int lsmash_construct_timeline( lsmash_root_t *root, uint32_t track_ID )
664 if( isom_check_initializer_present( root ) < 0 )
665 return LSMASH_ERR_FUNCTION_PARAM;
666 lsmash_file_t *file = root->file;
667 if( !file->moov
668 || !file->moov->mvhd
669 || file->moov->mvhd->timescale == 0 )
670 return LSMASH_ERR_INVALID_DATA;
671 /* Get track by track_ID. */
672 isom_trak_t *trak = isom_get_trak( file, track_ID );
673 if( !trak
674 || !trak->tkhd
675 || !trak->mdia
676 || !trak->mdia->mdhd
677 || trak->mdia->mdhd->timescale == 0
678 || !trak->mdia->minf
679 || !trak->mdia->minf->stbl )
680 return LSMASH_ERR_INVALID_DATA;
681 /* Create a timeline list if it doesn't exist. */
682 if( !file->timeline )
684 file->timeline = lsmash_create_entry_list();
685 if( !file->timeline )
686 return LSMASH_ERR_MEMORY_ALLOC;
688 /* Create a timeline. */
689 isom_timeline_t *timeline = isom_create_timeline();
690 if( !timeline )
691 return LSMASH_ERR_MEMORY_ALLOC;
692 timeline->track_ID = track_ID;
693 timeline->movie_timescale = file->moov->mvhd->timescale;
694 timeline->media_timescale = trak->mdia->mdhd->timescale;
695 timeline->track_duration = trak->tkhd->duration;
696 /* Preparation for construction. */
697 isom_elst_t *elst = trak->edts ? trak->edts->elst : NULL;
698 isom_minf_t *minf = trak->mdia->minf;
699 isom_dref_t *dref = minf->dinf->dref;
700 isom_stbl_t *stbl = minf->stbl;
701 isom_stsd_t *stsd = stbl->stsd;
702 isom_stts_t *stts = stbl->stts;
703 isom_ctts_t *ctts = stbl->ctts;
704 isom_stss_t *stss = stbl->stss;
705 isom_stps_t *stps = stbl->stps;
706 isom_sdtp_t *sdtp = stbl->sdtp;
707 isom_stsc_t *stsc = stbl->stsc;
708 isom_stsz_t *stsz = stbl->stsz;
709 isom_stco_t *stco = stbl->stco;
710 isom_sgpd_t *sgpd_rap = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
711 isom_sbgp_t *sbgp_rap = isom_get_sample_to_group ( stbl, ISOM_GROUP_TYPE_RAP );
712 isom_sgpd_t *sgpd_roll = isom_get_roll_recovery_sample_group_description( &stbl->sgpd_list );
713 isom_sbgp_t *sbgp_roll = isom_get_roll_recovery_sample_to_group ( &stbl->sbgp_list );
714 lsmash_entry_t *elst_entry = elst && elst->list ? elst->list->head : NULL;
715 lsmash_entry_t *stts_entry = stts && stts->list ? stts->list->head : NULL;
716 lsmash_entry_t *ctts_entry = ctts && ctts->list ? ctts->list->head : NULL;
717 lsmash_entry_t *stss_entry = stss && stss->list ? stss->list->head : NULL;
718 lsmash_entry_t *stps_entry = stps && stps->list ? stps->list->head : NULL;
719 lsmash_entry_t *sdtp_entry = sdtp && sdtp->list ? sdtp->list->head : NULL;
720 lsmash_entry_t *stsz_entry = stsz && stsz->list ? stsz->list->head : NULL;
721 lsmash_entry_t *stsc_entry = stsc && stsc->list ? stsc->list->head : NULL;
722 lsmash_entry_t *stco_entry = stco && stco->list ? stco->list->head : NULL;
723 lsmash_entry_t *sbgp_roll_entry = sbgp_roll && sbgp_roll->list ? sbgp_roll->list->head : NULL;
724 lsmash_entry_t *sbgp_rap_entry = sbgp_rap && sbgp_rap->list ? sbgp_rap->list->head : NULL;
725 lsmash_entry_t *next_stsc_entry = stsc_entry ? stsc_entry->next : NULL;
726 isom_stsc_entry_t *stsc_data = stsc_entry ? (isom_stsc_entry_t *)stsc_entry->data : NULL;
727 int err = LSMASH_ERR_INVALID_DATA;
728 int movie_fragments_present = (file->moov->mvex && file->moof_list.head);
729 if( !movie_fragments_present && (!stts_entry || !stsc_entry || !stco_entry || !stco_entry->data || (next_stsc_entry && !next_stsc_entry->data)) )
730 goto fail;
731 isom_sample_entry_t *description = (isom_sample_entry_t *)lsmash_get_entry_data( &stsd->list, stsc_data ? stsc_data->sample_description_index : 1 );
732 if( !description )
733 goto fail;
734 isom_dref_entry_t *dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( &dref->list, description->data_reference_index );
735 int all_sync = !stss;
736 int large_presentation = stco->large_presentation || lsmash_check_box_type_identical( stco->type, ISOM_BOX_TYPE_CO64 );
737 int is_lpcm_audio = isom_is_lpcm_audio( description );
738 int is_qt_fixed_comp_audio = isom_is_qt_fixed_compressed_audio( description );
739 int iso_sdtp = file->max_isom_version >= 2 || file->avc_extensions;
740 int allow_negative_sample_offset = ctts && ((file->max_isom_version >= 4 && ctts->version == 1) || file->qt_compatible);
741 uint32_t sample_number_in_stts_entry = 1;
742 uint32_t sample_number_in_ctts_entry = 1;
743 uint32_t sample_number_in_sbgp_roll_entry = 1;
744 uint32_t sample_number_in_sbgp_rap_entry = 1;
745 uint64_t dts = 0;
746 uint32_t chunk_number = 1;
747 uint64_t offset_from_chunk = 0;
748 uint64_t data_offset = stco_entry && stco_entry->data
749 ? large_presentation
750 ? ((isom_co64_entry_t *)stco_entry->data)->chunk_offset
751 : ((isom_stco_entry_t *)stco_entry->data)->chunk_offset
752 : 0;
753 uint32_t samples_per_packet;
754 uint32_t constant_sample_size;
755 if( is_qt_fixed_comp_audio )
756 isom_get_qt_fixed_comp_audio_sample_quants( timeline, description, &samples_per_packet, &constant_sample_size );
757 else
759 samples_per_packet = 1;
760 constant_sample_size = stsz->sample_size;
762 uint32_t sample_number = samples_per_packet;
763 uint32_t sample_number_in_chunk = samples_per_packet;
764 /* Copy edits. */
765 while( elst_entry )
767 isom_elst_entry_t *edit = (isom_elst_entry_t *)lsmash_memdup( elst_entry->data, sizeof(isom_elst_entry_t) );
768 if( !edit
769 || lsmash_add_entry( timeline->edit_list, edit ) < 0 )
771 err = LSMASH_ERR_MEMORY_ALLOC;
772 goto fail;
774 elst_entry = elst_entry->next;
776 /* Check what the first 2-bits of sample dependency means.
777 * This check is for chimera of ISO Base Media and QTFF. */
778 if( iso_sdtp && sdtp_entry )
780 while( sdtp_entry )
782 isom_sdtp_entry_t *sdtp_data = (isom_sdtp_entry_t *)sdtp_entry->data;
783 if( !sdtp_data )
784 goto fail;
785 if( sdtp_data->is_leading > 1 )
786 break; /* Apparently, it's defined under ISO Base Media. */
787 if( (sdtp_data->is_leading == 1) && (sdtp_data->sample_depends_on == ISOM_SAMPLE_IS_INDEPENDENT) )
789 /* Obviously, it's not defined under ISO Base Media. */
790 iso_sdtp = 0;
791 break;
793 sdtp_entry = sdtp_entry->next;
795 sdtp_entry = sdtp->list->head;
797 /**--- Construct media timeline. ---**/
798 isom_portable_chunk_t chunk;
799 chunk.data_offset = data_offset;
800 chunk.length = 0;
801 chunk.number = chunk_number;
802 chunk.file = (!dref_entry || !dref_entry->ref_file) ? NULL : dref_entry->ref_file;
803 if( (err = isom_add_portable_chunk_entry( timeline, &chunk )) < 0 )
804 goto fail;
805 uint32_t distance = NO_RANDOM_ACCESS_POINT;
806 uint32_t last_duration = UINT32_MAX;
807 uint32_t packet_number = 1;
808 isom_lpcm_bunch_t bunch = { 0 };
809 while( sample_number <= stsz->sample_count )
811 isom_sample_info_t info = { 0 };
812 /* Get sample duration and sample offset. */
813 for( uint32_t i = 0; i < samples_per_packet; i++ )
815 /* sample duration */
816 if( stts_entry )
818 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
819 if( !stts_data )
820 goto fail;
821 isom_increment_sample_number_in_entry( &sample_number_in_stts_entry, &stts_entry, stts_data->sample_count );
822 last_duration = stts_data->sample_delta;
824 info.duration += last_duration;
825 dts += last_duration;
826 /* sample offset */
827 uint32_t sample_offset;
828 if( ctts_entry )
830 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
831 if( !ctts_data )
832 goto fail;
833 isom_increment_sample_number_in_entry( &sample_number_in_ctts_entry, &ctts_entry, ctts_data->sample_count );
834 sample_offset = ctts_data->sample_offset;
835 if( allow_negative_sample_offset )
837 uint64_t cts = dts + (int32_t)sample_offset;
838 if( (cts + timeline->ctd_shift) < dts )
839 timeline->ctd_shift = dts - cts;
842 else
843 sample_offset = 0;
844 if( i == 0 )
845 info.offset = sample_offset;
847 timeline->media_duration += info.duration;
848 if( !is_qt_fixed_comp_audio )
850 /* Check whether sync sample or not. */
851 if( stss_entry )
853 isom_stss_entry_t *stss_data = (isom_stss_entry_t *)stss_entry->data;
854 if( !stss_data )
855 goto fail;
856 if( sample_number == stss_data->sample_number )
858 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
859 stss_entry = stss_entry->next;
860 distance = 0;
863 else if( all_sync )
864 /* Don't reset distance as 0 since MDCT-based audio frames need pre-roll for correct presentation
865 * though all of them could be marked as a sync sample. */
866 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
867 /* Check whether partial sync sample or not. */
868 if( stps_entry )
870 isom_stps_entry_t *stps_data = (isom_stps_entry_t *)stps_entry->data;
871 if( !stps_data )
872 goto fail;
873 if( sample_number == stps_data->sample_number )
875 info.prop.ra_flags |= QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC | QT_SAMPLE_RANDOM_ACCESS_FLAG_RAP;
876 stps_entry = stps_entry->next;
877 distance = 0;
880 /* Get sample dependency info. */
881 if( sdtp_entry )
883 isom_sdtp_entry_t *sdtp_data = (isom_sdtp_entry_t *)sdtp_entry->data;
884 if( !sdtp_data )
885 goto fail;
886 if( iso_sdtp )
887 info.prop.leading = sdtp_data->is_leading;
888 else
889 info.prop.allow_earlier = sdtp_data->is_leading;
890 info.prop.independent = sdtp_data->sample_depends_on;
891 info.prop.disposable = sdtp_data->sample_is_depended_on;
892 info.prop.redundant = sdtp_data->sample_has_redundancy;
893 sdtp_entry = sdtp_entry->next;
895 /* Get roll recovery grouping info. */
896 if( sbgp_roll_entry
897 && isom_get_roll_recovery_grouping_info( timeline,
898 &sbgp_roll_entry, sgpd_roll, NULL,
899 &sample_number_in_sbgp_roll_entry,
900 &info, sample_number ) < 0 )
901 goto fail;
902 info.prop.post_roll.identifier = sample_number;
903 /* Get random access point grouping info. */
904 if( sbgp_rap_entry
905 && isom_get_random_access_point_grouping_info( timeline,
906 &sbgp_rap_entry, sgpd_rap, NULL,
907 &sample_number_in_sbgp_rap_entry,
908 &info, &distance ) < 0 )
909 goto fail;
910 /* Set up distance from the previous random access point. */
911 if( distance != NO_RANDOM_ACCESS_POINT )
913 if( info.prop.pre_roll.distance == 0 )
914 info.prop.pre_roll.distance = distance;
915 ++distance;
918 else
919 /* All uncompressed and non-variable compressed audio frame is a sync sample. */
920 info.prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
921 /* Get size of sample in the stream. */
922 if( is_qt_fixed_comp_audio || !stsz_entry )
923 info.length = constant_sample_size;
924 else
926 if( !stsz_entry->data )
927 goto fail;
928 info.length = ((isom_stsz_entry_t *)stsz_entry->data)->entry_size;
929 stsz_entry = stsz_entry->next;
931 timeline->max_sample_size = LSMASH_MAX( timeline->max_sample_size, info.length );
932 /* Get chunk info. */
933 info.pos = data_offset;
934 info.index = stsc_data->sample_description_index;
935 info.chunk = (isom_portable_chunk_t *)timeline->chunk_list->tail->data;
936 offset_from_chunk += info.length;
937 if( sample_number_in_chunk == stsc_data->samples_per_chunk )
939 /* Set the length of the last chunk. */
940 if( info.chunk )
941 info.chunk->length = offset_from_chunk;
942 /* Move the next chunk. */
943 if( stco_entry )
944 stco_entry = stco_entry->next;
945 if( stco_entry
946 && stco_entry->data )
947 data_offset = large_presentation
948 ? ((isom_co64_entry_t *)stco_entry->data)->chunk_offset
949 : ((isom_stco_entry_t *)stco_entry->data)->chunk_offset;
950 chunk.data_offset = data_offset;
951 chunk.length = 0;
952 chunk.number = ++chunk_number;
953 offset_from_chunk = 0;
954 /* Check if the next entry is broken. */
955 while( next_stsc_entry && chunk_number > ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk )
957 /* Just skip broken next entry. */
958 lsmash_log( timeline, LSMASH_LOG_WARNING, "ignore broken entry in Sample To Chunk Box.\n" );
959 lsmash_log( timeline, LSMASH_LOG_WARNING, "timeline might be corrupted.\n" );
960 next_stsc_entry = next_stsc_entry->next;
961 if( next_stsc_entry
962 && !next_stsc_entry->data )
963 goto fail;
965 /* Check if the next chunk belongs to the next sequence of chunks. */
966 if( next_stsc_entry && chunk_number == ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk )
968 stsc_entry = next_stsc_entry;
969 next_stsc_entry = next_stsc_entry->next;
970 if( next_stsc_entry
971 && !next_stsc_entry->data )
972 goto fail;
973 stsc_data = (isom_stsc_entry_t *)stsc_entry->data;
974 /* Update sample description. */
975 description = (isom_sample_entry_t *)lsmash_get_entry_data( &stsd->list, stsc_data->sample_description_index );
976 is_lpcm_audio = isom_is_lpcm_audio( description );
977 is_qt_fixed_comp_audio = isom_is_qt_fixed_compressed_audio( description );
978 if( is_qt_fixed_comp_audio )
979 isom_get_qt_fixed_comp_audio_sample_quants( timeline, description, &samples_per_packet, &constant_sample_size );
980 else
982 samples_per_packet = 1;
983 constant_sample_size = stsz->sample_size;
985 /* Reference media data. */
986 dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( &dref->list, description->data_reference_index );
987 chunk.file = (!dref_entry || !dref_entry->ref_file) ? NULL : dref_entry->ref_file;
989 sample_number_in_chunk = samples_per_packet;
990 if( (err = isom_add_portable_chunk_entry( timeline, &chunk )) < 0 )
991 goto fail;
993 else
995 data_offset += info.length;
996 sample_number_in_chunk += samples_per_packet;
998 /* OK. Let's add its info. */
999 if( is_lpcm_audio )
1001 if( sample_number == samples_per_packet )
1002 isom_update_bunch( &bunch, &info );
1003 else if( isom_compare_lpcm_sample_info( &bunch, &info ) )
1005 if( (err = isom_add_lpcm_bunch_entry( timeline, &bunch )) < 0 )
1006 goto fail;
1007 isom_update_bunch( &bunch, &info );
1009 else
1010 ++ bunch.sample_count;
1012 else if( (err = isom_add_sample_info_entry( timeline, &info )) < 0 )
1013 goto fail;
1014 if( timeline->info_list->entry_count && timeline->bunch_list->entry_count )
1016 lsmash_log( timeline, LSMASH_LOG_ERROR, "LPCM + non-LPCM track is not supported.\n" );
1017 err = LSMASH_ERR_PATCH_WELCOME;
1018 goto fail;
1020 sample_number += samples_per_packet;
1021 packet_number += 1;
1023 isom_portable_chunk_t *last_chunk = lsmash_get_entry_data( timeline->chunk_list, timeline->chunk_list->entry_count );
1024 if( last_chunk )
1026 if( offset_from_chunk )
1027 last_chunk->length = offset_from_chunk;
1028 else
1030 /* Remove the last invalid chunk. */
1031 lsmash_remove_entry( timeline->chunk_list, timeline->chunk_list->entry_count, NULL );
1032 --chunk_number;
1035 uint32_t sample_count = packet_number - 1;
1036 if( movie_fragments_present )
1038 isom_tfra_t *tfra = isom_get_tfra( file->mfra, track_ID );
1039 lsmash_entry_t *tfra_entry = tfra && tfra->list ? tfra->list->head : NULL;
1040 isom_tfra_location_time_entry_t *rap = tfra_entry ? (isom_tfra_location_time_entry_t *)tfra_entry->data : NULL;
1041 chunk.data_offset = 0;
1042 chunk.length = 0;
1043 /* Movie fragments */
1044 for( lsmash_entry_t *moof_entry = file->moof_list.head; moof_entry; moof_entry = moof_entry->next )
1046 isom_moof_t *moof = (isom_moof_t *)moof_entry->data;
1047 if( !moof )
1048 goto fail;
1049 uint64_t last_sample_end_pos = 0;
1050 /* Track fragments */
1051 uint32_t traf_number = 1;
1052 for( lsmash_entry_t *traf_entry = moof->traf_list.head; traf_entry; traf_entry = traf_entry->next )
1054 isom_traf_t *traf = (isom_traf_t *)traf_entry->data;
1055 if( !traf )
1056 goto fail;
1057 isom_tfhd_t *tfhd = traf->tfhd;
1058 if( !tfhd )
1059 goto fail;
1060 isom_trex_t *trex = isom_get_trex( file->moov->mvex, tfhd->track_ID );
1061 if( !trex )
1062 goto fail;
1063 /* Ignore ISOM_TF_FLAGS_DURATION_IS_EMPTY flag even if set. */
1064 if( !traf->trun_list.head )
1066 ++traf_number;
1067 continue;
1069 /* Get base_data_offset. */
1070 uint64_t base_data_offset;
1071 if( tfhd->flags & ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT )
1072 base_data_offset = tfhd->base_data_offset;
1073 else if( (tfhd->flags & ISOM_TF_FLAGS_DEFAULT_BASE_IS_MOOF) || traf_entry == moof->traf_list.head )
1074 base_data_offset = moof->pos;
1075 else
1076 base_data_offset = last_sample_end_pos;
1077 /* sample grouping */
1078 isom_sgpd_t *sgpd_frag_rap;
1079 isom_sgpd_t *sgpd_frag_roll;
1080 sgpd_frag_rap = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_RAP );
1081 sbgp_rap = isom_get_fragment_sample_to_group ( traf, ISOM_GROUP_TYPE_RAP );
1082 sbgp_rap_entry = sbgp_rap && sbgp_rap->list ? sbgp_rap->list->head : NULL;
1083 sgpd_frag_roll = isom_get_roll_recovery_sample_group_description( &traf->sgpd_list );
1084 sbgp_roll = isom_get_roll_recovery_sample_to_group ( &traf->sbgp_list );
1085 sbgp_roll_entry = sbgp_roll && sbgp_roll->list ? sbgp_roll->list->head : NULL;
1086 int need_data_offset_only = (tfhd->track_ID != track_ID);
1087 /* Track runs */
1088 uint32_t trun_number = 1;
1089 for( lsmash_entry_t *trun_entry = traf->trun_list.head; trun_entry; trun_entry = trun_entry->next )
1091 isom_trun_t *trun = (isom_trun_t *)trun_entry->data;
1092 if( !trun )
1093 goto fail;
1094 if( trun->sample_count == 0 )
1096 ++trun_number;
1097 continue;
1099 /* Get data_offset. */
1100 if( trun->flags & ISOM_TR_FLAGS_DATA_OFFSET_PRESENT )
1101 data_offset = trun->data_offset + base_data_offset;
1102 else if( trun_entry == traf->trun_list.head )
1103 data_offset = base_data_offset;
1104 else
1105 data_offset = last_sample_end_pos;
1106 /* */
1107 uint32_t sample_description_index = 0;
1108 isom_sdtp_entry_t *sdtp_data = NULL;
1109 if( !need_data_offset_only )
1111 /* Get sample_description_index of this track fragment. */
1112 if( tfhd->flags & ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT )
1113 sample_description_index = tfhd->sample_description_index;
1114 else
1115 sample_description_index = trex->default_sample_description_index;
1116 description = (isom_sample_entry_t *)lsmash_get_entry_data( &stsd->list, sample_description_index );
1117 is_lpcm_audio = isom_is_lpcm_audio( description );
1118 /* Reference media data. */
1119 dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( &dref->list, description->data_reference_index );
1120 lsmash_file_t *ref_file = (!dref_entry || !dref_entry->ref_file) ? NULL : dref_entry->ref_file;
1121 /* Each track run can be considered as a chunk.
1122 * Here, we consider physically consecutive track runs as one chunk. */
1123 if( chunk.data_offset + chunk.length != data_offset || chunk.file != ref_file )
1125 chunk.data_offset = data_offset;
1126 chunk.length = 0;
1127 chunk.number = ++chunk_number;
1128 chunk.file = ref_file;
1129 if( (err = isom_add_portable_chunk_entry( timeline, &chunk )) < 0 )
1130 goto fail;
1132 /* Get dependency info for this track fragment. */
1133 sdtp_entry = traf->sdtp && traf->sdtp->list ? traf->sdtp->list->head : NULL;
1134 sdtp_data = sdtp_entry && sdtp_entry->data ? (isom_sdtp_entry_t *)sdtp_entry->data : NULL;
1136 /* Get info of each sample. */
1137 lsmash_entry_t *row_entry = trun->optional && trun->optional->head ? trun->optional->head : NULL;
1138 sample_number = 1;
1139 while( sample_number <= trun->sample_count )
1141 isom_sample_info_t info = { 0 };
1142 isom_trun_optional_row_t *row = row_entry && row_entry->data ? (isom_trun_optional_row_t *)row_entry->data : NULL;
1143 /* Get sample_size */
1144 if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT) )
1145 info.length = row->sample_size;
1146 else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT )
1147 info.length = tfhd->default_sample_size;
1148 else
1149 info.length = trex->default_sample_size;
1150 if( !need_data_offset_only )
1152 info.pos = data_offset;
1153 info.index = sample_description_index;
1154 info.chunk = (isom_portable_chunk_t *)timeline->chunk_list->tail->data;
1155 info.chunk->length += info.length;
1156 /* Get sample_duration. */
1157 if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT) )
1158 info.duration = row->sample_duration;
1159 else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT )
1160 info.duration = tfhd->default_sample_duration;
1161 else
1162 info.duration = trex->default_sample_duration;
1163 /* Get composition time offset. */
1164 if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT) )
1166 info.offset = row->sample_composition_time_offset;
1167 /* Check composition to decode timeline shift. */
1168 if( file->max_isom_version >= 6 && trun->version != 0 )
1170 uint64_t cts = dts + (int32_t)info.offset;
1171 if( (cts + timeline->ctd_shift) < dts )
1172 timeline->ctd_shift = dts - cts;
1175 else
1176 info.offset = 0;
1177 dts += info.duration;
1178 /* Update media duration and maximun sample size. */
1179 timeline->media_duration += info.duration;
1180 timeline->max_sample_size = LSMASH_MAX( timeline->max_sample_size, info.length );
1181 if( !is_lpcm_audio )
1183 /* Get sample_flags. */
1184 isom_sample_flags_t sample_flags;
1185 if( sample_number == 1 && (trun->flags & ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT) )
1186 sample_flags = trun->first_sample_flags;
1187 else if( row && (trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT) )
1188 sample_flags = row->sample_flags;
1189 else if( tfhd->flags & ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT )
1190 sample_flags = tfhd->default_sample_flags;
1191 else
1192 sample_flags = trex->default_sample_flags;
1193 if( sdtp_data )
1195 /* Independent and Disposable Samples Box overrides the information from sample_flags.
1196 * There is no description in the specification about this, but the intention should be such a thing.
1197 * The ground is that sample_flags is placed in media layer
1198 * while Independent and Disposable Samples Box is placed in track or presentation layer. */
1199 info.prop.leading = sdtp_data->is_leading;
1200 info.prop.independent = sdtp_data->sample_depends_on;
1201 info.prop.disposable = sdtp_data->sample_is_depended_on;
1202 info.prop.redundant = sdtp_data->sample_has_redundancy;
1203 if( sdtp_entry )
1204 sdtp_entry = sdtp_entry->next;
1205 sdtp_data = sdtp_entry ? (isom_sdtp_entry_t *)sdtp_entry->data : NULL;
1207 else
1209 info.prop.leading = sample_flags.is_leading;
1210 info.prop.independent = sample_flags.sample_depends_on;
1211 info.prop.disposable = sample_flags.sample_is_depended_on;
1212 info.prop.redundant = sample_flags.sample_has_redundancy;
1214 /* Check this sample is a sync sample or not.
1215 * Note: all sync sample shall be independent. */
1216 if( !sample_flags.sample_is_non_sync_sample
1217 && info.prop.independent != ISOM_SAMPLE_IS_NOT_INDEPENDENT )
1219 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1220 distance = 0;
1222 /* Get roll recovery grouping info. */
1223 uint32_t roll_id = sample_count + sample_number;
1224 if( sbgp_roll_entry
1225 && isom_get_roll_recovery_grouping_info( timeline,
1226 &sbgp_roll_entry, sgpd_roll, sgpd_frag_roll,
1227 &sample_number_in_sbgp_roll_entry,
1228 &info, roll_id ) < 0 )
1229 goto fail;
1230 info.prop.post_roll.identifier = roll_id;
1231 /* Get random access point grouping info. */
1232 if( sbgp_rap_entry
1233 && isom_get_random_access_point_grouping_info( timeline,
1234 &sbgp_rap_entry, sgpd_rap, sgpd_frag_rap,
1235 &sample_number_in_sbgp_rap_entry,
1236 &info, &distance ) < 0 )
1237 goto fail;
1238 /* Get the location of the sync sample from 'tfra' if it is not set up yet.
1239 * Note: there is no guarantee that its entries are placed in a specific order. */
1240 if( tfra )
1242 if( tfra->number_of_entry == 0
1243 && info.prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1244 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1245 if( rap
1246 && rap->moof_offset == moof->pos
1247 && rap->traf_number == traf_number
1248 && rap->trun_number == trun_number
1249 && rap->sample_number == sample_number )
1251 if( info.prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1252 info.prop.ra_flags |= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1253 if( tfra_entry )
1254 tfra_entry = tfra_entry->next;
1255 rap = tfra_entry ? (isom_tfra_location_time_entry_t *)tfra_entry->data : NULL;
1258 /* Set up distance from the previous random access point. */
1259 if( distance != NO_RANDOM_ACCESS_POINT )
1261 if( info.prop.pre_roll.distance == 0 )
1262 info.prop.pre_roll.distance = distance;
1263 ++distance;
1265 /* OK. Let's add its info. */
1266 if( (err = isom_add_sample_info_entry( timeline, &info )) < 0 )
1267 goto fail;
1269 else
1271 /* All LPCMFrame is a sync sample. */
1272 info.prop.ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1273 /* OK. Let's add its info. */
1274 if( sample_count == 0 && sample_number == 1 )
1275 isom_update_bunch( &bunch, &info );
1276 else if( isom_compare_lpcm_sample_info( &bunch, &info ) )
1278 if( (err = isom_add_lpcm_bunch_entry( timeline, &bunch )) < 0 )
1279 goto fail;
1280 isom_update_bunch( &bunch, &info );
1282 else
1283 ++ bunch.sample_count;
1285 if( timeline-> info_list->entry_count
1286 && timeline->bunch_list->entry_count )
1288 lsmash_log( timeline, LSMASH_LOG_ERROR, "LPCM + non-LPCM track is not supported.\n" );
1289 err = LSMASH_ERR_PATCH_WELCOME;
1290 goto fail;
1293 data_offset += info.length;
1294 last_sample_end_pos = data_offset;
1295 if( row_entry )
1296 row_entry = row_entry->next;
1297 ++sample_number;
1299 if( !need_data_offset_only )
1300 sample_count += sample_number - 1;
1301 ++trun_number;
1302 } /* Track runs */
1303 ++traf_number;
1304 } /* Track fragments */
1305 } /* Movie fragments */
1307 else if( timeline->chunk_list->entry_count == 0 )
1308 goto fail; /* No samples in this track. */
1309 if( bunch.sample_count && (err = isom_add_lpcm_bunch_entry( timeline, &bunch )) < 0 )
1310 goto fail;
1311 if( (err = lsmash_add_entry( file->timeline, timeline )) < 0 )
1312 goto fail;
1313 /* Finish timeline construction. */
1314 timeline->sample_count = sample_count;
1315 if( timeline->info_list->entry_count )
1317 timeline->get_dts = isom_get_dts_from_info_list;
1318 timeline->get_cts = isom_get_cts_from_info_list;
1319 timeline->get_sample_duration = isom_get_sample_duration_from_info_list;
1320 timeline->check_sample_existence = isom_check_sample_existence_in_info_list;
1321 timeline->get_sample = isom_get_sample_from_media_timeline;
1322 timeline->get_sample_info = isom_get_sample_info_from_media_timeline;
1323 timeline->get_sample_property = isom_get_sample_property_from_media_timeline;
1325 else
1327 timeline->get_dts = isom_get_dts_from_bunch_list;
1328 timeline->get_cts = isom_get_cts_from_bunch_list;
1329 timeline->get_sample_duration = isom_get_sample_duration_from_bunch_list;
1330 timeline->check_sample_existence = isom_check_sample_existence_in_bunch_list;
1331 timeline->get_sample = isom_get_lpcm_sample_from_media_timeline;
1332 timeline->get_sample_info = isom_get_lpcm_sample_info_from_media_timeline;
1333 timeline->get_sample_property = isom_get_lpcm_sample_property_from_media_timeline;
1335 return 0;
1336 fail:
1337 isom_destruct_timeline_direct( timeline );
1338 return err;
1341 int lsmash_get_dts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *dts )
1343 if( !sample_number || !dts )
1344 return LSMASH_ERR_FUNCTION_PARAM;
1345 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1346 if( !timeline || sample_number > timeline->sample_count )
1347 return LSMASH_ERR_NAMELESS;
1348 return timeline->get_dts( timeline, sample_number, dts );
1351 int lsmash_get_cts_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint64_t *cts )
1353 if( !sample_number || !cts )
1354 return LSMASH_ERR_FUNCTION_PARAM;
1355 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1356 if( !timeline || sample_number > timeline->sample_count )
1357 return LSMASH_ERR_NAMELESS;
1358 return timeline->get_cts( timeline, sample_number, cts );
1361 lsmash_sample_t *lsmash_get_sample_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number )
1363 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1364 return timeline ? timeline->get_sample( timeline, sample_number ) : NULL;
1367 int lsmash_get_sample_info_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, lsmash_sample_t *sample )
1369 if( !sample )
1370 return LSMASH_ERR_FUNCTION_PARAM;
1371 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1372 return timeline ? timeline->get_sample_info( timeline, sample_number, sample ) : -1;
1375 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 )
1377 if( !prop )
1378 return LSMASH_ERR_FUNCTION_PARAM;
1379 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1380 return timeline ? timeline->get_sample_property( timeline, sample_number, prop ) : -1;
1383 int lsmash_get_composition_to_decode_shift_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *ctd_shift )
1385 if( !ctd_shift )
1386 return LSMASH_ERR_FUNCTION_PARAM;
1387 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1388 if( !timeline )
1389 return LSMASH_ERR_NAMELESS;
1390 *ctd_shift = timeline->ctd_shift;
1391 return 0;
1394 static int isom_get_closest_past_random_accessible_point_from_media_timeline( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number )
1396 lsmash_entry_t *entry = lsmash_get_entry( timeline->info_list, sample_number-- );
1397 if( !entry
1398 || !entry->data )
1399 return LSMASH_ERR_NAMELESS;
1400 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1401 while( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1403 entry = entry->prev;
1404 if( !entry
1405 || !entry->data )
1406 return LSMASH_ERR_NAMELESS;
1407 info = (isom_sample_info_t *)entry->data;
1408 --sample_number;
1410 *rap_number = sample_number + 1;
1411 return 0;
1414 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 )
1416 lsmash_entry_t *entry = lsmash_get_entry( timeline->info_list, sample_number++ );
1417 if( !entry
1418 || !entry->data )
1419 return LSMASH_ERR_NAMELESS;
1420 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1421 while( info->prop.ra_flags == ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE )
1423 entry = entry->next;
1424 if( !entry
1425 || !entry->data )
1426 return LSMASH_ERR_NAMELESS;
1427 info = (isom_sample_info_t *)entry->data;
1428 ++sample_number;
1430 *rap_number = sample_number - 1;
1431 return 0;
1434 static int isom_get_closest_random_accessible_point_from_media_timeline_internal( isom_timeline_t *timeline, uint32_t sample_number, uint32_t *rap_number )
1436 if( !timeline )
1437 return LSMASH_ERR_NAMELESS;
1438 int ret;
1439 if( (ret = isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, sample_number, rap_number )) < 0
1440 && (ret = isom_get_closest_future_random_accessible_point_from_media_timeline( timeline, sample_number + 1, rap_number )) < 0 )
1441 return ret;
1442 return 0;
1445 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 )
1447 if( sample_number == 0 || !rap_number )
1448 return LSMASH_ERR_FUNCTION_PARAM;
1449 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1450 if( timeline->info_list->entry_count == 0 )
1452 *rap_number = sample_number; /* All LPCM is sync sample. */
1453 return 0;
1455 return isom_get_closest_random_accessible_point_from_media_timeline_internal( timeline, sample_number, rap_number );
1458 int lsmash_get_closest_random_accessible_point_detail_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number,
1459 uint32_t *rap_number, lsmash_random_access_flag *ra_flags, uint32_t *leading, uint32_t *distance )
1461 if( sample_number == 0 )
1462 return LSMASH_ERR_FUNCTION_PARAM;
1463 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1464 if( timeline->info_list->entry_count == 0 )
1466 /* All LPCM is sync sample. */
1467 *rap_number = sample_number;
1468 if( ra_flags )
1469 *ra_flags = ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC;
1470 if( leading )
1471 *leading = 0;
1472 if( distance )
1473 *distance = 0;
1474 return 0;
1476 int ret = isom_get_closest_random_accessible_point_from_media_timeline_internal( timeline, sample_number, rap_number );
1477 if( ret < 0 )
1478 return ret;
1479 isom_sample_info_t *info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, *rap_number );
1480 if( !info )
1481 return LSMASH_ERR_NAMELESS;
1482 if( ra_flags )
1483 *ra_flags = info->prop.ra_flags;
1484 if( leading )
1485 *leading = 0;
1486 if( distance )
1487 *distance = 0;
1488 if( sample_number < *rap_number )
1489 /* Impossible to desire to decode the sample of given number correctly. */
1490 return 0;
1491 else if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) )
1493 if( leading )
1495 /* Count leading samples. */
1496 uint32_t current_sample_number = *rap_number + 1;
1497 uint64_t dts;
1498 if( (ret = isom_get_dts_from_info_list( timeline, *rap_number, &dts )) < 0 )
1499 return ret;
1500 uint64_t rap_cts = timeline->ctd_shift ? (dts + (int32_t)info->offset + timeline->ctd_shift) : (dts + info->offset);
1503 dts += info->duration;
1504 if( rap_cts <= dts )
1505 break; /* leading samples of this random accessible point must not be present more. */
1506 info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, current_sample_number++ );
1507 if( !info )
1508 break;
1509 uint64_t cts = timeline->ctd_shift ? (dts + (int32_t)info->offset + timeline->ctd_shift) : (dts + info->offset);
1510 if( rap_cts > cts )
1511 ++ *leading;
1512 } while( 1 );
1514 if( !distance || sample_number == *rap_number )
1515 return 0;
1516 /* Measure distance from the first closest non-recovery random accessible point to the second. */
1517 uint32_t prev_rap_number = *rap_number;
1520 if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) < 0 )
1521 /* The previous random accessible point is not present. */
1522 return 0;
1523 info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, prev_rap_number );
1524 if( !info )
1525 return LSMASH_ERR_NAMELESS;
1526 if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) )
1528 /* Decode shall already complete at the first closest non-recovery random accessible point if starting to decode from the second. */
1529 *distance = *rap_number - prev_rap_number;
1530 return 0;
1532 } while( 1 );
1534 if( !distance )
1535 return 0;
1536 /* Calculate roll-distance. */
1537 if( info->prop.pre_roll.distance )
1539 /* Pre-roll recovery */
1540 uint32_t prev_rap_number = *rap_number;
1543 if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) < 0
1544 && *rap_number < info->prop.pre_roll.distance )
1546 /* The previous random accessible point is not present.
1547 * And sample of given number might be not able to decoded correctly. */
1548 *distance = 0;
1549 return 0;
1551 if( prev_rap_number + info->prop.pre_roll.distance <= *rap_number )
1554 * |<---- pre-roll distance ---->|
1555 * |<--------- distance -------->|
1556 * media +++++++++++++++++++++++++ *** +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1557 * ^ ^ ^ ^
1558 * random accessible point starting point random accessible point given sample
1559 * (complete)
1561 *distance = info->prop.pre_roll.distance;
1562 return 0;
1564 else if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) )
1567 * |<------------ pre-roll distance ------------------>|
1568 * |<------ distance ------->|
1569 * media ++++++++++++++++ *** ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1570 * ^ ^ ^ ^
1571 * random accessible point random accessible point given sample
1572 * (starting point) (complete)
1574 *distance = *rap_number - prev_rap_number;
1575 return 0;
1577 } while( 1 );
1579 /* Post-roll recovery */
1580 if( sample_number >= info->prop.post_roll.complete )
1582 * |<----- post-roll distance ----->|
1583 * (distance = 0)
1584 * media +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1585 * ^ ^ ^
1586 * random accessible point complete given sample
1587 * (starting point)
1589 return 0;
1590 uint32_t prev_rap_number = *rap_number;
1593 if( isom_get_closest_past_random_accessible_point_from_media_timeline( timeline, prev_rap_number - 1, &prev_rap_number ) < 0 )
1594 /* The previous random accessible point is not present. */
1595 return 0;
1596 info = (isom_sample_info_t *)lsmash_get_entry_data( timeline->info_list, prev_rap_number );
1597 if( !info )
1598 return LSMASH_ERR_NAMELESS;
1599 if( !(info->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_GDR) || sample_number >= info->prop.post_roll.complete )
1601 *distance = *rap_number - prev_rap_number;
1602 return 0;
1604 } while( 1 );
1607 int lsmash_check_sample_existence_in_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number )
1609 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1610 return timeline ? timeline->check_sample_existence( timeline, sample_number ) : 0;
1613 int lsmash_get_last_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t *last_sample_delta )
1615 if( !last_sample_delta )
1616 return LSMASH_ERR_FUNCTION_PARAM;
1617 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1618 return timeline ? timeline->get_sample_duration( timeline, timeline->sample_count, last_sample_delta ) : -1;
1621 int lsmash_get_sample_delta_from_media_timeline( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_number, uint32_t *sample_delta )
1623 if( !sample_delta )
1624 return LSMASH_ERR_FUNCTION_PARAM;
1625 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1626 return timeline ? timeline->get_sample_duration( timeline, sample_number, sample_delta ) : -1;
1629 uint32_t lsmash_get_sample_count_in_media_timeline( lsmash_root_t *root, uint32_t track_ID )
1631 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1632 if( !timeline )
1633 return 0;
1634 return timeline->sample_count;
1637 uint32_t lsmash_get_max_sample_size_in_media_timeline( lsmash_root_t *root, uint32_t track_ID )
1639 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1640 if( !timeline )
1641 return 0;
1642 return timeline->max_sample_size;
1645 uint64_t lsmash_get_media_duration_from_media_timeline( lsmash_root_t *root, uint32_t track_ID )
1647 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1648 if( !timeline )
1649 return 0;
1650 return timeline->media_duration;
1653 int lsmash_copy_timeline_map( lsmash_root_t *dst, uint32_t dst_track_ID, lsmash_root_t *src, uint32_t src_track_ID )
1655 if( isom_check_initializer_present( dst ) < 0
1656 || isom_check_initializer_present( src ) < 0 )
1657 return LSMASH_ERR_FUNCTION_PARAM;
1658 lsmash_file_t *dst_file = dst->file->initializer;
1659 isom_trak_t *dst_trak = isom_get_trak( dst_file, dst_track_ID );
1660 if( !dst_file->moov
1661 || !dst_file->moov->mvhd
1662 || dst_file->moov->mvhd->timescale == 0
1663 || !dst_trak
1664 || !dst_trak->mdia
1665 || !dst_trak->mdia->mdhd
1666 || dst_trak->mdia->mdhd->timescale == 0
1667 || !dst_trak->mdia->minf
1668 || !dst_trak->mdia->minf->stbl )
1669 return LSMASH_ERR_NAMELESS;
1670 if( dst_trak->edts
1671 && dst_trak->edts->elst )
1672 lsmash_remove_entries( dst_trak->edts->elst->list, NULL );
1673 uint32_t src_movie_timescale;
1674 uint32_t src_media_timescale;
1675 uint64_t src_track_duration;
1676 uint64_t src_media_duration;
1677 int32_t src_ctd_shift; /* Add timeline shift difference between src and dst to each media_time.
1678 * Therefore, call this function as later as possible. */
1679 lsmash_entry_t *src_entry = NULL;
1680 lsmash_file_t *src_file = src->file->initializer;
1681 isom_trak_t *src_trak = isom_get_trak( src_file, src_track_ID );
1682 int src_fragmented = !!(src_file->flags & LSMASH_FILE_MODE_FRAGMENTED);
1683 if( !src_trak
1684 || !src_trak->edts
1685 || !src_trak->edts->elst
1686 || !src_trak->edts->elst->list
1687 || src_fragmented )
1689 /* Get from constructed timeline instead of boxes. */
1690 isom_timeline_t *src_timeline = isom_get_timeline( src, src_track_ID );
1691 if( src_timeline
1692 && src_timeline->movie_timescale
1693 && src_timeline->media_timescale )
1695 src_entry = src_timeline->edit_list->head;
1696 if( !src_entry )
1697 return 0;
1698 src_movie_timescale = src_timeline->movie_timescale;
1699 src_media_timescale = src_timeline->media_timescale;
1700 src_track_duration = src_timeline->track_duration;
1701 src_media_duration = src_timeline->media_duration;
1702 src_ctd_shift = src_timeline->ctd_shift;
1704 else if( !src_fragmented )
1705 return LSMASH_ERR_NAMELESS;
1707 if( !src_entry )
1709 if( !src_file->moov
1710 || !src_file->moov->mvhd
1711 || src_file->moov->mvhd->timescale == 0
1712 || !src_trak->tkhd
1713 || !src_trak->mdia
1714 || !src_trak->mdia->mdhd
1715 || src_trak->mdia->mdhd->timescale == 0
1716 || !src_trak->mdia->minf
1717 || !src_trak->mdia->minf->stbl )
1718 return LSMASH_ERR_NAMELESS;
1719 src_entry = src_trak->edts->elst->list->head;
1720 if( !src_entry )
1721 return 0;
1722 src_movie_timescale = src_file->moov->mvhd->timescale;
1723 src_media_timescale = src_trak->mdia->mdhd->timescale;
1724 src_track_duration = src_trak->tkhd->duration;
1725 src_media_duration = src_trak->mdia->mdhd->duration;
1726 src_ctd_shift = src_trak->mdia->minf->stbl->cslg ? src_trak->mdia->minf->stbl->cslg->compositionToDTSShift : 0;
1728 /* Generate the edit list if absent in the destination. */
1729 if( (!dst_trak->edts && !isom_add_edts( dst_trak ))
1730 || (!dst_trak->edts->elst && !isom_add_elst( dst_trak->edts )) )
1731 return LSMASH_ERR_NAMELESS;
1732 uint32_t dst_movie_timescale = dst_file->moov->mvhd->timescale;
1733 uint32_t dst_media_timescale = dst_trak->mdia->mdhd->timescale;
1734 int32_t dst_ctd_shift = dst_trak->mdia->minf->stbl->cslg ? dst_trak->mdia->minf->stbl->cslg->compositionToDTSShift : 0;
1735 int32_t media_time_shift = src_ctd_shift - dst_ctd_shift;
1736 lsmash_entry_list_t *dst_list = dst_trak->edts->elst->list;
1737 while( src_entry )
1739 isom_elst_entry_t *src_data = (isom_elst_entry_t *)src_entry->data;
1740 if( !src_data )
1741 return LSMASH_ERR_NAMELESS;
1742 isom_elst_entry_t *dst_data = (isom_elst_entry_t *)lsmash_malloc( sizeof(isom_elst_entry_t) );
1743 if( !dst_data )
1744 return LSMASH_ERR_MEMORY_ALLOC;
1745 uint64_t segment_duration;
1746 if( src_data->segment_duration == 0 && !dst_file->fragment )
1747 /* The implicit duration edit is not suitable for non-fragmented movie file.
1748 * Set an appropriate duration from the source track. */
1749 segment_duration = src_fragmented
1750 ? (uint64_t)(src_media_duration * ((double)src_movie_timescale / src_media_timescale))
1751 : src_track_duration;
1752 else
1753 segment_duration = src_data->segment_duration;
1754 dst_data->segment_duration = segment_duration * ((double)dst_movie_timescale / src_movie_timescale) + 0.5;
1755 dst_data->media_rate = src_data->media_rate;
1756 if( src_data->media_time != ISOM_EDIT_MODE_EMPTY )
1757 dst_data->media_time = (src_data->media_time + media_time_shift) * ((double)dst_media_timescale / src_media_timescale) + 0.5;
1758 else
1759 dst_data->media_time = ISOM_EDIT_MODE_EMPTY;
1760 if( lsmash_add_entry( dst_list, dst_data ) < 0 )
1762 lsmash_free( dst_data );
1763 return LSMASH_ERR_MEMORY_ALLOC;
1765 src_entry = src_entry->next;
1767 return 0;
1770 int lsmash_set_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list )
1772 if( !root || !root->file || !ts_list )
1773 return -1;
1774 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1775 if( !timeline )
1776 return LSMASH_ERR_NAMELESS;
1777 if( timeline->info_list->entry_count == 0 )
1779 lsmash_log( timeline, LSMASH_LOG_ERROR, "Changing timestamps of LPCM track is not supported.\n" );
1780 return LSMASH_ERR_PATCH_WELCOME;
1782 if( ts_list->sample_count != timeline->info_list->entry_count )
1783 return LSMASH_ERR_INVALID_DATA; /* Number of samples must be same. */
1784 lsmash_media_ts_t *ts = ts_list->timestamp;
1785 if( ts[0].dts )
1786 return LSMASH_ERR_INVALID_DATA; /* DTS must start from value zero. */
1787 /* Update DTSs. */
1788 uint32_t sample_count = ts_list->sample_count;
1789 uint32_t i;
1790 if( timeline->info_list->entry_count > 1 )
1792 i = 1;
1793 lsmash_entry_t *entry = timeline->info_list->head;
1794 isom_sample_info_t *info;
1795 while( i < sample_count )
1797 info = (isom_sample_info_t *)entry->data;
1798 if( !info || (ts[i].dts < ts[i - 1].dts) )
1799 return LSMASH_ERR_INVALID_DATA;
1800 info->duration = ts[i].dts - ts[i - 1].dts;
1801 entry = entry->next;
1802 ++i;
1804 if( i > 1 )
1806 if( !entry
1807 || !entry->data )
1808 return LSMASH_ERR_INVALID_DATA;
1809 /* Copy the previous duration. */
1810 ((isom_sample_info_t *)entry->data)->duration = info->duration;
1812 else
1813 return LSMASH_ERR_INVALID_DATA; /* Irregular case: sample_count this timeline has is incorrect. */
1815 else /* still image */
1816 ((isom_sample_info_t *)timeline->info_list->head->data)->duration = UINT32_MAX;
1817 /* Update CTSs.
1818 * ToDo: hint track must not have any sample_offset. */
1819 i = 0;
1820 timeline->ctd_shift = 0;
1821 for( lsmash_entry_t *entry = timeline->info_list->head; entry; entry = entry->next )
1823 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1824 if( (ts[i].cts + timeline->ctd_shift) < ts[i].dts )
1825 timeline->ctd_shift = ts[i].dts - ts[i].cts;
1826 info->offset = ts[i].cts - ts[i].dts;
1827 ++i;
1829 if( timeline->ctd_shift && (!root->file->qt_compatible || root->file->max_isom_version < 4) )
1830 return LSMASH_ERR_INVALID_DATA; /* Don't allow composition to decode timeline shift. */
1831 return 0;
1834 int lsmash_get_media_timestamps( lsmash_root_t *root, uint32_t track_ID, lsmash_media_ts_list_t *ts_list )
1836 if( !ts_list )
1837 return LSMASH_ERR_FUNCTION_PARAM;
1838 isom_timeline_t *timeline = isom_get_timeline( root, track_ID );
1839 if( !timeline )
1840 return LSMASH_ERR_NAMELESS;
1841 uint32_t sample_count = timeline->info_list->entry_count;
1842 if( !sample_count )
1844 ts_list->sample_count = 0;
1845 ts_list->timestamp = NULL;
1846 return 0;
1848 lsmash_media_ts_t *ts = lsmash_malloc( sample_count * sizeof(lsmash_media_ts_t) );
1849 if( !ts )
1850 return LSMASH_ERR_MEMORY_ALLOC;
1851 uint64_t dts = 0;
1852 uint32_t i = 0;
1853 if( timeline->info_list->entry_count )
1854 for( lsmash_entry_t *entry = timeline->info_list->head; entry; entry = entry->next )
1856 isom_sample_info_t *info = (isom_sample_info_t *)entry->data;
1857 if( !info )
1859 lsmash_free( ts );
1860 return LSMASH_ERR_NAMELESS;
1862 ts[i].dts = dts;
1863 ts[i].cts = timeline->ctd_shift ? (dts + (int32_t)info->offset) : (dts + info->offset);
1864 dts += info->duration;
1865 ++i;
1867 else
1868 for( lsmash_entry_t *entry = timeline->bunch_list->head; entry; entry = entry->next )
1870 isom_lpcm_bunch_t *bunch = (isom_lpcm_bunch_t *)entry->data;
1871 if( !bunch )
1873 lsmash_free( ts );
1874 return LSMASH_ERR_NAMELESS;
1876 for( uint32_t j = 0; j < bunch->sample_count; j++ )
1878 ts[i].dts = dts;
1879 ts[i].cts = timeline->ctd_shift ? (dts + (int32_t)bunch->offset) : (dts + bunch->offset);
1880 dts += bunch->duration;
1881 ++i;
1884 ts_list->sample_count = sample_count;
1885 ts_list->timestamp = ts;
1886 return 0;
1889 void lsmash_delete_media_timestamps( lsmash_media_ts_list_t *ts_list )
1891 if( !ts_list )
1892 return;
1893 lsmash_freep( &ts_list->timestamp );
1894 ts_list->sample_count = 0;
1897 static int isom_compare_dts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b )
1899 int64_t diff = (int64_t)(a->dts - b->dts);
1900 return diff > 0 ? 1 : (diff == 0 ? 0 : -1);
1903 void lsmash_sort_timestamps_decoding_order( lsmash_media_ts_list_t *ts_list )
1905 if( !ts_list )
1906 return;
1907 qsort( ts_list->timestamp, ts_list->sample_count, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))isom_compare_dts );
1910 static int isom_compare_cts( const lsmash_media_ts_t *a, const lsmash_media_ts_t *b )
1912 int64_t diff = (int64_t)(a->cts - b->cts);
1913 return diff > 0 ? 1 : (diff == 0 ? 0 : -1);
1916 void lsmash_sort_timestamps_composition_order( lsmash_media_ts_list_t *ts_list )
1918 if( !ts_list )
1919 return;
1920 qsort( ts_list->timestamp, ts_list->sample_count, sizeof(lsmash_media_ts_t), (int(*)( const void *, const void * ))isom_compare_cts );
1923 int lsmash_get_max_sample_delay( lsmash_media_ts_list_t *ts_list, uint32_t *max_sample_delay )
1925 if( !ts_list || !max_sample_delay )
1926 return LSMASH_ERR_FUNCTION_PARAM;
1927 lsmash_media_ts_t *orig_ts = ts_list->timestamp;
1928 lsmash_media_ts_t *ts = lsmash_malloc( ts_list->sample_count * sizeof(lsmash_media_ts_t) );
1929 if( !ts )
1930 return LSMASH_ERR_MEMORY_ALLOC;
1931 ts_list->timestamp = ts;
1932 *max_sample_delay = 0;
1933 for( uint32_t i = 0; i < ts_list->sample_count; i++ )
1935 ts[i].cts = orig_ts[i].cts; /* for sorting */
1936 ts[i].dts = i;
1938 lsmash_sort_timestamps_composition_order( ts_list );
1939 for( uint32_t i = 0; i < ts_list->sample_count; i++ )
1940 if( i < ts[i].dts )
1942 uint32_t sample_delay = ts[i].dts - i;
1943 *max_sample_delay = LSMASH_MAX( *max_sample_delay, sample_delay );
1945 lsmash_free( ts );
1946 ts_list->timestamp = orig_ts;
1947 return 0;
1950 #endif /* LSMASH_DEMUXER_ENABLED */