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 */
29 static isom_sidx_t
*isom_get_sidx( lsmash_file_t
*file
, uint32_t reference_ID
)
31 if( reference_ID
== 0 || !file
)
33 for( lsmash_entry_t
*entry
= file
->sidx_list
.head
; entry
; entry
= entry
->next
)
35 isom_sidx_t
*sidx
= (isom_sidx_t
*)entry
->data
;
38 if( sidx
->reference_ID
== reference_ID
)
44 static int isom_finish_fragment_movie( lsmash_file_t
*file
);
46 /* A movie fragment cannot switch a sample description to another.
47 * So you must call this function before switching sample descriptions. */
48 int lsmash_create_fragment_movie( lsmash_root_t
*root
)
52 lsmash_file_t
*file
= root
->file
;
58 /* Finish and write the current movie fragment before starting a new one. */
59 if( isom_finish_fragment_movie( file
) < 0 )
61 /* Add a new movie fragment if the current one is not present or not written. */
62 if( !file
->fragment
->movie
|| (file
->fragment
->movie
->manager
& LSMASH_WRITTEN_BOX
) )
64 /* We always hold only one movie fragment except for the initial movie (a pair of moov and mdat). */
65 if( file
->fragment
->movie
&& file
->moof_list
.entry_count
!= 1 )
67 isom_moof_t
*moof
= isom_add_moof( file
);
68 if( !isom_add_mfhd( moof
) )
70 file
->fragment
->movie
= moof
;
71 moof
->mfhd
->sequence_number
= ++ file
->fragment
->fragment_count
;
72 if( file
->moof_list
.entry_count
== 1 )
74 /* Remove the previous movie fragment. */
75 if( file
->moof_list
.head
)
76 isom_remove_box_by_itself( file
->moof_list
.head
->data
);
81 static int isom_set_fragment_overall_duration( lsmash_file_t
*file
)
83 /* Get the longest duration of the tracks. */
84 uint64_t longest_duration
= 0;
85 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
87 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
90 || !trak
->cache
->fragment
93 || !trak
->mdia
->mdhd
->timescale
)
98 || !trak
->edts
->elst
->list
)
100 duration
= trak
->cache
->fragment
->largest_cts
+ trak
->cache
->fragment
->last_duration
;
101 duration
= (uint64_t)(((double)duration
/ trak
->mdia
->mdhd
->timescale
) * file
->moov
->mvhd
->timescale
);
106 for( lsmash_entry_t
*elst_entry
= trak
->edts
->elst
->list
->head
; elst_entry
; elst_entry
= elst_entry
->next
)
108 isom_elst_entry_t
*data
= (isom_elst_entry_t
*)elst_entry
->data
;
111 if( data
->segment_duration
== ISOM_EDIT_DURATION_IMPLICIT
)
113 uint64_t segment_duration
= trak
->cache
->fragment
->largest_cts
+ trak
->cache
->fragment
->last_duration
;
114 duration
+= (uint64_t)(((double)segment_duration
/ trak
->mdia
->mdhd
->timescale
) * file
->moov
->mvhd
->timescale
);
117 duration
+= data
->segment_duration
;
120 longest_duration
= LSMASH_MAX( duration
, longest_duration
);
122 isom_mehd_t
*mehd
= file
->moov
->mvex
->mehd
;
123 mehd
->fragment_duration
= longest_duration
;
125 mehd
->manager
&= ~LSMASH_PLACEHOLDER
;
126 isom_update_box_size( mehd
);
127 /* Write Movie Extends Header Box here. */
128 lsmash_bs_t
*bs
= file
->bs
;
129 uint64_t current_pos
= bs
->offset
;
130 lsmash_bs_write_seek( bs
, mehd
->pos
, SEEK_SET
);
131 int ret
= isom_write_box( bs
, (isom_box_t
*)mehd
);
132 lsmash_bs_write_seek( bs
, current_pos
, SEEK_SET
);
136 static int isom_write_fragment_random_access_info( lsmash_file_t
*file
)
138 if( !file
->moov
->mvex
)
140 /* Reconstruct the Movie Fragment Random Access Box.
141 * All 'time' field in the Track Fragment Random Access Boxes shall reflect edit list. */
142 uint32_t movie_timescale
= lsmash_get_movie_timescale( file
->root
);
143 if( movie_timescale
== 0 )
144 return -1; /* Division by zero will occur. */
145 for( lsmash_entry_t
*trex_entry
= file
->moov
->mvex
->trex_list
.head
; trex_entry
; trex_entry
= trex_entry
->next
)
147 isom_trex_t
*trex
= (isom_trex_t
*)trex_entry
->data
;
150 /* Get the edit list of the track associated with the trex->track_ID.
151 * If failed or absent, implicit timeline mapping edit is used, and skip this operation for the track. */
152 isom_trak_t
*trak
= isom_get_trak( file
, trex
->track_ID
);
157 || !trak
->edts
->elst
->list
158 || !trak
->edts
->elst
->list
->head
159 || !trak
->edts
->elst
->list
->head
->data
)
161 isom_elst_t
*elst
= trak
->edts
->elst
;
162 /* Get the Track Fragment Random Access Boxes of the track associated with the trex->track_ID.
163 * If failed or absent, skip reconstructing the Track Fragment Random Access Box of the track. */
164 isom_tfra_t
*tfra
= isom_get_tfra( file
->mfra
, trex
->track_ID
);
167 /* Reconstruct the Track Fragment Random Access Box. */
168 lsmash_entry_t
*edit_entry
= elst
->list
->head
;
169 isom_elst_entry_t
*edit
= edit_entry
->data
;
170 uint64_t edit_offset
= 0; /* units in media timescale */
171 uint32_t media_timescale
= lsmash_get_media_timescale( file
->root
, trex
->track_ID
);
172 for( lsmash_entry_t
*rap_entry
= tfra
->list
->head
; rap_entry
; )
174 isom_tfra_location_time_entry_t
*rap
= (isom_tfra_location_time_entry_t
*)rap_entry
->data
;
177 /* Irregular case. Drop this entry. */
178 lsmash_entry_t
*next
= rap_entry
->next
;
179 lsmash_remove_entry_direct( tfra
->list
, rap_entry
, NULL
);
183 uint64_t composition_time
= rap
->time
;
184 /* Skip edits that doesn't need the current sync sample indicated in the Track Fragment Random Access Box. */
187 uint64_t segment_duration
= edit
->segment_duration
== ISOM_EDIT_DURATION_IMPLICIT
188 ? trak
->cache
->fragment
->largest_cts
+ trak
->cache
->fragment
->last_duration
189 : ((edit
->segment_duration
- 1) / movie_timescale
+ 1) * media_timescale
;
190 if( edit
->media_time
!= ISOM_EDIT_MODE_EMPTY
191 && composition_time
< edit
->media_time
+ segment_duration
)
192 break; /* This Timeline Mapping Edit might require the current sync sample.
193 * Note: this condition doesn't cover all cases.
194 * For instance, matching the both following conditions
195 * 1. A sync sample isn't in the presentation.
196 * 2. The other samples, which precede it in the composition timeline, is in the presentation. */
197 edit_offset
+= segment_duration
;
198 edit_entry
= edit_entry
->next
;
201 /* No more presentation. */
205 edit
= edit_entry
->data
;
209 /* No more presentation.
210 * Drop the rest of sync samples since they are generally absent in the whole presentation.
211 * Though the exceptions are sync samples with earlier composition time, we ignore them. (SAP type 2: TEPT = TDEC = TSAP < TPTF)
212 * To support this exception, we need sorting entries of the list by composition times. */
215 lsmash_entry_t
*next
= rap_entry
->next
;
216 lsmash_remove_entry_direct( tfra
->list
, rap_entry
, NULL
);
221 /* If the sync sample isn't in the presentation,
222 * we pick the earliest presentation time of the current edit as its presentation time. */
223 rap
->time
= edit_offset
;
224 if( composition_time
>= edit
->media_time
)
225 rap
->time
+= composition_time
- edit
->media_time
;
226 rap_entry
= rap_entry
->next
;
228 tfra
->number_of_entry
= tfra
->list
->entry_count
;
230 /* Decide the size of the Movie Fragment Random Access Box. */
231 if( isom_update_box_size( file
->mfra
) == 0 )
233 /* Write the Movie Fragment Random Access Box. */
234 return isom_write_box( file
->bs
, (isom_box_t
*)file
->mfra
);
237 static int isom_update_indexed_material_offset
240 isom_sidx_t
*last_sidx
243 /* Update the size of each Segment Index Box. */
244 for( lsmash_entry_t
*entry
= file
->sidx_list
.head
; entry
; entry
= entry
->next
)
246 isom_sidx_t
*sidx
= (isom_sidx_t
*)entry
->data
;
249 if( isom_update_box_size( sidx
) == 0 )
252 /* first_offset: the sum of the size of subsequent Segment Index Boxes
253 * Be careful about changing the size of them. */
254 last_sidx
->first_offset
= 0;
255 for( lsmash_entry_t
*a_entry
= file
->sidx_list
.head
; a_entry
&& a_entry
->data
!= last_sidx
; a_entry
= a_entry
->next
)
257 isom_sidx_t
*a
= (isom_sidx_t
*)a_entry
->data
;
259 for( lsmash_entry_t
*b_entry
= a_entry
->next
; b_entry
; b_entry
= b_entry
->next
)
261 isom_sidx_t
*b
= (isom_sidx_t
*)b_entry
->data
;
262 a
->first_offset
+= b
->size
;
268 int isom_finish_final_fragment_movie
271 lsmash_adhoc_remux_t
*remux
274 /* Output the final movie fragment. */
275 if( isom_finish_fragment_movie( file
) < 0 )
277 if( file
->bs
->unseekable
)
279 /* Write Segment Index Boxes.
280 * This occurs only when the initial movie has no samples.
281 * We don't consider updating of chunk offsets within initial movie sample table here.
282 * This is reasonable since DASH requires no samples in the initial movie.
283 * This implementation is not suitable for live-streaming.
284 + To support live-streaming, it is good to use daisy-chained index. */
285 uint8_t *buf
[2] = { NULL
, NULL
};
286 if( file
->flags
& LSMASH_FILE_MODE_INDEX
)
288 /* Update the size of each Segment Index Box and establish the offset from the anchor point to the indexed material. */
289 if( isom_update_indexed_material_offset( file
, (isom_sidx_t
*)file
->sidx_list
.tail
->data
) < 0 )
291 /* Get the total size of all Segment Index Boxes. */
292 uint64_t total_sidx_size
= 0;
293 for( lsmash_entry_t
*entry
= file
->sidx_list
.head
; entry
; entry
= entry
->next
)
295 isom_sidx_t
*sidx
= (isom_sidx_t
*)entry
->data
;
298 total_sidx_size
+= sidx
->size
;
300 /* buffer size must be at least total_sidx_size * 2 */
301 size_t buffer_size
= total_sidx_size
* 2;
302 if( remux
&& remux
->buffer_size
> buffer_size
)
303 buffer_size
= remux
->buffer_size
;
304 /* Split to 2 buffers. */
305 if( (buf
[0] = (uint8_t *)lsmash_malloc( buffer_size
)) == NULL
)
307 size_t size
= buffer_size
/ 2;
308 buf
[1] = buf
[0] + size
;
309 /* Seek to the beginning of the first Movie Fragment Box. */
310 lsmash_bs_t
*bs
= file
->bs
;
311 if( lsmash_bs_write_seek( bs
, file
->fragment
->first_moof_pos
, SEEK_SET
) < 0 )
313 size_t read_num
= size
;
314 lsmash_bs_read_data( bs
, buf
[0], &read_num
);
315 uint64_t read_pos
= bs
->offset
;
317 if( lsmash_bs_write_seek( bs
, file
->fragment
->first_moof_pos
, SEEK_SET
) < 0 )
319 for( lsmash_entry_t
*entry
= file
->sidx_list
.head
; entry
; entry
= entry
->next
)
321 isom_sidx_t
*sidx
= (isom_sidx_t
*)entry
->data
;
324 if( isom_write_box( file
->bs
, (isom_box_t
*)sidx
) < 0 )
327 uint64_t write_pos
= bs
->offset
;
328 uint64_t total
= file
->size
+ total_sidx_size
;
329 if( isom_rearrange_boxes( file
, remux
, buf
, read_num
, size
, read_pos
, write_pos
, total
) < 0 )
331 file
->size
+= total_sidx_size
;
332 lsmash_freep( &buf
[0] );
333 /* Update moof_offset. */
335 for( lsmash_entry_t
*entry
= file
->mfra
->tfra_list
.head
; entry
; entry
= entry
->next
)
337 isom_tfra_t
*tfra
= (isom_tfra_t
*)entry
->data
;
340 for( lsmash_entry_t
*rap_entry
= tfra
->list
->head
; rap_entry
; rap_entry
= rap_entry
->next
)
342 isom_tfra_location_time_entry_t
*rap
= (isom_tfra_location_time_entry_t
*)rap_entry
->data
;
345 rap
->moof_offset
+= total_sidx_size
;
349 /* Write the overall random access information at the tail of the movie. */
350 if( isom_write_fragment_random_access_info( file
) )
352 /* Set overall duration of the movie. */
353 return isom_set_fragment_overall_duration( file
);
355 lsmash_free( buf
[0] );
359 #define GET_MOST_USED( box_name, index, flag_name ) \
360 if( most_used[index] < stats.flag_name[i] ) \
362 most_used[index] = stats.flag_name[i]; \
363 box_name->default_sample_flags.flag_name = i; \
366 static int isom_create_fragment_overall_default_settings( lsmash_file_t
*file
)
368 if( !isom_add_mvex( file
->moov
) )
370 if( !file
->bs
->unseekable
)
372 if( !isom_add_mehd( file
->moov
->mvex
) )
374 file
->moov
->mvex
->mehd
->manager
|= LSMASH_PLACEHOLDER
;
376 for( lsmash_entry_t
*trak_entry
= file
->moov
->trak_list
.head
; trak_entry
; trak_entry
= trak_entry
->next
)
378 isom_trak_t
*trak
= (isom_trak_t
*)trak_entry
->data
;
384 || !trak
->mdia
->minf
->stbl
)
386 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
387 if( !stbl
->stts
|| !stbl
->stts
->list
389 || (stbl
->stts
->list
->tail
&& !stbl
->stts
->list
->tail
->data
)
390 || (stbl
->stsz
->list
&& stbl
->stsz
->list
->head
&& !stbl
->stsz
->list
->head
->data
) )
392 isom_trex_t
*trex
= isom_add_trex( file
->moov
->mvex
);
395 trex
->track_ID
= trak
->tkhd
->track_ID
;
396 /* Set up defaults. */
397 trex
->default_sample_description_index
= trak
->cache
->chunk
.sample_description_index
398 ? trak
->cache
->chunk
.sample_description_index
400 trex
->default_sample_duration
= stbl
->stts
->list
->tail
401 ? ((isom_stts_entry_t
*)stbl
->stts
->list
->tail
->data
)->sample_delta
403 trex
->default_sample_size
= !stbl
->stsz
->list
404 ? stbl
->stsz
->sample_size
: stbl
->stsz
->list
->head
405 ? ((isom_stsz_entry_t
*)stbl
->stsz
->list
->head
->data
)->entry_size
: 0;
407 && stbl
->sdtp
->list
)
409 struct sample_flags_stats_t
411 uint32_t is_leading
[4];
412 uint32_t sample_depends_on
[4];
413 uint32_t sample_is_depended_on
[4];
414 uint32_t sample_has_redundancy
[4];
415 } stats
= { { 0 }, { 0 }, { 0 }, { 0 } };
416 for( lsmash_entry_t
*sdtp_entry
= stbl
->sdtp
->list
->head
; sdtp_entry
; sdtp_entry
= sdtp_entry
->next
)
418 isom_sdtp_entry_t
*data
= (isom_sdtp_entry_t
*)sdtp_entry
->data
;
421 ++ stats
.is_leading
[ data
->is_leading
];
422 ++ stats
.sample_depends_on
[ data
->sample_depends_on
];
423 ++ stats
.sample_is_depended_on
[ data
->sample_is_depended_on
];
424 ++ stats
.sample_has_redundancy
[ data
->sample_has_redundancy
];
426 uint32_t most_used
[4] = { 0, 0, 0, 0 };
427 for( int i
= 0; i
< 4; i
++ )
429 GET_MOST_USED( trex
, 0, is_leading
);
430 GET_MOST_USED( trex
, 1, sample_depends_on
);
431 GET_MOST_USED( trex
, 2, sample_is_depended_on
);
432 GET_MOST_USED( trex
, 3, sample_has_redundancy
);
435 trex
->default_sample_flags
.sample_is_non_sync_sample
= !trak
->cache
->all_sync
;
440 static int isom_prepare_random_access_info( lsmash_file_t
*file
)
442 if( file
->bs
->unseekable
)
444 if( !isom_add_mfra( file
)
445 || !isom_add_mfro( file
->mfra
) )
450 static int isom_output_fragment_media_data( lsmash_file_t
*file
)
452 isom_fragment_manager_t
*fragment
= file
->fragment
;
453 /* If there is no available Media Data Box to write samples, add and write a new one. */
454 if( fragment
->sample_count
)
456 if( !file
->mdat
&& !isom_add_mdat( file
) )
458 file
->mdat
->manager
&= ~(LSMASH_INCOMPLETE_BOX
| LSMASH_WRITTEN_BOX
);
459 if( isom_write_box( file
->bs
, (isom_box_t
*)file
->mdat
) < 0 )
461 file
->size
+= file
->mdat
->size
;
462 file
->mdat
->size
= 0;
463 file
->mdat
->media_size
= 0;
465 lsmash_remove_entries( fragment
->pool
, isom_remove_sample_pool
);
466 fragment
->pool_size
= 0;
467 fragment
->sample_count
= 0;
471 static int isom_finish_fragment_initial_movie( lsmash_file_t
*file
)
475 isom_moov_t
*moov
= file
->moov
;
476 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
478 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
485 || !trak
->mdia
->minf
->stbl
486 || isom_complement_data_reference( trak
->mdia
->minf
) < 0 )
488 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
489 if( isom_get_sample_count( trak
) )
491 /* Add stss box if any samples aren't sync sample. */
492 if( !trak
->cache
->all_sync
&& !stbl
->stss
&& !isom_add_stss( stbl
) )
494 if( isom_update_tkhd_duration( trak
) < 0 )
498 trak
->tkhd
->duration
= 0;
499 if( isom_update_bitrate_description( trak
->mdia
) < 0 )
501 /* Complete the last sample groups within tracks in the initial movie. */
502 if( trak
->cache
->rap
)
504 isom_sgpd_t
*sgpd
= isom_get_sample_group_description( stbl
, ISOM_GROUP_TYPE_RAP
);
505 if( !sgpd
|| isom_rap_grouping_established( trak
->cache
->rap
, 1, sgpd
, 0 ) < 0 )
507 lsmash_freep( &trak
->cache
->rap
);
509 if( trak
->cache
->roll
.pool
)
511 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group( &stbl
->sbgp_list
);
512 if( !sbgp
|| isom_all_recovery_completed( sbgp
, trak
->cache
->roll
.pool
) < 0 )
516 if( file
->mp4_version1
== 1 && isom_setup_iods( moov
) < 0 )
518 if( isom_create_fragment_overall_default_settings( file
) < 0
519 || isom_prepare_random_access_info ( file
) < 0
520 || isom_establish_movie ( file
) < 0 )
522 /* stco->co64 conversion, depending on last chunk's offset */
523 uint64_t meta_size
= file
->meta
? file
->meta
->size
: 0;
524 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; )
526 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
527 isom_stco_t
*stco
= trak
->mdia
->minf
->stbl
->stco
;
528 if( !stco
->list
->tail
/* no samples */
529 || stco
->large_presentation
530 || (((isom_stco_entry_t
*)stco
->list
->tail
->data
)->chunk_offset
+ moov
->size
+ meta_size
) <= UINT32_MAX
)
533 continue; /* no need to convert stco into co64 */
535 /* stco->co64 conversion */
536 if( isom_convert_stco_to_co64( trak
->mdia
->minf
->stbl
) < 0
537 || isom_update_box_size( moov
) == 0 )
539 entry
= moov
->trak_list
.head
; /* whenever any conversion, re-check all traks */
541 /* Now, the amount of offset is fixed. Apply that to stco/co64. */
542 uint64_t preceding_size
= moov
->size
+ meta_size
;
543 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
545 isom_stco_t
*stco
= ((isom_trak_t
*)entry
->data
)->mdia
->minf
->stbl
->stco
;
546 if( stco
->large_presentation
)
547 for( lsmash_entry_t
*co64_entry
= stco
->list
->head
; co64_entry
; co64_entry
= co64_entry
->next
)
548 ((isom_co64_entry_t
*)co64_entry
->data
)->chunk_offset
+= preceding_size
;
550 for( lsmash_entry_t
*stco_entry
= stco
->list
->head
; stco_entry
; stco_entry
= stco_entry
->next
)
551 ((isom_stco_entry_t
*)stco_entry
->data
)->chunk_offset
+= preceding_size
;
553 /* Write File Type Box here if it was not written yet. */
554 if( file
->ftyp
&& !(file
->ftyp
->manager
& LSMASH_WRITTEN_BOX
) )
556 if( isom_write_box( file
->bs
, (isom_box_t
*)file
->ftyp
) )
558 file
->size
+= file
->ftyp
->size
;
560 /* Write Movie Box. */
561 if( isom_write_box( file
->bs
, (isom_box_t
*)file
->moov
) < 0
562 || isom_write_box( file
->bs
, (isom_box_t
*)file
->meta
) < 0 )
564 file
->size
+= preceding_size
;
565 /* Output samples. */
566 if( isom_output_fragment_media_data( file
) < 0 )
568 /* Revert the number of samples in tracks to 0. */
569 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
571 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
572 if( trak
->cache
->fragment
)
573 trak
->cache
->fragment
->sample_count
= 0;
578 /* Return 1 if there is diffrence, otherwise return 0. */
579 static int isom_compare_sample_flags( isom_sample_flags_t
*a
, isom_sample_flags_t
*b
)
581 return (a
->reserved
!= b
->reserved
)
582 || (a
->is_leading
!= b
->is_leading
)
583 || (a
->sample_depends_on
!= b
->sample_depends_on
)
584 || (a
->sample_is_depended_on
!= b
->sample_is_depended_on
)
585 || (a
->sample_has_redundancy
!= b
->sample_has_redundancy
)
586 || (a
->sample_padding_value
!= b
->sample_padding_value
)
587 || (a
->sample_is_non_sync_sample
!= b
->sample_is_non_sync_sample
)
588 || (a
->sample_degradation_priority
!= b
->sample_degradation_priority
);
591 static int isom_finish_fragment_movie( lsmash_file_t
*file
)
595 || !file
->fragment
->pool
)
597 isom_moof_t
*moof
= file
->fragment
->movie
;
599 return isom_finish_fragment_initial_movie( file
);
600 /* Don't write the current movie fragment if containing no track fragments.
601 * This is a requirement of DASH Media Segment. */
602 if( !moof
->traf_list
.head
603 || !moof
->traf_list
.head
->data
)
605 /* Calculate appropriate default_sample_flags of each Track Fragment Header Box.
606 * And check whether that default_sample_flags is useful or not. */
607 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
609 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
614 || !traf
->file
->moov
->mvex
)
616 isom_tfhd_t
*tfhd
= traf
->tfhd
;
617 isom_trex_t
*trex
= isom_get_trex( file
->moov
->mvex
, tfhd
->track_ID
);
620 struct sample_flags_stats_t
622 uint32_t is_leading
[4];
623 uint32_t sample_depends_on
[4];
624 uint32_t sample_is_depended_on
[4];
625 uint32_t sample_has_redundancy
[4];
626 uint32_t sample_is_non_sync_sample
[2];
627 } stats
= { { 0 }, { 0 }, { 0 }, { 0 }, { 0 } };
628 for( lsmash_entry_t
*trun_entry
= traf
->trun_list
.head
; trun_entry
; trun_entry
= trun_entry
->next
)
630 isom_trun_t
*trun
= (isom_trun_t
*)trun_entry
->data
;
631 if( !trun
|| trun
->sample_count
== 0 )
633 isom_sample_flags_t
*sample_flags
;
634 if( trun
->flags
& ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT
)
636 if( !trun
->optional
)
638 for( lsmash_entry_t
*optional_entry
= trun
->optional
->head
; optional_entry
; optional_entry
= optional_entry
->next
)
640 isom_trun_optional_row_t
*row
= (isom_trun_optional_row_t
*)optional_entry
->data
;
643 sample_flags
= &row
->sample_flags
;
644 ++ stats
.is_leading
[ sample_flags
->is_leading
];
645 ++ stats
.sample_depends_on
[ sample_flags
->sample_depends_on
];
646 ++ stats
.sample_is_depended_on
[ sample_flags
->sample_is_depended_on
];
647 ++ stats
.sample_has_redundancy
[ sample_flags
->sample_has_redundancy
];
648 ++ stats
.sample_is_non_sync_sample
[ sample_flags
->sample_is_non_sync_sample
];
653 sample_flags
= &tfhd
->default_sample_flags
;
654 stats
.is_leading
[ sample_flags
->is_leading
] += trun
->sample_count
;
655 stats
.sample_depends_on
[ sample_flags
->sample_depends_on
] += trun
->sample_count
;
656 stats
.sample_is_depended_on
[ sample_flags
->sample_is_depended_on
] += trun
->sample_count
;
657 stats
.sample_has_redundancy
[ sample_flags
->sample_has_redundancy
] += trun
->sample_count
;
658 stats
.sample_is_non_sync_sample
[ sample_flags
->sample_is_non_sync_sample
] += trun
->sample_count
;
661 uint32_t most_used
[5] = { 0, 0, 0, 0, 0 };
662 for( int i
= 0; i
< 4; i
++ )
664 GET_MOST_USED( tfhd
, 0, is_leading
);
665 GET_MOST_USED( tfhd
, 1, sample_depends_on
);
666 GET_MOST_USED( tfhd
, 2, sample_is_depended_on
);
667 GET_MOST_USED( tfhd
, 3, sample_has_redundancy
);
669 GET_MOST_USED( tfhd
, 4, sample_is_non_sync_sample
);
671 int useful_default_sample_duration
= 0;
672 int useful_default_sample_size
= 0;
673 for( lsmash_entry_t
*trun_entry
= traf
->trun_list
.head
; trun_entry
; trun_entry
= trun_entry
->next
)
675 isom_trun_t
*trun
= (isom_trun_t
*)trun_entry
->data
;
676 if( !(trun
->flags
& ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT
) )
677 useful_default_sample_duration
= 1;
678 if( !(trun
->flags
& ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT
) )
679 useful_default_sample_size
= 1;
680 int useful_first_sample_flags
= 1;
681 int useful_default_sample_flags
= 1;
682 if( trun
->sample_count
== 1 )
684 /* It is enough to check only if first_sample_flags equals default_sample_flags or not.
685 * If it is equal, just use default_sample_flags.
686 * If not, just use first_sample_flags of this run. */
687 if( !isom_compare_sample_flags( &trun
->first_sample_flags
, &tfhd
->default_sample_flags
) )
688 useful_first_sample_flags
= 0;
690 else if( trun
->optional
691 && trun
->optional
->head
)
693 lsmash_entry_t
*optional_entry
= trun
->optional
->head
->next
;
694 isom_trun_optional_row_t
*row
= (isom_trun_optional_row_t
*)optional_entry
->data
;
695 isom_sample_flags_t representative_sample_flags
= row
->sample_flags
;
696 if( isom_compare_sample_flags( &tfhd
->default_sample_flags
, &representative_sample_flags
) )
697 useful_default_sample_flags
= 0;
698 if( !isom_compare_sample_flags( &trun
->first_sample_flags
, &representative_sample_flags
) )
699 useful_first_sample_flags
= 0;
700 if( useful_default_sample_flags
)
701 for( optional_entry
= optional_entry
->next
; optional_entry
; optional_entry
= optional_entry
->next
)
703 row
= (isom_trun_optional_row_t
*)optional_entry
->data
;
704 if( isom_compare_sample_flags( &representative_sample_flags
, &row
->sample_flags
) )
706 useful_default_sample_flags
= 0;
711 if( useful_default_sample_flags
)
713 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT
;
714 trun
->flags
&= ~ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT
;
718 useful_first_sample_flags
= 0;
719 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT
;
721 if( useful_first_sample_flags
)
722 trun
->flags
|= ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT
;
724 if( useful_default_sample_duration
&& tfhd
->default_sample_duration
!= trex
->default_sample_duration
)
725 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT
;
727 tfhd
->default_sample_duration
= trex
->default_sample_duration
; /* This might be redundant, but is to be more natural. */
728 if( useful_default_sample_size
&& tfhd
->default_sample_size
!= trex
->default_sample_size
)
729 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT
;
731 tfhd
->default_sample_size
= trex
->default_sample_size
; /* This might be redundant, but is to be more natural. */
732 if( !(tfhd
->flags
& ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT
) )
733 tfhd
->default_sample_flags
= trex
->default_sample_flags
; /* This might be redundant, but is to be more natural. */
734 else if( !isom_compare_sample_flags( &tfhd
->default_sample_flags
, &trex
->default_sample_flags
) )
735 tfhd
->flags
&= ~ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT
;
737 /* Complete the last sample groups in the previous track fragments. */
738 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
740 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
741 if( traf
->cache
->rap
)
743 isom_sgpd_t
*sgpd
= isom_get_fragment_sample_group_description( traf
, ISOM_GROUP_TYPE_RAP
);
744 if( !sgpd
|| isom_rap_grouping_established( traf
->cache
->rap
, 1, sgpd
, 1 ) < 0 )
746 lsmash_freep( &traf
->cache
->rap
);
748 if( traf
->cache
->roll
.pool
)
750 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group( &traf
->sbgp_list
);
751 if( !sbgp
|| isom_all_recovery_completed( sbgp
, traf
->cache
->roll
.pool
) < 0 )
755 /* Establish Movie Fragment Box.
756 * We write exactly one Media Data Box starting immediately after the corresponding Movie Fragment Box. */
757 if( file
->allow_moof_base
)
759 /* In this branch, we use default-base-is-moof flag, which indicates implicit base_data_offsets originate in the
760 * first byte of each enclosing Movie Fragment Box.
761 * 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
762 * the type field of it as the data_offset of the first track run like the following.
764 * _____________ _ offset := 0
773 * |___|_________|_ offset := the size of the Movie Fragment Box
779 * | a |_________|_ offset := the data_offset of the first track run
782 * |___|_________|_ offset := the size of a subsegment containing exactly one movie fragment
784 * For a pair of one Movie Fragment Box and one Media Data Box, placed in this order, implicit base_data_offsets
785 * indicated by the absence of both base-data-offset-present and default-base-is-moof are somewhat complicated
786 * since the implicit base_data_offset of the current track fragment is defined by the end of the data of the
787 * previous track fragment and the data_offset of the track runs could be negative value because of interleaving
788 * track runs or something other reasons.
789 * In contrast, implicit base_data_offsets indicated by default-base-is-moof are simple since the base_data_offset
790 * of each track fragment is always constant for that pair and has no dependency on other track fragments.
792 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
794 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
795 traf
->tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_BASE_IS_MOOF
;
796 traf
->tfhd
->base_data_offset
= file
->size
; /* not written actually though */
797 for( lsmash_entry_t
*trun_entry
= traf
->trun_list
.head
; trun_entry
; trun_entry
= trun_entry
->next
)
799 /* Here, data_offset is always greater than zero. */
800 isom_trun_t
*trun
= trun_entry
->data
;
801 trun
->flags
|= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT
;
804 /* Consider the update of tr_flags here. */
805 if( isom_update_box_size( moof
) == 0 )
807 /* Now, we can calculate offsets in the current movie fragment, so do it. */
808 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
810 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
811 for( lsmash_entry_t
*trun_entry
= traf
->trun_list
.head
; trun_entry
; trun_entry
= trun_entry
->next
)
813 isom_trun_t
*trun
= trun_entry
->data
;
814 trun
->data_offset
+= moof
->size
+ ISOM_BASEBOX_COMMON_SIZE
;
820 /* In this branch, we use explicit base_data_offset. */
821 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
823 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
824 traf
->tfhd
->flags
|= ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT
;
826 /* Consider the update of tf_flags here. */
827 if( isom_update_box_size( moof
) == 0 )
829 /* Now, we can calculate offsets in the current movie fragment, so do it. */
830 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
832 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
833 traf
->tfhd
->base_data_offset
= file
->size
+ moof
->size
+ ISOM_BASEBOX_COMMON_SIZE
;
836 /* Write Movie Fragment Box and its children. */
837 moof
->pos
= file
->size
;
838 if( isom_write_box( file
->bs
, (isom_box_t
*)moof
) )
840 if( file
->fragment
->fragment_count
== 1 )
841 file
->fragment
->first_moof_pos
= moof
->pos
;
842 file
->size
+= moof
->size
;
843 /* Output samples. */
844 if( isom_output_fragment_media_data( file
) < 0 )
846 /* Revert the number of samples in track fragments to 0. */
847 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
849 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
850 if( traf
->cache
->fragment
)
851 traf
->cache
->fragment
->sample_count
= 0;
853 if( !(file
->flags
& LSMASH_FILE_MODE_INDEX
) || file
->max_isom_version
< 6 )
855 /* Make the index of this subsegment. */
856 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
858 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
859 isom_tfhd_t
*tfhd
= traf
->tfhd
;
860 isom_fragment_t
*track_fragment
= traf
->cache
->fragment
;
861 isom_subsegment_t
*subsegment
= &track_fragment
->subsegment
;
862 isom_sidx_t
*sidx
= isom_get_sidx( file
, tfhd
->track_ID
);
863 isom_trak_t
*trak
= isom_get_trak( file
, tfhd
->track_ID
);
866 || !trak
->mdia
->mdhd
)
868 assert( traf
->tfdt
);
871 sidx
= isom_add_sidx( file
);
874 sidx
->reference_ID
= tfhd
->track_ID
;
875 sidx
->timescale
= trak
->mdia
->mdhd
->timescale
;
877 sidx
->reference_count
= 0;
878 if( isom_update_indexed_material_offset( file
, sidx
) < 0 )
881 /* One pair of a Movie Fragment Box with an associated Media Box per subsegment. */
882 isom_sidx_referenced_item_t
*data
= lsmash_malloc( sizeof(isom_sidx_referenced_item_t
) );
885 if( lsmash_add_entry( sidx
->list
, data
) < 0 )
890 sidx
->reference_count
= sidx
->list
->entry_count
;
891 data
->reference_type
= 0; /* media */
892 data
->reference_size
= file
->size
- moof
->pos
;
898 uint64_t composition_duration
= subsegment
->largest_cts
- subsegment
->smallest_cts
+ track_fragment
->last_duration
;
899 int subsegment_in_presentation
; /* If set to 1, TEPT is available. */
900 int first_rp_in_presentation
; /* If set to 1, both TSAP and TDEC are available. */
901 int first_sample_in_presentation
; /* If set to 1, TPTF is available. */
902 if( trak
->edts
&& trak
->edts
->elst
&& trak
->edts
->elst
->list
)
904 /**-- Explicit edits --**/
905 const isom_elst_t
*elst
= trak
->edts
->elst
;
906 const isom_elst_entry_t
*edit
= NULL
;
907 uint32_t movie_timescale
= file
->moov
->mvhd
->timescale
;
908 uint64_t pts
= subsegment
->segment_duration
;
909 /* This initialization is redundant since these are unused when uninitialized
910 * and are initialized always in used cases, but unclever compilers may
911 * complain that these variables may be uninitialized. */
917 subsegment_in_presentation
= 0;
918 first_rp_in_presentation
= 0;
919 first_sample_in_presentation
= 0;
920 for( lsmash_entry_t
*elst_entry
= elst
->list
->head
; elst_entry
; elst_entry
= elst_entry
->next
)
922 edit
= (isom_elst_entry_t
*)elst_entry
->data
;
925 uint64_t edit_end_pts
;
926 uint64_t edit_end_cts
;
927 if( edit
->segment_duration
== ISOM_EDIT_DURATION_IMPLICIT
928 || (elst
->version
== 0 && edit
->segment_duration
== ISOM_EDIT_DURATION_UNKNOWN32
)
929 || (elst
->version
== 1 && edit
->segment_duration
== ISOM_EDIT_DURATION_UNKNOWN64
) )
931 edit_end_cts
= UINT64_MAX
;
932 edit_end_pts
= UINT64_MAX
;
936 if( edit
->segment_duration
)
938 double segment_duration
= edit
->segment_duration
* ((double)sidx
->timescale
/ movie_timescale
);
939 edit_end_cts
= edit
->media_time
+ (uint64_t)(segment_duration
* ((double)edit
->media_rate
/ (1 << 16)));
940 edit_end_pts
= pts
+ (uint64_t)segment_duration
;
944 uint64_t segment_duration
= composition_duration
;
945 if( edit
->media_time
> subsegment
->smallest_cts
)
947 if( subsegment
->largest_cts
+ track_fragment
->last_duration
> edit
->media_time
)
948 segment_duration
-= edit
->media_time
- subsegment
->smallest_cts
;
950 segment_duration
= 0;
952 edit_end_cts
= edit
->media_time
+ (uint64_t)(segment_duration
* ((double)edit
->media_rate
/ (1 << 16)));
953 edit_end_pts
= pts
+ (uint64_t)segment_duration
;
956 if( edit
->media_time
== ISOM_EDIT_MODE_EMPTY
)
961 if( (subsegment
->smallest_cts
>= edit
->media_time
&& subsegment
->smallest_cts
< edit_end_cts
)
962 || (subsegment
->largest_cts
>= edit
->media_time
&& subsegment
->largest_cts
< edit_end_cts
) )
964 /* This subsegment is present in this edit. */
965 double rate
= (double)edit
->media_rate
/ (1 << 16);
966 uint64_t start_time
= LSMASH_MAX( subsegment
->smallest_cts
, edit
->media_time
);
967 if( sidx
->reference_count
== 1 )
968 sidx
->earliest_presentation_time
= pts
;
969 if( subsegment_in_presentation
== 0 )
971 subsegment_in_presentation
= 1;
972 if( subsegment
->smallest_cts
>= edit
->media_time
)
973 TEPT
= pts
+ (uint64_t)((subsegment
->smallest_cts
- start_time
) / rate
);
977 if( first_rp_in_presentation
== 0
978 && ((subsegment
->first_ed_cts
>= edit
->media_time
&& subsegment
->first_ed_cts
< edit_end_cts
)
979 || (subsegment
->first_rp_cts
>= edit
->media_time
&& subsegment
->first_rp_cts
< edit_end_cts
)) )
981 /* FIXME: to distinguish TSAP and TDEC, need something to indicate incorrectly decodable sample. */
982 first_rp_in_presentation
= 1;
983 if( subsegment
->first_ed_cts
>= edit
->media_time
&& subsegment
->first_ed_cts
< edit_end_cts
)
984 TSAP
= pts
+ (uint64_t)((subsegment
->first_ed_cts
- start_time
) / rate
);
989 if( first_sample_in_presentation
== 0
990 && subsegment
->first_cts
>= edit
->media_time
&& subsegment
->first_cts
< edit_end_cts
)
992 first_sample_in_presentation
= 1;
993 TPTF
= pts
+ (uint64_t)((subsegment
->first_cts
- start_time
) / rate
);
995 uint64_t subsegment_end_pts
= pts
+ (uint64_t)(composition_duration
/ rate
);
996 pts
= LSMASH_MIN( edit_end_pts
, subsegment_end_pts
);
997 /* Update subsegment_duration. */
998 data
->subsegment_duration
= pts
- subsegment
->segment_duration
;
1001 /* This subsegment is not present in this edit. */
1007 /**-- Implicit edit --**/
1008 if( sidx
->reference_count
== 1 )
1009 sidx
->earliest_presentation_time
= subsegment
->smallest_cts
;
1010 data
->subsegment_duration
= composition_duration
;
1011 /* FIXME: to distinguish TSAP and TDEC, need something to indicate incorrectly decodable sample. */
1012 TSAP
= subsegment
->first_rp_cts
;
1013 TDEC
= subsegment
->first_rp_cts
;
1014 TEPT
= subsegment
->smallest_cts
;
1015 TPTF
= subsegment
->first_cts
;
1016 subsegment_in_presentation
= 1;
1017 first_rp_in_presentation
= 1;
1018 first_sample_in_presentation
= 1;
1020 if( subsegment
->first_ra_flags
== ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
1021 || subsegment
->first_ra_number
== 0
1022 || subsegment
->first_rp_number
== 0
1023 || subsegment_in_presentation
== 0
1024 || first_rp_in_presentation
== 0 )
1026 /* No SAP in this subsegment. */
1027 data
->starts_with_SAP
= 0;
1029 data
->SAP_delta_time
= 0;
1033 data
->starts_with_SAP
= (subsegment
->first_ra_number
== 1);
1035 data
->SAP_delta_time
= TSAP
- TEPT
;
1036 /* Decide SAP_type. */
1037 if( first_sample_in_presentation
)
1039 if( TEPT
== TDEC
&& TDEC
== TSAP
&& TSAP
== TPTF
)
1041 else if( TEPT
== TDEC
&& TDEC
== TSAP
&& TSAP
< TPTF
)
1043 else if( TEPT
< TDEC
&& TDEC
== TSAP
&& TSAP
<= TPTF
)
1045 else if( TEPT
<= TPTF
&& TPTF
< TDEC
&& TDEC
== TSAP
)
1048 if( data
->SAP_type
== 0 )
1050 if( TEPT
== TDEC
&& TDEC
< TSAP
)
1052 else if( TEPT
< TDEC
&& TDEC
< TSAP
)
1056 subsegment
->segment_duration
+= data
->subsegment_duration
;
1057 subsegment
->first_ed_cts
= UINT64_MAX
;
1058 subsegment
->first_rp_cts
= UINT64_MAX
;
1059 subsegment
->first_rp_number
= 0;
1060 subsegment
->first_ra_number
= 0;
1061 subsegment
->first_ra_flags
= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
;
1062 subsegment
->decodable
= 0;
1067 #undef GET_MOST_USED
1069 static isom_trun_optional_row_t
*isom_request_trun_optional_row( isom_trun_t
*trun
, isom_tfhd_t
*tfhd
, uint32_t sample_number
)
1071 isom_trun_optional_row_t
*row
= NULL
;
1072 if( !trun
->optional
)
1074 trun
->optional
= lsmash_create_entry_list();
1075 if( !trun
->optional
)
1078 if( trun
->optional
->entry_count
< sample_number
)
1080 while( trun
->optional
->entry_count
< sample_number
)
1082 row
= lsmash_malloc( sizeof(isom_trun_optional_row_t
) );
1085 /* Copy from default. */
1086 row
->sample_duration
= tfhd
->default_sample_duration
;
1087 row
->sample_size
= tfhd
->default_sample_size
;
1088 row
->sample_flags
= tfhd
->default_sample_flags
;
1089 row
->sample_composition_time_offset
= 0;
1090 if( lsmash_add_entry( trun
->optional
, row
) )
1099 for( lsmash_entry_t
*entry
= trun
->optional
->head
; entry
; entry
= entry
->next
)
1101 row
= (isom_trun_optional_row_t
*)entry
->data
;
1104 if( ++i
== sample_number
)
1110 int lsmash_create_fragment_empty_duration( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t duration
)
1114 lsmash_file_t
*file
= root
->file
;
1117 || !file
->fragment
->movie
1120 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1124 isom_trex_t
*trex
= isom_get_trex( file
->moov
->mvex
, track_ID
);
1127 isom_moof_t
*moof
= file
->fragment
->movie
;
1128 isom_traf_t
*traf
= isom_get_traf( moof
, track_ID
);
1131 traf
= isom_add_traf( moof
);
1132 if( !isom_add_tfhd( traf
) )
1134 isom_tfhd_t
*tfhd
= traf
->tfhd
;
1135 tfhd
->flags
= ISOM_TF_FLAGS_DURATION_IS_EMPTY
; /* no samples for this track fragment yet */
1136 tfhd
->track_ID
= trak
->tkhd
->track_ID
;
1137 tfhd
->default_sample_duration
= duration
;
1138 if( duration
!= trex
->default_sample_duration
)
1139 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT
;
1140 traf
->cache
= trak
->cache
;
1141 traf
->cache
->fragment
->traf_number
= moof
->traf_list
.entry_count
;
1142 traf
->cache
->fragment
->last_duration
+= duration
; /* The duration of the last sample includes this empty-duration. */
1146 int isom_set_fragment_last_duration
1149 uint32_t last_duration
1152 isom_tfhd_t
*tfhd
= traf
->tfhd
;
1153 if( !traf
->trun_list
.tail
1154 || !traf
->trun_list
.tail
->data
)
1156 /* There are no track runs in this track fragment, so it is a empty-duration. */
1157 isom_trex_t
*trex
= isom_get_trex( traf
->file
->moov
->mvex
, tfhd
->track_ID
);
1160 tfhd
->flags
|= ISOM_TF_FLAGS_DURATION_IS_EMPTY
;
1161 if( last_duration
!= trex
->default_sample_duration
)
1162 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT
;
1163 tfhd
->default_sample_duration
= last_duration
;
1164 traf
->cache
->fragment
->last_duration
= last_duration
;
1167 /* Update the last sample_duration if needed. */
1168 isom_trun_t
*trun
= (isom_trun_t
*)traf
->trun_list
.tail
->data
;
1169 if( trun
->sample_count
== 1
1170 && traf
->trun_list
.entry_count
== 1 )
1172 isom_trex_t
*trex
= isom_get_trex( traf
->file
->moov
->mvex
, tfhd
->track_ID
);
1175 if( last_duration
!= trex
->default_sample_duration
)
1176 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT
;
1177 tfhd
->default_sample_duration
= last_duration
;
1179 else if( last_duration
!= tfhd
->default_sample_duration
)
1180 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT
;
1183 isom_trun_optional_row_t
*row
= isom_request_trun_optional_row( trun
, tfhd
, trun
->sample_count
);
1186 row
->sample_duration
= last_duration
;
1188 traf
->cache
->fragment
->last_duration
= last_duration
;
1192 int isom_append_fragment_track_run
1194 lsmash_file_t
*file
,
1198 if( !chunk
->pool
|| chunk
->pool
->size
== 0 )
1200 isom_fragment_manager_t
*fragment
= file
->fragment
;
1201 /* Move data in the pool of the current track fragment to the pool of the current movie fragment.
1202 * Empty the pool of current track. We don't delete data of samples here. */
1203 if( lsmash_add_entry( fragment
->pool
, chunk
->pool
) < 0 )
1205 fragment
->sample_count
+= chunk
->pool
->sample_count
;
1206 fragment
->pool_size
+= chunk
->pool
->size
;
1207 chunk
->pool
= isom_create_sample_pool( chunk
->pool
->size
);
1208 return chunk
->pool
? 0 : -1;
1211 static int isom_output_fragment_cache( isom_traf_t
*traf
)
1213 isom_cache_t
*cache
= traf
->cache
;
1214 if( isom_append_fragment_track_run( traf
->file
, &cache
->chunk
) < 0 )
1216 for( lsmash_entry_t
*entry
= traf
->sgpd_list
.head
; entry
; entry
= entry
->next
)
1218 isom_sgpd_t
*sgpd
= (isom_sgpd_t
*)entry
->data
;
1221 switch( sgpd
->grouping_type
)
1223 case ISOM_GROUP_TYPE_RAP
:
1225 isom_rap_group_t
*group
= cache
->rap
;
1228 if( traf
->file
->fragment
)
1233 if( !group
->random_access
)
1235 group
->random_access
->num_leading_samples_known
= 1;
1238 case ISOM_GROUP_TYPE_ROLL
:
1239 case ISOM_GROUP_TYPE_PROL
:
1240 if( !cache
->roll
.pool
)
1242 if( traf
->file
->fragment
)
1247 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group( &traf
->sbgp_list
);
1248 if( !sbgp
|| isom_all_recovery_completed( sbgp
, cache
->roll
.pool
) < 0 )
1258 int isom_flush_fragment_pooled_samples
1260 lsmash_file_t
*file
,
1262 uint32_t last_sample_duration
1265 isom_traf_t
*traf
= isom_get_traf( file
->fragment
->movie
, track_ID
);
1267 /* No samples. We don't return as an error here since user might call the flushing function even if the
1268 * current movie fragment has no track fragment with this track_ID. */
1271 || !traf
->cache
->fragment
)
1273 if( traf
->trun_list
.entry_count
1274 && traf
->trun_list
.tail
1275 && traf
->trun_list
.tail
->data
)
1277 /* Media Data Box preceded by Movie Fragment Box could change base_data_offsets in each track fragments later.
1278 * We can't consider this here because the length of Movie Fragment Box is unknown at this step yet. */
1279 isom_trun_t
*trun
= (isom_trun_t
*)traf
->trun_list
.tail
->data
;
1280 if( file
->fragment
->pool_size
)
1281 trun
->flags
|= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT
;
1282 trun
->data_offset
= file
->fragment
->pool_size
;
1284 if( isom_output_fragment_cache( traf
) < 0 )
1286 return isom_set_fragment_last_duration( traf
, last_sample_duration
);
1289 /* This function doesn't update sample_duration of the last sample in the previous movie fragment.
1290 * Instead of this, isom_finish_movie_fragment undertakes this task. */
1291 static int isom_update_fragment_previous_sample_duration( isom_traf_t
*traf
, isom_trex_t
*trex
, uint32_t duration
)
1293 isom_tfhd_t
*tfhd
= traf
->tfhd
;
1294 isom_trun_t
*trun
= (isom_trun_t
*)traf
->trun_list
.tail
->data
;
1295 int previous_run_has_previous_sample
= 0;
1296 if( trun
->sample_count
== 1 )
1298 if( traf
->trun_list
.entry_count
== 1 )
1299 return 0; /* The previous track run belongs to the previous movie fragment if it exists. */
1300 if( !traf
->trun_list
.tail
->prev
1301 || !traf
->trun_list
.tail
->prev
->data
)
1303 /* OK. The previous sample exists in the previous track run in the same track fragment. */
1304 trun
= (isom_trun_t
*)traf
->trun_list
.tail
->prev
->data
;
1305 previous_run_has_previous_sample
= 1;
1307 /* Update default_sample_duration of the Track Fragment Header Box
1308 * if this duration is what the first sample in the current track fragment owns. */
1309 if( (trun
->sample_count
== 2 && traf
->trun_list
.entry_count
== 1)
1310 || (trun
->sample_count
== 1 && traf
->trun_list
.entry_count
== 2) )
1312 if( duration
!= trex
->default_sample_duration
)
1313 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT
;
1314 tfhd
->default_sample_duration
= duration
;
1316 /* Update the previous sample_duration if needed. */
1317 if( duration
!= tfhd
->default_sample_duration
)
1318 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT
;
1321 uint32_t sample_number
= trun
->sample_count
- !previous_run_has_previous_sample
;
1322 isom_trun_optional_row_t
*row
= isom_request_trun_optional_row( trun
, tfhd
, sample_number
);
1325 row
->sample_duration
= duration
;
1327 traf
->cache
->fragment
->last_duration
= duration
;
1331 static isom_sample_flags_t
isom_generate_fragment_sample_flags( lsmash_sample_t
*sample
)
1333 isom_sample_flags_t flags
;
1335 flags
.is_leading
= sample
->prop
.leading
& 0x3;
1336 flags
.sample_depends_on
= sample
->prop
.independent
& 0x3;
1337 flags
.sample_is_depended_on
= sample
->prop
.disposable
& 0x3;
1338 flags
.sample_has_redundancy
= sample
->prop
.redundant
& 0x3;
1339 flags
.sample_padding_value
= 0;
1340 flags
.sample_is_non_sync_sample
= !(sample
->prop
.ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
);
1341 flags
.sample_degradation_priority
= 0;
1345 static int isom_update_fragment_sample_tables( isom_traf_t
*traf
, lsmash_sample_t
*sample
)
1347 isom_tfhd_t
*tfhd
= traf
->tfhd
;
1348 isom_trex_t
*trex
= isom_get_trex( traf
->file
->moov
->mvex
, tfhd
->track_ID
);
1351 lsmash_file_t
*file
= traf
->file
;
1352 isom_cache_t
*cache
= traf
->cache
;
1353 isom_chunk_t
*current
= &cache
->chunk
;
1354 if( !current
->pool
)
1356 /* Very initial settings, just once per track */
1357 current
->pool
= isom_create_sample_pool( 0 );
1358 if( !current
->pool
)
1361 /* Create a new track run if the duration exceeds max_chunk_duration.
1362 * Old one will be appended to the pool of this movie fragment. */
1363 uint32_t media_timescale
= lsmash_get_media_timescale( file
->root
, tfhd
->track_ID
);
1364 if( !media_timescale
)
1366 int delimit
= (file
->max_chunk_duration
< ((double)(sample
->dts
- current
->first_dts
) / media_timescale
))
1367 || (file
->max_chunk_size
< (current
->pool
->size
+ sample
->length
));
1368 isom_trun_t
*trun
= NULL
;
1369 if( !traf
->trun_list
.entry_count
|| delimit
)
1372 && traf
->trun_list
.entry_count
1373 && traf
->trun_list
.tail
1374 && traf
->trun_list
.tail
->data
)
1376 /* Media Data Box preceded by Movie Fragment Box could change base data offsets in each track fragments later.
1377 * We can't consider this here because the length of Movie Fragment Box is unknown at this step yet. */
1378 trun
= (isom_trun_t
*)traf
->trun_list
.tail
->data
;
1379 if( file
->fragment
->pool_size
)
1380 trun
->flags
|= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT
;
1381 trun
->data_offset
= file
->fragment
->pool_size
;
1383 trun
= isom_add_trun( traf
);
1389 if( !traf
->trun_list
.tail
1390 || !traf
->trun_list
.tail
->data
)
1392 trun
= (isom_trun_t
*)traf
->trun_list
.tail
->data
;
1394 isom_sample_flags_t sample_flags
= isom_generate_fragment_sample_flags( sample
);
1395 if( ++trun
->sample_count
== 1 )
1397 if( traf
->trun_list
.entry_count
== 1 )
1399 /* This track fragment isn't empty-duration-fragment any more. */
1400 tfhd
->flags
&= ~ISOM_TF_FLAGS_DURATION_IS_EMPTY
;
1401 /* Set up sample_description_index in this track fragment. */
1402 if( sample
->index
!= trex
->default_sample_description_index
)
1403 tfhd
->flags
|= ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT
;
1404 tfhd
->sample_description_index
= current
->sample_description_index
= sample
->index
;
1405 /* Set up default_sample_size used in this track fragment. */
1406 tfhd
->default_sample_size
= sample
->length
;
1407 /* Set up default_sample_flags used in this track fragment.
1408 * Note: we decide an appropriate default value at the end of this movie fragment. */
1409 tfhd
->default_sample_flags
= sample_flags
;
1410 /* Set up random access information if this sample is a sync sample.
1411 * We inform only the first sample in each movie fragment. */
1412 if( !file
->bs
->unseekable
&& (sample
->prop
.ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
) )
1414 isom_tfra_t
*tfra
= isom_get_tfra( file
->mfra
, tfhd
->track_ID
);
1417 tfra
= isom_add_tfra( file
->mfra
);
1420 tfra
->track_ID
= tfhd
->track_ID
;
1424 tfra
->list
= lsmash_create_entry_list();
1428 isom_tfra_location_time_entry_t
*rap
= lsmash_malloc( sizeof(isom_tfra_location_time_entry_t
) );
1431 rap
->time
= sample
->cts
; /* Set composition timestamp temporally.
1432 * At the end of the whole movie, this will be reset as presentation time. */
1433 rap
->moof_offset
= file
->size
; /* We place Movie Fragment Box in the head of each movie fragment. */
1434 rap
->traf_number
= cache
->fragment
->traf_number
;
1435 rap
->trun_number
= traf
->trun_list
.entry_count
;
1436 rap
->sample_number
= trun
->sample_count
;
1437 if( lsmash_add_entry( tfra
->list
, rap
) )
1442 tfra
->number_of_entry
= tfra
->list
->entry_count
;
1444 for( length
= 1; rap
->traf_number
>> (length
* 8); length
++ );
1445 tfra
->length_size_of_traf_num
= LSMASH_MAX( length
- 1, tfra
->length_size_of_traf_num
);
1446 for( length
= 1; rap
->traf_number
>> (length
* 8); length
++ );
1447 tfra
->length_size_of_trun_num
= LSMASH_MAX( length
- 1, tfra
->length_size_of_trun_num
);
1448 for( length
= 1; rap
->sample_number
>> (length
* 8); length
++ );
1449 tfra
->length_size_of_sample_num
= LSMASH_MAX( length
- 1, tfra
->length_size_of_sample_num
);
1451 /* Set up the base media decode time of this track fragment.
1452 * This feature is available under ISO Base Media version 6 or later.
1453 * For DASH Media Segment, each Track Fragment Box shall contain a Track Fragment Base Media Decode Time Box. */
1454 if( file
->max_isom_version
>= 6 || file
->media_segment
)
1456 assert( !traf
->tfdt
);
1457 if( !isom_add_tfdt( traf
) )
1459 if( sample
->dts
> UINT32_MAX
)
1460 traf
->tfdt
->version
= 1;
1461 traf
->tfdt
->baseMediaDecodeTime
= sample
->dts
;
1464 trun
->first_sample_flags
= sample_flags
;
1465 current
->first_dts
= sample
->dts
;
1467 /* Update the optional rows in the current track run except for sample_duration if needed. */
1468 if( sample
->length
!= tfhd
->default_sample_size
)
1469 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT
;
1470 if( isom_compare_sample_flags( &sample_flags
, &tfhd
->default_sample_flags
) )
1471 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT
;
1472 uint32_t sample_composition_time_offset
= sample
->cts
- sample
->dts
;
1473 if( sample_composition_time_offset
)
1475 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT
;
1476 /* Check if negative composition time offset is present. */
1477 isom_timestamp_t
*ts_cache
= &cache
->timestamp
;
1478 if( (sample
->cts
+ ts_cache
->ctd_shift
) < sample
->dts
)
1480 if( file
->max_isom_version
< 6 )
1481 return -1; /* Negative composition time offset is not supported. */
1482 if( (sample
->dts
- sample
->cts
) > INT32_MAX
)
1483 return -1; /* Overflow */
1484 ts_cache
->ctd_shift
= sample
->dts
- sample
->cts
;
1485 if( trun
->version
== 0 && file
->max_isom_version
>= 6 )
1491 isom_trun_optional_row_t
*row
= isom_request_trun_optional_row( trun
, tfhd
, trun
->sample_count
);
1494 row
->sample_size
= sample
->length
;
1495 row
->sample_flags
= sample_flags
;
1496 row
->sample_composition_time_offset
= sample_composition_time_offset
;
1498 /* Set up the sample groupings for random access. */
1499 if( isom_group_random_access( (isom_box_t
*)traf
, sample
) < 0
1500 || isom_group_roll_recovery( (isom_box_t
*)traf
, sample
) < 0 )
1502 /* Set up the previous sample_duration if this sample is not the first sample in the overall movie. */
1503 if( cache
->fragment
->has_samples
)
1505 /* Note: when using for live streaming, it is not good idea to return error (-1) by sample->dts < prev_dts
1506 * since that's trivial for such semi-permanent presentation. */
1507 uint64_t prev_dts
= cache
->timestamp
.dts
;
1508 if( sample
->dts
<= prev_dts
1509 || sample
->dts
> prev_dts
+ UINT32_MAX
)
1511 uint32_t sample_duration
= sample
->dts
- prev_dts
;
1512 if( isom_update_fragment_previous_sample_duration( traf
, trex
, sample_duration
) < 0 )
1516 cache
->timestamp
.dts
= sample
->dts
;
1517 cache
->timestamp
.cts
= sample
->cts
;
1518 cache
->fragment
->largest_cts
= LSMASH_MAX( sample
->cts
, cache
->fragment
->largest_cts
);
1519 isom_subsegment_t
*subsegment
= &cache
->fragment
->subsegment
;
1520 if( trun
->sample_count
== 1 && traf
->trun_list
.entry_count
== 1 )
1522 subsegment
->first_cts
= sample
->cts
;
1523 subsegment
->largest_cts
= sample
->cts
;
1524 subsegment
->smallest_cts
= sample
->cts
;
1528 subsegment
->largest_cts
= LSMASH_MAX( sample
->cts
, subsegment
->largest_cts
);
1529 subsegment
->smallest_cts
= LSMASH_MIN( sample
->cts
, subsegment
->smallest_cts
);
1531 if( subsegment
->first_ra_flags
== ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
)
1533 subsegment
->first_ra_flags
= sample
->prop
.ra_flags
;
1534 subsegment
->first_ra_number
= cache
->fragment
->sample_count
+ 1;
1535 if( sample
->prop
.ra_flags
& (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
| ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
) )
1537 subsegment
->first_rp_number
= subsegment
->first_ra_number
;
1538 subsegment
->first_rp_cts
= sample
->cts
;
1539 subsegment
->first_ed_cts
= sample
->cts
;
1540 subsegment
->decodable
= 1;
1543 else if( subsegment
->decodable
)
1545 if( (subsegment
->first_ra_flags
& (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
| ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
))
1546 ? (sample
->prop
.leading
== ISOM_SAMPLE_IS_DECODABLE_LEADING
)
1547 : (subsegment
->first_ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_POST_ROLL_START
) )
1548 subsegment
->first_ed_cts
= LSMASH_MIN( sample
->cts
, subsegment
->first_ed_cts
);
1550 subsegment
->decodable
= 0;
1555 static int isom_append_fragment_sample_internal_initial( isom_trak_t
*trak
, lsmash_sample_t
*sample
)
1557 /* Update the sample tables of this track fragment.
1558 * If a new chunk was created, append the previous one to the pool of this movie fragment. */
1559 uint32_t samples_per_packet
;
1560 int delimit
= isom_update_sample_tables( trak
, sample
, &samples_per_packet
);
1563 else if( delimit
== 1 )
1564 isom_append_fragment_track_run( trak
->file
, &trak
->cache
->chunk
);
1565 /* Add a new sample into the pool of this track fragment. */
1566 if( isom_pool_sample( trak
->cache
->chunk
.pool
, sample
, samples_per_packet
) )
1568 trak
->cache
->fragment
->has_samples
= 1;
1569 trak
->cache
->fragment
->sample_count
+= 1;
1573 static int isom_append_fragment_sample_internal( isom_traf_t
*traf
, lsmash_sample_t
*sample
)
1575 /* Update the sample tables of this track fragment.
1576 * If a new track run was created, append the previous one to the pool of this movie fragment. */
1577 int delimit
= isom_update_fragment_sample_tables( traf
, sample
);
1580 else if( delimit
== 1 )
1581 isom_append_fragment_track_run( traf
->file
, &traf
->cache
->chunk
);
1582 /* Add a new sample into the pool of this track fragment. */
1583 if( isom_pool_sample( traf
->cache
->chunk
.pool
, sample
, 1 ) )
1585 traf
->cache
->fragment
->has_samples
= 1;
1586 traf
->cache
->fragment
->sample_count
+= 1;
1590 int isom_append_fragment_sample
1592 lsmash_file_t
*file
,
1594 lsmash_sample_t
*sample
1597 isom_fragment_manager_t
*fragment
= file
->fragment
;
1598 assert( fragment
&& fragment
->pool
);
1599 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1603 || !trak
->cache
->fragment
1606 || !trak
->mdia
->mdhd
1607 || trak
->mdia
->mdhd
->timescale
== 0
1608 || !trak
->mdia
->minf
1609 || !trak
->mdia
->minf
->stbl
1610 || !trak
->mdia
->minf
->stbl
->stsd
1611 || !trak
->mdia
->minf
->stbl
->stsc
|| !trak
->mdia
->minf
->stbl
->stsc
->list
)
1613 int (*append_sample_func
)( void *, lsmash_sample_t
* ) = NULL
;
1614 void *track_fragment
= NULL
;
1615 if( !fragment
->movie
)
1617 /* Forbid adding a sample into the initial movie if requiring compatibility with Media Segment. */
1618 if( file
->media_segment
)
1620 append_sample_func
= (int (*)( void *, lsmash_sample_t
* ))isom_append_fragment_sample_internal_initial
;
1621 track_fragment
= trak
;
1625 isom_traf_t
*traf
= isom_get_traf( fragment
->movie
, track_ID
);
1628 traf
= isom_add_traf( fragment
->movie
);
1629 if( !isom_add_tfhd( traf
) )
1631 traf
->tfhd
->flags
= ISOM_TF_FLAGS_DURATION_IS_EMPTY
; /* no samples for this track fragment yet */
1632 traf
->tfhd
->track_ID
= trak
->tkhd
->track_ID
;
1633 traf
->cache
= trak
->cache
;
1634 traf
->cache
->fragment
->traf_number
= fragment
->movie
->traf_list
.entry_count
;
1635 if( (traf
->cache
->fragment
->rap_grouping
&& isom_add_sample_grouping( (isom_box_t
*)traf
, ISOM_GROUP_TYPE_RAP
) < 0)
1636 || (traf
->cache
->fragment
->roll_grouping
&& isom_add_sample_grouping( (isom_box_t
*)traf
, ISOM_GROUP_TYPE_ROLL
) < 0) )
1639 else if( !traf
->file
1640 || !traf
->file
->moov
1641 || !traf
->file
->moov
->mvex
1645 append_sample_func
= (int (*)( void *, lsmash_sample_t
* ))isom_append_fragment_sample_internal
;
1646 track_fragment
= traf
;
1648 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
, sample
->index
);
1651 if( isom_is_lpcm_audio( sample_entry
) )
1653 uint32_t frame_size
= ((isom_audio_entry_t
*)sample_entry
)->constBytesPerAudioPacket
;
1654 if( sample
->length
== frame_size
)
1655 return append_sample_func( track_fragment
, sample
);
1656 else if( sample
->length
< frame_size
)
1658 /* Append samples splitted into each LPCMFrame. */
1659 uint64_t dts
= sample
->dts
;
1660 uint64_t cts
= sample
->cts
;
1661 for( uint32_t offset
= 0; offset
< sample
->length
; offset
+= frame_size
)
1663 lsmash_sample_t
*lpcm_sample
= lsmash_create_sample( frame_size
);
1666 memcpy( lpcm_sample
->data
, sample
->data
+ offset
, frame_size
);
1667 lpcm_sample
->dts
= dts
++;
1668 lpcm_sample
->cts
= cts
++;
1669 lpcm_sample
->prop
= sample
->prop
;
1670 lpcm_sample
->index
= sample
->index
;
1671 if( append_sample_func( track_fragment
, lpcm_sample
) )
1673 lsmash_delete_sample( lpcm_sample
);
1677 lsmash_delete_sample( sample
);
1680 return append_sample_func( track_fragment
, sample
);