1 /*****************************************************************************
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 #include "common/internal.h" /* must be placed first */
31 static isom_sidx_t
*isom_get_sidx( lsmash_file_t
*file
, uint32_t reference_ID
)
33 if( reference_ID
== 0 || !file
)
35 for( lsmash_entry_t
*entry
= file
->sidx_list
.head
; entry
; entry
= entry
->next
)
37 isom_sidx_t
*sidx
= (isom_sidx_t
*)entry
->data
;
40 if( sidx
->reference_ID
== reference_ID
)
46 static int isom_finish_fragment_movie( lsmash_file_t
*file
);
48 /* A movie fragment cannot switch a sample description to another.
49 * So you must call this function before switching sample descriptions. */
50 int lsmash_create_fragment_movie( lsmash_root_t
*root
)
52 if( isom_check_initializer_present( root
) < 0 )
54 lsmash_file_t
*file
= root
->file
;
59 /* Finish and write the current movie fragment before starting a new one. */
60 if( isom_finish_fragment_movie( file
) < 0 )
62 /* Add a new movie fragment if the current one is not present or not written. */
63 if( !file
->fragment
->movie
|| (file
->fragment
->movie
->manager
& LSMASH_WRITTEN_BOX
) )
65 /* We always hold only one movie fragment except for the initial movie (a pair of moov and mdat). */
66 if( file
->fragment
->movie
&& file
->moof_list
.entry_count
!= 1 )
68 isom_moof_t
*moof
= isom_add_moof( file
);
69 if( !isom_add_mfhd( moof
) )
71 file
->fragment
->movie
= moof
;
72 moof
->mfhd
->sequence_number
= ++ file
->fragment_count
;
73 if( file
->moof_list
.entry_count
== 1 )
75 /* Remove the previous movie fragment. */
76 if( file
->moof_list
.head
)
77 isom_remove_box_by_itself( file
->moof_list
.head
->data
);
82 static int isom_set_fragment_overall_duration( lsmash_file_t
*file
)
84 assert( file
== file
->initializer
);
85 /* Get the longest duration of the tracks. */
86 uint64_t longest_duration
= 0;
87 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
89 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
92 || !trak
->cache
->fragment
95 || !trak
->mdia
->mdhd
->timescale
)
100 || !trak
->edts
->elst
->list
)
102 duration
= trak
->cache
->fragment
->largest_cts
+ trak
->cache
->fragment
->last_duration
;
103 duration
= (uint64_t)(((double)duration
/ trak
->mdia
->mdhd
->timescale
) * file
->moov
->mvhd
->timescale
);
108 for( lsmash_entry_t
*elst_entry
= trak
->edts
->elst
->list
->head
; elst_entry
; elst_entry
= elst_entry
->next
)
110 isom_elst_entry_t
*data
= (isom_elst_entry_t
*)elst_entry
->data
;
113 if( data
->segment_duration
== ISOM_EDIT_DURATION_IMPLICIT
)
115 uint64_t segment_duration
= trak
->cache
->fragment
->largest_cts
+ trak
->cache
->fragment
->last_duration
;
116 duration
+= (uint64_t)(((double)segment_duration
/ trak
->mdia
->mdhd
->timescale
) * file
->moov
->mvhd
->timescale
);
119 duration
+= data
->segment_duration
;
122 longest_duration
= LSMASH_MAX( duration
, longest_duration
);
124 isom_mehd_t
*mehd
= file
->moov
->mvex
->mehd
;
125 mehd
->fragment_duration
= longest_duration
;
127 mehd
->manager
&= ~(LSMASH_PLACEHOLDER
| LSMASH_WRITTEN_BOX
); /* Update per media segment. */
128 isom_update_box_size( mehd
);
129 /* Write Movie Extends Header Box here. */
130 lsmash_bs_t
*bs
= file
->bs
;
131 uint64_t current_pos
= bs
->offset
;
132 lsmash_bs_write_seek( bs
, mehd
->pos
, SEEK_SET
);
133 int ret
= isom_write_box( bs
, (isom_box_t
*)mehd
);
134 lsmash_bs_write_seek( bs
, current_pos
, SEEK_SET
);
138 static int isom_write_fragment_random_access_info( lsmash_file_t
*file
)
140 assert( file
== file
->initializer
);
143 if( !file
->moov
->mvex
)
145 /* Reconstruct the Movie Fragment Random Access Box.
146 * All 'time' field in the Track Fragment Random Access Boxes shall reflect edit list. */
147 uint32_t movie_timescale
= lsmash_get_movie_timescale( file
->root
);
148 if( movie_timescale
== 0 )
149 return -1; /* Division by zero will occur. */
150 for( lsmash_entry_t
*trex_entry
= file
->moov
->mvex
->trex_list
.head
; trex_entry
; trex_entry
= trex_entry
->next
)
152 isom_trex_t
*trex
= (isom_trex_t
*)trex_entry
->data
;
155 /* Get the edit list of the track associated with the trex->track_ID.
156 * If failed or absent, implicit timeline mapping edit is used, and skip this operation for the track. */
157 isom_trak_t
*trak
= isom_get_trak( file
, trex
->track_ID
);
162 || !trak
->edts
->elst
->list
163 || !trak
->edts
->elst
->list
->head
164 || !trak
->edts
->elst
->list
->head
->data
)
166 isom_elst_t
*elst
= trak
->edts
->elst
;
167 /* Get the Track Fragment Random Access Boxes of the track associated with the trex->track_ID.
168 * If failed or absent, skip reconstructing the Track Fragment Random Access Box of the track. */
169 isom_tfra_t
*tfra
= isom_get_tfra( file
->mfra
, trex
->track_ID
);
172 /* Reconstruct the Track Fragment Random Access Box. */
173 lsmash_entry_t
*edit_entry
= elst
->list
->head
;
174 isom_elst_entry_t
*edit
= edit_entry
->data
;
175 uint64_t edit_offset
= 0; /* units in media timescale */
176 uint32_t media_timescale
= lsmash_get_media_timescale( file
->root
, trex
->track_ID
);
177 for( lsmash_entry_t
*rap_entry
= tfra
->list
->head
; rap_entry
; )
179 isom_tfra_location_time_entry_t
*rap
= (isom_tfra_location_time_entry_t
*)rap_entry
->data
;
182 /* Irregular case. Drop this entry. */
183 lsmash_entry_t
*next
= rap_entry
->next
;
184 lsmash_remove_entry_direct( tfra
->list
, rap_entry
, NULL
);
188 uint64_t composition_time
= rap
->time
;
189 /* Skip edits that doesn't need the current sync sample indicated in the Track Fragment Random Access Box. */
192 uint64_t segment_duration
= edit
->segment_duration
== ISOM_EDIT_DURATION_IMPLICIT
193 ? trak
->cache
->fragment
->largest_cts
+ trak
->cache
->fragment
->last_duration
194 : ((edit
->segment_duration
- 1) / movie_timescale
+ 1) * media_timescale
;
195 if( edit
->media_time
!= ISOM_EDIT_MODE_EMPTY
196 && composition_time
< edit
->media_time
+ segment_duration
)
197 break; /* This Timeline Mapping Edit might require the current sync sample.
198 * Note: this condition doesn't cover all cases.
199 * For instance, matching the both following conditions
200 * 1. A sync sample isn't in the presentation.
201 * 2. The other samples, which precede it in the composition timeline, is in the presentation. */
202 edit_offset
+= segment_duration
;
203 edit_entry
= edit_entry
->next
;
206 /* No more presentation. */
210 edit
= edit_entry
->data
;
214 /* No more presentation.
215 * Drop the rest of sync samples since they are generally absent in the whole presentation.
216 * Though the exceptions are sync samples with earlier composition time, we ignore them. (SAP type 2: TEPT = TDEC = TSAP < TPTF)
217 * To support this exception, we need sorting entries of the list by composition times. */
220 lsmash_entry_t
*next
= rap_entry
->next
;
221 lsmash_remove_entry_direct( tfra
->list
, rap_entry
, NULL
);
226 /* If the sync sample isn't in the presentation,
227 * we pick the earliest presentation time of the current edit as its presentation time. */
228 rap
->time
= edit_offset
;
229 if( composition_time
>= edit
->media_time
)
230 rap
->time
+= composition_time
- edit
->media_time
;
231 rap_entry
= rap_entry
->next
;
233 tfra
->number_of_entry
= tfra
->list
->entry_count
;
235 /* Decide the size of the Movie Fragment Random Access Box. */
236 if( isom_update_box_size( file
->mfra
) == 0 )
238 /* Write the Movie Fragment Random Access Box. */
239 return isom_write_box( file
->bs
, (isom_box_t
*)file
->mfra
);
242 static int isom_update_indexed_material_offset
245 isom_sidx_t
*last_sidx
248 /* Update the size of each Segment Index Box. */
249 for( lsmash_entry_t
*entry
= file
->sidx_list
.head
; entry
; entry
= entry
->next
)
251 isom_sidx_t
*sidx
= (isom_sidx_t
*)entry
->data
;
254 if( isom_update_box_size( sidx
) == 0 )
257 /* first_offset: the sum of the size of subsequent Segment Index Boxes
258 * Be careful about changing the size of them. */
259 last_sidx
->first_offset
= 0;
260 for( lsmash_entry_t
*a_entry
= file
->sidx_list
.head
; a_entry
&& a_entry
->data
!= last_sidx
; a_entry
= a_entry
->next
)
262 isom_sidx_t
*a
= (isom_sidx_t
*)a_entry
->data
;
264 for( lsmash_entry_t
*b_entry
= a_entry
->next
; b_entry
; b_entry
= b_entry
->next
)
266 isom_sidx_t
*b
= (isom_sidx_t
*)b_entry
->data
;
267 a
->first_offset
+= b
->size
;
273 static int isom_write_segment_indexes
276 lsmash_adhoc_remux_t
*remux
279 /* Update the size of each Segment Index Box and establish the offset from the anchor point to the indexed material. */
280 if( isom_update_indexed_material_offset( file
, (isom_sidx_t
*)file
->sidx_list
.tail
->data
) < 0 )
282 /* Get the total size of all Segment Index Boxes. */
283 uint64_t total_sidx_size
= 0;
284 for( lsmash_entry_t
*entry
= file
->sidx_list
.head
; entry
; entry
= entry
->next
)
286 isom_sidx_t
*sidx
= (isom_sidx_t
*)entry
->data
;
289 total_sidx_size
+= sidx
->size
;
291 /* The buffer size must be at least total_sidx_size * 2. */
292 size_t buffer_size
= total_sidx_size
* 2;
293 if( remux
->buffer_size
> buffer_size
)
294 buffer_size
= remux
->buffer_size
;
295 /* Split to 2 buffers. */
296 uint8_t *buf
[2] = { NULL
, NULL
};
297 if( (buf
[0] = (uint8_t *)lsmash_malloc( buffer_size
)) == NULL
)
299 size_t size
= buffer_size
/ 2;
300 buf
[1] = buf
[0] + size
;
301 /* Seek to the beginning of the first Movie Fragment Box i.e. the first subsegment within this media segment. */
302 lsmash_bs_t
*bs
= file
->bs
;
303 if( lsmash_bs_write_seek( bs
, file
->fragment
->first_moof_pos
, SEEK_SET
) < 0 )
305 size_t read_num
= size
;
306 lsmash_bs_read_data( bs
, buf
[0], &read_num
);
307 uint64_t read_pos
= bs
->offset
;
308 /* Write the Segment Index Boxes actually here. */
309 if( lsmash_bs_write_seek( bs
, file
->fragment
->first_moof_pos
, SEEK_SET
) < 0 )
311 for( lsmash_entry_t
*entry
= file
->sidx_list
.head
; entry
; entry
= entry
->next
)
313 isom_sidx_t
*sidx
= (isom_sidx_t
*)entry
->data
;
316 if( isom_write_box( file
->bs
, (isom_box_t
*)sidx
) < 0 )
319 /* Rearrange subsequent data. */
320 uint64_t write_pos
= bs
->offset
;
321 uint64_t total
= file
->size
+ total_sidx_size
;
322 if( isom_rearrange_data( file
, remux
, buf
, read_num
, size
, read_pos
, write_pos
, total
) < 0 )
324 file
->size
+= total_sidx_size
;
325 lsmash_freep( &buf
[0] );
326 /* Update 'moof_offset' of each entry within the Track Fragment Random Access Boxes. */
328 for( lsmash_entry_t
*entry
= file
->mfra
->tfra_list
.head
; entry
; entry
= entry
->next
)
330 isom_tfra_t
*tfra
= (isom_tfra_t
*)entry
->data
;
333 for( lsmash_entry_t
*rap_entry
= tfra
->list
->head
; rap_entry
; rap_entry
= rap_entry
->next
)
335 isom_tfra_location_time_entry_t
*rap
= (isom_tfra_location_time_entry_t
*)rap_entry
->data
;
338 rap
->moof_offset
+= total_sidx_size
;
343 lsmash_free( buf
[0] );
347 int isom_finish_final_fragment_movie
350 lsmash_adhoc_remux_t
*remux
353 /* Output the final movie fragment. */
354 if( isom_finish_fragment_movie( file
) < 0 )
356 if( file
->bs
->unseekable
)
358 /* Write Segment Index Boxes.
359 * This occurs only when the initial movie has no samples.
360 * We don't consider updating of chunk offsets within initial movie sample table here.
361 * This is reasonable since DASH requires no samples in the initial movie.
362 * This implementation is not suitable for live-streaming.
363 + To support live-streaming, it is good to use daisy-chained index. */
364 if( (file
->flags
& LSMASH_FILE_MODE_MEDIA
)
365 && (file
->flags
& LSMASH_FILE_MODE_INDEX
)
366 && (file
->flags
& LSMASH_FILE_MODE_SEGMENT
)
367 && (!remux
|| isom_write_segment_indexes( file
, remux
) < 0) )
369 /* Write the overall random access information at the tail of the movie if this file is self-contained. */
370 if( isom_write_fragment_random_access_info( file
->initializer
) < 0 )
372 /* Set overall duration of the movie. */
373 return isom_set_fragment_overall_duration( file
->initializer
);
376 #define GET_MOST_USED( box_name, index, flag_name ) \
377 if( most_used[index] < stats.flag_name[i] ) \
379 most_used[index] = stats.flag_name[i]; \
380 box_name->default_sample_flags.flag_name = i; \
383 static int isom_create_fragment_overall_default_settings( lsmash_file_t
*file
)
385 assert( file
== file
->initializer
);
386 if( !isom_add_mvex( file
->moov
) )
388 if( !file
->bs
->unseekable
)
390 if( !isom_add_mehd( file
->moov
->mvex
) )
392 file
->moov
->mvex
->mehd
->manager
|= LSMASH_PLACEHOLDER
;
394 for( lsmash_entry_t
*trak_entry
= file
->moov
->trak_list
.head
; trak_entry
; trak_entry
= trak_entry
->next
)
396 isom_trak_t
*trak
= (isom_trak_t
*)trak_entry
->data
;
402 || !trak
->mdia
->minf
->stbl
)
404 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
405 if( !stbl
->stts
|| !stbl
->stts
->list
407 || (stbl
->stts
->list
->tail
&& !stbl
->stts
->list
->tail
->data
)
408 || (stbl
->stsz
->list
&& stbl
->stsz
->list
->head
&& !stbl
->stsz
->list
->head
->data
) )
410 isom_trex_t
*trex
= isom_add_trex( file
->moov
->mvex
);
413 trex
->track_ID
= trak
->tkhd
->track_ID
;
414 /* Set up defaults. */
415 trex
->default_sample_description_index
= trak
->cache
->chunk
.sample_description_index
416 ? trak
->cache
->chunk
.sample_description_index
418 trex
->default_sample_duration
= stbl
->stts
->list
->tail
419 ? ((isom_stts_entry_t
*)stbl
->stts
->list
->tail
->data
)->sample_delta
421 trex
->default_sample_size
= !stbl
->stsz
->list
422 ? stbl
->stsz
->sample_size
: stbl
->stsz
->list
->head
423 ? ((isom_stsz_entry_t
*)stbl
->stsz
->list
->head
->data
)->entry_size
: 0;
425 && stbl
->sdtp
->list
)
427 struct sample_flags_stats_t
429 uint32_t is_leading
[4];
430 uint32_t sample_depends_on
[4];
431 uint32_t sample_is_depended_on
[4];
432 uint32_t sample_has_redundancy
[4];
433 } stats
= { { 0 }, { 0 }, { 0 }, { 0 } };
434 for( lsmash_entry_t
*sdtp_entry
= stbl
->sdtp
->list
->head
; sdtp_entry
; sdtp_entry
= sdtp_entry
->next
)
436 isom_sdtp_entry_t
*data
= (isom_sdtp_entry_t
*)sdtp_entry
->data
;
439 ++ stats
.is_leading
[ data
->is_leading
];
440 ++ stats
.sample_depends_on
[ data
->sample_depends_on
];
441 ++ stats
.sample_is_depended_on
[ data
->sample_is_depended_on
];
442 ++ stats
.sample_has_redundancy
[ data
->sample_has_redundancy
];
444 uint32_t most_used
[4] = { 0, 0, 0, 0 };
445 for( int i
= 0; i
< 4; i
++ )
447 GET_MOST_USED( trex
, 0, is_leading
);
448 GET_MOST_USED( trex
, 1, sample_depends_on
);
449 GET_MOST_USED( trex
, 2, sample_is_depended_on
);
450 GET_MOST_USED( trex
, 3, sample_has_redundancy
);
453 trex
->default_sample_flags
.sample_is_non_sync_sample
= !trak
->cache
->all_sync
;
458 static int isom_prepare_random_access_info( lsmash_file_t
*file
)
460 assert( file
== file
->initializer
);
461 /* Don't write the random access info at the end of the file if unseekable or not self-contained. */
462 if( file
->bs
->unseekable
463 || !(file
->flags
& LSMASH_FILE_MODE_BOX
)
464 || !(file
->flags
& LSMASH_FILE_MODE_INITIALIZATION
)
465 || !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
466 || (file
->flags
& LSMASH_FILE_MODE_SEGMENT
) )
468 if( !isom_add_mfra( file
)
469 || !isom_add_mfro( file
->mfra
) )
474 static int isom_output_fragment_media_data( lsmash_file_t
*file
)
476 isom_fragment_manager_t
*fragment
= file
->fragment
;
477 /* If there is no available Media Data Box to write samples, add and write a new one. */
478 if( fragment
->sample_count
)
480 if( !file
->mdat
&& !isom_add_mdat( file
) )
482 file
->mdat
->manager
&= ~(LSMASH_INCOMPLETE_BOX
| LSMASH_WRITTEN_BOX
);
483 if( isom_write_box( file
->bs
, (isom_box_t
*)file
->mdat
) < 0 )
485 file
->size
+= file
->mdat
->size
;
486 file
->mdat
->size
= 0;
487 file
->mdat
->media_size
= 0;
489 lsmash_remove_entries( fragment
->pool
, isom_remove_sample_pool
);
490 fragment
->pool_size
= 0;
491 fragment
->sample_count
= 0;
495 static int isom_finish_fragment_initial_movie( lsmash_file_t
*file
)
497 assert( file
== file
->initializer
);
500 isom_moov_t
*moov
= file
->moov
;
501 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
503 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
510 || !trak
->mdia
->minf
->stbl
511 || isom_complement_data_reference( trak
->mdia
->minf
) < 0 )
513 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
514 if( isom_get_sample_count( trak
) )
516 /* Add stss box if any samples aren't sync sample. */
517 if( !trak
->cache
->all_sync
&& !stbl
->stss
&& !isom_add_stss( stbl
) )
519 if( isom_update_tkhd_duration( trak
) < 0 )
523 trak
->tkhd
->duration
= 0;
524 if( isom_update_bitrate_description( trak
->mdia
) < 0 )
526 /* Complete the last sample groups within tracks in the initial movie. */
527 if( trak
->cache
->rap
)
529 isom_sgpd_t
*sgpd
= isom_get_sample_group_description( stbl
, ISOM_GROUP_TYPE_RAP
);
530 if( !sgpd
|| isom_rap_grouping_established( trak
->cache
->rap
, 1, sgpd
, 0 ) < 0 )
532 lsmash_freep( &trak
->cache
->rap
);
534 if( trak
->cache
->roll
.pool
)
536 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group( &stbl
->sbgp_list
);
537 if( !sbgp
|| isom_all_recovery_completed( sbgp
, trak
->cache
->roll
.pool
) < 0 )
541 if( file
->mp4_version1
== 1 && isom_setup_iods( moov
) < 0 )
543 if( isom_create_fragment_overall_default_settings( file
) < 0
544 || isom_prepare_random_access_info ( file
) < 0
545 || isom_establish_movie ( file
) < 0 )
547 /* stco->co64 conversion, depending on last chunk's offset */
548 uint64_t meta_size
= file
->meta
? file
->meta
->size
: 0;
549 if( isom_check_large_offset_requirement( moov
, meta_size
) < 0 )
551 /* Now, the amount of the offset is fixed. apply it to stco/co64 */
552 uint64_t preceding_size
= moov
->size
+ meta_size
;
553 isom_add_preceding_box_size( moov
, preceding_size
);
554 /* Write File Type Box here if it was not written yet. */
555 if( file
->ftyp
&& !(file
->ftyp
->manager
& LSMASH_WRITTEN_BOX
) )
557 if( isom_write_box( file
->bs
, (isom_box_t
*)file
->ftyp
) < 0 )
559 file
->size
+= file
->ftyp
->size
;
561 /* Write Movie Box. */
562 if( isom_write_box( file
->bs
, (isom_box_t
*)file
->moov
) < 0
563 || isom_write_box( file
->bs
, (isom_box_t
*)file
->meta
) < 0 )
565 file
->size
+= preceding_size
;
566 /* Output samples. */
567 if( isom_output_fragment_media_data( file
) < 0 )
569 /* Revert the number of samples in tracks to 0. */
570 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
572 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
573 if( trak
->cache
->fragment
)
574 trak
->cache
->fragment
->sample_count
= 0;
579 /* Return 1 if there is diffrence, otherwise return 0. */
580 static int isom_compare_sample_flags( isom_sample_flags_t
*a
, isom_sample_flags_t
*b
)
582 return (a
->reserved
!= b
->reserved
)
583 || (a
->is_leading
!= b
->is_leading
)
584 || (a
->sample_depends_on
!= b
->sample_depends_on
)
585 || (a
->sample_is_depended_on
!= b
->sample_is_depended_on
)
586 || (a
->sample_has_redundancy
!= b
->sample_has_redundancy
)
587 || (a
->sample_padding_value
!= b
->sample_padding_value
)
588 || (a
->sample_is_non_sync_sample
!= b
->sample_is_non_sync_sample
)
589 || (a
->sample_degradation_priority
!= b
->sample_degradation_priority
);
592 static int isom_make_segment_index_entry
598 /* Make the index of this subsegment. */
599 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
601 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
602 isom_tfhd_t
*tfhd
= traf
->tfhd
;
603 isom_fragment_t
*track_fragment
= traf
->cache
->fragment
;
604 isom_subsegment_t
*subsegment
= &track_fragment
->subsegment
;
605 isom_sidx_t
*sidx
= isom_get_sidx( file
, tfhd
->track_ID
);
606 isom_trak_t
*trak
= isom_get_trak( file
->initializer
, tfhd
->track_ID
);
609 || !trak
->mdia
->mdhd
)
611 assert( traf
->tfdt
);
614 sidx
= isom_add_sidx( file
);
617 sidx
->reference_ID
= tfhd
->track_ID
;
618 sidx
->timescale
= trak
->mdia
->mdhd
->timescale
;
620 sidx
->reference_count
= 0;
621 if( isom_update_indexed_material_offset( file
, sidx
) < 0 )
624 /* One pair of a Movie Fragment Box with an associated Media Box per subsegment. */
625 isom_sidx_referenced_item_t
*data
= lsmash_malloc( sizeof(isom_sidx_referenced_item_t
) );
628 if( lsmash_add_entry( sidx
->list
, data
) < 0 )
633 sidx
->reference_count
= sidx
->list
->entry_count
;
634 data
->reference_type
= 0; /* media */
635 data
->reference_size
= file
->size
- moof
->pos
;
641 uint64_t composition_duration
= subsegment
->largest_cts
- subsegment
->smallest_cts
+ track_fragment
->last_duration
;
642 int subsegment_in_presentation
; /* If set to 1, TEPT is available. */
643 int first_rp_in_presentation
; /* If set to 1, both TSAP and TDEC are available. */
644 int first_sample_in_presentation
; /* If set to 1, TPTF is available. */
645 if( trak
->edts
&& trak
->edts
->elst
&& trak
->edts
->elst
->list
)
647 /**-- Explicit edits --**/
648 const isom_elst_t
*elst
= trak
->edts
->elst
;
649 const isom_elst_entry_t
*edit
= NULL
;
650 uint32_t movie_timescale
= file
->initializer
->moov
->mvhd
->timescale
;
651 uint64_t pts
= subsegment
->segment_duration
;
652 /* This initialization is redundant since these are unused when uninitialized
653 * and are initialized always in used cases, but unclever compilers may
654 * complain that these variables may be uninitialized. */
660 subsegment_in_presentation
= 0;
661 first_rp_in_presentation
= 0;
662 first_sample_in_presentation
= 0;
663 for( lsmash_entry_t
*elst_entry
= elst
->list
->head
; elst_entry
; elst_entry
= elst_entry
->next
)
665 edit
= (isom_elst_entry_t
*)elst_entry
->data
;
668 uint64_t edit_end_pts
;
669 uint64_t edit_end_cts
;
670 if( edit
->segment_duration
== ISOM_EDIT_DURATION_IMPLICIT
671 || (elst
->version
== 0 && edit
->segment_duration
== ISOM_EDIT_DURATION_UNKNOWN32
)
672 || (elst
->version
== 1 && edit
->segment_duration
== ISOM_EDIT_DURATION_UNKNOWN64
) )
674 edit_end_cts
= UINT64_MAX
;
675 edit_end_pts
= UINT64_MAX
;
679 if( edit
->segment_duration
)
681 double segment_duration
= edit
->segment_duration
* ((double)sidx
->timescale
/ movie_timescale
);
682 edit_end_cts
= edit
->media_time
+ (uint64_t)(segment_duration
* ((double)edit
->media_rate
/ (1 << 16)));
683 edit_end_pts
= pts
+ (uint64_t)segment_duration
;
687 uint64_t segment_duration
= composition_duration
;
688 if( edit
->media_time
> subsegment
->smallest_cts
)
690 if( subsegment
->largest_cts
+ track_fragment
->last_duration
> edit
->media_time
)
691 segment_duration
-= edit
->media_time
- subsegment
->smallest_cts
;
693 segment_duration
= 0;
695 edit_end_cts
= edit
->media_time
+ (uint64_t)(segment_duration
* ((double)edit
->media_rate
/ (1 << 16)));
696 edit_end_pts
= pts
+ (uint64_t)segment_duration
;
699 if( edit
->media_time
== ISOM_EDIT_MODE_EMPTY
)
704 if( (subsegment
->smallest_cts
>= edit
->media_time
&& subsegment
->smallest_cts
< edit_end_cts
)
705 || (subsegment
->largest_cts
>= edit
->media_time
&& subsegment
->largest_cts
< edit_end_cts
) )
707 /* This subsegment is present in this edit. */
708 double rate
= (double)edit
->media_rate
/ (1 << 16);
709 uint64_t start_time
= LSMASH_MAX( subsegment
->smallest_cts
, edit
->media_time
);
710 if( sidx
->reference_count
== 1 )
711 sidx
->earliest_presentation_time
= pts
;
712 if( subsegment_in_presentation
== 0 )
714 subsegment_in_presentation
= 1;
715 if( subsegment
->smallest_cts
>= edit
->media_time
)
716 TEPT
= pts
+ (uint64_t)((subsegment
->smallest_cts
- start_time
) / rate
);
720 if( first_rp_in_presentation
== 0
721 && ((subsegment
->first_ed_cts
>= edit
->media_time
&& subsegment
->first_ed_cts
< edit_end_cts
)
722 || (subsegment
->first_rp_cts
>= edit
->media_time
&& subsegment
->first_rp_cts
< edit_end_cts
)) )
724 /* FIXME: to distinguish TSAP and TDEC, need something to indicate incorrectly decodable sample. */
725 first_rp_in_presentation
= 1;
726 if( subsegment
->first_ed_cts
>= edit
->media_time
&& subsegment
->first_ed_cts
< edit_end_cts
)
727 TSAP
= pts
+ (uint64_t)((subsegment
->first_ed_cts
- start_time
) / rate
);
732 if( first_sample_in_presentation
== 0
733 && subsegment
->first_cts
>= edit
->media_time
&& subsegment
->first_cts
< edit_end_cts
)
735 first_sample_in_presentation
= 1;
736 TPTF
= pts
+ (uint64_t)((subsegment
->first_cts
- start_time
) / rate
);
738 uint64_t subsegment_end_pts
= pts
+ (uint64_t)(composition_duration
/ rate
);
739 pts
= LSMASH_MIN( edit_end_pts
, subsegment_end_pts
);
740 /* Update subsegment_duration. */
741 data
->subsegment_duration
= pts
- subsegment
->segment_duration
;
744 /* This subsegment is not present in this edit. */
750 /**-- Implicit edit --**/
751 if( sidx
->reference_count
== 1 )
752 sidx
->earliest_presentation_time
= subsegment
->smallest_cts
;
753 data
->subsegment_duration
= composition_duration
;
754 /* FIXME: to distinguish TSAP and TDEC, need something to indicate incorrectly decodable sample. */
755 TSAP
= subsegment
->first_rp_cts
;
756 TDEC
= subsegment
->first_rp_cts
;
757 TEPT
= subsegment
->smallest_cts
;
758 TPTF
= subsegment
->first_cts
;
759 subsegment_in_presentation
= 1;
760 first_rp_in_presentation
= 1;
761 first_sample_in_presentation
= 1;
763 if( subsegment
->first_ra_flags
== ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
764 || subsegment
->first_ra_number
== 0
765 || subsegment
->first_rp_number
== 0
766 || subsegment_in_presentation
== 0
767 || first_rp_in_presentation
== 0 )
769 /* No SAP in this subsegment. */
770 data
->starts_with_SAP
= 0;
772 data
->SAP_delta_time
= 0;
776 data
->starts_with_SAP
= (subsegment
->first_ra_number
== 1);
778 data
->SAP_delta_time
= TSAP
- TEPT
;
779 /* Decide SAP_type. */
780 if( first_sample_in_presentation
)
782 if( TEPT
== TDEC
&& TDEC
== TSAP
&& TSAP
== TPTF
)
784 else if( TEPT
== TDEC
&& TDEC
== TSAP
&& TSAP
< TPTF
)
786 else if( TEPT
< TDEC
&& TDEC
== TSAP
&& TSAP
<= TPTF
)
788 else if( TEPT
<= TPTF
&& TPTF
< TDEC
&& TDEC
== TSAP
)
791 if( data
->SAP_type
== 0 )
793 if( TEPT
== TDEC
&& TDEC
< TSAP
)
795 else if( TEPT
< TDEC
&& TDEC
< TSAP
)
799 subsegment
->segment_duration
+= data
->subsegment_duration
;
800 subsegment
->first_ed_cts
= UINT64_MAX
;
801 subsegment
->first_rp_cts
= UINT64_MAX
;
802 subsegment
->first_rp_number
= 0;
803 subsegment
->first_ra_number
= 0;
804 subsegment
->first_ra_flags
= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
;
805 subsegment
->decodable
= 0;
810 static int isom_finish_fragment_movie
816 || !file
->fragment
->pool
)
818 isom_moof_t
*moof
= file
->fragment
->movie
;
821 if( file
== file
->initializer
)
822 return isom_finish_fragment_initial_movie( file
);
824 return 0; /* No movie fragment to be finished. */
826 /* Don't write the current movie fragment if containing no track fragments.
827 * This is a requirement of DASH Media Segment. */
828 if( !moof
->traf_list
.head
829 || !moof
->traf_list
.head
->data
)
831 /* Calculate appropriate default_sample_flags of each Track Fragment Header Box.
832 * And check whether that default_sample_flags is useful or not. */
833 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
835 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
839 || !traf
->file
->initializer
840 || !traf
->file
->initializer
->moov
841 || !traf
->file
->initializer
->moov
->mvex
)
843 isom_tfhd_t
*tfhd
= traf
->tfhd
;
844 isom_trex_t
*trex
= isom_get_trex( file
->initializer
->moov
->mvex
, tfhd
->track_ID
);
847 struct sample_flags_stats_t
849 uint32_t is_leading
[4];
850 uint32_t sample_depends_on
[4];
851 uint32_t sample_is_depended_on
[4];
852 uint32_t sample_has_redundancy
[4];
853 uint32_t sample_is_non_sync_sample
[2];
854 } stats
= { { 0 }, { 0 }, { 0 }, { 0 }, { 0 } };
855 for( lsmash_entry_t
*trun_entry
= traf
->trun_list
.head
; trun_entry
; trun_entry
= trun_entry
->next
)
857 isom_trun_t
*trun
= (isom_trun_t
*)trun_entry
->data
;
858 if( !trun
|| trun
->sample_count
== 0 )
860 isom_sample_flags_t
*sample_flags
;
861 if( trun
->flags
& ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT
)
863 if( !trun
->optional
)
865 for( lsmash_entry_t
*optional_entry
= trun
->optional
->head
; optional_entry
; optional_entry
= optional_entry
->next
)
867 isom_trun_optional_row_t
*row
= (isom_trun_optional_row_t
*)optional_entry
->data
;
870 sample_flags
= &row
->sample_flags
;
871 ++ stats
.is_leading
[ sample_flags
->is_leading
];
872 ++ stats
.sample_depends_on
[ sample_flags
->sample_depends_on
];
873 ++ stats
.sample_is_depended_on
[ sample_flags
->sample_is_depended_on
];
874 ++ stats
.sample_has_redundancy
[ sample_flags
->sample_has_redundancy
];
875 ++ stats
.sample_is_non_sync_sample
[ sample_flags
->sample_is_non_sync_sample
];
880 sample_flags
= &tfhd
->default_sample_flags
;
881 stats
.is_leading
[ sample_flags
->is_leading
] += trun
->sample_count
;
882 stats
.sample_depends_on
[ sample_flags
->sample_depends_on
] += trun
->sample_count
;
883 stats
.sample_is_depended_on
[ sample_flags
->sample_is_depended_on
] += trun
->sample_count
;
884 stats
.sample_has_redundancy
[ sample_flags
->sample_has_redundancy
] += trun
->sample_count
;
885 stats
.sample_is_non_sync_sample
[ sample_flags
->sample_is_non_sync_sample
] += trun
->sample_count
;
888 uint32_t most_used
[5] = { 0, 0, 0, 0, 0 };
889 for( int i
= 0; i
< 4; i
++ )
891 GET_MOST_USED( tfhd
, 0, is_leading
);
892 GET_MOST_USED( tfhd
, 1, sample_depends_on
);
893 GET_MOST_USED( tfhd
, 2, sample_is_depended_on
);
894 GET_MOST_USED( tfhd
, 3, sample_has_redundancy
);
896 GET_MOST_USED( tfhd
, 4, sample_is_non_sync_sample
);
898 int useful_default_sample_duration
= 0;
899 int useful_default_sample_size
= 0;
900 for( lsmash_entry_t
*trun_entry
= traf
->trun_list
.head
; trun_entry
; trun_entry
= trun_entry
->next
)
902 isom_trun_t
*trun
= (isom_trun_t
*)trun_entry
->data
;
903 if( !(trun
->flags
& ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT
) )
904 useful_default_sample_duration
= 1;
905 if( !(trun
->flags
& ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT
) )
906 useful_default_sample_size
= 1;
907 int useful_first_sample_flags
= 1;
908 int useful_default_sample_flags
= 1;
909 if( trun
->sample_count
== 1 )
911 /* It is enough to check only if first_sample_flags equals default_sample_flags or not.
912 * If it is equal, just use default_sample_flags.
913 * If not, just use first_sample_flags of this run. */
914 if( !isom_compare_sample_flags( &trun
->first_sample_flags
, &tfhd
->default_sample_flags
) )
915 useful_first_sample_flags
= 0;
917 else if( trun
->optional
918 && trun
->optional
->head
)
920 lsmash_entry_t
*optional_entry
= trun
->optional
->head
->next
;
921 isom_trun_optional_row_t
*row
= (isom_trun_optional_row_t
*)optional_entry
->data
;
922 isom_sample_flags_t representative_sample_flags
= row
->sample_flags
;
923 if( isom_compare_sample_flags( &tfhd
->default_sample_flags
, &representative_sample_flags
) )
924 useful_default_sample_flags
= 0;
925 if( !isom_compare_sample_flags( &trun
->first_sample_flags
, &representative_sample_flags
) )
926 useful_first_sample_flags
= 0;
927 if( useful_default_sample_flags
)
928 for( optional_entry
= optional_entry
->next
; optional_entry
; optional_entry
= optional_entry
->next
)
930 row
= (isom_trun_optional_row_t
*)optional_entry
->data
;
931 if( isom_compare_sample_flags( &representative_sample_flags
, &row
->sample_flags
) )
933 useful_default_sample_flags
= 0;
938 if( useful_default_sample_flags
)
940 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT
;
941 trun
->flags
&= ~ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT
;
945 useful_first_sample_flags
= 0;
946 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT
;
948 if( useful_first_sample_flags
)
949 trun
->flags
|= ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT
;
951 if( useful_default_sample_duration
&& tfhd
->default_sample_duration
!= trex
->default_sample_duration
)
952 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT
;
954 tfhd
->default_sample_duration
= trex
->default_sample_duration
; /* This might be redundant, but is to be more natural. */
955 if( useful_default_sample_size
&& tfhd
->default_sample_size
!= trex
->default_sample_size
)
956 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT
;
958 tfhd
->default_sample_size
= trex
->default_sample_size
; /* This might be redundant, but is to be more natural. */
959 if( !(tfhd
->flags
& ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT
) )
960 tfhd
->default_sample_flags
= trex
->default_sample_flags
; /* This might be redundant, but is to be more natural. */
961 else if( !isom_compare_sample_flags( &tfhd
->default_sample_flags
, &trex
->default_sample_flags
) )
962 tfhd
->flags
&= ~ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT
;
964 /* Complete the last sample groups in the previous track fragments. */
965 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
967 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
968 if( traf
->cache
->rap
)
970 isom_sgpd_t
*sgpd
= isom_get_fragment_sample_group_description( traf
, ISOM_GROUP_TYPE_RAP
);
971 if( !sgpd
|| isom_rap_grouping_established( traf
->cache
->rap
, 1, sgpd
, 1 ) < 0 )
973 lsmash_freep( &traf
->cache
->rap
);
975 if( traf
->cache
->roll
.pool
)
977 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group( &traf
->sbgp_list
);
978 if( !sbgp
|| isom_all_recovery_completed( sbgp
, traf
->cache
->roll
.pool
) < 0 )
982 /* Establish Movie Fragment Box.
983 * We write exactly one Media Data Box starting immediately after the corresponding Movie Fragment Box. */
984 if( file
->allow_moof_base
)
986 /* In this branch, we use default-base-is-moof flag, which indicates implicit base_data_offsets originate in the
987 * first byte of each enclosing Movie Fragment Box.
988 * We use the sum of the size of the Movie Fragment Box and the offset from the size field of the Media Data Box to
989 * the type field of it as the data_offset of the first track run like the following.
991 * _____________ _ offset := 0
1000 * |___|_________|_ offset := the size of the Movie Fragment Box
1006 * | a |_________|_ offset := the data_offset of the first track run
1009 * |___|_________|_ offset := the size of a subsegment containing exactly one movie fragment
1011 * For a pair of one Movie Fragment Box and one Media Data Box, placed in this order, implicit base_data_offsets
1012 * indicated by the absence of both base-data-offset-present and default-base-is-moof are somewhat complicated
1013 * since the implicit base_data_offset of the current track fragment is defined by the end of the data of the
1014 * previous track fragment and the data_offset of the track runs could be negative value because of interleaving
1015 * track runs or something other reasons.
1016 * In contrast, implicit base_data_offsets indicated by default-base-is-moof are simple since the base_data_offset
1017 * of each track fragment is always constant for that pair and has no dependency on other track fragments.
1019 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
1021 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
1022 traf
->tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_BASE_IS_MOOF
;
1023 traf
->tfhd
->base_data_offset
= file
->size
; /* not written actually though */
1024 for( lsmash_entry_t
*trun_entry
= traf
->trun_list
.head
; trun_entry
; trun_entry
= trun_entry
->next
)
1026 /* Here, data_offset is always greater than zero. */
1027 isom_trun_t
*trun
= trun_entry
->data
;
1028 trun
->flags
|= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT
;
1031 /* Consider the update of tr_flags here. */
1032 if( isom_update_box_size( moof
) == 0 )
1034 /* Now, we can calculate offsets in the current movie fragment, so do it. */
1035 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
1037 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
1038 for( lsmash_entry_t
*trun_entry
= traf
->trun_list
.head
; trun_entry
; trun_entry
= trun_entry
->next
)
1040 isom_trun_t
*trun
= trun_entry
->data
;
1041 trun
->data_offset
+= moof
->size
+ ISOM_BASEBOX_COMMON_SIZE
;
1047 /* In this branch, we use explicit base_data_offset. */
1048 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
1050 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
1051 traf
->tfhd
->flags
|= ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT
;
1053 /* Consider the update of tf_flags here. */
1054 if( isom_update_box_size( moof
) == 0 )
1056 /* Now, we can calculate offsets in the current movie fragment, so do it. */
1057 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
1059 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
1060 traf
->tfhd
->base_data_offset
= file
->size
+ moof
->size
+ ISOM_BASEBOX_COMMON_SIZE
;
1063 /* Write Movie Fragment Box and its children. */
1064 moof
->pos
= file
->size
;
1065 if( isom_write_box( file
->bs
, (isom_box_t
*)moof
) < 0 )
1067 if( file
->fragment
->first_moof_pos
== FIRST_MOOF_POS_UNDETERMINED
)
1068 file
->fragment
->first_moof_pos
= moof
->pos
;
1069 file
->size
+= moof
->size
;
1070 /* Output samples. */
1071 if( isom_output_fragment_media_data( file
) < 0 )
1073 /* Revert the number of samples in track fragments to 0. */
1074 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
1076 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
1077 if( traf
->cache
->fragment
)
1078 traf
->cache
->fragment
->sample_count
= 0;
1080 if( !(file
->flags
& LSMASH_FILE_MODE_INDEX
) || file
->max_isom_version
< 6 )
1082 return isom_make_segment_index_entry( file
, moof
);
1085 #undef GET_MOST_USED
1087 static isom_trun_optional_row_t
*isom_request_trun_optional_row( isom_trun_t
*trun
, isom_tfhd_t
*tfhd
, uint32_t sample_number
)
1089 isom_trun_optional_row_t
*row
= NULL
;
1090 if( !trun
->optional
)
1092 trun
->optional
= lsmash_create_entry_list();
1093 if( !trun
->optional
)
1096 if( trun
->optional
->entry_count
< sample_number
)
1098 while( trun
->optional
->entry_count
< sample_number
)
1100 row
= lsmash_malloc( sizeof(isom_trun_optional_row_t
) );
1103 /* Copy from default. */
1104 row
->sample_duration
= tfhd
->default_sample_duration
;
1105 row
->sample_size
= tfhd
->default_sample_size
;
1106 row
->sample_flags
= tfhd
->default_sample_flags
;
1107 row
->sample_composition_time_offset
= 0;
1108 if( lsmash_add_entry( trun
->optional
, row
) < 0 )
1117 for( lsmash_entry_t
*entry
= trun
->optional
->head
; entry
; entry
= entry
->next
)
1119 row
= (isom_trun_optional_row_t
*)entry
->data
;
1122 if( ++i
== sample_number
)
1128 int lsmash_create_fragment_empty_duration
1130 lsmash_root_t
*root
,
1135 if( isom_check_initializer_present( root
) < 0 )
1137 lsmash_file_t
*file
= root
->file
;
1139 || !file
->fragment
->movie
1140 || !file
->initializer
->moov
)
1142 isom_trak_t
*trak
= isom_get_trak( file
->initializer
, track_ID
);
1146 isom_trex_t
*trex
= isom_get_trex( file
->initializer
->moov
->mvex
, track_ID
);
1149 isom_moof_t
*moof
= file
->fragment
->movie
;
1150 isom_traf_t
*traf
= isom_get_traf( moof
, track_ID
);
1153 traf
= isom_add_traf( moof
);
1154 if( !isom_add_tfhd( traf
) )
1156 isom_tfhd_t
*tfhd
= traf
->tfhd
;
1157 tfhd
->flags
= ISOM_TF_FLAGS_DURATION_IS_EMPTY
; /* no samples for this track fragment yet */
1158 tfhd
->track_ID
= trak
->tkhd
->track_ID
;
1159 tfhd
->default_sample_duration
= duration
;
1160 if( duration
!= trex
->default_sample_duration
)
1161 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT
;
1162 traf
->cache
= trak
->cache
;
1163 traf
->cache
->fragment
->traf_number
= moof
->traf_list
.entry_count
;
1164 traf
->cache
->fragment
->last_duration
+= duration
; /* The duration of the last sample includes this empty-duration. */
1168 int isom_set_fragment_last_duration
1171 uint32_t last_duration
1174 isom_tfhd_t
*tfhd
= traf
->tfhd
;
1175 if( !traf
->trun_list
.tail
1176 || !traf
->trun_list
.tail
->data
)
1178 /* There are no track runs in this track fragment, so it is a empty-duration. */
1179 isom_trex_t
*trex
= isom_get_trex( traf
->file
->initializer
->moov
->mvex
, tfhd
->track_ID
);
1182 tfhd
->flags
|= ISOM_TF_FLAGS_DURATION_IS_EMPTY
;
1183 if( last_duration
!= trex
->default_sample_duration
)
1184 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT
;
1185 tfhd
->default_sample_duration
= last_duration
;
1186 traf
->cache
->fragment
->last_duration
= last_duration
;
1189 /* Update the last sample_duration if needed. */
1190 isom_trun_t
*trun
= (isom_trun_t
*)traf
->trun_list
.tail
->data
;
1191 if( trun
->sample_count
== 1
1192 && traf
->trun_list
.entry_count
== 1 )
1194 isom_trex_t
*trex
= isom_get_trex( traf
->file
->initializer
->moov
->mvex
, tfhd
->track_ID
);
1197 if( last_duration
!= trex
->default_sample_duration
)
1198 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT
;
1199 tfhd
->default_sample_duration
= last_duration
;
1201 else if( last_duration
!= tfhd
->default_sample_duration
)
1202 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT
;
1205 isom_trun_optional_row_t
*row
= isom_request_trun_optional_row( trun
, tfhd
, trun
->sample_count
);
1208 row
->sample_duration
= last_duration
;
1210 traf
->cache
->fragment
->last_duration
= last_duration
;
1214 int isom_append_fragment_track_run
1216 lsmash_file_t
*file
,
1220 if( !chunk
->pool
|| chunk
->pool
->size
== 0 )
1222 isom_fragment_manager_t
*fragment
= file
->fragment
;
1223 /* Move data in the pool of the current track fragment to the pool of the current movie fragment.
1224 * Empty the pool of current track. We don't delete data of samples here. */
1225 if( lsmash_add_entry( fragment
->pool
, chunk
->pool
) < 0 )
1227 fragment
->sample_count
+= chunk
->pool
->sample_count
;
1228 fragment
->pool_size
+= chunk
->pool
->size
;
1229 chunk
->pool
= isom_create_sample_pool( chunk
->pool
->size
);
1230 return chunk
->pool
? 0 : -1;
1233 static int isom_output_fragment_cache( isom_traf_t
*traf
)
1235 isom_cache_t
*cache
= traf
->cache
;
1236 if( isom_append_fragment_track_run( traf
->file
, &cache
->chunk
) < 0 )
1238 for( lsmash_entry_t
*entry
= traf
->sgpd_list
.head
; entry
; entry
= entry
->next
)
1240 isom_sgpd_t
*sgpd
= (isom_sgpd_t
*)entry
->data
;
1243 switch( sgpd
->grouping_type
)
1245 case ISOM_GROUP_TYPE_RAP
:
1247 isom_rap_group_t
*group
= cache
->rap
;
1250 if( traf
->file
->fragment
)
1255 if( !group
->random_access
)
1257 group
->random_access
->num_leading_samples_known
= 1;
1260 case ISOM_GROUP_TYPE_ROLL
:
1261 case ISOM_GROUP_TYPE_PROL
:
1262 if( !cache
->roll
.pool
)
1264 if( traf
->file
->fragment
)
1269 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group( &traf
->sbgp_list
);
1270 if( !sbgp
|| isom_all_recovery_completed( sbgp
, cache
->roll
.pool
) < 0 )
1280 int isom_flush_fragment_pooled_samples
1282 lsmash_file_t
*file
,
1284 uint32_t last_sample_duration
1287 isom_traf_t
*traf
= isom_get_traf( file
->fragment
->movie
, track_ID
);
1289 /* No samples. We don't return as an error here since user might call the flushing function even if the
1290 * current movie fragment has no track fragment with this track_ID. */
1293 || !traf
->cache
->fragment
)
1295 if( traf
->trun_list
.entry_count
1296 && traf
->trun_list
.tail
1297 && traf
->trun_list
.tail
->data
)
1299 /* Media Data Box preceded by Movie Fragment Box could change base_data_offsets in each track fragments later.
1300 * We can't consider this here because the length of Movie Fragment Box is unknown at this step yet. */
1301 isom_trun_t
*trun
= (isom_trun_t
*)traf
->trun_list
.tail
->data
;
1302 if( file
->fragment
->pool_size
)
1303 trun
->flags
|= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT
;
1304 trun
->data_offset
= file
->fragment
->pool_size
;
1306 if( isom_output_fragment_cache( traf
) < 0 )
1308 return isom_set_fragment_last_duration( traf
, last_sample_duration
);
1311 /* This function doesn't update sample_duration of the last sample in the previous movie fragment.
1312 * Instead of this, isom_finish_movie_fragment undertakes this task. */
1313 static int isom_update_fragment_previous_sample_duration( isom_traf_t
*traf
, isom_trex_t
*trex
, uint32_t duration
)
1315 isom_tfhd_t
*tfhd
= traf
->tfhd
;
1316 isom_trun_t
*trun
= (isom_trun_t
*)traf
->trun_list
.tail
->data
;
1317 int previous_run_has_previous_sample
= 0;
1318 if( trun
->sample_count
== 1 )
1320 if( traf
->trun_list
.entry_count
== 1 )
1321 return 0; /* The previous track run belongs to the previous movie fragment if it exists. */
1322 if( !traf
->trun_list
.tail
->prev
1323 || !traf
->trun_list
.tail
->prev
->data
)
1325 /* OK. The previous sample exists in the previous track run in the same track fragment. */
1326 trun
= (isom_trun_t
*)traf
->trun_list
.tail
->prev
->data
;
1327 previous_run_has_previous_sample
= 1;
1329 /* Update default_sample_duration of the Track Fragment Header Box
1330 * if this duration is what the first sample in the current track fragment owns. */
1331 if( (trun
->sample_count
== 2 && traf
->trun_list
.entry_count
== 1)
1332 || (trun
->sample_count
== 1 && traf
->trun_list
.entry_count
== 2) )
1334 if( duration
!= trex
->default_sample_duration
)
1335 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT
;
1336 tfhd
->default_sample_duration
= duration
;
1338 /* Update the previous sample_duration if needed. */
1339 if( duration
!= tfhd
->default_sample_duration
)
1340 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT
;
1343 uint32_t sample_number
= trun
->sample_count
- !previous_run_has_previous_sample
;
1344 isom_trun_optional_row_t
*row
= isom_request_trun_optional_row( trun
, tfhd
, sample_number
);
1347 row
->sample_duration
= duration
;
1349 traf
->cache
->fragment
->last_duration
= duration
;
1353 static isom_sample_flags_t
isom_generate_fragment_sample_flags( lsmash_sample_t
*sample
)
1355 isom_sample_flags_t flags
;
1357 flags
.is_leading
= sample
->prop
.leading
& 0x3;
1358 flags
.sample_depends_on
= sample
->prop
.independent
& 0x3;
1359 flags
.sample_is_depended_on
= sample
->prop
.disposable
& 0x3;
1360 flags
.sample_has_redundancy
= sample
->prop
.redundant
& 0x3;
1361 flags
.sample_padding_value
= 0;
1362 flags
.sample_is_non_sync_sample
= !(sample
->prop
.ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
);
1363 flags
.sample_degradation_priority
= 0;
1367 static int isom_update_fragment_sample_tables( isom_traf_t
*traf
, lsmash_sample_t
*sample
)
1369 isom_tfhd_t
*tfhd
= traf
->tfhd
;
1370 isom_trex_t
*trex
= isom_get_trex( traf
->file
->initializer
->moov
->mvex
, tfhd
->track_ID
);
1373 lsmash_file_t
*file
= traf
->file
;
1374 isom_cache_t
*cache
= traf
->cache
;
1375 isom_chunk_t
*current
= &cache
->chunk
;
1376 if( !current
->pool
)
1378 /* Very initial settings, just once per track */
1379 current
->pool
= isom_create_sample_pool( 0 );
1380 if( !current
->pool
)
1383 /* Create a new track run if the duration exceeds max_chunk_duration.
1384 * Old one will be appended to the pool of this movie fragment. */
1385 uint32_t media_timescale
= lsmash_get_media_timescale( file
->root
, tfhd
->track_ID
);
1386 if( !media_timescale
)
1388 int delimit
= (file
->max_chunk_duration
< ((double)(sample
->dts
- current
->first_dts
) / media_timescale
))
1389 || (file
->max_chunk_size
< (current
->pool
->size
+ sample
->length
));
1390 isom_trun_t
*trun
= NULL
;
1391 if( !traf
->trun_list
.entry_count
|| delimit
)
1394 && traf
->trun_list
.entry_count
1395 && traf
->trun_list
.tail
1396 && traf
->trun_list
.tail
->data
)
1398 /* Media Data Box preceded by Movie Fragment Box could change base data offsets in each track fragments later.
1399 * We can't consider this here because the length of Movie Fragment Box is unknown at this step yet. */
1400 trun
= (isom_trun_t
*)traf
->trun_list
.tail
->data
;
1401 if( file
->fragment
->pool_size
)
1402 trun
->flags
|= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT
;
1403 trun
->data_offset
= file
->fragment
->pool_size
;
1405 trun
= isom_add_trun( traf
);
1411 if( !traf
->trun_list
.tail
1412 || !traf
->trun_list
.tail
->data
)
1414 trun
= (isom_trun_t
*)traf
->trun_list
.tail
->data
;
1416 isom_sample_flags_t sample_flags
= isom_generate_fragment_sample_flags( sample
);
1417 if( ++trun
->sample_count
== 1 )
1419 if( traf
->trun_list
.entry_count
== 1 )
1421 /* This track fragment isn't empty-duration-fragment any more. */
1422 tfhd
->flags
&= ~ISOM_TF_FLAGS_DURATION_IS_EMPTY
;
1423 /* Set up sample_description_index in this track fragment. */
1424 if( sample
->index
!= trex
->default_sample_description_index
)
1425 tfhd
->flags
|= ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT
;
1426 tfhd
->sample_description_index
= current
->sample_description_index
= sample
->index
;
1427 /* Set up default_sample_size used in this track fragment. */
1428 tfhd
->default_sample_size
= sample
->length
;
1429 /* Set up default_sample_flags used in this track fragment.
1430 * Note: we decide an appropriate default value at the end of this movie fragment. */
1431 tfhd
->default_sample_flags
= sample_flags
;
1432 /* Set up random access information if this sample is a sync sample.
1433 * We inform only the first sample in each movie fragment. */
1434 if( file
->mfra
&& (sample
->prop
.ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
) )
1436 isom_tfra_t
*tfra
= isom_get_tfra( file
->mfra
, tfhd
->track_ID
);
1439 tfra
= isom_add_tfra( file
->mfra
);
1442 tfra
->track_ID
= tfhd
->track_ID
;
1446 tfra
->list
= lsmash_create_entry_list();
1450 isom_tfra_location_time_entry_t
*rap
= lsmash_malloc( sizeof(isom_tfra_location_time_entry_t
) );
1453 rap
->time
= sample
->cts
; /* Set composition timestamp temporally.
1454 * At the end of the whole movie, this will be reset as presentation time. */
1455 rap
->moof_offset
= file
->size
; /* We place Movie Fragment Box in the head of each movie fragment. */
1456 rap
->traf_number
= cache
->fragment
->traf_number
;
1457 rap
->trun_number
= traf
->trun_list
.entry_count
;
1458 rap
->sample_number
= trun
->sample_count
;
1459 if( lsmash_add_entry( tfra
->list
, rap
) < 0 )
1464 tfra
->number_of_entry
= tfra
->list
->entry_count
;
1466 for( length
= 1; rap
->traf_number
>> (length
* 8); length
++ );
1467 tfra
->length_size_of_traf_num
= LSMASH_MAX( length
- 1, tfra
->length_size_of_traf_num
);
1468 for( length
= 1; rap
->traf_number
>> (length
* 8); length
++ );
1469 tfra
->length_size_of_trun_num
= LSMASH_MAX( length
- 1, tfra
->length_size_of_trun_num
);
1470 for( length
= 1; rap
->sample_number
>> (length
* 8); length
++ );
1471 tfra
->length_size_of_sample_num
= LSMASH_MAX( length
- 1, tfra
->length_size_of_sample_num
);
1473 /* Set up the base media decode time of this track fragment.
1474 * This feature is available under ISO Base Media version 6 or later.
1475 * For DASH Media Segment, each Track Fragment Box shall contain a Track Fragment Base Media Decode Time Box. */
1476 if( file
->max_isom_version
>= 6 || file
->media_segment
)
1478 assert( !traf
->tfdt
);
1479 if( !isom_add_tfdt( traf
) )
1481 if( sample
->dts
> UINT32_MAX
)
1482 traf
->tfdt
->version
= 1;
1483 traf
->tfdt
->baseMediaDecodeTime
= sample
->dts
;
1486 trun
->first_sample_flags
= sample_flags
;
1487 current
->first_dts
= sample
->dts
;
1489 /* Update the optional rows in the current track run except for sample_duration if needed. */
1490 if( sample
->length
!= tfhd
->default_sample_size
)
1491 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT
;
1492 if( isom_compare_sample_flags( &sample_flags
, &tfhd
->default_sample_flags
) )
1493 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT
;
1494 uint32_t sample_composition_time_offset
= sample
->cts
- sample
->dts
;
1495 if( sample_composition_time_offset
)
1497 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT
;
1498 /* Check if negative composition time offset is present. */
1499 isom_timestamp_t
*ts_cache
= &cache
->timestamp
;
1500 if( (sample
->cts
+ ts_cache
->ctd_shift
) < sample
->dts
)
1502 if( file
->max_isom_version
< 6 )
1503 return -1; /* Negative composition time offset is not supported. */
1504 if( (sample
->dts
- sample
->cts
) > INT32_MAX
)
1505 return -1; /* Overflow */
1506 ts_cache
->ctd_shift
= sample
->dts
- sample
->cts
;
1507 if( trun
->version
== 0 && file
->max_isom_version
>= 6 )
1513 isom_trun_optional_row_t
*row
= isom_request_trun_optional_row( trun
, tfhd
, trun
->sample_count
);
1516 row
->sample_size
= sample
->length
;
1517 row
->sample_flags
= sample_flags
;
1518 row
->sample_composition_time_offset
= sample_composition_time_offset
;
1520 /* Set up the sample groupings for random access. */
1521 if( isom_group_random_access( (isom_box_t
*)traf
, sample
) < 0
1522 || isom_group_roll_recovery( (isom_box_t
*)traf
, sample
) < 0 )
1524 /* Set up the previous sample_duration if this sample is not the first sample in the overall movie. */
1525 if( cache
->fragment
->has_samples
)
1527 /* Note: when using for live streaming, it is not good idea to return error (-1) by sample->dts < prev_dts
1528 * since that's trivial for such semi-permanent presentation. */
1529 uint64_t prev_dts
= cache
->timestamp
.dts
;
1530 if( sample
->dts
<= prev_dts
1531 || sample
->dts
> prev_dts
+ UINT32_MAX
)
1533 uint32_t sample_duration
= sample
->dts
- prev_dts
;
1534 if( isom_update_fragment_previous_sample_duration( traf
, trex
, sample_duration
) < 0 )
1538 cache
->timestamp
.dts
= sample
->dts
;
1539 cache
->timestamp
.cts
= sample
->cts
;
1540 cache
->fragment
->largest_cts
= LSMASH_MAX( sample
->cts
, cache
->fragment
->largest_cts
);
1541 isom_subsegment_t
*subsegment
= &cache
->fragment
->subsegment
;
1542 if( trun
->sample_count
== 1 && traf
->trun_list
.entry_count
== 1 )
1544 subsegment
->first_cts
= sample
->cts
;
1545 subsegment
->largest_cts
= sample
->cts
;
1546 subsegment
->smallest_cts
= sample
->cts
;
1550 subsegment
->largest_cts
= LSMASH_MAX( sample
->cts
, subsegment
->largest_cts
);
1551 subsegment
->smallest_cts
= LSMASH_MIN( sample
->cts
, subsegment
->smallest_cts
);
1553 if( subsegment
->first_ra_flags
== ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
)
1555 subsegment
->first_ra_flags
= sample
->prop
.ra_flags
;
1556 subsegment
->first_ra_number
= cache
->fragment
->sample_count
+ 1;
1557 if( sample
->prop
.ra_flags
& (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
| ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
) )
1559 subsegment
->first_rp_number
= subsegment
->first_ra_number
;
1560 subsegment
->first_rp_cts
= sample
->cts
;
1561 subsegment
->first_ed_cts
= sample
->cts
;
1562 subsegment
->decodable
= 1;
1565 else if( subsegment
->decodable
)
1567 if( (subsegment
->first_ra_flags
& (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
| ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
))
1568 ? (sample
->prop
.leading
== ISOM_SAMPLE_IS_DECODABLE_LEADING
)
1569 : (subsegment
->first_ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_POST_ROLL_START
) )
1570 subsegment
->first_ed_cts
= LSMASH_MIN( sample
->cts
, subsegment
->first_ed_cts
);
1572 subsegment
->decodable
= 0;
1577 static int isom_append_fragment_sample_internal_initial( isom_trak_t
*trak
, lsmash_sample_t
*sample
)
1579 /* Update the sample tables of this track fragment.
1580 * If a new chunk was created, append the previous one to the pool of this movie fragment. */
1581 uint32_t samples_per_packet
;
1582 int delimit
= isom_update_sample_tables( trak
, sample
, &samples_per_packet
);
1585 else if( delimit
== 1 )
1586 isom_append_fragment_track_run( trak
->file
, &trak
->cache
->chunk
);
1587 /* Add a new sample into the pool of this track fragment. */
1588 if( isom_pool_sample( trak
->cache
->chunk
.pool
, sample
, samples_per_packet
) < 0 )
1590 trak
->cache
->fragment
->has_samples
= 1;
1591 trak
->cache
->fragment
->sample_count
+= 1;
1595 static int isom_append_fragment_sample_internal( isom_traf_t
*traf
, lsmash_sample_t
*sample
)
1597 /* Update the sample tables of this track fragment.
1598 * If a new track run was created, append the previous one to the pool of this movie fragment. */
1599 int delimit
= isom_update_fragment_sample_tables( traf
, sample
);
1602 else if( delimit
== 1 )
1603 isom_append_fragment_track_run( traf
->file
, &traf
->cache
->chunk
);
1604 /* Add a new sample into the pool of this track fragment. */
1605 if( isom_pool_sample( traf
->cache
->chunk
.pool
, sample
, 1 ) < 0 )
1607 traf
->cache
->fragment
->has_samples
= 1;
1608 traf
->cache
->fragment
->sample_count
+= 1;
1612 int isom_append_fragment_sample
1614 lsmash_file_t
*file
,
1616 lsmash_sample_t
*sample
1619 isom_fragment_manager_t
*fragment
= file
->fragment
;
1620 assert( fragment
&& fragment
->pool
);
1621 isom_trak_t
*trak
= isom_get_trak( file
->initializer
, track_ID
);
1625 || !trak
->cache
->fragment
1628 || !trak
->mdia
->mdhd
1629 || trak
->mdia
->mdhd
->timescale
== 0
1630 || !trak
->mdia
->minf
1631 || !trak
->mdia
->minf
->stbl
1632 || !trak
->mdia
->minf
->stbl
->stsd
1633 || !trak
->mdia
->minf
->stbl
->stsc
|| !trak
->mdia
->minf
->stbl
->stsc
->list
)
1635 /* Write the Segment Type Box here if required and if it was not written yet. */
1636 if( !(file
->flags
& LSMASH_FILE_MODE_INITIALIZATION
)
1637 && file
->styp_list
.head
&& file
->styp_list
.head
->data
)
1639 isom_styp_t
*styp
= (isom_styp_t
*)file
->styp_list
.head
->data
;
1640 if( !(styp
->manager
& LSMASH_WRITTEN_BOX
) )
1642 if( isom_write_box( file
->bs
, (isom_box_t
*)styp
) < 0 )
1644 file
->size
+= styp
->size
;
1647 int (*append_sample_func
)( void *, lsmash_sample_t
* ) = NULL
;
1648 void *track_fragment
= NULL
;
1649 if( !fragment
->movie
)
1651 /* Forbid adding a sample into the initial movie if requiring compatibility with Media Segment. */
1652 if( file
->media_segment
)
1654 append_sample_func
= (int (*)( void *, lsmash_sample_t
* ))isom_append_fragment_sample_internal_initial
;
1655 track_fragment
= trak
;
1659 isom_traf_t
*traf
= isom_get_traf( fragment
->movie
, track_ID
);
1662 traf
= isom_add_traf( fragment
->movie
);
1663 if( !isom_add_tfhd( traf
) )
1665 traf
->tfhd
->flags
= ISOM_TF_FLAGS_DURATION_IS_EMPTY
; /* no samples for this track fragment yet */
1666 traf
->tfhd
->track_ID
= trak
->tkhd
->track_ID
;
1667 traf
->cache
= trak
->cache
;
1668 traf
->cache
->fragment
->traf_number
= fragment
->movie
->traf_list
.entry_count
;
1669 if( (traf
->cache
->fragment
->rap_grouping
&& isom_add_sample_grouping( (isom_box_t
*)traf
, ISOM_GROUP_TYPE_RAP
) < 0)
1670 || (traf
->cache
->fragment
->roll_grouping
&& isom_add_sample_grouping( (isom_box_t
*)traf
, ISOM_GROUP_TYPE_ROLL
) < 0) )
1673 else if( !traf
->file
1674 || !traf
->file
->initializer
1675 || !traf
->file
->initializer
->moov
1676 || !traf
->file
->initializer
->moov
->mvex
1680 append_sample_func
= (int (*)( void *, lsmash_sample_t
* ))isom_append_fragment_sample_internal
;
1681 track_fragment
= traf
;
1683 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
, sample
->index
);
1686 if( isom_is_lpcm_audio( sample_entry
) )
1688 uint32_t frame_size
= ((isom_audio_entry_t
*)sample_entry
)->constBytesPerAudioPacket
;
1689 if( sample
->length
== frame_size
)
1690 return append_sample_func( track_fragment
, sample
);
1691 else if( sample
->length
< frame_size
)
1693 /* Append samples splitted into each LPCMFrame. */
1694 uint64_t dts
= sample
->dts
;
1695 uint64_t cts
= sample
->cts
;
1696 for( uint32_t offset
= 0; offset
< sample
->length
; offset
+= frame_size
)
1698 lsmash_sample_t
*lpcm_sample
= lsmash_create_sample( frame_size
);
1701 memcpy( lpcm_sample
->data
, sample
->data
+ offset
, frame_size
);
1702 lpcm_sample
->dts
= dts
++;
1703 lpcm_sample
->cts
= cts
++;
1704 lpcm_sample
->prop
= sample
->prop
;
1705 lpcm_sample
->index
= sample
->index
;
1706 if( append_sample_func( track_fragment
, lpcm_sample
) < 0 )
1708 lsmash_delete_sample( lpcm_sample
);
1712 lsmash_delete_sample( sample
);
1715 return append_sample_func( track_fragment
, sample
);