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"
44 #define RTP_SAMPLE_HEADER_SIZE 4
45 #define RTP_PACKET_SIZE 12 /* a structure in Hint track sample */
46 #define RTP_HEADER_SIZE 12
47 #define RTP_CONSTRUCTOR_SIZE 16
49 int isom_check_initializer_present( lsmash_root_t
*root
)
51 if( LSMASH_IS_NON_EXISTING_BOX( root
)
52 || LSMASH_IS_NON_EXISTING_BOX( root
->file
)
53 || LSMASH_IS_NON_EXISTING_BOX( root
->file
->initializer
) )
54 return LSMASH_ERR_NAMELESS
;
58 isom_trak_t
*isom_get_trak( lsmash_file_t
*file
, uint32_t track_ID
)
61 || LSMASH_IS_NON_EXISTING_BOX( file
->moov
)
62 || file
!= file
->initializer
)
63 return isom_non_existing_trak();
64 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
66 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
67 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
68 || LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
69 return isom_non_existing_trak();
70 if( trak
->tkhd
->track_ID
== track_ID
)
73 return isom_non_existing_trak();
76 isom_trex_t
*isom_get_trex( isom_mvex_t
*mvex
, uint32_t track_ID
)
78 if( track_ID
== 0 || LSMASH_IS_NON_EXISTING_BOX( mvex
) )
79 return isom_non_existing_trex();
80 for( lsmash_entry_t
*entry
= mvex
->trex_list
.head
; entry
; entry
= entry
->next
)
82 isom_trex_t
*trex
= (isom_trex_t
*)entry
->data
;
83 if( LSMASH_IS_NON_EXISTING_BOX( trex
) )
84 return isom_non_existing_trex();
85 if( trex
->track_ID
== track_ID
)
88 return isom_non_existing_trex();
91 isom_traf_t
*isom_get_traf( isom_moof_t
*moof
, uint32_t track_ID
)
93 if( track_ID
== 0 || LSMASH_IS_NON_EXISTING_BOX( moof
) )
94 return isom_non_existing_traf();
95 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
97 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
98 if( LSMASH_IS_NON_EXISTING_BOX( traf
)
99 || LSMASH_IS_NON_EXISTING_BOX( traf
->tfhd
) )
100 return isom_non_existing_traf();
101 if( traf
->tfhd
->track_ID
== track_ID
)
104 return isom_non_existing_traf();
107 isom_tfra_t
*isom_get_tfra( isom_mfra_t
*mfra
, uint32_t track_ID
)
109 if( track_ID
== 0 || LSMASH_IS_NON_EXISTING_BOX( mfra
) )
110 return isom_non_existing_tfra();
111 for( lsmash_entry_t
*entry
= mfra
->tfra_list
.head
; entry
; entry
= entry
->next
)
113 isom_tfra_t
*tfra
= (isom_tfra_t
*)entry
->data
;
114 if( LSMASH_IS_NON_EXISTING_BOX( tfra
) )
115 return isom_non_existing_tfra();
116 if( tfra
->track_ID
== track_ID
)
119 return isom_non_existing_tfra();
122 static int isom_add_elst_entry( isom_elst_t
*elst
, uint64_t segment_duration
, int64_t media_time
, int32_t media_rate
)
124 assert( LSMASH_IS_EXISTING_BOX( elst
->file
) );
125 isom_elst_entry_t
*data
= lsmash_malloc( sizeof(isom_elst_entry_t
) );
127 return LSMASH_ERR_MEMORY_ALLOC
;
128 data
->segment_duration
= segment_duration
;
129 data
->media_time
= media_time
;
130 data
->media_rate
= media_rate
;
131 if( lsmash_list_add_entry( elst
->list
, data
) < 0 )
134 return LSMASH_ERR_MEMORY_ALLOC
;
136 if( !elst
->file
->undefined_64_ver
137 && (data
->segment_duration
> UINT32_MAX
138 || data
->media_time
> INT32_MAX
139 || data
->media_time
< INT32_MIN
) )
144 /* This function returns 0 if failed, sample_entry_number if succeeded. */
145 int lsmash_add_sample_entry( lsmash_root_t
*root
, uint32_t track_ID
, void *summary
)
147 if( LSMASH_IS_NON_EXISTING_BOX( root
) || !summary
148 || ((lsmash_summary_t
*)summary
)->data_ref_index
== 0
149 || ((lsmash_summary_t
*)summary
)->data_ref_index
> UINT16_MAX
)
151 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
152 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
153 || LSMASH_IS_NON_EXISTING_BOX( trak
->file
)
154 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->hdlr
)
155 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->stbl
->stsd
) )
157 isom_stsd_t
*stsd
= trak
->mdia
->minf
->stbl
->stsd
;
158 lsmash_media_type media_type
= trak
->mdia
->hdlr
->componentSubtype
;
159 if( isom_setup_sample_description( stsd
, media_type
, (lsmash_summary_t
*)summary
) < 0 )
162 return stsd
->list
.entry_count
;
165 static int isom_add_stts_entry( isom_stbl_t
*stbl
, uint32_t sample_delta
)
167 assert( LSMASH_IS_EXISTING_BOX( stbl
->stts
) );
168 if( !stbl
->stts
->list
)
169 return LSMASH_ERR_NAMELESS
;
170 isom_stts_entry_t
*data
= lsmash_malloc( sizeof(isom_stts_entry_t
) );
172 return LSMASH_ERR_MEMORY_ALLOC
;
173 data
->sample_count
= 1;
174 data
->sample_delta
= sample_delta
;
175 if( lsmash_list_add_entry( stbl
->stts
->list
, data
) < 0 )
178 return LSMASH_ERR_MEMORY_ALLOC
;
183 static int isom_add_ctts_entry( isom_stbl_t
*stbl
, uint32_t sample_count
, uint32_t sample_offset
)
185 assert( LSMASH_IS_EXISTING_BOX( stbl
->ctts
) );
186 if( !stbl
->ctts
->list
)
187 return LSMASH_ERR_NAMELESS
;
188 isom_ctts_entry_t
*data
= lsmash_malloc( sizeof(isom_ctts_entry_t
) );
190 return LSMASH_ERR_MEMORY_ALLOC
;
191 data
->sample_count
= sample_count
;
192 data
->sample_offset
= sample_offset
;
193 if( lsmash_list_add_entry( stbl
->ctts
->list
, data
) < 0 )
196 return LSMASH_ERR_MEMORY_ALLOC
;
201 static int isom_add_stsc_entry( isom_stbl_t
*stbl
, uint32_t first_chunk
, uint32_t samples_per_chunk
, uint32_t sample_description_index
)
203 assert( LSMASH_IS_EXISTING_BOX( stbl
->stsc
) );
204 if( !stbl
->stsc
->list
)
205 return LSMASH_ERR_NAMELESS
;
206 isom_stsc_entry_t
*data
= lsmash_malloc( sizeof(isom_stsc_entry_t
) );
208 return LSMASH_ERR_MEMORY_ALLOC
;
209 data
->first_chunk
= first_chunk
;
210 data
->samples_per_chunk
= samples_per_chunk
;
211 data
->sample_description_index
= sample_description_index
;
212 if( lsmash_list_add_entry( stbl
->stsc
->list
, data
) < 0 )
215 return LSMASH_ERR_MEMORY_ALLOC
;
220 static int isom_add_stsz_entry( isom_stbl_t
*stbl
, uint32_t entry_size
)
222 assert( LSMASH_IS_EXISTING_BOX( stbl
) );
223 if( LSMASH_IS_NON_EXISTING_BOX( stbl
->stsz
) )
224 return LSMASH_ERR_NAMELESS
;
225 isom_stsz_t
*stsz
= stbl
->stsz
;
226 /* retrieve initial sample_size */
227 if( stsz
->sample_count
== 0 )
228 stsz
->sample_size
= entry_size
;
229 /* if it seems constant sample size at present, update sample_count only */
230 if( !stsz
->list
&& stsz
->sample_size
== entry_size
)
232 ++ stsz
->sample_count
;
235 /* found sample_size varies, create sample_size list */
238 stsz
->list
= lsmash_list_create_simple();
240 return LSMASH_ERR_MEMORY_ALLOC
;
241 for( uint32_t i
= 0; i
< stsz
->sample_count
; i
++ )
243 isom_stsz_entry_t
*data
= lsmash_malloc( sizeof(isom_stsz_entry_t
) );
245 return LSMASH_ERR_MEMORY_ALLOC
;
246 data
->entry_size
= stsz
->sample_size
;
247 if( lsmash_list_add_entry( stsz
->list
, data
) < 0 )
250 return LSMASH_ERR_MEMORY_ALLOC
;
253 stsz
->sample_size
= 0;
255 isom_stsz_entry_t
*data
= lsmash_malloc( sizeof(isom_stsz_entry_t
) );
257 return LSMASH_ERR_MEMORY_ALLOC
;
258 data
->entry_size
= entry_size
;
259 if( lsmash_list_add_entry( stsz
->list
, data
) < 0 )
262 return LSMASH_ERR_MEMORY_ALLOC
;
264 ++ stsz
->sample_count
;
268 static int isom_add_stss_entry( isom_stbl_t
*stbl
, uint32_t sample_number
)
270 assert( LSMASH_IS_EXISTING_BOX( stbl
->stss
) );
271 if( !stbl
->stss
->list
)
272 return LSMASH_ERR_NAMELESS
;
273 isom_stss_entry_t
*data
= lsmash_malloc( sizeof(isom_stss_entry_t
) );
275 return LSMASH_ERR_MEMORY_ALLOC
;
276 data
->sample_number
= sample_number
;
277 if( lsmash_list_add_entry( stbl
->stss
->list
, data
) < 0 )
280 return LSMASH_ERR_MEMORY_ALLOC
;
285 static int isom_add_stps_entry( isom_stbl_t
*stbl
, uint32_t sample_number
)
287 assert( LSMASH_IS_EXISTING_BOX( stbl
->stps
) );
288 if( !stbl
->stps
->list
)
289 return LSMASH_ERR_NAMELESS
;
290 isom_stps_entry_t
*data
= lsmash_malloc( sizeof(isom_stps_entry_t
) );
292 return LSMASH_ERR_MEMORY_ALLOC
;
293 data
->sample_number
= sample_number
;
294 if( lsmash_list_add_entry( stbl
->stps
->list
, data
) < 0 )
297 return LSMASH_ERR_MEMORY_ALLOC
;
302 /* Between ISOBMFF and QTFF, the most significant 2-bit has different meaning.
303 * For the maximum compatibility, we set 0 to the most significant 2-bit when compatible with
304 * both ISOBMFF with AVCFF extensions and QTFF.
305 * compatibility == 0 -> neither AVCFF extensions nor QTFF compatible
306 * compatibility == 1 -> AVCFF extensions compatible
307 * compatibility == 2 -> QTFF compatible
308 * compatibility == 3 -> both AVCFF extensions and QTFF compatible */
309 static int isom_add_sdtp_entry( isom_box_t
*parent
, lsmash_sample_property_t
*prop
, int compatibility
)
311 if( !prop
|| LSMASH_IS_NON_EXISTING_BOX( parent
) )
312 return LSMASH_ERR_NAMELESS
;
313 isom_sdtp_t
*sdtp
= isom_non_existing_sdtp();
314 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_STBL
) )
315 sdtp
= ((isom_stbl_t
*)parent
)->sdtp
;
316 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
317 sdtp
= ((isom_traf_t
*)parent
)->sdtp
;
320 if( LSMASH_IS_NON_EXISTING_BOX( sdtp
)
322 return LSMASH_ERR_NAMELESS
;
323 isom_sdtp_entry_t
*data
= lsmash_malloc( sizeof(isom_sdtp_entry_t
) );
325 return LSMASH_ERR_MEMORY_ALLOC
;
326 if( compatibility
== 1 )
327 data
->is_leading
= prop
->leading
& 0x03;
328 else if( compatibility
== 2 )
329 data
->is_leading
= prop
->allow_earlier
& 0x03;
332 data
->is_leading
= 0;
333 assert( compatibility
== 3 );
335 data
->sample_depends_on
= prop
->independent
& 0x03;
336 data
->sample_is_depended_on
= prop
->disposable
& 0x03;
337 data
->sample_has_redundancy
= prop
->redundant
& 0x03;
338 if( lsmash_list_add_entry( sdtp
->list
, data
) < 0 )
341 return LSMASH_ERR_MEMORY_ALLOC
;
346 static int isom_add_co64_entry( isom_stbl_t
*stbl
, uint64_t chunk_offset
)
348 assert( LSMASH_IS_EXISTING_BOX( stbl
->stco
) );
349 if( !stbl
->stco
->list
)
350 return LSMASH_ERR_NAMELESS
;
351 isom_co64_entry_t
*data
= lsmash_malloc( sizeof(isom_co64_entry_t
) );
353 return LSMASH_ERR_MEMORY_ALLOC
;
354 data
->chunk_offset
= chunk_offset
;
355 if( lsmash_list_add_entry( stbl
->stco
->list
, data
) < 0 )
358 return LSMASH_ERR_MEMORY_ALLOC
;
363 static int isom_convert_stco_to_co64( isom_stbl_t
*stbl
)
365 assert( LSMASH_IS_EXISTING_BOX( stbl
->stco
) );
368 isom_stco_t
*stco
= stbl
->stco
;
369 LSMASH_MAKE_BOX_NON_EXISTING( stbl
->stco
);
370 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_co64( stbl
) ) )
372 err
= LSMASH_ERR_NAMELESS
;
375 /* move chunk_offset to co64 from stco */
376 for( lsmash_entry_t
*entry
= stco
->list
->head
; entry
; entry
= entry
->next
)
378 isom_stco_entry_t
*data
= (isom_stco_entry_t
*)entry
->data
;
379 if( (err
= isom_add_co64_entry( stbl
, data
->chunk_offset
)) < 0 )
383 isom_remove_box_by_itself( stco
);
387 static int isom_add_stco_entry( isom_stbl_t
*stbl
, uint64_t chunk_offset
)
389 if( !stbl
->stco
->list
)
390 return LSMASH_ERR_NAMELESS
;
391 if( stbl
->stco
->large_presentation
)
392 return isom_add_co64_entry( stbl
, chunk_offset
);
393 if( chunk_offset
> UINT32_MAX
)
395 int err
= isom_convert_stco_to_co64( stbl
);
398 return isom_add_co64_entry( stbl
, chunk_offset
);
400 isom_stco_entry_t
*data
= lsmash_malloc( sizeof(isom_stco_entry_t
) );
402 return LSMASH_ERR_MEMORY_ALLOC
;
403 data
->chunk_offset
= (uint32_t)chunk_offset
;
404 if( lsmash_list_add_entry( stbl
->stco
->list
, data
) < 0 )
407 return LSMASH_ERR_MEMORY_ALLOC
;
412 static isom_sgpd_t
*isom_get_sample_group_description_common( lsmash_entry_list_t
*list
, uint32_t grouping_type
)
414 for( lsmash_entry_t
*entry
= list
->head
; entry
; entry
= entry
->next
)
416 isom_sgpd_t
*sgpd
= (isom_sgpd_t
*)entry
->data
;
417 if( LSMASH_IS_NON_EXISTING_BOX( sgpd
)
419 return isom_non_existing_sgpd();
420 if( sgpd
->grouping_type
== grouping_type
)
423 return isom_non_existing_sgpd();
426 static isom_sbgp_t
*isom_get_sample_to_group_common( lsmash_entry_list_t
*list
, uint32_t grouping_type
)
428 for( lsmash_entry_t
*entry
= list
->head
; entry
; entry
= entry
->next
)
430 isom_sbgp_t
*sbgp
= (isom_sbgp_t
*)entry
->data
;
431 if( LSMASH_IS_NON_EXISTING_BOX( sbgp
)
433 return isom_non_existing_sbgp();
434 if( sbgp
->grouping_type
== grouping_type
)
437 return isom_non_existing_sbgp();
440 isom_sgpd_t
*isom_get_sample_group_description( isom_stbl_t
*stbl
, uint32_t grouping_type
)
442 return isom_get_sample_group_description_common( &stbl
->sgpd_list
, grouping_type
);
445 isom_sbgp_t
*isom_get_sample_to_group( isom_stbl_t
*stbl
, uint32_t grouping_type
)
447 return isom_get_sample_to_group_common( &stbl
->sbgp_list
, grouping_type
);
450 isom_sgpd_t
*isom_get_roll_recovery_sample_group_description( lsmash_entry_list_t
*list
)
453 if( ((sgpd
= isom_get_sample_group_description_common( list
, ISOM_GROUP_TYPE_ROLL
)), LSMASH_IS_EXISTING_BOX( sgpd
))
454 || ((sgpd
= isom_get_sample_group_description_common( list
, ISOM_GROUP_TYPE_PROL
)), LSMASH_IS_EXISTING_BOX( sgpd
)) )
456 return isom_non_existing_sgpd();
459 isom_sbgp_t
*isom_get_roll_recovery_sample_to_group( lsmash_entry_list_t
*list
)
462 if( ((sbgp
= isom_get_sample_to_group_common( list
, ISOM_GROUP_TYPE_ROLL
)), LSMASH_IS_EXISTING_BOX( sbgp
))
463 || ((sbgp
= isom_get_sample_to_group_common( list
, ISOM_GROUP_TYPE_PROL
)), LSMASH_IS_EXISTING_BOX( sbgp
)) )
465 return isom_non_existing_sbgp();
468 isom_sgpd_t
*isom_get_fragment_sample_group_description( isom_traf_t
*traf
, uint32_t grouping_type
)
470 return isom_get_sample_group_description_common( &traf
->sgpd_list
, grouping_type
);
473 isom_sbgp_t
*isom_get_fragment_sample_to_group( isom_traf_t
*traf
, uint32_t grouping_type
)
475 return isom_get_sample_to_group_common( &traf
->sbgp_list
, grouping_type
);
478 static isom_rap_entry_t
*isom_add_rap_group_entry( isom_sgpd_t
*sgpd
)
480 if( LSMASH_IS_NON_EXISTING_BOX( sgpd
) )
482 isom_rap_entry_t
*data
= lsmash_malloc( sizeof(isom_rap_entry_t
) );
485 data
->description_length
= 0;
486 data
->num_leading_samples_known
= 0;
487 data
->num_leading_samples
= 0;
488 if( lsmash_list_add_entry( sgpd
->list
, data
) < 0 )
496 static isom_roll_entry_t
*isom_add_roll_group_entry( isom_sgpd_t
*sgpd
, int16_t roll_distance
)
498 if( LSMASH_IS_NON_EXISTING_BOX( sgpd
) )
500 isom_roll_entry_t
*data
= lsmash_malloc( sizeof(isom_roll_entry_t
) );
503 data
->description_length
= 0;
504 data
->roll_distance
= roll_distance
;
505 if( lsmash_list_add_entry( sgpd
->list
, data
) < 0 )
513 static isom_group_assignment_entry_t
*isom_add_group_assignment_entry( isom_sbgp_t
*sbgp
, uint32_t sample_count
, uint32_t group_description_index
)
515 if( LSMASH_IS_NON_EXISTING_BOX( sbgp
) )
517 isom_group_assignment_entry_t
*data
= lsmash_malloc( sizeof(isom_group_assignment_entry_t
) );
520 data
->sample_count
= sample_count
;
521 data
->group_description_index
= group_description_index
;
522 if( lsmash_list_add_entry( sbgp
->list
, data
) < 0 )
530 static uint32_t isom_get_sample_count_from_sample_table( isom_stbl_t
*stbl
)
532 if( LSMASH_IS_EXISTING_BOX( stbl
->stsz
) )
533 return stbl
->stsz
->sample_count
;
534 else if( LSMASH_IS_EXISTING_BOX( stbl
->stz2
) )
535 return stbl
->stz2
->sample_count
;
540 uint32_t isom_get_sample_count( isom_trak_t
*trak
)
542 return isom_get_sample_count_from_sample_table( trak
->mdia
->minf
->stbl
);
545 static uint64_t isom_get_dts( isom_stts_t
*stts
, uint32_t sample_number
)
551 lsmash_entry_t
*entry
;
552 isom_stts_entry_t
*data
= NULL
;
553 for( entry
= stts
->list
->head
; entry
; entry
= entry
->next
)
555 data
= (isom_stts_entry_t
*)entry
->data
;
558 if( i
+ data
->sample_count
> sample_number
)
560 dts
+= (uint64_t)data
->sample_delta
* data
->sample_count
;
561 i
+= data
->sample_count
;
565 dts
+= (uint64_t)data
->sample_delta
* (sample_number
- i
);
570 static uint64_t isom_get_cts( isom_stts_t
*stts
, isom_ctts_t
*ctts
, uint32_t sample_number
)
574 if( LSMASH_IS_NON_EXISTING_BOX( ctts
) )
575 return isom_get_dts( stts
, sample_number
);
576 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. */
577 lsmash_entry_t
*entry
;
578 isom_ctts_entry_t
*data
= NULL
;
579 if( sample_number
== 0 )
581 for( entry
= ctts
->list
->head
; entry
; entry
= entry
->next
)
583 data
= (isom_ctts_entry_t
*)entry
->data
;
586 if( i
+ data
->sample_count
> sample_number
)
588 i
+= data
->sample_count
;
592 return isom_get_dts( stts
, sample_number
) + data
->sample_offset
;
596 static int isom_replace_last_sample_delta( isom_stbl_t
*stbl
, uint32_t sample_delta
)
598 assert( LSMASH_IS_EXISTING_BOX( stbl
->stts
) );
599 if( !stbl
->stts
->list
600 || !stbl
->stts
->list
->tail
601 || !stbl
->stts
->list
->tail
->data
)
602 return LSMASH_ERR_NAMELESS
;
603 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)stbl
->stts
->list
->tail
->data
;
604 if( sample_delta
!= last_stts_data
->sample_delta
)
606 if( last_stts_data
->sample_count
> 1 )
608 last_stts_data
->sample_count
-= 1;
609 int err
= isom_add_stts_entry( stbl
, sample_delta
);
614 last_stts_data
->sample_delta
= sample_delta
;
619 static int isom_update_mdhd_duration( isom_trak_t
*trak
, uint32_t last_sample_delta
)
621 assert( LSMASH_IS_EXISTING_BOX( trak
) );
622 if( LSMASH_IS_NON_EXISTING_BOX( trak
->file
)
623 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
)
625 || !trak
->mdia
->minf
->stbl
->stts
->list
)
626 return LSMASH_ERR_INVALID_DATA
;
627 lsmash_file_t
*file
= trak
->file
;
628 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
629 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
630 isom_stts_t
*stts
= stbl
->stts
;
631 isom_ctts_t
*ctts
= stbl
->ctts
;
632 isom_cslg_t
*cslg
= stbl
->cslg
;
634 uint32_t sample_count
= isom_get_sample_count( trak
);
635 if( sample_count
== 0 )
637 /* Return error if non-fragmented movie has no samples. */
638 if( !file
->fragment
&& !stts
->list
->entry_count
)
639 return LSMASH_ERR_INVALID_DATA
;
642 /* Now we have at least 1 sample, so do stts_entry. */
643 lsmash_entry_t
*last_stts
= stts
->list
->tail
;
644 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)last_stts
->data
;
645 if( sample_count
== 1 )
646 mdhd
->duration
= last_stts_data
->sample_delta
;
647 /* Now we have at least 2 samples,
648 * but dunno whether 1 stts_entry which has 2 samples or 2 stts_entry which has 1 samle each. */
649 else if( LSMASH_IS_NON_EXISTING_BOX( ctts
) )
651 /* use dts instead of cts */
652 mdhd
->duration
= isom_get_dts( stts
, sample_count
);
654 if( last_sample_delta
)
656 mdhd
->duration
+= last_sample_delta
;
657 if( (err
= isom_replace_last_sample_delta( stbl
, last_sample_delta
)) < 0 )
660 else if( last_stts_data
->sample_count
> 1 )
661 mdhd
->duration
+= last_stts_data
->sample_delta
; /* no need to update last_stts_data->sample_delta */
664 /* Remove the last entry. */
665 if( (err
= lsmash_list_remove_entry_tail( stts
->list
)) < 0 )
667 /* copy the previous sample_delta. */
668 ++ ((isom_stts_entry_t
*)stts
->list
->tail
->data
)->sample_count
;
669 mdhd
->duration
+= ((isom_stts_entry_t
*)stts
->list
->tail
->data
)->sample_delta
;
675 || ctts
->list
->entry_count
== 0 )
676 return LSMASH_ERR_INVALID_DATA
;
678 uint64_t max_cts
= 0;
679 uint64_t max2_cts
= 0;
680 uint64_t min_cts
= LSMASH_TIMESTAMP_UNDEFINED
;
681 int64_t max_offset
= 0;
682 int64_t min_offset
= UINT32_MAX
;
683 int32_t ctd_shift
= trak
->cache
->timestamp
.ctd_shift
;
686 lsmash_entry_t
*stts_entry
= stts
->list
->head
;
687 lsmash_entry_t
*ctts_entry
= ctts
->list
->head
;
688 for( uint32_t i
= 0; i
< sample_count
; i
++ )
690 if( !ctts_entry
|| !stts_entry
)
691 return LSMASH_ERR_INVALID_DATA
;
692 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
693 isom_ctts_entry_t
*ctts_data
= (isom_ctts_entry_t
*)ctts_entry
->data
;
694 if( !stts_data
|| !ctts_data
)
695 return LSMASH_ERR_INVALID_DATA
;
696 if( ctts_data
->sample_offset
!= ISOM_NON_OUTPUT_SAMPLE_OFFSET
)
701 /* Anyway, add composition to decode timeline shift for calculating maximum and minimum CTS correctly. */
702 int64_t sample_offset
= (int32_t)ctts_data
->sample_offset
;
703 cts
= dts
+ sample_offset
+ ctd_shift
;
704 max_offset
= LSMASH_MAX( max_offset
, sample_offset
);
705 min_offset
= LSMASH_MIN( min_offset
, sample_offset
);
709 cts
= dts
+ ctts_data
->sample_offset
;
710 max_offset
= LSMASH_MAX( max_offset
, ctts_data
->sample_offset
);
711 min_offset
= LSMASH_MIN( min_offset
, ctts_data
->sample_offset
);
713 min_cts
= LSMASH_MIN( min_cts
, cts
);
719 else if( max2_cts
< cts
)
722 dts
+= stts_data
->sample_delta
;
723 /* If finished sample_count of current entry, move to next. */
724 if( ++j
== ctts_data
->sample_count
)
726 ctts_entry
= ctts_entry
->next
;
729 if( ++k
== stts_data
->sample_count
)
731 stts_entry
= stts_entry
->next
;
735 dts
-= last_stts_data
->sample_delta
;
737 /* Overall presentation is extended exceeding this initial movie.
738 * So, any players shall display the movie exceeding the durations
739 * indicated in Movie Header Box, Track Header Boxes and Media Header Boxes.
740 * Samples up to the duration indicated in Movie Extends Header Box shall be displayed.
741 * In the absence of Movie Extends Header Box, all samples shall be displayed. */
742 mdhd
->duration
+= dts
+ last_sample_delta
;
745 if( !last_sample_delta
)
747 /* The spec allows an arbitrary value for the duration of the last sample. So, we pick last-1 sample's. */
748 last_sample_delta
= max_cts
- max2_cts
;
750 if( min_cts
!= LSMASH_TIMESTAMP_UNDEFINED
)
751 mdhd
->duration
= max_cts
- min_cts
+ last_sample_delta
;
752 /* To match dts and media duration, update stts and mdhd relatively. */
753 if( mdhd
->duration
> dts
)
754 last_sample_delta
= mdhd
->duration
- dts
;
756 mdhd
->duration
= dts
+ last_sample_delta
; /* media duration must not less than last dts. */
758 int err
= isom_replace_last_sample_delta( stbl
, last_sample_delta
);
761 /* Explicit composition information and timeline shifting */
762 if( LSMASH_IS_EXISTING_BOX( cslg
) || file
->qt_compatible
|| file
->max_isom_version
>= 4 )
766 /* Remove composition to decode timeline shift. */
767 max_cts
-= ctd_shift
;
768 max2_cts
-= ctd_shift
;
769 min_cts
-= ctd_shift
;
771 int64_t composition_end_time
= max_cts
+ (max_cts
- max2_cts
);
773 && min_cts
!= LSMASH_TIMESTAMP_UNDEFINED
774 && (min_offset
<= INT32_MAX
) && (min_offset
>= INT32_MIN
)
775 && (max_offset
<= INT32_MAX
) && (max_offset
>= INT32_MIN
)
776 && ((int64_t)min_cts
<= INT32_MAX
) && (composition_end_time
<= INT32_MAX
) )
778 if( LSMASH_IS_NON_EXISTING_BOX( cslg
) )
780 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_cslg( trak
->mdia
->minf
->stbl
) ) )
781 return LSMASH_ERR_NAMELESS
;
784 cslg
->compositionToDTSShift
= ctd_shift
;
785 cslg
->leastDecodeToDisplayDelta
= min_offset
;
786 cslg
->greatestDecodeToDisplayDelta
= max_offset
;
787 cslg
->compositionStartTime
= min_cts
;
788 cslg
->compositionEndTime
= composition_end_time
;
791 isom_remove_box_by_itself( cslg
);
794 if( mdhd
->duration
> UINT32_MAX
&& !file
->undefined_64_ver
)
799 static int isom_update_mvhd_duration( isom_moov_t
*moov
)
801 assert( LSMASH_IS_EXISTING_BOX( moov
) );
802 if( LSMASH_IS_NON_EXISTING_BOX( moov
->mvhd
->file
) )
803 return LSMASH_ERR_INVALID_DATA
;
804 isom_mvhd_t
*mvhd
= moov
->mvhd
;
806 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
808 /* We pick maximum track duration as movie duration. */
809 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
810 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
811 || LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
812 return LSMASH_ERR_INVALID_DATA
;
813 mvhd
->duration
= entry
!= moov
->trak_list
.head
814 ? LSMASH_MAX( mvhd
->duration
, trak
->tkhd
->duration
)
815 : trak
->tkhd
->duration
;
817 if( mvhd
->duration
> UINT32_MAX
&& !mvhd
->file
->undefined_64_ver
)
822 int isom_update_tkhd_duration( isom_trak_t
*trak
)
824 assert( LSMASH_IS_EXISTING_BOX( trak
) );
825 if( LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
)
826 || LSMASH_IS_NON_EXISTING_BOX( trak
->file
->moov
->mvhd
) )
827 return LSMASH_ERR_INVALID_DATA
;
828 lsmash_file_t
*file
= trak
->file
;
829 isom_tkhd_t
*tkhd
= trak
->tkhd
;
832 || LSMASH_IS_NON_EXISTING_BOX( trak
->edts
->elst
) )
834 /* If this presentation might be extended or this track doesn't have edit list, calculate track duration from media duration. */
835 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
)
836 || trak
->mdia
->mdhd
->timescale
== 0 )
837 return LSMASH_ERR_INVALID_DATA
;
838 if( trak
->mdia
->mdhd
->duration
== 0 )
840 int err
= isom_update_mdhd_duration( trak
, 0 );
844 tkhd
->duration
= trak
->mdia
->mdhd
->duration
* ((double)file
->moov
->mvhd
->timescale
/ trak
->mdia
->mdhd
->timescale
);
848 /* If the presentation won't be extended and this track has any edit, then track duration is just the sum of the segment_duartions. */
849 for( lsmash_entry_t
*entry
= trak
->edts
->elst
->list
->head
; entry
; entry
= entry
->next
)
851 isom_elst_entry_t
*data
= (isom_elst_entry_t
*)entry
->data
;
853 return LSMASH_ERR_INVALID_DATA
;
854 tkhd
->duration
+= data
->segment_duration
;
857 if( tkhd
->duration
> UINT32_MAX
&& !file
->undefined_64_ver
)
859 if( !file
->fragment
&& tkhd
->duration
== 0 )
860 tkhd
->duration
= tkhd
->version
== 1 ? 0xffffffffffffffff : 0xffffffff;
861 return isom_update_mvhd_duration( file
->moov
);
864 int lsmash_update_track_duration( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t last_sample_delta
)
866 if( isom_check_initializer_present( root
) < 0 )
867 return LSMASH_ERR_FUNCTION_PARAM
;
868 lsmash_file_t
*file
= root
->file
;
869 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
870 if( LSMASH_IS_NON_EXISTING_BOX( trak
) )
871 return LSMASH_ERR_NAMELESS
;
872 int err
= isom_update_mdhd_duration( trak
, last_sample_delta
);
875 /* If the presentation won't be extended and this track has any edit, we don't change or update duration in tkhd. */
877 && LSMASH_IS_EXISTING_BOX( trak
->edts
)
878 && LSMASH_IS_EXISTING_BOX( trak
->edts
->elst
) )
879 err
= isom_update_mvhd_duration( file
->moov
); /* Only update movie duration. */
881 err
= isom_update_tkhd_duration( trak
); /* Also update movie duration internally. */
885 static inline int isom_increment_sample_number_in_entry
887 uint32_t *sample_number_in_entry
,
888 uint32_t sample_count_in_entry
,
889 lsmash_entry_t
**entry
892 if( *sample_number_in_entry
!= sample_count_in_entry
)
894 *sample_number_in_entry
+= 1;
897 /* Precede the next entry. */
898 *sample_number_in_entry
= 1;
901 *entry
= (*entry
)->next
;
902 if( *entry
&& !(*entry
)->data
)
903 return LSMASH_ERR_INVALID_DATA
;
908 int isom_calculate_bitrate_description
912 uint32_t *bufferSizeDB
,
913 uint32_t *maxBitrate
,
914 uint32_t *avgBitrate
,
915 uint32_t sample_description_index
918 isom_stsz_t
*stsz
= stbl
->stsz
;
919 lsmash_entry_list_t
*stsz_list
= LSMASH_IS_EXISTING_BOX( stsz
) ? stsz
->list
: stbl
->stz2
->list
;
920 lsmash_entry_t
*stsz_entry
= stsz_list
? stsz_list
->head
: NULL
;
921 lsmash_entry_t
*stts_entry
= stbl
->stts
->list
->head
;
922 lsmash_entry_t
*stsc_entry
= NULL
;
923 lsmash_entry_t
*next_stsc_entry
= stbl
->stsc
->list
->head
;
924 isom_stts_entry_t
*stts_data
= NULL
;
925 isom_stsc_entry_t
*stsc_data
= NULL
;
926 if( next_stsc_entry
&& !next_stsc_entry
->data
)
927 return LSMASH_ERR_INVALID_DATA
;
930 uint32_t time_wnd
= 0;
931 uint32_t chunk_number
= 0;
932 uint32_t sample_number_in_stts
= 1;
933 uint32_t sample_number_in_chunk
= 1;
934 uint32_t constant_sample_size
= LSMASH_IS_EXISTING_BOX( stsz
) ? stsz
->sample_size
: 0;
941 if( !stsc_data
|| sample_number_in_chunk
== stsc_data
->samples_per_chunk
)
943 /* Move the next chunk. */
944 sample_number_in_chunk
= 1;
946 /* Check if the next entry is broken. */
947 while( next_stsc_entry
&& ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
< chunk_number
)
949 /* Just skip broken next entry. */
950 next_stsc_entry
= next_stsc_entry
->next
;
951 if( next_stsc_entry
&& !next_stsc_entry
->data
)
952 return LSMASH_ERR_INVALID_DATA
;
954 /* Check if the next chunk belongs to the next sequence of chunks. */
955 if( next_stsc_entry
&& ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
== chunk_number
)
957 stsc_entry
= next_stsc_entry
;
958 next_stsc_entry
= next_stsc_entry
->next
;
959 if( next_stsc_entry
&& !next_stsc_entry
->data
)
960 return LSMASH_ERR_INVALID_DATA
;
961 stsc_data
= (isom_stsc_entry_t
*)stsc_entry
->data
;
962 /* Check if the next contiguous chunks belong to given sample description. */
963 if( stsc_data
->sample_description_index
!= sample_description_index
)
965 /* Skip chunks which don't belong to given sample description. */
966 uint32_t number_of_skips
= 0;
967 uint32_t first_chunk
= stsc_data
->first_chunk
;
968 uint32_t samples_per_chunk
= stsc_data
->samples_per_chunk
;
969 while( next_stsc_entry
)
971 if( ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->sample_description_index
!= sample_description_index
)
973 stsc_data
= (isom_stsc_entry_t
*)next_stsc_entry
->data
;
974 number_of_skips
+= (stsc_data
->first_chunk
- first_chunk
) * samples_per_chunk
;
975 first_chunk
= stsc_data
->first_chunk
;
976 samples_per_chunk
= stsc_data
->samples_per_chunk
;
978 else if( ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
<= first_chunk
)
982 /* Just skip the next entry. */
983 next_stsc_entry
= next_stsc_entry
->next
;
984 if( next_stsc_entry
&& !next_stsc_entry
->data
)
985 return LSMASH_ERR_INVALID_DATA
;
987 if( !next_stsc_entry
)
988 break; /* There is no more chunks which don't belong to given sample description. */
989 number_of_skips
+= (((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
- first_chunk
) * samples_per_chunk
;
990 for( uint32_t i
= 0; i
< number_of_skips
; i
++ )
996 stsz_entry
= stsz_entry
->next
;
1000 if( (err
= isom_increment_sample_number_in_entry( &sample_number_in_stts
,
1001 ((isom_stts_entry_t
*)stts_entry
->data
)->sample_count
,
1002 &stts_entry
)) < 0 )
1005 if( (stsz_list
&& !stsz_entry
) || !stts_entry
)
1007 chunk_number
= stsc_data
->first_chunk
;
1012 ++sample_number_in_chunk
;
1013 /* Get current sample's size. */
1019 isom_stsz_entry_t
*stsz_data
= (isom_stsz_entry_t
*)stsz_entry
->data
;
1021 return LSMASH_ERR_INVALID_DATA
;
1022 size
= stsz_data
->entry_size
;
1023 stsz_entry
= stsz_entry
->next
;
1026 size
= constant_sample_size
;
1027 /* Get current sample's DTS. */
1029 dts
+= stts_data
->sample_delta
;
1030 stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
1032 return LSMASH_ERR_INVALID_DATA
;
1033 if( (err
= isom_increment_sample_number_in_entry( &sample_number_in_stts
, stts_data
->sample_count
, &stts_entry
)) < 0 )
1035 /* Calculate bitrate description. */
1036 if( *bufferSizeDB
< size
)
1037 *bufferSizeDB
= size
;
1038 *avgBitrate
+= size
;
1040 if( dts
> time_wnd
+ mdhd
->timescale
)
1042 if( rate
> *maxBitrate
)
1048 double duration
= (double)mdhd
->duration
/ mdhd
->timescale
;
1049 *avgBitrate
= (uint32_t)(*avgBitrate
/ duration
);
1050 if( *maxBitrate
== 0 )
1051 *maxBitrate
= *avgBitrate
;
1052 /* Convert to bits per second. */
1058 int isom_is_variable_size( isom_stbl_t
*stbl
)
1060 if( (LSMASH_IS_EXISTING_BOX( stbl
->stz2
) && stbl
->stz2
->sample_count
> 1)
1061 || (LSMASH_IS_EXISTING_BOX( stbl
->stsz
) && stbl
->stsz
->sample_count
> 1 && stbl
->stsz
->sample_size
== 0) )
1067 uint32_t isom_get_first_sample_size( isom_stbl_t
*stbl
)
1069 if( LSMASH_IS_EXISTING_BOX( stbl
->stsz
) )
1072 if( stbl
->stsz
->sample_size
)
1073 return stbl
->stsz
->sample_size
;
1074 else if( stbl
->stsz
->list
&& stbl
->stsz
->list
->head
&& stbl
->stsz
->list
->head
->data
)
1075 return ((isom_stsz_entry_t
*)stbl
->stsz
->list
->head
->data
)->entry_size
;
1079 else if( LSMASH_IS_EXISTING_BOX( stbl
->stz2
) )
1082 if( stbl
->stz2
->list
&& stbl
->stz2
->list
->head
&& stbl
->stz2
->list
->head
->data
)
1083 return ((isom_stsz_entry_t
*)stbl
->stz2
->list
->head
->data
)->entry_size
;
1091 int isom_update_bitrate_description( isom_mdia_t
*mdia
)
1093 if( LSMASH_IS_NON_EXISTING_BOX( mdia
->mdhd
) )
1094 return LSMASH_ERR_INVALID_DATA
;
1095 isom_stbl_t
*stbl
= mdia
->minf
->stbl
;
1096 if( LSMASH_IS_NON_EXISTING_BOX( stbl
->stsd
)
1097 || (LSMASH_IS_NON_EXISTING_BOX( stbl
->stsz
) && LSMASH_IS_NON_EXISTING_BOX( stbl
->stz2
))
1098 || !stbl
->stsc
->list
1099 || !stbl
->stts
->list
)
1100 return LSMASH_ERR_INVALID_DATA
;
1101 uint32_t sample_description_index
= 0;
1102 for( lsmash_entry_t
*entry
= stbl
->stsd
->list
.head
; entry
; entry
= entry
->next
)
1104 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)entry
->data
;
1106 return LSMASH_ERR_INVALID_DATA
;
1107 ++sample_description_index
;
1108 isom_bitrate_updater_t func_update_bitrate
= isom_get_bitrate_updater( sample_entry
);
1109 if( func_update_bitrate
)
1111 int err
= func_update_bitrate( stbl
, mdia
->mdhd
, sample_description_index
);
1116 return sample_description_index
? 0 : LSMASH_ERR_INVALID_DATA
;
1119 static inline uint64_t isom_get_current_mp4time( void )
1121 return (uint64_t)time( NULL
) + ISOM_MAC_EPOCH_OFFSET
;
1124 static int isom_set_media_creation_time( isom_trak_t
*trak
, uint64_t current_mp4time
)
1126 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
) )
1127 return LSMASH_ERR_NAMELESS
;
1128 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
1129 if( mdhd
->creation_time
== 0 )
1130 mdhd
->creation_time
= mdhd
->modification_time
= current_mp4time
;
1134 static int isom_set_track_creation_time( isom_trak_t
*trak
, uint64_t current_mp4time
)
1136 assert( LSMASH_IS_EXISTING_BOX( trak
) );
1137 if( LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
1138 return LSMASH_ERR_NAMELESS
;
1139 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1140 if( tkhd
->creation_time
== 0 )
1141 tkhd
->creation_time
= tkhd
->modification_time
= current_mp4time
;
1142 return isom_set_media_creation_time( trak
, current_mp4time
);
1145 static int isom_set_movie_creation_time( lsmash_file_t
*file
)
1147 if( LSMASH_IS_NON_EXISTING_BOX( file
->moov
->mvhd
) )
1148 return LSMASH_ERR_NAMELESS
;
1149 uint64_t current_mp4time
= isom_get_current_mp4time();
1150 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
1152 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
1153 if( LSMASH_IS_NON_EXISTING_BOX( trak
) )
1154 return LSMASH_ERR_INVALID_DATA
;
1155 int err
= isom_set_track_creation_time( trak
, current_mp4time
);
1159 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
1160 if( mvhd
->creation_time
== 0 )
1161 mvhd
->creation_time
= mvhd
->modification_time
= current_mp4time
;
1165 int isom_setup_handler_reference( isom_hdlr_t
*hdlr
, uint32_t media_type
)
1167 assert( LSMASH_IS_EXISTING_BOX( hdlr
) );
1168 isom_box_t
*parent
= hdlr
->parent
;
1169 lsmash_file_t
*file
= hdlr
->file
;
1170 if( LSMASH_IS_NON_EXISTING_BOX( parent
)
1171 || LSMASH_IS_NON_EXISTING_BOX( file
) )
1172 return LSMASH_ERR_NAMELESS
;
1173 isom_mdia_t
*mdia
= lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_MDIA
) ? (isom_mdia_t
*)parent
: isom_non_existing_mdia();
1174 isom_meta_t
*meta
= lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_META
) ? (isom_meta_t
*)parent
1175 : lsmash_check_box_type_identical( parent
->type
, QT_BOX_TYPE_META
) ? (isom_meta_t
*)parent
: isom_non_existing_meta();
1176 uint32_t type
= LSMASH_IS_EXISTING_BOX( mdia
) ? (file
->qt_compatible
? QT_HANDLER_TYPE_MEDIA
: 0)
1177 : (LSMASH_IS_EXISTING_BOX( meta
) ? 0 : QT_HANDLER_TYPE_DATA
);
1178 uint32_t subtype
= media_type
;
1179 hdlr
->componentType
= type
;
1180 hdlr
->componentSubtype
= subtype
;
1181 char *type_name
= NULL
;
1182 char *subtype_name
= NULL
;
1183 uint8_t type_name_length
= 0;
1184 uint8_t subtype_name_length
= 0;
1186 type_name
= "Media ";
1188 type_name
= "Metadata ";
1189 else /* if( minf ) */
1190 type_name
= "Data ";
1191 type_name_length
= strlen( type_name
);
1196 uint8_t subtype_name_length
;
1199 { ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
, "Sound ", 6 },
1200 { ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
, "Video ", 6 },
1201 { ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK
, "Hint ", 5 },
1202 { ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK
, "Metadata ", 9 },
1203 { ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK
, "Text ", 5 },
1204 { ISOM_META_HANDLER_TYPE_ITUNES_METADATA
, "iTunes ", 7 },
1205 { QT_REFERENCE_HANDLER_TYPE_ALIAS
, "Alias ", 6 },
1206 { QT_REFERENCE_HANDLER_TYPE_RESOURCE
, "Resource ", 9 },
1207 { QT_REFERENCE_HANDLER_TYPE_URL
, "URL ", 4 },
1208 { subtype
, "Unknown ", 8 }
1210 for( int i
= 0; subtype_table
[i
].subtype
; i
++ )
1211 if( subtype
== subtype_table
[i
].subtype
)
1213 subtype_name
= subtype_table
[i
].subtype_name
;
1214 subtype_name_length
= subtype_table
[i
].subtype_name_length
;
1217 uint32_t name_length
= 15 + subtype_name_length
+ type_name_length
+ file
->isom_compatible
+ file
->qt_compatible
;
1218 uint8_t *name
= lsmash_malloc( name_length
);
1220 return LSMASH_ERR_MEMORY_ALLOC
;
1221 if( file
->qt_compatible
)
1222 name
[0] = name_length
& 0xff;
1223 memcpy( name
+ file
->qt_compatible
, "L-SMASH ", 8 );
1224 memcpy( name
+ file
->qt_compatible
+ 8, subtype_name
, subtype_name_length
);
1225 memcpy( name
+ file
->qt_compatible
+ 8 + subtype_name_length
, type_name
, type_name_length
);
1226 memcpy( name
+ file
->qt_compatible
+ 8 + subtype_name_length
+ type_name_length
, "Handler", 7 );
1227 if( file
->isom_compatible
)
1228 name
[name_length
- 1] = 0;
1229 hdlr
->componentName
= name
;
1230 hdlr
->componentName_length
= name_length
;
1234 isom_trak_t
*isom_track_create( lsmash_file_t
*file
, lsmash_media_type media_type
)
1236 /* Don't allow to create a new track if the initial movie is already written. */
1237 if( (file
->fragment
&& file
->fragment
->movie
)
1238 || (LSMASH_IS_EXISTING_BOX( file
->moov
) && (file
->moov
->manager
& LSMASH_WRITTEN_BOX
)) )
1239 return isom_non_existing_trak();
1240 isom_trak_t
*trak
= isom_add_trak( file
->moov
);
1241 if( LSMASH_IS_NON_EXISTING_BOX( trak
->file
->moov
->mvhd
) )
1243 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_tkhd( trak
) )
1244 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_mdia( trak
) )
1245 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_mdhd( trak
->mdia
) )
1246 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_minf( trak
->mdia
) )
1247 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_dinf( trak
->mdia
->minf
) )
1248 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_dref( trak
->mdia
->minf
->dinf
) )
1249 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stbl( trak
->mdia
->minf
) )
1250 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stsd( trak
->mdia
->minf
->stbl
) )
1251 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stts( trak
->mdia
->minf
->stbl
) )
1252 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stsc( trak
->mdia
->minf
->stbl
) )
1253 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stco( trak
->mdia
->minf
->stbl
) )
1254 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stsz( trak
->mdia
->minf
->stbl
) ) )
1256 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_hdlr( trak
->mdia
) )
1257 || isom_setup_handler_reference( trak
->mdia
->hdlr
, media_type
) < 0 )
1259 if( file
->qt_compatible
)
1261 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_hdlr( trak
->mdia
->minf
) )
1262 || isom_setup_handler_reference( trak
->mdia
->minf
->hdlr
, QT_REFERENCE_HANDLER_TYPE_URL
) < 0 )
1265 switch( media_type
)
1267 case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
:
1268 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_vmhd( trak
->mdia
->minf
) ) )
1270 trak
->mdia
->minf
->vmhd
->flags
= 0x000001;
1272 case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
:
1273 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_smhd( trak
->mdia
->minf
) ) )
1275 trak
->cache
->is_audio
= 1;
1277 case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK
:
1278 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_hmhd( trak
->mdia
->minf
) ) )
1280 trak
->mdia
->minf
->hmhd
->combinedPDUsize
= 0;
1281 trak
->mdia
->minf
->hmhd
->PDUcount
= 0;
1282 trak
->mdia
->minf
->hmhd
->maxPDUsize
= 0;
1284 case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK
:
1285 if( file
->qt_compatible
|| file
->itunes_movie
)
1287 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_gmhd( trak
->mdia
->minf
) )
1288 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_gmin( trak
->mdia
->minf
->gmhd
) )
1289 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_text( trak
->mdia
->minf
->gmhd
) ) )
1291 /* Default Text Media Information Box. */
1293 isom_text_t
*text
= trak
->mdia
->minf
->gmhd
->text
;
1294 text
->matrix
[0] = 0x00010000;
1295 text
->matrix
[4] = 0x00010000;
1296 text
->matrix
[8] = 0x40000000;
1300 goto fail
; /* We support only reference text media track for chapter yet. */
1303 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_nmhd( trak
->mdia
->minf
) ) )
1307 /* Default Track Header Box. */
1309 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1310 if( media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
)
1311 tkhd
->volume
= 0x0100;
1312 tkhd
->matrix
[0] = 0x00010000;
1313 tkhd
->matrix
[4] = 0x00010000;
1314 tkhd
->matrix
[8] = 0x40000000;
1315 tkhd
->duration
= 0xffffffff;
1316 tkhd
->track_ID
= trak
->file
->moov
->mvhd
->next_track_ID
++;
1318 trak
->mdia
->mdhd
->language
= file
->qt_compatible
? 0 : ISOM_LANGUAGE_CODE_UNDEFINED
;
1321 isom_remove_box_by_itself( trak
);
1322 return isom_non_existing_trak();
1325 isom_moov_t
*isom_movie_create( lsmash_file_t
*file
)
1327 isom_moov_t
*moov
= isom_add_moov( file
);
1328 isom_mvhd_t
*mvhd
= isom_add_mvhd( moov
);
1329 if( LSMASH_IS_NON_EXISTING_BOX( mvhd
) )
1331 isom_remove_box_by_itself( moov
);
1332 return isom_non_existing_moov();
1334 /* Default Movie Header Box. */
1335 mvhd
->rate
= 0x00010000;
1336 mvhd
->volume
= 0x0100;
1337 mvhd
->matrix
[0] = 0x00010000;
1338 mvhd
->matrix
[4] = 0x00010000;
1339 mvhd
->matrix
[8] = 0x40000000;
1340 mvhd
->next_track_ID
= 1;
1341 file
->initializer
= file
;
1345 /*******************************
1347 *******************************/
1349 /*---- track manipulators ----*/
1351 void lsmash_delete_track( lsmash_root_t
*root
, uint32_t track_ID
)
1353 if( isom_check_initializer_present( root
) < 0 )
1355 for( lsmash_entry_t
*entry
= root
->file
->initializer
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
1357 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
1358 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
1359 || LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
1361 if( trak
->tkhd
->track_ID
== track_ID
)
1363 isom_remove_box_by_itself( trak
);
1369 uint32_t lsmash_create_track( lsmash_root_t
*root
, lsmash_media_type media_type
)
1371 if( isom_check_initializer_present( root
) < 0 )
1373 isom_trak_t
*trak
= isom_track_create( root
->file
, media_type
);
1374 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
1375 || LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
1377 return trak
->tkhd
->track_ID
;
1380 uint32_t lsmash_get_track_ID( lsmash_root_t
*root
, uint32_t track_number
)
1382 if( isom_check_initializer_present( root
) < 0
1383 || LSMASH_IS_NON_EXISTING_BOX( root
->file
->initializer
->moov
) )
1385 isom_trak_t
*trak
= (isom_trak_t
*)lsmash_list_get_entry_data( &root
->file
->initializer
->moov
->trak_list
, track_number
);
1386 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
1387 || LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
1389 return trak
->tkhd
->track_ID
;
1392 void lsmash_initialize_track_parameters( lsmash_track_parameters_t
*param
)
1394 memset( param
, 0, sizeof(lsmash_track_parameters_t
) );
1395 param
->audio_volume
= 0x0100;
1396 param
->matrix
[0] = 0x00010000;
1397 param
->matrix
[4] = 0x00010000;
1398 param
->matrix
[8] = 0x40000000;
1401 int lsmash_set_track_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_track_parameters_t
*param
)
1403 if( isom_check_initializer_present( root
) < 0 )
1404 return LSMASH_ERR_FUNCTION_PARAM
;
1405 lsmash_file_t
*file
= root
->file
;
1406 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1407 if( LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
)
1408 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->hdlr
)
1409 || LSMASH_IS_NON_EXISTING_BOX( file
->moov
->mvhd
) )
1410 return LSMASH_ERR_NAMELESS
;
1411 /* Prepare Track Aperture Modes if required. */
1412 if( file
->qt_compatible
&& param
->aperture_modes
)
1414 if( LSMASH_IS_NON_EXISTING_BOX( trak
->tapt
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_tapt( trak
) ) )
1415 return LSMASH_ERR_NAMELESS
;
1416 isom_tapt_t
*tapt
= trak
->tapt
;
1417 if( (LSMASH_IS_NON_EXISTING_BOX( tapt
->clef
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_clef( tapt
) ))
1418 || (LSMASH_IS_NON_EXISTING_BOX( tapt
->prof
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_prof( tapt
) ))
1419 || (LSMASH_IS_NON_EXISTING_BOX( tapt
->enof
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_enof( tapt
) )) )
1420 return LSMASH_ERR_NAMELESS
;
1423 isom_remove_box_by_itself( trak
->tapt
);
1424 /* Set up Track Header. */
1425 uint32_t media_type
= trak
->mdia
->hdlr
->componentSubtype
;
1426 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1427 tkhd
->flags
= param
->mode
;
1428 tkhd
->track_ID
= param
->track_ID
? param
->track_ID
: tkhd
->track_ID
;
1429 tkhd
->duration
= LSMASH_IS_NON_EXISTING_BOX( trak
->edts
->elst
) ? param
->duration
: tkhd
->duration
;
1431 * alternate_group, layer, volume and matrix
1432 * According to 14496-14, these value are all set to defaut values in 14496-12.
1433 * And when a file is read as an MPEG-4 file, these values shall be ignored.
1434 * If a file complies with other specifications, then those fields may have non-default values
1435 * as required by those other specifications. */
1436 if( param
->alternate_group
)
1438 if( file
->qt_compatible
|| file
->itunes_movie
|| file
->max_3gpp_version
>= 4 )
1439 tkhd
->alternate_group
= param
->alternate_group
;
1442 tkhd
->alternate_group
= 0;
1443 lsmash_log( NULL
, LSMASH_LOG_WARNING
,
1444 "alternate_group is specified but not compatible with any of the brands. It won't be set.\n" );
1448 tkhd
->alternate_group
= 0;
1449 if( file
->qt_compatible
|| file
->itunes_movie
)
1451 tkhd
->layer
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->video_layer
: 0;
1452 tkhd
->volume
= media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
? param
->audio_volume
: 0;
1453 if( media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
)
1454 for( int i
= 0; i
< 9; i
++ )
1455 tkhd
->matrix
[i
] = param
->matrix
[i
];
1457 for( int i
= 0; i
< 9; i
++ )
1458 tkhd
->matrix
[i
] = 0;
1463 tkhd
->volume
= media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
? 0x0100 : 0;
1464 tkhd
->matrix
[0] = 0x00010000;
1465 tkhd
->matrix
[1] = 0;
1466 tkhd
->matrix
[2] = 0;
1467 tkhd
->matrix
[3] = 0;
1468 tkhd
->matrix
[4] = 0x00010000;
1469 tkhd
->matrix
[5] = 0;
1470 tkhd
->matrix
[6] = 0;
1471 tkhd
->matrix
[7] = 0;
1472 tkhd
->matrix
[8] = 0x40000000;
1474 /* visual presentation size */
1475 tkhd
->width
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->display_width
: 0;
1476 tkhd
->height
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->display_height
: 0;
1477 /* Update next_track_ID if needed. */
1478 if( file
->moov
->mvhd
->next_track_ID
<= tkhd
->track_ID
)
1479 file
->moov
->mvhd
->next_track_ID
= tkhd
->track_ID
+ 1;
1483 int lsmash_get_track_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_track_parameters_t
*param
)
1485 if( isom_check_initializer_present( root
) < 0 )
1486 return LSMASH_ERR_FUNCTION_PARAM
;
1487 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1488 if( LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
1489 return LSMASH_ERR_NAMELESS
;
1490 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1491 param
->mode
= tkhd
->flags
;
1492 param
->track_ID
= tkhd
->track_ID
;
1493 param
->duration
= tkhd
->duration
;
1494 param
->video_layer
= tkhd
->layer
;
1495 param
->alternate_group
= tkhd
->alternate_group
;
1496 param
->audio_volume
= tkhd
->volume
;
1497 for( int i
= 0; i
< 9; i
++ )
1498 param
->matrix
[i
] = tkhd
->matrix
[i
];
1499 param
->display_width
= tkhd
->width
;
1500 param
->display_height
= tkhd
->height
;
1501 param
->aperture_modes
= LSMASH_IS_EXISTING_BOX( trak
->tapt
);
1505 static inline int check_dref_presence( isom_trak_t
*trak
)
1507 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->dinf
->dref
) )
1508 return LSMASH_ERR_NAMELESS
;
1512 uint32_t lsmash_count_data_reference
1514 lsmash_root_t
*root
,
1518 if( isom_check_initializer_present( root
) < 0 )
1520 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1521 if( check_dref_presence( trak
) < 0 )
1523 return trak
->mdia
->minf
->dinf
->dref
->list
.entry_count
;
1526 int lsmash_get_data_reference
1528 lsmash_root_t
*root
,
1530 lsmash_data_reference_t
*data_ref
1533 if( isom_check_initializer_present( root
) < 0 || !data_ref
)
1534 return LSMASH_ERR_FUNCTION_PARAM
;
1535 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1536 if( check_dref_presence( trak
) < 0 )
1537 return LSMASH_ERR_NAMELESS
;
1538 isom_dref_entry_t
*url
= lsmash_list_get_entry_data( &trak
->mdia
->minf
->dinf
->dref
->list
, data_ref
->index
);
1539 if( LSMASH_IS_NON_EXISTING_BOX( url
) )
1540 return LSMASH_ERR_NAMELESS
;
1541 if( !(url
->flags
& 0x000001) && url
->location
)
1543 int length
= strlen( url
->location
);
1544 char *location
= lsmash_malloc( length
+ 1 );
1546 return LSMASH_ERR_MEMORY_ALLOC
;
1547 memcpy( location
, url
->location
, length
);
1548 location
[length
] = '\0';
1549 data_ref
->location
= location
;
1552 data_ref
->location
= NULL
;
1556 void lsmash_cleanup_data_reference
1558 lsmash_data_reference_t
*data_ref
1563 lsmash_freep( &data_ref
->location
);
1566 int lsmash_create_data_reference
1568 lsmash_root_t
*root
,
1570 lsmash_data_reference_t
*data_ref
,
1574 /* At present, we don't support external data references for movie fragments.
1575 * Note that, for external media data, default-base-is-moof is meaningless since relative
1576 * offsets from Movie Fragment Boxes make no sense.
1577 * In the future, the condition of !(file->flags & LSMASH_FILE_MODE_WRITE) may be removed
1578 * for the implementation which does not write actually and does reference read-only file. */
1579 if( LSMASH_IS_NON_EXISTING_BOX( root
)
1580 || LSMASH_IS_NON_EXISTING_BOX( file
)
1581 || file
->root
!= root
1582 || (!(file
->flags
& LSMASH_FILE_MODE_MEDIA
) && !(file
->flags
& LSMASH_FILE_MODE_INITIALIZATION
))
1583 || !(file
->flags
& LSMASH_FILE_MODE_WRITE
)
1584 || (root
->file
!= file
&& ((file
->flags
& LSMASH_FILE_MODE_FRAGMENTED
) || file
->fragment
))
1586 return LSMASH_ERR_FUNCTION_PARAM
;
1587 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
1588 if( check_dref_presence( trak
) < 0 )
1589 return LSMASH_ERR_NAMELESS
;
1590 isom_dref_entry_t
*url
= isom_add_dref_entry( trak
->mdia
->minf
->dinf
->dref
, ISOM_BOX_TYPE_URL
);
1591 if( LSMASH_IS_NON_EXISTING_BOX( url
) )
1592 return LSMASH_ERR_NAMELESS
;
1593 if( !data_ref
->location
|| root
->file
== file
)
1595 /* Media data is in the same file. */
1596 url
->flags
= 0x000001;
1597 url
->ref_file
= root
->file
;
1601 /* Set the location of the file. */
1602 int length
= strlen( data_ref
->location
);
1603 url
->location
= lsmash_malloc( length
+ 1 );
1604 if( !url
->location
)
1606 isom_remove_box_by_itself( url
);
1607 return LSMASH_ERR_MEMORY_ALLOC
;
1609 memcpy( url
->location
, data_ref
->location
, length
);
1610 url
->location
[length
] = '\0';
1611 url
->location_length
= length
+ 1;
1612 url
->ref_file
= file
;
1614 data_ref
->index
= trak
->mdia
->minf
->dinf
->dref
->list
.entry_count
;
1618 int lsmash_assign_data_reference
1620 lsmash_root_t
*root
,
1622 uint32_t data_ref_index
,
1626 if( isom_check_initializer_present( root
) < 0
1627 || !file
|| file
->root
!= root
1628 || !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
1629 || !(file
->flags
& LSMASH_FILE_MODE_READ
)
1630 || data_ref_index
== 0 )
1631 return LSMASH_ERR_FUNCTION_PARAM
;
1632 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1633 if( check_dref_presence( trak
) < 0 )
1634 return LSMASH_ERR_NAMELESS
;
1635 isom_dref_entry_t
*url
= (isom_dref_entry_t
*)lsmash_list_get_entry_data( &trak
->mdia
->minf
->dinf
->dref
->list
, data_ref_index
);
1636 if( LSMASH_IS_NON_EXISTING_BOX( url
) )
1637 return LSMASH_ERR_NAMELESS
;
1638 if( !(url
->flags
& 0x000001) )
1639 /* Reference an external media data. */
1640 url
->ref_file
= file
;
1644 static int isom_set_media_handler_name( lsmash_file_t
*file
, uint32_t track_ID
, char *handler_name
)
1646 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1647 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->hdlr
) )
1648 return LSMASH_ERR_NAMELESS
;
1649 isom_hdlr_t
*hdlr
= trak
->mdia
->hdlr
;
1650 uint8_t *name
= NULL
;
1651 uint32_t name_length
= strlen( handler_name
) + file
->isom_compatible
+ file
->qt_compatible
;
1652 if( file
->qt_compatible
)
1653 name_length
= LSMASH_MIN( name_length
, 255 );
1654 if( name_length
> hdlr
->componentName_length
&& hdlr
->componentName
)
1655 name
= lsmash_realloc( hdlr
->componentName
, name_length
);
1656 else if( !hdlr
->componentName
)
1657 name
= lsmash_malloc( name_length
);
1659 name
= hdlr
->componentName
;
1661 return LSMASH_ERR_MEMORY_ALLOC
;
1662 if( file
->qt_compatible
)
1663 name
[0] = name_length
& 0xff;
1664 memcpy( name
+ file
->qt_compatible
, handler_name
, strlen( handler_name
) );
1665 if( file
->isom_compatible
)
1666 name
[name_length
- 1] = 0;
1667 hdlr
->componentName
= name
;
1668 hdlr
->componentName_length
= name_length
;
1672 static int isom_set_data_handler_name( lsmash_file_t
*file
, uint32_t track_ID
, char *handler_name
)
1674 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1675 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->hdlr
) )
1676 return LSMASH_ERR_NAMELESS
;
1677 isom_hdlr_t
*hdlr
= trak
->mdia
->minf
->hdlr
;
1678 uint8_t *name
= NULL
;
1679 uint32_t name_length
= strlen( handler_name
) + file
->isom_compatible
+ file
->qt_compatible
;
1680 if( file
->qt_compatible
)
1681 name_length
= LSMASH_MIN( name_length
, 255 );
1682 if( name_length
> hdlr
->componentName_length
&& hdlr
->componentName
)
1683 name
= lsmash_realloc( hdlr
->componentName
, name_length
);
1684 else if( !hdlr
->componentName
)
1685 name
= lsmash_malloc( name_length
);
1687 name
= hdlr
->componentName
;
1689 return LSMASH_ERR_MEMORY_ALLOC
;
1690 if( file
->qt_compatible
)
1691 name
[0] = name_length
& 0xff;
1692 memcpy( name
+ file
->qt_compatible
, handler_name
, strlen( handler_name
) );
1693 if( file
->isom_compatible
)
1694 name
[name_length
- 1] = 0;
1695 hdlr
->componentName
= name
;
1696 hdlr
->componentName_length
= name_length
;
1700 uint32_t lsmash_get_media_timescale( lsmash_root_t
*root
, uint32_t track_ID
)
1702 if( isom_check_initializer_present( root
) < 0 )
1704 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1705 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
) )
1707 return trak
->mdia
->mdhd
->timescale
;
1710 uint64_t lsmash_get_media_duration( lsmash_root_t
*root
, uint32_t track_ID
)
1712 if( isom_check_initializer_present( root
) < 0 )
1714 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1715 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
) )
1717 return trak
->mdia
->mdhd
->duration
;
1720 uint64_t lsmash_get_track_duration( lsmash_root_t
*root
, uint32_t track_ID
)
1722 if( isom_check_initializer_present( root
) < 0 )
1724 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1725 if( LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
1727 return trak
->tkhd
->duration
;
1730 uint32_t lsmash_get_last_sample_delta( lsmash_root_t
*root
, uint32_t track_ID
)
1732 if( isom_check_initializer_present( root
) < 0 )
1734 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
1735 if( !trak
->mdia
->minf
->stbl
->stts
->list
1736 || !trak
->mdia
->minf
->stbl
->stts
->list
->tail
1737 || !trak
->mdia
->minf
->stbl
->stts
->list
->tail
->data
)
1739 return ((isom_stts_entry_t
*)trak
->mdia
->minf
->stbl
->stts
->list
->tail
->data
)->sample_delta
;
1742 uint32_t lsmash_get_start_time_offset( lsmash_root_t
*root
, uint32_t track_ID
)
1744 if( isom_check_initializer_present( root
) < 0 )
1746 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
1747 if( !trak
->mdia
->minf
->stbl
->ctts
->list
1748 || !trak
->mdia
->minf
->stbl
->ctts
->list
->head
1749 || !trak
->mdia
->minf
->stbl
->ctts
->list
->head
->data
)
1751 return ((isom_ctts_entry_t
*)trak
->mdia
->minf
->stbl
->ctts
->list
->head
->data
)->sample_offset
;
1754 uint32_t lsmash_get_composition_to_decode_shift( lsmash_root_t
*root
, uint32_t track_ID
)
1756 if( isom_check_initializer_present( root
) < 0 )
1758 lsmash_file_t
*file
= root
->file
->initializer
;
1759 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1760 uint32_t sample_count
= isom_get_sample_count( trak
);
1761 if( sample_count
== 0 )
1763 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
1764 if( !stbl
->stts
->list
1765 || !stbl
->ctts
->list
)
1767 if( !(file
->max_isom_version
>= 4 && stbl
->ctts
->version
== 1) && !file
->qt_compatible
)
1768 return 0; /* This movie shall not have composition to decode timeline shift. */
1769 lsmash_entry_t
*stts_entry
= stbl
->stts
->list
->head
;
1770 lsmash_entry_t
*ctts_entry
= stbl
->ctts
->list
->head
;
1771 if( !stts_entry
|| !ctts_entry
)
1775 uint32_t ctd_shift
= 0;
1778 for( uint32_t k
= 0; k
< sample_count
; k
++ )
1780 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
1781 isom_ctts_entry_t
*ctts_data
= (isom_ctts_entry_t
*)ctts_entry
->data
;
1782 if( !stts_data
|| !ctts_data
)
1784 if( ctts_data
->sample_offset
!= ISOM_NON_OUTPUT_SAMPLE_OFFSET
)
1786 cts
= dts
+ (int32_t)ctts_data
->sample_offset
;
1787 if( dts
> cts
+ ctd_shift
)
1788 ctd_shift
= dts
- cts
;
1790 dts
+= stts_data
->sample_delta
;
1791 if( ++i
== stts_data
->sample_count
)
1793 stts_entry
= stts_entry
->next
;
1798 if( ++j
== ctts_data
->sample_count
)
1800 ctts_entry
= ctts_entry
->next
;
1809 uint16_t lsmash_pack_iso_language( char *iso_language
)
1811 if( !iso_language
|| strlen( iso_language
) != 3 )
1813 return (uint16_t)LSMASH_PACK_ISO_LANGUAGE( iso_language
[0], iso_language
[1], iso_language
[2] );
1816 static int isom_iso2mac_language( uint16_t ISO_language
, uint16_t *MAC_language
)
1818 assert( MAC_language
);
1820 for( ; isom_languages
[i
].iso_name
; i
++ )
1821 if( ISO_language
== isom_languages
[i
].iso_name
)
1823 if( !isom_languages
[i
].iso_name
)
1824 return LSMASH_ERR_NAMELESS
;
1825 *MAC_language
= isom_languages
[i
].mac_value
;
1829 static int isom_mac2iso_language( uint16_t MAC_language
, uint16_t *ISO_language
)
1831 assert( ISO_language
);
1833 for( ; isom_languages
[i
].iso_name
; i
++ )
1834 if( MAC_language
== isom_languages
[i
].mac_value
)
1836 *ISO_language
= isom_languages
[i
].iso_name
? isom_languages
[i
].iso_name
: ISOM_LANGUAGE_CODE_UNDEFINED
;
1840 static int isom_set_media_language( lsmash_file_t
*file
, uint32_t track_ID
, uint16_t ISO_language
, uint16_t MAC_language
)
1842 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1843 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
) )
1844 return LSMASH_ERR_NAMELESS
;
1845 uint16_t language
= 0;
1846 if( file
->isom_compatible
)
1849 language
= ISO_language
;
1850 else if( MAC_language
)
1852 int err
= isom_mac2iso_language( MAC_language
, &language
);
1857 language
= ISOM_LANGUAGE_CODE_UNDEFINED
;
1859 else if( file
->qt_compatible
)
1863 int err
= isom_iso2mac_language( ISO_language
, &language
);
1868 language
= MAC_language
;
1871 return LSMASH_ERR_INVALID_DATA
;
1872 trak
->mdia
->mdhd
->language
= language
;
1876 int isom_add_sample_grouping( isom_box_t
*parent
, isom_grouping_type grouping_type
)
1880 if( ((sgpd
= isom_add_sgpd( parent
)), LSMASH_IS_NON_EXISTING_BOX( sgpd
))
1881 || ((sbgp
= isom_add_sbgp( parent
)), LSMASH_IS_NON_EXISTING_BOX( sbgp
)) )
1882 return LSMASH_ERR_NAMELESS
;
1883 sbgp
->grouping_type
= grouping_type
;
1884 sgpd
->grouping_type
= grouping_type
;
1885 sgpd
->version
= 1; /* We use version 1 for Sample Group Description Box because it is recommended in the spec. */
1886 switch( grouping_type
)
1888 case ISOM_GROUP_TYPE_RAP
:
1889 sgpd
->default_length
= 1;
1891 case ISOM_GROUP_TYPE_ROLL
:
1892 case ISOM_GROUP_TYPE_PROL
:
1893 sgpd
->default_length
= 2;
1896 /* We don't consider other grouping types currently. */
1902 static int isom_create_sample_grouping( isom_trak_t
*trak
, isom_grouping_type grouping_type
)
1904 assert( LSMASH_IS_EXISTING_BOX( trak
) );
1905 lsmash_file_t
*file
= trak
->file
;
1906 switch( grouping_type
)
1908 case ISOM_GROUP_TYPE_RAP
:
1909 assert( file
->max_isom_version
>= 6 );
1911 case ISOM_GROUP_TYPE_ROLL
:
1912 case ISOM_GROUP_TYPE_PROL
:
1913 assert( file
->avc_extensions
|| file
->qt_compatible
);
1919 int err
= isom_add_sample_grouping( (isom_box_t
*)trak
->mdia
->minf
->stbl
, grouping_type
);
1922 if( trak
->cache
->fragment
&& file
->max_isom_version
>= 6 )
1923 switch( grouping_type
)
1925 case ISOM_GROUP_TYPE_RAP
:
1926 trak
->cache
->fragment
->rap_grouping
= 1;
1928 case ISOM_GROUP_TYPE_ROLL
:
1929 case ISOM_GROUP_TYPE_PROL
:
1930 trak
->cache
->fragment
->roll_grouping
= 1;
1933 /* We don't consider other grouping types currently. */
1939 static int isom_compress_sample_size_table( isom_stbl_t
*stbl
)
1941 if( stbl
->file
->max_3gpp_version
)
1942 /* 3GPP: Limitations to the ISO base media file format
1943 * - compact sample sizes ('stz2') shall not be used for tracks containing H.263, MPEG-4 video, AMR, AMR-WB, AAC or Timed text.
1944 * Note that 'mp4a' check is incomplete here since this restriction is not applied to Enhanced aacPlus audio (HE-AAC v2). */
1945 for( lsmash_entry_t
*entry
= stbl
->stsd
->list
.head
; entry
; entry
= entry
->next
)
1947 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)entry
->data
;
1948 if( LSMASH_IS_NON_EXISTING_BOX( sample_entry
) )
1949 return LSMASH_ERR_INVALID_DATA
;
1950 lsmash_codec_type_t sample_type
= sample_entry
->type
;
1951 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_S263_VIDEO
)
1952 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4V_VIDEO
)
1953 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4A_AUDIO
)
1954 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_SAMR_AUDIO
)
1955 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_SAWB_AUDIO
)
1956 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_TX3G_TEXT
) )
1959 if( LSMASH_IS_EXISTING_BOX( stbl
->stsz
) && isom_is_variable_size( stbl
) )
1961 int max_num_bits
= 0;
1962 for( lsmash_entry_t
*entry
= stbl
->stsz
->list
->head
; entry
; entry
= entry
->next
)
1964 isom_stsz_entry_t
*data
= (isom_stsz_entry_t
*)entry
->data
;
1966 return LSMASH_ERR_INVALID_DATA
;
1968 for( num_bits
= 1; data
->entry_size
>> num_bits
; num_bits
++ );
1969 if( max_num_bits
< num_bits
)
1971 max_num_bits
= num_bits
;
1972 if( max_num_bits
> 16 )
1973 return 0; /* not compressible */
1976 if( max_num_bits
<= 16 && LSMASH_IS_BOX_ADDITION_SUCCESS( isom_add_stz2( stbl
) ) )
1978 /* The sample size table can be compressed by using 'stz2'. */
1979 isom_stsz_t
*stsz
= stbl
->stsz
;
1980 isom_stz2_t
*stz2
= stbl
->stz2
;
1981 stz2
->sample_count
= stsz
->sample_count
;
1982 if( max_num_bits
<= 4 )
1983 stz2
->field_size
= 4;
1984 else if( max_num_bits
<= 8 )
1985 stz2
->field_size
= 8;
1987 stz2
->field_size
= 16;
1988 lsmash_list_move_entries( stz2
->list
, stsz
->list
);
1989 isom_remove_box_by_itself( stsz
);
1995 static int isom_add_dependency_type( isom_stbl_t
*stbl
, lsmash_file_t
*file
, lsmash_sample_property_t
*prop
)
1997 if( !file
->qt_compatible
&& !file
->avc_extensions
)
1999 int compatibility
= file
->avc_extensions
&& file
->qt_compatible
? 3
2000 : file
->qt_compatible
? 2
2001 : file
->avc_extensions
? 1
2003 if( LSMASH_IS_EXISTING_BOX( stbl
->sdtp
) )
2004 return isom_add_sdtp_entry( (isom_box_t
*)stbl
, prop
, compatibility
);
2005 /* no null check for prop */
2006 if( !prop
->allow_earlier
2008 && !prop
->independent
2009 && !prop
->disposable
2010 && !prop
->redundant
)
2012 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_sdtp( (isom_box_t
*)stbl
) ) )
2013 return LSMASH_ERR_NAMELESS
;
2014 uint32_t count
= isom_get_sample_count_from_sample_table( stbl
);
2015 /* fill past samples with ISOM_SAMPLE_*_UNKNOWN */
2016 lsmash_sample_property_t null_prop
= { 0 };
2017 for( uint32_t i
= 1; i
< count
; i
++ )
2019 int err
= isom_add_sdtp_entry( (isom_box_t
*)stbl
, &null_prop
, compatibility
);
2023 return isom_add_sdtp_entry( (isom_box_t
*)stbl
, prop
, compatibility
);
2026 void lsmash_initialize_media_parameters( lsmash_media_parameters_t
*param
)
2028 memset( param
, 0, sizeof(lsmash_media_parameters_t
) );
2029 param
->timescale
= 1;
2032 int lsmash_set_media_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_media_parameters_t
*param
)
2034 if( isom_check_initializer_present( root
) < 0 )
2035 return LSMASH_ERR_FUNCTION_PARAM
;
2036 lsmash_file_t
*file
= root
->file
;
2037 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2038 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
)
2039 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->stbl
) )
2040 return LSMASH_ERR_NAMELESS
;
2041 trak
->mdia
->mdhd
->timescale
= param
->timescale
;
2042 int err
= isom_set_media_language( file
, track_ID
, param
->ISO_language
, param
->MAC_language
);
2045 if( param
->media_handler_name
2046 && (err
= isom_set_media_handler_name( file
, track_ID
, param
->media_handler_name
)) < 0 )
2048 if( file
->qt_compatible
&& param
->data_handler_name
2049 && (err
= isom_set_data_handler_name( file
, track_ID
, param
->data_handler_name
)) < 0 )
2051 if( (file
->avc_extensions
|| file
->qt_compatible
) && param
->roll_grouping
2052 && (err
= isom_create_sample_grouping( trak
, ISOM_GROUP_TYPE_ROLL
)) < 0 )
2054 if( (file
->max_isom_version
>= 6) && param
->rap_grouping
2055 && (err
= isom_create_sample_grouping( trak
, ISOM_GROUP_TYPE_RAP
)) < 0 )
2057 if( !file
->qt_compatible
&& param
->compact_sample_size_table
)
2058 trak
->mdia
->minf
->stbl
->compress_sample_size_table
= isom_compress_sample_size_table
;
2059 if( !param
->no_sample_dependency_table
)
2060 trak
->mdia
->minf
->stbl
->add_dependency_type
= isom_add_dependency_type
;
2064 static uint32_t get_actual_handler_name_length( isom_hdlr_t
*hdlr
, lsmash_file_t
*file
)
2066 if( hdlr
->componentName_length
== 0 )
2070 if( file
->qt_compatible
)
2072 length
= LSMASH_MIN( hdlr
->componentName
[0], hdlr
->componentName_length
- 1 );
2073 if( !file
->isom_compatible
)
2075 name
= &hdlr
->componentName
[1];
2079 length
= hdlr
->componentName_length
;
2080 name
= hdlr
->componentName
;
2082 /* Considering fool-proof such as not terminated by '\0'. */
2084 while( i
< length
&& name
[i
] )
2089 int lsmash_get_media_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_media_parameters_t
*param
)
2091 if( isom_check_initializer_present( root
) < 0 )
2092 return LSMASH_ERR_FUNCTION_PARAM
;
2093 lsmash_file_t
*file
= root
->file
->initializer
;
2094 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2095 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
)
2096 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->hdlr
)
2097 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->stbl
) )
2098 return LSMASH_ERR_NAMELESS
;
2099 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
2100 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2101 param
->timescale
= mdhd
->timescale
;
2102 param
->handler_type
= trak
->mdia
->hdlr
->componentSubtype
;
2103 param
->duration
= mdhd
->duration
;
2104 /* Whether sample grouping present. */
2108 sbgp
= isom_get_sample_to_group ( stbl
, ISOM_GROUP_TYPE_RAP
);
2109 sgpd
= isom_get_sample_group_description( stbl
, ISOM_GROUP_TYPE_RAP
);
2110 param
->rap_grouping
= LSMASH_IS_EXISTING_BOX( sbgp
) && LSMASH_IS_EXISTING_BOX( sgpd
);
2111 sbgp
= isom_get_roll_recovery_sample_to_group ( &stbl
->sbgp_list
);
2112 sgpd
= isom_get_roll_recovery_sample_group_description( &stbl
->sgpd_list
);
2113 param
->roll_grouping
= LSMASH_IS_EXISTING_BOX( sbgp
) && LSMASH_IS_EXISTING_BOX( sgpd
);
2115 /* Get media language. */
2116 if( mdhd
->language
>= 0x800 )
2118 param
->MAC_language
= 0;
2119 param
->ISO_language
= mdhd
->language
;
2123 param
->MAC_language
= mdhd
->language
;
2124 param
->ISO_language
= 0;
2126 /* Get handler name(s). */
2127 isom_hdlr_t
*hdlr
= trak
->mdia
->hdlr
;
2128 uint32_t actual_length
= get_actual_handler_name_length( hdlr
, file
);
2129 uint32_t length
= LSMASH_MIN( 255, actual_length
);
2132 memcpy( param
->media_handler_name_shadow
, hdlr
->componentName
+ file
->qt_compatible
, length
);
2133 param
->media_handler_name_shadow
[length
] = '\0';
2134 param
->media_handler_name
= param
->media_handler_name_shadow
;
2138 param
->media_handler_name
= NULL
;
2139 memset( param
->media_handler_name_shadow
, 0, sizeof(param
->media_handler_name_shadow
) );
2141 if( LSMASH_IS_EXISTING_BOX( trak
->mdia
->minf
->hdlr
) )
2143 hdlr
= trak
->mdia
->minf
->hdlr
;
2144 actual_length
= get_actual_handler_name_length( hdlr
, file
);
2145 length
= LSMASH_MIN( 255, actual_length
);
2148 memcpy( param
->data_handler_name_shadow
, hdlr
->componentName
+ file
->qt_compatible
, length
);
2149 param
->data_handler_name_shadow
[length
] = '\0';
2150 param
->data_handler_name
= param
->data_handler_name_shadow
;
2154 param
->data_handler_name
= NULL
;
2155 memset( param
->data_handler_name_shadow
, 0, sizeof(param
->data_handler_name_shadow
) );
2160 param
->data_handler_name
= NULL
;
2161 memset( param
->data_handler_name_shadow
, 0, sizeof(param
->data_handler_name_shadow
) );
2163 param
->compact_sample_size_table
= LSMASH_IS_EXISTING_BOX( stbl
->stz2
);
2164 param
->no_sample_dependency_table
= LSMASH_IS_NON_EXISTING_BOX( stbl
->sdtp
);
2165 param
->reserved
[0] = param
->reserved
[1] = 0;
2169 /*---- movie manipulators ----*/
2171 void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t
*param
)
2173 memset( param
, 0, sizeof(lsmash_movie_parameters_t
) );
2174 param
->timescale
= 600;
2175 param
->playback_rate
= 0x00010000;
2176 param
->playback_volume
= 0x0100;
2179 int lsmash_set_movie_parameters( lsmash_root_t
*root
, lsmash_movie_parameters_t
*param
)
2181 if( LSMASH_IS_NON_EXISTING_BOX( root
) )
2182 return LSMASH_ERR_FUNCTION_PARAM
;
2183 lsmash_file_t
*file
= root
->file
;
2184 if( LSMASH_IS_NON_EXISTING_BOX( file
->moov
->mvhd
) )
2185 return LSMASH_ERR_NAMELESS
;
2186 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
2187 mvhd
->timescale
= param
->timescale
;
2188 if( file
->qt_compatible
|| file
->itunes_movie
)
2190 mvhd
->rate
= param
->playback_rate
;
2191 mvhd
->volume
= param
->playback_volume
;
2192 mvhd
->previewTime
= param
->preview_time
;
2193 mvhd
->previewDuration
= param
->preview_duration
;
2194 mvhd
->posterTime
= param
->poster_time
;
2198 mvhd
->rate
= 0x00010000;
2199 mvhd
->volume
= 0x0100;
2200 mvhd
->previewTime
= 0;
2201 mvhd
->previewDuration
= 0;
2202 mvhd
->posterTime
= 0;
2207 int lsmash_get_movie_parameters( lsmash_root_t
*root
, lsmash_movie_parameters_t
*param
)
2209 if( isom_check_initializer_present( root
) < 0 )
2210 return LSMASH_ERR_FUNCTION_PARAM
;
2211 lsmash_file_t
*file
= root
->file
->initializer
;
2212 if( LSMASH_IS_NON_EXISTING_BOX( file
->moov
->mvhd
) )
2213 return LSMASH_ERR_NAMELESS
;
2214 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
2215 param
->timescale
= mvhd
->timescale
;
2216 param
->duration
= mvhd
->duration
;
2217 param
->playback_rate
= mvhd
->rate
;
2218 param
->playback_volume
= mvhd
->volume
;
2219 param
->preview_time
= mvhd
->previewTime
;
2220 param
->preview_duration
= mvhd
->previewDuration
;
2221 param
->poster_time
= mvhd
->posterTime
;
2222 param
->number_of_tracks
= file
->moov
->trak_list
.entry_count
;
2226 uint32_t lsmash_get_movie_timescale( lsmash_root_t
*root
)
2228 if( isom_check_initializer_present( root
) < 0 )
2230 return root
->file
->initializer
->moov
->mvhd
->timescale
;
2233 int lsmash_reserve_media_data_size
2235 lsmash_root_t
*root
,
2236 uint64_t media_data_size
2239 if( isom_check_initializer_present( root
) < 0 )
2240 return LSMASH_ERR_FUNCTION_PARAM
;
2241 lsmash_file_t
*file
= root
->file
->initializer
;
2242 if( LSMASH_IS_EXISTING_BOX( file
->mdat
) /* whether the Media Data Box is already written or not */
2243 || file
->fragment
) /* For fragmented movies, this function makes no sense. */
2244 return LSMASH_ERR_NAMELESS
;
2245 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_mdat( file
) ) )
2246 return LSMASH_ERR_NAMELESS
;
2247 file
->mdat
->reserved_size
= media_data_size
;
2251 static int isom_scan_trak_profileLevelIndication
2254 mp4a_audioProfileLevelIndication
*audio_pli
,
2255 mp4sys_visualProfileLevelIndication
*visual_pli
2258 isom_stsd_t
*stsd
= trak
->mdia
->minf
->stbl
->stsd
;
2259 if( !stsd
->list
.head
)
2260 return LSMASH_ERR_INVALID_DATA
;
2261 for( lsmash_entry_t
*entry
= stsd
->list
.head
; entry
; entry
= entry
->next
)
2263 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)entry
->data
;
2264 if( LSMASH_IS_NON_EXISTING_BOX( sample_entry
) )
2265 return LSMASH_ERR_INVALID_DATA
;
2266 lsmash_codec_type_t sample_type
= sample_entry
->type
;
2267 if( LSMASH_IS_EXISTING_BOX( trak
->mdia
->minf
->vmhd
) )
2269 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC1_VIDEO
)
2270 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC2_VIDEO
)
2271 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC3_VIDEO
)
2272 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC4_VIDEO
)
2273 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVCP_VIDEO
)
2274 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_SVC1_VIDEO
)
2275 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MVC1_VIDEO
)
2276 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MVC2_VIDEO
) )
2278 /* FIXME: Do we have to arbitrate like audio? */
2279 if( *visual_pli
== MP4SYS_VISUAL_PLI_NONE_REQUIRED
)
2280 *visual_pli
= MP4SYS_VISUAL_PLI_H264_AVC
;
2283 *visual_pli
= MP4SYS_VISUAL_PLI_NOT_SPECIFIED
;
2285 else if( LSMASH_IS_EXISTING_BOX( trak
->mdia
->minf
->smhd
) )
2287 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4A_AUDIO
) )
2289 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)sample_entry
;
2290 isom_esds_t
*esds
= (isom_esds_t
*)isom_get_extension_box_format( &audio
->extensions
, ISOM_BOX_TYPE_ESDS
);
2291 if( LSMASH_IS_NON_EXISTING_BOX( esds
) || !esds
->ES
)
2292 return LSMASH_ERR_INVALID_DATA
;
2293 lsmash_audio_summary_t
*summary
= (lsmash_audio_summary_t
*)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO
);
2296 if( mp4sys_setup_summary_from_DecoderSpecificInfo( summary
, esds
->ES
) < 0 )
2297 *audio_pli
= MP4A_AUDIO_PLI_NOT_SPECIFIED
;
2299 *audio_pli
= mp4a_max_audioProfileLevelIndication( *audio_pli
, mp4a_get_audioProfileLevelIndication( summary
) );
2300 lsmash_cleanup_summary( (lsmash_summary_t
*)summary
);
2303 /* NOTE: Audio CODECs other than 'mp4a' does not have appropriate pli. */
2304 *audio_pli
= MP4A_AUDIO_PLI_NOT_SPECIFIED
;
2307 ; /* FIXME: Do we have to set OD_profileLevelIndication? */
2312 int isom_setup_iods( isom_moov_t
*moov
)
2314 if( LSMASH_IS_NON_EXISTING_BOX( moov
->iods
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_iods( moov
) ) )
2315 return LSMASH_ERR_NAMELESS
;
2316 isom_iods_t
*iods
= moov
->iods
;
2317 int err
= LSMASH_ERR_NAMELESS
;
2318 iods
->OD
= mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */
2321 mp4a_audioProfileLevelIndication audio_pli
= MP4A_AUDIO_PLI_NONE_REQUIRED
;
2322 mp4sys_visualProfileLevelIndication visual_pli
= MP4SYS_VISUAL_PLI_NONE_REQUIRED
;
2323 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
2325 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2326 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
2327 || LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
2329 if( (err
= isom_scan_trak_profileLevelIndication( trak
, &audio_pli
, &visual_pli
)) < 0 )
2331 if( (err
= mp4sys_create_ES_ID_Inc( iods
->OD
, trak
->tkhd
->track_ID
)) < 0 )
2334 if( (err
= mp4sys_to_InitialObjectDescriptor( iods
->OD
,
2335 0, /* FIXME: I'm not quite sure what the spec says. */
2336 MP4SYS_OD_PLI_NONE_REQUIRED
, MP4SYS_SCENE_PLI_NONE_REQUIRED
,
2337 audio_pli
, visual_pli
,
2338 MP4SYS_GRAPHICS_PLI_NONE_REQUIRED
)) < 0 )
2342 isom_remove_box_by_itself( iods
);
2346 int lsmash_create_object_descriptor( lsmash_root_t
*root
)
2348 if( isom_check_initializer_present( root
) < 0 )
2349 return LSMASH_ERR_FUNCTION_PARAM
;
2350 lsmash_file_t
*file
= root
->file
;
2351 /* Return error if this file is not compatible with MP4 file format. */
2352 if( !file
->mp4_version1
2353 && !file
->mp4_version2
)
2354 return LSMASH_ERR_FUNCTION_PARAM
;
2355 return isom_setup_iods( file
->moov
);
2358 /*---- finishing functions ----*/
2360 int isom_complement_data_reference( isom_minf_t
*minf
)
2362 if( LSMASH_IS_NON_EXISTING_BOX( minf
->dinf
->dref
) )
2363 return LSMASH_ERR_INVALID_DATA
;
2364 /* Complement data referece if absent. */
2365 if( !minf
->dinf
->dref
->list
.head
)
2367 isom_dref_entry_t
*url
= isom_add_dref_entry( minf
->dinf
->dref
, ISOM_BOX_TYPE_URL
);
2368 if( LSMASH_IS_NON_EXISTING_BOX( url
) )
2369 return LSMASH_ERR_NAMELESS
;
2370 url
->flags
= 0x000001; /* Media data is in the same file. */
2375 static lsmash_file_t
*isom_get_written_media_file
2378 uint32_t sample_description_index
2381 isom_minf_t
*minf
= trak
->mdia
->minf
;
2382 isom_sample_entry_t
*description
= (isom_sample_entry_t
*)lsmash_list_get_entry_data( &minf
->stbl
->stsd
->list
, sample_description_index
);
2383 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 );
2384 lsmash_file_t
*file
= (!dref_entry
|| LSMASH_IS_NON_EXISTING_BOX( dref_entry
->ref_file
)) ? trak
->file
: dref_entry
->ref_file
;
2385 if( !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
2386 || !(file
->flags
& LSMASH_FILE_MODE_WRITE
) )
2391 int isom_check_large_offset_requirement
2397 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; )
2399 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2400 isom_stco_t
*stco
= trak
->mdia
->minf
->stbl
->stco
;
2401 if( !stco
->list
->tail
/* no samples */
2402 || stco
->large_presentation
2403 || (((isom_stco_entry_t
*)stco
->list
->tail
->data
)->chunk_offset
+ moov
->size
+ meta_size
) <= UINT32_MAX
)
2405 entry
= entry
->next
;
2406 continue; /* no need to convert stco into co64 */
2408 /* stco->co64 conversion */
2409 int err
= isom_convert_stco_to_co64( trak
->mdia
->minf
->stbl
);
2412 if( isom_update_box_size( moov
) == 0 )
2413 return LSMASH_ERR_INVALID_DATA
;
2414 entry
= moov
->trak_list
.head
; /* whenever any conversion, re-check all traks */
2419 void isom_add_preceding_box_size
2422 uint64_t preceding_size
2425 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
2427 /* Apply to the chunks in the same file. */
2428 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2429 isom_stsc_t
*stsc
= trak
->mdia
->minf
->stbl
->stsc
;
2430 isom_stco_t
*stco
= trak
->mdia
->minf
->stbl
->stco
;
2431 lsmash_entry_t
*stsc_entry
= stsc
->list
->head
;
2432 isom_stsc_entry_t
*stsc_data
= stsc_entry
? (isom_stsc_entry_t
*)stsc_entry
->data
: NULL
;
2433 uint32_t chunk_number
= 1;
2434 for( lsmash_entry_t
*stco_entry
= stco
->list
->head
; stco_entry
; )
2437 && stsc_data
->first_chunk
== chunk_number
)
2439 lsmash_file_t
*ref_file
= isom_get_written_media_file( trak
, stsc_data
->sample_description_index
);
2440 stsc_entry
= stsc_entry
->next
;
2441 stsc_data
= stsc_entry
? (isom_stsc_entry_t
*)stsc_entry
->data
: NULL
;
2442 if( ref_file
!= trak
->file
)
2444 /* The chunks are not contained in the same file. Skip applying the offset.
2445 * If no more stsc entries, the rest of the chunks is not contained in the same file. */
2446 if( !stsc_entry
|| !stsc_data
)
2448 while( stco_entry
&& chunk_number
< stsc_data
->first_chunk
)
2450 stco_entry
= stco_entry
->next
;
2456 if( stco
->large_presentation
)
2457 ((isom_co64_entry_t
*)stco_entry
->data
)->chunk_offset
+= preceding_size
;
2459 ((isom_stco_entry_t
*)stco_entry
->data
)->chunk_offset
+= preceding_size
;
2460 stco_entry
= stco_entry
->next
;
2466 int isom_establish_movie( lsmash_file_t
*file
)
2468 assert( file
== file
->initializer
);
2470 if( (err
= isom_check_mandatory_boxes( file
)) < 0
2471 || (err
= isom_set_movie_creation_time( file
)) < 0 )
2473 if( isom_update_box_size( file
->moov
) == 0 )
2474 return LSMASH_ERR_INVALID_DATA
;
2478 int lsmash_finish_movie
2480 lsmash_root_t
*root
,
2481 lsmash_adhoc_remux_t
*remux
2484 if( isom_check_initializer_present( root
) < 0 )
2485 return LSMASH_ERR_FUNCTION_PARAM
;
2486 lsmash_file_t
*file
= root
->file
;
2488 || LSMASH_IS_NON_EXISTING_BOX( file
->initializer
->moov
) )
2489 return LSMASH_ERR_INVALID_DATA
;
2490 if( file
->fragment
)
2491 return isom_finish_final_fragment_movie( file
, remux
);
2492 if( file
!= file
->initializer
)
2493 return LSMASH_ERR_INVALID_DATA
;
2495 isom_moov_t
*moov
= file
->moov
;
2496 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
2498 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2499 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
2500 || LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
)
2502 || !trak
->mdia
->minf
->stbl
->stsd
->list
.head
2503 || !trak
->mdia
->minf
->stbl
->stsd
->list
.head
->data
2504 || !trak
->mdia
->minf
->stbl
->stco
->list
2505 || !trak
->mdia
->minf
->stbl
->stco
->list
->tail
)
2506 return LSMASH_ERR_INVALID_DATA
;
2507 if( (err
= isom_complement_data_reference( trak
->mdia
->minf
)) < 0 )
2509 uint32_t track_ID
= trak
->tkhd
->track_ID
;
2510 uint32_t related_track_ID
= trak
->related_track_ID
;
2511 /* Disable the track if the track is a track reference chapter. */
2512 if( trak
->is_chapter
)
2513 trak
->tkhd
->flags
&= ~ISOM_TRACK_ENABLED
;
2514 if( trak
->is_chapter
&& related_track_ID
)
2516 /* In order that the track duration of the chapter track doesn't exceed that of the related track. */
2518 edit
.duration
= LSMASH_MIN( trak
->tkhd
->duration
, lsmash_get_track_duration( root
, related_track_ID
) );
2519 edit
.start_time
= 0;
2520 edit
.rate
= ISOM_EDIT_MODE_NORMAL
;
2521 if( (err
= lsmash_create_explicit_timeline_map( root
, track_ID
, edit
)) < 0 )
2524 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2525 /* Compress sample size table. */
2526 if( stbl
->compress_sample_size_table
2527 && (err
= stbl
->compress_sample_size_table( stbl
)) < 0 )
2529 /* Add stss box if any samples aren't sync sample. */
2530 if( !trak
->cache
->all_sync
&& !stbl
->stss
&& !isom_add_stss( stbl
) )
2531 return LSMASH_ERR_NAMELESS
;
2532 if( (err
= isom_update_tkhd_duration( trak
)) < 0
2533 || (err
= isom_update_bitrate_description( trak
->mdia
)) < 0 )
2536 if( file
->mp4_version1
== 1 && (err
= isom_setup_iods( moov
)) < 0 )
2538 if( (err
= isom_establish_movie( file
)) < 0 )
2540 /* Write the size of Media Data Box here. */
2541 lsmash_bs_t
*bs
= file
->bs
;
2542 file
->mdat
->manager
&= ~LSMASH_INCOMPLETE_BOX
;
2543 if( (err
= isom_write_box( bs
, (isom_box_t
*)file
->mdat
)) < 0 )
2545 /* Write the Movie Box and a Meta Box if no optimization for progressive download. */
2546 uint64_t meta_size
= LSMASH_IS_EXISTING_BOX( file
->meta
) ? file
->meta
->size
: 0;
2549 if( (err
= isom_write_box( bs
, (isom_box_t
*)file
->moov
)) < 0
2550 || (err
= isom_write_box( bs
, (isom_box_t
*)file
->meta
)) < 0 )
2552 file
->size
+= moov
->size
+ meta_size
;
2555 /* stco->co64 conversion, depending on last chunk's offset */
2556 if( (err
= isom_check_large_offset_requirement( moov
, meta_size
)) < 0 )
2558 /* now the amount of offset is fixed. */
2559 uint64_t mtf_size
= moov
->size
+ meta_size
; /* sum of size of boxes moved to front */
2560 /* buffer size must be at least mtf_size * 2 */
2561 remux
->buffer_size
= LSMASH_MAX( remux
->buffer_size
, mtf_size
* 2 );
2562 /* Split to 2 buffers. */
2563 uint8_t *buf
[2] = { NULL
, NULL
};
2564 if( (buf
[0] = (uint8_t*)lsmash_malloc( remux
->buffer_size
)) == NULL
)
2565 return LSMASH_ERR_MEMORY_ALLOC
; /* NOTE: I think we still can fallback to "return isom_write_moov();" here. */
2566 size_t size
= remux
->buffer_size
/ 2;
2567 buf
[1] = buf
[0] + size
;
2568 /* Now, the amount of the offset is fixed. apply it to stco/co64 */
2569 isom_add_preceding_box_size( moov
, mtf_size
);
2570 /* Backup starting area of mdat and write moov + meta there instead. */
2571 isom_mdat_t
*mdat
= file
->mdat
;
2572 uint64_t total
= file
->size
+ mtf_size
;
2573 uint64_t placeholder_pos
= mdat
->pos
;
2574 if( (err
= lsmash_bs_write_seek( bs
, placeholder_pos
, SEEK_SET
)) < 0 )
2576 size_t read_num
= size
;
2577 lsmash_bs_read_data( bs
, buf
[0], &read_num
);
2578 uint64_t read_pos
= bs
->offset
;
2579 /* Write moov + meta there instead. */
2580 if( (err
= lsmash_bs_write_seek( bs
, placeholder_pos
, SEEK_SET
)) < 0
2581 || (err
= isom_write_box( bs
, (isom_box_t
*)file
->moov
)) < 0
2582 || (err
= isom_write_box( bs
, (isom_box_t
*)file
->meta
)) < 0 )
2584 uint64_t write_pos
= bs
->offset
;
2585 /* Update the positions */
2586 mdat
->pos
+= mtf_size
;
2587 /* Move Media Data Box. */
2588 if( (err
= isom_rearrange_data( file
, remux
, buf
, read_num
, size
, read_pos
, write_pos
, total
)) < 0 )
2590 file
->size
+= mtf_size
;
2591 lsmash_free( buf
[0] );
2594 lsmash_free( buf
[0] );
2598 int lsmash_set_last_sample_delta( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t sample_delta
)
2600 if( isom_check_initializer_present( root
) < 0 || track_ID
== 0 )
2601 return LSMASH_ERR_FUNCTION_PARAM
;
2602 lsmash_file_t
*file
= root
->file
;
2604 && file
->fragment
->movie
)
2606 isom_traf_t
*traf
= isom_get_traf( file
->fragment
->movie
, track_ID
);
2607 if( LSMASH_IS_NON_EXISTING_BOX( traf
)
2608 || LSMASH_IS_NON_EXISTING_BOX( traf
->tfhd
)
2610 return LSMASH_ERR_NAMELESS
;
2611 return isom_set_fragment_last_duration( traf
, sample_delta
);
2613 if( file
!= file
->initializer
)
2614 return LSMASH_ERR_INVALID_DATA
;
2615 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2616 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
)
2617 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->stbl
->stsd
)
2618 || (LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->stbl
->stsz
)
2619 && LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->stbl
->stz2
))
2621 || !trak
->mdia
->minf
->stbl
->stts
->list
)
2622 return LSMASH_ERR_NAMELESS
;
2623 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2624 isom_stts_t
*stts
= stbl
->stts
;
2625 uint32_t sample_count
= isom_get_sample_count( trak
);
2627 if( !stts
->list
->tail
)
2629 if( sample_count
== 0 )
2630 return 0; /* no samples */
2631 if( sample_count
> 1 )
2632 return LSMASH_ERR_INVALID_DATA
; /* irregular sample_count */
2633 /* Set the duration of the first sample.
2634 * This duration is also the duration of the last sample. */
2635 if( (err
= isom_add_stts_entry( stbl
, sample_delta
)) < 0 )
2637 return lsmash_update_track_duration( root
, track_ID
, 0 );
2640 for( lsmash_entry_t
*entry
= stts
->list
->head
; entry
; entry
= entry
->next
)
2641 i
+= ((isom_stts_entry_t
*)entry
->data
)->sample_count
;
2642 if( sample_count
< i
)
2643 return LSMASH_ERR_INVALID_DATA
;
2644 int no_last
= (sample_count
> i
);
2645 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)stts
->list
->tail
->data
;
2646 if( !last_stts_data
)
2647 return LSMASH_ERR_INVALID_DATA
;
2648 /* Consider QuikcTime fixed compression audio. */
2649 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)lsmash_list_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
,
2650 trak
->cache
->chunk
.sample_description_index
);
2651 if( LSMASH_IS_NON_EXISTING_BOX( audio
) )
2652 return LSMASH_ERR_INVALID_DATA
;
2653 if( (audio
->manager
& LSMASH_AUDIO_DESCRIPTION
)
2654 && (audio
->manager
& LSMASH_QTFF_BASE
)
2655 && (audio
->version
== 1)
2656 && (audio
->compression_ID
!= QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION
) )
2658 if( audio
->samplesPerPacket
== 0 )
2659 return LSMASH_ERR_INVALID_DATA
;
2660 int exclude_last_sample
= no_last
? 0 : 1;
2661 uint32_t j
= audio
->samplesPerPacket
;
2662 for( lsmash_entry_t
*entry
= stts
->list
->tail
; entry
&& j
> 1; entry
= entry
->prev
)
2664 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)entry
->data
;
2666 return LSMASH_ERR_INVALID_DATA
;
2667 for( uint32_t k
= exclude_last_sample
; k
< stts_data
->sample_count
&& j
> 1; k
++ )
2669 sample_delta
-= stts_data
->sample_delta
;
2672 exclude_last_sample
= 0;
2675 /* Set sample_delta. */
2678 /* The duration of the last sample is not set yet. */
2679 if( sample_count
- i
> 1 )
2680 return LSMASH_ERR_INVALID_DATA
;
2681 /* Add a sample_delta. */
2682 if( sample_delta
== last_stts_data
->sample_delta
)
2683 ++ last_stts_data
->sample_count
;
2684 else if( (err
= isom_add_stts_entry( stbl
, sample_delta
)) < 0 )
2687 /* The duration of the last sample is already set. Replace it with a new one. */
2688 else if( (err
= isom_replace_last_sample_delta( stbl
, sample_delta
)) < 0 )
2690 return lsmash_update_track_duration( root
, track_ID
, sample_delta
);
2693 /*---- timeline manipulator ----*/
2695 int lsmash_modify_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t edit_number
, lsmash_edit_t edit
)
2697 if( isom_check_initializer_present( root
) < 0
2698 || edit
.start_time
< -1 )
2699 return LSMASH_ERR_FUNCTION_PARAM
;
2700 lsmash_file_t
*file
= root
->file
->initializer
;
2701 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2702 if( !trak
->edts
->elst
->list
)
2703 return LSMASH_ERR_NAMELESS
;
2704 isom_elst_t
*elst
= trak
->edts
->elst
;
2705 isom_elst_entry_t
*data
= (isom_elst_entry_t
*)lsmash_list_get_entry_data( elst
->list
, edit_number
);
2707 return LSMASH_ERR_NAMELESS
;
2708 data
->segment_duration
= edit
.duration
;
2709 data
->media_time
= edit
.start_time
;
2710 data
->media_rate
= edit
.rate
;
2711 if( elst
->pos
== 0 || !file
->fragment
|| file
->bs
->unseekable
)
2712 return isom_update_tkhd_duration( trak
);
2713 /* Rewrite the specified entry.
2714 * Note: we don't update the version of the Edit List Box. */
2715 lsmash_bs_t
*bs
= file
->bs
;
2716 uint64_t current_pos
= bs
->offset
;
2717 uint64_t entry_pos
= elst
->pos
+ ISOM_LIST_FULLBOX_COMMON_SIZE
+ ((uint64_t)edit_number
- 1) * (elst
->version
== 1 ? 20 : 12);
2718 lsmash_bs_write_seek( bs
, entry_pos
, SEEK_SET
);
2721 lsmash_bs_put_be64( bs
, data
->segment_duration
);
2722 lsmash_bs_put_be64( bs
, data
->media_time
);
2726 lsmash_bs_put_be32( bs
, (uint32_t)LSMASH_MIN( data
->segment_duration
, UINT32_MAX
) );
2727 lsmash_bs_put_be32( bs
, (uint32_t)data
->media_time
);
2729 lsmash_bs_put_be32( bs
, data
->media_rate
);
2730 int ret
= lsmash_bs_flush_buffer( bs
);
2731 lsmash_bs_write_seek( bs
, current_pos
, SEEK_SET
);
2735 int lsmash_create_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_edit_t edit
)
2737 if( isom_check_initializer_present( root
) < 0 || edit
.start_time
< -1 )
2738 return LSMASH_ERR_FUNCTION_PARAM
;
2739 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
2740 if( LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
2741 return LSMASH_ERR_NAMELESS
;
2742 edit
.duration
= (edit
.duration
|| root
->file
->fragment
) ? edit
.duration
2743 : trak
->tkhd
->duration
? trak
->tkhd
->duration
2744 : isom_update_tkhd_duration( trak
) < 0 ? 0
2745 : trak
->tkhd
->duration
;
2746 if( (LSMASH_IS_NON_EXISTING_BOX( trak
->edts
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_edts( trak
) ))
2747 || (LSMASH_IS_NON_EXISTING_BOX( trak
->edts
->elst
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_elst( trak
->edts
) )) )
2748 return LSMASH_ERR_NAMELESS
;
2749 int err
= isom_add_elst_entry( trak
->edts
->elst
, edit
.duration
, edit
.start_time
, edit
.rate
);
2752 return isom_update_tkhd_duration( trak
);
2755 int lsmash_get_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t edit_number
, lsmash_edit_t
*edit
)
2757 if( isom_check_initializer_present( root
) < 0 || !edit
)
2758 return LSMASH_ERR_FUNCTION_PARAM
;
2759 isom_elst_entry_t
*data
;
2760 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2761 if( LSMASH_IS_NON_EXISTING_BOX( trak
) )
2762 data
= isom_timelime_get_explicit_timeline_map( root
, track_ID
, edit_number
);
2765 if( LSMASH_IS_NON_EXISTING_BOX( trak
->edts
->elst
) )
2769 edit
->start_time
= 0;
2773 data
= (isom_elst_entry_t
*)lsmash_list_get_entry_data( trak
->edts
->elst
->list
, edit_number
);
2776 return LSMASH_ERR_NAMELESS
;
2777 edit
->duration
= data
->segment_duration
;
2778 edit
->start_time
= data
->media_time
;
2779 edit
->rate
= data
->media_rate
;
2783 uint32_t lsmash_count_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
)
2785 if( isom_check_initializer_present( root
) < 0 )
2786 return LSMASH_ERR_FUNCTION_PARAM
;
2787 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2788 if( LSMASH_IS_NON_EXISTING_BOX( trak
) )
2789 return isom_timelime_count_explicit_timeline_map( root
, track_ID
);
2791 return trak
->edts
->elst
->list
? trak
->edts
->elst
->list
->entry_count
: 0;
2794 /*---- create / modification time fields manipulators ----*/
2796 int lsmash_update_media_modification_time( lsmash_root_t
*root
, uint32_t track_ID
)
2798 if( isom_check_initializer_present( root
) < 0 )
2799 return LSMASH_ERR_FUNCTION_PARAM
;
2800 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2801 if( LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
) )
2802 return LSMASH_ERR_NAMELESS
;
2803 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
2804 mdhd
->modification_time
= isom_get_current_mp4time();
2805 /* overwrite strange creation_time */
2806 if( mdhd
->creation_time
> mdhd
->modification_time
)
2807 mdhd
->creation_time
= mdhd
->modification_time
;
2811 int lsmash_update_track_modification_time( lsmash_root_t
*root
, uint32_t track_ID
)
2813 if( isom_check_initializer_present( root
) < 0 )
2814 return LSMASH_ERR_FUNCTION_PARAM
;
2815 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2816 if( LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
) )
2817 return LSMASH_ERR_NAMELESS
;
2818 isom_tkhd_t
*tkhd
= trak
->tkhd
;
2819 tkhd
->modification_time
= isom_get_current_mp4time();
2820 /* overwrite strange creation_time */
2821 if( tkhd
->creation_time
> tkhd
->modification_time
)
2822 tkhd
->creation_time
= tkhd
->modification_time
;
2826 int lsmash_update_movie_modification_time( lsmash_root_t
*root
)
2828 if( isom_check_initializer_present( root
) < 0 )
2829 return LSMASH_ERR_FUNCTION_PARAM
;
2830 lsmash_file_t
*file
= root
->file
->initializer
;
2831 if( LSMASH_IS_NON_EXISTING_BOX( file
->moov
->mvhd
) )
2832 return LSMASH_ERR_INVALID_DATA
;
2833 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
2834 mvhd
->modification_time
= isom_get_current_mp4time();
2835 /* overwrite strange creation_time */
2836 if( mvhd
->creation_time
> mvhd
->modification_time
)
2837 mvhd
->creation_time
= mvhd
->modification_time
;
2841 /*---- sample manipulators ----*/
2842 lsmash_sample_t
*lsmash_create_sample( uint32_t size
)
2844 lsmash_sample_t
*sample
= lsmash_malloc_zero( sizeof(lsmash_sample_t
) );
2849 sample
->data
= lsmash_malloc( size
);
2852 lsmash_free( sample
);
2855 sample
->length
= size
;
2859 int lsmash_sample_alloc( lsmash_sample_t
*sample
, uint32_t size
)
2862 return LSMASH_ERR_FUNCTION_PARAM
;
2865 lsmash_free( sample
->data
);
2866 sample
->data
= NULL
;
2870 if( size
== sample
->length
)
2874 data
= lsmash_malloc( size
);
2876 data
= lsmash_realloc( sample
->data
, size
);
2878 return LSMASH_ERR_MEMORY_ALLOC
;
2879 sample
->data
= data
;
2880 sample
->length
= size
;
2884 void lsmash_delete_sample( lsmash_sample_t
*sample
)
2888 lsmash_free( sample
->data
);
2889 lsmash_free( sample
);
2892 isom_sample_pool_t
*isom_create_sample_pool( uint64_t size
)
2894 isom_sample_pool_t
*pool
= lsmash_malloc_zero( sizeof(isom_sample_pool_t
) );
2899 pool
->data
= lsmash_malloc( size
);
2902 lsmash_free( pool
);
2909 void isom_remove_sample_pool( isom_sample_pool_t
*pool
)
2913 lsmash_free( pool
->data
);
2914 lsmash_free( pool
);
2917 static uint32_t isom_add_size( isom_stbl_t
*stbl
, uint32_t sample_size
)
2919 if( isom_add_stsz_entry( stbl
, sample_size
) < 0 )
2921 return isom_get_sample_count_from_sample_table( stbl
);
2924 static uint32_t isom_add_dts( isom_stbl_t
*stbl
, uint64_t dts
, uint64_t prev_dts
)
2926 isom_stts_t
*stts
= stbl
->stts
;
2927 if( stts
->list
->entry_count
== 0 )
2928 return isom_add_stts_entry( stbl
, dts
) < 0 ? 0 : dts
;
2929 if( dts
<= prev_dts
)
2931 uint32_t sample_delta
= dts
- prev_dts
;
2932 isom_stts_entry_t
*data
= (isom_stts_entry_t
*)stts
->list
->tail
->data
;
2933 if( data
->sample_delta
== sample_delta
)
2934 ++ data
->sample_count
;
2935 else if( isom_add_stts_entry( stbl
, sample_delta
) < 0 )
2937 return sample_delta
;
2940 /* Add ctts box and the first ctts entry. */
2941 static int isom_add_initial_sample_offset( isom_stbl_t
*stbl
, uint32_t sample_offset
)
2943 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_ctts( stbl
) ) )
2944 return LSMASH_ERR_NAMELESS
;
2945 if( sample_offset
== ISOM_NON_OUTPUT_SAMPLE_OFFSET
)
2946 stbl
->ctts
->version
= 1;
2947 uint32_t sample_count
= isom_get_sample_count_from_sample_table( stbl
);
2948 if( sample_count
> 1 )
2950 /* Set all prior samples' sample_offset to 0. */
2951 int err
= isom_add_ctts_entry( stbl
, sample_count
- 1, 0 );
2955 return isom_add_ctts_entry( stbl
, 1, sample_offset
);
2958 static int isom_add_sample_offset( isom_stbl_t
*stbl
, uint32_t sample_offset
)
2960 if( !stbl
->ctts
->list
)
2961 return LSMASH_ERR_INVALID_DATA
;
2962 isom_ctts_entry_t
*data
= (isom_ctts_entry_t
*)stbl
->ctts
->list
->tail
->data
;
2963 if( data
->sample_offset
== sample_offset
)
2964 ++ data
->sample_count
;
2967 int err
= isom_add_ctts_entry( stbl
, 1, sample_offset
);
2974 static int isom_add_cts( isom_stbl_t
*stbl
, uint64_t dts
, uint64_t cts
, int non_output_sample
)
2976 uint32_t sample_offset
= !non_output_sample
? cts
- dts
: ISOM_NON_OUTPUT_SAMPLE_OFFSET
;
2977 if( LSMASH_IS_EXISTING_BOX( stbl
->ctts
) )
2978 return isom_add_sample_offset( stbl
, sample_offset
);
2979 return sample_offset
!= 0 ? isom_add_initial_sample_offset( stbl
, sample_offset
) : 0;
2982 static int isom_check_sample_offset_compatibility( lsmash_file_t
*file
, uint64_t dts
, uint64_t cts
, int non_output_sample
)
2984 if( non_output_sample
)
2986 if( file
->min_isom_version
< 4 )
2987 return LSMASH_ERR_INVALID_DATA
; /* Non-output sample can be indicated under 'iso4' or later brands. */
2991 if( file
->isom_compatible
&& file
->qt_compatible
&& (((cts
>= dts
) ? (cts
- dts
) : (dts
- cts
)) > INT32_MAX
) )
2992 return LSMASH_ERR_INVALID_DATA
; /* sample_offset is not compatible with both ISOBMFF and QTFF. */
2994 if( non_output_sample
|| cts
< dts
)
2996 /* Negative sample offset is required. */
2997 if( file
->max_isom_version
< 4 && !file
->qt_compatible
)
2998 return LSMASH_ERR_INVALID_DATA
; /* Negative sample offset is not supported in both ISOBMFF and QTFF. */
2999 if( file
->max_isom_version
>= 4 && file
->qt_compatible
)
3000 return LSMASH_ERR_INVALID_DATA
; /* ctts version 1 is not defined in QTFF. */
3005 void isom_update_cache_timestamp
3007 isom_cache_t
*cache
,
3011 uint32_t sample_duration
,
3012 int non_output_sample
3015 cache
->timestamp
.dts
= dts
;
3016 cache
->timestamp
.cts
= non_output_sample
? cache
->timestamp
.cts
: cts
;
3017 cache
->timestamp
.ctd_shift
= ctd_shift
;
3018 if( cache
->fragment
)
3020 cache
->fragment
->last_duration
= sample_duration
;
3021 if( !non_output_sample
)
3022 cache
->fragment
->largest_cts
3023 = cache
->fragment
->largest_cts
!= LSMASH_TIMESTAMP_UNDEFINED
3024 ? LSMASH_MAX( cache
->timestamp
.cts
, cache
->fragment
->largest_cts
)
3025 : cache
->timestamp
.cts
;
3029 static int isom_add_timestamp( isom_stbl_t
*stbl
, isom_cache_t
*cache
, lsmash_file_t
*file
, uint64_t dts
, uint64_t cts
)
3031 if( !cache
|| !stbl
->stts
->list
)
3032 return LSMASH_ERR_INVALID_DATA
;
3033 int non_output_sample
= (cts
== LSMASH_TIMESTAMP_UNDEFINED
);
3034 int err
= isom_check_sample_offset_compatibility( file
, dts
, cts
, non_output_sample
);
3037 uint32_t sample_count
= isom_get_sample_count_from_sample_table( stbl
);
3038 uint32_t sample_delta
= sample_count
> 1 ? isom_add_dts( stbl
, dts
, cache
->timestamp
.dts
) : 0;
3039 if( sample_count
> 1 && sample_delta
== 0 )
3040 return LSMASH_ERR_INVALID_DATA
;
3041 if( (err
= isom_add_cts( stbl
, dts
, cts
, non_output_sample
)) < 0 )
3043 int32_t ctd_shift
= cache
->timestamp
.ctd_shift
;
3044 if( !non_output_sample
&& ((cts
+ ctd_shift
) < dts
) )
3046 /* Check overflow of composition to decode timeline shift. */
3047 if( (dts
- cts
) > INT32_MAX
)
3048 return LSMASH_ERR_INVALID_DATA
;
3049 assert( LSMASH_IS_EXISTING_BOX( stbl
->ctts
) );
3050 if( stbl
->ctts
->version
== 0 && !file
->qt_compatible
)
3051 stbl
->ctts
->version
= 1;
3052 ctd_shift
= dts
- cts
;
3054 isom_update_cache_timestamp( cache
, dts
, cts
, ctd_shift
, sample_delta
, non_output_sample
);
3058 static int isom_add_sync_point( isom_stbl_t
*stbl
, isom_cache_t
*cache
, uint32_t sample_number
, lsmash_sample_property_t
*prop
)
3060 if( !(prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
) ) /* no null check for prop */
3062 if( !cache
->all_sync
)
3064 if( LSMASH_IS_NON_EXISTING_BOX( stbl
->stss
)
3065 && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stss( stbl
) ) )
3066 return LSMASH_ERR_NAMELESS
;
3067 int err
= isom_add_stss_entry( stbl
, 1 );
3068 if( err
< 0 ) /* Declare here the first sample is a sync sample. */
3070 cache
->all_sync
= 0;
3073 if( cache
->all_sync
) /* We don't need stss box if all samples are sync sample. */
3075 if( LSMASH_IS_NON_EXISTING_BOX( stbl
->stss
) )
3077 if( isom_get_sample_count_from_sample_table( stbl
) == 1 )
3079 cache
->all_sync
= 1; /* Also the first sample is a sync sample. */
3082 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stss( stbl
) ) )
3083 return LSMASH_ERR_NAMELESS
;
3085 return isom_add_stss_entry( stbl
, sample_number
);
3088 static int isom_add_partial_sync( isom_stbl_t
*stbl
, lsmash_file_t
*file
, uint32_t sample_number
, lsmash_sample_property_t
*prop
)
3090 if( !file
->qt_compatible
)
3092 if( !(prop
->ra_flags
& QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
) )
3094 /* This sample is a partial sync sample. */
3095 if( LSMASH_IS_NON_EXISTING_BOX( stbl
->stps
)
3096 && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stps( stbl
) ) )
3097 return LSMASH_ERR_NAMELESS
;
3098 return isom_add_stps_entry( stbl
, sample_number
);
3101 int isom_rap_grouping_established( isom_rap_group_t
*group
, int num_leading_samples_known
, isom_sgpd_t
*sgpd
, int is_fragment
)
3103 isom_rap_entry_t
*rap
= group
->random_access
;
3106 assert( rap
== (isom_rap_entry_t
*)sgpd
->list
->tail
->data
);
3107 rap
->num_leading_samples_known
= num_leading_samples_known
;
3108 /* Avoid duplication of sample group descriptions. */
3109 uint32_t group_description_index
= is_fragment
? 0x10001 : 1;
3110 for( lsmash_entry_t
*entry
= sgpd
->list
->head
; entry
!= sgpd
->list
->tail
; entry
= entry
->next
)
3112 isom_rap_entry_t
*data
= (isom_rap_entry_t
*)entry
->data
;
3114 return LSMASH_ERR_INVALID_DATA
;
3115 if( rap
->num_leading_samples_known
== data
->num_leading_samples_known
3116 && rap
->num_leading_samples
== data
->num_leading_samples
)
3118 /* The same description already exists.
3119 * Remove the latest random access entry. */
3120 lsmash_list_remove_entry_tail( sgpd
->list
);
3121 /* Replace assigned group_description_index with the one corresponding the same description. */
3122 if( group
->assignment
->group_description_index
== 0 )
3124 /* We don't create consecutive sample groups not assigned to 'rap '.
3125 * So the previous sample group shall be a group of 'rap ' if any. */
3126 if( group
->prev_assignment
)
3128 assert( group
->prev_assignment
->group_description_index
);
3129 group
->prev_assignment
->group_description_index
= group_description_index
;
3133 group
->assignment
->group_description_index
= group_description_index
;
3136 ++group_description_index
;
3138 group
->random_access
= NULL
;
3142 int isom_group_random_access( isom_box_t
*parent
, isom_cache_t
*cache
, lsmash_sample_t
*sample
)
3144 if( parent
->file
->max_isom_version
< 6 )
3148 uint32_t sample_count
;
3150 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_STBL
) )
3152 isom_stbl_t
*stbl
= (isom_stbl_t
*)parent
;
3153 sbgp
= isom_get_sample_to_group ( stbl
, ISOM_GROUP_TYPE_RAP
);
3154 sgpd
= isom_get_sample_group_description( stbl
, ISOM_GROUP_TYPE_RAP
);
3155 sample_count
= isom_get_sample_count_from_sample_table( stbl
);
3158 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
3160 isom_traf_t
*traf
= (isom_traf_t
*)parent
;
3161 sbgp
= isom_get_fragment_sample_to_group ( traf
, ISOM_GROUP_TYPE_RAP
);
3162 sgpd
= isom_get_fragment_sample_group_description( traf
, ISOM_GROUP_TYPE_RAP
);
3163 sample_count
= cache
->fragment
->sample_count
+ 1; /* Cached sample_count is incremented later in isom_fragment_update_cache(). */
3169 sbgp
= isom_non_existing_sbgp();
3170 sgpd
= isom_non_existing_sgpd();
3171 /* redundant initializations to suppress warnings from unclever compilers */
3175 if( LSMASH_IS_NON_EXISTING_BOX( sbgp
)
3176 || LSMASH_IS_NON_EXISTING_BOX( sgpd
) )
3178 lsmash_sample_property_t
*prop
= &sample
->prop
;
3179 uint8_t is_rap
= (prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
)
3180 || (prop
->ra_flags
& QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
)
3181 || (prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
)
3182 || (LSMASH_IS_POST_ROLL_START( prop
->ra_flags
) && prop
->post_roll
.identifier
== prop
->post_roll
.complete
);
3183 isom_rap_group_t
*group
= cache
->rap
;
3186 /* This sample is the first sample, create a grouping cache. */
3187 assert( sample_count
== 1 );
3188 group
= lsmash_malloc( sizeof(isom_rap_group_t
) );
3190 return LSMASH_ERR_MEMORY_ALLOC
;
3193 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3194 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3198 /* The first sample is not always a random access point. */
3199 group
->random_access
= NULL
;
3200 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3202 if( !group
->assignment
)
3204 lsmash_free( group
);
3205 return LSMASH_ERR_MEMORY_ALLOC
;
3207 group
->prev_assignment
= NULL
;
3208 group
->is_prev_rap
= is_rap
;
3213 if( group
->is_prev_rap
)
3215 /* OK. here, the previous sample is a menber of 'rap '. */
3218 /* This sample isn't a member of 'rap ' and the previous sample is.
3219 * So we create a new group and set 0 on its group_description_index. */
3220 group
->prev_assignment
= group
->assignment
;
3221 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3222 if( !group
->assignment
)
3224 lsmash_free( group
);
3225 return LSMASH_ERR_MEMORY_ALLOC
;
3228 else if( !LSMASH_IS_CLOSED_RAP( prop
->ra_flags
) )
3230 /* Create a new group since there is the possibility the next sample is a leading sample.
3231 * This sample is a member of 'rap ', so we set appropriate value on its group_description_index. */
3232 if( (err
= isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
)) < 0 )
3234 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3235 group
->prev_assignment
= group
->assignment
;
3236 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3237 if( !group
->assignment
)
3239 lsmash_free( group
);
3240 return LSMASH_ERR_MEMORY_ALLOC
;
3243 else /* The previous and current sample are a member of 'rap ', and the next sample must not be a leading sample. */
3244 ++ group
->assignment
->sample_count
;
3248 /* This sample is a member of 'rap ' and the previous sample isn't.
3249 * So we create a new group and set appropriate value on its group_description_index. */
3250 if( (err
= isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
)) < 0 )
3252 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3253 group
->prev_assignment
= group
->assignment
;
3254 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3255 if( !group
->assignment
)
3257 lsmash_free( group
);
3258 return LSMASH_ERR_MEMORY_ALLOC
;
3261 else /* The previous and current sample aren't a member of 'rap '. */
3262 ++ group
->assignment
->sample_count
;
3263 /* Obtain the property of the latest random access point group. */
3264 if( !is_rap
&& group
->random_access
)
3266 if( prop
->leading
== ISOM_SAMPLE_LEADING_UNKNOWN
)
3268 /* We can no longer know num_leading_samples in this group. */
3269 if( (err
= isom_rap_grouping_established( group
, 0, sgpd
, is_fragment
)) < 0 )
3274 if( prop
->leading
== ISOM_SAMPLE_IS_UNDECODABLE_LEADING
3275 || prop
->leading
== ISOM_SAMPLE_IS_DECODABLE_LEADING
)
3276 ++ group
->random_access
->num_leading_samples
;
3277 /* no more consecutive leading samples in this group */
3278 else if( (err
= isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
)) < 0 )
3282 group
->is_prev_rap
= is_rap
;
3286 static int isom_roll_grouping_established( isom_roll_group_t
*group
)
3288 /* Avoid duplication of sample group descriptions. */
3289 isom_sgpd_t
*sgpd
= group
->sgpd
;
3290 uint32_t group_description_index
= group
->is_fragment
? 0x10001 : 1;
3291 for( lsmash_entry_t
*entry
= sgpd
->list
->head
; entry
; entry
= entry
->next
)
3293 isom_roll_entry_t
*data
= (isom_roll_entry_t
*)entry
->data
;
3295 return LSMASH_ERR_INVALID_DATA
;
3296 if( group
->roll_distance
== data
->roll_distance
)
3298 /* The same description already exists.
3299 * Set the group_description_index corresponding the same description. */
3300 group
->assignment
->group_description_index
= group_description_index
;
3303 ++group_description_index
;
3305 /* Add a new roll recovery description. */
3306 if( !isom_add_roll_group_entry( sgpd
, group
->roll_distance
) )
3307 return LSMASH_ERR_MEMORY_ALLOC
;
3308 group
->assignment
->group_description_index
= sgpd
->list
->entry_count
+ (group
->is_fragment
? 0x10000 : 0);
3312 static int isom_deduplicate_roll_group( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3315 uint32_t current_group_number
= sbgp
->list
->entry_count
- pool
->entry_count
+ 1;
3316 isom_group_assignment_entry_t
*prev_assignment
= (isom_group_assignment_entry_t
*)lsmash_list_get_entry_data( sbgp
->list
, current_group_number
- 1 );
3317 for( lsmash_entry_t
*entry
= pool
->head
; entry
; )
3319 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3321 || !group
->assignment
)
3322 return LSMASH_ERR_INVALID_DATA
;
3323 if( !group
->delimited
|| group
->described
!= ROLL_DISTANCE_DETERMINED
)
3325 if( prev_assignment
&& prev_assignment
->group_description_index
== group
->assignment
->group_description_index
)
3327 /* Merge the current group with the previous. */
3328 lsmash_entry_t
*next_entry
= entry
->next
;
3329 prev_assignment
->sample_count
+= group
->assignment
->sample_count
;
3331 if( (err
= lsmash_list_remove_entry( sbgp
->list
, current_group_number
)) < 0
3332 || (err
= lsmash_list_remove_entry_direct( pool
, entry
)) < 0 )
3338 entry
= entry
->next
;
3339 prev_assignment
= group
->assignment
;
3340 ++current_group_number
;
3346 /* Remove pooled caches that has become unnecessary. */
3347 static int isom_clean_roll_pool( lsmash_entry_list_t
*pool
)
3349 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= pool
->head
)
3351 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3353 return LSMASH_ERR_INVALID_DATA
;
3354 if( !group
->delimited
|| group
->described
!= ROLL_DISTANCE_DETERMINED
)
3356 int err
= lsmash_list_remove_entry_direct( pool
, entry
);
3363 static int isom_flush_roll_pool( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3366 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3368 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3370 return LSMASH_ERR_INVALID_DATA
;
3371 if( group
->delimited
3372 && group
->described
== ROLL_DISTANCE_DETERMINED
3373 && group
->roll_distance
!= 0
3374 && (err
= isom_roll_grouping_established( group
)) < 0 )
3377 if( (err
= isom_deduplicate_roll_group( sbgp
, pool
)) < 0 )
3379 return isom_clean_roll_pool( pool
);
3382 static int isom_all_recovery_described( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3384 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3386 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3388 return LSMASH_ERR_INVALID_DATA
;
3389 group
->described
= ROLL_DISTANCE_DETERMINED
;
3391 return isom_flush_roll_pool( sbgp
, pool
);
3394 int isom_all_recovery_completed( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3396 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3398 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3400 return LSMASH_ERR_INVALID_DATA
;
3401 group
->described
= ROLL_DISTANCE_DETERMINED
;
3402 group
->delimited
= 1;
3404 return isom_flush_roll_pool( sbgp
, pool
);
3407 static isom_roll_entry_t
*isom_get_roll_description
3409 isom_roll_group_t
*group
3412 uint32_t group_description_index
= group
->assignment
->group_description_index
;
3413 if( group_description_index
&& group
->is_fragment
)
3415 assert( group_description_index
> 0x10000 );
3416 group_description_index
-= 0x10000;
3418 return (isom_roll_entry_t
*)lsmash_list_get_entry_data( group
->sgpd
->list
, group_description_index
);
3421 int isom_group_roll_recovery( isom_box_t
*parent
, isom_cache_t
*cache
, lsmash_sample_t
*sample
)
3423 if( !parent
->file
->avc_extensions
3424 && !parent
->file
->qt_compatible
)
3426 uint32_t sample_count
;
3428 lsmash_entry_list_t
*sbgp_list
;
3429 lsmash_entry_list_t
*sgpd_list
;
3430 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_STBL
) )
3432 isom_stbl_t
*stbl
= (isom_stbl_t
*)parent
;
3433 sbgp_list
= &stbl
->sbgp_list
;
3434 sgpd_list
= &stbl
->sgpd_list
;
3435 sample_count
= isom_get_sample_count_from_sample_table( stbl
);
3438 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
3440 if( parent
->file
->max_isom_version
< 6 )
3442 isom_traf_t
*traf
= (isom_traf_t
*)parent
;
3443 sbgp_list
= &traf
->sbgp_list
;
3444 sgpd_list
= &traf
->sgpd_list
;
3445 sample_count
= cache
->fragment
->sample_count
+ 1; /* Cached sample_count is incremented later in isom_fragment_update_cache(). */
3451 return LSMASH_ERR_INVALID_DATA
;
3453 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group ( sbgp_list
);
3454 isom_sgpd_t
*sgpd
= isom_get_roll_recovery_sample_group_description( sgpd_list
);
3455 if( LSMASH_IS_NON_EXISTING_BOX( sbgp
)
3456 || LSMASH_IS_NON_EXISTING_BOX( sgpd
)
3457 || sbgp
->grouping_type
!= sgpd
->grouping_type
)
3459 /* Check if 'roll' -> 'prol' conversion is needed. */
3461 && sbgp
->grouping_type
== ISOM_GROUP_TYPE_ROLL
3462 && !(sample
->prop
.ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
) )
3464 /* Since not every samples is a sync sample, change grouping_type into 'prol'. */
3465 sbgp
->grouping_type
= ISOM_GROUP_TYPE_PROL
;
3466 sgpd
->grouping_type
= ISOM_GROUP_TYPE_PROL
;
3468 lsmash_entry_list_t
*pool
= cache
->roll
.pool
;
3471 pool
= lsmash_list_create_simple();
3473 return LSMASH_ERR_MEMORY_ALLOC
;
3474 cache
->roll
.pool
= pool
;
3476 lsmash_sample_property_t
*prop
= &sample
->prop
;
3477 isom_roll_group_t
*group
= (isom_roll_group_t
*)lsmash_list_get_entry_data( pool
, pool
->entry_count
);
3478 int is_recovery_start
= LSMASH_IS_POST_ROLL_START( prop
->ra_flags
);
3479 int valid_pre_roll
= !is_recovery_start
3480 && (prop
->ra_flags
!= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
)
3481 && (prop
->pre_roll
.distance
> 0)
3482 && (prop
->pre_roll
.distance
<= -INT16_MIN
);
3483 int new_group
= !group
|| is_recovery_start
|| (group
->prev_is_recovery_start
!= is_recovery_start
);
3486 /* Check pre-roll distance. */
3487 assert( group
->assignment
&& group
->sgpd
);
3488 isom_roll_entry_t
*prev_roll
= isom_get_roll_description( group
);
3490 new_group
= valid_pre_roll
;
3491 else if( !valid_pre_roll
|| (prop
->pre_roll
.distance
!= -prev_roll
->roll_distance
) )
3492 /* Pre-roll distance is different from the previous. */
3498 group
->delimited
= 1;
3500 assert( sample_count
== 1 );
3501 /* Create a new group. */
3502 group
= lsmash_malloc_zero( sizeof(isom_roll_group_t
) );
3504 return LSMASH_ERR_MEMORY_ALLOC
;
3506 group
->prev_is_recovery_start
= is_recovery_start
;
3507 group
->is_fragment
= is_fragment
;
3508 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3509 if( !group
->assignment
|| lsmash_list_add_entry( pool
, group
) < 0 )
3511 lsmash_free( group
);
3512 return LSMASH_ERR_MEMORY_ALLOC
;
3514 if( is_recovery_start
)
3516 /* a member of non-roll or post-roll group */
3517 group
->first_sample
= sample_count
;
3518 group
->recovery_point
= prop
->post_roll
.complete
;
3522 group
->described
= ROLL_DISTANCE_DETERMINED
;
3523 if( valid_pre_roll
)
3525 /* a member of pre-roll group */
3526 group
->roll_distance
= -(signed)prop
->pre_roll
.distance
;
3527 int err
= isom_roll_grouping_established( group
);
3532 /* a member of non-roll group */
3533 group
->roll_distance
= 0;
3538 group
->prev_is_recovery_start
= is_recovery_start
;
3539 group
->assignment
->sample_count
+= 1;
3541 /* If encountered a RAP, all recovery is completed here. */
3542 if( prop
->ra_flags
& (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
3543 | ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
3544 | QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
) )
3545 return isom_all_recovery_described( sbgp
, pool
);
3546 /* Check whether this sample is a random access recovery point or not. */
3547 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3549 group
= (isom_roll_group_t
*)entry
->data
;
3551 return LSMASH_ERR_INVALID_DATA
;
3552 if( group
->described
== ROLL_DISTANCE_DETERMINED
)
3554 if( group
->described
== ROLL_DISTANCE_INITIALIZED
)
3556 /* Let's consider the following picture sequence.
3557 * coded order : P[0] P[1] P[2] P[3] P[4] P[5]
3560 * Here, P[0] conveys a recovery point SEI and P[3] is the recovery point.
3561 * Correctness of decoded pictures is specified by recovery point in output order for both AVC and HEVC.
3562 * Therefore, as follows,
3563 * output order : P[0] P[2] P[1] P[5]|P[3] P[4]
3564 * ---(incorrect?)--->|
3565 * there is no guarantee that P[5] is decoded and output correctly.
3566 * From this, it can be said that the roll_distance of this sequence is equal to 5. */
3567 isom_roll_entry_t
*post_roll
= isom_get_roll_description( group
);
3568 if( post_roll
&& post_roll
->roll_distance
> 0 )
3570 if( sample
->cts
!= LSMASH_TIMESTAMP_UNDEFINED
3571 && group
->rp_cts
!= LSMASH_TIMESTAMP_UNDEFINED
3572 && group
->rp_cts
> sample
->cts
)
3573 /* Updated roll_distance for composition reordering. */
3574 post_roll
->roll_distance
= sample_count
- group
->first_sample
;
3575 if( ++ group
->wait_and_see_count
>= MAX_ROLL_WAIT_AND_SEE_COUNT
)
3576 group
->described
= ROLL_DISTANCE_DETERMINED
;
3579 else if( prop
->post_roll
.identifier
== group
->recovery_point
)
3581 int16_t distance
= sample_count
- group
->first_sample
;
3582 group
->rp_cts
= sample
->cts
;
3583 group
->roll_distance
= distance
;
3584 /* Add a roll recovery entry only when roll_distance isn't zero since roll_distance = 0 must not be used. */
3587 /* Now, this group is a 'roll'.
3588 * The roll_distance may be updated later because of composition reordering. */
3589 group
->described
= ROLL_DISTANCE_INITIALIZED
;
3590 group
->wait_and_see_count
= 0;
3591 /* All groups with uninitialized roll_distance before the current group are described. */
3592 lsmash_entry_t
*current
= entry
;
3593 for( entry
= pool
->head
; entry
!= current
; entry
= entry
->next
)
3595 group
= (isom_roll_group_t
*)entry
->data
;
3596 if( !group
|| group
->described
!= ROLL_DISTANCE_INITIALIZED
)
3598 group
->described
= ROLL_DISTANCE_DETERMINED
;
3600 /* Cache the mark of the first recovery point in a subsegment. */
3602 && cache
->fragment
->subsegment
.first_rp_number
== 0 )
3603 cache
->fragment
->subsegment
.is_first_recovery_point
= 1;
3606 /* Random Accessible Point */
3607 return isom_all_recovery_described( sbgp
, pool
);
3610 return isom_flush_roll_pool( sbgp
, pool
);
3613 static int isom_update_chunk_tables
3616 lsmash_file_t
*media_file
,
3617 isom_chunk_t
*current
3620 isom_stsc_entry_t
*last_stsc_data
= stbl
->stsc
->list
->tail
? (isom_stsc_entry_t
*)stbl
->stsc
->list
->tail
->data
: NULL
;
3621 /* Create a new chunk sequence in this track if needed. */
3623 if( (!last_stsc_data
3624 || current
->pool
->sample_count
!= last_stsc_data
->samples_per_chunk
3625 || current
->sample_description_index
!= last_stsc_data
->sample_description_index
)
3626 && (err
= isom_add_stsc_entry( stbl
, current
->chunk_number
,
3627 current
->pool
->sample_count
,
3628 current
->sample_description_index
)) < 0 )
3630 /* Add a new chunk offset in this track. */
3631 uint64_t offset
= media_file
->size
;
3632 if( media_file
->fragment
)
3633 offset
+= ISOM_BASEBOX_COMMON_SIZE
+ media_file
->fragment
->pool_size
;
3634 return isom_add_stco_entry( stbl
, offset
);
3637 /* This function decides to put a give sample on the current chunk or the next new one.
3638 * Returns 1 if pooled samples must be flushed.
3639 * FIXME: I wonder if this function should have a extra argument which indicates force_to_flush_cached_chunk.
3640 * see lsmash_append_sample for detail. */
3641 static int isom_add_sample_to_chunk
3644 lsmash_sample_t
*sample
3647 if( LSMASH_IS_NON_EXISTING_BOX( trak
->file
)
3648 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->mdhd
)
3649 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->dinf
->dref
)
3650 || LSMASH_IS_NON_EXISTING_BOX( trak
->mdia
->minf
->stbl
->stsd
)
3652 || trak
->mdia
->mdhd
->timescale
== 0
3653 || !trak
->mdia
->minf
->stbl
->stsc
->list
)
3654 return LSMASH_ERR_INVALID_DATA
;
3655 isom_chunk_t
*current
= &trak
->cache
->chunk
;
3656 if( !current
->pool
)
3658 /* Very initial settings, just once per track */
3659 current
->pool
= isom_create_sample_pool( 0 );
3660 if( !current
->pool
)
3661 return LSMASH_ERR_MEMORY_ALLOC
;
3663 if( current
->pool
->sample_count
== 0 )
3665 /* Cannot decide whether we should flush the current sample or not here yet. */
3666 current
->chunk_number
+= 1;
3667 current
->sample_description_index
= sample
->index
;
3668 current
->first_dts
= sample
->dts
;
3671 if( sample
->dts
< current
->first_dts
)
3672 return LSMASH_ERR_INVALID_DATA
; /* easy error check. */
3673 lsmash_file_t
*media_file
= isom_get_written_media_file( trak
, current
->sample_description_index
);
3674 if( (current
->sample_description_index
== sample
->index
)
3675 && (media_file
->max_chunk_duration
>= ((double)(sample
->dts
- current
->first_dts
) / trak
->mdia
->mdhd
->timescale
))
3676 && (media_file
->max_chunk_size
>= current
->pool
->size
+ sample
->length
) )
3677 return 0; /* No need to flush current cached chunk, the current sample must be put into that. */
3678 /* NOTE: chunk relative stuff must be pushed into file after a chunk is fully determined with its contents.
3679 * Now the current cached chunk is fixed, actually add the chunk relative properties to its file accordingly. */
3680 int err
= isom_update_chunk_tables( trak
->mdia
->minf
->stbl
, media_file
, current
);
3683 /* Update and re-initialize cache, using the current sample */
3684 current
->chunk_number
+= 1;
3685 current
->sample_description_index
= sample
->index
;
3686 current
->first_dts
= sample
->dts
;
3687 /* current->pool must be flushed in isom_append_sample_internal() */
3691 static int isom_write_pooled_samples( lsmash_file_t
*file
, isom_sample_pool_t
*pool
)
3693 if( LSMASH_IS_NON_EXISTING_BOX( file
)
3695 || !file
->bs
->stream
3696 || !(file
->flags
& LSMASH_FILE_MODE_WRITE
)
3697 || !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
3698 || ((file
->flags
& LSMASH_FILE_MODE_BOX
) && LSMASH_IS_NON_EXISTING_BOX( file
->mdat
)) )
3699 return LSMASH_ERR_INVALID_DATA
;
3700 lsmash_bs_put_bytes( file
->bs
, pool
->size
, pool
->data
);
3701 int err
= lsmash_bs_flush_buffer( file
->bs
);
3704 if( LSMASH_IS_EXISTING_BOX( file
->mdat
) )
3705 file
->mdat
->media_size
+= pool
->size
;
3706 file
->size
+= pool
->size
;
3707 pool
->sample_count
= 0;
3712 int isom_update_sample_tables
3715 lsmash_sample_t
*sample
,
3716 uint32_t *samples_per_packet
,
3717 isom_sample_entry_t
*sample_entry
3721 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)sample_entry
;
3722 if( (audio
->manager
& LSMASH_AUDIO_DESCRIPTION
)
3723 && (audio
->manager
& LSMASH_QTFF_BASE
)
3724 && (audio
->version
== 1)
3725 && (audio
->compression_ID
!= QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION
) )
3727 /* Add entries of the sample table for each uncompressed sample. */
3728 uint64_t sample_duration
= trak
->mdia
->mdhd
->timescale
/ (audio
->samplerate
>> 16);
3729 if( audio
->samplesPerPacket
== 0 || sample_duration
== 0 || sample
->cts
== LSMASH_TIMESTAMP_UNDEFINED
)
3730 return LSMASH_ERR_INVALID_DATA
;
3731 uint64_t sample_dts
= sample
->dts
;
3732 uint64_t sample_cts
= sample
->cts
;
3733 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3734 for( uint32_t i
= 0; i
< audio
->samplesPerPacket
; i
++ )
3736 /* Add a size of uncomressed audio and increment sample_count.
3737 * This points to individual uncompressed audio samples, each one byte in size, within the compressed frames. */
3738 uint32_t sample_count
= isom_add_size( stbl
, 1 );
3739 if( sample_count
== 0 )
3740 return LSMASH_ERR_NAMELESS
;
3741 /* Add a decoding timestamp and a composition timestamp. */
3742 if( (err
= isom_add_timestamp( stbl
, trak
->cache
, trak
->file
, sample_dts
, sample_cts
)) < 0 )
3744 sample_dts
+= sample_duration
;
3745 sample_cts
+= sample_duration
;
3747 *samples_per_packet
= audio
->samplesPerPacket
;
3751 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3752 /* Add a sample_size and increment sample_count. */
3753 uint32_t sample_count
= isom_add_size( stbl
, sample
->length
);
3754 if( sample_count
== 0 )
3755 return LSMASH_ERR_NAMELESS
;
3756 /* Add a decoding timestamp and a composition timestamp. */
3757 if( (err
= isom_add_timestamp( stbl
, trak
->cache
, trak
->file
, sample
->dts
, sample
->cts
)) < 0 )
3759 /* Add a sync point if needed. */
3760 if( (err
= isom_add_sync_point( stbl
, trak
->cache
, sample_count
, &sample
->prop
)) < 0 )
3762 /* Add a partial sync point if needed. */
3763 if( (err
= isom_add_partial_sync( stbl
, trak
->file
, sample_count
, &sample
->prop
)) < 0 )
3765 /* Add leading, independent, disposable and redundant information if needed. */
3766 if( stbl
->add_dependency_type
3767 && (err
= stbl
->add_dependency_type( stbl
, trak
->file
, &sample
->prop
)) < 0 )
3769 /* Group samples into random access point type if needed. */
3770 if( (err
= isom_group_random_access( (isom_box_t
*)stbl
, trak
->cache
, sample
)) < 0 )
3772 /* Group samples into random access recovery point type if needed. */
3773 if( (err
= isom_group_roll_recovery( (isom_box_t
*)stbl
, trak
->cache
, sample
)) < 0 )
3775 *samples_per_packet
= 1;
3777 /* Add a chunk if needed. */
3778 return isom_add_sample_to_chunk( trak
, sample
);
3781 static int isom_output_cached_chunk( isom_trak_t
*trak
)
3783 isom_chunk_t
*chunk
= &trak
->cache
->chunk
;
3784 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3785 isom_stsc_entry_t
*last_stsc_data
= stbl
->stsc
->list
->tail
? (isom_stsc_entry_t
*)stbl
->stsc
->list
->tail
->data
: NULL
;
3786 /* Create a new chunk sequence in this track if needed. */
3788 if( (!last_stsc_data
3789 || chunk
->pool
->sample_count
!= last_stsc_data
->samples_per_chunk
3790 || chunk
->sample_description_index
!= last_stsc_data
->sample_description_index
)
3791 && (err
= isom_add_stsc_entry( stbl
, chunk
->chunk_number
,
3792 chunk
->pool
->sample_count
,
3793 chunk
->sample_description_index
)) < 0 )
3795 lsmash_file_t
*file
= isom_get_written_media_file( trak
, chunk
->sample_description_index
);
3796 if( file
->fragment
)
3798 /* Add a new chunk offset in this track. */
3799 if( (err
= isom_add_stco_entry( stbl
, file
->size
+ ISOM_BASEBOX_COMMON_SIZE
+ file
->fragment
->pool_size
)) < 0 )
3801 return isom_append_fragment_track_run( file
, chunk
);
3803 /* Add a new chunk offset in this track. */
3804 if( (err
= isom_add_stco_entry( stbl
, file
->size
)) < 0 )
3806 /* Output pooled samples in this track. */
3807 return isom_write_pooled_samples( file
, chunk
->pool
);
3810 int isom_pool_sample( isom_sample_pool_t
*pool
, lsmash_sample_t
*sample
, uint32_t samples_per_packet
)
3812 uint64_t pool_size
= pool
->size
+ sample
->length
;
3813 if( pool
->alloc
< pool_size
)
3816 uint64_t alloc
= pool_size
+ (1<<16);
3818 data
= lsmash_malloc( alloc
);
3820 data
= lsmash_realloc( pool
->data
, alloc
);
3822 return LSMASH_ERR_MEMORY_ALLOC
;
3824 pool
->alloc
= alloc
;
3826 memcpy( pool
->data
+ pool
->size
, sample
->data
, sample
->length
);
3827 pool
->size
= pool_size
;
3828 pool
->sample_count
+= samples_per_packet
;
3829 lsmash_delete_sample( sample
);
3833 static int isom_append_sample_internal
3836 lsmash_sample_t
*sample
,
3837 isom_sample_entry_t
*sample_entry
3840 uint32_t samples_per_packet
;
3841 int ret
= isom_update_sample_tables( trak
, sample
, &samples_per_packet
, sample_entry
);
3844 /* ret == 1 means pooled samples must be flushed. */
3845 isom_sample_pool_t
*current_pool
= trak
->cache
->chunk
.pool
;
3848 /* The sample_description_index in the cache is one of the next written chunk.
3849 * Therefore, it cannot be referenced here. */
3850 lsmash_entry_list_t
*stsc_list
= trak
->mdia
->minf
->stbl
->stsc
->list
;
3851 isom_stsc_entry_t
*last_stsc_data
= (isom_stsc_entry_t
*)stsc_list
->tail
->data
;
3852 lsmash_file_t
*file
= isom_get_written_media_file( trak
, last_stsc_data
->sample_description_index
);
3853 if( (ret
= isom_write_pooled_samples( file
, current_pool
)) < 0 )
3856 /* Arbitration system between tracks with extremely scattering dts.
3857 * Here, we check whether asynchronization between the tracks exceeds the tolerance.
3858 * If a track has too old "first DTS" in its cached chunk than current sample's DTS, then its pooled samples must be flushed.
3859 * We don't consider presentation of media since any edit can pick an arbitrary portion of media in track.
3860 * Note: you needn't read this loop until you grasp the basic handling of chunks. */
3861 lsmash_file_t
*file
= trak
->file
;
3862 double tolerance
= file
->max_async_tolerance
;
3863 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
3865 isom_trak_t
*other
= (isom_trak_t
*)entry
->data
;
3868 if( LSMASH_IS_NON_EXISTING_BOX( other
)
3869 || LSMASH_IS_NON_EXISTING_BOX( other
->mdia
->mdhd
)
3871 || other
->mdia
->mdhd
->timescale
== 0
3872 || !other
->mdia
->minf
->stbl
->stsc
->list
)
3873 return LSMASH_ERR_INVALID_DATA
;
3874 isom_chunk_t
*chunk
= &other
->cache
->chunk
;
3875 if( !chunk
->pool
|| chunk
->pool
->sample_count
== 0 )
3877 double diff
= ((double)sample
->dts
/ trak
->mdia
->mdhd
->timescale
)
3878 - ((double)chunk
->first_dts
/ other
->mdia
->mdhd
->timescale
);
3879 if( diff
> tolerance
&& (ret
= isom_output_cached_chunk( other
)) < 0 )
3881 /* Note: we don't flush the cached chunk in the current track and the current sample here
3882 * even if the conditional expression of '-diff > tolerance' meets.
3883 * That's useless because appending a sample to another track would be a good equivalent.
3884 * It's even harmful because it causes excess chunk division by calling
3885 * isom_output_cached_chunk() which always generates a new chunk.
3886 * Anyway some excess chunk division will be there, but rather less without it.
3887 * To completely avoid this, we need to observe at least whether the current sample will be placed
3888 * right next to the previous chunk of the same track or not. */
3890 /* anyway the current sample must be pooled. */
3891 return isom_pool_sample( current_pool
, sample
, samples_per_packet
);
3894 int isom_append_sample_by_type
3897 lsmash_sample_t
*sample
,
3898 isom_sample_entry_t
*sample_entry
,
3899 int (*func_append_sample
)( void *, lsmash_sample_t
*, isom_sample_entry_t
* )
3902 if( isom_is_lpcm_audio( sample_entry
) )
3904 uint32_t frame_size
= ((isom_audio_entry_t
*)sample_entry
)->constBytesPerAudioPacket
;
3905 if( sample
->length
== frame_size
)
3906 return func_append_sample( track
, sample
, sample_entry
);
3907 else if( sample
->length
< frame_size
|| sample
->cts
== LSMASH_TIMESTAMP_UNDEFINED
)
3908 return LSMASH_ERR_INVALID_DATA
;
3909 /* Append samples splitted into each LPCMFrame. */
3910 uint64_t dts
= sample
->dts
;
3911 uint64_t cts
= sample
->cts
;
3912 for( uint32_t offset
= 0; offset
< sample
->length
; offset
+= frame_size
)
3914 lsmash_sample_t
*lpcm_sample
= lsmash_create_sample( frame_size
);
3916 return LSMASH_ERR_MEMORY_ALLOC
;
3917 memcpy( lpcm_sample
->data
, sample
->data
+ offset
, frame_size
);
3918 lpcm_sample
->dts
= dts
++;
3919 lpcm_sample
->cts
= cts
++;
3920 lpcm_sample
->prop
= sample
->prop
;
3921 lpcm_sample
->index
= sample
->index
;
3922 int err
= func_append_sample( track
, lpcm_sample
, sample_entry
);
3925 lsmash_delete_sample( lpcm_sample
);
3929 lsmash_delete_sample( sample
);
3932 else if( lsmash_check_codec_type_identical( sample_entry
->type
, ISOM_CODEC_TYPE_RTP_HINT
)
3933 || lsmash_check_codec_type_identical( sample_entry
->type
, ISOM_CODEC_TYPE_RRTP_HINT
) )
3935 /* calculate PDU statistics for hmhd box.
3936 * It requires accessing sample data to get the number of packets per sample. */
3937 isom_trak_t
*trak
= (isom_trak_t
*)track
;
3938 isom_hmhd_t
*hmhd
= trak
->mdia
->minf
->hmhd
;
3939 uint16_t packetcount
= LSMASH_GET_BE16( sample
->data
);
3940 uint8_t *data
= sample
->data
+ RTP_SAMPLE_HEADER_SIZE
+ RTP_PACKET_SIZE
;
3941 /* Calculate only packet headers and packet payloads sizes int PDU size. Later use these two to get avgPDUsize. */
3942 hmhd
->combinedPDUsize
+= sample
->length
- (packetcount
* RTP_CONSTRUCTOR_SIZE
) - RTP_SAMPLE_HEADER_SIZE
;
3943 hmhd
->PDUcount
+= packetcount
;
3944 for( unsigned int i
= 0; i
< packetcount
; i
++ )
3946 /* constructor type */
3950 uint16_t length
= *(data
+ 2);
3951 /* Check if this packet is larger than any of the previous ones. */
3952 hmhd
->maxPDUsize
= hmhd
->maxPDUsize
> length
+ RTP_HEADER_SIZE
? hmhd
->maxPDUsize
: length
+ RTP_HEADER_SIZE
;
3953 data
+= RTP_CONSTRUCTOR_SIZE
+ RTP_PACKET_SIZE
;
3954 } /* TODO: other constructor types */
3956 } /* TODO: add other hint tracks that have a hmhd box */
3957 return func_append_sample( track
, sample
, sample_entry
);
3960 /* This function is for non-fragmented movie. */
3961 static int isom_append_sample
3963 lsmash_file_t
*file
,
3965 lsmash_sample_t
*sample
,
3966 isom_sample_entry_t
*sample_entry
3969 /* If there is no available Media Data Box to write samples, add and write a new one before any chunk offset is decided. */
3971 int mdat_absent
= LSMASH_IS_NON_EXISTING_BOX( file
->mdat
);
3972 if( mdat_absent
|| !(file
->mdat
->manager
& LSMASH_INCOMPLETE_BOX
) )
3974 if( mdat_absent
&& LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_mdat( file
) ) )
3975 return LSMASH_ERR_NAMELESS
;
3976 file
->mdat
->manager
|= LSMASH_PLACEHOLDER
;
3977 if( (err
= isom_write_box( file
->bs
, (isom_box_t
*)file
->mdat
)) < 0 )
3979 file
->size
+= file
->mdat
->size
;
3981 return isom_append_sample_by_type( trak
, sample
, sample_entry
, (int (*)( void *, lsmash_sample_t
*, isom_sample_entry_t
* ))isom_append_sample_internal
);
3984 static int isom_output_cache( isom_trak_t
*trak
)
3987 isom_cache_t
*cache
= trak
->cache
;
3988 if( cache
->chunk
.pool
3989 && cache
->chunk
.pool
->sample_count
3990 && (err
= isom_output_cached_chunk( trak
)) < 0 )
3992 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3993 for( lsmash_entry_t
*entry
= stbl
->sgpd_list
.head
; entry
; entry
= entry
->next
)
3995 isom_sgpd_t
*sgpd
= (isom_sgpd_t
*)entry
->data
;
3996 if( LSMASH_IS_NON_EXISTING_BOX( sgpd
) )
3997 return LSMASH_ERR_INVALID_DATA
;
3998 switch( sgpd
->grouping_type
)
4000 case ISOM_GROUP_TYPE_RAP
:
4002 isom_rap_group_t
*group
= cache
->rap
;
4005 if( stbl
->file
->fragment
)
4008 return LSMASH_ERR_NAMELESS
;
4010 if( !group
->random_access
)
4012 group
->random_access
->num_leading_samples_known
= 1;
4015 case ISOM_GROUP_TYPE_ROLL
:
4016 case ISOM_GROUP_TYPE_PROL
:
4017 if( !cache
->roll
.pool
)
4019 if( stbl
->file
->fragment
)
4022 return LSMASH_ERR_NAMELESS
;
4024 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group( &stbl
->sbgp_list
);
4025 if( LSMASH_IS_NON_EXISTING_BOX( sbgp
) )
4026 return LSMASH_ERR_NAMELESS
;
4027 if( (err
= isom_all_recovery_completed( sbgp
, cache
->roll
.pool
)) < 0 )
4037 int lsmash_flush_pooled_samples( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t last_sample_delta
)
4039 if( isom_check_initializer_present( root
) < 0 )
4040 return LSMASH_ERR_FUNCTION_PARAM
;
4041 lsmash_file_t
*file
= root
->file
;
4043 && file
->fragment
->movie
)
4044 return isom_flush_fragment_pooled_samples( file
, track_ID
, last_sample_delta
);
4045 if( file
!= file
->initializer
)
4046 return LSMASH_ERR_INVALID_DATA
;
4047 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
4048 if( LSMASH_IS_NON_EXISTING_BOX( trak
)
4050 || !trak
->mdia
->minf
->stbl
->stsc
->list
)
4051 return LSMASH_ERR_NAMELESS
;
4052 int err
= isom_output_cache( trak
);
4055 return lsmash_set_last_sample_delta( root
, track_ID
, last_sample_delta
);
4058 int lsmash_append_sample( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_sample_t
*sample
)
4060 if( isom_check_initializer_present( root
) < 0
4063 || sample
->data
== NULL
4064 || sample
->dts
== LSMASH_TIMESTAMP_UNDEFINED
)
4065 return LSMASH_ERR_FUNCTION_PARAM
;
4066 lsmash_file_t
*file
= root
->file
;
4067 /* We think max_chunk_duration == 0, which means all samples will be cached on memory, should be prevented.
4068 * This means removal of a feature that we used to have, but anyway very alone chunk does not make sense. */
4070 || !(file
->flags
& LSMASH_FILE_MODE_BOX
)
4071 || file
->max_chunk_duration
== 0
4072 || file
->max_async_tolerance
== 0 )
4073 return LSMASH_ERR_NAMELESS
;
4074 /* Write File Type Box here if it was not written yet. */
4075 if( file
->flags
& LSMASH_FILE_MODE_INITIALIZATION
)
4077 if( LSMASH_IS_EXISTING_BOX( file
->ftyp
) && !(file
->ftyp
->manager
& LSMASH_WRITTEN_BOX
) )
4079 int err
= isom_write_box( file
->bs
, (isom_box_t
*)file
->ftyp
);
4082 file
->size
+= file
->ftyp
->size
;
4085 /* Get a sample initializer. */
4086 isom_trak_t
*trak
= isom_get_trak( file
->initializer
, track_ID
);
4087 if( LSMASH_IS_NON_EXISTING_BOX( trak
->file
)
4088 || LSMASH_IS_NON_EXISTING_BOX( trak
->tkhd
)
4089 || trak
->mdia
->mdhd
->timescale
== 0
4091 || !trak
->mdia
->minf
->stbl
->stsc
->list
)
4092 return LSMASH_ERR_NAMELESS
;
4093 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)lsmash_list_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
, sample
->index
);
4094 if( LSMASH_IS_NON_EXISTING_BOX( sample_entry
) )
4095 return LSMASH_ERR_NAMELESS
;
4096 /* Append a sample. */
4097 if( (file
->flags
& LSMASH_FILE_MODE_FRAGMENTED
)
4099 && file
->fragment
->pool
)
4100 return isom_append_fragment_sample( file
, trak
, sample
, sample_entry
);
4101 if( file
!= file
->initializer
)
4102 return LSMASH_ERR_INVALID_DATA
;
4103 return isom_append_sample( file
, trak
, sample
, sample_entry
);
4106 /*---- misc functions ----*/
4108 int lsmash_delete_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
)
4110 if( isom_check_initializer_present( root
) < 0 )
4111 return LSMASH_ERR_FUNCTION_PARAM
;
4112 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
4113 if( LSMASH_IS_NON_EXISTING_BOX( trak
) )
4114 return LSMASH_ERR_NAMELESS
;
4115 isom_remove_box_by_itself( trak
->edts
);
4116 return isom_update_tkhd_duration( trak
);
4119 void lsmash_delete_tyrant_chapter( lsmash_root_t
*root
)
4121 if( isom_check_initializer_present( root
) < 0
4122 || LSMASH_IS_NON_EXISTING_BOX( root
->file
->initializer
->moov
->udta
) )
4124 isom_remove_box_by_itself( root
->file
->moov
->udta
->chpl
);
4127 int lsmash_set_sdp( lsmash_root_t
*root
, uint32_t track_ID
, char *sdptext
)
4129 if( isom_check_initializer_present( root
) < 0 || sdptext
== NULL
)
4130 return LSMASH_ERR_FUNCTION_PARAM
;
4131 lsmash_file_t
*file
= root
->file
;
4132 if( LSMASH_IS_NON_EXISTING_BOX( file
->moov
) || !file
->isom_compatible
)
4133 return LSMASH_ERR_NAMELESS
;
4137 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
4138 if( LSMASH_IS_NON_EXISTING_BOX( trak
) )
4139 return LSMASH_ERR_NAMELESS
;
4140 if( LSMASH_IS_NON_EXISTING_BOX( trak
->udta
)
4141 && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_udta( trak
) ) )
4142 return LSMASH_ERR_NAMELESS
;
4147 if( LSMASH_IS_NON_EXISTING_BOX( file
->moov
->udta
)
4148 && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_udta( file
->moov
) ) )
4149 return LSMASH_ERR_NAMELESS
;
4150 udta
= file
->moov
->udta
;
4152 assert( LSMASH_IS_EXISTING_BOX( udta
) );
4153 if( LSMASH_IS_NON_EXISTING_BOX( udta
->hnti
)
4154 && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_hnti( udta
) ) )
4155 return LSMASH_ERR_NAMELESS
;
4156 isom_hnti_t
*hnti
= udta
->hnti
;
4157 /* If track ID is given, text is meant for track hnti box,
4158 * otherwise it is meant for movie 'hnti' box. */
4161 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_sdp( hnti
) ) )
4162 return LSMASH_ERR_NAMELESS
;
4163 isom_sdp_t
*sdp
= hnti
->sdp
;
4164 sdp
->sdp_length
= strlen( sdptext
); /* leaves \0 out*/
4165 sdp
->sdptext
= lsmash_memdup( sdptext
, sdp
->sdp_length
);
4167 return LSMASH_ERR_MEMORY_ALLOC
;
4171 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_rtp( hnti
) ) )
4172 return LSMASH_ERR_NAMELESS
;
4173 isom_rtp_t
*rtp
= hnti
->rtp
;
4174 rtp
->descriptionformat
= LSMASH_4CC( 's', 'd', 'p', ' ' );
4175 rtp
->sdp_length
= strlen( sdptext
); /* leaves \0 out */
4176 rtp
->sdptext
= lsmash_memdup( sdptext
, rtp
->sdp_length
);
4178 return LSMASH_ERR_MEMORY_ALLOC
;
4183 int lsmash_set_copyright( lsmash_root_t
*root
, uint32_t track_ID
, uint16_t ISO_language
, char *notice
)
4185 if( isom_check_initializer_present( root
) < 0
4186 || (ISO_language
&& ISO_language
< 0x800)
4188 return LSMASH_ERR_FUNCTION_PARAM
;
4189 lsmash_file_t
*file
= root
->file
;
4190 if( !file
->isom_compatible
)
4191 return LSMASH_ERR_NAMELESS
;
4195 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
4196 if( LSMASH_IS_NON_EXISTING_BOX( trak
->udta
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_udta( trak
) ) )
4197 return LSMASH_ERR_NAMELESS
;
4202 if( LSMASH_IS_NON_EXISTING_BOX( file
->moov
->udta
) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_udta( file
->moov
) ) )
4203 return LSMASH_ERR_NAMELESS
;
4204 udta
= file
->moov
->udta
;
4206 assert( LSMASH_IS_EXISTING_BOX( udta
) );
4207 for( lsmash_entry_t
*entry
= udta
->cprt_list
.head
; entry
; entry
= entry
->next
)
4209 isom_cprt_t
*cprt
= (isom_cprt_t
*)entry
->data
;
4210 if( LSMASH_IS_NON_EXISTING_BOX( cprt
) || cprt
->language
== ISO_language
)
4211 return LSMASH_ERR_NAMELESS
;
4213 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_cprt( udta
) ) )
4214 return LSMASH_ERR_NAMELESS
;
4215 isom_cprt_t
*cprt
= (isom_cprt_t
*)udta
->cprt_list
.tail
->data
;
4216 cprt
->language
= ISO_language
;
4217 cprt
->notice_length
= strlen( notice
) + 1;
4218 cprt
->notice
= lsmash_memdup( notice
, cprt
->notice_length
);