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 */
36 #include "codecs/mp4a.h"
37 #include "codecs/mp4sys.h"
38 #include "codecs/description.h"
41 int isom_check_initializer_present( lsmash_root_t
*root
)
45 || !root
->file
->initializer
)
46 return LSMASH_ERR_NAMELESS
;
50 isom_trak_t
*isom_get_trak( lsmash_file_t
*file
, uint32_t track_ID
)
54 || file
!= file
->initializer
57 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
59 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
63 if( trak
->tkhd
->track_ID
== track_ID
)
69 isom_trex_t
*isom_get_trex( isom_mvex_t
*mvex
, uint32_t track_ID
)
71 if( track_ID
== 0 || !mvex
)
73 for( lsmash_entry_t
*entry
= mvex
->trex_list
.head
; entry
; entry
= entry
->next
)
75 isom_trex_t
*trex
= (isom_trex_t
*)entry
->data
;
78 if( trex
->track_ID
== track_ID
)
84 isom_traf_t
*isom_get_traf( isom_moof_t
*moof
, uint32_t track_ID
)
86 if( track_ID
== 0 || !moof
)
88 for( lsmash_entry_t
*entry
= moof
->traf_list
.head
; entry
; entry
= entry
->next
)
90 isom_traf_t
*traf
= (isom_traf_t
*)entry
->data
;
94 if( traf
->tfhd
->track_ID
== track_ID
)
100 isom_tfra_t
*isom_get_tfra( isom_mfra_t
*mfra
, uint32_t track_ID
)
102 if( track_ID
== 0 || !mfra
)
104 for( lsmash_entry_t
*entry
= mfra
->tfra_list
.head
; entry
; entry
= entry
->next
)
106 isom_tfra_t
*tfra
= (isom_tfra_t
*)entry
->data
;
109 if( tfra
->track_ID
== track_ID
)
115 static int isom_add_elst_entry( isom_elst_t
*elst
, uint64_t segment_duration
, int64_t media_time
, int32_t media_rate
)
117 assert( elst
->file
);
118 isom_elst_entry_t
*data
= lsmash_malloc( sizeof(isom_elst_entry_t
) );
120 return LSMASH_ERR_MEMORY_ALLOC
;
121 data
->segment_duration
= segment_duration
;
122 data
->media_time
= media_time
;
123 data
->media_rate
= media_rate
;
124 if( lsmash_add_entry( elst
->list
, data
) < 0 )
127 return LSMASH_ERR_MEMORY_ALLOC
;
129 if( !elst
->file
->undefined_64_ver
130 && (data
->segment_duration
> UINT32_MAX
131 || data
->media_time
> INT32_MAX
132 || data
->media_time
< INT32_MIN
) )
137 isom_dcr_ps_entry_t
*isom_create_ps_entry( uint8_t *ps
, uint32_t ps_size
)
139 isom_dcr_ps_entry_t
*entry
= lsmash_malloc( sizeof(isom_dcr_ps_entry_t
) );
142 entry
->nalUnit
= lsmash_memdup( ps
, ps_size
);
143 if( !entry
->nalUnit
)
145 lsmash_free( entry
);
148 entry
->nalUnitLength
= ps_size
;
153 void isom_remove_dcr_ps( isom_dcr_ps_entry_t
*ps
)
157 lsmash_free( ps
->nalUnit
);
161 /* This function returns 0 if failed, sample_entry_number if succeeded. */
162 int lsmash_add_sample_entry( lsmash_root_t
*root
, uint32_t track_ID
, void *summary
)
164 if( !root
|| !summary
)
166 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
171 || !trak
->mdia
->minf
->stbl
172 || !trak
->mdia
->minf
->stbl
->stsd
)
174 isom_stsd_t
*stsd
= trak
->mdia
->minf
->stbl
->stsd
;
175 lsmash_entry_list_t
*list
= &stsd
->list
;
176 int ret
= LSMASH_ERR_NAMELESS
;
177 lsmash_codec_type_t sample_type
= ((lsmash_summary_t
*)summary
)->sample_type
;
178 if( lsmash_check_codec_type_identical( sample_type
, LSMASH_CODEC_TYPE_RAW
) )
180 if( trak
->mdia
->minf
->vmhd
)
181 ret
= isom_setup_visual_description( stsd
, sample_type
, (lsmash_video_summary_t
*)summary
);
182 else if( trak
->mdia
->minf
->smhd
)
183 ret
= isom_setup_audio_description( stsd
, sample_type
, (lsmash_audio_summary_t
*)summary
);
184 return ret
< 0 ? 0 : list
->entry_count
;
186 typedef void (*opaque_func_t
)( void );
187 static struct description_setup_table_tag
189 lsmash_codec_type_t type
;
191 } description_setup_table
[160] = { { LSMASH_CODEC_TYPE_INITIALIZER
, NULL
} };
192 if( !description_setup_table
[0].func
)
195 #define ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( type, func ) \
196 description_setup_table[i++] = (struct description_setup_table_tag){ type, (opaque_func_t)func }
197 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO
, isom_setup_visual_description
);
198 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO
, isom_setup_visual_description
);
199 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO
, isom_setup_visual_description
);
200 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC4_VIDEO
, isom_setup_visual_description
);
201 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO
, isom_setup_visual_description
);
202 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO
, isom_setup_visual_description
);
204 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO
, isom_setup_visual_description
);
205 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO
, isom_setup_visual_description
);
206 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO
, isom_setup_visual_description
);
207 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO
, isom_setup_visual_description
);
208 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO
, isom_setup_visual_description
);
209 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO
, isom_setup_visual_description
);
210 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO
, isom_setup_visual_description
);
211 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO
, isom_setup_visual_description
);
212 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO
, isom_setup_visual_description
);
214 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO
, isom_setup_visual_description
);
215 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO
, isom_setup_visual_description
);
216 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO
, isom_setup_visual_description
);
217 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO
, isom_setup_visual_description
);
218 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO
, isom_setup_visual_description
);
219 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO
, isom_setup_visual_description
);
220 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO
, isom_setup_visual_description
);
221 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO
, isom_setup_visual_description
);
222 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO
, isom_setup_visual_description
);
223 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO
, isom_setup_visual_description
);
224 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO
, isom_setup_visual_description
);
225 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO
, isom_setup_visual_description
);
226 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO
, isom_setup_visual_description
);
227 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO
, isom_setup_visual_description
);
228 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO
, isom_setup_visual_description
);
229 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO
, isom_setup_visual_description
);
230 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO
, isom_setup_visual_description
);
231 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO
, isom_setup_visual_description
);
232 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO
, isom_setup_visual_description
);
233 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO
, isom_setup_visual_description
);
234 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO
, isom_setup_visual_description
);
235 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO
, isom_setup_visual_description
);
236 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO
, isom_setup_visual_description
);
237 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO
, isom_setup_visual_description
);
238 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO
, isom_setup_visual_description
);
239 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO
, isom_setup_visual_description
);
240 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO
, isom_setup_visual_description
);
241 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO
, isom_setup_visual_description
);
242 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO
, isom_setup_visual_description
);
243 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO
, isom_setup_visual_description
);
244 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO
, isom_setup_audio_description
);
245 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO
, isom_setup_audio_description
);
246 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO
, isom_setup_audio_description
);
247 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO
, isom_setup_audio_description
);
248 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO
, isom_setup_audio_description
);
249 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO
, isom_setup_audio_description
);
250 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO
, isom_setup_audio_description
);
251 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO
, isom_setup_audio_description
);
252 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO
, isom_setup_audio_description
);
253 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO
, isom_setup_audio_description
);
255 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO
, isom_setup_audio_description
);
256 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO
, isom_setup_audio_description
);
257 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO
, isom_setup_audio_description
);
258 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO
, isom_setup_audio_description
);
259 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO
, isom_setup_audio_description
);
260 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO
, isom_setup_audio_description
);
261 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_RAW_AUDIO
, isom_setup_audio_description
);
262 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO
, isom_setup_audio_description
);
263 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO
, isom_setup_audio_description
);
264 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO
, isom_setup_audio_description
);
265 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO
, isom_setup_audio_description
);
266 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO
, isom_setup_audio_description
);
268 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MP4A_AUDIO
, isom_setup_audio_description
);
269 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MAC3_AUDIO
, isom_setup_audio_description
);
270 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MAC6_AUDIO
, isom_setup_audio_description
);
271 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_AGSM_AUDIO
, isom_setup_audio_description
);
272 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ALAW_AUDIO
, isom_setup_audio_description
);
273 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULAW_AUDIO
, isom_setup_audio_description
);
274 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FULLMP3_AUDIO
, isom_setup_audio_description
);
275 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM2_AUDIO
, isom_setup_audio_description
);
276 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM17_AUDIO
, isom_setup_audio_description
);
277 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_GSM49_AUDIO
, isom_setup_audio_description
);
278 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO
, isom_setup_audio_description
);
279 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO
, isom_setup_audio_description
);
280 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO
, isom_setup_audio_description
);
281 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO
, isom_setup_audio_description
);
282 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO
, isom_setup_audio_description
);
283 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO
, isom_setup_audio_description
);
284 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO
, isom_setup_audio_description
);
285 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO
, isom_setup_audio_description
);
286 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO
, isom_setup_audio_description
);
287 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED
, isom_setup_audio_description
);
288 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT
, isom_add_tx3g_description
);
289 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT
, isom_add_qt_text_description
);
291 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4S_SYSTEM
, isom_add_mp4s_entry
);
294 for( int i
= 0; description_setup_table
[i
].func
; i
++ )
295 if( lsmash_check_codec_type_identical( sample_type
, description_setup_table
[i
].type
) )
297 if( (opaque_func_t
)isom_setup_visual_description
== description_setup_table
[i
].func
)
298 ret
= isom_setup_visual_description( stsd
, sample_type
, (lsmash_video_summary_t
*)summary
);
299 else if( (opaque_func_t
)isom_setup_audio_description
== description_setup_table
[i
].func
)
300 ret
= isom_setup_audio_description( stsd
, sample_type
, (lsmash_audio_summary_t
*)summary
);
301 else if( (opaque_func_t
)isom_add_tx3g_description
== description_setup_table
[i
].func
)
302 ret
= isom_setup_tx3g_description( stsd
, (lsmash_summary_t
*)summary
);
303 else if( (opaque_func_t
)isom_add_qt_text_description
== description_setup_table
[i
].func
)
305 isom_qt_text_entry_t
*text
= isom_add_qt_text_description( stsd
);
308 text
->data_reference_index
= ((lsmash_summary_t
*)summary
)->data_ref_index
;
314 return ret
< 0 ? 0 : list
->entry_count
;
317 static int isom_add_stts_entry( isom_stbl_t
*stbl
, uint32_t sample_delta
)
321 || !stbl
->stts
->list
)
322 return LSMASH_ERR_NAMELESS
;
323 isom_stts_entry_t
*data
= lsmash_malloc( sizeof(isom_stts_entry_t
) );
325 return LSMASH_ERR_MEMORY_ALLOC
;
326 data
->sample_count
= 1;
327 data
->sample_delta
= sample_delta
;
328 if( lsmash_add_entry( stbl
->stts
->list
, data
) < 0 )
331 return LSMASH_ERR_MEMORY_ALLOC
;
336 static int isom_add_ctts_entry( isom_stbl_t
*stbl
, uint32_t sample_offset
)
340 || !stbl
->ctts
->list
)
341 return LSMASH_ERR_NAMELESS
;
342 isom_ctts_entry_t
*data
= lsmash_malloc( sizeof(isom_ctts_entry_t
) );
344 return LSMASH_ERR_MEMORY_ALLOC
;
345 data
->sample_count
= 1;
346 data
->sample_offset
= sample_offset
;
347 if( lsmash_add_entry( stbl
->ctts
->list
, data
) < 0 )
350 return LSMASH_ERR_MEMORY_ALLOC
;
355 static int isom_add_stsc_entry( isom_stbl_t
*stbl
, uint32_t first_chunk
, uint32_t samples_per_chunk
, uint32_t sample_description_index
)
359 || !stbl
->stsc
->list
)
360 return LSMASH_ERR_NAMELESS
;
361 isom_stsc_entry_t
*data
= lsmash_malloc( sizeof(isom_stsc_entry_t
) );
363 return LSMASH_ERR_MEMORY_ALLOC
;
364 data
->first_chunk
= first_chunk
;
365 data
->samples_per_chunk
= samples_per_chunk
;
366 data
->sample_description_index
= sample_description_index
;
367 if( lsmash_add_entry( stbl
->stsc
->list
, data
) < 0 )
370 return LSMASH_ERR_MEMORY_ALLOC
;
375 static int isom_add_stsz_entry( isom_stbl_t
*stbl
, uint32_t entry_size
)
379 return LSMASH_ERR_NAMELESS
;
380 isom_stsz_t
*stsz
= stbl
->stsz
;
381 /* retrieve initial sample_size */
382 if( stsz
->sample_count
== 0 )
383 stsz
->sample_size
= entry_size
;
384 /* if it seems constant access_unit size at present, update sample_count only */
385 if( !stsz
->list
&& stsz
->sample_size
== entry_size
)
387 ++ stsz
->sample_count
;
390 /* found sample_size varies, create sample_size list */
393 stsz
->list
= lsmash_create_entry_list();
395 return LSMASH_ERR_MEMORY_ALLOC
;
396 for( uint32_t i
= 0; i
< stsz
->sample_count
; i
++ )
398 isom_stsz_entry_t
*data
= lsmash_malloc( sizeof(isom_stsz_entry_t
) );
400 return LSMASH_ERR_MEMORY_ALLOC
;
401 data
->entry_size
= stsz
->sample_size
;
402 if( lsmash_add_entry( stsz
->list
, data
) < 0 )
405 return LSMASH_ERR_MEMORY_ALLOC
;
408 stsz
->sample_size
= 0;
410 isom_stsz_entry_t
*data
= lsmash_malloc( sizeof(isom_stsz_entry_t
) );
412 return LSMASH_ERR_MEMORY_ALLOC
;
413 data
->entry_size
= entry_size
;
414 if( lsmash_add_entry( stsz
->list
, data
) < 0 )
417 return LSMASH_ERR_MEMORY_ALLOC
;
419 ++ stsz
->sample_count
;
423 static int isom_add_stss_entry( isom_stbl_t
*stbl
, uint32_t sample_number
)
427 || !stbl
->stss
->list
)
428 return LSMASH_ERR_NAMELESS
;
429 isom_stss_entry_t
*data
= lsmash_malloc( sizeof(isom_stss_entry_t
) );
431 return LSMASH_ERR_MEMORY_ALLOC
;
432 data
->sample_number
= sample_number
;
433 if( lsmash_add_entry( stbl
->stss
->list
, data
) < 0 )
436 return LSMASH_ERR_MEMORY_ALLOC
;
441 static int isom_add_stps_entry( isom_stbl_t
*stbl
, uint32_t sample_number
)
445 || !stbl
->stps
->list
)
446 return LSMASH_ERR_NAMELESS
;
447 isom_stps_entry_t
*data
= lsmash_malloc( sizeof(isom_stps_entry_t
) );
449 return LSMASH_ERR_MEMORY_ALLOC
;
450 data
->sample_number
= sample_number
;
451 if( lsmash_add_entry( stbl
->stps
->list
, data
) < 0 )
454 return LSMASH_ERR_MEMORY_ALLOC
;
459 /* Between ISOBMFF and QTFF, the most significant 2-bit has different meaning.
460 * For the maximum compatibility, we set 0 to the most significant 2-bit when compatible with
461 * both ISOBMFF with AVCFF extensions and QTFF.
462 * compatibility == 0 -> neither AVCFF extensions nor QTFF compatible
463 * compatibility == 1 -> AVCFF extensions compatible
464 * compatibility == 2 -> QTFF compatible
465 * compatibility == 3 -> both AVCFF extensions and QTFF compatible */
466 static int isom_add_sdtp_entry( isom_box_t
*parent
, lsmash_sample_property_t
*prop
, int compatibility
)
468 if( !prop
|| !parent
)
469 return LSMASH_ERR_NAMELESS
;
470 isom_sdtp_t
*sdtp
= NULL
;
471 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_STBL
) )
472 sdtp
= ((isom_stbl_t
*)parent
)->sdtp
;
473 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
474 sdtp
= ((isom_traf_t
*)parent
)->sdtp
;
479 return LSMASH_ERR_NAMELESS
;
480 isom_sdtp_entry_t
*data
= lsmash_malloc( sizeof(isom_sdtp_entry_t
) );
482 return LSMASH_ERR_MEMORY_ALLOC
;
483 if( compatibility
== 1 )
484 data
->is_leading
= prop
->leading
& 0x03;
485 else if( compatibility
== 2 )
486 data
->is_leading
= prop
->allow_earlier
& 0x03;
489 data
->is_leading
= 0;
490 assert( compatibility
== 3 );
492 data
->sample_depends_on
= prop
->independent
& 0x03;
493 data
->sample_is_depended_on
= prop
->disposable
& 0x03;
494 data
->sample_has_redundancy
= prop
->redundant
& 0x03;
495 if( lsmash_add_entry( sdtp
->list
, data
) < 0 )
498 return LSMASH_ERR_MEMORY_ALLOC
;
503 static int isom_add_co64_entry( isom_stbl_t
*stbl
, uint64_t chunk_offset
)
507 || !stbl
->stco
->list
)
508 return LSMASH_ERR_NAMELESS
;
509 isom_co64_entry_t
*data
= lsmash_malloc( sizeof(isom_co64_entry_t
) );
511 return LSMASH_ERR_MEMORY_ALLOC
;
512 data
->chunk_offset
= chunk_offset
;
513 if( lsmash_add_entry( stbl
->stco
->list
, data
) < 0 )
516 return LSMASH_ERR_MEMORY_ALLOC
;
521 static int isom_convert_stco_to_co64( isom_stbl_t
*stbl
)
525 isom_stco_t
*stco
= stbl
->stco
;
527 if( !isom_add_co64( stbl
) )
529 err
= LSMASH_ERR_NAMELESS
;
532 /* move chunk_offset to co64 from stco */
533 for( lsmash_entry_t
*entry
= stco
->list
->head
; entry
; entry
= entry
->next
)
535 isom_stco_entry_t
*data
= (isom_stco_entry_t
*)entry
->data
;
536 if( (err
= isom_add_co64_entry( stbl
, data
->chunk_offset
)) < 0 )
540 isom_remove_box_by_itself( stco
);
544 static int isom_add_stco_entry( isom_stbl_t
*stbl
, uint64_t chunk_offset
)
548 || !stbl
->stco
->list
)
549 return LSMASH_ERR_NAMELESS
;
550 if( stbl
->stco
->large_presentation
)
551 return isom_add_co64_entry( stbl
, chunk_offset
);
552 if( chunk_offset
> UINT32_MAX
)
554 int err
= isom_convert_stco_to_co64( stbl
);
557 return isom_add_co64_entry( stbl
, chunk_offset
);
559 isom_stco_entry_t
*data
= lsmash_malloc( sizeof(isom_stco_entry_t
) );
561 return LSMASH_ERR_MEMORY_ALLOC
;
562 data
->chunk_offset
= (uint32_t)chunk_offset
;
563 if( lsmash_add_entry( stbl
->stco
->list
, data
) < 0 )
566 return LSMASH_ERR_MEMORY_ALLOC
;
571 static isom_sgpd_t
*isom_get_sample_group_description_common( lsmash_entry_list_t
*list
, uint32_t grouping_type
)
573 for( lsmash_entry_t
*entry
= list
->head
; entry
; entry
= entry
->next
)
575 isom_sgpd_t
*sgpd
= (isom_sgpd_t
*)entry
->data
;
579 if( sgpd
->grouping_type
== grouping_type
)
585 static isom_sbgp_t
*isom_get_sample_to_group_common( lsmash_entry_list_t
*list
, uint32_t grouping_type
)
587 for( lsmash_entry_t
*entry
= list
->head
; entry
; entry
= entry
->next
)
589 isom_sbgp_t
*sbgp
= (isom_sbgp_t
*)entry
->data
;
593 if( sbgp
->grouping_type
== grouping_type
)
599 isom_sgpd_t
*isom_get_sample_group_description( isom_stbl_t
*stbl
, uint32_t grouping_type
)
601 return isom_get_sample_group_description_common( &stbl
->sgpd_list
, grouping_type
);
604 isom_sbgp_t
*isom_get_sample_to_group( isom_stbl_t
*stbl
, uint32_t grouping_type
)
606 return isom_get_sample_to_group_common( &stbl
->sbgp_list
, grouping_type
);
609 isom_sgpd_t
*isom_get_roll_recovery_sample_group_description( lsmash_entry_list_t
*list
)
612 if( (sgpd
= isom_get_sample_group_description_common( list
, ISOM_GROUP_TYPE_ROLL
))
613 || (sgpd
= isom_get_sample_group_description_common( list
, ISOM_GROUP_TYPE_PROL
)) )
618 isom_sbgp_t
*isom_get_roll_recovery_sample_to_group( lsmash_entry_list_t
*list
)
621 if( (sbgp
= isom_get_sample_to_group_common( list
, ISOM_GROUP_TYPE_ROLL
))
622 || (sbgp
= isom_get_sample_to_group_common( list
, ISOM_GROUP_TYPE_PROL
)) )
627 isom_sgpd_t
*isom_get_fragment_sample_group_description( isom_traf_t
*traf
, uint32_t grouping_type
)
629 return isom_get_sample_group_description_common( &traf
->sgpd_list
, grouping_type
);
632 isom_sbgp_t
*isom_get_fragment_sample_to_group( isom_traf_t
*traf
, uint32_t grouping_type
)
634 return isom_get_sample_to_group_common( &traf
->sbgp_list
, grouping_type
);
637 static isom_rap_entry_t
*isom_add_rap_group_entry( isom_sgpd_t
*sgpd
)
641 isom_rap_entry_t
*data
= lsmash_malloc( sizeof(isom_rap_entry_t
) );
644 data
->description_length
= 0;
645 data
->num_leading_samples_known
= 0;
646 data
->num_leading_samples
= 0;
647 if( lsmash_add_entry( sgpd
->list
, data
) < 0 )
655 static isom_roll_entry_t
*isom_add_roll_group_entry( isom_sgpd_t
*sgpd
, int16_t roll_distance
)
659 isom_roll_entry_t
*data
= lsmash_malloc( sizeof(isom_roll_entry_t
) );
662 data
->description_length
= 0;
663 data
->roll_distance
= roll_distance
;
664 if( lsmash_add_entry( sgpd
->list
, data
) < 0 )
672 static isom_group_assignment_entry_t
*isom_add_group_assignment_entry( isom_sbgp_t
*sbgp
, uint32_t sample_count
, uint32_t group_description_index
)
676 isom_group_assignment_entry_t
*data
= lsmash_malloc( sizeof(isom_group_assignment_entry_t
) );
679 data
->sample_count
= sample_count
;
680 data
->group_description_index
= group_description_index
;
681 if( lsmash_add_entry( sbgp
->list
, data
) < 0 )
689 uint32_t isom_get_sample_count( isom_trak_t
*trak
)
694 || !trak
->mdia
->minf
->stbl
695 || !trak
->mdia
->minf
->stbl
->stsz
)
697 return trak
->mdia
->minf
->stbl
->stsz
->sample_count
;
700 static uint64_t isom_get_dts( isom_stts_t
*stts
, uint32_t sample_number
)
707 lsmash_entry_t
*entry
;
708 isom_stts_entry_t
*data
;
709 for( entry
= stts
->list
->head
; entry
; entry
= entry
->next
)
711 data
= (isom_stts_entry_t
*)entry
->data
;
714 if( i
+ data
->sample_count
> sample_number
)
716 dts
+= (uint64_t)data
->sample_delta
* data
->sample_count
;
717 i
+= data
->sample_count
;
721 dts
+= (uint64_t)data
->sample_delta
* (sample_number
- i
);
726 static uint64_t isom_get_cts( isom_stts_t
*stts
, isom_ctts_t
*ctts
, uint32_t sample_number
)
732 return isom_get_dts( stts
, sample_number
);
733 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. */
734 lsmash_entry_t
*entry
;
735 isom_ctts_entry_t
*data
;
736 if( sample_number
== 0 )
738 for( entry
= ctts
->list
->head
; entry
; entry
= entry
->next
)
740 data
= (isom_ctts_entry_t
*)entry
->data
;
743 if( i
+ data
->sample_count
> sample_number
)
745 i
+= data
->sample_count
;
749 return isom_get_dts( stts
, sample_number
) + data
->sample_offset
;
753 static int isom_replace_last_sample_delta( isom_stbl_t
*stbl
, uint32_t sample_delta
)
758 || !stbl
->stts
->list
->tail
759 || !stbl
->stts
->list
->tail
->data
)
760 return LSMASH_ERR_NAMELESS
;
761 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)stbl
->stts
->list
->tail
->data
;
762 if( sample_delta
!= last_stts_data
->sample_delta
)
764 if( last_stts_data
->sample_count
> 1 )
766 last_stts_data
->sample_count
-= 1;
767 int err
= isom_add_stts_entry( stbl
, sample_delta
);
772 last_stts_data
->sample_delta
= sample_delta
;
777 static int isom_update_mdhd_duration( isom_trak_t
*trak
, uint32_t last_sample_delta
)
785 || !trak
->mdia
->minf
->stbl
786 || !trak
->mdia
->minf
->stbl
->stts
787 || !trak
->mdia
->minf
->stbl
->stts
->list
)
788 return LSMASH_ERR_INVALID_DATA
;
789 lsmash_file_t
*file
= trak
->file
;
790 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
791 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
792 isom_stts_t
*stts
= stbl
->stts
;
793 isom_ctts_t
*ctts
= stbl
->ctts
;
794 isom_cslg_t
*cslg
= stbl
->cslg
;
796 uint32_t sample_count
= isom_get_sample_count( trak
);
797 if( sample_count
== 0 )
799 /* Return error if non-fragmented movie has no samples. */
800 if( !file
->fragment
&& !stts
->list
->entry_count
)
801 return LSMASH_ERR_INVALID_DATA
;
804 /* Now we have at least 1 sample, so do stts_entry. */
805 lsmash_entry_t
*last_stts
= stts
->list
->tail
;
806 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)last_stts
->data
;
807 if( sample_count
== 1 )
808 mdhd
->duration
= last_stts_data
->sample_delta
;
809 /* Now we have at least 2 samples,
810 * but dunno whether 1 stts_entry which has 2 samples or 2 stts_entry which has 1 samle each. */
813 /* use dts instead of cts */
814 mdhd
->duration
= isom_get_dts( stts
, sample_count
);
816 if( last_sample_delta
)
818 mdhd
->duration
+= last_sample_delta
;
819 if( (err
= isom_replace_last_sample_delta( stbl
, last_sample_delta
)) < 0 )
822 else if( last_stts_data
->sample_count
> 1 )
823 mdhd
->duration
+= last_stts_data
->sample_delta
; /* no need to update last_stts_data->sample_delta */
826 /* Remove the last entry. */
827 if( (err
= lsmash_remove_entry_tail( stts
->list
, NULL
)) < 0 )
829 /* copy the previous sample_delta. */
830 ++ ((isom_stts_entry_t
*)stts
->list
->tail
->data
)->sample_count
;
831 mdhd
->duration
+= ((isom_stts_entry_t
*)stts
->list
->tail
->data
)->sample_delta
;
837 || ctts
->list
->entry_count
== 0 )
838 return LSMASH_ERR_INVALID_DATA
;
840 uint64_t max_cts
= 0;
841 uint64_t max2_cts
= 0;
842 uint64_t min_cts
= UINT64_MAX
;
843 uint32_t max_offset
= 0;
844 uint32_t min_offset
= UINT32_MAX
;
845 int32_t ctd_shift
= trak
->cache
->timestamp
.ctd_shift
;
848 lsmash_entry_t
*stts_entry
= stts
->list
->head
;
849 lsmash_entry_t
*ctts_entry
= ctts
->list
->head
;
850 for( uint32_t i
= 0; i
< sample_count
; i
++ )
852 if( !ctts_entry
|| !stts_entry
)
853 return LSMASH_ERR_INVALID_DATA
;
854 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
855 isom_ctts_entry_t
*ctts_data
= (isom_ctts_entry_t
*)ctts_entry
->data
;
856 if( !stts_data
|| !ctts_data
)
857 return LSMASH_ERR_INVALID_DATA
;
861 /* Anyway, add composition to decode timeline shift for calculating maximum and minimum CTS correctly. */
862 int32_t sample_offset
= (int32_t)ctts_data
->sample_offset
;
863 cts
= dts
+ sample_offset
+ ctd_shift
;
864 max_offset
= LSMASH_MAX( (int32_t)max_offset
, sample_offset
);
865 min_offset
= LSMASH_MIN( (int32_t)min_offset
, sample_offset
);
869 cts
= dts
+ ctts_data
->sample_offset
;
870 max_offset
= LSMASH_MAX( max_offset
, ctts_data
->sample_offset
);
871 min_offset
= LSMASH_MIN( min_offset
, ctts_data
->sample_offset
);
873 min_cts
= LSMASH_MIN( min_cts
, cts
);
879 else if( max2_cts
< cts
)
881 dts
+= stts_data
->sample_delta
;
882 /* If finished sample_count of current entry, move to next. */
883 if( ++j
== ctts_data
->sample_count
)
885 ctts_entry
= ctts_entry
->next
;
888 if( ++k
== stts_data
->sample_count
)
890 stts_entry
= stts_entry
->next
;
894 dts
-= last_stts_data
->sample_delta
;
896 /* Overall presentation is extended exceeding this initial movie.
897 * So, any players shall display the movie exceeding the durations
898 * indicated in Movie Header Box, Track Header Boxes and Media Header Boxes.
899 * Samples up to the duration indicated in Movie Extends Header Box shall be displayed.
900 * In the absence of Movie Extends Header Box, all samples shall be displayed. */
901 mdhd
->duration
+= dts
+ last_sample_delta
;
904 if( !last_sample_delta
)
906 /* The spec allows an arbitrary value for the duration of the last sample. So, we pick last-1 sample's. */
907 last_sample_delta
= max_cts
- max2_cts
;
909 mdhd
->duration
= max_cts
- min_cts
+ last_sample_delta
;
910 /* To match dts and media duration, update stts and mdhd relatively. */
911 if( mdhd
->duration
> dts
)
912 last_sample_delta
= mdhd
->duration
- dts
;
914 mdhd
->duration
= dts
+ last_sample_delta
; /* media duration must not less than last dts. */
916 int err
= isom_replace_last_sample_delta( stbl
, last_sample_delta
);
919 /* Explicit composition information and timeline shifting */
920 if( cslg
|| file
->qt_compatible
|| file
->max_isom_version
>= 4 )
924 /* Remove composition to decode timeline shift. */
925 max_cts
-= ctd_shift
;
926 max2_cts
-= ctd_shift
;
927 min_cts
-= ctd_shift
;
929 int64_t composition_end_time
= max_cts
+ (max_cts
- max2_cts
);
931 && ((int32_t)min_offset
<= INT32_MAX
) && ((int32_t)max_offset
<= INT32_MAX
)
932 && ((int64_t)min_cts
<= INT32_MAX
) && (composition_end_time
<= INT32_MAX
) )
936 if( !isom_add_cslg( trak
->mdia
->minf
->stbl
) )
937 return LSMASH_ERR_NAMELESS
;
940 cslg
->compositionToDTSShift
= ctd_shift
;
941 cslg
->leastDecodeToDisplayDelta
= min_offset
;
942 cslg
->greatestDecodeToDisplayDelta
= max_offset
;
943 cslg
->compositionStartTime
= min_cts
;
944 cslg
->compositionEndTime
= composition_end_time
;
947 isom_remove_box_by_itself( cslg
);
950 if( mdhd
->duration
> UINT32_MAX
&& !file
->undefined_64_ver
)
955 static int isom_update_mvhd_duration( isom_moov_t
*moov
)
959 || !moov
->mvhd
->file
)
960 return LSMASH_ERR_INVALID_DATA
;
961 isom_mvhd_t
*mvhd
= moov
->mvhd
;
963 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
965 /* We pick maximum track duration as movie duration. */
966 isom_trak_t
*data
= (isom_trak_t
*)entry
->data
;
969 return LSMASH_ERR_INVALID_DATA
;
970 mvhd
->duration
= entry
!= moov
->trak_list
.head
971 ? LSMASH_MAX( mvhd
->duration
, data
->tkhd
->duration
)
972 : data
->tkhd
->duration
;
974 if( mvhd
->duration
> UINT32_MAX
&& !mvhd
->file
->undefined_64_ver
)
979 int isom_update_tkhd_duration( isom_trak_t
*trak
)
984 || !trak
->file
->moov
)
985 return LSMASH_ERR_INVALID_DATA
;
986 lsmash_file_t
*file
= trak
->file
;
987 isom_tkhd_t
*tkhd
= trak
->tkhd
;
991 || !trak
->edts
->elst
)
993 /* If this presentation might be extended or this track doesn't have edit list, calculate track duration from media duration. */
997 || trak
->mdia
->mdhd
->timescale
== 0 )
998 return LSMASH_ERR_INVALID_DATA
;
999 if( trak
->mdia
->mdhd
->duration
== 0 )
1001 int err
= isom_update_mdhd_duration( trak
, 0 );
1005 tkhd
->duration
= trak
->mdia
->mdhd
->duration
* ((double)file
->moov
->mvhd
->timescale
/ trak
->mdia
->mdhd
->timescale
);
1009 /* If the presentation won't be extended and this track has any edit, then track duration is just the sum of the segment_duartions. */
1010 for( lsmash_entry_t
*entry
= trak
->edts
->elst
->list
->head
; entry
; entry
= entry
->next
)
1012 isom_elst_entry_t
*data
= (isom_elst_entry_t
*)entry
->data
;
1014 return LSMASH_ERR_INVALID_DATA
;
1015 tkhd
->duration
+= data
->segment_duration
;
1018 if( tkhd
->duration
> UINT32_MAX
&& !file
->undefined_64_ver
)
1020 if( !file
->fragment
&& tkhd
->duration
== 0 )
1021 tkhd
->duration
= tkhd
->version
== 1 ? 0xffffffffffffffff : 0xffffffff;
1022 return isom_update_mvhd_duration( file
->moov
);
1025 int lsmash_update_track_duration( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t last_sample_delta
)
1027 if( isom_check_initializer_present( root
) < 0 )
1028 return LSMASH_ERR_FUNCTION_PARAM
;
1029 lsmash_file_t
*file
= root
->file
;
1030 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1032 return LSMASH_ERR_NAMELESS
;
1033 int err
= isom_update_mdhd_duration( trak
, last_sample_delta
);
1036 /* If the presentation won't be extended and this track has any edit, we don't change or update duration in tkhd. */
1037 if( !file
->fragment
&& trak
->edts
&& trak
->edts
->elst
)
1038 err
= isom_update_mvhd_duration( file
->moov
); /* Only update movie duration. */
1040 err
= isom_update_tkhd_duration( trak
); /* Also update movie duration internally. */
1044 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
)
1046 if( *sample_number_in_entry
!= sample_count_in_entry
)
1048 *sample_number_in_entry
+= 1;
1051 /* Precede the next entry. */
1052 *sample_number_in_entry
= 1;
1055 *entry
= (*entry
)->next
;
1056 if( *entry
&& !(*entry
)->data
)
1057 return LSMASH_ERR_INVALID_DATA
;
1062 static int isom_calculate_bitrate_description( isom_mdia_t
*mdia
, uint32_t *bufferSizeDB
, uint32_t *maxBitrate
, uint32_t *avgBitrate
, uint32_t sample_description_index
)
1064 isom_stsz_t
*stsz
= mdia
->minf
->stbl
->stsz
;
1065 lsmash_entry_t
*stsz_entry
= stsz
->list
? stsz
->list
->head
: NULL
;
1066 lsmash_entry_t
*stts_entry
= mdia
->minf
->stbl
->stts
->list
->head
;
1067 lsmash_entry_t
*stsc_entry
= NULL
;
1068 lsmash_entry_t
*next_stsc_entry
= mdia
->minf
->stbl
->stsc
->list
->head
;
1069 isom_stts_entry_t
*stts_data
= NULL
;
1070 isom_stsc_entry_t
*stsc_data
= NULL
;
1071 if( next_stsc_entry
&& !next_stsc_entry
->data
)
1072 return LSMASH_ERR_INVALID_DATA
;
1075 uint32_t time_wnd
= 0;
1076 uint32_t timescale
= mdia
->mdhd
->timescale
;
1077 uint32_t chunk_number
= 0;
1078 uint32_t sample_number_in_stts
= 1;
1079 uint32_t sample_number_in_chunk
= 1;
1086 if( !stsc_data
|| sample_number_in_chunk
== stsc_data
->samples_per_chunk
)
1088 /* Move the next chunk. */
1089 sample_number_in_chunk
= 1;
1091 /* Check if the next entry is broken. */
1092 while( next_stsc_entry
&& ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
< chunk_number
)
1094 /* Just skip broken next entry. */
1095 next_stsc_entry
= next_stsc_entry
->next
;
1096 if( next_stsc_entry
&& !next_stsc_entry
->data
)
1097 return LSMASH_ERR_INVALID_DATA
;
1099 /* Check if the next chunk belongs to the next sequence of chunks. */
1100 if( next_stsc_entry
&& ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
== chunk_number
)
1102 stsc_entry
= next_stsc_entry
;
1103 next_stsc_entry
= next_stsc_entry
->next
;
1104 if( next_stsc_entry
&& !next_stsc_entry
->data
)
1105 return LSMASH_ERR_INVALID_DATA
;
1106 stsc_data
= (isom_stsc_entry_t
*)stsc_entry
->data
;
1107 /* Check if the next contiguous chunks belong to given sample description. */
1108 if( stsc_data
->sample_description_index
!= sample_description_index
)
1110 /* Skip chunks which don't belong to given sample description. */
1111 uint32_t number_of_skips
= 0;
1112 uint32_t first_chunk
= stsc_data
->first_chunk
;
1113 uint32_t samples_per_chunk
= stsc_data
->samples_per_chunk
;
1114 while( next_stsc_entry
)
1116 if( ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->sample_description_index
!= sample_description_index
)
1118 stsc_data
= (isom_stsc_entry_t
*)next_stsc_entry
->data
;
1119 number_of_skips
+= (stsc_data
->first_chunk
- first_chunk
) * samples_per_chunk
;
1120 first_chunk
= stsc_data
->first_chunk
;
1121 samples_per_chunk
= stsc_data
->samples_per_chunk
;
1123 else if( ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
<= first_chunk
)
1124 ; /* broken entry */
1127 /* Just skip the next entry. */
1128 next_stsc_entry
= next_stsc_entry
->next
;
1129 if( next_stsc_entry
&& !next_stsc_entry
->data
)
1130 return LSMASH_ERR_INVALID_DATA
;
1132 if( !next_stsc_entry
)
1133 break; /* There is no more chunks which don't belong to given sample description. */
1134 number_of_skips
+= (((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
- first_chunk
) * samples_per_chunk
;
1135 for( uint32_t i
= 0; i
< number_of_skips
; i
++ )
1141 stsz_entry
= stsz_entry
->next
;
1145 if( (err
= isom_increment_sample_number_in_entry( &sample_number_in_stts
,
1146 ((isom_stts_entry_t
*)stts_entry
->data
)->sample_count
,
1147 &stts_entry
)) < 0 )
1150 if( (stsz
->list
&& !stsz_entry
) || !stts_entry
)
1152 chunk_number
= stsc_data
->first_chunk
;
1157 ++sample_number_in_chunk
;
1158 /* Get current sample's size. */
1164 isom_stsz_entry_t
*stsz_data
= (isom_stsz_entry_t
*)stsz_entry
->data
;
1166 return LSMASH_ERR_INVALID_DATA
;
1167 size
= stsz_data
->entry_size
;
1168 stsz_entry
= stsz_entry
->next
;
1171 size
= stsz
->sample_size
;
1172 /* Get current sample's DTS. */
1174 dts
+= stts_data
->sample_delta
;
1175 stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
1177 return LSMASH_ERR_INVALID_DATA
;
1178 if( (err
= isom_increment_sample_number_in_entry( &sample_number_in_stts
, stts_data
->sample_count
, &stts_entry
)) < 0 )
1180 /* Calculate bitrate description. */
1181 if( *bufferSizeDB
< size
)
1182 *bufferSizeDB
= size
;
1183 *avgBitrate
+= size
;
1185 if( dts
> time_wnd
+ timescale
)
1187 if( rate
> *maxBitrate
)
1193 double duration
= (double)mdia
->mdhd
->duration
/ timescale
;
1194 *avgBitrate
= (uint32_t)(*avgBitrate
/ duration
);
1195 if( *maxBitrate
== 0 )
1196 *maxBitrate
= *avgBitrate
;
1197 /* Convert to bits per second. */
1203 int isom_update_bitrate_description( isom_mdia_t
*mdia
)
1208 || !mdia
->minf
->stbl
)
1209 return LSMASH_ERR_INVALID_DATA
;
1210 isom_stbl_t
*stbl
= mdia
->minf
->stbl
;
1213 || !stbl
->stsc
|| !stbl
->stsc
->list
1214 || !stbl
->stts
|| !stbl
->stts
->list
)
1215 return LSMASH_ERR_INVALID_DATA
;
1216 uint32_t sample_description_index
= 0;
1217 for( lsmash_entry_t
*entry
= stbl
->stsd
->list
.head
; entry
; entry
= entry
->next
)
1219 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)entry
->data
;
1221 return LSMASH_ERR_INVALID_DATA
;
1222 ++sample_description_index
;
1224 uint32_t bufferSizeDB
;
1225 uint32_t maxBitrate
;
1226 uint32_t avgBitrate
;
1227 /* set bitrate info */
1228 lsmash_codec_type_t sample_type
= sample_entry
->type
;
1229 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC1_VIDEO
)
1230 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC2_VIDEO
)
1231 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC3_VIDEO
)
1232 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC4_VIDEO
)
1233 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_HVC1_VIDEO
)
1234 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_HEV1_VIDEO
) )
1236 isom_visual_entry_t
*stsd_data
= (isom_visual_entry_t
*)sample_entry
;
1238 return LSMASH_ERR_INVALID_DATA
;
1239 isom_btrt_t
*btrt
= (isom_btrt_t
*)isom_get_extension_box_format( &stsd_data
->extensions
, ISOM_BOX_TYPE_BTRT
);
1242 if( (err
= isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
)) < 0 )
1244 btrt
->bufferSizeDB
= bufferSizeDB
;
1245 btrt
->maxBitrate
= maxBitrate
;
1246 btrt
->avgBitrate
= avgBitrate
;
1249 else if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4V_VIDEO
) )
1251 isom_visual_entry_t
*stsd_data
= (isom_visual_entry_t
*)sample_entry
;
1253 return LSMASH_ERR_INVALID_DATA
;
1254 isom_esds_t
*esds
= (isom_esds_t
*)isom_get_extension_box_format( &stsd_data
->extensions
, ISOM_BOX_TYPE_ESDS
);
1255 if( !esds
|| !esds
->ES
)
1256 return LSMASH_ERR_INVALID_DATA
;
1257 if( (err
= isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
)) < 0 )
1259 /* FIXME: avgBitrate is 0 only if VBR in proper. */
1260 if( (err
= mp4sys_update_DecoderConfigDescriptor( esds
->ES
, bufferSizeDB
, maxBitrate
, 0 )) < 0 )
1263 else if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4A_AUDIO
) )
1265 isom_audio_entry_t
*stsd_data
= (isom_audio_entry_t
*)sample_entry
;
1267 return LSMASH_ERR_INVALID_DATA
;
1268 isom_esds_t
*esds
= NULL
;
1269 if( ((isom_audio_entry_t
*)sample_entry
)->version
)
1271 /* MPEG-4 Audio in QTFF */
1272 isom_wave_t
*wave
= (isom_wave_t
*)isom_get_extension_box_format( &stsd_data
->extensions
, QT_BOX_TYPE_WAVE
);
1274 return LSMASH_ERR_INVALID_DATA
;
1275 esds
= (isom_esds_t
*)isom_get_extension_box_format( &wave
->extensions
, ISOM_BOX_TYPE_ESDS
);
1278 esds
= (isom_esds_t
*)isom_get_extension_box_format( &stsd_data
->extensions
, ISOM_BOX_TYPE_ESDS
);
1279 if( !esds
|| !esds
->ES
)
1280 return LSMASH_ERR_INVALID_DATA
;
1281 if( (err
= isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
)) < 0 )
1283 /* FIXME: avgBitrate is 0 only if VBR in proper. */
1284 if( (err
= mp4sys_update_DecoderConfigDescriptor( esds
->ES
, bufferSizeDB
, maxBitrate
, 0 )) < 0 )
1287 else if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_ALAC_AUDIO
)
1288 || lsmash_check_codec_type_identical( sample_type
, QT_CODEC_TYPE_ALAC_AUDIO
) )
1290 isom_audio_entry_t
*alac
= (isom_audio_entry_t
*)sample_entry
;
1292 return LSMASH_ERR_INVALID_DATA
;
1293 uint8_t *exdata
= NULL
;
1294 uint32_t exdata_size
= 0;
1295 isom_box_t
*alac_ext
= isom_get_extension_box( &alac
->extensions
, QT_BOX_TYPE_WAVE
);
1298 /* Apple Lossless Audio inside QuickTime file format
1299 * Though average bitrate field we found is always set to 0 apparently,
1300 * we set up maxFrameBytes and avgBitRate fields. */
1301 if( alac_ext
->manager
& LSMASH_BINARY_CODED_BOX
)
1302 exdata
= isom_get_child_box_position( alac_ext
->binary
, alac_ext
->size
, QT_BOX_TYPE_ALAC
, &exdata_size
);
1305 isom_wave_t
*wave
= (isom_wave_t
*)alac_ext
;
1306 isom_box_t
*wave_ext
= isom_get_extension_box( &wave
->extensions
, QT_BOX_TYPE_ALAC
);
1307 if( !wave_ext
|| !(wave_ext
->manager
& LSMASH_BINARY_CODED_BOX
) )
1308 return LSMASH_ERR_INVALID_DATA
;
1309 exdata
= wave_ext
->binary
;
1310 exdata_size
= wave_ext
->size
;
1315 /* Apple Lossless Audio inside ISO Base Media file format */
1316 isom_box_t
*ext
= isom_get_extension_box( &alac
->extensions
, ISOM_BOX_TYPE_ALAC
);
1317 if( !ext
|| !(ext
->manager
& LSMASH_BINARY_CODED_BOX
) )
1318 return LSMASH_ERR_INVALID_DATA
;
1319 exdata
= ext
->binary
;
1320 exdata_size
= ext
->size
;
1322 if( !exdata
|| exdata_size
< 36 )
1323 return LSMASH_ERR_INVALID_DATA
;
1324 if( (err
= isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
)) < 0 )
1328 LSMASH_SET_BE32( &exdata
[0], bufferSizeDB
);
1330 LSMASH_SET_BE32( &exdata
[4], avgBitrate
);
1332 else if( isom_is_waveform_audio( sample_type
) )
1334 isom_box_t
*ext
= isom_get_extension_box( &sample_entry
->extensions
, QT_BOX_TYPE_WAVE
);
1336 return LSMASH_ERR_INVALID_DATA
;
1337 uint8_t *exdata
= NULL
;
1338 uint32_t exdata_size
= 0;
1339 if( ext
->manager
& LSMASH_BINARY_CODED_BOX
)
1340 exdata
= isom_get_child_box_position( ext
->binary
, ext
->size
, sample_type
, &exdata_size
);
1343 isom_wave_t
*wave
= (isom_wave_t
*)ext
;
1344 isom_box_t
*wave_ext
= isom_get_extension_box( &wave
->extensions
, sample_type
);
1345 if( !wave_ext
|| !(wave_ext
->manager
& LSMASH_BINARY_CODED_BOX
) )
1346 return LSMASH_ERR_INVALID_DATA
;
1347 exdata
= wave_ext
->binary
;
1348 exdata_size
= wave_ext
->size
;
1350 /* Check whether exdata is valid or not. */
1351 if( !exdata
|| exdata_size
< ISOM_BASEBOX_COMMON_SIZE
+ 18 )
1352 return LSMASH_ERR_INVALID_DATA
;
1353 exdata
+= ISOM_BASEBOX_COMMON_SIZE
;
1354 uint16_t cbSize
= LSMASH_GET_LE16( &exdata
[16] );
1355 if( exdata_size
< ISOM_BASEBOX_COMMON_SIZE
+ 18 + cbSize
)
1356 return LSMASH_ERR_INVALID_DATA
;
1357 /* WAVEFORMATEX.nAvgBytesPerSec */
1358 if( (err
= isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
)) < 0 )
1360 uint32_t nAvgBytesPerSec
= avgBitrate
/ 8;
1361 LSMASH_SET_LE32( &exdata
[8], nAvgBytesPerSec
);
1362 if( lsmash_check_codec_type_identical( sample_type
, QT_CODEC_TYPE_FULLMP3_AUDIO
)
1363 || lsmash_check_codec_type_identical( sample_type
, QT_CODEC_TYPE_MP3_AUDIO
) )
1365 /* MPEGLAYER3WAVEFORMAT.nBlockSize */
1366 uint32_t nSamplesPerSec
= LSMASH_GET_LE32( &exdata
[ 4] );
1367 uint16_t nFramesPerBlock
= LSMASH_GET_LE16( &exdata
[26] );
1368 uint16_t padding
= 0; /* FIXME? */
1369 uint16_t nBlockSize
= (144 * (avgBitrate
/ nSamplesPerSec
) + padding
) * nFramesPerBlock
;
1370 LSMASH_SET_LE16( &exdata
[24], nBlockSize
);
1373 else if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_DTSC_AUDIO
)
1374 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_DTSE_AUDIO
)
1375 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_DTSH_AUDIO
)
1376 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_DTSL_AUDIO
) )
1378 isom_audio_entry_t
*dts_audio
= (isom_audio_entry_t
*)sample_entry
;
1380 return LSMASH_ERR_INVALID_DATA
;
1381 isom_box_t
*ext
= isom_get_extension_box( &dts_audio
->extensions
, ISOM_BOX_TYPE_DDTS
);
1382 if( !(ext
&& (ext
->manager
& LSMASH_BINARY_CODED_BOX
) && ext
->binary
&& ext
->size
>= 28) )
1383 return LSMASH_ERR_INVALID_DATA
;
1384 if( (err
= isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
)) < 0 )
1386 if( !stbl
->stsz
->list
)
1387 maxBitrate
= avgBitrate
;
1388 uint8_t *exdata
= ext
->binary
+ 12;
1389 LSMASH_SET_BE32( &exdata
[0], maxBitrate
);
1390 LSMASH_SET_BE32( &exdata
[4], avgBitrate
);
1392 else if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_EC_3_AUDIO
) )
1394 isom_audio_entry_t
*eac3
= (isom_audio_entry_t
*)sample_entry
;
1396 return LSMASH_ERR_INVALID_DATA
;
1397 isom_box_t
*ext
= isom_get_extension_box( &eac3
->extensions
, ISOM_BOX_TYPE_DEC3
);
1398 if( !(ext
&& (ext
->manager
& LSMASH_BINARY_CODED_BOX
) && ext
->binary
&& ext
->size
>= 10) )
1399 return LSMASH_ERR_INVALID_DATA
;
1401 if( stbl
->stsz
->list
)
1403 if( (err
= isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
)) < 0 )
1405 bitrate
= maxBitrate
/ 1000; /* Use maximum bitrate if VBR. */
1408 bitrate
= stbl
->stsz
->sample_size
* (eac3
->samplerate
>> 16) / 192000; /* 192000 == 1536 * 1000 / 8 */
1409 uint8_t *exdata
= ext
->binary
+ 8;
1410 exdata
[0] = (bitrate
>> 5) & 0xff;
1411 exdata
[1] = (bitrate
& 0x1f) << 3;
1414 return sample_description_index
? 0 : LSMASH_ERR_INVALID_DATA
;
1417 static inline uint64_t isom_get_current_mp4time( void )
1419 return (uint64_t)time( NULL
) + ISOM_MAC_EPOCH_OFFSET
;
1422 static int isom_set_media_creation_time( isom_trak_t
*trak
, uint64_t current_mp4time
)
1425 || !trak
->mdia
->mdhd
)
1426 return LSMASH_ERR_NAMELESS
;
1427 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
1428 if( mdhd
->creation_time
== 0 )
1429 mdhd
->creation_time
= mdhd
->modification_time
= current_mp4time
;
1433 static int isom_set_track_creation_time( isom_trak_t
*trak
, uint64_t current_mp4time
)
1437 return LSMASH_ERR_NAMELESS
;
1438 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1439 if( tkhd
->creation_time
== 0 )
1440 tkhd
->creation_time
= tkhd
->modification_time
= current_mp4time
;
1441 return isom_set_media_creation_time( trak
, current_mp4time
);
1444 static int isom_set_movie_creation_time( lsmash_file_t
*file
)
1448 || !file
->moov
->mvhd
)
1449 return LSMASH_ERR_NAMELESS
;
1450 uint64_t current_mp4time
= isom_get_current_mp4time();
1451 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
1453 int err
= isom_set_track_creation_time( (isom_trak_t
*)entry
->data
, current_mp4time
);
1457 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
1458 if( mvhd
->creation_time
== 0 )
1459 mvhd
->creation_time
= mvhd
->modification_time
= current_mp4time
;
1463 int isom_setup_handler_reference( isom_hdlr_t
*hdlr
, uint32_t media_type
)
1465 isom_box_t
*parent
= hdlr
->parent
;
1466 lsmash_file_t
*file
= hdlr
->file
;
1467 if( !parent
|| !file
)
1468 return LSMASH_ERR_NAMELESS
;
1469 isom_mdia_t
*mdia
= lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_MDIA
) ? (isom_mdia_t
*)parent
: NULL
;
1470 isom_meta_t
*meta
= lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_META
) ? (isom_meta_t
*)parent
1471 : lsmash_check_box_type_identical( parent
->type
, QT_BOX_TYPE_META
) ? (isom_meta_t
*)parent
: NULL
;
1472 uint32_t type
= mdia
? (file
->qt_compatible
? QT_HANDLER_TYPE_MEDIA
: 0) : (meta
? 0 : QT_HANDLER_TYPE_DATA
);
1473 uint32_t subtype
= media_type
;
1474 hdlr
->componentType
= type
;
1475 hdlr
->componentSubtype
= subtype
;
1476 char *type_name
= NULL
;
1477 char *subtype_name
= NULL
;
1478 uint8_t type_name_length
= 0;
1479 uint8_t subtype_name_length
= 0;
1481 type_name
= "Media ";
1483 type_name
= "Metadata ";
1484 else /* if( minf ) */
1485 type_name
= "Data ";
1486 type_name_length
= strlen( type_name
);
1491 uint8_t subtype_name_length
;
1494 { ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
, "Sound ", 6 },
1495 { ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
, "Video ", 6 },
1496 { ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK
, "Hint ", 5 },
1497 { ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK
, "Metadata ", 9 },
1498 { ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK
, "Text ", 5 },
1499 { ISOM_META_HANDLER_TYPE_ITUNES_METADATA
, "iTunes ", 7 },
1500 { QT_REFERENCE_HANDLER_TYPE_ALIAS
, "Alias ", 6 },
1501 { QT_REFERENCE_HANDLER_TYPE_RESOURCE
, "Resource ", 9 },
1502 { QT_REFERENCE_HANDLER_TYPE_URL
, "URL ", 4 },
1503 { subtype
, "Unknown ", 8 }
1505 for( int i
= 0; subtype_table
[i
].subtype
; i
++ )
1506 if( subtype
== subtype_table
[i
].subtype
)
1508 subtype_name
= subtype_table
[i
].subtype_name
;
1509 subtype_name_length
= subtype_table
[i
].subtype_name_length
;
1512 uint32_t name_length
= 15 + subtype_name_length
+ type_name_length
+ file
->isom_compatible
+ file
->qt_compatible
;
1513 uint8_t *name
= lsmash_malloc( name_length
);
1515 return LSMASH_ERR_MEMORY_ALLOC
;
1516 if( file
->qt_compatible
)
1517 name
[0] = name_length
& 0xff;
1518 memcpy( name
+ file
->qt_compatible
, "L-SMASH ", 8 );
1519 memcpy( name
+ file
->qt_compatible
+ 8, subtype_name
, subtype_name_length
);
1520 memcpy( name
+ file
->qt_compatible
+ 8 + subtype_name_length
, type_name
, type_name_length
);
1521 memcpy( name
+ file
->qt_compatible
+ 8 + subtype_name_length
+ type_name_length
, "Handler", 7 );
1522 if( file
->isom_compatible
)
1523 name
[name_length
- 1] = 0;
1524 hdlr
->componentName
= name
;
1525 hdlr
->componentName_length
= name_length
;
1529 /*******************************
1531 *******************************/
1533 /*---- track manipulators ----*/
1535 void lsmash_delete_track( lsmash_root_t
*root
, uint32_t track_ID
)
1537 if( isom_check_initializer_present( root
) < 0
1538 || !root
->file
->initializer
->moov
)
1540 for( lsmash_entry_t
*entry
= root
->file
->initializer
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
1542 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
1546 if( trak
->tkhd
->track_ID
== track_ID
)
1548 isom_remove_box_by_itself( trak
);
1554 uint32_t lsmash_create_track( lsmash_root_t
*root
, lsmash_media_type media_type
)
1556 if( isom_check_initializer_present( root
) < 0 )
1558 lsmash_file_t
*file
= root
->file
;
1559 /* Don't allow to create a new track if the initial movie is already written. */
1560 if( (file
->fragment
&& file
->fragment
->movie
)
1561 || (file
->moov
&& (file
->moov
->manager
& LSMASH_WRITTEN_BOX
)) )
1563 isom_trak_t
*trak
= isom_add_trak( file
->moov
);
1566 || !trak
->file
->moov
1567 || !trak
->file
->moov
->mvhd
)
1569 if( !isom_add_tkhd( trak
)
1570 || !isom_add_mdia( trak
)
1571 || !isom_add_mdhd( trak
->mdia
)
1572 || !isom_add_minf( trak
->mdia
)
1573 || !isom_add_dinf( trak
->mdia
->minf
)
1574 || !isom_add_dref( trak
->mdia
->minf
->dinf
)
1575 || !isom_add_stbl( trak
->mdia
->minf
)
1576 || !isom_add_stsd( trak
->mdia
->minf
->stbl
)
1577 || !isom_add_stts( trak
->mdia
->minf
->stbl
)
1578 || !isom_add_stsc( trak
->mdia
->minf
->stbl
)
1579 || !isom_add_stco( trak
->mdia
->minf
->stbl
)
1580 || !isom_add_stsz( trak
->mdia
->minf
->stbl
) )
1582 if( !isom_add_hdlr( trak
->mdia
)
1583 || isom_setup_handler_reference( trak
->mdia
->hdlr
, media_type
) < 0 )
1585 if( file
->qt_compatible
)
1587 if( !isom_add_hdlr( trak
->mdia
->minf
)
1588 || isom_setup_handler_reference( trak
->mdia
->minf
->hdlr
, QT_REFERENCE_HANDLER_TYPE_URL
) < 0 )
1591 switch( media_type
)
1593 case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
:
1594 if( !isom_add_vmhd( trak
->mdia
->minf
) )
1596 trak
->mdia
->minf
->vmhd
->flags
= 0x000001;
1598 case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
:
1599 if( !isom_add_smhd( trak
->mdia
->minf
) )
1601 trak
->cache
->is_audio
= 1;
1603 case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK
:
1604 if( !isom_add_hmhd( trak
->mdia
->minf
) )
1607 case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK
:
1608 if( file
->qt_compatible
|| file
->itunes_movie
)
1610 if( !isom_add_gmhd( trak
->mdia
->minf
)
1611 || !isom_add_gmin( trak
->mdia
->minf
->gmhd
)
1612 || !isom_add_text( trak
->mdia
->minf
->gmhd
) )
1614 /* Default Text Media Information Box. */
1616 isom_text_t
*text
= trak
->mdia
->minf
->gmhd
->text
;
1617 text
->matrix
[0] = 0x00010000;
1618 text
->matrix
[4] = 0x00010000;
1619 text
->matrix
[8] = 0x40000000;
1623 goto fail
; /* We support only reference text media track for chapter yet. */
1626 if( !isom_add_nmhd( trak
->mdia
->minf
) )
1630 /* Default Track Header Box. */
1632 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1633 if( media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
)
1634 tkhd
->volume
= 0x0100;
1635 tkhd
->matrix
[0] = 0x00010000;
1636 tkhd
->matrix
[4] = 0x00010000;
1637 tkhd
->matrix
[8] = 0x40000000;
1638 tkhd
->duration
= 0xffffffff;
1639 tkhd
->track_ID
= trak
->file
->moov
->mvhd
->next_track_ID
++;
1641 trak
->mdia
->mdhd
->language
= file
->qt_compatible
? 0 : ISOM_LANGUAGE_CODE_UNDEFINED
;
1642 return trak
->tkhd
->track_ID
;
1644 isom_remove_box_by_itself( trak
);
1648 uint32_t lsmash_get_track_ID( lsmash_root_t
*root
, uint32_t track_number
)
1650 if( isom_check_initializer_present( root
) < 0
1651 || !root
->file
->initializer
->moov
)
1653 isom_trak_t
*trak
= (isom_trak_t
*)lsmash_get_entry_data( &root
->file
->initializer
->moov
->trak_list
, track_number
);
1657 return trak
->tkhd
->track_ID
;
1660 void lsmash_initialize_track_parameters( lsmash_track_parameters_t
*param
)
1662 memset( param
, 0, sizeof(lsmash_track_parameters_t
) );
1663 param
->audio_volume
= 0x0100;
1664 param
->matrix
[0] = 0x00010000;
1665 param
->matrix
[4] = 0x00010000;
1666 param
->matrix
[8] = 0x40000000;
1669 int lsmash_set_track_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_track_parameters_t
*param
)
1671 if( isom_check_initializer_present( root
) < 0 )
1672 return LSMASH_ERR_FUNCTION_PARAM
;
1673 lsmash_file_t
*file
= root
->file
;
1674 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1677 || !trak
->mdia
->hdlr
1678 || !file
->moov
->mvhd
)
1679 return LSMASH_ERR_NAMELESS
;
1680 /* Prepare Track Aperture Modes if required. */
1681 if( file
->qt_compatible
&& param
->aperture_modes
)
1683 if( !trak
->tapt
&& !isom_add_tapt( trak
) )
1684 return LSMASH_ERR_NAMELESS
;
1685 isom_tapt_t
*tapt
= trak
->tapt
;
1686 if( (!tapt
->clef
&& !isom_add_clef( tapt
))
1687 || (!tapt
->prof
&& !isom_add_prof( tapt
))
1688 || (!tapt
->enof
&& !isom_add_enof( tapt
)) )
1689 return LSMASH_ERR_NAMELESS
;
1692 isom_remove_box_by_itself( trak
->tapt
);
1693 /* Set up Track Header. */
1694 uint32_t media_type
= trak
->mdia
->hdlr
->componentSubtype
;
1695 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1696 tkhd
->flags
= param
->mode
;
1697 tkhd
->track_ID
= param
->track_ID
? param
->track_ID
: tkhd
->track_ID
;
1698 tkhd
->duration
= !trak
->edts
|| !trak
->edts
->elst
? param
->duration
: tkhd
->duration
;
1700 * alternate_group, layer, volume and matrix
1701 * According to 14496-14, these value are all set to defaut values in 14496-12.
1702 * And when a file is read as an MPEG-4 file, these values shall be ignored.
1703 * If a file complies with other specifications, then those fields may have non-default values
1704 * as required by those other specifications. */
1705 if( param
->alternate_group
)
1707 if( file
->qt_compatible
|| file
->itunes_movie
|| file
->max_3gpp_version
>= 4 )
1708 tkhd
->alternate_group
= param
->alternate_group
;
1711 tkhd
->alternate_group
= 0;
1712 lsmash_log( NULL
, LSMASH_LOG_WARNING
,
1713 "alternate_group is specified but not compatible with any of the brands. It won't be set.\n" );
1717 tkhd
->alternate_group
= 0;
1718 if( file
->qt_compatible
|| file
->itunes_movie
)
1720 tkhd
->layer
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->video_layer
: 0;
1721 tkhd
->volume
= media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
? param
->audio_volume
: 0;
1722 if( media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
)
1723 for( int i
= 0; i
< 9; i
++ )
1724 tkhd
->matrix
[i
] = param
->matrix
[i
];
1726 for( int i
= 0; i
< 9; i
++ )
1727 tkhd
->matrix
[i
] = 0;
1732 tkhd
->volume
= media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
? 0x0100 : 0;
1733 tkhd
->matrix
[0] = 0x00010000;
1734 tkhd
->matrix
[1] = 0;
1735 tkhd
->matrix
[2] = 0;
1736 tkhd
->matrix
[3] = 0;
1737 tkhd
->matrix
[4] = 0x00010000;
1738 tkhd
->matrix
[5] = 0;
1739 tkhd
->matrix
[6] = 0;
1740 tkhd
->matrix
[7] = 0;
1741 tkhd
->matrix
[8] = 0x40000000;
1743 /* visual presentation size */
1744 tkhd
->width
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->display_width
: 0;
1745 tkhd
->height
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->display_height
: 0;
1746 /* Update next_track_ID if needed. */
1747 if( file
->moov
->mvhd
->next_track_ID
<= tkhd
->track_ID
)
1748 file
->moov
->mvhd
->next_track_ID
= tkhd
->track_ID
+ 1;
1752 int lsmash_get_track_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_track_parameters_t
*param
)
1754 if( isom_check_initializer_present( root
) < 0 )
1755 return LSMASH_ERR_FUNCTION_PARAM
;
1756 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1758 return LSMASH_ERR_NAMELESS
;
1759 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1760 param
->mode
= tkhd
->flags
;
1761 param
->track_ID
= tkhd
->track_ID
;
1762 param
->duration
= tkhd
->duration
;
1763 param
->video_layer
= tkhd
->layer
;
1764 param
->alternate_group
= tkhd
->alternate_group
;
1765 param
->audio_volume
= tkhd
->volume
;
1766 for( int i
= 0; i
< 9; i
++ )
1767 param
->matrix
[i
] = tkhd
->matrix
[i
];
1768 param
->display_width
= tkhd
->width
;
1769 param
->display_height
= tkhd
->height
;
1770 param
->aperture_modes
= !!trak
->tapt
;
1774 static inline int check_dref_presence( isom_trak_t
*trak
)
1778 || !trak
->mdia
->minf
1779 || !trak
->mdia
->minf
->dinf
1780 || !trak
->mdia
->minf
->dinf
->dref
)
1781 return LSMASH_ERR_NAMELESS
;
1785 uint32_t lsmash_count_data_reference
1787 lsmash_root_t
*root
,
1791 if( isom_check_initializer_present( root
) < 0 )
1793 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1794 if( check_dref_presence( trak
) < 0 )
1796 return trak
->mdia
->minf
->dinf
->dref
->list
.entry_count
;
1799 int lsmash_get_data_reference
1801 lsmash_root_t
*root
,
1803 lsmash_data_reference_t
*data_ref
1806 if( isom_check_initializer_present( root
) < 0 || !data_ref
)
1807 return LSMASH_ERR_FUNCTION_PARAM
;
1808 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1809 if( check_dref_presence( trak
) < 0 )
1810 return LSMASH_ERR_NAMELESS
;
1811 isom_dref_entry_t
*url
= lsmash_get_entry_data( &trak
->mdia
->minf
->dinf
->dref
->list
, data_ref
->index
);
1813 return LSMASH_ERR_NAMELESS
;
1814 if( !(url
->flags
& 0x000001) && url
->location
)
1816 int length
= strlen( url
->location
);
1817 char *location
= lsmash_malloc( length
+ 1 );
1819 return LSMASH_ERR_MEMORY_ALLOC
;
1820 memcpy( location
, url
->location
, length
);
1821 location
[length
] = '\0';
1822 data_ref
->location
= location
;
1825 data_ref
->location
= NULL
;
1829 void lsmash_cleanup_data_reference
1831 lsmash_data_reference_t
*data_ref
1836 lsmash_freep( &data_ref
->location
);
1839 int lsmash_create_data_reference
1841 lsmash_root_t
*root
,
1843 lsmash_data_reference_t
*data_ref
,
1847 /* At present, we don't support external data references for movie fragments.
1848 * Note that, for external media data, default-base-is-moof is meaningless since relative
1849 * offsets from Movie Fragment Boxes make no sense.
1850 * In the future, the condition of !(file->flags & LSMASH_FILE_MODE_WRITE) may be removed
1851 * for the implementation which does not write actually and does reference read-only file. */
1852 if( !root
|| !file
|| file
->root
!= root
1853 || (!(file
->flags
& LSMASH_FILE_MODE_MEDIA
) && !(file
->flags
& LSMASH_FILE_MODE_INITIALIZATION
))
1854 || !(file
->flags
& LSMASH_FILE_MODE_WRITE
)
1855 || (root
->file
!= file
&& ((file
->flags
& LSMASH_FILE_MODE_FRAGMENTED
) || file
->fragment
))
1857 return LSMASH_ERR_FUNCTION_PARAM
;
1858 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
1859 if( check_dref_presence( trak
) < 0 )
1860 return LSMASH_ERR_NAMELESS
;
1861 isom_dref_entry_t
*url
= isom_add_dref_entry( trak
->mdia
->minf
->dinf
->dref
);
1863 return LSMASH_ERR_NAMELESS
;
1864 if( !data_ref
->location
|| root
->file
== file
)
1866 /* Media data is in the same file. */
1867 url
->flags
= 0x000001;
1868 url
->ref_file
= root
->file
;
1872 /* Set the location of the file. */
1873 int length
= strlen( data_ref
->location
);
1874 url
->location
= lsmash_malloc( length
+ 1 );
1875 if( !url
->location
)
1877 isom_remove_box_by_itself( url
);
1878 return LSMASH_ERR_MEMORY_ALLOC
;
1880 memcpy( url
->location
, data_ref
->location
, length
);
1881 url
->location
[length
] = '\0';
1882 url
->location_length
= length
+ 1;
1883 url
->ref_file
= file
;
1885 data_ref
->index
= trak
->mdia
->minf
->dinf
->dref
->list
.entry_count
;
1889 int lsmash_assign_data_reference
1891 lsmash_root_t
*root
,
1893 uint32_t data_ref_index
,
1897 if( isom_check_initializer_present( root
) < 0
1898 || !file
|| file
->root
!= root
1899 || !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
1900 || !(file
->flags
& LSMASH_FILE_MODE_READ
)
1901 || data_ref_index
== 0 )
1902 return LSMASH_ERR_FUNCTION_PARAM
;
1903 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1904 if( check_dref_presence( trak
) < 0 )
1905 return LSMASH_ERR_NAMELESS
;
1906 isom_dref_entry_t
*url
= (isom_dref_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->dinf
->dref
->list
, data_ref_index
);
1908 return LSMASH_ERR_NAMELESS
;
1909 if( !(url
->flags
& 0x000001) )
1910 /* Reference an external media data. */
1911 url
->ref_file
= file
;
1915 static int isom_set_media_handler_name( lsmash_file_t
*file
, uint32_t track_ID
, char *handler_name
)
1917 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1920 || !trak
->mdia
->hdlr
)
1921 return LSMASH_ERR_NAMELESS
;
1922 isom_hdlr_t
*hdlr
= trak
->mdia
->hdlr
;
1923 uint8_t *name
= NULL
;
1924 uint32_t name_length
= strlen( handler_name
) + file
->isom_compatible
+ file
->qt_compatible
;
1925 if( file
->qt_compatible
)
1926 name_length
= LSMASH_MIN( name_length
, 255 );
1927 if( name_length
> hdlr
->componentName_length
&& hdlr
->componentName
)
1928 name
= lsmash_realloc( hdlr
->componentName
, name_length
);
1929 else if( !hdlr
->componentName
)
1930 name
= lsmash_malloc( name_length
);
1932 name
= hdlr
->componentName
;
1934 return LSMASH_ERR_MEMORY_ALLOC
;
1935 if( file
->qt_compatible
)
1936 name
[0] = name_length
& 0xff;
1937 memcpy( name
+ file
->qt_compatible
, handler_name
, strlen( handler_name
) );
1938 if( file
->isom_compatible
)
1939 name
[name_length
- 1] = 0;
1940 hdlr
->componentName
= name
;
1941 hdlr
->componentName_length
= name_length
;
1945 static int isom_set_data_handler_name( lsmash_file_t
*file
, uint32_t track_ID
, char *handler_name
)
1947 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1950 || !trak
->mdia
->minf
1951 || !trak
->mdia
->minf
->hdlr
)
1952 return LSMASH_ERR_NAMELESS
;
1953 isom_hdlr_t
*hdlr
= trak
->mdia
->minf
->hdlr
;
1954 uint8_t *name
= NULL
;
1955 uint32_t name_length
= strlen( handler_name
) + file
->isom_compatible
+ file
->qt_compatible
;
1956 if( file
->qt_compatible
)
1957 name_length
= LSMASH_MIN( name_length
, 255 );
1958 if( name_length
> hdlr
->componentName_length
&& hdlr
->componentName
)
1959 name
= lsmash_realloc( hdlr
->componentName
, name_length
);
1960 else if( !hdlr
->componentName
)
1961 name
= lsmash_malloc( name_length
);
1963 name
= hdlr
->componentName
;
1965 return LSMASH_ERR_MEMORY_ALLOC
;
1966 if( file
->qt_compatible
)
1967 name
[0] = name_length
& 0xff;
1968 memcpy( name
+ file
->qt_compatible
, handler_name
, strlen( handler_name
) );
1969 if( file
->isom_compatible
)
1970 name
[name_length
- 1] = 0;
1971 hdlr
->componentName
= name
;
1972 hdlr
->componentName_length
= name_length
;
1976 uint32_t lsmash_get_media_timescale( lsmash_root_t
*root
, uint32_t track_ID
)
1978 if( isom_check_initializer_present( root
) < 0 )
1980 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1983 || !trak
->mdia
->mdhd
)
1985 return trak
->mdia
->mdhd
->timescale
;
1988 uint64_t lsmash_get_media_duration( lsmash_root_t
*root
, uint32_t track_ID
)
1990 if( isom_check_initializer_present( root
) < 0 )
1992 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1995 || !trak
->mdia
->mdhd
)
1997 return trak
->mdia
->mdhd
->duration
;
2000 uint64_t lsmash_get_track_duration( lsmash_root_t
*root
, uint32_t track_ID
)
2002 if( isom_check_initializer_present( root
) < 0 )
2004 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2008 return trak
->tkhd
->duration
;
2011 uint32_t lsmash_get_last_sample_delta( lsmash_root_t
*root
, uint32_t track_ID
)
2013 if( isom_check_initializer_present( root
) < 0 )
2015 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
2018 || !trak
->mdia
->minf
2019 || !trak
->mdia
->minf
->stbl
2020 || !trak
->mdia
->minf
->stbl
->stts
2021 || !trak
->mdia
->minf
->stbl
->stts
->list
2022 || !trak
->mdia
->minf
->stbl
->stts
->list
->tail
2023 || !trak
->mdia
->minf
->stbl
->stts
->list
->tail
->data
)
2025 return ((isom_stts_entry_t
*)trak
->mdia
->minf
->stbl
->stts
->list
->tail
->data
)->sample_delta
;
2028 uint32_t lsmash_get_start_time_offset( lsmash_root_t
*root
, uint32_t track_ID
)
2030 if( isom_check_initializer_present( root
) < 0 )
2032 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
2035 || !trak
->mdia
->minf
2036 || !trak
->mdia
->minf
->stbl
2037 || !trak
->mdia
->minf
->stbl
->ctts
2038 || !trak
->mdia
->minf
->stbl
->ctts
->list
2039 || !trak
->mdia
->minf
->stbl
->ctts
->list
->head
2040 || !trak
->mdia
->minf
->stbl
->ctts
->list
->head
->data
)
2042 return ((isom_ctts_entry_t
*)trak
->mdia
->minf
->stbl
->ctts
->list
->head
->data
)->sample_offset
;
2045 uint32_t lsmash_get_composition_to_decode_shift( lsmash_root_t
*root
, uint32_t track_ID
)
2047 if( isom_check_initializer_present( root
) < 0 )
2049 lsmash_file_t
*file
= root
->file
->initializer
;
2050 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2053 || !trak
->mdia
->minf
2054 || !trak
->mdia
->minf
->stbl
)
2056 uint32_t sample_count
= isom_get_sample_count( trak
);
2057 if( sample_count
== 0 )
2059 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2060 if( !stbl
->stts
|| !stbl
->stts
->list
2061 || !stbl
->ctts
|| !stbl
->ctts
->list
)
2063 if( !(file
->max_isom_version
>= 4 && stbl
->ctts
->version
== 1) && !file
->qt_compatible
)
2064 return 0; /* This movie shall not have composition to decode timeline shift. */
2065 lsmash_entry_t
*stts_entry
= stbl
->stts
->list
->head
;
2066 lsmash_entry_t
*ctts_entry
= stbl
->ctts
->list
->head
;
2067 if( !stts_entry
|| !ctts_entry
)
2071 uint32_t ctd_shift
= 0;
2074 for( uint32_t k
= 0; k
< sample_count
; k
++ )
2076 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
2077 isom_ctts_entry_t
*ctts_data
= (isom_ctts_entry_t
*)ctts_entry
->data
;
2078 if( !stts_data
|| !ctts_data
)
2080 cts
= dts
+ (int32_t)ctts_data
->sample_offset
;
2081 if( dts
> cts
+ ctd_shift
)
2082 ctd_shift
= dts
- cts
;
2083 dts
+= stts_data
->sample_delta
;
2084 if( ++i
== stts_data
->sample_count
)
2086 stts_entry
= stts_entry
->next
;
2091 if( ++j
== ctts_data
->sample_count
)
2093 ctts_entry
= ctts_entry
->next
;
2102 uint16_t lsmash_pack_iso_language( char *iso_language
)
2104 if( !iso_language
|| strlen( iso_language
) != 3 )
2106 return (uint16_t)LSMASH_PACK_ISO_LANGUAGE( iso_language
[0], iso_language
[1], iso_language
[2] );
2109 static int isom_iso2mac_language( uint16_t ISO_language
, uint16_t *MAC_language
)
2111 assert( MAC_language
);
2113 for( ; isom_languages
[i
].iso_name
; i
++ )
2114 if( ISO_language
== isom_languages
[i
].iso_name
)
2116 if( !isom_languages
[i
].iso_name
)
2117 return LSMASH_ERR_NAMELESS
;
2118 *MAC_language
= isom_languages
[i
].mac_value
;
2122 static int isom_mac2iso_language( uint16_t MAC_language
, uint16_t *ISO_language
)
2124 assert( ISO_language
);
2126 for( ; isom_languages
[i
].iso_name
; i
++ )
2127 if( MAC_language
== isom_languages
[i
].mac_value
)
2129 *ISO_language
= isom_languages
[i
].iso_name
? isom_languages
[i
].iso_name
: ISOM_LANGUAGE_CODE_UNDEFINED
;
2133 static int isom_set_media_language( lsmash_file_t
*file
, uint32_t track_ID
, uint16_t ISO_language
, uint16_t MAC_language
)
2135 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2138 || !trak
->mdia
->mdhd
)
2139 return LSMASH_ERR_NAMELESS
;
2140 uint16_t language
= 0;
2141 if( file
->isom_compatible
)
2144 language
= ISO_language
;
2145 else if( MAC_language
)
2147 int err
= isom_mac2iso_language( MAC_language
, &language
);
2152 language
= ISOM_LANGUAGE_CODE_UNDEFINED
;
2154 else if( file
->qt_compatible
)
2158 int err
= isom_iso2mac_language( ISO_language
, &language
);
2163 language
= MAC_language
;
2166 return LSMASH_ERR_INVALID_DATA
;
2167 trak
->mdia
->mdhd
->language
= language
;
2171 int isom_add_sample_grouping( isom_box_t
*parent
, isom_grouping_type grouping_type
)
2175 if( NULL
== (sgpd
= isom_add_sgpd( parent
))
2176 || NULL
== (sbgp
= isom_add_sbgp( parent
)) )
2177 return LSMASH_ERR_NAMELESS
;
2178 sbgp
->grouping_type
= grouping_type
;
2179 sgpd
->grouping_type
= grouping_type
;
2180 sgpd
->version
= 1; /* We use version 1 for Sample Group Description Box because it is recommended in the spec. */
2181 switch( grouping_type
)
2183 case ISOM_GROUP_TYPE_RAP
:
2184 sgpd
->default_length
= 1;
2186 case ISOM_GROUP_TYPE_ROLL
:
2187 case ISOM_GROUP_TYPE_PROL
:
2188 sgpd
->default_length
= 2;
2191 /* We don't consider other grouping types currently. */
2197 static int isom_create_sample_grouping( isom_trak_t
*trak
, isom_grouping_type grouping_type
)
2199 lsmash_file_t
*file
= trak
->file
;
2200 switch( grouping_type
)
2202 case ISOM_GROUP_TYPE_RAP
:
2203 assert( file
->max_isom_version
>= 6 );
2205 case ISOM_GROUP_TYPE_ROLL
:
2206 case ISOM_GROUP_TYPE_PROL
:
2207 assert( file
->avc_extensions
|| file
->qt_compatible
);
2213 int err
= isom_add_sample_grouping( (isom_box_t
*)trak
->mdia
->minf
->stbl
, grouping_type
);
2216 if( trak
->cache
->fragment
&& file
->max_isom_version
>= 6 )
2217 switch( grouping_type
)
2219 case ISOM_GROUP_TYPE_RAP
:
2220 trak
->cache
->fragment
->rap_grouping
= 1;
2222 case ISOM_GROUP_TYPE_ROLL
:
2223 case ISOM_GROUP_TYPE_PROL
:
2224 trak
->cache
->fragment
->roll_grouping
= 1;
2227 /* We don't consider other grouping types currently. */
2233 void lsmash_initialize_media_parameters( lsmash_media_parameters_t
*param
)
2235 memset( param
, 0, sizeof(lsmash_media_parameters_t
) );
2236 param
->timescale
= 1;
2239 int lsmash_set_media_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_media_parameters_t
*param
)
2241 if( isom_check_initializer_present( root
) < 0 )
2242 return LSMASH_ERR_FUNCTION_PARAM
;
2243 lsmash_file_t
*file
= root
->file
;
2244 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2247 || !trak
->mdia
->mdhd
2248 || !trak
->mdia
->minf
2249 || !trak
->mdia
->minf
->stbl
)
2250 return LSMASH_ERR_NAMELESS
;
2251 trak
->mdia
->mdhd
->timescale
= param
->timescale
;
2252 int err
= isom_set_media_language( file
, track_ID
, param
->ISO_language
, param
->MAC_language
);
2255 if( param
->media_handler_name
2256 && (err
= isom_set_media_handler_name( file
, track_ID
, param
->media_handler_name
)) < 0 )
2258 if( file
->qt_compatible
&& param
->data_handler_name
2259 && (err
= isom_set_data_handler_name( file
, track_ID
, param
->data_handler_name
)) < 0 )
2261 if( (file
->avc_extensions
|| file
->qt_compatible
) && param
->roll_grouping
2262 && (err
= isom_create_sample_grouping( trak
, ISOM_GROUP_TYPE_ROLL
)) < 0 )
2264 if( (file
->max_isom_version
>= 6) && param
->rap_grouping
2265 && (err
= isom_create_sample_grouping( trak
, ISOM_GROUP_TYPE_RAP
)) < 0 )
2270 int lsmash_get_media_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_media_parameters_t
*param
)
2272 if( isom_check_initializer_present( root
) < 0 )
2273 return LSMASH_ERR_FUNCTION_PARAM
;
2274 lsmash_file_t
*file
= root
->file
->initializer
;
2275 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2278 || !trak
->mdia
->mdhd
2279 || !trak
->mdia
->hdlr
2280 || !trak
->mdia
->minf
2281 || !trak
->mdia
->minf
->stbl
)
2282 return LSMASH_ERR_NAMELESS
;
2283 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
2284 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2285 param
->timescale
= mdhd
->timescale
;
2286 param
->handler_type
= trak
->mdia
->hdlr
->componentSubtype
;
2287 param
->duration
= mdhd
->duration
;
2288 /* Whether sample grouping present. */
2292 sbgp
= isom_get_sample_to_group ( stbl
, ISOM_GROUP_TYPE_RAP
);
2293 sgpd
= isom_get_sample_group_description( stbl
, ISOM_GROUP_TYPE_RAP
);
2294 param
->rap_grouping
= sbgp
&& sgpd
;
2295 sbgp
= isom_get_roll_recovery_sample_to_group ( &stbl
->sbgp_list
);
2296 sgpd
= isom_get_roll_recovery_sample_group_description( &stbl
->sgpd_list
);
2297 param
->roll_grouping
= sbgp
&& sgpd
;
2299 /* Get media language. */
2300 if( mdhd
->language
>= 0x800 )
2302 param
->MAC_language
= 0;
2303 param
->ISO_language
= mdhd
->language
;
2307 param
->MAC_language
= mdhd
->language
;
2308 param
->ISO_language
= 0;
2310 /* Get handler name(s). */
2311 isom_hdlr_t
*hdlr
= trak
->mdia
->hdlr
;
2312 int length
= LSMASH_MIN( 255, hdlr
->componentName_length
);
2315 memcpy( param
->media_handler_name_shadow
, hdlr
->componentName
+ file
->qt_compatible
, length
);
2316 param
->media_handler_name_shadow
[length
- 2 + file
->isom_compatible
+ file
->qt_compatible
] = '\0';
2317 param
->media_handler_name
= param
->media_handler_name_shadow
;
2321 param
->media_handler_name
= NULL
;
2322 memset( param
->media_handler_name_shadow
, 0, sizeof(param
->media_handler_name_shadow
) );
2324 if( trak
->mdia
->minf
->hdlr
)
2326 hdlr
= trak
->mdia
->minf
->hdlr
;
2327 length
= LSMASH_MIN( 255, hdlr
->componentName_length
);
2330 memcpy( param
->data_handler_name_shadow
, hdlr
->componentName
+ file
->qt_compatible
, length
);
2331 param
->data_handler_name_shadow
[length
- 2 + file
->isom_compatible
+ file
->qt_compatible
] = '\0';
2332 param
->data_handler_name
= param
->data_handler_name_shadow
;
2336 param
->data_handler_name
= NULL
;
2337 memset( param
->data_handler_name_shadow
, 0, sizeof(param
->data_handler_name_shadow
) );
2342 param
->data_handler_name
= NULL
;
2343 memset( param
->data_handler_name_shadow
, 0, sizeof(param
->data_handler_name_shadow
) );
2348 /*---- movie manipulators ----*/
2350 void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t
*param
)
2352 memset( param
, 0, sizeof(lsmash_movie_parameters_t
) );
2353 param
->timescale
= 600;
2354 param
->playback_rate
= 0x00010000;
2355 param
->playback_volume
= 0x0100;
2358 int lsmash_set_movie_parameters( lsmash_root_t
*root
, lsmash_movie_parameters_t
*param
)
2361 return LSMASH_ERR_FUNCTION_PARAM
;
2362 lsmash_file_t
*file
= root
->file
;
2365 || !file
->moov
->mvhd
)
2366 return LSMASH_ERR_NAMELESS
;
2367 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
2368 mvhd
->timescale
= param
->timescale
;
2369 if( file
->qt_compatible
|| file
->itunes_movie
)
2371 mvhd
->rate
= param
->playback_rate
;
2372 mvhd
->volume
= param
->playback_volume
;
2373 mvhd
->previewTime
= param
->preview_time
;
2374 mvhd
->previewDuration
= param
->preview_duration
;
2375 mvhd
->posterTime
= param
->poster_time
;
2379 mvhd
->rate
= 0x00010000;
2380 mvhd
->volume
= 0x0100;
2381 mvhd
->previewTime
= 0;
2382 mvhd
->previewDuration
= 0;
2383 mvhd
->posterTime
= 0;
2388 int lsmash_get_movie_parameters( lsmash_root_t
*root
, lsmash_movie_parameters_t
*param
)
2390 if( isom_check_initializer_present( root
) < 0 )
2391 return LSMASH_ERR_FUNCTION_PARAM
;
2392 lsmash_file_t
*file
= root
->file
->initializer
;
2394 || !file
->moov
->mvhd
)
2395 return LSMASH_ERR_NAMELESS
;
2396 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
2397 param
->timescale
= mvhd
->timescale
;
2398 param
->duration
= mvhd
->duration
;
2399 param
->playback_rate
= mvhd
->rate
;
2400 param
->playback_volume
= mvhd
->volume
;
2401 param
->preview_time
= mvhd
->previewTime
;
2402 param
->preview_duration
= mvhd
->previewDuration
;
2403 param
->poster_time
= mvhd
->posterTime
;
2404 param
->number_of_tracks
= file
->moov
->trak_list
.entry_count
;
2408 uint32_t lsmash_get_movie_timescale( lsmash_root_t
*root
)
2410 if( isom_check_initializer_present( root
) < 0
2411 || !root
->file
->initializer
->moov
2412 || !root
->file
->initializer
->moov
->mvhd
)
2414 return root
->file
->initializer
->moov
->mvhd
->timescale
;
2417 static int isom_scan_trak_profileLevelIndication
2420 mp4a_audioProfileLevelIndication
*audio_pli
,
2421 mp4sys_visualProfileLevelIndication
*visual_pli
2426 || !trak
->mdia
->minf
2427 || !trak
->mdia
->minf
->stbl
)
2428 return LSMASH_ERR_INVALID_DATA
;
2429 isom_stsd_t
*stsd
= trak
->mdia
->minf
->stbl
->stsd
;
2431 || !stsd
->list
.head
)
2432 return LSMASH_ERR_INVALID_DATA
;
2433 for( lsmash_entry_t
*entry
= stsd
->list
.head
; entry
; entry
= entry
->next
)
2435 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)entry
->data
;
2437 return LSMASH_ERR_INVALID_DATA
;
2438 lsmash_codec_type_t sample_type
= sample_entry
->type
;
2439 if( trak
->mdia
->minf
->vmhd
)
2441 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC1_VIDEO
)
2442 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC2_VIDEO
)
2443 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC3_VIDEO
)
2444 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC4_VIDEO
)
2445 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVCP_VIDEO
)
2446 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_SVC1_VIDEO
)
2447 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MVC1_VIDEO
)
2448 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MVC2_VIDEO
) )
2450 /* FIXME: Do we have to arbitrate like audio? */
2451 if( *visual_pli
== MP4SYS_VISUAL_PLI_NONE_REQUIRED
)
2452 *visual_pli
= MP4SYS_VISUAL_PLI_H264_AVC
;
2455 *visual_pli
= MP4SYS_VISUAL_PLI_NOT_SPECIFIED
;
2457 else if( trak
->mdia
->minf
->smhd
)
2459 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4A_AUDIO
) )
2461 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)sample_entry
;
2462 isom_esds_t
*esds
= (isom_esds_t
*)isom_get_extension_box_format( &audio
->extensions
, ISOM_BOX_TYPE_ESDS
);
2463 if( !esds
|| !esds
->ES
)
2464 return LSMASH_ERR_INVALID_DATA
;
2465 lsmash_audio_summary_t
*summary
= (lsmash_audio_summary_t
*)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO
);
2468 if( mp4sys_setup_summary_from_DecoderSpecificInfo( summary
, esds
->ES
) < 0 )
2469 *audio_pli
= MP4A_AUDIO_PLI_NOT_SPECIFIED
;
2471 *audio_pli
= mp4a_max_audioProfileLevelIndication( *audio_pli
, mp4a_get_audioProfileLevelIndication( summary
) );
2472 lsmash_cleanup_summary( (lsmash_summary_t
*)summary
);
2475 /* NOTE: Audio CODECs other than 'mp4a' does not have appropriate pli. */
2476 *audio_pli
= MP4A_AUDIO_PLI_NOT_SPECIFIED
;
2479 ; /* FIXME: Do we have to set OD_profileLevelIndication? */
2484 int isom_setup_iods( isom_moov_t
*moov
)
2486 if( !moov
->iods
&& !isom_add_iods( moov
) )
2487 return LSMASH_ERR_NAMELESS
;
2488 isom_iods_t
*iods
= moov
->iods
;
2489 int err
= LSMASH_ERR_NAMELESS
;
2490 iods
->OD
= mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */
2493 mp4a_audioProfileLevelIndication audio_pli
= MP4A_AUDIO_PLI_NONE_REQUIRED
;
2494 mp4sys_visualProfileLevelIndication visual_pli
= MP4SYS_VISUAL_PLI_NONE_REQUIRED
;
2495 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
2497 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2501 if( (err
= isom_scan_trak_profileLevelIndication( trak
, &audio_pli
, &visual_pli
)) < 0 )
2503 if( (err
= mp4sys_create_ES_ID_Inc( iods
->OD
, trak
->tkhd
->track_ID
)) < 0 )
2506 if( (err
= mp4sys_to_InitialObjectDescriptor( iods
->OD
,
2507 0, /* FIXME: I'm not quite sure what the spec says. */
2508 MP4SYS_OD_PLI_NONE_REQUIRED
, MP4SYS_SCENE_PLI_NONE_REQUIRED
,
2509 audio_pli
, visual_pli
,
2510 MP4SYS_GRAPHICS_PLI_NONE_REQUIRED
)) < 0 )
2514 isom_remove_box_by_itself( iods
);
2518 int lsmash_create_object_descriptor( lsmash_root_t
*root
)
2520 if( isom_check_initializer_present( root
) < 0 )
2521 return LSMASH_ERR_FUNCTION_PARAM
;
2522 lsmash_file_t
*file
= root
->file
;
2523 /* Return error if this file is not compatible with MP4 file format. */
2524 if( !file
->mp4_version1
2525 && !file
->mp4_version2
)
2526 return LSMASH_ERR_FUNCTION_PARAM
;
2527 return isom_setup_iods( file
->moov
);
2530 /*---- finishing functions ----*/
2532 int isom_complement_data_reference( isom_minf_t
*minf
)
2535 || !minf
->dinf
->dref
)
2536 return LSMASH_ERR_INVALID_DATA
;
2537 /* Complement data referece if absent. */
2538 if( !minf
->dinf
->dref
->list
.head
)
2540 isom_dref_entry_t
*url
= isom_add_dref_entry( minf
->dinf
->dref
);
2542 return LSMASH_ERR_NAMELESS
;
2543 url
->flags
= 0x000001; /* Media data is in the same file. */
2548 static lsmash_file_t
*isom_get_written_media_file
2551 uint32_t sample_description_index
2554 isom_minf_t
*minf
= trak
->mdia
->minf
;
2555 isom_sample_entry_t
*description
= (isom_sample_entry_t
*)lsmash_get_entry_data( &minf
->stbl
->stsd
->list
, sample_description_index
);
2556 isom_dref_entry_t
*dref_entry
= (isom_dref_entry_t
*)lsmash_get_entry_data( &minf
->dinf
->dref
->list
, description
->data_reference_index
);
2557 lsmash_file_t
*file
= (!dref_entry
|| !dref_entry
->ref_file
) ? trak
->file
: dref_entry
->ref_file
;
2558 if( !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
2559 || !(file
->flags
& LSMASH_FILE_MODE_WRITE
) )
2564 int isom_check_large_offset_requirement
2570 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; )
2572 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2573 isom_stco_t
*stco
= trak
->mdia
->minf
->stbl
->stco
;
2574 if( !stco
->list
->tail
/* no samples */
2575 || stco
->large_presentation
2576 || (((isom_stco_entry_t
*)stco
->list
->tail
->data
)->chunk_offset
+ moov
->size
+ meta_size
) <= UINT32_MAX
)
2578 entry
= entry
->next
;
2579 continue; /* no need to convert stco into co64 */
2581 /* stco->co64 conversion */
2582 int err
= isom_convert_stco_to_co64( trak
->mdia
->minf
->stbl
);
2585 if( isom_update_box_size( moov
) == 0 )
2586 return LSMASH_ERR_INVALID_DATA
;
2587 entry
= moov
->trak_list
.head
; /* whenever any conversion, re-check all traks */
2592 void isom_add_preceding_box_size
2595 uint64_t preceding_size
2598 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
2600 /* Apply to the chunks in the same file. */
2601 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2602 isom_stsc_t
*stsc
= trak
->mdia
->minf
->stbl
->stsc
;
2603 isom_stco_t
*stco
= trak
->mdia
->minf
->stbl
->stco
;
2604 lsmash_entry_t
*stsc_entry
= stsc
->list
->head
;
2605 isom_stsc_entry_t
*stsc_data
= stsc_entry
? (isom_stsc_entry_t
*)stsc_entry
->data
: NULL
;
2606 uint32_t chunk_number
= 1;
2607 for( lsmash_entry_t
*stco_entry
= stco
->list
->head
; stco_entry
; )
2610 && stsc_data
->first_chunk
== chunk_number
)
2612 lsmash_file_t
*ref_file
= isom_get_written_media_file( trak
, stsc_data
->sample_description_index
);
2613 stsc_entry
= stsc_entry
->next
;
2614 stsc_data
= stsc_entry
? (isom_stsc_entry_t
*)stsc_entry
->data
: NULL
;
2615 if( ref_file
!= trak
->file
)
2617 /* The chunks are not contained in the same file. Skip applying the offset.
2618 * If no more stsc entries, the rest of the chunks is not contained in the same file. */
2619 if( !stsc_entry
|| !stsc_data
)
2621 while( stco_entry
&& chunk_number
< stsc_data
->first_chunk
)
2623 stco_entry
= stco_entry
->next
;
2629 if( stco
->large_presentation
)
2630 ((isom_co64_entry_t
*)stco_entry
->data
)->chunk_offset
+= preceding_size
;
2632 ((isom_stco_entry_t
*)stco_entry
->data
)->chunk_offset
+= preceding_size
;
2633 stco_entry
= stco_entry
->next
;
2639 int isom_establish_movie( lsmash_file_t
*file
)
2641 assert( file
== file
->initializer
);
2643 if( (err
= isom_check_mandatory_boxes( file
)) < 0
2644 || (err
= isom_set_movie_creation_time( file
)) < 0 )
2646 if( isom_update_box_size( file
->moov
) == 0 )
2647 return LSMASH_ERR_INVALID_DATA
;
2651 int lsmash_finish_movie
2653 lsmash_root_t
*root
,
2654 lsmash_adhoc_remux_t
*remux
2657 if( isom_check_initializer_present( root
) < 0 )
2658 return LSMASH_ERR_FUNCTION_PARAM
;
2659 lsmash_file_t
*file
= root
->file
;
2662 || !file
->initializer
->moov
)
2663 return LSMASH_ERR_INVALID_DATA
;
2664 if( file
->fragment
)
2665 return isom_finish_final_fragment_movie( file
, remux
);
2666 if( file
!= file
->initializer
)
2667 return LSMASH_ERR_INVALID_DATA
;
2669 isom_moov_t
*moov
= file
->moov
;
2670 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
2672 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2677 || !trak
->mdia
->minf
2678 || !trak
->mdia
->minf
->stbl
2679 || !trak
->mdia
->minf
->stbl
->stco
2680 || !trak
->mdia
->minf
->stbl
->stco
->list
2681 || !trak
->mdia
->minf
->stbl
->stco
->list
->tail
)
2682 return LSMASH_ERR_INVALID_DATA
;
2683 if( (err
= isom_complement_data_reference( trak
->mdia
->minf
)) < 0 )
2685 uint32_t track_ID
= trak
->tkhd
->track_ID
;
2686 uint32_t related_track_ID
= trak
->related_track_ID
;
2687 /* Disable the track if the track is a track reference chapter. */
2688 if( trak
->is_chapter
)
2689 trak
->tkhd
->flags
&= ~ISOM_TRACK_ENABLED
;
2690 if( trak
->is_chapter
&& related_track_ID
)
2692 /* In order that the track duration of the chapter track doesn't exceed that of the related track. */
2694 edit
.duration
= LSMASH_MIN( trak
->tkhd
->duration
, lsmash_get_track_duration( root
, related_track_ID
) );
2695 edit
.start_time
= 0;
2696 edit
.rate
= ISOM_EDIT_MODE_NORMAL
;
2697 if( (err
= lsmash_create_explicit_timeline_map( root
, track_ID
, edit
)) < 0 )
2700 /* Add stss box if any samples aren't sync sample. */
2701 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2702 if( !trak
->cache
->all_sync
&& !stbl
->stss
&& !isom_add_stss( stbl
) )
2703 return LSMASH_ERR_NAMELESS
;
2704 if( (err
= isom_update_tkhd_duration( trak
)) < 0
2705 || (err
= isom_update_bitrate_description( trak
->mdia
)) < 0 )
2708 if( file
->mp4_version1
== 1 && (err
= isom_setup_iods( moov
)) < 0 )
2710 if( (err
= isom_establish_movie( file
)) < 0 )
2712 /* Write the size of Media Data Box here. */
2713 lsmash_bs_t
*bs
= file
->bs
;
2714 file
->mdat
->manager
&= ~LSMASH_INCOMPLETE_BOX
;
2715 if( (err
= isom_write_box( bs
, (isom_box_t
*)file
->mdat
)) < 0 )
2717 /* Write the Movie Box and a Meta Box if no optimization for progressive download. */
2718 uint64_t meta_size
= file
->meta
? file
->meta
->size
: 0;
2721 if( (err
= isom_write_box( bs
, (isom_box_t
*)file
->moov
)) < 0
2722 || (err
= isom_write_box( bs
, (isom_box_t
*)file
->meta
)) < 0 )
2724 file
->size
+= moov
->size
+ meta_size
;
2727 /* stco->co64 conversion, depending on last chunk's offset */
2728 if( (err
= isom_check_large_offset_requirement( moov
, meta_size
)) < 0 )
2730 /* now the amount of offset is fixed. */
2731 uint64_t mtf_size
= moov
->size
+ meta_size
; /* sum of size of boxes moved to front */
2732 /* buffer size must be at least mtf_size * 2 */
2733 remux
->buffer_size
= LSMASH_MAX( remux
->buffer_size
, mtf_size
* 2 );
2734 /* Split to 2 buffers. */
2735 uint8_t *buf
[2] = { NULL
, NULL
};
2736 if( (buf
[0] = (uint8_t*)lsmash_malloc( remux
->buffer_size
)) == NULL
)
2737 return LSMASH_ERR_MEMORY_ALLOC
; /* NOTE: I think we still can fallback to "return isom_write_moov();" here. */
2738 size_t size
= remux
->buffer_size
/ 2;
2739 buf
[1] = buf
[0] + size
;
2740 /* Now, the amount of the offset is fixed. apply it to stco/co64 */
2741 isom_add_preceding_box_size( moov
, mtf_size
);
2742 /* Backup starting area of mdat and write moov + meta there instead. */
2743 isom_mdat_t
*mdat
= file
->mdat
;
2744 uint64_t total
= file
->size
+ mtf_size
;
2745 uint64_t placeholder_pos
= file
->free
? file
->free
->pos
: mdat
->pos
;
2746 if( (err
= lsmash_bs_write_seek( bs
, placeholder_pos
, SEEK_SET
)) < 0 )
2748 size_t read_num
= size
;
2749 lsmash_bs_read_data( bs
, buf
[0], &read_num
);
2750 uint64_t read_pos
= bs
->offset
;
2751 /* Write moov + meta there instead. */
2752 if( (err
= lsmash_bs_write_seek( bs
, placeholder_pos
, SEEK_SET
)) < 0
2753 || (err
= isom_write_box( bs
, (isom_box_t
*)file
->moov
)) < 0
2754 || (err
= isom_write_box( bs
, (isom_box_t
*)file
->meta
)) < 0 )
2756 uint64_t write_pos
= bs
->offset
;
2757 /* Update the positions */
2758 mdat
->pos
+= mtf_size
;
2760 file
->free
->pos
+= mtf_size
;
2761 /* Move Media Data Box. */
2762 if( (err
= isom_rearrange_data( file
, remux
, buf
, read_num
, size
, read_pos
, write_pos
, total
)) < 0 )
2764 file
->size
+= mtf_size
;
2765 lsmash_free( buf
[0] );
2768 lsmash_free( buf
[0] );
2772 int lsmash_set_last_sample_delta( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t sample_delta
)
2774 if( isom_check_initializer_present( root
) < 0 || track_ID
== 0 )
2775 return LSMASH_ERR_FUNCTION_PARAM
;
2776 lsmash_file_t
*file
= root
->file
;
2778 && file
->fragment
->movie
)
2780 isom_traf_t
*traf
= isom_get_traf( file
->fragment
->movie
, track_ID
);
2784 return LSMASH_ERR_NAMELESS
;
2785 return isom_set_fragment_last_duration( traf
, sample_delta
);
2787 if( file
!= file
->initializer
)
2788 return LSMASH_ERR_INVALID_DATA
;
2789 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2793 || !trak
->mdia
->mdhd
2794 || !trak
->mdia
->minf
2795 || !trak
->mdia
->minf
->stbl
2796 || !trak
->mdia
->minf
->stbl
->stsd
2797 || !trak
->mdia
->minf
->stbl
->stsz
2798 || !trak
->mdia
->minf
->stbl
->stts
2799 || !trak
->mdia
->minf
->stbl
->stts
->list
)
2800 return LSMASH_ERR_NAMELESS
;
2801 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2802 isom_stts_t
*stts
= stbl
->stts
;
2803 uint32_t sample_count
= isom_get_sample_count( trak
);
2805 if( !stts
->list
->tail
)
2808 return 0; /* no samples */
2809 if( sample_count
> 1 )
2810 return LSMASH_ERR_INVALID_DATA
; /* irregular sample_count */
2811 /* Set the duration of the first sample.
2812 * This duration is also the duration of the last sample. */
2813 if( (err
= isom_add_stts_entry( stbl
, sample_delta
)) < 0 )
2815 return lsmash_update_track_duration( root
, track_ID
, 0 );
2818 for( lsmash_entry_t
*entry
= stts
->list
->head
; entry
; entry
= entry
->next
)
2819 i
+= ((isom_stts_entry_t
*)entry
->data
)->sample_count
;
2820 if( sample_count
< i
)
2821 return LSMASH_ERR_INVALID_DATA
;
2822 int no_last
= (sample_count
> i
);
2823 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)stts
->list
->tail
->data
;
2824 if( !last_stts_data
)
2825 return LSMASH_ERR_INVALID_DATA
;
2826 /* Consider QuikcTime fixed compression audio. */
2827 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
,
2828 trak
->cache
->chunk
.sample_description_index
);
2830 return LSMASH_ERR_INVALID_DATA
;
2831 if( (audio
->manager
& LSMASH_AUDIO_DESCRIPTION
)
2832 && (audio
->manager
& LSMASH_QTFF_BASE
)
2833 && (audio
->version
== 1)
2834 && (audio
->compression_ID
!= QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION
) )
2836 if( audio
->samplesPerPacket
== 0 )
2837 return LSMASH_ERR_INVALID_DATA
;
2838 int exclude_last_sample
= no_last
? 0 : 1;
2839 uint32_t j
= audio
->samplesPerPacket
;
2840 for( lsmash_entry_t
*entry
= stts
->list
->tail
; entry
&& j
> 1; entry
= entry
->prev
)
2842 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)entry
->data
;
2844 return LSMASH_ERR_INVALID_DATA
;
2845 for( uint32_t k
= exclude_last_sample
; k
< stts_data
->sample_count
&& j
> 1; k
++ )
2847 sample_delta
-= stts_data
->sample_delta
;
2850 exclude_last_sample
= 0;
2853 /* Set sample_delta. */
2856 /* The duration of the last sample is not set yet. */
2857 if( sample_count
- i
> 1 )
2858 return LSMASH_ERR_INVALID_DATA
;
2859 /* Add a sample_delta. */
2860 if( sample_delta
== last_stts_data
->sample_delta
)
2861 ++ last_stts_data
->sample_count
;
2862 else if( (err
= isom_add_stts_entry( stbl
, sample_delta
)) < 0 )
2865 /* The duration of the last sample is already set. Replace it with a new one. */
2866 else if( (err
= isom_replace_last_sample_delta( stbl
, sample_delta
)) < 0 )
2868 return lsmash_update_track_duration( root
, track_ID
, sample_delta
);
2871 /*---- timeline manipulator ----*/
2873 int lsmash_modify_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t edit_number
, lsmash_edit_t edit
)
2875 if( isom_check_initializer_present( root
) < 0
2876 || edit
.start_time
< -1 )
2877 return LSMASH_ERR_FUNCTION_PARAM
;
2878 lsmash_file_t
*file
= root
->file
->initializer
;
2879 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2882 || !trak
->edts
->elst
2883 || !trak
->edts
->elst
->list
)
2884 return LSMASH_ERR_NAMELESS
;
2885 isom_elst_t
*elst
= trak
->edts
->elst
;
2886 isom_elst_entry_t
*data
= (isom_elst_entry_t
*)lsmash_get_entry_data( elst
->list
, edit_number
);
2888 return LSMASH_ERR_NAMELESS
;
2889 data
->segment_duration
= edit
.duration
;
2890 data
->media_time
= edit
.start_time
;
2891 data
->media_rate
= edit
.rate
;
2892 if( elst
->pos
== 0 || !file
->fragment
|| file
->bs
->unseekable
)
2893 return isom_update_tkhd_duration( trak
);
2894 /* Rewrite the specified entry.
2895 * Note: we don't update the version of the Edit List Box. */
2896 lsmash_bs_t
*bs
= file
->bs
;
2897 uint64_t current_pos
= bs
->offset
;
2898 uint64_t entry_pos
= elst
->pos
+ ISOM_LIST_FULLBOX_COMMON_SIZE
+ ((uint64_t)edit_number
- 1) * (elst
->version
== 1 ? 20 : 12);
2899 lsmash_bs_write_seek( bs
, entry_pos
, SEEK_SET
);
2902 lsmash_bs_put_be64( bs
, data
->segment_duration
);
2903 lsmash_bs_put_be64( bs
, data
->media_time
);
2907 lsmash_bs_put_be32( bs
, (uint32_t)LSMASH_MIN( data
->segment_duration
, UINT32_MAX
) );
2908 lsmash_bs_put_be32( bs
, (uint32_t)data
->media_time
);
2910 lsmash_bs_put_be32( bs
, data
->media_rate
);
2911 int ret
= lsmash_bs_flush_buffer( bs
);
2912 lsmash_bs_write_seek( bs
, current_pos
, SEEK_SET
);
2916 int lsmash_create_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_edit_t edit
)
2918 if( isom_check_initializer_present( root
) < 0 || edit
.start_time
< -1 )
2919 return LSMASH_ERR_FUNCTION_PARAM
;
2920 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
2923 return LSMASH_ERR_NAMELESS
;
2924 edit
.duration
= (edit
.duration
|| root
->file
->fragment
) ? edit
.duration
2925 : trak
->tkhd
->duration
? trak
->tkhd
->duration
2926 : isom_update_tkhd_duration( trak
) < 0 ? 0
2927 : trak
->tkhd
->duration
;
2928 if( (!trak
->edts
&& !isom_add_edts( trak
))
2929 || (!trak
->edts
->elst
&& !isom_add_elst( trak
->edts
)) )
2930 return LSMASH_ERR_NAMELESS
;
2931 int err
= isom_add_elst_entry( trak
->edts
->elst
, edit
.duration
, edit
.start_time
, edit
.rate
);
2934 return isom_update_tkhd_duration( trak
);
2937 int lsmash_get_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t edit_number
, lsmash_edit_t
*edit
)
2939 if( isom_check_initializer_present( root
) < 0 || !edit
)
2940 return LSMASH_ERR_FUNCTION_PARAM
;
2941 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2943 return LSMASH_ERR_NAMELESS
;
2945 || !trak
->edts
->elst
)
2949 edit
->start_time
= 0;
2953 isom_elst_entry_t
*elst
= (isom_elst_entry_t
*)lsmash_get_entry_data( trak
->edts
->elst
->list
, edit_number
);
2955 return LSMASH_ERR_NAMELESS
;
2956 edit
->duration
= elst
->segment_duration
;
2957 edit
->start_time
= elst
->media_time
;
2958 edit
->rate
= elst
->media_rate
;
2962 uint32_t lsmash_count_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
)
2964 if( isom_check_initializer_present( root
) < 0 )
2965 return LSMASH_ERR_FUNCTION_PARAM
;
2966 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2969 || !trak
->edts
->elst
2970 || !trak
->edts
->elst
->list
)
2972 return trak
->edts
->elst
->list
->entry_count
;
2975 /*---- create / modification time fields manipulators ----*/
2977 int lsmash_update_media_modification_time( lsmash_root_t
*root
, uint32_t track_ID
)
2979 if( isom_check_initializer_present( root
) < 0 )
2980 return LSMASH_ERR_FUNCTION_PARAM
;
2981 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2984 || !trak
->mdia
->mdhd
)
2985 return LSMASH_ERR_NAMELESS
;
2986 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
2987 mdhd
->modification_time
= isom_get_current_mp4time();
2988 /* overwrite strange creation_time */
2989 if( mdhd
->creation_time
> mdhd
->modification_time
)
2990 mdhd
->creation_time
= mdhd
->modification_time
;
2994 int lsmash_update_track_modification_time( lsmash_root_t
*root
, uint32_t track_ID
)
2996 if( isom_check_initializer_present( root
) < 0 )
2997 return LSMASH_ERR_FUNCTION_PARAM
;
2998 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
3001 return LSMASH_ERR_NAMELESS
;
3002 isom_tkhd_t
*tkhd
= trak
->tkhd
;
3003 tkhd
->modification_time
= isom_get_current_mp4time();
3004 /* overwrite strange creation_time */
3005 if( tkhd
->creation_time
> tkhd
->modification_time
)
3006 tkhd
->creation_time
= tkhd
->modification_time
;
3010 int lsmash_update_movie_modification_time( lsmash_root_t
*root
)
3012 if( isom_check_initializer_present( root
) < 0 )
3013 return LSMASH_ERR_FUNCTION_PARAM
;
3014 lsmash_file_t
*file
= root
->file
->initializer
;
3016 || !file
->moov
->mvhd
)
3017 return LSMASH_ERR_INVALID_DATA
;
3018 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
3019 mvhd
->modification_time
= isom_get_current_mp4time();
3020 /* overwrite strange creation_time */
3021 if( mvhd
->creation_time
> mvhd
->modification_time
)
3022 mvhd
->creation_time
= mvhd
->modification_time
;
3026 /*---- sample manipulators ----*/
3027 lsmash_sample_t
*lsmash_create_sample( uint32_t size
)
3029 lsmash_sample_t
*sample
= lsmash_malloc_zero( sizeof(lsmash_sample_t
) );
3034 sample
->data
= lsmash_malloc( size
);
3037 lsmash_free( sample
);
3040 sample
->length
= size
;
3044 int lsmash_sample_alloc( lsmash_sample_t
*sample
, uint32_t size
)
3047 return LSMASH_ERR_FUNCTION_PARAM
;
3050 lsmash_free( sample
->data
);
3051 sample
->data
= NULL
;
3055 if( size
== sample
->length
)
3059 data
= lsmash_malloc( size
);
3061 data
= lsmash_realloc( sample
->data
, size
);
3063 return LSMASH_ERR_MEMORY_ALLOC
;
3064 sample
->data
= data
;
3065 sample
->length
= size
;
3069 void lsmash_delete_sample( lsmash_sample_t
*sample
)
3073 lsmash_free( sample
->data
);
3074 lsmash_free( sample
);
3077 isom_sample_pool_t
*isom_create_sample_pool( uint64_t size
)
3079 isom_sample_pool_t
*pool
= lsmash_malloc_zero( sizeof(isom_sample_pool_t
) );
3084 pool
->data
= lsmash_malloc( size
);
3087 lsmash_free( pool
);
3094 void isom_remove_sample_pool( isom_sample_pool_t
*pool
)
3098 lsmash_free( pool
->data
);
3099 lsmash_free( pool
);
3102 static uint32_t isom_add_size( isom_trak_t
*trak
, uint32_t sample_size
)
3104 if( isom_add_stsz_entry( trak
->mdia
->minf
->stbl
, sample_size
) < 0 )
3106 return isom_get_sample_count( trak
);
3109 static uint32_t isom_add_dts( isom_stbl_t
*stbl
, isom_timestamp_t
*cache
, uint64_t dts
)
3111 isom_stts_t
*stts
= stbl
->stts
;
3112 if( stts
->list
->entry_count
== 0 )
3114 if( isom_add_stts_entry( stbl
, dts
) < 0 )
3119 if( dts
<= cache
->dts
)
3121 uint32_t sample_delta
= dts
- cache
->dts
;
3122 isom_stts_entry_t
*data
= (isom_stts_entry_t
*)stts
->list
->tail
->data
;
3123 if( data
->sample_delta
== sample_delta
)
3124 ++ data
->sample_count
;
3125 else if( isom_add_stts_entry( stbl
, sample_delta
) < 0 )
3128 return sample_delta
;
3131 static int isom_add_cts( isom_stbl_t
*stbl
, isom_timestamp_t
*cache
, uint64_t cts
)
3134 isom_ctts_t
*ctts
= stbl
->ctts
;
3137 if( cts
== cache
->dts
)
3142 /* Add ctts box and the first ctts entry. */
3143 if( !isom_add_ctts( stbl
) )
3144 return LSMASH_ERR_NAMELESS
;
3145 if( (err
= isom_add_ctts_entry( stbl
, 0 )) < 0 )
3148 isom_ctts_entry_t
*data
= (isom_ctts_entry_t
*)ctts
->list
->head
->data
;
3149 uint32_t sample_count
= stbl
->stsz
->sample_count
;
3150 if( sample_count
!= 1 )
3152 data
->sample_count
= sample_count
- 1;
3153 if( (err
= isom_add_ctts_entry( stbl
, cts
- cache
->dts
)) < 0 )
3157 data
->sample_offset
= cts
;
3162 return LSMASH_ERR_INVALID_DATA
;
3163 isom_ctts_entry_t
*data
= (isom_ctts_entry_t
*)ctts
->list
->tail
->data
;
3164 uint32_t sample_offset
= cts
- cache
->dts
;
3165 if( data
->sample_offset
== sample_offset
)
3166 ++ data
->sample_count
;
3167 else if( (err
= isom_add_ctts_entry( stbl
, sample_offset
)) < 0 )
3173 static int isom_add_timestamp( isom_trak_t
*trak
, uint64_t dts
, uint64_t cts
)
3176 || !trak
->mdia
->minf
->stbl
->stts
3177 || !trak
->mdia
->minf
->stbl
->stts
->list
)
3178 return LSMASH_ERR_INVALID_DATA
;
3179 lsmash_file_t
*file
= trak
->file
;
3180 if( file
->isom_compatible
&& file
->qt_compatible
&& (cts
- dts
) > INT32_MAX
)
3181 return LSMASH_ERR_INVALID_DATA
; /* sample_offset is not compatible. */
3182 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3183 isom_timestamp_t
*ts_cache
= &trak
->cache
->timestamp
;
3184 uint32_t sample_count
= isom_get_sample_count( trak
);
3185 uint32_t sample_delta
= sample_count
> 1 ? isom_add_dts( stbl
, ts_cache
, dts
) : 0;
3186 if( sample_count
> 1 && sample_delta
== 0 )
3187 return LSMASH_ERR_INVALID_DATA
;
3188 int err
= isom_add_cts( stbl
, ts_cache
, cts
);
3191 if( (cts
+ ts_cache
->ctd_shift
) < dts
)
3193 if( (file
->max_isom_version
< 4 && !file
->qt_compatible
) /* Negative sample offset is not supported. */
3194 || (file
->max_isom_version
>= 4 && file
->qt_compatible
) /* ctts version 1 is not defined in QTFF. */
3195 || ((dts
- cts
) > INT32_MAX
) ) /* Overflow */
3196 return LSMASH_ERR_INVALID_DATA
;
3197 ts_cache
->ctd_shift
= dts
- cts
;
3198 if( stbl
->ctts
->version
== 0 && !file
->qt_compatible
)
3199 stbl
->ctts
->version
= 1;
3201 if( trak
->cache
->fragment
)
3203 isom_fragment_t
*fragment_cache
= trak
->cache
->fragment
;
3204 fragment_cache
->last_duration
= sample_delta
;
3205 fragment_cache
->largest_cts
= LSMASH_MAX( ts_cache
->cts
, fragment_cache
->largest_cts
);
3210 static int isom_add_sync_point( isom_trak_t
*trak
, uint32_t sample_number
, lsmash_sample_property_t
*prop
)
3212 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3213 isom_cache_t
*cache
= trak
->cache
;
3214 if( !(prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
) ) /* no null check for prop */
3216 if( !cache
->all_sync
)
3218 if( !stbl
->stss
&& !isom_add_stss( stbl
) )
3219 return LSMASH_ERR_NAMELESS
;
3220 int err
= isom_add_stss_entry( stbl
, 1 );
3221 if( err
< 0 ) /* Declare here the first sample is a sync sample. */
3223 cache
->all_sync
= 0;
3226 if( cache
->all_sync
) /* We don't need stss box if all samples are sync sample. */
3230 if( isom_get_sample_count( trak
) == 1 )
3232 cache
->all_sync
= 1; /* Also the first sample is a sync sample. */
3235 if( !isom_add_stss( stbl
) )
3236 return LSMASH_ERR_NAMELESS
;
3238 return isom_add_stss_entry( stbl
, sample_number
);
3241 static int isom_add_partial_sync( isom_trak_t
*trak
, uint32_t sample_number
, lsmash_sample_property_t
*prop
)
3243 if( !trak
->file
->qt_compatible
)
3245 if( !(prop
->ra_flags
& QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
) )
3247 /* This sample is a partial sync sample. */
3248 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3249 if( !stbl
->stps
&& !isom_add_stps( stbl
) )
3250 return LSMASH_ERR_NAMELESS
;
3251 return isom_add_stps_entry( stbl
, sample_number
);
3254 static int isom_add_dependency_type( isom_trak_t
*trak
, lsmash_sample_property_t
*prop
)
3256 if( !trak
->file
->qt_compatible
&& !trak
->file
->avc_extensions
)
3258 int compatibility
= trak
->file
->avc_extensions
&& trak
->file
->qt_compatible
? 3
3259 : trak
->file
->qt_compatible
? 2
3260 : trak
->file
->avc_extensions
? 1
3262 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3264 return isom_add_sdtp_entry( (isom_box_t
*)stbl
, prop
, compatibility
);
3265 /* no null check for prop */
3266 if( !prop
->allow_earlier
3268 && !prop
->independent
3269 && !prop
->disposable
3270 && !prop
->redundant
)
3272 if( !isom_add_sdtp( (isom_box_t
*)stbl
) )
3273 return LSMASH_ERR_NAMELESS
;
3274 uint32_t count
= isom_get_sample_count( trak
);
3275 /* fill past samples with ISOM_SAMPLE_*_UNKNOWN */
3276 lsmash_sample_property_t null_prop
= { 0 };
3277 for( uint32_t i
= 1; i
< count
; i
++ )
3279 int err
= isom_add_sdtp_entry( (isom_box_t
*)stbl
, &null_prop
, compatibility
);
3283 return isom_add_sdtp_entry( (isom_box_t
*)stbl
, prop
, compatibility
);
3286 int isom_rap_grouping_established( isom_rap_group_t
*group
, int num_leading_samples_known
, isom_sgpd_t
*sgpd
, int is_fragment
)
3288 isom_rap_entry_t
*rap
= group
->random_access
;
3291 assert( rap
== (isom_rap_entry_t
*)sgpd
->list
->tail
->data
);
3292 rap
->num_leading_samples_known
= num_leading_samples_known
;
3293 /* Avoid duplication of sample group descriptions. */
3294 uint32_t group_description_index
= is_fragment
? 0x10001 : 1;
3295 for( lsmash_entry_t
*entry
= sgpd
->list
->head
; entry
!= sgpd
->list
->tail
; entry
= entry
->next
)
3297 isom_rap_entry_t
*data
= (isom_rap_entry_t
*)entry
->data
;
3299 return LSMASH_ERR_INVALID_DATA
;
3300 if( rap
->num_leading_samples_known
== data
->num_leading_samples_known
3301 && rap
->num_leading_samples
== data
->num_leading_samples
)
3303 /* The same description already exists.
3304 * Remove the latest random access entry. */
3305 lsmash_remove_entry_tail( sgpd
->list
, NULL
);
3306 /* Replace assigned group_description_index with the one corresponding the same description. */
3307 if( group
->assignment
->group_description_index
== 0 )
3309 /* We don't create consecutive sample groups not assigned to 'rap '.
3310 * So the previous sample group shall be a group of 'rap ' if any. */
3311 if( group
->prev_assignment
)
3313 assert( group
->prev_assignment
->group_description_index
);
3314 group
->prev_assignment
->group_description_index
= group_description_index
;
3318 group
->assignment
->group_description_index
= group_description_index
;
3321 ++group_description_index
;
3323 group
->random_access
= NULL
;
3327 int isom_group_random_access( isom_box_t
*parent
, lsmash_sample_t
*sample
)
3329 if( parent
->file
->max_isom_version
< 6 )
3333 isom_cache_t
*cache
;
3334 uint32_t sample_count
;
3336 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAK
) )
3338 isom_trak_t
*trak
= (isom_trak_t
*)parent
;
3339 sbgp
= isom_get_sample_to_group ( trak
->mdia
->minf
->stbl
, ISOM_GROUP_TYPE_RAP
);
3340 sgpd
= isom_get_sample_group_description( trak
->mdia
->minf
->stbl
, ISOM_GROUP_TYPE_RAP
);
3341 cache
= trak
->cache
;
3342 sample_count
= isom_get_sample_count( trak
);
3345 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
3347 isom_traf_t
*traf
= (isom_traf_t
*)parent
;
3348 sbgp
= isom_get_fragment_sample_to_group ( traf
, ISOM_GROUP_TYPE_RAP
);
3349 sgpd
= isom_get_fragment_sample_group_description( traf
, ISOM_GROUP_TYPE_RAP
);
3350 cache
= traf
->cache
;
3351 sample_count
= cache
->fragment
->sample_count
+ 1;
3359 /* redundant initializations to suppress warnings from unclever compilers */
3364 if( !sbgp
|| !sgpd
)
3366 lsmash_sample_property_t
*prop
= &sample
->prop
;
3367 uint8_t is_rap
= (prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
)
3368 || (prop
->ra_flags
& QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
)
3369 || (prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
)
3370 || (LSMASH_IS_POST_ROLL_START( prop
->ra_flags
) && prop
->post_roll
.identifier
== prop
->post_roll
.complete
);
3371 isom_rap_group_t
*group
= cache
->rap
;
3374 /* This sample is the first sample, create a grouping cache. */
3375 assert( sample_count
== 1 );
3376 group
= lsmash_malloc( sizeof(isom_rap_group_t
) );
3378 return LSMASH_ERR_MEMORY_ALLOC
;
3381 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3382 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3386 /* The first sample is not always a random access point. */
3387 group
->random_access
= NULL
;
3388 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3390 if( !group
->assignment
)
3392 lsmash_free( group
);
3393 return LSMASH_ERR_MEMORY_ALLOC
;
3395 group
->prev_assignment
= NULL
;
3396 group
->is_prev_rap
= is_rap
;
3401 if( group
->is_prev_rap
)
3403 /* OK. here, the previous sample is a menber of 'rap '. */
3406 /* This sample isn't a member of 'rap ' and the previous sample is.
3407 * So we create a new group and set 0 on its group_description_index. */
3408 group
->prev_assignment
= group
->assignment
;
3409 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3410 if( !group
->assignment
)
3412 lsmash_free( group
);
3413 return LSMASH_ERR_MEMORY_ALLOC
;
3416 else if( !LSMASH_IS_CLOSED_RAP( prop
->ra_flags
) )
3418 /* Create a new group since there is the possibility the next sample is a leading sample.
3419 * This sample is a member of 'rap ', so we set appropriate value on its group_description_index. */
3420 if( (err
= isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
)) < 0 )
3422 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3423 group
->prev_assignment
= group
->assignment
;
3424 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3425 if( !group
->assignment
)
3427 lsmash_free( group
);
3428 return LSMASH_ERR_MEMORY_ALLOC
;
3431 else /* The previous and current sample are a member of 'rap ', and the next sample must not be a leading sample. */
3432 ++ group
->assignment
->sample_count
;
3436 /* This sample is a member of 'rap ' and the previous sample isn't.
3437 * So we create a new group and set appropriate value on its group_description_index. */
3438 if( (err
= isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
)) < 0 )
3440 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3441 group
->prev_assignment
= group
->assignment
;
3442 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3443 if( !group
->assignment
)
3445 lsmash_free( group
);
3446 return LSMASH_ERR_MEMORY_ALLOC
;
3449 else /* The previous and current sample aren't a member of 'rap '. */
3450 ++ group
->assignment
->sample_count
;
3451 /* Obtain the property of the latest random access point group. */
3452 if( !is_rap
&& group
->random_access
)
3454 if( prop
->leading
== ISOM_SAMPLE_LEADING_UNKNOWN
)
3456 /* We can no longer know num_leading_samples in this group. */
3457 if( (err
= isom_rap_grouping_established( group
, 0, sgpd
, is_fragment
)) < 0 )
3462 if( prop
->leading
== ISOM_SAMPLE_IS_UNDECODABLE_LEADING
3463 || prop
->leading
== ISOM_SAMPLE_IS_DECODABLE_LEADING
)
3464 ++ group
->random_access
->num_leading_samples
;
3465 /* no more consecutive leading samples in this group */
3466 else if( (err
= isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
)) < 0 )
3470 group
->is_prev_rap
= is_rap
;
3474 static int isom_roll_grouping_established( isom_roll_group_t
*group
)
3476 /* Avoid duplication of sample group descriptions. */
3477 isom_sgpd_t
*sgpd
= group
->sgpd
;
3478 uint32_t group_description_index
= group
->is_fragment
? 0x10001 : 1;
3479 for( lsmash_entry_t
*entry
= sgpd
->list
->head
; entry
; entry
= entry
->next
)
3481 isom_roll_entry_t
*data
= (isom_roll_entry_t
*)entry
->data
;
3483 return LSMASH_ERR_INVALID_DATA
;
3484 if( group
->roll_distance
== data
->roll_distance
)
3486 /* The same description already exists.
3487 * Set the group_description_index corresponding the same description. */
3488 group
->assignment
->group_description_index
= group_description_index
;
3491 ++group_description_index
;
3493 /* Add a new roll recovery description. */
3494 if( !isom_add_roll_group_entry( sgpd
, group
->roll_distance
) )
3495 return LSMASH_ERR_MEMORY_ALLOC
;
3496 group
->assignment
->group_description_index
= sgpd
->list
->entry_count
+ (group
->is_fragment
? 0x10000 : 0);
3500 static int isom_deduplicate_roll_group( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3503 uint32_t current_group_number
= sbgp
->list
->entry_count
- pool
->entry_count
+ 1;
3504 isom_group_assignment_entry_t
*prev_assignment
= (isom_group_assignment_entry_t
*)lsmash_get_entry_data( sbgp
->list
, current_group_number
- 1 );
3505 for( lsmash_entry_t
*entry
= pool
->head
; entry
; )
3507 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3509 || !group
->assignment
)
3510 return LSMASH_ERR_INVALID_DATA
;
3511 if( !group
->delimited
|| group
->described
!= ROLL_DISTANCE_DETERMINED
)
3513 if( prev_assignment
&& prev_assignment
->group_description_index
== group
->assignment
->group_description_index
)
3515 /* Merge the current group with the previous. */
3516 lsmash_entry_t
*next_entry
= entry
->next
;
3517 prev_assignment
->sample_count
+= group
->assignment
->sample_count
;
3519 if( (err
= lsmash_remove_entry( sbgp
->list
, current_group_number
, NULL
)) < 0
3520 || (err
= lsmash_remove_entry_direct( pool
, entry
, NULL
)) < 0 )
3526 entry
= entry
->next
;
3527 prev_assignment
= group
->assignment
;
3528 ++current_group_number
;
3534 /* Remove pooled caches that has become unnecessary. */
3535 static int isom_clean_roll_pool( lsmash_entry_list_t
*pool
)
3537 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= pool
->head
)
3539 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3541 return LSMASH_ERR_INVALID_DATA
;
3542 if( !group
->delimited
|| group
->described
!= ROLL_DISTANCE_DETERMINED
)
3544 int err
= lsmash_remove_entry_direct( pool
, entry
, NULL
);
3551 static int isom_flush_roll_pool( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3554 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3556 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3558 return LSMASH_ERR_INVALID_DATA
;
3559 if( group
->delimited
3560 && group
->described
== ROLL_DISTANCE_DETERMINED
3561 && group
->roll_distance
!= 0
3562 && (err
= isom_roll_grouping_established( group
)) < 0 )
3565 if( (err
= isom_deduplicate_roll_group( sbgp
, pool
)) < 0 )
3567 return isom_clean_roll_pool( pool
);
3570 static int isom_all_recovery_described( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3572 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3574 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3576 return LSMASH_ERR_INVALID_DATA
;
3577 group
->described
= ROLL_DISTANCE_DETERMINED
;
3579 return isom_flush_roll_pool( sbgp
, pool
);
3582 int isom_all_recovery_completed( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3584 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3586 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3588 return LSMASH_ERR_INVALID_DATA
;
3589 group
->described
= ROLL_DISTANCE_DETERMINED
;
3590 group
->delimited
= 1;
3592 return isom_flush_roll_pool( sbgp
, pool
);
3595 static isom_roll_entry_t
*isom_get_roll_description
3597 isom_roll_group_t
*group
3600 uint32_t group_description_index
= group
->assignment
->group_description_index
;
3601 if( group_description_index
&& group
->is_fragment
)
3603 assert( group_description_index
> 0x10000 );
3604 group_description_index
-= 0x10000;
3606 return (isom_roll_entry_t
*)lsmash_get_entry_data( group
->sgpd
->list
, group_description_index
);
3609 int isom_group_roll_recovery( isom_box_t
*parent
, lsmash_sample_t
*sample
)
3611 if( !parent
->file
->avc_extensions
3612 && !parent
->file
->qt_compatible
)
3614 uint32_t sample_count
;
3616 isom_cache_t
*cache
;
3617 lsmash_entry_list_t
*sbgp_list
;
3618 lsmash_entry_list_t
*sgpd_list
;
3619 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAK
) )
3621 isom_trak_t
*trak
= (isom_trak_t
*)parent
;
3622 cache
= trak
->cache
;
3623 sbgp_list
= &trak
->mdia
->minf
->stbl
->sbgp_list
;
3624 sgpd_list
= &trak
->mdia
->minf
->stbl
->sgpd_list
;
3625 sample_count
= isom_get_sample_count( trak
);
3628 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
3630 if( parent
->file
->max_isom_version
< 6 )
3632 isom_traf_t
*traf
= (isom_traf_t
*)parent
;
3633 cache
= traf
->cache
;
3634 sbgp_list
= &traf
->sbgp_list
;
3635 sgpd_list
= &traf
->sgpd_list
;
3636 sample_count
= cache
->fragment
->sample_count
+ 1;
3642 return LSMASH_ERR_INVALID_DATA
;
3644 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group ( sbgp_list
);
3645 isom_sgpd_t
*sgpd
= isom_get_roll_recovery_sample_group_description( sgpd_list
);
3646 if( !sbgp
|| !sgpd
|| sbgp
->grouping_type
!= sgpd
->grouping_type
)
3648 /* Check if 'roll' -> 'prol' conversion is needed. */
3650 && sbgp
->grouping_type
== ISOM_GROUP_TYPE_ROLL
3651 && !(sample
->prop
.ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
) )
3653 /* Since not every samples is a sync sample, change grouping_type into 'prol'. */
3654 sbgp
->grouping_type
= ISOM_GROUP_TYPE_PROL
;
3655 sgpd
->grouping_type
= ISOM_GROUP_TYPE_PROL
;
3657 lsmash_entry_list_t
*pool
= cache
->roll
.pool
;
3660 pool
= lsmash_create_entry_list();
3662 return LSMASH_ERR_MEMORY_ALLOC
;
3663 cache
->roll
.pool
= pool
;
3665 lsmash_sample_property_t
*prop
= &sample
->prop
;
3666 isom_roll_group_t
*group
= (isom_roll_group_t
*)lsmash_get_entry_data( pool
, pool
->entry_count
);
3667 int is_recovery_start
= LSMASH_IS_POST_ROLL_START( prop
->ra_flags
);
3668 int valid_pre_roll
= !is_recovery_start
3669 && (prop
->ra_flags
!= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
)
3670 && (prop
->pre_roll
.distance
> 0)
3671 && (prop
->pre_roll
.distance
<= -INT16_MIN
);
3672 int new_group
= !group
|| is_recovery_start
|| (group
->prev_is_recovery_start
!= is_recovery_start
);
3675 /* Check pre-roll distance. */
3676 assert( group
->assignment
&& group
->sgpd
);
3677 isom_roll_entry_t
*prev_roll
= isom_get_roll_description( group
);
3679 new_group
= valid_pre_roll
;
3680 else if( !valid_pre_roll
|| (prop
->pre_roll
.distance
!= -prev_roll
->roll_distance
) )
3681 /* Pre-roll distance is different from the previous. */
3687 group
->delimited
= 1;
3689 assert( sample_count
== 1 );
3690 /* Create a new group. */
3691 group
= lsmash_malloc_zero( sizeof(isom_roll_group_t
) );
3693 return LSMASH_ERR_MEMORY_ALLOC
;
3695 group
->prev_is_recovery_start
= is_recovery_start
;
3696 group
->is_fragment
= is_fragment
;
3697 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3698 if( !group
->assignment
|| lsmash_add_entry( pool
, group
) < 0 )
3700 lsmash_free( group
);
3701 return LSMASH_ERR_MEMORY_ALLOC
;
3703 if( is_recovery_start
)
3705 /* a member of non-roll or post-roll group */
3706 group
->first_sample
= sample_count
;
3707 group
->recovery_point
= prop
->post_roll
.complete
;
3711 group
->described
= ROLL_DISTANCE_DETERMINED
;
3712 if( valid_pre_roll
)
3714 /* a member of pre-roll group */
3715 group
->roll_distance
= -prop
->pre_roll
.distance
;
3716 int err
= isom_roll_grouping_established( group
);
3721 /* a member of non-roll group */
3722 group
->roll_distance
= 0;
3727 group
->prev_is_recovery_start
= is_recovery_start
;
3728 group
->assignment
->sample_count
+= 1;
3730 /* If encountered a RAP, all recovery is completed here. */
3731 if( prop
->ra_flags
& (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
3732 | ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
3733 | QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
) )
3734 return isom_all_recovery_described( sbgp
, pool
);
3735 /* Check whether this sample is a random access recovery point or not. */
3736 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3738 group
= (isom_roll_group_t
*)entry
->data
;
3740 return LSMASH_ERR_INVALID_DATA
;
3741 if( group
->described
== ROLL_DISTANCE_DETERMINED
)
3743 if( group
->described
== ROLL_DISTANCE_INITIALIZED
)
3745 /* Let's consider the following picture sequence.
3746 * coded order : P[0] P[1] P[2] P[3] P[4] P[5]
3749 * Here, P[0] conveys a recovery point SEI and P[3] is the recovery point.
3750 * Correctness of decoded pictures is specified by recovery point in output order for both AVC and HEVC.
3751 * Therefore, as follows,
3752 * output order : P[0] P[2] P[1] P[5]|P[3] P[4]
3753 * ---(incorrect?)--->|
3754 * there is no guarantee that P[5] is decoded and output correctly.
3755 * From this, it can be said that the roll_distance of this sequence is equal to 5. */
3756 isom_roll_entry_t
*post_roll
= isom_get_roll_description( group
);
3757 if( post_roll
&& post_roll
->roll_distance
> 0 )
3759 if( group
->rp_cts
> sample
->cts
)
3760 /* Updated roll_distance for composition reordering. */
3761 post_roll
->roll_distance
= sample_count
- group
->first_sample
;
3762 if( ++ group
->wait_and_see_count
>= MAX_ROLL_WAIT_AND_SEE_COUNT
)
3763 group
->described
= ROLL_DISTANCE_DETERMINED
;
3766 else if( prop
->post_roll
.identifier
== group
->recovery_point
)
3768 int16_t distance
= sample_count
- group
->first_sample
;
3769 group
->rp_cts
= sample
->cts
;
3770 group
->roll_distance
= distance
;
3771 /* Add a roll recovery entry only when roll_distance isn't zero since roll_distance = 0 must not be used. */
3774 /* Now, this group is a 'roll'.
3775 * The roll_distance may be updated later because of composition reordering. */
3776 group
->described
= ROLL_DISTANCE_INITIALIZED
;
3777 group
->wait_and_see_count
= 0;
3778 /* All groups with uninitialized roll_distance before the current group are described. */
3779 lsmash_entry_t
*current
= entry
;
3780 for( entry
= pool
->head
; entry
!= current
; entry
= entry
->next
)
3782 group
= (isom_roll_group_t
*)entry
->data
;
3783 if( !group
|| group
->described
!= ROLL_DISTANCE_INITIALIZED
)
3785 group
->described
= ROLL_DISTANCE_DETERMINED
;
3787 /* Cache the CTS of the first recovery point in a subsegment. */
3789 && cache
->fragment
->subsegment
.first_rp_number
== 0 )
3791 cache
->fragment
->subsegment
.first_rp_number
= sample_count
;
3792 cache
->fragment
->subsegment
.first_rp_cts
= sample
->cts
;
3793 cache
->fragment
->subsegment
.first_ed_cts
= sample
->cts
;
3794 cache
->fragment
->subsegment
.decodable
= 1;
3798 /* Random Accessible Point */
3799 return isom_all_recovery_described( sbgp
, pool
);
3802 return isom_flush_roll_pool( sbgp
, pool
);
3805 /* returns 1 if pooled samples must be flushed. */
3806 /* FIXME: I wonder if this function should have a extra argument which indicates force_to_flush_cached_chunk.
3807 see lsmash_append_sample for detail. */
3808 static int isom_add_chunk( isom_trak_t
*trak
, lsmash_sample_t
*sample
)
3812 || !trak
->mdia
->mdhd
3813 || trak
->mdia
->mdhd
->timescale
== 0
3814 || !trak
->mdia
->minf
->dinf
3815 || !trak
->mdia
->minf
->dinf
->dref
3816 || !trak
->mdia
->minf
->stbl
->stsd
3817 || !trak
->mdia
->minf
->stbl
->stsc
3818 || !trak
->mdia
->minf
->stbl
->stsc
->list
)
3819 return LSMASH_ERR_INVALID_DATA
;
3820 isom_chunk_t
*current
= &trak
->cache
->chunk
;
3821 if( !current
->pool
)
3823 /* Very initial settings, just once per track */
3824 current
->pool
= isom_create_sample_pool( 0 );
3825 if( !current
->pool
)
3826 return LSMASH_ERR_MEMORY_ALLOC
;
3828 if( current
->pool
->sample_count
== 0 )
3830 /* Cannot decide whether we should flush the current sample or not here yet. */
3831 current
->chunk_number
+= 1;
3832 current
->sample_description_index
= sample
->index
;
3833 current
->first_dts
= sample
->dts
;
3836 if( sample
->dts
< current
->first_dts
)
3837 return LSMASH_ERR_INVALID_DATA
; /* easy error check. */
3838 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3839 lsmash_file_t
*file
= isom_get_written_media_file( trak
, current
->sample_description_index
);
3840 if( (current
->sample_description_index
== sample
->index
)
3841 && (file
->max_chunk_duration
>= ((double)(sample
->dts
- current
->first_dts
) / trak
->mdia
->mdhd
->timescale
))
3842 && (file
->max_chunk_size
>= current
->pool
->size
+ sample
->length
) )
3843 return 0; /* No need to flush current cached chunk, the current sample must be put into that. */
3844 /* NOTE: chunk relative stuff must be pushed into file after a chunk is fully determined with its contents. */
3845 /* Now the current cached chunk is fixed, actually add the chunk relative properties to its file accordingly. */
3846 isom_stsc_entry_t
*last_stsc_data
= stbl
->stsc
->list
->tail
? (isom_stsc_entry_t
*)stbl
->stsc
->list
->tail
->data
: NULL
;
3847 /* Create a new chunk sequence in this track if needed. */
3849 if( (!last_stsc_data
3850 || current
->pool
->sample_count
!= last_stsc_data
->samples_per_chunk
3851 || current
->sample_description_index
!= last_stsc_data
->sample_description_index
)
3852 && (err
= isom_add_stsc_entry( stbl
, current
->chunk_number
,
3853 current
->pool
->sample_count
,
3854 current
->sample_description_index
)) < 0 )
3856 /* Add a new chunk offset in this track. */
3857 uint64_t offset
= file
->size
;
3858 if( file
->fragment
)
3859 offset
+= ISOM_BASEBOX_COMMON_SIZE
+ file
->fragment
->pool_size
;
3860 if( (err
= isom_add_stco_entry( stbl
, offset
)) < 0 )
3862 /* Update and re-initialize cache, using the current sample */
3863 current
->chunk_number
+= 1;
3864 current
->sample_description_index
= sample
->index
;
3865 current
->first_dts
= sample
->dts
;
3866 /* current->pool must be flushed in isom_append_sample_internal() */
3870 static int isom_write_pooled_samples( lsmash_file_t
*file
, isom_sample_pool_t
*pool
)
3874 || !file
->bs
->stream
3875 || !(file
->flags
& LSMASH_FILE_MODE_WRITE
)
3876 || !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
3877 || ((file
->flags
& LSMASH_FILE_MODE_BOX
) && !file
->mdat
) )
3878 return LSMASH_ERR_INVALID_DATA
;
3879 lsmash_bs_put_bytes( file
->bs
, pool
->size
, pool
->data
);
3880 int err
= lsmash_bs_flush_buffer( file
->bs
);
3884 file
->mdat
->media_size
+= pool
->size
;
3885 file
->size
+= pool
->size
;
3886 pool
->sample_count
= 0;
3891 int isom_update_sample_tables( isom_trak_t
*trak
, lsmash_sample_t
*sample
, uint32_t *samples_per_packet
)
3894 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
, sample
->index
);
3895 if( (audio
->manager
& LSMASH_AUDIO_DESCRIPTION
)
3896 && (audio
->manager
& LSMASH_QTFF_BASE
)
3897 && (audio
->version
== 1)
3898 && (audio
->compression_ID
!= QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION
) )
3900 /* Add entries of the sample table for each uncompressed sample. */
3901 uint64_t sample_duration
= trak
->mdia
->mdhd
->timescale
/ (audio
->samplerate
>> 16);
3902 if( audio
->samplesPerPacket
== 0 || sample_duration
== 0 )
3903 return LSMASH_ERR_INVALID_DATA
;
3904 uint64_t sample_dts
= sample
->dts
;
3905 uint64_t sample_cts
= sample
->cts
;
3906 for( uint32_t i
= 0; i
< audio
->samplesPerPacket
; i
++ )
3908 /* Add a size of uncomressed audio and increment sample_count.
3909 * This points to individual uncompressed audio samples, each one byte in size, within the compressed frames. */
3910 uint32_t sample_count
= isom_add_size( trak
, 1 );
3911 if( sample_count
== 0 )
3912 return LSMASH_ERR_NAMELESS
;
3913 /* Add a decoding timestamp and a composition timestamp. */
3914 if( (err
= isom_add_timestamp( trak
, sample_dts
, sample_cts
)) < 0 )
3916 sample_dts
+= sample_duration
;
3917 sample_cts
+= sample_duration
;
3919 *samples_per_packet
= audio
->samplesPerPacket
;
3923 /* Add a sample_size and increment sample_count. */
3924 uint32_t sample_count
= isom_add_size( trak
, sample
->length
);
3925 if( sample_count
== 0 )
3926 return LSMASH_ERR_NAMELESS
;
3927 /* Add a decoding timestamp and a composition timestamp. */
3928 if( (err
= isom_add_timestamp( trak
, sample
->dts
, sample
->cts
)) < 0 )
3930 /* Add a sync point if needed. */
3931 if( (err
= isom_add_sync_point( trak
, sample_count
, &sample
->prop
)) < 0 )
3933 /* Add a partial sync point if needed. */
3934 if( (err
= isom_add_partial_sync( trak
, sample_count
, &sample
->prop
)) < 0 )
3936 /* Add leading, independent, disposable and redundant information if needed. */
3937 if( (err
= isom_add_dependency_type( trak
, &sample
->prop
)) < 0 )
3939 /* Group samples into random access point type if needed. */
3940 if( (err
= isom_group_random_access( (isom_box_t
*)trak
, sample
)) < 0 )
3942 /* Group samples into random access recovery point type if needed. */
3943 if( (err
= isom_group_roll_recovery( (isom_box_t
*)trak
, sample
)) < 0 )
3945 *samples_per_packet
= 1;
3947 /* Add a chunk if needed. */
3948 return isom_add_chunk( trak
, sample
);
3951 static int isom_output_cached_chunk( isom_trak_t
*trak
)
3953 isom_chunk_t
*chunk
= &trak
->cache
->chunk
;
3954 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3955 isom_stsc_entry_t
*last_stsc_data
= stbl
->stsc
->list
->tail
? (isom_stsc_entry_t
*)stbl
->stsc
->list
->tail
->data
: NULL
;
3956 /* Create a new chunk sequence in this track if needed. */
3958 if( (!last_stsc_data
3959 || chunk
->pool
->sample_count
!= last_stsc_data
->samples_per_chunk
3960 || chunk
->sample_description_index
!= last_stsc_data
->sample_description_index
)
3961 && (err
= isom_add_stsc_entry( stbl
, chunk
->chunk_number
,
3962 chunk
->pool
->sample_count
,
3963 chunk
->sample_description_index
)) < 0 )
3965 lsmash_file_t
*file
= isom_get_written_media_file( trak
, chunk
->sample_description_index
);
3966 if( file
->fragment
)
3968 /* Add a new chunk offset in this track. */
3969 if( (err
= isom_add_stco_entry( stbl
, file
->size
+ ISOM_BASEBOX_COMMON_SIZE
+ file
->fragment
->pool_size
)) < 0 )
3971 return isom_append_fragment_track_run( file
, chunk
);
3973 /* Add a new chunk offset in this track. */
3974 if( (err
= isom_add_stco_entry( stbl
, file
->size
)) < 0 )
3976 /* Output pooled samples in this track. */
3977 return isom_write_pooled_samples( file
, chunk
->pool
);
3980 int isom_pool_sample( isom_sample_pool_t
*pool
, lsmash_sample_t
*sample
, uint32_t samples_per_packet
)
3982 uint64_t pool_size
= pool
->size
+ sample
->length
;
3983 if( pool
->alloc
< pool_size
)
3986 uint64_t alloc
= pool_size
+ (1<<16);
3988 data
= lsmash_malloc( alloc
);
3990 data
= lsmash_realloc( pool
->data
, alloc
);
3992 return LSMASH_ERR_MEMORY_ALLOC
;
3994 pool
->alloc
= alloc
;
3996 memcpy( pool
->data
+ pool
->size
, sample
->data
, sample
->length
);
3997 pool
->size
= pool_size
;
3998 pool
->sample_count
+= samples_per_packet
;
3999 lsmash_delete_sample( sample
);
4003 static int isom_append_sample_internal( isom_trak_t
*trak
, lsmash_sample_t
*sample
)
4005 uint32_t samples_per_packet
;
4006 int ret
= isom_update_sample_tables( trak
, sample
, &samples_per_packet
);
4009 /* ret == 1 means pooled samples must be flushed. */
4010 isom_sample_pool_t
*current_pool
= trak
->cache
->chunk
.pool
;
4013 /* The sample_description_index in the cache is one of the next written chunk.
4014 * Therefore, it cannot be referenced here. */
4015 lsmash_entry_list_t
*stsc_list
= trak
->mdia
->minf
->stbl
->stsc
->list
;
4016 isom_stsc_entry_t
*last_stsc_data
= (isom_stsc_entry_t
*)stsc_list
->tail
->data
;
4017 lsmash_file_t
*file
= isom_get_written_media_file( trak
, last_stsc_data
->sample_description_index
);
4018 if( (ret
= isom_write_pooled_samples( file
, current_pool
)) < 0 )
4021 /* Arbitration system between tracks with extremely scattering dts.
4022 * Here, we check whether asynchronization between the tracks exceeds the tolerance.
4023 * If a track has too old "first DTS" in its cached chunk than current sample's DTS, then its pooled samples must be flushed.
4024 * We don't consider presentation of media since any edit can pick an arbitrary portion of media in track.
4025 * Note: you needn't read this loop until you grasp the basic handling of chunks. */
4026 lsmash_file_t
*file
= trak
->file
;
4027 double tolerance
= file
->max_async_tolerance
;
4028 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
4030 isom_trak_t
*other
= (isom_trak_t
*)entry
->data
;
4036 || !other
->mdia
->mdhd
4037 || other
->mdia
->mdhd
->timescale
== 0
4038 || !other
->mdia
->minf
4039 || !other
->mdia
->minf
->stbl
4040 || !other
->mdia
->minf
->stbl
->stsc
4041 || !other
->mdia
->minf
->stbl
->stsc
->list
)
4042 return LSMASH_ERR_INVALID_DATA
;
4043 isom_chunk_t
*chunk
= &other
->cache
->chunk
;
4044 if( !chunk
->pool
|| chunk
->pool
->sample_count
== 0 )
4046 double diff
= ((double)sample
->dts
/ trak
->mdia
->mdhd
->timescale
)
4047 - ((double)chunk
->first_dts
/ other
->mdia
->mdhd
->timescale
);
4048 if( diff
> tolerance
&& (ret
= isom_output_cached_chunk( other
)) < 0 )
4050 /* Note: we don't flush the cached chunk in the current track and the current sample here
4051 * even if the conditional expression of '-diff > tolerance' meets.
4052 * That's useless because appending a sample to another track would be a good equivalent.
4053 * It's even harmful because it causes excess chunk division by calling
4054 * isom_output_cached_chunk() which always generates a new chunk.
4055 * Anyway some excess chunk division will be there, but rather less without it.
4056 * To completely avoid this, we need to observe at least whether the current sample will be placed
4057 * right next to the previous chunk of the same track or not. */
4059 /* anyway the current sample must be pooled. */
4060 return isom_pool_sample( current_pool
, sample
, samples_per_packet
);
4063 /* This function is for non-fragmented movie. */
4064 static int isom_append_sample( lsmash_file_t
*file
, uint32_t track_ID
, lsmash_sample_t
*sample
)
4066 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
4071 || !trak
->mdia
->mdhd
4072 || trak
->mdia
->mdhd
->timescale
== 0
4073 || !trak
->mdia
->minf
4074 || !trak
->mdia
->minf
->stbl
4075 || !trak
->mdia
->minf
->stbl
->stsd
4076 || !trak
->mdia
->minf
->stbl
->stsc
|| !trak
->mdia
->minf
->stbl
->stsc
->list
)
4077 return LSMASH_ERR_NAMELESS
;
4078 /* If there is no available Media Data Box to write samples, add and write a new one before any chunk offset is decided. */
4082 if( !isom_add_mdat( file
) )
4083 return LSMASH_ERR_NAMELESS
;
4084 file
->mdat
->manager
|= LSMASH_PLACEHOLDER
;
4085 if( (err
= isom_write_box( file
->bs
, (isom_box_t
*)file
->mdat
)) < 0 )
4087 assert( file
->free
);
4088 file
->size
+= file
->free
->size
+ file
->mdat
->size
;
4090 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
, sample
->index
);
4092 return LSMASH_ERR_NAMELESS
;
4093 if( isom_is_lpcm_audio( sample_entry
) )
4095 uint32_t frame_size
= ((isom_audio_entry_t
*)sample_entry
)->constBytesPerAudioPacket
;
4096 if( sample
->length
== frame_size
)
4097 return isom_append_sample_internal( trak
, sample
);
4098 else if( sample
->length
< frame_size
)
4099 return LSMASH_ERR_INVALID_DATA
;
4100 /* Append samples splitted into each LPCMFrame. */
4101 uint64_t dts
= sample
->dts
;
4102 uint64_t cts
= sample
->cts
;
4103 for( uint32_t offset
= 0; offset
< sample
->length
; offset
+= frame_size
)
4105 lsmash_sample_t
*lpcm_sample
= lsmash_create_sample( frame_size
);
4107 return LSMASH_ERR_MEMORY_ALLOC
;
4108 memcpy( lpcm_sample
->data
, sample
->data
+ offset
, frame_size
);
4109 lpcm_sample
->dts
= dts
++;
4110 lpcm_sample
->cts
= cts
++;
4111 lpcm_sample
->prop
= sample
->prop
;
4112 lpcm_sample
->index
= sample
->index
;
4113 if( (err
= isom_append_sample_internal( trak
, lpcm_sample
)) < 0 )
4115 lsmash_delete_sample( lpcm_sample
);
4119 lsmash_delete_sample( sample
);
4122 return isom_append_sample_internal( trak
, sample
);
4125 static int isom_output_cache( isom_trak_t
*trak
)
4128 isom_cache_t
*cache
= trak
->cache
;
4129 if( cache
->chunk
.pool
4130 && cache
->chunk
.pool
->sample_count
4131 && (err
= isom_output_cached_chunk( trak
)) < 0 )
4133 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
4134 for( lsmash_entry_t
*entry
= stbl
->sgpd_list
.head
; entry
; entry
= entry
->next
)
4136 isom_sgpd_t
*sgpd
= (isom_sgpd_t
*)entry
->data
;
4138 return LSMASH_ERR_INVALID_DATA
;
4139 switch( sgpd
->grouping_type
)
4141 case ISOM_GROUP_TYPE_RAP
:
4143 isom_rap_group_t
*group
= cache
->rap
;
4146 if( stbl
->file
->fragment
)
4149 return LSMASH_ERR_NAMELESS
;
4151 if( !group
->random_access
)
4153 group
->random_access
->num_leading_samples_known
= 1;
4156 case ISOM_GROUP_TYPE_ROLL
:
4157 case ISOM_GROUP_TYPE_PROL
:
4158 if( !cache
->roll
.pool
)
4160 if( stbl
->file
->fragment
)
4163 return LSMASH_ERR_NAMELESS
;
4165 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group( &stbl
->sbgp_list
);
4167 return LSMASH_ERR_NAMELESS
;
4168 if( (err
= isom_all_recovery_completed( sbgp
, cache
->roll
.pool
)) < 0 )
4178 int lsmash_flush_pooled_samples( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t last_sample_delta
)
4180 if( isom_check_initializer_present( root
) < 0 )
4181 return LSMASH_ERR_FUNCTION_PARAM
;
4182 lsmash_file_t
*file
= root
->file
;
4184 && file
->fragment
->movie
)
4185 return isom_flush_fragment_pooled_samples( file
, track_ID
, last_sample_delta
);
4186 if( file
!= file
->initializer
)
4187 return LSMASH_ERR_INVALID_DATA
;
4188 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
4192 || !trak
->mdia
->minf
4193 || !trak
->mdia
->minf
->stbl
4194 || !trak
->mdia
->minf
->stbl
->stsc
4195 || !trak
->mdia
->minf
->stbl
->stsc
->list
)
4196 return LSMASH_ERR_NAMELESS
;
4197 int err
= isom_output_cache( trak
);
4200 return lsmash_set_last_sample_delta( root
, track_ID
, last_sample_delta
);
4203 int lsmash_append_sample( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_sample_t
*sample
)
4205 if( isom_check_initializer_present( root
) < 0
4209 return LSMASH_ERR_FUNCTION_PARAM
;
4210 lsmash_file_t
*file
= root
->file
;
4211 /* We think max_chunk_duration == 0, which means all samples will be cached on memory, should be prevented.
4212 * This means removal of a feature that we used to have, but anyway very alone chunk does not make sense. */
4215 || !(file
->flags
& LSMASH_FILE_MODE_BOX
)
4216 || file
->max_chunk_duration
== 0
4217 || file
->max_async_tolerance
== 0 )
4218 return LSMASH_ERR_NAMELESS
;
4219 /* Write File Type Box here if it was not written yet. */
4220 if( file
->flags
& LSMASH_FILE_MODE_INITIALIZATION
)
4222 if( file
->ftyp
&& !(file
->ftyp
->manager
& LSMASH_WRITTEN_BOX
) )
4224 int err
= isom_write_box( file
->bs
, (isom_box_t
*)file
->ftyp
);
4227 file
->size
+= file
->ftyp
->size
;
4230 if( (file
->flags
& LSMASH_FILE_MODE_FRAGMENTED
)
4232 && file
->fragment
->pool
)
4233 return isom_append_fragment_sample( file
, track_ID
, sample
);
4234 if( file
!= file
->initializer
)
4235 return LSMASH_ERR_INVALID_DATA
;
4236 return isom_append_sample( file
, track_ID
, sample
);
4239 /*---- misc functions ----*/
4241 int lsmash_delete_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
)
4243 if( isom_check_initializer_present( root
) < 0 )
4244 return LSMASH_ERR_FUNCTION_PARAM
;
4245 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
4247 return LSMASH_ERR_NAMELESS
;
4248 isom_remove_box_by_itself( trak
->edts
);
4249 return isom_update_tkhd_duration( trak
);
4252 void lsmash_delete_tyrant_chapter( lsmash_root_t
*root
)
4254 if( isom_check_initializer_present( root
) < 0
4255 || !root
->file
->initializer
->moov
4256 || !root
->file
->initializer
->moov
->udta
)
4258 isom_remove_box_by_itself( root
->file
->moov
->udta
->chpl
);
4261 int lsmash_set_copyright( lsmash_root_t
*root
, uint32_t track_ID
, uint16_t ISO_language
, char *notice
)
4263 if( isom_check_initializer_present( root
) < 0
4264 || (ISO_language
&& ISO_language
< 0x800)
4266 return LSMASH_ERR_FUNCTION_PARAM
;
4267 lsmash_file_t
*file
= root
->file
;
4269 || !file
->isom_compatible
)
4270 return LSMASH_ERR_NAMELESS
;
4274 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
4275 if( !trak
|| (!trak
->udta
&& !isom_add_udta( trak
)) )
4276 return LSMASH_ERR_NAMELESS
;
4281 if( !file
->moov
->udta
&& !isom_add_udta( file
->moov
) )
4282 return LSMASH_ERR_NAMELESS
;
4283 udta
= file
->moov
->udta
;
4286 for( lsmash_entry_t
*entry
= udta
->cprt_list
.head
; entry
; entry
= entry
->next
)
4288 isom_cprt_t
*cprt
= (isom_cprt_t
*)entry
->data
;
4289 if( !cprt
|| cprt
->language
== ISO_language
)
4290 return LSMASH_ERR_NAMELESS
;
4292 if( !isom_add_cprt( udta
) )
4293 return LSMASH_ERR_NAMELESS
;
4294 isom_cprt_t
*cprt
= (isom_cprt_t
*)udta
->cprt_list
.tail
->data
;
4295 cprt
->language
= ISO_language
;
4296 cprt
->notice_length
= strlen( notice
) + 1;
4297 cprt
->notice
= lsmash_memdup( notice
, cprt
->notice_length
);