AMR importer: Apply refined bytestream reader.
[L-SMASH.git] / core / fragment.c
blob97d58a4b7dc25071cf34c9434844ad49c84acc1e
1 /*****************************************************************************
2 * fragment.c
3 *****************************************************************************
4 * Copyright (C) 2011-2014 L-SMASH project
6 * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
8 * Permission to use, copy, modify, and/or distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 *****************************************************************************/
21 /* This file is available under an ISC license. */
23 #include "common/internal.h" /* must be placed first */
25 #include <string.h>
26 #include "box.h"
27 #include "write.h"
29 static isom_sidx_t *isom_get_sidx( lsmash_file_t *file, uint32_t reference_ID )
31 if( reference_ID == 0 || !file )
32 return NULL;
33 for( lsmash_entry_t *entry = file->sidx_list.head; entry; entry = entry->next )
35 isom_sidx_t *sidx = (isom_sidx_t *)entry->data;
36 if( !sidx )
37 return NULL;
38 if( sidx->reference_ID == reference_ID )
39 return sidx;
41 return NULL;
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 )
50 if( !root )
51 return -1;
52 lsmash_file_t *file = root->file;
53 if( !file
54 || !file->bs
55 || !file->fragment
56 || !file->moov )
57 return -1;
58 /* Finish and write the current movie fragment before starting a new one. */
59 if( isom_finish_fragment_movie( file ) < 0 )
60 return -1;
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 )
66 return -1;
67 isom_moof_t *moof = isom_add_moof( file );
68 if( !isom_add_mfhd( moof ) )
69 return -1;
70 file->fragment->movie = moof;
71 moof->mfhd->sequence_number = ++ file->fragment->fragment_count;
72 if( file->moof_list.entry_count == 1 )
73 return 0;
74 /* Remove the previous movie fragment. */
75 if( file->moof_list.head )
76 isom_remove_box_by_itself( file->moof_list.head->data );
78 return 0;
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;
88 if( !trak
89 || !trak->cache
90 || !trak->cache->fragment
91 || !trak->mdia
92 || !trak->mdia->mdhd
93 || !trak->mdia->mdhd->timescale )
94 return -1;
95 uint64_t duration;
96 if( !trak->edts
97 || !trak->edts->elst
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);
103 else
105 duration = 0;
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;
109 if( !data )
110 return -1;
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);
116 else
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;
124 mehd->version = 1;
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 );
133 return ret;
136 static int isom_write_fragment_random_access_info( lsmash_file_t *file )
138 if( !file->moov->mvex )
139 return 0;
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;
148 if( !trex )
149 return -1;
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 );
153 if( !trak )
154 return -1;
155 if( !trak->edts
156 || !trak->edts->elst
157 || !trak->edts->elst->list
158 || !trak->edts->elst->list->head
159 || !trak->edts->elst->list->head->data )
160 continue;
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 );
165 if( !tfra )
166 continue;
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;
175 if( !rap )
177 /* Irregular case. Drop this entry. */
178 lsmash_entry_t *next = rap_entry->next;
179 lsmash_remove_entry_direct( tfra->list, rap_entry, NULL );
180 rap_entry = next;
181 continue;
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. */
185 while( edit )
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;
199 if( !edit_entry )
201 /* No more presentation. */
202 edit = NULL;
203 break;
205 edit = edit_entry->data;
207 if( !edit )
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. */
213 while( rap_entry )
215 lsmash_entry_t *next = rap_entry->next;
216 lsmash_remove_entry_direct( tfra->list, rap_entry, NULL );
217 rap_entry = next;
219 break;
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 )
232 return -1;
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
239 lsmash_file_t *file,
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;
247 if( !sidx )
248 continue;
249 if( isom_update_box_size( sidx ) == 0 )
250 return -1;
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;
258 a->first_offset = 0;
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;
265 return 0;
268 int isom_finish_final_fragment_movie
270 lsmash_file_t *file,
271 lsmash_adhoc_remux_t *remux
274 /* Output the final movie fragment. */
275 if( isom_finish_fragment_movie( file ) < 0 )
276 return -1;
277 if( file->bs->unseekable )
278 return 0;
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 )
290 return -1;
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;
296 if( !sidx )
297 continue;
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 )
306 return -1;
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 )
312 goto fail;
313 size_t read_num = size;
314 lsmash_bs_read_data( bs, buf[0], &read_num );
315 uint64_t read_pos = bs->offset;
316 /* */
317 if( lsmash_bs_write_seek( bs, file->fragment->first_moof_pos, SEEK_SET ) < 0 )
318 goto fail;
319 for( lsmash_entry_t *entry = file->sidx_list.head; entry; entry = entry->next )
321 isom_sidx_t *sidx = (isom_sidx_t *)entry->data;
322 if( !sidx )
323 continue;
324 if( isom_write_box( file->bs, (isom_box_t *)sidx ) < 0 )
325 return -1;
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 )
330 goto fail;
331 file->size += total_sidx_size;
332 lsmash_freep( &buf[0] );
333 /* Update moof_offset. */
334 if( file->mfra )
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;
338 if( !tfra )
339 continue;
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;
343 if( !rap )
344 continue;
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 ) )
351 return -1;
352 /* Set overall duration of the movie. */
353 return isom_set_fragment_overall_duration( file );
354 fail:
355 lsmash_free( buf[0] );
356 return -1;
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 ) )
369 return -1;
370 if( !file->bs->unseekable )
372 if( !isom_add_mehd( file->moov->mvex ) )
373 return -1;
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;
379 if( !trak
380 || !trak->cache
381 || !trak->tkhd
382 || !trak->mdia
383 || !trak->mdia->minf
384 || !trak->mdia->minf->stbl )
385 return -1;
386 isom_stbl_t *stbl = trak->mdia->minf->stbl;
387 if( !stbl->stts || !stbl->stts->list
388 || !stbl->stsz
389 || (stbl->stts->list->tail && !stbl->stts->list->tail->data)
390 || (stbl->stsz->list && stbl->stsz->list->head && !stbl->stsz->list->head->data) )
391 return -1;
392 isom_trex_t *trex = isom_add_trex( file->moov->mvex );
393 if( !trex )
394 return -1;
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
399 : 1;
400 trex->default_sample_duration = stbl->stts->list->tail
401 ? ((isom_stts_entry_t *)stbl->stts->list->tail->data)->sample_delta
402 : 1;
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;
406 if( stbl->sdtp
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;
419 if( !data )
420 return -1;
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;
437 return 0;
440 static int isom_prepare_random_access_info( lsmash_file_t *file )
442 if( file->bs->unseekable )
443 return 0;
444 if( !isom_add_mfra( file )
445 || !isom_add_mfro( file->mfra ) )
446 return -1;
447 return 0;
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 ) )
457 return -1;
458 file->mdat->manager &= ~(LSMASH_INCOMPLETE_BOX | LSMASH_WRITTEN_BOX);
459 if( isom_write_box( file->bs, (isom_box_t *)file->mdat ) < 0 )
460 return -1;
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;
468 return 0;
471 static int isom_finish_fragment_initial_movie( lsmash_file_t *file )
473 if( !file->moov )
474 return -1;
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;
479 if( !trak
480 || !trak->cache
481 || !trak->tkhd
482 || !trak->mdia
483 || !trak->mdia->mdhd
484 || !trak->mdia->minf
485 || !trak->mdia->minf->stbl
486 || isom_complement_data_reference( trak->mdia->minf ) < 0 )
487 return -1;
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 ) )
493 return -1;
494 if( isom_update_tkhd_duration( trak ) < 0 )
495 return -1;
497 else
498 trak->tkhd->duration = 0;
499 if( isom_update_bitrate_description( trak->mdia ) < 0 )
500 return -1;
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 )
506 return -1;
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 )
513 return -1;
516 if( file->mp4_version1 == 1 && isom_setup_iods( moov ) < 0 )
517 return -1;
518 if( isom_create_fragment_overall_default_settings( file ) < 0
519 || isom_prepare_random_access_info ( file ) < 0
520 || isom_establish_movie ( file ) < 0 )
521 return -1;
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 )
532 entry = entry->next;
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 )
538 return -1;
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;
549 else
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 ) )
557 return -1;
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 )
563 return -1;
564 file->size += preceding_size;
565 /* Output samples. */
566 if( isom_output_fragment_media_data( file ) < 0 )
567 return -1;
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;
575 return 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 )
593 if( !file->moov
594 || !file->fragment
595 || !file->fragment->pool )
596 return -1;
597 isom_moof_t *moof = file->fragment->movie;
598 if( !moof )
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 )
604 return 0;
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;
610 if( !traf
611 || !traf->tfhd
612 || !traf->file
613 || !traf->file->moov
614 || !traf->file->moov->mvex )
615 return -1;
616 isom_tfhd_t *tfhd = traf->tfhd;
617 isom_trex_t *trex = isom_get_trex( file->moov->mvex, tfhd->track_ID );
618 if( !trex )
619 return -1;
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 )
632 return -1;
633 isom_sample_flags_t *sample_flags;
634 if( trun->flags & ISOM_TR_FLAGS_SAMPLE_FLAGS_PRESENT )
636 if( !trun->optional )
637 return -1;
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;
641 if( !row )
642 return -1;
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 ];
651 else
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 );
668 if( i < 2 )
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;
707 break;
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;
716 else
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;
726 else
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;
730 else
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 )
745 return -1;
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 )
752 return -1;
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
765 * | | |
766 * | m | s i z e |
767 * | |_________|
768 * | o | |
769 * | | t y p e |
770 * | o |_________|
771 * | | |
772 * | f | d a t a |
773 * |___|_________|_ offset := the size of the Movie Fragment Box
774 * | | |
775 * | m | s i z e |
776 * | |_________|
777 * | d | |
778 * | | t y p e |
779 * | a |_________|_ offset := the data_offset of the first track run
780 * | | |
781 * | t | d a t a |
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 )
806 return -1;
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;
818 else
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 )
828 return -1;
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 ) )
839 return -1;
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 )
845 return -1;
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 )
854 return 0;
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 );
864 if( !trak
865 || !trak->mdia
866 || !trak->mdia->mdhd )
867 return -1;
868 assert( traf->tfdt );
869 if( !sidx )
871 sidx = isom_add_sidx( file );
872 if( !sidx )
873 return -1;
874 sidx->reference_ID = tfhd->track_ID;
875 sidx->timescale = trak->mdia->mdhd->timescale;
876 sidx->reserved = 0;
877 sidx->reference_count = 0;
878 if( isom_update_indexed_material_offset( file, sidx ) < 0 )
879 return -1;
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) );
883 if( !data )
884 return -1;
885 if( lsmash_add_entry( sidx->list, data ) < 0 )
887 lsmash_free( data );
888 return -1;
890 sidx->reference_count = sidx->list->entry_count;
891 data->reference_type = 0; /* media */
892 data->reference_size = file->size - moof->pos;
893 /* presentation */
894 uint64_t TSAP;
895 uint64_t TDEC;
896 uint64_t TEPT;
897 uint64_t TPTF;
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. */
912 TSAP = 0;
913 TDEC = 0;
914 TEPT = 0;
915 TPTF = 0;
916 /* */
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;
923 if( !edit )
924 continue;
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;
934 else
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;
942 else
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;
949 else
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 )
958 pts = edit_end_pts;
959 continue;
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);
974 else
975 TEPT = pts;
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);
985 else
986 TSAP = pts;
987 TDEC = TSAP;
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;
1000 else
1001 /* This subsegment is not present in this edit. */
1002 pts = edit_end_pts;
1005 else
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;
1028 data->SAP_type = 0;
1029 data->SAP_delta_time = 0;
1031 else
1033 data->starts_with_SAP = (subsegment->first_ra_number == 1);
1034 data->SAP_type = 0;
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 )
1040 data->SAP_type = 1;
1041 else if( TEPT == TDEC && TDEC == TSAP && TSAP < TPTF )
1042 data->SAP_type = 2;
1043 else if( TEPT < TDEC && TDEC == TSAP && TSAP <= TPTF )
1044 data->SAP_type = 3;
1045 else if( TEPT <= TPTF && TPTF < TDEC && TDEC == TSAP )
1046 data->SAP_type = 4;
1048 if( data->SAP_type == 0 )
1050 if( TEPT == TDEC && TDEC < TSAP )
1051 data->SAP_type = 5;
1052 else if( TEPT < TDEC && TDEC < TSAP )
1053 data->SAP_type = 6;
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;
1064 return 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 )
1076 return NULL;
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) );
1083 if( !row )
1084 return NULL;
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 ) )
1092 lsmash_free( row );
1093 return NULL;
1096 return row;
1098 uint32_t i = 0;
1099 for( lsmash_entry_t *entry = trun->optional->head; entry; entry = entry->next )
1101 row = (isom_trun_optional_row_t *)entry->data;
1102 if( !row )
1103 return NULL;
1104 if( ++i == sample_number )
1105 return row;
1107 return NULL;
1110 int lsmash_create_fragment_empty_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t duration )
1112 if( !root )
1113 return -1;
1114 lsmash_file_t *file = root->file;
1115 if( !file
1116 || !file->fragment
1117 || !file->fragment->movie
1118 || !file->moov )
1119 return -1;
1120 isom_trak_t *trak = isom_get_trak( file, track_ID );
1121 if( !trak
1122 || !trak->tkhd )
1123 return -1;
1124 isom_trex_t *trex = isom_get_trex( file->moov->mvex, track_ID );
1125 if( !trex )
1126 return -1;
1127 isom_moof_t *moof = file->fragment->movie;
1128 isom_traf_t *traf = isom_get_traf( moof, track_ID );
1129 if( traf )
1130 return -1;
1131 traf = isom_add_traf( moof );
1132 if( !isom_add_tfhd( traf ) )
1133 return -1;
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. */
1143 return 0;
1146 int isom_set_fragment_last_duration
1148 isom_traf_t *traf,
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 );
1158 if( !trex )
1159 return -1;
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;
1165 return 0;
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 );
1173 if( !trex )
1174 return -1;
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;
1181 if( trun->flags )
1183 isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, trun->sample_count );
1184 if( !row )
1185 return -1;
1186 row->sample_duration = last_duration;
1188 traf->cache->fragment->last_duration = last_duration;
1189 return 0;
1192 int isom_append_fragment_track_run
1194 lsmash_file_t *file,
1195 isom_chunk_t *chunk
1198 if( !chunk->pool || chunk->pool->size == 0 )
1199 return 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 )
1204 return -1;
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 )
1215 return -1;
1216 for( lsmash_entry_t *entry = traf->sgpd_list.head; entry; entry = entry->next )
1218 isom_sgpd_t *sgpd = (isom_sgpd_t *)entry->data;
1219 if( !sgpd )
1220 return -1;
1221 switch( sgpd->grouping_type )
1223 case ISOM_GROUP_TYPE_RAP :
1225 isom_rap_group_t *group = cache->rap;
1226 if( !group )
1228 if( traf->file->fragment )
1229 continue;
1230 else
1231 return -1;
1233 if( !group->random_access )
1234 continue;
1235 group->random_access->num_leading_samples_known = 1;
1236 break;
1238 case ISOM_GROUP_TYPE_ROLL :
1239 case ISOM_GROUP_TYPE_PROL :
1240 if( !cache->roll.pool )
1242 if( traf->file->fragment )
1243 continue;
1244 else
1245 return -1;
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 )
1249 return -1;
1250 break;
1251 default :
1252 break;
1255 return 0;
1258 int isom_flush_fragment_pooled_samples
1260 lsmash_file_t *file,
1261 uint32_t track_ID,
1262 uint32_t last_sample_duration
1265 isom_traf_t *traf = isom_get_traf( file->fragment->movie, track_ID );
1266 if( !traf )
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. */
1269 return 0;
1270 if( !traf->cache
1271 || !traf->cache->fragment )
1272 return -1;
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 )
1285 return -1;
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 )
1302 return -1;
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;
1319 if( trun->flags )
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 );
1323 if( !row )
1324 return -1;
1325 row->sample_duration = duration;
1327 traf->cache->fragment->last_duration = duration;
1328 return 0;
1331 static isom_sample_flags_t isom_generate_fragment_sample_flags( lsmash_sample_t *sample )
1333 isom_sample_flags_t flags;
1334 flags.reserved = 0;
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;
1342 return flags;
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 );
1349 if( !trex )
1350 return -1;
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 )
1359 return -1;
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 )
1365 return -1;
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 )
1371 if( 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 );
1384 if( !trun )
1385 return -1;
1387 else
1389 if( !traf->trun_list.tail
1390 || !traf->trun_list.tail->data )
1391 return -1;
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 );
1415 if( !tfra )
1417 tfra = isom_add_tfra( file->mfra );
1418 if( !tfra )
1419 return -1;
1420 tfra->track_ID = tfhd->track_ID;
1422 if( !tfra->list )
1424 tfra->list = lsmash_create_entry_list();
1425 if( !tfra->list )
1426 return -1;
1428 isom_tfra_location_time_entry_t *rap = lsmash_malloc( sizeof(isom_tfra_location_time_entry_t) );
1429 if( !rap )
1430 return -1;
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 ) )
1439 lsmash_free( rap );
1440 return -1;
1442 tfra->number_of_entry = tfra->list->entry_count;
1443 int length;
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 ) )
1458 return -1;
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 )
1486 trun->version = 1;
1489 if( trun->flags )
1491 isom_trun_optional_row_t *row = isom_request_trun_optional_row( trun, tfhd, trun->sample_count );
1492 if( !row )
1493 return -1;
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 )
1501 return -1;
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 )
1510 return -1;
1511 uint32_t sample_duration = sample->dts - prev_dts;
1512 if( isom_update_fragment_previous_sample_duration( traf, trex, sample_duration ) < 0 )
1513 return -1;
1515 /* Cache */
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;
1526 else
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 );
1549 else
1550 subsegment->decodable = 0;
1552 return delimit;
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 );
1561 if( delimit < 0 )
1562 return -1;
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 ) )
1567 return -1;
1568 trak->cache->fragment->has_samples = 1;
1569 trak->cache->fragment->sample_count += 1;
1570 return 0;
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 );
1578 if( delimit < 0 )
1579 return -1;
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 ) )
1584 return -1;
1585 traf->cache->fragment->has_samples = 1;
1586 traf->cache->fragment->sample_count += 1;
1587 return 0;
1590 int isom_append_fragment_sample
1592 lsmash_file_t *file,
1593 uint32_t track_ID,
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 );
1600 if( !trak
1601 || !trak->file
1602 || !trak->cache
1603 || !trak->cache->fragment
1604 || !trak->tkhd
1605 || !trak->mdia
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 )
1612 return -1;
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 )
1619 return -1;
1620 append_sample_func = (int (*)( void *, lsmash_sample_t * ))isom_append_fragment_sample_internal_initial;
1621 track_fragment = trak;
1623 else
1625 isom_traf_t *traf = isom_get_traf( fragment->movie, track_ID );
1626 if( !traf )
1628 traf = isom_add_traf( fragment->movie );
1629 if( !isom_add_tfhd( traf ) )
1630 return -1;
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) )
1637 return -1;
1639 else if( !traf->file
1640 || !traf->file->moov
1641 || !traf->file->moov->mvex
1642 || !traf->cache
1643 || !traf->tfhd )
1644 return -1;
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 );
1649 if( !sample_entry )
1650 return -1;
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 )
1657 return -1;
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 );
1664 if( !lpcm_sample )
1665 return -1;
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 );
1674 return -1;
1677 lsmash_delete_sample( sample );
1678 return 0;
1680 return append_sample_func( track_fragment, sample );