1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2010-2017 L-SMASH project
6 * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
7 * Contributors: Takashi Hirata <silverfilain@gmail.com>
9 * Permission to use, copy, modify, and/or distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 *****************************************************************************/
22 /* This file is available under an ISC license. */
24 #include "common/internal.h" /* must be placed first */
31 #include "box_default.h"
38 #include "codecs/mp4a.h"
39 #include "codecs/mp4sys.h"
40 #include "codecs/description.h"
43 int isom_check_initializer_present( lsmash_root_t
*root
)
45 if( LSMASH_IS_NON_EXISTING_BOX( root
)
46 || LSMASH_IS_NON_EXISTING_BOX( root
->file
)
47 || LSMASH_IS_NON_EXISTING_BOX( root
->file
->initializer
) )
48 return LSMASH_ERR_NAMELESS
;
52 isom_trak_t
*isom_get_trak( lsmash_file_t
*file
, uint32_t track_ID
)
55 || LSMASH_IS_NON_EXISTING_BOX( file
->moov
)
56 || file
!= file
->initializer
)
57 return isom_non_existing_trak();
58 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
60 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
61 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
62 || LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
63 return isom_non_existing_trak();
64 if( trak
->tkhd
->track_ID
== track_ID
)
67 return isom_non_existing_trak();
70 isom_trex_t
*isom_get_trex( isom_mvex_t
*mvex
, uint32_t track_ID
)
72 if( track_ID
== 0 || LSMASH_IS_NON_EXISTING_BOX( mvex
) )
73 return isom_non_existing_trex();
74 for( lsmash_entry_t
*entry
= mvex
->trex_list
.head
; entry
; entry
= entry
->next
)
76 isom_trex_t
*trex
= (isom_trex_t
*)entry
->data
;
77 if( LSMASH_IS_NON_EXISTING_BOX( trex
) )
78 return isom_non_existing_trex();
79 if( trex
->track_ID
== track_ID
)
82 return isom_non_existing_trex();
85 isom_traf_t
*isom_get_traf( isom_moof_t
*moof
, uint32_t track_ID
)
87 if( track_ID
== 0 || LSMASH_IS_NON_EXISTING_BOX( moof
) )
88 return isom_non_existing_traf();
89 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
91 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
92 if( LSMASH_IS_NON_EXISTING_BOX( traf
)
93 || LSMASH_IS_NON_EXISTING_BOX( traf
->tfhd
) )
94 return isom_non_existing_traf();
95 if( traf
->tfhd
->track_ID
== track_ID
)
98 return isom_non_existing_traf();
101 isom_tfra_t
*isom_get_tfra( isom_mfra_t
*mfra
, uint32_t track_ID
)
103 if( track_ID
== 0 || LSMASH_IS_NON_EXISTING_BOX( mfra
) )
104 return isom_non_existing_tfra();
105 for( lsmash_entry_t
*entry
= mfra
->tfra_list
.head
; entry
; entry
= entry
->next
)
107 isom_tfra_t
*tfra
= (isom_tfra_t
*)entry
->data
;
108 if( LSMASH_IS_NON_EXISTING_BOX( tfra
) )
109 return isom_non_existing_tfra();
110 if( tfra
->track_ID
== track_ID
)
113 return isom_non_existing_tfra();
116 static int isom_add_elst_entry( isom_elst_t
*elst
, uint64_t segment_duration
, int64_t media_time
, int32_t media_rate
)
118 assert( LSMASH_IS_EXISTING_BOX( elst
->file
) );
119 isom_elst_entry_t
*data
= lsmash_malloc( sizeof(isom_elst_entry_t
) );
121 return LSMASH_ERR_MEMORY_ALLOC
;
122 data
->segment_duration
= segment_duration
;
123 data
->media_time
= media_time
;
124 data
->media_rate
= media_rate
;
125 if( lsmash_list_add_entry( elst
->list
, data
) < 0 )
128 return LSMASH_ERR_MEMORY_ALLOC
;
130 if( !elst
->file
->undefined_64_ver
131 && (data
->segment_duration
> UINT32_MAX
132 || data
->media_time
> INT32_MAX
133 || data
->media_time
< INT32_MIN
) )
138 /* This function returns 0 if failed, sample_entry_number if succeeded. */
139 int lsmash_add_sample_entry( lsmash_root_t
*root
, uint32_t track_ID
, void *summary
)
141 if( LSMASH_IS_NON_EXISTING_BOX( root
) || !summary
142 || ((lsmash_summary_t
*)summary
)->data_ref_index
== 0
143 || ((lsmash_summary_t
*)summary
)->data_ref_index
> UINT16_MAX
)
145 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
146 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
147 || LSMASH_IS_NON_EXISTING_BOX( trak
->file
)
148 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->hdlr
)
149 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->stbl
->stsd
) )
151 isom_stsd_t
*stsd
= trak
->mdia
->minf
->stbl
->stsd
;
152 lsmash_media_type media_type
= trak
->mdia
->hdlr
->componentSubtype
;
153 if( isom_setup_sample_description( stsd
, media_type
, (lsmash_summary_t
*)summary
) < 0 )
156 return stsd
->list
.entry_count
;
159 static int isom_add_stts_entry( isom_stbl_t
*stbl
, uint32_t sample_delta
)
161 assert( LSMASH_IS_EXISTING_BOX( stbl
->stts
) );
162 if( !stbl
->stts
->list
)
163 return LSMASH_ERR_NAMELESS
;
164 isom_stts_entry_t
*data
= lsmash_malloc( sizeof(isom_stts_entry_t
) );
166 return LSMASH_ERR_MEMORY_ALLOC
;
167 data
->sample_count
= 1;
168 data
->sample_delta
= sample_delta
;
169 if( lsmash_list_add_entry( stbl
->stts
->list
, data
) < 0 )
172 return LSMASH_ERR_MEMORY_ALLOC
;
177 static int isom_add_ctts_entry( isom_stbl_t
*stbl
, uint32_t sample_count
, uint32_t sample_offset
)
179 assert( LSMASH_IS_EXISTING_BOX( stbl
->ctts
) );
180 if( !stbl
->ctts
->list
)
181 return LSMASH_ERR_NAMELESS
;
182 isom_ctts_entry_t
*data
= lsmash_malloc( sizeof(isom_ctts_entry_t
) );
184 return LSMASH_ERR_MEMORY_ALLOC
;
185 data
->sample_count
= sample_count
;
186 data
->sample_offset
= sample_offset
;
187 if( lsmash_list_add_entry( stbl
->ctts
->list
, data
) < 0 )
190 return LSMASH_ERR_MEMORY_ALLOC
;
195 static int isom_add_stsc_entry( isom_stbl_t
*stbl
, uint32_t first_chunk
, uint32_t samples_per_chunk
, uint32_t sample_description_index
)
197 assert( LSMASH_IS_EXISTING_BOX( stbl
->stsc
) );
198 if( !stbl
->stsc
->list
)
199 return LSMASH_ERR_NAMELESS
;
200 isom_stsc_entry_t
*data
= lsmash_malloc( sizeof(isom_stsc_entry_t
) );
202 return LSMASH_ERR_MEMORY_ALLOC
;
203 data
->first_chunk
= first_chunk
;
204 data
->samples_per_chunk
= samples_per_chunk
;
205 data
->sample_description_index
= sample_description_index
;
206 if( lsmash_list_add_entry( stbl
->stsc
->list
, data
) < 0 )
209 return LSMASH_ERR_MEMORY_ALLOC
;
214 static int isom_add_stsz_entry( isom_stbl_t
*stbl
, uint32_t entry_size
)
216 assert( LSMASH_IS_EXISTING_BOX( stbl
) );
217 if( LSMASH_IS_NON_EXISTING_BOX( stbl
->stsz
) )
218 return LSMASH_ERR_NAMELESS
;
219 isom_stsz_t
*stsz
= stbl
->stsz
;
220 /* retrieve initial sample_size */
221 if( stsz
->sample_count
== 0 )
222 stsz
->sample_size
= entry_size
;
223 /* if it seems constant sample size at present, update sample_count only */
224 if( !stsz
->list
&& stsz
->sample_size
== entry_size
)
226 ++ stsz
->sample_count
;
229 /* found sample_size varies, create sample_size list */
232 stsz
->list
= lsmash_list_create_simple();
234 return LSMASH_ERR_MEMORY_ALLOC
;
235 for( uint32_t i
= 0; i
< stsz
->sample_count
; i
++ )
237 isom_stsz_entry_t
*data
= lsmash_malloc( sizeof(isom_stsz_entry_t
) );
239 return LSMASH_ERR_MEMORY_ALLOC
;
240 data
->entry_size
= stsz
->sample_size
;
241 if( lsmash_list_add_entry( stsz
->list
, data
) < 0 )
244 return LSMASH_ERR_MEMORY_ALLOC
;
247 stsz
->sample_size
= 0;
249 isom_stsz_entry_t
*data
= lsmash_malloc( sizeof(isom_stsz_entry_t
) );
251 return LSMASH_ERR_MEMORY_ALLOC
;
252 data
->entry_size
= entry_size
;
253 if( lsmash_list_add_entry( stsz
->list
, data
) < 0 )
256 return LSMASH_ERR_MEMORY_ALLOC
;
258 ++ stsz
->sample_count
;
262 static int isom_add_stss_entry( isom_stbl_t
*stbl
, uint32_t sample_number
)
264 assert( LSMASH_IS_EXISTING_BOX( stbl
->stss
) );
265 if( !stbl
->stss
->list
)
266 return LSMASH_ERR_NAMELESS
;
267 isom_stss_entry_t
*data
= lsmash_malloc( sizeof(isom_stss_entry_t
) );
269 return LSMASH_ERR_MEMORY_ALLOC
;
270 data
->sample_number
= sample_number
;
271 if( lsmash_list_add_entry( stbl
->stss
->list
, data
) < 0 )
274 return LSMASH_ERR_MEMORY_ALLOC
;
279 static int isom_add_stps_entry( isom_stbl_t
*stbl
, uint32_t sample_number
)
281 assert( LSMASH_IS_EXISTING_BOX( stbl
->stps
) );
282 if( !stbl
->stps
->list
)
283 return LSMASH_ERR_NAMELESS
;
284 isom_stps_entry_t
*data
= lsmash_malloc( sizeof(isom_stps_entry_t
) );
286 return LSMASH_ERR_MEMORY_ALLOC
;
287 data
->sample_number
= sample_number
;
288 if( lsmash_list_add_entry( stbl
->stps
->list
, data
) < 0 )
291 return LSMASH_ERR_MEMORY_ALLOC
;
296 /* Between ISOBMFF and QTFF, the most significant 2-bit has different meaning.
297 * For the maximum compatibility, we set 0 to the most significant 2-bit when compatible with
298 * both ISOBMFF with AVCFF extensions and QTFF.
299 * compatibility == 0 -> neither AVCFF extensions nor QTFF compatible
300 * compatibility == 1 -> AVCFF extensions compatible
301 * compatibility == 2 -> QTFF compatible
302 * compatibility == 3 -> both AVCFF extensions and QTFF compatible */
303 static int isom_add_sdtp_entry( isom_box_t
*parent
, lsmash_sample_property_t
*prop
, int compatibility
)
305 if( !prop
|| LSMASH_IS_NON_EXISTING_BOX( parent
) )
306 return LSMASH_ERR_NAMELESS
;
307 isom_sdtp_t
*sdtp
= isom_non_existing_sdtp();
308 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_STBL
) )
309 sdtp
= ((isom_stbl_t
*)parent
)->sdtp
;
310 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
311 sdtp
= ((isom_traf_t
*)parent
)->sdtp
;
314 if( LSMASH_IS_NON_EXISTING_BOX( sdtp
)
316 return LSMASH_ERR_NAMELESS
;
317 isom_sdtp_entry_t
*data
= lsmash_malloc( sizeof(isom_sdtp_entry_t
) );
319 return LSMASH_ERR_MEMORY_ALLOC
;
320 if( compatibility
== 1 )
321 data
->is_leading
= prop
->leading
& 0x03;
322 else if( compatibility
== 2 )
323 data
->is_leading
= prop
->allow_earlier
& 0x03;
326 data
->is_leading
= 0;
327 assert( compatibility
== 3 );
329 data
->sample_depends_on
= prop
->independent
& 0x03;
330 data
->sample_is_depended_on
= prop
->disposable
& 0x03;
331 data
->sample_has_redundancy
= prop
->redundant
& 0x03;
332 if( lsmash_list_add_entry( sdtp
->list
, data
) < 0 )
335 return LSMASH_ERR_MEMORY_ALLOC
;
340 static int isom_add_co64_entry( isom_stbl_t
*stbl
, uint64_t chunk_offset
)
342 assert( LSMASH_IS_EXISTING_BOX( stbl
->stco
) );
343 if( !stbl
->stco
->list
)
344 return LSMASH_ERR_NAMELESS
;
345 isom_co64_entry_t
*data
= lsmash_malloc( sizeof(isom_co64_entry_t
) );
347 return LSMASH_ERR_MEMORY_ALLOC
;
348 data
->chunk_offset
= chunk_offset
;
349 if( lsmash_list_add_entry( stbl
->stco
->list
, data
) < 0 )
352 return LSMASH_ERR_MEMORY_ALLOC
;
357 static int isom_convert_stco_to_co64( isom_stbl_t
*stbl
)
359 assert( LSMASH_IS_EXISTING_BOX( stbl
->stco
) );
362 isom_stco_t
*stco
= stbl
->stco
;
363 LSMASH_MAKE_BOX_NON_EXISTING( stbl
->stco
);
364 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_co64( stbl
) ) )
366 err
= LSMASH_ERR_NAMELESS
;
369 /* move chunk_offset to co64 from stco */
370 for( lsmash_entry_t
*entry
= stco
->list
->head
; entry
; entry
= entry
->next
)
372 isom_stco_entry_t
*data
= (isom_stco_entry_t
*)entry
->data
;
373 if( (err
= isom_add_co64_entry( stbl
, data
->chunk_offset
)) < 0 )
377 isom_remove_box_by_itself( stco
);
381 static int isom_add_stco_entry( isom_stbl_t
*stbl
, uint64_t chunk_offset
)
383 if( !stbl
->stco
->list
)
384 return LSMASH_ERR_NAMELESS
;
385 if( stbl
->stco
->large_presentation
)
386 return isom_add_co64_entry( stbl
, chunk_offset
);
387 if( chunk_offset
> UINT32_MAX
)
389 int err
= isom_convert_stco_to_co64( stbl
);
392 return isom_add_co64_entry( stbl
, chunk_offset
);
394 isom_stco_entry_t
*data
= lsmash_malloc( sizeof(isom_stco_entry_t
) );
396 return LSMASH_ERR_MEMORY_ALLOC
;
397 data
->chunk_offset
= (uint32_t)chunk_offset
;
398 if( lsmash_list_add_entry( stbl
->stco
->list
, data
) < 0 )
401 return LSMASH_ERR_MEMORY_ALLOC
;
406 static isom_sgpd_t
*isom_get_sample_group_description_common( lsmash_entry_list_t
*list
, uint32_t grouping_type
)
408 for( lsmash_entry_t
*entry
= list
->head
; entry
; entry
= entry
->next
)
410 isom_sgpd_t
*sgpd
= (isom_sgpd_t
*)entry
->data
;
411 if( LSMASH_IS_NON_EXISTING_BOX( sgpd
)
413 return isom_non_existing_sgpd();
414 if( sgpd
->grouping_type
== grouping_type
)
417 return isom_non_existing_sgpd();
420 static isom_sbgp_t
*isom_get_sample_to_group_common( lsmash_entry_list_t
*list
, uint32_t grouping_type
)
422 for( lsmash_entry_t
*entry
= list
->head
; entry
; entry
= entry
->next
)
424 isom_sbgp_t
*sbgp
= (isom_sbgp_t
*)entry
->data
;
425 if( LSMASH_IS_NON_EXISTING_BOX( sbgp
)
427 return isom_non_existing_sbgp();
428 if( sbgp
->grouping_type
== grouping_type
)
431 return isom_non_existing_sbgp();
434 isom_sgpd_t
*isom_get_sample_group_description( isom_stbl_t
*stbl
, uint32_t grouping_type
)
436 return isom_get_sample_group_description_common( &stbl
->sgpd_list
, grouping_type
);
439 isom_sbgp_t
*isom_get_sample_to_group( isom_stbl_t
*stbl
, uint32_t grouping_type
)
441 return isom_get_sample_to_group_common( &stbl
->sbgp_list
, grouping_type
);
444 isom_sgpd_t
*isom_get_roll_recovery_sample_group_description( lsmash_entry_list_t
*list
)
447 if( ((sgpd
= isom_get_sample_group_description_common( list
, ISOM_GROUP_TYPE_ROLL
)), LSMASH_IS_EXISTING_BOX( sgpd
))
448 || ((sgpd
= isom_get_sample_group_description_common( list
, ISOM_GROUP_TYPE_PROL
)), LSMASH_IS_EXISTING_BOX( sgpd
)) )
450 return isom_non_existing_sgpd();
453 isom_sbgp_t
*isom_get_roll_recovery_sample_to_group( lsmash_entry_list_t
*list
)
456 if( ((sbgp
= isom_get_sample_to_group_common( list
, ISOM_GROUP_TYPE_ROLL
)), LSMASH_IS_EXISTING_BOX( sbgp
))
457 || ((sbgp
= isom_get_sample_to_group_common( list
, ISOM_GROUP_TYPE_PROL
)), LSMASH_IS_EXISTING_BOX( sbgp
)) )
459 return isom_non_existing_sbgp();
462 isom_sgpd_t
*isom_get_fragment_sample_group_description( isom_traf_t
*traf
, uint32_t grouping_type
)
464 return isom_get_sample_group_description_common( &traf
->sgpd_list
, grouping_type
);
467 isom_sbgp_t
*isom_get_fragment_sample_to_group( isom_traf_t
*traf
, uint32_t grouping_type
)
469 return isom_get_sample_to_group_common( &traf
->sbgp_list
, grouping_type
);
472 static isom_rap_entry_t
*isom_add_rap_group_entry( isom_sgpd_t
*sgpd
)
474 if( LSMASH_IS_NON_EXISTING_BOX( sgpd
) )
476 isom_rap_entry_t
*data
= lsmash_malloc( sizeof(isom_rap_entry_t
) );
479 data
->description_length
= 0;
480 data
->num_leading_samples_known
= 0;
481 data
->num_leading_samples
= 0;
482 if( lsmash_list_add_entry( sgpd
->list
, data
) < 0 )
490 static isom_roll_entry_t
*isom_add_roll_group_entry( isom_sgpd_t
*sgpd
, int16_t roll_distance
)
492 if( LSMASH_IS_NON_EXISTING_BOX( sgpd
) )
494 isom_roll_entry_t
*data
= lsmash_malloc( sizeof(isom_roll_entry_t
) );
497 data
->description_length
= 0;
498 data
->roll_distance
= roll_distance
;
499 if( lsmash_list_add_entry( sgpd
->list
, data
) < 0 )
507 static isom_group_assignment_entry_t
*isom_add_group_assignment_entry( isom_sbgp_t
*sbgp
, uint32_t sample_count
, uint32_t group_description_index
)
509 if( LSMASH_IS_NON_EXISTING_BOX( sbgp
) )
511 isom_group_assignment_entry_t
*data
= lsmash_malloc( sizeof(isom_group_assignment_entry_t
) );
514 data
->sample_count
= sample_count
;
515 data
->group_description_index
= group_description_index
;
516 if( lsmash_list_add_entry( sbgp
->list
, data
) < 0 )
524 static uint32_t isom_get_sample_count_from_sample_table( isom_stbl_t
*stbl
)
526 if( LSMASH_IS_EXISTING_BOX( stbl
->stsz
) )
527 return stbl
->stsz
->sample_count
;
528 else if( LSMASH_IS_EXISTING_BOX( stbl
->stz2
) )
529 return stbl
->stz2
->sample_count
;
534 uint32_t isom_get_sample_count( isom_trak_t
*trak
)
536 return isom_get_sample_count_from_sample_table( trak
->mdia
->minf
->stbl
);
539 static uint64_t isom_get_dts( isom_stts_t
*stts
, uint32_t sample_number
)
545 lsmash_entry_t
*entry
;
546 isom_stts_entry_t
*data
= NULL
;
547 for( entry
= stts
->list
->head
; entry
; entry
= entry
->next
)
549 data
= (isom_stts_entry_t
*)entry
->data
;
552 if( i
+ data
->sample_count
> sample_number
)
554 dts
+= (uint64_t)data
->sample_delta
* data
->sample_count
;
555 i
+= data
->sample_count
;
559 dts
+= (uint64_t)data
->sample_delta
* (sample_number
- i
);
564 static uint64_t isom_get_cts( isom_stts_t
*stts
, isom_ctts_t
*ctts
, uint32_t sample_number
)
568 if( LSMASH_IS_NON_EXISTING_BOX( ctts
) )
569 return isom_get_dts( stts
, sample_number
);
570 uint32_t i
= 1; /* This can be 0 (and then condition below shall be changed) but I dare use same algorithm with isom_get_dts. */
571 lsmash_entry_t
*entry
;
572 isom_ctts_entry_t
*data
= NULL
;
573 if( sample_number
== 0 )
575 for( entry
= ctts
->list
->head
; entry
; entry
= entry
->next
)
577 data
= (isom_ctts_entry_t
*)entry
->data
;
580 if( i
+ data
->sample_count
> sample_number
)
582 i
+= data
->sample_count
;
586 return isom_get_dts( stts
, sample_number
) + data
->sample_offset
;
590 static int isom_replace_last_sample_delta( isom_stbl_t
*stbl
, uint32_t sample_delta
)
592 assert( LSMASH_IS_EXISTING_BOX( stbl
->stts
) );
593 if( !stbl
->stts
->list
594 || !stbl
->stts
->list
->tail
595 || !stbl
->stts
->list
->tail
->data
)
596 return LSMASH_ERR_NAMELESS
;
597 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)stbl
->stts
->list
->tail
->data
;
598 if( sample_delta
!= last_stts_data
->sample_delta
)
600 if( last_stts_data
->sample_count
> 1 )
602 last_stts_data
->sample_count
-= 1;
603 int err
= isom_add_stts_entry( stbl
, sample_delta
);
608 last_stts_data
->sample_delta
= sample_delta
;
613 static int isom_update_mdhd_duration( isom_trak_t
*trak
, uint32_t last_sample_delta
)
615 assert( LSMASH_IS_EXISTING_BOX( trak
) );
616 if( LSMASH_IS_NON_EXISTING_BOX( trak
->file
)
617 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
)
619 || !trak
->mdia
->minf
->stbl
->stts
->list
)
620 return LSMASH_ERR_INVALID_DATA
;
621 lsmash_file_t
*file
= trak
->file
;
622 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
623 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
624 isom_stts_t
*stts
= stbl
->stts
;
625 isom_ctts_t
*ctts
= stbl
->ctts
;
626 isom_cslg_t
*cslg
= stbl
->cslg
;
628 uint32_t sample_count
= isom_get_sample_count( trak
);
629 if( sample_count
== 0 )
631 /* Return error if non-fragmented movie has no samples. */
632 if( !file
->fragment
&& !stts
->list
->entry_count
)
633 return LSMASH_ERR_INVALID_DATA
;
636 /* Now we have at least 1 sample, so do stts_entry. */
637 lsmash_entry_t
*last_stts
= stts
->list
->tail
;
638 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)last_stts
->data
;
639 if( sample_count
== 1 )
640 mdhd
->duration
= last_stts_data
->sample_delta
;
641 /* Now we have at least 2 samples,
642 * but dunno whether 1 stts_entry which has 2 samples or 2 stts_entry which has 1 samle each. */
643 else if( LSMASH_IS_NON_EXISTING_BOX( ctts
) )
645 /* use dts instead of cts */
646 mdhd
->duration
= isom_get_dts( stts
, sample_count
);
648 if( last_sample_delta
)
650 mdhd
->duration
+= last_sample_delta
;
651 if( (err
= isom_replace_last_sample_delta( stbl
, last_sample_delta
)) < 0 )
654 else if( last_stts_data
->sample_count
> 1 )
655 mdhd
->duration
+= last_stts_data
->sample_delta
; /* no need to update last_stts_data->sample_delta */
658 /* Remove the last entry. */
659 if( (err
= lsmash_list_remove_entry_tail( stts
->list
)) < 0 )
661 /* copy the previous sample_delta. */
662 ++ ((isom_stts_entry_t
*)stts
->list
->tail
->data
)->sample_count
;
663 mdhd
->duration
+= ((isom_stts_entry_t
*)stts
->list
->tail
->data
)->sample_delta
;
669 || ctts
->list
->entry_count
== 0 )
670 return LSMASH_ERR_INVALID_DATA
;
672 uint64_t max_cts
= 0;
673 uint64_t max2_cts
= 0;
674 uint64_t min_cts
= LSMASH_TIMESTAMP_UNDEFINED
;
675 int64_t max_offset
= 0;
676 int64_t min_offset
= UINT32_MAX
;
677 int32_t ctd_shift
= trak
->cache
->timestamp
.ctd_shift
;
680 lsmash_entry_t
*stts_entry
= stts
->list
->head
;
681 lsmash_entry_t
*ctts_entry
= ctts
->list
->head
;
682 for( uint32_t i
= 0; i
< sample_count
; i
++ )
684 if( !ctts_entry
|| !stts_entry
)
685 return LSMASH_ERR_INVALID_DATA
;
686 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
687 isom_ctts_entry_t
*ctts_data
= (isom_ctts_entry_t
*)ctts_entry
->data
;
688 if( !stts_data
|| !ctts_data
)
689 return LSMASH_ERR_INVALID_DATA
;
690 if( ctts_data
->sample_offset
!= ISOM_NON_OUTPUT_SAMPLE_OFFSET
)
695 /* Anyway, add composition to decode timeline shift for calculating maximum and minimum CTS correctly. */
696 int64_t sample_offset
= (int32_t)ctts_data
->sample_offset
;
697 cts
= dts
+ sample_offset
+ ctd_shift
;
698 max_offset
= LSMASH_MAX( max_offset
, sample_offset
);
699 min_offset
= LSMASH_MIN( min_offset
, sample_offset
);
703 cts
= dts
+ ctts_data
->sample_offset
;
704 max_offset
= LSMASH_MAX( max_offset
, ctts_data
->sample_offset
);
705 min_offset
= LSMASH_MIN( min_offset
, ctts_data
->sample_offset
);
707 min_cts
= LSMASH_MIN( min_cts
, cts
);
713 else if( max2_cts
< cts
)
716 dts
+= stts_data
->sample_delta
;
717 /* If finished sample_count of current entry, move to next. */
718 if( ++j
== ctts_data
->sample_count
)
720 ctts_entry
= ctts_entry
->next
;
723 if( ++k
== stts_data
->sample_count
)
725 stts_entry
= stts_entry
->next
;
729 dts
-= last_stts_data
->sample_delta
;
731 /* Overall presentation is extended exceeding this initial movie.
732 * So, any players shall display the movie exceeding the durations
733 * indicated in Movie Header Box, Track Header Boxes and Media Header Boxes.
734 * Samples up to the duration indicated in Movie Extends Header Box shall be displayed.
735 * In the absence of Movie Extends Header Box, all samples shall be displayed. */
736 mdhd
->duration
+= dts
+ last_sample_delta
;
739 if( !last_sample_delta
)
741 /* The spec allows an arbitrary value for the duration of the last sample. So, we pick last-1 sample's. */
742 last_sample_delta
= max_cts
- max2_cts
;
744 if( min_cts
!= LSMASH_TIMESTAMP_UNDEFINED
)
745 mdhd
->duration
= max_cts
- min_cts
+ last_sample_delta
;
746 /* To match dts and media duration, update stts and mdhd relatively. */
747 if( mdhd
->duration
> dts
)
748 last_sample_delta
= mdhd
->duration
- dts
;
750 mdhd
->duration
= dts
+ last_sample_delta
; /* media duration must not less than last dts. */
752 int err
= isom_replace_last_sample_delta( stbl
, last_sample_delta
);
755 /* Explicit composition information and timeline shifting */
756 if( LSMASH_IS_EXISTING_BOX( cslg
) || file
->qt_compatible
|| file
->max_isom_version
>= 4 )
760 /* Remove composition to decode timeline shift. */
761 max_cts
-= ctd_shift
;
762 max2_cts
-= ctd_shift
;
763 min_cts
-= ctd_shift
;
765 int64_t composition_end_time
= max_cts
+ (max_cts
- max2_cts
);
767 && min_cts
!= LSMASH_TIMESTAMP_UNDEFINED
768 && (min_offset
<= INT32_MAX
) && (min_offset
>= INT32_MIN
)
769 && (max_offset
<= INT32_MAX
) && (max_offset
>= INT32_MIN
)
770 && ((int64_t)min_cts
<= INT32_MAX
) && (composition_end_time
<= INT32_MAX
) )
772 if( LSMASH_IS_NON_EXISTING_BOX( cslg
) )
774 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_cslg( trak
->mdia
->minf
->stbl
) ) )
775 return LSMASH_ERR_NAMELESS
;
778 cslg
->compositionToDTSShift
= ctd_shift
;
779 cslg
->leastDecodeToDisplayDelta
= min_offset
;
780 cslg
->greatestDecodeToDisplayDelta
= max_offset
;
781 cslg
->compositionStartTime
= min_cts
;
782 cslg
->compositionEndTime
= composition_end_time
;
785 isom_remove_box_by_itself( cslg
);
788 if( mdhd
->duration
> UINT32_MAX
&& !file
->undefined_64_ver
)
793 static int isom_update_mvhd_duration( isom_moov_t
*moov
)
795 assert( LSMASH_IS_EXISTING_BOX( moov
) );
796 if( LSMASH_IS_NON_EXISTING_BOX( moov
->mvhd
->file
) )
797 return LSMASH_ERR_INVALID_DATA
;
798 isom_mvhd_t
*mvhd
= moov
->mvhd
;
800 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
802 /* We pick maximum track duration as movie duration. */
803 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
804 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
805 || LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
806 return LSMASH_ERR_INVALID_DATA
;
807 mvhd
->duration
= entry
!= moov
->trak_list
.head
808 ? LSMASH_MAX( mvhd
->duration
, trak
->tkhd
->duration
)
809 : trak
->tkhd
->duration
;
811 if( mvhd
->duration
> UINT32_MAX
&& !mvhd
->file
->undefined_64_ver
)
816 int isom_update_tkhd_duration( isom_trak_t
*trak
)
818 assert( LSMASH_IS_EXISTING_BOX( trak
) );
819 if( LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
)
820 || LSMASH_IS_NON_EXISTING_BOX( trak
->file
->moov
->mvhd
) )
821 return LSMASH_ERR_INVALID_DATA
;
822 lsmash_file_t
*file
= trak
->file
;
823 isom_tkhd_t
*tkhd
= trak
->tkhd
;
826 || LSMASH_IS_NON_EXISTING_BOX( trak
->edts
->elst
) )
828 /* If this presentation might be extended or this track doesn't have edit list, calculate track duration from media duration. */
829 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
)
830 || trak
->mdia
->mdhd
->timescale
== 0 )
831 return LSMASH_ERR_INVALID_DATA
;
832 if( trak
->mdia
->mdhd
->duration
== 0 )
834 int err
= isom_update_mdhd_duration( trak
, 0 );
838 tkhd
->duration
= trak
->mdia
->mdhd
->duration
* ((double)file
->moov
->mvhd
->timescale
/ trak
->mdia
->mdhd
->timescale
);
842 /* If the presentation won't be extended and this track has any edit, then track duration is just the sum of the segment_duartions. */
843 for( lsmash_entry_t
*entry
= trak
->edts
->elst
->list
->head
; entry
; entry
= entry
->next
)
845 isom_elst_entry_t
*data
= (isom_elst_entry_t
*)entry
->data
;
847 return LSMASH_ERR_INVALID_DATA
;
848 tkhd
->duration
+= data
->segment_duration
;
851 if( tkhd
->duration
> UINT32_MAX
&& !file
->undefined_64_ver
)
853 if( !file
->fragment
&& tkhd
->duration
== 0 )
854 tkhd
->duration
= tkhd
->version
== 1 ? 0xffffffffffffffff : 0xffffffff;
855 return isom_update_mvhd_duration( file
->moov
);
858 int lsmash_update_track_duration( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t last_sample_delta
)
860 if( isom_check_initializer_present( root
) < 0 )
861 return LSMASH_ERR_FUNCTION_PARAM
;
862 lsmash_file_t
*file
= root
->file
;
863 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
864 if( LSMASH_IS_NON_EXISTING_BOX( trak
) )
865 return LSMASH_ERR_NAMELESS
;
866 int err
= isom_update_mdhd_duration( trak
, last_sample_delta
);
869 /* If the presentation won't be extended and this track has any edit, we don't change or update duration in tkhd. */
871 && LSMASH_IS_EXISTING_BOX( trak
->edts
)
872 && LSMASH_IS_EXISTING_BOX( trak
->edts
->elst
) )
873 err
= isom_update_mvhd_duration( file
->moov
); /* Only update movie duration. */
875 err
= isom_update_tkhd_duration( trak
); /* Also update movie duration internally. */
879 static inline int isom_increment_sample_number_in_entry
881 uint32_t *sample_number_in_entry
,
882 uint32_t sample_count_in_entry
,
883 lsmash_entry_t
**entry
886 if( *sample_number_in_entry
!= sample_count_in_entry
)
888 *sample_number_in_entry
+= 1;
891 /* Precede the next entry. */
892 *sample_number_in_entry
= 1;
895 *entry
= (*entry
)->next
;
896 if( *entry
&& !(*entry
)->data
)
897 return LSMASH_ERR_INVALID_DATA
;
902 int isom_calculate_bitrate_description
906 uint32_t *bufferSizeDB
,
907 uint32_t *maxBitrate
,
908 uint32_t *avgBitrate
,
909 uint32_t sample_description_index
912 isom_stsz_t
*stsz
= stbl
->stsz
;
913 lsmash_entry_list_t
*stsz_list
= LSMASH_IS_EXISTING_BOX( stsz
) ? stsz
->list
: stbl
->stz2
->list
;
914 lsmash_entry_t
*stsz_entry
= stsz_list
? stsz_list
->head
: NULL
;
915 lsmash_entry_t
*stts_entry
= stbl
->stts
->list
->head
;
916 lsmash_entry_t
*stsc_entry
= NULL
;
917 lsmash_entry_t
*next_stsc_entry
= stbl
->stsc
->list
->head
;
918 isom_stts_entry_t
*stts_data
= NULL
;
919 isom_stsc_entry_t
*stsc_data
= NULL
;
920 if( next_stsc_entry
&& !next_stsc_entry
->data
)
921 return LSMASH_ERR_INVALID_DATA
;
924 uint32_t time_wnd
= 0;
925 uint32_t chunk_number
= 0;
926 uint32_t sample_number_in_stts
= 1;
927 uint32_t sample_number_in_chunk
= 1;
928 uint32_t constant_sample_size
= LSMASH_IS_EXISTING_BOX( stsz
) ? stsz
->sample_size
: 0;
935 if( !stsc_data
|| sample_number_in_chunk
== stsc_data
->samples_per_chunk
)
937 /* Move the next chunk. */
938 sample_number_in_chunk
= 1;
940 /* Check if the next entry is broken. */
941 while( next_stsc_entry
&& ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
< chunk_number
)
943 /* Just skip broken next entry. */
944 next_stsc_entry
= next_stsc_entry
->next
;
945 if( next_stsc_entry
&& !next_stsc_entry
->data
)
946 return LSMASH_ERR_INVALID_DATA
;
948 /* Check if the next chunk belongs to the next sequence of chunks. */
949 if( next_stsc_entry
&& ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
== chunk_number
)
951 stsc_entry
= next_stsc_entry
;
952 next_stsc_entry
= next_stsc_entry
->next
;
953 if( next_stsc_entry
&& !next_stsc_entry
->data
)
954 return LSMASH_ERR_INVALID_DATA
;
955 stsc_data
= (isom_stsc_entry_t
*)stsc_entry
->data
;
956 /* Check if the next contiguous chunks belong to given sample description. */
957 if( stsc_data
->sample_description_index
!= sample_description_index
)
959 /* Skip chunks which don't belong to given sample description. */
960 uint32_t number_of_skips
= 0;
961 uint32_t first_chunk
= stsc_data
->first_chunk
;
962 uint32_t samples_per_chunk
= stsc_data
->samples_per_chunk
;
963 while( next_stsc_entry
)
965 if( ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->sample_description_index
!= sample_description_index
)
967 stsc_data
= (isom_stsc_entry_t
*)next_stsc_entry
->data
;
968 number_of_skips
+= (stsc_data
->first_chunk
- first_chunk
) * samples_per_chunk
;
969 first_chunk
= stsc_data
->first_chunk
;
970 samples_per_chunk
= stsc_data
->samples_per_chunk
;
972 else if( ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
<= first_chunk
)
976 /* Just skip the next entry. */
977 next_stsc_entry
= next_stsc_entry
->next
;
978 if( next_stsc_entry
&& !next_stsc_entry
->data
)
979 return LSMASH_ERR_INVALID_DATA
;
981 if( !next_stsc_entry
)
982 break; /* There is no more chunks which don't belong to given sample description. */
983 number_of_skips
+= (((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
- first_chunk
) * samples_per_chunk
;
984 for( uint32_t i
= 0; i
< number_of_skips
; i
++ )
990 stsz_entry
= stsz_entry
->next
;
994 if( (err
= isom_increment_sample_number_in_entry( &sample_number_in_stts
,
995 ((isom_stts_entry_t
*)stts_entry
->data
)->sample_count
,
999 if( (stsz_list
&& !stsz_entry
) || !stts_entry
)
1001 chunk_number
= stsc_data
->first_chunk
;
1006 ++sample_number_in_chunk
;
1007 /* Get current sample's size. */
1013 isom_stsz_entry_t
*stsz_data
= (isom_stsz_entry_t
*)stsz_entry
->data
;
1015 return LSMASH_ERR_INVALID_DATA
;
1016 size
= stsz_data
->entry_size
;
1017 stsz_entry
= stsz_entry
->next
;
1020 size
= constant_sample_size
;
1021 /* Get current sample's DTS. */
1023 dts
+= stts_data
->sample_delta
;
1024 stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
1026 return LSMASH_ERR_INVALID_DATA
;
1027 if( (err
= isom_increment_sample_number_in_entry( &sample_number_in_stts
, stts_data
->sample_count
, &stts_entry
)) < 0 )
1029 /* Calculate bitrate description. */
1030 if( *bufferSizeDB
< size
)
1031 *bufferSizeDB
= size
;
1032 *avgBitrate
+= size
;
1034 if( dts
> time_wnd
+ mdhd
->timescale
)
1036 if( rate
> *maxBitrate
)
1042 double duration
= (double)mdhd
->duration
/ mdhd
->timescale
;
1043 *avgBitrate
= (uint32_t)(*avgBitrate
/ duration
);
1044 if( *maxBitrate
== 0 )
1045 *maxBitrate
= *avgBitrate
;
1046 /* Convert to bits per second. */
1052 int isom_is_variable_size( isom_stbl_t
*stbl
)
1054 if( (LSMASH_IS_EXISTING_BOX( stbl
->stz2
) && stbl
->stz2
->sample_count
> 1)
1055 || (LSMASH_IS_EXISTING_BOX( stbl
->stsz
) && stbl
->stsz
->sample_count
> 1 && stbl
->stsz
->sample_size
== 0) )
1061 uint32_t isom_get_first_sample_size( isom_stbl_t
*stbl
)
1063 if( LSMASH_IS_EXISTING_BOX( stbl
->stsz
) )
1066 if( stbl
->stsz
->sample_size
)
1067 return stbl
->stsz
->sample_size
;
1068 else if( stbl
->stsz
->list
&& stbl
->stsz
->list
->head
&& stbl
->stsz
->list
->head
->data
)
1069 return ((isom_stsz_entry_t
*)stbl
->stsz
->list
->head
->data
)->entry_size
;
1073 else if( LSMASH_IS_EXISTING_BOX( stbl
->stz2
) )
1076 if( stbl
->stz2
->list
&& stbl
->stz2
->list
->head
&& stbl
->stz2
->list
->head
->data
)
1077 return ((isom_stsz_entry_t
*)stbl
->stz2
->list
->head
->data
)->entry_size
;
1085 int isom_update_bitrate_description( isom_mdia_t
*mdia
)
1087 if( LSMASH_IS_NON_EXISTING_BOX( mdia
->mdhd
) )
1088 return LSMASH_ERR_INVALID_DATA
;
1089 isom_stbl_t
*stbl
= mdia
->minf
->stbl
;
1090 if( LSMASH_IS_NON_EXISTING_BOX( stbl
->stsd
)
1091 || (LSMASH_IS_NON_EXISTING_BOX( stbl
->stsz
) && LSMASH_IS_NON_EXISTING_BOX( stbl
->stz2
))
1092 || !stbl
->stsc
->list
1093 || !stbl
->stts
->list
)
1094 return LSMASH_ERR_INVALID_DATA
;
1095 uint32_t sample_description_index
= 0;
1096 for( lsmash_entry_t
*entry
= stbl
->stsd
->list
.head
; entry
; entry
= entry
->next
)
1098 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)entry
->data
;
1100 return LSMASH_ERR_INVALID_DATA
;
1101 ++sample_description_index
;
1102 isom_bitrate_updater_t func_update_bitrate
= isom_get_bitrate_updater( sample_entry
);
1103 if( func_update_bitrate
)
1105 int err
= func_update_bitrate( stbl
, mdia
->mdhd
, sample_description_index
);
1110 return sample_description_index
? 0 : LSMASH_ERR_INVALID_DATA
;
1113 static inline uint64_t isom_get_current_mp4time( void )
1115 return (uint64_t)time( NULL
) + ISOM_MAC_EPOCH_OFFSET
;
1118 static int isom_set_media_creation_time( isom_trak_t
*trak
, uint64_t current_mp4time
)
1120 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
) )
1121 return LSMASH_ERR_NAMELESS
;
1122 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
1123 if( mdhd
->creation_time
== 0 )
1124 mdhd
->creation_time
= mdhd
->modification_time
= current_mp4time
;
1128 static int isom_set_track_creation_time( isom_trak_t
*trak
, uint64_t current_mp4time
)
1130 assert( LSMASH_IS_EXISTING_BOX( trak
) );
1131 if( LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
1132 return LSMASH_ERR_NAMELESS
;
1133 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1134 if( tkhd
->creation_time
== 0 )
1135 tkhd
->creation_time
= tkhd
->modification_time
= current_mp4time
;
1136 return isom_set_media_creation_time( trak
, current_mp4time
);
1139 static int isom_set_movie_creation_time( lsmash_file_t
*file
)
1141 if( LSMASH_IS_NON_EXISTING_BOX( file
->moov
->mvhd
) )
1142 return LSMASH_ERR_NAMELESS
;
1143 uint64_t current_mp4time
= isom_get_current_mp4time();
1144 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
1146 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
1147 if( LSMASH_IS_NON_EXISTING_BOX( trak
) )
1148 return LSMASH_ERR_INVALID_DATA
;
1149 int err
= isom_set_track_creation_time( trak
, current_mp4time
);
1153 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
1154 if( mvhd
->creation_time
== 0 )
1155 mvhd
->creation_time
= mvhd
->modification_time
= current_mp4time
;
1159 int isom_setup_handler_reference( isom_hdlr_t
*hdlr
, uint32_t media_type
)
1161 assert( LSMASH_IS_EXISTING_BOX( hdlr
) );
1162 isom_box_t
*parent
= hdlr
->parent
;
1163 lsmash_file_t
*file
= hdlr
->file
;
1164 if( LSMASH_IS_NON_EXISTING_BOX( parent
)
1165 || LSMASH_IS_NON_EXISTING_BOX( file
) )
1166 return LSMASH_ERR_NAMELESS
;
1167 isom_mdia_t
*mdia
= lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_MDIA
) ? (isom_mdia_t
*)parent
: isom_non_existing_mdia();
1168 isom_meta_t
*meta
= lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_META
) ? (isom_meta_t
*)parent
1169 : lsmash_check_box_type_identical( parent
->type
, QT_BOX_TYPE_META
) ? (isom_meta_t
*)parent
: isom_non_existing_meta();
1170 uint32_t type
= LSMASH_IS_EXISTING_BOX( mdia
) ? (file
->qt_compatible
? QT_HANDLER_TYPE_MEDIA
: 0)
1171 : (LSMASH_IS_EXISTING_BOX( meta
) ? 0 : QT_HANDLER_TYPE_DATA
);
1172 uint32_t subtype
= media_type
;
1173 hdlr
->componentType
= type
;
1174 hdlr
->componentSubtype
= subtype
;
1175 char *type_name
= NULL
;
1176 char *subtype_name
= NULL
;
1177 uint8_t type_name_length
= 0;
1178 uint8_t subtype_name_length
= 0;
1180 type_name
= "Media ";
1182 type_name
= "Metadata ";
1183 else /* if( minf ) */
1184 type_name
= "Data ";
1185 type_name_length
= strlen( type_name
);
1190 uint8_t subtype_name_length
;
1193 { ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
, "Sound ", 6 },
1194 { ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
, "Video ", 6 },
1195 { ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK
, "Hint ", 5 },
1196 { ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK
, "Metadata ", 9 },
1197 { ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK
, "Text ", 5 },
1198 { ISOM_META_HANDLER_TYPE_ITUNES_METADATA
, "iTunes ", 7 },
1199 { QT_REFERENCE_HANDLER_TYPE_ALIAS
, "Alias ", 6 },
1200 { QT_REFERENCE_HANDLER_TYPE_RESOURCE
, "Resource ", 9 },
1201 { QT_REFERENCE_HANDLER_TYPE_URL
, "URL ", 4 },
1202 { subtype
, "Unknown ", 8 }
1204 for( int i
= 0; subtype_table
[i
].subtype
; i
++ )
1205 if( subtype
== subtype_table
[i
].subtype
)
1207 subtype_name
= subtype_table
[i
].subtype_name
;
1208 subtype_name_length
= subtype_table
[i
].subtype_name_length
;
1211 uint32_t name_length
= 15 + subtype_name_length
+ type_name_length
+ file
->isom_compatible
+ file
->qt_compatible
;
1212 uint8_t *name
= lsmash_malloc( name_length
);
1214 return LSMASH_ERR_MEMORY_ALLOC
;
1215 if( file
->qt_compatible
)
1216 name
[0] = name_length
& 0xff;
1217 memcpy( name
+ file
->qt_compatible
, "L-SMASH ", 8 );
1218 memcpy( name
+ file
->qt_compatible
+ 8, subtype_name
, subtype_name_length
);
1219 memcpy( name
+ file
->qt_compatible
+ 8 + subtype_name_length
, type_name
, type_name_length
);
1220 memcpy( name
+ file
->qt_compatible
+ 8 + subtype_name_length
+ type_name_length
, "Handler", 7 );
1221 if( file
->isom_compatible
)
1222 name
[name_length
- 1] = 0;
1223 hdlr
->componentName
= name
;
1224 hdlr
->componentName_length
= name_length
;
1228 isom_trak_t
*isom_track_create( lsmash_file_t
*file
, lsmash_media_type media_type
)
1230 /* Don't allow to create a new track if the initial movie is already written. */
1231 if( (file
->fragment
&& file
->fragment
->movie
)
1232 || (LSMASH_IS_EXISTING_BOX( file
->moov
) && (file
->moov
->manager
& LSMASH_WRITTEN_BOX
)) )
1233 return isom_non_existing_trak();
1234 isom_trak_t
*trak
= isom_add_trak( file
->moov
);
1235 if( LSMASH_IS_NON_EXISTING_BOX( trak
->file
->moov
->mvhd
) )
1237 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_tkhd( trak
) )
1238 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_mdia( trak
) )
1239 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_mdhd( trak
->mdia
) )
1240 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_minf( trak
->mdia
) )
1241 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_dinf( trak
->mdia
->minf
) )
1242 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_dref( trak
->mdia
->minf
->dinf
) )
1243 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stbl( trak
->mdia
->minf
) )
1244 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stsd( trak
->mdia
->minf
->stbl
) )
1245 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stts( trak
->mdia
->minf
->stbl
) )
1246 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stsc( trak
->mdia
->minf
->stbl
) )
1247 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stco( trak
->mdia
->minf
->stbl
) )
1248 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stsz( trak
->mdia
->minf
->stbl
) ) )
1250 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_hdlr( trak
->mdia
) )
1251 || isom_setup_handler_reference( trak
->mdia
->hdlr
, media_type
) < 0 )
1253 if( file
->qt_compatible
)
1255 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_hdlr( trak
->mdia
->minf
) )
1256 || isom_setup_handler_reference( trak
->mdia
->minf
->hdlr
, QT_REFERENCE_HANDLER_TYPE_URL
) < 0 )
1259 switch( media_type
)
1261 case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
:
1262 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_vmhd( trak
->mdia
->minf
) ) )
1264 trak
->mdia
->minf
->vmhd
->flags
= 0x000001;
1266 case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
:
1267 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_smhd( trak
->mdia
->minf
) ) )
1269 trak
->cache
->is_audio
= 1;
1271 case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK
:
1272 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_hmhd( trak
->mdia
->minf
) ) )
1275 case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK
:
1276 if( file
->qt_compatible
|| file
->itunes_movie
)
1278 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_gmhd( trak
->mdia
->minf
) )
1279 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_gmin( trak
->mdia
->minf
->gmhd
) )
1280 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_text( trak
->mdia
->minf
->gmhd
) ) )
1282 /* Default Text Media Information Box. */
1284 isom_text_t
*text
= trak
->mdia
->minf
->gmhd
->text
;
1285 text
->matrix
[0] = 0x00010000;
1286 text
->matrix
[4] = 0x00010000;
1287 text
->matrix
[8] = 0x40000000;
1291 goto fail
; /* We support only reference text media track for chapter yet. */
1294 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_nmhd( trak
->mdia
->minf
) ) )
1298 /* Default Track Header Box. */
1300 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1301 if( media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
)
1302 tkhd
->volume
= 0x0100;
1303 tkhd
->matrix
[0] = 0x00010000;
1304 tkhd
->matrix
[4] = 0x00010000;
1305 tkhd
->matrix
[8] = 0x40000000;
1306 tkhd
->duration
= 0xffffffff;
1307 tkhd
->track_ID
= trak
->file
->moov
->mvhd
->next_track_ID
++;
1309 trak
->mdia
->mdhd
->language
= file
->qt_compatible
? 0 : ISOM_LANGUAGE_CODE_UNDEFINED
;
1312 isom_remove_box_by_itself( trak
);
1313 return isom_non_existing_trak();
1316 isom_moov_t
*isom_movie_create( lsmash_file_t
*file
)
1318 isom_moov_t
*moov
= isom_add_moov( file
);
1319 isom_mvhd_t
*mvhd
= isom_add_mvhd( moov
);
1320 if( LSMASH_IS_NON_EXISTING_BOX( mvhd
) )
1322 isom_remove_box_by_itself( moov
);
1323 return isom_non_existing_moov();
1325 /* Default Movie Header Box. */
1326 mvhd
->rate
= 0x00010000;
1327 mvhd
->volume
= 0x0100;
1328 mvhd
->matrix
[0] = 0x00010000;
1329 mvhd
->matrix
[4] = 0x00010000;
1330 mvhd
->matrix
[8] = 0x40000000;
1331 mvhd
->next_track_ID
= 1;
1332 file
->initializer
= file
;
1336 /*******************************
1338 *******************************/
1340 /*---- track manipulators ----*/
1342 void lsmash_delete_track( lsmash_root_t
*root
, uint32_t track_ID
)
1344 if( isom_check_initializer_present( root
) < 0 )
1346 for( lsmash_entry_t
*entry
= root
->file
->initializer
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
1348 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
1349 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
1350 || LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
1352 if( trak
->tkhd
->track_ID
== track_ID
)
1354 isom_remove_box_by_itself( trak
);
1360 uint32_t lsmash_create_track( lsmash_root_t
*root
, lsmash_media_type media_type
)
1362 if( isom_check_initializer_present( root
) < 0 )
1364 isom_trak_t
*trak
= isom_track_create( root
->file
, media_type
);
1365 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
1366 || LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
1368 return trak
->tkhd
->track_ID
;
1371 uint32_t lsmash_get_track_ID( lsmash_root_t
*root
, uint32_t track_number
)
1373 if( isom_check_initializer_present( root
) < 0
1374 || LSMASH_IS_NON_EXISTING_BOX( root
->file
->initializer
->moov
) )
1376 isom_trak_t
*trak
= (isom_trak_t
*)lsmash_list_get_entry_data( &root
->file
->initializer
->moov
->trak_list
, track_number
);
1377 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
1378 || LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
1380 return trak
->tkhd
->track_ID
;
1383 void lsmash_initialize_track_parameters( lsmash_track_parameters_t
*param
)
1385 memset( param
, 0, sizeof(lsmash_track_parameters_t
) );
1386 param
->audio_volume
= 0x0100;
1387 param
->matrix
[0] = 0x00010000;
1388 param
->matrix
[4] = 0x00010000;
1389 param
->matrix
[8] = 0x40000000;
1392 int lsmash_set_track_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_track_parameters_t
*param
)
1394 if( isom_check_initializer_present( root
) < 0 )
1395 return LSMASH_ERR_FUNCTION_PARAM
;
1396 lsmash_file_t
*file
= root
->file
;
1397 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1398 if( LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
)
1399 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->hdlr
)
1400 || LSMASH_IS_NON_EXISTING_BOX( file
->moov
->mvhd
) )
1401 return LSMASH_ERR_NAMELESS
;
1402 /* Prepare Track Aperture Modes if required. */
1403 if( file
->qt_compatible
&& param
->aperture_modes
)
1405 if( LSMASH_IS_NON_EXISTING_BOX( trak
->tapt
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_tapt( trak
) ) )
1406 return LSMASH_ERR_NAMELESS
;
1407 isom_tapt_t
*tapt
= trak
->tapt
;
1408 if( (LSMASH_IS_NON_EXISTING_BOX( tapt
->clef
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_clef( tapt
) ))
1409 || (LSMASH_IS_NON_EXISTING_BOX( tapt
->prof
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_prof( tapt
) ))
1410 || (LSMASH_IS_NON_EXISTING_BOX( tapt
->enof
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_enof( tapt
) )) )
1411 return LSMASH_ERR_NAMELESS
;
1414 isom_remove_box_by_itself( trak
->tapt
);
1415 /* Set up Track Header. */
1416 uint32_t media_type
= trak
->mdia
->hdlr
->componentSubtype
;
1417 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1418 tkhd
->flags
= param
->mode
;
1419 tkhd
->track_ID
= param
->track_ID
? param
->track_ID
: tkhd
->track_ID
;
1420 tkhd
->duration
= LSMASH_IS_NON_EXISTING_BOX( trak
->edts
->elst
) ? param
->duration
: tkhd
->duration
;
1422 * alternate_group, layer, volume and matrix
1423 * According to 14496-14, these value are all set to defaut values in 14496-12.
1424 * And when a file is read as an MPEG-4 file, these values shall be ignored.
1425 * If a file complies with other specifications, then those fields may have non-default values
1426 * as required by those other specifications. */
1427 if( param
->alternate_group
)
1429 if( file
->qt_compatible
|| file
->itunes_movie
|| file
->max_3gpp_version
>= 4 )
1430 tkhd
->alternate_group
= param
->alternate_group
;
1433 tkhd
->alternate_group
= 0;
1434 lsmash_log( NULL
, LSMASH_LOG_WARNING
,
1435 "alternate_group is specified but not compatible with any of the brands. It won't be set.\n" );
1439 tkhd
->alternate_group
= 0;
1440 if( file
->qt_compatible
|| file
->itunes_movie
)
1442 tkhd
->layer
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->video_layer
: 0;
1443 tkhd
->volume
= media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
? param
->audio_volume
: 0;
1444 if( media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
)
1445 for( int i
= 0; i
< 9; i
++ )
1446 tkhd
->matrix
[i
] = param
->matrix
[i
];
1448 for( int i
= 0; i
< 9; i
++ )
1449 tkhd
->matrix
[i
] = 0;
1454 tkhd
->volume
= media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
? 0x0100 : 0;
1455 tkhd
->matrix
[0] = 0x00010000;
1456 tkhd
->matrix
[1] = 0;
1457 tkhd
->matrix
[2] = 0;
1458 tkhd
->matrix
[3] = 0;
1459 tkhd
->matrix
[4] = 0x00010000;
1460 tkhd
->matrix
[5] = 0;
1461 tkhd
->matrix
[6] = 0;
1462 tkhd
->matrix
[7] = 0;
1463 tkhd
->matrix
[8] = 0x40000000;
1465 /* visual presentation size */
1466 tkhd
->width
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->display_width
: 0;
1467 tkhd
->height
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->display_height
: 0;
1468 /* Update next_track_ID if needed. */
1469 if( file
->moov
->mvhd
->next_track_ID
<= tkhd
->track_ID
)
1470 file
->moov
->mvhd
->next_track_ID
= tkhd
->track_ID
+ 1;
1474 int lsmash_get_track_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_track_parameters_t
*param
)
1476 if( isom_check_initializer_present( root
) < 0 )
1477 return LSMASH_ERR_FUNCTION_PARAM
;
1478 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1479 if( LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
1480 return LSMASH_ERR_NAMELESS
;
1481 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1482 param
->mode
= tkhd
->flags
;
1483 param
->track_ID
= tkhd
->track_ID
;
1484 param
->duration
= tkhd
->duration
;
1485 param
->video_layer
= tkhd
->layer
;
1486 param
->alternate_group
= tkhd
->alternate_group
;
1487 param
->audio_volume
= tkhd
->volume
;
1488 for( int i
= 0; i
< 9; i
++ )
1489 param
->matrix
[i
] = tkhd
->matrix
[i
];
1490 param
->display_width
= tkhd
->width
;
1491 param
->display_height
= tkhd
->height
;
1492 param
->aperture_modes
= !!trak
->tapt
;
1496 static inline int check_dref_presence( isom_trak_t
*trak
)
1498 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->dinf
->dref
) )
1499 return LSMASH_ERR_NAMELESS
;
1503 uint32_t lsmash_count_data_reference
1505 lsmash_root_t
*root
,
1509 if( isom_check_initializer_present( root
) < 0 )
1511 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1512 if( check_dref_presence( trak
) < 0 )
1514 return trak
->mdia
->minf
->dinf
->dref
->list
.entry_count
;
1517 int lsmash_get_data_reference
1519 lsmash_root_t
*root
,
1521 lsmash_data_reference_t
*data_ref
1524 if( isom_check_initializer_present( root
) < 0 || !data_ref
)
1525 return LSMASH_ERR_FUNCTION_PARAM
;
1526 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1527 if( check_dref_presence( trak
) < 0 )
1528 return LSMASH_ERR_NAMELESS
;
1529 isom_dref_entry_t
*url
= lsmash_list_get_entry_data( &trak
->mdia
->minf
->dinf
->dref
->list
, data_ref
->index
);
1530 if( LSMASH_IS_NON_EXISTING_BOX( url
) )
1531 return LSMASH_ERR_NAMELESS
;
1532 if( !(url
->flags
& 0x000001) && url
->location
)
1534 int length
= strlen( url
->location
);
1535 char *location
= lsmash_malloc( length
+ 1 );
1537 return LSMASH_ERR_MEMORY_ALLOC
;
1538 memcpy( location
, url
->location
, length
);
1539 location
[length
] = '\0';
1540 data_ref
->location
= location
;
1543 data_ref
->location
= NULL
;
1547 void lsmash_cleanup_data_reference
1549 lsmash_data_reference_t
*data_ref
1554 lsmash_freep( &data_ref
->location
);
1557 int lsmash_create_data_reference
1559 lsmash_root_t
*root
,
1561 lsmash_data_reference_t
*data_ref
,
1565 /* At present, we don't support external data references for movie fragments.
1566 * Note that, for external media data, default-base-is-moof is meaningless since relative
1567 * offsets from Movie Fragment Boxes make no sense.
1568 * In the future, the condition of !(file->flags & LSMASH_FILE_MODE_WRITE) may be removed
1569 * for the implementation which does not write actually and does reference read-only file. */
1570 if( LSMASH_IS_NON_EXISTING_BOX( root
)
1571 || LSMASH_IS_NON_EXISTING_BOX( file
)
1572 || file
->root
!= root
1573 || (!(file
->flags
& LSMASH_FILE_MODE_MEDIA
) && !(file
->flags
& LSMASH_FILE_MODE_INITIALIZATION
))
1574 || !(file
->flags
& LSMASH_FILE_MODE_WRITE
)
1575 || (root
->file
!= file
&& ((file
->flags
& LSMASH_FILE_MODE_FRAGMENTED
) || file
->fragment
))
1577 return LSMASH_ERR_FUNCTION_PARAM
;
1578 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
1579 if( check_dref_presence( trak
) < 0 )
1580 return LSMASH_ERR_NAMELESS
;
1581 isom_dref_entry_t
*url
= isom_add_dref_entry( trak
->mdia
->minf
->dinf
->dref
, ISOM_BOX_TYPE_URL
);
1582 if( LSMASH_IS_NON_EXISTING_BOX( url
) )
1583 return LSMASH_ERR_NAMELESS
;
1584 if( !data_ref
->location
|| root
->file
== file
)
1586 /* Media data is in the same file. */
1587 url
->flags
= 0x000001;
1588 url
->ref_file
= root
->file
;
1592 /* Set the location of the file. */
1593 int length
= strlen( data_ref
->location
);
1594 url
->location
= lsmash_malloc( length
+ 1 );
1595 if( !url
->location
)
1597 isom_remove_box_by_itself( url
);
1598 return LSMASH_ERR_MEMORY_ALLOC
;
1600 memcpy( url
->location
, data_ref
->location
, length
);
1601 url
->location
[length
] = '\0';
1602 url
->location_length
= length
+ 1;
1603 url
->ref_file
= file
;
1605 data_ref
->index
= trak
->mdia
->minf
->dinf
->dref
->list
.entry_count
;
1609 int lsmash_assign_data_reference
1611 lsmash_root_t
*root
,
1613 uint32_t data_ref_index
,
1617 if( isom_check_initializer_present( root
) < 0
1618 || !file
|| file
->root
!= root
1619 || !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
1620 || !(file
->flags
& LSMASH_FILE_MODE_READ
)
1621 || data_ref_index
== 0 )
1622 return LSMASH_ERR_FUNCTION_PARAM
;
1623 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1624 if( check_dref_presence( trak
) < 0 )
1625 return LSMASH_ERR_NAMELESS
;
1626 isom_dref_entry_t
*url
= (isom_dref_entry_t
*)lsmash_list_get_entry_data( &trak
->mdia
->minf
->dinf
->dref
->list
, data_ref_index
);
1627 if( LSMASH_IS_NON_EXISTING_BOX( url
) )
1628 return LSMASH_ERR_NAMELESS
;
1629 if( !(url
->flags
& 0x000001) )
1630 /* Reference an external media data. */
1631 url
->ref_file
= file
;
1635 static int isom_set_media_handler_name( lsmash_file_t
*file
, uint32_t track_ID
, char *handler_name
)
1637 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1638 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->hdlr
) )
1639 return LSMASH_ERR_NAMELESS
;
1640 isom_hdlr_t
*hdlr
= trak
->mdia
->hdlr
;
1641 uint8_t *name
= NULL
;
1642 uint32_t name_length
= strlen( handler_name
) + file
->isom_compatible
+ file
->qt_compatible
;
1643 if( file
->qt_compatible
)
1644 name_length
= LSMASH_MIN( name_length
, 255 );
1645 if( name_length
> hdlr
->componentName_length
&& hdlr
->componentName
)
1646 name
= lsmash_realloc( hdlr
->componentName
, name_length
);
1647 else if( !hdlr
->componentName
)
1648 name
= lsmash_malloc( name_length
);
1650 name
= hdlr
->componentName
;
1652 return LSMASH_ERR_MEMORY_ALLOC
;
1653 if( file
->qt_compatible
)
1654 name
[0] = name_length
& 0xff;
1655 memcpy( name
+ file
->qt_compatible
, handler_name
, strlen( handler_name
) );
1656 if( file
->isom_compatible
)
1657 name
[name_length
- 1] = 0;
1658 hdlr
->componentName
= name
;
1659 hdlr
->componentName_length
= name_length
;
1663 static int isom_set_data_handler_name( lsmash_file_t
*file
, uint32_t track_ID
, char *handler_name
)
1665 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1666 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->hdlr
) )
1667 return LSMASH_ERR_NAMELESS
;
1668 isom_hdlr_t
*hdlr
= trak
->mdia
->minf
->hdlr
;
1669 uint8_t *name
= NULL
;
1670 uint32_t name_length
= strlen( handler_name
) + file
->isom_compatible
+ file
->qt_compatible
;
1671 if( file
->qt_compatible
)
1672 name_length
= LSMASH_MIN( name_length
, 255 );
1673 if( name_length
> hdlr
->componentName_length
&& hdlr
->componentName
)
1674 name
= lsmash_realloc( hdlr
->componentName
, name_length
);
1675 else if( !hdlr
->componentName
)
1676 name
= lsmash_malloc( name_length
);
1678 name
= hdlr
->componentName
;
1680 return LSMASH_ERR_MEMORY_ALLOC
;
1681 if( file
->qt_compatible
)
1682 name
[0] = name_length
& 0xff;
1683 memcpy( name
+ file
->qt_compatible
, handler_name
, strlen( handler_name
) );
1684 if( file
->isom_compatible
)
1685 name
[name_length
- 1] = 0;
1686 hdlr
->componentName
= name
;
1687 hdlr
->componentName_length
= name_length
;
1691 uint32_t lsmash_get_media_timescale( lsmash_root_t
*root
, uint32_t track_ID
)
1693 if( isom_check_initializer_present( root
) < 0 )
1695 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1696 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
) )
1698 return trak
->mdia
->mdhd
->timescale
;
1701 uint64_t lsmash_get_media_duration( lsmash_root_t
*root
, uint32_t track_ID
)
1703 if( isom_check_initializer_present( root
) < 0 )
1705 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1706 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
) )
1708 return trak
->mdia
->mdhd
->duration
;
1711 uint64_t lsmash_get_track_duration( lsmash_root_t
*root
, uint32_t track_ID
)
1713 if( isom_check_initializer_present( root
) < 0 )
1715 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1716 if( LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
1718 return trak
->tkhd
->duration
;
1721 uint32_t lsmash_get_last_sample_delta( lsmash_root_t
*root
, uint32_t track_ID
)
1723 if( isom_check_initializer_present( root
) < 0 )
1725 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
1726 if( !trak
->mdia
->minf
->stbl
->stts
->list
1727 || !trak
->mdia
->minf
->stbl
->stts
->list
->tail
1728 || !trak
->mdia
->minf
->stbl
->stts
->list
->tail
->data
)
1730 return ((isom_stts_entry_t
*)trak
->mdia
->minf
->stbl
->stts
->list
->tail
->data
)->sample_delta
;
1733 uint32_t lsmash_get_start_time_offset( lsmash_root_t
*root
, uint32_t track_ID
)
1735 if( isom_check_initializer_present( root
) < 0 )
1737 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
1738 if( !trak
->mdia
->minf
->stbl
->ctts
->list
1739 || !trak
->mdia
->minf
->stbl
->ctts
->list
->head
1740 || !trak
->mdia
->minf
->stbl
->ctts
->list
->head
->data
)
1742 return ((isom_ctts_entry_t
*)trak
->mdia
->minf
->stbl
->ctts
->list
->head
->data
)->sample_offset
;
1745 uint32_t lsmash_get_composition_to_decode_shift( lsmash_root_t
*root
, uint32_t track_ID
)
1747 if( isom_check_initializer_present( root
) < 0 )
1749 lsmash_file_t
*file
= root
->file
->initializer
;
1750 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1751 uint32_t sample_count
= isom_get_sample_count( trak
);
1752 if( sample_count
== 0 )
1754 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
1755 if( !stbl
->stts
->list
1756 || !stbl
->ctts
->list
)
1758 if( !(file
->max_isom_version
>= 4 && stbl
->ctts
->version
== 1) && !file
->qt_compatible
)
1759 return 0; /* This movie shall not have composition to decode timeline shift. */
1760 lsmash_entry_t
*stts_entry
= stbl
->stts
->list
->head
;
1761 lsmash_entry_t
*ctts_entry
= stbl
->ctts
->list
->head
;
1762 if( !stts_entry
|| !ctts_entry
)
1766 uint32_t ctd_shift
= 0;
1769 for( uint32_t k
= 0; k
< sample_count
; k
++ )
1771 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
1772 isom_ctts_entry_t
*ctts_data
= (isom_ctts_entry_t
*)ctts_entry
->data
;
1773 if( !stts_data
|| !ctts_data
)
1775 if( ctts_data
->sample_offset
!= ISOM_NON_OUTPUT_SAMPLE_OFFSET
)
1777 cts
= dts
+ (int32_t)ctts_data
->sample_offset
;
1778 if( dts
> cts
+ ctd_shift
)
1779 ctd_shift
= dts
- cts
;
1781 dts
+= stts_data
->sample_delta
;
1782 if( ++i
== stts_data
->sample_count
)
1784 stts_entry
= stts_entry
->next
;
1789 if( ++j
== ctts_data
->sample_count
)
1791 ctts_entry
= ctts_entry
->next
;
1800 uint16_t lsmash_pack_iso_language( char *iso_language
)
1802 if( !iso_language
|| strlen( iso_language
) != 3 )
1804 return (uint16_t)LSMASH_PACK_ISO_LANGUAGE( iso_language
[0], iso_language
[1], iso_language
[2] );
1807 static int isom_iso2mac_language( uint16_t ISO_language
, uint16_t *MAC_language
)
1809 assert( MAC_language
);
1811 for( ; isom_languages
[i
].iso_name
; i
++ )
1812 if( ISO_language
== isom_languages
[i
].iso_name
)
1814 if( !isom_languages
[i
].iso_name
)
1815 return LSMASH_ERR_NAMELESS
;
1816 *MAC_language
= isom_languages
[i
].mac_value
;
1820 static int isom_mac2iso_language( uint16_t MAC_language
, uint16_t *ISO_language
)
1822 assert( ISO_language
);
1824 for( ; isom_languages
[i
].iso_name
; i
++ )
1825 if( MAC_language
== isom_languages
[i
].mac_value
)
1827 *ISO_language
= isom_languages
[i
].iso_name
? isom_languages
[i
].iso_name
: ISOM_LANGUAGE_CODE_UNDEFINED
;
1831 static int isom_set_media_language( lsmash_file_t
*file
, uint32_t track_ID
, uint16_t ISO_language
, uint16_t MAC_language
)
1833 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1834 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
) )
1835 return LSMASH_ERR_NAMELESS
;
1836 uint16_t language
= 0;
1837 if( file
->isom_compatible
)
1840 language
= ISO_language
;
1841 else if( MAC_language
)
1843 int err
= isom_mac2iso_language( MAC_language
, &language
);
1848 language
= ISOM_LANGUAGE_CODE_UNDEFINED
;
1850 else if( file
->qt_compatible
)
1854 int err
= isom_iso2mac_language( ISO_language
, &language
);
1859 language
= MAC_language
;
1862 return LSMASH_ERR_INVALID_DATA
;
1863 trak
->mdia
->mdhd
->language
= language
;
1867 int isom_add_sample_grouping( isom_box_t
*parent
, isom_grouping_type grouping_type
)
1871 if( ((sgpd
= isom_add_sgpd( parent
)), LSMASH_IS_NON_EXISTING_BOX( sgpd
))
1872 || ((sbgp
= isom_add_sbgp( parent
)), LSMASH_IS_NON_EXISTING_BOX( sbgp
)) )
1873 return LSMASH_ERR_NAMELESS
;
1874 sbgp
->grouping_type
= grouping_type
;
1875 sgpd
->grouping_type
= grouping_type
;
1876 sgpd
->version
= 1; /* We use version 1 for Sample Group Description Box because it is recommended in the spec. */
1877 switch( grouping_type
)
1879 case ISOM_GROUP_TYPE_RAP
:
1880 sgpd
->default_length
= 1;
1882 case ISOM_GROUP_TYPE_ROLL
:
1883 case ISOM_GROUP_TYPE_PROL
:
1884 sgpd
->default_length
= 2;
1887 /* We don't consider other grouping types currently. */
1893 static int isom_create_sample_grouping( isom_trak_t
*trak
, isom_grouping_type grouping_type
)
1895 assert( LSMASH_IS_EXISTING_BOX( trak
) );
1896 lsmash_file_t
*file
= trak
->file
;
1897 switch( grouping_type
)
1899 case ISOM_GROUP_TYPE_RAP
:
1900 assert( file
->max_isom_version
>= 6 );
1902 case ISOM_GROUP_TYPE_ROLL
:
1903 case ISOM_GROUP_TYPE_PROL
:
1904 assert( file
->avc_extensions
|| file
->qt_compatible
);
1910 int err
= isom_add_sample_grouping( (isom_box_t
*)trak
->mdia
->minf
->stbl
, grouping_type
);
1913 if( trak
->cache
->fragment
&& file
->max_isom_version
>= 6 )
1914 switch( grouping_type
)
1916 case ISOM_GROUP_TYPE_RAP
:
1917 trak
->cache
->fragment
->rap_grouping
= 1;
1919 case ISOM_GROUP_TYPE_ROLL
:
1920 case ISOM_GROUP_TYPE_PROL
:
1921 trak
->cache
->fragment
->roll_grouping
= 1;
1924 /* We don't consider other grouping types currently. */
1930 static int isom_compress_sample_size_table( isom_stbl_t
*stbl
)
1932 if( stbl
->file
->max_3gpp_version
)
1933 /* 3GPP: Limitations to the ISO base media file format
1934 * - compact sample sizes ('stz2') shall not be used for tracks containing H.263, MPEG-4 video, AMR, AMR-WB, AAC or Timed text.
1935 * Note that 'mp4a' check is incomplete here since this restriction is not applied to Enhanced aacPlus audio (HE-AAC v2). */
1936 for( lsmash_entry_t
*entry
= stbl
->stsd
->list
.head
; entry
; entry
= entry
->next
)
1938 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)entry
->data
;
1939 if( LSMASH_IS_NON_EXISTING_BOX( sample_entry
) )
1940 return LSMASH_ERR_INVALID_DATA
;
1941 lsmash_codec_type_t sample_type
= sample_entry
->type
;
1942 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_S263_VIDEO
)
1943 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4V_VIDEO
)
1944 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4A_AUDIO
)
1945 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_SAMR_AUDIO
)
1946 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_SAWB_AUDIO
)
1947 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_TX3G_TEXT
) )
1950 if( LSMASH_IS_EXISTING_BOX( stbl
->stsz
) && isom_is_variable_size( stbl
) )
1952 int max_num_bits
= 0;
1953 for( lsmash_entry_t
*entry
= stbl
->stsz
->list
->head
; entry
; entry
= entry
->next
)
1955 isom_stsz_entry_t
*data
= (isom_stsz_entry_t
*)entry
->data
;
1957 return LSMASH_ERR_INVALID_DATA
;
1959 for( num_bits
= 1; data
->entry_size
>> num_bits
; num_bits
++ );
1960 if( max_num_bits
< num_bits
)
1962 max_num_bits
= num_bits
;
1963 if( max_num_bits
> 16 )
1964 return 0; /* not compressible */
1967 if( max_num_bits
<= 16 && LSMASH_IS_BOX_ADDITION_SUCCESS( isom_add_stz2( stbl
) ) )
1969 /* The sample size table can be compressed by using 'stz2'. */
1970 isom_stsz_t
*stsz
= stbl
->stsz
;
1971 isom_stz2_t
*stz2
= stbl
->stz2
;
1972 stz2
->sample_count
= stsz
->sample_count
;
1973 if( max_num_bits
<= 4 )
1974 stz2
->field_size
= 4;
1975 else if( max_num_bits
<= 8 )
1976 stz2
->field_size
= 8;
1978 stz2
->field_size
= 16;
1979 lsmash_list_move_entries( stz2
->list
, stsz
->list
);
1980 isom_remove_box_by_itself( stsz
);
1986 static int isom_add_dependency_type( isom_stbl_t
*stbl
, lsmash_file_t
*file
, lsmash_sample_property_t
*prop
)
1988 if( !file
->qt_compatible
&& !file
->avc_extensions
)
1990 int compatibility
= file
->avc_extensions
&& file
->qt_compatible
? 3
1991 : file
->qt_compatible
? 2
1992 : file
->avc_extensions
? 1
1994 if( LSMASH_IS_EXISTING_BOX( stbl
->sdtp
) )
1995 return isom_add_sdtp_entry( (isom_box_t
*)stbl
, prop
, compatibility
);
1996 /* no null check for prop */
1997 if( !prop
->allow_earlier
1999 && !prop
->independent
2000 && !prop
->disposable
2001 && !prop
->redundant
)
2003 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_sdtp( (isom_box_t
*)stbl
) ) )
2004 return LSMASH_ERR_NAMELESS
;
2005 uint32_t count
= isom_get_sample_count_from_sample_table( stbl
);
2006 /* fill past samples with ISOM_SAMPLE_*_UNKNOWN */
2007 lsmash_sample_property_t null_prop
= { 0 };
2008 for( uint32_t i
= 1; i
< count
; i
++ )
2010 int err
= isom_add_sdtp_entry( (isom_box_t
*)stbl
, &null_prop
, compatibility
);
2014 return isom_add_sdtp_entry( (isom_box_t
*)stbl
, prop
, compatibility
);
2017 void lsmash_initialize_media_parameters( lsmash_media_parameters_t
*param
)
2019 memset( param
, 0, sizeof(lsmash_media_parameters_t
) );
2020 param
->timescale
= 1;
2023 int lsmash_set_media_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_media_parameters_t
*param
)
2025 if( isom_check_initializer_present( root
) < 0 )
2026 return LSMASH_ERR_FUNCTION_PARAM
;
2027 lsmash_file_t
*file
= root
->file
;
2028 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2029 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
)
2030 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->stbl
) )
2031 return LSMASH_ERR_NAMELESS
;
2032 trak
->mdia
->mdhd
->timescale
= param
->timescale
;
2033 int err
= isom_set_media_language( file
, track_ID
, param
->ISO_language
, param
->MAC_language
);
2036 if( param
->media_handler_name
2037 && (err
= isom_set_media_handler_name( file
, track_ID
, param
->media_handler_name
)) < 0 )
2039 if( file
->qt_compatible
&& param
->data_handler_name
2040 && (err
= isom_set_data_handler_name( file
, track_ID
, param
->data_handler_name
)) < 0 )
2042 if( (file
->avc_extensions
|| file
->qt_compatible
) && param
->roll_grouping
2043 && (err
= isom_create_sample_grouping( trak
, ISOM_GROUP_TYPE_ROLL
)) < 0 )
2045 if( (file
->max_isom_version
>= 6) && param
->rap_grouping
2046 && (err
= isom_create_sample_grouping( trak
, ISOM_GROUP_TYPE_RAP
)) < 0 )
2048 if( !file
->qt_compatible
&& param
->compact_sample_size_table
)
2049 trak
->mdia
->minf
->stbl
->compress_sample_size_table
= isom_compress_sample_size_table
;
2050 if( !param
->no_sample_dependency_table
)
2051 trak
->mdia
->minf
->stbl
->add_dependency_type
= isom_add_dependency_type
;
2055 static uint32_t get_actual_handler_name_length( isom_hdlr_t
*hdlr
, lsmash_file_t
*file
)
2057 if( hdlr
->componentName_length
== 0 )
2061 if( file
->qt_compatible
)
2063 length
= LSMASH_MIN( hdlr
->componentName
[0], hdlr
->componentName_length
- 1 );
2064 if( !file
->isom_compatible
)
2066 name
= &hdlr
->componentName
[1];
2070 length
= hdlr
->componentName_length
;
2071 name
= hdlr
->componentName
;
2073 /* Considering fool-proof such as not terminated by '\0'. */
2075 while( i
< length
&& name
[i
] )
2080 int lsmash_get_media_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_media_parameters_t
*param
)
2082 if( isom_check_initializer_present( root
) < 0 )
2083 return LSMASH_ERR_FUNCTION_PARAM
;
2084 lsmash_file_t
*file
= root
->file
->initializer
;
2085 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2086 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
)
2087 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->hdlr
)
2088 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->stbl
) )
2089 return LSMASH_ERR_NAMELESS
;
2090 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
2091 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2092 param
->timescale
= mdhd
->timescale
;
2093 param
->handler_type
= trak
->mdia
->hdlr
->componentSubtype
;
2094 param
->duration
= mdhd
->duration
;
2095 /* Whether sample grouping present. */
2099 sbgp
= isom_get_sample_to_group ( stbl
, ISOM_GROUP_TYPE_RAP
);
2100 sgpd
= isom_get_sample_group_description( stbl
, ISOM_GROUP_TYPE_RAP
);
2101 param
->rap_grouping
= LSMASH_IS_EXISTING_BOX( sbgp
) && LSMASH_IS_EXISTING_BOX( sgpd
);
2102 sbgp
= isom_get_roll_recovery_sample_to_group ( &stbl
->sbgp_list
);
2103 sgpd
= isom_get_roll_recovery_sample_group_description( &stbl
->sgpd_list
);
2104 param
->roll_grouping
= LSMASH_IS_EXISTING_BOX( sbgp
) && LSMASH_IS_EXISTING_BOX( sgpd
);
2106 /* Get media language. */
2107 if( mdhd
->language
>= 0x800 )
2109 param
->MAC_language
= 0;
2110 param
->ISO_language
= mdhd
->language
;
2114 param
->MAC_language
= mdhd
->language
;
2115 param
->ISO_language
= 0;
2117 /* Get handler name(s). */
2118 isom_hdlr_t
*hdlr
= trak
->mdia
->hdlr
;
2119 uint32_t actual_length
= get_actual_handler_name_length( hdlr
, file
);
2120 uint32_t length
= LSMASH_MIN( 255, actual_length
);
2123 memcpy( param
->media_handler_name_shadow
, hdlr
->componentName
+ file
->qt_compatible
, length
);
2124 param
->media_handler_name_shadow
[length
] = '\0';
2125 param
->media_handler_name
= param
->media_handler_name_shadow
;
2129 param
->media_handler_name
= NULL
;
2130 memset( param
->media_handler_name_shadow
, 0, sizeof(param
->media_handler_name_shadow
) );
2132 if( LSMASH_IS_EXISTING_BOX( trak
->mdia
->minf
->hdlr
) )
2134 hdlr
= trak
->mdia
->minf
->hdlr
;
2135 actual_length
= get_actual_handler_name_length( hdlr
, file
);
2136 length
= LSMASH_MIN( 255, actual_length
);
2139 memcpy( param
->data_handler_name_shadow
, hdlr
->componentName
+ file
->qt_compatible
, length
);
2140 param
->data_handler_name_shadow
[length
] = '\0';
2141 param
->data_handler_name
= param
->data_handler_name_shadow
;
2145 param
->data_handler_name
= NULL
;
2146 memset( param
->data_handler_name_shadow
, 0, sizeof(param
->data_handler_name_shadow
) );
2151 param
->data_handler_name
= NULL
;
2152 memset( param
->data_handler_name_shadow
, 0, sizeof(param
->data_handler_name_shadow
) );
2154 param
->compact_sample_size_table
= !!stbl
->stz2
;
2155 param
->no_sample_dependency_table
= !stbl
->sdtp
;
2156 param
->reserved
[0] = param
->reserved
[1] = 0;
2160 /*---- movie manipulators ----*/
2162 void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t
*param
)
2164 memset( param
, 0, sizeof(lsmash_movie_parameters_t
) );
2165 param
->timescale
= 600;
2166 param
->playback_rate
= 0x00010000;
2167 param
->playback_volume
= 0x0100;
2170 int lsmash_set_movie_parameters( lsmash_root_t
*root
, lsmash_movie_parameters_t
*param
)
2172 if( LSMASH_IS_NON_EXISTING_BOX( root
) )
2173 return LSMASH_ERR_FUNCTION_PARAM
;
2174 lsmash_file_t
*file
= root
->file
;
2175 if( LSMASH_IS_NON_EXISTING_BOX( file
->moov
->mvhd
) )
2176 return LSMASH_ERR_NAMELESS
;
2177 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
2178 mvhd
->timescale
= param
->timescale
;
2179 if( file
->qt_compatible
|| file
->itunes_movie
)
2181 mvhd
->rate
= param
->playback_rate
;
2182 mvhd
->volume
= param
->playback_volume
;
2183 mvhd
->previewTime
= param
->preview_time
;
2184 mvhd
->previewDuration
= param
->preview_duration
;
2185 mvhd
->posterTime
= param
->poster_time
;
2189 mvhd
->rate
= 0x00010000;
2190 mvhd
->volume
= 0x0100;
2191 mvhd
->previewTime
= 0;
2192 mvhd
->previewDuration
= 0;
2193 mvhd
->posterTime
= 0;
2198 int lsmash_get_movie_parameters( lsmash_root_t
*root
, lsmash_movie_parameters_t
*param
)
2200 if( isom_check_initializer_present( root
) < 0 )
2201 return LSMASH_ERR_FUNCTION_PARAM
;
2202 lsmash_file_t
*file
= root
->file
->initializer
;
2203 if( LSMASH_IS_NON_EXISTING_BOX( file
->moov
->mvhd
) )
2204 return LSMASH_ERR_NAMELESS
;
2205 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
2206 param
->timescale
= mvhd
->timescale
;
2207 param
->duration
= mvhd
->duration
;
2208 param
->playback_rate
= mvhd
->rate
;
2209 param
->playback_volume
= mvhd
->volume
;
2210 param
->preview_time
= mvhd
->previewTime
;
2211 param
->preview_duration
= mvhd
->previewDuration
;
2212 param
->poster_time
= mvhd
->posterTime
;
2213 param
->number_of_tracks
= file
->moov
->trak_list
.entry_count
;
2217 uint32_t lsmash_get_movie_timescale( lsmash_root_t
*root
)
2219 if( isom_check_initializer_present( root
) < 0 )
2221 return root
->file
->initializer
->moov
->mvhd
->timescale
;
2224 int lsmash_reserve_media_data_size
2226 lsmash_root_t
*root
,
2227 uint64_t media_data_size
2230 if( isom_check_initializer_present( root
) < 0 )
2231 return LSMASH_ERR_FUNCTION_PARAM
;
2232 lsmash_file_t
*file
= root
->file
->initializer
;
2233 if( LSMASH_IS_EXISTING_BOX( file
->mdat
) /* whether the Media Data Box is already written or not */
2234 || file
->fragment
) /* For fragmented movies, this function makes no sense. */
2235 return LSMASH_ERR_NAMELESS
;
2236 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_mdat( file
) ) )
2237 return LSMASH_ERR_NAMELESS
;
2238 file
->mdat
->reserved_size
= media_data_size
;
2242 static int isom_scan_trak_profileLevelIndication
2245 mp4a_audioProfileLevelIndication
*audio_pli
,
2246 mp4sys_visualProfileLevelIndication
*visual_pli
2249 isom_stsd_t
*stsd
= trak
->mdia
->minf
->stbl
->stsd
;
2250 if( !stsd
->list
.head
)
2251 return LSMASH_ERR_INVALID_DATA
;
2252 for( lsmash_entry_t
*entry
= stsd
->list
.head
; entry
; entry
= entry
->next
)
2254 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)entry
->data
;
2255 if( LSMASH_IS_NON_EXISTING_BOX( sample_entry
) )
2256 return LSMASH_ERR_INVALID_DATA
;
2257 lsmash_codec_type_t sample_type
= sample_entry
->type
;
2258 if( LSMASH_IS_EXISTING_BOX( trak
->mdia
->minf
->vmhd
) )
2260 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC1_VIDEO
)
2261 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC2_VIDEO
)
2262 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC3_VIDEO
)
2263 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC4_VIDEO
)
2264 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVCP_VIDEO
)
2265 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_SVC1_VIDEO
)
2266 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MVC1_VIDEO
)
2267 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MVC2_VIDEO
) )
2269 /* FIXME: Do we have to arbitrate like audio? */
2270 if( *visual_pli
== MP4SYS_VISUAL_PLI_NONE_REQUIRED
)
2271 *visual_pli
= MP4SYS_VISUAL_PLI_H264_AVC
;
2274 *visual_pli
= MP4SYS_VISUAL_PLI_NOT_SPECIFIED
;
2276 else if( LSMASH_IS_EXISTING_BOX( trak
->mdia
->minf
->smhd
) )
2278 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4A_AUDIO
) )
2280 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)sample_entry
;
2281 isom_esds_t
*esds
= (isom_esds_t
*)isom_get_extension_box_format( &audio
->extensions
, ISOM_BOX_TYPE_ESDS
);
2282 if( LSMASH_IS_NON_EXISTING_BOX( esds
) || !esds
->ES
)
2283 return LSMASH_ERR_INVALID_DATA
;
2284 lsmash_audio_summary_t
*summary
= (lsmash_audio_summary_t
*)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO
);
2287 if( mp4sys_setup_summary_from_DecoderSpecificInfo( summary
, esds
->ES
) < 0 )
2288 *audio_pli
= MP4A_AUDIO_PLI_NOT_SPECIFIED
;
2290 *audio_pli
= mp4a_max_audioProfileLevelIndication( *audio_pli
, mp4a_get_audioProfileLevelIndication( summary
) );
2291 lsmash_cleanup_summary( (lsmash_summary_t
*)summary
);
2294 /* NOTE: Audio CODECs other than 'mp4a' does not have appropriate pli. */
2295 *audio_pli
= MP4A_AUDIO_PLI_NOT_SPECIFIED
;
2298 ; /* FIXME: Do we have to set OD_profileLevelIndication? */
2303 int isom_setup_iods( isom_moov_t
*moov
)
2305 if( LSMASH_IS_NON_EXISTING_BOX( moov
->iods
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_iods( moov
) ) )
2306 return LSMASH_ERR_NAMELESS
;
2307 isom_iods_t
*iods
= moov
->iods
;
2308 int err
= LSMASH_ERR_NAMELESS
;
2309 iods
->OD
= mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */
2312 mp4a_audioProfileLevelIndication audio_pli
= MP4A_AUDIO_PLI_NONE_REQUIRED
;
2313 mp4sys_visualProfileLevelIndication visual_pli
= MP4SYS_VISUAL_PLI_NONE_REQUIRED
;
2314 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
2316 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2317 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
2318 || LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
2320 if( (err
= isom_scan_trak_profileLevelIndication( trak
, &audio_pli
, &visual_pli
)) < 0 )
2322 if( (err
= mp4sys_create_ES_ID_Inc( iods
->OD
, trak
->tkhd
->track_ID
)) < 0 )
2325 if( (err
= mp4sys_to_InitialObjectDescriptor( iods
->OD
,
2326 0, /* FIXME: I'm not quite sure what the spec says. */
2327 MP4SYS_OD_PLI_NONE_REQUIRED
, MP4SYS_SCENE_PLI_NONE_REQUIRED
,
2328 audio_pli
, visual_pli
,
2329 MP4SYS_GRAPHICS_PLI_NONE_REQUIRED
)) < 0 )
2333 isom_remove_box_by_itself( iods
);
2337 int lsmash_create_object_descriptor( lsmash_root_t
*root
)
2339 if( isom_check_initializer_present( root
) < 0 )
2340 return LSMASH_ERR_FUNCTION_PARAM
;
2341 lsmash_file_t
*file
= root
->file
;
2342 /* Return error if this file is not compatible with MP4 file format. */
2343 if( !file
->mp4_version1
2344 && !file
->mp4_version2
)
2345 return LSMASH_ERR_FUNCTION_PARAM
;
2346 return isom_setup_iods( file
->moov
);
2349 /*---- finishing functions ----*/
2351 int isom_complement_data_reference( isom_minf_t
*minf
)
2353 if( LSMASH_IS_NON_EXISTING_BOX( minf
->dinf
->dref
) )
2354 return LSMASH_ERR_INVALID_DATA
;
2355 /* Complement data referece if absent. */
2356 if( !minf
->dinf
->dref
->list
.head
)
2358 isom_dref_entry_t
*url
= isom_add_dref_entry( minf
->dinf
->dref
, ISOM_BOX_TYPE_URL
);
2359 if( LSMASH_IS_NON_EXISTING_BOX( url
) )
2360 return LSMASH_ERR_NAMELESS
;
2361 url
->flags
= 0x000001; /* Media data is in the same file. */
2366 static lsmash_file_t
*isom_get_written_media_file
2369 uint32_t sample_description_index
2372 isom_minf_t
*minf
= trak
->mdia
->minf
;
2373 isom_sample_entry_t
*description
= (isom_sample_entry_t
*)lsmash_list_get_entry_data( &minf
->stbl
->stsd
->list
, sample_description_index
);
2374 isom_dref_entry_t
*dref_entry
= (isom_dref_entry_t
*)lsmash_list_get_entry_data( &minf
->dinf
->dref
->list
, description
? description
->data_reference_index
: 1 );
2375 lsmash_file_t
*file
= (!dref_entry
|| LSMASH_IS_NON_EXISTING_BOX( dref_entry
->ref_file
)) ? trak
->file
: dref_entry
->ref_file
;
2376 if( !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
2377 || !(file
->flags
& LSMASH_FILE_MODE_WRITE
) )
2382 int isom_check_large_offset_requirement
2388 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; )
2390 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2391 isom_stco_t
*stco
= trak
->mdia
->minf
->stbl
->stco
;
2392 if( !stco
->list
->tail
/* no samples */
2393 || stco
->large_presentation
2394 || (((isom_stco_entry_t
*)stco
->list
->tail
->data
)->chunk_offset
+ moov
->size
+ meta_size
) <= UINT32_MAX
)
2396 entry
= entry
->next
;
2397 continue; /* no need to convert stco into co64 */
2399 /* stco->co64 conversion */
2400 int err
= isom_convert_stco_to_co64( trak
->mdia
->minf
->stbl
);
2403 if( isom_update_box_size( moov
) == 0 )
2404 return LSMASH_ERR_INVALID_DATA
;
2405 entry
= moov
->trak_list
.head
; /* whenever any conversion, re-check all traks */
2410 void isom_add_preceding_box_size
2413 uint64_t preceding_size
2416 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
2418 /* Apply to the chunks in the same file. */
2419 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2420 isom_stsc_t
*stsc
= trak
->mdia
->minf
->stbl
->stsc
;
2421 isom_stco_t
*stco
= trak
->mdia
->minf
->stbl
->stco
;
2422 lsmash_entry_t
*stsc_entry
= stsc
->list
->head
;
2423 isom_stsc_entry_t
*stsc_data
= stsc_entry
? (isom_stsc_entry_t
*)stsc_entry
->data
: NULL
;
2424 uint32_t chunk_number
= 1;
2425 for( lsmash_entry_t
*stco_entry
= stco
->list
->head
; stco_entry
; )
2428 && stsc_data
->first_chunk
== chunk_number
)
2430 lsmash_file_t
*ref_file
= isom_get_written_media_file( trak
, stsc_data
->sample_description_index
);
2431 stsc_entry
= stsc_entry
->next
;
2432 stsc_data
= stsc_entry
? (isom_stsc_entry_t
*)stsc_entry
->data
: NULL
;
2433 if( ref_file
!= trak
->file
)
2435 /* The chunks are not contained in the same file. Skip applying the offset.
2436 * If no more stsc entries, the rest of the chunks is not contained in the same file. */
2437 if( !stsc_entry
|| !stsc_data
)
2439 while( stco_entry
&& chunk_number
< stsc_data
->first_chunk
)
2441 stco_entry
= stco_entry
->next
;
2447 if( stco
->large_presentation
)
2448 ((isom_co64_entry_t
*)stco_entry
->data
)->chunk_offset
+= preceding_size
;
2450 ((isom_stco_entry_t
*)stco_entry
->data
)->chunk_offset
+= preceding_size
;
2451 stco_entry
= stco_entry
->next
;
2457 int isom_establish_movie( lsmash_file_t
*file
)
2459 assert( file
== file
->initializer
);
2461 if( (err
= isom_check_mandatory_boxes( file
)) < 0
2462 || (err
= isom_set_movie_creation_time( file
)) < 0 )
2464 if( isom_update_box_size( file
->moov
) == 0 )
2465 return LSMASH_ERR_INVALID_DATA
;
2469 int lsmash_finish_movie
2471 lsmash_root_t
*root
,
2472 lsmash_adhoc_remux_t
*remux
2475 if( isom_check_initializer_present( root
) < 0 )
2476 return LSMASH_ERR_FUNCTION_PARAM
;
2477 lsmash_file_t
*file
= root
->file
;
2479 || LSMASH_IS_NON_EXISTING_BOX( file
->initializer
->moov
) )
2480 return LSMASH_ERR_INVALID_DATA
;
2481 if( file
->fragment
)
2482 return isom_finish_final_fragment_movie( file
, remux
);
2483 if( file
!= file
->initializer
)
2484 return LSMASH_ERR_INVALID_DATA
;
2486 isom_moov_t
*moov
= file
->moov
;
2487 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
2489 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2490 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
2491 || LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
)
2493 || !trak
->mdia
->minf
->stbl
->stsd
->list
.head
2494 || !trak
->mdia
->minf
->stbl
->stsd
->list
.head
->data
2495 || !trak
->mdia
->minf
->stbl
->stco
->list
2496 || !trak
->mdia
->minf
->stbl
->stco
->list
->tail
)
2497 return LSMASH_ERR_INVALID_DATA
;
2498 if( (err
= isom_complement_data_reference( trak
->mdia
->minf
)) < 0 )
2500 uint32_t track_ID
= trak
->tkhd
->track_ID
;
2501 uint32_t related_track_ID
= trak
->related_track_ID
;
2502 /* Disable the track if the track is a track reference chapter. */
2503 if( trak
->is_chapter
)
2504 trak
->tkhd
->flags
&= ~ISOM_TRACK_ENABLED
;
2505 if( trak
->is_chapter
&& related_track_ID
)
2507 /* In order that the track duration of the chapter track doesn't exceed that of the related track. */
2509 edit
.duration
= LSMASH_MIN( trak
->tkhd
->duration
, lsmash_get_track_duration( root
, related_track_ID
) );
2510 edit
.start_time
= 0;
2511 edit
.rate
= ISOM_EDIT_MODE_NORMAL
;
2512 if( (err
= lsmash_create_explicit_timeline_map( root
, track_ID
, edit
)) < 0 )
2515 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2516 /* Compress sample size table. */
2517 if( stbl
->compress_sample_size_table
2518 && (err
= stbl
->compress_sample_size_table( stbl
)) < 0 )
2520 /* Add stss box if any samples aren't sync sample. */
2521 if( !trak
->cache
->all_sync
&& !stbl
->stss
&& !isom_add_stss( stbl
) )
2522 return LSMASH_ERR_NAMELESS
;
2523 if( (err
= isom_update_tkhd_duration( trak
)) < 0
2524 || (err
= isom_update_bitrate_description( trak
->mdia
)) < 0 )
2527 if( file
->mp4_version1
== 1 && (err
= isom_setup_iods( moov
)) < 0 )
2529 if( (err
= isom_establish_movie( file
)) < 0 )
2531 /* Write the size of Media Data Box here. */
2532 lsmash_bs_t
*bs
= file
->bs
;
2533 file
->mdat
->manager
&= ~LSMASH_INCOMPLETE_BOX
;
2534 if( (err
= isom_write_box( bs
, (isom_box_t
*)file
->mdat
)) < 0 )
2536 /* Write the Movie Box and a Meta Box if no optimization for progressive download. */
2537 uint64_t meta_size
= file
->meta
? file
->meta
->size
: 0;
2540 if( (err
= isom_write_box( bs
, (isom_box_t
*)file
->moov
)) < 0
2541 || (err
= isom_write_box( bs
, (isom_box_t
*)file
->meta
)) < 0 )
2543 file
->size
+= moov
->size
+ meta_size
;
2546 /* stco->co64 conversion, depending on last chunk's offset */
2547 if( (err
= isom_check_large_offset_requirement( moov
, meta_size
)) < 0 )
2549 /* now the amount of offset is fixed. */
2550 uint64_t mtf_size
= moov
->size
+ meta_size
; /* sum of size of boxes moved to front */
2551 /* buffer size must be at least mtf_size * 2 */
2552 remux
->buffer_size
= LSMASH_MAX( remux
->buffer_size
, mtf_size
* 2 );
2553 /* Split to 2 buffers. */
2554 uint8_t *buf
[2] = { NULL
, NULL
};
2555 if( (buf
[0] = (uint8_t*)lsmash_malloc( remux
->buffer_size
)) == NULL
)
2556 return LSMASH_ERR_MEMORY_ALLOC
; /* NOTE: I think we still can fallback to "return isom_write_moov();" here. */
2557 size_t size
= remux
->buffer_size
/ 2;
2558 buf
[1] = buf
[0] + size
;
2559 /* Now, the amount of the offset is fixed. apply it to stco/co64 */
2560 isom_add_preceding_box_size( moov
, mtf_size
);
2561 /* Backup starting area of mdat and write moov + meta there instead. */
2562 isom_mdat_t
*mdat
= file
->mdat
;
2563 uint64_t total
= file
->size
+ mtf_size
;
2564 uint64_t placeholder_pos
= mdat
->pos
;
2565 if( (err
= lsmash_bs_write_seek( bs
, placeholder_pos
, SEEK_SET
)) < 0 )
2567 size_t read_num
= size
;
2568 lsmash_bs_read_data( bs
, buf
[0], &read_num
);
2569 uint64_t read_pos
= bs
->offset
;
2570 /* Write moov + meta there instead. */
2571 if( (err
= lsmash_bs_write_seek( bs
, placeholder_pos
, SEEK_SET
)) < 0
2572 || (err
= isom_write_box( bs
, (isom_box_t
*)file
->moov
)) < 0
2573 || (err
= isom_write_box( bs
, (isom_box_t
*)file
->meta
)) < 0 )
2575 uint64_t write_pos
= bs
->offset
;
2576 /* Update the positions */
2577 mdat
->pos
+= mtf_size
;
2578 /* Move Media Data Box. */
2579 if( (err
= isom_rearrange_data( file
, remux
, buf
, read_num
, size
, read_pos
, write_pos
, total
)) < 0 )
2581 file
->size
+= mtf_size
;
2582 lsmash_free( buf
[0] );
2585 lsmash_free( buf
[0] );
2589 int lsmash_set_last_sample_delta( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t sample_delta
)
2591 if( isom_check_initializer_present( root
) < 0 || track_ID
== 0 )
2592 return LSMASH_ERR_FUNCTION_PARAM
;
2593 lsmash_file_t
*file
= root
->file
;
2595 && file
->fragment
->movie
)
2597 isom_traf_t
*traf
= isom_get_traf( file
->fragment
->movie
, track_ID
);
2598 if( LSMASH_IS_NON_EXISTING_BOX( traf
)
2599 || LSMASH_IS_NON_EXISTING_BOX( traf
->tfhd
)
2601 return LSMASH_ERR_NAMELESS
;
2602 return isom_set_fragment_last_duration( traf
, sample_delta
);
2604 if( file
!= file
->initializer
)
2605 return LSMASH_ERR_INVALID_DATA
;
2606 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2607 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
)
2608 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->stbl
->stsd
)
2609 || (LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->stbl
->stsz
)
2610 && LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->stbl
->stz2
))
2612 || !trak
->mdia
->minf
->stbl
->stts
->list
)
2613 return LSMASH_ERR_NAMELESS
;
2614 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2615 isom_stts_t
*stts
= stbl
->stts
;
2616 uint32_t sample_count
= isom_get_sample_count( trak
);
2618 if( !stts
->list
->tail
)
2620 if( sample_count
== 0 )
2621 return 0; /* no samples */
2622 if( sample_count
> 1 )
2623 return LSMASH_ERR_INVALID_DATA
; /* irregular sample_count */
2624 /* Set the duration of the first sample.
2625 * This duration is also the duration of the last sample. */
2626 if( (err
= isom_add_stts_entry( stbl
, sample_delta
)) < 0 )
2628 return lsmash_update_track_duration( root
, track_ID
, 0 );
2631 for( lsmash_entry_t
*entry
= stts
->list
->head
; entry
; entry
= entry
->next
)
2632 i
+= ((isom_stts_entry_t
*)entry
->data
)->sample_count
;
2633 if( sample_count
< i
)
2634 return LSMASH_ERR_INVALID_DATA
;
2635 int no_last
= (sample_count
> i
);
2636 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)stts
->list
->tail
->data
;
2637 if( !last_stts_data
)
2638 return LSMASH_ERR_INVALID_DATA
;
2639 /* Consider QuikcTime fixed compression audio. */
2640 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)lsmash_list_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
,
2641 trak
->cache
->chunk
.sample_description_index
);
2642 if( LSMASH_IS_NON_EXISTING_BOX( audio
) )
2643 return LSMASH_ERR_INVALID_DATA
;
2644 if( (audio
->manager
& LSMASH_AUDIO_DESCRIPTION
)
2645 && (audio
->manager
& LSMASH_QTFF_BASE
)
2646 && (audio
->version
== 1)
2647 && (audio
->compression_ID
!= QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION
) )
2649 if( audio
->samplesPerPacket
== 0 )
2650 return LSMASH_ERR_INVALID_DATA
;
2651 int exclude_last_sample
= no_last
? 0 : 1;
2652 uint32_t j
= audio
->samplesPerPacket
;
2653 for( lsmash_entry_t
*entry
= stts
->list
->tail
; entry
&& j
> 1; entry
= entry
->prev
)
2655 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)entry
->data
;
2657 return LSMASH_ERR_INVALID_DATA
;
2658 for( uint32_t k
= exclude_last_sample
; k
< stts_data
->sample_count
&& j
> 1; k
++ )
2660 sample_delta
-= stts_data
->sample_delta
;
2663 exclude_last_sample
= 0;
2666 /* Set sample_delta. */
2669 /* The duration of the last sample is not set yet. */
2670 if( sample_count
- i
> 1 )
2671 return LSMASH_ERR_INVALID_DATA
;
2672 /* Add a sample_delta. */
2673 if( sample_delta
== last_stts_data
->sample_delta
)
2674 ++ last_stts_data
->sample_count
;
2675 else if( (err
= isom_add_stts_entry( stbl
, sample_delta
)) < 0 )
2678 /* The duration of the last sample is already set. Replace it with a new one. */
2679 else if( (err
= isom_replace_last_sample_delta( stbl
, sample_delta
)) < 0 )
2681 return lsmash_update_track_duration( root
, track_ID
, sample_delta
);
2684 /*---- timeline manipulator ----*/
2686 int lsmash_modify_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t edit_number
, lsmash_edit_t edit
)
2688 if( isom_check_initializer_present( root
) < 0
2689 || edit
.start_time
< -1 )
2690 return LSMASH_ERR_FUNCTION_PARAM
;
2691 lsmash_file_t
*file
= root
->file
->initializer
;
2692 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2693 if( !trak
->edts
->elst
->list
)
2694 return LSMASH_ERR_NAMELESS
;
2695 isom_elst_t
*elst
= trak
->edts
->elst
;
2696 isom_elst_entry_t
*data
= (isom_elst_entry_t
*)lsmash_list_get_entry_data( elst
->list
, edit_number
);
2698 return LSMASH_ERR_NAMELESS
;
2699 data
->segment_duration
= edit
.duration
;
2700 data
->media_time
= edit
.start_time
;
2701 data
->media_rate
= edit
.rate
;
2702 if( elst
->pos
== 0 || !file
->fragment
|| file
->bs
->unseekable
)
2703 return isom_update_tkhd_duration( trak
);
2704 /* Rewrite the specified entry.
2705 * Note: we don't update the version of the Edit List Box. */
2706 lsmash_bs_t
*bs
= file
->bs
;
2707 uint64_t current_pos
= bs
->offset
;
2708 uint64_t entry_pos
= elst
->pos
+ ISOM_LIST_FULLBOX_COMMON_SIZE
+ ((uint64_t)edit_number
- 1) * (elst
->version
== 1 ? 20 : 12);
2709 lsmash_bs_write_seek( bs
, entry_pos
, SEEK_SET
);
2712 lsmash_bs_put_be64( bs
, data
->segment_duration
);
2713 lsmash_bs_put_be64( bs
, data
->media_time
);
2717 lsmash_bs_put_be32( bs
, (uint32_t)LSMASH_MIN( data
->segment_duration
, UINT32_MAX
) );
2718 lsmash_bs_put_be32( bs
, (uint32_t)data
->media_time
);
2720 lsmash_bs_put_be32( bs
, data
->media_rate
);
2721 int ret
= lsmash_bs_flush_buffer( bs
);
2722 lsmash_bs_write_seek( bs
, current_pos
, SEEK_SET
);
2726 int lsmash_create_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_edit_t edit
)
2728 if( isom_check_initializer_present( root
) < 0 || edit
.start_time
< -1 )
2729 return LSMASH_ERR_FUNCTION_PARAM
;
2730 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
2731 if( LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
2732 return LSMASH_ERR_NAMELESS
;
2733 edit
.duration
= (edit
.duration
|| root
->file
->fragment
) ? edit
.duration
2734 : trak
->tkhd
->duration
? trak
->tkhd
->duration
2735 : isom_update_tkhd_duration( trak
) < 0 ? 0
2736 : trak
->tkhd
->duration
;
2737 if( (LSMASH_IS_NON_EXISTING_BOX( trak
->edts
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_edts( trak
) ))
2738 || (LSMASH_IS_NON_EXISTING_BOX( trak
->edts
->elst
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_elst( trak
->edts
) )) )
2739 return LSMASH_ERR_NAMELESS
;
2740 int err
= isom_add_elst_entry( trak
->edts
->elst
, edit
.duration
, edit
.start_time
, edit
.rate
);
2743 return isom_update_tkhd_duration( trak
);
2746 int lsmash_get_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t edit_number
, lsmash_edit_t
*edit
)
2748 if( isom_check_initializer_present( root
) < 0 || !edit
)
2749 return LSMASH_ERR_FUNCTION_PARAM
;
2750 isom_elst_entry_t
*data
;
2751 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2752 if( LSMASH_IS_NON_EXISTING_BOX( trak
) )
2753 data
= isom_timelime_get_explicit_timeline_map( root
, track_ID
, edit_number
);
2756 if( LSMASH_IS_NON_EXISTING_BOX( trak
->edts
->elst
) )
2760 edit
->start_time
= 0;
2764 data
= (isom_elst_entry_t
*)lsmash_list_get_entry_data( trak
->edts
->elst
->list
, edit_number
);
2767 return LSMASH_ERR_NAMELESS
;
2768 edit
->duration
= data
->segment_duration
;
2769 edit
->start_time
= data
->media_time
;
2770 edit
->rate
= data
->media_rate
;
2774 uint32_t lsmash_count_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
)
2776 if( isom_check_initializer_present( root
) < 0 )
2777 return LSMASH_ERR_FUNCTION_PARAM
;
2778 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2779 if( LSMASH_IS_NON_EXISTING_BOX( trak
) )
2780 return isom_timelime_count_explicit_timeline_map( root
, track_ID
);
2782 return trak
->edts
->elst
->list
? trak
->edts
->elst
->list
->entry_count
: 0;
2785 /*---- create / modification time fields manipulators ----*/
2787 int lsmash_update_media_modification_time( lsmash_root_t
*root
, uint32_t track_ID
)
2789 if( isom_check_initializer_present( root
) < 0 )
2790 return LSMASH_ERR_FUNCTION_PARAM
;
2791 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2792 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
) )
2793 return LSMASH_ERR_NAMELESS
;
2794 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
2795 mdhd
->modification_time
= isom_get_current_mp4time();
2796 /* overwrite strange creation_time */
2797 if( mdhd
->creation_time
> mdhd
->modification_time
)
2798 mdhd
->creation_time
= mdhd
->modification_time
;
2802 int lsmash_update_track_modification_time( lsmash_root_t
*root
, uint32_t track_ID
)
2804 if( isom_check_initializer_present( root
) < 0 )
2805 return LSMASH_ERR_FUNCTION_PARAM
;
2806 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2807 if( LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
2808 return LSMASH_ERR_NAMELESS
;
2809 isom_tkhd_t
*tkhd
= trak
->tkhd
;
2810 tkhd
->modification_time
= isom_get_current_mp4time();
2811 /* overwrite strange creation_time */
2812 if( tkhd
->creation_time
> tkhd
->modification_time
)
2813 tkhd
->creation_time
= tkhd
->modification_time
;
2817 int lsmash_update_movie_modification_time( lsmash_root_t
*root
)
2819 if( isom_check_initializer_present( root
) < 0 )
2820 return LSMASH_ERR_FUNCTION_PARAM
;
2821 lsmash_file_t
*file
= root
->file
->initializer
;
2822 if( LSMASH_IS_NON_EXISTING_BOX( file
->moov
->mvhd
) )
2823 return LSMASH_ERR_INVALID_DATA
;
2824 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
2825 mvhd
->modification_time
= isom_get_current_mp4time();
2826 /* overwrite strange creation_time */
2827 if( mvhd
->creation_time
> mvhd
->modification_time
)
2828 mvhd
->creation_time
= mvhd
->modification_time
;
2832 /*---- sample manipulators ----*/
2833 lsmash_sample_t
*lsmash_create_sample( uint32_t size
)
2835 lsmash_sample_t
*sample
= lsmash_malloc_zero( sizeof(lsmash_sample_t
) );
2840 sample
->data
= lsmash_malloc( size
);
2843 lsmash_free( sample
);
2846 sample
->length
= size
;
2850 int lsmash_sample_alloc( lsmash_sample_t
*sample
, uint32_t size
)
2853 return LSMASH_ERR_FUNCTION_PARAM
;
2856 lsmash_free( sample
->data
);
2857 sample
->data
= NULL
;
2861 if( size
== sample
->length
)
2865 data
= lsmash_malloc( size
);
2867 data
= lsmash_realloc( sample
->data
, size
);
2869 return LSMASH_ERR_MEMORY_ALLOC
;
2870 sample
->data
= data
;
2871 sample
->length
= size
;
2875 void lsmash_delete_sample( lsmash_sample_t
*sample
)
2879 lsmash_free( sample
->data
);
2880 lsmash_free( sample
);
2883 isom_sample_pool_t
*isom_create_sample_pool( uint64_t size
)
2885 isom_sample_pool_t
*pool
= lsmash_malloc_zero( sizeof(isom_sample_pool_t
) );
2890 pool
->data
= lsmash_malloc( size
);
2893 lsmash_free( pool
);
2900 void isom_remove_sample_pool( isom_sample_pool_t
*pool
)
2904 lsmash_free( pool
->data
);
2905 lsmash_free( pool
);
2908 static uint32_t isom_add_size( isom_stbl_t
*stbl
, uint32_t sample_size
)
2910 if( isom_add_stsz_entry( stbl
, sample_size
) < 0 )
2912 return isom_get_sample_count_from_sample_table( stbl
);
2915 static uint32_t isom_add_dts( isom_stbl_t
*stbl
, uint64_t dts
, uint64_t prev_dts
)
2917 isom_stts_t
*stts
= stbl
->stts
;
2918 if( stts
->list
->entry_count
== 0 )
2919 return isom_add_stts_entry( stbl
, dts
) < 0 ? 0 : dts
;
2920 if( dts
<= prev_dts
)
2922 uint32_t sample_delta
= dts
- prev_dts
;
2923 isom_stts_entry_t
*data
= (isom_stts_entry_t
*)stts
->list
->tail
->data
;
2924 if( data
->sample_delta
== sample_delta
)
2925 ++ data
->sample_count
;
2926 else if( isom_add_stts_entry( stbl
, sample_delta
) < 0 )
2928 return sample_delta
;
2931 /* Add ctts box and the first ctts entry. */
2932 static int isom_add_initial_sample_offset( isom_stbl_t
*stbl
, uint32_t sample_offset
)
2934 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_ctts( stbl
) ) )
2935 return LSMASH_ERR_NAMELESS
;
2936 if( sample_offset
== ISOM_NON_OUTPUT_SAMPLE_OFFSET
)
2937 stbl
->ctts
->version
= 1;
2938 uint32_t sample_count
= isom_get_sample_count_from_sample_table( stbl
);
2939 if( sample_count
> 1 )
2941 /* Set all prior samples' sample_offset to 0. */
2942 int err
= isom_add_ctts_entry( stbl
, sample_count
- 1, 0 );
2946 return isom_add_ctts_entry( stbl
, 1, sample_offset
);
2949 static int isom_add_sample_offset( isom_stbl_t
*stbl
, uint32_t sample_offset
)
2951 if( !stbl
->ctts
->list
)
2952 return LSMASH_ERR_INVALID_DATA
;
2953 isom_ctts_entry_t
*data
= (isom_ctts_entry_t
*)stbl
->ctts
->list
->tail
->data
;
2954 if( data
->sample_offset
== sample_offset
)
2955 ++ data
->sample_count
;
2958 int err
= isom_add_ctts_entry( stbl
, 1, sample_offset
);
2965 static int isom_add_cts( isom_stbl_t
*stbl
, uint64_t dts
, uint64_t cts
, int non_output_sample
)
2967 uint32_t sample_offset
= !non_output_sample
? cts
- dts
: ISOM_NON_OUTPUT_SAMPLE_OFFSET
;
2968 if( LSMASH_IS_EXISTING_BOX( stbl
->ctts
) )
2969 return isom_add_sample_offset( stbl
, sample_offset
);
2970 return sample_offset
!= 0 ? isom_add_initial_sample_offset( stbl
, sample_offset
) : 0;
2973 static int isom_check_sample_offset_compatibility( lsmash_file_t
*file
, uint64_t dts
, uint64_t cts
, int non_output_sample
)
2975 if( non_output_sample
)
2977 if( file
->min_isom_version
< 4 )
2978 return LSMASH_ERR_INVALID_DATA
; /* Non-output sample can be indicated under 'iso4' or later brands. */
2982 if( file
->isom_compatible
&& file
->qt_compatible
&& (((cts
>= dts
) ? (cts
- dts
) : (dts
- cts
)) > INT32_MAX
) )
2983 return LSMASH_ERR_INVALID_DATA
; /* sample_offset is not compatible with both ISOBMFF and QTFF. */
2985 if( non_output_sample
|| cts
< dts
)
2987 /* Negative sample offset is required. */
2988 if( file
->max_isom_version
< 4 && !file
->qt_compatible
)
2989 return LSMASH_ERR_INVALID_DATA
; /* Negative sample offset is not supported in both ISOBMFF and QTFF. */
2990 if( file
->max_isom_version
>= 4 && file
->qt_compatible
)
2991 return LSMASH_ERR_INVALID_DATA
; /* ctts version 1 is not defined in QTFF. */
2996 void isom_update_cache_timestamp
2998 isom_cache_t
*cache
,
3002 uint32_t sample_duration
,
3003 int non_output_sample
3006 cache
->timestamp
.dts
= dts
;
3007 cache
->timestamp
.cts
= non_output_sample
? cache
->timestamp
.cts
: cts
;
3008 cache
->timestamp
.ctd_shift
= ctd_shift
;
3009 if( cache
->fragment
)
3011 cache
->fragment
->last_duration
= sample_duration
;
3012 if( !non_output_sample
)
3013 cache
->fragment
->largest_cts
3014 = cache
->fragment
->largest_cts
!= LSMASH_TIMESTAMP_UNDEFINED
3015 ? LSMASH_MAX( cache
->timestamp
.cts
, cache
->fragment
->largest_cts
)
3016 : cache
->timestamp
.cts
;
3020 static int isom_add_timestamp( isom_stbl_t
*stbl
, isom_cache_t
*cache
, lsmash_file_t
*file
, uint64_t dts
, uint64_t cts
)
3022 if( !cache
|| !stbl
->stts
->list
)
3023 return LSMASH_ERR_INVALID_DATA
;
3024 int non_output_sample
= (cts
== LSMASH_TIMESTAMP_UNDEFINED
);
3025 int err
= isom_check_sample_offset_compatibility( file
, dts
, cts
, non_output_sample
);
3028 uint32_t sample_count
= isom_get_sample_count_from_sample_table( stbl
);
3029 uint32_t sample_delta
= sample_count
> 1 ? isom_add_dts( stbl
, dts
, cache
->timestamp
.dts
) : 0;
3030 if( sample_count
> 1 && sample_delta
== 0 )
3031 return LSMASH_ERR_INVALID_DATA
;
3032 if( (err
= isom_add_cts( stbl
, dts
, cts
, non_output_sample
)) < 0 )
3034 int32_t ctd_shift
= cache
->timestamp
.ctd_shift
;
3035 if( !non_output_sample
&& ((cts
+ ctd_shift
) < dts
) )
3037 /* Check overflow of composition to decode timeline shift. */
3038 if( (dts
- cts
) > INT32_MAX
)
3039 return LSMASH_ERR_INVALID_DATA
;
3040 assert( LSMASH_IS_EXISTING_BOX( stbl
->ctts
) );
3041 if( stbl
->ctts
->version
== 0 && !file
->qt_compatible
)
3042 stbl
->ctts
->version
= 1;
3043 ctd_shift
= dts
- cts
;
3045 isom_update_cache_timestamp( cache
, dts
, cts
, ctd_shift
, sample_delta
, non_output_sample
);
3049 static int isom_add_sync_point( isom_stbl_t
*stbl
, isom_cache_t
*cache
, uint32_t sample_number
, lsmash_sample_property_t
*prop
)
3051 if( !(prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
) ) /* no null check for prop */
3053 if( !cache
->all_sync
)
3055 if( LSMASH_IS_NON_EXISTING_BOX( stbl
->stss
)
3056 && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stss( stbl
) ) )
3057 return LSMASH_ERR_NAMELESS
;
3058 int err
= isom_add_stss_entry( stbl
, 1 );
3059 if( err
< 0 ) /* Declare here the first sample is a sync sample. */
3061 cache
->all_sync
= 0;
3064 if( cache
->all_sync
) /* We don't need stss box if all samples are sync sample. */
3066 if( LSMASH_IS_NON_EXISTING_BOX( stbl
->stss
) )
3068 if( isom_get_sample_count_from_sample_table( stbl
) == 1 )
3070 cache
->all_sync
= 1; /* Also the first sample is a sync sample. */
3073 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stss( stbl
) ) )
3074 return LSMASH_ERR_NAMELESS
;
3076 return isom_add_stss_entry( stbl
, sample_number
);
3079 static int isom_add_partial_sync( isom_stbl_t
*stbl
, lsmash_file_t
*file
, uint32_t sample_number
, lsmash_sample_property_t
*prop
)
3081 if( !file
->qt_compatible
)
3083 if( !(prop
->ra_flags
& QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
) )
3085 /* This sample is a partial sync sample. */
3086 if( LSMASH_IS_NON_EXISTING_BOX( stbl
->stps
)
3087 && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stps( stbl
) ) )
3088 return LSMASH_ERR_NAMELESS
;
3089 return isom_add_stps_entry( stbl
, sample_number
);
3092 int isom_rap_grouping_established( isom_rap_group_t
*group
, int num_leading_samples_known
, isom_sgpd_t
*sgpd
, int is_fragment
)
3094 isom_rap_entry_t
*rap
= group
->random_access
;
3097 assert( rap
== (isom_rap_entry_t
*)sgpd
->list
->tail
->data
);
3098 rap
->num_leading_samples_known
= num_leading_samples_known
;
3099 /* Avoid duplication of sample group descriptions. */
3100 uint32_t group_description_index
= is_fragment
? 0x10001 : 1;
3101 for( lsmash_entry_t
*entry
= sgpd
->list
->head
; entry
!= sgpd
->list
->tail
; entry
= entry
->next
)
3103 isom_rap_entry_t
*data
= (isom_rap_entry_t
*)entry
->data
;
3105 return LSMASH_ERR_INVALID_DATA
;
3106 if( rap
->num_leading_samples_known
== data
->num_leading_samples_known
3107 && rap
->num_leading_samples
== data
->num_leading_samples
)
3109 /* The same description already exists.
3110 * Remove the latest random access entry. */
3111 lsmash_list_remove_entry_tail( sgpd
->list
);
3112 /* Replace assigned group_description_index with the one corresponding the same description. */
3113 if( group
->assignment
->group_description_index
== 0 )
3115 /* We don't create consecutive sample groups not assigned to 'rap '.
3116 * So the previous sample group shall be a group of 'rap ' if any. */
3117 if( group
->prev_assignment
)
3119 assert( group
->prev_assignment
->group_description_index
);
3120 group
->prev_assignment
->group_description_index
= group_description_index
;
3124 group
->assignment
->group_description_index
= group_description_index
;
3127 ++group_description_index
;
3129 group
->random_access
= NULL
;
3133 int isom_group_random_access( isom_box_t
*parent
, isom_cache_t
*cache
, lsmash_sample_t
*sample
)
3135 if( parent
->file
->max_isom_version
< 6 )
3139 uint32_t sample_count
;
3141 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_STBL
) )
3143 isom_stbl_t
*stbl
= (isom_stbl_t
*)parent
;
3144 sbgp
= isom_get_sample_to_group ( stbl
, ISOM_GROUP_TYPE_RAP
);
3145 sgpd
= isom_get_sample_group_description( stbl
, ISOM_GROUP_TYPE_RAP
);
3146 sample_count
= isom_get_sample_count_from_sample_table( stbl
);
3149 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
3151 isom_traf_t
*traf
= (isom_traf_t
*)parent
;
3152 sbgp
= isom_get_fragment_sample_to_group ( traf
, ISOM_GROUP_TYPE_RAP
);
3153 sgpd
= isom_get_fragment_sample_group_description( traf
, ISOM_GROUP_TYPE_RAP
);
3154 sample_count
= cache
->fragment
->sample_count
+ 1; /* Cached sample_count is incremented later in isom_fragment_update_cache(). */
3160 sbgp
= isom_non_existing_sbgp();
3161 sgpd
= isom_non_existing_sgpd();
3162 /* redundant initializations to suppress warnings from unclever compilers */
3166 if( LSMASH_IS_NON_EXISTING_BOX( sbgp
)
3167 || LSMASH_IS_NON_EXISTING_BOX( sgpd
) )
3169 lsmash_sample_property_t
*prop
= &sample
->prop
;
3170 uint8_t is_rap
= (prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
)
3171 || (prop
->ra_flags
& QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
)
3172 || (prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
)
3173 || (LSMASH_IS_POST_ROLL_START( prop
->ra_flags
) && prop
->post_roll
.identifier
== prop
->post_roll
.complete
);
3174 isom_rap_group_t
*group
= cache
->rap
;
3177 /* This sample is the first sample, create a grouping cache. */
3178 assert( sample_count
== 1 );
3179 group
= lsmash_malloc( sizeof(isom_rap_group_t
) );
3181 return LSMASH_ERR_MEMORY_ALLOC
;
3184 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3185 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3189 /* The first sample is not always a random access point. */
3190 group
->random_access
= NULL
;
3191 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3193 if( !group
->assignment
)
3195 lsmash_free( group
);
3196 return LSMASH_ERR_MEMORY_ALLOC
;
3198 group
->prev_assignment
= NULL
;
3199 group
->is_prev_rap
= is_rap
;
3204 if( group
->is_prev_rap
)
3206 /* OK. here, the previous sample is a menber of 'rap '. */
3209 /* This sample isn't a member of 'rap ' and the previous sample is.
3210 * So we create a new group and set 0 on its group_description_index. */
3211 group
->prev_assignment
= group
->assignment
;
3212 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3213 if( !group
->assignment
)
3215 lsmash_free( group
);
3216 return LSMASH_ERR_MEMORY_ALLOC
;
3219 else if( !LSMASH_IS_CLOSED_RAP( prop
->ra_flags
) )
3221 /* Create a new group since there is the possibility the next sample is a leading sample.
3222 * This sample is a member of 'rap ', so we set appropriate value on its group_description_index. */
3223 if( (err
= isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
)) < 0 )
3225 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3226 group
->prev_assignment
= group
->assignment
;
3227 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3228 if( !group
->assignment
)
3230 lsmash_free( group
);
3231 return LSMASH_ERR_MEMORY_ALLOC
;
3234 else /* The previous and current sample are a member of 'rap ', and the next sample must not be a leading sample. */
3235 ++ group
->assignment
->sample_count
;
3239 /* This sample is a member of 'rap ' and the previous sample isn't.
3240 * So we create a new group and set appropriate value on its group_description_index. */
3241 if( (err
= isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
)) < 0 )
3243 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3244 group
->prev_assignment
= group
->assignment
;
3245 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3246 if( !group
->assignment
)
3248 lsmash_free( group
);
3249 return LSMASH_ERR_MEMORY_ALLOC
;
3252 else /* The previous and current sample aren't a member of 'rap '. */
3253 ++ group
->assignment
->sample_count
;
3254 /* Obtain the property of the latest random access point group. */
3255 if( !is_rap
&& group
->random_access
)
3257 if( prop
->leading
== ISOM_SAMPLE_LEADING_UNKNOWN
)
3259 /* We can no longer know num_leading_samples in this group. */
3260 if( (err
= isom_rap_grouping_established( group
, 0, sgpd
, is_fragment
)) < 0 )
3265 if( prop
->leading
== ISOM_SAMPLE_IS_UNDECODABLE_LEADING
3266 || prop
->leading
== ISOM_SAMPLE_IS_DECODABLE_LEADING
)
3267 ++ group
->random_access
->num_leading_samples
;
3268 /* no more consecutive leading samples in this group */
3269 else if( (err
= isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
)) < 0 )
3273 group
->is_prev_rap
= is_rap
;
3277 static int isom_roll_grouping_established( isom_roll_group_t
*group
)
3279 /* Avoid duplication of sample group descriptions. */
3280 isom_sgpd_t
*sgpd
= group
->sgpd
;
3281 uint32_t group_description_index
= group
->is_fragment
? 0x10001 : 1;
3282 for( lsmash_entry_t
*entry
= sgpd
->list
->head
; entry
; entry
= entry
->next
)
3284 isom_roll_entry_t
*data
= (isom_roll_entry_t
*)entry
->data
;
3286 return LSMASH_ERR_INVALID_DATA
;
3287 if( group
->roll_distance
== data
->roll_distance
)
3289 /* The same description already exists.
3290 * Set the group_description_index corresponding the same description. */
3291 group
->assignment
->group_description_index
= group_description_index
;
3294 ++group_description_index
;
3296 /* Add a new roll recovery description. */
3297 if( !isom_add_roll_group_entry( sgpd
, group
->roll_distance
) )
3298 return LSMASH_ERR_MEMORY_ALLOC
;
3299 group
->assignment
->group_description_index
= sgpd
->list
->entry_count
+ (group
->is_fragment
? 0x10000 : 0);
3303 static int isom_deduplicate_roll_group( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3306 uint32_t current_group_number
= sbgp
->list
->entry_count
- pool
->entry_count
+ 1;
3307 isom_group_assignment_entry_t
*prev_assignment
= (isom_group_assignment_entry_t
*)lsmash_list_get_entry_data( sbgp
->list
, current_group_number
- 1 );
3308 for( lsmash_entry_t
*entry
= pool
->head
; entry
; )
3310 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3312 || !group
->assignment
)
3313 return LSMASH_ERR_INVALID_DATA
;
3314 if( !group
->delimited
|| group
->described
!= ROLL_DISTANCE_DETERMINED
)
3316 if( prev_assignment
&& prev_assignment
->group_description_index
== group
->assignment
->group_description_index
)
3318 /* Merge the current group with the previous. */
3319 lsmash_entry_t
*next_entry
= entry
->next
;
3320 prev_assignment
->sample_count
+= group
->assignment
->sample_count
;
3322 if( (err
= lsmash_list_remove_entry( sbgp
->list
, current_group_number
)) < 0
3323 || (err
= lsmash_list_remove_entry_direct( pool
, entry
)) < 0 )
3329 entry
= entry
->next
;
3330 prev_assignment
= group
->assignment
;
3331 ++current_group_number
;
3337 /* Remove pooled caches that has become unnecessary. */
3338 static int isom_clean_roll_pool( lsmash_entry_list_t
*pool
)
3340 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= pool
->head
)
3342 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3344 return LSMASH_ERR_INVALID_DATA
;
3345 if( !group
->delimited
|| group
->described
!= ROLL_DISTANCE_DETERMINED
)
3347 int err
= lsmash_list_remove_entry_direct( pool
, entry
);
3354 static int isom_flush_roll_pool( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3357 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3359 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3361 return LSMASH_ERR_INVALID_DATA
;
3362 if( group
->delimited
3363 && group
->described
== ROLL_DISTANCE_DETERMINED
3364 && group
->roll_distance
!= 0
3365 && (err
= isom_roll_grouping_established( group
)) < 0 )
3368 if( (err
= isom_deduplicate_roll_group( sbgp
, pool
)) < 0 )
3370 return isom_clean_roll_pool( pool
);
3373 static int isom_all_recovery_described( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3375 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3377 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3379 return LSMASH_ERR_INVALID_DATA
;
3380 group
->described
= ROLL_DISTANCE_DETERMINED
;
3382 return isom_flush_roll_pool( sbgp
, pool
);
3385 int isom_all_recovery_completed( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3387 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3389 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3391 return LSMASH_ERR_INVALID_DATA
;
3392 group
->described
= ROLL_DISTANCE_DETERMINED
;
3393 group
->delimited
= 1;
3395 return isom_flush_roll_pool( sbgp
, pool
);
3398 static isom_roll_entry_t
*isom_get_roll_description
3400 isom_roll_group_t
*group
3403 uint32_t group_description_index
= group
->assignment
->group_description_index
;
3404 if( group_description_index
&& group
->is_fragment
)
3406 assert( group_description_index
> 0x10000 );
3407 group_description_index
-= 0x10000;
3409 return (isom_roll_entry_t
*)lsmash_list_get_entry_data( group
->sgpd
->list
, group_description_index
);
3412 int isom_group_roll_recovery( isom_box_t
*parent
, isom_cache_t
*cache
, lsmash_sample_t
*sample
)
3414 if( !parent
->file
->avc_extensions
3415 && !parent
->file
->qt_compatible
)
3417 uint32_t sample_count
;
3419 lsmash_entry_list_t
*sbgp_list
;
3420 lsmash_entry_list_t
*sgpd_list
;
3421 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_STBL
) )
3423 isom_stbl_t
*stbl
= (isom_stbl_t
*)parent
;
3424 sbgp_list
= &stbl
->sbgp_list
;
3425 sgpd_list
= &stbl
->sgpd_list
;
3426 sample_count
= isom_get_sample_count_from_sample_table( stbl
);
3429 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
3431 if( parent
->file
->max_isom_version
< 6 )
3433 isom_traf_t
*traf
= (isom_traf_t
*)parent
;
3434 sbgp_list
= &traf
->sbgp_list
;
3435 sgpd_list
= &traf
->sgpd_list
;
3436 sample_count
= cache
->fragment
->sample_count
+ 1; /* Cached sample_count is incremented later in isom_fragment_update_cache(). */
3442 return LSMASH_ERR_INVALID_DATA
;
3444 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group ( sbgp_list
);
3445 isom_sgpd_t
*sgpd
= isom_get_roll_recovery_sample_group_description( sgpd_list
);
3446 if( LSMASH_IS_NON_EXISTING_BOX( sbgp
)
3447 || LSMASH_IS_NON_EXISTING_BOX( sgpd
)
3448 || sbgp
->grouping_type
!= sgpd
->grouping_type
)
3450 /* Check if 'roll' -> 'prol' conversion is needed. */
3452 && sbgp
->grouping_type
== ISOM_GROUP_TYPE_ROLL
3453 && !(sample
->prop
.ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
) )
3455 /* Since not every samples is a sync sample, change grouping_type into 'prol'. */
3456 sbgp
->grouping_type
= ISOM_GROUP_TYPE_PROL
;
3457 sgpd
->grouping_type
= ISOM_GROUP_TYPE_PROL
;
3459 lsmash_entry_list_t
*pool
= cache
->roll
.pool
;
3462 pool
= lsmash_list_create_simple();
3464 return LSMASH_ERR_MEMORY_ALLOC
;
3465 cache
->roll
.pool
= pool
;
3467 lsmash_sample_property_t
*prop
= &sample
->prop
;
3468 isom_roll_group_t
*group
= (isom_roll_group_t
*)lsmash_list_get_entry_data( pool
, pool
->entry_count
);
3469 int is_recovery_start
= LSMASH_IS_POST_ROLL_START( prop
->ra_flags
);
3470 int valid_pre_roll
= !is_recovery_start
3471 && (prop
->ra_flags
!= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
)
3472 && (prop
->pre_roll
.distance
> 0)
3473 && (prop
->pre_roll
.distance
<= -INT16_MIN
);
3474 int new_group
= !group
|| is_recovery_start
|| (group
->prev_is_recovery_start
!= is_recovery_start
);
3477 /* Check pre-roll distance. */
3478 assert( group
->assignment
&& group
->sgpd
);
3479 isom_roll_entry_t
*prev_roll
= isom_get_roll_description( group
);
3481 new_group
= valid_pre_roll
;
3482 else if( !valid_pre_roll
|| (prop
->pre_roll
.distance
!= -prev_roll
->roll_distance
) )
3483 /* Pre-roll distance is different from the previous. */
3489 group
->delimited
= 1;
3491 assert( sample_count
== 1 );
3492 /* Create a new group. */
3493 group
= lsmash_malloc_zero( sizeof(isom_roll_group_t
) );
3495 return LSMASH_ERR_MEMORY_ALLOC
;
3497 group
->prev_is_recovery_start
= is_recovery_start
;
3498 group
->is_fragment
= is_fragment
;
3499 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3500 if( !group
->assignment
|| lsmash_list_add_entry( pool
, group
) < 0 )
3502 lsmash_free( group
);
3503 return LSMASH_ERR_MEMORY_ALLOC
;
3505 if( is_recovery_start
)
3507 /* a member of non-roll or post-roll group */
3508 group
->first_sample
= sample_count
;
3509 group
->recovery_point
= prop
->post_roll
.complete
;
3513 group
->described
= ROLL_DISTANCE_DETERMINED
;
3514 if( valid_pre_roll
)
3516 /* a member of pre-roll group */
3517 group
->roll_distance
= -(signed)prop
->pre_roll
.distance
;
3518 int err
= isom_roll_grouping_established( group
);
3523 /* a member of non-roll group */
3524 group
->roll_distance
= 0;
3529 group
->prev_is_recovery_start
= is_recovery_start
;
3530 group
->assignment
->sample_count
+= 1;
3532 /* If encountered a RAP, all recovery is completed here. */
3533 if( prop
->ra_flags
& (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
3534 | ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
3535 | QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
) )
3536 return isom_all_recovery_described( sbgp
, pool
);
3537 /* Check whether this sample is a random access recovery point or not. */
3538 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3540 group
= (isom_roll_group_t
*)entry
->data
;
3542 return LSMASH_ERR_INVALID_DATA
;
3543 if( group
->described
== ROLL_DISTANCE_DETERMINED
)
3545 if( group
->described
== ROLL_DISTANCE_INITIALIZED
)
3547 /* Let's consider the following picture sequence.
3548 * coded order : P[0] P[1] P[2] P[3] P[4] P[5]
3551 * Here, P[0] conveys a recovery point SEI and P[3] is the recovery point.
3552 * Correctness of decoded pictures is specified by recovery point in output order for both AVC and HEVC.
3553 * Therefore, as follows,
3554 * output order : P[0] P[2] P[1] P[5]|P[3] P[4]
3555 * ---(incorrect?)--->|
3556 * there is no guarantee that P[5] is decoded and output correctly.
3557 * From this, it can be said that the roll_distance of this sequence is equal to 5. */
3558 isom_roll_entry_t
*post_roll
= isom_get_roll_description( group
);
3559 if( post_roll
&& post_roll
->roll_distance
> 0 )
3561 if( sample
->cts
!= LSMASH_TIMESTAMP_UNDEFINED
3562 && group
->rp_cts
!= LSMASH_TIMESTAMP_UNDEFINED
3563 && group
->rp_cts
> sample
->cts
)
3564 /* Updated roll_distance for composition reordering. */
3565 post_roll
->roll_distance
= sample_count
- group
->first_sample
;
3566 if( ++ group
->wait_and_see_count
>= MAX_ROLL_WAIT_AND_SEE_COUNT
)
3567 group
->described
= ROLL_DISTANCE_DETERMINED
;
3570 else if( prop
->post_roll
.identifier
== group
->recovery_point
)
3572 int16_t distance
= sample_count
- group
->first_sample
;
3573 group
->rp_cts
= sample
->cts
;
3574 group
->roll_distance
= distance
;
3575 /* Add a roll recovery entry only when roll_distance isn't zero since roll_distance = 0 must not be used. */
3578 /* Now, this group is a 'roll'.
3579 * The roll_distance may be updated later because of composition reordering. */
3580 group
->described
= ROLL_DISTANCE_INITIALIZED
;
3581 group
->wait_and_see_count
= 0;
3582 /* All groups with uninitialized roll_distance before the current group are described. */
3583 lsmash_entry_t
*current
= entry
;
3584 for( entry
= pool
->head
; entry
!= current
; entry
= entry
->next
)
3586 group
= (isom_roll_group_t
*)entry
->data
;
3587 if( !group
|| group
->described
!= ROLL_DISTANCE_INITIALIZED
)
3589 group
->described
= ROLL_DISTANCE_DETERMINED
;
3591 /* Cache the mark of the first recovery point in a subsegment. */
3593 && cache
->fragment
->subsegment
.first_rp_number
== 0 )
3594 cache
->fragment
->subsegment
.is_first_recovery_point
= 1;
3597 /* Random Accessible Point */
3598 return isom_all_recovery_described( sbgp
, pool
);
3601 return isom_flush_roll_pool( sbgp
, pool
);
3604 static int isom_update_chunk_tables
3607 lsmash_file_t
*media_file
,
3608 isom_chunk_t
*current
3611 isom_stsc_entry_t
*last_stsc_data
= stbl
->stsc
->list
->tail
? (isom_stsc_entry_t
*)stbl
->stsc
->list
->tail
->data
: NULL
;
3612 /* Create a new chunk sequence in this track if needed. */
3614 if( (!last_stsc_data
3615 || current
->pool
->sample_count
!= last_stsc_data
->samples_per_chunk
3616 || current
->sample_description_index
!= last_stsc_data
->sample_description_index
)
3617 && (err
= isom_add_stsc_entry( stbl
, current
->chunk_number
,
3618 current
->pool
->sample_count
,
3619 current
->sample_description_index
)) < 0 )
3621 /* Add a new chunk offset in this track. */
3622 uint64_t offset
= media_file
->size
;
3623 if( media_file
->fragment
)
3624 offset
+= ISOM_BASEBOX_COMMON_SIZE
+ media_file
->fragment
->pool_size
;
3625 return isom_add_stco_entry( stbl
, offset
);
3628 /* This function decides to put a give sample on the current chunk or the next new one.
3629 * Returns 1 if pooled samples must be flushed.
3630 * FIXME: I wonder if this function should have a extra argument which indicates force_to_flush_cached_chunk.
3631 * see lsmash_append_sample for detail. */
3632 static int isom_add_sample_to_chunk
3635 lsmash_sample_t
*sample
3638 if( LSMASH_IS_NON_EXISTING_BOX( trak
->file
)
3639 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
)
3640 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->dinf
->dref
)
3641 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->stbl
->stsd
)
3643 || trak
->mdia
->mdhd
->timescale
== 0
3644 || !trak
->mdia
->minf
->stbl
->stsc
->list
)
3645 return LSMASH_ERR_INVALID_DATA
;
3646 isom_chunk_t
*current
= &trak
->cache
->chunk
;
3647 if( !current
->pool
)
3649 /* Very initial settings, just once per track */
3650 current
->pool
= isom_create_sample_pool( 0 );
3651 if( !current
->pool
)
3652 return LSMASH_ERR_MEMORY_ALLOC
;
3654 if( current
->pool
->sample_count
== 0 )
3656 /* Cannot decide whether we should flush the current sample or not here yet. */
3657 current
->chunk_number
+= 1;
3658 current
->sample_description_index
= sample
->index
;
3659 current
->first_dts
= sample
->dts
;
3662 if( sample
->dts
< current
->first_dts
)
3663 return LSMASH_ERR_INVALID_DATA
; /* easy error check. */
3664 lsmash_file_t
*media_file
= isom_get_written_media_file( trak
, current
->sample_description_index
);
3665 if( (current
->sample_description_index
== sample
->index
)
3666 && (media_file
->max_chunk_duration
>= ((double)(sample
->dts
- current
->first_dts
) / trak
->mdia
->mdhd
->timescale
))
3667 && (media_file
->max_chunk_size
>= current
->pool
->size
+ sample
->length
) )
3668 return 0; /* No need to flush current cached chunk, the current sample must be put into that. */
3669 /* NOTE: chunk relative stuff must be pushed into file after a chunk is fully determined with its contents.
3670 * Now the current cached chunk is fixed, actually add the chunk relative properties to its file accordingly. */
3671 int err
= isom_update_chunk_tables( trak
->mdia
->minf
->stbl
, media_file
, current
);
3674 /* Update and re-initialize cache, using the current sample */
3675 current
->chunk_number
+= 1;
3676 current
->sample_description_index
= sample
->index
;
3677 current
->first_dts
= sample
->dts
;
3678 /* current->pool must be flushed in isom_append_sample_internal() */
3682 static int isom_write_pooled_samples( lsmash_file_t
*file
, isom_sample_pool_t
*pool
)
3684 if( LSMASH_IS_NON_EXISTING_BOX( file
)
3686 || !file
->bs
->stream
3687 || !(file
->flags
& LSMASH_FILE_MODE_WRITE
)
3688 || !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
3689 || ((file
->flags
& LSMASH_FILE_MODE_BOX
) && LSMASH_IS_NON_EXISTING_BOX( file
->mdat
)) )
3690 return LSMASH_ERR_INVALID_DATA
;
3691 lsmash_bs_put_bytes( file
->bs
, pool
->size
, pool
->data
);
3692 int err
= lsmash_bs_flush_buffer( file
->bs
);
3695 if( LSMASH_IS_EXISTING_BOX( file
->mdat
) )
3696 file
->mdat
->media_size
+= pool
->size
;
3697 file
->size
+= pool
->size
;
3698 pool
->sample_count
= 0;
3703 int isom_update_sample_tables
3706 lsmash_sample_t
*sample
,
3707 uint32_t *samples_per_packet
,
3708 isom_sample_entry_t
*sample_entry
3712 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)sample_entry
;
3713 if( (audio
->manager
& LSMASH_AUDIO_DESCRIPTION
)
3714 && (audio
->manager
& LSMASH_QTFF_BASE
)
3715 && (audio
->version
== 1)
3716 && (audio
->compression_ID
!= QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION
) )
3718 /* Add entries of the sample table for each uncompressed sample. */
3719 uint64_t sample_duration
= trak
->mdia
->mdhd
->timescale
/ (audio
->samplerate
>> 16);
3720 if( audio
->samplesPerPacket
== 0 || sample_duration
== 0 || sample
->cts
== LSMASH_TIMESTAMP_UNDEFINED
)
3721 return LSMASH_ERR_INVALID_DATA
;
3722 uint64_t sample_dts
= sample
->dts
;
3723 uint64_t sample_cts
= sample
->cts
;
3724 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3725 for( uint32_t i
= 0; i
< audio
->samplesPerPacket
; i
++ )
3727 /* Add a size of uncomressed audio and increment sample_count.
3728 * This points to individual uncompressed audio samples, each one byte in size, within the compressed frames. */
3729 uint32_t sample_count
= isom_add_size( stbl
, 1 );
3730 if( sample_count
== 0 )
3731 return LSMASH_ERR_NAMELESS
;
3732 /* Add a decoding timestamp and a composition timestamp. */
3733 if( (err
= isom_add_timestamp( stbl
, trak
->cache
, trak
->file
, sample_dts
, sample_cts
)) < 0 )
3735 sample_dts
+= sample_duration
;
3736 sample_cts
+= sample_duration
;
3738 *samples_per_packet
= audio
->samplesPerPacket
;
3742 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3743 /* Add a sample_size and increment sample_count. */
3744 uint32_t sample_count
= isom_add_size( stbl
, sample
->length
);
3745 if( sample_count
== 0 )
3746 return LSMASH_ERR_NAMELESS
;
3747 /* Add a decoding timestamp and a composition timestamp. */
3748 if( (err
= isom_add_timestamp( stbl
, trak
->cache
, trak
->file
, sample
->dts
, sample
->cts
)) < 0 )
3750 /* Add a sync point if needed. */
3751 if( (err
= isom_add_sync_point( stbl
, trak
->cache
, sample_count
, &sample
->prop
)) < 0 )
3753 /* Add a partial sync point if needed. */
3754 if( (err
= isom_add_partial_sync( stbl
, trak
->file
, sample_count
, &sample
->prop
)) < 0 )
3756 /* Add leading, independent, disposable and redundant information if needed. */
3757 if( stbl
->add_dependency_type
3758 && (err
= stbl
->add_dependency_type( stbl
, trak
->file
, &sample
->prop
)) < 0 )
3760 /* Group samples into random access point type if needed. */
3761 if( (err
= isom_group_random_access( (isom_box_t
*)stbl
, trak
->cache
, sample
)) < 0 )
3763 /* Group samples into random access recovery point type if needed. */
3764 if( (err
= isom_group_roll_recovery( (isom_box_t
*)stbl
, trak
->cache
, sample
)) < 0 )
3766 *samples_per_packet
= 1;
3768 /* Add a chunk if needed. */
3769 return isom_add_sample_to_chunk( trak
, sample
);
3772 static int isom_output_cached_chunk( isom_trak_t
*trak
)
3774 isom_chunk_t
*chunk
= &trak
->cache
->chunk
;
3775 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3776 isom_stsc_entry_t
*last_stsc_data
= stbl
->stsc
->list
->tail
? (isom_stsc_entry_t
*)stbl
->stsc
->list
->tail
->data
: NULL
;
3777 /* Create a new chunk sequence in this track if needed. */
3779 if( (!last_stsc_data
3780 || chunk
->pool
->sample_count
!= last_stsc_data
->samples_per_chunk
3781 || chunk
->sample_description_index
!= last_stsc_data
->sample_description_index
)
3782 && (err
= isom_add_stsc_entry( stbl
, chunk
->chunk_number
,
3783 chunk
->pool
->sample_count
,
3784 chunk
->sample_description_index
)) < 0 )
3786 lsmash_file_t
*file
= isom_get_written_media_file( trak
, chunk
->sample_description_index
);
3787 if( file
->fragment
)
3789 /* Add a new chunk offset in this track. */
3790 if( (err
= isom_add_stco_entry( stbl
, file
->size
+ ISOM_BASEBOX_COMMON_SIZE
+ file
->fragment
->pool_size
)) < 0 )
3792 return isom_append_fragment_track_run( file
, chunk
);
3794 /* Add a new chunk offset in this track. */
3795 if( (err
= isom_add_stco_entry( stbl
, file
->size
)) < 0 )
3797 /* Output pooled samples in this track. */
3798 return isom_write_pooled_samples( file
, chunk
->pool
);
3801 int isom_pool_sample( isom_sample_pool_t
*pool
, lsmash_sample_t
*sample
, uint32_t samples_per_packet
)
3803 uint64_t pool_size
= pool
->size
+ sample
->length
;
3804 if( pool
->alloc
< pool_size
)
3807 uint64_t alloc
= pool_size
+ (1<<16);
3809 data
= lsmash_malloc( alloc
);
3811 data
= lsmash_realloc( pool
->data
, alloc
);
3813 return LSMASH_ERR_MEMORY_ALLOC
;
3815 pool
->alloc
= alloc
;
3817 memcpy( pool
->data
+ pool
->size
, sample
->data
, sample
->length
);
3818 pool
->size
= pool_size
;
3819 pool
->sample_count
+= samples_per_packet
;
3820 lsmash_delete_sample( sample
);
3824 static int isom_append_sample_internal
3827 lsmash_sample_t
*sample
,
3828 isom_sample_entry_t
*sample_entry
3831 uint32_t samples_per_packet
;
3832 int ret
= isom_update_sample_tables( trak
, sample
, &samples_per_packet
, sample_entry
);
3835 /* ret == 1 means pooled samples must be flushed. */
3836 isom_sample_pool_t
*current_pool
= trak
->cache
->chunk
.pool
;
3839 /* The sample_description_index in the cache is one of the next written chunk.
3840 * Therefore, it cannot be referenced here. */
3841 lsmash_entry_list_t
*stsc_list
= trak
->mdia
->minf
->stbl
->stsc
->list
;
3842 isom_stsc_entry_t
*last_stsc_data
= (isom_stsc_entry_t
*)stsc_list
->tail
->data
;
3843 lsmash_file_t
*file
= isom_get_written_media_file( trak
, last_stsc_data
->sample_description_index
);
3844 if( (ret
= isom_write_pooled_samples( file
, current_pool
)) < 0 )
3847 /* Arbitration system between tracks with extremely scattering dts.
3848 * Here, we check whether asynchronization between the tracks exceeds the tolerance.
3849 * If a track has too old "first DTS" in its cached chunk than current sample's DTS, then its pooled samples must be flushed.
3850 * We don't consider presentation of media since any edit can pick an arbitrary portion of media in track.
3851 * Note: you needn't read this loop until you grasp the basic handling of chunks. */
3852 lsmash_file_t
*file
= trak
->file
;
3853 double tolerance
= file
->max_async_tolerance
;
3854 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
3856 isom_trak_t
*other
= (isom_trak_t
*)entry
->data
;
3859 if( LSMASH_IS_NON_EXISTING_BOX( other
)
3860 || LSMASH_IS_NON_EXISTING_BOX( other
->mdia
->mdhd
)
3862 || other
->mdia
->mdhd
->timescale
== 0
3863 || !other
->mdia
->minf
->stbl
->stsc
->list
)
3864 return LSMASH_ERR_INVALID_DATA
;
3865 isom_chunk_t
*chunk
= &other
->cache
->chunk
;
3866 if( !chunk
->pool
|| chunk
->pool
->sample_count
== 0 )
3868 double diff
= ((double)sample
->dts
/ trak
->mdia
->mdhd
->timescale
)
3869 - ((double)chunk
->first_dts
/ other
->mdia
->mdhd
->timescale
);
3870 if( diff
> tolerance
&& (ret
= isom_output_cached_chunk( other
)) < 0 )
3872 /* Note: we don't flush the cached chunk in the current track and the current sample here
3873 * even if the conditional expression of '-diff > tolerance' meets.
3874 * That's useless because appending a sample to another track would be a good equivalent.
3875 * It's even harmful because it causes excess chunk division by calling
3876 * isom_output_cached_chunk() which always generates a new chunk.
3877 * Anyway some excess chunk division will be there, but rather less without it.
3878 * To completely avoid this, we need to observe at least whether the current sample will be placed
3879 * right next to the previous chunk of the same track or not. */
3881 /* anyway the current sample must be pooled. */
3882 return isom_pool_sample( current_pool
, sample
, samples_per_packet
);
3885 int isom_append_sample_by_type
3888 lsmash_sample_t
*sample
,
3889 isom_sample_entry_t
*sample_entry
,
3890 int (*func_append_sample
)( void *, lsmash_sample_t
*, isom_sample_entry_t
* )
3893 if( isom_is_lpcm_audio( sample_entry
) )
3895 uint32_t frame_size
= ((isom_audio_entry_t
*)sample_entry
)->constBytesPerAudioPacket
;
3896 if( sample
->length
== frame_size
)
3897 return func_append_sample( track
, sample
, sample_entry
);
3898 else if( sample
->length
< frame_size
|| sample
->cts
== LSMASH_TIMESTAMP_UNDEFINED
)
3899 return LSMASH_ERR_INVALID_DATA
;
3900 /* Append samples splitted into each LPCMFrame. */
3901 uint64_t dts
= sample
->dts
;
3902 uint64_t cts
= sample
->cts
;
3903 for( uint32_t offset
= 0; offset
< sample
->length
; offset
+= frame_size
)
3905 lsmash_sample_t
*lpcm_sample
= lsmash_create_sample( frame_size
);
3907 return LSMASH_ERR_MEMORY_ALLOC
;
3908 memcpy( lpcm_sample
->data
, sample
->data
+ offset
, frame_size
);
3909 lpcm_sample
->dts
= dts
++;
3910 lpcm_sample
->cts
= cts
++;
3911 lpcm_sample
->prop
= sample
->prop
;
3912 lpcm_sample
->index
= sample
->index
;
3913 int err
= func_append_sample( track
, lpcm_sample
, sample_entry
);
3916 lsmash_delete_sample( lpcm_sample
);
3920 lsmash_delete_sample( sample
);
3923 return func_append_sample( track
, sample
, sample_entry
);
3926 /* This function is for non-fragmented movie. */
3927 static int isom_append_sample
3929 lsmash_file_t
*file
,
3931 lsmash_sample_t
*sample
,
3932 isom_sample_entry_t
*sample_entry
3935 /* If there is no available Media Data Box to write samples, add and write a new one before any chunk offset is decided. */
3937 int mdat_absent
= LSMASH_IS_NON_EXISTING_BOX( file
->mdat
);
3938 if( mdat_absent
|| !(file
->mdat
->manager
& LSMASH_INCOMPLETE_BOX
) )
3940 if( mdat_absent
&& LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_mdat( file
) ) )
3941 return LSMASH_ERR_NAMELESS
;
3942 file
->mdat
->manager
|= LSMASH_PLACEHOLDER
;
3943 if( (err
= isom_write_box( file
->bs
, (isom_box_t
*)file
->mdat
)) < 0 )
3945 file
->size
+= file
->mdat
->size
;
3947 return isom_append_sample_by_type( trak
, sample
, sample_entry
, (int (*)( void *, lsmash_sample_t
*, isom_sample_entry_t
* ))isom_append_sample_internal
);
3950 static int isom_output_cache( isom_trak_t
*trak
)
3953 isom_cache_t
*cache
= trak
->cache
;
3954 if( cache
->chunk
.pool
3955 && cache
->chunk
.pool
->sample_count
3956 && (err
= isom_output_cached_chunk( trak
)) < 0 )
3958 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3959 for( lsmash_entry_t
*entry
= stbl
->sgpd_list
.head
; entry
; entry
= entry
->next
)
3961 isom_sgpd_t
*sgpd
= (isom_sgpd_t
*)entry
->data
;
3962 if( LSMASH_IS_NON_EXISTING_BOX( sgpd
) )
3963 return LSMASH_ERR_INVALID_DATA
;
3964 switch( sgpd
->grouping_type
)
3966 case ISOM_GROUP_TYPE_RAP
:
3968 isom_rap_group_t
*group
= cache
->rap
;
3971 if( stbl
->file
->fragment
)
3974 return LSMASH_ERR_NAMELESS
;
3976 if( !group
->random_access
)
3978 group
->random_access
->num_leading_samples_known
= 1;
3981 case ISOM_GROUP_TYPE_ROLL
:
3982 case ISOM_GROUP_TYPE_PROL
:
3983 if( !cache
->roll
.pool
)
3985 if( stbl
->file
->fragment
)
3988 return LSMASH_ERR_NAMELESS
;
3990 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group( &stbl
->sbgp_list
);
3991 if( LSMASH_IS_NON_EXISTING_BOX( sbgp
) )
3992 return LSMASH_ERR_NAMELESS
;
3993 if( (err
= isom_all_recovery_completed( sbgp
, cache
->roll
.pool
)) < 0 )
4003 int lsmash_flush_pooled_samples( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t last_sample_delta
)
4005 if( isom_check_initializer_present( root
) < 0 )
4006 return LSMASH_ERR_FUNCTION_PARAM
;
4007 lsmash_file_t
*file
= root
->file
;
4009 && file
->fragment
->movie
)
4010 return isom_flush_fragment_pooled_samples( file
, track_ID
, last_sample_delta
);
4011 if( file
!= file
->initializer
)
4012 return LSMASH_ERR_INVALID_DATA
;
4013 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
4014 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
4016 || !trak
->mdia
->minf
->stbl
->stsc
->list
)
4017 return LSMASH_ERR_NAMELESS
;
4018 int err
= isom_output_cache( trak
);
4021 return lsmash_set_last_sample_delta( root
, track_ID
, last_sample_delta
);
4024 int lsmash_append_sample( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_sample_t
*sample
)
4026 if( isom_check_initializer_present( root
) < 0
4029 || sample
->data
== NULL
4030 || sample
->dts
== LSMASH_TIMESTAMP_UNDEFINED
)
4031 return LSMASH_ERR_FUNCTION_PARAM
;
4032 lsmash_file_t
*file
= root
->file
;
4033 /* We think max_chunk_duration == 0, which means all samples will be cached on memory, should be prevented.
4034 * This means removal of a feature that we used to have, but anyway very alone chunk does not make sense. */
4036 || !(file
->flags
& LSMASH_FILE_MODE_BOX
)
4037 || file
->max_chunk_duration
== 0
4038 || file
->max_async_tolerance
== 0 )
4039 return LSMASH_ERR_NAMELESS
;
4040 /* Write File Type Box here if it was not written yet. */
4041 if( file
->flags
& LSMASH_FILE_MODE_INITIALIZATION
)
4043 if( LSMASH_IS_EXISTING_BOX( file
->ftyp
) && !(file
->ftyp
->manager
& LSMASH_WRITTEN_BOX
) )
4045 int err
= isom_write_box( file
->bs
, (isom_box_t
*)file
->ftyp
);
4048 file
->size
+= file
->ftyp
->size
;
4051 /* Get a sample initializer. */
4052 isom_trak_t
*trak
= isom_get_trak( file
->initializer
, track_ID
);
4053 if( LSMASH_IS_NON_EXISTING_BOX( trak
->file
)
4054 || LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
)
4055 || trak
->mdia
->mdhd
->timescale
== 0
4057 || !trak
->mdia
->minf
->stbl
->stsc
->list
)
4058 return LSMASH_ERR_NAMELESS
;
4059 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)lsmash_list_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
, sample
->index
);
4060 if( LSMASH_IS_NON_EXISTING_BOX( sample_entry
) )
4061 return LSMASH_ERR_NAMELESS
;
4062 /* Append a sample. */
4063 if( (file
->flags
& LSMASH_FILE_MODE_FRAGMENTED
)
4065 && file
->fragment
->pool
)
4066 return isom_append_fragment_sample( file
, trak
, sample
, sample_entry
);
4067 if( file
!= file
->initializer
)
4068 return LSMASH_ERR_INVALID_DATA
;
4069 return isom_append_sample( file
, trak
, sample
, sample_entry
);
4072 /*---- misc functions ----*/
4074 int lsmash_delete_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
)
4076 if( isom_check_initializer_present( root
) < 0 )
4077 return LSMASH_ERR_FUNCTION_PARAM
;
4078 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
4079 if( LSMASH_IS_NON_EXISTING_BOX( trak
) )
4080 return LSMASH_ERR_NAMELESS
;
4081 isom_remove_box_by_itself( trak
->edts
);
4082 return isom_update_tkhd_duration( trak
);
4085 void lsmash_delete_tyrant_chapter( lsmash_root_t
*root
)
4087 if( isom_check_initializer_present( root
) < 0
4088 || LSMASH_IS_NON_EXISTING_BOX( root
->file
->initializer
->moov
->udta
) )
4090 isom_remove_box_by_itself( root
->file
->moov
->udta
->chpl
);
4093 int lsmash_set_copyright( lsmash_root_t
*root
, uint32_t track_ID
, uint16_t ISO_language
, char *notice
)
4095 if( isom_check_initializer_present( root
) < 0
4096 || (ISO_language
&& ISO_language
< 0x800)
4098 return LSMASH_ERR_FUNCTION_PARAM
;
4099 lsmash_file_t
*file
= root
->file
;
4100 if( !file
->isom_compatible
)
4101 return LSMASH_ERR_NAMELESS
;
4105 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
4106 if( LSMASH_IS_NON_EXISTING_BOX( trak
->udta
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_udta( trak
) ) )
4107 return LSMASH_ERR_NAMELESS
;
4112 if( LSMASH_IS_NON_EXISTING_BOX( file
->moov
->udta
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_udta( file
->moov
) ) )
4113 return LSMASH_ERR_NAMELESS
;
4114 udta
= file
->moov
->udta
;
4116 assert( LSMASH_IS_EXISTING_BOX( udta
) );
4117 for( lsmash_entry_t
*entry
= udta
->cprt_list
.head
; entry
; entry
= entry
->next
)
4119 isom_cprt_t
*cprt
= (isom_cprt_t
*)entry
->data
;
4120 if( LSMASH_IS_NON_EXISTING_BOX( cprt
) || cprt
->language
== ISO_language
)
4121 return LSMASH_ERR_NAMELESS
;
4123 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_cprt( udta
) ) )
4124 return LSMASH_ERR_NAMELESS
;
4125 isom_cprt_t
*cprt
= (isom_cprt_t
*)udta
->cprt_list
.tail
->data
;
4126 cprt
->language
= ISO_language
;
4127 cprt
->notice_length
= strlen( notice
) + 1;
4128 cprt
->notice
= lsmash_memdup( notice
, cprt
->notice_length
);