1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2010-2015 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 */
37 #include "codecs/mp4a.h"
38 #include "codecs/mp4sys.h"
39 #include "codecs/description.h"
42 int isom_check_initializer_present( lsmash_root_t
*root
)
46 || !root
->file
->initializer
)
47 return LSMASH_ERR_NAMELESS
;
51 isom_trak_t
*isom_get_trak( lsmash_file_t
*file
, uint32_t track_ID
)
55 || file
!= file
->initializer
58 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
60 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
64 if( trak
->tkhd
->track_ID
== track_ID
)
70 isom_trex_t
*isom_get_trex( isom_mvex_t
*mvex
, uint32_t track_ID
)
72 if( track_ID
== 0 || !mvex
)
74 for( lsmash_entry_t
*entry
= mvex
->trex_list
.head
; entry
; entry
= entry
->next
)
76 isom_trex_t
*trex
= (isom_trex_t
*)entry
->data
;
79 if( trex
->track_ID
== track_ID
)
85 isom_traf_t
*isom_get_traf( isom_moof_t
*moof
, uint32_t track_ID
)
87 if( track_ID
== 0 || !moof
)
89 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
91 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
95 if( traf
->tfhd
->track_ID
== track_ID
)
101 isom_tfra_t
*isom_get_tfra( isom_mfra_t
*mfra
, uint32_t track_ID
)
103 if( track_ID
== 0 || !mfra
)
105 for( lsmash_entry_t
*entry
= mfra
->tfra_list
.head
; entry
; entry
= entry
->next
)
107 isom_tfra_t
*tfra
= (isom_tfra_t
*)entry
->data
;
110 if( tfra
->track_ID
== track_ID
)
116 static int isom_add_elst_entry( isom_elst_t
*elst
, uint64_t segment_duration
, int64_t media_time
, int32_t media_rate
)
118 assert( elst
->file
);
119 isom_elst_entry_t
*data
= lsmash_malloc( sizeof(isom_elst_entry_t
) );
121 return LSMASH_ERR_MEMORY_ALLOC
;
122 data
->segment_duration
= segment_duration
;
123 data
->media_time
= media_time
;
124 data
->media_rate
= media_rate
;
125 if( lsmash_add_entry( elst
->list
, data
) < 0 )
128 return LSMASH_ERR_MEMORY_ALLOC
;
130 if( !elst
->file
->undefined_64_ver
131 && (data
->segment_duration
> UINT32_MAX
132 || data
->media_time
> INT32_MAX
133 || data
->media_time
< INT32_MIN
) )
138 /* This function returns 0 if failed, sample_entry_number if succeeded. */
139 int lsmash_add_sample_entry( lsmash_root_t
*root
, uint32_t track_ID
, void *summary
)
141 if( !root
|| !summary
142 || ((lsmash_summary_t
*)summary
)->data_ref_index
== 0
143 || ((lsmash_summary_t
*)summary
)->data_ref_index
> UINT16_MAX
)
145 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
151 || !trak
->mdia
->minf
->stbl
152 || !trak
->mdia
->minf
->stbl
->stsd
)
154 isom_stsd_t
*stsd
= trak
->mdia
->minf
->stbl
->stsd
;
155 lsmash_media_type media_type
= trak
->mdia
->hdlr
->componentSubtype
;
156 if( isom_setup_sample_description( stsd
, media_type
, (lsmash_summary_t
*)summary
) < 0 )
159 return stsd
->list
.entry_count
;
162 static int isom_add_stts_entry( isom_stbl_t
*stbl
, uint32_t sample_delta
)
166 || !stbl
->stts
->list
)
167 return LSMASH_ERR_NAMELESS
;
168 isom_stts_entry_t
*data
= lsmash_malloc( sizeof(isom_stts_entry_t
) );
170 return LSMASH_ERR_MEMORY_ALLOC
;
171 data
->sample_count
= 1;
172 data
->sample_delta
= sample_delta
;
173 if( lsmash_add_entry( stbl
->stts
->list
, data
) < 0 )
176 return LSMASH_ERR_MEMORY_ALLOC
;
181 static int isom_add_ctts_entry( isom_stbl_t
*stbl
, uint32_t sample_offset
)
185 || !stbl
->ctts
->list
)
186 return LSMASH_ERR_NAMELESS
;
187 isom_ctts_entry_t
*data
= lsmash_malloc( sizeof(isom_ctts_entry_t
) );
189 return LSMASH_ERR_MEMORY_ALLOC
;
190 data
->sample_count
= 1;
191 data
->sample_offset
= sample_offset
;
192 if( lsmash_add_entry( stbl
->ctts
->list
, data
) < 0 )
195 return LSMASH_ERR_MEMORY_ALLOC
;
200 static int isom_add_stsc_entry( isom_stbl_t
*stbl
, uint32_t first_chunk
, uint32_t samples_per_chunk
, uint32_t sample_description_index
)
204 || !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_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
)
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_create_entry_list();
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_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_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
)
272 || !stbl
->stss
->list
)
273 return LSMASH_ERR_NAMELESS
;
274 isom_stss_entry_t
*data
= lsmash_malloc( sizeof(isom_stss_entry_t
) );
276 return LSMASH_ERR_MEMORY_ALLOC
;
277 data
->sample_number
= sample_number
;
278 if( lsmash_add_entry( stbl
->stss
->list
, data
) < 0 )
281 return LSMASH_ERR_MEMORY_ALLOC
;
286 static int isom_add_stps_entry( isom_stbl_t
*stbl
, uint32_t sample_number
)
290 || !stbl
->stps
->list
)
291 return LSMASH_ERR_NAMELESS
;
292 isom_stps_entry_t
*data
= lsmash_malloc( sizeof(isom_stps_entry_t
) );
294 return LSMASH_ERR_MEMORY_ALLOC
;
295 data
->sample_number
= sample_number
;
296 if( lsmash_add_entry( stbl
->stps
->list
, data
) < 0 )
299 return LSMASH_ERR_MEMORY_ALLOC
;
304 /* Between ISOBMFF and QTFF, the most significant 2-bit has different meaning.
305 * For the maximum compatibility, we set 0 to the most significant 2-bit when compatible with
306 * both ISOBMFF with AVCFF extensions and QTFF.
307 * compatibility == 0 -> neither AVCFF extensions nor QTFF compatible
308 * compatibility == 1 -> AVCFF extensions compatible
309 * compatibility == 2 -> QTFF compatible
310 * compatibility == 3 -> both AVCFF extensions and QTFF compatible */
311 static int isom_add_sdtp_entry( isom_box_t
*parent
, lsmash_sample_property_t
*prop
, int compatibility
)
313 if( !prop
|| !parent
)
314 return LSMASH_ERR_NAMELESS
;
315 isom_sdtp_t
*sdtp
= NULL
;
316 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_STBL
) )
317 sdtp
= ((isom_stbl_t
*)parent
)->sdtp
;
318 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
319 sdtp
= ((isom_traf_t
*)parent
)->sdtp
;
324 return LSMASH_ERR_NAMELESS
;
325 isom_sdtp_entry_t
*data
= lsmash_malloc( sizeof(isom_sdtp_entry_t
) );
327 return LSMASH_ERR_MEMORY_ALLOC
;
328 if( compatibility
== 1 )
329 data
->is_leading
= prop
->leading
& 0x03;
330 else if( compatibility
== 2 )
331 data
->is_leading
= prop
->allow_earlier
& 0x03;
334 data
->is_leading
= 0;
335 assert( compatibility
== 3 );
337 data
->sample_depends_on
= prop
->independent
& 0x03;
338 data
->sample_is_depended_on
= prop
->disposable
& 0x03;
339 data
->sample_has_redundancy
= prop
->redundant
& 0x03;
340 if( lsmash_add_entry( sdtp
->list
, data
) < 0 )
343 return LSMASH_ERR_MEMORY_ALLOC
;
348 static int isom_add_co64_entry( isom_stbl_t
*stbl
, uint64_t chunk_offset
)
352 || !stbl
->stco
->list
)
353 return LSMASH_ERR_NAMELESS
;
354 isom_co64_entry_t
*data
= lsmash_malloc( sizeof(isom_co64_entry_t
) );
356 return LSMASH_ERR_MEMORY_ALLOC
;
357 data
->chunk_offset
= chunk_offset
;
358 if( lsmash_add_entry( stbl
->stco
->list
, data
) < 0 )
361 return LSMASH_ERR_MEMORY_ALLOC
;
366 static int isom_convert_stco_to_co64( isom_stbl_t
*stbl
)
370 isom_stco_t
*stco
= stbl
->stco
;
372 if( !isom_add_co64( stbl
) )
374 err
= LSMASH_ERR_NAMELESS
;
377 /* move chunk_offset to co64 from stco */
378 for( lsmash_entry_t
*entry
= stco
->list
->head
; entry
; entry
= entry
->next
)
380 isom_stco_entry_t
*data
= (isom_stco_entry_t
*)entry
->data
;
381 if( (err
= isom_add_co64_entry( stbl
, data
->chunk_offset
)) < 0 )
385 isom_remove_box_by_itself( stco
);
389 static int isom_add_stco_entry( isom_stbl_t
*stbl
, uint64_t chunk_offset
)
393 || !stbl
->stco
->list
)
394 return LSMASH_ERR_NAMELESS
;
395 if( stbl
->stco
->large_presentation
)
396 return isom_add_co64_entry( stbl
, chunk_offset
);
397 if( chunk_offset
> UINT32_MAX
)
399 int err
= isom_convert_stco_to_co64( stbl
);
402 return isom_add_co64_entry( stbl
, chunk_offset
);
404 isom_stco_entry_t
*data
= lsmash_malloc( sizeof(isom_stco_entry_t
) );
406 return LSMASH_ERR_MEMORY_ALLOC
;
407 data
->chunk_offset
= (uint32_t)chunk_offset
;
408 if( lsmash_add_entry( stbl
->stco
->list
, data
) < 0 )
411 return LSMASH_ERR_MEMORY_ALLOC
;
416 static isom_sgpd_t
*isom_get_sample_group_description_common( lsmash_entry_list_t
*list
, uint32_t grouping_type
)
418 for( lsmash_entry_t
*entry
= list
->head
; entry
; entry
= entry
->next
)
420 isom_sgpd_t
*sgpd
= (isom_sgpd_t
*)entry
->data
;
424 if( sgpd
->grouping_type
== grouping_type
)
430 static isom_sbgp_t
*isom_get_sample_to_group_common( lsmash_entry_list_t
*list
, uint32_t grouping_type
)
432 for( lsmash_entry_t
*entry
= list
->head
; entry
; entry
= entry
->next
)
434 isom_sbgp_t
*sbgp
= (isom_sbgp_t
*)entry
->data
;
438 if( sbgp
->grouping_type
== grouping_type
)
444 isom_sgpd_t
*isom_get_sample_group_description( isom_stbl_t
*stbl
, uint32_t grouping_type
)
446 return isom_get_sample_group_description_common( &stbl
->sgpd_list
, grouping_type
);
449 isom_sbgp_t
*isom_get_sample_to_group( isom_stbl_t
*stbl
, uint32_t grouping_type
)
451 return isom_get_sample_to_group_common( &stbl
->sbgp_list
, grouping_type
);
454 isom_sgpd_t
*isom_get_roll_recovery_sample_group_description( lsmash_entry_list_t
*list
)
457 if( (sgpd
= isom_get_sample_group_description_common( list
, ISOM_GROUP_TYPE_ROLL
))
458 || (sgpd
= isom_get_sample_group_description_common( list
, ISOM_GROUP_TYPE_PROL
)) )
463 isom_sbgp_t
*isom_get_roll_recovery_sample_to_group( lsmash_entry_list_t
*list
)
466 if( (sbgp
= isom_get_sample_to_group_common( list
, ISOM_GROUP_TYPE_ROLL
))
467 || (sbgp
= isom_get_sample_to_group_common( list
, ISOM_GROUP_TYPE_PROL
)) )
472 isom_sgpd_t
*isom_get_fragment_sample_group_description( isom_traf_t
*traf
, uint32_t grouping_type
)
474 return isom_get_sample_group_description_common( &traf
->sgpd_list
, grouping_type
);
477 isom_sbgp_t
*isom_get_fragment_sample_to_group( isom_traf_t
*traf
, uint32_t grouping_type
)
479 return isom_get_sample_to_group_common( &traf
->sbgp_list
, grouping_type
);
482 static isom_rap_entry_t
*isom_add_rap_group_entry( isom_sgpd_t
*sgpd
)
486 isom_rap_entry_t
*data
= lsmash_malloc( sizeof(isom_rap_entry_t
) );
489 data
->description_length
= 0;
490 data
->num_leading_samples_known
= 0;
491 data
->num_leading_samples
= 0;
492 if( lsmash_add_entry( sgpd
->list
, data
) < 0 )
500 static isom_roll_entry_t
*isom_add_roll_group_entry( isom_sgpd_t
*sgpd
, int16_t roll_distance
)
504 isom_roll_entry_t
*data
= lsmash_malloc( sizeof(isom_roll_entry_t
) );
507 data
->description_length
= 0;
508 data
->roll_distance
= roll_distance
;
509 if( lsmash_add_entry( sgpd
->list
, data
) < 0 )
517 static isom_group_assignment_entry_t
*isom_add_group_assignment_entry( isom_sbgp_t
*sbgp
, uint32_t sample_count
, uint32_t group_description_index
)
521 isom_group_assignment_entry_t
*data
= lsmash_malloc( sizeof(isom_group_assignment_entry_t
) );
524 data
->sample_count
= sample_count
;
525 data
->group_description_index
= group_description_index
;
526 if( lsmash_add_entry( sbgp
->list
, data
) < 0 )
534 static uint32_t isom_get_sample_count_from_sample_table( isom_stbl_t
*stbl
)
537 return stbl
->stsz
->sample_count
;
538 else if( stbl
->stz2
)
539 return stbl
->stz2
->sample_count
;
544 uint32_t isom_get_sample_count( isom_trak_t
*trak
)
549 || !trak
->mdia
->minf
->stbl
)
551 return isom_get_sample_count_from_sample_table( trak
->mdia
->minf
->stbl
);
554 static uint64_t isom_get_dts( isom_stts_t
*stts
, uint32_t sample_number
)
561 lsmash_entry_t
*entry
;
562 isom_stts_entry_t
*data
= NULL
;
563 for( entry
= stts
->list
->head
; entry
; entry
= entry
->next
)
565 data
= (isom_stts_entry_t
*)entry
->data
;
568 if( i
+ data
->sample_count
> sample_number
)
570 dts
+= (uint64_t)data
->sample_delta
* data
->sample_count
;
571 i
+= data
->sample_count
;
575 dts
+= (uint64_t)data
->sample_delta
* (sample_number
- i
);
580 static uint64_t isom_get_cts( isom_stts_t
*stts
, isom_ctts_t
*ctts
, uint32_t sample_number
)
586 return isom_get_dts( stts
, sample_number
);
587 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. */
588 lsmash_entry_t
*entry
;
589 isom_ctts_entry_t
*data
= NULL
;
590 if( sample_number
== 0 )
592 for( entry
= ctts
->list
->head
; entry
; entry
= entry
->next
)
594 data
= (isom_ctts_entry_t
*)entry
->data
;
597 if( i
+ data
->sample_count
> sample_number
)
599 i
+= data
->sample_count
;
603 return isom_get_dts( stts
, sample_number
) + data
->sample_offset
;
607 static int isom_replace_last_sample_delta( isom_stbl_t
*stbl
, uint32_t sample_delta
)
612 || !stbl
->stts
->list
->tail
613 || !stbl
->stts
->list
->tail
->data
)
614 return LSMASH_ERR_NAMELESS
;
615 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)stbl
->stts
->list
->tail
->data
;
616 if( sample_delta
!= last_stts_data
->sample_delta
)
618 if( last_stts_data
->sample_count
> 1 )
620 last_stts_data
->sample_count
-= 1;
621 int err
= isom_add_stts_entry( stbl
, sample_delta
);
626 last_stts_data
->sample_delta
= sample_delta
;
631 static int isom_update_mdhd_duration( isom_trak_t
*trak
, uint32_t last_sample_delta
)
639 || !trak
->mdia
->minf
->stbl
640 || !trak
->mdia
->minf
->stbl
->stts
641 || !trak
->mdia
->minf
->stbl
->stts
->list
)
642 return LSMASH_ERR_INVALID_DATA
;
643 lsmash_file_t
*file
= trak
->file
;
644 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
645 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
646 isom_stts_t
*stts
= stbl
->stts
;
647 isom_ctts_t
*ctts
= stbl
->ctts
;
648 isom_cslg_t
*cslg
= stbl
->cslg
;
650 uint32_t sample_count
= isom_get_sample_count( trak
);
651 if( sample_count
== 0 )
653 /* Return error if non-fragmented movie has no samples. */
654 if( !file
->fragment
&& !stts
->list
->entry_count
)
655 return LSMASH_ERR_INVALID_DATA
;
658 /* Now we have at least 1 sample, so do stts_entry. */
659 lsmash_entry_t
*last_stts
= stts
->list
->tail
;
660 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)last_stts
->data
;
661 if( sample_count
== 1 )
662 mdhd
->duration
= last_stts_data
->sample_delta
;
663 /* Now we have at least 2 samples,
664 * but dunno whether 1 stts_entry which has 2 samples or 2 stts_entry which has 1 samle each. */
667 /* use dts instead of cts */
668 mdhd
->duration
= isom_get_dts( stts
, sample_count
);
670 if( last_sample_delta
)
672 mdhd
->duration
+= last_sample_delta
;
673 if( (err
= isom_replace_last_sample_delta( stbl
, last_sample_delta
)) < 0 )
676 else if( last_stts_data
->sample_count
> 1 )
677 mdhd
->duration
+= last_stts_data
->sample_delta
; /* no need to update last_stts_data->sample_delta */
680 /* Remove the last entry. */
681 if( (err
= lsmash_remove_entry_tail( stts
->list
, NULL
)) < 0 )
683 /* copy the previous sample_delta. */
684 ++ ((isom_stts_entry_t
*)stts
->list
->tail
->data
)->sample_count
;
685 mdhd
->duration
+= ((isom_stts_entry_t
*)stts
->list
->tail
->data
)->sample_delta
;
691 || ctts
->list
->entry_count
== 0 )
692 return LSMASH_ERR_INVALID_DATA
;
694 uint64_t max_cts
= 0;
695 uint64_t max2_cts
= 0;
696 uint64_t min_cts
= UINT64_MAX
;
697 int64_t max_offset
= 0;
698 int64_t min_offset
= UINT32_MAX
;
699 int32_t ctd_shift
= trak
->cache
->timestamp
.ctd_shift
;
702 lsmash_entry_t
*stts_entry
= stts
->list
->head
;
703 lsmash_entry_t
*ctts_entry
= ctts
->list
->head
;
704 for( uint32_t i
= 0; i
< sample_count
; i
++ )
706 if( !ctts_entry
|| !stts_entry
)
707 return LSMASH_ERR_INVALID_DATA
;
708 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
709 isom_ctts_entry_t
*ctts_data
= (isom_ctts_entry_t
*)ctts_entry
->data
;
710 if( !stts_data
|| !ctts_data
)
711 return LSMASH_ERR_INVALID_DATA
;
715 /* Anyway, add composition to decode timeline shift for calculating maximum and minimum CTS correctly. */
716 int64_t sample_offset
= (int32_t)ctts_data
->sample_offset
;
717 cts
= dts
+ sample_offset
+ ctd_shift
;
718 max_offset
= LSMASH_MAX( max_offset
, sample_offset
);
719 min_offset
= LSMASH_MIN( min_offset
, sample_offset
);
723 cts
= dts
+ ctts_data
->sample_offset
;
724 max_offset
= LSMASH_MAX( max_offset
, ctts_data
->sample_offset
);
725 min_offset
= LSMASH_MIN( min_offset
, ctts_data
->sample_offset
);
727 min_cts
= LSMASH_MIN( min_cts
, cts
);
733 else if( max2_cts
< cts
)
735 dts
+= stts_data
->sample_delta
;
736 /* If finished sample_count of current entry, move to next. */
737 if( ++j
== ctts_data
->sample_count
)
739 ctts_entry
= ctts_entry
->next
;
742 if( ++k
== stts_data
->sample_count
)
744 stts_entry
= stts_entry
->next
;
748 dts
-= last_stts_data
->sample_delta
;
750 /* Overall presentation is extended exceeding this initial movie.
751 * So, any players shall display the movie exceeding the durations
752 * indicated in Movie Header Box, Track Header Boxes and Media Header Boxes.
753 * Samples up to the duration indicated in Movie Extends Header Box shall be displayed.
754 * In the absence of Movie Extends Header Box, all samples shall be displayed. */
755 mdhd
->duration
+= dts
+ last_sample_delta
;
758 if( !last_sample_delta
)
760 /* The spec allows an arbitrary value for the duration of the last sample. So, we pick last-1 sample's. */
761 last_sample_delta
= max_cts
- max2_cts
;
763 mdhd
->duration
= max_cts
- min_cts
+ last_sample_delta
;
764 /* To match dts and media duration, update stts and mdhd relatively. */
765 if( mdhd
->duration
> dts
)
766 last_sample_delta
= mdhd
->duration
- dts
;
768 mdhd
->duration
= dts
+ last_sample_delta
; /* media duration must not less than last dts. */
770 int err
= isom_replace_last_sample_delta( stbl
, last_sample_delta
);
773 /* Explicit composition information and timeline shifting */
774 if( cslg
|| file
->qt_compatible
|| file
->max_isom_version
>= 4 )
778 /* Remove composition to decode timeline shift. */
779 max_cts
-= ctd_shift
;
780 max2_cts
-= ctd_shift
;
781 min_cts
-= ctd_shift
;
783 int64_t composition_end_time
= max_cts
+ (max_cts
- max2_cts
);
785 && (min_offset
<= INT32_MAX
) && (min_offset
>= INT32_MIN
)
786 && (max_offset
<= INT32_MAX
) && (max_offset
>= INT32_MIN
)
787 && ((int64_t)min_cts
<= INT32_MAX
) && (composition_end_time
<= INT32_MAX
) )
791 if( !isom_add_cslg( trak
->mdia
->minf
->stbl
) )
792 return LSMASH_ERR_NAMELESS
;
795 cslg
->compositionToDTSShift
= ctd_shift
;
796 cslg
->leastDecodeToDisplayDelta
= min_offset
;
797 cslg
->greatestDecodeToDisplayDelta
= max_offset
;
798 cslg
->compositionStartTime
= min_cts
;
799 cslg
->compositionEndTime
= composition_end_time
;
802 isom_remove_box_by_itself( cslg
);
805 if( mdhd
->duration
> UINT32_MAX
&& !file
->undefined_64_ver
)
810 static int isom_update_mvhd_duration( isom_moov_t
*moov
)
814 || !moov
->mvhd
->file
)
815 return LSMASH_ERR_INVALID_DATA
;
816 isom_mvhd_t
*mvhd
= moov
->mvhd
;
818 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
820 /* We pick maximum track duration as movie duration. */
821 isom_trak_t
*data
= (isom_trak_t
*)entry
->data
;
824 return LSMASH_ERR_INVALID_DATA
;
825 mvhd
->duration
= entry
!= moov
->trak_list
.head
826 ? LSMASH_MAX( mvhd
->duration
, data
->tkhd
->duration
)
827 : data
->tkhd
->duration
;
829 if( mvhd
->duration
> UINT32_MAX
&& !mvhd
->file
->undefined_64_ver
)
834 int isom_update_tkhd_duration( isom_trak_t
*trak
)
839 || !trak
->file
->moov
)
840 return LSMASH_ERR_INVALID_DATA
;
841 lsmash_file_t
*file
= trak
->file
;
842 isom_tkhd_t
*tkhd
= trak
->tkhd
;
846 || !trak
->edts
->elst
)
848 /* If this presentation might be extended or this track doesn't have edit list, calculate track duration from media duration. */
852 || trak
->mdia
->mdhd
->timescale
== 0 )
853 return LSMASH_ERR_INVALID_DATA
;
854 if( trak
->mdia
->mdhd
->duration
== 0 )
856 int err
= isom_update_mdhd_duration( trak
, 0 );
860 tkhd
->duration
= trak
->mdia
->mdhd
->duration
* ((double)file
->moov
->mvhd
->timescale
/ trak
->mdia
->mdhd
->timescale
);
864 /* If the presentation won't be extended and this track has any edit, then track duration is just the sum of the segment_duartions. */
865 for( lsmash_entry_t
*entry
= trak
->edts
->elst
->list
->head
; entry
; entry
= entry
->next
)
867 isom_elst_entry_t
*data
= (isom_elst_entry_t
*)entry
->data
;
869 return LSMASH_ERR_INVALID_DATA
;
870 tkhd
->duration
+= data
->segment_duration
;
873 if( tkhd
->duration
> UINT32_MAX
&& !file
->undefined_64_ver
)
875 if( !file
->fragment
&& tkhd
->duration
== 0 )
876 tkhd
->duration
= tkhd
->version
== 1 ? 0xffffffffffffffff : 0xffffffff;
877 return isom_update_mvhd_duration( file
->moov
);
880 int lsmash_update_track_duration( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t last_sample_delta
)
882 if( isom_check_initializer_present( root
) < 0 )
883 return LSMASH_ERR_FUNCTION_PARAM
;
884 lsmash_file_t
*file
= root
->file
;
885 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
887 return LSMASH_ERR_NAMELESS
;
888 int err
= isom_update_mdhd_duration( trak
, last_sample_delta
);
891 /* If the presentation won't be extended and this track has any edit, we don't change or update duration in tkhd. */
892 if( !file
->fragment
&& trak
->edts
&& trak
->edts
->elst
)
893 err
= isom_update_mvhd_duration( file
->moov
); /* Only update movie duration. */
895 err
= isom_update_tkhd_duration( trak
); /* Also update movie duration internally. */
899 static inline int isom_increment_sample_number_in_entry
901 uint32_t *sample_number_in_entry
,
902 uint32_t sample_count_in_entry
,
903 lsmash_entry_t
**entry
906 if( *sample_number_in_entry
!= sample_count_in_entry
)
908 *sample_number_in_entry
+= 1;
911 /* Precede the next entry. */
912 *sample_number_in_entry
= 1;
915 *entry
= (*entry
)->next
;
916 if( *entry
&& !(*entry
)->data
)
917 return LSMASH_ERR_INVALID_DATA
;
922 int isom_calculate_bitrate_description
926 uint32_t *bufferSizeDB
,
927 uint32_t *maxBitrate
,
928 uint32_t *avgBitrate
,
929 uint32_t sample_description_index
932 isom_stsz_t
*stsz
= stbl
->stsz
;
933 lsmash_entry_list_t
*stsz_list
= stsz
? stsz
->list
: stbl
->stz2
->list
;
934 lsmash_entry_t
*stsz_entry
= stsz_list
? stsz_list
->head
: NULL
;
935 lsmash_entry_t
*stts_entry
= stbl
->stts
->list
->head
;
936 lsmash_entry_t
*stsc_entry
= NULL
;
937 lsmash_entry_t
*next_stsc_entry
= stbl
->stsc
->list
->head
;
938 isom_stts_entry_t
*stts_data
= NULL
;
939 isom_stsc_entry_t
*stsc_data
= NULL
;
940 if( next_stsc_entry
&& !next_stsc_entry
->data
)
941 return LSMASH_ERR_INVALID_DATA
;
944 uint32_t time_wnd
= 0;
945 uint32_t chunk_number
= 0;
946 uint32_t sample_number_in_stts
= 1;
947 uint32_t sample_number_in_chunk
= 1;
948 uint32_t constant_sample_size
= stsz
? stsz
->sample_size
: 0;
955 if( !stsc_data
|| sample_number_in_chunk
== stsc_data
->samples_per_chunk
)
957 /* Move the next chunk. */
958 sample_number_in_chunk
= 1;
960 /* Check if the next entry is broken. */
961 while( next_stsc_entry
&& ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
< chunk_number
)
963 /* Just skip broken next entry. */
964 next_stsc_entry
= next_stsc_entry
->next
;
965 if( next_stsc_entry
&& !next_stsc_entry
->data
)
966 return LSMASH_ERR_INVALID_DATA
;
968 /* Check if the next chunk belongs to the next sequence of chunks. */
969 if( next_stsc_entry
&& ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
== chunk_number
)
971 stsc_entry
= next_stsc_entry
;
972 next_stsc_entry
= next_stsc_entry
->next
;
973 if( next_stsc_entry
&& !next_stsc_entry
->data
)
974 return LSMASH_ERR_INVALID_DATA
;
975 stsc_data
= (isom_stsc_entry_t
*)stsc_entry
->data
;
976 /* Check if the next contiguous chunks belong to given sample description. */
977 if( stsc_data
->sample_description_index
!= sample_description_index
)
979 /* Skip chunks which don't belong to given sample description. */
980 uint32_t number_of_skips
= 0;
981 uint32_t first_chunk
= stsc_data
->first_chunk
;
982 uint32_t samples_per_chunk
= stsc_data
->samples_per_chunk
;
983 while( next_stsc_entry
)
985 if( ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->sample_description_index
!= sample_description_index
)
987 stsc_data
= (isom_stsc_entry_t
*)next_stsc_entry
->data
;
988 number_of_skips
+= (stsc_data
->first_chunk
- first_chunk
) * samples_per_chunk
;
989 first_chunk
= stsc_data
->first_chunk
;
990 samples_per_chunk
= stsc_data
->samples_per_chunk
;
992 else if( ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
<= first_chunk
)
996 /* Just skip the next entry. */
997 next_stsc_entry
= next_stsc_entry
->next
;
998 if( next_stsc_entry
&& !next_stsc_entry
->data
)
999 return LSMASH_ERR_INVALID_DATA
;
1001 if( !next_stsc_entry
)
1002 break; /* There is no more chunks which don't belong to given sample description. */
1003 number_of_skips
+= (((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
- first_chunk
) * samples_per_chunk
;
1004 for( uint32_t i
= 0; i
< number_of_skips
; i
++ )
1010 stsz_entry
= stsz_entry
->next
;
1014 if( (err
= isom_increment_sample_number_in_entry( &sample_number_in_stts
,
1015 ((isom_stts_entry_t
*)stts_entry
->data
)->sample_count
,
1016 &stts_entry
)) < 0 )
1019 if( (stsz_list
&& !stsz_entry
) || !stts_entry
)
1021 chunk_number
= stsc_data
->first_chunk
;
1026 ++sample_number_in_chunk
;
1027 /* Get current sample's size. */
1033 isom_stsz_entry_t
*stsz_data
= (isom_stsz_entry_t
*)stsz_entry
->data
;
1035 return LSMASH_ERR_INVALID_DATA
;
1036 size
= stsz_data
->entry_size
;
1037 stsz_entry
= stsz_entry
->next
;
1040 size
= constant_sample_size
;
1041 /* Get current sample's DTS. */
1043 dts
+= stts_data
->sample_delta
;
1044 stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
1046 return LSMASH_ERR_INVALID_DATA
;
1047 if( (err
= isom_increment_sample_number_in_entry( &sample_number_in_stts
, stts_data
->sample_count
, &stts_entry
)) < 0 )
1049 /* Calculate bitrate description. */
1050 if( *bufferSizeDB
< size
)
1051 *bufferSizeDB
= size
;
1052 *avgBitrate
+= size
;
1054 if( dts
> time_wnd
+ mdhd
->timescale
)
1056 if( rate
> *maxBitrate
)
1062 double duration
= (double)mdhd
->duration
/ mdhd
->timescale
;
1063 *avgBitrate
= (uint32_t)(*avgBitrate
/ duration
);
1064 if( *maxBitrate
== 0 )
1065 *maxBitrate
= *avgBitrate
;
1066 /* Convert to bits per second. */
1072 int isom_is_variable_size( isom_stbl_t
*stbl
)
1074 if( (stbl
->stz2
&& stbl
->stz2
->sample_count
> 1)
1075 || (stbl
->stsz
&& stbl
->stsz
->sample_count
> 1 && stbl
->stsz
->sample_size
== 0) )
1081 uint32_t isom_get_first_sample_size( isom_stbl_t
*stbl
)
1086 if( stbl
->stsz
->sample_size
)
1087 return stbl
->stsz
->sample_size
;
1088 else if( stbl
->stsz
->list
&& stbl
->stsz
->list
->head
&& stbl
->stsz
->list
->head
->data
)
1089 return ((isom_stsz_entry_t
*)stbl
->stsz
->list
->head
->data
)->entry_size
;
1093 else if( stbl
->stz2
)
1096 if( stbl
->stz2
->list
&& stbl
->stz2
->list
->head
&& stbl
->stz2
->list
->head
->data
)
1097 return ((isom_stsz_entry_t
*)stbl
->stz2
->list
->head
->data
)->entry_size
;
1105 int isom_update_bitrate_description( isom_mdia_t
*mdia
)
1110 || !mdia
->minf
->stbl
)
1111 return LSMASH_ERR_INVALID_DATA
;
1112 isom_stbl_t
*stbl
= mdia
->minf
->stbl
;
1114 || (!stbl
->stsz
&& !stbl
->stz2
)
1115 || !stbl
->stsc
|| !stbl
->stsc
->list
1116 || !stbl
->stts
|| !stbl
->stts
->list
)
1117 return LSMASH_ERR_INVALID_DATA
;
1118 uint32_t sample_description_index
= 0;
1119 for( lsmash_entry_t
*entry
= stbl
->stsd
->list
.head
; entry
; entry
= entry
->next
)
1121 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)entry
->data
;
1123 return LSMASH_ERR_INVALID_DATA
;
1124 ++sample_description_index
;
1125 isom_bitrate_updater_t func_update_bitrate
= isom_get_bitrate_updater( sample_entry
);
1126 if( func_update_bitrate
)
1128 int err
= func_update_bitrate( stbl
, mdia
->mdhd
, sample_description_index
);
1133 return sample_description_index
? 0 : LSMASH_ERR_INVALID_DATA
;
1136 static inline uint64_t isom_get_current_mp4time( void )
1138 return (uint64_t)time( NULL
) + ISOM_MAC_EPOCH_OFFSET
;
1141 static int isom_set_media_creation_time( isom_trak_t
*trak
, uint64_t current_mp4time
)
1144 || !trak
->mdia
->mdhd
)
1145 return LSMASH_ERR_NAMELESS
;
1146 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
1147 if( mdhd
->creation_time
== 0 )
1148 mdhd
->creation_time
= mdhd
->modification_time
= current_mp4time
;
1152 static int isom_set_track_creation_time( isom_trak_t
*trak
, uint64_t current_mp4time
)
1156 return LSMASH_ERR_NAMELESS
;
1157 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1158 if( tkhd
->creation_time
== 0 )
1159 tkhd
->creation_time
= tkhd
->modification_time
= current_mp4time
;
1160 return isom_set_media_creation_time( trak
, current_mp4time
);
1163 static int isom_set_movie_creation_time( lsmash_file_t
*file
)
1167 || !file
->moov
->mvhd
)
1168 return LSMASH_ERR_NAMELESS
;
1169 uint64_t current_mp4time
= isom_get_current_mp4time();
1170 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
1172 int err
= isom_set_track_creation_time( (isom_trak_t
*)entry
->data
, current_mp4time
);
1176 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
1177 if( mvhd
->creation_time
== 0 )
1178 mvhd
->creation_time
= mvhd
->modification_time
= current_mp4time
;
1182 int isom_setup_handler_reference( isom_hdlr_t
*hdlr
, uint32_t media_type
)
1184 isom_box_t
*parent
= hdlr
->parent
;
1185 lsmash_file_t
*file
= hdlr
->file
;
1186 if( !parent
|| !file
)
1187 return LSMASH_ERR_NAMELESS
;
1188 isom_mdia_t
*mdia
= lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_MDIA
) ? (isom_mdia_t
*)parent
: NULL
;
1189 isom_meta_t
*meta
= lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_META
) ? (isom_meta_t
*)parent
1190 : lsmash_check_box_type_identical( parent
->type
, QT_BOX_TYPE_META
) ? (isom_meta_t
*)parent
: NULL
;
1191 uint32_t type
= mdia
? (file
->qt_compatible
? QT_HANDLER_TYPE_MEDIA
: 0) : (meta
? 0 : QT_HANDLER_TYPE_DATA
);
1192 uint32_t subtype
= media_type
;
1193 hdlr
->componentType
= type
;
1194 hdlr
->componentSubtype
= subtype
;
1195 char *type_name
= NULL
;
1196 char *subtype_name
= NULL
;
1197 uint8_t type_name_length
= 0;
1198 uint8_t subtype_name_length
= 0;
1200 type_name
= "Media ";
1202 type_name
= "Metadata ";
1203 else /* if( minf ) */
1204 type_name
= "Data ";
1205 type_name_length
= strlen( type_name
);
1210 uint8_t subtype_name_length
;
1213 { ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
, "Sound ", 6 },
1214 { ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
, "Video ", 6 },
1215 { ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK
, "Hint ", 5 },
1216 { ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK
, "Metadata ", 9 },
1217 { ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK
, "Text ", 5 },
1218 { ISOM_META_HANDLER_TYPE_ITUNES_METADATA
, "iTunes ", 7 },
1219 { QT_REFERENCE_HANDLER_TYPE_ALIAS
, "Alias ", 6 },
1220 { QT_REFERENCE_HANDLER_TYPE_RESOURCE
, "Resource ", 9 },
1221 { QT_REFERENCE_HANDLER_TYPE_URL
, "URL ", 4 },
1222 { subtype
, "Unknown ", 8 }
1224 for( int i
= 0; subtype_table
[i
].subtype
; i
++ )
1225 if( subtype
== subtype_table
[i
].subtype
)
1227 subtype_name
= subtype_table
[i
].subtype_name
;
1228 subtype_name_length
= subtype_table
[i
].subtype_name_length
;
1231 uint32_t name_length
= 15 + subtype_name_length
+ type_name_length
+ file
->isom_compatible
+ file
->qt_compatible
;
1232 uint8_t *name
= lsmash_malloc( name_length
);
1234 return LSMASH_ERR_MEMORY_ALLOC
;
1235 if( file
->qt_compatible
)
1236 name
[0] = name_length
& 0xff;
1237 memcpy( name
+ file
->qt_compatible
, "L-SMASH ", 8 );
1238 memcpy( name
+ file
->qt_compatible
+ 8, subtype_name
, subtype_name_length
);
1239 memcpy( name
+ file
->qt_compatible
+ 8 + subtype_name_length
, type_name
, type_name_length
);
1240 memcpy( name
+ file
->qt_compatible
+ 8 + subtype_name_length
+ type_name_length
, "Handler", 7 );
1241 if( file
->isom_compatible
)
1242 name
[name_length
- 1] = 0;
1243 hdlr
->componentName
= name
;
1244 hdlr
->componentName_length
= name_length
;
1248 isom_trak_t
*isom_track_create( lsmash_file_t
*file
, lsmash_media_type media_type
)
1250 /* Don't allow to create a new track if the initial movie is already written. */
1251 if( (file
->fragment
&& file
->fragment
->movie
)
1252 || (file
->moov
&& (file
->moov
->manager
& LSMASH_WRITTEN_BOX
)) )
1254 isom_trak_t
*trak
= isom_add_trak( file
->moov
);
1257 || !trak
->file
->moov
1258 || !trak
->file
->moov
->mvhd
)
1260 if( !isom_add_tkhd( trak
)
1261 || !isom_add_mdia( trak
)
1262 || !isom_add_mdhd( trak
->mdia
)
1263 || !isom_add_minf( trak
->mdia
)
1264 || !isom_add_dinf( trak
->mdia
->minf
)
1265 || !isom_add_dref( trak
->mdia
->minf
->dinf
)
1266 || !isom_add_stbl( trak
->mdia
->minf
)
1267 || !isom_add_stsd( trak
->mdia
->minf
->stbl
)
1268 || !isom_add_stts( trak
->mdia
->minf
->stbl
)
1269 || !isom_add_stsc( trak
->mdia
->minf
->stbl
)
1270 || !isom_add_stco( trak
->mdia
->minf
->stbl
)
1271 || !isom_add_stsz( trak
->mdia
->minf
->stbl
) )
1273 if( !isom_add_hdlr( trak
->mdia
)
1274 || isom_setup_handler_reference( trak
->mdia
->hdlr
, media_type
) < 0 )
1276 if( file
->qt_compatible
)
1278 if( !isom_add_hdlr( trak
->mdia
->minf
)
1279 || isom_setup_handler_reference( trak
->mdia
->minf
->hdlr
, QT_REFERENCE_HANDLER_TYPE_URL
) < 0 )
1282 switch( media_type
)
1284 case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
:
1285 if( !isom_add_vmhd( trak
->mdia
->minf
) )
1287 trak
->mdia
->minf
->vmhd
->flags
= 0x000001;
1289 case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
:
1290 if( !isom_add_smhd( trak
->mdia
->minf
) )
1292 trak
->cache
->is_audio
= 1;
1294 case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK
:
1295 if( !isom_add_hmhd( trak
->mdia
->minf
) )
1298 case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK
:
1299 if( file
->qt_compatible
|| file
->itunes_movie
)
1301 if( !isom_add_gmhd( trak
->mdia
->minf
)
1302 || !isom_add_gmin( trak
->mdia
->minf
->gmhd
)
1303 || !isom_add_text( trak
->mdia
->minf
->gmhd
) )
1305 /* Default Text Media Information Box. */
1307 isom_text_t
*text
= trak
->mdia
->minf
->gmhd
->text
;
1308 text
->matrix
[0] = 0x00010000;
1309 text
->matrix
[4] = 0x00010000;
1310 text
->matrix
[8] = 0x40000000;
1314 goto fail
; /* We support only reference text media track for chapter yet. */
1317 if( !isom_add_nmhd( trak
->mdia
->minf
) )
1321 /* Default Track Header Box. */
1323 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1324 if( media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
)
1325 tkhd
->volume
= 0x0100;
1326 tkhd
->matrix
[0] = 0x00010000;
1327 tkhd
->matrix
[4] = 0x00010000;
1328 tkhd
->matrix
[8] = 0x40000000;
1329 tkhd
->duration
= 0xffffffff;
1330 tkhd
->track_ID
= trak
->file
->moov
->mvhd
->next_track_ID
++;
1332 trak
->mdia
->mdhd
->language
= file
->qt_compatible
? 0 : ISOM_LANGUAGE_CODE_UNDEFINED
;
1335 isom_remove_box_by_itself( trak
);
1339 isom_moov_t
*isom_movie_create( lsmash_file_t
*file
)
1341 isom_moov_t
*moov
= isom_add_moov( file
);
1342 isom_mvhd_t
*mvhd
= isom_add_mvhd( moov
);
1345 isom_remove_box_by_itself( moov
);
1348 /* Default Movie Header Box. */
1349 mvhd
->rate
= 0x00010000;
1350 mvhd
->volume
= 0x0100;
1351 mvhd
->matrix
[0] = 0x00010000;
1352 mvhd
->matrix
[4] = 0x00010000;
1353 mvhd
->matrix
[8] = 0x40000000;
1354 mvhd
->next_track_ID
= 1;
1355 file
->initializer
= file
;
1359 /*******************************
1361 *******************************/
1363 /*---- track manipulators ----*/
1365 void lsmash_delete_track( lsmash_root_t
*root
, uint32_t track_ID
)
1367 if( isom_check_initializer_present( root
) < 0
1368 || !root
->file
->initializer
->moov
)
1370 for( lsmash_entry_t
*entry
= root
->file
->initializer
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
1372 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
1376 if( trak
->tkhd
->track_ID
== track_ID
)
1378 isom_remove_box_by_itself( trak
);
1384 uint32_t lsmash_create_track( lsmash_root_t
*root
, lsmash_media_type media_type
)
1386 if( isom_check_initializer_present( root
) < 0 )
1388 isom_trak_t
*trak
= isom_track_create( root
->file
, media_type
);
1392 return trak
->tkhd
->track_ID
;
1395 uint32_t lsmash_get_track_ID( lsmash_root_t
*root
, uint32_t track_number
)
1397 if( isom_check_initializer_present( root
) < 0
1398 || !root
->file
->initializer
->moov
)
1400 isom_trak_t
*trak
= (isom_trak_t
*)lsmash_get_entry_data( &root
->file
->initializer
->moov
->trak_list
, track_number
);
1404 return trak
->tkhd
->track_ID
;
1407 void lsmash_initialize_track_parameters( lsmash_track_parameters_t
*param
)
1409 memset( param
, 0, sizeof(lsmash_track_parameters_t
) );
1410 param
->audio_volume
= 0x0100;
1411 param
->matrix
[0] = 0x00010000;
1412 param
->matrix
[4] = 0x00010000;
1413 param
->matrix
[8] = 0x40000000;
1416 int lsmash_set_track_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_track_parameters_t
*param
)
1418 if( isom_check_initializer_present( root
) < 0 )
1419 return LSMASH_ERR_FUNCTION_PARAM
;
1420 lsmash_file_t
*file
= root
->file
;
1421 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1424 || !trak
->mdia
->hdlr
1425 || !file
->moov
->mvhd
)
1426 return LSMASH_ERR_NAMELESS
;
1427 /* Prepare Track Aperture Modes if required. */
1428 if( file
->qt_compatible
&& param
->aperture_modes
)
1430 if( !trak
->tapt
&& !isom_add_tapt( trak
) )
1431 return LSMASH_ERR_NAMELESS
;
1432 isom_tapt_t
*tapt
= trak
->tapt
;
1433 if( (!tapt
->clef
&& !isom_add_clef( tapt
))
1434 || (!tapt
->prof
&& !isom_add_prof( tapt
))
1435 || (!tapt
->enof
&& !isom_add_enof( tapt
)) )
1436 return LSMASH_ERR_NAMELESS
;
1439 isom_remove_box_by_itself( trak
->tapt
);
1440 /* Set up Track Header. */
1441 uint32_t media_type
= trak
->mdia
->hdlr
->componentSubtype
;
1442 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1443 tkhd
->flags
= param
->mode
;
1444 tkhd
->track_ID
= param
->track_ID
? param
->track_ID
: tkhd
->track_ID
;
1445 tkhd
->duration
= !trak
->edts
|| !trak
->edts
->elst
? param
->duration
: tkhd
->duration
;
1447 * alternate_group, layer, volume and matrix
1448 * According to 14496-14, these value are all set to defaut values in 14496-12.
1449 * And when a file is read as an MPEG-4 file, these values shall be ignored.
1450 * If a file complies with other specifications, then those fields may have non-default values
1451 * as required by those other specifications. */
1452 if( param
->alternate_group
)
1454 if( file
->qt_compatible
|| file
->itunes_movie
|| file
->max_3gpp_version
>= 4 )
1455 tkhd
->alternate_group
= param
->alternate_group
;
1458 tkhd
->alternate_group
= 0;
1459 lsmash_log( NULL
, LSMASH_LOG_WARNING
,
1460 "alternate_group is specified but not compatible with any of the brands. It won't be set.\n" );
1464 tkhd
->alternate_group
= 0;
1465 if( file
->qt_compatible
|| file
->itunes_movie
)
1467 tkhd
->layer
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->video_layer
: 0;
1468 tkhd
->volume
= media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
? param
->audio_volume
: 0;
1469 if( media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
)
1470 for( int i
= 0; i
< 9; i
++ )
1471 tkhd
->matrix
[i
] = param
->matrix
[i
];
1473 for( int i
= 0; i
< 9; i
++ )
1474 tkhd
->matrix
[i
] = 0;
1479 tkhd
->volume
= media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
? 0x0100 : 0;
1480 tkhd
->matrix
[0] = 0x00010000;
1481 tkhd
->matrix
[1] = 0;
1482 tkhd
->matrix
[2] = 0;
1483 tkhd
->matrix
[3] = 0;
1484 tkhd
->matrix
[4] = 0x00010000;
1485 tkhd
->matrix
[5] = 0;
1486 tkhd
->matrix
[6] = 0;
1487 tkhd
->matrix
[7] = 0;
1488 tkhd
->matrix
[8] = 0x40000000;
1490 /* visual presentation size */
1491 tkhd
->width
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->display_width
: 0;
1492 tkhd
->height
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->display_height
: 0;
1493 /* Update next_track_ID if needed. */
1494 if( file
->moov
->mvhd
->next_track_ID
<= tkhd
->track_ID
)
1495 file
->moov
->mvhd
->next_track_ID
= tkhd
->track_ID
+ 1;
1499 int lsmash_get_track_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_track_parameters_t
*param
)
1501 if( isom_check_initializer_present( root
) < 0 )
1502 return LSMASH_ERR_FUNCTION_PARAM
;
1503 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1505 return LSMASH_ERR_NAMELESS
;
1506 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1507 param
->mode
= tkhd
->flags
;
1508 param
->track_ID
= tkhd
->track_ID
;
1509 param
->duration
= tkhd
->duration
;
1510 param
->video_layer
= tkhd
->layer
;
1511 param
->alternate_group
= tkhd
->alternate_group
;
1512 param
->audio_volume
= tkhd
->volume
;
1513 for( int i
= 0; i
< 9; i
++ )
1514 param
->matrix
[i
] = tkhd
->matrix
[i
];
1515 param
->display_width
= tkhd
->width
;
1516 param
->display_height
= tkhd
->height
;
1517 param
->aperture_modes
= !!trak
->tapt
;
1521 static inline int check_dref_presence( isom_trak_t
*trak
)
1525 || !trak
->mdia
->minf
1526 || !trak
->mdia
->minf
->dinf
1527 || !trak
->mdia
->minf
->dinf
->dref
)
1528 return LSMASH_ERR_NAMELESS
;
1532 uint32_t lsmash_count_data_reference
1534 lsmash_root_t
*root
,
1538 if( isom_check_initializer_present( root
) < 0 )
1540 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1541 if( check_dref_presence( trak
) < 0 )
1543 return trak
->mdia
->minf
->dinf
->dref
->list
.entry_count
;
1546 int lsmash_get_data_reference
1548 lsmash_root_t
*root
,
1550 lsmash_data_reference_t
*data_ref
1553 if( isom_check_initializer_present( root
) < 0 || !data_ref
)
1554 return LSMASH_ERR_FUNCTION_PARAM
;
1555 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1556 if( check_dref_presence( trak
) < 0 )
1557 return LSMASH_ERR_NAMELESS
;
1558 isom_dref_entry_t
*url
= lsmash_get_entry_data( &trak
->mdia
->minf
->dinf
->dref
->list
, data_ref
->index
);
1560 return LSMASH_ERR_NAMELESS
;
1561 if( !(url
->flags
& 0x000001) && url
->location
)
1563 int length
= strlen( url
->location
);
1564 char *location
= lsmash_malloc( length
+ 1 );
1566 return LSMASH_ERR_MEMORY_ALLOC
;
1567 memcpy( location
, url
->location
, length
);
1568 location
[length
] = '\0';
1569 data_ref
->location
= location
;
1572 data_ref
->location
= NULL
;
1576 void lsmash_cleanup_data_reference
1578 lsmash_data_reference_t
*data_ref
1583 lsmash_freep( &data_ref
->location
);
1586 int lsmash_create_data_reference
1588 lsmash_root_t
*root
,
1590 lsmash_data_reference_t
*data_ref
,
1594 /* At present, we don't support external data references for movie fragments.
1595 * Note that, for external media data, default-base-is-moof is meaningless since relative
1596 * offsets from Movie Fragment Boxes make no sense.
1597 * In the future, the condition of !(file->flags & LSMASH_FILE_MODE_WRITE) may be removed
1598 * for the implementation which does not write actually and does reference read-only file. */
1599 if( !root
|| !file
|| file
->root
!= root
1600 || (!(file
->flags
& LSMASH_FILE_MODE_MEDIA
) && !(file
->flags
& LSMASH_FILE_MODE_INITIALIZATION
))
1601 || !(file
->flags
& LSMASH_FILE_MODE_WRITE
)
1602 || (root
->file
!= file
&& ((file
->flags
& LSMASH_FILE_MODE_FRAGMENTED
) || file
->fragment
))
1604 return LSMASH_ERR_FUNCTION_PARAM
;
1605 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
1606 if( check_dref_presence( trak
) < 0 )
1607 return LSMASH_ERR_NAMELESS
;
1608 isom_dref_entry_t
*url
= isom_add_dref_entry( trak
->mdia
->minf
->dinf
->dref
, ISOM_BOX_TYPE_URL
);
1610 return LSMASH_ERR_NAMELESS
;
1611 if( !data_ref
->location
|| root
->file
== file
)
1613 /* Media data is in the same file. */
1614 url
->flags
= 0x000001;
1615 url
->ref_file
= root
->file
;
1619 /* Set the location of the file. */
1620 int length
= strlen( data_ref
->location
);
1621 url
->location
= lsmash_malloc( length
+ 1 );
1622 if( !url
->location
)
1624 isom_remove_box_by_itself( url
);
1625 return LSMASH_ERR_MEMORY_ALLOC
;
1627 memcpy( url
->location
, data_ref
->location
, length
);
1628 url
->location
[length
] = '\0';
1629 url
->location_length
= length
+ 1;
1630 url
->ref_file
= file
;
1632 data_ref
->index
= trak
->mdia
->minf
->dinf
->dref
->list
.entry_count
;
1636 int lsmash_assign_data_reference
1638 lsmash_root_t
*root
,
1640 uint32_t data_ref_index
,
1644 if( isom_check_initializer_present( root
) < 0
1645 || !file
|| file
->root
!= root
1646 || !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
1647 || !(file
->flags
& LSMASH_FILE_MODE_READ
)
1648 || data_ref_index
== 0 )
1649 return LSMASH_ERR_FUNCTION_PARAM
;
1650 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1651 if( check_dref_presence( trak
) < 0 )
1652 return LSMASH_ERR_NAMELESS
;
1653 isom_dref_entry_t
*url
= (isom_dref_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->dinf
->dref
->list
, data_ref_index
);
1655 return LSMASH_ERR_NAMELESS
;
1656 if( !(url
->flags
& 0x000001) )
1657 /* Reference an external media data. */
1658 url
->ref_file
= file
;
1662 static int isom_set_media_handler_name( lsmash_file_t
*file
, uint32_t track_ID
, char *handler_name
)
1664 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1667 || !trak
->mdia
->hdlr
)
1668 return LSMASH_ERR_NAMELESS
;
1669 isom_hdlr_t
*hdlr
= trak
->mdia
->hdlr
;
1670 uint8_t *name
= NULL
;
1671 uint32_t name_length
= strlen( handler_name
) + file
->isom_compatible
+ file
->qt_compatible
;
1672 if( file
->qt_compatible
)
1673 name_length
= LSMASH_MIN( name_length
, 255 );
1674 if( name_length
> hdlr
->componentName_length
&& hdlr
->componentName
)
1675 name
= lsmash_realloc( hdlr
->componentName
, name_length
);
1676 else if( !hdlr
->componentName
)
1677 name
= lsmash_malloc( name_length
);
1679 name
= hdlr
->componentName
;
1681 return LSMASH_ERR_MEMORY_ALLOC
;
1682 if( file
->qt_compatible
)
1683 name
[0] = name_length
& 0xff;
1684 memcpy( name
+ file
->qt_compatible
, handler_name
, strlen( handler_name
) );
1685 if( file
->isom_compatible
)
1686 name
[name_length
- 1] = 0;
1687 hdlr
->componentName
= name
;
1688 hdlr
->componentName_length
= name_length
;
1692 static int isom_set_data_handler_name( lsmash_file_t
*file
, uint32_t track_ID
, char *handler_name
)
1694 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1697 || !trak
->mdia
->minf
1698 || !trak
->mdia
->minf
->hdlr
)
1699 return LSMASH_ERR_NAMELESS
;
1700 isom_hdlr_t
*hdlr
= trak
->mdia
->minf
->hdlr
;
1701 uint8_t *name
= NULL
;
1702 uint32_t name_length
= strlen( handler_name
) + file
->isom_compatible
+ file
->qt_compatible
;
1703 if( file
->qt_compatible
)
1704 name_length
= LSMASH_MIN( name_length
, 255 );
1705 if( name_length
> hdlr
->componentName_length
&& hdlr
->componentName
)
1706 name
= lsmash_realloc( hdlr
->componentName
, name_length
);
1707 else if( !hdlr
->componentName
)
1708 name
= lsmash_malloc( name_length
);
1710 name
= hdlr
->componentName
;
1712 return LSMASH_ERR_MEMORY_ALLOC
;
1713 if( file
->qt_compatible
)
1714 name
[0] = name_length
& 0xff;
1715 memcpy( name
+ file
->qt_compatible
, handler_name
, strlen( handler_name
) );
1716 if( file
->isom_compatible
)
1717 name
[name_length
- 1] = 0;
1718 hdlr
->componentName
= name
;
1719 hdlr
->componentName_length
= name_length
;
1723 uint32_t lsmash_get_media_timescale( lsmash_root_t
*root
, uint32_t track_ID
)
1725 if( isom_check_initializer_present( root
) < 0 )
1727 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1730 || !trak
->mdia
->mdhd
)
1732 return trak
->mdia
->mdhd
->timescale
;
1735 uint64_t lsmash_get_media_duration( lsmash_root_t
*root
, uint32_t track_ID
)
1737 if( isom_check_initializer_present( root
) < 0 )
1739 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1742 || !trak
->mdia
->mdhd
)
1744 return trak
->mdia
->mdhd
->duration
;
1747 uint64_t lsmash_get_track_duration( lsmash_root_t
*root
, uint32_t track_ID
)
1749 if( isom_check_initializer_present( root
) < 0 )
1751 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1755 return trak
->tkhd
->duration
;
1758 uint32_t lsmash_get_last_sample_delta( lsmash_root_t
*root
, uint32_t track_ID
)
1760 if( isom_check_initializer_present( root
) < 0 )
1762 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
1765 || !trak
->mdia
->minf
1766 || !trak
->mdia
->minf
->stbl
1767 || !trak
->mdia
->minf
->stbl
->stts
1768 || !trak
->mdia
->minf
->stbl
->stts
->list
1769 || !trak
->mdia
->minf
->stbl
->stts
->list
->tail
1770 || !trak
->mdia
->minf
->stbl
->stts
->list
->tail
->data
)
1772 return ((isom_stts_entry_t
*)trak
->mdia
->minf
->stbl
->stts
->list
->tail
->data
)->sample_delta
;
1775 uint32_t lsmash_get_start_time_offset( lsmash_root_t
*root
, uint32_t track_ID
)
1777 if( isom_check_initializer_present( root
) < 0 )
1779 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
1782 || !trak
->mdia
->minf
1783 || !trak
->mdia
->minf
->stbl
1784 || !trak
->mdia
->minf
->stbl
->ctts
1785 || !trak
->mdia
->minf
->stbl
->ctts
->list
1786 || !trak
->mdia
->minf
->stbl
->ctts
->list
->head
1787 || !trak
->mdia
->minf
->stbl
->ctts
->list
->head
->data
)
1789 return ((isom_ctts_entry_t
*)trak
->mdia
->minf
->stbl
->ctts
->list
->head
->data
)->sample_offset
;
1792 uint32_t lsmash_get_composition_to_decode_shift( lsmash_root_t
*root
, uint32_t track_ID
)
1794 if( isom_check_initializer_present( root
) < 0 )
1796 lsmash_file_t
*file
= root
->file
->initializer
;
1797 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1800 || !trak
->mdia
->minf
1801 || !trak
->mdia
->minf
->stbl
)
1803 uint32_t sample_count
= isom_get_sample_count( trak
);
1804 if( sample_count
== 0 )
1806 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
1807 if( !stbl
->stts
|| !stbl
->stts
->list
1808 || !stbl
->ctts
|| !stbl
->ctts
->list
)
1810 if( !(file
->max_isom_version
>= 4 && stbl
->ctts
->version
== 1) && !file
->qt_compatible
)
1811 return 0; /* This movie shall not have composition to decode timeline shift. */
1812 lsmash_entry_t
*stts_entry
= stbl
->stts
->list
->head
;
1813 lsmash_entry_t
*ctts_entry
= stbl
->ctts
->list
->head
;
1814 if( !stts_entry
|| !ctts_entry
)
1818 uint32_t ctd_shift
= 0;
1821 for( uint32_t k
= 0; k
< sample_count
; k
++ )
1823 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
1824 isom_ctts_entry_t
*ctts_data
= (isom_ctts_entry_t
*)ctts_entry
->data
;
1825 if( !stts_data
|| !ctts_data
)
1827 cts
= dts
+ (int32_t)ctts_data
->sample_offset
;
1828 if( dts
> cts
+ ctd_shift
)
1829 ctd_shift
= dts
- cts
;
1830 dts
+= stts_data
->sample_delta
;
1831 if( ++i
== stts_data
->sample_count
)
1833 stts_entry
= stts_entry
->next
;
1838 if( ++j
== ctts_data
->sample_count
)
1840 ctts_entry
= ctts_entry
->next
;
1849 uint16_t lsmash_pack_iso_language( char *iso_language
)
1851 if( !iso_language
|| strlen( iso_language
) != 3 )
1853 return (uint16_t)LSMASH_PACK_ISO_LANGUAGE( iso_language
[0], iso_language
[1], iso_language
[2] );
1856 static int isom_iso2mac_language( uint16_t ISO_language
, uint16_t *MAC_language
)
1858 assert( MAC_language
);
1860 for( ; isom_languages
[i
].iso_name
; i
++ )
1861 if( ISO_language
== isom_languages
[i
].iso_name
)
1863 if( !isom_languages
[i
].iso_name
)
1864 return LSMASH_ERR_NAMELESS
;
1865 *MAC_language
= isom_languages
[i
].mac_value
;
1869 static int isom_mac2iso_language( uint16_t MAC_language
, uint16_t *ISO_language
)
1871 assert( ISO_language
);
1873 for( ; isom_languages
[i
].iso_name
; i
++ )
1874 if( MAC_language
== isom_languages
[i
].mac_value
)
1876 *ISO_language
= isom_languages
[i
].iso_name
? isom_languages
[i
].iso_name
: ISOM_LANGUAGE_CODE_UNDEFINED
;
1880 static int isom_set_media_language( lsmash_file_t
*file
, uint32_t track_ID
, uint16_t ISO_language
, uint16_t MAC_language
)
1882 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1885 || !trak
->mdia
->mdhd
)
1886 return LSMASH_ERR_NAMELESS
;
1887 uint16_t language
= 0;
1888 if( file
->isom_compatible
)
1891 language
= ISO_language
;
1892 else if( MAC_language
)
1894 int err
= isom_mac2iso_language( MAC_language
, &language
);
1899 language
= ISOM_LANGUAGE_CODE_UNDEFINED
;
1901 else if( file
->qt_compatible
)
1905 int err
= isom_iso2mac_language( ISO_language
, &language
);
1910 language
= MAC_language
;
1913 return LSMASH_ERR_INVALID_DATA
;
1914 trak
->mdia
->mdhd
->language
= language
;
1918 int isom_add_sample_grouping( isom_box_t
*parent
, isom_grouping_type grouping_type
)
1922 if( NULL
== (sgpd
= isom_add_sgpd( parent
))
1923 || NULL
== (sbgp
= isom_add_sbgp( parent
)) )
1924 return LSMASH_ERR_NAMELESS
;
1925 sbgp
->grouping_type
= grouping_type
;
1926 sgpd
->grouping_type
= grouping_type
;
1927 sgpd
->version
= 1; /* We use version 1 for Sample Group Description Box because it is recommended in the spec. */
1928 switch( grouping_type
)
1930 case ISOM_GROUP_TYPE_RAP
:
1931 sgpd
->default_length
= 1;
1933 case ISOM_GROUP_TYPE_ROLL
:
1934 case ISOM_GROUP_TYPE_PROL
:
1935 sgpd
->default_length
= 2;
1938 /* We don't consider other grouping types currently. */
1944 static int isom_create_sample_grouping( isom_trak_t
*trak
, isom_grouping_type grouping_type
)
1946 lsmash_file_t
*file
= trak
->file
;
1947 switch( grouping_type
)
1949 case ISOM_GROUP_TYPE_RAP
:
1950 assert( file
->max_isom_version
>= 6 );
1952 case ISOM_GROUP_TYPE_ROLL
:
1953 case ISOM_GROUP_TYPE_PROL
:
1954 assert( file
->avc_extensions
|| file
->qt_compatible
);
1960 int err
= isom_add_sample_grouping( (isom_box_t
*)trak
->mdia
->minf
->stbl
, grouping_type
);
1963 if( trak
->cache
->fragment
&& file
->max_isom_version
>= 6 )
1964 switch( grouping_type
)
1966 case ISOM_GROUP_TYPE_RAP
:
1967 trak
->cache
->fragment
->rap_grouping
= 1;
1969 case ISOM_GROUP_TYPE_ROLL
:
1970 case ISOM_GROUP_TYPE_PROL
:
1971 trak
->cache
->fragment
->roll_grouping
= 1;
1974 /* We don't consider other grouping types currently. */
1980 static int isom_compress_sample_size_table( isom_stbl_t
*stbl
)
1982 if( stbl
->file
->max_3gpp_version
)
1983 /* 3GPP: Limitations to the ISO base media file format
1984 * - compact sample sizes ('stz2') shall not be used for tracks containing H.263, MPEG-4 video, AMR, AMR-WB, AAC or Timed text.
1985 * Note that 'mp4a' check is incomplete here since this restriction is not applied to Enhanced aacPlus audio (HE-AAC v2). */
1986 for( lsmash_entry_t
*entry
= stbl
->stsd
->list
.head
; entry
; entry
= entry
->next
)
1988 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)entry
->data
;
1990 return LSMASH_ERR_INVALID_DATA
;
1991 lsmash_codec_type_t sample_type
= sample_entry
->type
;
1992 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_S263_VIDEO
)
1993 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4V_VIDEO
)
1994 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4A_AUDIO
)
1995 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_SAMR_AUDIO
)
1996 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_SAWB_AUDIO
)
1997 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_TX3G_TEXT
) )
2000 if( stbl
->stsz
&& isom_is_variable_size( stbl
) )
2002 int max_num_bits
= 0;
2003 for( lsmash_entry_t
*entry
= stbl
->stsz
->list
->head
; entry
; entry
= entry
->next
)
2005 isom_stsz_entry_t
*data
= (isom_stsz_entry_t
*)entry
->data
;
2007 return LSMASH_ERR_INVALID_DATA
;
2009 for( num_bits
= 1; data
->entry_size
>> num_bits
; num_bits
++ );
2010 if( max_num_bits
< num_bits
)
2012 max_num_bits
= num_bits
;
2013 if( max_num_bits
> 16 )
2014 return 0; /* not compressible */
2017 if( max_num_bits
<= 16 && isom_add_stz2( stbl
) )
2019 /* The sample size table can be compressed by using 'stz2'. */
2020 isom_stsz_t
*stsz
= stbl
->stsz
;
2021 isom_stz2_t
*stz2
= stbl
->stz2
;
2022 stz2
->sample_count
= stsz
->sample_count
;
2023 if( max_num_bits
<= 4 )
2024 stz2
->field_size
= 4;
2025 else if( max_num_bits
<= 8 )
2026 stz2
->field_size
= 8;
2028 stz2
->field_size
= 16;
2029 lsmash_move_entries( stz2
->list
, stsz
->list
);
2030 isom_remove_box_by_itself( stsz
);
2036 static int isom_add_dependency_type( isom_stbl_t
*stbl
, lsmash_file_t
*file
, lsmash_sample_property_t
*prop
)
2038 if( !file
->qt_compatible
&& !file
->avc_extensions
)
2040 int compatibility
= file
->avc_extensions
&& file
->qt_compatible
? 3
2041 : file
->qt_compatible
? 2
2042 : file
->avc_extensions
? 1
2045 return isom_add_sdtp_entry( (isom_box_t
*)stbl
, prop
, compatibility
);
2046 /* no null check for prop */
2047 if( !prop
->allow_earlier
2049 && !prop
->independent
2050 && !prop
->disposable
2051 && !prop
->redundant
)
2053 if( !isom_add_sdtp( (isom_box_t
*)stbl
) )
2054 return LSMASH_ERR_NAMELESS
;
2055 uint32_t count
= isom_get_sample_count_from_sample_table( stbl
);
2056 /* fill past samples with ISOM_SAMPLE_*_UNKNOWN */
2057 lsmash_sample_property_t null_prop
= { 0 };
2058 for( uint32_t i
= 1; i
< count
; i
++ )
2060 int err
= isom_add_sdtp_entry( (isom_box_t
*)stbl
, &null_prop
, compatibility
);
2064 return isom_add_sdtp_entry( (isom_box_t
*)stbl
, prop
, compatibility
);
2067 void lsmash_initialize_media_parameters( lsmash_media_parameters_t
*param
)
2069 memset( param
, 0, sizeof(lsmash_media_parameters_t
) );
2070 param
->timescale
= 1;
2073 int lsmash_set_media_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_media_parameters_t
*param
)
2075 if( isom_check_initializer_present( root
) < 0 )
2076 return LSMASH_ERR_FUNCTION_PARAM
;
2077 lsmash_file_t
*file
= root
->file
;
2078 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2081 || !trak
->mdia
->mdhd
2082 || !trak
->mdia
->minf
2083 || !trak
->mdia
->minf
->stbl
)
2084 return LSMASH_ERR_NAMELESS
;
2085 trak
->mdia
->mdhd
->timescale
= param
->timescale
;
2086 int err
= isom_set_media_language( file
, track_ID
, param
->ISO_language
, param
->MAC_language
);
2089 if( param
->media_handler_name
2090 && (err
= isom_set_media_handler_name( file
, track_ID
, param
->media_handler_name
)) < 0 )
2092 if( file
->qt_compatible
&& param
->data_handler_name
2093 && (err
= isom_set_data_handler_name( file
, track_ID
, param
->data_handler_name
)) < 0 )
2095 if( (file
->avc_extensions
|| file
->qt_compatible
) && param
->roll_grouping
2096 && (err
= isom_create_sample_grouping( trak
, ISOM_GROUP_TYPE_ROLL
)) < 0 )
2098 if( (file
->max_isom_version
>= 6) && param
->rap_grouping
2099 && (err
= isom_create_sample_grouping( trak
, ISOM_GROUP_TYPE_RAP
)) < 0 )
2101 if( !file
->qt_compatible
&& param
->compact_sample_size_table
)
2102 trak
->mdia
->minf
->stbl
->compress_sample_size_table
= isom_compress_sample_size_table
;
2103 if( !param
->no_sample_dependency_table
)
2104 trak
->mdia
->minf
->stbl
->add_dependency_type
= isom_add_dependency_type
;
2108 static uint32_t get_actual_handler_name_length( isom_hdlr_t
*hdlr
, lsmash_file_t
*file
)
2110 if( hdlr
->componentName_length
== 0 )
2114 if( file
->qt_compatible
)
2116 length
= LSMASH_MIN( hdlr
->componentName
[0], hdlr
->componentName_length
- 1 );
2117 if( !file
->isom_compatible
)
2119 name
= &hdlr
->componentName
[1];
2123 length
= hdlr
->componentName_length
;
2124 name
= hdlr
->componentName
;
2126 /* Considering fool-proof such as not terminated by '\0'. */
2128 while( i
< length
&& name
[i
] )
2133 int lsmash_get_media_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_media_parameters_t
*param
)
2135 if( isom_check_initializer_present( root
) < 0 )
2136 return LSMASH_ERR_FUNCTION_PARAM
;
2137 lsmash_file_t
*file
= root
->file
->initializer
;
2138 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2141 || !trak
->mdia
->mdhd
2142 || !trak
->mdia
->hdlr
2143 || !trak
->mdia
->minf
2144 || !trak
->mdia
->minf
->stbl
)
2145 return LSMASH_ERR_NAMELESS
;
2146 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
2147 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2148 param
->timescale
= mdhd
->timescale
;
2149 param
->handler_type
= trak
->mdia
->hdlr
->componentSubtype
;
2150 param
->duration
= mdhd
->duration
;
2151 /* Whether sample grouping present. */
2155 sbgp
= isom_get_sample_to_group ( stbl
, ISOM_GROUP_TYPE_RAP
);
2156 sgpd
= isom_get_sample_group_description( stbl
, ISOM_GROUP_TYPE_RAP
);
2157 param
->rap_grouping
= sbgp
&& sgpd
;
2158 sbgp
= isom_get_roll_recovery_sample_to_group ( &stbl
->sbgp_list
);
2159 sgpd
= isom_get_roll_recovery_sample_group_description( &stbl
->sgpd_list
);
2160 param
->roll_grouping
= sbgp
&& sgpd
;
2162 /* Get media language. */
2163 if( mdhd
->language
>= 0x800 )
2165 param
->MAC_language
= 0;
2166 param
->ISO_language
= mdhd
->language
;
2170 param
->MAC_language
= mdhd
->language
;
2171 param
->ISO_language
= 0;
2173 /* Get handler name(s). */
2174 isom_hdlr_t
*hdlr
= trak
->mdia
->hdlr
;
2175 uint32_t actual_length
= get_actual_handler_name_length( hdlr
, file
);
2176 uint32_t length
= LSMASH_MIN( 255, actual_length
);
2179 memcpy( param
->media_handler_name_shadow
, hdlr
->componentName
+ file
->qt_compatible
, length
);
2180 param
->media_handler_name_shadow
[length
] = '\0';
2181 param
->media_handler_name
= param
->media_handler_name_shadow
;
2185 param
->media_handler_name
= NULL
;
2186 memset( param
->media_handler_name_shadow
, 0, sizeof(param
->media_handler_name_shadow
) );
2188 if( trak
->mdia
->minf
->hdlr
)
2190 hdlr
= trak
->mdia
->minf
->hdlr
;
2191 actual_length
= get_actual_handler_name_length( hdlr
, file
);
2192 length
= LSMASH_MIN( 255, actual_length
);
2195 memcpy( param
->data_handler_name_shadow
, hdlr
->componentName
+ file
->qt_compatible
, length
);
2196 param
->data_handler_name_shadow
[length
] = '\0';
2197 param
->data_handler_name
= param
->data_handler_name_shadow
;
2201 param
->data_handler_name
= NULL
;
2202 memset( param
->data_handler_name_shadow
, 0, sizeof(param
->data_handler_name_shadow
) );
2207 param
->data_handler_name
= NULL
;
2208 memset( param
->data_handler_name_shadow
, 0, sizeof(param
->data_handler_name_shadow
) );
2210 param
->compact_sample_size_table
= !!stbl
->stz2
;
2211 param
->no_sample_dependency_table
= !!stbl
->sdtp
;
2212 param
->reserved
[0] = param
->reserved
[1] = 0;
2216 /*---- movie manipulators ----*/
2218 void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t
*param
)
2220 memset( param
, 0, sizeof(lsmash_movie_parameters_t
) );
2221 param
->timescale
= 600;
2222 param
->playback_rate
= 0x00010000;
2223 param
->playback_volume
= 0x0100;
2226 int lsmash_set_movie_parameters( lsmash_root_t
*root
, lsmash_movie_parameters_t
*param
)
2229 return LSMASH_ERR_FUNCTION_PARAM
;
2230 lsmash_file_t
*file
= root
->file
;
2233 || !file
->moov
->mvhd
)
2234 return LSMASH_ERR_NAMELESS
;
2235 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
2236 mvhd
->timescale
= param
->timescale
;
2237 if( file
->qt_compatible
|| file
->itunes_movie
)
2239 mvhd
->rate
= param
->playback_rate
;
2240 mvhd
->volume
= param
->playback_volume
;
2241 mvhd
->previewTime
= param
->preview_time
;
2242 mvhd
->previewDuration
= param
->preview_duration
;
2243 mvhd
->posterTime
= param
->poster_time
;
2247 mvhd
->rate
= 0x00010000;
2248 mvhd
->volume
= 0x0100;
2249 mvhd
->previewTime
= 0;
2250 mvhd
->previewDuration
= 0;
2251 mvhd
->posterTime
= 0;
2256 int lsmash_get_movie_parameters( lsmash_root_t
*root
, lsmash_movie_parameters_t
*param
)
2258 if( isom_check_initializer_present( root
) < 0 )
2259 return LSMASH_ERR_FUNCTION_PARAM
;
2260 lsmash_file_t
*file
= root
->file
->initializer
;
2262 || !file
->moov
->mvhd
)
2263 return LSMASH_ERR_NAMELESS
;
2264 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
2265 param
->timescale
= mvhd
->timescale
;
2266 param
->duration
= mvhd
->duration
;
2267 param
->playback_rate
= mvhd
->rate
;
2268 param
->playback_volume
= mvhd
->volume
;
2269 param
->preview_time
= mvhd
->previewTime
;
2270 param
->preview_duration
= mvhd
->previewDuration
;
2271 param
->poster_time
= mvhd
->posterTime
;
2272 param
->number_of_tracks
= file
->moov
->trak_list
.entry_count
;
2276 uint32_t lsmash_get_movie_timescale( lsmash_root_t
*root
)
2278 if( isom_check_initializer_present( root
) < 0
2279 || !root
->file
->initializer
->moov
2280 || !root
->file
->initializer
->moov
->mvhd
)
2282 return root
->file
->initializer
->moov
->mvhd
->timescale
;
2285 static int isom_scan_trak_profileLevelIndication
2288 mp4a_audioProfileLevelIndication
*audio_pli
,
2289 mp4sys_visualProfileLevelIndication
*visual_pli
2294 || !trak
->mdia
->minf
2295 || !trak
->mdia
->minf
->stbl
)
2296 return LSMASH_ERR_INVALID_DATA
;
2297 isom_stsd_t
*stsd
= trak
->mdia
->minf
->stbl
->stsd
;
2299 || !stsd
->list
.head
)
2300 return LSMASH_ERR_INVALID_DATA
;
2301 for( lsmash_entry_t
*entry
= stsd
->list
.head
; entry
; entry
= entry
->next
)
2303 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)entry
->data
;
2305 return LSMASH_ERR_INVALID_DATA
;
2306 lsmash_codec_type_t sample_type
= sample_entry
->type
;
2307 if( trak
->mdia
->minf
->vmhd
)
2309 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC1_VIDEO
)
2310 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC2_VIDEO
)
2311 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC3_VIDEO
)
2312 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC4_VIDEO
)
2313 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVCP_VIDEO
)
2314 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_SVC1_VIDEO
)
2315 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MVC1_VIDEO
)
2316 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MVC2_VIDEO
) )
2318 /* FIXME: Do we have to arbitrate like audio? */
2319 if( *visual_pli
== MP4SYS_VISUAL_PLI_NONE_REQUIRED
)
2320 *visual_pli
= MP4SYS_VISUAL_PLI_H264_AVC
;
2323 *visual_pli
= MP4SYS_VISUAL_PLI_NOT_SPECIFIED
;
2325 else if( trak
->mdia
->minf
->smhd
)
2327 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4A_AUDIO
) )
2329 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)sample_entry
;
2330 isom_esds_t
*esds
= (isom_esds_t
*)isom_get_extension_box_format( &audio
->extensions
, ISOM_BOX_TYPE_ESDS
);
2331 if( !esds
|| !esds
->ES
)
2332 return LSMASH_ERR_INVALID_DATA
;
2333 lsmash_audio_summary_t
*summary
= (lsmash_audio_summary_t
*)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO
);
2336 if( mp4sys_setup_summary_from_DecoderSpecificInfo( summary
, esds
->ES
) < 0 )
2337 *audio_pli
= MP4A_AUDIO_PLI_NOT_SPECIFIED
;
2339 *audio_pli
= mp4a_max_audioProfileLevelIndication( *audio_pli
, mp4a_get_audioProfileLevelIndication( summary
) );
2340 lsmash_cleanup_summary( (lsmash_summary_t
*)summary
);
2343 /* NOTE: Audio CODECs other than 'mp4a' does not have appropriate pli. */
2344 *audio_pli
= MP4A_AUDIO_PLI_NOT_SPECIFIED
;
2347 ; /* FIXME: Do we have to set OD_profileLevelIndication? */
2352 int isom_setup_iods( isom_moov_t
*moov
)
2354 if( !moov
->iods
&& !isom_add_iods( moov
) )
2355 return LSMASH_ERR_NAMELESS
;
2356 isom_iods_t
*iods
= moov
->iods
;
2357 int err
= LSMASH_ERR_NAMELESS
;
2358 iods
->OD
= mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */
2361 mp4a_audioProfileLevelIndication audio_pli
= MP4A_AUDIO_PLI_NONE_REQUIRED
;
2362 mp4sys_visualProfileLevelIndication visual_pli
= MP4SYS_VISUAL_PLI_NONE_REQUIRED
;
2363 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
2365 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2369 if( (err
= isom_scan_trak_profileLevelIndication( trak
, &audio_pli
, &visual_pli
)) < 0 )
2371 if( (err
= mp4sys_create_ES_ID_Inc( iods
->OD
, trak
->tkhd
->track_ID
)) < 0 )
2374 if( (err
= mp4sys_to_InitialObjectDescriptor( iods
->OD
,
2375 0, /* FIXME: I'm not quite sure what the spec says. */
2376 MP4SYS_OD_PLI_NONE_REQUIRED
, MP4SYS_SCENE_PLI_NONE_REQUIRED
,
2377 audio_pli
, visual_pli
,
2378 MP4SYS_GRAPHICS_PLI_NONE_REQUIRED
)) < 0 )
2382 isom_remove_box_by_itself( iods
);
2386 int lsmash_create_object_descriptor( lsmash_root_t
*root
)
2388 if( isom_check_initializer_present( root
) < 0 )
2389 return LSMASH_ERR_FUNCTION_PARAM
;
2390 lsmash_file_t
*file
= root
->file
;
2391 /* Return error if this file is not compatible with MP4 file format. */
2392 if( !file
->mp4_version1
2393 && !file
->mp4_version2
)
2394 return LSMASH_ERR_FUNCTION_PARAM
;
2395 return isom_setup_iods( file
->moov
);
2398 /*---- finishing functions ----*/
2400 int isom_complement_data_reference( isom_minf_t
*minf
)
2403 || !minf
->dinf
->dref
)
2404 return LSMASH_ERR_INVALID_DATA
;
2405 /* Complement data referece if absent. */
2406 if( !minf
->dinf
->dref
->list
.head
)
2408 isom_dref_entry_t
*url
= isom_add_dref_entry( minf
->dinf
->dref
, ISOM_BOX_TYPE_URL
);
2410 return LSMASH_ERR_NAMELESS
;
2411 url
->flags
= 0x000001; /* Media data is in the same file. */
2416 static lsmash_file_t
*isom_get_written_media_file
2419 uint32_t sample_description_index
2422 isom_minf_t
*minf
= trak
->mdia
->minf
;
2423 isom_sample_entry_t
*description
= (isom_sample_entry_t
*)lsmash_get_entry_data( &minf
->stbl
->stsd
->list
, sample_description_index
);
2424 isom_dref_entry_t
*dref_entry
= (isom_dref_entry_t
*)lsmash_get_entry_data( &minf
->dinf
->dref
->list
, description
? description
->data_reference_index
: 1 );
2425 lsmash_file_t
*file
= (!dref_entry
|| !dref_entry
->ref_file
) ? trak
->file
: dref_entry
->ref_file
;
2426 if( !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
2427 || !(file
->flags
& LSMASH_FILE_MODE_WRITE
) )
2432 int isom_check_large_offset_requirement
2438 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; )
2440 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2441 isom_stco_t
*stco
= trak
->mdia
->minf
->stbl
->stco
;
2442 if( !stco
->list
->tail
/* no samples */
2443 || stco
->large_presentation
2444 || (((isom_stco_entry_t
*)stco
->list
->tail
->data
)->chunk_offset
+ moov
->size
+ meta_size
) <= UINT32_MAX
)
2446 entry
= entry
->next
;
2447 continue; /* no need to convert stco into co64 */
2449 /* stco->co64 conversion */
2450 int err
= isom_convert_stco_to_co64( trak
->mdia
->minf
->stbl
);
2453 if( isom_update_box_size( moov
) == 0 )
2454 return LSMASH_ERR_INVALID_DATA
;
2455 entry
= moov
->trak_list
.head
; /* whenever any conversion, re-check all traks */
2460 void isom_add_preceding_box_size
2463 uint64_t preceding_size
2466 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
2468 /* Apply to the chunks in the same file. */
2469 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2470 isom_stsc_t
*stsc
= trak
->mdia
->minf
->stbl
->stsc
;
2471 isom_stco_t
*stco
= trak
->mdia
->minf
->stbl
->stco
;
2472 lsmash_entry_t
*stsc_entry
= stsc
->list
->head
;
2473 isom_stsc_entry_t
*stsc_data
= stsc_entry
? (isom_stsc_entry_t
*)stsc_entry
->data
: NULL
;
2474 uint32_t chunk_number
= 1;
2475 for( lsmash_entry_t
*stco_entry
= stco
->list
->head
; stco_entry
; )
2478 && stsc_data
->first_chunk
== chunk_number
)
2480 lsmash_file_t
*ref_file
= isom_get_written_media_file( trak
, stsc_data
->sample_description_index
);
2481 stsc_entry
= stsc_entry
->next
;
2482 stsc_data
= stsc_entry
? (isom_stsc_entry_t
*)stsc_entry
->data
: NULL
;
2483 if( ref_file
!= trak
->file
)
2485 /* The chunks are not contained in the same file. Skip applying the offset.
2486 * If no more stsc entries, the rest of the chunks is not contained in the same file. */
2487 if( !stsc_entry
|| !stsc_data
)
2489 while( stco_entry
&& chunk_number
< stsc_data
->first_chunk
)
2491 stco_entry
= stco_entry
->next
;
2497 if( stco
->large_presentation
)
2498 ((isom_co64_entry_t
*)stco_entry
->data
)->chunk_offset
+= preceding_size
;
2500 ((isom_stco_entry_t
*)stco_entry
->data
)->chunk_offset
+= preceding_size
;
2501 stco_entry
= stco_entry
->next
;
2507 int isom_establish_movie( lsmash_file_t
*file
)
2509 assert( file
== file
->initializer
);
2511 if( (err
= isom_check_mandatory_boxes( file
)) < 0
2512 || (err
= isom_set_movie_creation_time( file
)) < 0 )
2514 if( isom_update_box_size( file
->moov
) == 0 )
2515 return LSMASH_ERR_INVALID_DATA
;
2519 int lsmash_finish_movie
2521 lsmash_root_t
*root
,
2522 lsmash_adhoc_remux_t
*remux
2525 if( isom_check_initializer_present( root
) < 0 )
2526 return LSMASH_ERR_FUNCTION_PARAM
;
2527 lsmash_file_t
*file
= root
->file
;
2530 || !file
->initializer
->moov
)
2531 return LSMASH_ERR_INVALID_DATA
;
2532 if( file
->fragment
)
2533 return isom_finish_final_fragment_movie( file
, remux
);
2534 if( file
!= file
->initializer
)
2535 return LSMASH_ERR_INVALID_DATA
;
2537 isom_moov_t
*moov
= file
->moov
;
2538 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
2540 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2545 || !trak
->mdia
->minf
2546 || !trak
->mdia
->minf
->stbl
2547 || !trak
->mdia
->minf
->stbl
->stsd
2548 || !trak
->mdia
->minf
->stbl
->stsd
->list
.head
2549 || !trak
->mdia
->minf
->stbl
->stsd
->list
.head
->data
2550 || !trak
->mdia
->minf
->stbl
->stco
2551 || !trak
->mdia
->minf
->stbl
->stco
->list
2552 || !trak
->mdia
->minf
->stbl
->stco
->list
->tail
)
2553 return LSMASH_ERR_INVALID_DATA
;
2554 if( (err
= isom_complement_data_reference( trak
->mdia
->minf
)) < 0 )
2556 uint32_t track_ID
= trak
->tkhd
->track_ID
;
2557 uint32_t related_track_ID
= trak
->related_track_ID
;
2558 /* Disable the track if the track is a track reference chapter. */
2559 if( trak
->is_chapter
)
2560 trak
->tkhd
->flags
&= ~ISOM_TRACK_ENABLED
;
2561 if( trak
->is_chapter
&& related_track_ID
)
2563 /* In order that the track duration of the chapter track doesn't exceed that of the related track. */
2565 edit
.duration
= LSMASH_MIN( trak
->tkhd
->duration
, lsmash_get_track_duration( root
, related_track_ID
) );
2566 edit
.start_time
= 0;
2567 edit
.rate
= ISOM_EDIT_MODE_NORMAL
;
2568 if( (err
= lsmash_create_explicit_timeline_map( root
, track_ID
, edit
)) < 0 )
2571 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2572 /* Compress sample size table. */
2573 if( stbl
->compress_sample_size_table
2574 && (err
= stbl
->compress_sample_size_table( stbl
)) < 0 )
2576 /* Add stss box if any samples aren't sync sample. */
2577 if( !trak
->cache
->all_sync
&& !stbl
->stss
&& !isom_add_stss( stbl
) )
2578 return LSMASH_ERR_NAMELESS
;
2579 if( (err
= isom_update_tkhd_duration( trak
)) < 0
2580 || (err
= isom_update_bitrate_description( trak
->mdia
)) < 0 )
2583 if( file
->mp4_version1
== 1 && (err
= isom_setup_iods( moov
)) < 0 )
2585 if( (err
= isom_establish_movie( file
)) < 0 )
2587 /* Write the size of Media Data Box here. */
2588 lsmash_bs_t
*bs
= file
->bs
;
2589 file
->mdat
->manager
&= ~LSMASH_INCOMPLETE_BOX
;
2590 if( (err
= isom_write_box( bs
, (isom_box_t
*)file
->mdat
)) < 0 )
2592 /* Write the Movie Box and a Meta Box if no optimization for progressive download. */
2593 uint64_t meta_size
= file
->meta
? file
->meta
->size
: 0;
2596 if( (err
= isom_write_box( bs
, (isom_box_t
*)file
->moov
)) < 0
2597 || (err
= isom_write_box( bs
, (isom_box_t
*)file
->meta
)) < 0 )
2599 file
->size
+= moov
->size
+ meta_size
;
2602 /* stco->co64 conversion, depending on last chunk's offset */
2603 if( (err
= isom_check_large_offset_requirement( moov
, meta_size
)) < 0 )
2605 /* now the amount of offset is fixed. */
2606 uint64_t mtf_size
= moov
->size
+ meta_size
; /* sum of size of boxes moved to front */
2607 /* buffer size must be at least mtf_size * 2 */
2608 remux
->buffer_size
= LSMASH_MAX( remux
->buffer_size
, mtf_size
* 2 );
2609 /* Split to 2 buffers. */
2610 uint8_t *buf
[2] = { NULL
, NULL
};
2611 if( (buf
[0] = (uint8_t*)lsmash_malloc( remux
->buffer_size
)) == NULL
)
2612 return LSMASH_ERR_MEMORY_ALLOC
; /* NOTE: I think we still can fallback to "return isom_write_moov();" here. */
2613 size_t size
= remux
->buffer_size
/ 2;
2614 buf
[1] = buf
[0] + size
;
2615 /* Now, the amount of the offset is fixed. apply it to stco/co64 */
2616 isom_add_preceding_box_size( moov
, mtf_size
);
2617 /* Backup starting area of mdat and write moov + meta there instead. */
2618 isom_mdat_t
*mdat
= file
->mdat
;
2619 uint64_t total
= file
->size
+ mtf_size
;
2620 uint64_t placeholder_pos
= file
->free
? file
->free
->pos
: mdat
->pos
;
2621 if( (err
= lsmash_bs_write_seek( bs
, placeholder_pos
, SEEK_SET
)) < 0 )
2623 size_t read_num
= size
;
2624 lsmash_bs_read_data( bs
, buf
[0], &read_num
);
2625 uint64_t read_pos
= bs
->offset
;
2626 /* Write moov + meta there instead. */
2627 if( (err
= lsmash_bs_write_seek( bs
, placeholder_pos
, SEEK_SET
)) < 0
2628 || (err
= isom_write_box( bs
, (isom_box_t
*)file
->moov
)) < 0
2629 || (err
= isom_write_box( bs
, (isom_box_t
*)file
->meta
)) < 0 )
2631 uint64_t write_pos
= bs
->offset
;
2632 /* Update the positions */
2633 mdat
->pos
+= mtf_size
;
2635 file
->free
->pos
+= mtf_size
;
2636 /* Move Media Data Box. */
2637 if( (err
= isom_rearrange_data( file
, remux
, buf
, read_num
, size
, read_pos
, write_pos
, total
)) < 0 )
2639 file
->size
+= mtf_size
;
2640 lsmash_free( buf
[0] );
2643 lsmash_free( buf
[0] );
2647 int lsmash_set_last_sample_delta( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t sample_delta
)
2649 if( isom_check_initializer_present( root
) < 0 || track_ID
== 0 )
2650 return LSMASH_ERR_FUNCTION_PARAM
;
2651 lsmash_file_t
*file
= root
->file
;
2653 && file
->fragment
->movie
)
2655 isom_traf_t
*traf
= isom_get_traf( file
->fragment
->movie
, track_ID
);
2659 return LSMASH_ERR_NAMELESS
;
2660 return isom_set_fragment_last_duration( traf
, sample_delta
);
2662 if( file
!= file
->initializer
)
2663 return LSMASH_ERR_INVALID_DATA
;
2664 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2668 || !trak
->mdia
->mdhd
2669 || !trak
->mdia
->minf
2670 || !trak
->mdia
->minf
->stbl
2671 || !trak
->mdia
->minf
->stbl
->stsd
2672 || (!trak
->mdia
->minf
->stbl
->stsz
&& !trak
->mdia
->minf
->stbl
->stz2
)
2673 || !trak
->mdia
->minf
->stbl
->stts
2674 || !trak
->mdia
->minf
->stbl
->stts
->list
)
2675 return LSMASH_ERR_NAMELESS
;
2676 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2677 isom_stts_t
*stts
= stbl
->stts
;
2678 uint32_t sample_count
= isom_get_sample_count( trak
);
2680 if( !stts
->list
->tail
)
2683 return 0; /* no samples */
2684 if( sample_count
> 1 )
2685 return LSMASH_ERR_INVALID_DATA
; /* irregular sample_count */
2686 /* Set the duration of the first sample.
2687 * This duration is also the duration of the last sample. */
2688 if( (err
= isom_add_stts_entry( stbl
, sample_delta
)) < 0 )
2690 return lsmash_update_track_duration( root
, track_ID
, 0 );
2693 for( lsmash_entry_t
*entry
= stts
->list
->head
; entry
; entry
= entry
->next
)
2694 i
+= ((isom_stts_entry_t
*)entry
->data
)->sample_count
;
2695 if( sample_count
< i
)
2696 return LSMASH_ERR_INVALID_DATA
;
2697 int no_last
= (sample_count
> i
);
2698 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)stts
->list
->tail
->data
;
2699 if( !last_stts_data
)
2700 return LSMASH_ERR_INVALID_DATA
;
2701 /* Consider QuikcTime fixed compression audio. */
2702 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
,
2703 trak
->cache
->chunk
.sample_description_index
);
2705 return LSMASH_ERR_INVALID_DATA
;
2706 if( (audio
->manager
& LSMASH_AUDIO_DESCRIPTION
)
2707 && (audio
->manager
& LSMASH_QTFF_BASE
)
2708 && (audio
->version
== 1)
2709 && (audio
->compression_ID
!= QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION
) )
2711 if( audio
->samplesPerPacket
== 0 )
2712 return LSMASH_ERR_INVALID_DATA
;
2713 int exclude_last_sample
= no_last
? 0 : 1;
2714 uint32_t j
= audio
->samplesPerPacket
;
2715 for( lsmash_entry_t
*entry
= stts
->list
->tail
; entry
&& j
> 1; entry
= entry
->prev
)
2717 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)entry
->data
;
2719 return LSMASH_ERR_INVALID_DATA
;
2720 for( uint32_t k
= exclude_last_sample
; k
< stts_data
->sample_count
&& j
> 1; k
++ )
2722 sample_delta
-= stts_data
->sample_delta
;
2725 exclude_last_sample
= 0;
2728 /* Set sample_delta. */
2731 /* The duration of the last sample is not set yet. */
2732 if( sample_count
- i
> 1 )
2733 return LSMASH_ERR_INVALID_DATA
;
2734 /* Add a sample_delta. */
2735 if( sample_delta
== last_stts_data
->sample_delta
)
2736 ++ last_stts_data
->sample_count
;
2737 else if( (err
= isom_add_stts_entry( stbl
, sample_delta
)) < 0 )
2740 /* The duration of the last sample is already set. Replace it with a new one. */
2741 else if( (err
= isom_replace_last_sample_delta( stbl
, sample_delta
)) < 0 )
2743 return lsmash_update_track_duration( root
, track_ID
, sample_delta
);
2746 /*---- timeline manipulator ----*/
2748 int lsmash_modify_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t edit_number
, lsmash_edit_t edit
)
2750 if( isom_check_initializer_present( root
) < 0
2751 || edit
.start_time
< -1 )
2752 return LSMASH_ERR_FUNCTION_PARAM
;
2753 lsmash_file_t
*file
= root
->file
->initializer
;
2754 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2757 || !trak
->edts
->elst
2758 || !trak
->edts
->elst
->list
)
2759 return LSMASH_ERR_NAMELESS
;
2760 isom_elst_t
*elst
= trak
->edts
->elst
;
2761 isom_elst_entry_t
*data
= (isom_elst_entry_t
*)lsmash_get_entry_data( elst
->list
, edit_number
);
2763 return LSMASH_ERR_NAMELESS
;
2764 data
->segment_duration
= edit
.duration
;
2765 data
->media_time
= edit
.start_time
;
2766 data
->media_rate
= edit
.rate
;
2767 if( elst
->pos
== 0 || !file
->fragment
|| file
->bs
->unseekable
)
2768 return isom_update_tkhd_duration( trak
);
2769 /* Rewrite the specified entry.
2770 * Note: we don't update the version of the Edit List Box. */
2771 lsmash_bs_t
*bs
= file
->bs
;
2772 uint64_t current_pos
= bs
->offset
;
2773 uint64_t entry_pos
= elst
->pos
+ ISOM_LIST_FULLBOX_COMMON_SIZE
+ ((uint64_t)edit_number
- 1) * (elst
->version
== 1 ? 20 : 12);
2774 lsmash_bs_write_seek( bs
, entry_pos
, SEEK_SET
);
2777 lsmash_bs_put_be64( bs
, data
->segment_duration
);
2778 lsmash_bs_put_be64( bs
, data
->media_time
);
2782 lsmash_bs_put_be32( bs
, (uint32_t)LSMASH_MIN( data
->segment_duration
, UINT32_MAX
) );
2783 lsmash_bs_put_be32( bs
, (uint32_t)data
->media_time
);
2785 lsmash_bs_put_be32( bs
, data
->media_rate
);
2786 int ret
= lsmash_bs_flush_buffer( bs
);
2787 lsmash_bs_write_seek( bs
, current_pos
, SEEK_SET
);
2791 int lsmash_create_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_edit_t edit
)
2793 if( isom_check_initializer_present( root
) < 0 || edit
.start_time
< -1 )
2794 return LSMASH_ERR_FUNCTION_PARAM
;
2795 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
2798 return LSMASH_ERR_NAMELESS
;
2799 edit
.duration
= (edit
.duration
|| root
->file
->fragment
) ? edit
.duration
2800 : trak
->tkhd
->duration
? trak
->tkhd
->duration
2801 : isom_update_tkhd_duration( trak
) < 0 ? 0
2802 : trak
->tkhd
->duration
;
2803 if( (!trak
->edts
&& !isom_add_edts( trak
))
2804 || (!trak
->edts
->elst
&& !isom_add_elst( trak
->edts
)) )
2805 return LSMASH_ERR_NAMELESS
;
2806 int err
= isom_add_elst_entry( trak
->edts
->elst
, edit
.duration
, edit
.start_time
, edit
.rate
);
2809 return isom_update_tkhd_duration( trak
);
2812 int lsmash_get_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t edit_number
, lsmash_edit_t
*edit
)
2814 if( isom_check_initializer_present( root
) < 0 || !edit
)
2815 return LSMASH_ERR_FUNCTION_PARAM
;
2816 isom_elst_entry_t
*data
;
2817 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2819 data
= isom_timelime_get_explicit_timeline_map( root
, track_ID
, edit_number
);
2823 || !trak
->edts
->elst
)
2827 edit
->start_time
= 0;
2831 data
= (isom_elst_entry_t
*)lsmash_get_entry_data( trak
->edts
->elst
->list
, edit_number
);
2834 return LSMASH_ERR_NAMELESS
;
2835 edit
->duration
= data
->segment_duration
;
2836 edit
->start_time
= data
->media_time
;
2837 edit
->rate
= data
->media_rate
;
2841 uint32_t lsmash_count_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
)
2843 if( isom_check_initializer_present( root
) < 0 )
2844 return LSMASH_ERR_FUNCTION_PARAM
;
2845 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2847 return isom_timelime_count_explicit_timeline_map( root
, track_ID
);
2851 || !trak
->edts
->elst
2852 || !trak
->edts
->elst
->list
)
2854 return trak
->edts
->elst
->list
->entry_count
;
2858 /*---- create / modification time fields manipulators ----*/
2860 int lsmash_update_media_modification_time( lsmash_root_t
*root
, uint32_t track_ID
)
2862 if( isom_check_initializer_present( root
) < 0 )
2863 return LSMASH_ERR_FUNCTION_PARAM
;
2864 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2867 || !trak
->mdia
->mdhd
)
2868 return LSMASH_ERR_NAMELESS
;
2869 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
2870 mdhd
->modification_time
= isom_get_current_mp4time();
2871 /* overwrite strange creation_time */
2872 if( mdhd
->creation_time
> mdhd
->modification_time
)
2873 mdhd
->creation_time
= mdhd
->modification_time
;
2877 int lsmash_update_track_modification_time( lsmash_root_t
*root
, uint32_t track_ID
)
2879 if( isom_check_initializer_present( root
) < 0 )
2880 return LSMASH_ERR_FUNCTION_PARAM
;
2881 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2884 return LSMASH_ERR_NAMELESS
;
2885 isom_tkhd_t
*tkhd
= trak
->tkhd
;
2886 tkhd
->modification_time
= isom_get_current_mp4time();
2887 /* overwrite strange creation_time */
2888 if( tkhd
->creation_time
> tkhd
->modification_time
)
2889 tkhd
->creation_time
= tkhd
->modification_time
;
2893 int lsmash_update_movie_modification_time( lsmash_root_t
*root
)
2895 if( isom_check_initializer_present( root
) < 0 )
2896 return LSMASH_ERR_FUNCTION_PARAM
;
2897 lsmash_file_t
*file
= root
->file
->initializer
;
2899 || !file
->moov
->mvhd
)
2900 return LSMASH_ERR_INVALID_DATA
;
2901 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
2902 mvhd
->modification_time
= isom_get_current_mp4time();
2903 /* overwrite strange creation_time */
2904 if( mvhd
->creation_time
> mvhd
->modification_time
)
2905 mvhd
->creation_time
= mvhd
->modification_time
;
2909 /*---- sample manipulators ----*/
2910 lsmash_sample_t
*lsmash_create_sample( uint32_t size
)
2912 lsmash_sample_t
*sample
= lsmash_malloc_zero( sizeof(lsmash_sample_t
) );
2917 sample
->data
= lsmash_malloc( size
);
2920 lsmash_free( sample
);
2923 sample
->length
= size
;
2927 int lsmash_sample_alloc( lsmash_sample_t
*sample
, uint32_t size
)
2930 return LSMASH_ERR_FUNCTION_PARAM
;
2933 lsmash_free( sample
->data
);
2934 sample
->data
= NULL
;
2938 if( size
== sample
->length
)
2942 data
= lsmash_malloc( size
);
2944 data
= lsmash_realloc( sample
->data
, size
);
2946 return LSMASH_ERR_MEMORY_ALLOC
;
2947 sample
->data
= data
;
2948 sample
->length
= size
;
2952 void lsmash_delete_sample( lsmash_sample_t
*sample
)
2956 lsmash_free( sample
->data
);
2957 lsmash_free( sample
);
2960 isom_sample_pool_t
*isom_create_sample_pool( uint64_t size
)
2962 isom_sample_pool_t
*pool
= lsmash_malloc_zero( sizeof(isom_sample_pool_t
) );
2967 pool
->data
= lsmash_malloc( size
);
2970 lsmash_free( pool
);
2977 void isom_remove_sample_pool( isom_sample_pool_t
*pool
)
2981 lsmash_free( pool
->data
);
2982 lsmash_free( pool
);
2985 static uint32_t isom_add_size( isom_stbl_t
*stbl
, uint32_t sample_size
)
2987 if( isom_add_stsz_entry( stbl
, sample_size
) < 0 )
2989 return isom_get_sample_count_from_sample_table( stbl
);
2992 static uint32_t isom_add_dts( isom_stbl_t
*stbl
, isom_timestamp_t
*cache
, uint64_t dts
)
2994 isom_stts_t
*stts
= stbl
->stts
;
2995 if( stts
->list
->entry_count
== 0 )
2997 if( isom_add_stts_entry( stbl
, dts
) < 0 )
3002 if( dts
<= cache
->dts
)
3004 uint32_t sample_delta
= dts
- cache
->dts
;
3005 isom_stts_entry_t
*data
= (isom_stts_entry_t
*)stts
->list
->tail
->data
;
3006 if( data
->sample_delta
== sample_delta
)
3007 ++ data
->sample_count
;
3008 else if( isom_add_stts_entry( stbl
, sample_delta
) < 0 )
3011 return sample_delta
;
3014 static int isom_add_cts( isom_stbl_t
*stbl
, isom_timestamp_t
*cache
, uint64_t cts
)
3017 isom_ctts_t
*ctts
= stbl
->ctts
;
3020 if( cts
== cache
->dts
)
3025 /* Add ctts box and the first ctts entry. */
3026 if( !isom_add_ctts( stbl
) )
3027 return LSMASH_ERR_NAMELESS
;
3028 if( (err
= isom_add_ctts_entry( stbl
, 0 )) < 0 )
3031 isom_ctts_entry_t
*data
= (isom_ctts_entry_t
*)ctts
->list
->head
->data
;
3032 uint32_t sample_count
= stbl
->stsz
? stbl
->stsz
->sample_count
: stbl
->stz2
->sample_count
;
3033 if( sample_count
!= 1 )
3035 data
->sample_count
= sample_count
- 1;
3036 if( (err
= isom_add_ctts_entry( stbl
, cts
- cache
->dts
)) < 0 )
3040 data
->sample_offset
= cts
;
3045 return LSMASH_ERR_INVALID_DATA
;
3046 isom_ctts_entry_t
*data
= (isom_ctts_entry_t
*)ctts
->list
->tail
->data
;
3047 uint32_t sample_offset
= cts
- cache
->dts
;
3048 if( data
->sample_offset
== sample_offset
)
3049 ++ data
->sample_count
;
3050 else if( (err
= isom_add_ctts_entry( stbl
, sample_offset
)) < 0 )
3056 static int isom_add_timestamp( isom_stbl_t
*stbl
, isom_cache_t
*cache
, lsmash_file_t
*file
, uint64_t dts
, uint64_t cts
)
3060 || !stbl
->stts
->list
)
3061 return LSMASH_ERR_INVALID_DATA
;
3062 if( file
->isom_compatible
&& file
->qt_compatible
&& (cts
- dts
) > INT32_MAX
)
3063 return LSMASH_ERR_INVALID_DATA
; /* sample_offset is not compatible. */
3064 isom_timestamp_t
*ts_cache
= &cache
->timestamp
;
3065 uint32_t sample_count
= isom_get_sample_count_from_sample_table( stbl
);
3066 uint32_t sample_delta
= sample_count
> 1 ? isom_add_dts( stbl
, ts_cache
, dts
) : 0;
3067 if( sample_count
> 1 && sample_delta
== 0 )
3068 return LSMASH_ERR_INVALID_DATA
;
3069 int err
= isom_add_cts( stbl
, ts_cache
, cts
);
3072 if( (cts
+ ts_cache
->ctd_shift
) < dts
)
3074 if( (file
->max_isom_version
< 4 && !file
->qt_compatible
) /* Negative sample offset is not supported. */
3075 || (file
->max_isom_version
>= 4 && file
->qt_compatible
) /* ctts version 1 is not defined in QTFF. */
3076 || ((dts
- cts
) > INT32_MAX
) ) /* Overflow */
3077 return LSMASH_ERR_INVALID_DATA
;
3078 ts_cache
->ctd_shift
= dts
- cts
;
3079 if( stbl
->ctts
->version
== 0 && !file
->qt_compatible
)
3080 stbl
->ctts
->version
= 1;
3082 if( cache
->fragment
)
3084 isom_fragment_t
*fragment_cache
= cache
->fragment
;
3085 fragment_cache
->last_duration
= sample_delta
;
3086 fragment_cache
->largest_cts
= LSMASH_MAX( ts_cache
->cts
, fragment_cache
->largest_cts
);
3091 static int isom_add_sync_point( isom_stbl_t
*stbl
, isom_cache_t
*cache
, uint32_t sample_number
, lsmash_sample_property_t
*prop
)
3093 if( !(prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
) ) /* no null check for prop */
3095 if( !cache
->all_sync
)
3097 if( !stbl
->stss
&& !isom_add_stss( stbl
) )
3098 return LSMASH_ERR_NAMELESS
;
3099 int err
= isom_add_stss_entry( stbl
, 1 );
3100 if( err
< 0 ) /* Declare here the first sample is a sync sample. */
3102 cache
->all_sync
= 0;
3105 if( cache
->all_sync
) /* We don't need stss box if all samples are sync sample. */
3109 if( isom_get_sample_count_from_sample_table( stbl
) == 1 )
3111 cache
->all_sync
= 1; /* Also the first sample is a sync sample. */
3114 if( !isom_add_stss( stbl
) )
3115 return LSMASH_ERR_NAMELESS
;
3117 return isom_add_stss_entry( stbl
, sample_number
);
3120 static int isom_add_partial_sync( isom_stbl_t
*stbl
, lsmash_file_t
*file
, uint32_t sample_number
, lsmash_sample_property_t
*prop
)
3122 if( !file
->qt_compatible
)
3124 if( !(prop
->ra_flags
& QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
) )
3126 /* This sample is a partial sync sample. */
3127 if( !stbl
->stps
&& !isom_add_stps( stbl
) )
3128 return LSMASH_ERR_NAMELESS
;
3129 return isom_add_stps_entry( stbl
, sample_number
);
3132 int isom_rap_grouping_established( isom_rap_group_t
*group
, int num_leading_samples_known
, isom_sgpd_t
*sgpd
, int is_fragment
)
3134 isom_rap_entry_t
*rap
= group
->random_access
;
3137 assert( rap
== (isom_rap_entry_t
*)sgpd
->list
->tail
->data
);
3138 rap
->num_leading_samples_known
= num_leading_samples_known
;
3139 /* Avoid duplication of sample group descriptions. */
3140 uint32_t group_description_index
= is_fragment
? 0x10001 : 1;
3141 for( lsmash_entry_t
*entry
= sgpd
->list
->head
; entry
!= sgpd
->list
->tail
; entry
= entry
->next
)
3143 isom_rap_entry_t
*data
= (isom_rap_entry_t
*)entry
->data
;
3145 return LSMASH_ERR_INVALID_DATA
;
3146 if( rap
->num_leading_samples_known
== data
->num_leading_samples_known
3147 && rap
->num_leading_samples
== data
->num_leading_samples
)
3149 /* The same description already exists.
3150 * Remove the latest random access entry. */
3151 lsmash_remove_entry_tail( sgpd
->list
, NULL
);
3152 /* Replace assigned group_description_index with the one corresponding the same description. */
3153 if( group
->assignment
->group_description_index
== 0 )
3155 /* We don't create consecutive sample groups not assigned to 'rap '.
3156 * So the previous sample group shall be a group of 'rap ' if any. */
3157 if( group
->prev_assignment
)
3159 assert( group
->prev_assignment
->group_description_index
);
3160 group
->prev_assignment
->group_description_index
= group_description_index
;
3164 group
->assignment
->group_description_index
= group_description_index
;
3167 ++group_description_index
;
3169 group
->random_access
= NULL
;
3173 int isom_group_random_access( isom_box_t
*parent
, isom_cache_t
*cache
, lsmash_sample_t
*sample
)
3175 if( parent
->file
->max_isom_version
< 6 )
3179 uint32_t sample_count
;
3181 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_STBL
) )
3183 isom_stbl_t
*stbl
= (isom_stbl_t
*)parent
;
3184 sbgp
= isom_get_sample_to_group ( stbl
, ISOM_GROUP_TYPE_RAP
);
3185 sgpd
= isom_get_sample_group_description( stbl
, ISOM_GROUP_TYPE_RAP
);
3186 sample_count
= isom_get_sample_count_from_sample_table( stbl
);
3189 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
3191 isom_traf_t
*traf
= (isom_traf_t
*)parent
;
3192 sbgp
= isom_get_fragment_sample_to_group ( traf
, ISOM_GROUP_TYPE_RAP
);
3193 sgpd
= isom_get_fragment_sample_group_description( traf
, ISOM_GROUP_TYPE_RAP
);
3194 sample_count
= cache
->fragment
->sample_count
+ 1;
3202 /* redundant initializations to suppress warnings from unclever compilers */
3206 if( !sbgp
|| !sgpd
)
3208 lsmash_sample_property_t
*prop
= &sample
->prop
;
3209 uint8_t is_rap
= (prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
)
3210 || (prop
->ra_flags
& QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
)
3211 || (prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
)
3212 || (LSMASH_IS_POST_ROLL_START( prop
->ra_flags
) && prop
->post_roll
.identifier
== prop
->post_roll
.complete
);
3213 isom_rap_group_t
*group
= cache
->rap
;
3216 /* This sample is the first sample, create a grouping cache. */
3217 assert( sample_count
== 1 );
3218 group
= lsmash_malloc( sizeof(isom_rap_group_t
) );
3220 return LSMASH_ERR_MEMORY_ALLOC
;
3223 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3224 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3228 /* The first sample is not always a random access point. */
3229 group
->random_access
= NULL
;
3230 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3232 if( !group
->assignment
)
3234 lsmash_free( group
);
3235 return LSMASH_ERR_MEMORY_ALLOC
;
3237 group
->prev_assignment
= NULL
;
3238 group
->is_prev_rap
= is_rap
;
3243 if( group
->is_prev_rap
)
3245 /* OK. here, the previous sample is a menber of 'rap '. */
3248 /* This sample isn't a member of 'rap ' and the previous sample is.
3249 * So we create a new group and set 0 on its group_description_index. */
3250 group
->prev_assignment
= group
->assignment
;
3251 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3252 if( !group
->assignment
)
3254 lsmash_free( group
);
3255 return LSMASH_ERR_MEMORY_ALLOC
;
3258 else if( !LSMASH_IS_CLOSED_RAP( prop
->ra_flags
) )
3260 /* Create a new group since there is the possibility the next sample is a leading sample.
3261 * This sample is a member of 'rap ', so we set appropriate value on its group_description_index. */
3262 if( (err
= isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
)) < 0 )
3264 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3265 group
->prev_assignment
= group
->assignment
;
3266 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3267 if( !group
->assignment
)
3269 lsmash_free( group
);
3270 return LSMASH_ERR_MEMORY_ALLOC
;
3273 else /* The previous and current sample are a member of 'rap ', and the next sample must not be a leading sample. */
3274 ++ group
->assignment
->sample_count
;
3278 /* This sample is a member of 'rap ' and the previous sample isn't.
3279 * So we create a new group and set appropriate value on its group_description_index. */
3280 if( (err
= isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
)) < 0 )
3282 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3283 group
->prev_assignment
= group
->assignment
;
3284 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3285 if( !group
->assignment
)
3287 lsmash_free( group
);
3288 return LSMASH_ERR_MEMORY_ALLOC
;
3291 else /* The previous and current sample aren't a member of 'rap '. */
3292 ++ group
->assignment
->sample_count
;
3293 /* Obtain the property of the latest random access point group. */
3294 if( !is_rap
&& group
->random_access
)
3296 if( prop
->leading
== ISOM_SAMPLE_LEADING_UNKNOWN
)
3298 /* We can no longer know num_leading_samples in this group. */
3299 if( (err
= isom_rap_grouping_established( group
, 0, sgpd
, is_fragment
)) < 0 )
3304 if( prop
->leading
== ISOM_SAMPLE_IS_UNDECODABLE_LEADING
3305 || prop
->leading
== ISOM_SAMPLE_IS_DECODABLE_LEADING
)
3306 ++ group
->random_access
->num_leading_samples
;
3307 /* no more consecutive leading samples in this group */
3308 else if( (err
= isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
)) < 0 )
3312 group
->is_prev_rap
= is_rap
;
3316 static int isom_roll_grouping_established( isom_roll_group_t
*group
)
3318 /* Avoid duplication of sample group descriptions. */
3319 isom_sgpd_t
*sgpd
= group
->sgpd
;
3320 uint32_t group_description_index
= group
->is_fragment
? 0x10001 : 1;
3321 for( lsmash_entry_t
*entry
= sgpd
->list
->head
; entry
; entry
= entry
->next
)
3323 isom_roll_entry_t
*data
= (isom_roll_entry_t
*)entry
->data
;
3325 return LSMASH_ERR_INVALID_DATA
;
3326 if( group
->roll_distance
== data
->roll_distance
)
3328 /* The same description already exists.
3329 * Set the group_description_index corresponding the same description. */
3330 group
->assignment
->group_description_index
= group_description_index
;
3333 ++group_description_index
;
3335 /* Add a new roll recovery description. */
3336 if( !isom_add_roll_group_entry( sgpd
, group
->roll_distance
) )
3337 return LSMASH_ERR_MEMORY_ALLOC
;
3338 group
->assignment
->group_description_index
= sgpd
->list
->entry_count
+ (group
->is_fragment
? 0x10000 : 0);
3342 static int isom_deduplicate_roll_group( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3345 uint32_t current_group_number
= sbgp
->list
->entry_count
- pool
->entry_count
+ 1;
3346 isom_group_assignment_entry_t
*prev_assignment
= (isom_group_assignment_entry_t
*)lsmash_get_entry_data( sbgp
->list
, current_group_number
- 1 );
3347 for( lsmash_entry_t
*entry
= pool
->head
; entry
; )
3349 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3351 || !group
->assignment
)
3352 return LSMASH_ERR_INVALID_DATA
;
3353 if( !group
->delimited
|| group
->described
!= ROLL_DISTANCE_DETERMINED
)
3355 if( prev_assignment
&& prev_assignment
->group_description_index
== group
->assignment
->group_description_index
)
3357 /* Merge the current group with the previous. */
3358 lsmash_entry_t
*next_entry
= entry
->next
;
3359 prev_assignment
->sample_count
+= group
->assignment
->sample_count
;
3361 if( (err
= lsmash_remove_entry( sbgp
->list
, current_group_number
, NULL
)) < 0
3362 || (err
= lsmash_remove_entry_direct( pool
, entry
, NULL
)) < 0 )
3368 entry
= entry
->next
;
3369 prev_assignment
= group
->assignment
;
3370 ++current_group_number
;
3376 /* Remove pooled caches that has become unnecessary. */
3377 static int isom_clean_roll_pool( lsmash_entry_list_t
*pool
)
3379 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= pool
->head
)
3381 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3383 return LSMASH_ERR_INVALID_DATA
;
3384 if( !group
->delimited
|| group
->described
!= ROLL_DISTANCE_DETERMINED
)
3386 int err
= lsmash_remove_entry_direct( pool
, entry
, NULL
);
3393 static int isom_flush_roll_pool( 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 if( group
->delimited
3402 && group
->described
== ROLL_DISTANCE_DETERMINED
3403 && group
->roll_distance
!= 0
3404 && (err
= isom_roll_grouping_established( group
)) < 0 )
3407 if( (err
= isom_deduplicate_roll_group( sbgp
, pool
)) < 0 )
3409 return isom_clean_roll_pool( pool
);
3412 static int isom_all_recovery_described( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3414 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3416 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3418 return LSMASH_ERR_INVALID_DATA
;
3419 group
->described
= ROLL_DISTANCE_DETERMINED
;
3421 return isom_flush_roll_pool( sbgp
, pool
);
3424 int isom_all_recovery_completed( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3426 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3428 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3430 return LSMASH_ERR_INVALID_DATA
;
3431 group
->described
= ROLL_DISTANCE_DETERMINED
;
3432 group
->delimited
= 1;
3434 return isom_flush_roll_pool( sbgp
, pool
);
3437 static isom_roll_entry_t
*isom_get_roll_description
3439 isom_roll_group_t
*group
3442 uint32_t group_description_index
= group
->assignment
->group_description_index
;
3443 if( group_description_index
&& group
->is_fragment
)
3445 assert( group_description_index
> 0x10000 );
3446 group_description_index
-= 0x10000;
3448 return (isom_roll_entry_t
*)lsmash_get_entry_data( group
->sgpd
->list
, group_description_index
);
3451 int isom_group_roll_recovery( isom_box_t
*parent
, isom_cache_t
*cache
, lsmash_sample_t
*sample
)
3453 if( !parent
->file
->avc_extensions
3454 && !parent
->file
->qt_compatible
)
3456 uint32_t sample_count
;
3458 lsmash_entry_list_t
*sbgp_list
;
3459 lsmash_entry_list_t
*sgpd_list
;
3460 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_STBL
) )
3462 isom_stbl_t
*stbl
= (isom_stbl_t
*)parent
;
3463 sbgp_list
= &stbl
->sbgp_list
;
3464 sgpd_list
= &stbl
->sgpd_list
;
3465 sample_count
= isom_get_sample_count_from_sample_table( stbl
);
3468 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
3470 if( parent
->file
->max_isom_version
< 6 )
3472 isom_traf_t
*traf
= (isom_traf_t
*)parent
;
3473 sbgp_list
= &traf
->sbgp_list
;
3474 sgpd_list
= &traf
->sgpd_list
;
3475 sample_count
= cache
->fragment
->sample_count
+ 1;
3481 return LSMASH_ERR_INVALID_DATA
;
3483 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group ( sbgp_list
);
3484 isom_sgpd_t
*sgpd
= isom_get_roll_recovery_sample_group_description( sgpd_list
);
3485 if( !sbgp
|| !sgpd
|| sbgp
->grouping_type
!= sgpd
->grouping_type
)
3487 /* Check if 'roll' -> 'prol' conversion is needed. */
3489 && sbgp
->grouping_type
== ISOM_GROUP_TYPE_ROLL
3490 && !(sample
->prop
.ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
) )
3492 /* Since not every samples is a sync sample, change grouping_type into 'prol'. */
3493 sbgp
->grouping_type
= ISOM_GROUP_TYPE_PROL
;
3494 sgpd
->grouping_type
= ISOM_GROUP_TYPE_PROL
;
3496 lsmash_entry_list_t
*pool
= cache
->roll
.pool
;
3499 pool
= lsmash_create_entry_list();
3501 return LSMASH_ERR_MEMORY_ALLOC
;
3502 cache
->roll
.pool
= pool
;
3504 lsmash_sample_property_t
*prop
= &sample
->prop
;
3505 isom_roll_group_t
*group
= (isom_roll_group_t
*)lsmash_get_entry_data( pool
, pool
->entry_count
);
3506 int is_recovery_start
= LSMASH_IS_POST_ROLL_START( prop
->ra_flags
);
3507 int valid_pre_roll
= !is_recovery_start
3508 && (prop
->ra_flags
!= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
)
3509 && (prop
->pre_roll
.distance
> 0)
3510 && (prop
->pre_roll
.distance
<= -INT16_MIN
);
3511 int new_group
= !group
|| is_recovery_start
|| (group
->prev_is_recovery_start
!= is_recovery_start
);
3514 /* Check pre-roll distance. */
3515 assert( group
->assignment
&& group
->sgpd
);
3516 isom_roll_entry_t
*prev_roll
= isom_get_roll_description( group
);
3518 new_group
= valid_pre_roll
;
3519 else if( !valid_pre_roll
|| (prop
->pre_roll
.distance
!= -prev_roll
->roll_distance
) )
3520 /* Pre-roll distance is different from the previous. */
3526 group
->delimited
= 1;
3528 assert( sample_count
== 1 );
3529 /* Create a new group. */
3530 group
= lsmash_malloc_zero( sizeof(isom_roll_group_t
) );
3532 return LSMASH_ERR_MEMORY_ALLOC
;
3534 group
->prev_is_recovery_start
= is_recovery_start
;
3535 group
->is_fragment
= is_fragment
;
3536 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3537 if( !group
->assignment
|| lsmash_add_entry( pool
, group
) < 0 )
3539 lsmash_free( group
);
3540 return LSMASH_ERR_MEMORY_ALLOC
;
3542 if( is_recovery_start
)
3544 /* a member of non-roll or post-roll group */
3545 group
->first_sample
= sample_count
;
3546 group
->recovery_point
= prop
->post_roll
.complete
;
3550 group
->described
= ROLL_DISTANCE_DETERMINED
;
3551 if( valid_pre_roll
)
3553 /* a member of pre-roll group */
3554 group
->roll_distance
= -(signed)prop
->pre_roll
.distance
;
3555 int err
= isom_roll_grouping_established( group
);
3560 /* a member of non-roll group */
3561 group
->roll_distance
= 0;
3566 group
->prev_is_recovery_start
= is_recovery_start
;
3567 group
->assignment
->sample_count
+= 1;
3569 /* If encountered a RAP, all recovery is completed here. */
3570 if( prop
->ra_flags
& (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
3571 | ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
3572 | QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
) )
3573 return isom_all_recovery_described( sbgp
, pool
);
3574 /* Check whether this sample is a random access recovery point or not. */
3575 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3577 group
= (isom_roll_group_t
*)entry
->data
;
3579 return LSMASH_ERR_INVALID_DATA
;
3580 if( group
->described
== ROLL_DISTANCE_DETERMINED
)
3582 if( group
->described
== ROLL_DISTANCE_INITIALIZED
)
3584 /* Let's consider the following picture sequence.
3585 * coded order : P[0] P[1] P[2] P[3] P[4] P[5]
3588 * Here, P[0] conveys a recovery point SEI and P[3] is the recovery point.
3589 * Correctness of decoded pictures is specified by recovery point in output order for both AVC and HEVC.
3590 * Therefore, as follows,
3591 * output order : P[0] P[2] P[1] P[5]|P[3] P[4]
3592 * ---(incorrect?)--->|
3593 * there is no guarantee that P[5] is decoded and output correctly.
3594 * From this, it can be said that the roll_distance of this sequence is equal to 5. */
3595 isom_roll_entry_t
*post_roll
= isom_get_roll_description( group
);
3596 if( post_roll
&& post_roll
->roll_distance
> 0 )
3598 if( group
->rp_cts
> sample
->cts
)
3599 /* Updated roll_distance for composition reordering. */
3600 post_roll
->roll_distance
= sample_count
- group
->first_sample
;
3601 if( ++ group
->wait_and_see_count
>= MAX_ROLL_WAIT_AND_SEE_COUNT
)
3602 group
->described
= ROLL_DISTANCE_DETERMINED
;
3605 else if( prop
->post_roll
.identifier
== group
->recovery_point
)
3607 int16_t distance
= sample_count
- group
->first_sample
;
3608 group
->rp_cts
= sample
->cts
;
3609 group
->roll_distance
= distance
;
3610 /* Add a roll recovery entry only when roll_distance isn't zero since roll_distance = 0 must not be used. */
3613 /* Now, this group is a 'roll'.
3614 * The roll_distance may be updated later because of composition reordering. */
3615 group
->described
= ROLL_DISTANCE_INITIALIZED
;
3616 group
->wait_and_see_count
= 0;
3617 /* All groups with uninitialized roll_distance before the current group are described. */
3618 lsmash_entry_t
*current
= entry
;
3619 for( entry
= pool
->head
; entry
!= current
; entry
= entry
->next
)
3621 group
= (isom_roll_group_t
*)entry
->data
;
3622 if( !group
|| group
->described
!= ROLL_DISTANCE_INITIALIZED
)
3624 group
->described
= ROLL_DISTANCE_DETERMINED
;
3626 /* Cache the CTS of the first recovery point in a subsegment. */
3628 && cache
->fragment
->subsegment
.first_rp_number
== 0 )
3630 cache
->fragment
->subsegment
.first_rp_number
= sample_count
;
3631 cache
->fragment
->subsegment
.first_rp_cts
= sample
->cts
;
3632 cache
->fragment
->subsegment
.first_ed_cts
= sample
->cts
;
3633 cache
->fragment
->subsegment
.decodable
= 1;
3637 /* Random Accessible Point */
3638 return isom_all_recovery_described( sbgp
, pool
);
3641 return isom_flush_roll_pool( sbgp
, pool
);
3644 static int isom_update_chunk_tables
3647 lsmash_file_t
*media_file
,
3648 isom_chunk_t
*current
3651 isom_stsc_entry_t
*last_stsc_data
= stbl
->stsc
->list
->tail
? (isom_stsc_entry_t
*)stbl
->stsc
->list
->tail
->data
: NULL
;
3652 /* Create a new chunk sequence in this track if needed. */
3654 if( (!last_stsc_data
3655 || current
->pool
->sample_count
!= last_stsc_data
->samples_per_chunk
3656 || current
->sample_description_index
!= last_stsc_data
->sample_description_index
)
3657 && (err
= isom_add_stsc_entry( stbl
, current
->chunk_number
,
3658 current
->pool
->sample_count
,
3659 current
->sample_description_index
)) < 0 )
3661 /* Add a new chunk offset in this track. */
3662 uint64_t offset
= media_file
->size
;
3663 if( media_file
->fragment
)
3664 offset
+= ISOM_BASEBOX_COMMON_SIZE
+ media_file
->fragment
->pool_size
;
3665 return isom_add_stco_entry( stbl
, offset
);
3668 /* This function decides to put a give sample on the current chunk or the next new one.
3669 * Returns 1 if pooled samples must be flushed.
3670 * FIXME: I wonder if this function should have a extra argument which indicates force_to_flush_cached_chunk.
3671 * see lsmash_append_sample for detail. */
3672 static int isom_add_sample_to_chunk
3675 lsmash_sample_t
*sample
3680 || !trak
->mdia
->mdhd
3681 || trak
->mdia
->mdhd
->timescale
== 0
3682 || !trak
->mdia
->minf
->dinf
3683 || !trak
->mdia
->minf
->dinf
->dref
3684 || !trak
->mdia
->minf
->stbl
->stsd
3685 || !trak
->mdia
->minf
->stbl
->stsc
3686 || !trak
->mdia
->minf
->stbl
->stsc
->list
)
3687 return LSMASH_ERR_INVALID_DATA
;
3688 isom_chunk_t
*current
= &trak
->cache
->chunk
;
3689 if( !current
->pool
)
3691 /* Very initial settings, just once per track */
3692 current
->pool
= isom_create_sample_pool( 0 );
3693 if( !current
->pool
)
3694 return LSMASH_ERR_MEMORY_ALLOC
;
3696 if( current
->pool
->sample_count
== 0 )
3698 /* Cannot decide whether we should flush the current sample or not here yet. */
3699 current
->chunk_number
+= 1;
3700 current
->sample_description_index
= sample
->index
;
3701 current
->first_dts
= sample
->dts
;
3704 if( sample
->dts
< current
->first_dts
)
3705 return LSMASH_ERR_INVALID_DATA
; /* easy error check. */
3706 lsmash_file_t
*media_file
= isom_get_written_media_file( trak
, current
->sample_description_index
);
3707 if( (current
->sample_description_index
== sample
->index
)
3708 && (media_file
->max_chunk_duration
>= ((double)(sample
->dts
- current
->first_dts
) / trak
->mdia
->mdhd
->timescale
))
3709 && (media_file
->max_chunk_size
>= current
->pool
->size
+ sample
->length
) )
3710 return 0; /* No need to flush current cached chunk, the current sample must be put into that. */
3711 /* NOTE: chunk relative stuff must be pushed into file after a chunk is fully determined with its contents.
3712 * Now the current cached chunk is fixed, actually add the chunk relative properties to its file accordingly. */
3713 int err
= isom_update_chunk_tables( trak
->mdia
->minf
->stbl
, media_file
, current
);
3716 /* Update and re-initialize cache, using the current sample */
3717 current
->chunk_number
+= 1;
3718 current
->sample_description_index
= sample
->index
;
3719 current
->first_dts
= sample
->dts
;
3720 /* current->pool must be flushed in isom_append_sample_internal() */
3724 static int isom_write_pooled_samples( lsmash_file_t
*file
, isom_sample_pool_t
*pool
)
3728 || !file
->bs
->stream
3729 || !(file
->flags
& LSMASH_FILE_MODE_WRITE
)
3730 || !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
3731 || ((file
->flags
& LSMASH_FILE_MODE_BOX
) && !file
->mdat
) )
3732 return LSMASH_ERR_INVALID_DATA
;
3733 lsmash_bs_put_bytes( file
->bs
, pool
->size
, pool
->data
);
3734 int err
= lsmash_bs_flush_buffer( file
->bs
);
3738 file
->mdat
->media_size
+= pool
->size
;
3739 file
->size
+= pool
->size
;
3740 pool
->sample_count
= 0;
3745 int isom_update_sample_tables
3748 lsmash_sample_t
*sample
,
3749 uint32_t *samples_per_packet
,
3750 isom_sample_entry_t
*sample_entry
3754 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)sample_entry
;
3755 if( (audio
->manager
& LSMASH_AUDIO_DESCRIPTION
)
3756 && (audio
->manager
& LSMASH_QTFF_BASE
)
3757 && (audio
->version
== 1)
3758 && (audio
->compression_ID
!= QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION
) )
3760 /* Add entries of the sample table for each uncompressed sample. */
3761 uint64_t sample_duration
= trak
->mdia
->mdhd
->timescale
/ (audio
->samplerate
>> 16);
3762 if( audio
->samplesPerPacket
== 0 || sample_duration
== 0 )
3763 return LSMASH_ERR_INVALID_DATA
;
3764 uint64_t sample_dts
= sample
->dts
;
3765 uint64_t sample_cts
= sample
->cts
;
3766 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3767 for( uint32_t i
= 0; i
< audio
->samplesPerPacket
; i
++ )
3769 /* Add a size of uncomressed audio and increment sample_count.
3770 * This points to individual uncompressed audio samples, each one byte in size, within the compressed frames. */
3771 uint32_t sample_count
= isom_add_size( stbl
, 1 );
3772 if( sample_count
== 0 )
3773 return LSMASH_ERR_NAMELESS
;
3774 /* Add a decoding timestamp and a composition timestamp. */
3775 if( (err
= isom_add_timestamp( stbl
, trak
->cache
, trak
->file
, sample_dts
, sample_cts
)) < 0 )
3777 sample_dts
+= sample_duration
;
3778 sample_cts
+= sample_duration
;
3780 *samples_per_packet
= audio
->samplesPerPacket
;
3784 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3785 /* Add a sample_size and increment sample_count. */
3786 uint32_t sample_count
= isom_add_size( stbl
, sample
->length
);
3787 if( sample_count
== 0 )
3788 return LSMASH_ERR_NAMELESS
;
3789 /* Add a decoding timestamp and a composition timestamp. */
3790 if( (err
= isom_add_timestamp( stbl
, trak
->cache
, trak
->file
, sample
->dts
, sample
->cts
)) < 0 )
3792 /* Add a sync point if needed. */
3793 if( (err
= isom_add_sync_point( stbl
, trak
->cache
, sample_count
, &sample
->prop
)) < 0 )
3795 /* Add a partial sync point if needed. */
3796 if( (err
= isom_add_partial_sync( stbl
, trak
->file
, sample_count
, &sample
->prop
)) < 0 )
3798 /* Add leading, independent, disposable and redundant information if needed. */
3799 if( stbl
->add_dependency_type
3800 && (err
= stbl
->add_dependency_type( stbl
, trak
->file
, &sample
->prop
)) < 0 )
3802 /* Group samples into random access point type if needed. */
3803 if( (err
= isom_group_random_access( (isom_box_t
*)stbl
, trak
->cache
, sample
)) < 0 )
3805 /* Group samples into random access recovery point type if needed. */
3806 if( (err
= isom_group_roll_recovery( (isom_box_t
*)stbl
, trak
->cache
, sample
)) < 0 )
3808 *samples_per_packet
= 1;
3810 /* Add a chunk if needed. */
3811 return isom_add_sample_to_chunk( trak
, sample
);
3814 static int isom_output_cached_chunk( isom_trak_t
*trak
)
3816 isom_chunk_t
*chunk
= &trak
->cache
->chunk
;
3817 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3818 isom_stsc_entry_t
*last_stsc_data
= stbl
->stsc
->list
->tail
? (isom_stsc_entry_t
*)stbl
->stsc
->list
->tail
->data
: NULL
;
3819 /* Create a new chunk sequence in this track if needed. */
3821 if( (!last_stsc_data
3822 || chunk
->pool
->sample_count
!= last_stsc_data
->samples_per_chunk
3823 || chunk
->sample_description_index
!= last_stsc_data
->sample_description_index
)
3824 && (err
= isom_add_stsc_entry( stbl
, chunk
->chunk_number
,
3825 chunk
->pool
->sample_count
,
3826 chunk
->sample_description_index
)) < 0 )
3828 lsmash_file_t
*file
= isom_get_written_media_file( trak
, chunk
->sample_description_index
);
3829 if( file
->fragment
)
3831 /* Add a new chunk offset in this track. */
3832 if( (err
= isom_add_stco_entry( stbl
, file
->size
+ ISOM_BASEBOX_COMMON_SIZE
+ file
->fragment
->pool_size
)) < 0 )
3834 return isom_append_fragment_track_run( file
, chunk
);
3836 /* Add a new chunk offset in this track. */
3837 if( (err
= isom_add_stco_entry( stbl
, file
->size
)) < 0 )
3839 /* Output pooled samples in this track. */
3840 return isom_write_pooled_samples( file
, chunk
->pool
);
3843 int isom_pool_sample( isom_sample_pool_t
*pool
, lsmash_sample_t
*sample
, uint32_t samples_per_packet
)
3845 uint64_t pool_size
= pool
->size
+ sample
->length
;
3846 if( pool
->alloc
< pool_size
)
3849 uint64_t alloc
= pool_size
+ (1<<16);
3851 data
= lsmash_malloc( alloc
);
3853 data
= lsmash_realloc( pool
->data
, alloc
);
3855 return LSMASH_ERR_MEMORY_ALLOC
;
3857 pool
->alloc
= alloc
;
3859 memcpy( pool
->data
+ pool
->size
, sample
->data
, sample
->length
);
3860 pool
->size
= pool_size
;
3861 pool
->sample_count
+= samples_per_packet
;
3862 lsmash_delete_sample( sample
);
3866 static int isom_append_sample_internal
3869 lsmash_sample_t
*sample
,
3870 isom_sample_entry_t
*sample_entry
3873 uint32_t samples_per_packet
;
3874 int ret
= isom_update_sample_tables( trak
, sample
, &samples_per_packet
, sample_entry
);
3877 /* ret == 1 means pooled samples must be flushed. */
3878 isom_sample_pool_t
*current_pool
= trak
->cache
->chunk
.pool
;
3881 /* The sample_description_index in the cache is one of the next written chunk.
3882 * Therefore, it cannot be referenced here. */
3883 lsmash_entry_list_t
*stsc_list
= trak
->mdia
->minf
->stbl
->stsc
->list
;
3884 isom_stsc_entry_t
*last_stsc_data
= (isom_stsc_entry_t
*)stsc_list
->tail
->data
;
3885 lsmash_file_t
*file
= isom_get_written_media_file( trak
, last_stsc_data
->sample_description_index
);
3886 if( (ret
= isom_write_pooled_samples( file
, current_pool
)) < 0 )
3889 /* Arbitration system between tracks with extremely scattering dts.
3890 * Here, we check whether asynchronization between the tracks exceeds the tolerance.
3891 * If a track has too old "first DTS" in its cached chunk than current sample's DTS, then its pooled samples must be flushed.
3892 * We don't consider presentation of media since any edit can pick an arbitrary portion of media in track.
3893 * Note: you needn't read this loop until you grasp the basic handling of chunks. */
3894 lsmash_file_t
*file
= trak
->file
;
3895 double tolerance
= file
->max_async_tolerance
;
3896 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
3898 isom_trak_t
*other
= (isom_trak_t
*)entry
->data
;
3904 || !other
->mdia
->mdhd
3905 || other
->mdia
->mdhd
->timescale
== 0
3906 || !other
->mdia
->minf
3907 || !other
->mdia
->minf
->stbl
3908 || !other
->mdia
->minf
->stbl
->stsc
3909 || !other
->mdia
->minf
->stbl
->stsc
->list
)
3910 return LSMASH_ERR_INVALID_DATA
;
3911 isom_chunk_t
*chunk
= &other
->cache
->chunk
;
3912 if( !chunk
->pool
|| chunk
->pool
->sample_count
== 0 )
3914 double diff
= ((double)sample
->dts
/ trak
->mdia
->mdhd
->timescale
)
3915 - ((double)chunk
->first_dts
/ other
->mdia
->mdhd
->timescale
);
3916 if( diff
> tolerance
&& (ret
= isom_output_cached_chunk( other
)) < 0 )
3918 /* Note: we don't flush the cached chunk in the current track and the current sample here
3919 * even if the conditional expression of '-diff > tolerance' meets.
3920 * That's useless because appending a sample to another track would be a good equivalent.
3921 * It's even harmful because it causes excess chunk division by calling
3922 * isom_output_cached_chunk() which always generates a new chunk.
3923 * Anyway some excess chunk division will be there, but rather less without it.
3924 * To completely avoid this, we need to observe at least whether the current sample will be placed
3925 * right next to the previous chunk of the same track or not. */
3927 /* anyway the current sample must be pooled. */
3928 return isom_pool_sample( current_pool
, sample
, samples_per_packet
);
3931 int isom_append_sample_by_type
3934 lsmash_sample_t
*sample
,
3935 isom_sample_entry_t
*sample_entry
,
3936 int (*func_append_sample
)( void *, lsmash_sample_t
*, isom_sample_entry_t
* )
3939 if( isom_is_lpcm_audio( sample_entry
) )
3941 uint32_t frame_size
= ((isom_audio_entry_t
*)sample_entry
)->constBytesPerAudioPacket
;
3942 if( sample
->length
== frame_size
)
3943 return func_append_sample( track
, sample
, sample_entry
);
3944 else if( sample
->length
< frame_size
)
3945 return LSMASH_ERR_INVALID_DATA
;
3946 /* Append samples splitted into each LPCMFrame. */
3947 uint64_t dts
= sample
->dts
;
3948 uint64_t cts
= sample
->cts
;
3949 for( uint32_t offset
= 0; offset
< sample
->length
; offset
+= frame_size
)
3951 lsmash_sample_t
*lpcm_sample
= lsmash_create_sample( frame_size
);
3953 return LSMASH_ERR_MEMORY_ALLOC
;
3954 memcpy( lpcm_sample
->data
, sample
->data
+ offset
, frame_size
);
3955 lpcm_sample
->dts
= dts
++;
3956 lpcm_sample
->cts
= cts
++;
3957 lpcm_sample
->prop
= sample
->prop
;
3958 lpcm_sample
->index
= sample
->index
;
3959 int err
= func_append_sample( track
, lpcm_sample
, sample_entry
);
3962 lsmash_delete_sample( lpcm_sample
);
3966 lsmash_delete_sample( sample
);
3969 return func_append_sample( track
, sample
, sample_entry
);
3972 /* This function is for non-fragmented movie. */
3973 static int isom_append_sample
3975 lsmash_file_t
*file
,
3977 lsmash_sample_t
*sample
,
3978 isom_sample_entry_t
*sample_entry
3981 /* If there is no available Media Data Box to write samples, add and write a new one before any chunk offset is decided. */
3985 if( !isom_add_mdat( file
) )
3986 return LSMASH_ERR_NAMELESS
;
3987 file
->mdat
->manager
|= LSMASH_PLACEHOLDER
;
3988 if( (err
= isom_write_box( file
->bs
, (isom_box_t
*)file
->mdat
)) < 0 )
3990 assert( file
->free
);
3991 file
->size
+= file
->free
->size
+ file
->mdat
->size
;
3993 return isom_append_sample_by_type( trak
, sample
, sample_entry
, (int (*)( void *, lsmash_sample_t
*, isom_sample_entry_t
* ))isom_append_sample_internal
);
3996 static int isom_output_cache( isom_trak_t
*trak
)
3999 isom_cache_t
*cache
= trak
->cache
;
4000 if( cache
->chunk
.pool
4001 && cache
->chunk
.pool
->sample_count
4002 && (err
= isom_output_cached_chunk( trak
)) < 0 )
4004 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
4005 for( lsmash_entry_t
*entry
= stbl
->sgpd_list
.head
; entry
; entry
= entry
->next
)
4007 isom_sgpd_t
*sgpd
= (isom_sgpd_t
*)entry
->data
;
4009 return LSMASH_ERR_INVALID_DATA
;
4010 switch( sgpd
->grouping_type
)
4012 case ISOM_GROUP_TYPE_RAP
:
4014 isom_rap_group_t
*group
= cache
->rap
;
4017 if( stbl
->file
->fragment
)
4020 return LSMASH_ERR_NAMELESS
;
4022 if( !group
->random_access
)
4024 group
->random_access
->num_leading_samples_known
= 1;
4027 case ISOM_GROUP_TYPE_ROLL
:
4028 case ISOM_GROUP_TYPE_PROL
:
4029 if( !cache
->roll
.pool
)
4031 if( stbl
->file
->fragment
)
4034 return LSMASH_ERR_NAMELESS
;
4036 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group( &stbl
->sbgp_list
);
4038 return LSMASH_ERR_NAMELESS
;
4039 if( (err
= isom_all_recovery_completed( sbgp
, cache
->roll
.pool
)) < 0 )
4049 int lsmash_flush_pooled_samples( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t last_sample_delta
)
4051 if( isom_check_initializer_present( root
) < 0 )
4052 return LSMASH_ERR_FUNCTION_PARAM
;
4053 lsmash_file_t
*file
= root
->file
;
4055 && file
->fragment
->movie
)
4056 return isom_flush_fragment_pooled_samples( file
, track_ID
, last_sample_delta
);
4057 if( file
!= file
->initializer
)
4058 return LSMASH_ERR_INVALID_DATA
;
4059 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
4063 || !trak
->mdia
->minf
4064 || !trak
->mdia
->minf
->stbl
4065 || !trak
->mdia
->minf
->stbl
->stsc
4066 || !trak
->mdia
->minf
->stbl
->stsc
->list
)
4067 return LSMASH_ERR_NAMELESS
;
4068 int err
= isom_output_cache( trak
);
4071 return lsmash_set_last_sample_delta( root
, track_ID
, last_sample_delta
);
4074 int lsmash_append_sample( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_sample_t
*sample
)
4076 if( isom_check_initializer_present( root
) < 0
4080 return LSMASH_ERR_FUNCTION_PARAM
;
4081 lsmash_file_t
*file
= root
->file
;
4082 /* We think max_chunk_duration == 0, which means all samples will be cached on memory, should be prevented.
4083 * This means removal of a feature that we used to have, but anyway very alone chunk does not make sense. */
4086 || !(file
->flags
& LSMASH_FILE_MODE_BOX
)
4087 || file
->max_chunk_duration
== 0
4088 || file
->max_async_tolerance
== 0 )
4089 return LSMASH_ERR_NAMELESS
;
4090 /* Write File Type Box here if it was not written yet. */
4091 if( file
->flags
& LSMASH_FILE_MODE_INITIALIZATION
)
4093 if( file
->ftyp
&& !(file
->ftyp
->manager
& LSMASH_WRITTEN_BOX
) )
4095 int err
= isom_write_box( file
->bs
, (isom_box_t
*)file
->ftyp
);
4098 file
->size
+= file
->ftyp
->size
;
4101 /* Get a sample initializer. */
4102 isom_trak_t
*trak
= isom_get_trak( file
->initializer
, track_ID
);
4108 || !trak
->mdia
->mdhd
4109 || trak
->mdia
->mdhd
->timescale
== 0
4110 || !trak
->mdia
->minf
4111 || !trak
->mdia
->minf
->stbl
4112 || !trak
->mdia
->minf
->stbl
->stsd
4113 || !trak
->mdia
->minf
->stbl
->stsc
|| !trak
->mdia
->minf
->stbl
->stsc
->list
)
4114 return LSMASH_ERR_NAMELESS
;
4115 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
, sample
->index
);
4117 return LSMASH_ERR_NAMELESS
;
4118 /* Append a sample. */
4119 if( (file
->flags
& LSMASH_FILE_MODE_FRAGMENTED
)
4121 && file
->fragment
->pool
)
4122 return isom_append_fragment_sample( file
, trak
, sample
, sample_entry
);
4123 if( file
!= file
->initializer
)
4124 return LSMASH_ERR_INVALID_DATA
;
4125 return isom_append_sample( file
, trak
, sample
, sample_entry
);
4128 /*---- misc functions ----*/
4130 int lsmash_delete_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
)
4132 if( isom_check_initializer_present( root
) < 0 )
4133 return LSMASH_ERR_FUNCTION_PARAM
;
4134 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
4136 return LSMASH_ERR_NAMELESS
;
4137 isom_remove_box_by_itself( trak
->edts
);
4138 return isom_update_tkhd_duration( trak
);
4141 void lsmash_delete_tyrant_chapter( lsmash_root_t
*root
)
4143 if( isom_check_initializer_present( root
) < 0
4144 || !root
->file
->initializer
->moov
4145 || !root
->file
->initializer
->moov
->udta
)
4147 isom_remove_box_by_itself( root
->file
->moov
->udta
->chpl
);
4150 int lsmash_set_copyright( lsmash_root_t
*root
, uint32_t track_ID
, uint16_t ISO_language
, char *notice
)
4152 if( isom_check_initializer_present( root
) < 0
4153 || (ISO_language
&& ISO_language
< 0x800)
4155 return LSMASH_ERR_FUNCTION_PARAM
;
4156 lsmash_file_t
*file
= root
->file
;
4158 || !file
->isom_compatible
)
4159 return LSMASH_ERR_NAMELESS
;
4163 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
4164 if( !trak
|| (!trak
->udta
&& !isom_add_udta( trak
)) )
4165 return LSMASH_ERR_NAMELESS
;
4170 if( !file
->moov
->udta
&& !isom_add_udta( file
->moov
) )
4171 return LSMASH_ERR_NAMELESS
;
4172 udta
= file
->moov
->udta
;
4175 for( lsmash_entry_t
*entry
= udta
->cprt_list
.head
; entry
; entry
= entry
->next
)
4177 isom_cprt_t
*cprt
= (isom_cprt_t
*)entry
->data
;
4178 if( !cprt
|| cprt
->language
== ISO_language
)
4179 return LSMASH_ERR_NAMELESS
;
4181 if( !isom_add_cprt( udta
) )
4182 return LSMASH_ERR_NAMELESS
;
4183 isom_cprt_t
*cprt
= (isom_cprt_t
*)udta
->cprt_list
.tail
->data
;
4184 cprt
->language
= ISO_language
;
4185 cprt
->notice_length
= strlen( notice
) + 1;
4186 cprt
->notice
= lsmash_memdup( notice
, cprt
->notice_length
);