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
165 || ((lsmash_summary_t
*)summary
)->data_ref_index
== 0
166 || ((lsmash_summary_t
*)summary
)->data_ref_index
> UINT16_MAX
)
168 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
173 || !trak
->mdia
->minf
->stbl
174 || !trak
->mdia
->minf
->stbl
->stsd
)
176 isom_stsd_t
*stsd
= trak
->mdia
->minf
->stbl
->stsd
;
177 lsmash_entry_list_t
*list
= &stsd
->list
;
178 int ret
= LSMASH_ERR_NAMELESS
;
179 lsmash_codec_type_t sample_type
= ((lsmash_summary_t
*)summary
)->sample_type
;
180 if( lsmash_check_codec_type_identical( sample_type
, LSMASH_CODEC_TYPE_RAW
) )
182 if( trak
->mdia
->minf
->vmhd
)
183 ret
= isom_setup_visual_description( stsd
, sample_type
, (lsmash_video_summary_t
*)summary
);
184 else if( trak
->mdia
->minf
->smhd
)
185 ret
= isom_setup_audio_description( stsd
, sample_type
, (lsmash_audio_summary_t
*)summary
);
186 return ret
< 0 ? 0 : list
->entry_count
;
188 typedef void (*opaque_func_t
)( void );
189 static struct description_setup_table_tag
191 lsmash_codec_type_t type
;
193 } description_setup_table
[160] = { { LSMASH_CODEC_TYPE_INITIALIZER
, NULL
} };
194 if( !description_setup_table
[0].func
)
197 #define ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( type, func ) \
198 description_setup_table[i++] = (struct description_setup_table_tag){ type, (opaque_func_t)func }
199 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO
, isom_setup_visual_description
);
200 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO
, isom_setup_visual_description
);
201 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO
, isom_setup_visual_description
);
202 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC4_VIDEO
, isom_setup_visual_description
);
203 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO
, isom_setup_visual_description
);
204 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO
, isom_setup_visual_description
);
206 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO
, isom_setup_visual_description
);
207 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO
, isom_setup_visual_description
);
208 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO
, isom_setup_visual_description
);
209 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO
, isom_setup_visual_description
);
210 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO
, isom_setup_visual_description
);
211 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO
, isom_setup_visual_description
);
212 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO
, isom_setup_visual_description
);
213 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO
, isom_setup_visual_description
);
214 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO
, isom_setup_visual_description
);
216 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO
, isom_setup_visual_description
);
217 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO
, isom_setup_visual_description
);
218 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO
, isom_setup_visual_description
);
219 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO
, isom_setup_visual_description
);
220 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO
, isom_setup_visual_description
);
221 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO
, isom_setup_visual_description
);
222 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO
, isom_setup_visual_description
);
223 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO
, isom_setup_visual_description
);
224 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO
, isom_setup_visual_description
);
225 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO
, isom_setup_visual_description
);
226 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO
, isom_setup_visual_description
);
227 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO
, isom_setup_visual_description
);
228 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO
, isom_setup_visual_description
);
229 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO
, isom_setup_visual_description
);
230 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO
, isom_setup_visual_description
);
231 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO
, isom_setup_visual_description
);
232 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO
, isom_setup_visual_description
);
233 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO
, isom_setup_visual_description
);
234 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO
, isom_setup_visual_description
);
235 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO
, isom_setup_visual_description
);
236 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO
, isom_setup_visual_description
);
237 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO
, isom_setup_visual_description
);
238 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO
, isom_setup_visual_description
);
239 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO
, isom_setup_visual_description
);
240 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO
, isom_setup_visual_description
);
241 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO
, isom_setup_visual_description
);
242 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO
, isom_setup_visual_description
);
243 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO
, isom_setup_visual_description
);
244 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO
, isom_setup_visual_description
);
245 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO
, isom_setup_visual_description
);
246 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO
, isom_setup_audio_description
);
247 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO
, isom_setup_audio_description
);
248 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO
, isom_setup_audio_description
);
249 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO
, isom_setup_audio_description
);
250 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO
, isom_setup_audio_description
);
251 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO
, isom_setup_audio_description
);
252 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO
, isom_setup_audio_description
);
253 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO
, isom_setup_audio_description
);
254 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO
, isom_setup_audio_description
);
255 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO
, isom_setup_audio_description
);
257 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO
, isom_setup_audio_description
);
258 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO
, isom_setup_audio_description
);
259 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO
, isom_setup_audio_description
);
260 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO
, isom_setup_audio_description
);
261 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO
, isom_setup_audio_description
);
262 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO
, isom_setup_audio_description
);
263 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_RAW_AUDIO
, isom_setup_audio_description
);
264 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO
, isom_setup_audio_description
);
265 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO
, isom_setup_audio_description
);
266 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO
, isom_setup_audio_description
);
267 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO
, isom_setup_audio_description
);
268 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO
, isom_setup_audio_description
);
270 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MP4A_AUDIO
, isom_setup_audio_description
);
271 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MAC3_AUDIO
, isom_setup_audio_description
);
272 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MAC6_AUDIO
, isom_setup_audio_description
);
273 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_AGSM_AUDIO
, isom_setup_audio_description
);
274 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ALAW_AUDIO
, isom_setup_audio_description
);
275 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULAW_AUDIO
, isom_setup_audio_description
);
276 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FULLMP3_AUDIO
, isom_setup_audio_description
);
277 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM2_AUDIO
, isom_setup_audio_description
);
278 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM17_AUDIO
, isom_setup_audio_description
);
279 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_GSM49_AUDIO
, isom_setup_audio_description
);
280 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO
, isom_setup_audio_description
);
281 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO
, isom_setup_audio_description
);
282 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO
, isom_setup_audio_description
);
283 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO
, isom_setup_audio_description
);
284 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO
, isom_setup_audio_description
);
285 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO
, isom_setup_audio_description
);
286 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO
, isom_setup_audio_description
);
287 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO
, isom_setup_audio_description
);
288 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO
, isom_setup_audio_description
);
289 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED
, isom_setup_audio_description
);
290 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT
, isom_add_tx3g_description
);
291 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT
, isom_add_qt_text_description
);
293 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4S_SYSTEM
, isom_add_mp4s_entry
);
296 for( int i
= 0; description_setup_table
[i
].func
; i
++ )
297 if( lsmash_check_codec_type_identical( sample_type
, description_setup_table
[i
].type
) )
299 if( (opaque_func_t
)isom_setup_visual_description
== description_setup_table
[i
].func
)
300 ret
= isom_setup_visual_description( stsd
, sample_type
, (lsmash_video_summary_t
*)summary
);
301 else if( (opaque_func_t
)isom_setup_audio_description
== description_setup_table
[i
].func
)
302 ret
= isom_setup_audio_description( stsd
, sample_type
, (lsmash_audio_summary_t
*)summary
);
303 else if( (opaque_func_t
)isom_add_tx3g_description
== description_setup_table
[i
].func
)
304 ret
= isom_setup_tx3g_description( stsd
, (lsmash_summary_t
*)summary
);
305 else if( (opaque_func_t
)isom_add_qt_text_description
== description_setup_table
[i
].func
)
307 isom_qt_text_entry_t
*text
= isom_add_qt_text_description( stsd
);
310 text
->data_reference_index
= ((lsmash_summary_t
*)summary
)->data_ref_index
;
316 return ret
< 0 ? 0 : list
->entry_count
;
319 static int isom_add_stts_entry( isom_stbl_t
*stbl
, uint32_t sample_delta
)
323 || !stbl
->stts
->list
)
324 return LSMASH_ERR_NAMELESS
;
325 isom_stts_entry_t
*data
= lsmash_malloc( sizeof(isom_stts_entry_t
) );
327 return LSMASH_ERR_MEMORY_ALLOC
;
328 data
->sample_count
= 1;
329 data
->sample_delta
= sample_delta
;
330 if( lsmash_add_entry( stbl
->stts
->list
, data
) < 0 )
333 return LSMASH_ERR_MEMORY_ALLOC
;
338 static int isom_add_ctts_entry( isom_stbl_t
*stbl
, uint32_t sample_offset
)
342 || !stbl
->ctts
->list
)
343 return LSMASH_ERR_NAMELESS
;
344 isom_ctts_entry_t
*data
= lsmash_malloc( sizeof(isom_ctts_entry_t
) );
346 return LSMASH_ERR_MEMORY_ALLOC
;
347 data
->sample_count
= 1;
348 data
->sample_offset
= sample_offset
;
349 if( lsmash_add_entry( stbl
->ctts
->list
, data
) < 0 )
352 return LSMASH_ERR_MEMORY_ALLOC
;
357 static int isom_add_stsc_entry( isom_stbl_t
*stbl
, uint32_t first_chunk
, uint32_t samples_per_chunk
, uint32_t sample_description_index
)
361 || !stbl
->stsc
->list
)
362 return LSMASH_ERR_NAMELESS
;
363 isom_stsc_entry_t
*data
= lsmash_malloc( sizeof(isom_stsc_entry_t
) );
365 return LSMASH_ERR_MEMORY_ALLOC
;
366 data
->first_chunk
= first_chunk
;
367 data
->samples_per_chunk
= samples_per_chunk
;
368 data
->sample_description_index
= sample_description_index
;
369 if( lsmash_add_entry( stbl
->stsc
->list
, data
) < 0 )
372 return LSMASH_ERR_MEMORY_ALLOC
;
377 static int isom_add_stsz_entry( isom_stbl_t
*stbl
, uint32_t entry_size
)
381 return LSMASH_ERR_NAMELESS
;
382 isom_stsz_t
*stsz
= stbl
->stsz
;
383 /* retrieve initial sample_size */
384 if( stsz
->sample_count
== 0 )
385 stsz
->sample_size
= entry_size
;
386 /* if it seems constant access_unit size at present, update sample_count only */
387 if( !stsz
->list
&& stsz
->sample_size
== entry_size
)
389 ++ stsz
->sample_count
;
392 /* found sample_size varies, create sample_size list */
395 stsz
->list
= lsmash_create_entry_list();
397 return LSMASH_ERR_MEMORY_ALLOC
;
398 for( uint32_t i
= 0; i
< stsz
->sample_count
; i
++ )
400 isom_stsz_entry_t
*data
= lsmash_malloc( sizeof(isom_stsz_entry_t
) );
402 return LSMASH_ERR_MEMORY_ALLOC
;
403 data
->entry_size
= stsz
->sample_size
;
404 if( lsmash_add_entry( stsz
->list
, data
) < 0 )
407 return LSMASH_ERR_MEMORY_ALLOC
;
410 stsz
->sample_size
= 0;
412 isom_stsz_entry_t
*data
= lsmash_malloc( sizeof(isom_stsz_entry_t
) );
414 return LSMASH_ERR_MEMORY_ALLOC
;
415 data
->entry_size
= entry_size
;
416 if( lsmash_add_entry( stsz
->list
, data
) < 0 )
419 return LSMASH_ERR_MEMORY_ALLOC
;
421 ++ stsz
->sample_count
;
425 static int isom_add_stss_entry( isom_stbl_t
*stbl
, uint32_t sample_number
)
429 || !stbl
->stss
->list
)
430 return LSMASH_ERR_NAMELESS
;
431 isom_stss_entry_t
*data
= lsmash_malloc( sizeof(isom_stss_entry_t
) );
433 return LSMASH_ERR_MEMORY_ALLOC
;
434 data
->sample_number
= sample_number
;
435 if( lsmash_add_entry( stbl
->stss
->list
, data
) < 0 )
438 return LSMASH_ERR_MEMORY_ALLOC
;
443 static int isom_add_stps_entry( isom_stbl_t
*stbl
, uint32_t sample_number
)
447 || !stbl
->stps
->list
)
448 return LSMASH_ERR_NAMELESS
;
449 isom_stps_entry_t
*data
= lsmash_malloc( sizeof(isom_stps_entry_t
) );
451 return LSMASH_ERR_MEMORY_ALLOC
;
452 data
->sample_number
= sample_number
;
453 if( lsmash_add_entry( stbl
->stps
->list
, data
) < 0 )
456 return LSMASH_ERR_MEMORY_ALLOC
;
461 /* Between ISOBMFF and QTFF, the most significant 2-bit has different meaning.
462 * For the maximum compatibility, we set 0 to the most significant 2-bit when compatible with
463 * both ISOBMFF with AVCFF extensions and QTFF.
464 * compatibility == 0 -> neither AVCFF extensions nor QTFF compatible
465 * compatibility == 1 -> AVCFF extensions compatible
466 * compatibility == 2 -> QTFF compatible
467 * compatibility == 3 -> both AVCFF extensions and QTFF compatible */
468 static int isom_add_sdtp_entry( isom_box_t
*parent
, lsmash_sample_property_t
*prop
, int compatibility
)
470 if( !prop
|| !parent
)
471 return LSMASH_ERR_NAMELESS
;
472 isom_sdtp_t
*sdtp
= NULL
;
473 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_STBL
) )
474 sdtp
= ((isom_stbl_t
*)parent
)->sdtp
;
475 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
476 sdtp
= ((isom_traf_t
*)parent
)->sdtp
;
481 return LSMASH_ERR_NAMELESS
;
482 isom_sdtp_entry_t
*data
= lsmash_malloc( sizeof(isom_sdtp_entry_t
) );
484 return LSMASH_ERR_MEMORY_ALLOC
;
485 if( compatibility
== 1 )
486 data
->is_leading
= prop
->leading
& 0x03;
487 else if( compatibility
== 2 )
488 data
->is_leading
= prop
->allow_earlier
& 0x03;
491 data
->is_leading
= 0;
492 assert( compatibility
== 3 );
494 data
->sample_depends_on
= prop
->independent
& 0x03;
495 data
->sample_is_depended_on
= prop
->disposable
& 0x03;
496 data
->sample_has_redundancy
= prop
->redundant
& 0x03;
497 if( lsmash_add_entry( sdtp
->list
, data
) < 0 )
500 return LSMASH_ERR_MEMORY_ALLOC
;
505 static int isom_add_co64_entry( isom_stbl_t
*stbl
, uint64_t chunk_offset
)
509 || !stbl
->stco
->list
)
510 return LSMASH_ERR_NAMELESS
;
511 isom_co64_entry_t
*data
= lsmash_malloc( sizeof(isom_co64_entry_t
) );
513 return LSMASH_ERR_MEMORY_ALLOC
;
514 data
->chunk_offset
= chunk_offset
;
515 if( lsmash_add_entry( stbl
->stco
->list
, data
) < 0 )
518 return LSMASH_ERR_MEMORY_ALLOC
;
523 static int isom_convert_stco_to_co64( isom_stbl_t
*stbl
)
527 isom_stco_t
*stco
= stbl
->stco
;
529 if( !isom_add_co64( stbl
) )
531 err
= LSMASH_ERR_NAMELESS
;
534 /* move chunk_offset to co64 from stco */
535 for( lsmash_entry_t
*entry
= stco
->list
->head
; entry
; entry
= entry
->next
)
537 isom_stco_entry_t
*data
= (isom_stco_entry_t
*)entry
->data
;
538 if( (err
= isom_add_co64_entry( stbl
, data
->chunk_offset
)) < 0 )
542 isom_remove_box_by_itself( stco
);
546 static int isom_add_stco_entry( isom_stbl_t
*stbl
, uint64_t chunk_offset
)
550 || !stbl
->stco
->list
)
551 return LSMASH_ERR_NAMELESS
;
552 if( stbl
->stco
->large_presentation
)
553 return isom_add_co64_entry( stbl
, chunk_offset
);
554 if( chunk_offset
> UINT32_MAX
)
556 int err
= isom_convert_stco_to_co64( stbl
);
559 return isom_add_co64_entry( stbl
, chunk_offset
);
561 isom_stco_entry_t
*data
= lsmash_malloc( sizeof(isom_stco_entry_t
) );
563 return LSMASH_ERR_MEMORY_ALLOC
;
564 data
->chunk_offset
= (uint32_t)chunk_offset
;
565 if( lsmash_add_entry( stbl
->stco
->list
, data
) < 0 )
568 return LSMASH_ERR_MEMORY_ALLOC
;
573 static isom_sgpd_t
*isom_get_sample_group_description_common( lsmash_entry_list_t
*list
, uint32_t grouping_type
)
575 for( lsmash_entry_t
*entry
= list
->head
; entry
; entry
= entry
->next
)
577 isom_sgpd_t
*sgpd
= (isom_sgpd_t
*)entry
->data
;
581 if( sgpd
->grouping_type
== grouping_type
)
587 static isom_sbgp_t
*isom_get_sample_to_group_common( lsmash_entry_list_t
*list
, uint32_t grouping_type
)
589 for( lsmash_entry_t
*entry
= list
->head
; entry
; entry
= entry
->next
)
591 isom_sbgp_t
*sbgp
= (isom_sbgp_t
*)entry
->data
;
595 if( sbgp
->grouping_type
== grouping_type
)
601 isom_sgpd_t
*isom_get_sample_group_description( isom_stbl_t
*stbl
, uint32_t grouping_type
)
603 return isom_get_sample_group_description_common( &stbl
->sgpd_list
, grouping_type
);
606 isom_sbgp_t
*isom_get_sample_to_group( isom_stbl_t
*stbl
, uint32_t grouping_type
)
608 return isom_get_sample_to_group_common( &stbl
->sbgp_list
, grouping_type
);
611 isom_sgpd_t
*isom_get_roll_recovery_sample_group_description( lsmash_entry_list_t
*list
)
614 if( (sgpd
= isom_get_sample_group_description_common( list
, ISOM_GROUP_TYPE_ROLL
))
615 || (sgpd
= isom_get_sample_group_description_common( list
, ISOM_GROUP_TYPE_PROL
)) )
620 isom_sbgp_t
*isom_get_roll_recovery_sample_to_group( lsmash_entry_list_t
*list
)
623 if( (sbgp
= isom_get_sample_to_group_common( list
, ISOM_GROUP_TYPE_ROLL
))
624 || (sbgp
= isom_get_sample_to_group_common( list
, ISOM_GROUP_TYPE_PROL
)) )
629 isom_sgpd_t
*isom_get_fragment_sample_group_description( isom_traf_t
*traf
, uint32_t grouping_type
)
631 return isom_get_sample_group_description_common( &traf
->sgpd_list
, grouping_type
);
634 isom_sbgp_t
*isom_get_fragment_sample_to_group( isom_traf_t
*traf
, uint32_t grouping_type
)
636 return isom_get_sample_to_group_common( &traf
->sbgp_list
, grouping_type
);
639 static isom_rap_entry_t
*isom_add_rap_group_entry( isom_sgpd_t
*sgpd
)
643 isom_rap_entry_t
*data
= lsmash_malloc( sizeof(isom_rap_entry_t
) );
646 data
->description_length
= 0;
647 data
->num_leading_samples_known
= 0;
648 data
->num_leading_samples
= 0;
649 if( lsmash_add_entry( sgpd
->list
, data
) < 0 )
657 static isom_roll_entry_t
*isom_add_roll_group_entry( isom_sgpd_t
*sgpd
, int16_t roll_distance
)
661 isom_roll_entry_t
*data
= lsmash_malloc( sizeof(isom_roll_entry_t
) );
664 data
->description_length
= 0;
665 data
->roll_distance
= roll_distance
;
666 if( lsmash_add_entry( sgpd
->list
, data
) < 0 )
674 static isom_group_assignment_entry_t
*isom_add_group_assignment_entry( isom_sbgp_t
*sbgp
, uint32_t sample_count
, uint32_t group_description_index
)
678 isom_group_assignment_entry_t
*data
= lsmash_malloc( sizeof(isom_group_assignment_entry_t
) );
681 data
->sample_count
= sample_count
;
682 data
->group_description_index
= group_description_index
;
683 if( lsmash_add_entry( sbgp
->list
, data
) < 0 )
691 uint32_t isom_get_sample_count( isom_trak_t
*trak
)
696 || !trak
->mdia
->minf
->stbl
697 || !trak
->mdia
->minf
->stbl
->stsz
)
699 return trak
->mdia
->minf
->stbl
->stsz
->sample_count
;
702 static uint64_t isom_get_dts( isom_stts_t
*stts
, uint32_t sample_number
)
709 lsmash_entry_t
*entry
;
710 isom_stts_entry_t
*data
;
711 for( entry
= stts
->list
->head
; entry
; entry
= entry
->next
)
713 data
= (isom_stts_entry_t
*)entry
->data
;
716 if( i
+ data
->sample_count
> sample_number
)
718 dts
+= (uint64_t)data
->sample_delta
* data
->sample_count
;
719 i
+= data
->sample_count
;
723 dts
+= (uint64_t)data
->sample_delta
* (sample_number
- i
);
728 static uint64_t isom_get_cts( isom_stts_t
*stts
, isom_ctts_t
*ctts
, uint32_t sample_number
)
734 return isom_get_dts( stts
, sample_number
);
735 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. */
736 lsmash_entry_t
*entry
;
737 isom_ctts_entry_t
*data
;
738 if( sample_number
== 0 )
740 for( entry
= ctts
->list
->head
; entry
; entry
= entry
->next
)
742 data
= (isom_ctts_entry_t
*)entry
->data
;
745 if( i
+ data
->sample_count
> sample_number
)
747 i
+= data
->sample_count
;
751 return isom_get_dts( stts
, sample_number
) + data
->sample_offset
;
755 static int isom_replace_last_sample_delta( isom_stbl_t
*stbl
, uint32_t sample_delta
)
760 || !stbl
->stts
->list
->tail
761 || !stbl
->stts
->list
->tail
->data
)
762 return LSMASH_ERR_NAMELESS
;
763 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)stbl
->stts
->list
->tail
->data
;
764 if( sample_delta
!= last_stts_data
->sample_delta
)
766 if( last_stts_data
->sample_count
> 1 )
768 last_stts_data
->sample_count
-= 1;
769 int err
= isom_add_stts_entry( stbl
, sample_delta
);
774 last_stts_data
->sample_delta
= sample_delta
;
779 static int isom_update_mdhd_duration( isom_trak_t
*trak
, uint32_t last_sample_delta
)
787 || !trak
->mdia
->minf
->stbl
788 || !trak
->mdia
->minf
->stbl
->stts
789 || !trak
->mdia
->minf
->stbl
->stts
->list
)
790 return LSMASH_ERR_INVALID_DATA
;
791 lsmash_file_t
*file
= trak
->file
;
792 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
793 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
794 isom_stts_t
*stts
= stbl
->stts
;
795 isom_ctts_t
*ctts
= stbl
->ctts
;
796 isom_cslg_t
*cslg
= stbl
->cslg
;
798 uint32_t sample_count
= isom_get_sample_count( trak
);
799 if( sample_count
== 0 )
801 /* Return error if non-fragmented movie has no samples. */
802 if( !file
->fragment
&& !stts
->list
->entry_count
)
803 return LSMASH_ERR_INVALID_DATA
;
806 /* Now we have at least 1 sample, so do stts_entry. */
807 lsmash_entry_t
*last_stts
= stts
->list
->tail
;
808 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)last_stts
->data
;
809 if( sample_count
== 1 )
810 mdhd
->duration
= last_stts_data
->sample_delta
;
811 /* Now we have at least 2 samples,
812 * but dunno whether 1 stts_entry which has 2 samples or 2 stts_entry which has 1 samle each. */
815 /* use dts instead of cts */
816 mdhd
->duration
= isom_get_dts( stts
, sample_count
);
818 if( last_sample_delta
)
820 mdhd
->duration
+= last_sample_delta
;
821 if( (err
= isom_replace_last_sample_delta( stbl
, last_sample_delta
)) < 0 )
824 else if( last_stts_data
->sample_count
> 1 )
825 mdhd
->duration
+= last_stts_data
->sample_delta
; /* no need to update last_stts_data->sample_delta */
828 /* Remove the last entry. */
829 if( (err
= lsmash_remove_entry_tail( stts
->list
, NULL
)) < 0 )
831 /* copy the previous sample_delta. */
832 ++ ((isom_stts_entry_t
*)stts
->list
->tail
->data
)->sample_count
;
833 mdhd
->duration
+= ((isom_stts_entry_t
*)stts
->list
->tail
->data
)->sample_delta
;
839 || ctts
->list
->entry_count
== 0 )
840 return LSMASH_ERR_INVALID_DATA
;
842 uint64_t max_cts
= 0;
843 uint64_t max2_cts
= 0;
844 uint64_t min_cts
= UINT64_MAX
;
845 uint32_t max_offset
= 0;
846 uint32_t min_offset
= UINT32_MAX
;
847 int32_t ctd_shift
= trak
->cache
->timestamp
.ctd_shift
;
850 lsmash_entry_t
*stts_entry
= stts
->list
->head
;
851 lsmash_entry_t
*ctts_entry
= ctts
->list
->head
;
852 for( uint32_t i
= 0; i
< sample_count
; i
++ )
854 if( !ctts_entry
|| !stts_entry
)
855 return LSMASH_ERR_INVALID_DATA
;
856 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
857 isom_ctts_entry_t
*ctts_data
= (isom_ctts_entry_t
*)ctts_entry
->data
;
858 if( !stts_data
|| !ctts_data
)
859 return LSMASH_ERR_INVALID_DATA
;
863 /* Anyway, add composition to decode timeline shift for calculating maximum and minimum CTS correctly. */
864 int32_t sample_offset
= (int32_t)ctts_data
->sample_offset
;
865 cts
= dts
+ sample_offset
+ ctd_shift
;
866 max_offset
= LSMASH_MAX( (int32_t)max_offset
, sample_offset
);
867 min_offset
= LSMASH_MIN( (int32_t)min_offset
, sample_offset
);
871 cts
= dts
+ ctts_data
->sample_offset
;
872 max_offset
= LSMASH_MAX( max_offset
, ctts_data
->sample_offset
);
873 min_offset
= LSMASH_MIN( min_offset
, ctts_data
->sample_offset
);
875 min_cts
= LSMASH_MIN( min_cts
, cts
);
881 else if( max2_cts
< cts
)
883 dts
+= stts_data
->sample_delta
;
884 /* If finished sample_count of current entry, move to next. */
885 if( ++j
== ctts_data
->sample_count
)
887 ctts_entry
= ctts_entry
->next
;
890 if( ++k
== stts_data
->sample_count
)
892 stts_entry
= stts_entry
->next
;
896 dts
-= last_stts_data
->sample_delta
;
898 /* Overall presentation is extended exceeding this initial movie.
899 * So, any players shall display the movie exceeding the durations
900 * indicated in Movie Header Box, Track Header Boxes and Media Header Boxes.
901 * Samples up to the duration indicated in Movie Extends Header Box shall be displayed.
902 * In the absence of Movie Extends Header Box, all samples shall be displayed. */
903 mdhd
->duration
+= dts
+ last_sample_delta
;
906 if( !last_sample_delta
)
908 /* The spec allows an arbitrary value for the duration of the last sample. So, we pick last-1 sample's. */
909 last_sample_delta
= max_cts
- max2_cts
;
911 mdhd
->duration
= max_cts
- min_cts
+ last_sample_delta
;
912 /* To match dts and media duration, update stts and mdhd relatively. */
913 if( mdhd
->duration
> dts
)
914 last_sample_delta
= mdhd
->duration
- dts
;
916 mdhd
->duration
= dts
+ last_sample_delta
; /* media duration must not less than last dts. */
918 int err
= isom_replace_last_sample_delta( stbl
, last_sample_delta
);
921 /* Explicit composition information and timeline shifting */
922 if( cslg
|| file
->qt_compatible
|| file
->max_isom_version
>= 4 )
926 /* Remove composition to decode timeline shift. */
927 max_cts
-= ctd_shift
;
928 max2_cts
-= ctd_shift
;
929 min_cts
-= ctd_shift
;
931 int64_t composition_end_time
= max_cts
+ (max_cts
- max2_cts
);
933 && ((int32_t)min_offset
<= INT32_MAX
) && ((int32_t)max_offset
<= INT32_MAX
)
934 && ((int64_t)min_cts
<= INT32_MAX
) && (composition_end_time
<= INT32_MAX
) )
938 if( !isom_add_cslg( trak
->mdia
->minf
->stbl
) )
939 return LSMASH_ERR_NAMELESS
;
942 cslg
->compositionToDTSShift
= ctd_shift
;
943 cslg
->leastDecodeToDisplayDelta
= min_offset
;
944 cslg
->greatestDecodeToDisplayDelta
= max_offset
;
945 cslg
->compositionStartTime
= min_cts
;
946 cslg
->compositionEndTime
= composition_end_time
;
949 isom_remove_box_by_itself( cslg
);
952 if( mdhd
->duration
> UINT32_MAX
&& !file
->undefined_64_ver
)
957 static int isom_update_mvhd_duration( isom_moov_t
*moov
)
961 || !moov
->mvhd
->file
)
962 return LSMASH_ERR_INVALID_DATA
;
963 isom_mvhd_t
*mvhd
= moov
->mvhd
;
965 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
967 /* We pick maximum track duration as movie duration. */
968 isom_trak_t
*data
= (isom_trak_t
*)entry
->data
;
971 return LSMASH_ERR_INVALID_DATA
;
972 mvhd
->duration
= entry
!= moov
->trak_list
.head
973 ? LSMASH_MAX( mvhd
->duration
, data
->tkhd
->duration
)
974 : data
->tkhd
->duration
;
976 if( mvhd
->duration
> UINT32_MAX
&& !mvhd
->file
->undefined_64_ver
)
981 int isom_update_tkhd_duration( isom_trak_t
*trak
)
986 || !trak
->file
->moov
)
987 return LSMASH_ERR_INVALID_DATA
;
988 lsmash_file_t
*file
= trak
->file
;
989 isom_tkhd_t
*tkhd
= trak
->tkhd
;
993 || !trak
->edts
->elst
)
995 /* If this presentation might be extended or this track doesn't have edit list, calculate track duration from media duration. */
999 || trak
->mdia
->mdhd
->timescale
== 0 )
1000 return LSMASH_ERR_INVALID_DATA
;
1001 if( trak
->mdia
->mdhd
->duration
== 0 )
1003 int err
= isom_update_mdhd_duration( trak
, 0 );
1007 tkhd
->duration
= trak
->mdia
->mdhd
->duration
* ((double)file
->moov
->mvhd
->timescale
/ trak
->mdia
->mdhd
->timescale
);
1011 /* If the presentation won't be extended and this track has any edit, then track duration is just the sum of the segment_duartions. */
1012 for( lsmash_entry_t
*entry
= trak
->edts
->elst
->list
->head
; entry
; entry
= entry
->next
)
1014 isom_elst_entry_t
*data
= (isom_elst_entry_t
*)entry
->data
;
1016 return LSMASH_ERR_INVALID_DATA
;
1017 tkhd
->duration
+= data
->segment_duration
;
1020 if( tkhd
->duration
> UINT32_MAX
&& !file
->undefined_64_ver
)
1022 if( !file
->fragment
&& tkhd
->duration
== 0 )
1023 tkhd
->duration
= tkhd
->version
== 1 ? 0xffffffffffffffff : 0xffffffff;
1024 return isom_update_mvhd_duration( file
->moov
);
1027 int lsmash_update_track_duration( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t last_sample_delta
)
1029 if( isom_check_initializer_present( root
) < 0 )
1030 return LSMASH_ERR_FUNCTION_PARAM
;
1031 lsmash_file_t
*file
= root
->file
;
1032 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1034 return LSMASH_ERR_NAMELESS
;
1035 int err
= isom_update_mdhd_duration( trak
, last_sample_delta
);
1038 /* If the presentation won't be extended and this track has any edit, we don't change or update duration in tkhd. */
1039 if( !file
->fragment
&& trak
->edts
&& trak
->edts
->elst
)
1040 err
= isom_update_mvhd_duration( file
->moov
); /* Only update movie duration. */
1042 err
= isom_update_tkhd_duration( trak
); /* Also update movie duration internally. */
1046 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
)
1048 if( *sample_number_in_entry
!= sample_count_in_entry
)
1050 *sample_number_in_entry
+= 1;
1053 /* Precede the next entry. */
1054 *sample_number_in_entry
= 1;
1057 *entry
= (*entry
)->next
;
1058 if( *entry
&& !(*entry
)->data
)
1059 return LSMASH_ERR_INVALID_DATA
;
1064 static int isom_calculate_bitrate_description( isom_mdia_t
*mdia
, uint32_t *bufferSizeDB
, uint32_t *maxBitrate
, uint32_t *avgBitrate
, uint32_t sample_description_index
)
1066 isom_stsz_t
*stsz
= mdia
->minf
->stbl
->stsz
;
1067 lsmash_entry_t
*stsz_entry
= stsz
->list
? stsz
->list
->head
: NULL
;
1068 lsmash_entry_t
*stts_entry
= mdia
->minf
->stbl
->stts
->list
->head
;
1069 lsmash_entry_t
*stsc_entry
= NULL
;
1070 lsmash_entry_t
*next_stsc_entry
= mdia
->minf
->stbl
->stsc
->list
->head
;
1071 isom_stts_entry_t
*stts_data
= NULL
;
1072 isom_stsc_entry_t
*stsc_data
= NULL
;
1073 if( next_stsc_entry
&& !next_stsc_entry
->data
)
1074 return LSMASH_ERR_INVALID_DATA
;
1077 uint32_t time_wnd
= 0;
1078 uint32_t timescale
= mdia
->mdhd
->timescale
;
1079 uint32_t chunk_number
= 0;
1080 uint32_t sample_number_in_stts
= 1;
1081 uint32_t sample_number_in_chunk
= 1;
1088 if( !stsc_data
|| sample_number_in_chunk
== stsc_data
->samples_per_chunk
)
1090 /* Move the next chunk. */
1091 sample_number_in_chunk
= 1;
1093 /* Check if the next entry is broken. */
1094 while( next_stsc_entry
&& ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
< chunk_number
)
1096 /* Just skip broken next entry. */
1097 next_stsc_entry
= next_stsc_entry
->next
;
1098 if( next_stsc_entry
&& !next_stsc_entry
->data
)
1099 return LSMASH_ERR_INVALID_DATA
;
1101 /* Check if the next chunk belongs to the next sequence of chunks. */
1102 if( next_stsc_entry
&& ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
== chunk_number
)
1104 stsc_entry
= next_stsc_entry
;
1105 next_stsc_entry
= next_stsc_entry
->next
;
1106 if( next_stsc_entry
&& !next_stsc_entry
->data
)
1107 return LSMASH_ERR_INVALID_DATA
;
1108 stsc_data
= (isom_stsc_entry_t
*)stsc_entry
->data
;
1109 /* Check if the next contiguous chunks belong to given sample description. */
1110 if( stsc_data
->sample_description_index
!= sample_description_index
)
1112 /* Skip chunks which don't belong to given sample description. */
1113 uint32_t number_of_skips
= 0;
1114 uint32_t first_chunk
= stsc_data
->first_chunk
;
1115 uint32_t samples_per_chunk
= stsc_data
->samples_per_chunk
;
1116 while( next_stsc_entry
)
1118 if( ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->sample_description_index
!= sample_description_index
)
1120 stsc_data
= (isom_stsc_entry_t
*)next_stsc_entry
->data
;
1121 number_of_skips
+= (stsc_data
->first_chunk
- first_chunk
) * samples_per_chunk
;
1122 first_chunk
= stsc_data
->first_chunk
;
1123 samples_per_chunk
= stsc_data
->samples_per_chunk
;
1125 else if( ((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
<= first_chunk
)
1126 ; /* broken entry */
1129 /* Just skip the next entry. */
1130 next_stsc_entry
= next_stsc_entry
->next
;
1131 if( next_stsc_entry
&& !next_stsc_entry
->data
)
1132 return LSMASH_ERR_INVALID_DATA
;
1134 if( !next_stsc_entry
)
1135 break; /* There is no more chunks which don't belong to given sample description. */
1136 number_of_skips
+= (((isom_stsc_entry_t
*)next_stsc_entry
->data
)->first_chunk
- first_chunk
) * samples_per_chunk
;
1137 for( uint32_t i
= 0; i
< number_of_skips
; i
++ )
1143 stsz_entry
= stsz_entry
->next
;
1147 if( (err
= isom_increment_sample_number_in_entry( &sample_number_in_stts
,
1148 ((isom_stts_entry_t
*)stts_entry
->data
)->sample_count
,
1149 &stts_entry
)) < 0 )
1152 if( (stsz
->list
&& !stsz_entry
) || !stts_entry
)
1154 chunk_number
= stsc_data
->first_chunk
;
1159 ++sample_number_in_chunk
;
1160 /* Get current sample's size. */
1166 isom_stsz_entry_t
*stsz_data
= (isom_stsz_entry_t
*)stsz_entry
->data
;
1168 return LSMASH_ERR_INVALID_DATA
;
1169 size
= stsz_data
->entry_size
;
1170 stsz_entry
= stsz_entry
->next
;
1173 size
= stsz
->sample_size
;
1174 /* Get current sample's DTS. */
1176 dts
+= stts_data
->sample_delta
;
1177 stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
1179 return LSMASH_ERR_INVALID_DATA
;
1180 if( (err
= isom_increment_sample_number_in_entry( &sample_number_in_stts
, stts_data
->sample_count
, &stts_entry
)) < 0 )
1182 /* Calculate bitrate description. */
1183 if( *bufferSizeDB
< size
)
1184 *bufferSizeDB
= size
;
1185 *avgBitrate
+= size
;
1187 if( dts
> time_wnd
+ timescale
)
1189 if( rate
> *maxBitrate
)
1195 double duration
= (double)mdia
->mdhd
->duration
/ timescale
;
1196 *avgBitrate
= (uint32_t)(*avgBitrate
/ duration
);
1197 if( *maxBitrate
== 0 )
1198 *maxBitrate
= *avgBitrate
;
1199 /* Convert to bits per second. */
1205 int isom_update_bitrate_description( isom_mdia_t
*mdia
)
1210 || !mdia
->minf
->stbl
)
1211 return LSMASH_ERR_INVALID_DATA
;
1212 isom_stbl_t
*stbl
= mdia
->minf
->stbl
;
1215 || !stbl
->stsc
|| !stbl
->stsc
->list
1216 || !stbl
->stts
|| !stbl
->stts
->list
)
1217 return LSMASH_ERR_INVALID_DATA
;
1218 uint32_t sample_description_index
= 0;
1219 for( lsmash_entry_t
*entry
= stbl
->stsd
->list
.head
; entry
; entry
= entry
->next
)
1221 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)entry
->data
;
1223 return LSMASH_ERR_INVALID_DATA
;
1224 ++sample_description_index
;
1226 uint32_t bufferSizeDB
;
1227 uint32_t maxBitrate
;
1228 uint32_t avgBitrate
;
1229 /* set bitrate info */
1230 lsmash_codec_type_t sample_type
= sample_entry
->type
;
1231 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC1_VIDEO
)
1232 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC2_VIDEO
)
1233 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC3_VIDEO
)
1234 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC4_VIDEO
)
1235 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_HVC1_VIDEO
)
1236 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_HEV1_VIDEO
) )
1238 isom_visual_entry_t
*stsd_data
= (isom_visual_entry_t
*)sample_entry
;
1240 return LSMASH_ERR_INVALID_DATA
;
1241 isom_btrt_t
*btrt
= (isom_btrt_t
*)isom_get_extension_box_format( &stsd_data
->extensions
, ISOM_BOX_TYPE_BTRT
);
1244 if( (err
= isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
)) < 0 )
1246 btrt
->bufferSizeDB
= bufferSizeDB
;
1247 btrt
->maxBitrate
= maxBitrate
;
1248 btrt
->avgBitrate
= avgBitrate
;
1251 else if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4V_VIDEO
) )
1253 isom_visual_entry_t
*stsd_data
= (isom_visual_entry_t
*)sample_entry
;
1255 return LSMASH_ERR_INVALID_DATA
;
1256 isom_esds_t
*esds
= (isom_esds_t
*)isom_get_extension_box_format( &stsd_data
->extensions
, ISOM_BOX_TYPE_ESDS
);
1257 if( !esds
|| !esds
->ES
)
1258 return LSMASH_ERR_INVALID_DATA
;
1259 if( (err
= isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
)) < 0 )
1261 /* FIXME: avgBitrate is 0 only if VBR in proper. */
1262 if( (err
= mp4sys_update_DecoderConfigDescriptor( esds
->ES
, bufferSizeDB
, maxBitrate
, 0 )) < 0 )
1265 else if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4A_AUDIO
) )
1267 isom_audio_entry_t
*stsd_data
= (isom_audio_entry_t
*)sample_entry
;
1269 return LSMASH_ERR_INVALID_DATA
;
1270 isom_esds_t
*esds
= NULL
;
1271 if( ((isom_audio_entry_t
*)sample_entry
)->version
)
1273 /* MPEG-4 Audio in QTFF */
1274 isom_wave_t
*wave
= (isom_wave_t
*)isom_get_extension_box_format( &stsd_data
->extensions
, QT_BOX_TYPE_WAVE
);
1276 return LSMASH_ERR_INVALID_DATA
;
1277 esds
= (isom_esds_t
*)isom_get_extension_box_format( &wave
->extensions
, ISOM_BOX_TYPE_ESDS
);
1280 esds
= (isom_esds_t
*)isom_get_extension_box_format( &stsd_data
->extensions
, ISOM_BOX_TYPE_ESDS
);
1281 if( !esds
|| !esds
->ES
)
1282 return LSMASH_ERR_INVALID_DATA
;
1283 if( (err
= isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
)) < 0 )
1285 /* FIXME: avgBitrate is 0 only if VBR in proper. */
1286 if( (err
= mp4sys_update_DecoderConfigDescriptor( esds
->ES
, bufferSizeDB
, maxBitrate
, 0 )) < 0 )
1289 else if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_ALAC_AUDIO
)
1290 || lsmash_check_codec_type_identical( sample_type
, QT_CODEC_TYPE_ALAC_AUDIO
) )
1292 isom_audio_entry_t
*alac
= (isom_audio_entry_t
*)sample_entry
;
1294 return LSMASH_ERR_INVALID_DATA
;
1295 uint8_t *exdata
= NULL
;
1296 uint32_t exdata_size
= 0;
1297 isom_box_t
*alac_ext
= isom_get_extension_box( &alac
->extensions
, QT_BOX_TYPE_WAVE
);
1300 /* Apple Lossless Audio inside QuickTime file format
1301 * Though average bitrate field we found is always set to 0 apparently,
1302 * we set up maxFrameBytes and avgBitRate fields. */
1303 if( alac_ext
->manager
& LSMASH_BINARY_CODED_BOX
)
1304 exdata
= isom_get_child_box_position( alac_ext
->binary
, alac_ext
->size
, QT_BOX_TYPE_ALAC
, &exdata_size
);
1307 isom_wave_t
*wave
= (isom_wave_t
*)alac_ext
;
1308 isom_box_t
*wave_ext
= isom_get_extension_box( &wave
->extensions
, QT_BOX_TYPE_ALAC
);
1309 if( !wave_ext
|| !(wave_ext
->manager
& LSMASH_BINARY_CODED_BOX
) )
1310 return LSMASH_ERR_INVALID_DATA
;
1311 exdata
= wave_ext
->binary
;
1312 exdata_size
= wave_ext
->size
;
1317 /* Apple Lossless Audio inside ISO Base Media file format */
1318 isom_box_t
*ext
= isom_get_extension_box( &alac
->extensions
, ISOM_BOX_TYPE_ALAC
);
1319 if( !ext
|| !(ext
->manager
& LSMASH_BINARY_CODED_BOX
) )
1320 return LSMASH_ERR_INVALID_DATA
;
1321 exdata
= ext
->binary
;
1322 exdata_size
= ext
->size
;
1324 if( !exdata
|| exdata_size
< 36 )
1325 return LSMASH_ERR_INVALID_DATA
;
1326 if( (err
= isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
)) < 0 )
1330 LSMASH_SET_BE32( &exdata
[0], bufferSizeDB
);
1332 LSMASH_SET_BE32( &exdata
[4], avgBitrate
);
1334 else if( isom_is_waveform_audio( sample_type
) )
1336 isom_box_t
*ext
= isom_get_extension_box( &sample_entry
->extensions
, QT_BOX_TYPE_WAVE
);
1338 return LSMASH_ERR_INVALID_DATA
;
1339 uint8_t *exdata
= NULL
;
1340 uint32_t exdata_size
= 0;
1341 if( ext
->manager
& LSMASH_BINARY_CODED_BOX
)
1342 exdata
= isom_get_child_box_position( ext
->binary
, ext
->size
, sample_type
, &exdata_size
);
1345 isom_wave_t
*wave
= (isom_wave_t
*)ext
;
1346 isom_box_t
*wave_ext
= isom_get_extension_box( &wave
->extensions
, sample_type
);
1347 if( !wave_ext
|| !(wave_ext
->manager
& LSMASH_BINARY_CODED_BOX
) )
1348 return LSMASH_ERR_INVALID_DATA
;
1349 exdata
= wave_ext
->binary
;
1350 exdata_size
= wave_ext
->size
;
1352 /* Check whether exdata is valid or not. */
1353 if( !exdata
|| exdata_size
< ISOM_BASEBOX_COMMON_SIZE
+ 18 )
1354 return LSMASH_ERR_INVALID_DATA
;
1355 exdata
+= ISOM_BASEBOX_COMMON_SIZE
;
1356 uint16_t cbSize
= LSMASH_GET_LE16( &exdata
[16] );
1357 if( exdata_size
< ISOM_BASEBOX_COMMON_SIZE
+ 18 + cbSize
)
1358 return LSMASH_ERR_INVALID_DATA
;
1359 /* WAVEFORMATEX.nAvgBytesPerSec */
1360 if( (err
= isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
)) < 0 )
1362 uint32_t nAvgBytesPerSec
= avgBitrate
/ 8;
1363 LSMASH_SET_LE32( &exdata
[8], nAvgBytesPerSec
);
1364 if( lsmash_check_codec_type_identical( sample_type
, QT_CODEC_TYPE_FULLMP3_AUDIO
)
1365 || lsmash_check_codec_type_identical( sample_type
, QT_CODEC_TYPE_MP3_AUDIO
) )
1367 /* MPEGLAYER3WAVEFORMAT.nBlockSize */
1368 uint32_t nSamplesPerSec
= LSMASH_GET_LE32( &exdata
[ 4] );
1369 uint16_t nFramesPerBlock
= LSMASH_GET_LE16( &exdata
[26] );
1370 uint16_t padding
= 0; /* FIXME? */
1371 uint16_t nBlockSize
= (144 * (avgBitrate
/ nSamplesPerSec
) + padding
) * nFramesPerBlock
;
1372 LSMASH_SET_LE16( &exdata
[24], nBlockSize
);
1375 else if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_DTSC_AUDIO
)
1376 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_DTSE_AUDIO
)
1377 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_DTSH_AUDIO
)
1378 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_DTSL_AUDIO
) )
1380 isom_audio_entry_t
*dts_audio
= (isom_audio_entry_t
*)sample_entry
;
1382 return LSMASH_ERR_INVALID_DATA
;
1383 isom_box_t
*ext
= isom_get_extension_box( &dts_audio
->extensions
, ISOM_BOX_TYPE_DDTS
);
1384 if( !(ext
&& (ext
->manager
& LSMASH_BINARY_CODED_BOX
) && ext
->binary
&& ext
->size
>= 28) )
1385 return LSMASH_ERR_INVALID_DATA
;
1386 if( (err
= isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
)) < 0 )
1388 if( !stbl
->stsz
->list
)
1389 maxBitrate
= avgBitrate
;
1390 uint8_t *exdata
= ext
->binary
+ 12;
1391 LSMASH_SET_BE32( &exdata
[0], maxBitrate
);
1392 LSMASH_SET_BE32( &exdata
[4], avgBitrate
);
1394 else if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_EC_3_AUDIO
) )
1396 isom_audio_entry_t
*eac3
= (isom_audio_entry_t
*)sample_entry
;
1398 return LSMASH_ERR_INVALID_DATA
;
1399 isom_box_t
*ext
= isom_get_extension_box( &eac3
->extensions
, ISOM_BOX_TYPE_DEC3
);
1400 if( !(ext
&& (ext
->manager
& LSMASH_BINARY_CODED_BOX
) && ext
->binary
&& ext
->size
>= 10) )
1401 return LSMASH_ERR_INVALID_DATA
;
1403 if( stbl
->stsz
->list
)
1405 if( (err
= isom_calculate_bitrate_description( mdia
, &bufferSizeDB
, &maxBitrate
, &avgBitrate
, sample_description_index
)) < 0 )
1407 bitrate
= maxBitrate
/ 1000; /* Use maximum bitrate if VBR. */
1410 bitrate
= stbl
->stsz
->sample_size
* (eac3
->samplerate
>> 16) / 192000; /* 192000 == 1536 * 1000 / 8 */
1411 uint8_t *exdata
= ext
->binary
+ 8;
1412 exdata
[0] = (bitrate
>> 5) & 0xff;
1413 exdata
[1] = (bitrate
& 0x1f) << 3;
1416 return sample_description_index
? 0 : LSMASH_ERR_INVALID_DATA
;
1419 static inline uint64_t isom_get_current_mp4time( void )
1421 return (uint64_t)time( NULL
) + ISOM_MAC_EPOCH_OFFSET
;
1424 static int isom_set_media_creation_time( isom_trak_t
*trak
, uint64_t current_mp4time
)
1427 || !trak
->mdia
->mdhd
)
1428 return LSMASH_ERR_NAMELESS
;
1429 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
1430 if( mdhd
->creation_time
== 0 )
1431 mdhd
->creation_time
= mdhd
->modification_time
= current_mp4time
;
1435 static int isom_set_track_creation_time( isom_trak_t
*trak
, uint64_t current_mp4time
)
1439 return LSMASH_ERR_NAMELESS
;
1440 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1441 if( tkhd
->creation_time
== 0 )
1442 tkhd
->creation_time
= tkhd
->modification_time
= current_mp4time
;
1443 return isom_set_media_creation_time( trak
, current_mp4time
);
1446 static int isom_set_movie_creation_time( lsmash_file_t
*file
)
1450 || !file
->moov
->mvhd
)
1451 return LSMASH_ERR_NAMELESS
;
1452 uint64_t current_mp4time
= isom_get_current_mp4time();
1453 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
1455 int err
= isom_set_track_creation_time( (isom_trak_t
*)entry
->data
, current_mp4time
);
1459 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
1460 if( mvhd
->creation_time
== 0 )
1461 mvhd
->creation_time
= mvhd
->modification_time
= current_mp4time
;
1465 int isom_setup_handler_reference( isom_hdlr_t
*hdlr
, uint32_t media_type
)
1467 isom_box_t
*parent
= hdlr
->parent
;
1468 lsmash_file_t
*file
= hdlr
->file
;
1469 if( !parent
|| !file
)
1470 return LSMASH_ERR_NAMELESS
;
1471 isom_mdia_t
*mdia
= lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_MDIA
) ? (isom_mdia_t
*)parent
: NULL
;
1472 isom_meta_t
*meta
= lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_META
) ? (isom_meta_t
*)parent
1473 : lsmash_check_box_type_identical( parent
->type
, QT_BOX_TYPE_META
) ? (isom_meta_t
*)parent
: NULL
;
1474 uint32_t type
= mdia
? (file
->qt_compatible
? QT_HANDLER_TYPE_MEDIA
: 0) : (meta
? 0 : QT_HANDLER_TYPE_DATA
);
1475 uint32_t subtype
= media_type
;
1476 hdlr
->componentType
= type
;
1477 hdlr
->componentSubtype
= subtype
;
1478 char *type_name
= NULL
;
1479 char *subtype_name
= NULL
;
1480 uint8_t type_name_length
= 0;
1481 uint8_t subtype_name_length
= 0;
1483 type_name
= "Media ";
1485 type_name
= "Metadata ";
1486 else /* if( minf ) */
1487 type_name
= "Data ";
1488 type_name_length
= strlen( type_name
);
1493 uint8_t subtype_name_length
;
1496 { ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
, "Sound ", 6 },
1497 { ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
, "Video ", 6 },
1498 { ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK
, "Hint ", 5 },
1499 { ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK
, "Metadata ", 9 },
1500 { ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK
, "Text ", 5 },
1501 { ISOM_META_HANDLER_TYPE_ITUNES_METADATA
, "iTunes ", 7 },
1502 { QT_REFERENCE_HANDLER_TYPE_ALIAS
, "Alias ", 6 },
1503 { QT_REFERENCE_HANDLER_TYPE_RESOURCE
, "Resource ", 9 },
1504 { QT_REFERENCE_HANDLER_TYPE_URL
, "URL ", 4 },
1505 { subtype
, "Unknown ", 8 }
1507 for( int i
= 0; subtype_table
[i
].subtype
; i
++ )
1508 if( subtype
== subtype_table
[i
].subtype
)
1510 subtype_name
= subtype_table
[i
].subtype_name
;
1511 subtype_name_length
= subtype_table
[i
].subtype_name_length
;
1514 uint32_t name_length
= 15 + subtype_name_length
+ type_name_length
+ file
->isom_compatible
+ file
->qt_compatible
;
1515 uint8_t *name
= lsmash_malloc( name_length
);
1517 return LSMASH_ERR_MEMORY_ALLOC
;
1518 if( file
->qt_compatible
)
1519 name
[0] = name_length
& 0xff;
1520 memcpy( name
+ file
->qt_compatible
, "L-SMASH ", 8 );
1521 memcpy( name
+ file
->qt_compatible
+ 8, subtype_name
, subtype_name_length
);
1522 memcpy( name
+ file
->qt_compatible
+ 8 + subtype_name_length
, type_name
, type_name_length
);
1523 memcpy( name
+ file
->qt_compatible
+ 8 + subtype_name_length
+ type_name_length
, "Handler", 7 );
1524 if( file
->isom_compatible
)
1525 name
[name_length
- 1] = 0;
1526 hdlr
->componentName
= name
;
1527 hdlr
->componentName_length
= name_length
;
1531 isom_trak_t
*isom_track_create( lsmash_file_t
*file
, lsmash_media_type media_type
)
1533 /* Don't allow to create a new track if the initial movie is already written. */
1534 if( (file
->fragment
&& file
->fragment
->movie
)
1535 || (file
->moov
&& (file
->moov
->manager
& LSMASH_WRITTEN_BOX
)) )
1537 isom_trak_t
*trak
= isom_add_trak( file
->moov
);
1540 || !trak
->file
->moov
1541 || !trak
->file
->moov
->mvhd
)
1543 if( !isom_add_tkhd( trak
)
1544 || !isom_add_mdia( trak
)
1545 || !isom_add_mdhd( trak
->mdia
)
1546 || !isom_add_minf( trak
->mdia
)
1547 || !isom_add_dinf( trak
->mdia
->minf
)
1548 || !isom_add_dref( trak
->mdia
->minf
->dinf
)
1549 || !isom_add_stbl( trak
->mdia
->minf
)
1550 || !isom_add_stsd( trak
->mdia
->minf
->stbl
)
1551 || !isom_add_stts( trak
->mdia
->minf
->stbl
)
1552 || !isom_add_stsc( trak
->mdia
->minf
->stbl
)
1553 || !isom_add_stco( trak
->mdia
->minf
->stbl
)
1554 || !isom_add_stsz( trak
->mdia
->minf
->stbl
) )
1556 if( !isom_add_hdlr( trak
->mdia
)
1557 || isom_setup_handler_reference( trak
->mdia
->hdlr
, media_type
) < 0 )
1559 if( file
->qt_compatible
)
1561 if( !isom_add_hdlr( trak
->mdia
->minf
)
1562 || isom_setup_handler_reference( trak
->mdia
->minf
->hdlr
, QT_REFERENCE_HANDLER_TYPE_URL
) < 0 )
1565 switch( media_type
)
1567 case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
:
1568 if( !isom_add_vmhd( trak
->mdia
->minf
) )
1570 trak
->mdia
->minf
->vmhd
->flags
= 0x000001;
1572 case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
:
1573 if( !isom_add_smhd( trak
->mdia
->minf
) )
1575 trak
->cache
->is_audio
= 1;
1577 case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK
:
1578 if( !isom_add_hmhd( trak
->mdia
->minf
) )
1581 case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK
:
1582 if( file
->qt_compatible
|| file
->itunes_movie
)
1584 if( !isom_add_gmhd( trak
->mdia
->minf
)
1585 || !isom_add_gmin( trak
->mdia
->minf
->gmhd
)
1586 || !isom_add_text( trak
->mdia
->minf
->gmhd
) )
1588 /* Default Text Media Information Box. */
1590 isom_text_t
*text
= trak
->mdia
->minf
->gmhd
->text
;
1591 text
->matrix
[0] = 0x00010000;
1592 text
->matrix
[4] = 0x00010000;
1593 text
->matrix
[8] = 0x40000000;
1597 goto fail
; /* We support only reference text media track for chapter yet. */
1600 if( !isom_add_nmhd( trak
->mdia
->minf
) )
1604 /* Default Track Header Box. */
1606 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1607 if( media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
)
1608 tkhd
->volume
= 0x0100;
1609 tkhd
->matrix
[0] = 0x00010000;
1610 tkhd
->matrix
[4] = 0x00010000;
1611 tkhd
->matrix
[8] = 0x40000000;
1612 tkhd
->duration
= 0xffffffff;
1613 tkhd
->track_ID
= trak
->file
->moov
->mvhd
->next_track_ID
++;
1615 trak
->mdia
->mdhd
->language
= file
->qt_compatible
? 0 : ISOM_LANGUAGE_CODE_UNDEFINED
;
1618 isom_remove_box_by_itself( trak
);
1622 isom_moov_t
*isom_movie_create( lsmash_file_t
*file
)
1624 isom_moov_t
*moov
= isom_add_moov( file
);
1625 isom_mvhd_t
*mvhd
= isom_add_mvhd( moov
);
1628 isom_remove_box_by_itself( moov
);
1631 /* Default Movie Header Box. */
1632 mvhd
->rate
= 0x00010000;
1633 mvhd
->volume
= 0x0100;
1634 mvhd
->matrix
[0] = 0x00010000;
1635 mvhd
->matrix
[4] = 0x00010000;
1636 mvhd
->matrix
[8] = 0x40000000;
1637 mvhd
->next_track_ID
= 1;
1638 file
->initializer
= file
;
1642 /*******************************
1644 *******************************/
1646 /*---- track manipulators ----*/
1648 void lsmash_delete_track( lsmash_root_t
*root
, uint32_t track_ID
)
1650 if( isom_check_initializer_present( root
) < 0
1651 || !root
->file
->initializer
->moov
)
1653 for( lsmash_entry_t
*entry
= root
->file
->initializer
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
1655 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
1659 if( trak
->tkhd
->track_ID
== track_ID
)
1661 isom_remove_box_by_itself( trak
);
1667 uint32_t lsmash_create_track( lsmash_root_t
*root
, lsmash_media_type media_type
)
1669 if( isom_check_initializer_present( root
) < 0 )
1671 isom_trak_t
*trak
= isom_track_create( root
->file
, media_type
);
1675 return trak
->tkhd
->track_ID
;
1678 uint32_t lsmash_get_track_ID( lsmash_root_t
*root
, uint32_t track_number
)
1680 if( isom_check_initializer_present( root
) < 0
1681 || !root
->file
->initializer
->moov
)
1683 isom_trak_t
*trak
= (isom_trak_t
*)lsmash_get_entry_data( &root
->file
->initializer
->moov
->trak_list
, track_number
);
1687 return trak
->tkhd
->track_ID
;
1690 void lsmash_initialize_track_parameters( lsmash_track_parameters_t
*param
)
1692 memset( param
, 0, sizeof(lsmash_track_parameters_t
) );
1693 param
->audio_volume
= 0x0100;
1694 param
->matrix
[0] = 0x00010000;
1695 param
->matrix
[4] = 0x00010000;
1696 param
->matrix
[8] = 0x40000000;
1699 int lsmash_set_track_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_track_parameters_t
*param
)
1701 if( isom_check_initializer_present( root
) < 0 )
1702 return LSMASH_ERR_FUNCTION_PARAM
;
1703 lsmash_file_t
*file
= root
->file
;
1704 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1707 || !trak
->mdia
->hdlr
1708 || !file
->moov
->mvhd
)
1709 return LSMASH_ERR_NAMELESS
;
1710 /* Prepare Track Aperture Modes if required. */
1711 if( file
->qt_compatible
&& param
->aperture_modes
)
1713 if( !trak
->tapt
&& !isom_add_tapt( trak
) )
1714 return LSMASH_ERR_NAMELESS
;
1715 isom_tapt_t
*tapt
= trak
->tapt
;
1716 if( (!tapt
->clef
&& !isom_add_clef( tapt
))
1717 || (!tapt
->prof
&& !isom_add_prof( tapt
))
1718 || (!tapt
->enof
&& !isom_add_enof( tapt
)) )
1719 return LSMASH_ERR_NAMELESS
;
1722 isom_remove_box_by_itself( trak
->tapt
);
1723 /* Set up Track Header. */
1724 uint32_t media_type
= trak
->mdia
->hdlr
->componentSubtype
;
1725 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1726 tkhd
->flags
= param
->mode
;
1727 tkhd
->track_ID
= param
->track_ID
? param
->track_ID
: tkhd
->track_ID
;
1728 tkhd
->duration
= !trak
->edts
|| !trak
->edts
->elst
? param
->duration
: tkhd
->duration
;
1730 * alternate_group, layer, volume and matrix
1731 * According to 14496-14, these value are all set to defaut values in 14496-12.
1732 * And when a file is read as an MPEG-4 file, these values shall be ignored.
1733 * If a file complies with other specifications, then those fields may have non-default values
1734 * as required by those other specifications. */
1735 if( param
->alternate_group
)
1737 if( file
->qt_compatible
|| file
->itunes_movie
|| file
->max_3gpp_version
>= 4 )
1738 tkhd
->alternate_group
= param
->alternate_group
;
1741 tkhd
->alternate_group
= 0;
1742 lsmash_log( NULL
, LSMASH_LOG_WARNING
,
1743 "alternate_group is specified but not compatible with any of the brands. It won't be set.\n" );
1747 tkhd
->alternate_group
= 0;
1748 if( file
->qt_compatible
|| file
->itunes_movie
)
1750 tkhd
->layer
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->video_layer
: 0;
1751 tkhd
->volume
= media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
? param
->audio_volume
: 0;
1752 if( media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
)
1753 for( int i
= 0; i
< 9; i
++ )
1754 tkhd
->matrix
[i
] = param
->matrix
[i
];
1756 for( int i
= 0; i
< 9; i
++ )
1757 tkhd
->matrix
[i
] = 0;
1762 tkhd
->volume
= media_type
== ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK
? 0x0100 : 0;
1763 tkhd
->matrix
[0] = 0x00010000;
1764 tkhd
->matrix
[1] = 0;
1765 tkhd
->matrix
[2] = 0;
1766 tkhd
->matrix
[3] = 0;
1767 tkhd
->matrix
[4] = 0x00010000;
1768 tkhd
->matrix
[5] = 0;
1769 tkhd
->matrix
[6] = 0;
1770 tkhd
->matrix
[7] = 0;
1771 tkhd
->matrix
[8] = 0x40000000;
1773 /* visual presentation size */
1774 tkhd
->width
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->display_width
: 0;
1775 tkhd
->height
= media_type
== ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK
? param
->display_height
: 0;
1776 /* Update next_track_ID if needed. */
1777 if( file
->moov
->mvhd
->next_track_ID
<= tkhd
->track_ID
)
1778 file
->moov
->mvhd
->next_track_ID
= tkhd
->track_ID
+ 1;
1782 int lsmash_get_track_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_track_parameters_t
*param
)
1784 if( isom_check_initializer_present( root
) < 0 )
1785 return LSMASH_ERR_FUNCTION_PARAM
;
1786 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1788 return LSMASH_ERR_NAMELESS
;
1789 isom_tkhd_t
*tkhd
= trak
->tkhd
;
1790 param
->mode
= tkhd
->flags
;
1791 param
->track_ID
= tkhd
->track_ID
;
1792 param
->duration
= tkhd
->duration
;
1793 param
->video_layer
= tkhd
->layer
;
1794 param
->alternate_group
= tkhd
->alternate_group
;
1795 param
->audio_volume
= tkhd
->volume
;
1796 for( int i
= 0; i
< 9; i
++ )
1797 param
->matrix
[i
] = tkhd
->matrix
[i
];
1798 param
->display_width
= tkhd
->width
;
1799 param
->display_height
= tkhd
->height
;
1800 param
->aperture_modes
= !!trak
->tapt
;
1804 static inline int check_dref_presence( isom_trak_t
*trak
)
1808 || !trak
->mdia
->minf
1809 || !trak
->mdia
->minf
->dinf
1810 || !trak
->mdia
->minf
->dinf
->dref
)
1811 return LSMASH_ERR_NAMELESS
;
1815 uint32_t lsmash_count_data_reference
1817 lsmash_root_t
*root
,
1821 if( isom_check_initializer_present( root
) < 0 )
1823 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1824 if( check_dref_presence( trak
) < 0 )
1826 return trak
->mdia
->minf
->dinf
->dref
->list
.entry_count
;
1829 int lsmash_get_data_reference
1831 lsmash_root_t
*root
,
1833 lsmash_data_reference_t
*data_ref
1836 if( isom_check_initializer_present( root
) < 0 || !data_ref
)
1837 return LSMASH_ERR_FUNCTION_PARAM
;
1838 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1839 if( check_dref_presence( trak
) < 0 )
1840 return LSMASH_ERR_NAMELESS
;
1841 isom_dref_entry_t
*url
= lsmash_get_entry_data( &trak
->mdia
->minf
->dinf
->dref
->list
, data_ref
->index
);
1843 return LSMASH_ERR_NAMELESS
;
1844 if( !(url
->flags
& 0x000001) && url
->location
)
1846 int length
= strlen( url
->location
);
1847 char *location
= lsmash_malloc( length
+ 1 );
1849 return LSMASH_ERR_MEMORY_ALLOC
;
1850 memcpy( location
, url
->location
, length
);
1851 location
[length
] = '\0';
1852 data_ref
->location
= location
;
1855 data_ref
->location
= NULL
;
1859 void lsmash_cleanup_data_reference
1861 lsmash_data_reference_t
*data_ref
1866 lsmash_freep( &data_ref
->location
);
1869 int lsmash_create_data_reference
1871 lsmash_root_t
*root
,
1873 lsmash_data_reference_t
*data_ref
,
1877 /* At present, we don't support external data references for movie fragments.
1878 * Note that, for external media data, default-base-is-moof is meaningless since relative
1879 * offsets from Movie Fragment Boxes make no sense.
1880 * In the future, the condition of !(file->flags & LSMASH_FILE_MODE_WRITE) may be removed
1881 * for the implementation which does not write actually and does reference read-only file. */
1882 if( !root
|| !file
|| file
->root
!= root
1883 || (!(file
->flags
& LSMASH_FILE_MODE_MEDIA
) && !(file
->flags
& LSMASH_FILE_MODE_INITIALIZATION
))
1884 || !(file
->flags
& LSMASH_FILE_MODE_WRITE
)
1885 || (root
->file
!= file
&& ((file
->flags
& LSMASH_FILE_MODE_FRAGMENTED
) || file
->fragment
))
1887 return LSMASH_ERR_FUNCTION_PARAM
;
1888 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
1889 if( check_dref_presence( trak
) < 0 )
1890 return LSMASH_ERR_NAMELESS
;
1891 isom_dref_entry_t
*url
= isom_add_dref_entry( trak
->mdia
->minf
->dinf
->dref
, ISOM_BOX_TYPE_URL
);
1893 return LSMASH_ERR_NAMELESS
;
1894 if( !data_ref
->location
|| root
->file
== file
)
1896 /* Media data is in the same file. */
1897 url
->flags
= 0x000001;
1898 url
->ref_file
= root
->file
;
1902 /* Set the location of the file. */
1903 int length
= strlen( data_ref
->location
);
1904 url
->location
= lsmash_malloc( length
+ 1 );
1905 if( !url
->location
)
1907 isom_remove_box_by_itself( url
);
1908 return LSMASH_ERR_MEMORY_ALLOC
;
1910 memcpy( url
->location
, data_ref
->location
, length
);
1911 url
->location
[length
] = '\0';
1912 url
->location_length
= length
+ 1;
1913 url
->ref_file
= file
;
1915 data_ref
->index
= trak
->mdia
->minf
->dinf
->dref
->list
.entry_count
;
1919 int lsmash_assign_data_reference
1921 lsmash_root_t
*root
,
1923 uint32_t data_ref_index
,
1927 if( isom_check_initializer_present( root
) < 0
1928 || !file
|| file
->root
!= root
1929 || !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
1930 || !(file
->flags
& LSMASH_FILE_MODE_READ
)
1931 || data_ref_index
== 0 )
1932 return LSMASH_ERR_FUNCTION_PARAM
;
1933 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
1934 if( check_dref_presence( trak
) < 0 )
1935 return LSMASH_ERR_NAMELESS
;
1936 isom_dref_entry_t
*url
= (isom_dref_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->dinf
->dref
->list
, data_ref_index
);
1938 return LSMASH_ERR_NAMELESS
;
1939 if( !(url
->flags
& 0x000001) )
1940 /* Reference an external media data. */
1941 url
->ref_file
= file
;
1945 static int isom_set_media_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
->hdlr
)
1951 return LSMASH_ERR_NAMELESS
;
1952 isom_hdlr_t
*hdlr
= trak
->mdia
->hdlr
;
1953 uint8_t *name
= NULL
;
1954 uint32_t name_length
= strlen( handler_name
) + file
->isom_compatible
+ file
->qt_compatible
;
1955 if( file
->qt_compatible
)
1956 name_length
= LSMASH_MIN( name_length
, 255 );
1957 if( name_length
> hdlr
->componentName_length
&& hdlr
->componentName
)
1958 name
= lsmash_realloc( hdlr
->componentName
, name_length
);
1959 else if( !hdlr
->componentName
)
1960 name
= lsmash_malloc( name_length
);
1962 name
= hdlr
->componentName
;
1964 return LSMASH_ERR_MEMORY_ALLOC
;
1965 if( file
->qt_compatible
)
1966 name
[0] = name_length
& 0xff;
1967 memcpy( name
+ file
->qt_compatible
, handler_name
, strlen( handler_name
) );
1968 if( file
->isom_compatible
)
1969 name
[name_length
- 1] = 0;
1970 hdlr
->componentName
= name
;
1971 hdlr
->componentName_length
= name_length
;
1975 static int isom_set_data_handler_name( lsmash_file_t
*file
, uint32_t track_ID
, char *handler_name
)
1977 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
1980 || !trak
->mdia
->minf
1981 || !trak
->mdia
->minf
->hdlr
)
1982 return LSMASH_ERR_NAMELESS
;
1983 isom_hdlr_t
*hdlr
= trak
->mdia
->minf
->hdlr
;
1984 uint8_t *name
= NULL
;
1985 uint32_t name_length
= strlen( handler_name
) + file
->isom_compatible
+ file
->qt_compatible
;
1986 if( file
->qt_compatible
)
1987 name_length
= LSMASH_MIN( name_length
, 255 );
1988 if( name_length
> hdlr
->componentName_length
&& hdlr
->componentName
)
1989 name
= lsmash_realloc( hdlr
->componentName
, name_length
);
1990 else if( !hdlr
->componentName
)
1991 name
= lsmash_malloc( name_length
);
1993 name
= hdlr
->componentName
;
1995 return LSMASH_ERR_MEMORY_ALLOC
;
1996 if( file
->qt_compatible
)
1997 name
[0] = name_length
& 0xff;
1998 memcpy( name
+ file
->qt_compatible
, handler_name
, strlen( handler_name
) );
1999 if( file
->isom_compatible
)
2000 name
[name_length
- 1] = 0;
2001 hdlr
->componentName
= name
;
2002 hdlr
->componentName_length
= name_length
;
2006 uint32_t lsmash_get_media_timescale( lsmash_root_t
*root
, uint32_t track_ID
)
2008 if( isom_check_initializer_present( root
) < 0 )
2010 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2013 || !trak
->mdia
->mdhd
)
2015 return trak
->mdia
->mdhd
->timescale
;
2018 uint64_t lsmash_get_media_duration( lsmash_root_t
*root
, uint32_t track_ID
)
2020 if( isom_check_initializer_present( root
) < 0 )
2022 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2025 || !trak
->mdia
->mdhd
)
2027 return trak
->mdia
->mdhd
->duration
;
2030 uint64_t lsmash_get_track_duration( lsmash_root_t
*root
, uint32_t track_ID
)
2032 if( isom_check_initializer_present( root
) < 0 )
2034 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2038 return trak
->tkhd
->duration
;
2041 uint32_t lsmash_get_last_sample_delta( lsmash_root_t
*root
, uint32_t track_ID
)
2043 if( isom_check_initializer_present( root
) < 0 )
2045 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
2048 || !trak
->mdia
->minf
2049 || !trak
->mdia
->minf
->stbl
2050 || !trak
->mdia
->minf
->stbl
->stts
2051 || !trak
->mdia
->minf
->stbl
->stts
->list
2052 || !trak
->mdia
->minf
->stbl
->stts
->list
->tail
2053 || !trak
->mdia
->minf
->stbl
->stts
->list
->tail
->data
)
2055 return ((isom_stts_entry_t
*)trak
->mdia
->minf
->stbl
->stts
->list
->tail
->data
)->sample_delta
;
2058 uint32_t lsmash_get_start_time_offset( lsmash_root_t
*root
, uint32_t track_ID
)
2060 if( isom_check_initializer_present( root
) < 0 )
2062 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
2065 || !trak
->mdia
->minf
2066 || !trak
->mdia
->minf
->stbl
2067 || !trak
->mdia
->minf
->stbl
->ctts
2068 || !trak
->mdia
->minf
->stbl
->ctts
->list
2069 || !trak
->mdia
->minf
->stbl
->ctts
->list
->head
2070 || !trak
->mdia
->minf
->stbl
->ctts
->list
->head
->data
)
2072 return ((isom_ctts_entry_t
*)trak
->mdia
->minf
->stbl
->ctts
->list
->head
->data
)->sample_offset
;
2075 uint32_t lsmash_get_composition_to_decode_shift( lsmash_root_t
*root
, uint32_t track_ID
)
2077 if( isom_check_initializer_present( root
) < 0 )
2079 lsmash_file_t
*file
= root
->file
->initializer
;
2080 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2083 || !trak
->mdia
->minf
2084 || !trak
->mdia
->minf
->stbl
)
2086 uint32_t sample_count
= isom_get_sample_count( trak
);
2087 if( sample_count
== 0 )
2089 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2090 if( !stbl
->stts
|| !stbl
->stts
->list
2091 || !stbl
->ctts
|| !stbl
->ctts
->list
)
2093 if( !(file
->max_isom_version
>= 4 && stbl
->ctts
->version
== 1) && !file
->qt_compatible
)
2094 return 0; /* This movie shall not have composition to decode timeline shift. */
2095 lsmash_entry_t
*stts_entry
= stbl
->stts
->list
->head
;
2096 lsmash_entry_t
*ctts_entry
= stbl
->ctts
->list
->head
;
2097 if( !stts_entry
|| !ctts_entry
)
2101 uint32_t ctd_shift
= 0;
2104 for( uint32_t k
= 0; k
< sample_count
; k
++ )
2106 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)stts_entry
->data
;
2107 isom_ctts_entry_t
*ctts_data
= (isom_ctts_entry_t
*)ctts_entry
->data
;
2108 if( !stts_data
|| !ctts_data
)
2110 cts
= dts
+ (int32_t)ctts_data
->sample_offset
;
2111 if( dts
> cts
+ ctd_shift
)
2112 ctd_shift
= dts
- cts
;
2113 dts
+= stts_data
->sample_delta
;
2114 if( ++i
== stts_data
->sample_count
)
2116 stts_entry
= stts_entry
->next
;
2121 if( ++j
== ctts_data
->sample_count
)
2123 ctts_entry
= ctts_entry
->next
;
2132 uint16_t lsmash_pack_iso_language( char *iso_language
)
2134 if( !iso_language
|| strlen( iso_language
) != 3 )
2136 return (uint16_t)LSMASH_PACK_ISO_LANGUAGE( iso_language
[0], iso_language
[1], iso_language
[2] );
2139 static int isom_iso2mac_language( uint16_t ISO_language
, uint16_t *MAC_language
)
2141 assert( MAC_language
);
2143 for( ; isom_languages
[i
].iso_name
; i
++ )
2144 if( ISO_language
== isom_languages
[i
].iso_name
)
2146 if( !isom_languages
[i
].iso_name
)
2147 return LSMASH_ERR_NAMELESS
;
2148 *MAC_language
= isom_languages
[i
].mac_value
;
2152 static int isom_mac2iso_language( uint16_t MAC_language
, uint16_t *ISO_language
)
2154 assert( ISO_language
);
2156 for( ; isom_languages
[i
].iso_name
; i
++ )
2157 if( MAC_language
== isom_languages
[i
].mac_value
)
2159 *ISO_language
= isom_languages
[i
].iso_name
? isom_languages
[i
].iso_name
: ISOM_LANGUAGE_CODE_UNDEFINED
;
2163 static int isom_set_media_language( lsmash_file_t
*file
, uint32_t track_ID
, uint16_t ISO_language
, uint16_t MAC_language
)
2165 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2168 || !trak
->mdia
->mdhd
)
2169 return LSMASH_ERR_NAMELESS
;
2170 uint16_t language
= 0;
2171 if( file
->isom_compatible
)
2174 language
= ISO_language
;
2175 else if( MAC_language
)
2177 int err
= isom_mac2iso_language( MAC_language
, &language
);
2182 language
= ISOM_LANGUAGE_CODE_UNDEFINED
;
2184 else if( file
->qt_compatible
)
2188 int err
= isom_iso2mac_language( ISO_language
, &language
);
2193 language
= MAC_language
;
2196 return LSMASH_ERR_INVALID_DATA
;
2197 trak
->mdia
->mdhd
->language
= language
;
2201 int isom_add_sample_grouping( isom_box_t
*parent
, isom_grouping_type grouping_type
)
2205 if( NULL
== (sgpd
= isom_add_sgpd( parent
))
2206 || NULL
== (sbgp
= isom_add_sbgp( parent
)) )
2207 return LSMASH_ERR_NAMELESS
;
2208 sbgp
->grouping_type
= grouping_type
;
2209 sgpd
->grouping_type
= grouping_type
;
2210 sgpd
->version
= 1; /* We use version 1 for Sample Group Description Box because it is recommended in the spec. */
2211 switch( grouping_type
)
2213 case ISOM_GROUP_TYPE_RAP
:
2214 sgpd
->default_length
= 1;
2216 case ISOM_GROUP_TYPE_ROLL
:
2217 case ISOM_GROUP_TYPE_PROL
:
2218 sgpd
->default_length
= 2;
2221 /* We don't consider other grouping types currently. */
2227 static int isom_create_sample_grouping( isom_trak_t
*trak
, isom_grouping_type grouping_type
)
2229 lsmash_file_t
*file
= trak
->file
;
2230 switch( grouping_type
)
2232 case ISOM_GROUP_TYPE_RAP
:
2233 assert( file
->max_isom_version
>= 6 );
2235 case ISOM_GROUP_TYPE_ROLL
:
2236 case ISOM_GROUP_TYPE_PROL
:
2237 assert( file
->avc_extensions
|| file
->qt_compatible
);
2243 int err
= isom_add_sample_grouping( (isom_box_t
*)trak
->mdia
->minf
->stbl
, grouping_type
);
2246 if( trak
->cache
->fragment
&& file
->max_isom_version
>= 6 )
2247 switch( grouping_type
)
2249 case ISOM_GROUP_TYPE_RAP
:
2250 trak
->cache
->fragment
->rap_grouping
= 1;
2252 case ISOM_GROUP_TYPE_ROLL
:
2253 case ISOM_GROUP_TYPE_PROL
:
2254 trak
->cache
->fragment
->roll_grouping
= 1;
2257 /* We don't consider other grouping types currently. */
2263 void lsmash_initialize_media_parameters( lsmash_media_parameters_t
*param
)
2265 memset( param
, 0, sizeof(lsmash_media_parameters_t
) );
2266 param
->timescale
= 1;
2269 int lsmash_set_media_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_media_parameters_t
*param
)
2271 if( isom_check_initializer_present( root
) < 0 )
2272 return LSMASH_ERR_FUNCTION_PARAM
;
2273 lsmash_file_t
*file
= root
->file
;
2274 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2277 || !trak
->mdia
->mdhd
2278 || !trak
->mdia
->minf
2279 || !trak
->mdia
->minf
->stbl
)
2280 return LSMASH_ERR_NAMELESS
;
2281 trak
->mdia
->mdhd
->timescale
= param
->timescale
;
2282 int err
= isom_set_media_language( file
, track_ID
, param
->ISO_language
, param
->MAC_language
);
2285 if( param
->media_handler_name
2286 && (err
= isom_set_media_handler_name( file
, track_ID
, param
->media_handler_name
)) < 0 )
2288 if( file
->qt_compatible
&& param
->data_handler_name
2289 && (err
= isom_set_data_handler_name( file
, track_ID
, param
->data_handler_name
)) < 0 )
2291 if( (file
->avc_extensions
|| file
->qt_compatible
) && param
->roll_grouping
2292 && (err
= isom_create_sample_grouping( trak
, ISOM_GROUP_TYPE_ROLL
)) < 0 )
2294 if( (file
->max_isom_version
>= 6) && param
->rap_grouping
2295 && (err
= isom_create_sample_grouping( trak
, ISOM_GROUP_TYPE_RAP
)) < 0 )
2300 int lsmash_get_media_parameters( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_media_parameters_t
*param
)
2302 if( isom_check_initializer_present( root
) < 0 )
2303 return LSMASH_ERR_FUNCTION_PARAM
;
2304 lsmash_file_t
*file
= root
->file
->initializer
;
2305 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2308 || !trak
->mdia
->mdhd
2309 || !trak
->mdia
->hdlr
2310 || !trak
->mdia
->minf
2311 || !trak
->mdia
->minf
->stbl
)
2312 return LSMASH_ERR_NAMELESS
;
2313 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
2314 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2315 param
->timescale
= mdhd
->timescale
;
2316 param
->handler_type
= trak
->mdia
->hdlr
->componentSubtype
;
2317 param
->duration
= mdhd
->duration
;
2318 /* Whether sample grouping present. */
2322 sbgp
= isom_get_sample_to_group ( stbl
, ISOM_GROUP_TYPE_RAP
);
2323 sgpd
= isom_get_sample_group_description( stbl
, ISOM_GROUP_TYPE_RAP
);
2324 param
->rap_grouping
= sbgp
&& sgpd
;
2325 sbgp
= isom_get_roll_recovery_sample_to_group ( &stbl
->sbgp_list
);
2326 sgpd
= isom_get_roll_recovery_sample_group_description( &stbl
->sgpd_list
);
2327 param
->roll_grouping
= sbgp
&& sgpd
;
2329 /* Get media language. */
2330 if( mdhd
->language
>= 0x800 )
2332 param
->MAC_language
= 0;
2333 param
->ISO_language
= mdhd
->language
;
2337 param
->MAC_language
= mdhd
->language
;
2338 param
->ISO_language
= 0;
2340 /* Get handler name(s). */
2341 isom_hdlr_t
*hdlr
= trak
->mdia
->hdlr
;
2342 int length
= LSMASH_MIN( 255, hdlr
->componentName_length
);
2345 memcpy( param
->media_handler_name_shadow
, hdlr
->componentName
+ file
->qt_compatible
, length
);
2346 param
->media_handler_name_shadow
[length
- 2 + file
->isom_compatible
+ file
->qt_compatible
] = '\0';
2347 param
->media_handler_name
= param
->media_handler_name_shadow
;
2351 param
->media_handler_name
= NULL
;
2352 memset( param
->media_handler_name_shadow
, 0, sizeof(param
->media_handler_name_shadow
) );
2354 if( trak
->mdia
->minf
->hdlr
)
2356 hdlr
= trak
->mdia
->minf
->hdlr
;
2357 length
= LSMASH_MIN( 255, hdlr
->componentName_length
);
2360 memcpy( param
->data_handler_name_shadow
, hdlr
->componentName
+ file
->qt_compatible
, length
);
2361 param
->data_handler_name_shadow
[length
- 2 + file
->isom_compatible
+ file
->qt_compatible
] = '\0';
2362 param
->data_handler_name
= param
->data_handler_name_shadow
;
2366 param
->data_handler_name
= NULL
;
2367 memset( param
->data_handler_name_shadow
, 0, sizeof(param
->data_handler_name_shadow
) );
2372 param
->data_handler_name
= NULL
;
2373 memset( param
->data_handler_name_shadow
, 0, sizeof(param
->data_handler_name_shadow
) );
2378 /*---- movie manipulators ----*/
2380 void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t
*param
)
2382 memset( param
, 0, sizeof(lsmash_movie_parameters_t
) );
2383 param
->timescale
= 600;
2384 param
->playback_rate
= 0x00010000;
2385 param
->playback_volume
= 0x0100;
2388 int lsmash_set_movie_parameters( lsmash_root_t
*root
, lsmash_movie_parameters_t
*param
)
2391 return LSMASH_ERR_FUNCTION_PARAM
;
2392 lsmash_file_t
*file
= root
->file
;
2395 || !file
->moov
->mvhd
)
2396 return LSMASH_ERR_NAMELESS
;
2397 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
2398 mvhd
->timescale
= param
->timescale
;
2399 if( file
->qt_compatible
|| file
->itunes_movie
)
2401 mvhd
->rate
= param
->playback_rate
;
2402 mvhd
->volume
= param
->playback_volume
;
2403 mvhd
->previewTime
= param
->preview_time
;
2404 mvhd
->previewDuration
= param
->preview_duration
;
2405 mvhd
->posterTime
= param
->poster_time
;
2409 mvhd
->rate
= 0x00010000;
2410 mvhd
->volume
= 0x0100;
2411 mvhd
->previewTime
= 0;
2412 mvhd
->previewDuration
= 0;
2413 mvhd
->posterTime
= 0;
2418 int lsmash_get_movie_parameters( lsmash_root_t
*root
, lsmash_movie_parameters_t
*param
)
2420 if( isom_check_initializer_present( root
) < 0 )
2421 return LSMASH_ERR_FUNCTION_PARAM
;
2422 lsmash_file_t
*file
= root
->file
->initializer
;
2424 || !file
->moov
->mvhd
)
2425 return LSMASH_ERR_NAMELESS
;
2426 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
2427 param
->timescale
= mvhd
->timescale
;
2428 param
->duration
= mvhd
->duration
;
2429 param
->playback_rate
= mvhd
->rate
;
2430 param
->playback_volume
= mvhd
->volume
;
2431 param
->preview_time
= mvhd
->previewTime
;
2432 param
->preview_duration
= mvhd
->previewDuration
;
2433 param
->poster_time
= mvhd
->posterTime
;
2434 param
->number_of_tracks
= file
->moov
->trak_list
.entry_count
;
2438 uint32_t lsmash_get_movie_timescale( lsmash_root_t
*root
)
2440 if( isom_check_initializer_present( root
) < 0
2441 || !root
->file
->initializer
->moov
2442 || !root
->file
->initializer
->moov
->mvhd
)
2444 return root
->file
->initializer
->moov
->mvhd
->timescale
;
2447 static int isom_scan_trak_profileLevelIndication
2450 mp4a_audioProfileLevelIndication
*audio_pli
,
2451 mp4sys_visualProfileLevelIndication
*visual_pli
2456 || !trak
->mdia
->minf
2457 || !trak
->mdia
->minf
->stbl
)
2458 return LSMASH_ERR_INVALID_DATA
;
2459 isom_stsd_t
*stsd
= trak
->mdia
->minf
->stbl
->stsd
;
2461 || !stsd
->list
.head
)
2462 return LSMASH_ERR_INVALID_DATA
;
2463 for( lsmash_entry_t
*entry
= stsd
->list
.head
; entry
; entry
= entry
->next
)
2465 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)entry
->data
;
2467 return LSMASH_ERR_INVALID_DATA
;
2468 lsmash_codec_type_t sample_type
= sample_entry
->type
;
2469 if( trak
->mdia
->minf
->vmhd
)
2471 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC1_VIDEO
)
2472 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC2_VIDEO
)
2473 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC3_VIDEO
)
2474 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVC4_VIDEO
)
2475 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_AVCP_VIDEO
)
2476 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_SVC1_VIDEO
)
2477 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MVC1_VIDEO
)
2478 || lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MVC2_VIDEO
) )
2480 /* FIXME: Do we have to arbitrate like audio? */
2481 if( *visual_pli
== MP4SYS_VISUAL_PLI_NONE_REQUIRED
)
2482 *visual_pli
= MP4SYS_VISUAL_PLI_H264_AVC
;
2485 *visual_pli
= MP4SYS_VISUAL_PLI_NOT_SPECIFIED
;
2487 else if( trak
->mdia
->minf
->smhd
)
2489 if( lsmash_check_codec_type_identical( sample_type
, ISOM_CODEC_TYPE_MP4A_AUDIO
) )
2491 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)sample_entry
;
2492 isom_esds_t
*esds
= (isom_esds_t
*)isom_get_extension_box_format( &audio
->extensions
, ISOM_BOX_TYPE_ESDS
);
2493 if( !esds
|| !esds
->ES
)
2494 return LSMASH_ERR_INVALID_DATA
;
2495 lsmash_audio_summary_t
*summary
= (lsmash_audio_summary_t
*)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO
);
2498 if( mp4sys_setup_summary_from_DecoderSpecificInfo( summary
, esds
->ES
) < 0 )
2499 *audio_pli
= MP4A_AUDIO_PLI_NOT_SPECIFIED
;
2501 *audio_pli
= mp4a_max_audioProfileLevelIndication( *audio_pli
, mp4a_get_audioProfileLevelIndication( summary
) );
2502 lsmash_cleanup_summary( (lsmash_summary_t
*)summary
);
2505 /* NOTE: Audio CODECs other than 'mp4a' does not have appropriate pli. */
2506 *audio_pli
= MP4A_AUDIO_PLI_NOT_SPECIFIED
;
2509 ; /* FIXME: Do we have to set OD_profileLevelIndication? */
2514 int isom_setup_iods( isom_moov_t
*moov
)
2516 if( !moov
->iods
&& !isom_add_iods( moov
) )
2517 return LSMASH_ERR_NAMELESS
;
2518 isom_iods_t
*iods
= moov
->iods
;
2519 int err
= LSMASH_ERR_NAMELESS
;
2520 iods
->OD
= mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */
2523 mp4a_audioProfileLevelIndication audio_pli
= MP4A_AUDIO_PLI_NONE_REQUIRED
;
2524 mp4sys_visualProfileLevelIndication visual_pli
= MP4SYS_VISUAL_PLI_NONE_REQUIRED
;
2525 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
2527 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2531 if( (err
= isom_scan_trak_profileLevelIndication( trak
, &audio_pli
, &visual_pli
)) < 0 )
2533 if( (err
= mp4sys_create_ES_ID_Inc( iods
->OD
, trak
->tkhd
->track_ID
)) < 0 )
2536 if( (err
= mp4sys_to_InitialObjectDescriptor( iods
->OD
,
2537 0, /* FIXME: I'm not quite sure what the spec says. */
2538 MP4SYS_OD_PLI_NONE_REQUIRED
, MP4SYS_SCENE_PLI_NONE_REQUIRED
,
2539 audio_pli
, visual_pli
,
2540 MP4SYS_GRAPHICS_PLI_NONE_REQUIRED
)) < 0 )
2544 isom_remove_box_by_itself( iods
);
2548 int lsmash_create_object_descriptor( lsmash_root_t
*root
)
2550 if( isom_check_initializer_present( root
) < 0 )
2551 return LSMASH_ERR_FUNCTION_PARAM
;
2552 lsmash_file_t
*file
= root
->file
;
2553 /* Return error if this file is not compatible with MP4 file format. */
2554 if( !file
->mp4_version1
2555 && !file
->mp4_version2
)
2556 return LSMASH_ERR_FUNCTION_PARAM
;
2557 return isom_setup_iods( file
->moov
);
2560 /*---- finishing functions ----*/
2562 int isom_complement_data_reference( isom_minf_t
*minf
)
2565 || !minf
->dinf
->dref
)
2566 return LSMASH_ERR_INVALID_DATA
;
2567 /* Complement data referece if absent. */
2568 if( !minf
->dinf
->dref
->list
.head
)
2570 isom_dref_entry_t
*url
= isom_add_dref_entry( minf
->dinf
->dref
, ISOM_BOX_TYPE_URL
);
2572 return LSMASH_ERR_NAMELESS
;
2573 url
->flags
= 0x000001; /* Media data is in the same file. */
2578 static lsmash_file_t
*isom_get_written_media_file
2581 uint32_t sample_description_index
2584 isom_minf_t
*minf
= trak
->mdia
->minf
;
2585 isom_sample_entry_t
*description
= (isom_sample_entry_t
*)lsmash_get_entry_data( &minf
->stbl
->stsd
->list
, sample_description_index
);
2586 isom_dref_entry_t
*dref_entry
= (isom_dref_entry_t
*)lsmash_get_entry_data( &minf
->dinf
->dref
->list
, description
->data_reference_index
);
2587 lsmash_file_t
*file
= (!dref_entry
|| !dref_entry
->ref_file
) ? trak
->file
: dref_entry
->ref_file
;
2588 if( !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
2589 || !(file
->flags
& LSMASH_FILE_MODE_WRITE
) )
2594 int isom_check_large_offset_requirement
2600 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; )
2602 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2603 isom_stco_t
*stco
= trak
->mdia
->minf
->stbl
->stco
;
2604 if( !stco
->list
->tail
/* no samples */
2605 || stco
->large_presentation
2606 || (((isom_stco_entry_t
*)stco
->list
->tail
->data
)->chunk_offset
+ moov
->size
+ meta_size
) <= UINT32_MAX
)
2608 entry
= entry
->next
;
2609 continue; /* no need to convert stco into co64 */
2611 /* stco->co64 conversion */
2612 int err
= isom_convert_stco_to_co64( trak
->mdia
->minf
->stbl
);
2615 if( isom_update_box_size( moov
) == 0 )
2616 return LSMASH_ERR_INVALID_DATA
;
2617 entry
= moov
->trak_list
.head
; /* whenever any conversion, re-check all traks */
2622 void isom_add_preceding_box_size
2625 uint64_t preceding_size
2628 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
2630 /* Apply to the chunks in the same file. */
2631 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2632 isom_stsc_t
*stsc
= trak
->mdia
->minf
->stbl
->stsc
;
2633 isom_stco_t
*stco
= trak
->mdia
->minf
->stbl
->stco
;
2634 lsmash_entry_t
*stsc_entry
= stsc
->list
->head
;
2635 isom_stsc_entry_t
*stsc_data
= stsc_entry
? (isom_stsc_entry_t
*)stsc_entry
->data
: NULL
;
2636 uint32_t chunk_number
= 1;
2637 for( lsmash_entry_t
*stco_entry
= stco
->list
->head
; stco_entry
; )
2640 && stsc_data
->first_chunk
== chunk_number
)
2642 lsmash_file_t
*ref_file
= isom_get_written_media_file( trak
, stsc_data
->sample_description_index
);
2643 stsc_entry
= stsc_entry
->next
;
2644 stsc_data
= stsc_entry
? (isom_stsc_entry_t
*)stsc_entry
->data
: NULL
;
2645 if( ref_file
!= trak
->file
)
2647 /* The chunks are not contained in the same file. Skip applying the offset.
2648 * If no more stsc entries, the rest of the chunks is not contained in the same file. */
2649 if( !stsc_entry
|| !stsc_data
)
2651 while( stco_entry
&& chunk_number
< stsc_data
->first_chunk
)
2653 stco_entry
= stco_entry
->next
;
2659 if( stco
->large_presentation
)
2660 ((isom_co64_entry_t
*)stco_entry
->data
)->chunk_offset
+= preceding_size
;
2662 ((isom_stco_entry_t
*)stco_entry
->data
)->chunk_offset
+= preceding_size
;
2663 stco_entry
= stco_entry
->next
;
2669 int isom_establish_movie( lsmash_file_t
*file
)
2671 assert( file
== file
->initializer
);
2673 if( (err
= isom_check_mandatory_boxes( file
)) < 0
2674 || (err
= isom_set_movie_creation_time( file
)) < 0 )
2676 if( isom_update_box_size( file
->moov
) == 0 )
2677 return LSMASH_ERR_INVALID_DATA
;
2681 int lsmash_finish_movie
2683 lsmash_root_t
*root
,
2684 lsmash_adhoc_remux_t
*remux
2687 if( isom_check_initializer_present( root
) < 0 )
2688 return LSMASH_ERR_FUNCTION_PARAM
;
2689 lsmash_file_t
*file
= root
->file
;
2692 || !file
->initializer
->moov
)
2693 return LSMASH_ERR_INVALID_DATA
;
2694 if( file
->fragment
)
2695 return isom_finish_final_fragment_movie( file
, remux
);
2696 if( file
!= file
->initializer
)
2697 return LSMASH_ERR_INVALID_DATA
;
2699 isom_moov_t
*moov
= file
->moov
;
2700 for( lsmash_entry_t
*entry
= moov
->trak_list
.head
; entry
; entry
= entry
->next
)
2702 isom_trak_t
*trak
= (isom_trak_t
*)entry
->data
;
2707 || !trak
->mdia
->minf
2708 || !trak
->mdia
->minf
->stbl
2709 || !trak
->mdia
->minf
->stbl
->stco
2710 || !trak
->mdia
->minf
->stbl
->stco
->list
2711 || !trak
->mdia
->minf
->stbl
->stco
->list
->tail
)
2712 return LSMASH_ERR_INVALID_DATA
;
2713 if( (err
= isom_complement_data_reference( trak
->mdia
->minf
)) < 0 )
2715 uint32_t track_ID
= trak
->tkhd
->track_ID
;
2716 uint32_t related_track_ID
= trak
->related_track_ID
;
2717 /* Disable the track if the track is a track reference chapter. */
2718 if( trak
->is_chapter
)
2719 trak
->tkhd
->flags
&= ~ISOM_TRACK_ENABLED
;
2720 if( trak
->is_chapter
&& related_track_ID
)
2722 /* In order that the track duration of the chapter track doesn't exceed that of the related track. */
2724 edit
.duration
= LSMASH_MIN( trak
->tkhd
->duration
, lsmash_get_track_duration( root
, related_track_ID
) );
2725 edit
.start_time
= 0;
2726 edit
.rate
= ISOM_EDIT_MODE_NORMAL
;
2727 if( (err
= lsmash_create_explicit_timeline_map( root
, track_ID
, edit
)) < 0 )
2730 /* Add stss box if any samples aren't sync sample. */
2731 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2732 if( !trak
->cache
->all_sync
&& !stbl
->stss
&& !isom_add_stss( stbl
) )
2733 return LSMASH_ERR_NAMELESS
;
2734 if( (err
= isom_update_tkhd_duration( trak
)) < 0
2735 || (err
= isom_update_bitrate_description( trak
->mdia
)) < 0 )
2738 if( file
->mp4_version1
== 1 && (err
= isom_setup_iods( moov
)) < 0 )
2740 if( (err
= isom_establish_movie( file
)) < 0 )
2742 /* Write the size of Media Data Box here. */
2743 lsmash_bs_t
*bs
= file
->bs
;
2744 file
->mdat
->manager
&= ~LSMASH_INCOMPLETE_BOX
;
2745 if( (err
= isom_write_box( bs
, (isom_box_t
*)file
->mdat
)) < 0 )
2747 /* Write the Movie Box and a Meta Box if no optimization for progressive download. */
2748 uint64_t meta_size
= file
->meta
? file
->meta
->size
: 0;
2751 if( (err
= isom_write_box( bs
, (isom_box_t
*)file
->moov
)) < 0
2752 || (err
= isom_write_box( bs
, (isom_box_t
*)file
->meta
)) < 0 )
2754 file
->size
+= moov
->size
+ meta_size
;
2757 /* stco->co64 conversion, depending on last chunk's offset */
2758 if( (err
= isom_check_large_offset_requirement( moov
, meta_size
)) < 0 )
2760 /* now the amount of offset is fixed. */
2761 uint64_t mtf_size
= moov
->size
+ meta_size
; /* sum of size of boxes moved to front */
2762 /* buffer size must be at least mtf_size * 2 */
2763 remux
->buffer_size
= LSMASH_MAX( remux
->buffer_size
, mtf_size
* 2 );
2764 /* Split to 2 buffers. */
2765 uint8_t *buf
[2] = { NULL
, NULL
};
2766 if( (buf
[0] = (uint8_t*)lsmash_malloc( remux
->buffer_size
)) == NULL
)
2767 return LSMASH_ERR_MEMORY_ALLOC
; /* NOTE: I think we still can fallback to "return isom_write_moov();" here. */
2768 size_t size
= remux
->buffer_size
/ 2;
2769 buf
[1] = buf
[0] + size
;
2770 /* Now, the amount of the offset is fixed. apply it to stco/co64 */
2771 isom_add_preceding_box_size( moov
, mtf_size
);
2772 /* Backup starting area of mdat and write moov + meta there instead. */
2773 isom_mdat_t
*mdat
= file
->mdat
;
2774 uint64_t total
= file
->size
+ mtf_size
;
2775 uint64_t placeholder_pos
= file
->free
? file
->free
->pos
: mdat
->pos
;
2776 if( (err
= lsmash_bs_write_seek( bs
, placeholder_pos
, SEEK_SET
)) < 0 )
2778 size_t read_num
= size
;
2779 lsmash_bs_read_data( bs
, buf
[0], &read_num
);
2780 uint64_t read_pos
= bs
->offset
;
2781 /* Write moov + meta there instead. */
2782 if( (err
= lsmash_bs_write_seek( bs
, placeholder_pos
, SEEK_SET
)) < 0
2783 || (err
= isom_write_box( bs
, (isom_box_t
*)file
->moov
)) < 0
2784 || (err
= isom_write_box( bs
, (isom_box_t
*)file
->meta
)) < 0 )
2786 uint64_t write_pos
= bs
->offset
;
2787 /* Update the positions */
2788 mdat
->pos
+= mtf_size
;
2790 file
->free
->pos
+= mtf_size
;
2791 /* Move Media Data Box. */
2792 if( (err
= isom_rearrange_data( file
, remux
, buf
, read_num
, size
, read_pos
, write_pos
, total
)) < 0 )
2794 file
->size
+= mtf_size
;
2795 lsmash_free( buf
[0] );
2798 lsmash_free( buf
[0] );
2802 int lsmash_set_last_sample_delta( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t sample_delta
)
2804 if( isom_check_initializer_present( root
) < 0 || track_ID
== 0 )
2805 return LSMASH_ERR_FUNCTION_PARAM
;
2806 lsmash_file_t
*file
= root
->file
;
2808 && file
->fragment
->movie
)
2810 isom_traf_t
*traf
= isom_get_traf( file
->fragment
->movie
, track_ID
);
2814 return LSMASH_ERR_NAMELESS
;
2815 return isom_set_fragment_last_duration( traf
, sample_delta
);
2817 if( file
!= file
->initializer
)
2818 return LSMASH_ERR_INVALID_DATA
;
2819 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2823 || !trak
->mdia
->mdhd
2824 || !trak
->mdia
->minf
2825 || !trak
->mdia
->minf
->stbl
2826 || !trak
->mdia
->minf
->stbl
->stsd
2827 || !trak
->mdia
->minf
->stbl
->stsz
2828 || !trak
->mdia
->minf
->stbl
->stts
2829 || !trak
->mdia
->minf
->stbl
->stts
->list
)
2830 return LSMASH_ERR_NAMELESS
;
2831 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
2832 isom_stts_t
*stts
= stbl
->stts
;
2833 uint32_t sample_count
= isom_get_sample_count( trak
);
2835 if( !stts
->list
->tail
)
2838 return 0; /* no samples */
2839 if( sample_count
> 1 )
2840 return LSMASH_ERR_INVALID_DATA
; /* irregular sample_count */
2841 /* Set the duration of the first sample.
2842 * This duration is also the duration of the last sample. */
2843 if( (err
= isom_add_stts_entry( stbl
, sample_delta
)) < 0 )
2845 return lsmash_update_track_duration( root
, track_ID
, 0 );
2848 for( lsmash_entry_t
*entry
= stts
->list
->head
; entry
; entry
= entry
->next
)
2849 i
+= ((isom_stts_entry_t
*)entry
->data
)->sample_count
;
2850 if( sample_count
< i
)
2851 return LSMASH_ERR_INVALID_DATA
;
2852 int no_last
= (sample_count
> i
);
2853 isom_stts_entry_t
*last_stts_data
= (isom_stts_entry_t
*)stts
->list
->tail
->data
;
2854 if( !last_stts_data
)
2855 return LSMASH_ERR_INVALID_DATA
;
2856 /* Consider QuikcTime fixed compression audio. */
2857 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
,
2858 trak
->cache
->chunk
.sample_description_index
);
2860 return LSMASH_ERR_INVALID_DATA
;
2861 if( (audio
->manager
& LSMASH_AUDIO_DESCRIPTION
)
2862 && (audio
->manager
& LSMASH_QTFF_BASE
)
2863 && (audio
->version
== 1)
2864 && (audio
->compression_ID
!= QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION
) )
2866 if( audio
->samplesPerPacket
== 0 )
2867 return LSMASH_ERR_INVALID_DATA
;
2868 int exclude_last_sample
= no_last
? 0 : 1;
2869 uint32_t j
= audio
->samplesPerPacket
;
2870 for( lsmash_entry_t
*entry
= stts
->list
->tail
; entry
&& j
> 1; entry
= entry
->prev
)
2872 isom_stts_entry_t
*stts_data
= (isom_stts_entry_t
*)entry
->data
;
2874 return LSMASH_ERR_INVALID_DATA
;
2875 for( uint32_t k
= exclude_last_sample
; k
< stts_data
->sample_count
&& j
> 1; k
++ )
2877 sample_delta
-= stts_data
->sample_delta
;
2880 exclude_last_sample
= 0;
2883 /* Set sample_delta. */
2886 /* The duration of the last sample is not set yet. */
2887 if( sample_count
- i
> 1 )
2888 return LSMASH_ERR_INVALID_DATA
;
2889 /* Add a sample_delta. */
2890 if( sample_delta
== last_stts_data
->sample_delta
)
2891 ++ last_stts_data
->sample_count
;
2892 else if( (err
= isom_add_stts_entry( stbl
, sample_delta
)) < 0 )
2895 /* The duration of the last sample is already set. Replace it with a new one. */
2896 else if( (err
= isom_replace_last_sample_delta( stbl
, sample_delta
)) < 0 )
2898 return lsmash_update_track_duration( root
, track_ID
, sample_delta
);
2901 /*---- timeline manipulator ----*/
2903 int lsmash_modify_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t edit_number
, lsmash_edit_t edit
)
2905 if( isom_check_initializer_present( root
) < 0
2906 || edit
.start_time
< -1 )
2907 return LSMASH_ERR_FUNCTION_PARAM
;
2908 lsmash_file_t
*file
= root
->file
->initializer
;
2909 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
2912 || !trak
->edts
->elst
2913 || !trak
->edts
->elst
->list
)
2914 return LSMASH_ERR_NAMELESS
;
2915 isom_elst_t
*elst
= trak
->edts
->elst
;
2916 isom_elst_entry_t
*data
= (isom_elst_entry_t
*)lsmash_get_entry_data( elst
->list
, edit_number
);
2918 return LSMASH_ERR_NAMELESS
;
2919 data
->segment_duration
= edit
.duration
;
2920 data
->media_time
= edit
.start_time
;
2921 data
->media_rate
= edit
.rate
;
2922 if( elst
->pos
== 0 || !file
->fragment
|| file
->bs
->unseekable
)
2923 return isom_update_tkhd_duration( trak
);
2924 /* Rewrite the specified entry.
2925 * Note: we don't update the version of the Edit List Box. */
2926 lsmash_bs_t
*bs
= file
->bs
;
2927 uint64_t current_pos
= bs
->offset
;
2928 uint64_t entry_pos
= elst
->pos
+ ISOM_LIST_FULLBOX_COMMON_SIZE
+ ((uint64_t)edit_number
- 1) * (elst
->version
== 1 ? 20 : 12);
2929 lsmash_bs_write_seek( bs
, entry_pos
, SEEK_SET
);
2932 lsmash_bs_put_be64( bs
, data
->segment_duration
);
2933 lsmash_bs_put_be64( bs
, data
->media_time
);
2937 lsmash_bs_put_be32( bs
, (uint32_t)LSMASH_MIN( data
->segment_duration
, UINT32_MAX
) );
2938 lsmash_bs_put_be32( bs
, (uint32_t)data
->media_time
);
2940 lsmash_bs_put_be32( bs
, data
->media_rate
);
2941 int ret
= lsmash_bs_flush_buffer( bs
);
2942 lsmash_bs_write_seek( bs
, current_pos
, SEEK_SET
);
2946 int lsmash_create_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_edit_t edit
)
2948 if( isom_check_initializer_present( root
) < 0 || edit
.start_time
< -1 )
2949 return LSMASH_ERR_FUNCTION_PARAM
;
2950 isom_trak_t
*trak
= isom_get_trak( root
->file
, track_ID
);
2953 return LSMASH_ERR_NAMELESS
;
2954 edit
.duration
= (edit
.duration
|| root
->file
->fragment
) ? edit
.duration
2955 : trak
->tkhd
->duration
? trak
->tkhd
->duration
2956 : isom_update_tkhd_duration( trak
) < 0 ? 0
2957 : trak
->tkhd
->duration
;
2958 if( (!trak
->edts
&& !isom_add_edts( trak
))
2959 || (!trak
->edts
->elst
&& !isom_add_elst( trak
->edts
)) )
2960 return LSMASH_ERR_NAMELESS
;
2961 int err
= isom_add_elst_entry( trak
->edts
->elst
, edit
.duration
, edit
.start_time
, edit
.rate
);
2964 return isom_update_tkhd_duration( trak
);
2967 int lsmash_get_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t edit_number
, lsmash_edit_t
*edit
)
2969 if( isom_check_initializer_present( root
) < 0 || !edit
)
2970 return LSMASH_ERR_FUNCTION_PARAM
;
2971 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2973 return LSMASH_ERR_NAMELESS
;
2975 || !trak
->edts
->elst
)
2979 edit
->start_time
= 0;
2983 isom_elst_entry_t
*elst
= (isom_elst_entry_t
*)lsmash_get_entry_data( trak
->edts
->elst
->list
, edit_number
);
2985 return LSMASH_ERR_NAMELESS
;
2986 edit
->duration
= elst
->segment_duration
;
2987 edit
->start_time
= elst
->media_time
;
2988 edit
->rate
= elst
->media_rate
;
2992 uint32_t lsmash_count_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
)
2994 if( isom_check_initializer_present( root
) < 0 )
2995 return LSMASH_ERR_FUNCTION_PARAM
;
2996 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
2999 || !trak
->edts
->elst
3000 || !trak
->edts
->elst
->list
)
3002 return trak
->edts
->elst
->list
->entry_count
;
3005 /*---- create / modification time fields manipulators ----*/
3007 int lsmash_update_media_modification_time( lsmash_root_t
*root
, uint32_t track_ID
)
3009 if( isom_check_initializer_present( root
) < 0 )
3010 return LSMASH_ERR_FUNCTION_PARAM
;
3011 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
3014 || !trak
->mdia
->mdhd
)
3015 return LSMASH_ERR_NAMELESS
;
3016 isom_mdhd_t
*mdhd
= trak
->mdia
->mdhd
;
3017 mdhd
->modification_time
= isom_get_current_mp4time();
3018 /* overwrite strange creation_time */
3019 if( mdhd
->creation_time
> mdhd
->modification_time
)
3020 mdhd
->creation_time
= mdhd
->modification_time
;
3024 int lsmash_update_track_modification_time( lsmash_root_t
*root
, uint32_t track_ID
)
3026 if( isom_check_initializer_present( root
) < 0 )
3027 return LSMASH_ERR_FUNCTION_PARAM
;
3028 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
3031 return LSMASH_ERR_NAMELESS
;
3032 isom_tkhd_t
*tkhd
= trak
->tkhd
;
3033 tkhd
->modification_time
= isom_get_current_mp4time();
3034 /* overwrite strange creation_time */
3035 if( tkhd
->creation_time
> tkhd
->modification_time
)
3036 tkhd
->creation_time
= tkhd
->modification_time
;
3040 int lsmash_update_movie_modification_time( lsmash_root_t
*root
)
3042 if( isom_check_initializer_present( root
) < 0 )
3043 return LSMASH_ERR_FUNCTION_PARAM
;
3044 lsmash_file_t
*file
= root
->file
->initializer
;
3046 || !file
->moov
->mvhd
)
3047 return LSMASH_ERR_INVALID_DATA
;
3048 isom_mvhd_t
*mvhd
= file
->moov
->mvhd
;
3049 mvhd
->modification_time
= isom_get_current_mp4time();
3050 /* overwrite strange creation_time */
3051 if( mvhd
->creation_time
> mvhd
->modification_time
)
3052 mvhd
->creation_time
= mvhd
->modification_time
;
3056 /*---- sample manipulators ----*/
3057 lsmash_sample_t
*lsmash_create_sample( uint32_t size
)
3059 lsmash_sample_t
*sample
= lsmash_malloc_zero( sizeof(lsmash_sample_t
) );
3064 sample
->data
= lsmash_malloc( size
);
3067 lsmash_free( sample
);
3070 sample
->length
= size
;
3074 int lsmash_sample_alloc( lsmash_sample_t
*sample
, uint32_t size
)
3077 return LSMASH_ERR_FUNCTION_PARAM
;
3080 lsmash_free( sample
->data
);
3081 sample
->data
= NULL
;
3085 if( size
== sample
->length
)
3089 data
= lsmash_malloc( size
);
3091 data
= lsmash_realloc( sample
->data
, size
);
3093 return LSMASH_ERR_MEMORY_ALLOC
;
3094 sample
->data
= data
;
3095 sample
->length
= size
;
3099 void lsmash_delete_sample( lsmash_sample_t
*sample
)
3103 lsmash_free( sample
->data
);
3104 lsmash_free( sample
);
3107 isom_sample_pool_t
*isom_create_sample_pool( uint64_t size
)
3109 isom_sample_pool_t
*pool
= lsmash_malloc_zero( sizeof(isom_sample_pool_t
) );
3114 pool
->data
= lsmash_malloc( size
);
3117 lsmash_free( pool
);
3124 void isom_remove_sample_pool( isom_sample_pool_t
*pool
)
3128 lsmash_free( pool
->data
);
3129 lsmash_free( pool
);
3132 static uint32_t isom_add_size( isom_trak_t
*trak
, uint32_t sample_size
)
3134 if( isom_add_stsz_entry( trak
->mdia
->minf
->stbl
, sample_size
) < 0 )
3136 return isom_get_sample_count( trak
);
3139 static uint32_t isom_add_dts( isom_stbl_t
*stbl
, isom_timestamp_t
*cache
, uint64_t dts
)
3141 isom_stts_t
*stts
= stbl
->stts
;
3142 if( stts
->list
->entry_count
== 0 )
3144 if( isom_add_stts_entry( stbl
, dts
) < 0 )
3149 if( dts
<= cache
->dts
)
3151 uint32_t sample_delta
= dts
- cache
->dts
;
3152 isom_stts_entry_t
*data
= (isom_stts_entry_t
*)stts
->list
->tail
->data
;
3153 if( data
->sample_delta
== sample_delta
)
3154 ++ data
->sample_count
;
3155 else if( isom_add_stts_entry( stbl
, sample_delta
) < 0 )
3158 return sample_delta
;
3161 static int isom_add_cts( isom_stbl_t
*stbl
, isom_timestamp_t
*cache
, uint64_t cts
)
3164 isom_ctts_t
*ctts
= stbl
->ctts
;
3167 if( cts
== cache
->dts
)
3172 /* Add ctts box and the first ctts entry. */
3173 if( !isom_add_ctts( stbl
) )
3174 return LSMASH_ERR_NAMELESS
;
3175 if( (err
= isom_add_ctts_entry( stbl
, 0 )) < 0 )
3178 isom_ctts_entry_t
*data
= (isom_ctts_entry_t
*)ctts
->list
->head
->data
;
3179 uint32_t sample_count
= stbl
->stsz
->sample_count
;
3180 if( sample_count
!= 1 )
3182 data
->sample_count
= sample_count
- 1;
3183 if( (err
= isom_add_ctts_entry( stbl
, cts
- cache
->dts
)) < 0 )
3187 data
->sample_offset
= cts
;
3192 return LSMASH_ERR_INVALID_DATA
;
3193 isom_ctts_entry_t
*data
= (isom_ctts_entry_t
*)ctts
->list
->tail
->data
;
3194 uint32_t sample_offset
= cts
- cache
->dts
;
3195 if( data
->sample_offset
== sample_offset
)
3196 ++ data
->sample_count
;
3197 else if( (err
= isom_add_ctts_entry( stbl
, sample_offset
)) < 0 )
3203 static int isom_add_timestamp( isom_trak_t
*trak
, uint64_t dts
, uint64_t cts
)
3206 || !trak
->mdia
->minf
->stbl
->stts
3207 || !trak
->mdia
->minf
->stbl
->stts
->list
)
3208 return LSMASH_ERR_INVALID_DATA
;
3209 lsmash_file_t
*file
= trak
->file
;
3210 if( file
->isom_compatible
&& file
->qt_compatible
&& (cts
- dts
) > INT32_MAX
)
3211 return LSMASH_ERR_INVALID_DATA
; /* sample_offset is not compatible. */
3212 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3213 isom_timestamp_t
*ts_cache
= &trak
->cache
->timestamp
;
3214 uint32_t sample_count
= isom_get_sample_count( trak
);
3215 uint32_t sample_delta
= sample_count
> 1 ? isom_add_dts( stbl
, ts_cache
, dts
) : 0;
3216 if( sample_count
> 1 && sample_delta
== 0 )
3217 return LSMASH_ERR_INVALID_DATA
;
3218 int err
= isom_add_cts( stbl
, ts_cache
, cts
);
3221 if( (cts
+ ts_cache
->ctd_shift
) < dts
)
3223 if( (file
->max_isom_version
< 4 && !file
->qt_compatible
) /* Negative sample offset is not supported. */
3224 || (file
->max_isom_version
>= 4 && file
->qt_compatible
) /* ctts version 1 is not defined in QTFF. */
3225 || ((dts
- cts
) > INT32_MAX
) ) /* Overflow */
3226 return LSMASH_ERR_INVALID_DATA
;
3227 ts_cache
->ctd_shift
= dts
- cts
;
3228 if( stbl
->ctts
->version
== 0 && !file
->qt_compatible
)
3229 stbl
->ctts
->version
= 1;
3231 if( trak
->cache
->fragment
)
3233 isom_fragment_t
*fragment_cache
= trak
->cache
->fragment
;
3234 fragment_cache
->last_duration
= sample_delta
;
3235 fragment_cache
->largest_cts
= LSMASH_MAX( ts_cache
->cts
, fragment_cache
->largest_cts
);
3240 static int isom_add_sync_point( isom_trak_t
*trak
, uint32_t sample_number
, lsmash_sample_property_t
*prop
)
3242 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3243 isom_cache_t
*cache
= trak
->cache
;
3244 if( !(prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
) ) /* no null check for prop */
3246 if( !cache
->all_sync
)
3248 if( !stbl
->stss
&& !isom_add_stss( stbl
) )
3249 return LSMASH_ERR_NAMELESS
;
3250 int err
= isom_add_stss_entry( stbl
, 1 );
3251 if( err
< 0 ) /* Declare here the first sample is a sync sample. */
3253 cache
->all_sync
= 0;
3256 if( cache
->all_sync
) /* We don't need stss box if all samples are sync sample. */
3260 if( isom_get_sample_count( trak
) == 1 )
3262 cache
->all_sync
= 1; /* Also the first sample is a sync sample. */
3265 if( !isom_add_stss( stbl
) )
3266 return LSMASH_ERR_NAMELESS
;
3268 return isom_add_stss_entry( stbl
, sample_number
);
3271 static int isom_add_partial_sync( isom_trak_t
*trak
, uint32_t sample_number
, lsmash_sample_property_t
*prop
)
3273 if( !trak
->file
->qt_compatible
)
3275 if( !(prop
->ra_flags
& QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
) )
3277 /* This sample is a partial sync sample. */
3278 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3279 if( !stbl
->stps
&& !isom_add_stps( stbl
) )
3280 return LSMASH_ERR_NAMELESS
;
3281 return isom_add_stps_entry( stbl
, sample_number
);
3284 static int isom_add_dependency_type( isom_trak_t
*trak
, lsmash_sample_property_t
*prop
)
3286 if( !trak
->file
->qt_compatible
&& !trak
->file
->avc_extensions
)
3288 int compatibility
= trak
->file
->avc_extensions
&& trak
->file
->qt_compatible
? 3
3289 : trak
->file
->qt_compatible
? 2
3290 : trak
->file
->avc_extensions
? 1
3292 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3294 return isom_add_sdtp_entry( (isom_box_t
*)stbl
, prop
, compatibility
);
3295 /* no null check for prop */
3296 if( !prop
->allow_earlier
3298 && !prop
->independent
3299 && !prop
->disposable
3300 && !prop
->redundant
)
3302 if( !isom_add_sdtp( (isom_box_t
*)stbl
) )
3303 return LSMASH_ERR_NAMELESS
;
3304 uint32_t count
= isom_get_sample_count( trak
);
3305 /* fill past samples with ISOM_SAMPLE_*_UNKNOWN */
3306 lsmash_sample_property_t null_prop
= { 0 };
3307 for( uint32_t i
= 1; i
< count
; i
++ )
3309 int err
= isom_add_sdtp_entry( (isom_box_t
*)stbl
, &null_prop
, compatibility
);
3313 return isom_add_sdtp_entry( (isom_box_t
*)stbl
, prop
, compatibility
);
3316 int isom_rap_grouping_established( isom_rap_group_t
*group
, int num_leading_samples_known
, isom_sgpd_t
*sgpd
, int is_fragment
)
3318 isom_rap_entry_t
*rap
= group
->random_access
;
3321 assert( rap
== (isom_rap_entry_t
*)sgpd
->list
->tail
->data
);
3322 rap
->num_leading_samples_known
= num_leading_samples_known
;
3323 /* Avoid duplication of sample group descriptions. */
3324 uint32_t group_description_index
= is_fragment
? 0x10001 : 1;
3325 for( lsmash_entry_t
*entry
= sgpd
->list
->head
; entry
!= sgpd
->list
->tail
; entry
= entry
->next
)
3327 isom_rap_entry_t
*data
= (isom_rap_entry_t
*)entry
->data
;
3329 return LSMASH_ERR_INVALID_DATA
;
3330 if( rap
->num_leading_samples_known
== data
->num_leading_samples_known
3331 && rap
->num_leading_samples
== data
->num_leading_samples
)
3333 /* The same description already exists.
3334 * Remove the latest random access entry. */
3335 lsmash_remove_entry_tail( sgpd
->list
, NULL
);
3336 /* Replace assigned group_description_index with the one corresponding the same description. */
3337 if( group
->assignment
->group_description_index
== 0 )
3339 /* We don't create consecutive sample groups not assigned to 'rap '.
3340 * So the previous sample group shall be a group of 'rap ' if any. */
3341 if( group
->prev_assignment
)
3343 assert( group
->prev_assignment
->group_description_index
);
3344 group
->prev_assignment
->group_description_index
= group_description_index
;
3348 group
->assignment
->group_description_index
= group_description_index
;
3351 ++group_description_index
;
3353 group
->random_access
= NULL
;
3357 int isom_group_random_access( isom_box_t
*parent
, lsmash_sample_t
*sample
)
3359 if( parent
->file
->max_isom_version
< 6 )
3363 isom_cache_t
*cache
;
3364 uint32_t sample_count
;
3366 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAK
) )
3368 isom_trak_t
*trak
= (isom_trak_t
*)parent
;
3369 sbgp
= isom_get_sample_to_group ( trak
->mdia
->minf
->stbl
, ISOM_GROUP_TYPE_RAP
);
3370 sgpd
= isom_get_sample_group_description( trak
->mdia
->minf
->stbl
, ISOM_GROUP_TYPE_RAP
);
3371 cache
= trak
->cache
;
3372 sample_count
= isom_get_sample_count( trak
);
3375 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
3377 isom_traf_t
*traf
= (isom_traf_t
*)parent
;
3378 sbgp
= isom_get_fragment_sample_to_group ( traf
, ISOM_GROUP_TYPE_RAP
);
3379 sgpd
= isom_get_fragment_sample_group_description( traf
, ISOM_GROUP_TYPE_RAP
);
3380 cache
= traf
->cache
;
3381 sample_count
= cache
->fragment
->sample_count
+ 1;
3389 /* redundant initializations to suppress warnings from unclever compilers */
3394 if( !sbgp
|| !sgpd
)
3396 lsmash_sample_property_t
*prop
= &sample
->prop
;
3397 uint8_t is_rap
= (prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
)
3398 || (prop
->ra_flags
& QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
)
3399 || (prop
->ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
)
3400 || (LSMASH_IS_POST_ROLL_START( prop
->ra_flags
) && prop
->post_roll
.identifier
== prop
->post_roll
.complete
);
3401 isom_rap_group_t
*group
= cache
->rap
;
3404 /* This sample is the first sample, create a grouping cache. */
3405 assert( sample_count
== 1 );
3406 group
= lsmash_malloc( sizeof(isom_rap_group_t
) );
3408 return LSMASH_ERR_MEMORY_ALLOC
;
3411 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3412 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3416 /* The first sample is not always a random access point. */
3417 group
->random_access
= NULL
;
3418 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3420 if( !group
->assignment
)
3422 lsmash_free( group
);
3423 return LSMASH_ERR_MEMORY_ALLOC
;
3425 group
->prev_assignment
= NULL
;
3426 group
->is_prev_rap
= is_rap
;
3431 if( group
->is_prev_rap
)
3433 /* OK. here, the previous sample is a menber of 'rap '. */
3436 /* This sample isn't a member of 'rap ' and the previous sample is.
3437 * So we create a new group and set 0 on its group_description_index. */
3438 group
->prev_assignment
= group
->assignment
;
3439 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3440 if( !group
->assignment
)
3442 lsmash_free( group
);
3443 return LSMASH_ERR_MEMORY_ALLOC
;
3446 else if( !LSMASH_IS_CLOSED_RAP( prop
->ra_flags
) )
3448 /* Create a new group since there is the possibility the next sample is a leading sample.
3449 * This sample is a member of 'rap ', so we set appropriate value on its group_description_index. */
3450 if( (err
= isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
)) < 0 )
3452 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3453 group
->prev_assignment
= group
->assignment
;
3454 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3455 if( !group
->assignment
)
3457 lsmash_free( group
);
3458 return LSMASH_ERR_MEMORY_ALLOC
;
3461 else /* The previous and current sample are a member of 'rap ', and the next sample must not be a leading sample. */
3462 ++ group
->assignment
->sample_count
;
3466 /* This sample is a member of 'rap ' and the previous sample isn't.
3467 * So we create a new group and set appropriate value on its group_description_index. */
3468 if( (err
= isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
)) < 0 )
3470 group
->random_access
= isom_add_rap_group_entry( sgpd
);
3471 group
->prev_assignment
= group
->assignment
;
3472 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, sgpd
->list
->entry_count
+ (is_fragment
? 0x10000 : 0) );
3473 if( !group
->assignment
)
3475 lsmash_free( group
);
3476 return LSMASH_ERR_MEMORY_ALLOC
;
3479 else /* The previous and current sample aren't a member of 'rap '. */
3480 ++ group
->assignment
->sample_count
;
3481 /* Obtain the property of the latest random access point group. */
3482 if( !is_rap
&& group
->random_access
)
3484 if( prop
->leading
== ISOM_SAMPLE_LEADING_UNKNOWN
)
3486 /* We can no longer know num_leading_samples in this group. */
3487 if( (err
= isom_rap_grouping_established( group
, 0, sgpd
, is_fragment
)) < 0 )
3492 if( prop
->leading
== ISOM_SAMPLE_IS_UNDECODABLE_LEADING
3493 || prop
->leading
== ISOM_SAMPLE_IS_DECODABLE_LEADING
)
3494 ++ group
->random_access
->num_leading_samples
;
3495 /* no more consecutive leading samples in this group */
3496 else if( (err
= isom_rap_grouping_established( group
, 1, sgpd
, is_fragment
)) < 0 )
3500 group
->is_prev_rap
= is_rap
;
3504 static int isom_roll_grouping_established( isom_roll_group_t
*group
)
3506 /* Avoid duplication of sample group descriptions. */
3507 isom_sgpd_t
*sgpd
= group
->sgpd
;
3508 uint32_t group_description_index
= group
->is_fragment
? 0x10001 : 1;
3509 for( lsmash_entry_t
*entry
= sgpd
->list
->head
; entry
; entry
= entry
->next
)
3511 isom_roll_entry_t
*data
= (isom_roll_entry_t
*)entry
->data
;
3513 return LSMASH_ERR_INVALID_DATA
;
3514 if( group
->roll_distance
== data
->roll_distance
)
3516 /* The same description already exists.
3517 * Set the group_description_index corresponding the same description. */
3518 group
->assignment
->group_description_index
= group_description_index
;
3521 ++group_description_index
;
3523 /* Add a new roll recovery description. */
3524 if( !isom_add_roll_group_entry( sgpd
, group
->roll_distance
) )
3525 return LSMASH_ERR_MEMORY_ALLOC
;
3526 group
->assignment
->group_description_index
= sgpd
->list
->entry_count
+ (group
->is_fragment
? 0x10000 : 0);
3530 static int isom_deduplicate_roll_group( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3533 uint32_t current_group_number
= sbgp
->list
->entry_count
- pool
->entry_count
+ 1;
3534 isom_group_assignment_entry_t
*prev_assignment
= (isom_group_assignment_entry_t
*)lsmash_get_entry_data( sbgp
->list
, current_group_number
- 1 );
3535 for( lsmash_entry_t
*entry
= pool
->head
; entry
; )
3537 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3539 || !group
->assignment
)
3540 return LSMASH_ERR_INVALID_DATA
;
3541 if( !group
->delimited
|| group
->described
!= ROLL_DISTANCE_DETERMINED
)
3543 if( prev_assignment
&& prev_assignment
->group_description_index
== group
->assignment
->group_description_index
)
3545 /* Merge the current group with the previous. */
3546 lsmash_entry_t
*next_entry
= entry
->next
;
3547 prev_assignment
->sample_count
+= group
->assignment
->sample_count
;
3549 if( (err
= lsmash_remove_entry( sbgp
->list
, current_group_number
, NULL
)) < 0
3550 || (err
= lsmash_remove_entry_direct( pool
, entry
, NULL
)) < 0 )
3556 entry
= entry
->next
;
3557 prev_assignment
= group
->assignment
;
3558 ++current_group_number
;
3564 /* Remove pooled caches that has become unnecessary. */
3565 static int isom_clean_roll_pool( lsmash_entry_list_t
*pool
)
3567 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= pool
->head
)
3569 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3571 return LSMASH_ERR_INVALID_DATA
;
3572 if( !group
->delimited
|| group
->described
!= ROLL_DISTANCE_DETERMINED
)
3574 int err
= lsmash_remove_entry_direct( pool
, entry
, NULL
);
3581 static int isom_flush_roll_pool( 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 if( group
->delimited
3590 && group
->described
== ROLL_DISTANCE_DETERMINED
3591 && group
->roll_distance
!= 0
3592 && (err
= isom_roll_grouping_established( group
)) < 0 )
3595 if( (err
= isom_deduplicate_roll_group( sbgp
, pool
)) < 0 )
3597 return isom_clean_roll_pool( pool
);
3600 static int isom_all_recovery_described( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3602 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3604 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3606 return LSMASH_ERR_INVALID_DATA
;
3607 group
->described
= ROLL_DISTANCE_DETERMINED
;
3609 return isom_flush_roll_pool( sbgp
, pool
);
3612 int isom_all_recovery_completed( isom_sbgp_t
*sbgp
, lsmash_entry_list_t
*pool
)
3614 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3616 isom_roll_group_t
*group
= (isom_roll_group_t
*)entry
->data
;
3618 return LSMASH_ERR_INVALID_DATA
;
3619 group
->described
= ROLL_DISTANCE_DETERMINED
;
3620 group
->delimited
= 1;
3622 return isom_flush_roll_pool( sbgp
, pool
);
3625 static isom_roll_entry_t
*isom_get_roll_description
3627 isom_roll_group_t
*group
3630 uint32_t group_description_index
= group
->assignment
->group_description_index
;
3631 if( group_description_index
&& group
->is_fragment
)
3633 assert( group_description_index
> 0x10000 );
3634 group_description_index
-= 0x10000;
3636 return (isom_roll_entry_t
*)lsmash_get_entry_data( group
->sgpd
->list
, group_description_index
);
3639 int isom_group_roll_recovery( isom_box_t
*parent
, lsmash_sample_t
*sample
)
3641 if( !parent
->file
->avc_extensions
3642 && !parent
->file
->qt_compatible
)
3644 uint32_t sample_count
;
3646 isom_cache_t
*cache
;
3647 lsmash_entry_list_t
*sbgp_list
;
3648 lsmash_entry_list_t
*sgpd_list
;
3649 if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAK
) )
3651 isom_trak_t
*trak
= (isom_trak_t
*)parent
;
3652 cache
= trak
->cache
;
3653 sbgp_list
= &trak
->mdia
->minf
->stbl
->sbgp_list
;
3654 sgpd_list
= &trak
->mdia
->minf
->stbl
->sgpd_list
;
3655 sample_count
= isom_get_sample_count( trak
);
3658 else if( lsmash_check_box_type_identical( parent
->type
, ISOM_BOX_TYPE_TRAF
) )
3660 if( parent
->file
->max_isom_version
< 6 )
3662 isom_traf_t
*traf
= (isom_traf_t
*)parent
;
3663 cache
= traf
->cache
;
3664 sbgp_list
= &traf
->sbgp_list
;
3665 sgpd_list
= &traf
->sgpd_list
;
3666 sample_count
= cache
->fragment
->sample_count
+ 1;
3672 return LSMASH_ERR_INVALID_DATA
;
3674 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group ( sbgp_list
);
3675 isom_sgpd_t
*sgpd
= isom_get_roll_recovery_sample_group_description( sgpd_list
);
3676 if( !sbgp
|| !sgpd
|| sbgp
->grouping_type
!= sgpd
->grouping_type
)
3678 /* Check if 'roll' -> 'prol' conversion is needed. */
3680 && sbgp
->grouping_type
== ISOM_GROUP_TYPE_ROLL
3681 && !(sample
->prop
.ra_flags
& ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
) )
3683 /* Since not every samples is a sync sample, change grouping_type into 'prol'. */
3684 sbgp
->grouping_type
= ISOM_GROUP_TYPE_PROL
;
3685 sgpd
->grouping_type
= ISOM_GROUP_TYPE_PROL
;
3687 lsmash_entry_list_t
*pool
= cache
->roll
.pool
;
3690 pool
= lsmash_create_entry_list();
3692 return LSMASH_ERR_MEMORY_ALLOC
;
3693 cache
->roll
.pool
= pool
;
3695 lsmash_sample_property_t
*prop
= &sample
->prop
;
3696 isom_roll_group_t
*group
= (isom_roll_group_t
*)lsmash_get_entry_data( pool
, pool
->entry_count
);
3697 int is_recovery_start
= LSMASH_IS_POST_ROLL_START( prop
->ra_flags
);
3698 int valid_pre_roll
= !is_recovery_start
3699 && (prop
->ra_flags
!= ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE
)
3700 && (prop
->pre_roll
.distance
> 0)
3701 && (prop
->pre_roll
.distance
<= -INT16_MIN
);
3702 int new_group
= !group
|| is_recovery_start
|| (group
->prev_is_recovery_start
!= is_recovery_start
);
3705 /* Check pre-roll distance. */
3706 assert( group
->assignment
&& group
->sgpd
);
3707 isom_roll_entry_t
*prev_roll
= isom_get_roll_description( group
);
3709 new_group
= valid_pre_roll
;
3710 else if( !valid_pre_roll
|| (prop
->pre_roll
.distance
!= -prev_roll
->roll_distance
) )
3711 /* Pre-roll distance is different from the previous. */
3717 group
->delimited
= 1;
3719 assert( sample_count
== 1 );
3720 /* Create a new group. */
3721 group
= lsmash_malloc_zero( sizeof(isom_roll_group_t
) );
3723 return LSMASH_ERR_MEMORY_ALLOC
;
3725 group
->prev_is_recovery_start
= is_recovery_start
;
3726 group
->is_fragment
= is_fragment
;
3727 group
->assignment
= isom_add_group_assignment_entry( sbgp
, 1, 0 );
3728 if( !group
->assignment
|| lsmash_add_entry( pool
, group
) < 0 )
3730 lsmash_free( group
);
3731 return LSMASH_ERR_MEMORY_ALLOC
;
3733 if( is_recovery_start
)
3735 /* a member of non-roll or post-roll group */
3736 group
->first_sample
= sample_count
;
3737 group
->recovery_point
= prop
->post_roll
.complete
;
3741 group
->described
= ROLL_DISTANCE_DETERMINED
;
3742 if( valid_pre_roll
)
3744 /* a member of pre-roll group */
3745 group
->roll_distance
= -prop
->pre_roll
.distance
;
3746 int err
= isom_roll_grouping_established( group
);
3751 /* a member of non-roll group */
3752 group
->roll_distance
= 0;
3757 group
->prev_is_recovery_start
= is_recovery_start
;
3758 group
->assignment
->sample_count
+= 1;
3760 /* If encountered a RAP, all recovery is completed here. */
3761 if( prop
->ra_flags
& (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
3762 | ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
3763 | QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC
) )
3764 return isom_all_recovery_described( sbgp
, pool
);
3765 /* Check whether this sample is a random access recovery point or not. */
3766 for( lsmash_entry_t
*entry
= pool
->head
; entry
; entry
= entry
->next
)
3768 group
= (isom_roll_group_t
*)entry
->data
;
3770 return LSMASH_ERR_INVALID_DATA
;
3771 if( group
->described
== ROLL_DISTANCE_DETERMINED
)
3773 if( group
->described
== ROLL_DISTANCE_INITIALIZED
)
3775 /* Let's consider the following picture sequence.
3776 * coded order : P[0] P[1] P[2] P[3] P[4] P[5]
3779 * Here, P[0] conveys a recovery point SEI and P[3] is the recovery point.
3780 * Correctness of decoded pictures is specified by recovery point in output order for both AVC and HEVC.
3781 * Therefore, as follows,
3782 * output order : P[0] P[2] P[1] P[5]|P[3] P[4]
3783 * ---(incorrect?)--->|
3784 * there is no guarantee that P[5] is decoded and output correctly.
3785 * From this, it can be said that the roll_distance of this sequence is equal to 5. */
3786 isom_roll_entry_t
*post_roll
= isom_get_roll_description( group
);
3787 if( post_roll
&& post_roll
->roll_distance
> 0 )
3789 if( group
->rp_cts
> sample
->cts
)
3790 /* Updated roll_distance for composition reordering. */
3791 post_roll
->roll_distance
= sample_count
- group
->first_sample
;
3792 if( ++ group
->wait_and_see_count
>= MAX_ROLL_WAIT_AND_SEE_COUNT
)
3793 group
->described
= ROLL_DISTANCE_DETERMINED
;
3796 else if( prop
->post_roll
.identifier
== group
->recovery_point
)
3798 int16_t distance
= sample_count
- group
->first_sample
;
3799 group
->rp_cts
= sample
->cts
;
3800 group
->roll_distance
= distance
;
3801 /* Add a roll recovery entry only when roll_distance isn't zero since roll_distance = 0 must not be used. */
3804 /* Now, this group is a 'roll'.
3805 * The roll_distance may be updated later because of composition reordering. */
3806 group
->described
= ROLL_DISTANCE_INITIALIZED
;
3807 group
->wait_and_see_count
= 0;
3808 /* All groups with uninitialized roll_distance before the current group are described. */
3809 lsmash_entry_t
*current
= entry
;
3810 for( entry
= pool
->head
; entry
!= current
; entry
= entry
->next
)
3812 group
= (isom_roll_group_t
*)entry
->data
;
3813 if( !group
|| group
->described
!= ROLL_DISTANCE_INITIALIZED
)
3815 group
->described
= ROLL_DISTANCE_DETERMINED
;
3817 /* Cache the CTS of the first recovery point in a subsegment. */
3819 && cache
->fragment
->subsegment
.first_rp_number
== 0 )
3821 cache
->fragment
->subsegment
.first_rp_number
= sample_count
;
3822 cache
->fragment
->subsegment
.first_rp_cts
= sample
->cts
;
3823 cache
->fragment
->subsegment
.first_ed_cts
= sample
->cts
;
3824 cache
->fragment
->subsegment
.decodable
= 1;
3828 /* Random Accessible Point */
3829 return isom_all_recovery_described( sbgp
, pool
);
3832 return isom_flush_roll_pool( sbgp
, pool
);
3835 /* returns 1 if pooled samples must be flushed. */
3836 /* FIXME: I wonder if this function should have a extra argument which indicates force_to_flush_cached_chunk.
3837 see lsmash_append_sample for detail. */
3838 static int isom_add_chunk( isom_trak_t
*trak
, lsmash_sample_t
*sample
)
3842 || !trak
->mdia
->mdhd
3843 || trak
->mdia
->mdhd
->timescale
== 0
3844 || !trak
->mdia
->minf
->dinf
3845 || !trak
->mdia
->minf
->dinf
->dref
3846 || !trak
->mdia
->minf
->stbl
->stsd
3847 || !trak
->mdia
->minf
->stbl
->stsc
3848 || !trak
->mdia
->minf
->stbl
->stsc
->list
)
3849 return LSMASH_ERR_INVALID_DATA
;
3850 isom_chunk_t
*current
= &trak
->cache
->chunk
;
3851 if( !current
->pool
)
3853 /* Very initial settings, just once per track */
3854 current
->pool
= isom_create_sample_pool( 0 );
3855 if( !current
->pool
)
3856 return LSMASH_ERR_MEMORY_ALLOC
;
3858 if( current
->pool
->sample_count
== 0 )
3860 /* Cannot decide whether we should flush the current sample or not here yet. */
3861 current
->chunk_number
+= 1;
3862 current
->sample_description_index
= sample
->index
;
3863 current
->first_dts
= sample
->dts
;
3866 if( sample
->dts
< current
->first_dts
)
3867 return LSMASH_ERR_INVALID_DATA
; /* easy error check. */
3868 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3869 lsmash_file_t
*file
= isom_get_written_media_file( trak
, current
->sample_description_index
);
3870 if( (current
->sample_description_index
== sample
->index
)
3871 && (file
->max_chunk_duration
>= ((double)(sample
->dts
- current
->first_dts
) / trak
->mdia
->mdhd
->timescale
))
3872 && (file
->max_chunk_size
>= current
->pool
->size
+ sample
->length
) )
3873 return 0; /* No need to flush current cached chunk, the current sample must be put into that. */
3874 /* NOTE: chunk relative stuff must be pushed into file after a chunk is fully determined with its contents. */
3875 /* Now the current cached chunk is fixed, actually add the chunk relative properties to its file accordingly. */
3876 isom_stsc_entry_t
*last_stsc_data
= stbl
->stsc
->list
->tail
? (isom_stsc_entry_t
*)stbl
->stsc
->list
->tail
->data
: NULL
;
3877 /* Create a new chunk sequence in this track if needed. */
3879 if( (!last_stsc_data
3880 || current
->pool
->sample_count
!= last_stsc_data
->samples_per_chunk
3881 || current
->sample_description_index
!= last_stsc_data
->sample_description_index
)
3882 && (err
= isom_add_stsc_entry( stbl
, current
->chunk_number
,
3883 current
->pool
->sample_count
,
3884 current
->sample_description_index
)) < 0 )
3886 /* Add a new chunk offset in this track. */
3887 uint64_t offset
= file
->size
;
3888 if( file
->fragment
)
3889 offset
+= ISOM_BASEBOX_COMMON_SIZE
+ file
->fragment
->pool_size
;
3890 if( (err
= isom_add_stco_entry( stbl
, offset
)) < 0 )
3892 /* Update and re-initialize cache, using the current sample */
3893 current
->chunk_number
+= 1;
3894 current
->sample_description_index
= sample
->index
;
3895 current
->first_dts
= sample
->dts
;
3896 /* current->pool must be flushed in isom_append_sample_internal() */
3900 static int isom_write_pooled_samples( lsmash_file_t
*file
, isom_sample_pool_t
*pool
)
3904 || !file
->bs
->stream
3905 || !(file
->flags
& LSMASH_FILE_MODE_WRITE
)
3906 || !(file
->flags
& LSMASH_FILE_MODE_MEDIA
)
3907 || ((file
->flags
& LSMASH_FILE_MODE_BOX
) && !file
->mdat
) )
3908 return LSMASH_ERR_INVALID_DATA
;
3909 lsmash_bs_put_bytes( file
->bs
, pool
->size
, pool
->data
);
3910 int err
= lsmash_bs_flush_buffer( file
->bs
);
3914 file
->mdat
->media_size
+= pool
->size
;
3915 file
->size
+= pool
->size
;
3916 pool
->sample_count
= 0;
3921 int isom_update_sample_tables( isom_trak_t
*trak
, lsmash_sample_t
*sample
, uint32_t *samples_per_packet
)
3924 isom_audio_entry_t
*audio
= (isom_audio_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
, sample
->index
);
3925 if( (audio
->manager
& LSMASH_AUDIO_DESCRIPTION
)
3926 && (audio
->manager
& LSMASH_QTFF_BASE
)
3927 && (audio
->version
== 1)
3928 && (audio
->compression_ID
!= QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION
) )
3930 /* Add entries of the sample table for each uncompressed sample. */
3931 uint64_t sample_duration
= trak
->mdia
->mdhd
->timescale
/ (audio
->samplerate
>> 16);
3932 if( audio
->samplesPerPacket
== 0 || sample_duration
== 0 )
3933 return LSMASH_ERR_INVALID_DATA
;
3934 uint64_t sample_dts
= sample
->dts
;
3935 uint64_t sample_cts
= sample
->cts
;
3936 for( uint32_t i
= 0; i
< audio
->samplesPerPacket
; i
++ )
3938 /* Add a size of uncomressed audio and increment sample_count.
3939 * This points to individual uncompressed audio samples, each one byte in size, within the compressed frames. */
3940 uint32_t sample_count
= isom_add_size( trak
, 1 );
3941 if( sample_count
== 0 )
3942 return LSMASH_ERR_NAMELESS
;
3943 /* Add a decoding timestamp and a composition timestamp. */
3944 if( (err
= isom_add_timestamp( trak
, sample_dts
, sample_cts
)) < 0 )
3946 sample_dts
+= sample_duration
;
3947 sample_cts
+= sample_duration
;
3949 *samples_per_packet
= audio
->samplesPerPacket
;
3953 /* Add a sample_size and increment sample_count. */
3954 uint32_t sample_count
= isom_add_size( trak
, sample
->length
);
3955 if( sample_count
== 0 )
3956 return LSMASH_ERR_NAMELESS
;
3957 /* Add a decoding timestamp and a composition timestamp. */
3958 if( (err
= isom_add_timestamp( trak
, sample
->dts
, sample
->cts
)) < 0 )
3960 /* Add a sync point if needed. */
3961 if( (err
= isom_add_sync_point( trak
, sample_count
, &sample
->prop
)) < 0 )
3963 /* Add a partial sync point if needed. */
3964 if( (err
= isom_add_partial_sync( trak
, sample_count
, &sample
->prop
)) < 0 )
3966 /* Add leading, independent, disposable and redundant information if needed. */
3967 if( (err
= isom_add_dependency_type( trak
, &sample
->prop
)) < 0 )
3969 /* Group samples into random access point type if needed. */
3970 if( (err
= isom_group_random_access( (isom_box_t
*)trak
, sample
)) < 0 )
3972 /* Group samples into random access recovery point type if needed. */
3973 if( (err
= isom_group_roll_recovery( (isom_box_t
*)trak
, sample
)) < 0 )
3975 *samples_per_packet
= 1;
3977 /* Add a chunk if needed. */
3978 return isom_add_chunk( trak
, sample
);
3981 static int isom_output_cached_chunk( isom_trak_t
*trak
)
3983 isom_chunk_t
*chunk
= &trak
->cache
->chunk
;
3984 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
3985 isom_stsc_entry_t
*last_stsc_data
= stbl
->stsc
->list
->tail
? (isom_stsc_entry_t
*)stbl
->stsc
->list
->tail
->data
: NULL
;
3986 /* Create a new chunk sequence in this track if needed. */
3988 if( (!last_stsc_data
3989 || chunk
->pool
->sample_count
!= last_stsc_data
->samples_per_chunk
3990 || chunk
->sample_description_index
!= last_stsc_data
->sample_description_index
)
3991 && (err
= isom_add_stsc_entry( stbl
, chunk
->chunk_number
,
3992 chunk
->pool
->sample_count
,
3993 chunk
->sample_description_index
)) < 0 )
3995 lsmash_file_t
*file
= isom_get_written_media_file( trak
, chunk
->sample_description_index
);
3996 if( file
->fragment
)
3998 /* Add a new chunk offset in this track. */
3999 if( (err
= isom_add_stco_entry( stbl
, file
->size
+ ISOM_BASEBOX_COMMON_SIZE
+ file
->fragment
->pool_size
)) < 0 )
4001 return isom_append_fragment_track_run( file
, chunk
);
4003 /* Add a new chunk offset in this track. */
4004 if( (err
= isom_add_stco_entry( stbl
, file
->size
)) < 0 )
4006 /* Output pooled samples in this track. */
4007 return isom_write_pooled_samples( file
, chunk
->pool
);
4010 int isom_pool_sample( isom_sample_pool_t
*pool
, lsmash_sample_t
*sample
, uint32_t samples_per_packet
)
4012 uint64_t pool_size
= pool
->size
+ sample
->length
;
4013 if( pool
->alloc
< pool_size
)
4016 uint64_t alloc
= pool_size
+ (1<<16);
4018 data
= lsmash_malloc( alloc
);
4020 data
= lsmash_realloc( pool
->data
, alloc
);
4022 return LSMASH_ERR_MEMORY_ALLOC
;
4024 pool
->alloc
= alloc
;
4026 memcpy( pool
->data
+ pool
->size
, sample
->data
, sample
->length
);
4027 pool
->size
= pool_size
;
4028 pool
->sample_count
+= samples_per_packet
;
4029 lsmash_delete_sample( sample
);
4033 static int isom_append_sample_internal( isom_trak_t
*trak
, lsmash_sample_t
*sample
)
4035 uint32_t samples_per_packet
;
4036 int ret
= isom_update_sample_tables( trak
, sample
, &samples_per_packet
);
4039 /* ret == 1 means pooled samples must be flushed. */
4040 isom_sample_pool_t
*current_pool
= trak
->cache
->chunk
.pool
;
4043 /* The sample_description_index in the cache is one of the next written chunk.
4044 * Therefore, it cannot be referenced here. */
4045 lsmash_entry_list_t
*stsc_list
= trak
->mdia
->minf
->stbl
->stsc
->list
;
4046 isom_stsc_entry_t
*last_stsc_data
= (isom_stsc_entry_t
*)stsc_list
->tail
->data
;
4047 lsmash_file_t
*file
= isom_get_written_media_file( trak
, last_stsc_data
->sample_description_index
);
4048 if( (ret
= isom_write_pooled_samples( file
, current_pool
)) < 0 )
4051 /* Arbitration system between tracks with extremely scattering dts.
4052 * Here, we check whether asynchronization between the tracks exceeds the tolerance.
4053 * If a track has too old "first DTS" in its cached chunk than current sample's DTS, then its pooled samples must be flushed.
4054 * We don't consider presentation of media since any edit can pick an arbitrary portion of media in track.
4055 * Note: you needn't read this loop until you grasp the basic handling of chunks. */
4056 lsmash_file_t
*file
= trak
->file
;
4057 double tolerance
= file
->max_async_tolerance
;
4058 for( lsmash_entry_t
*entry
= file
->moov
->trak_list
.head
; entry
; entry
= entry
->next
)
4060 isom_trak_t
*other
= (isom_trak_t
*)entry
->data
;
4066 || !other
->mdia
->mdhd
4067 || other
->mdia
->mdhd
->timescale
== 0
4068 || !other
->mdia
->minf
4069 || !other
->mdia
->minf
->stbl
4070 || !other
->mdia
->minf
->stbl
->stsc
4071 || !other
->mdia
->minf
->stbl
->stsc
->list
)
4072 return LSMASH_ERR_INVALID_DATA
;
4073 isom_chunk_t
*chunk
= &other
->cache
->chunk
;
4074 if( !chunk
->pool
|| chunk
->pool
->sample_count
== 0 )
4076 double diff
= ((double)sample
->dts
/ trak
->mdia
->mdhd
->timescale
)
4077 - ((double)chunk
->first_dts
/ other
->mdia
->mdhd
->timescale
);
4078 if( diff
> tolerance
&& (ret
= isom_output_cached_chunk( other
)) < 0 )
4080 /* Note: we don't flush the cached chunk in the current track and the current sample here
4081 * even if the conditional expression of '-diff > tolerance' meets.
4082 * That's useless because appending a sample to another track would be a good equivalent.
4083 * It's even harmful because it causes excess chunk division by calling
4084 * isom_output_cached_chunk() which always generates a new chunk.
4085 * Anyway some excess chunk division will be there, but rather less without it.
4086 * To completely avoid this, we need to observe at least whether the current sample will be placed
4087 * right next to the previous chunk of the same track or not. */
4089 /* anyway the current sample must be pooled. */
4090 return isom_pool_sample( current_pool
, sample
, samples_per_packet
);
4093 /* This function is for non-fragmented movie. */
4094 static int isom_append_sample( lsmash_file_t
*file
, uint32_t track_ID
, lsmash_sample_t
*sample
)
4096 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
4101 || !trak
->mdia
->mdhd
4102 || trak
->mdia
->mdhd
->timescale
== 0
4103 || !trak
->mdia
->minf
4104 || !trak
->mdia
->minf
->stbl
4105 || !trak
->mdia
->minf
->stbl
->stsd
4106 || !trak
->mdia
->minf
->stbl
->stsc
|| !trak
->mdia
->minf
->stbl
->stsc
->list
)
4107 return LSMASH_ERR_NAMELESS
;
4108 /* If there is no available Media Data Box to write samples, add and write a new one before any chunk offset is decided. */
4112 if( !isom_add_mdat( file
) )
4113 return LSMASH_ERR_NAMELESS
;
4114 file
->mdat
->manager
|= LSMASH_PLACEHOLDER
;
4115 if( (err
= isom_write_box( file
->bs
, (isom_box_t
*)file
->mdat
)) < 0 )
4117 assert( file
->free
);
4118 file
->size
+= file
->free
->size
+ file
->mdat
->size
;
4120 isom_sample_entry_t
*sample_entry
= (isom_sample_entry_t
*)lsmash_get_entry_data( &trak
->mdia
->minf
->stbl
->stsd
->list
, sample
->index
);
4122 return LSMASH_ERR_NAMELESS
;
4123 if( isom_is_lpcm_audio( sample_entry
) )
4125 uint32_t frame_size
= ((isom_audio_entry_t
*)sample_entry
)->constBytesPerAudioPacket
;
4126 if( sample
->length
== frame_size
)
4127 return isom_append_sample_internal( trak
, sample
);
4128 else if( sample
->length
< frame_size
)
4129 return LSMASH_ERR_INVALID_DATA
;
4130 /* Append samples splitted into each LPCMFrame. */
4131 uint64_t dts
= sample
->dts
;
4132 uint64_t cts
= sample
->cts
;
4133 for( uint32_t offset
= 0; offset
< sample
->length
; offset
+= frame_size
)
4135 lsmash_sample_t
*lpcm_sample
= lsmash_create_sample( frame_size
);
4137 return LSMASH_ERR_MEMORY_ALLOC
;
4138 memcpy( lpcm_sample
->data
, sample
->data
+ offset
, frame_size
);
4139 lpcm_sample
->dts
= dts
++;
4140 lpcm_sample
->cts
= cts
++;
4141 lpcm_sample
->prop
= sample
->prop
;
4142 lpcm_sample
->index
= sample
->index
;
4143 if( (err
= isom_append_sample_internal( trak
, lpcm_sample
)) < 0 )
4145 lsmash_delete_sample( lpcm_sample
);
4149 lsmash_delete_sample( sample
);
4152 return isom_append_sample_internal( trak
, sample
);
4155 static int isom_output_cache( isom_trak_t
*trak
)
4158 isom_cache_t
*cache
= trak
->cache
;
4159 if( cache
->chunk
.pool
4160 && cache
->chunk
.pool
->sample_count
4161 && (err
= isom_output_cached_chunk( trak
)) < 0 )
4163 isom_stbl_t
*stbl
= trak
->mdia
->minf
->stbl
;
4164 for( lsmash_entry_t
*entry
= stbl
->sgpd_list
.head
; entry
; entry
= entry
->next
)
4166 isom_sgpd_t
*sgpd
= (isom_sgpd_t
*)entry
->data
;
4168 return LSMASH_ERR_INVALID_DATA
;
4169 switch( sgpd
->grouping_type
)
4171 case ISOM_GROUP_TYPE_RAP
:
4173 isom_rap_group_t
*group
= cache
->rap
;
4176 if( stbl
->file
->fragment
)
4179 return LSMASH_ERR_NAMELESS
;
4181 if( !group
->random_access
)
4183 group
->random_access
->num_leading_samples_known
= 1;
4186 case ISOM_GROUP_TYPE_ROLL
:
4187 case ISOM_GROUP_TYPE_PROL
:
4188 if( !cache
->roll
.pool
)
4190 if( stbl
->file
->fragment
)
4193 return LSMASH_ERR_NAMELESS
;
4195 isom_sbgp_t
*sbgp
= isom_get_roll_recovery_sample_to_group( &stbl
->sbgp_list
);
4197 return LSMASH_ERR_NAMELESS
;
4198 if( (err
= isom_all_recovery_completed( sbgp
, cache
->roll
.pool
)) < 0 )
4208 int lsmash_flush_pooled_samples( lsmash_root_t
*root
, uint32_t track_ID
, uint32_t last_sample_delta
)
4210 if( isom_check_initializer_present( root
) < 0 )
4211 return LSMASH_ERR_FUNCTION_PARAM
;
4212 lsmash_file_t
*file
= root
->file
;
4214 && file
->fragment
->movie
)
4215 return isom_flush_fragment_pooled_samples( file
, track_ID
, last_sample_delta
);
4216 if( file
!= file
->initializer
)
4217 return LSMASH_ERR_INVALID_DATA
;
4218 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
4222 || !trak
->mdia
->minf
4223 || !trak
->mdia
->minf
->stbl
4224 || !trak
->mdia
->minf
->stbl
->stsc
4225 || !trak
->mdia
->minf
->stbl
->stsc
->list
)
4226 return LSMASH_ERR_NAMELESS
;
4227 int err
= isom_output_cache( trak
);
4230 return lsmash_set_last_sample_delta( root
, track_ID
, last_sample_delta
);
4233 int lsmash_append_sample( lsmash_root_t
*root
, uint32_t track_ID
, lsmash_sample_t
*sample
)
4235 if( isom_check_initializer_present( root
) < 0
4239 return LSMASH_ERR_FUNCTION_PARAM
;
4240 lsmash_file_t
*file
= root
->file
;
4241 /* We think max_chunk_duration == 0, which means all samples will be cached on memory, should be prevented.
4242 * This means removal of a feature that we used to have, but anyway very alone chunk does not make sense. */
4245 || !(file
->flags
& LSMASH_FILE_MODE_BOX
)
4246 || file
->max_chunk_duration
== 0
4247 || file
->max_async_tolerance
== 0 )
4248 return LSMASH_ERR_NAMELESS
;
4249 /* Write File Type Box here if it was not written yet. */
4250 if( file
->flags
& LSMASH_FILE_MODE_INITIALIZATION
)
4252 if( file
->ftyp
&& !(file
->ftyp
->manager
& LSMASH_WRITTEN_BOX
) )
4254 int err
= isom_write_box( file
->bs
, (isom_box_t
*)file
->ftyp
);
4257 file
->size
+= file
->ftyp
->size
;
4260 if( (file
->flags
& LSMASH_FILE_MODE_FRAGMENTED
)
4262 && file
->fragment
->pool
)
4263 return isom_append_fragment_sample( file
, track_ID
, sample
);
4264 if( file
!= file
->initializer
)
4265 return LSMASH_ERR_INVALID_DATA
;
4266 return isom_append_sample( file
, track_ID
, sample
);
4269 /*---- misc functions ----*/
4271 int lsmash_delete_explicit_timeline_map( lsmash_root_t
*root
, uint32_t track_ID
)
4273 if( isom_check_initializer_present( root
) < 0 )
4274 return LSMASH_ERR_FUNCTION_PARAM
;
4275 isom_trak_t
*trak
= isom_get_trak( root
->file
->initializer
, track_ID
);
4277 return LSMASH_ERR_NAMELESS
;
4278 isom_remove_box_by_itself( trak
->edts
);
4279 return isom_update_tkhd_duration( trak
);
4282 void lsmash_delete_tyrant_chapter( lsmash_root_t
*root
)
4284 if( isom_check_initializer_present( root
) < 0
4285 || !root
->file
->initializer
->moov
4286 || !root
->file
->initializer
->moov
->udta
)
4288 isom_remove_box_by_itself( root
->file
->moov
->udta
->chpl
);
4291 int lsmash_set_copyright( lsmash_root_t
*root
, uint32_t track_ID
, uint16_t ISO_language
, char *notice
)
4293 if( isom_check_initializer_present( root
) < 0
4294 || (ISO_language
&& ISO_language
< 0x800)
4296 return LSMASH_ERR_FUNCTION_PARAM
;
4297 lsmash_file_t
*file
= root
->file
;
4299 || !file
->isom_compatible
)
4300 return LSMASH_ERR_NAMELESS
;
4304 isom_trak_t
*trak
= isom_get_trak( file
, track_ID
);
4305 if( !trak
|| (!trak
->udta
&& !isom_add_udta( trak
)) )
4306 return LSMASH_ERR_NAMELESS
;
4311 if( !file
->moov
->udta
&& !isom_add_udta( file
->moov
) )
4312 return LSMASH_ERR_NAMELESS
;
4313 udta
= file
->moov
->udta
;
4316 for( lsmash_entry_t
*entry
= udta
->cprt_list
.head
; entry
; entry
= entry
->next
)
4318 isom_cprt_t
*cprt
= (isom_cprt_t
*)entry
->data
;
4319 if( !cprt
|| cprt
->language
== ISO_language
)
4320 return LSMASH_ERR_NAMELESS
;
4322 if( !isom_add_cprt( udta
) )
4323 return LSMASH_ERR_NAMELESS
;
4324 isom_cprt_t
*cprt
= (isom_cprt_t
*)udta
->cprt_list
.tail
->data
;
4325 cprt
->language
= ISO_language
;
4326 cprt
->notice_length
= strlen( notice
) + 1;
4327 cprt
->notice
= lsmash_memdup( notice
, cprt
->notice_length
);