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 )
53 return LSMASH_ERR_FUNCTION_PARAM
;
54 lsmash_file_t
*file
= root
->file
;
58 return LSMASH_ERR_NAMELESS
;
59 /* Finish and write the current movie fragment before starting a new one. */
60 int ret
= isom_finish_fragment_movie( file
);
63 /* Add a new movie fragment if the current one is not present or not written. */
64 if( !file
->fragment
->movie
|| (file
->fragment
->movie
->manager
& LSMASH_WRITTEN_BOX
) )
66 /* We always hold only one movie fragment except for the initial movie (a pair of moov and mdat). */
67 if( file
->fragment
->movie
&& file
->moof_list
.entry_count
!= 1 )
68 return LSMASH_ERR_NAMELESS
;
69 isom_moof_t
*moof
= isom_add_moof( file
);
70 if( !isom_add_mfhd( moof
) )
71 return LSMASH_ERR_NAMELESS
;
72 file
->fragment
->movie
= moof
;
73 moof
->mfhd
->sequence_number
= ++ file
->fragment_count
;
74 if( file
->moof_list
.entry_count
== 1 )
76 /* Remove the previous movie fragment. */
77 if( file
->moof_list
.head
)
78 isom_remove_box_by_itself( file
->moof_list
.head
->data
);
83 static int isom_set_fragment_overall_duration( lsmash_file_t
*file
)
85 assert( file
== file
->initializer
);
86 /* Get the longest duration of the tracks. */
87 uint64_t longest_duration
= 0;
88 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
90 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
93 || !trak
->cache
->fragment
96 || trak
->mdia
->mdhd
->timescale
== 0 )
97 return LSMASH_ERR_NAMELESS
;
101 || !trak
->edts
->elst
->list
)
103 duration
= trak
->cache
->fragment
->largest_cts
+ trak
->cache
->fragment
->last_duration
;
104 duration
= (uint64_t)(((double)duration
/ trak
->mdia
->mdhd
->timescale
) * file
->moov
->mvhd
->timescale
);
109 for( lsmash_entry_t
*elst_entry
= trak
->edts
->elst
->list
->head
; elst_entry
; elst_entry
= elst_entry
->next
)
111 isom_elst_entry_t
*data
= (isom_elst_entry_t
*)elst_entry
->data
;
113 return LSMASH_ERR_NAMELESS
;
114 if( data
->segment_duration
== ISOM_EDIT_DURATION_IMPLICIT
)
116 uint64_t segment_duration
= trak
->cache
->fragment
->largest_cts
+ trak
->cache
->fragment
->last_duration
;
117 duration
+= (uint64_t)(((double)segment_duration
/ trak
->mdia
->mdhd
->timescale
) * file
->moov
->mvhd
->timescale
);
120 duration
+= data
->segment_duration
;
123 longest_duration
= LSMASH_MAX( duration
, longest_duration
);
125 isom_mehd_t
*mehd
= file
->moov
->mvex
->mehd
;
126 mehd
->fragment_duration
= longest_duration
;
128 mehd
->manager
&= ~(LSMASH_PLACEHOLDER
| LSMASH_WRITTEN_BOX
); /* Update per media segment. */
129 isom_update_box_size( mehd
);
130 /* Write Movie Extends Header Box here. */
131 lsmash_bs_t
*bs
= file
->bs
;
132 uint64_t current_pos
= bs
->offset
;
133 lsmash_bs_write_seek( bs
, mehd
->pos
, SEEK_SET
);
134 int ret
= isom_write_box( bs
, (isom_box_t
*)mehd
);
135 lsmash_bs_write_seek( bs
, current_pos
, SEEK_SET
);
139 static int isom_write_fragment_random_access_info( lsmash_file_t
*file
)
141 assert( file
== file
->initializer
);
144 if( !file
->moov
->mvex
)
145 return LSMASH_ERR_NAMELESS
;
146 /* Reconstruct the Movie Fragment Random Access Box.
147 * All 'time' field in the Track Fragment Random Access Boxes shall reflect edit list. */
148 uint32_t movie_timescale
= lsmash_get_movie_timescale( file
->root
);
149 if( movie_timescale
== 0 )
150 return LSMASH_ERR_NAMELESS
; /* Division by zero will occur. */
151 for( lsmash_entry_t
*trex_entry
= file
->moov
->mvex
->trex_list
.head
; trex_entry
; trex_entry
= trex_entry
->next
)
153 isom_trex_t
*trex
= (isom_trex_t
*)trex_entry
->data
;
155 return LSMASH_ERR_NAMELESS
;
156 /* Get the edit list of the track associated with the trex->track_ID.
157 * If failed or absent, implicit timeline mapping edit is used, and skip this operation for the track. */
158 isom_trak_t
*trak
= isom_get_trak( file
, trex
->track_ID
);
160 return LSMASH_ERR_NAMELESS
;
163 || !trak
->edts
->elst
->list
164 || !trak
->edts
->elst
->list
->head
165 || !trak
->edts
->elst
->list
->head
->data
)
167 isom_elst_t
*elst
= trak
->edts
->elst
;
168 /* Get the Track Fragment Random Access Boxes of the track associated with the trex->track_ID.
169 * If failed or absent, skip reconstructing the Track Fragment Random Access Box of the track. */
170 isom_tfra_t
*tfra
= isom_get_tfra( file
->mfra
, trex
->track_ID
);
173 /* Reconstruct the Track Fragment Random Access Box. */
174 lsmash_entry_t
*edit_entry
= elst
->list
->head
;
175 isom_elst_entry_t
*edit
= edit_entry
->data
;
176 uint64_t edit_offset
= 0; /* units in media timescale */
177 uint32_t media_timescale
= lsmash_get_media_timescale( file
->root
, trex
->track_ID
);
178 for( lsmash_entry_t
*rap_entry
= tfra
->list
->head
; rap_entry
; )
180 isom_tfra_location_time_entry_t
*rap
= (isom_tfra_location_time_entry_t
*)rap_entry
->data
;
183 /* Irregular case. Drop this entry. */
184 lsmash_entry_t
*next
= rap_entry
->next
;
185 lsmash_remove_entry_direct( tfra
->list
, rap_entry
, NULL
);
189 uint64_t composition_time
= rap
->time
;
190 /* Skip edits that doesn't need the current sync sample indicated in the Track Fragment Random Access Box. */
193 uint64_t segment_duration
= edit
->segment_duration
== ISOM_EDIT_DURATION_IMPLICIT
194 ? trak
->cache
->fragment
->largest_cts
+ trak
->cache
->fragment
->last_duration
195 : ((edit
->segment_duration
- 1) / movie_timescale
+ 1) * media_timescale
;
196 if( edit
->media_time
!= ISOM_EDIT_MODE_EMPTY
197 && composition_time
< edit
->media_time
+ segment_duration
)
198 break; /* This Timeline Mapping Edit might require the current sync sample.
199 * Note: this condition doesn't cover all cases.
200 * For instance, matching the both following conditions
201 * 1. A sync sample isn't in the presentation.
202 * 2. The other samples, which precede it in the composition timeline, is in the presentation. */
203 edit_offset
+= segment_duration
;
204 edit_entry
= edit_entry
->next
;
207 /* No more presentation. */
211 edit
= edit_entry
->data
;
215 /* No more presentation.
216 * Drop the rest of sync samples since they are generally absent in the whole presentation.
217 * Though the exceptions are sync samples with earlier composition time, we ignore them. (SAP type 2: TEPT = TDEC = TSAP < TPTF)
218 * To support this exception, we need sorting entries of the list by composition times. */
221 lsmash_entry_t
*next
= rap_entry
->next
;
222 lsmash_remove_entry_direct( tfra
->list
, rap_entry
, NULL
);
227 /* If the sync sample isn't in the presentation,
228 * we pick the earliest presentation time of the current edit as its presentation time. */
229 rap
->time
= edit_offset
;
230 if( composition_time
>= edit
->media_time
)
231 rap
->time
+= composition_time
- edit
->media_time
;
232 rap_entry
= rap_entry
->next
;
234 tfra
->number_of_entry
= tfra
->list
->entry_count
;
236 /* Decide the size of the Movie Fragment Random Access Box. */
237 if( isom_update_box_size( file
->mfra
) == 0 )
238 return LSMASH_ERR_NAMELESS
;
239 /* Write the Movie Fragment Random Access Box. */
240 return isom_write_box( file
->bs
, (isom_box_t
*)file
->mfra
);
243 static int isom_update_indexed_material_offset
246 isom_sidx_t
*last_sidx
249 /* Update the size of each Segment Index Box. */
250 for( lsmash_entry_t
*entry
= file
->sidx_list
.head
; entry
; entry
= entry
->next
)
252 isom_sidx_t
*sidx
= (isom_sidx_t
*)entry
->data
;
255 if( isom_update_box_size( sidx
) == 0 )
256 return LSMASH_ERR_NAMELESS
;
258 /* first_offset: the sum of the size of subsequent Segment Index Boxes
259 * Be careful about changing the size of them. */
260 last_sidx
->first_offset
= 0;
261 for( lsmash_entry_t
*a_entry
= file
->sidx_list
.head
; a_entry
&& a_entry
->data
!= last_sidx
; a_entry
= a_entry
->next
)
263 isom_sidx_t
*a
= (isom_sidx_t
*)a_entry
->data
;
265 for( lsmash_entry_t
*b_entry
= a_entry
->next
; b_entry
; b_entry
= b_entry
->next
)
267 isom_sidx_t
*b
= (isom_sidx_t
*)b_entry
->data
;
268 a
->first_offset
+= b
->size
;
274 static int isom_write_segment_indexes
277 lsmash_adhoc_remux_t
*remux
280 /* Update the size of each Segment Index Box and establish the offset from the anchor point to the indexed material. */
282 if( (ret
= isom_update_indexed_material_offset( file
, (isom_sidx_t
*)file
->sidx_list
.tail
->data
)) < 0 )
284 /* Get the total size of all Segment Index Boxes. */
285 uint64_t total_sidx_size
= 0;
286 for( lsmash_entry_t
*entry
= file
->sidx_list
.head
; entry
; entry
= entry
->next
)
288 isom_sidx_t
*sidx
= (isom_sidx_t
*)entry
->data
;
291 total_sidx_size
+= sidx
->size
;
293 /* The buffer size must be at least total_sidx_size * 2. */
294 size_t buffer_size
= total_sidx_size
* 2;
295 if( remux
->buffer_size
> buffer_size
)
296 buffer_size
= remux
->buffer_size
;
297 /* Split to 2 buffers. */
298 uint8_t *buf
[2] = { NULL
, NULL
};
299 if( (buf
[0] = (uint8_t *)lsmash_malloc( buffer_size
)) == NULL
)
300 return LSMASH_ERR_MEMORY_ALLOC
;
301 size_t size
= buffer_size
/ 2;
302 buf
[1] = buf
[0] + size
;
303 /* Seek to the beginning of the first Movie Fragment Box i.e. the first subsegment within this media segment. */
304 lsmash_bs_t
*bs
= file
->bs
;
306 if( (ret64
= lsmash_bs_write_seek( bs
, file
->fragment
->first_moof_pos
, SEEK_SET
)) < 0 )
311 size_t read_num
= size
;
312 lsmash_bs_read_data( bs
, buf
[0], &read_num
);
313 uint64_t read_pos
= bs
->offset
;
314 /* Write the Segment Index Boxes actually here. */
315 if( (ret64
= lsmash_bs_write_seek( bs
, file
->fragment
->first_moof_pos
, SEEK_SET
)) < 0 )
320 for( lsmash_entry_t
*entry
= file
->sidx_list
.head
; entry
; entry
= entry
->next
)
322 isom_sidx_t
*sidx
= (isom_sidx_t
*)entry
->data
;
325 if( (ret
= isom_write_box( file
->bs
, (isom_box_t
*)sidx
)) < 0 )
328 /* Rearrange subsequent data. */
329 uint64_t write_pos
= bs
->offset
;
330 uint64_t total
= file
->size
+ total_sidx_size
;
331 if( (ret
= isom_rearrange_data( file
, remux
, buf
, read_num
, size
, read_pos
, write_pos
, total
)) < 0 )
333 file
->size
+= total_sidx_size
;
334 lsmash_freep( &buf
[0] );
335 /* Update 'moof_offset' of each entry within the Track Fragment Random Access Boxes. */
337 for( lsmash_entry_t
*entry
= file
->mfra
->tfra_list
.head
; entry
; entry
= entry
->next
)
339 isom_tfra_t
*tfra
= (isom_tfra_t
*)entry
->data
;
342 for( lsmash_entry_t
*rap_entry
= tfra
->list
->head
; rap_entry
; rap_entry
= rap_entry
->next
)
344 isom_tfra_location_time_entry_t
*rap
= (isom_tfra_location_time_entry_t
*)rap_entry
->data
;
347 rap
->moof_offset
+= total_sidx_size
;
352 lsmash_free( buf
[0] );
356 int isom_finish_final_fragment_movie
359 lsmash_adhoc_remux_t
*remux
362 /* Output the final movie fragment. */
364 if( (ret
= isom_finish_fragment_movie( file
)) < 0 )
366 if( file
->bs
->unseekable
)
368 /* Write Segment Index Boxes.
369 * This occurs only when the initial movie has no samples.
370 * We don't consider updating of chunk offsets within initial movie sample table here.
371 * This is reasonable since DASH requires no samples in the initial movie.
372 * This implementation is not suitable for live-streaming.
373 + To support live-streaming, it is good to use daisy-chained index. */
374 if( (file
->flags
& LSMASH_FILE_MODE_MEDIA
)
375 && (file
->flags
& LSMASH_FILE_MODE_INDEX
)
376 && (file
->flags
& LSMASH_FILE_MODE_SEGMENT
) )
379 return LSMASH_ERR_FUNCTION_PARAM
;
380 if( (ret
= isom_write_segment_indexes( file
, remux
)) < 0 )
383 /* Write the overall random access information at the tail of the movie if this file is self-contained. */
384 if( (ret
= isom_write_fragment_random_access_info( file
->initializer
)) < 0 )
386 /* Set overall duration of the movie. */
387 return isom_set_fragment_overall_duration( file
->initializer
);
390 #define GET_MOST_USED( box_name, index, flag_name ) \
391 if( most_used[index] < stats.flag_name[i] ) \
393 most_used[index] = stats.flag_name[i]; \
394 box_name->default_sample_flags.flag_name = i; \
397 static int isom_create_fragment_overall_default_settings( lsmash_file_t
*file
)
399 assert( file
== file
->initializer
);
400 if( !isom_add_mvex( file
->moov
) )
401 return LSMASH_ERR_NAMELESS
;
402 if( !file
->bs
->unseekable
)
404 if( !isom_add_mehd( file
->moov
->mvex
) )
405 return LSMASH_ERR_NAMELESS
;
406 file
->moov
->mvex
->mehd
->manager
|= LSMASH_PLACEHOLDER
;
408 for( lsmash_entry_t
*trak_entry
= file
->moov
->trak_list
.head
; trak_entry
; trak_entry
= trak_entry
->next
)
410 isom_trak_t
*trak
= (isom_trak_t
*)trak_entry
->data
;
416 || !trak
->mdia
->minf
->stbl
)
417 return LSMASH_ERR_NAMELESS
;
418 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
419 if( !stbl
->stts
|| !stbl
->stts
->list
421 || (stbl
->stts
->list
->tail
&& !stbl
->stts
->list
->tail
->data
)
422 || (stbl
->stsz
->list
&& stbl
->stsz
->list
->head
&& !stbl
->stsz
->list
->head
->data
) )
423 return LSMASH_ERR_NAMELESS
;
424 isom_trex_t
*trex
= isom_add_trex( file
->moov
->mvex
);
426 return LSMASH_ERR_NAMELESS
;
427 trex
->track_ID
= trak
->tkhd
->track_ID
;
428 /* Set up defaults. */
429 trex
->default_sample_description_index
= trak
->cache
->chunk
.sample_description_index
430 ? trak
->cache
->chunk
.sample_description_index
432 trex
->default_sample_duration
= stbl
->stts
->list
->tail
433 ? ((isom_stts_entry_t
*)stbl
->stts
->list
->tail
->data
)->sample_delta
435 trex
->default_sample_size
= !stbl
->stsz
->list
436 ? stbl
->stsz
->sample_size
: stbl
->stsz
->list
->head
437 ? ((isom_stsz_entry_t
*)stbl
->stsz
->list
->head
->data
)->entry_size
: 0;
439 && stbl
->sdtp
->list
)
441 struct sample_flags_stats_t
443 uint32_t is_leading
[4];
444 uint32_t sample_depends_on
[4];
445 uint32_t sample_is_depended_on
[4];
446 uint32_t sample_has_redundancy
[4];
447 } stats
= { { 0 }, { 0 }, { 0 }, { 0 } };
448 for( lsmash_entry_t
*sdtp_entry
= stbl
->sdtp
->list
->head
; sdtp_entry
; sdtp_entry
= sdtp_entry
->next
)
450 isom_sdtp_entry_t
*data
= (isom_sdtp_entry_t
*)sdtp_entry
->data
;
452 return LSMASH_ERR_NAMELESS
;
453 ++ stats
.is_leading
[ data
->is_leading
];
454 ++ stats
.sample_depends_on
[ data
->sample_depends_on
];
455 ++ stats
.sample_is_depended_on
[ data
->sample_is_depended_on
];
456 ++ stats
.sample_has_redundancy
[ data
->sample_has_redundancy
];
458 uint32_t most_used
[4] = { 0, 0, 0, 0 };
459 for( int i
= 0; i
< 4; i
++ )
461 GET_MOST_USED( trex
, 0, is_leading
);
462 GET_MOST_USED( trex
, 1, sample_depends_on
);
463 GET_MOST_USED( trex
, 2, sample_is_depended_on
);
464 GET_MOST_USED( trex
, 3, sample_has_redundancy
);
467 trex
->default_sample_flags
.sample_is_non_sync_sample
= !trak
->cache
->all_sync
;
472 static int isom_prepare_random_access_info( lsmash_file_t
*file
)
474 assert( file
== file
->initializer
);
475 /* Don't write the random access info at the end of the file if unseekable or not self-contained. */
476 if( file
->bs
->unseekable
477 || !(file
->flags
& LSMASH_FILE_MODE_BOX
)
478 || !(file
->flags
& LSMASH_FILE_MODE_INITIALIZATION
)
479 || !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
480 || (file
->flags
& LSMASH_FILE_MODE_SEGMENT
) )
482 if( !isom_add_mfra( file
)
483 || !isom_add_mfro( file
->mfra
) )
484 return LSMASH_ERR_NAMELESS
;
488 static int isom_output_fragment_media_data( lsmash_file_t
*file
)
490 isom_fragment_manager_t
*fragment
= file
->fragment
;
491 /* If there is no available Media Data Box to write samples, add and write a new one. */
492 if( fragment
->sample_count
)
494 if( !file
->mdat
&& !isom_add_mdat( file
) )
495 return LSMASH_ERR_NAMELESS
;
496 file
->mdat
->manager
&= ~(LSMASH_INCOMPLETE_BOX
| LSMASH_WRITTEN_BOX
);
497 int ret
= isom_write_box( file
->bs
, (isom_box_t
*)file
->mdat
);
500 file
->size
+= file
->mdat
->size
;
501 file
->mdat
->size
= 0;
502 file
->mdat
->media_size
= 0;
504 lsmash_remove_entries( fragment
->pool
, isom_remove_sample_pool
);
505 fragment
->pool_size
= 0;
506 fragment
->sample_count
= 0;
510 static int isom_finish_fragment_initial_movie( lsmash_file_t
*file
)
512 assert( file
== file
->initializer
);
514 return LSMASH_ERR_NAMELESS
;
515 isom_moov_t
*moov
= file
->moov
;
517 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
519 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
526 || !trak
->mdia
->minf
->stbl
)
527 return LSMASH_ERR_NAMELESS
;
528 if( (ret
= isom_complement_data_reference( trak
->mdia
->minf
)) < 0 )
530 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
531 if( isom_get_sample_count( trak
) )
533 /* Add stss box if any samples aren't sync sample. */
534 if( !trak
->cache
->all_sync
&& !stbl
->stss
&& !isom_add_stss( stbl
) )
536 if( (ret
= isom_update_tkhd_duration( trak
)) < 0 )
540 trak
->tkhd
->duration
= 0;
541 if( (ret
= isom_update_bitrate_description( trak
->mdia
)) < 0 )
543 /* Complete the last sample groups within tracks in the initial movie. */
544 if( trak
->cache
->rap
)
546 isom_sgpd_t
*sgpd
= isom_get_sample_group_description( stbl
, ISOM_GROUP_TYPE_RAP
);
548 return LSMASH_ERR_NAMELESS
;
549 if( (ret
= isom_rap_grouping_established( trak
->cache
->rap
, 1, sgpd
, 0 )) < 0 )
551 lsmash_freep( &trak
->cache
->rap
);
553 if( trak
->cache
->roll
.pool
)
555 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group( &stbl
->sbgp_list
);
557 return LSMASH_ERR_NAMELESS
;
558 if( (ret
= isom_all_recovery_completed( sbgp
, trak
->cache
->roll
.pool
)) < 0 )
562 if( file
->mp4_version1
== 1 && (ret
= isom_setup_iods( moov
)) < 0 )
564 if( (ret
= isom_create_fragment_overall_default_settings( file
)) < 0
565 || (ret
= isom_prepare_random_access_info ( file
)) < 0
566 || (ret
= isom_establish_movie ( file
)) < 0 )
568 /* stco->co64 conversion, depending on last chunk's offset */
569 uint64_t meta_size
= file
->meta
? file
->meta
->size
: 0;
570 if( (ret
= isom_check_large_offset_requirement( moov
, meta_size
)) < 0 )
572 /* Now, the amount of the offset is fixed. apply it to stco/co64 */
573 uint64_t preceding_size
= moov
->size
+ meta_size
;
574 isom_add_preceding_box_size( moov
, preceding_size
);
575 /* Write File Type Box here if it was not written yet. */
576 if( file
->ftyp
&& !(file
->ftyp
->manager
& LSMASH_WRITTEN_BOX
) )
578 if( isom_write_box( file
->bs
, (isom_box_t
*)file
->ftyp
) < 0 )
580 file
->size
+= file
->ftyp
->size
;
582 /* Write Movie Box. */
583 if( (ret
= isom_write_box( file
->bs
, (isom_box_t
*)file
->moov
)) < 0
584 || (ret
= isom_write_box( file
->bs
, (isom_box_t
*)file
->meta
)) < 0 )
586 file
->size
+= preceding_size
;
587 /* Output samples. */
588 if( (ret
= isom_output_fragment_media_data( file
)) < 0 )
590 /* Revert the number of samples in tracks to 0. */
591 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
593 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
594 if( trak
->cache
->fragment
)
595 trak
->cache
->fragment
->sample_count
= 0;
600 /* Return 1 if there is diffrence, otherwise return 0. */
601 static int isom_compare_sample_flags( isom_sample_flags_t
*a
, isom_sample_flags_t
*b
)
603 return (a
->reserved
!= b
->reserved
)
604 || (a
->is_leading
!= b
->is_leading
)
605 || (a
->sample_depends_on
!= b
->sample_depends_on
)
606 || (a
->sample_is_depended_on
!= b
->sample_is_depended_on
)
607 || (a
->sample_has_redundancy
!= b
->sample_has_redundancy
)
608 || (a
->sample_padding_value
!= b
->sample_padding_value
)
609 || (a
->sample_is_non_sync_sample
!= b
->sample_is_non_sync_sample
)
610 || (a
->sample_degradation_priority
!= b
->sample_degradation_priority
);
613 static int isom_make_segment_index_entry
619 /* Make the index of this subsegment. */
620 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
622 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
623 isom_tfhd_t
*tfhd
= traf
->tfhd
;
624 isom_fragment_t
*track_fragment
= traf
->cache
->fragment
;
625 isom_subsegment_t
*subsegment
= &track_fragment
->subsegment
;
626 isom_sidx_t
*sidx
= isom_get_sidx( file
, tfhd
->track_ID
);
627 isom_trak_t
*trak
= isom_get_trak( file
->initializer
, tfhd
->track_ID
);
630 || !trak
->mdia
->mdhd
)
631 return LSMASH_ERR_NAMELESS
;
632 assert( traf
->tfdt
);
635 sidx
= isom_add_sidx( file
);
637 return LSMASH_ERR_NAMELESS
;
638 sidx
->reference_ID
= tfhd
->track_ID
;
639 sidx
->timescale
= trak
->mdia
->mdhd
->timescale
;
641 sidx
->reference_count
= 0;
642 int ret
= isom_update_indexed_material_offset( file
, sidx
);
646 /* One pair of a Movie Fragment Box with an associated Media Box per subsegment. */
647 isom_sidx_referenced_item_t
*data
= lsmash_malloc( sizeof(isom_sidx_referenced_item_t
) );
649 return LSMASH_ERR_NAMELESS
;
650 if( lsmash_add_entry( sidx
->list
, data
) < 0 )
653 return LSMASH_ERR_MEMORY_ALLOC
;
655 sidx
->reference_count
= sidx
->list
->entry_count
;
656 data
->reference_type
= 0; /* media */
657 data
->reference_size
= file
->size
- moof
->pos
;
663 uint64_t composition_duration
= subsegment
->largest_cts
- subsegment
->smallest_cts
+ track_fragment
->last_duration
;
664 int subsegment_in_presentation
; /* If set to 1, TEPT is available. */
665 int first_rp_in_presentation
; /* If set to 1, both TSAP and TDEC are available. */
666 int first_sample_in_presentation
; /* If set to 1, TPTF is available. */
667 if( trak
->edts
&& trak
->edts
->elst
&& trak
->edts
->elst
->list
)
669 /**-- Explicit edits --**/
670 const isom_elst_t
*elst
= trak
->edts
->elst
;
671 const isom_elst_entry_t
*edit
= NULL
;
672 uint32_t movie_timescale
= file
->initializer
->moov
->mvhd
->timescale
;
673 uint64_t pts
= subsegment
->segment_duration
;
674 /* This initialization is redundant since these are unused when uninitialized
675 * and are initialized always in used cases, but unclever compilers may
676 * complain that these variables may be uninitialized. */
682 subsegment_in_presentation
= 0;
683 first_rp_in_presentation
= 0;
684 first_sample_in_presentation
= 0;
685 for( lsmash_entry_t
*elst_entry
= elst
->list
->head
; elst_entry
; elst_entry
= elst_entry
->next
)
687 edit
= (isom_elst_entry_t
*)elst_entry
->data
;
690 uint64_t edit_end_pts
;
691 uint64_t edit_end_cts
;
692 if( edit
->segment_duration
== ISOM_EDIT_DURATION_IMPLICIT
693 || (elst
->version
== 0 && edit
->segment_duration
== ISOM_EDIT_DURATION_UNKNOWN32
)
694 || (elst
->version
== 1 && edit
->segment_duration
== ISOM_EDIT_DURATION_UNKNOWN64
) )
696 edit_end_cts
= UINT64_MAX
;
697 edit_end_pts
= UINT64_MAX
;
701 if( edit
->segment_duration
)
703 double segment_duration
= edit
->segment_duration
* ((double)sidx
->timescale
/ movie_timescale
);
704 edit_end_cts
= edit
->media_time
+ (uint64_t)(segment_duration
* ((double)edit
->media_rate
/ (1 << 16)));
705 edit_end_pts
= pts
+ (uint64_t)segment_duration
;
709 uint64_t segment_duration
= composition_duration
;
710 if( edit
->media_time
> subsegment
->smallest_cts
)
712 if( subsegment
->largest_cts
+ track_fragment
->last_duration
> edit
->media_time
)
713 segment_duration
-= edit
->media_time
- subsegment
->smallest_cts
;
715 segment_duration
= 0;
717 edit_end_cts
= edit
->media_time
+ (uint64_t)(segment_duration
* ((double)edit
->media_rate
/ (1 << 16)));
718 edit_end_pts
= pts
+ (uint64_t)segment_duration
;
721 if( edit
->media_time
== ISOM_EDIT_MODE_EMPTY
)
726 if( (subsegment
->smallest_cts
>= edit
->media_time
&& subsegment
->smallest_cts
< edit_end_cts
)
727 || (subsegment
->largest_cts
>= edit
->media_time
&& subsegment
->largest_cts
< edit_end_cts
) )
729 /* This subsegment is present in this edit. */
730 double rate
= (double)edit
->media_rate
/ (1 << 16);
731 uint64_t start_time
= LSMASH_MAX( subsegment
->smallest_cts
, edit
->media_time
);
732 if( sidx
->reference_count
== 1 )
733 sidx
->earliest_presentation_time
= pts
;
734 if( subsegment_in_presentation
== 0 )
736 subsegment_in_presentation
= 1;
737 if( subsegment
->smallest_cts
>= edit
->media_time
)
738 TEPT
= pts
+ (uint64_t)((subsegment
->smallest_cts
- start_time
) / rate
);
742 if( first_rp_in_presentation
== 0
743 && ((subsegment
->first_ed_cts
>= edit
->media_time
&& subsegment
->first_ed_cts
< edit_end_cts
)
744 || (subsegment
->first_rp_cts
>= edit
->media_time
&& subsegment
->first_rp_cts
< edit_end_cts
)) )
746 /* FIXME: to distinguish TSAP and TDEC, need something to indicate incorrectly decodable sample. */
747 first_rp_in_presentation
= 1;
748 if( subsegment
->first_ed_cts
>= edit
->media_time
&& subsegment
->first_ed_cts
< edit_end_cts
)
749 TSAP
= pts
+ (uint64_t)((subsegment
->first_ed_cts
- start_time
) / rate
);
754 if( first_sample_in_presentation
== 0
755 && subsegment
->first_cts
>= edit
->media_time
&& subsegment
->first_cts
< edit_end_cts
)
757 first_sample_in_presentation
= 1;
758 TPTF
= pts
+ (uint64_t)((subsegment
->first_cts
- start_time
) / rate
);
760 uint64_t subsegment_end_pts
= pts
+ (uint64_t)(composition_duration
/ rate
);
761 pts
= LSMASH_MIN( edit_end_pts
, subsegment_end_pts
);
762 /* Update subsegment_duration. */
763 data
->subsegment_duration
= pts
- subsegment
->segment_duration
;
766 /* This subsegment is not present in this edit. */
772 /**-- Implicit edit --**/
773 if( sidx
->reference_count
== 1 )
774 sidx
->earliest_presentation_time
= subsegment
->smallest_cts
;
775 data
->subsegment_duration
= composition_duration
;
776 /* FIXME: to distinguish TSAP and TDEC, need something to indicate incorrectly decodable sample. */
777 TSAP
= subsegment
->first_rp_cts
;
778 TDEC
= subsegment
->first_rp_cts
;
779 TEPT
= subsegment
->smallest_cts
;
780 TPTF
= subsegment
->first_cts
;
781 subsegment_in_presentation
= 1;
782 first_rp_in_presentation
= 1;
783 first_sample_in_presentation
= 1;
785 if( subsegment
->first_ra_flags
== ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
786 || subsegment
->first_ra_number
== 0
787 || subsegment
->first_rp_number
== 0
788 || subsegment_in_presentation
== 0
789 || first_rp_in_presentation
== 0 )
791 /* No SAP in this subsegment. */
792 data
->starts_with_SAP
= 0;
794 data
->SAP_delta_time
= 0;
798 data
->starts_with_SAP
= (subsegment
->first_ra_number
== 1);
800 data
->SAP_delta_time
= TSAP
- TEPT
;
801 /* Decide SAP_type. */
802 if( first_sample_in_presentation
)
804 if( TEPT
== TDEC
&& TDEC
== TSAP
&& TSAP
== TPTF
)
806 else if( TEPT
== TDEC
&& TDEC
== TSAP
&& TSAP
< TPTF
)
808 else if( TEPT
< TDEC
&& TDEC
== TSAP
&& TSAP
<= TPTF
)
810 else if( TEPT
<= TPTF
&& TPTF
< TDEC
&& TDEC
== TSAP
)
813 if( data
->SAP_type
== 0 )
815 if( TEPT
== TDEC
&& TDEC
< TSAP
)
817 else if( TEPT
< TDEC
&& TDEC
< TSAP
)
821 subsegment
->segment_duration
+= data
->subsegment_duration
;
822 subsegment
->first_ed_cts
= UINT64_MAX
;
823 subsegment
->first_rp_cts
= UINT64_MAX
;
824 subsegment
->first_rp_number
= 0;
825 subsegment
->first_ra_number
= 0;
826 subsegment
->first_ra_flags
= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
;
827 subsegment
->decodable
= 0;
832 static int isom_finish_fragment_movie
838 || !file
->fragment
->pool
)
839 return LSMASH_ERR_NAMELESS
;
840 isom_moof_t
*moof
= file
->fragment
->movie
;
843 if( file
== file
->initializer
)
844 return isom_finish_fragment_initial_movie( file
);
846 return 0; /* No movie fragment to be finished. */
848 /* Don't write the current movie fragment if containing no track fragments.
849 * This is a requirement of DASH Media Segment. */
850 if( !moof
->traf_list
.head
851 || !moof
->traf_list
.head
->data
)
853 /* Calculate appropriate default_sample_flags of each Track Fragment Header Box.
854 * And check whether that default_sample_flags is useful or not. */
855 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
857 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
861 || !traf
->file
->initializer
862 || !traf
->file
->initializer
->moov
863 || !traf
->file
->initializer
->moov
->mvex
)
864 return LSMASH_ERR_NAMELESS
;
865 isom_tfhd_t
*tfhd
= traf
->tfhd
;
866 isom_trex_t
*trex
= isom_get_trex( file
->initializer
->moov
->mvex
, tfhd
->track_ID
);
868 return LSMASH_ERR_NAMELESS
;
869 struct sample_flags_stats_t
871 uint32_t is_leading
[4];
872 uint32_t sample_depends_on
[4];
873 uint32_t sample_is_depended_on
[4];
874 uint32_t sample_has_redundancy
[4];
875 uint32_t sample_is_non_sync_sample
[2];
876 } stats
= { { 0 }, { 0 }, { 0 }, { 0 }, { 0 } };
877 for( lsmash_entry_t
*trun_entry
= traf
->trun_list
.head
; trun_entry
; trun_entry
= trun_entry
->next
)
879 isom_trun_t
*trun
= (isom_trun_t
*)trun_entry
->data
;
880 if( !trun
|| trun
->sample_count
== 0 )
881 return LSMASH_ERR_NAMELESS
;
882 isom_sample_flags_t
*sample_flags
;
883 if( trun
->flags
& ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT
)
885 if( !trun
->optional
)
886 return LSMASH_ERR_NAMELESS
;
887 for( lsmash_entry_t
*optional_entry
= trun
->optional
->head
; optional_entry
; optional_entry
= optional_entry
->next
)
889 isom_trun_optional_row_t
*row
= (isom_trun_optional_row_t
*)optional_entry
->data
;
891 return LSMASH_ERR_NAMELESS
;
892 sample_flags
= &row
->sample_flags
;
893 ++ stats
.is_leading
[ sample_flags
->is_leading
];
894 ++ stats
.sample_depends_on
[ sample_flags
->sample_depends_on
];
895 ++ stats
.sample_is_depended_on
[ sample_flags
->sample_is_depended_on
];
896 ++ stats
.sample_has_redundancy
[ sample_flags
->sample_has_redundancy
];
897 ++ stats
.sample_is_non_sync_sample
[ sample_flags
->sample_is_non_sync_sample
];
902 sample_flags
= &tfhd
->default_sample_flags
;
903 stats
.is_leading
[ sample_flags
->is_leading
] += trun
->sample_count
;
904 stats
.sample_depends_on
[ sample_flags
->sample_depends_on
] += trun
->sample_count
;
905 stats
.sample_is_depended_on
[ sample_flags
->sample_is_depended_on
] += trun
->sample_count
;
906 stats
.sample_has_redundancy
[ sample_flags
->sample_has_redundancy
] += trun
->sample_count
;
907 stats
.sample_is_non_sync_sample
[ sample_flags
->sample_is_non_sync_sample
] += trun
->sample_count
;
910 uint32_t most_used
[5] = { 0, 0, 0, 0, 0 };
911 for( int i
= 0; i
< 4; i
++ )
913 GET_MOST_USED( tfhd
, 0, is_leading
);
914 GET_MOST_USED( tfhd
, 1, sample_depends_on
);
915 GET_MOST_USED( tfhd
, 2, sample_is_depended_on
);
916 GET_MOST_USED( tfhd
, 3, sample_has_redundancy
);
918 GET_MOST_USED( tfhd
, 4, sample_is_non_sync_sample
);
920 int useful_default_sample_duration
= 0;
921 int useful_default_sample_size
= 0;
922 for( lsmash_entry_t
*trun_entry
= traf
->trun_list
.head
; trun_entry
; trun_entry
= trun_entry
->next
)
924 isom_trun_t
*trun
= (isom_trun_t
*)trun_entry
->data
;
925 if( !(trun
->flags
& ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT
) )
926 useful_default_sample_duration
= 1;
927 if( !(trun
->flags
& ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT
) )
928 useful_default_sample_size
= 1;
929 int useful_first_sample_flags
= 1;
930 int useful_default_sample_flags
= 1;
931 if( trun
->sample_count
== 1 )
933 /* It is enough to check only if first_sample_flags equals default_sample_flags or not.
934 * If it is equal, just use default_sample_flags.
935 * If not, just use first_sample_flags of this run. */
936 if( !isom_compare_sample_flags( &trun
->first_sample_flags
, &tfhd
->default_sample_flags
) )
937 useful_first_sample_flags
= 0;
939 else if( trun
->optional
940 && trun
->optional
->head
)
942 lsmash_entry_t
*optional_entry
= trun
->optional
->head
->next
;
943 isom_trun_optional_row_t
*row
= (isom_trun_optional_row_t
*)optional_entry
->data
;
944 isom_sample_flags_t representative_sample_flags
= row
->sample_flags
;
945 if( isom_compare_sample_flags( &tfhd
->default_sample_flags
, &representative_sample_flags
) )
946 useful_default_sample_flags
= 0;
947 if( !isom_compare_sample_flags( &trun
->first_sample_flags
, &representative_sample_flags
) )
948 useful_first_sample_flags
= 0;
949 if( useful_default_sample_flags
)
950 for( optional_entry
= optional_entry
->next
; optional_entry
; optional_entry
= optional_entry
->next
)
952 row
= (isom_trun_optional_row_t
*)optional_entry
->data
;
953 if( isom_compare_sample_flags( &representative_sample_flags
, &row
->sample_flags
) )
955 useful_default_sample_flags
= 0;
960 if( useful_default_sample_flags
)
962 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT
;
963 trun
->flags
&= ~ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT
;
967 useful_first_sample_flags
= 0;
968 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT
;
970 if( useful_first_sample_flags
)
971 trun
->flags
|= ISOM_TR_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT
;
973 if( useful_default_sample_duration
&& tfhd
->default_sample_duration
!= trex
->default_sample_duration
)
974 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT
;
976 tfhd
->default_sample_duration
= trex
->default_sample_duration
; /* This might be redundant, but is to be more natural. */
977 if( useful_default_sample_size
&& tfhd
->default_sample_size
!= trex
->default_sample_size
)
978 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT
;
980 tfhd
->default_sample_size
= trex
->default_sample_size
; /* This might be redundant, but is to be more natural. */
981 if( !(tfhd
->flags
& ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT
) )
982 tfhd
->default_sample_flags
= trex
->default_sample_flags
; /* This might be redundant, but is to be more natural. */
983 else if( !isom_compare_sample_flags( &tfhd
->default_sample_flags
, &trex
->default_sample_flags
) )
984 tfhd
->flags
&= ~ISOM_TF_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT
;
986 /* Complete the last sample groups in the previous track fragments. */
988 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
990 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
991 if( traf
->cache
->rap
)
993 isom_sgpd_t
*sgpd
= isom_get_fragment_sample_group_description( traf
, ISOM_GROUP_TYPE_RAP
);
995 return LSMASH_ERR_NAMELESS
;
996 if( (ret
= isom_rap_grouping_established( traf
->cache
->rap
, 1, sgpd
, 1 )) < 0 )
998 lsmash_freep( &traf
->cache
->rap
);
1000 if( traf
->cache
->roll
.pool
)
1002 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group( &traf
->sbgp_list
);
1004 return LSMASH_ERR_NAMELESS
;
1005 if( (ret
= isom_all_recovery_completed( sbgp
, traf
->cache
->roll
.pool
)) < 0 )
1009 /* Establish Movie Fragment Box.
1010 * We write exactly one Media Data Box starting immediately after the corresponding Movie Fragment Box. */
1011 if( file
->allow_moof_base
)
1013 /* In this branch, we use default-base-is-moof flag, which indicates implicit base_data_offsets originate in the
1014 * first byte of each enclosing Movie Fragment Box.
1015 * 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
1016 * the type field of it as the data_offset of the first track run like the following.
1018 * _____________ _ offset := 0
1027 * |___|_________|_ offset := the size of the Movie Fragment Box
1033 * | a |_________|_ offset := the data_offset of the first track run
1036 * |___|_________|_ offset := the size of a subsegment containing exactly one movie fragment
1038 * For a pair of one Movie Fragment Box and one Media Data Box, placed in this order, implicit base_data_offsets
1039 * indicated by the absence of both base-data-offset-present and default-base-is-moof are somewhat complicated
1040 * since the implicit base_data_offset of the current track fragment is defined by the end of the data of the
1041 * previous track fragment and the data_offset of the track runs could be negative value because of interleaving
1042 * track runs or something other reasons.
1043 * In contrast, implicit base_data_offsets indicated by default-base-is-moof are simple since the base_data_offset
1044 * of each track fragment is always constant for that pair and has no dependency on other track fragments.
1046 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
1048 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
1049 traf
->tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_BASE_IS_MOOF
;
1050 traf
->tfhd
->base_data_offset
= file
->size
; /* not written actually though */
1051 for( lsmash_entry_t
*trun_entry
= traf
->trun_list
.head
; trun_entry
; trun_entry
= trun_entry
->next
)
1053 /* Here, data_offset is always greater than zero. */
1054 isom_trun_t
*trun
= trun_entry
->data
;
1055 trun
->flags
|= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT
;
1058 /* Consider the update of tr_flags here. */
1059 if( isom_update_box_size( moof
) == 0 )
1060 return LSMASH_ERR_NAMELESS
;
1061 /* Now, we can calculate offsets in the current movie fragment, so do it. */
1062 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
1064 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
1065 for( lsmash_entry_t
*trun_entry
= traf
->trun_list
.head
; trun_entry
; trun_entry
= trun_entry
->next
)
1067 isom_trun_t
*trun
= trun_entry
->data
;
1068 trun
->data_offset
+= moof
->size
+ ISOM_BASEBOX_COMMON_SIZE
;
1074 /* In this branch, we use explicit base_data_offset. */
1075 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
1077 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
1078 traf
->tfhd
->flags
|= ISOM_TF_FLAGS_BASE_DATA_OFFSET_PRESENT
;
1080 /* Consider the update of tf_flags here. */
1081 if( isom_update_box_size( moof
) == 0 )
1082 return LSMASH_ERR_NAMELESS
;
1083 /* Now, we can calculate offsets in the current movie fragment, so do it. */
1084 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
1086 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
1087 traf
->tfhd
->base_data_offset
= file
->size
+ moof
->size
+ ISOM_BASEBOX_COMMON_SIZE
;
1090 /* Write Movie Fragment Box and its children. */
1091 moof
->pos
= file
->size
;
1092 if( (ret
= isom_write_box( file
->bs
, (isom_box_t
*)moof
)) < 0 )
1094 if( file
->fragment
->first_moof_pos
== FIRST_MOOF_POS_UNDETERMINED
)
1095 file
->fragment
->first_moof_pos
= moof
->pos
;
1096 file
->size
+= moof
->size
;
1097 /* Output samples. */
1098 if( (ret
= isom_output_fragment_media_data( file
)) < 0 )
1100 /* Revert the number of samples in track fragments to 0. */
1101 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
1103 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
1104 if( traf
->cache
->fragment
)
1105 traf
->cache
->fragment
->sample_count
= 0;
1107 if( !(file
->flags
& LSMASH_FILE_MODE_INDEX
) || file
->max_isom_version
< 6 )
1109 return isom_make_segment_index_entry( file
, moof
);
1112 #undef GET_MOST_USED
1114 static isom_trun_optional_row_t
*isom_request_trun_optional_row( isom_trun_t
*trun
, isom_tfhd_t
*tfhd
, uint32_t sample_number
)
1116 isom_trun_optional_row_t
*row
= NULL
;
1117 if( !trun
->optional
)
1119 trun
->optional
= lsmash_create_entry_list();
1120 if( !trun
->optional
)
1123 if( trun
->optional
->entry_count
< sample_number
)
1125 while( trun
->optional
->entry_count
< sample_number
)
1127 row
= lsmash_malloc( sizeof(isom_trun_optional_row_t
) );
1130 /* Copy from default. */
1131 row
->sample_duration
= tfhd
->default_sample_duration
;
1132 row
->sample_size
= tfhd
->default_sample_size
;
1133 row
->sample_flags
= tfhd
->default_sample_flags
;
1134 row
->sample_composition_time_offset
= 0;
1135 if( lsmash_add_entry( trun
->optional
, row
) < 0 )
1144 for( lsmash_entry_t
*entry
= trun
->optional
->head
; entry
; entry
= entry
->next
)
1146 row
= (isom_trun_optional_row_t
*)entry
->data
;
1149 if( ++i
== sample_number
)
1155 int lsmash_create_fragment_empty_duration
1157 lsmash_root_t
*root
,
1162 if( isom_check_initializer_present( root
) < 0 )
1163 return LSMASH_ERR_FUNCTION_PARAM
;
1164 lsmash_file_t
*file
= root
->file
;
1166 || !file
->fragment
->movie
1167 || !file
->initializer
->moov
)
1168 return LSMASH_ERR_NAMELESS
;
1169 isom_trak_t
*trak
= isom_get_trak( file
->initializer
, track_ID
);
1172 return LSMASH_ERR_NAMELESS
;
1173 isom_trex_t
*trex
= isom_get_trex( file
->initializer
->moov
->mvex
, track_ID
);
1175 return LSMASH_ERR_NAMELESS
;
1176 isom_moof_t
*moof
= file
->fragment
->movie
;
1177 isom_traf_t
*traf
= isom_get_traf( moof
, track_ID
);
1179 return LSMASH_ERR_NAMELESS
;
1180 traf
= isom_add_traf( moof
);
1181 if( !isom_add_tfhd( traf
) )
1182 return LSMASH_ERR_NAMELESS
;
1183 isom_tfhd_t
*tfhd
= traf
->tfhd
;
1184 tfhd
->flags
= ISOM_TF_FLAGS_DURATION_IS_EMPTY
; /* no samples for this track fragment yet */
1185 tfhd
->track_ID
= trak
->tkhd
->track_ID
;
1186 tfhd
->default_sample_duration
= duration
;
1187 if( duration
!= trex
->default_sample_duration
)
1188 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT
;
1189 traf
->cache
= trak
->cache
;
1190 traf
->cache
->fragment
->traf_number
= moof
->traf_list
.entry_count
;
1191 traf
->cache
->fragment
->last_duration
+= duration
; /* The duration of the last sample includes this empty-duration. */
1195 int isom_set_fragment_last_duration
1198 uint32_t last_duration
1201 isom_tfhd_t
*tfhd
= traf
->tfhd
;
1202 if( !traf
->trun_list
.tail
1203 || !traf
->trun_list
.tail
->data
)
1205 /* There are no track runs in this track fragment, so it is a empty-duration. */
1206 isom_trex_t
*trex
= isom_get_trex( traf
->file
->initializer
->moov
->mvex
, tfhd
->track_ID
);
1208 return LSMASH_ERR_NAMELESS
;
1209 tfhd
->flags
|= ISOM_TF_FLAGS_DURATION_IS_EMPTY
;
1210 if( last_duration
!= trex
->default_sample_duration
)
1211 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT
;
1212 tfhd
->default_sample_duration
= last_duration
;
1213 traf
->cache
->fragment
->last_duration
= last_duration
;
1216 /* Update the last sample_duration if needed. */
1217 isom_trun_t
*trun
= (isom_trun_t
*)traf
->trun_list
.tail
->data
;
1218 if( trun
->sample_count
== 1
1219 && traf
->trun_list
.entry_count
== 1 )
1221 isom_trex_t
*trex
= isom_get_trex( traf
->file
->initializer
->moov
->mvex
, tfhd
->track_ID
);
1223 return LSMASH_ERR_NAMELESS
;
1224 if( last_duration
!= trex
->default_sample_duration
)
1225 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT
;
1226 tfhd
->default_sample_duration
= last_duration
;
1228 else if( last_duration
!= tfhd
->default_sample_duration
)
1229 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT
;
1232 isom_trun_optional_row_t
*row
= isom_request_trun_optional_row( trun
, tfhd
, trun
->sample_count
);
1234 return LSMASH_ERR_NAMELESS
;
1235 row
->sample_duration
= last_duration
;
1237 traf
->cache
->fragment
->last_duration
= last_duration
;
1241 int isom_append_fragment_track_run
1243 lsmash_file_t
*file
,
1247 if( !chunk
->pool
|| chunk
->pool
->size
== 0 )
1249 isom_fragment_manager_t
*fragment
= file
->fragment
;
1250 /* Move data in the pool of the current track fragment to the pool of the current movie fragment.
1251 * Empty the pool of current track. We don't delete data of samples here. */
1252 if( lsmash_add_entry( fragment
->pool
, chunk
->pool
) < 0 )
1253 return LSMASH_ERR_MEMORY_ALLOC
;
1254 fragment
->sample_count
+= chunk
->pool
->sample_count
;
1255 fragment
->pool_size
+= chunk
->pool
->size
;
1256 chunk
->pool
= isom_create_sample_pool( chunk
->pool
->size
);
1257 return chunk
->pool
? 0 : LSMASH_ERR_MEMORY_ALLOC
;
1260 static int isom_output_fragment_cache( isom_traf_t
*traf
)
1262 isom_cache_t
*cache
= traf
->cache
;
1263 int ret
= isom_append_fragment_track_run( traf
->file
, &cache
->chunk
);
1266 for( lsmash_entry_t
*entry
= traf
->sgpd_list
.head
; entry
; entry
= entry
->next
)
1268 isom_sgpd_t
*sgpd
= (isom_sgpd_t
*)entry
->data
;
1270 return LSMASH_ERR_NAMELESS
;
1271 switch( sgpd
->grouping_type
)
1273 case ISOM_GROUP_TYPE_RAP
:
1275 isom_rap_group_t
*group
= cache
->rap
;
1278 if( traf
->file
->fragment
)
1281 return LSMASH_ERR_NAMELESS
;
1283 if( !group
->random_access
)
1285 group
->random_access
->num_leading_samples_known
= 1;
1288 case ISOM_GROUP_TYPE_ROLL
:
1289 case ISOM_GROUP_TYPE_PROL
:
1290 if( !cache
->roll
.pool
)
1292 if( traf
->file
->fragment
)
1295 return LSMASH_ERR_NAMELESS
;
1297 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group( &traf
->sbgp_list
);
1299 return LSMASH_ERR_NAMELESS
;
1300 if( (ret
= isom_all_recovery_completed( sbgp
, cache
->roll
.pool
)) < 0 )
1310 int isom_flush_fragment_pooled_samples
1312 lsmash_file_t
*file
,
1314 uint32_t last_sample_duration
1317 isom_traf_t
*traf
= isom_get_traf( file
->fragment
->movie
, track_ID
);
1319 /* No samples. We don't return as an error here since user might call the flushing function even if the
1320 * current movie fragment has no track fragment with this track_ID. */
1323 || !traf
->cache
->fragment
)
1324 return LSMASH_ERR_NAMELESS
;
1325 if( traf
->trun_list
.entry_count
1326 && traf
->trun_list
.tail
1327 && traf
->trun_list
.tail
->data
)
1329 /* Media Data Box preceded by Movie Fragment Box could change base_data_offsets in each track fragments later.
1330 * We can't consider this here because the length of Movie Fragment Box is unknown at this step yet. */
1331 isom_trun_t
*trun
= (isom_trun_t
*)traf
->trun_list
.tail
->data
;
1332 if( file
->fragment
->pool_size
)
1333 trun
->flags
|= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT
;
1334 trun
->data_offset
= file
->fragment
->pool_size
;
1336 int ret
= isom_output_fragment_cache( traf
);
1339 return isom_set_fragment_last_duration( traf
, last_sample_duration
);
1342 /* This function doesn't update sample_duration of the last sample in the previous movie fragment.
1343 * Instead of this, isom_finish_movie_fragment undertakes this task. */
1344 static int isom_update_fragment_previous_sample_duration( isom_traf_t
*traf
, isom_trex_t
*trex
, uint32_t duration
)
1346 isom_tfhd_t
*tfhd
= traf
->tfhd
;
1347 isom_trun_t
*trun
= (isom_trun_t
*)traf
->trun_list
.tail
->data
;
1348 int previous_run_has_previous_sample
= 0;
1349 if( trun
->sample_count
== 1 )
1351 if( traf
->trun_list
.entry_count
== 1 )
1352 return 0; /* The previous track run belongs to the previous movie fragment if it exists. */
1353 if( !traf
->trun_list
.tail
->prev
1354 || !traf
->trun_list
.tail
->prev
->data
)
1355 return LSMASH_ERR_NAMELESS
;
1356 /* OK. The previous sample exists in the previous track run in the same track fragment. */
1357 trun
= (isom_trun_t
*)traf
->trun_list
.tail
->prev
->data
;
1358 previous_run_has_previous_sample
= 1;
1360 /* Update default_sample_duration of the Track Fragment Header Box
1361 * if this duration is what the first sample in the current track fragment owns. */
1362 if( (trun
->sample_count
== 2 && traf
->trun_list
.entry_count
== 1)
1363 || (trun
->sample_count
== 1 && traf
->trun_list
.entry_count
== 2) )
1365 if( duration
!= trex
->default_sample_duration
)
1366 tfhd
->flags
|= ISOM_TF_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT
;
1367 tfhd
->default_sample_duration
= duration
;
1369 /* Update the previous sample_duration if needed. */
1370 if( duration
!= tfhd
->default_sample_duration
)
1371 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_DURATION_PRESENT
;
1374 uint32_t sample_number
= trun
->sample_count
- !previous_run_has_previous_sample
;
1375 isom_trun_optional_row_t
*row
= isom_request_trun_optional_row( trun
, tfhd
, sample_number
);
1377 return LSMASH_ERR_NAMELESS
;
1378 row
->sample_duration
= duration
;
1380 traf
->cache
->fragment
->last_duration
= duration
;
1384 static isom_sample_flags_t
isom_generate_fragment_sample_flags( lsmash_sample_t
*sample
)
1386 isom_sample_flags_t flags
;
1388 flags
.is_leading
= sample
->prop
.leading
& 0x3;
1389 flags
.sample_depends_on
= sample
->prop
.independent
& 0x3;
1390 flags
.sample_is_depended_on
= sample
->prop
.disposable
& 0x3;
1391 flags
.sample_has_redundancy
= sample
->prop
.redundant
& 0x3;
1392 flags
.sample_padding_value
= 0;
1393 flags
.sample_is_non_sync_sample
= !(sample
->prop
.ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
);
1394 flags
.sample_degradation_priority
= 0;
1398 static int isom_update_fragment_sample_tables( isom_traf_t
*traf
, lsmash_sample_t
*sample
)
1400 isom_tfhd_t
*tfhd
= traf
->tfhd
;
1401 isom_trex_t
*trex
= isom_get_trex( traf
->file
->initializer
->moov
->mvex
, tfhd
->track_ID
);
1403 return LSMASH_ERR_NAMELESS
;
1404 lsmash_file_t
*file
= traf
->file
;
1405 isom_cache_t
*cache
= traf
->cache
;
1406 isom_chunk_t
*current
= &cache
->chunk
;
1407 if( !current
->pool
)
1409 /* Very initial settings, just once per track */
1410 current
->pool
= isom_create_sample_pool( 0 );
1411 if( !current
->pool
)
1412 return LSMASH_ERR_MEMORY_ALLOC
;
1414 /* Create a new track run if the duration exceeds max_chunk_duration.
1415 * Old one will be appended to the pool of this movie fragment. */
1416 uint32_t media_timescale
= lsmash_get_media_timescale( file
->root
, tfhd
->track_ID
);
1417 if( !media_timescale
)
1418 return LSMASH_ERR_NAMELESS
;
1419 int delimit
= (file
->max_chunk_duration
< ((double)(sample
->dts
- current
->first_dts
) / media_timescale
))
1420 || (file
->max_chunk_size
< (current
->pool
->size
+ sample
->length
));
1421 isom_trun_t
*trun
= NULL
;
1422 if( !traf
->trun_list
.entry_count
|| delimit
)
1425 && traf
->trun_list
.entry_count
1426 && traf
->trun_list
.tail
1427 && traf
->trun_list
.tail
->data
)
1429 /* Media Data Box preceded by Movie Fragment Box could change base data offsets in each track fragments later.
1430 * We can't consider this here because the length of Movie Fragment Box is unknown at this step yet. */
1431 trun
= (isom_trun_t
*)traf
->trun_list
.tail
->data
;
1432 if( file
->fragment
->pool_size
)
1433 trun
->flags
|= ISOM_TR_FLAGS_DATA_OFFSET_PRESENT
;
1434 trun
->data_offset
= file
->fragment
->pool_size
;
1436 trun
= isom_add_trun( traf
);
1438 return LSMASH_ERR_NAMELESS
;
1442 if( !traf
->trun_list
.tail
1443 || !traf
->trun_list
.tail
->data
)
1444 return LSMASH_ERR_NAMELESS
;
1445 trun
= (isom_trun_t
*)traf
->trun_list
.tail
->data
;
1447 isom_sample_flags_t sample_flags
= isom_generate_fragment_sample_flags( sample
);
1448 if( ++trun
->sample_count
== 1 )
1450 if( traf
->trun_list
.entry_count
== 1 )
1452 /* This track fragment isn't empty-duration-fragment any more. */
1453 tfhd
->flags
&= ~ISOM_TF_FLAGS_DURATION_IS_EMPTY
;
1454 /* Set up sample_description_index in this track fragment. */
1455 if( sample
->index
!= trex
->default_sample_description_index
)
1456 tfhd
->flags
|= ISOM_TF_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT
;
1457 tfhd
->sample_description_index
= current
->sample_description_index
= sample
->index
;
1458 /* Set up default_sample_size used in this track fragment. */
1459 tfhd
->default_sample_size
= sample
->length
;
1460 /* Set up default_sample_flags used in this track fragment.
1461 * Note: we decide an appropriate default value at the end of this movie fragment. */
1462 tfhd
->default_sample_flags
= sample_flags
;
1463 /* Set up random access information if this sample is a sync sample.
1464 * We inform only the first sample in each movie fragment. */
1465 if( file
->mfra
&& (sample
->prop
.ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
) )
1467 isom_tfra_t
*tfra
= isom_get_tfra( file
->mfra
, tfhd
->track_ID
);
1470 tfra
= isom_add_tfra( file
->mfra
);
1472 return LSMASH_ERR_NAMELESS
;
1473 tfra
->track_ID
= tfhd
->track_ID
;
1477 tfra
->list
= lsmash_create_entry_list();
1479 return LSMASH_ERR_MEMORY_ALLOC
;
1481 isom_tfra_location_time_entry_t
*rap
= lsmash_malloc( sizeof(isom_tfra_location_time_entry_t
) );
1483 return LSMASH_ERR_MEMORY_ALLOC
;
1484 rap
->time
= sample
->cts
; /* Set composition timestamp temporally.
1485 * At the end of the whole movie, this will be reset as presentation time. */
1486 rap
->moof_offset
= file
->size
; /* We place Movie Fragment Box in the head of each movie fragment. */
1487 rap
->traf_number
= cache
->fragment
->traf_number
;
1488 rap
->trun_number
= traf
->trun_list
.entry_count
;
1489 rap
->sample_number
= trun
->sample_count
;
1490 if( lsmash_add_entry( tfra
->list
, rap
) < 0 )
1493 return LSMASH_ERR_MEMORY_ALLOC
;
1495 tfra
->number_of_entry
= tfra
->list
->entry_count
;
1497 for( length
= 1; rap
->traf_number
>> (length
* 8); length
++ );
1498 tfra
->length_size_of_traf_num
= LSMASH_MAX( length
- 1, tfra
->length_size_of_traf_num
);
1499 for( length
= 1; rap
->traf_number
>> (length
* 8); length
++ );
1500 tfra
->length_size_of_trun_num
= LSMASH_MAX( length
- 1, tfra
->length_size_of_trun_num
);
1501 for( length
= 1; rap
->sample_number
>> (length
* 8); length
++ );
1502 tfra
->length_size_of_sample_num
= LSMASH_MAX( length
- 1, tfra
->length_size_of_sample_num
);
1504 /* Set up the base media decode time of this track fragment.
1505 * This feature is available under ISO Base Media version 6 or later.
1506 * For DASH Media Segment, each Track Fragment Box shall contain a Track Fragment Base Media Decode Time Box. */
1507 if( file
->max_isom_version
>= 6 || file
->media_segment
)
1509 assert( !traf
->tfdt
);
1510 if( !isom_add_tfdt( traf
) )
1511 return LSMASH_ERR_NAMELESS
;
1512 if( sample
->dts
> UINT32_MAX
)
1513 traf
->tfdt
->version
= 1;
1514 traf
->tfdt
->baseMediaDecodeTime
= sample
->dts
;
1517 trun
->first_sample_flags
= sample_flags
;
1518 current
->first_dts
= sample
->dts
;
1520 /* Update the optional rows in the current track run except for sample_duration if needed. */
1521 if( sample
->length
!= tfhd
->default_sample_size
)
1522 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_SIZE_PRESENT
;
1523 if( isom_compare_sample_flags( &sample_flags
, &tfhd
->default_sample_flags
) )
1524 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT
;
1525 uint32_t sample_composition_time_offset
= sample
->cts
- sample
->dts
;
1526 if( sample_composition_time_offset
)
1528 trun
->flags
|= ISOM_TR_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSET_PRESENT
;
1529 /* Check if negative composition time offset is present. */
1530 isom_timestamp_t
*ts_cache
= &cache
->timestamp
;
1531 if( (sample
->cts
+ ts_cache
->ctd_shift
) < sample
->dts
)
1533 if( file
->max_isom_version
< 6 )
1534 return LSMASH_ERR_INVALID_DATA
; /* Negative composition time offset is invalid. */
1535 if( (sample
->dts
- sample
->cts
) > INT32_MAX
)
1536 return LSMASH_ERR_INVALID_DATA
; /* Overflow */
1537 ts_cache
->ctd_shift
= sample
->dts
- sample
->cts
;
1538 if( trun
->version
== 0 && file
->max_isom_version
>= 6 )
1544 isom_trun_optional_row_t
*row
= isom_request_trun_optional_row( trun
, tfhd
, trun
->sample_count
);
1546 return LSMASH_ERR_NAMELESS
;
1547 row
->sample_size
= sample
->length
;
1548 row
->sample_flags
= sample_flags
;
1549 row
->sample_composition_time_offset
= sample_composition_time_offset
;
1551 /* Set up the sample groupings for random access. */
1553 if( (ret
= isom_group_random_access( (isom_box_t
*)traf
, sample
)) < 0
1554 || (ret
= isom_group_roll_recovery( (isom_box_t
*)traf
, sample
)) < 0 )
1556 /* Set up the previous sample_duration if this sample is not the first sample in the overall movie. */
1557 if( cache
->fragment
->has_samples
)
1559 /* Note: when using for live streaming, it is not good idea to return error by sample->dts < prev_dts
1560 * since that's trivial for such semi-permanent presentation. */
1561 uint64_t prev_dts
= cache
->timestamp
.dts
;
1562 if( sample
->dts
<= prev_dts
1563 || sample
->dts
> prev_dts
+ UINT32_MAX
)
1564 return LSMASH_ERR_INVALID_DATA
;
1565 uint32_t sample_duration
= sample
->dts
- prev_dts
;
1566 if( (ret
= isom_update_fragment_previous_sample_duration( traf
, trex
, sample_duration
)) < 0 )
1570 cache
->timestamp
.dts
= sample
->dts
;
1571 cache
->timestamp
.cts
= sample
->cts
;
1572 cache
->fragment
->largest_cts
= LSMASH_MAX( sample
->cts
, cache
->fragment
->largest_cts
);
1573 isom_subsegment_t
*subsegment
= &cache
->fragment
->subsegment
;
1574 if( trun
->sample_count
== 1 && traf
->trun_list
.entry_count
== 1 )
1576 subsegment
->first_cts
= sample
->cts
;
1577 subsegment
->largest_cts
= sample
->cts
;
1578 subsegment
->smallest_cts
= sample
->cts
;
1582 subsegment
->largest_cts
= LSMASH_MAX( sample
->cts
, subsegment
->largest_cts
);
1583 subsegment
->smallest_cts
= LSMASH_MIN( sample
->cts
, subsegment
->smallest_cts
);
1585 if( subsegment
->first_ra_flags
== ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
)
1587 subsegment
->first_ra_flags
= sample
->prop
.ra_flags
;
1588 subsegment
->first_ra_number
= cache
->fragment
->sample_count
+ 1;
1589 if( sample
->prop
.ra_flags
& (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
| ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
) )
1591 subsegment
->first_rp_number
= subsegment
->first_ra_number
;
1592 subsegment
->first_rp_cts
= sample
->cts
;
1593 subsegment
->first_ed_cts
= sample
->cts
;
1594 subsegment
->decodable
= 1;
1597 else if( subsegment
->decodable
)
1599 if( (subsegment
->first_ra_flags
& (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
| ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
))
1600 ? (sample
->prop
.leading
== ISOM_SAMPLE_IS_DECODABLE_LEADING
)
1601 : (subsegment
->first_ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_POST_ROLL_START
) )
1602 subsegment
->first_ed_cts
= LSMASH_MIN( sample
->cts
, subsegment
->first_ed_cts
);
1604 subsegment
->decodable
= 0;
1609 static int isom_append_fragment_sample_internal_initial( isom_trak_t
*trak
, lsmash_sample_t
*sample
)
1611 /* Update the sample tables of this track fragment.
1612 * If a new chunk was created, append the previous one to the pool of this movie fragment. */
1613 uint32_t samples_per_packet
;
1614 int ret
= isom_update_sample_tables( trak
, sample
, &samples_per_packet
);
1618 isom_append_fragment_track_run( trak
->file
, &trak
->cache
->chunk
);
1619 /* Add a new sample into the pool of this track fragment. */
1620 if( (ret
= isom_pool_sample( trak
->cache
->chunk
.pool
, sample
, samples_per_packet
)) < 0 )
1622 trak
->cache
->fragment
->has_samples
= 1;
1623 trak
->cache
->fragment
->sample_count
+= 1;
1627 static int isom_append_fragment_sample_internal( isom_traf_t
*traf
, lsmash_sample_t
*sample
)
1629 /* Update the sample tables of this track fragment.
1630 * If a new track run was created, append the previous one to the pool of this movie fragment. */
1631 int ret
= isom_update_fragment_sample_tables( traf
, sample
);
1635 isom_append_fragment_track_run( traf
->file
, &traf
->cache
->chunk
);
1636 /* Add a new sample into the pool of this track fragment. */
1637 if( (ret
= isom_pool_sample( traf
->cache
->chunk
.pool
, sample
, 1 )) < 0 )
1639 traf
->cache
->fragment
->has_samples
= 1;
1640 traf
->cache
->fragment
->sample_count
+= 1;
1644 int isom_append_fragment_sample
1646 lsmash_file_t
*file
,
1648 lsmash_sample_t
*sample
1651 isom_fragment_manager_t
*fragment
= file
->fragment
;
1652 assert( fragment
&& fragment
->pool
);
1653 isom_trak_t
*trak
= isom_get_trak( file
->initializer
, track_ID
);
1657 || !trak
->cache
->fragment
1660 || !trak
->mdia
->mdhd
1661 || trak
->mdia
->mdhd
->timescale
== 0
1662 || !trak
->mdia
->minf
1663 || !trak
->mdia
->minf
->stbl
1664 || !trak
->mdia
->minf
->stbl
->stsd
1665 || !trak
->mdia
->minf
->stbl
->stsc
|| !trak
->mdia
->minf
->stbl
->stsc
->list
)
1666 return LSMASH_ERR_NAMELESS
;
1667 /* Write the Segment Type Box here if required and if it was not written yet. */
1668 if( !(file
->flags
& LSMASH_FILE_MODE_INITIALIZATION
)
1669 && file
->styp_list
.head
&& file
->styp_list
.head
->data
)
1671 isom_styp_t
*styp
= (isom_styp_t
*)file
->styp_list
.head
->data
;
1672 if( !(styp
->manager
& LSMASH_WRITTEN_BOX
) )
1674 int ret
= isom_write_box( file
->bs
, (isom_box_t
*)styp
);
1677 file
->size
+= styp
->size
;
1680 int (*append_sample_func
)( void *, lsmash_sample_t
* ) = NULL
;
1681 void *track_fragment
= NULL
;
1682 if( !fragment
->movie
)
1684 /* Forbid adding a sample into the initial movie if requiring compatibility with Media Segment. */
1685 if( file
->media_segment
)
1686 return LSMASH_ERR_NAMELESS
;
1687 append_sample_func
= (int (*)( void *, lsmash_sample_t
* ))isom_append_fragment_sample_internal_initial
;
1688 track_fragment
= trak
;
1692 isom_traf_t
*traf
= isom_get_traf( fragment
->movie
, track_ID
);
1695 traf
= isom_add_traf( fragment
->movie
);
1696 if( !isom_add_tfhd( traf
) )
1697 return LSMASH_ERR_NAMELESS
;
1698 traf
->tfhd
->flags
= ISOM_TF_FLAGS_DURATION_IS_EMPTY
; /* no samples for this track fragment yet */
1699 traf
->tfhd
->track_ID
= trak
->tkhd
->track_ID
;
1700 traf
->cache
= trak
->cache
;
1701 traf
->cache
->fragment
->traf_number
= fragment
->movie
->traf_list
.entry_count
;
1703 if( (traf
->cache
->fragment
->rap_grouping
&& (ret
= isom_add_sample_grouping( (isom_box_t
*)traf
, ISOM_GROUP_TYPE_RAP
)) < 0)
1704 || (traf
->cache
->fragment
->roll_grouping
&& (ret
= isom_add_sample_grouping( (isom_box_t
*)traf
, ISOM_GROUP_TYPE_ROLL
)) < 0) )
1707 else if( !traf
->file
1708 || !traf
->file
->initializer
1709 || !traf
->file
->initializer
->moov
1710 || !traf
->file
->initializer
->moov
->mvex
1713 return LSMASH_ERR_NAMELESS
;
1714 append_sample_func
= (int (*)( void *, lsmash_sample_t
* ))isom_append_fragment_sample_internal
;
1715 track_fragment
= traf
;
1717 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
, sample
->index
);
1719 return LSMASH_ERR_NAMELESS
;
1720 if( isom_is_lpcm_audio( sample_entry
) )
1722 uint32_t frame_size
= ((isom_audio_entry_t
*)sample_entry
)->constBytesPerAudioPacket
;
1723 if( sample
->length
== frame_size
)
1724 return append_sample_func( track_fragment
, sample
);
1725 else if( sample
->length
< frame_size
)
1726 return LSMASH_ERR_INVALID_DATA
;
1727 /* Append samples splitted into each LPCMFrame. */
1728 uint64_t dts
= sample
->dts
;
1729 uint64_t cts
= sample
->cts
;
1730 for( uint32_t offset
= 0; offset
< sample
->length
; offset
+= frame_size
)
1732 lsmash_sample_t
*lpcm_sample
= lsmash_create_sample( frame_size
);
1734 return LSMASH_ERR_MEMORY_ALLOC
;
1735 memcpy( lpcm_sample
->data
, sample
->data
+ offset
, frame_size
);
1736 lpcm_sample
->dts
= dts
++;
1737 lpcm_sample
->cts
= cts
++;
1738 lpcm_sample
->prop
= sample
->prop
;
1739 lpcm_sample
->index
= sample
->index
;
1740 int ret
= append_sample_func( track_fragment
, lpcm_sample
);
1743 lsmash_delete_sample( lpcm_sample
);
1747 lsmash_delete_sample( sample
);
1750 return append_sample_func( track_fragment
, sample
);