1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2010-2014 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 */
38 #include "codecs/mp4a.h"
39 #include "codecs/mp4sys.h"
40 #include "codecs/description.h"
43 isom_trak_t
*isom_get_trak( lsmash_file_t
*file
, uint32_t track_ID
)
49 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
51 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
55 if( trak
->tkhd
->track_ID
== track_ID
)
61 isom_trex_t
*isom_get_trex( isom_mvex_t
*mvex
, uint32_t track_ID
)
63 if( track_ID
== 0 || !mvex
)
65 for( lsmash_entry_t
*entry
= mvex
->trex_list
.head
; entry
; entry
= entry
->next
)
67 isom_trex_t
*trex
= (isom_trex_t
*)entry
->data
;
70 if( trex
->track_ID
== track_ID
)
76 isom_traf_t
*isom_get_traf( isom_moof_t
*moof
, uint32_t track_ID
)
78 if( track_ID
== 0 || !moof
)
80 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
82 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
86 if( traf
->tfhd
->track_ID
== track_ID
)
92 isom_tfra_t
*isom_get_tfra( isom_mfra_t
*mfra
, uint32_t track_ID
)
94 if( track_ID
== 0 || !mfra
)
96 for( lsmash_entry_t
*entry
= mfra
->tfra_list
.head
; entry
; entry
= entry
->next
)
98 isom_tfra_t
*tfra
= (isom_tfra_t
*)entry
->data
;
101 if( tfra
->track_ID
== track_ID
)
107 static int isom_add_elst_entry( isom_elst_t
*elst
, uint64_t segment_duration
, int64_t media_time
, int32_t media_rate
)
109 assert( elst
->file
);
110 isom_elst_entry_t
*data
= lsmash_malloc( sizeof(isom_elst_entry_t
) );
113 data
->segment_duration
= segment_duration
;
114 data
->media_time
= media_time
;
115 data
->media_rate
= media_rate
;
116 if( lsmash_add_entry( elst
->list
, data
) )
121 if( !elst
->file
->undefined_64_ver
122 && (data
->segment_duration
> UINT32_MAX
123 || data
->media_time
> INT32_MAX
124 || data
->media_time
< INT32_MIN
) )
129 isom_dcr_ps_entry_t
*isom_create_ps_entry( uint8_t *ps
, uint32_t ps_size
)
131 isom_dcr_ps_entry_t
*entry
= lsmash_malloc( sizeof(isom_dcr_ps_entry_t
) );
134 entry
->nalUnit
= lsmash_memdup( ps
, ps_size
);
135 if( !entry
->nalUnit
)
137 lsmash_free( entry
);
140 entry
->nalUnitLength
= ps_size
;
145 void isom_remove_dcr_ps( isom_dcr_ps_entry_t
*ps
)
150 lsmash_free( ps
->nalUnit
);
154 /* This function returns 0 if failed, sample_entry_number if succeeded. */
155 int lsmash_add_sample_entry( lsmash_root_t
*root
, uint32_t track_ID
, void *summary
)
157 if( !root
|| !summary
)
159 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
164 || !trak
->mdia
->minf
->stbl
165 || !trak
->mdia
->minf
->stbl
->stsd
)
167 isom_stsd_t
*stsd
= trak
->mdia
->minf
->stbl
->stsd
;
168 lsmash_entry_list_t
*list
= &stsd
->list
;
170 lsmash_codec_type_t sample_type
= ((lsmash_summary_t
*)summary
)->sample_type
;
171 if( lsmash_check_codec_type_identical( sample_type
, LSMASH_CODEC_TYPE_RAW
) )
173 if( trak
->mdia
->minf
->vmhd
)
174 ret
= isom_setup_visual_description( stsd
, sample_type
, (lsmash_video_summary_t
*)summary
);
175 else if( trak
->mdia
->minf
->smhd
)
176 ret
= isom_setup_audio_description( stsd
, sample_type
, (lsmash_audio_summary_t
*)summary
);
177 return ret
< 0 ? 0 : list
->entry_count
;
179 typedef void (*opaque_func_t
)( void );
180 static struct description_setup_table_tag
182 lsmash_codec_type_t type
;
184 } description_setup_table
[160] = { { LSMASH_CODEC_TYPE_INITIALIZER
, NULL
} };
185 if( !description_setup_table
[0].func
)
188 #define ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( type, func ) \
189 description_setup_table[i++] = (struct description_setup_table_tag){ type, (opaque_func_t)func }
190 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO
, isom_setup_visual_description
);
191 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO
, isom_setup_visual_description
);
192 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO
, isom_setup_visual_description
);
193 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC4_VIDEO
, isom_setup_visual_description
);
194 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO
, isom_setup_visual_description
);
195 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO
, isom_setup_visual_description
);
197 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO
, isom_setup_visual_description
);
198 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO
, isom_setup_visual_description
);
199 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO
, isom_setup_visual_description
);
200 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO
, isom_setup_visual_description
);
201 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO
, isom_setup_visual_description
);
202 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO
, isom_setup_visual_description
);
203 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO
, isom_setup_visual_description
);
204 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO
, isom_setup_visual_description
);
205 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO
, isom_setup_visual_description
);
207 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO
, isom_setup_visual_description
);
208 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO
, isom_setup_visual_description
);
209 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO
, isom_setup_visual_description
);
210 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO
, isom_setup_visual_description
);
211 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO
, isom_setup_visual_description
);
212 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO
, isom_setup_visual_description
);
213 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO
, isom_setup_visual_description
);
214 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO
, isom_setup_visual_description
);
215 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO
, isom_setup_visual_description
);
216 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO
, isom_setup_visual_description
);
217 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO
, isom_setup_visual_description
);
218 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO
, isom_setup_visual_description
);
219 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO
, isom_setup_visual_description
);
220 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO
, isom_setup_visual_description
);
221 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO
, isom_setup_visual_description
);
222 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO
, isom_setup_visual_description
);
223 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO
, isom_setup_visual_description
);
224 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO
, isom_setup_visual_description
);
225 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO
, isom_setup_visual_description
);
226 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO
, isom_setup_visual_description
);
227 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO
, isom_setup_visual_description
);
228 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO
, isom_setup_visual_description
);
229 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO
, isom_setup_visual_description
);
230 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO
, isom_setup_visual_description
);
231 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO
, isom_setup_visual_description
);
232 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO
, isom_setup_visual_description
);
233 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO
, isom_setup_visual_description
);
234 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO
, isom_setup_visual_description
);
235 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO
, isom_setup_visual_description
);
236 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO
, isom_setup_visual_description
);
237 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO
, isom_setup_audio_description
);
238 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO
, isom_setup_audio_description
);
239 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO
, isom_setup_audio_description
);
240 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO
, isom_setup_audio_description
);
241 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO
, isom_setup_audio_description
);
242 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO
, isom_setup_audio_description
);
243 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO
, isom_setup_audio_description
);
244 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO
, isom_setup_audio_description
);
245 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO
, isom_setup_audio_description
);
246 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO
, isom_setup_audio_description
);
248 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO
, isom_setup_audio_description
);
249 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO
, isom_setup_audio_description
);
250 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO
, isom_setup_audio_description
);
251 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO
, isom_setup_audio_description
);
252 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO
, isom_setup_audio_description
);
253 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO
, isom_setup_audio_description
);
254 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_RAW_AUDIO
, isom_setup_audio_description
);
255 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO
, isom_setup_audio_description
);
256 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO
, isom_setup_audio_description
);
257 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO
, isom_setup_audio_description
);
258 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO
, isom_setup_audio_description
);
259 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO
, isom_setup_audio_description
);
261 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MP4A_AUDIO
, isom_setup_audio_description
);
262 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MAC3_AUDIO
, isom_setup_audio_description
);
263 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MAC6_AUDIO
, isom_setup_audio_description
);
264 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_AGSM_AUDIO
, isom_setup_audio_description
);
265 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ALAW_AUDIO
, isom_setup_audio_description
);
266 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULAW_AUDIO
, isom_setup_audio_description
);
267 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FULLMP3_AUDIO
, isom_setup_audio_description
);
268 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM2_AUDIO
, isom_setup_audio_description
);
269 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM17_AUDIO
, isom_setup_audio_description
);
270 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_GSM49_AUDIO
, isom_setup_audio_description
);
271 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO
, isom_setup_audio_description
);
272 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO
, isom_setup_audio_description
);
273 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO
, isom_setup_audio_description
);
274 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO
, isom_setup_audio_description
);
275 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO
, isom_setup_audio_description
);
276 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO
, isom_setup_audio_description
);
277 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO
, isom_setup_audio_description
);
278 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO
, isom_setup_audio_description
);
279 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO
, isom_setup_audio_description
);
280 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED
, isom_setup_audio_description
);
281 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT
, isom_add_tx3g_description
);
282 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT
, isom_add_qt_text_description
);
284 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4S_SYSTEM
, isom_add_mp4s_entry
);
287 for( int i
= 0; description_setup_table
[i
].func
; i
++ )
288 if( lsmash_check_codec_type_identical( sample_type
, description_setup_table
[i
].type
) )
290 if( (opaque_func_t
)isom_setup_visual_description
== description_setup_table
[i
].func
)
291 ret
= isom_setup_visual_description( stsd
, sample_type
, (lsmash_video_summary_t
*)summary
);
292 else if( (opaque_func_t
)isom_setup_audio_description
== description_setup_table
[i
].func
)
293 ret
= isom_setup_audio_description( stsd
, sample_type
, (lsmash_audio_summary_t
*)summary
);
294 else if( (opaque_func_t
)isom_add_tx3g_description
== description_setup_table
[i
].func
)
295 ret
= isom_setup_tx3g_description( stsd
);
296 else if( (opaque_func_t
)isom_add_qt_text_description
== description_setup_table
[i
].func
)
298 isom_qt_text_entry_t
*text
= isom_add_qt_text_description( stsd
);
301 text
->data_reference_index
= 1;
307 return ret
< 0 ? 0 : list
->entry_count
;
310 static int isom_add_stts_entry( isom_stbl_t
*stbl
, uint32_t sample_delta
)
314 || !stbl
->stts
->list
)
316 isom_stts_entry_t
*data
= lsmash_malloc( sizeof(isom_stts_entry_t
) );
319 data
->sample_count
= 1;
320 data
->sample_delta
= sample_delta
;
321 if( lsmash_add_entry( stbl
->stts
->list
, data
) )
329 static int isom_add_ctts_entry( isom_stbl_t
*stbl
, uint32_t sample_offset
)
333 || !stbl
->ctts
->list
)
335 isom_ctts_entry_t
*data
= lsmash_malloc( sizeof(isom_ctts_entry_t
) );
338 data
->sample_count
= 1;
339 data
->sample_offset
= sample_offset
;
340 if( lsmash_add_entry( stbl
->ctts
->list
, data
) )
348 static int isom_add_stsc_entry( isom_stbl_t
*stbl
, uint32_t first_chunk
, uint32_t samples_per_chunk
, uint32_t sample_description_index
)
352 || !stbl
->stsc
->list
)
354 isom_stsc_entry_t
*data
= lsmash_malloc( sizeof(isom_stsc_entry_t
) );
357 data
->first_chunk
= first_chunk
;
358 data
->samples_per_chunk
= samples_per_chunk
;
359 data
->sample_description_index
= sample_description_index
;
360 if( lsmash_add_entry( stbl
->stsc
->list
, data
) )
368 static int isom_add_stsz_entry( isom_stbl_t
*stbl
, uint32_t entry_size
)
373 isom_stsz_t
*stsz
= stbl
->stsz
;
374 /* retrieve initial sample_size */
375 if( stsz
->sample_count
== 0 )
376 stsz
->sample_size
= entry_size
;
377 /* if it seems constant access_unit size at present, update sample_count only */
378 if( !stsz
->list
&& stsz
->sample_size
== entry_size
)
380 ++ stsz
->sample_count
;
383 /* found sample_size varies, create sample_size list */
386 stsz
->list
= lsmash_create_entry_list();
389 for( uint32_t i
= 0; i
< stsz
->sample_count
; i
++ )
391 isom_stsz_entry_t
*data
= lsmash_malloc( sizeof(isom_stsz_entry_t
) );
394 data
->entry_size
= stsz
->sample_size
;
395 if( lsmash_add_entry( stsz
->list
, data
) )
401 stsz
->sample_size
= 0;
403 isom_stsz_entry_t
*data
= lsmash_malloc( sizeof(isom_stsz_entry_t
) );
406 data
->entry_size
= entry_size
;
407 if( lsmash_add_entry( stsz
->list
, data
) )
412 ++ stsz
->sample_count
;
416 static int isom_add_stss_entry( isom_stbl_t
*stbl
, uint32_t sample_number
)
420 || !stbl
->stss
->list
)
422 isom_stss_entry_t
*data
= lsmash_malloc( sizeof(isom_stss_entry_t
) );
425 data
->sample_number
= sample_number
;
426 if( lsmash_add_entry( stbl
->stss
->list
, data
) )
434 static int isom_add_stps_entry( isom_stbl_t
*stbl
, uint32_t sample_number
)
438 || !stbl
->stps
->list
)
440 isom_stps_entry_t
*data
= lsmash_malloc( sizeof(isom_stps_entry_t
) );
443 data
->sample_number
= sample_number
;
444 if( lsmash_add_entry( stbl
->stps
->list
, data
) )
452 /* Between ISOBMFF and QTFF, the most significant 2-bit has different meaning.
453 * For the maximum compatibility, we set 0 to the most significant 2-bit when compatible with
454 * both ISOBMFF with AVCFF extensions and QTFF.
455 * compatibility == 0 -> neither AVCFF extensions nor QTFF compatible
456 * compatibility == 1 -> AVCFF extensions compatible
457 * compatibility == 2 -> QTFF compatible
458 * compatibility == 3 -> both AVCFF extensions and QTFF compatible */
459 static int isom_add_sdtp_entry( isom_box_t
*parent
, lsmash_sample_property_t
*prop
, int compatibility
)
461 if( !prop
|| !parent
)
463 isom_sdtp_t
*sdtp
= NULL
;
464 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_STBL
) )
465 sdtp
= ((isom_stbl_t
*)parent
)->sdtp
;
466 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
467 sdtp
= ((isom_traf_t
*)parent
)->sdtp
;
473 isom_sdtp_entry_t
*data
= lsmash_malloc( sizeof(isom_sdtp_entry_t
) );
476 if( compatibility
== 1 )
477 data
->is_leading
= prop
->leading
& 0x03;
478 else if( compatibility
== 2 )
479 data
->is_leading
= prop
->allow_earlier
& 0x03;
482 data
->is_leading
= 0;
483 assert( compatibility
== 3 );
485 data
->sample_depends_on
= prop
->independent
& 0x03;
486 data
->sample_is_depended_on
= prop
->disposable
& 0x03;
487 data
->sample_has_redundancy
= prop
->redundant
& 0x03;
488 if( lsmash_add_entry( sdtp
->list
, data
) < 0 )
496 static int isom_add_co64_entry( isom_stbl_t
*stbl
, uint64_t chunk_offset
)
500 || !stbl
->stco
->list
)
502 isom_co64_entry_t
*data
= lsmash_malloc( sizeof(isom_co64_entry_t
) );
505 data
->chunk_offset
= chunk_offset
;
506 if( lsmash_add_entry( stbl
->stco
->list
, data
) )
514 int isom_convert_stco_to_co64( isom_stbl_t
*stbl
)
518 isom_stco_t
*stco
= stbl
->stco
;
520 if( !isom_add_co64( stbl
) )
525 /* move chunk_offset to co64 from stco */
526 for( lsmash_entry_t
*entry
= stco
->list
->head
; entry
; entry
= entry
->next
)
528 isom_stco_entry_t
*data
= (isom_stco_entry_t
*)entry
->data
;
529 if( isom_add_co64_entry( stbl
, data
->chunk_offset
) )
536 isom_remove_box_by_itself( stco
);
540 static int isom_add_stco_entry( isom_stbl_t
*stbl
, uint64_t chunk_offset
)
544 || !stbl
->stco
->list
)
546 if( stbl
->stco
->large_presentation
)
547 return isom_add_co64_entry( stbl
, chunk_offset
);
548 if( chunk_offset
> UINT32_MAX
)
550 if( isom_convert_stco_to_co64( stbl
) < 0 )
552 return isom_add_co64_entry( stbl
, chunk_offset
);
554 isom_stco_entry_t
*data
= lsmash_malloc( sizeof(isom_stco_entry_t
) );
557 data
->chunk_offset
= (uint32_t)chunk_offset
;
558 if( lsmash_add_entry( stbl
->stco
->list
, data
) )
566 static isom_sgpd_t
*isom_get_sample_group_description_common( lsmash_entry_list_t
*list
, uint32_t grouping_type
)
568 for( lsmash_entry_t
*entry
= list
->head
; entry
; entry
= entry
->next
)
570 isom_sgpd_t
*sgpd
= (isom_sgpd_t
*)entry
->data
;
574 if( sgpd
->grouping_type
== grouping_type
)
580 static isom_sbgp_t
*isom_get_sample_to_group_common( lsmash_entry_list_t
*list
, uint32_t grouping_type
)
582 for( lsmash_entry_t
*entry
= list
->head
; entry
; entry
= entry
->next
)
584 isom_sbgp_t
*sbgp
= (isom_sbgp_t
*)entry
->data
;
588 if( sbgp
->grouping_type
== grouping_type
)
594 isom_sgpd_t
*isom_get_sample_group_description( isom_stbl_t
*stbl
, uint32_t grouping_type
)
596 return isom_get_sample_group_description_common( &stbl
->sgpd_list
, grouping_type
);
599 isom_sbgp_t
*isom_get_sample_to_group( isom_stbl_t
*stbl
, uint32_t grouping_type
)
601 return isom_get_sample_to_group_common( &stbl
->sbgp_list
, grouping_type
);
604 isom_sgpd_t
*isom_get_roll_recovery_sample_group_description( lsmash_entry_list_t
*list
)
607 if( (sgpd
= isom_get_sample_group_description_common( list
, ISOM_GROUP_TYPE_ROLL
))
608 || (sgpd
= isom_get_sample_group_description_common( list
, ISOM_GROUP_TYPE_PROL
)) )
613 isom_sbgp_t
*isom_get_roll_recovery_sample_to_group( lsmash_entry_list_t
*list
)
616 if( (sbgp
= isom_get_sample_to_group_common( list
, ISOM_GROUP_TYPE_ROLL
))
617 || (sbgp
= isom_get_sample_to_group_common( list
, ISOM_GROUP_TYPE_PROL
)) )
622 isom_sgpd_t
*isom_get_fragment_sample_group_description( isom_traf_t
*traf
, uint32_t grouping_type
)
624 return isom_get_sample_group_description_common( &traf
->sgpd_list
, grouping_type
);
627 isom_sbgp_t
*isom_get_fragment_sample_to_group( isom_traf_t
*traf
, uint32_t grouping_type
)
629 return isom_get_sample_to_group_common( &traf
->sbgp_list
, grouping_type
);
632 static isom_rap_entry_t
*isom_add_rap_group_entry( isom_sgpd_t
*sgpd
)
636 isom_rap_entry_t
*data
= lsmash_malloc( sizeof(isom_rap_entry_t
) );
639 data
->description_length
= 0;
640 data
->num_leading_samples_known
= 0;
641 data
->num_leading_samples
= 0;
642 if( lsmash_add_entry( sgpd
->list
, data
) )
650 static isom_roll_entry_t
*isom_add_roll_group_entry( isom_sgpd_t
*sgpd
, int16_t roll_distance
)
654 isom_roll_entry_t
*data
= lsmash_malloc( sizeof(isom_roll_entry_t
) );
657 data
->description_length
= 0;
658 data
->roll_distance
= roll_distance
;
659 if( lsmash_add_entry( sgpd
->list
, data
) )
667 static isom_group_assignment_entry_t
*isom_add_group_assignment_entry( isom_sbgp_t
*sbgp
, uint32_t sample_count
, uint32_t group_description_index
)
671 isom_group_assignment_entry_t
*data
= lsmash_malloc( sizeof(isom_group_assignment_entry_t
) );
674 data
->sample_count
= sample_count
;
675 data
->group_description_index
= group_description_index
;
676 if( lsmash_add_entry( sbgp
->list
, data
) )
684 int isom_check_compatibility( lsmash_file_t
*file
)
688 /* Clear flags for compatibility. */
689 ptrdiff_t compat_offset
= offsetof( lsmash_file_t
, qt_compatible
);
690 memset( (int8_t *)file
+ compat_offset
, 0, sizeof(lsmash_file_t
) - compat_offset
);
691 file
->min_isom_version
= UINT8_MAX
; /* undefined value */
692 /* Get the brand container. */
693 isom_ftyp_t
*ftyp
= file
->ftyp
? file
->ftyp
: (isom_ftyp_t
*)lsmash_get_entry_data( &file
->styp_list
, 1 );
694 /* Check brand to decide mandatory boxes. */
697 /* No brand declaration means this file is a MP4 version 1 or QuickTime file format. */
699 && file
->moov
->iods
)
701 file
->mp4_version1
= 1;
702 file
->isom_compatible
= 1;
706 file
->qt_compatible
= 1;
707 file
->undefined_64_ver
= 1;
711 for( uint32_t i
= 0; i
<= ftyp
->brand_count
; i
++ )
713 uint32_t brand
= (i
== ftyp
->brand_count
? ftyp
->major_brand
: ftyp
->compatible_brands
[i
]);
716 case ISOM_BRAND_TYPE_QT
:
717 file
->qt_compatible
= 1;
719 case ISOM_BRAND_TYPE_MP41
:
720 file
->mp4_version1
= 1;
722 case ISOM_BRAND_TYPE_MP42
:
723 file
->mp4_version2
= 1;
725 case ISOM_BRAND_TYPE_AVC1
:
726 case ISOM_BRAND_TYPE_ISOM
:
727 file
->max_isom_version
= LSMASH_MAX( file
->max_isom_version
, 1 );
728 file
->min_isom_version
= LSMASH_MIN( file
->min_isom_version
, 1 );
730 case ISOM_BRAND_TYPE_ISO2
:
731 file
->max_isom_version
= LSMASH_MAX( file
->max_isom_version
, 2 );
732 file
->min_isom_version
= LSMASH_MIN( file
->min_isom_version
, 2 );
734 case ISOM_BRAND_TYPE_ISO3
:
735 file
->max_isom_version
= LSMASH_MAX( file
->max_isom_version
, 3 );
736 file
->min_isom_version
= LSMASH_MIN( file
->min_isom_version
, 3 );
738 case ISOM_BRAND_TYPE_ISO4
:
739 file
->max_isom_version
= LSMASH_MAX( file
->max_isom_version
, 4 );
740 file
->min_isom_version
= LSMASH_MIN( file
->min_isom_version
, 4 );
742 case ISOM_BRAND_TYPE_ISO5
:
743 file
->max_isom_version
= LSMASH_MAX( file
->max_isom_version
, 5 );
744 file
->min_isom_version
= LSMASH_MIN( file
->min_isom_version
, 5 );
746 case ISOM_BRAND_TYPE_ISO6
:
747 file
->max_isom_version
= LSMASH_MAX( file
->max_isom_version
, 6 );
748 file
->min_isom_version
= LSMASH_MIN( file
->min_isom_version
, 6 );
750 case ISOM_BRAND_TYPE_ISO7
:
751 file
->max_isom_version
= LSMASH_MAX( file
->max_isom_version
, 7 );
752 file
->min_isom_version
= LSMASH_MIN( file
->min_isom_version
, 7 );
754 case ISOM_BRAND_TYPE_M4A
:
755 case ISOM_BRAND_TYPE_M4B
:
756 case ISOM_BRAND_TYPE_M4P
:
757 case ISOM_BRAND_TYPE_M4V
:
758 file
->itunes_movie
= 1;
760 case ISOM_BRAND_TYPE_3GP4
:
761 file
->max_3gpp_version
= LSMASH_MAX( file
->max_3gpp_version
, 4 );
763 case ISOM_BRAND_TYPE_3GP5
:
764 file
->max_3gpp_version
= LSMASH_MAX( file
->max_3gpp_version
, 5 );
766 case ISOM_BRAND_TYPE_3GE6
:
767 case ISOM_BRAND_TYPE_3GG6
:
768 case ISOM_BRAND_TYPE_3GP6
:
769 case ISOM_BRAND_TYPE_3GR6
:
770 case ISOM_BRAND_TYPE_3GS6
:
771 file
->max_3gpp_version
= LSMASH_MAX( file
->max_3gpp_version
, 6 );
773 case ISOM_BRAND_TYPE_3GP7
:
774 file
->max_3gpp_version
= LSMASH_MAX( file
->max_3gpp_version
, 7 );
776 case ISOM_BRAND_TYPE_3GP8
:
777 file
->max_3gpp_version
= LSMASH_MAX( file
->max_3gpp_version
, 8 );
779 case ISOM_BRAND_TYPE_3GE9
:
780 case ISOM_BRAND_TYPE_3GF9
:
781 case ISOM_BRAND_TYPE_3GG9
:
782 case ISOM_BRAND_TYPE_3GH9
:
783 case ISOM_BRAND_TYPE_3GM9
:
784 case ISOM_BRAND_TYPE_3GP9
:
785 case ISOM_BRAND_TYPE_3GR9
:
786 case ISOM_BRAND_TYPE_3GS9
:
787 case ISOM_BRAND_TYPE_3GT9
:
788 file
->max_3gpp_version
= LSMASH_MAX( file
->max_3gpp_version
, 9 );
795 case ISOM_BRAND_TYPE_AVC1
:
796 case ISOM_BRAND_TYPE_ISO2
:
797 case ISOM_BRAND_TYPE_ISO3
:
798 case ISOM_BRAND_TYPE_ISO4
:
799 case ISOM_BRAND_TYPE_ISO5
:
800 case ISOM_BRAND_TYPE_ISO6
:
801 file
->avc_extensions
= 1;
803 case ISOM_BRAND_TYPE_3GP4
:
804 case ISOM_BRAND_TYPE_3GP5
:
805 case ISOM_BRAND_TYPE_3GP6
:
806 case ISOM_BRAND_TYPE_3GP7
:
807 case ISOM_BRAND_TYPE_3GP8
:
808 case ISOM_BRAND_TYPE_3GP9
:
809 file
->forbid_tref
= 1;
811 case ISOM_BRAND_TYPE_3GH9
:
812 case ISOM_BRAND_TYPE_3GM9
:
813 case ISOM_BRAND_TYPE_DASH
:
814 case ISOM_BRAND_TYPE_DSMS
:
815 case ISOM_BRAND_TYPE_LMSG
:
816 case ISOM_BRAND_TYPE_MSDH
:
817 case ISOM_BRAND_TYPE_MSIX
:
818 case ISOM_BRAND_TYPE_SIMS
:
819 file
->media_segment
= 1;
825 file
->isom_compatible
= !file
->qt_compatible
826 || file
->mp4_version1
827 || file
->mp4_version2
828 || file
->itunes_movie
829 || file
->max_3gpp_version
;
830 file
->undefined_64_ver
= file
->qt_compatible
|| file
->itunes_movie
;
831 if( file
->flags
& LSMASH_FILE_MODE_WRITE
)
833 /* Media Segment is incompatible with ISO Base Media File Format version 4 or former must be compatible with
834 * version 6 or later since it requires default-base-is-moof and Track Fragment Base Media Decode Time Box. */
835 if( file
->media_segment
&& (file
->min_isom_version
< 5 || (file
->max_isom_version
&& file
->max_isom_version
< 6)) )
837 file
->allow_moof_base
= (file
->max_isom_version
>= 5 && file
->min_isom_version
>= 5)
838 || (file
->max_isom_version
== 0 && file
->min_isom_version
== UINT8_MAX
&& file
->media_segment
);
843 uint32_t isom_get_sample_count( isom_trak_t
*trak
)
848 || !trak
->mdia
->minf
->stbl
849 || !trak
->mdia
->minf
->stbl
->stsz
)
851 return trak
->mdia
->minf
->stbl
->stsz
->sample_count
;
854 static uint64_t isom_get_dts( isom_stts_t
*stts
, uint32_t sample_number
)
861 lsmash_entry_t
*entry
;
862 isom_stts_entry_t
*data
;
863 for( entry
= stts
->list
->head
; entry
; entry
= entry
->next
)
865 data
= (isom_stts_entry_t
*)entry
->data
;
868 if( i
+ data
->sample_count
> sample_number
)
870 dts
+= (uint64_t)data
->sample_delta
* data
->sample_count
;
871 i
+= data
->sample_count
;
875 dts
+= (uint64_t)data
->sample_delta
* (sample_number
- i
);
880 static uint64_t isom_get_cts( isom_stts_t
*stts
, isom_ctts_t
*ctts
, uint32_t sample_number
)
886 return isom_get_dts( stts
, sample_number
);
887 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. */
888 lsmash_entry_t
*entry
;
889 isom_ctts_entry_t
*data
;
890 if( sample_number
== 0 )
892 for( entry
= ctts
->list
->head
; entry
; entry
= entry
->next
)
894 data
= (isom_ctts_entry_t
*)entry
->data
;
897 if( i
+ data
->sample_count
> sample_number
)
899 i
+= data
->sample_count
;
903 return isom_get_dts( stts
, sample_number
) + data
->sample_offset
;
907 static int isom_replace_last_sample_delta( isom_stbl_t
*stbl
, uint32_t sample_delta
)
912 || !stbl
->stts
->list
->tail
913 || !stbl
->stts
->list
->tail
->data
)
915 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)stbl
->stts
->list
->tail
->data
;
916 if( sample_delta
!= last_stts_data
->sample_delta
)
918 if( last_stts_data
->sample_count
> 1 )
920 last_stts_data
->sample_count
-= 1;
921 if( isom_add_stts_entry( stbl
, sample_delta
) )
925 last_stts_data
->sample_delta
= sample_delta
;
930 static int isom_update_mdhd_duration( isom_trak_t
*trak
, uint32_t last_sample_delta
)
938 || !trak
->mdia
->minf
->stbl
939 || !trak
->mdia
->minf
->stbl
->stts
940 || !trak
->mdia
->minf
->stbl
->stts
->list
)
942 lsmash_file_t
*file
= trak
->file
;
943 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
944 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
945 isom_stts_t
*stts
= stbl
->stts
;
946 isom_ctts_t
*ctts
= stbl
->ctts
;
947 isom_cslg_t
*cslg
= stbl
->cslg
;
949 uint32_t sample_count
= isom_get_sample_count( trak
);
950 if( sample_count
== 0 )
952 /* Return error if non-fragmented movie has no samples. */
953 if( !file
->fragment
&& !stts
->list
->entry_count
)
957 /* Now we have at least 1 sample, so do stts_entry. */
958 lsmash_entry_t
*last_stts
= stts
->list
->tail
;
959 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)last_stts
->data
;
960 if( sample_count
== 1 )
961 mdhd
->duration
= last_stts_data
->sample_delta
;
962 /* Now we have at least 2 samples,
963 * but dunno whether 1 stts_entry which has 2 samples or 2 stts_entry which has 1 samle each. */
966 /* use dts instead of cts */
967 mdhd
->duration
= isom_get_dts( stts
, sample_count
);
968 if( last_sample_delta
)
970 mdhd
->duration
+= last_sample_delta
;
971 if( isom_replace_last_sample_delta( stbl
, last_sample_delta
) )
974 else if( last_stts_data
->sample_count
> 1 )
975 mdhd
->duration
+= last_stts_data
->sample_delta
; /* no need to update last_stts_data->sample_delta */
978 /* Remove the last entry. */
979 if( lsmash_remove_entry_tail( stts
->list
, NULL
) )
981 /* copy the previous sample_delta. */
982 ++ ((isom_stts_entry_t
*)stts
->list
->tail
->data
)->sample_count
;
983 mdhd
->duration
+= ((isom_stts_entry_t
*)stts
->list
->tail
->data
)->sample_delta
;
989 || ctts
->list
->entry_count
== 0 )
992 uint64_t max_cts
= 0;
993 uint64_t max2_cts
= 0;
994 uint64_t min_cts
= UINT64_MAX
;
995 uint32_t max_offset
= 0;
996 uint32_t min_offset
= UINT32_MAX
;
997 int32_t ctd_shift
= trak
->cache
->timestamp
.ctd_shift
;
1000 lsmash_entry_t
*stts_entry
= stts
->list
->head
;
1001 lsmash_entry_t
*ctts_entry
= ctts
->list
->head
;
1002 for( uint32_t i
= 0; i
< sample_count
; i
++ )
1004 if( !ctts_entry
|| !stts_entry
)
1006 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
1007 isom_ctts_entry_t
*ctts_data
= (isom_ctts_entry_t
*)ctts_entry
->data
;
1008 if( !stts_data
|| !ctts_data
)
1013 /* Anyway, add composition to decode timeline shift for calculating maximum and minimum CTS correctly. */
1014 int32_t sample_offset
= (int32_t)ctts_data
->sample_offset
;
1015 cts
= dts
+ sample_offset
+ ctd_shift
;
1016 max_offset
= LSMASH_MAX( (int32_t)max_offset
, sample_offset
);
1017 min_offset
= LSMASH_MIN( (int32_t)min_offset
, sample_offset
);
1021 cts
= dts
+ ctts_data
->sample_offset
;
1022 max_offset
= LSMASH_MAX( max_offset
, ctts_data
->sample_offset
);
1023 min_offset
= LSMASH_MIN( min_offset
, ctts_data
->sample_offset
);
1025 min_cts
= LSMASH_MIN( min_cts
, cts
);
1031 else if( max2_cts
< cts
)
1033 dts
+= stts_data
->sample_delta
;
1034 /* If finished sample_count of current entry, move to next. */
1035 if( ++j
== ctts_data
->sample_count
)
1037 ctts_entry
= ctts_entry
->next
;
1040 if( ++k
== stts_data
->sample_count
)
1042 stts_entry
= stts_entry
->next
;
1046 dts
-= last_stts_data
->sample_delta
;
1047 if( file
->fragment
)
1048 /* Overall presentation is extended exceeding this initial movie.
1049 * So, any players shall display the movie exceeding the durations
1050 * indicated in Movie Header Box, Track Header Boxes and Media Header Boxes.
1051 * Samples up to the duration indicated in Movie Extends Header Box shall be displayed.
1052 * In the absence of Movie Extends Header Box, all samples shall be displayed. */
1053 mdhd
->duration
+= dts
+ last_sample_delta
;
1056 if( !last_sample_delta
)
1058 /* The spec allows an arbitrary value for the duration of the last sample. So, we pick last-1 sample's. */
1059 last_sample_delta
= max_cts
- max2_cts
;
1061 mdhd
->duration
= max_cts
- min_cts
+ last_sample_delta
;
1062 /* To match dts and media duration, update stts and mdhd relatively. */
1063 if( mdhd
->duration
> dts
)
1064 last_sample_delta
= mdhd
->duration
- dts
;
1066 mdhd
->duration
= dts
+ last_sample_delta
; /* media duration must not less than last dts. */
1068 if( isom_replace_last_sample_delta( stbl
, last_sample_delta
) )
1070 /* Explicit composition information and timeline shifting */
1071 if( cslg
|| file
->qt_compatible
|| file
->max_isom_version
>= 4 )
1075 /* Remove composition to decode timeline shift. */
1076 max_cts
-= ctd_shift
;
1077 max2_cts
-= ctd_shift
;
1078 min_cts
-= ctd_shift
;
1080 int64_t composition_end_time
= max_cts
+ (max_cts
- max2_cts
);
1082 && ((int32_t)min_offset
<= INT32_MAX
) && ((int32_t)max_offset
<= INT32_MAX
)
1083 && ((int64_t)min_cts
<= INT32_MAX
) && (composition_end_time
<= INT32_MAX
) )
1087 if( !isom_add_cslg( trak
->mdia
->minf
->stbl
) )
1091 cslg
->compositionToDTSShift
= ctd_shift
;
1092 cslg
->leastDecodeToDisplayDelta
= min_offset
;
1093 cslg
->greatestDecodeToDisplayDelta
= max_offset
;
1094 cslg
->compositionStartTime
= min_cts
;
1095 cslg
->compositionEndTime
= composition_end_time
;
1100 lsmash_free( cslg
);
1105 if( mdhd
->duration
> UINT32_MAX
&& !file
->undefined_64_ver
)
1110 static int isom_update_mvhd_duration( isom_moov_t
*moov
)
1114 || !moov
->mvhd
->file
)
1116 isom_mvhd_t
*mvhd
= moov
->mvhd
;
1118 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
1120 /* We pick maximum track duration as movie duration. */
1121 isom_trak_t
*data
= (isom_trak_t
*)entry
->data
;
1125 mvhd
->duration
= entry
!= moov
->trak_list
.head
1126 ? LSMASH_MAX( mvhd
->duration
, data
->tkhd
->duration
)
1127 : data
->tkhd
->duration
;
1129 if( mvhd
->duration
> UINT32_MAX
&& !mvhd
->file
->undefined_64_ver
)
1134 int isom_update_tkhd_duration( isom_trak_t
*trak
)
1139 || !trak
->file
->moov
)
1141 lsmash_file_t
*file
= trak
->file
;
1142 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1146 || !trak
->edts
->elst
)
1148 /* If this presentation might be extended or this track doesn't have edit list, calculate track duration from media duration. */
1150 || !trak
->mdia
->mdhd
1151 || !file
->moov
->mvhd
1152 || trak
->mdia
->mdhd
->timescale
== 0 )
1154 if( trak
->mdia
->mdhd
->duration
== 0 && isom_update_mdhd_duration( trak
, 0 ) )
1156 tkhd
->duration
= trak
->mdia
->mdhd
->duration
* ((double)file
->moov
->mvhd
->timescale
/ trak
->mdia
->mdhd
->timescale
);
1160 /* If the presentation won't be extended and this track has any edit, then track duration is just the sum of the segment_duartions. */
1161 for( lsmash_entry_t
*entry
= trak
->edts
->elst
->list
->head
; entry
; entry
= entry
->next
)
1163 isom_elst_entry_t
*data
= (isom_elst_entry_t
*)entry
->data
;
1166 tkhd
->duration
+= data
->segment_duration
;
1169 if( tkhd
->duration
> UINT32_MAX
&& !file
->undefined_64_ver
)
1171 if( !file
->fragment
&& tkhd
->duration
== 0 )
1172 tkhd
->duration
= tkhd
->version
== 1 ? 0xffffffffffffffff : 0xffffffff;
1173 return isom_update_mvhd_duration( file
->moov
);
1176 int lsmash_update_track_duration( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t last_sample_delta
)
1180 lsmash_file_t
*file
= root
->file
;
1181 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1184 if( isom_update_mdhd_duration( trak
, last_sample_delta
) )
1186 /* If the presentation won't be extended and this track has any edit, we don't change or update duration in tkhd. */
1187 return (!file
->fragment
&& trak
->edts
&& trak
->edts
->elst
)
1188 ? isom_update_mvhd_duration( file
->moov
) /* Only update movie duration. */
1189 : isom_update_tkhd_duration( trak
); /* Also update movie duration internally. */
1192 static inline int isom_increment_sample_number_in_entry( uint32_t *sample_number_in_entry
, uint32_t sample_count_in_entry
, lsmash_entry_t
**entry
)
1194 if( *sample_number_in_entry
!= sample_count_in_entry
)
1196 *sample_number_in_entry
+= 1;
1199 /* Precede the next entry. */
1200 *sample_number_in_entry
= 1;
1203 *entry
= (*entry
)->next
;
1204 if( *entry
&& !(*entry
)->data
)
1210 static int isom_calculate_bitrate_description( isom_mdia_t
*mdia
, uint32_t *bufferSizeDB
, uint32_t *maxBitrate
, uint32_t *avgBitrate
, uint32_t sample_description_index
)
1212 isom_stsz_t
*stsz
= mdia
->minf
->stbl
->stsz
;
1213 lsmash_entry_t
*stsz_entry
= stsz
->list
? stsz
->list
->head
: NULL
;
1214 lsmash_entry_t
*stts_entry
= mdia
->minf
->stbl
->stts
->list
->head
;
1215 lsmash_entry_t
*stsc_entry
= NULL
;
1216 lsmash_entry_t
*next_stsc_entry
= mdia
->minf
->stbl
->stsc
->list
->head
;
1217 isom_stts_entry_t
*stts_data
= NULL
;
1218 isom_stsc_entry_t
*stsc_data
= NULL
;
1219 if( next_stsc_entry
&& !next_stsc_entry
->data
)
1223 uint32_t time_wnd
= 0;
1224 uint32_t timescale
= mdia
->mdhd
->timescale
;
1225 uint32_t chunk_number
= 0;
1226 uint32_t sample_number_in_stts
= 1;
1227 uint32_t sample_number_in_chunk
= 1;
1233 if( !stsc_data
|| sample_number_in_chunk
== stsc_data
->samples_per_chunk
)
1235 /* Move the next chunk. */
1236 sample_number_in_chunk
= 1;
1238 /* Check if the next entry is broken. */
1239 while( next_stsc_entry
&& ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
< chunk_number
)
1241 /* Just skip broken next entry. */
1242 next_stsc_entry
= next_stsc_entry
->next
;
1243 if( next_stsc_entry
&& !next_stsc_entry
->data
)
1246 /* Check if the next chunk belongs to the next sequence of chunks. */
1247 if( next_stsc_entry
&& ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
== chunk_number
)
1249 stsc_entry
= next_stsc_entry
;
1250 next_stsc_entry
= next_stsc_entry
->next
;
1251 if( next_stsc_entry
&& !next_stsc_entry
->data
)
1253 stsc_data
= (isom_stsc_entry_t
*)stsc_entry
->data
;
1254 /* Check if the next contiguous chunks belong to given sample description. */
1255 if( stsc_data
->sample_description_index
!= sample_description_index
)
1257 /* Skip chunks which don't belong to given sample description. */
1258 uint32_t number_of_skips
= 0;
1259 uint32_t first_chunk
= stsc_data
->first_chunk
;
1260 uint32_t samples_per_chunk
= stsc_data
->samples_per_chunk
;
1261 while( next_stsc_entry
)
1263 if( ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->sample_description_index
!= sample_description_index
)
1265 stsc_data
= (isom_stsc_entry_t
*)next_stsc_entry
->data
;
1266 number_of_skips
+= (stsc_data
->first_chunk
- first_chunk
) * samples_per_chunk
;
1267 first_chunk
= stsc_data
->first_chunk
;
1268 samples_per_chunk
= stsc_data
->samples_per_chunk
;
1270 else if( ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
<= first_chunk
)
1271 ; /* broken entry */
1274 /* Just skip the next entry. */
1275 next_stsc_entry
= next_stsc_entry
->next
;
1276 if( next_stsc_entry
&& !next_stsc_entry
->data
)
1279 if( !next_stsc_entry
)
1280 break; /* There is no more chunks which don't belong to given sample description. */
1281 number_of_skips
+= (((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
- first_chunk
) * samples_per_chunk
;
1282 for( uint32_t i
= 0; i
< number_of_skips
; i
++ )
1288 stsz_entry
= stsz_entry
->next
;
1292 if( isom_increment_sample_number_in_entry( &sample_number_in_stts
, ((isom_stts_entry_t
*)stts_entry
->data
)->sample_count
, &stts_entry
) )
1295 if( (stsz
->list
&& !stsz_entry
) || !stts_entry
)
1297 chunk_number
= stsc_data
->first_chunk
;
1302 ++sample_number_in_chunk
;
1303 /* Get current sample's size. */
1309 isom_stsz_entry_t
*stsz_data
= (isom_stsz_entry_t
*)stsz_entry
->data
;
1312 size
= stsz_data
->entry_size
;
1313 stsz_entry
= stsz_entry
->next
;
1316 size
= stsz
->sample_size
;
1317 /* Get current sample's DTS. */
1319 dts
+= stts_data
->sample_delta
;
1320 stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
1323 isom_increment_sample_number_in_entry( &sample_number_in_stts
, stts_data
->sample_count
, &stts_entry
);
1324 /* Calculate bitrate description. */
1325 if( *bufferSizeDB
< size
)
1326 *bufferSizeDB
= size
;
1327 *avgBitrate
+= size
;
1329 if( dts
> time_wnd
+ timescale
)
1331 if( rate
> *maxBitrate
)
1337 double duration
= (double)mdia
->mdhd
->duration
/ timescale
;
1338 *avgBitrate
= (uint32_t)(*avgBitrate
/ duration
);
1339 if( *maxBitrate
== 0 )
1340 *maxBitrate
= *avgBitrate
;
1341 /* Convert to bits per second. */
1347 int isom_update_bitrate_description( isom_mdia_t
*mdia
)
1352 || !mdia
->minf
->stbl
)
1354 isom_stbl_t
*stbl
= mdia
->minf
->stbl
;
1357 || !stbl
->stsc
|| !stbl
->stsc
->list
1358 || !stbl
->stts
|| !stbl
->stts
->list
)
1360 uint32_t sample_description_index
= 0;
1361 for( lsmash_entry_t
*entry
= stbl
->stsd
->list
.head
; entry
; entry
= entry
->next
)
1363 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)entry
->data
;
1366 ++sample_description_index
;
1367 uint32_t bufferSizeDB
;
1368 uint32_t maxBitrate
;
1369 uint32_t avgBitrate
;
1370 /* set bitrate info */
1371 lsmash_codec_type_t sample_type
= sample_entry
->type
;
1372 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC1_VIDEO
)
1373 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC2_VIDEO
)
1374 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC3_VIDEO
)
1375 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC4_VIDEO
)
1376 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_HVC1_VIDEO
)
1377 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_HEV1_VIDEO
) )
1379 isom_visual_entry_t
*stsd_data
= (isom_visual_entry_t
*)sample_entry
;
1382 isom_btrt_t
*btrt
= (isom_btrt_t
*)isom_get_extension_box_format( &stsd_data
->extensions
, ISOM_BOX_TYPE_BTRT
);
1385 if( isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
) )
1387 btrt
->bufferSizeDB
= bufferSizeDB
;
1388 btrt
->maxBitrate
= maxBitrate
;
1389 btrt
->avgBitrate
= avgBitrate
;
1392 else if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4V_VIDEO
) )
1394 isom_visual_entry_t
*stsd_data
= (isom_visual_entry_t
*)sample_entry
;
1397 isom_esds_t
*esds
= (isom_esds_t
*)isom_get_extension_box_format( &stsd_data
->extensions
, ISOM_BOX_TYPE_ESDS
);
1398 if( !esds
|| !esds
->ES
)
1400 if( isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
) )
1402 /* FIXME: avgBitrate is 0 only if VBR in proper. */
1403 if( mp4sys_update_DecoderConfigDescriptor( esds
->ES
, bufferSizeDB
, maxBitrate
, 0 ) )
1406 else if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4A_AUDIO
) )
1408 isom_audio_entry_t
*stsd_data
= (isom_audio_entry_t
*)sample_entry
;
1411 isom_esds_t
*esds
= NULL
;
1412 if( ((isom_audio_entry_t
*)sample_entry
)->version
)
1414 /* MPEG-4 Audio in QTFF */
1415 isom_wave_t
*wave
= (isom_wave_t
*)isom_get_extension_box_format( &stsd_data
->extensions
, QT_BOX_TYPE_WAVE
);
1418 esds
= (isom_esds_t
*)isom_get_extension_box_format( &wave
->extensions
, ISOM_BOX_TYPE_ESDS
);
1421 esds
= (isom_esds_t
*)isom_get_extension_box_format( &stsd_data
->extensions
, ISOM_BOX_TYPE_ESDS
);
1422 if( !esds
|| !esds
->ES
)
1424 if( isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
) )
1426 /* FIXME: avgBitrate is 0 only if VBR in proper. */
1427 if( mp4sys_update_DecoderConfigDescriptor( esds
->ES
, bufferSizeDB
, maxBitrate
, 0 ) )
1430 else if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_ALAC_AUDIO
)
1431 || lsmash_check_codec_type_identical( sample_type
, QT_CODEC_TYPE_ALAC_AUDIO
) )
1433 isom_audio_entry_t
*alac
= (isom_audio_entry_t
*)sample_entry
;
1436 uint8_t *exdata
= NULL
;
1437 uint32_t exdata_size
= 0;
1438 isom_box_t
*alac_ext
= isom_get_extension_box( &alac
->extensions
, QT_BOX_TYPE_WAVE
);
1441 /* Apple Lossless Audio inside QuickTime file format
1442 * Though average bitrate field we found is always set to 0 apparently,
1443 * we set up maxFrameBytes and avgBitRate fields. */
1444 if( alac_ext
->manager
& LSMASH_BINARY_CODED_BOX
)
1445 exdata
= isom_get_child_box_position( alac_ext
->binary
, alac_ext
->size
, QT_BOX_TYPE_ALAC
, &exdata_size
);
1448 isom_wave_t
*wave
= (isom_wave_t
*)alac_ext
;
1449 isom_box_t
*wave_ext
= isom_get_extension_box( &wave
->extensions
, QT_BOX_TYPE_ALAC
);
1450 if( !wave_ext
|| !(wave_ext
->manager
& LSMASH_BINARY_CODED_BOX
) )
1452 exdata
= wave_ext
->binary
;
1453 exdata_size
= wave_ext
->size
;
1458 /* Apple Lossless Audio inside ISO Base Media file format */
1459 isom_box_t
*ext
= isom_get_extension_box( &alac
->extensions
, ISOM_BOX_TYPE_ALAC
);
1460 if( !ext
|| !(ext
->manager
& LSMASH_BINARY_CODED_BOX
) )
1462 exdata
= ext
->binary
;
1463 exdata_size
= ext
->size
;
1465 if( !exdata
|| exdata_size
< 36 )
1467 if( isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
) )
1471 LSMASH_SET_BE32( &exdata
[0], bufferSizeDB
);
1473 LSMASH_SET_BE32( &exdata
[4], avgBitrate
);
1475 else if( isom_is_waveform_audio( sample_type
) )
1477 isom_box_t
*ext
= isom_get_extension_box( &sample_entry
->extensions
, QT_BOX_TYPE_WAVE
);
1480 uint8_t *exdata
= NULL
;
1481 uint32_t exdata_size
= 0;
1482 if( ext
->manager
& LSMASH_BINARY_CODED_BOX
)
1483 exdata
= isom_get_child_box_position( ext
->binary
, ext
->size
, sample_type
, &exdata_size
);
1486 isom_wave_t
*wave
= (isom_wave_t
*)ext
;
1487 isom_box_t
*wave_ext
= isom_get_extension_box( &wave
->extensions
, sample_type
);
1488 if( !wave_ext
|| !(wave_ext
->manager
& LSMASH_BINARY_CODED_BOX
) )
1490 exdata
= wave_ext
->binary
;
1491 exdata_size
= wave_ext
->size
;
1493 /* Check whether exdata is valid or not. */
1494 if( !exdata
|| exdata_size
< ISOM_BASEBOX_COMMON_SIZE
+ 18 )
1496 exdata
+= ISOM_BASEBOX_COMMON_SIZE
;
1497 uint16_t cbSize
= LSMASH_GET_LE16( &exdata
[16] );
1498 if( exdata_size
< ISOM_BASEBOX_COMMON_SIZE
+ 18 + cbSize
)
1500 /* WAVEFORMATEX.nAvgBytesPerSec */
1501 if( isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
) < 0 )
1503 uint32_t nAvgBytesPerSec
= avgBitrate
/ 8;
1504 LSMASH_SET_LE32( &exdata
[8], nAvgBytesPerSec
);
1505 if( lsmash_check_codec_type_identical( sample_type
, QT_CODEC_TYPE_FULLMP3_AUDIO
)
1506 || lsmash_check_codec_type_identical( sample_type
, QT_CODEC_TYPE_MP3_AUDIO
) )
1508 /* MPEGLAYER3WAVEFORMAT.nBlockSize */
1509 uint32_t nSamplesPerSec
= LSMASH_GET_LE32( &exdata
[ 4] );
1510 uint16_t nFramesPerBlock
= LSMASH_GET_LE16( &exdata
[26] );
1511 uint16_t padding
= 0; /* FIXME? */
1512 uint16_t nBlockSize
= (144 * (avgBitrate
/ nSamplesPerSec
) + padding
) * nFramesPerBlock
;
1513 LSMASH_SET_LE16( &exdata
[24], nBlockSize
);
1516 else if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_DTSC_AUDIO
)
1517 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_DTSE_AUDIO
)
1518 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_DTSH_AUDIO
)
1519 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_DTSL_AUDIO
) )
1521 isom_audio_entry_t
*dts_audio
= (isom_audio_entry_t
*)sample_entry
;
1524 isom_box_t
*ext
= isom_get_extension_box( &dts_audio
->extensions
, ISOM_BOX_TYPE_DDTS
);
1525 if( !(ext
&& (ext
->manager
& LSMASH_BINARY_CODED_BOX
) && ext
->binary
&& ext
->size
>= 28) )
1527 if( isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
) )
1529 if( !stbl
->stsz
->list
)
1530 maxBitrate
= avgBitrate
;
1531 uint8_t *exdata
= ext
->binary
+ 12;
1532 LSMASH_SET_BE32( &exdata
[0], maxBitrate
);
1533 LSMASH_SET_BE32( &exdata
[4], avgBitrate
);
1535 else if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_EC_3_AUDIO
) )
1537 isom_audio_entry_t
*eac3
= (isom_audio_entry_t
*)sample_entry
;
1540 isom_box_t
*ext
= isom_get_extension_box( &eac3
->extensions
, ISOM_BOX_TYPE_DEC3
);
1541 if( !(ext
&& (ext
->manager
& LSMASH_BINARY_CODED_BOX
) && ext
->binary
&& ext
->size
>= 10) )
1544 if( stbl
->stsz
->list
)
1546 if( isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
) )
1548 bitrate
= maxBitrate
/ 1000; /* Use maximum bitrate if VBR. */
1551 bitrate
= stbl
->stsz
->sample_size
* (eac3
->samplerate
>> 16) / 192000; /* 192000 == 1536 * 1000 / 8 */
1552 uint8_t *exdata
= ext
->binary
+ 8;
1553 exdata
[0] = (bitrate
>> 5) & 0xff;
1554 exdata
[1] = (bitrate
& 0x1f) << 3;
1557 return sample_description_index
? 0 : -1;
1560 static int isom_check_mandatory_boxes( lsmash_file_t
*file
)
1564 || !file
->moov
->mvhd
)
1566 /* A movie requires at least one track. */
1567 if( !file
->moov
->trak_list
.head
)
1569 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
1571 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
1575 || !trak
->mdia
->mdhd
1576 || !trak
->mdia
->hdlr
1577 || !trak
->mdia
->minf
1578 || !trak
->mdia
->minf
->dinf
1579 || !trak
->mdia
->minf
->dinf
->dref
1580 || !trak
->mdia
->minf
->stbl
1581 || !trak
->mdia
->minf
->stbl
->stsd
1582 || !trak
->mdia
->minf
->stbl
->stsz
1583 || !trak
->mdia
->minf
->stbl
->stts
1584 || !trak
->mdia
->minf
->stbl
->stsc
1585 || !trak
->mdia
->minf
->stbl
->stco
)
1587 if( file
->qt_compatible
&& !trak
->mdia
->minf
->hdlr
)
1589 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
1590 if( !stbl
->stsd
->list
.head
)
1593 && (!stbl
->stsd
->list
.head
1594 || !stbl
->stts
->list
|| !stbl
->stts
->list
->head
1595 || !stbl
->stsc
->list
|| !stbl
->stsc
->list
->head
1596 || !stbl
->stco
->list
|| !stbl
->stco
->list
->head
) )
1599 if( !file
->fragment
)
1601 if( !file
->moov
->mvex
)
1603 for( lsmash_entry_t
*entry
= file
->moov
->mvex
->trex_list
.head
; entry
; entry
= entry
->next
)
1604 if( !entry
->data
) /* trex */
1609 static inline uint64_t isom_get_current_mp4time( void )
1611 return (uint64_t)time( NULL
) + ISOM_MAC_EPOCH_OFFSET
;
1614 static int isom_set_media_creation_time( isom_trak_t
*trak
, uint64_t current_mp4time
)
1617 || !trak
->mdia
->mdhd
)
1619 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
1620 if( mdhd
->creation_time
== 0 )
1621 mdhd
->creation_time
= mdhd
->modification_time
= current_mp4time
;
1625 static int isom_set_track_creation_time( isom_trak_t
*trak
, uint64_t current_mp4time
)
1630 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1631 if( tkhd
->creation_time
== 0 )
1632 tkhd
->creation_time
= tkhd
->modification_time
= current_mp4time
;
1633 if( isom_set_media_creation_time( trak
, current_mp4time
) )
1638 static int isom_set_movie_creation_time( lsmash_file_t
*file
)
1642 || !file
->moov
->mvhd
)
1644 uint64_t current_mp4time
= isom_get_current_mp4time();
1645 for( uint32_t i
= 1; i
<= file
->moov
->trak_list
.entry_count
; i
++ )
1646 if( isom_set_track_creation_time( isom_get_trak( file
, i
), current_mp4time
) )
1648 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
1649 if( mvhd
->creation_time
== 0 )
1650 mvhd
->creation_time
= mvhd
->modification_time
= current_mp4time
;
1654 int isom_setup_handler_reference( isom_hdlr_t
*hdlr
, uint32_t media_type
)
1656 isom_box_t
*parent
= hdlr
->parent
;
1657 lsmash_file_t
*file
= hdlr
->file
;
1658 if( !parent
|| !file
)
1660 isom_mdia_t
*mdia
= lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_MDIA
) ? (isom_mdia_t
*)parent
: NULL
;
1661 isom_meta_t
*meta
= lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_META
) ? (isom_meta_t
*)parent
1662 : lsmash_check_box_type_identical( parent
->type
, QT_BOX_TYPE_META
) ? (isom_meta_t
*)parent
: NULL
;
1663 uint32_t type
= mdia
? (file
->qt_compatible
? QT_HANDLER_TYPE_MEDIA
: 0) : (meta
? 0 : QT_HANDLER_TYPE_DATA
);
1664 uint32_t subtype
= media_type
;
1665 hdlr
->componentType
= type
;
1666 hdlr
->componentSubtype
= subtype
;
1667 char *type_name
= NULL
;
1668 char *subtype_name
= NULL
;
1669 uint8_t type_name_length
= 0;
1670 uint8_t subtype_name_length
= 0;
1672 type_name
= "Media ";
1674 type_name
= "Metadata ";
1675 else /* if( minf ) */
1676 type_name
= "Data ";
1677 type_name_length
= strlen( type_name
);
1682 uint8_t subtype_name_length
;
1685 { ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
, "Sound ", 6 },
1686 { ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
, "Video ", 6 },
1687 { ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK
, "Hint ", 5 },
1688 { ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK
, "Metadata ", 9 },
1689 { ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK
, "Text ", 5 },
1690 { ISOM_META_HANDLER_TYPE_ITUNES_METADATA
, "iTunes ", 7 },
1691 { QT_REFERENCE_HANDLER_TYPE_ALIAS
, "Alias ", 6 },
1692 { QT_REFERENCE_HANDLER_TYPE_RESOURCE
, "Resource ", 9 },
1693 { QT_REFERENCE_HANDLER_TYPE_URL
, "URL ", 4 },
1694 { subtype
, "Unknown ", 8 }
1696 for( int i
= 0; subtype_table
[i
].subtype
; i
++ )
1697 if( subtype
== subtype_table
[i
].subtype
)
1699 subtype_name
= subtype_table
[i
].subtype_name
;
1700 subtype_name_length
= subtype_table
[i
].subtype_name_length
;
1703 uint32_t name_length
= 15 + subtype_name_length
+ type_name_length
+ file
->isom_compatible
+ file
->qt_compatible
;
1704 uint8_t *name
= lsmash_malloc( name_length
);
1707 if( file
->qt_compatible
)
1708 name
[0] = name_length
& 0xff;
1709 memcpy( name
+ file
->qt_compatible
, "L-SMASH ", 8 );
1710 memcpy( name
+ file
->qt_compatible
+ 8, subtype_name
, subtype_name_length
);
1711 memcpy( name
+ file
->qt_compatible
+ 8 + subtype_name_length
, type_name
, type_name_length
);
1712 memcpy( name
+ file
->qt_compatible
+ 8 + subtype_name_length
+ type_name_length
, "Handler", 7 );
1713 if( file
->isom_compatible
)
1714 name
[name_length
- 1] = 0;
1715 hdlr
->componentName
= name
;
1716 hdlr
->componentName_length
= name_length
;
1720 /*******************************
1722 *******************************/
1724 /*---- track manipulators ----*/
1726 void lsmash_delete_track( lsmash_root_t
*root
, uint32_t track_ID
)
1730 || !root
->file
->moov
)
1732 for( lsmash_entry_t
*entry
= root
->file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
1734 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
1738 if( trak
->tkhd
->track_ID
== track_ID
)
1740 isom_remove_box_by_itself( trak
);
1746 uint32_t lsmash_create_track( lsmash_root_t
*root
, lsmash_media_type media_type
)
1748 if( !root
|| !root
->file
)
1750 lsmash_file_t
*file
= root
->file
;
1751 /* Don't allow to create a new track if the initial movie is already written. */
1752 if( (file
->fragment
&& file
->fragment
->movie
)
1753 || (file
->moov
&& (file
->moov
->manager
& LSMASH_WRITTEN_BOX
)) )
1755 isom_trak_t
*trak
= isom_add_trak( file
->moov
);
1758 || !trak
->file
->moov
1759 || !trak
->file
->moov
->mvhd
)
1761 if( !isom_add_tkhd( trak
)
1762 || !isom_add_mdia( trak
)
1763 || !isom_add_mdhd( trak
->mdia
)
1764 || !isom_add_minf( trak
->mdia
)
1765 || !isom_add_dinf( trak
->mdia
->minf
)
1766 || !isom_add_dref( trak
->mdia
->minf
->dinf
)
1767 || !isom_add_stbl( trak
->mdia
->minf
)
1768 || !isom_add_stsd( trak
->mdia
->minf
->stbl
)
1769 || !isom_add_stts( trak
->mdia
->minf
->stbl
)
1770 || !isom_add_stsc( trak
->mdia
->minf
->stbl
)
1771 || !isom_add_stco( trak
->mdia
->minf
->stbl
)
1772 || !isom_add_stsz( trak
->mdia
->minf
->stbl
) )
1774 if( !isom_add_hdlr( trak
->mdia
)
1775 || isom_setup_handler_reference( trak
->mdia
->hdlr
, media_type
) )
1777 if( file
->qt_compatible
)
1779 if( !isom_add_hdlr( trak
->mdia
->minf
)
1780 || isom_setup_handler_reference( trak
->mdia
->minf
->hdlr
, QT_REFERENCE_HANDLER_TYPE_URL
) )
1783 switch( media_type
)
1785 case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
:
1786 if( !isom_add_vmhd( trak
->mdia
->minf
) )
1788 trak
->mdia
->minf
->vmhd
->flags
= 0x000001;
1790 case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
:
1791 if( !isom_add_smhd( trak
->mdia
->minf
) )
1793 trak
->cache
->is_audio
= 1;
1795 case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK
:
1796 if( !isom_add_hmhd( trak
->mdia
->minf
) )
1799 case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK
:
1800 if( file
->qt_compatible
|| file
->itunes_movie
)
1802 if( !isom_add_gmhd( trak
->mdia
->minf
)
1803 || !isom_add_gmin( trak
->mdia
->minf
->gmhd
)
1804 || !isom_add_text( trak
->mdia
->minf
->gmhd
) )
1806 /* Default Text Media Information Box. */
1808 isom_text_t
*text
= trak
->mdia
->minf
->gmhd
->text
;
1809 text
->matrix
[0] = 0x00010000;
1810 text
->matrix
[4] = 0x00010000;
1811 text
->matrix
[8] = 0x40000000;
1815 goto fail
; /* We support only reference text media track for chapter yet. */
1818 if( !isom_add_nmhd( trak
->mdia
->minf
) )
1822 /* Default Track Header Box. */
1824 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1825 if( media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
)
1826 tkhd
->volume
= 0x0100;
1827 tkhd
->matrix
[0] = 0x00010000;
1828 tkhd
->matrix
[4] = 0x00010000;
1829 tkhd
->matrix
[8] = 0x40000000;
1830 tkhd
->duration
= 0xffffffff;
1831 tkhd
->track_ID
= trak
->file
->moov
->mvhd
->next_track_ID
++;
1833 trak
->mdia
->mdhd
->language
= file
->qt_compatible
? 0 : ISOM_LANGUAGE_CODE_UNDEFINED
;
1834 return trak
->tkhd
->track_ID
;
1836 isom_remove_box_by_itself( trak
);
1840 uint32_t lsmash_get_track_ID( lsmash_root_t
*root
, uint32_t track_number
)
1844 || !root
->file
->moov
)
1846 isom_trak_t
*trak
= (isom_trak_t
*)lsmash_get_entry_data( &root
->file
->moov
->trak_list
, track_number
);
1850 return trak
->tkhd
->track_ID
;
1853 void lsmash_initialize_track_parameters( lsmash_track_parameters_t
*param
)
1855 memset( param
, 0, sizeof(lsmash_track_parameters_t
) );
1856 param
->audio_volume
= 0x0100;
1857 param
->matrix
[0] = 0x00010000;
1858 param
->matrix
[4] = 0x00010000;
1859 param
->matrix
[8] = 0x40000000;
1862 int lsmash_set_track_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_track_parameters_t
*param
)
1866 lsmash_file_t
*file
= root
->file
;
1867 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1870 || !trak
->mdia
->hdlr
1871 || !file
->moov
->mvhd
)
1873 /* Prepare Track Aperture Modes if required. */
1874 if( file
->qt_compatible
&& param
->aperture_modes
)
1876 if( !trak
->tapt
&& !isom_add_tapt( trak
) )
1878 isom_tapt_t
*tapt
= trak
->tapt
;
1879 if( (!tapt
->clef
&& !isom_add_clef( tapt
))
1880 || (!tapt
->prof
&& !isom_add_prof( tapt
))
1881 || (!tapt
->enof
&& !isom_add_enof( tapt
)) )
1885 isom_remove_box_by_itself( trak
->tapt
);
1886 /* Set up Track Header. */
1887 uint32_t media_type
= trak
->mdia
->hdlr
->componentSubtype
;
1888 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1889 tkhd
->flags
= param
->mode
;
1890 tkhd
->track_ID
= param
->track_ID
? param
->track_ID
: tkhd
->track_ID
;
1891 tkhd
->duration
= !trak
->edts
|| !trak
->edts
->elst
? param
->duration
: tkhd
->duration
;
1893 * alternate_group, layer, volume and matrix
1894 * According to 14496-14, these value are all set to defaut values in 14496-12.
1895 * And when a file is read as an MPEG-4 file, these values shall be ignored.
1896 * If a file complies with other specifications, then those fields may have non-default values
1897 * as required by those other specifications. */
1898 if( param
->alternate_group
)
1900 if( file
->qt_compatible
|| file
->itunes_movie
|| file
->max_3gpp_version
>= 4 )
1901 tkhd
->alternate_group
= param
->alternate_group
;
1904 tkhd
->alternate_group
= 0;
1905 lsmash_log( NULL
, LSMASH_LOG_WARNING
,
1906 "alternate_group is specified but not compatible with any of the brands. It won't be set.\n" );
1910 tkhd
->alternate_group
= 0;
1911 if( file
->qt_compatible
|| file
->itunes_movie
)
1913 tkhd
->layer
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->video_layer
: 0;
1914 tkhd
->volume
= media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
? param
->audio_volume
: 0;
1915 if( media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
)
1916 for( int i
= 0; i
< 9; i
++ )
1917 tkhd
->matrix
[i
] = param
->matrix
[i
];
1919 for( int i
= 0; i
< 9; i
++ )
1920 tkhd
->matrix
[i
] = 0;
1925 tkhd
->volume
= media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
? 0x0100 : 0;
1926 tkhd
->matrix
[0] = 0x00010000;
1927 tkhd
->matrix
[1] = 0;
1928 tkhd
->matrix
[2] = 0;
1929 tkhd
->matrix
[3] = 0;
1930 tkhd
->matrix
[4] = 0x00010000;
1931 tkhd
->matrix
[5] = 0;
1932 tkhd
->matrix
[6] = 0;
1933 tkhd
->matrix
[7] = 0;
1934 tkhd
->matrix
[8] = 0x40000000;
1936 /* visual presentation size */
1937 tkhd
->width
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->display_width
: 0;
1938 tkhd
->height
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->display_height
: 0;
1939 /* Update next_track_ID if needed. */
1940 if( file
->moov
->mvhd
->next_track_ID
<= tkhd
->track_ID
)
1941 file
->moov
->mvhd
->next_track_ID
= tkhd
->track_ID
+ 1;
1945 int lsmash_get_track_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_track_parameters_t
*param
)
1949 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
1952 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1953 param
->mode
= tkhd
->flags
;
1954 param
->track_ID
= tkhd
->track_ID
;
1955 param
->duration
= tkhd
->duration
;
1956 param
->video_layer
= tkhd
->layer
;
1957 param
->alternate_group
= tkhd
->alternate_group
;
1958 param
->audio_volume
= tkhd
->volume
;
1959 for( int i
= 0; i
< 9; i
++ )
1960 param
->matrix
[i
] = tkhd
->matrix
[i
];
1961 param
->display_width
= tkhd
->width
;
1962 param
->display_height
= tkhd
->height
;
1963 param
->aperture_modes
= !!trak
->tapt
;
1967 static int isom_set_media_handler_name( lsmash_file_t
*file
, uint32_t track_ID
, char *handler_name
)
1969 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1972 || !trak
->mdia
->hdlr
)
1974 isom_hdlr_t
*hdlr
= trak
->mdia
->hdlr
;
1975 uint8_t *name
= NULL
;
1976 uint32_t name_length
= strlen( handler_name
) + file
->isom_compatible
+ file
->qt_compatible
;
1977 if( file
->qt_compatible
)
1978 name_length
= LSMASH_MIN( name_length
, 255 );
1979 if( name_length
> hdlr
->componentName_length
&& hdlr
->componentName
)
1980 name
= lsmash_realloc( hdlr
->componentName
, name_length
);
1981 else if( !hdlr
->componentName
)
1982 name
= lsmash_malloc( name_length
);
1984 name
= hdlr
->componentName
;
1987 if( file
->qt_compatible
)
1988 name
[0] = name_length
& 0xff;
1989 memcpy( name
+ file
->qt_compatible
, handler_name
, strlen( handler_name
) );
1990 if( file
->isom_compatible
)
1991 name
[name_length
- 1] = 0;
1992 hdlr
->componentName
= name
;
1993 hdlr
->componentName_length
= name_length
;
1997 static int isom_set_data_handler_name( lsmash_file_t
*file
, uint32_t track_ID
, char *handler_name
)
1999 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2002 || !trak
->mdia
->minf
2003 || !trak
->mdia
->minf
->hdlr
)
2005 isom_hdlr_t
*hdlr
= trak
->mdia
->minf
->hdlr
;
2006 uint8_t *name
= NULL
;
2007 uint32_t name_length
= strlen( handler_name
) + file
->isom_compatible
+ file
->qt_compatible
;
2008 if( file
->qt_compatible
)
2009 name_length
= LSMASH_MIN( name_length
, 255 );
2010 if( name_length
> hdlr
->componentName_length
&& hdlr
->componentName
)
2011 name
= lsmash_realloc( hdlr
->componentName
, name_length
);
2012 else if( !hdlr
->componentName
)
2013 name
= lsmash_malloc( name_length
);
2015 name
= hdlr
->componentName
;
2018 if( file
->qt_compatible
)
2019 name
[0] = name_length
& 0xff;
2020 memcpy( name
+ file
->qt_compatible
, handler_name
, strlen( handler_name
) );
2021 if( file
->isom_compatible
)
2022 name
[name_length
- 1] = 0;
2023 hdlr
->componentName
= name
;
2024 hdlr
->componentName_length
= name_length
;
2028 uint32_t lsmash_get_media_timescale( lsmash_root_t
*root
, uint32_t track_ID
)
2032 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
2035 || !trak
->mdia
->mdhd
)
2037 return trak
->mdia
->mdhd
->timescale
;
2040 uint64_t lsmash_get_media_duration( lsmash_root_t
*root
, uint32_t track_ID
)
2044 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
2047 || !trak
->mdia
->mdhd
)
2049 return trak
->mdia
->mdhd
->duration
;
2052 uint64_t lsmash_get_track_duration( lsmash_root_t
*root
, uint32_t track_ID
)
2056 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
2060 return trak
->tkhd
->duration
;
2063 uint32_t lsmash_get_last_sample_delta( lsmash_root_t
*root
, uint32_t track_ID
)
2067 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
2070 || !trak
->mdia
->minf
2071 || !trak
->mdia
->minf
->stbl
2072 || !trak
->mdia
->minf
->stbl
->stts
2073 || !trak
->mdia
->minf
->stbl
->stts
->list
2074 || !trak
->mdia
->minf
->stbl
->stts
->list
->tail
2075 || !trak
->mdia
->minf
->stbl
->stts
->list
->tail
->data
)
2077 return ((isom_stts_entry_t
*)trak
->mdia
->minf
->stbl
->stts
->list
->tail
->data
)->sample_delta
;
2080 uint32_t lsmash_get_start_time_offset( lsmash_root_t
*root
, uint32_t track_ID
)
2084 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
2087 || !trak
->mdia
->minf
2088 || !trak
->mdia
->minf
->stbl
2089 || !trak
->mdia
->minf
->stbl
->ctts
2090 || !trak
->mdia
->minf
->stbl
->ctts
->list
2091 || !trak
->mdia
->minf
->stbl
->ctts
->list
->head
2092 || !trak
->mdia
->minf
->stbl
->ctts
->list
->head
->data
)
2094 return ((isom_ctts_entry_t
*)trak
->mdia
->minf
->stbl
->ctts
->list
->head
->data
)->sample_offset
;
2097 uint32_t lsmash_get_composition_to_decode_shift( lsmash_root_t
*root
, uint32_t track_ID
)
2101 lsmash_file_t
*file
= root
->file
;
2102 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2105 || !trak
->mdia
->minf
2106 || !trak
->mdia
->minf
->stbl
)
2108 uint32_t sample_count
= isom_get_sample_count( trak
);
2109 if( sample_count
== 0 )
2111 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2112 if( !stbl
->stts
|| !stbl
->stts
->list
2113 || !stbl
->ctts
|| !stbl
->ctts
->list
)
2115 if( !(file
->max_isom_version
>= 4 && stbl
->ctts
->version
== 1) && !file
->qt_compatible
)
2116 return 0; /* This movie shall not have composition to decode timeline shift. */
2117 lsmash_entry_t
*stts_entry
= stbl
->stts
->list
->head
;
2118 lsmash_entry_t
*ctts_entry
= stbl
->ctts
->list
->head
;
2119 if( !stts_entry
|| !ctts_entry
)
2123 uint32_t ctd_shift
= 0;
2126 for( uint32_t k
= 0; k
< sample_count
; k
++ )
2128 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
2129 isom_ctts_entry_t
*ctts_data
= (isom_ctts_entry_t
*)ctts_entry
->data
;
2130 if( !stts_data
|| !ctts_data
)
2132 cts
= dts
+ (int32_t)ctts_data
->sample_offset
;
2133 if( dts
> cts
+ ctd_shift
)
2134 ctd_shift
= dts
- cts
;
2135 dts
+= stts_data
->sample_delta
;
2136 if( ++i
== stts_data
->sample_count
)
2138 stts_entry
= stts_entry
->next
;
2143 if( ++j
== ctts_data
->sample_count
)
2145 ctts_entry
= ctts_entry
->next
;
2154 uint16_t lsmash_pack_iso_language( char *iso_language
)
2156 if( !iso_language
|| strlen( iso_language
) != 3 )
2158 return (uint16_t)LSMASH_PACK_ISO_LANGUAGE( iso_language
[0], iso_language
[1], iso_language
[2] );
2161 static int isom_iso2mac_language( uint16_t ISO_language
, uint16_t *MAC_language
)
2166 for( ; isom_languages
[i
].iso_name
; i
++ )
2167 if( ISO_language
== isom_languages
[i
].iso_name
)
2169 if( !isom_languages
[i
].iso_name
)
2171 *MAC_language
= isom_languages
[i
].mac_value
;
2175 static int isom_mac2iso_language( uint16_t MAC_language
, uint16_t *ISO_language
)
2180 for( ; isom_languages
[i
].iso_name
; i
++ )
2181 if( MAC_language
== isom_languages
[i
].mac_value
)
2183 *ISO_language
= isom_languages
[i
].iso_name
? isom_languages
[i
].iso_name
: ISOM_LANGUAGE_CODE_UNDEFINED
;
2187 static int isom_set_media_language( lsmash_file_t
*file
, uint32_t track_ID
, uint16_t ISO_language
, uint16_t MAC_language
)
2189 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2192 || !trak
->mdia
->mdhd
)
2194 uint16_t language
= 0;
2195 if( file
->isom_compatible
)
2198 language
= ISO_language
;
2199 else if( MAC_language
)
2201 if( isom_mac2iso_language( MAC_language
, &language
) )
2205 language
= ISOM_LANGUAGE_CODE_UNDEFINED
;
2207 else if( file
->qt_compatible
)
2211 if( isom_iso2mac_language( ISO_language
, &language
) )
2215 language
= MAC_language
;
2219 trak
->mdia
->mdhd
->language
= language
;
2223 int isom_add_sample_grouping( isom_box_t
*parent
, isom_grouping_type grouping_type
)
2227 if( NULL
== (sgpd
= isom_add_sgpd( parent
))
2228 || NULL
== (sbgp
= isom_add_sbgp( parent
)) )
2230 sbgp
->grouping_type
= grouping_type
;
2231 sgpd
->grouping_type
= grouping_type
;
2232 sgpd
->version
= 1; /* We use version 1 for Sample Group Description Box because it is recommended in the spec. */
2233 switch( grouping_type
)
2235 case ISOM_GROUP_TYPE_RAP
:
2236 sgpd
->default_length
= 1;
2238 case ISOM_GROUP_TYPE_ROLL
:
2239 case ISOM_GROUP_TYPE_PROL
:
2240 sgpd
->default_length
= 2;
2243 /* We don't consider other grouping types currently. */
2249 static int isom_create_sample_grouping( isom_trak_t
*trak
, isom_grouping_type grouping_type
)
2251 lsmash_file_t
*file
= trak
->file
;
2252 switch( grouping_type
)
2254 case ISOM_GROUP_TYPE_RAP
:
2255 assert( file
->max_isom_version
>= 6 );
2257 case ISOM_GROUP_TYPE_ROLL
:
2258 case ISOM_GROUP_TYPE_PROL
:
2259 assert( file
->avc_extensions
|| file
->qt_compatible
);
2265 if( isom_add_sample_grouping( (isom_box_t
*)trak
->mdia
->minf
->stbl
, grouping_type
) < 0 )
2267 if( trak
->cache
->fragment
&& file
->max_isom_version
>= 6 )
2268 switch( grouping_type
)
2270 case ISOM_GROUP_TYPE_RAP
:
2271 trak
->cache
->fragment
->rap_grouping
= 1;
2273 case ISOM_GROUP_TYPE_ROLL
:
2274 case ISOM_GROUP_TYPE_PROL
:
2275 trak
->cache
->fragment
->roll_grouping
= 1;
2278 /* We don't consider other grouping types currently. */
2284 void lsmash_initialize_media_parameters( lsmash_media_parameters_t
*param
)
2286 memset( param
, 0, sizeof(lsmash_media_parameters_t
) );
2287 param
->timescale
= 1;
2290 int lsmash_set_media_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_media_parameters_t
*param
)
2294 lsmash_file_t
*file
= root
->file
;
2295 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2298 || !trak
->mdia
->mdhd
2299 || !trak
->mdia
->minf
2300 || !trak
->mdia
->minf
->stbl
)
2302 trak
->mdia
->mdhd
->timescale
= param
->timescale
;
2303 if( isom_set_media_language( file
, track_ID
, param
->ISO_language
, param
->MAC_language
) )
2305 if( param
->media_handler_name
2306 && isom_set_media_handler_name( file
, track_ID
, param
->media_handler_name
) )
2308 if( file
->qt_compatible
&& param
->data_handler_name
2309 && isom_set_data_handler_name( file
, track_ID
, param
->data_handler_name
) )
2311 if( (file
->avc_extensions
|| file
->qt_compatible
) && param
->roll_grouping
2312 && isom_create_sample_grouping( trak
, ISOM_GROUP_TYPE_ROLL
) )
2314 if( (file
->max_isom_version
>= 6) && param
->rap_grouping
2315 && isom_create_sample_grouping( trak
, ISOM_GROUP_TYPE_RAP
) )
2320 int lsmash_get_media_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_media_parameters_t
*param
)
2324 lsmash_file_t
*file
= root
->file
;
2325 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2328 || !trak
->mdia
->mdhd
2329 || !trak
->mdia
->hdlr
2330 || !trak
->mdia
->minf
2331 || !trak
->mdia
->minf
->stbl
)
2333 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
2334 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2335 param
->timescale
= mdhd
->timescale
;
2336 param
->handler_type
= trak
->mdia
->hdlr
->componentSubtype
;
2337 param
->duration
= mdhd
->duration
;
2338 /* Whether sample grouping present. */
2342 sbgp
= isom_get_sample_to_group ( stbl
, ISOM_GROUP_TYPE_RAP
);
2343 sgpd
= isom_get_sample_group_description( stbl
, ISOM_GROUP_TYPE_RAP
);
2344 param
->rap_grouping
= sbgp
&& sgpd
;
2345 sbgp
= isom_get_roll_recovery_sample_to_group ( &stbl
->sbgp_list
);
2346 sgpd
= isom_get_roll_recovery_sample_group_description( &stbl
->sgpd_list
);
2347 param
->roll_grouping
= sbgp
&& sgpd
;
2349 /* Get media language. */
2350 if( mdhd
->language
>= 0x800 )
2352 param
->MAC_language
= 0;
2353 param
->ISO_language
= mdhd
->language
;
2357 param
->MAC_language
= mdhd
->language
;
2358 param
->ISO_language
= 0;
2360 /* Get handler name(s). */
2361 isom_hdlr_t
*hdlr
= trak
->mdia
->hdlr
;
2362 int length
= LSMASH_MIN( 255, hdlr
->componentName_length
);
2365 memcpy( param
->media_handler_name_shadow
, hdlr
->componentName
+ file
->qt_compatible
, length
);
2366 param
->media_handler_name_shadow
[length
- 2 + file
->isom_compatible
+ file
->qt_compatible
] = '\0';
2367 param
->media_handler_name
= param
->media_handler_name_shadow
;
2371 param
->media_handler_name
= NULL
;
2372 memset( param
->media_handler_name_shadow
, 0, sizeof(param
->media_handler_name_shadow
) );
2374 if( trak
->mdia
->minf
->hdlr
)
2376 hdlr
= trak
->mdia
->minf
->hdlr
;
2377 length
= LSMASH_MIN( 255, hdlr
->componentName_length
);
2380 memcpy( param
->data_handler_name_shadow
, hdlr
->componentName
+ file
->qt_compatible
, length
);
2381 param
->data_handler_name_shadow
[length
- 2 + file
->isom_compatible
+ file
->qt_compatible
] = '\0';
2382 param
->data_handler_name
= param
->data_handler_name_shadow
;
2386 param
->data_handler_name
= NULL
;
2387 memset( param
->data_handler_name_shadow
, 0, sizeof(param
->data_handler_name_shadow
) );
2392 param
->data_handler_name
= NULL
;
2393 memset( param
->data_handler_name_shadow
, 0, sizeof(param
->data_handler_name_shadow
) );
2398 /*---- movie manipulators ----*/
2400 void lsmash_discard_boxes( lsmash_root_t
*root
)
2402 if( !root
|| !root
->file
)
2404 isom_remove_all_extension_boxes( &root
->file
->extensions
);
2407 int lsmash_open_file
2409 const char *filename
,
2411 lsmash_file_parameters_t
*param
2414 if( !filename
|| !param
)
2416 char mode
[4] = { 0 };
2417 lsmash_file_mode file_mode
= 0;
2418 if( open_mode
== 0 )
2420 memcpy( mode
, "w+b", 4 );
2421 file_mode
= LSMASH_FILE_MODE_WRITE
2422 | LSMASH_FILE_MODE_BOX
2423 | LSMASH_FILE_MODE_INITIALIZATION
2424 | LSMASH_FILE_MODE_MEDIA
;
2426 #ifdef LSMASH_DEMUXER_ENABLED
2427 else if( open_mode
== 1 )
2429 memcpy( mode
, "rb", 3 );
2430 file_mode
= LSMASH_FILE_MODE_READ
;
2433 if( file_mode
== 0 )
2435 FILE *stream
= NULL
;
2437 if( !strcmp( filename
, "-" ) )
2439 if( file_mode
& LSMASH_FILE_MODE_READ
)
2444 else if( file_mode
& LSMASH_FILE_MODE_WRITE
)
2448 file_mode
|= LSMASH_FILE_MODE_FRAGMENTED
;
2452 stream
= lsmash_fopen( filename
, mode
);
2455 memset( param
, 0, sizeof(lsmash_file_parameters_t
) );
2456 param
->mode
= file_mode
;
2457 param
->opaque
= (void *)stream
;
2458 param
->read
= lsmash_fread_wrapper
;
2459 param
->write
= lsmash_fwrite_wrapper
;
2460 param
->seek
= seekable
? lsmash_fseek_wrapper
: NULL
;
2461 param
->major_brand
= 0;
2462 param
->brands
= NULL
;
2463 param
->brand_count
= 0;
2464 param
->minor_version
= 0;
2465 param
->max_chunk_duration
= 0.5;
2466 param
->max_async_tolerance
= 2.0;
2467 param
->max_chunk_size
= 4 * 1024 * 1024;
2468 param
->max_read_size
= 4 * 1024 * 1024;
2472 int lsmash_close_file
2474 lsmash_file_parameters_t
*param
2479 if( !param
->opaque
)
2481 int ret
= fclose( (FILE *)param
->opaque
);
2482 param
->opaque
= NULL
;
2486 static int isom_set_brands
2488 lsmash_file_t
*file
,
2489 lsmash_brand_type major_brand
,
2490 uint32_t minor_version
,
2491 lsmash_brand_type
*brands
,
2492 uint32_t brand_count
2495 if( brand_count
> 50 )
2496 return -1; /* We support setting brands up to 50. */
2497 if( major_brand
== 0 || brand_count
== 0 )
2499 /* Absence of File Type Box means this file is a QuickTime or MP4 version 1 format file. */
2500 isom_remove_box_by_itself( file
->ftyp
);
2501 /* Anyway we use QTFF as a default file format. */
2502 file
->qt_compatible
= 1;
2506 if( file
->flags
& LSMASH_FILE_MODE_INITIALIZATION
)
2508 /* Add File Type Box if absent yet. */
2509 if( !file
->ftyp
&& !isom_add_ftyp( file
) )
2515 /* Add Segment Type Box if absent yet. */
2516 ftyp
= file
->styp_list
.head
&& file
->styp_list
.head
->data
2517 ? (isom_styp_t
*)file
->styp_list
.head
->data
2518 : isom_add_styp( file
);
2522 /* Allocate an array of compatible brands.
2523 * ISO/IEC 14496-12 doesn't forbid the absence of brands in the compatible brand list.
2524 * For a reason of safety, however, we set at least one brand in the list. */
2525 size_t alloc_size
= (brand_count
? brand_count
: 1) * sizeof(uint32_t);
2526 lsmash_brand_type
*compatible_brands
;
2527 if( !file
->compatible_brands
)
2528 compatible_brands
= lsmash_malloc( alloc_size
);
2530 compatible_brands
= lsmash_realloc( file
->compatible_brands
, alloc_size
);
2531 if( !compatible_brands
)
2533 /* Set compatible brands. */
2535 for( uint32_t i
= 0; i
< brand_count
; i
++ )
2536 compatible_brands
[i
] = brands
[i
];
2539 /* At least one compatible brand. */
2540 compatible_brands
[0] = major_brand
;
2543 file
->compatible_brands
= compatible_brands
;
2544 /* Duplicate an array of compatible brands. */
2545 lsmash_free( ftyp
->compatible_brands
);
2546 ftyp
->compatible_brands
= lsmash_memdup( compatible_brands
, alloc_size
);
2547 if( !ftyp
->compatible_brands
)
2549 lsmash_freep( &file
->compatible_brands
);
2552 ftyp
->size
= ISOM_BASEBOX_COMMON_SIZE
+ 8 + brand_count
* 4;
2553 ftyp
->major_brand
= major_brand
;
2554 ftyp
->minor_version
= minor_version
;
2555 ftyp
->brand_count
= brand_count
;
2556 file
->brand_count
= brand_count
;
2557 return isom_check_compatibility( file
);
2560 lsmash_file_t
*lsmash_set_file
2562 lsmash_root_t
*root
,
2563 lsmash_file_parameters_t
*param
2566 if( !root
|| !param
)
2569 /* Handling of multiple files is not supported yet. */
2571 lsmash_file_t
*file
= isom_add_file( root
);
2574 lsmash_bs_t
*bs
= lsmash_bs_create();
2578 file
->flags
= param
->mode
;
2579 file
->bs
->stream
= param
->opaque
;
2580 file
->bs
->read
= param
->read
;
2581 file
->bs
->write
= param
->write
;
2582 file
->bs
->seek
= param
->seek
;
2583 file
->bs
->unseekable
= (param
->seek
== NULL
);
2584 file
->bs
->buffer
.max_size
= param
->max_read_size
;
2585 file
->max_chunk_duration
= param
->max_chunk_duration
;
2586 file
->max_async_tolerance
= LSMASH_MAX( param
->max_async_tolerance
, 2 * param
->max_chunk_duration
);
2587 file
->max_chunk_size
= param
->max_chunk_size
;
2588 if( (file
->flags
& LSMASH_FILE_MODE_WRITE
)
2589 && (file
->flags
& LSMASH_FILE_MODE_BOX
) )
2591 /* Establish the fragment handler if required. */
2592 if( file
->flags
& LSMASH_FILE_MODE_FRAGMENTED
)
2594 file
->fragment
= lsmash_malloc_zero( sizeof(isom_fragment_manager_t
) );
2595 if( !file
->fragment
)
2597 file
->fragment
->pool
= lsmash_create_entry_list();
2598 if( !file
->fragment
->pool
)
2601 else if( file
->bs
->unseekable
)
2602 /* For unseekable output operations, LSMASH_FILE_MODE_FRAGMENTED shall be set. */
2604 /* Establish file types. */
2605 if( isom_set_brands( file
, param
->major_brand
,
2606 param
->minor_version
,
2607 param
->brands
, param
->brand_count
) < 0 )
2609 /* Create the movie header if the initialization of the streams is required. */
2610 if( file
->flags
& LSMASH_FILE_MODE_INITIALIZATION
)
2612 if( !isom_add_moov( file
)
2613 || !isom_add_mvhd( file
->moov
) )
2615 /* Default Movie Header Box. */
2616 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
2617 mvhd
->rate
= 0x00010000;
2618 mvhd
->volume
= 0x0100;
2619 mvhd
->matrix
[0] = 0x00010000;
2620 mvhd
->matrix
[4] = 0x00010000;
2621 mvhd
->matrix
[8] = 0x40000000;
2622 mvhd
->next_track_ID
= 1;
2628 isom_remove_box_by_itself( file
);
2632 int64_t lsmash_read_file
2634 lsmash_file_t
*file
,
2635 lsmash_file_parameters_t
*param
2638 #ifdef LSMASH_DEMUXER_ENABLED
2639 if( !file
|| !file
->bs
)
2642 if( file
->flags
& (LSMASH_FILE_MODE_READ
| LSMASH_FILE_MODE_DUMP
) )
2644 /* Get the file size if seekable when reading. */
2645 if( !file
->bs
->unseekable
)
2647 ret
= lsmash_bs_read_seek( file
->bs
, 0, SEEK_END
);
2650 file
->bs
->written
= ret
;
2651 lsmash_bs_read_seek( file
->bs
, 0, SEEK_SET
);
2655 /* Read whole boxes. */
2656 if( isom_read_file( file
) < 0 )
2663 isom_ftyp_t
*ftyp
= file
->ftyp
;
2664 param
->major_brand
= ftyp
->major_brand
? ftyp
->major_brand
: ISOM_BRAND_TYPE_QT
;
2665 param
->minor_version
= ftyp
->minor_version
;
2666 param
->brands
= file
->compatible_brands
;
2667 param
->brand_count
= file
->brand_count
;
2669 else if( file
->styp_list
.head
&& file
->styp_list
.head
->data
)
2672 isom_styp_t
*styp
= (isom_styp_t
*)file
->styp_list
.head
->data
;
2673 param
->major_brand
= styp
->major_brand
? styp
->major_brand
: ISOM_BRAND_TYPE_QT
;
2674 param
->minor_version
= styp
->minor_version
;
2675 param
->brands
= file
->compatible_brands
;
2676 param
->brand_count
= file
->brand_count
;
2680 param
->major_brand
= file
->mp4_version1
? ISOM_BRAND_TYPE_MP41
: ISOM_BRAND_TYPE_QT
;
2681 param
->minor_version
= 0;
2682 param
->brands
= NULL
;
2683 param
->brand_count
= 0;
2693 lsmash_root_t
*lsmash_open_movie( const char *filename
, lsmash_file_mode mode
)
2695 if( !filename
|| ((mode
& LSMASH_FILE_MODE_WRITE
) && (mode
& LSMASH_FILE_MODE_READ
)) )
2698 if( mode
& LSMASH_FILE_MODE_WRITE
)
2700 #ifdef LSMASH_DEMUXER_ENABLED
2701 else if( mode
& LSMASH_FILE_MODE_READ
)
2706 lsmash_root_t
*root
= lsmash_create_root();
2709 lsmash_file_parameters_t param
;
2710 if( lsmash_open_file( filename
, open_mode
, ¶m
) < 0 )
2712 lsmash_destroy_root( root
);
2716 lsmash_file_t
*file
= lsmash_set_file( root
, ¶m
);
2717 if( !file
|| (open_mode
== 1 && lsmash_read_file( file
, ¶m
) < 0) )
2719 lsmash_close_file( ¶m
);
2720 lsmash_destroy_root( root
);
2723 /* Don't fclose() here. */
2724 param
.opaque
= NULL
;
2725 lsmash_close_file( ¶m
);
2726 /* Call fclose() when calling lsmash_destroy_root(). */
2727 file
->bc_fclose
= 1;
2731 void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t
*param
)
2733 memset( param
, 0, sizeof(lsmash_movie_parameters_t
) );
2734 param
->timescale
= 600;
2735 param
->playback_rate
= 0x00010000;
2736 param
->playback_volume
= 0x0100;
2739 int lsmash_set_movie_parameters( lsmash_root_t
*root
, lsmash_movie_parameters_t
*param
)
2743 lsmash_file_t
*file
= root
->file
;
2746 || !file
->moov
->mvhd
2747 || ((param
->major_brand
|| (param
->brands
&& param
->number_of_brands
))
2748 && isom_set_brands( file
, param
->major_brand
, param
->minor_version
, param
->brands
, param
->number_of_brands
) < 0) )
2750 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
2751 if( param
->max_chunk_duration
> 0 )
2752 file
->max_chunk_duration
= param
->max_chunk_duration
;
2753 if( param
->max_async_tolerance
> 0 )
2754 file
->max_async_tolerance
= LSMASH_MAX( param
->max_async_tolerance
, 2 * param
->max_chunk_duration
);
2755 if( param
->max_chunk_size
)
2756 file
->max_chunk_size
= param
->max_chunk_size
;
2757 if( file
->bs
&& param
->max_read_size
)
2758 file
->bs
->buffer
.max_size
= param
->max_read_size
;
2759 mvhd
->timescale
= param
->timescale
;
2760 if( file
->qt_compatible
|| file
->itunes_movie
)
2762 mvhd
->rate
= param
->playback_rate
;
2763 mvhd
->volume
= param
->playback_volume
;
2764 mvhd
->previewTime
= param
->preview_time
;
2765 mvhd
->previewDuration
= param
->preview_duration
;
2766 mvhd
->posterTime
= param
->poster_time
;
2770 mvhd
->rate
= 0x00010000;
2771 mvhd
->volume
= 0x0100;
2772 mvhd
->previewTime
= 0;
2773 mvhd
->previewDuration
= 0;
2774 mvhd
->posterTime
= 0;
2779 int lsmash_get_movie_parameters( lsmash_root_t
*root
, lsmash_movie_parameters_t
*param
)
2783 lsmash_file_t
*file
= root
->file
;
2786 || !file
->moov
->mvhd
)
2788 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
2791 isom_ftyp_t
*ftyp
= file
->ftyp
;
2792 uint32_t brand_count
= LSMASH_MIN( ftyp
->brand_count
, 50 ); /* brands up to 50 */
2793 for( uint32_t i
= 0; i
< brand_count
; i
++ )
2794 param
->brands_shadow
[i
] = ftyp
->compatible_brands
[i
];
2795 param
->major_brand
= ftyp
->major_brand
;
2796 param
->brands
= param
->brands_shadow
;
2797 param
->number_of_brands
= brand_count
;
2798 param
->minor_version
= ftyp
->minor_version
;
2800 param
->max_chunk_duration
= file
->max_chunk_duration
;
2801 param
->max_async_tolerance
= file
->max_async_tolerance
;
2802 param
->max_chunk_size
= file
->max_chunk_size
;
2803 param
->max_read_size
= file
->bs
? file
->bs
->buffer
.max_size
: 0;
2804 param
->timescale
= mvhd
->timescale
;
2805 param
->duration
= mvhd
->duration
;
2806 param
->playback_rate
= mvhd
->rate
;
2807 param
->playback_volume
= mvhd
->volume
;
2808 param
->preview_time
= mvhd
->previewTime
;
2809 param
->preview_duration
= mvhd
->previewDuration
;
2810 param
->poster_time
= mvhd
->posterTime
;
2811 param
->number_of_tracks
= file
->moov
->trak_list
.entry_count
;
2815 uint32_t lsmash_get_movie_timescale( lsmash_root_t
*root
)
2819 || !root
->file
->moov
2820 || !root
->file
->moov
->mvhd
)
2822 return root
->file
->moov
->mvhd
->timescale
;
2825 static int isom_scan_trak_profileLevelIndication
2828 mp4a_audioProfileLevelIndication
*audio_pli
,
2829 mp4sys_visualProfileLevelIndication
*visual_pli
2834 || !trak
->mdia
->minf
2835 || !trak
->mdia
->minf
->stbl
)
2837 isom_stsd_t
*stsd
= trak
->mdia
->minf
->stbl
->stsd
;
2839 || !stsd
->list
.head
)
2841 for( lsmash_entry_t
*entry
= stsd
->list
.head
; entry
; entry
= entry
->next
)
2843 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)entry
->data
;
2846 lsmash_codec_type_t sample_type
= sample_entry
->type
;
2847 if( trak
->mdia
->minf
->vmhd
)
2849 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC1_VIDEO
)
2850 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC2_VIDEO
)
2851 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC3_VIDEO
)
2852 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC4_VIDEO
)
2853 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVCP_VIDEO
)
2854 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_SVC1_VIDEO
)
2855 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MVC1_VIDEO
)
2856 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MVC2_VIDEO
) )
2858 /* FIXME: Do we have to arbitrate like audio? */
2859 if( *visual_pli
== MP4SYS_VISUAL_PLI_NONE_REQUIRED
)
2860 *visual_pli
= MP4SYS_VISUAL_PLI_H264_AVC
;
2863 *visual_pli
= MP4SYS_VISUAL_PLI_NOT_SPECIFIED
;
2865 else if( trak
->mdia
->minf
->smhd
)
2867 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4A_AUDIO
) )
2869 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)sample_entry
;
2870 isom_esds_t
*esds
= (isom_esds_t
*)isom_get_extension_box_format( &audio
->extensions
, ISOM_BOX_TYPE_ESDS
);
2871 if( !esds
|| !esds
->ES
)
2873 lsmash_audio_summary_t
*summary
= (lsmash_audio_summary_t
*)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO
);
2876 if( mp4sys_setup_summary_from_DecoderSpecificInfo( summary
, esds
->ES
) < 0 )
2877 *audio_pli
= MP4A_AUDIO_PLI_NOT_SPECIFIED
;
2879 *audio_pli
= mp4a_max_audioProfileLevelIndication( *audio_pli
, mp4a_get_audioProfileLevelIndication( summary
) );
2880 lsmash_cleanup_summary( (lsmash_summary_t
*)summary
);
2883 /* NOTE: Audio CODECs other than 'mp4a' does not have appropriate pli. */
2884 *audio_pli
= MP4A_AUDIO_PLI_NOT_SPECIFIED
;
2887 ; /* FIXME: Do we have to set OD_profileLevelIndication? */
2892 int isom_setup_iods( isom_moov_t
*moov
)
2894 if( !moov
->iods
&& !isom_add_iods( moov
) )
2896 isom_iods_t
*iods
= moov
->iods
;
2897 iods
->OD
= mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */
2900 mp4a_audioProfileLevelIndication audio_pli
= MP4A_AUDIO_PLI_NONE_REQUIRED
;
2901 mp4sys_visualProfileLevelIndication visual_pli
= MP4SYS_VISUAL_PLI_NONE_REQUIRED
;
2902 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
2904 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2908 if( isom_scan_trak_profileLevelIndication( trak
, &audio_pli
, &visual_pli
) )
2910 if( mp4sys_create_ES_ID_Inc( iods
->OD
, trak
->tkhd
->track_ID
) )
2913 if( mp4sys_to_InitialObjectDescriptor( iods
->OD
,
2914 0, /* FIXME: I'm not quite sure what the spec says. */
2915 MP4SYS_OD_PLI_NONE_REQUIRED
, MP4SYS_SCENE_PLI_NONE_REQUIRED
,
2916 audio_pli
, visual_pli
,
2917 MP4SYS_GRAPHICS_PLI_NONE_REQUIRED
) )
2921 isom_remove_box_by_itself( iods
);
2925 int lsmash_create_object_descriptor( lsmash_root_t
*root
)
2927 if( !root
|| !root
->file
)
2929 lsmash_file_t
*file
= root
->file
;
2930 /* Return error if this file is not compatible with MP4 file format. */
2931 if( !file
->mp4_version1
&& !file
->mp4_version2
)
2933 return isom_setup_iods( file
->moov
);
2936 /*---- finishing functions ----*/
2938 int isom_complement_data_reference( isom_minf_t
*minf
)
2941 || !minf
->dinf
->dref
)
2943 /* Complement data referece if absent. */
2944 if( !minf
->dinf
->dref
->list
.head
)
2946 isom_dref_entry_t
*url
= isom_add_dref_entry( minf
->dinf
->dref
);
2949 url
->flags
= 0x000001; /* Media data is in the same file. */
2954 int isom_rearrange_boxes
2956 lsmash_file_t
*file
,
2957 lsmash_adhoc_remux_t
*remux
,
2968 lsmash_bs_t
*bs
= file
->bs
;
2969 while( read_num
== size
)
2971 if( lsmash_bs_write_seek( bs
, read_pos
, SEEK_SET
) < 0 )
2973 lsmash_bs_read_data( bs
, buf
[buf_switch
], &read_num
);
2974 read_pos
= bs
->offset
;
2976 if( lsmash_bs_write_seek( bs
, write_pos
, SEEK_SET
) < 0 )
2978 if( lsmash_bs_write_data( bs
, buf
[buf_switch
], size
) < 0 )
2980 write_pos
= bs
->offset
;
2981 if( remux
&& remux
->func
)
2982 remux
->func( remux
->param
, write_pos
, file_size
); // FIXME:
2984 if( lsmash_bs_write_data( bs
, buf
[buf_switch
^ 0x1], read_num
) < 0 )
2986 if( remux
&& remux
->func
)
2987 remux
->func( remux
->param
, file_size
, file_size
); // FIXME:
2991 int isom_establish_movie( lsmash_file_t
*file
)
2993 if( isom_check_mandatory_boxes( file
)
2994 || isom_set_movie_creation_time( file
)
2995 || isom_update_box_size( file
->moov
) == 0 )
3000 int lsmash_finish_movie
3002 lsmash_root_t
*root
,
3003 lsmash_adhoc_remux_t
*remux
3008 lsmash_file_t
*file
= root
->file
;
3013 if( file
->fragment
)
3014 return isom_finish_final_fragment_movie( file
, remux
);
3015 isom_moov_t
*moov
= file
->moov
;
3016 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
3018 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
3023 || !trak
->mdia
->minf
3024 || !trak
->mdia
->minf
->stbl
3025 || isom_complement_data_reference( trak
->mdia
->minf
) < 0 )
3027 uint32_t track_ID
= trak
->tkhd
->track_ID
;
3028 uint32_t related_track_ID
= trak
->related_track_ID
;
3029 /* Disable the track if the track is a track reference chapter. */
3030 if( trak
->is_chapter
)
3031 trak
->tkhd
->flags
&= ~ISOM_TRACK_ENABLED
;
3032 if( trak
->is_chapter
&& related_track_ID
)
3034 /* In order that the track duration of the chapter track doesn't exceed that of the related track. */
3036 edit
.duration
= LSMASH_MIN( trak
->tkhd
->duration
, lsmash_get_track_duration( root
, related_track_ID
) );
3037 edit
.start_time
= 0;
3038 edit
.rate
= ISOM_EDIT_MODE_NORMAL
;
3039 if( lsmash_create_explicit_timeline_map( root
, track_ID
, edit
) )
3042 /* Add stss box if any samples aren't sync sample. */
3043 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3044 if( !trak
->cache
->all_sync
&& !stbl
->stss
&& !isom_add_stss( stbl
) )
3046 if( isom_update_tkhd_duration( trak
) < 0 )
3048 if( isom_update_bitrate_description( trak
->mdia
) < 0 )
3051 if( file
->mp4_version1
== 1 && isom_setup_iods( moov
) < 0 )
3053 if( isom_establish_movie( file
) < 0 )
3055 /* Write the size of Media Data Box here. */
3056 lsmash_bs_t
*bs
= file
->bs
;
3057 file
->mdat
->manager
&= ~LSMASH_INCOMPLETE_BOX
;
3058 if( isom_write_box( bs
, (isom_box_t
*)file
->mdat
) < 0 )
3060 /* Write the Movie Box and a Meta Box if no optimization for progressive download. */
3061 uint64_t meta_size
= file
->meta
? file
->meta
->size
: 0;
3064 if( isom_write_box( bs
, (isom_box_t
*)file
->moov
)
3065 || isom_write_box( bs
, (isom_box_t
*)file
->meta
) )
3067 file
->size
+= moov
->size
+ meta_size
;
3070 /* stco->co64 conversion, depending on last chunk's offset */
3071 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; )
3073 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
3074 isom_stco_t
*stco
= trak
->mdia
->minf
->stbl
->stco
;
3075 if( !stco
->list
->tail
)
3077 if( stco
->large_presentation
3078 || (((isom_stco_entry_t
*)stco
->list
->tail
->data
)->chunk_offset
+ moov
->size
+ meta_size
) <= UINT32_MAX
)
3080 entry
= entry
->next
;
3081 continue; /* no need to convert stco into co64 */
3083 /* stco->co64 conversion */
3084 if( isom_convert_stco_to_co64( trak
->mdia
->minf
->stbl
) < 0
3085 || isom_update_box_size( moov
) == 0 )
3087 entry
= moov
->trak_list
.head
; /* whenever any conversion, re-check all traks */
3089 /* now the amount of offset is fixed. */
3090 uint64_t mtf_size
= moov
->size
+ meta_size
; /* sum of size of boxes moved to front */
3091 /* buffer size must be at least mtf_size * 2 */
3092 remux
->buffer_size
= LSMASH_MAX( remux
->buffer_size
, mtf_size
* 2 );
3093 /* Split to 2 buffers. */
3094 uint8_t *buf
[2] = { NULL
, NULL
};
3095 if( (buf
[0] = (uint8_t*)lsmash_malloc( remux
->buffer_size
)) == NULL
)
3096 return -1; /* NOTE: i think we still can fallback to "return isom_write_moov();" here. */
3097 size_t size
= remux
->buffer_size
/ 2;
3098 buf
[1] = buf
[0] + size
;
3099 /* now the amount of offset is fixed. apply that to stco/co64 */
3100 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
3102 isom_stco_t
*stco
= ((isom_trak_t
*)entry
->data
)->mdia
->minf
->stbl
->stco
;
3103 if( stco
->large_presentation
)
3104 for( lsmash_entry_t
*co64_entry
= stco
->list
->head
; co64_entry
; co64_entry
= co64_entry
->next
)
3105 ((isom_co64_entry_t
*)co64_entry
->data
)->chunk_offset
+= mtf_size
;
3107 for( lsmash_entry_t
*stco_entry
= stco
->list
->head
; stco_entry
; stco_entry
= stco_entry
->next
)
3108 ((isom_stco_entry_t
*)stco_entry
->data
)->chunk_offset
+= mtf_size
;
3110 /* Backup starting area of mdat and write moov + meta there instead. */
3111 isom_mdat_t
*mdat
= file
->mdat
;
3112 uint64_t total
= file
->size
+ mtf_size
;
3113 uint64_t placeholder_pos
= file
->free
? file
->free
->pos
: mdat
->pos
;
3114 if( lsmash_bs_write_seek( bs
, placeholder_pos
, SEEK_SET
) < 0 )
3116 size_t read_num
= size
;
3117 lsmash_bs_read_data( bs
, buf
[0], &read_num
);
3118 uint64_t read_pos
= bs
->offset
;
3119 /* Write moov + meta there instead. */
3120 if( lsmash_bs_write_seek( bs
, placeholder_pos
, SEEK_SET
) < 0
3121 || isom_write_box( bs
, (isom_box_t
*)file
->moov
) < 0
3122 || isom_write_box( bs
, (isom_box_t
*)file
->meta
) < 0 )
3124 uint64_t write_pos
= bs
->offset
;
3125 /* Update the positions */
3126 mdat
->pos
+= mtf_size
;
3128 file
->free
->pos
+= mtf_size
;
3129 /* Move Media Data Box. */
3130 if( isom_rearrange_boxes( file
, remux
, buf
, read_num
, size
, read_pos
, write_pos
, total
) < 0 )
3132 file
->size
+= mtf_size
;
3133 lsmash_free( buf
[0] );
3136 lsmash_free( buf
[0] );
3140 int lsmash_set_last_sample_delta( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t sample_delta
)
3142 if( !root
|| track_ID
== 0 )
3144 lsmash_file_t
*file
= root
->file
;
3146 && file
->fragment
->movie
)
3148 isom_traf_t
*traf
= isom_get_traf( file
->fragment
->movie
, track_ID
);
3153 return isom_set_fragment_last_duration( traf
, sample_delta
);
3155 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
3159 || !trak
->mdia
->mdhd
3160 || !trak
->mdia
->minf
3161 || !trak
->mdia
->minf
->stbl
3162 || !trak
->mdia
->minf
->stbl
->stsd
3163 || !trak
->mdia
->minf
->stbl
->stsz
3164 || !trak
->mdia
->minf
->stbl
->stts
3165 || !trak
->mdia
->minf
->stbl
->stts
->list
)
3167 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3168 isom_stts_t
*stts
= stbl
->stts
;
3169 uint32_t sample_count
= isom_get_sample_count( trak
);
3170 if( !stts
->list
->tail
)
3173 return 0; /* no samples */
3174 if( sample_count
> 1 )
3175 return -1; /* irregular sample_count */
3176 /* Set the duration of the first sample.
3177 * This duration is also the duration of the last sample. */
3178 if( isom_add_stts_entry( stbl
, sample_delta
) )
3180 return lsmash_update_track_duration( root
, track_ID
, 0 );
3183 for( lsmash_entry_t
*entry
= stts
->list
->head
; entry
; entry
= entry
->next
)
3184 i
+= ((isom_stts_entry_t
*)entry
->data
)->sample_count
;
3185 if( sample_count
< i
)
3187 int no_last
= (sample_count
> i
);
3188 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)stts
->list
->tail
->data
;
3189 if( !last_stts_data
)
3191 /* Consider QuikcTime fixed compression audio. */
3192 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
,
3193 trak
->cache
->chunk
.sample_description_index
);
3196 if( (audio
->manager
& LSMASH_AUDIO_DESCRIPTION
)
3197 && (audio
->manager
& LSMASH_QTFF_BASE
)
3198 && (audio
->version
== 1)
3199 && (audio
->compression_ID
!= QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION
) )
3201 if( audio
->samplesPerPacket
== 0 )
3203 int exclude_last_sample
= no_last
? 0 : 1;
3204 uint32_t j
= audio
->samplesPerPacket
;
3205 for( lsmash_entry_t
*entry
= stts
->list
->tail
; entry
&& j
> 1; entry
= entry
->prev
)
3207 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)entry
->data
;
3210 for( uint32_t k
= exclude_last_sample
; k
< stts_data
->sample_count
&& j
> 1; k
++ )
3212 sample_delta
-= stts_data
->sample_delta
;
3215 exclude_last_sample
= 0;
3218 /* Set sample_delta. */
3221 /* The duration of the last sample is not set yet. */
3222 if( sample_count
- i
> 1 )
3224 /* Add a sample_delta. */
3225 if( sample_delta
== last_stts_data
->sample_delta
)
3226 ++ last_stts_data
->sample_count
;
3227 else if( isom_add_stts_entry( stbl
, sample_delta
) < 0 )
3230 /* The duration of the last sample is already set. Replace it with a new one. */
3231 else if( isom_replace_last_sample_delta( stbl
, sample_delta
) < 0 )
3233 return lsmash_update_track_duration( root
, track_ID
, sample_delta
);
3236 /*---- timeline manipulator ----*/
3238 int lsmash_modify_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t edit_number
, lsmash_edit_t edit
)
3240 if( !root
|| edit
.duration
== 0 || edit
.start_time
< -1 )
3242 lsmash_file_t
*file
= root
->file
;
3243 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
3246 || !trak
->edts
->elst
3247 || !trak
->edts
->elst
->list
)
3249 isom_elst_t
*elst
= trak
->edts
->elst
;
3250 isom_elst_entry_t
*data
= (isom_elst_entry_t
*)lsmash_get_entry_data( elst
->list
, edit_number
);
3253 data
->segment_duration
= edit
.duration
;
3254 data
->media_time
= edit
.start_time
;
3255 data
->media_rate
= edit
.rate
;
3256 if( elst
->pos
== 0 || !file
->fragment
|| file
->bs
->unseekable
)
3257 return isom_update_tkhd_duration( trak
);
3258 /* Rewrite the specified entry.
3259 * Note: we don't update the version of the Edit List Box. */
3260 lsmash_bs_t
*bs
= file
->bs
;
3261 uint64_t current_pos
= bs
->offset
;
3262 uint64_t entry_pos
= elst
->pos
+ ISOM_LIST_FULLBOX_COMMON_SIZE
+ ((uint64_t)edit_number
- 1) * (elst
->version
== 1 ? 20 : 12);
3263 lsmash_bs_write_seek( bs
, entry_pos
, SEEK_SET
);
3266 lsmash_bs_put_be64( bs
, data
->segment_duration
);
3267 lsmash_bs_put_be64( bs
, data
->media_time
);
3271 lsmash_bs_put_be32( bs
, (uint32_t)LSMASH_MIN( data
->segment_duration
, UINT32_MAX
) );
3272 lsmash_bs_put_be32( bs
, (uint32_t)data
->media_time
);
3274 lsmash_bs_put_be32( bs
, data
->media_rate
);
3275 int ret
= lsmash_bs_flush_buffer( bs
);
3276 lsmash_bs_write_seek( bs
, current_pos
, SEEK_SET
);
3280 int lsmash_create_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_edit_t edit
)
3282 if( !root
|| edit
.start_time
< -1 )
3284 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
3288 edit
.duration
= (edit
.duration
|| root
->file
->fragment
) ? edit
.duration
3289 : trak
->tkhd
->duration
? trak
->tkhd
->duration
3290 : isom_update_tkhd_duration( trak
) < 0 ? 0
3291 : trak
->tkhd
->duration
;
3292 if( (!trak
->edts
&& !isom_add_edts( trak
))
3293 || (!trak
->edts
->elst
&& !isom_add_elst( trak
->edts
))
3294 || isom_add_elst_entry( trak
->edts
->elst
, edit
.duration
, edit
.start_time
, edit
.rate
) )
3296 return isom_update_tkhd_duration( trak
);
3299 int lsmash_get_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t edit_number
, lsmash_edit_t
*edit
)
3301 if( !root
|| !edit
)
3303 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
3307 || !trak
->edts
->elst
)
3311 edit
->start_time
= 0;
3315 isom_elst_entry_t
*elst
= (isom_elst_entry_t
*)lsmash_get_entry_data( trak
->edts
->elst
->list
, edit_number
);
3318 edit
->duration
= elst
->segment_duration
;
3319 edit
->start_time
= elst
->media_time
;
3320 edit
->rate
= elst
->media_rate
;
3324 uint32_t lsmash_count_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
)
3328 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
3331 || !trak
->edts
->elst
3332 || !trak
->edts
->elst
->list
)
3334 return trak
->edts
->elst
->list
->entry_count
;
3337 /*---- create / modification time fields manipulators ----*/
3339 int lsmash_update_media_modification_time( lsmash_root_t
*root
, uint32_t track_ID
)
3343 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
3346 || !trak
->mdia
->mdhd
)
3348 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
3349 mdhd
->modification_time
= isom_get_current_mp4time();
3350 /* overwrite strange creation_time */
3351 if( mdhd
->creation_time
> mdhd
->modification_time
)
3352 mdhd
->creation_time
= mdhd
->modification_time
;
3356 int lsmash_update_track_modification_time( lsmash_root_t
*root
, uint32_t track_ID
)
3360 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
3364 isom_tkhd_t
*tkhd
= trak
->tkhd
;
3365 tkhd
->modification_time
= isom_get_current_mp4time();
3366 /* overwrite strange creation_time */
3367 if( tkhd
->creation_time
> tkhd
->modification_time
)
3368 tkhd
->creation_time
= tkhd
->modification_time
;
3372 int lsmash_update_movie_modification_time( lsmash_root_t
*root
)
3376 lsmash_file_t
*file
= root
->file
;
3379 || !file
->moov
->mvhd
)
3381 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
3382 mvhd
->modification_time
= isom_get_current_mp4time();
3383 /* overwrite strange creation_time */
3384 if( mvhd
->creation_time
> mvhd
->modification_time
)
3385 mvhd
->creation_time
= mvhd
->modification_time
;
3389 /*---- sample manipulators ----*/
3390 lsmash_sample_t
*lsmash_create_sample( uint32_t size
)
3392 lsmash_sample_t
*sample
= lsmash_malloc_zero( sizeof(lsmash_sample_t
) );
3397 sample
->data
= lsmash_malloc( size
);
3400 lsmash_free( sample
);
3403 sample
->length
= size
;
3407 int lsmash_sample_alloc( lsmash_sample_t
*sample
, uint32_t size
)
3414 lsmash_free( sample
->data
);
3415 sample
->data
= NULL
;
3419 if( size
== sample
->length
)
3423 data
= lsmash_malloc( size
);
3425 data
= lsmash_realloc( sample
->data
, size
);
3428 sample
->data
= data
;
3429 sample
->length
= size
;
3433 void lsmash_delete_sample( lsmash_sample_t
*sample
)
3438 lsmash_free( sample
->data
);
3439 lsmash_free( sample
);
3442 isom_sample_pool_t
*isom_create_sample_pool( uint64_t size
)
3444 isom_sample_pool_t
*pool
= lsmash_malloc_zero( sizeof(isom_sample_pool_t
) );
3449 pool
->data
= lsmash_malloc( size
);
3452 lsmash_free( pool
);
3459 void isom_remove_sample_pool( isom_sample_pool_t
*pool
)
3464 lsmash_free( pool
->data
);
3465 lsmash_free( pool
);
3468 static uint32_t isom_add_size( isom_trak_t
*trak
, uint32_t sample_size
)
3470 if( isom_add_stsz_entry( trak
->mdia
->minf
->stbl
, sample_size
) )
3472 return isom_get_sample_count( trak
);
3475 static uint32_t isom_add_dts( isom_stbl_t
*stbl
, isom_timestamp_t
*cache
, uint64_t dts
)
3477 isom_stts_t
*stts
= stbl
->stts
;
3478 if( stts
->list
->entry_count
== 0 )
3480 if( isom_add_stts_entry( stbl
, dts
) )
3485 if( dts
<= cache
->dts
)
3487 uint32_t sample_delta
= dts
- cache
->dts
;
3488 isom_stts_entry_t
*data
= (isom_stts_entry_t
*)stts
->list
->tail
->data
;
3489 if( data
->sample_delta
== sample_delta
)
3490 ++ data
->sample_count
;
3491 else if( isom_add_stts_entry( stbl
, sample_delta
) )
3494 return sample_delta
;
3497 static int isom_add_cts( isom_stbl_t
*stbl
, isom_timestamp_t
*cache
, uint64_t cts
)
3499 isom_ctts_t
*ctts
= stbl
->ctts
;
3502 if( cts
== cache
->dts
)
3507 /* Add ctts box and the first ctts entry. */
3508 if( !isom_add_ctts( stbl
) || isom_add_ctts_entry( stbl
, 0 ) < 0 )
3511 isom_ctts_entry_t
*data
= (isom_ctts_entry_t
*)ctts
->list
->head
->data
;
3512 uint32_t sample_count
= stbl
->stsz
->sample_count
;
3513 if( sample_count
!= 1 )
3515 data
->sample_count
= sample_count
- 1;
3516 if( isom_add_ctts_entry( stbl
, cts
- cache
->dts
) )
3520 data
->sample_offset
= cts
;
3526 isom_ctts_entry_t
*data
= (isom_ctts_entry_t
*)ctts
->list
->tail
->data
;
3527 uint32_t sample_offset
= cts
- cache
->dts
;
3528 if( data
->sample_offset
== sample_offset
)
3529 ++ data
->sample_count
;
3530 else if( isom_add_ctts_entry( stbl
, sample_offset
) )
3536 static int isom_add_timestamp( isom_trak_t
*trak
, uint64_t dts
, uint64_t cts
)
3539 || !trak
->mdia
->minf
->stbl
->stts
3540 || !trak
->mdia
->minf
->stbl
->stts
->list
)
3542 lsmash_file_t
*file
= trak
->file
;
3543 if( file
->isom_compatible
&& file
->qt_compatible
&& (cts
- dts
) > INT32_MAX
)
3544 return -1; /* sample_offset is not compatible. */
3545 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3546 isom_timestamp_t
*ts_cache
= &trak
->cache
->timestamp
;
3547 uint32_t sample_count
= isom_get_sample_count( trak
);
3548 uint32_t sample_delta
= sample_count
> 1 ? isom_add_dts( stbl
, ts_cache
, dts
) : 0;
3549 if( sample_count
> 1 && !sample_delta
)
3551 if( isom_add_cts( stbl
, ts_cache
, cts
) )
3553 if( (cts
+ ts_cache
->ctd_shift
) < dts
)
3555 if( (file
->max_isom_version
< 4 && !file
->qt_compatible
) /* Negative sample offset is not supported. */
3556 || (file
->max_isom_version
>= 4 && file
->qt_compatible
) /* ctts version 1 is not defined in QTFF. */
3557 || ((dts
- cts
) > INT32_MAX
) ) /* Overflow */
3559 ts_cache
->ctd_shift
= dts
- cts
;
3560 if( stbl
->ctts
->version
== 0 && !file
->qt_compatible
)
3561 stbl
->ctts
->version
= 1;
3563 if( trak
->cache
->fragment
)
3565 isom_fragment_t
*fragment_cache
= trak
->cache
->fragment
;
3566 fragment_cache
->last_duration
= sample_delta
;
3567 fragment_cache
->largest_cts
= LSMASH_MAX( ts_cache
->cts
, fragment_cache
->largest_cts
);
3572 static int isom_add_sync_point( isom_trak_t
*trak
, uint32_t sample_number
, lsmash_sample_property_t
*prop
)
3574 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3575 isom_cache_t
*cache
= trak
->cache
;
3576 if( !(prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
) ) /* no null check for prop */
3578 if( !cache
->all_sync
)
3580 if( !stbl
->stss
&& !isom_add_stss( stbl
) )
3582 if( isom_add_stss_entry( stbl
, 1 ) ) /* Declare here the first sample is a sync sample. */
3584 cache
->all_sync
= 0;
3587 if( cache
->all_sync
) /* We don't need stss box if all samples are sync sample. */
3591 if( isom_get_sample_count( trak
) == 1 )
3593 cache
->all_sync
= 1; /* Also the first sample is a sync sample. */
3596 if( !isom_add_stss( stbl
) )
3599 return isom_add_stss_entry( stbl
, sample_number
);
3602 static int isom_add_partial_sync( isom_trak_t
*trak
, uint32_t sample_number
, lsmash_sample_property_t
*prop
)
3604 if( !trak
->file
->qt_compatible
)
3606 if( !(prop
->ra_flags
& QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
) )
3608 /* This sample is a partial sync sample. */
3609 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3610 if( !stbl
->stps
&& !isom_add_stps( stbl
) )
3612 return isom_add_stps_entry( stbl
, sample_number
);
3615 static int isom_add_dependency_type( isom_trak_t
*trak
, lsmash_sample_property_t
*prop
)
3617 if( !trak
->file
->qt_compatible
&& !trak
->file
->avc_extensions
)
3619 int compatibility
= trak
->file
->avc_extensions
&& trak
->file
->qt_compatible
? 3
3620 : trak
->file
->qt_compatible
? 2
3621 : trak
->file
->avc_extensions
? 1
3623 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3625 return isom_add_sdtp_entry( (isom_box_t
*)stbl
, prop
, compatibility
);
3626 if( !prop
->allow_earlier
&& !prop
->leading
&& !prop
->independent
&& !prop
->disposable
&& !prop
->redundant
) /* no null check for prop */
3628 if( !isom_add_sdtp( (isom_box_t
*)stbl
) )
3630 uint32_t count
= isom_get_sample_count( trak
);
3631 /* fill past samples with ISOM_SAMPLE_*_UNKNOWN */
3632 lsmash_sample_property_t null_prop
= { 0 };
3633 for( uint32_t i
= 1; i
< count
; i
++ )
3634 if( isom_add_sdtp_entry( (isom_box_t
*)stbl
, &null_prop
, compatibility
) )
3636 return isom_add_sdtp_entry( (isom_box_t
*)stbl
, prop
, compatibility
);
3639 int isom_rap_grouping_established( isom_rap_group_t
*group
, int num_leading_samples_known
, isom_sgpd_t
*sgpd
, int is_fragment
)
3641 isom_rap_entry_t
*rap
= group
->random_access
;
3644 assert( rap
== (isom_rap_entry_t
*)sgpd
->list
->tail
->data
);
3645 rap
->num_leading_samples_known
= num_leading_samples_known
;
3646 /* Avoid duplication of sample group descriptions. */
3647 uint32_t group_description_index
= is_fragment
? 0x10001 : 1;
3648 for( lsmash_entry_t
*entry
= sgpd
->list
->head
; entry
!= sgpd
->list
->tail
; entry
= entry
->next
)
3650 isom_rap_entry_t
*data
= (isom_rap_entry_t
*)entry
->data
;
3653 if( rap
->num_leading_samples_known
== data
->num_leading_samples_known
3654 && rap
->num_leading_samples
== data
->num_leading_samples
)
3656 /* The same description already exists.
3657 * Remove the latest random access entry. */
3658 lsmash_remove_entry_tail( sgpd
->list
, NULL
);
3659 /* Replace assigned group_description_index with the one corresponding the same description. */
3660 if( group
->assignment
->group_description_index
== 0 )
3662 /* We don't create consecutive sample groups not assigned to 'rap '.
3663 * So the previous sample group shall be a group of 'rap ' if any. */
3664 if( group
->prev_assignment
)
3666 assert( group
->prev_assignment
->group_description_index
);
3667 group
->prev_assignment
->group_description_index
= group_description_index
;
3671 group
->assignment
->group_description_index
= group_description_index
;
3674 ++group_description_index
;
3676 group
->random_access
= NULL
;
3680 int isom_group_random_access( isom_box_t
*parent
, lsmash_sample_t
*sample
)
3682 if( parent
->file
->max_isom_version
< 6 )
3686 isom_cache_t
*cache
;
3687 uint32_t sample_count
;
3689 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAK
) )
3691 isom_trak_t
*trak
= (isom_trak_t
*)parent
;
3692 sbgp
= isom_get_sample_to_group ( trak
->mdia
->minf
->stbl
, ISOM_GROUP_TYPE_RAP
);
3693 sgpd
= isom_get_sample_group_description( trak
->mdia
->minf
->stbl
, ISOM_GROUP_TYPE_RAP
);
3694 cache
= trak
->cache
;
3695 sample_count
= isom_get_sample_count( trak
);
3698 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
3700 isom_traf_t
*traf
= (isom_traf_t
*)parent
;
3701 sbgp
= isom_get_fragment_sample_to_group ( traf
, ISOM_GROUP_TYPE_RAP
);
3702 sgpd
= isom_get_fragment_sample_group_description( traf
, ISOM_GROUP_TYPE_RAP
);
3703 cache
= traf
->cache
;
3704 sample_count
= cache
->fragment
->sample_count
+ 1;
3712 /* redundant initializations to suppress warnings from unclever compilers */
3717 if( !sbgp
|| !sgpd
)
3719 lsmash_sample_property_t
*prop
= &sample
->prop
;
3720 uint8_t is_rap
= (prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
)
3721 || (prop
->ra_flags
& QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
)
3722 || (prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
)
3723 || (LSMASH_IS_POST_ROLL_START( prop
->ra_flags
) && prop
->post_roll
.identifier
== prop
->post_roll
.complete
);
3724 isom_rap_group_t
*group
= cache
->rap
;
3727 /* This sample is the first sample, create a grouping cache. */
3728 assert( sample_count
== 1 );
3729 group
= lsmash_malloc( sizeof(isom_rap_group_t
) );
3734 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3735 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3739 /* The first sample is not always a random access point. */
3740 group
->random_access
= NULL
;
3741 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3743 if( !group
->assignment
)
3745 lsmash_free( group
);
3748 group
->prev_assignment
= NULL
;
3749 group
->is_prev_rap
= is_rap
;
3753 if( group
->is_prev_rap
)
3755 /* OK. here, the previous sample is a menber of 'rap '. */
3758 /* This sample isn't a member of 'rap ' and the previous sample is.
3759 * So we create a new group and set 0 on its group_description_index. */
3760 group
->prev_assignment
= group
->assignment
;
3761 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3762 if( !group
->assignment
)
3764 lsmash_free( group
);
3768 else if( !LSMASH_IS_CLOSED_RAP( prop
->ra_flags
) )
3770 /* Create a new group since there is the possibility the next sample is a leading sample.
3771 * This sample is a member of 'rap ', so we set appropriate value on its group_description_index. */
3772 if( isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
) < 0 )
3774 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3775 group
->prev_assignment
= group
->assignment
;
3776 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3777 if( !group
->assignment
)
3779 lsmash_free( group
);
3783 else /* The previous and current sample are a member of 'rap ', and the next sample must not be a leading sample. */
3784 ++ group
->assignment
->sample_count
;
3788 /* This sample is a member of 'rap ' and the previous sample isn't.
3789 * So we create a new group and set appropriate value on its group_description_index. */
3790 if( isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
) < 0 )
3792 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3793 group
->prev_assignment
= group
->assignment
;
3794 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3795 if( !group
->assignment
)
3797 lsmash_free( group
);
3801 else /* The previous and current sample aren't a member of 'rap '. */
3802 ++ group
->assignment
->sample_count
;
3803 /* Obtain the property of the latest random access point group. */
3804 if( !is_rap
&& group
->random_access
)
3806 if( prop
->leading
== ISOM_SAMPLE_LEADING_UNKNOWN
)
3808 /* We can no longer know num_leading_samples in this group. */
3809 if( isom_rap_grouping_established( group
, 0, sgpd
, is_fragment
) < 0 )
3814 if( prop
->leading
== ISOM_SAMPLE_IS_UNDECODABLE_LEADING
3815 || prop
->leading
== ISOM_SAMPLE_IS_DECODABLE_LEADING
)
3816 ++ group
->random_access
->num_leading_samples
;
3817 /* no more consecutive leading samples in this group */
3818 else if( isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
) < 0 )
3822 group
->is_prev_rap
= is_rap
;
3826 static int isom_roll_grouping_established( isom_roll_group_t
*group
)
3828 /* Avoid duplication of sample group descriptions. */
3829 isom_sgpd_t
*sgpd
= group
->sgpd
;
3830 uint32_t group_description_index
= group
->is_fragment
? 0x10001 : 1;
3831 for( lsmash_entry_t
*entry
= sgpd
->list
->head
; entry
; entry
= entry
->next
)
3833 isom_roll_entry_t
*data
= (isom_roll_entry_t
*)entry
->data
;
3836 if( group
->roll_distance
== data
->roll_distance
)
3838 /* The same description already exists.
3839 * Set the group_description_index corresponding the same description. */
3840 group
->assignment
->group_description_index
= group_description_index
;
3843 ++group_description_index
;
3845 /* Add a new roll recovery description. */
3846 if( !isom_add_roll_group_entry( sgpd
, group
->roll_distance
) )
3848 group
->assignment
->group_description_index
= sgpd
->list
->entry_count
+ (group
->is_fragment
? 0x10000 : 0);
3852 static int isom_deduplicate_roll_group( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3855 uint32_t current_group_number
= sbgp
->list
->entry_count
- pool
->entry_count
+ 1;
3856 isom_group_assignment_entry_t
*prev_assignment
= (isom_group_assignment_entry_t
*)lsmash_get_entry_data( sbgp
->list
, current_group_number
- 1 );
3857 for( lsmash_entry_t
*entry
= pool
->head
; entry
; )
3859 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3861 || !group
->assignment
)
3863 if( !group
->delimited
|| group
->described
!= ROLL_DISTANCE_DETERMINED
)
3865 if( prev_assignment
&& prev_assignment
->group_description_index
== group
->assignment
->group_description_index
)
3867 /* Merge the current group with the previous. */
3868 lsmash_entry_t
*next_entry
= entry
->next
;
3869 prev_assignment
->sample_count
+= group
->assignment
->sample_count
;
3870 if( lsmash_remove_entry( sbgp
->list
, current_group_number
, NULL
)
3871 || lsmash_remove_entry_direct( pool
, entry
, NULL
) )
3877 entry
= entry
->next
;
3878 prev_assignment
= group
->assignment
;
3879 ++current_group_number
;
3885 /* Remove pooled caches that has become unnecessary. */
3886 static int isom_clean_roll_pool( lsmash_entry_list_t
*pool
)
3888 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= pool
->head
)
3890 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3893 if( !group
->delimited
|| group
->described
!= ROLL_DISTANCE_DETERMINED
)
3895 if( lsmash_remove_entry_direct( pool
, entry
, NULL
) )
3901 static int isom_flush_roll_pool( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3903 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3905 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3908 if( group
->delimited
3909 && group
->described
== ROLL_DISTANCE_DETERMINED
3910 && group
->roll_distance
!= 0
3911 && isom_roll_grouping_established( group
) < 0 )
3914 if( isom_deduplicate_roll_group( sbgp
, pool
) < 0 )
3916 return isom_clean_roll_pool( pool
);
3919 static int isom_all_recovery_described( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3921 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3923 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3926 group
->described
= ROLL_DISTANCE_DETERMINED
;
3928 return isom_flush_roll_pool( sbgp
, pool
);
3931 int isom_all_recovery_completed( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3933 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3935 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3938 group
->described
= ROLL_DISTANCE_DETERMINED
;
3939 group
->delimited
= 1;
3941 return isom_flush_roll_pool( sbgp
, pool
);
3944 static isom_roll_entry_t
*isom_get_roll_description
3946 isom_roll_group_t
*group
3949 uint32_t group_description_index
= group
->assignment
->group_description_index
;
3950 if( group_description_index
&& group
->is_fragment
)
3952 assert( group_description_index
> 0x10000 );
3953 group_description_index
-= 0x10000;
3955 return (isom_roll_entry_t
*)lsmash_get_entry_data( group
->sgpd
->list
, group_description_index
);
3958 int isom_group_roll_recovery( isom_box_t
*parent
, lsmash_sample_t
*sample
)
3960 if( !parent
->file
->avc_extensions
3961 && !parent
->file
->qt_compatible
)
3963 uint32_t sample_count
;
3965 isom_cache_t
*cache
;
3966 lsmash_entry_list_t
*sbgp_list
;
3967 lsmash_entry_list_t
*sgpd_list
;
3968 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAK
) )
3970 isom_trak_t
*trak
= (isom_trak_t
*)parent
;
3971 cache
= trak
->cache
;
3972 sbgp_list
= &trak
->mdia
->minf
->stbl
->sbgp_list
;
3973 sgpd_list
= &trak
->mdia
->minf
->stbl
->sgpd_list
;
3974 sample_count
= isom_get_sample_count( trak
);
3977 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
3979 if( parent
->file
->max_isom_version
< 6 )
3981 isom_traf_t
*traf
= (isom_traf_t
*)parent
;
3982 cache
= traf
->cache
;
3983 sbgp_list
= &traf
->sbgp_list
;
3984 sgpd_list
= &traf
->sgpd_list
;
3985 sample_count
= cache
->fragment
->sample_count
+ 1;
3993 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group ( sbgp_list
);
3994 isom_sgpd_t
*sgpd
= isom_get_roll_recovery_sample_group_description( sgpd_list
);
3995 if( !sbgp
|| !sgpd
|| sbgp
->grouping_type
!= sgpd
->grouping_type
)
3997 /* Check if 'roll' -> 'prol' conversion is needed. */
3999 && sbgp
->grouping_type
== ISOM_GROUP_TYPE_ROLL
4000 && !(sample
->prop
.ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
) )
4002 /* Since not every samples is a sync sample, change grouping_type into 'prol'. */
4003 sbgp
->grouping_type
= ISOM_GROUP_TYPE_PROL
;
4004 sgpd
->grouping_type
= ISOM_GROUP_TYPE_PROL
;
4006 lsmash_entry_list_t
*pool
= cache
->roll
.pool
;
4009 pool
= lsmash_create_entry_list();
4012 cache
->roll
.pool
= pool
;
4014 lsmash_sample_property_t
*prop
= &sample
->prop
;
4015 isom_roll_group_t
*group
= (isom_roll_group_t
*)lsmash_get_entry_data( pool
, pool
->entry_count
);
4016 int is_recovery_start
= LSMASH_IS_POST_ROLL_START( prop
->ra_flags
);
4017 int valid_pre_roll
= !is_recovery_start
4018 && (prop
->ra_flags
!= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
)
4019 && (prop
->pre_roll
.distance
> 0)
4020 && (prop
->pre_roll
.distance
<= -INT16_MIN
);
4021 int new_group
= !group
|| is_recovery_start
|| (group
->prev_is_recovery_start
!= is_recovery_start
);
4024 /* Check pre-roll distance. */
4025 assert( group
->assignment
&& group
->sgpd
);
4026 isom_roll_entry_t
*prev_roll
= isom_get_roll_description( group
);
4028 new_group
= valid_pre_roll
;
4029 else if( !valid_pre_roll
|| (prop
->pre_roll
.distance
!= -prev_roll
->roll_distance
) )
4030 /* Pre-roll distance is different from the previous. */
4036 group
->delimited
= 1;
4038 assert( sample_count
== 1 );
4039 /* Create a new group. */
4040 group
= lsmash_malloc_zero( sizeof(isom_roll_group_t
) );
4044 group
->prev_is_recovery_start
= is_recovery_start
;
4045 group
->is_fragment
= is_fragment
;
4046 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
4047 if( !group
->assignment
|| lsmash_add_entry( pool
, group
) )
4049 lsmash_free( group
);
4052 if( is_recovery_start
)
4054 /* a member of non-roll or post-roll group */
4055 group
->first_sample
= sample_count
;
4056 group
->recovery_point
= prop
->post_roll
.complete
;
4060 group
->described
= ROLL_DISTANCE_DETERMINED
;
4061 if( valid_pre_roll
)
4063 /* a member of pre-roll group */
4064 group
->roll_distance
= -prop
->pre_roll
.distance
;
4065 if( isom_roll_grouping_established( group
) < 0 )
4069 /* a member of non-roll group */
4070 group
->roll_distance
= 0;
4075 group
->prev_is_recovery_start
= is_recovery_start
;
4076 group
->assignment
->sample_count
+= 1;
4078 /* If encountered a RAP, all recovery is completed here. */
4079 if( prop
->ra_flags
& (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
4080 | ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
4081 | QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
) )
4082 return isom_all_recovery_described( sbgp
, pool
);
4083 /* Check whether this sample is a random access recovery point or not. */
4084 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
4086 group
= (isom_roll_group_t
*)entry
->data
;
4089 if( group
->described
== ROLL_DISTANCE_DETERMINED
)
4091 if( group
->described
== ROLL_DISTANCE_INITIALIZED
)
4093 /* Let's consider the following picture sequence.
4094 * coded order : P[0] P[1] P[2] P[3] P[4] P[5]
4097 * Here, P[0] conveys a recovery point SEI and P[3] is the recovery point.
4098 * Correctness of decoded pictures is specified by recovery point in output order for both AVC and HEVC.
4099 * Therefore, as follows,
4100 * output order : P[0] P[2] P[1] P[5]|P[3] P[4]
4101 * ---(incorrect?)--->|
4102 * there is no guarantee that P[5] is decoded and output correctly.
4103 * From this, it can be said that the roll_distance of this sequence is equal to 5. */
4104 isom_roll_entry_t
*post_roll
= isom_get_roll_description( group
);
4105 if( post_roll
&& post_roll
->roll_distance
> 0 )
4107 if( group
->rp_cts
> sample
->cts
)
4108 /* Updated roll_distance for composition reordering. */
4109 post_roll
->roll_distance
= sample_count
- group
->first_sample
;
4110 if( ++ group
->wait_and_see_count
>= MAX_ROLL_WAIT_AND_SEE_COUNT
)
4111 group
->described
= ROLL_DISTANCE_DETERMINED
;
4114 else if( prop
->post_roll
.identifier
== group
->recovery_point
)
4116 int16_t distance
= sample_count
- group
->first_sample
;
4117 group
->rp_cts
= sample
->cts
;
4118 group
->roll_distance
= distance
;
4119 /* Add a roll recovery entry only when roll_distance isn't zero since roll_distance = 0 must not be used. */
4122 /* Now, this group is a 'roll'.
4123 * The roll_distance may be updated later because of composition reordering. */
4124 group
->described
= ROLL_DISTANCE_INITIALIZED
;
4125 group
->wait_and_see_count
= 0;
4126 /* All groups with uninitialized roll_distance before the current group are described. */
4127 lsmash_entry_t
*current
= entry
;
4128 for( entry
= pool
->head
; entry
!= current
; entry
= entry
->next
)
4130 group
= (isom_roll_group_t
*)entry
->data
;
4131 if( !group
|| group
->described
!= ROLL_DISTANCE_INITIALIZED
)
4133 group
->described
= ROLL_DISTANCE_DETERMINED
;
4135 /* Cache the CTS of the first recovery point in a subsegment. */
4137 && cache
->fragment
->subsegment
.first_rp_number
== 0 )
4139 cache
->fragment
->subsegment
.first_rp_number
= sample_count
;
4140 cache
->fragment
->subsegment
.first_rp_cts
= sample
->cts
;
4141 cache
->fragment
->subsegment
.first_ed_cts
= sample
->cts
;
4142 cache
->fragment
->subsegment
.decodable
= 1;
4146 /* Random Accessible Point */
4147 return isom_all_recovery_described( sbgp
, pool
);
4150 return isom_flush_roll_pool( sbgp
, pool
);
4153 /* returns 1 if pooled samples must be flushed. */
4154 /* FIXME: I wonder if this function should have a extra argument which indicates force_to_flush_cached_chunk.
4155 see lsmash_append_sample for detail. */
4156 static int isom_add_chunk( isom_trak_t
*trak
, lsmash_sample_t
*sample
)
4160 || !trak
->mdia
->mdhd
4161 || trak
->mdia
->mdhd
->timescale
== 0
4162 || !trak
->mdia
->minf
->stbl
->stsc
4163 || !trak
->mdia
->minf
->stbl
->stsc
->list
)
4165 lsmash_file_t
*file
= trak
->file
;
4166 isom_chunk_t
*current
= &trak
->cache
->chunk
;
4167 if( !current
->pool
)
4169 /* Very initial settings, just once per track */
4170 current
->pool
= isom_create_sample_pool( 0 );
4171 if( !current
->pool
)
4174 if( !current
->pool
->sample_count
)
4176 /* Cannot decide whether we should flush the current sample or not here yet. */
4177 current
->chunk_number
+= 1;
4178 current
->sample_description_index
= sample
->index
;
4179 current
->first_dts
= sample
->dts
;
4182 if( sample
->dts
< current
->first_dts
)
4183 return -1; /* easy error check. */
4184 if( (file
->max_chunk_duration
>= ((double)(sample
->dts
- current
->first_dts
) / trak
->mdia
->mdhd
->timescale
))
4185 && (file
->max_chunk_size
>= current
->pool
->size
+ sample
->length
)
4186 && (current
->sample_description_index
== sample
->index
) )
4187 return 0; /* No need to flush current cached chunk, the current sample must be put into that. */
4188 /* NOTE: chunk relative stuff must be pushed into file after a chunk is fully determined with its contents. */
4189 /* Now the current cached chunk is fixed, actually add the chunk relative properties to its file accordingly. */
4190 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
4191 isom_stsc_entry_t
*last_stsc_data
= stbl
->stsc
->list
->tail
? (isom_stsc_entry_t
*)stbl
->stsc
->list
->tail
->data
: NULL
;
4192 /* Create a new chunk sequence in this track if needed. */
4193 if( (!last_stsc_data
4194 || current
->pool
->sample_count
!= last_stsc_data
->samples_per_chunk
4195 || current
->sample_description_index
!= last_stsc_data
->sample_description_index
)
4196 && isom_add_stsc_entry( stbl
, current
->chunk_number
,
4197 current
->pool
->sample_count
,
4198 current
->sample_description_index
) )
4200 /* Add a new chunk offset in this track. */
4201 uint64_t offset
= file
->size
;
4202 if( file
->fragment
)
4203 offset
+= ISOM_BASEBOX_COMMON_SIZE
+ file
->fragment
->pool_size
;
4204 if( isom_add_stco_entry( stbl
, offset
) )
4206 /* Update and re-initialize cache, using the current sample */
4207 current
->chunk_number
+= 1;
4208 current
->sample_description_index
= sample
->index
;
4209 current
->first_dts
= sample
->dts
;
4210 /* current->pool must be flushed in isom_append_sample_internal() */
4214 static int isom_write_pooled_samples( lsmash_file_t
*file
, isom_sample_pool_t
*pool
)
4219 || !file
->bs
->stream
)
4221 lsmash_bs_put_bytes( file
->bs
, pool
->size
, pool
->data
);
4222 if( lsmash_bs_flush_buffer( file
->bs
) )
4224 file
->mdat
->media_size
+= pool
->size
;
4225 file
->size
+= pool
->size
;
4226 pool
->sample_count
= 0;
4231 int isom_update_sample_tables( isom_trak_t
*trak
, lsmash_sample_t
*sample
, uint32_t *samples_per_packet
)
4233 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
, sample
->index
);
4234 if( (audio
->manager
& LSMASH_AUDIO_DESCRIPTION
)
4235 && (audio
->manager
& LSMASH_QTFF_BASE
)
4236 && (audio
->version
== 1)
4237 && (audio
->compression_ID
!= QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION
) )
4239 /* Add entries of the sample table for each uncompressed sample. */
4240 uint64_t sample_duration
= trak
->mdia
->mdhd
->timescale
/ (audio
->samplerate
>> 16);
4241 if( audio
->samplesPerPacket
== 0 || sample_duration
== 0 )
4243 uint64_t sample_dts
= sample
->dts
;
4244 uint64_t sample_cts
= sample
->cts
;
4245 for( uint32_t i
= 0; i
< audio
->samplesPerPacket
; i
++ )
4247 /* Add a size of uncomressed audio and increment sample_count.
4248 * This points to individual uncompressed audio samples, each one byte in size, within the compressed frames. */
4249 uint32_t sample_count
= isom_add_size( trak
, 1 );
4250 if( sample_count
== 0 )
4252 /* Add a decoding timestamp and a composition timestamp. */
4253 if( isom_add_timestamp( trak
, sample_dts
, sample_cts
) < 0 )
4255 sample_dts
+= sample_duration
;
4256 sample_cts
+= sample_duration
;
4258 *samples_per_packet
= audio
->samplesPerPacket
;
4262 /* Add a sample_size and increment sample_count. */
4263 uint32_t sample_count
= isom_add_size( trak
, sample
->length
);
4264 if( sample_count
== 0 )
4266 /* Add a decoding timestamp and a composition timestamp. */
4267 if( isom_add_timestamp( trak
, sample
->dts
, sample
->cts
) < 0 )
4269 /* Add a sync point if needed. */
4270 if( isom_add_sync_point( trak
, sample_count
, &sample
->prop
) < 0 )
4272 /* Add a partial sync point if needed. */
4273 if( isom_add_partial_sync( trak
, sample_count
, &sample
->prop
) < 0 )
4275 /* Add leading, independent, disposable and redundant information if needed. */
4276 if( isom_add_dependency_type( trak
, &sample
->prop
) < 0 )
4278 /* Group samples into random access point type if needed. */
4279 if( isom_group_random_access( (isom_box_t
*)trak
, sample
) < 0 )
4281 /* Group samples into random access recovery point type if needed. */
4282 if( isom_group_roll_recovery( (isom_box_t
*)trak
, sample
) < 0 )
4284 *samples_per_packet
= 1;
4286 /* Add a chunk if needed. */
4287 return isom_add_chunk( trak
, sample
);
4290 static int isom_output_cached_chunk( isom_trak_t
*trak
)
4292 isom_chunk_t
*chunk
= &trak
->cache
->chunk
;
4293 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
4294 isom_stsc_entry_t
*last_stsc_data
= stbl
->stsc
->list
->tail
? (isom_stsc_entry_t
*)stbl
->stsc
->list
->tail
->data
: NULL
;
4295 /* Create a new chunk sequence in this track if needed. */
4296 if( (!last_stsc_data
4297 || chunk
->pool
->sample_count
!= last_stsc_data
->samples_per_chunk
4298 || chunk
->sample_description_index
!= last_stsc_data
->sample_description_index
)
4299 && isom_add_stsc_entry( stbl
, chunk
->chunk_number
,
4300 chunk
->pool
->sample_count
,
4301 chunk
->sample_description_index
) )
4303 lsmash_file_t
*file
= trak
->file
;
4304 if( file
->fragment
)
4306 /* Add a new chunk offset in this track. */
4307 if( isom_add_stco_entry( stbl
, file
->size
+ ISOM_BASEBOX_COMMON_SIZE
+ file
->fragment
->pool_size
) )
4309 return isom_append_fragment_track_run( file
, chunk
);
4311 /* Add a new chunk offset in this track. */
4312 if( isom_add_stco_entry( stbl
, file
->size
) )
4314 /* Output pooled samples in this track. */
4315 return isom_write_pooled_samples( file
, chunk
->pool
);
4318 int isom_pool_sample( isom_sample_pool_t
*pool
, lsmash_sample_t
*sample
, uint32_t samples_per_packet
)
4320 uint64_t pool_size
= pool
->size
+ sample
->length
;
4321 if( pool
->alloc
< pool_size
)
4324 uint64_t alloc
= pool_size
+ (1<<16);
4326 data
= lsmash_malloc( alloc
);
4328 data
= lsmash_realloc( pool
->data
, alloc
);
4332 pool
->alloc
= alloc
;
4334 memcpy( pool
->data
+ pool
->size
, sample
->data
, sample
->length
);
4335 pool
->size
= pool_size
;
4336 pool
->sample_count
+= samples_per_packet
;
4337 lsmash_delete_sample( sample
);
4341 static int isom_append_sample_internal( isom_trak_t
*trak
, lsmash_sample_t
*sample
)
4343 uint32_t samples_per_packet
;
4344 int flush
= isom_update_sample_tables( trak
, sample
, &samples_per_packet
);
4347 /* flush == 1 means pooled samples must be flushed. */
4348 lsmash_file_t
*file
= trak
->file
;
4349 isom_sample_pool_t
*current_pool
= trak
->cache
->chunk
.pool
;
4350 if( flush
== 1 && isom_write_pooled_samples( file
, current_pool
) )
4352 /* Arbitration system between tracks with extremely scattering dts.
4353 * Here, we check whether asynchronization between the tracks exceeds the tolerance.
4354 * If a track has too old "first DTS" in its cached chunk than current sample's DTS, then its pooled samples must be flushed.
4355 * We don't consider presentation of media since any edit can pick an arbitrary portion of media in track.
4356 * Note: you needn't read this loop until you grasp the basic handling of chunks. */
4357 double tolerance
= file
->max_async_tolerance
;
4358 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
4360 isom_trak_t
*other
= (isom_trak_t
*)entry
->data
;
4366 || !other
->mdia
->mdhd
4367 || other
->mdia
->mdhd
->timescale
== 0
4368 || !other
->mdia
->minf
4369 || !other
->mdia
->minf
->stbl
4370 || !other
->mdia
->minf
->stbl
->stsc
4371 || !other
->mdia
->minf
->stbl
->stsc
->list
)
4373 isom_chunk_t
*chunk
= &other
->cache
->chunk
;
4374 if( !chunk
->pool
|| chunk
->pool
->sample_count
== 0 )
4376 double diff
= ((double)sample
->dts
/ trak
->mdia
->mdhd
->timescale
)
4377 - ((double)chunk
->first_dts
/ other
->mdia
->mdhd
->timescale
);
4378 if( diff
> tolerance
&& isom_output_cached_chunk( other
) )
4380 /* Note: we don't flush the cached chunk in the current track and the current sample here
4381 * even if the conditional expression of '-diff > tolerance' meets.
4382 * That's useless because appending a sample to another track would be a good equivalent.
4383 * It's even harmful because it causes excess chunk division by calling
4384 * isom_output_cached_chunk() which always generates a new chunk.
4385 * Anyway some excess chunk division will be there, but rather less without it.
4386 * To completely avoid this, we need to observe at least whether the current sample will be placed
4387 * right next to the previous chunk of the same track or not. */
4389 /* anyway the current sample must be pooled. */
4390 return isom_pool_sample( current_pool
, sample
, samples_per_packet
);
4393 /* This function is for non-fragmented movie. */
4394 static int isom_append_sample( lsmash_file_t
*file
, uint32_t track_ID
, lsmash_sample_t
*sample
)
4396 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
4401 || !trak
->mdia
->mdhd
4402 || trak
->mdia
->mdhd
->timescale
== 0
4403 || !trak
->mdia
->minf
4404 || !trak
->mdia
->minf
->stbl
4405 || !trak
->mdia
->minf
->stbl
->stsd
4406 || !trak
->mdia
->minf
->stbl
->stsc
|| !trak
->mdia
->minf
->stbl
->stsc
->list
)
4408 /* If there is no available Media Data Box to write samples, add and write a new one before any chunk offset is decided. */
4411 if( !isom_add_mdat( file
) )
4413 file
->mdat
->manager
|= LSMASH_PLACEHOLDER
;
4414 if( isom_write_box( file
->bs
, (isom_box_t
*)file
->mdat
) < 0 )
4416 assert( file
->free
);
4417 file
->size
+= file
->free
->size
+ file
->mdat
->size
;
4419 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
, sample
->index
);
4422 if( isom_is_lpcm_audio( sample_entry
) )
4424 uint32_t frame_size
= ((isom_audio_entry_t
*)sample_entry
)->constBytesPerAudioPacket
;
4425 if( sample
->length
== frame_size
)
4426 return isom_append_sample_internal( trak
, sample
);
4427 else if( sample
->length
< frame_size
)
4429 /* Append samples splitted into each LPCMFrame. */
4430 uint64_t dts
= sample
->dts
;
4431 uint64_t cts
= sample
->cts
;
4432 for( uint32_t offset
= 0; offset
< sample
->length
; offset
+= frame_size
)
4434 lsmash_sample_t
*lpcm_sample
= lsmash_create_sample( frame_size
);
4437 memcpy( lpcm_sample
->data
, sample
->data
+ offset
, frame_size
);
4438 lpcm_sample
->dts
= dts
++;
4439 lpcm_sample
->cts
= cts
++;
4440 lpcm_sample
->prop
= sample
->prop
;
4441 lpcm_sample
->index
= sample
->index
;
4442 if( isom_append_sample_internal( trak
, lpcm_sample
) )
4444 lsmash_delete_sample( lpcm_sample
);
4448 lsmash_delete_sample( sample
);
4451 return isom_append_sample_internal( trak
, sample
);
4454 static int isom_output_cache( isom_trak_t
*trak
)
4456 isom_cache_t
*cache
= trak
->cache
;
4457 if( cache
->chunk
.pool
4458 && cache
->chunk
.pool
->sample_count
4459 && isom_output_cached_chunk( trak
) < 0 )
4461 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
4462 for( lsmash_entry_t
*entry
= stbl
->sgpd_list
.head
; entry
; entry
= entry
->next
)
4464 isom_sgpd_t
*sgpd
= (isom_sgpd_t
*)entry
->data
;
4467 switch( sgpd
->grouping_type
)
4469 case ISOM_GROUP_TYPE_RAP
:
4471 isom_rap_group_t
*group
= cache
->rap
;
4474 if( stbl
->file
->fragment
)
4479 if( !group
->random_access
)
4481 group
->random_access
->num_leading_samples_known
= 1;
4484 case ISOM_GROUP_TYPE_ROLL
:
4485 case ISOM_GROUP_TYPE_PROL
:
4486 if( !cache
->roll
.pool
)
4488 if( stbl
->file
->fragment
)
4493 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group( &stbl
->sbgp_list
);
4494 if( !sbgp
|| isom_all_recovery_completed( sbgp
, cache
->roll
.pool
) < 0 )
4504 int lsmash_flush_pooled_samples( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t last_sample_delta
)
4506 if( !root
|| !root
->file
)
4508 lsmash_file_t
*file
= root
->file
;
4510 && file
->fragment
->movie
)
4511 return isom_flush_fragment_pooled_samples( file
, track_ID
, last_sample_delta
);
4512 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
4516 || !trak
->mdia
->minf
4517 || !trak
->mdia
->minf
->stbl
4518 || !trak
->mdia
->minf
->stbl
->stsc
4519 || !trak
->mdia
->minf
->stbl
->stsc
->list
)
4521 if( isom_output_cache( trak
) < 0 )
4523 return lsmash_set_last_sample_delta( root
, track_ID
, last_sample_delta
);
4526 int lsmash_append_sample( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_sample_t
*sample
)
4530 lsmash_file_t
*file
= root
->file
;
4531 /* We think max_chunk_duration == 0, which means all samples will be cached on memory, should be prevented.
4532 * This means removal of a feature that we used to have, but anyway very alone chunk does not make sense. */
4538 || file
->max_chunk_duration
== 0
4539 || file
->max_async_tolerance
== 0 )
4541 /* Write File Type Box here if it was not written yet. */
4542 if( file
->ftyp
&& !(file
->ftyp
->manager
& LSMASH_WRITTEN_BOX
) )
4544 if( isom_write_box( file
->bs
, (isom_box_t
*)file
->ftyp
) )
4546 file
->size
+= file
->ftyp
->size
;
4549 && file
->fragment
->pool
)
4550 return isom_append_fragment_sample( file
, track_ID
, sample
);
4551 return isom_append_sample( file
, track_ID
, sample
);
4554 /*---- misc functions ----*/
4556 int lsmash_delete_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
)
4560 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
4563 isom_remove_box_by_itself( trak
->edts
);
4564 return isom_update_tkhd_duration( trak
);
4567 void lsmash_delete_tyrant_chapter( lsmash_root_t
*root
)
4571 || !root
->file
->moov
4572 || !root
->file
->moov
->udta
)
4574 isom_remove_box_by_itself( root
->file
->moov
->udta
->chpl
);
4577 int lsmash_set_copyright( lsmash_root_t
*root
, uint32_t track_ID
, uint16_t ISO_language
, char *notice
)
4581 lsmash_file_t
*file
= root
->file
;
4584 || !file
->isom_compatible
4585 || (ISO_language
&& ISO_language
< 0x800)
4591 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
4592 if( !trak
|| (!trak
->udta
&& isom_add_udta( trak
)) )
4598 if( !file
->moov
->udta
&& isom_add_udta( file
->moov
) )
4600 udta
= file
->moov
->udta
;
4603 for( lsmash_entry_t
*entry
= udta
->cprt_list
.head
; entry
; entry
= entry
->next
)
4605 isom_cprt_t
*cprt
= (isom_cprt_t
*)entry
->data
;
4606 if( !cprt
|| cprt
->language
== ISO_language
)
4609 if( !isom_add_cprt( udta
) )
4611 isom_cprt_t
*cprt
= (isom_cprt_t
*)udta
->cprt_list
.tail
->data
;
4612 cprt
->language
= ISO_language
;
4613 cprt
->notice_length
= strlen( notice
) + 1;
4614 cprt
->notice
= lsmash_memdup( notice
, cprt
->notice_length
);