Fix a crash when checking compatibilities of segment files.
[L-SMASH.git] / core / isom.c
blobeeab2abaf9fe209ba0ac61565d81f9414e179622
1 /*****************************************************************************
2 * isom.c:
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 */
26 #include <stdlib.h>
27 #include <string.h>
28 #include <inttypes.h>
29 #ifdef _WIN32
30 #include <windows.h>
31 #endif
33 #include "box.h"
34 #include "write.h"
35 #include "fragment.h"
36 #include "read.h"
38 #include "codecs/mp4a.h"
39 #include "codecs/mp4sys.h"
40 #include "codecs/description.h"
42 /*---- ----*/
43 isom_trak_t *isom_get_trak( lsmash_file_t *file, uint32_t track_ID )
45 if( track_ID == 0
46 || !file
47 || !file->moov )
48 return NULL;
49 for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next )
51 isom_trak_t *trak = (isom_trak_t *)entry->data;
52 if( !trak
53 || !trak->tkhd )
54 return NULL;
55 if( trak->tkhd->track_ID == track_ID )
56 return trak;
58 return NULL;
61 isom_trex_t *isom_get_trex( isom_mvex_t *mvex, uint32_t track_ID )
63 if( track_ID == 0 || !mvex )
64 return NULL;
65 for( lsmash_entry_t *entry = mvex->trex_list.head; entry; entry = entry->next )
67 isom_trex_t *trex = (isom_trex_t *)entry->data;
68 if( !trex )
69 return NULL;
70 if( trex->track_ID == track_ID )
71 return trex;
73 return NULL;
76 isom_traf_t *isom_get_traf( isom_moof_t *moof, uint32_t track_ID )
78 if( track_ID == 0 || !moof )
79 return NULL;
80 for( lsmash_entry_t *entry = moof->traf_list.head; entry; entry = entry->next )
82 isom_traf_t *traf = (isom_traf_t *)entry->data;
83 if( !traf
84 || !traf->tfhd )
85 return NULL;
86 if( traf->tfhd->track_ID == track_ID )
87 return traf;
89 return NULL;
92 isom_tfra_t *isom_get_tfra( isom_mfra_t *mfra, uint32_t track_ID )
94 if( track_ID == 0 || !mfra )
95 return NULL;
96 for( lsmash_entry_t *entry = mfra->tfra_list.head; entry; entry = entry->next )
98 isom_tfra_t *tfra = (isom_tfra_t *)entry->data;
99 if( !tfra )
100 return NULL;
101 if( tfra->track_ID == track_ID )
102 return tfra;
104 return NULL;
107 static int isom_add_elst_entry( isom_elst_t *elst, uint64_t segment_duration, int64_t media_time, int32_t media_rate )
109 assert( elst->file );
110 isom_elst_entry_t *data = lsmash_malloc( sizeof(isom_elst_entry_t) );
111 if( !data )
112 return -1;
113 data->segment_duration = segment_duration;
114 data->media_time = media_time;
115 data->media_rate = media_rate;
116 if( lsmash_add_entry( elst->list, data ) )
118 lsmash_free( data );
119 return -1;
121 if( !elst->file->undefined_64_ver
122 && (data->segment_duration > UINT32_MAX
123 || data->media_time > INT32_MAX
124 || data->media_time < INT32_MIN) )
125 elst->version = 1;
126 return 0;
129 isom_dcr_ps_entry_t *isom_create_ps_entry( uint8_t *ps, uint32_t ps_size )
131 isom_dcr_ps_entry_t *entry = lsmash_malloc( sizeof(isom_dcr_ps_entry_t) );
132 if( !entry )
133 return NULL;
134 entry->nalUnit = lsmash_memdup( ps, ps_size );
135 if( !entry->nalUnit )
137 lsmash_free( entry );
138 return NULL;
140 entry->nalUnitLength = ps_size;
141 entry->unused = 0;
142 return entry;
145 void isom_remove_dcr_ps( isom_dcr_ps_entry_t *ps )
147 if( !ps )
148 return;
149 if( ps->nalUnit )
150 lsmash_free( ps->nalUnit );
151 lsmash_free( ps );
154 /* This function returns 0 if failed, sample_entry_number if succeeded. */
155 int lsmash_add_sample_entry( lsmash_root_t *root, uint32_t track_ID, void *summary )
157 if( !root || !summary )
158 return 0;
159 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
160 if( !trak
161 || !trak->file
162 || !trak->mdia
163 || !trak->mdia->minf
164 || !trak->mdia->minf->stbl
165 || !trak->mdia->minf->stbl->stsd )
166 return 0;
167 isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd;
168 lsmash_entry_list_t *list = &stsd->list;
169 int ret = -1;
170 lsmash_codec_type_t sample_type = ((lsmash_summary_t *)summary)->sample_type;
171 if( lsmash_check_codec_type_identical( sample_type, LSMASH_CODEC_TYPE_RAW ) )
173 if( trak->mdia->minf->vmhd )
174 ret = isom_setup_visual_description( stsd, sample_type, (lsmash_video_summary_t *)summary );
175 else if( trak->mdia->minf->smhd )
176 ret = isom_setup_audio_description( stsd, sample_type, (lsmash_audio_summary_t *)summary );
177 return ret < 0 ? 0 : list->entry_count;
179 typedef void (*opaque_func_t)( void );
180 static struct description_setup_table_tag
182 lsmash_codec_type_t type;
183 opaque_func_t func;
184 } description_setup_table[160] = { { LSMASH_CODEC_TYPE_INITIALIZER, NULL } };
185 if( !description_setup_table[0].func )
187 int i = 0;
188 #define ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( type, func ) \
189 description_setup_table[i++] = (struct description_setup_table_tag){ type, (opaque_func_t)func }
190 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, isom_setup_visual_description );
191 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO, isom_setup_visual_description );
192 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO, isom_setup_visual_description );
193 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC4_VIDEO, isom_setup_visual_description );
194 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO, isom_setup_visual_description );
195 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO, isom_setup_visual_description );
196 #if 0
197 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO, isom_setup_visual_description );
198 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO, isom_setup_visual_description );
199 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO, isom_setup_visual_description );
200 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO, isom_setup_visual_description );
201 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, isom_setup_visual_description );
202 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO, isom_setup_visual_description );
203 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO, isom_setup_visual_description );
204 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO, isom_setup_visual_description );
205 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO, isom_setup_visual_description );
206 #endif
207 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, isom_setup_visual_description );
208 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO, isom_setup_visual_description );
209 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, isom_setup_visual_description );
210 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, isom_setup_visual_description );
211 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, isom_setup_visual_description );
212 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, isom_setup_visual_description );
213 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, isom_setup_visual_description );
214 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, isom_setup_visual_description );
215 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, isom_setup_visual_description );
216 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, isom_setup_visual_description );
217 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, isom_setup_visual_description );
218 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, isom_setup_visual_description );
219 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, isom_setup_visual_description );
220 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, isom_setup_visual_description );
221 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, isom_setup_visual_description );
222 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, isom_setup_visual_description );
223 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, isom_setup_visual_description );
224 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, isom_setup_visual_description );
225 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, isom_setup_visual_description );
226 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, isom_setup_visual_description );
227 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, isom_setup_visual_description );
228 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, isom_setup_visual_description );
229 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO, isom_setup_visual_description );
230 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO, isom_setup_visual_description );
231 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, isom_setup_visual_description );
232 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, isom_setup_visual_description );
233 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, isom_setup_visual_description );
234 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, isom_setup_visual_description );
235 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, isom_setup_visual_description );
236 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, isom_setup_visual_description );
237 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, isom_setup_audio_description );
238 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, isom_setup_audio_description );
239 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, isom_setup_audio_description );
240 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, isom_setup_audio_description );
241 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, isom_setup_audio_description );
242 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, isom_setup_audio_description );
243 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, isom_setup_audio_description );
244 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, isom_setup_audio_description );
245 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, isom_setup_audio_description );
246 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, isom_setup_audio_description );
247 #if 0
248 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO, isom_setup_audio_description );
249 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO, isom_setup_audio_description );
250 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO, isom_setup_audio_description );
251 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO, isom_setup_audio_description );
252 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO, isom_setup_audio_description );
253 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO, isom_setup_audio_description );
254 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_RAW_AUDIO, isom_setup_audio_description );
255 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO, isom_setup_audio_description );
256 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO, isom_setup_audio_description );
257 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO, isom_setup_audio_description );
258 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO, isom_setup_audio_description );
259 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO, isom_setup_audio_description );
260 #endif
261 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MP4A_AUDIO, isom_setup_audio_description );
262 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MAC3_AUDIO, isom_setup_audio_description );
263 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MAC6_AUDIO, isom_setup_audio_description );
264 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_AGSM_AUDIO, isom_setup_audio_description );
265 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ALAW_AUDIO, isom_setup_audio_description );
266 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULAW_AUDIO, isom_setup_audio_description );
267 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FULLMP3_AUDIO, isom_setup_audio_description );
268 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM2_AUDIO, isom_setup_audio_description );
269 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM17_AUDIO, isom_setup_audio_description );
270 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_GSM49_AUDIO, isom_setup_audio_description );
271 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, isom_setup_audio_description );
272 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, isom_setup_audio_description );
273 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, isom_setup_audio_description );
274 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, isom_setup_audio_description );
275 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, isom_setup_audio_description );
276 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, isom_setup_audio_description );
277 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, isom_setup_audio_description );
278 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, isom_setup_audio_description );
279 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, isom_setup_audio_description );
280 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, isom_setup_audio_description );
281 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT, isom_add_tx3g_description );
282 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT, isom_add_qt_text_description );
283 #if 0
284 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4S_SYSTEM, isom_add_mp4s_entry );
285 #endif
287 for( int i = 0; description_setup_table[i].func; i++ )
288 if( lsmash_check_codec_type_identical( sample_type, description_setup_table[i].type ) )
290 if( (opaque_func_t)isom_setup_visual_description == description_setup_table[i].func )
291 ret = isom_setup_visual_description( stsd, sample_type, (lsmash_video_summary_t *)summary );
292 else if( (opaque_func_t)isom_setup_audio_description == description_setup_table[i].func )
293 ret = isom_setup_audio_description( stsd, sample_type, (lsmash_audio_summary_t *)summary );
294 else if( (opaque_func_t)isom_add_tx3g_description == description_setup_table[i].func )
295 ret = isom_setup_tx3g_description( stsd );
296 else if( (opaque_func_t)isom_add_qt_text_description == description_setup_table[i].func )
298 isom_qt_text_entry_t *text = isom_add_qt_text_description( stsd );
299 if( text )
301 text->data_reference_index = 1;
302 ret = 0;
305 break;
307 return ret < 0 ? 0 : list->entry_count;
310 static int isom_add_stts_entry( isom_stbl_t *stbl, uint32_t sample_delta )
312 if( !stbl
313 || !stbl->stts
314 || !stbl->stts->list )
315 return -1;
316 isom_stts_entry_t *data = lsmash_malloc( sizeof(isom_stts_entry_t) );
317 if( !data )
318 return -1;
319 data->sample_count = 1;
320 data->sample_delta = sample_delta;
321 if( lsmash_add_entry( stbl->stts->list, data ) )
323 lsmash_free( data );
324 return -1;
326 return 0;
329 static int isom_add_ctts_entry( isom_stbl_t *stbl, uint32_t sample_offset )
331 if( !stbl
332 || !stbl->ctts
333 || !stbl->ctts->list )
334 return -1;
335 isom_ctts_entry_t *data = lsmash_malloc( sizeof(isom_ctts_entry_t) );
336 if( !data )
337 return -1;
338 data->sample_count = 1;
339 data->sample_offset = sample_offset;
340 if( lsmash_add_entry( stbl->ctts->list, data ) )
342 lsmash_free( data );
343 return -1;
345 return 0;
348 static int isom_add_stsc_entry( isom_stbl_t *stbl, uint32_t first_chunk, uint32_t samples_per_chunk, uint32_t sample_description_index )
350 if( !stbl
351 || !stbl->stsc
352 || !stbl->stsc->list )
353 return -1;
354 isom_stsc_entry_t *data = lsmash_malloc( sizeof(isom_stsc_entry_t) );
355 if( !data )
356 return -1;
357 data->first_chunk = first_chunk;
358 data->samples_per_chunk = samples_per_chunk;
359 data->sample_description_index = sample_description_index;
360 if( lsmash_add_entry( stbl->stsc->list, data ) )
362 lsmash_free( data );
363 return -1;
365 return 0;
368 static int isom_add_stsz_entry( isom_stbl_t *stbl, uint32_t entry_size )
370 if( !stbl
371 || !stbl->stsz )
372 return -1;
373 isom_stsz_t *stsz = stbl->stsz;
374 /* retrieve initial sample_size */
375 if( stsz->sample_count == 0 )
376 stsz->sample_size = entry_size;
377 /* if it seems constant access_unit size at present, update sample_count only */
378 if( !stsz->list && stsz->sample_size == entry_size )
380 ++ stsz->sample_count;
381 return 0;
383 /* found sample_size varies, create sample_size list */
384 if( !stsz->list )
386 stsz->list = lsmash_create_entry_list();
387 if( !stsz->list )
388 return -1;
389 for( uint32_t i = 0; i < stsz->sample_count; i++ )
391 isom_stsz_entry_t *data = lsmash_malloc( sizeof(isom_stsz_entry_t) );
392 if( !data )
393 return -1;
394 data->entry_size = stsz->sample_size;
395 if( lsmash_add_entry( stsz->list, data ) )
397 lsmash_free( data );
398 return -1;
401 stsz->sample_size = 0;
403 isom_stsz_entry_t *data = lsmash_malloc( sizeof(isom_stsz_entry_t) );
404 if( !data )
405 return -1;
406 data->entry_size = entry_size;
407 if( lsmash_add_entry( stsz->list, data ) )
409 lsmash_free( data );
410 return -1;
412 ++ stsz->sample_count;
413 return 0;
416 static int isom_add_stss_entry( isom_stbl_t *stbl, uint32_t sample_number )
418 if( !stbl
419 || !stbl->stss
420 || !stbl->stss->list )
421 return -1;
422 isom_stss_entry_t *data = lsmash_malloc( sizeof(isom_stss_entry_t) );
423 if( !data )
424 return -1;
425 data->sample_number = sample_number;
426 if( lsmash_add_entry( stbl->stss->list, data ) )
428 lsmash_free( data );
429 return -1;
431 return 0;
434 static int isom_add_stps_entry( isom_stbl_t *stbl, uint32_t sample_number )
436 if( !stbl
437 || !stbl->stps
438 || !stbl->stps->list )
439 return -1;
440 isom_stps_entry_t *data = lsmash_malloc( sizeof(isom_stps_entry_t) );
441 if( !data )
442 return -1;
443 data->sample_number = sample_number;
444 if( lsmash_add_entry( stbl->stps->list, data ) )
446 lsmash_free( data );
447 return -1;
449 return 0;
452 /* Between ISOBMFF and QTFF, the most significant 2-bit has different meaning.
453 * For the maximum compatibility, we set 0 to the most significant 2-bit when compatible with
454 * both ISOBMFF with AVCFF extensions and QTFF.
455 * compatibility == 0 -> neither AVCFF extensions nor QTFF compatible
456 * compatibility == 1 -> AVCFF extensions compatible
457 * compatibility == 2 -> QTFF compatible
458 * compatibility == 3 -> both AVCFF extensions and QTFF compatible */
459 static int isom_add_sdtp_entry( isom_box_t *parent, lsmash_sample_property_t *prop, int compatibility )
461 if( !prop || !parent )
462 return -1;
463 isom_sdtp_t *sdtp = NULL;
464 if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) )
465 sdtp = ((isom_stbl_t *)parent)->sdtp;
466 else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) )
467 sdtp = ((isom_traf_t *)parent)->sdtp;
468 else
469 assert( 0 );
470 if( !sdtp
471 || !sdtp->list )
472 return -1;
473 isom_sdtp_entry_t *data = lsmash_malloc( sizeof(isom_sdtp_entry_t) );
474 if( !data )
475 return -1;
476 if( compatibility == 1 )
477 data->is_leading = prop->leading & 0x03;
478 else if( compatibility == 2 )
479 data->is_leading = prop->allow_earlier & 0x03;
480 else
482 data->is_leading = 0;
483 assert( compatibility == 3 );
485 data->sample_depends_on = prop->independent & 0x03;
486 data->sample_is_depended_on = prop->disposable & 0x03;
487 data->sample_has_redundancy = prop->redundant & 0x03;
488 if( lsmash_add_entry( sdtp->list, data ) < 0 )
490 lsmash_free( data );
491 return -1;
493 return 0;
496 static int isom_add_co64_entry( isom_stbl_t *stbl, uint64_t chunk_offset )
498 if( !stbl
499 || !stbl->stco
500 || !stbl->stco->list )
501 return -1;
502 isom_co64_entry_t *data = lsmash_malloc( sizeof(isom_co64_entry_t) );
503 if( !data )
504 return -1;
505 data->chunk_offset = chunk_offset;
506 if( lsmash_add_entry( stbl->stco->list, data ) )
508 lsmash_free( data );
509 return -1;
511 return 0;
514 int isom_convert_stco_to_co64( isom_stbl_t *stbl )
516 /* backup stco */
517 int ret = 0;
518 isom_stco_t *stco = stbl->stco;
519 stbl->stco = NULL;
520 if( !isom_add_co64( stbl ) )
522 ret = -1;
523 goto fail;
525 /* move chunk_offset to co64 from stco */
526 for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next )
528 isom_stco_entry_t *data = (isom_stco_entry_t*)entry->data;
529 if( isom_add_co64_entry( stbl, data->chunk_offset ) )
531 ret = -1;
532 goto fail;
535 fail:
536 isom_remove_box_by_itself( stco );
537 return ret;
540 static int isom_add_stco_entry( isom_stbl_t *stbl, uint64_t chunk_offset )
542 if( !stbl
543 || !stbl->stco
544 || !stbl->stco->list )
545 return -1;
546 if( stbl->stco->large_presentation )
547 return isom_add_co64_entry( stbl, chunk_offset );
548 if( chunk_offset > UINT32_MAX )
550 if( isom_convert_stco_to_co64( stbl ) < 0 )
551 return -1;
552 return isom_add_co64_entry( stbl, chunk_offset );
554 isom_stco_entry_t *data = lsmash_malloc( sizeof(isom_stco_entry_t) );
555 if( !data )
556 return -1;
557 data->chunk_offset = (uint32_t)chunk_offset;
558 if( lsmash_add_entry( stbl->stco->list, data ) )
560 lsmash_free( data );
561 return -1;
563 return 0;
566 static isom_sgpd_t *isom_get_sample_group_description_common( lsmash_entry_list_t *list, uint32_t grouping_type )
568 for( lsmash_entry_t *entry = list->head; entry; entry = entry->next )
570 isom_sgpd_t *sgpd = (isom_sgpd_t *)entry->data;
571 if( !sgpd
572 || !sgpd->list )
573 return NULL;
574 if( sgpd->grouping_type == grouping_type )
575 return sgpd;
577 return NULL;
580 static isom_sbgp_t *isom_get_sample_to_group_common( lsmash_entry_list_t *list, uint32_t grouping_type )
582 for( lsmash_entry_t *entry = list->head; entry; entry = entry->next )
584 isom_sbgp_t *sbgp = (isom_sbgp_t *)entry->data;
585 if( !sbgp
586 || !sbgp->list )
587 return NULL;
588 if( sbgp->grouping_type == grouping_type )
589 return sbgp;
591 return NULL;
594 isom_sgpd_t *isom_get_sample_group_description( isom_stbl_t *stbl, uint32_t grouping_type )
596 return isom_get_sample_group_description_common( &stbl->sgpd_list, grouping_type );
599 isom_sbgp_t *isom_get_sample_to_group( isom_stbl_t *stbl, uint32_t grouping_type )
601 return isom_get_sample_to_group_common( &stbl->sbgp_list, grouping_type );
604 isom_sgpd_t *isom_get_roll_recovery_sample_group_description( lsmash_entry_list_t *list )
606 isom_sgpd_t *sgpd;
607 if( (sgpd = isom_get_sample_group_description_common( list, ISOM_GROUP_TYPE_ROLL ))
608 || (sgpd = isom_get_sample_group_description_common( list, ISOM_GROUP_TYPE_PROL )) )
609 return sgpd;
610 return NULL;
613 isom_sbgp_t *isom_get_roll_recovery_sample_to_group( lsmash_entry_list_t *list )
615 isom_sbgp_t *sbgp;
616 if( (sbgp = isom_get_sample_to_group_common( list, ISOM_GROUP_TYPE_ROLL ))
617 || (sbgp = isom_get_sample_to_group_common( list, ISOM_GROUP_TYPE_PROL )) )
618 return sbgp;
619 return NULL;
622 isom_sgpd_t *isom_get_fragment_sample_group_description( isom_traf_t *traf, uint32_t grouping_type )
624 return isom_get_sample_group_description_common( &traf->sgpd_list, grouping_type );
627 isom_sbgp_t *isom_get_fragment_sample_to_group( isom_traf_t *traf, uint32_t grouping_type )
629 return isom_get_sample_to_group_common( &traf->sbgp_list, grouping_type );
632 static isom_rap_entry_t *isom_add_rap_group_entry( isom_sgpd_t *sgpd )
634 if( !sgpd )
635 return NULL;
636 isom_rap_entry_t *data = lsmash_malloc( sizeof(isom_rap_entry_t) );
637 if( !data )
638 return NULL;
639 data->description_length = 0;
640 data->num_leading_samples_known = 0;
641 data->num_leading_samples = 0;
642 if( lsmash_add_entry( sgpd->list, data ) )
644 lsmash_free( data );
645 return NULL;
647 return data;
650 static isom_roll_entry_t *isom_add_roll_group_entry( isom_sgpd_t *sgpd, int16_t roll_distance )
652 if( !sgpd )
653 return NULL;
654 isom_roll_entry_t *data = lsmash_malloc( sizeof(isom_roll_entry_t) );
655 if( !data )
656 return NULL;
657 data->description_length = 0;
658 data->roll_distance = roll_distance;
659 if( lsmash_add_entry( sgpd->list, data ) )
661 lsmash_free( data );
662 return NULL;
664 return data;
667 static isom_group_assignment_entry_t *isom_add_group_assignment_entry( isom_sbgp_t *sbgp, uint32_t sample_count, uint32_t group_description_index )
669 if( !sbgp )
670 return NULL;
671 isom_group_assignment_entry_t *data = lsmash_malloc( sizeof(isom_group_assignment_entry_t) );
672 if( !data )
673 return NULL;
674 data->sample_count = sample_count;
675 data->group_description_index = group_description_index;
676 if( lsmash_add_entry( sbgp->list, data ) )
678 lsmash_free( data );
679 return NULL;
681 return data;
684 int isom_check_compatibility( lsmash_file_t *file )
686 if( !file )
687 return -1;
688 /* Clear flags for compatibility. */
689 ptrdiff_t compat_offset = offsetof( lsmash_file_t, qt_compatible );
690 memset( (int8_t *)file + compat_offset, 0, sizeof(lsmash_file_t) - compat_offset );
691 file->min_isom_version = UINT8_MAX; /* undefined value */
692 /* Get the brand container. */
693 isom_ftyp_t *ftyp = file->ftyp ? file->ftyp : (isom_ftyp_t *)lsmash_get_entry_data( &file->styp_list, 1 );
694 /* Check brand to decide mandatory boxes. */
695 if( !ftyp )
697 /* No brand declaration means this file is a MP4 version 1 or QuickTime file format. */
698 if( file->moov
699 && file->moov->iods )
701 file->mp4_version1 = 1;
702 file->isom_compatible = 1;
704 else
706 file->qt_compatible = 1;
707 file->undefined_64_ver = 1;
709 return 0;
711 for( uint32_t i = 0; i <= ftyp->brand_count; i++ )
713 uint32_t brand = (i == ftyp->brand_count ? ftyp->major_brand : ftyp->compatible_brands[i]);
714 switch( brand )
716 case ISOM_BRAND_TYPE_QT :
717 file->qt_compatible = 1;
718 break;
719 case ISOM_BRAND_TYPE_MP41 :
720 file->mp4_version1 = 1;
721 break;
722 case ISOM_BRAND_TYPE_MP42 :
723 file->mp4_version2 = 1;
724 break;
725 case ISOM_BRAND_TYPE_AVC1 :
726 case ISOM_BRAND_TYPE_ISOM :
727 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 1 );
728 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 1 );
729 break;
730 case ISOM_BRAND_TYPE_ISO2 :
731 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 2 );
732 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 2 );
733 break;
734 case ISOM_BRAND_TYPE_ISO3 :
735 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 3 );
736 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 3 );
737 break;
738 case ISOM_BRAND_TYPE_ISO4 :
739 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 4 );
740 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 4 );
741 break;
742 case ISOM_BRAND_TYPE_ISO5 :
743 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 5 );
744 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 5 );
745 break;
746 case ISOM_BRAND_TYPE_ISO6 :
747 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 6 );
748 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 6 );
749 break;
750 case ISOM_BRAND_TYPE_ISO7 :
751 file->max_isom_version = LSMASH_MAX( file->max_isom_version, 7 );
752 file->min_isom_version = LSMASH_MIN( file->min_isom_version, 7 );
753 break;
754 case ISOM_BRAND_TYPE_M4A :
755 case ISOM_BRAND_TYPE_M4B :
756 case ISOM_BRAND_TYPE_M4P :
757 case ISOM_BRAND_TYPE_M4V :
758 file->itunes_movie = 1;
759 break;
760 case ISOM_BRAND_TYPE_3GP4 :
761 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 4 );
762 break;
763 case ISOM_BRAND_TYPE_3GP5 :
764 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 5 );
765 break;
766 case ISOM_BRAND_TYPE_3GE6 :
767 case ISOM_BRAND_TYPE_3GG6 :
768 case ISOM_BRAND_TYPE_3GP6 :
769 case ISOM_BRAND_TYPE_3GR6 :
770 case ISOM_BRAND_TYPE_3GS6 :
771 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 6 );
772 break;
773 case ISOM_BRAND_TYPE_3GP7 :
774 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 7 );
775 break;
776 case ISOM_BRAND_TYPE_3GP8 :
777 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 8 );
778 break;
779 case ISOM_BRAND_TYPE_3GE9 :
780 case ISOM_BRAND_TYPE_3GF9 :
781 case ISOM_BRAND_TYPE_3GG9 :
782 case ISOM_BRAND_TYPE_3GH9 :
783 case ISOM_BRAND_TYPE_3GM9 :
784 case ISOM_BRAND_TYPE_3GP9 :
785 case ISOM_BRAND_TYPE_3GR9 :
786 case ISOM_BRAND_TYPE_3GS9 :
787 case ISOM_BRAND_TYPE_3GT9 :
788 file->max_3gpp_version = LSMASH_MAX( file->max_3gpp_version, 9 );
789 break;
790 default :
791 break;
793 switch( brand )
795 case ISOM_BRAND_TYPE_AVC1 :
796 case ISOM_BRAND_TYPE_ISO2 :
797 case ISOM_BRAND_TYPE_ISO3 :
798 case ISOM_BRAND_TYPE_ISO4 :
799 case ISOM_BRAND_TYPE_ISO5 :
800 case ISOM_BRAND_TYPE_ISO6 :
801 file->avc_extensions = 1;
802 break;
803 case ISOM_BRAND_TYPE_3GP4 :
804 case ISOM_BRAND_TYPE_3GP5 :
805 case ISOM_BRAND_TYPE_3GP6 :
806 case ISOM_BRAND_TYPE_3GP7 :
807 case ISOM_BRAND_TYPE_3GP8 :
808 case ISOM_BRAND_TYPE_3GP9 :
809 file->forbid_tref = 1;
810 break;
811 case ISOM_BRAND_TYPE_3GH9 :
812 case ISOM_BRAND_TYPE_3GM9 :
813 case ISOM_BRAND_TYPE_DASH :
814 case ISOM_BRAND_TYPE_DSMS :
815 case ISOM_BRAND_TYPE_LMSG :
816 case ISOM_BRAND_TYPE_MSDH :
817 case ISOM_BRAND_TYPE_MSIX :
818 case ISOM_BRAND_TYPE_SIMS :
819 file->media_segment = 1;
820 break;
821 default :
822 break;
825 file->isom_compatible = !file->qt_compatible
826 || file->mp4_version1
827 || file->mp4_version2
828 || file->itunes_movie
829 || file->max_3gpp_version;
830 file->undefined_64_ver = file->qt_compatible || file->itunes_movie;
831 if( file->flags & LSMASH_FILE_MODE_WRITE )
833 /* Media Segment is incompatible with ISO Base Media File Format version 4 or former must be compatible with
834 * version 6 or later since it requires default-base-is-moof and Track Fragment Base Media Decode Time Box. */
835 if( file->media_segment && (file->min_isom_version < 5 || (file->max_isom_version && file->max_isom_version < 6)) )
836 return -1;
837 file->allow_moof_base = (file->max_isom_version >= 5 && file->min_isom_version >= 5)
838 || (file->max_isom_version == 0 && file->min_isom_version == UINT8_MAX && file->media_segment);
840 return 0;
843 uint32_t isom_get_sample_count( isom_trak_t *trak )
845 if( !trak
846 || !trak->mdia
847 || !trak->mdia->minf
848 || !trak->mdia->minf->stbl
849 || !trak->mdia->minf->stbl->stsz )
850 return 0;
851 return trak->mdia->minf->stbl->stsz->sample_count;
854 static uint64_t isom_get_dts( isom_stts_t *stts, uint32_t sample_number )
856 if( !stts
857 || !stts->list )
858 return 0;
859 uint64_t dts = 0;
860 uint32_t i = 1;
861 lsmash_entry_t *entry;
862 isom_stts_entry_t *data;
863 for( entry = stts->list->head; entry; entry = entry->next )
865 data = (isom_stts_entry_t *)entry->data;
866 if( !data )
867 return 0;
868 if( i + data->sample_count > sample_number )
869 break;
870 dts += (uint64_t)data->sample_delta * data->sample_count;
871 i += data->sample_count;
873 if( !entry )
874 return 0;
875 dts += (uint64_t)data->sample_delta * (sample_number - i);
876 return dts;
879 #if 0
880 static uint64_t isom_get_cts( isom_stts_t *stts, isom_ctts_t *ctts, uint32_t sample_number )
882 if( !stts
883 || !stts->list )
884 return 0;
885 if( !ctts )
886 return isom_get_dts( stts, sample_number );
887 uint32_t i = 1; /* This can be 0 (and then condition below shall be changed) but I dare use same algorithm with isom_get_dts. */
888 lsmash_entry_t *entry;
889 isom_ctts_entry_t *data;
890 if( sample_number == 0 )
891 return 0;
892 for( entry = ctts->list->head; entry; entry = entry->next )
894 data = (isom_ctts_entry_t *)entry->data;
895 if( !data )
896 return 0;
897 if( i + data->sample_count > sample_number )
898 break;
899 i += data->sample_count;
901 if( !entry )
902 return 0;
903 return isom_get_dts( stts, sample_number ) + data->sample_offset;
905 #endif
907 static int isom_replace_last_sample_delta( isom_stbl_t *stbl, uint32_t sample_delta )
909 if( !stbl
910 || !stbl->stts
911 || !stbl->stts->list
912 || !stbl->stts->list->tail
913 || !stbl->stts->list->tail->data )
914 return -1;
915 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stbl->stts->list->tail->data;
916 if( sample_delta != last_stts_data->sample_delta )
918 if( last_stts_data->sample_count > 1 )
920 last_stts_data->sample_count -= 1;
921 if( isom_add_stts_entry( stbl, sample_delta ) )
922 return -1;
924 else
925 last_stts_data->sample_delta = sample_delta;
927 return 0;
930 static int isom_update_mdhd_duration( isom_trak_t *trak, uint32_t last_sample_delta )
932 if( !trak
933 || !trak->file
934 || !trak->cache
935 || !trak->mdia
936 || !trak->mdia->mdhd
937 || !trak->mdia->minf
938 || !trak->mdia->minf->stbl
939 || !trak->mdia->minf->stbl->stts
940 || !trak->mdia->minf->stbl->stts->list )
941 return -1;
942 lsmash_file_t *file = trak->file;
943 isom_mdhd_t *mdhd = trak->mdia->mdhd;
944 isom_stbl_t *stbl = trak->mdia->minf->stbl;
945 isom_stts_t *stts = stbl->stts;
946 isom_ctts_t *ctts = stbl->ctts;
947 isom_cslg_t *cslg = stbl->cslg;
948 mdhd->duration = 0;
949 uint32_t sample_count = isom_get_sample_count( trak );
950 if( sample_count == 0 )
952 /* Return error if non-fragmented movie has no samples. */
953 if( !file->fragment && !stts->list->entry_count )
954 return -1;
955 return 0;
957 /* Now we have at least 1 sample, so do stts_entry. */
958 lsmash_entry_t *last_stts = stts->list->tail;
959 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)last_stts->data;
960 if( sample_count == 1 )
961 mdhd->duration = last_stts_data->sample_delta;
962 /* Now we have at least 2 samples,
963 * but dunno whether 1 stts_entry which has 2 samples or 2 stts_entry which has 1 samle each. */
964 else if( !ctts )
966 /* use dts instead of cts */
967 mdhd->duration = isom_get_dts( stts, sample_count );
968 if( last_sample_delta )
970 mdhd->duration += last_sample_delta;
971 if( isom_replace_last_sample_delta( stbl, last_sample_delta ) )
972 return -1;
974 else if( last_stts_data->sample_count > 1 )
975 mdhd->duration += last_stts_data->sample_delta; /* no need to update last_stts_data->sample_delta */
976 else
978 /* Remove the last entry. */
979 if( lsmash_remove_entry_tail( stts->list, NULL ) )
980 return -1;
981 /* copy the previous sample_delta. */
982 ++ ((isom_stts_entry_t *)stts->list->tail->data)->sample_count;
983 mdhd->duration += ((isom_stts_entry_t *)stts->list->tail->data)->sample_delta;
986 else
988 if( !ctts->list
989 || ctts->list->entry_count == 0 )
990 return -1;
991 uint64_t dts = 0;
992 uint64_t max_cts = 0;
993 uint64_t max2_cts = 0;
994 uint64_t min_cts = UINT64_MAX;
995 uint32_t max_offset = 0;
996 uint32_t min_offset = UINT32_MAX;
997 int32_t ctd_shift = trak->cache->timestamp.ctd_shift;
998 uint32_t j = 0;
999 uint32_t k = 0;
1000 lsmash_entry_t *stts_entry = stts->list->head;
1001 lsmash_entry_t *ctts_entry = ctts->list->head;
1002 for( uint32_t i = 0; i < sample_count; i++ )
1004 if( !ctts_entry || !stts_entry )
1005 return -1;
1006 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
1007 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
1008 if( !stts_data || !ctts_data )
1009 return -1;
1010 uint64_t cts;
1011 if( ctd_shift )
1013 /* Anyway, add composition to decode timeline shift for calculating maximum and minimum CTS correctly. */
1014 int32_t sample_offset = (int32_t)ctts_data->sample_offset;
1015 cts = dts + sample_offset + ctd_shift;
1016 max_offset = LSMASH_MAX( (int32_t)max_offset, sample_offset );
1017 min_offset = LSMASH_MIN( (int32_t)min_offset, sample_offset );
1019 else
1021 cts = dts + ctts_data->sample_offset;
1022 max_offset = LSMASH_MAX( max_offset, ctts_data->sample_offset );
1023 min_offset = LSMASH_MIN( min_offset, ctts_data->sample_offset );
1025 min_cts = LSMASH_MIN( min_cts, cts );
1026 if( max_cts < cts )
1028 max2_cts = max_cts;
1029 max_cts = cts;
1031 else if( max2_cts < cts )
1032 max2_cts = cts;
1033 dts += stts_data->sample_delta;
1034 /* If finished sample_count of current entry, move to next. */
1035 if( ++j == ctts_data->sample_count )
1037 ctts_entry = ctts_entry->next;
1038 j = 0;
1040 if( ++k == stts_data->sample_count )
1042 stts_entry = stts_entry->next;
1043 k = 0;
1046 dts -= last_stts_data->sample_delta;
1047 if( file->fragment )
1048 /* Overall presentation is extended exceeding this initial movie.
1049 * So, any players shall display the movie exceeding the durations
1050 * indicated in Movie Header Box, Track Header Boxes and Media Header Boxes.
1051 * Samples up to the duration indicated in Movie Extends Header Box shall be displayed.
1052 * In the absence of Movie Extends Header Box, all samples shall be displayed. */
1053 mdhd->duration += dts + last_sample_delta;
1054 else
1056 if( !last_sample_delta )
1058 /* The spec allows an arbitrary value for the duration of the last sample. So, we pick last-1 sample's. */
1059 last_sample_delta = max_cts - max2_cts;
1061 mdhd->duration = max_cts - min_cts + last_sample_delta;
1062 /* To match dts and media duration, update stts and mdhd relatively. */
1063 if( mdhd->duration > dts )
1064 last_sample_delta = mdhd->duration - dts;
1065 else
1066 mdhd->duration = dts + last_sample_delta; /* media duration must not less than last dts. */
1068 if( isom_replace_last_sample_delta( stbl, last_sample_delta ) )
1069 return -1;
1070 /* Explicit composition information and timeline shifting */
1071 if( cslg || file->qt_compatible || file->max_isom_version >= 4 )
1073 if( ctd_shift )
1075 /* Remove composition to decode timeline shift. */
1076 max_cts -= ctd_shift;
1077 max2_cts -= ctd_shift;
1078 min_cts -= ctd_shift;
1080 int64_t composition_end_time = max_cts + (max_cts - max2_cts);
1081 if( !file->fragment
1082 && ((int32_t)min_offset <= INT32_MAX) && ((int32_t)max_offset <= INT32_MAX)
1083 && ((int64_t)min_cts <= INT32_MAX) && (composition_end_time <= INT32_MAX) )
1085 if( !cslg )
1087 if( !isom_add_cslg( trak->mdia->minf->stbl ) )
1088 return -1;
1089 cslg = stbl->cslg;
1091 cslg->compositionToDTSShift = ctd_shift;
1092 cslg->leastDecodeToDisplayDelta = min_offset;
1093 cslg->greatestDecodeToDisplayDelta = max_offset;
1094 cslg->compositionStartTime = min_cts;
1095 cslg->compositionEndTime = composition_end_time;
1097 else
1099 if( cslg )
1100 lsmash_free( cslg );
1101 stbl->cslg = NULL;
1105 if( mdhd->duration > UINT32_MAX && !file->undefined_64_ver )
1106 mdhd->version = 1;
1107 return 0;
1110 static int isom_update_mvhd_duration( isom_moov_t *moov )
1112 if( !moov
1113 || !moov->mvhd
1114 || !moov->mvhd->file )
1115 return -1;
1116 isom_mvhd_t *mvhd = moov->mvhd;
1117 mvhd->duration = 0;
1118 for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next )
1120 /* We pick maximum track duration as movie duration. */
1121 isom_trak_t *data = (isom_trak_t *)entry->data;
1122 if( !data
1123 || !data->tkhd )
1124 return -1;
1125 mvhd->duration = entry != moov->trak_list.head
1126 ? LSMASH_MAX( mvhd->duration, data->tkhd->duration )
1127 : data->tkhd->duration;
1129 if( mvhd->duration > UINT32_MAX && !mvhd->file->undefined_64_ver )
1130 mvhd->version = 1;
1131 return 0;
1134 int isom_update_tkhd_duration( isom_trak_t *trak )
1136 if( !trak
1137 || !trak->tkhd
1138 || !trak->file
1139 || !trak->file->moov )
1140 return -1;
1141 lsmash_file_t *file = trak->file;
1142 isom_tkhd_t *tkhd = trak->tkhd;
1143 tkhd->duration = 0;
1144 if( file->fragment
1145 || !trak->edts
1146 || !trak->edts->elst )
1148 /* If this presentation might be extended or this track doesn't have edit list, calculate track duration from media duration. */
1149 if( !trak->mdia
1150 || !trak->mdia->mdhd
1151 || !file->moov->mvhd
1152 || trak->mdia->mdhd->timescale == 0 )
1153 return -1;
1154 if( trak->mdia->mdhd->duration == 0 && isom_update_mdhd_duration( trak, 0 ) )
1155 return -1;
1156 tkhd->duration = trak->mdia->mdhd->duration * ((double)file->moov->mvhd->timescale / trak->mdia->mdhd->timescale);
1158 else
1160 /* If the presentation won't be extended and this track has any edit, then track duration is just the sum of the segment_duartions. */
1161 for( lsmash_entry_t *entry = trak->edts->elst->list->head; entry; entry = entry->next )
1163 isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data;
1164 if( !data )
1165 return -1;
1166 tkhd->duration += data->segment_duration;
1169 if( tkhd->duration > UINT32_MAX && !file->undefined_64_ver )
1170 tkhd->version = 1;
1171 if( !file->fragment && tkhd->duration == 0 )
1172 tkhd->duration = tkhd->version == 1 ? 0xffffffffffffffff : 0xffffffff;
1173 return isom_update_mvhd_duration( file->moov );
1176 int lsmash_update_track_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta )
1178 if( !root )
1179 return -1;
1180 lsmash_file_t *file = root->file;
1181 isom_trak_t *trak = isom_get_trak( file, track_ID );
1182 if( !trak )
1183 return -1;
1184 if( isom_update_mdhd_duration( trak, last_sample_delta ) )
1185 return -1;
1186 /* If the presentation won't be extended and this track has any edit, we don't change or update duration in tkhd. */
1187 return (!file->fragment && trak->edts && trak->edts->elst)
1188 ? isom_update_mvhd_duration( file->moov ) /* Only update movie duration. */
1189 : isom_update_tkhd_duration( trak ); /* Also update movie duration internally. */
1192 static inline int isom_increment_sample_number_in_entry( uint32_t *sample_number_in_entry, uint32_t sample_count_in_entry, lsmash_entry_t **entry )
1194 if( *sample_number_in_entry != sample_count_in_entry )
1196 *sample_number_in_entry += 1;
1197 return 0;
1199 /* Precede the next entry. */
1200 *sample_number_in_entry = 1;
1201 if( *entry )
1203 *entry = (*entry)->next;
1204 if( *entry && !(*entry)->data )
1205 return -1;
1207 return 0;
1210 static int isom_calculate_bitrate_description( isom_mdia_t *mdia, uint32_t *bufferSizeDB, uint32_t *maxBitrate, uint32_t *avgBitrate, uint32_t sample_description_index )
1212 isom_stsz_t *stsz = mdia->minf->stbl->stsz;
1213 lsmash_entry_t *stsz_entry = stsz->list ? stsz->list->head : NULL;
1214 lsmash_entry_t *stts_entry = mdia->minf->stbl->stts->list->head;
1215 lsmash_entry_t *stsc_entry = NULL;
1216 lsmash_entry_t *next_stsc_entry = mdia->minf->stbl->stsc->list->head;
1217 isom_stts_entry_t *stts_data = NULL;
1218 isom_stsc_entry_t *stsc_data = NULL;
1219 if( next_stsc_entry && !next_stsc_entry->data )
1220 return -1;
1221 uint32_t rate = 0;
1222 uint64_t dts = 0;
1223 uint32_t time_wnd = 0;
1224 uint32_t timescale = mdia->mdhd->timescale;
1225 uint32_t chunk_number = 0;
1226 uint32_t sample_number_in_stts = 1;
1227 uint32_t sample_number_in_chunk = 1;
1228 *bufferSizeDB = 0;
1229 *maxBitrate = 0;
1230 *avgBitrate = 0;
1231 while( stts_entry )
1233 if( !stsc_data || sample_number_in_chunk == stsc_data->samples_per_chunk )
1235 /* Move the next chunk. */
1236 sample_number_in_chunk = 1;
1237 ++chunk_number;
1238 /* Check if the next entry is broken. */
1239 while( next_stsc_entry && ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk < chunk_number )
1241 /* Just skip broken next entry. */
1242 next_stsc_entry = next_stsc_entry->next;
1243 if( next_stsc_entry && !next_stsc_entry->data )
1244 return -1;
1246 /* Check if the next chunk belongs to the next sequence of chunks. */
1247 if( next_stsc_entry && ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk == chunk_number )
1249 stsc_entry = next_stsc_entry;
1250 next_stsc_entry = next_stsc_entry->next;
1251 if( next_stsc_entry && !next_stsc_entry->data )
1252 return -1;
1253 stsc_data = (isom_stsc_entry_t *)stsc_entry->data;
1254 /* Check if the next contiguous chunks belong to given sample description. */
1255 if( stsc_data->sample_description_index != sample_description_index )
1257 /* Skip chunks which don't belong to given sample description. */
1258 uint32_t number_of_skips = 0;
1259 uint32_t first_chunk = stsc_data->first_chunk;
1260 uint32_t samples_per_chunk = stsc_data->samples_per_chunk;
1261 while( next_stsc_entry )
1263 if( ((isom_stsc_entry_t *)next_stsc_entry->data)->sample_description_index != sample_description_index )
1265 stsc_data = (isom_stsc_entry_t *)next_stsc_entry->data;
1266 number_of_skips += (stsc_data->first_chunk - first_chunk) * samples_per_chunk;
1267 first_chunk = stsc_data->first_chunk;
1268 samples_per_chunk = stsc_data->samples_per_chunk;
1270 else if( ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk <= first_chunk )
1271 ; /* broken entry */
1272 else
1273 break;
1274 /* Just skip the next entry. */
1275 next_stsc_entry = next_stsc_entry->next;
1276 if( next_stsc_entry && !next_stsc_entry->data )
1277 return -1;
1279 if( !next_stsc_entry )
1280 break; /* There is no more chunks which don't belong to given sample description. */
1281 number_of_skips += (((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk - first_chunk) * samples_per_chunk;
1282 for( uint32_t i = 0; i < number_of_skips; i++ )
1284 if( stsz->list )
1286 if( !stsz_entry )
1287 break;
1288 stsz_entry = stsz_entry->next;
1290 if( !stts_entry )
1291 break;
1292 if( isom_increment_sample_number_in_entry( &sample_number_in_stts, ((isom_stts_entry_t *)stts_entry->data)->sample_count, &stts_entry ) )
1293 return -1;
1295 if( (stsz->list && !stsz_entry) || !stts_entry )
1296 break;
1297 chunk_number = stsc_data->first_chunk;
1301 else
1302 ++sample_number_in_chunk;
1303 /* Get current sample's size. */
1304 uint32_t size;
1305 if( stsz->list )
1307 if( !stsz_entry )
1308 break;
1309 isom_stsz_entry_t *stsz_data = (isom_stsz_entry_t *)stsz_entry->data;
1310 if( !stsz_data )
1311 return -1;
1312 size = stsz_data->entry_size;
1313 stsz_entry = stsz_entry->next;
1315 else
1316 size = stsz->sample_size;
1317 /* Get current sample's DTS. */
1318 if( stts_data )
1319 dts += stts_data->sample_delta;
1320 stts_data = (isom_stts_entry_t *)stts_entry->data;
1321 if( !stts_data )
1322 return -1;
1323 isom_increment_sample_number_in_entry( &sample_number_in_stts, stts_data->sample_count, &stts_entry );
1324 /* Calculate bitrate description. */
1325 if( *bufferSizeDB < size )
1326 *bufferSizeDB = size;
1327 *avgBitrate += size;
1328 rate += size;
1329 if( dts > time_wnd + timescale )
1331 if( rate > *maxBitrate )
1332 *maxBitrate = rate;
1333 time_wnd = dts;
1334 rate = 0;
1337 double duration = (double)mdia->mdhd->duration / timescale;
1338 *avgBitrate = (uint32_t)(*avgBitrate / duration);
1339 if( *maxBitrate == 0 )
1340 *maxBitrate = *avgBitrate;
1341 /* Convert to bits per second. */
1342 *maxBitrate *= 8;
1343 *avgBitrate *= 8;
1344 return 0;
1347 int isom_update_bitrate_description( isom_mdia_t *mdia )
1349 if( !mdia
1350 || !mdia->mdhd
1351 || !mdia->minf
1352 || !mdia->minf->stbl )
1353 return -1;
1354 isom_stbl_t *stbl = mdia->minf->stbl;
1355 if( !stbl->stsd
1356 || !stbl->stsz
1357 || !stbl->stsc || !stbl->stsc->list
1358 || !stbl->stts || !stbl->stts->list )
1359 return -1;
1360 uint32_t sample_description_index = 0;
1361 for( lsmash_entry_t *entry = stbl->stsd->list.head; entry; entry = entry->next )
1363 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)entry->data;
1364 if( !sample_entry )
1365 return -1;
1366 ++sample_description_index;
1367 uint32_t bufferSizeDB;
1368 uint32_t maxBitrate;
1369 uint32_t avgBitrate;
1370 /* set bitrate info */
1371 lsmash_codec_type_t sample_type = sample_entry->type;
1372 if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO )
1373 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC2_VIDEO )
1374 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC3_VIDEO )
1375 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC4_VIDEO )
1376 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_HVC1_VIDEO )
1377 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_HEV1_VIDEO ) )
1379 isom_visual_entry_t *stsd_data = (isom_visual_entry_t *)sample_entry;
1380 if( !stsd_data )
1381 return -1;
1382 isom_btrt_t *btrt = (isom_btrt_t *)isom_get_extension_box_format( &stsd_data->extensions, ISOM_BOX_TYPE_BTRT );
1383 if( btrt )
1385 if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) )
1386 return -1;
1387 btrt->bufferSizeDB = bufferSizeDB;
1388 btrt->maxBitrate = maxBitrate;
1389 btrt->avgBitrate = avgBitrate;
1392 else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4V_VIDEO ) )
1394 isom_visual_entry_t *stsd_data = (isom_visual_entry_t *)sample_entry;
1395 if( !stsd_data )
1396 return -1;
1397 isom_esds_t *esds = (isom_esds_t *)isom_get_extension_box_format( &stsd_data->extensions, ISOM_BOX_TYPE_ESDS );
1398 if( !esds || !esds->ES )
1399 return -1;
1400 if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) )
1401 return -1;
1402 /* FIXME: avgBitrate is 0 only if VBR in proper. */
1403 if( mp4sys_update_DecoderConfigDescriptor( esds->ES, bufferSizeDB, maxBitrate, 0 ) )
1404 return -1;
1406 else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) )
1408 isom_audio_entry_t *stsd_data = (isom_audio_entry_t *)sample_entry;
1409 if( !stsd_data )
1410 return -1;
1411 isom_esds_t *esds = NULL;
1412 if( ((isom_audio_entry_t *)sample_entry)->version )
1414 /* MPEG-4 Audio in QTFF */
1415 isom_wave_t *wave = (isom_wave_t *)isom_get_extension_box_format( &stsd_data->extensions, QT_BOX_TYPE_WAVE );
1416 if( !wave )
1417 return -1;
1418 esds = (isom_esds_t *)isom_get_extension_box_format( &wave->extensions, ISOM_BOX_TYPE_ESDS );
1420 else
1421 esds = (isom_esds_t *)isom_get_extension_box_format( &stsd_data->extensions, ISOM_BOX_TYPE_ESDS );
1422 if( !esds || !esds->ES )
1423 return -1;
1424 if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) )
1425 return -1;
1426 /* FIXME: avgBitrate is 0 only if VBR in proper. */
1427 if( mp4sys_update_DecoderConfigDescriptor( esds->ES, bufferSizeDB, maxBitrate, 0 ) )
1428 return -1;
1430 else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_ALAC_AUDIO )
1431 || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ALAC_AUDIO ) )
1433 isom_audio_entry_t *alac = (isom_audio_entry_t *)sample_entry;
1434 if( !alac )
1435 return -1;
1436 uint8_t *exdata = NULL;
1437 uint32_t exdata_size = 0;
1438 isom_box_t *alac_ext = isom_get_extension_box( &alac->extensions, QT_BOX_TYPE_WAVE );
1439 if( alac_ext )
1441 /* Apple Lossless Audio inside QuickTime file format
1442 * Though average bitrate field we found is always set to 0 apparently,
1443 * we set up maxFrameBytes and avgBitRate fields. */
1444 if( alac_ext->manager & LSMASH_BINARY_CODED_BOX )
1445 exdata = isom_get_child_box_position( alac_ext->binary, alac_ext->size, QT_BOX_TYPE_ALAC, &exdata_size );
1446 else
1448 isom_wave_t *wave = (isom_wave_t *)alac_ext;
1449 isom_box_t *wave_ext = isom_get_extension_box( &wave->extensions, QT_BOX_TYPE_ALAC );
1450 if( !wave_ext || !(wave_ext->manager & LSMASH_BINARY_CODED_BOX) )
1451 return -1;
1452 exdata = wave_ext->binary;
1453 exdata_size = wave_ext->size;
1456 else
1458 /* Apple Lossless Audio inside ISO Base Media file format */
1459 isom_box_t *ext = isom_get_extension_box( &alac->extensions, ISOM_BOX_TYPE_ALAC );
1460 if( !ext || !(ext->manager & LSMASH_BINARY_CODED_BOX) )
1461 return -1;
1462 exdata = ext->binary;
1463 exdata_size = ext->size;
1465 if( !exdata || exdata_size < 36 )
1466 return -1;
1467 if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) )
1468 return -1;
1469 exdata += 24;
1470 /* maxFrameBytes */
1471 LSMASH_SET_BE32( &exdata[0], bufferSizeDB );
1472 /* avgBitRate */
1473 LSMASH_SET_BE32( &exdata[4], avgBitrate );
1475 else if( isom_is_waveform_audio( sample_type ) )
1477 isom_box_t *ext = isom_get_extension_box( &sample_entry->extensions, QT_BOX_TYPE_WAVE );
1478 if( !ext )
1479 return -1;
1480 uint8_t *exdata = NULL;
1481 uint32_t exdata_size = 0;
1482 if( ext->manager & LSMASH_BINARY_CODED_BOX )
1483 exdata = isom_get_child_box_position( ext->binary, ext->size, sample_type, &exdata_size );
1484 else
1486 isom_wave_t *wave = (isom_wave_t *)ext;
1487 isom_box_t *wave_ext = isom_get_extension_box( &wave->extensions, sample_type );
1488 if( !wave_ext || !(wave_ext->manager & LSMASH_BINARY_CODED_BOX) )
1489 return -1;
1490 exdata = wave_ext->binary;
1491 exdata_size = wave_ext->size;
1493 /* Check whether exdata is valid or not. */
1494 if( !exdata || exdata_size < ISOM_BASEBOX_COMMON_SIZE + 18 )
1495 return -1;
1496 exdata += ISOM_BASEBOX_COMMON_SIZE;
1497 uint16_t cbSize = LSMASH_GET_LE16( &exdata[16] );
1498 if( exdata_size < ISOM_BASEBOX_COMMON_SIZE + 18 + cbSize )
1499 return -1;
1500 /* WAVEFORMATEX.nAvgBytesPerSec */
1501 if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) < 0 )
1502 return -1;
1503 uint32_t nAvgBytesPerSec = avgBitrate / 8;
1504 LSMASH_SET_LE32( &exdata[8], nAvgBytesPerSec );
1505 if( lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_FULLMP3_AUDIO )
1506 || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_MP3_AUDIO ) )
1508 /* MPEGLAYER3WAVEFORMAT.nBlockSize */
1509 uint32_t nSamplesPerSec = LSMASH_GET_LE32( &exdata[ 4] );
1510 uint16_t nFramesPerBlock = LSMASH_GET_LE16( &exdata[26] );
1511 uint16_t padding = 0; /* FIXME? */
1512 uint16_t nBlockSize = (144 * (avgBitrate / nSamplesPerSec) + padding) * nFramesPerBlock;
1513 LSMASH_SET_LE16( &exdata[24], nBlockSize );
1516 else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSC_AUDIO )
1517 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSE_AUDIO )
1518 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSH_AUDIO )
1519 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSL_AUDIO ) )
1521 isom_audio_entry_t *dts_audio = (isom_audio_entry_t *)sample_entry;
1522 if( !dts_audio )
1523 return -1;
1524 isom_box_t *ext = isom_get_extension_box( &dts_audio->extensions, ISOM_BOX_TYPE_DDTS );
1525 if( !(ext && (ext->manager & LSMASH_BINARY_CODED_BOX) && ext->binary && ext->size >= 28) )
1526 return -1;
1527 if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) )
1528 return -1;
1529 if( !stbl->stsz->list )
1530 maxBitrate = avgBitrate;
1531 uint8_t *exdata = ext->binary + 12;
1532 LSMASH_SET_BE32( &exdata[0], maxBitrate );
1533 LSMASH_SET_BE32( &exdata[4], avgBitrate );
1535 else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_EC_3_AUDIO ) )
1537 isom_audio_entry_t *eac3 = (isom_audio_entry_t *)sample_entry;
1538 if( !eac3 )
1539 return -1;
1540 isom_box_t *ext = isom_get_extension_box( &eac3->extensions, ISOM_BOX_TYPE_DEC3 );
1541 if( !(ext && (ext->manager & LSMASH_BINARY_CODED_BOX) && ext->binary && ext->size >= 10) )
1542 return -1;
1543 uint16_t bitrate;
1544 if( stbl->stsz->list )
1546 if( isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index ) )
1547 return -1;
1548 bitrate = maxBitrate / 1000; /* Use maximum bitrate if VBR. */
1550 else
1551 bitrate = stbl->stsz->sample_size * (eac3->samplerate >> 16) / 192000; /* 192000 == 1536 * 1000 / 8 */
1552 uint8_t *exdata = ext->binary + 8;
1553 exdata[0] = (bitrate >> 5) & 0xff;
1554 exdata[1] = (bitrate & 0x1f) << 3;
1557 return sample_description_index ? 0 : -1;
1560 static int isom_check_mandatory_boxes( lsmash_file_t *file )
1562 if( !file
1563 || !file->moov
1564 || !file->moov->mvhd )
1565 return -1;
1566 /* A movie requires at least one track. */
1567 if( !file->moov->trak_list.head )
1568 return -1;
1569 for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next )
1571 isom_trak_t *trak = (isom_trak_t *)entry->data;
1572 if( !trak
1573 || !trak->tkhd
1574 || !trak->mdia
1575 || !trak->mdia->mdhd
1576 || !trak->mdia->hdlr
1577 || !trak->mdia->minf
1578 || !trak->mdia->minf->dinf
1579 || !trak->mdia->minf->dinf->dref
1580 || !trak->mdia->minf->stbl
1581 || !trak->mdia->minf->stbl->stsd
1582 || !trak->mdia->minf->stbl->stsz
1583 || !trak->mdia->minf->stbl->stts
1584 || !trak->mdia->minf->stbl->stsc
1585 || !trak->mdia->minf->stbl->stco )
1586 return -1;
1587 if( file->qt_compatible && !trak->mdia->minf->hdlr )
1588 return -1;
1589 isom_stbl_t *stbl = trak->mdia->minf->stbl;
1590 if( !stbl->stsd->list.head )
1591 return -1;
1592 if( !file->fragment
1593 && (!stbl->stsd->list.head
1594 || !stbl->stts->list || !stbl->stts->list->head
1595 || !stbl->stsc->list || !stbl->stsc->list->head
1596 || !stbl->stco->list || !stbl->stco->list->head) )
1597 return -1;
1599 if( !file->fragment )
1600 return 0;
1601 if( !file->moov->mvex )
1602 return -1;
1603 for( lsmash_entry_t *entry = file->moov->mvex->trex_list.head; entry; entry = entry->next )
1604 if( !entry->data ) /* trex */
1605 return -1;
1606 return 0;
1609 static inline uint64_t isom_get_current_mp4time( void )
1611 return (uint64_t)time( NULL ) + ISOM_MAC_EPOCH_OFFSET;
1614 static int isom_set_media_creation_time( isom_trak_t *trak, uint64_t current_mp4time )
1616 if( !trak->mdia
1617 || !trak->mdia->mdhd )
1618 return -1;
1619 isom_mdhd_t *mdhd = trak->mdia->mdhd;
1620 if( mdhd->creation_time == 0 )
1621 mdhd->creation_time = mdhd->modification_time = current_mp4time;
1622 return 0;
1625 static int isom_set_track_creation_time( isom_trak_t *trak, uint64_t current_mp4time )
1627 if( !trak
1628 || !trak->tkhd )
1629 return -1;
1630 isom_tkhd_t *tkhd = trak->tkhd;
1631 if( tkhd->creation_time == 0 )
1632 tkhd->creation_time = tkhd->modification_time = current_mp4time;
1633 if( isom_set_media_creation_time( trak, current_mp4time ) )
1634 return -1;
1635 return 0;
1638 static int isom_set_movie_creation_time( lsmash_file_t *file )
1640 if( !file
1641 || !file->moov
1642 || !file->moov->mvhd )
1643 return -1;
1644 uint64_t current_mp4time = isom_get_current_mp4time();
1645 for( uint32_t i = 1; i <= file->moov->trak_list.entry_count; i++ )
1646 if( isom_set_track_creation_time( isom_get_trak( file, i ), current_mp4time ) )
1647 return -1;
1648 isom_mvhd_t *mvhd = file->moov->mvhd;
1649 if( mvhd->creation_time == 0 )
1650 mvhd->creation_time = mvhd->modification_time = current_mp4time;
1651 return 0;
1654 int isom_setup_handler_reference( isom_hdlr_t *hdlr, uint32_t media_type )
1656 isom_box_t *parent = hdlr->parent;
1657 lsmash_file_t *file = hdlr->file;
1658 if( !parent || !file )
1659 return -1;
1660 isom_mdia_t *mdia = lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MDIA ) ? (isom_mdia_t *)parent : NULL;
1661 isom_meta_t *meta = lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) ? (isom_meta_t *)parent
1662 : lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) ? (isom_meta_t *)parent : NULL;
1663 uint32_t type = mdia ? (file->qt_compatible ? QT_HANDLER_TYPE_MEDIA : 0) : (meta ? 0 : QT_HANDLER_TYPE_DATA);
1664 uint32_t subtype = media_type;
1665 hdlr->componentType = type;
1666 hdlr->componentSubtype = subtype;
1667 char *type_name = NULL;
1668 char *subtype_name = NULL;
1669 uint8_t type_name_length = 0;
1670 uint8_t subtype_name_length = 0;
1671 if( mdia )
1672 type_name = "Media ";
1673 else if( meta )
1674 type_name = "Metadata ";
1675 else /* if( minf ) */
1676 type_name = "Data ";
1677 type_name_length = strlen( type_name );
1678 struct
1680 uint32_t subtype;
1681 char *subtype_name;
1682 uint8_t subtype_name_length;
1683 } subtype_table[] =
1685 { ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK, "Sound ", 6 },
1686 { ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK, "Video ", 6 },
1687 { ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK, "Hint ", 5 },
1688 { ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK, "Metadata ", 9 },
1689 { ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK, "Text ", 5 },
1690 { ISOM_META_HANDLER_TYPE_ITUNES_METADATA, "iTunes ", 7 },
1691 { QT_REFERENCE_HANDLER_TYPE_ALIAS, "Alias ", 6 },
1692 { QT_REFERENCE_HANDLER_TYPE_RESOURCE, "Resource ", 9 },
1693 { QT_REFERENCE_HANDLER_TYPE_URL, "URL ", 4 },
1694 { subtype, "Unknown ", 8 }
1696 for( int i = 0; subtype_table[i].subtype; i++ )
1697 if( subtype == subtype_table[i].subtype )
1699 subtype_name = subtype_table[i].subtype_name;
1700 subtype_name_length = subtype_table[i].subtype_name_length;
1701 break;
1703 uint32_t name_length = 15 + subtype_name_length + type_name_length + file->isom_compatible + file->qt_compatible;
1704 uint8_t *name = lsmash_malloc( name_length );
1705 if( !name )
1706 return -1;
1707 if( file->qt_compatible )
1708 name[0] = name_length & 0xff;
1709 memcpy( name + file->qt_compatible, "L-SMASH ", 8 );
1710 memcpy( name + file->qt_compatible + 8, subtype_name, subtype_name_length );
1711 memcpy( name + file->qt_compatible + 8 + subtype_name_length, type_name, type_name_length );
1712 memcpy( name + file->qt_compatible + 8 + subtype_name_length + type_name_length, "Handler", 7 );
1713 if( file->isom_compatible )
1714 name[name_length - 1] = 0;
1715 hdlr->componentName = name;
1716 hdlr->componentName_length = name_length;
1717 return 0;
1720 /*******************************
1721 public interfaces
1722 *******************************/
1724 /*---- track manipulators ----*/
1726 void lsmash_delete_track( lsmash_root_t *root, uint32_t track_ID )
1728 if( !root
1729 || !root->file
1730 || !root->file->moov )
1731 return;
1732 for( lsmash_entry_t *entry = root->file->moov->trak_list.head; entry; entry = entry->next )
1734 isom_trak_t *trak = (isom_trak_t *)entry->data;
1735 if( !trak
1736 || !trak->tkhd )
1737 return;
1738 if( trak->tkhd->track_ID == track_ID )
1740 isom_remove_box_by_itself( trak );
1741 return;
1746 uint32_t lsmash_create_track( lsmash_root_t *root, lsmash_media_type media_type )
1748 if( !root || !root->file )
1749 return 0;
1750 lsmash_file_t *file = root->file;
1751 /* Don't allow to create a new track if the initial movie is already written. */
1752 if( (file->fragment && file->fragment->movie)
1753 || (file->moov && (file->moov->manager & LSMASH_WRITTEN_BOX)) )
1754 return 0;
1755 isom_trak_t *trak = isom_add_trak( file->moov );
1756 if( !trak
1757 || !trak->file
1758 || !trak->file->moov
1759 || !trak->file->moov->mvhd )
1760 goto fail;
1761 if( !isom_add_tkhd( trak )
1762 || !isom_add_mdia( trak )
1763 || !isom_add_mdhd( trak->mdia )
1764 || !isom_add_minf( trak->mdia )
1765 || !isom_add_dinf( trak->mdia->minf )
1766 || !isom_add_dref( trak->mdia->minf->dinf )
1767 || !isom_add_stbl( trak->mdia->minf )
1768 || !isom_add_stsd( trak->mdia->minf->stbl )
1769 || !isom_add_stts( trak->mdia->minf->stbl )
1770 || !isom_add_stsc( trak->mdia->minf->stbl )
1771 || !isom_add_stco( trak->mdia->minf->stbl )
1772 || !isom_add_stsz( trak->mdia->minf->stbl ) )
1773 goto fail;
1774 if( !isom_add_hdlr( trak->mdia )
1775 || isom_setup_handler_reference( trak->mdia->hdlr, media_type ) )
1776 goto fail;
1777 if( file->qt_compatible )
1779 if( !isom_add_hdlr( trak->mdia->minf )
1780 || isom_setup_handler_reference( trak->mdia->minf->hdlr, QT_REFERENCE_HANDLER_TYPE_URL ) )
1781 goto fail;
1783 switch( media_type )
1785 case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK :
1786 if( !isom_add_vmhd( trak->mdia->minf ) )
1787 goto fail;
1788 trak->mdia->minf->vmhd->flags = 0x000001;
1789 break;
1790 case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK :
1791 if( !isom_add_smhd( trak->mdia->minf ) )
1792 goto fail;
1793 trak->cache->is_audio = 1;
1794 break;
1795 case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK :
1796 if( !isom_add_hmhd( trak->mdia->minf ) )
1797 goto fail;
1798 break;
1799 case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK :
1800 if( file->qt_compatible || file->itunes_movie )
1802 if( !isom_add_gmhd( trak->mdia->minf )
1803 || !isom_add_gmin( trak->mdia->minf->gmhd )
1804 || !isom_add_text( trak->mdia->minf->gmhd ) )
1805 return 0;
1806 /* Default Text Media Information Box. */
1808 isom_text_t *text = trak->mdia->minf->gmhd->text;
1809 text->matrix[0] = 0x00010000;
1810 text->matrix[4] = 0x00010000;
1811 text->matrix[8] = 0x40000000;
1814 else
1815 goto fail; /* We support only reference text media track for chapter yet. */
1816 break;
1817 default :
1818 if( !isom_add_nmhd( trak->mdia->minf ) )
1819 goto fail;
1820 break;
1822 /* Default Track Header Box. */
1824 isom_tkhd_t *tkhd = trak->tkhd;
1825 if( media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK )
1826 tkhd->volume = 0x0100;
1827 tkhd->matrix[0] = 0x00010000;
1828 tkhd->matrix[4] = 0x00010000;
1829 tkhd->matrix[8] = 0x40000000;
1830 tkhd->duration = 0xffffffff;
1831 tkhd->track_ID = trak->file->moov->mvhd->next_track_ID ++;
1833 trak->mdia->mdhd->language = file->qt_compatible ? 0 : ISOM_LANGUAGE_CODE_UNDEFINED;
1834 return trak->tkhd->track_ID;
1835 fail:
1836 isom_remove_box_by_itself( trak );
1837 return 0;
1840 uint32_t lsmash_get_track_ID( lsmash_root_t *root, uint32_t track_number )
1842 if( !root
1843 || !root->file
1844 || !root->file->moov )
1845 return 0;
1846 isom_trak_t *trak = (isom_trak_t *)lsmash_get_entry_data( &root->file->moov->trak_list, track_number );
1847 if( !trak
1848 || !trak->tkhd )
1849 return 0;
1850 return trak->tkhd->track_ID;
1853 void lsmash_initialize_track_parameters( lsmash_track_parameters_t *param )
1855 memset( param, 0, sizeof(lsmash_track_parameters_t) );
1856 param->audio_volume = 0x0100;
1857 param->matrix[0] = 0x00010000;
1858 param->matrix[4] = 0x00010000;
1859 param->matrix[8] = 0x40000000;
1862 int lsmash_set_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param )
1864 if( !root )
1865 return -1;
1866 lsmash_file_t *file = root->file;
1867 isom_trak_t *trak = isom_get_trak( file, track_ID );
1868 if( !trak
1869 || !trak->mdia
1870 || !trak->mdia->hdlr
1871 || !file->moov->mvhd )
1872 return -1;
1873 /* Prepare Track Aperture Modes if required. */
1874 if( file->qt_compatible && param->aperture_modes )
1876 if( !trak->tapt && !isom_add_tapt( trak ) )
1877 return -1;
1878 isom_tapt_t *tapt = trak->tapt;
1879 if( (!tapt->clef && !isom_add_clef( tapt ))
1880 || (!tapt->prof && !isom_add_prof( tapt ))
1881 || (!tapt->enof && !isom_add_enof( tapt )) )
1882 return -1;
1884 else
1885 isom_remove_box_by_itself( trak->tapt );
1886 /* Set up Track Header. */
1887 uint32_t media_type = trak->mdia->hdlr->componentSubtype;
1888 isom_tkhd_t *tkhd = trak->tkhd;
1889 tkhd->flags = param->mode;
1890 tkhd->track_ID = param->track_ID ? param->track_ID : tkhd->track_ID;
1891 tkhd->duration = !trak->edts || !trak->edts->elst ? param->duration : tkhd->duration;
1892 /* Template fields
1893 * alternate_group, layer, volume and matrix
1894 * According to 14496-14, these value are all set to defaut values in 14496-12.
1895 * And when a file is read as an MPEG-4 file, these values shall be ignored.
1896 * If a file complies with other specifications, then those fields may have non-default values
1897 * as required by those other specifications. */
1898 if( param->alternate_group )
1900 if( file->qt_compatible || file->itunes_movie || file->max_3gpp_version >= 4 )
1901 tkhd->alternate_group = param->alternate_group;
1902 else
1904 tkhd->alternate_group = 0;
1905 lsmash_log( NULL, LSMASH_LOG_WARNING,
1906 "alternate_group is specified but not compatible with any of the brands. It won't be set.\n" );
1909 else
1910 tkhd->alternate_group = 0;
1911 if( file->qt_compatible || file->itunes_movie )
1913 tkhd->layer = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->video_layer : 0;
1914 tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? param->audio_volume : 0;
1915 if( media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK )
1916 for( int i = 0; i < 9; i++ )
1917 tkhd->matrix[i] = param->matrix[i];
1918 else
1919 for( int i = 0; i < 9; i++ )
1920 tkhd->matrix[i] = 0;
1922 else
1924 tkhd->layer = 0;
1925 tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? 0x0100 : 0;
1926 tkhd->matrix[0] = 0x00010000;
1927 tkhd->matrix[1] = 0;
1928 tkhd->matrix[2] = 0;
1929 tkhd->matrix[3] = 0;
1930 tkhd->matrix[4] = 0x00010000;
1931 tkhd->matrix[5] = 0;
1932 tkhd->matrix[6] = 0;
1933 tkhd->matrix[7] = 0;
1934 tkhd->matrix[8] = 0x40000000;
1936 /* visual presentation size */
1937 tkhd->width = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_width : 0;
1938 tkhd->height = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_height : 0;
1939 /* Update next_track_ID if needed. */
1940 if( file->moov->mvhd->next_track_ID <= tkhd->track_ID )
1941 file->moov->mvhd->next_track_ID = tkhd->track_ID + 1;
1942 return 0;
1945 int lsmash_get_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param )
1947 if( !root )
1948 return -1;
1949 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
1950 if( !trak )
1951 return -1;
1952 isom_tkhd_t *tkhd = trak->tkhd;
1953 param->mode = tkhd->flags;
1954 param->track_ID = tkhd->track_ID;
1955 param->duration = tkhd->duration;
1956 param->video_layer = tkhd->layer;
1957 param->alternate_group = tkhd->alternate_group;
1958 param->audio_volume = tkhd->volume;
1959 for( int i = 0; i < 9; i++ )
1960 param->matrix[i] = tkhd->matrix[i];
1961 param->display_width = tkhd->width;
1962 param->display_height = tkhd->height;
1963 param->aperture_modes = !!trak->tapt;
1964 return 0;
1967 static int isom_set_media_handler_name( lsmash_file_t *file, uint32_t track_ID, char *handler_name )
1969 isom_trak_t *trak = isom_get_trak( file, track_ID );
1970 if( !trak
1971 || !trak->mdia
1972 || !trak->mdia->hdlr )
1973 return -1;
1974 isom_hdlr_t *hdlr = trak->mdia->hdlr;
1975 uint8_t *name = NULL;
1976 uint32_t name_length = strlen( handler_name ) + file->isom_compatible + file->qt_compatible;
1977 if( file->qt_compatible )
1978 name_length = LSMASH_MIN( name_length, 255 );
1979 if( name_length > hdlr->componentName_length && hdlr->componentName )
1980 name = lsmash_realloc( hdlr->componentName, name_length );
1981 else if( !hdlr->componentName )
1982 name = lsmash_malloc( name_length );
1983 else
1984 name = hdlr->componentName;
1985 if( !name )
1986 return -1;
1987 if( file->qt_compatible )
1988 name[0] = name_length & 0xff;
1989 memcpy( name + file->qt_compatible, handler_name, strlen( handler_name ) );
1990 if( file->isom_compatible )
1991 name[name_length - 1] = 0;
1992 hdlr->componentName = name;
1993 hdlr->componentName_length = name_length;
1994 return 0;
1997 static int isom_set_data_handler_name( lsmash_file_t *file, uint32_t track_ID, char *handler_name )
1999 isom_trak_t *trak = isom_get_trak( file, track_ID );
2000 if( !trak
2001 || !trak->mdia
2002 || !trak->mdia->minf
2003 || !trak->mdia->minf->hdlr )
2004 return -1;
2005 isom_hdlr_t *hdlr = trak->mdia->minf->hdlr;
2006 uint8_t *name = NULL;
2007 uint32_t name_length = strlen( handler_name ) + file->isom_compatible + file->qt_compatible;
2008 if( file->qt_compatible )
2009 name_length = LSMASH_MIN( name_length, 255 );
2010 if( name_length > hdlr->componentName_length && hdlr->componentName )
2011 name = lsmash_realloc( hdlr->componentName, name_length );
2012 else if( !hdlr->componentName )
2013 name = lsmash_malloc( name_length );
2014 else
2015 name = hdlr->componentName;
2016 if( !name )
2017 return -1;
2018 if( file->qt_compatible )
2019 name[0] = name_length & 0xff;
2020 memcpy( name + file->qt_compatible, handler_name, strlen( handler_name ) );
2021 if( file->isom_compatible )
2022 name[name_length - 1] = 0;
2023 hdlr->componentName = name;
2024 hdlr->componentName_length = name_length;
2025 return 0;
2028 uint32_t lsmash_get_media_timescale( lsmash_root_t *root, uint32_t track_ID )
2030 if( !root )
2031 return 0;
2032 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
2033 if( !trak
2034 || !trak->mdia
2035 || !trak->mdia->mdhd )
2036 return 0;
2037 return trak->mdia->mdhd->timescale;
2040 uint64_t lsmash_get_media_duration( lsmash_root_t *root, uint32_t track_ID )
2042 if( !root )
2043 return 0;
2044 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
2045 if( !trak
2046 || !trak->mdia
2047 || !trak->mdia->mdhd )
2048 return 0;
2049 return trak->mdia->mdhd->duration;
2052 uint64_t lsmash_get_track_duration( lsmash_root_t *root, uint32_t track_ID )
2054 if( !root )
2055 return 0;
2056 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
2057 if( !trak
2058 || !trak->tkhd )
2059 return 0;
2060 return trak->tkhd->duration;
2063 uint32_t lsmash_get_last_sample_delta( lsmash_root_t *root, uint32_t track_ID )
2065 if( !root )
2066 return 0;
2067 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
2068 if( !trak
2069 || !trak->mdia
2070 || !trak->mdia->minf
2071 || !trak->mdia->minf->stbl
2072 || !trak->mdia->minf->stbl->stts
2073 || !trak->mdia->minf->stbl->stts->list
2074 || !trak->mdia->minf->stbl->stts->list->tail
2075 || !trak->mdia->minf->stbl->stts->list->tail->data )
2076 return 0;
2077 return ((isom_stts_entry_t *)trak->mdia->minf->stbl->stts->list->tail->data)->sample_delta;
2080 uint32_t lsmash_get_start_time_offset( lsmash_root_t *root, uint32_t track_ID )
2082 if( !root )
2083 return 0;
2084 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
2085 if( !trak
2086 || !trak->mdia
2087 || !trak->mdia->minf
2088 || !trak->mdia->minf->stbl
2089 || !trak->mdia->minf->stbl->ctts
2090 || !trak->mdia->minf->stbl->ctts->list
2091 || !trak->mdia->minf->stbl->ctts->list->head
2092 || !trak->mdia->minf->stbl->ctts->list->head->data )
2093 return 0;
2094 return ((isom_ctts_entry_t *)trak->mdia->minf->stbl->ctts->list->head->data)->sample_offset;
2097 uint32_t lsmash_get_composition_to_decode_shift( lsmash_root_t *root, uint32_t track_ID )
2099 if( !root )
2100 return 0;
2101 lsmash_file_t *file = root->file;
2102 isom_trak_t *trak = isom_get_trak( file, track_ID );
2103 if( !trak
2104 || !trak->mdia
2105 || !trak->mdia->minf
2106 || !trak->mdia->minf->stbl )
2107 return 0;
2108 uint32_t sample_count = isom_get_sample_count( trak );
2109 if( sample_count == 0 )
2110 return 0;
2111 isom_stbl_t *stbl = trak->mdia->minf->stbl;
2112 if( !stbl->stts || !stbl->stts->list
2113 || !stbl->ctts || !stbl->ctts->list )
2114 return 0;
2115 if( !(file->max_isom_version >= 4 && stbl->ctts->version == 1) && !file->qt_compatible )
2116 return 0; /* This movie shall not have composition to decode timeline shift. */
2117 lsmash_entry_t *stts_entry = stbl->stts->list->head;
2118 lsmash_entry_t *ctts_entry = stbl->ctts->list->head;
2119 if( !stts_entry || !ctts_entry )
2120 return 0;
2121 uint64_t dts = 0;
2122 uint64_t cts = 0;
2123 uint32_t ctd_shift = 0;
2124 uint32_t i = 0;
2125 uint32_t j = 0;
2126 for( uint32_t k = 0; k < sample_count; k++ )
2128 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
2129 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
2130 if( !stts_data || !ctts_data )
2131 return 0;
2132 cts = dts + (int32_t)ctts_data->sample_offset;
2133 if( dts > cts + ctd_shift )
2134 ctd_shift = dts - cts;
2135 dts += stts_data->sample_delta;
2136 if( ++i == stts_data->sample_count )
2138 stts_entry = stts_entry->next;
2139 if( !stts_entry )
2140 return 0;
2141 i = 0;
2143 if( ++j == ctts_data->sample_count )
2145 ctts_entry = ctts_entry->next;
2146 if( !ctts_entry )
2147 return 0;
2148 j = 0;
2151 return ctd_shift;
2154 uint16_t lsmash_pack_iso_language( char *iso_language )
2156 if( !iso_language || strlen( iso_language ) != 3 )
2157 return 0;
2158 return (uint16_t)LSMASH_PACK_ISO_LANGUAGE( iso_language[0], iso_language[1], iso_language[2] );
2161 static int isom_iso2mac_language( uint16_t ISO_language, uint16_t *MAC_language )
2163 if( !MAC_language )
2164 return -1;
2165 int i = 0;
2166 for( ; isom_languages[i].iso_name; i++ )
2167 if( ISO_language == isom_languages[i].iso_name )
2168 break;
2169 if( !isom_languages[i].iso_name )
2170 return -1;
2171 *MAC_language = isom_languages[i].mac_value;
2172 return 0;
2175 static int isom_mac2iso_language( uint16_t MAC_language, uint16_t *ISO_language )
2177 if( !ISO_language )
2178 return -1;
2179 int i = 0;
2180 for( ; isom_languages[i].iso_name; i++ )
2181 if( MAC_language == isom_languages[i].mac_value )
2182 break;
2183 *ISO_language = isom_languages[i].iso_name ? isom_languages[i].iso_name : ISOM_LANGUAGE_CODE_UNDEFINED;
2184 return 0;
2187 static int isom_set_media_language( lsmash_file_t *file, uint32_t track_ID, uint16_t ISO_language, uint16_t MAC_language )
2189 isom_trak_t *trak = isom_get_trak( file, track_ID );
2190 if( !trak
2191 || !trak->mdia
2192 || !trak->mdia->mdhd )
2193 return -1;
2194 uint16_t language = 0;
2195 if( file->isom_compatible )
2197 if( ISO_language )
2198 language = ISO_language;
2199 else if( MAC_language )
2201 if( isom_mac2iso_language( MAC_language, &language ) )
2202 return -1;
2204 else
2205 language = ISOM_LANGUAGE_CODE_UNDEFINED;
2207 else if( file->qt_compatible )
2209 if( ISO_language )
2211 if( isom_iso2mac_language( ISO_language, &language ) )
2212 return -1;
2214 else
2215 language = MAC_language;
2217 else
2218 return -1;
2219 trak->mdia->mdhd->language = language;
2220 return 0;
2223 int isom_add_sample_grouping( isom_box_t *parent, isom_grouping_type grouping_type )
2225 isom_sgpd_t *sgpd;
2226 isom_sbgp_t *sbgp;
2227 if( NULL == (sgpd = isom_add_sgpd( parent ))
2228 || NULL == (sbgp = isom_add_sbgp( parent )) )
2229 return -1;
2230 sbgp->grouping_type = grouping_type;
2231 sgpd->grouping_type = grouping_type;
2232 sgpd->version = 1; /* We use version 1 for Sample Group Description Box because it is recommended in the spec. */
2233 switch( grouping_type )
2235 case ISOM_GROUP_TYPE_RAP :
2236 sgpd->default_length = 1;
2237 break;
2238 case ISOM_GROUP_TYPE_ROLL :
2239 case ISOM_GROUP_TYPE_PROL :
2240 sgpd->default_length = 2;
2241 break;
2242 default :
2243 /* We don't consider other grouping types currently. */
2244 break;
2246 return 0;
2249 static int isom_create_sample_grouping( isom_trak_t *trak, isom_grouping_type grouping_type )
2251 lsmash_file_t *file = trak->file;
2252 switch( grouping_type )
2254 case ISOM_GROUP_TYPE_RAP :
2255 assert( file->max_isom_version >= 6 );
2256 break;
2257 case ISOM_GROUP_TYPE_ROLL :
2258 case ISOM_GROUP_TYPE_PROL :
2259 assert( file->avc_extensions || file->qt_compatible );
2260 break;
2261 default :
2262 assert( 0 );
2263 break;
2265 if( isom_add_sample_grouping( (isom_box_t *)trak->mdia->minf->stbl, grouping_type ) < 0 )
2266 return -1;
2267 if( trak->cache->fragment && file->max_isom_version >= 6 )
2268 switch( grouping_type )
2270 case ISOM_GROUP_TYPE_RAP :
2271 trak->cache->fragment->rap_grouping = 1;
2272 break;
2273 case ISOM_GROUP_TYPE_ROLL :
2274 case ISOM_GROUP_TYPE_PROL :
2275 trak->cache->fragment->roll_grouping = 1;
2276 break;
2277 default :
2278 /* We don't consider other grouping types currently. */
2279 break;
2281 return 0;
2284 void lsmash_initialize_media_parameters( lsmash_media_parameters_t *param )
2286 memset( param, 0, sizeof(lsmash_media_parameters_t) );
2287 param->timescale = 1;
2290 int lsmash_set_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param )
2292 if( !root )
2293 return -1;
2294 lsmash_file_t *file = root->file;
2295 isom_trak_t *trak = isom_get_trak( file, track_ID );
2296 if( !trak
2297 || !trak->mdia
2298 || !trak->mdia->mdhd
2299 || !trak->mdia->minf
2300 || !trak->mdia->minf->stbl )
2301 return -1;
2302 trak->mdia->mdhd->timescale = param->timescale;
2303 if( isom_set_media_language( file, track_ID, param->ISO_language, param->MAC_language ) )
2304 return -1;
2305 if( param->media_handler_name
2306 && isom_set_media_handler_name( file, track_ID, param->media_handler_name ) )
2307 return -1;
2308 if( file->qt_compatible && param->data_handler_name
2309 && isom_set_data_handler_name( file, track_ID, param->data_handler_name ) )
2310 return -1;
2311 if( (file->avc_extensions || file->qt_compatible) && param->roll_grouping
2312 && isom_create_sample_grouping( trak, ISOM_GROUP_TYPE_ROLL ) )
2313 return -1;
2314 if( (file->max_isom_version >= 6) && param->rap_grouping
2315 && isom_create_sample_grouping( trak, ISOM_GROUP_TYPE_RAP ) )
2316 return -1;
2317 return 0;
2320 int lsmash_get_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param )
2322 if( !root )
2323 return -1;
2324 lsmash_file_t *file = root->file;
2325 isom_trak_t *trak = isom_get_trak( file, track_ID );
2326 if( !trak
2327 || !trak->mdia
2328 || !trak->mdia->mdhd
2329 || !trak->mdia->hdlr
2330 || !trak->mdia->minf
2331 || !trak->mdia->minf->stbl )
2332 return -1;
2333 isom_mdhd_t *mdhd = trak->mdia->mdhd;
2334 isom_stbl_t *stbl = trak->mdia->minf->stbl;
2335 param->timescale = mdhd->timescale;
2336 param->handler_type = trak->mdia->hdlr->componentSubtype;
2337 param->duration = mdhd->duration;
2338 /* Whether sample grouping present. */
2340 isom_sbgp_t *sbgp;
2341 isom_sgpd_t *sgpd;
2342 sbgp = isom_get_sample_to_group ( stbl, ISOM_GROUP_TYPE_RAP );
2343 sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
2344 param->rap_grouping = sbgp && sgpd;
2345 sbgp = isom_get_roll_recovery_sample_to_group ( &stbl->sbgp_list );
2346 sgpd = isom_get_roll_recovery_sample_group_description( &stbl->sgpd_list );
2347 param->roll_grouping = sbgp && sgpd;
2349 /* Get media language. */
2350 if( mdhd->language >= 0x800 )
2352 param->MAC_language = 0;
2353 param->ISO_language = mdhd->language;
2355 else
2357 param->MAC_language = mdhd->language;
2358 param->ISO_language = 0;
2360 /* Get handler name(s). */
2361 isom_hdlr_t *hdlr = trak->mdia->hdlr;
2362 int length = LSMASH_MIN( 255, hdlr->componentName_length );
2363 if( length )
2365 memcpy( param->media_handler_name_shadow, hdlr->componentName + file->qt_compatible, length );
2366 param->media_handler_name_shadow[length - 2 + file->isom_compatible + file->qt_compatible] = '\0';
2367 param->media_handler_name = param->media_handler_name_shadow;
2369 else
2371 param->media_handler_name = NULL;
2372 memset( param->media_handler_name_shadow, 0, sizeof(param->media_handler_name_shadow) );
2374 if( trak->mdia->minf->hdlr )
2376 hdlr = trak->mdia->minf->hdlr;
2377 length = LSMASH_MIN( 255, hdlr->componentName_length );
2378 if( length )
2380 memcpy( param->data_handler_name_shadow, hdlr->componentName + file->qt_compatible, length );
2381 param->data_handler_name_shadow[length - 2 + file->isom_compatible + file->qt_compatible] = '\0';
2382 param->data_handler_name = param->data_handler_name_shadow;
2384 else
2386 param->data_handler_name = NULL;
2387 memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) );
2390 else
2392 param->data_handler_name = NULL;
2393 memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) );
2395 return 0;
2398 /*---- movie manipulators ----*/
2400 void lsmash_discard_boxes( lsmash_root_t *root )
2402 if( !root || !root->file )
2403 return;
2404 isom_remove_all_extension_boxes( &root->file->extensions );
2407 int lsmash_open_file
2409 const char *filename,
2410 int open_mode,
2411 lsmash_file_parameters_t *param
2414 if( !filename || !param )
2415 return -1;
2416 char mode[4] = { 0 };
2417 lsmash_file_mode file_mode = 0;
2418 if( open_mode == 0 )
2420 memcpy( mode, "w+b", 4 );
2421 file_mode = LSMASH_FILE_MODE_WRITE
2422 | LSMASH_FILE_MODE_BOX
2423 | LSMASH_FILE_MODE_INITIALIZATION
2424 | LSMASH_FILE_MODE_MEDIA;
2426 #ifdef LSMASH_DEMUXER_ENABLED
2427 else if( open_mode == 1 )
2429 memcpy( mode, "rb", 3 );
2430 file_mode = LSMASH_FILE_MODE_READ;
2432 #endif
2433 if( file_mode == 0 )
2434 return -1;
2435 FILE *stream = NULL;
2436 int seekable = 1;
2437 if( !strcmp( filename, "-" ) )
2439 if( file_mode & LSMASH_FILE_MODE_READ )
2441 stream = stdin;
2442 seekable = 0;
2444 else if( file_mode & LSMASH_FILE_MODE_WRITE )
2446 stream = stdout;
2447 seekable = 0;
2448 file_mode |= LSMASH_FILE_MODE_FRAGMENTED;
2451 else
2452 stream = lsmash_fopen( filename, mode );
2453 if( !stream )
2454 return -1;
2455 memset( param, 0, sizeof(lsmash_file_parameters_t) );
2456 param->mode = file_mode;
2457 param->opaque = (void *)stream;
2458 param->read = lsmash_fread_wrapper;
2459 param->write = lsmash_fwrite_wrapper;
2460 param->seek = seekable ? lsmash_fseek_wrapper : NULL;
2461 param->major_brand = 0;
2462 param->brands = NULL;
2463 param->brand_count = 0;
2464 param->minor_version = 0;
2465 param->max_chunk_duration = 0.5;
2466 param->max_async_tolerance = 2.0;
2467 param->max_chunk_size = 4 * 1024 * 1024;
2468 param->max_read_size = 4 * 1024 * 1024;
2469 return 0;
2472 int lsmash_close_file
2474 lsmash_file_parameters_t *param
2477 if( !param )
2478 return -1;
2479 if( !param->opaque )
2480 return 0;
2481 int ret = fclose( (FILE *)param->opaque );
2482 param->opaque = NULL;
2483 return ret;
2486 static int isom_set_brands
2488 lsmash_file_t *file,
2489 lsmash_brand_type major_brand,
2490 uint32_t minor_version,
2491 lsmash_brand_type *brands,
2492 uint32_t brand_count
2495 if( brand_count > 50 )
2496 return -1; /* We support setting brands up to 50. */
2497 if( major_brand == 0 || brand_count == 0 )
2499 /* Absence of File Type Box means this file is a QuickTime or MP4 version 1 format file. */
2500 isom_remove_box_by_itself( file->ftyp );
2501 /* Anyway we use QTFF as a default file format. */
2502 file->qt_compatible = 1;
2503 return 0;
2505 isom_ftyp_t *ftyp;
2506 if( file->flags & LSMASH_FILE_MODE_INITIALIZATION )
2508 /* Add File Type Box if absent yet. */
2509 if( !file->ftyp && !isom_add_ftyp( file ) )
2510 return -1;
2511 ftyp = file->ftyp;
2513 else
2515 /* Add Segment Type Box if absent yet. */
2516 ftyp = file->styp_list.head && file->styp_list.head->data
2517 ? (isom_styp_t *)file->styp_list.head->data
2518 : isom_add_styp( file );
2519 if( !ftyp )
2520 return -1;
2522 /* Allocate an array of compatible brands.
2523 * ISO/IEC 14496-12 doesn't forbid the absence of brands in the compatible brand list.
2524 * For a reason of safety, however, we set at least one brand in the list. */
2525 size_t alloc_size = (brand_count ? brand_count : 1) * sizeof(uint32_t);
2526 lsmash_brand_type *compatible_brands;
2527 if( !file->compatible_brands )
2528 compatible_brands = lsmash_malloc( alloc_size );
2529 else
2530 compatible_brands = lsmash_realloc( file->compatible_brands, alloc_size );
2531 if( !compatible_brands )
2532 return -1;
2533 /* Set compatible brands. */
2534 if( brand_count )
2535 for( uint32_t i = 0; i < brand_count; i++ )
2536 compatible_brands[i] = brands[i];
2537 else
2539 /* At least one compatible brand. */
2540 compatible_brands[0] = major_brand;
2541 brand_count = 1;
2543 file->compatible_brands = compatible_brands;
2544 /* Duplicate an array of compatible brands. */
2545 lsmash_free( ftyp->compatible_brands );
2546 ftyp->compatible_brands = lsmash_memdup( compatible_brands, alloc_size );
2547 if( !ftyp->compatible_brands )
2549 lsmash_freep( &file->compatible_brands );
2550 return -1;
2552 ftyp->size = ISOM_BASEBOX_COMMON_SIZE + 8 + brand_count * 4;
2553 ftyp->major_brand = major_brand;
2554 ftyp->minor_version = minor_version;
2555 ftyp->brand_count = brand_count;
2556 file->brand_count = brand_count;
2557 return isom_check_compatibility( file );
2560 lsmash_file_t *lsmash_set_file
2562 lsmash_root_t *root,
2563 lsmash_file_parameters_t *param
2566 if( !root || !param )
2567 return NULL;
2568 if( root->file )
2569 /* Handling of multiple files is not supported yet. */
2570 return NULL;
2571 lsmash_file_t *file = isom_add_file( root );
2572 if( !file )
2573 return NULL;
2574 lsmash_bs_t *bs = lsmash_bs_create();
2575 if( !bs )
2576 goto fail;
2577 file->bs = bs;
2578 file->flags = param->mode;
2579 file->bs->stream = param->opaque;
2580 file->bs->read = param->read;
2581 file->bs->write = param->write;
2582 file->bs->seek = param->seek;
2583 file->bs->unseekable = (param->seek == NULL);
2584 file->bs->buffer.max_size = param->max_read_size;
2585 file->max_chunk_duration = param->max_chunk_duration;
2586 file->max_async_tolerance = LSMASH_MAX( param->max_async_tolerance, 2 * param->max_chunk_duration );
2587 file->max_chunk_size = param->max_chunk_size;
2588 if( (file->flags & LSMASH_FILE_MODE_WRITE)
2589 && (file->flags & LSMASH_FILE_MODE_BOX) )
2591 /* Establish the fragment handler if required. */
2592 if( file->flags & LSMASH_FILE_MODE_FRAGMENTED )
2594 file->fragment = lsmash_malloc_zero( sizeof(isom_fragment_manager_t) );
2595 if( !file->fragment )
2596 goto fail;
2597 file->fragment->pool = lsmash_create_entry_list();
2598 if( !file->fragment->pool )
2599 goto fail;
2601 else if( file->bs->unseekable )
2602 /* For unseekable output operations, LSMASH_FILE_MODE_FRAGMENTED shall be set. */
2603 goto fail;
2604 /* Establish file types. */
2605 if( isom_set_brands( file, param->major_brand,
2606 param->minor_version,
2607 param->brands, param->brand_count ) < 0 )
2608 goto fail;
2609 /* Create the movie header if the initialization of the streams is required. */
2610 if( file->flags & LSMASH_FILE_MODE_INITIALIZATION )
2612 if( !isom_add_moov( file )
2613 || !isom_add_mvhd( file->moov ) )
2614 goto fail;
2615 /* Default Movie Header Box. */
2616 isom_mvhd_t *mvhd = file->moov->mvhd;
2617 mvhd->rate = 0x00010000;
2618 mvhd->volume = 0x0100;
2619 mvhd->matrix[0] = 0x00010000;
2620 mvhd->matrix[4] = 0x00010000;
2621 mvhd->matrix[8] = 0x40000000;
2622 mvhd->next_track_ID = 1;
2625 root->file = file;
2626 return file;
2627 fail:
2628 isom_remove_box_by_itself( file );
2629 return NULL;
2632 int64_t lsmash_read_file
2634 lsmash_file_t *file,
2635 lsmash_file_parameters_t *param
2638 #ifdef LSMASH_DEMUXER_ENABLED
2639 if( !file || !file->bs )
2640 return -1LL;
2641 int64_t ret = -1;
2642 if( file->flags & (LSMASH_FILE_MODE_READ | LSMASH_FILE_MODE_DUMP) )
2644 /* Get the file size if seekable when reading. */
2645 if( !file->bs->unseekable )
2647 ret = lsmash_bs_read_seek( file->bs, 0, SEEK_END );
2648 if( ret < 0 )
2649 return ret;
2650 file->bs->written = ret;
2651 lsmash_bs_read_seek( file->bs, 0, SEEK_SET );
2653 else
2654 ret = 0;
2655 /* Read whole boxes. */
2656 if( isom_read_file( file ) < 0 )
2657 return -1;
2658 if( param )
2660 if( file->ftyp )
2662 /* file types */
2663 isom_ftyp_t *ftyp = file->ftyp;
2664 param->major_brand = ftyp->major_brand ? ftyp->major_brand : ISOM_BRAND_TYPE_QT;
2665 param->minor_version = ftyp->minor_version;
2666 param->brands = file->compatible_brands;
2667 param->brand_count = file->brand_count;
2669 else if( file->styp_list.head && file->styp_list.head->data )
2671 /* segment types */
2672 isom_styp_t *styp = (isom_styp_t *)file->styp_list.head->data;
2673 param->major_brand = styp->major_brand ? styp->major_brand : ISOM_BRAND_TYPE_QT;
2674 param->minor_version = styp->minor_version;
2675 param->brands = file->compatible_brands;
2676 param->brand_count = file->brand_count;
2678 else
2680 param->major_brand = file->mp4_version1 ? ISOM_BRAND_TYPE_MP41 : ISOM_BRAND_TYPE_QT;
2681 param->minor_version = 0;
2682 param->brands = NULL;
2683 param->brand_count = 0;
2687 return ret;
2688 #else
2689 return -1LL;
2690 #endif
2693 lsmash_root_t *lsmash_open_movie( const char *filename, lsmash_file_mode mode )
2695 if( !filename || ((mode & LSMASH_FILE_MODE_WRITE) && (mode & LSMASH_FILE_MODE_READ)) )
2696 return NULL;
2697 int open_mode = -1;
2698 if( mode & LSMASH_FILE_MODE_WRITE )
2699 open_mode = 0;
2700 #ifdef LSMASH_DEMUXER_ENABLED
2701 else if( mode & LSMASH_FILE_MODE_READ )
2702 open_mode = 1;
2703 #endif
2704 if( open_mode < 0 )
2705 return NULL;
2706 lsmash_root_t *root = lsmash_create_root();
2707 if( !root )
2708 return NULL;
2709 lsmash_file_parameters_t param;
2710 if( lsmash_open_file( filename, open_mode, &param ) < 0 )
2712 lsmash_destroy_root( root );
2713 return NULL;
2715 param.mode |= mode;
2716 lsmash_file_t *file = lsmash_set_file( root, &param );
2717 if( !file || (open_mode == 1 && lsmash_read_file( file, &param ) < 0) )
2719 lsmash_close_file( &param );
2720 lsmash_destroy_root( root );
2721 return NULL;
2723 /* Don't fclose() here. */
2724 param.opaque = NULL;
2725 lsmash_close_file( &param );
2726 /* Call fclose() when calling lsmash_destroy_root(). */
2727 file->bc_fclose = 1;
2728 return root;
2731 void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t *param )
2733 memset( param, 0, sizeof(lsmash_movie_parameters_t) );
2734 param->timescale = 600;
2735 param->playback_rate = 0x00010000;
2736 param->playback_volume = 0x0100;
2739 int lsmash_set_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param )
2741 if( !root )
2742 return -1;
2743 lsmash_file_t *file = root->file;
2744 if( !file
2745 || !file->moov
2746 || !file->moov->mvhd
2747 || ((param->major_brand || (param->brands && param->number_of_brands))
2748 && isom_set_brands( file, param->major_brand, param->minor_version, param->brands, param->number_of_brands ) < 0) )
2749 return -1;
2750 isom_mvhd_t *mvhd = file->moov->mvhd;
2751 if( param->max_chunk_duration > 0 )
2752 file->max_chunk_duration = param->max_chunk_duration;
2753 if( param->max_async_tolerance > 0 )
2754 file->max_async_tolerance = LSMASH_MAX( param->max_async_tolerance, 2 * param->max_chunk_duration );
2755 if( param->max_chunk_size )
2756 file->max_chunk_size = param->max_chunk_size;
2757 if( file->bs && param->max_read_size )
2758 file->bs->buffer.max_size = param->max_read_size;
2759 mvhd->timescale = param->timescale;
2760 if( file->qt_compatible || file->itunes_movie )
2762 mvhd->rate = param->playback_rate;
2763 mvhd->volume = param->playback_volume;
2764 mvhd->previewTime = param->preview_time;
2765 mvhd->previewDuration = param->preview_duration;
2766 mvhd->posterTime = param->poster_time;
2768 else
2770 mvhd->rate = 0x00010000;
2771 mvhd->volume = 0x0100;
2772 mvhd->previewTime = 0;
2773 mvhd->previewDuration = 0;
2774 mvhd->posterTime = 0;
2776 return 0;
2779 int lsmash_get_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param )
2781 if( !root )
2782 return -1;
2783 lsmash_file_t *file = root->file;
2784 if( !file
2785 || !file->moov
2786 || !file->moov->mvhd )
2787 return -1;
2788 isom_mvhd_t *mvhd = file->moov->mvhd;
2789 if( file->ftyp )
2791 isom_ftyp_t *ftyp = file->ftyp;
2792 uint32_t brand_count = LSMASH_MIN( ftyp->brand_count, 50 ); /* brands up to 50 */
2793 for( uint32_t i = 0; i < brand_count; i++ )
2794 param->brands_shadow[i] = ftyp->compatible_brands[i];
2795 param->major_brand = ftyp->major_brand;
2796 param->brands = param->brands_shadow;
2797 param->number_of_brands = brand_count;
2798 param->minor_version = ftyp->minor_version;
2800 param->max_chunk_duration = file->max_chunk_duration;
2801 param->max_async_tolerance = file->max_async_tolerance;
2802 param->max_chunk_size = file->max_chunk_size;
2803 param->max_read_size = file->bs ? file->bs->buffer.max_size : 0;
2804 param->timescale = mvhd->timescale;
2805 param->duration = mvhd->duration;
2806 param->playback_rate = mvhd->rate;
2807 param->playback_volume = mvhd->volume;
2808 param->preview_time = mvhd->previewTime;
2809 param->preview_duration = mvhd->previewDuration;
2810 param->poster_time = mvhd->posterTime;
2811 param->number_of_tracks = file->moov->trak_list.entry_count;
2812 return 0;
2815 uint32_t lsmash_get_movie_timescale( lsmash_root_t *root )
2817 if( !root
2818 || !root->file
2819 || !root->file->moov
2820 || !root->file->moov->mvhd )
2821 return 0;
2822 return root->file->moov->mvhd->timescale;
2825 static int isom_scan_trak_profileLevelIndication
2827 isom_trak_t *trak,
2828 mp4a_audioProfileLevelIndication *audio_pli,
2829 mp4sys_visualProfileLevelIndication *visual_pli
2832 if( !trak
2833 || !trak->mdia
2834 || !trak->mdia->minf
2835 || !trak->mdia->minf->stbl )
2836 return -1;
2837 isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd;
2838 if( !stsd
2839 || !stsd->list.head )
2840 return -1;
2841 for( lsmash_entry_t *entry = stsd->list.head; entry; entry = entry->next )
2843 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)entry->data;
2844 if( !sample_entry )
2845 return -1;
2846 lsmash_codec_type_t sample_type = sample_entry->type;
2847 if( trak->mdia->minf->vmhd )
2849 if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO )
2850 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC2_VIDEO )
2851 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC3_VIDEO )
2852 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC4_VIDEO )
2853 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVCP_VIDEO )
2854 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_SVC1_VIDEO )
2855 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MVC1_VIDEO )
2856 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MVC2_VIDEO ) )
2858 /* FIXME: Do we have to arbitrate like audio? */
2859 if( *visual_pli == MP4SYS_VISUAL_PLI_NONE_REQUIRED )
2860 *visual_pli = MP4SYS_VISUAL_PLI_H264_AVC;
2862 else
2863 *visual_pli = MP4SYS_VISUAL_PLI_NOT_SPECIFIED;
2865 else if( trak->mdia->minf->smhd )
2867 if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) )
2869 isom_audio_entry_t *audio = (isom_audio_entry_t *)sample_entry;
2870 isom_esds_t *esds = (isom_esds_t *)isom_get_extension_box_format( &audio->extensions, ISOM_BOX_TYPE_ESDS );
2871 if( !esds || !esds->ES )
2872 return -1;
2873 lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO );
2874 if( !summary )
2875 continue;
2876 if( mp4sys_setup_summary_from_DecoderSpecificInfo( summary, esds->ES ) < 0 )
2877 *audio_pli = MP4A_AUDIO_PLI_NOT_SPECIFIED;
2878 else
2879 *audio_pli = mp4a_max_audioProfileLevelIndication( *audio_pli, mp4a_get_audioProfileLevelIndication( summary ) );
2880 lsmash_cleanup_summary( (lsmash_summary_t *)summary );
2882 else
2883 /* NOTE: Audio CODECs other than 'mp4a' does not have appropriate pli. */
2884 *audio_pli = MP4A_AUDIO_PLI_NOT_SPECIFIED;
2886 else
2887 ; /* FIXME: Do we have to set OD_profileLevelIndication? */
2889 return 0;
2892 int isom_setup_iods( isom_moov_t *moov )
2894 if( !moov->iods && !isom_add_iods( moov ) )
2895 return -1;
2896 isom_iods_t *iods = moov->iods;
2897 iods->OD = mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */
2898 if( !iods->OD )
2899 goto fail;
2900 mp4a_audioProfileLevelIndication audio_pli = MP4A_AUDIO_PLI_NONE_REQUIRED;
2901 mp4sys_visualProfileLevelIndication visual_pli = MP4SYS_VISUAL_PLI_NONE_REQUIRED;
2902 for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next )
2904 isom_trak_t *trak = (isom_trak_t *)entry->data;
2905 if( !trak
2906 || !trak->tkhd )
2907 goto fail;
2908 if( isom_scan_trak_profileLevelIndication( trak, &audio_pli, &visual_pli ) )
2909 goto fail;
2910 if( mp4sys_create_ES_ID_Inc( iods->OD, trak->tkhd->track_ID ) )
2911 goto fail;
2913 if( mp4sys_to_InitialObjectDescriptor( iods->OD,
2914 0, /* FIXME: I'm not quite sure what the spec says. */
2915 MP4SYS_OD_PLI_NONE_REQUIRED, MP4SYS_SCENE_PLI_NONE_REQUIRED,
2916 audio_pli, visual_pli,
2917 MP4SYS_GRAPHICS_PLI_NONE_REQUIRED ) )
2918 goto fail;
2919 return 0;
2920 fail:
2921 isom_remove_box_by_itself( iods );
2922 return -1;
2925 int lsmash_create_object_descriptor( lsmash_root_t *root )
2927 if( !root || !root->file )
2928 return -1;
2929 lsmash_file_t *file = root->file;
2930 /* Return error if this file is not compatible with MP4 file format. */
2931 if( !file->mp4_version1 && !file->mp4_version2 )
2932 return -1;
2933 return isom_setup_iods( file->moov );
2936 /*---- finishing functions ----*/
2938 int isom_complement_data_reference( isom_minf_t *minf )
2940 if( !minf->dinf
2941 || !minf->dinf->dref )
2942 return -1;
2943 /* Complement data referece if absent. */
2944 if( !minf->dinf->dref->list.head )
2946 isom_dref_entry_t *url = isom_add_dref_entry( minf->dinf->dref );
2947 if( !url )
2948 return -1;
2949 url->flags = 0x000001; /* Media data is in the same file. */
2951 return 0;
2954 int isom_rearrange_boxes
2956 lsmash_file_t *file,
2957 lsmash_adhoc_remux_t *remux,
2958 uint8_t *buf[2],
2959 size_t read_num,
2960 size_t size,
2961 uint64_t read_pos,
2962 uint64_t write_pos,
2963 uint64_t file_size
2966 /* Copy-pastan */
2967 int buf_switch = 1;
2968 lsmash_bs_t *bs = file->bs;
2969 while( read_num == size )
2971 if( lsmash_bs_write_seek( bs, read_pos, SEEK_SET ) < 0 )
2972 return -1;
2973 lsmash_bs_read_data( bs, buf[buf_switch], &read_num );
2974 read_pos = bs->offset;
2975 buf_switch ^= 0x1;
2976 if( lsmash_bs_write_seek( bs, write_pos, SEEK_SET ) < 0 )
2977 return -1;
2978 if( lsmash_bs_write_data( bs, buf[buf_switch], size ) < 0 )
2979 return -1;
2980 write_pos = bs->offset;
2981 if( remux && remux->func )
2982 remux->func( remux->param, write_pos, file_size ); // FIXME:
2984 if( lsmash_bs_write_data( bs, buf[buf_switch ^ 0x1], read_num ) < 0 )
2985 return -1;
2986 if( remux && remux->func )
2987 remux->func( remux->param, file_size, file_size ); // FIXME:
2988 return 0;
2991 int isom_establish_movie( lsmash_file_t *file )
2993 if( isom_check_mandatory_boxes( file )
2994 || isom_set_movie_creation_time( file )
2995 || isom_update_box_size( file->moov ) == 0 )
2996 return -1;
2997 return 0;
3000 int lsmash_finish_movie
3002 lsmash_root_t *root,
3003 lsmash_adhoc_remux_t *remux
3006 if( !root )
3007 return -1;
3008 lsmash_file_t *file = root->file;
3009 if( !file
3010 || !file->bs
3011 || !file->moov )
3012 return -1;
3013 if( file->fragment )
3014 return isom_finish_final_fragment_movie( file, remux );
3015 isom_moov_t *moov = file->moov;
3016 for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next )
3018 isom_trak_t *trak = (isom_trak_t *)entry->data;
3019 if( !trak
3020 || !trak->cache
3021 || !trak->tkhd
3022 || !trak->mdia
3023 || !trak->mdia->minf
3024 || !trak->mdia->minf->stbl
3025 || isom_complement_data_reference( trak->mdia->minf ) < 0 )
3026 return -1;
3027 uint32_t track_ID = trak->tkhd->track_ID;
3028 uint32_t related_track_ID = trak->related_track_ID;
3029 /* Disable the track if the track is a track reference chapter. */
3030 if( trak->is_chapter )
3031 trak->tkhd->flags &= ~ISOM_TRACK_ENABLED;
3032 if( trak->is_chapter && related_track_ID )
3034 /* In order that the track duration of the chapter track doesn't exceed that of the related track. */
3035 lsmash_edit_t edit;
3036 edit.duration = LSMASH_MIN( trak->tkhd->duration, lsmash_get_track_duration( root, related_track_ID ) );
3037 edit.start_time = 0;
3038 edit.rate = ISOM_EDIT_MODE_NORMAL;
3039 if( lsmash_create_explicit_timeline_map( root, track_ID, edit ) )
3040 return -1;
3042 /* Add stss box if any samples aren't sync sample. */
3043 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3044 if( !trak->cache->all_sync && !stbl->stss && !isom_add_stss( stbl ) )
3045 return -1;
3046 if( isom_update_tkhd_duration( trak ) < 0 )
3047 return -1;
3048 if( isom_update_bitrate_description( trak->mdia ) < 0 )
3049 return -1;
3051 if( file->mp4_version1 == 1 && isom_setup_iods( moov ) < 0 )
3052 return -1;
3053 if( isom_establish_movie( file ) < 0 )
3054 return -1;
3055 /* Write the size of Media Data Box here. */
3056 lsmash_bs_t *bs = file->bs;
3057 file->mdat->manager &= ~LSMASH_INCOMPLETE_BOX;
3058 if( isom_write_box( bs, (isom_box_t *)file->mdat ) < 0 )
3059 return -1;
3060 /* Write the Movie Box and a Meta Box if no optimization for progressive download. */
3061 uint64_t meta_size = file->meta ? file->meta->size : 0;
3062 if( !remux )
3064 if( isom_write_box( bs, (isom_box_t *)file->moov )
3065 || isom_write_box( bs, (isom_box_t *)file->meta ) )
3066 return -1;
3067 file->size += moov->size + meta_size;
3068 return 0;
3070 /* stco->co64 conversion, depending on last chunk's offset */
3071 for( lsmash_entry_t *entry = moov->trak_list.head; entry; )
3073 isom_trak_t *trak = (isom_trak_t *)entry->data;
3074 isom_stco_t *stco = trak->mdia->minf->stbl->stco;
3075 if( !stco->list->tail )
3076 return -1;
3077 if( stco->large_presentation
3078 || (((isom_stco_entry_t *)stco->list->tail->data)->chunk_offset + moov->size + meta_size) <= UINT32_MAX )
3080 entry = entry->next;
3081 continue; /* no need to convert stco into co64 */
3083 /* stco->co64 conversion */
3084 if( isom_convert_stco_to_co64( trak->mdia->minf->stbl ) < 0
3085 || isom_update_box_size( moov ) == 0 )
3086 return -1;
3087 entry = moov->trak_list.head; /* whenever any conversion, re-check all traks */
3089 /* now the amount of offset is fixed. */
3090 uint64_t mtf_size = moov->size + meta_size; /* sum of size of boxes moved to front */
3091 /* buffer size must be at least mtf_size * 2 */
3092 remux->buffer_size = LSMASH_MAX( remux->buffer_size, mtf_size * 2 );
3093 /* Split to 2 buffers. */
3094 uint8_t *buf[2] = { NULL, NULL };
3095 if( (buf[0] = (uint8_t*)lsmash_malloc( remux->buffer_size )) == NULL )
3096 return -1; /* NOTE: i think we still can fallback to "return isom_write_moov();" here. */
3097 size_t size = remux->buffer_size / 2;
3098 buf[1] = buf[0] + size;
3099 /* now the amount of offset is fixed. apply that to stco/co64 */
3100 for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next )
3102 isom_stco_t *stco = ((isom_trak_t *)entry->data)->mdia->minf->stbl->stco;
3103 if( stco->large_presentation )
3104 for( lsmash_entry_t *co64_entry = stco->list->head ; co64_entry ; co64_entry = co64_entry->next )
3105 ((isom_co64_entry_t *)co64_entry->data)->chunk_offset += mtf_size;
3106 else
3107 for( lsmash_entry_t *stco_entry = stco->list->head ; stco_entry ; stco_entry = stco_entry->next )
3108 ((isom_stco_entry_t *)stco_entry->data)->chunk_offset += mtf_size;
3110 /* Backup starting area of mdat and write moov + meta there instead. */
3111 isom_mdat_t *mdat = file->mdat;
3112 uint64_t total = file->size + mtf_size;
3113 uint64_t placeholder_pos = file->free ? file->free->pos : mdat->pos;
3114 if( lsmash_bs_write_seek( bs, placeholder_pos, SEEK_SET ) < 0 )
3115 goto fail;
3116 size_t read_num = size;
3117 lsmash_bs_read_data( bs, buf[0], &read_num );
3118 uint64_t read_pos = bs->offset;
3119 /* Write moov + meta there instead. */
3120 if( lsmash_bs_write_seek( bs, placeholder_pos, SEEK_SET ) < 0
3121 || isom_write_box( bs, (isom_box_t *)file->moov ) < 0
3122 || isom_write_box( bs, (isom_box_t *)file->meta ) < 0 )
3123 goto fail;
3124 uint64_t write_pos = bs->offset;
3125 /* Update the positions */
3126 mdat->pos += mtf_size;
3127 if( file->free )
3128 file->free->pos += mtf_size;
3129 /* Move Media Data Box. */
3130 if( isom_rearrange_boxes( file, remux, buf, read_num, size, read_pos, write_pos, total ) < 0 )
3131 goto fail;
3132 file->size += mtf_size;
3133 lsmash_free( buf[0] );
3134 return 0;
3135 fail:
3136 lsmash_free( buf[0] );
3137 return -1;
3140 int lsmash_set_last_sample_delta( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_delta )
3142 if( !root || track_ID == 0 )
3143 return -1;
3144 lsmash_file_t *file = root->file;
3145 if( file->fragment
3146 && file->fragment->movie )
3148 isom_traf_t *traf = isom_get_traf( file->fragment->movie, track_ID );
3149 if( !traf
3150 || !traf->cache
3151 || !traf->tfhd )
3152 return -1;
3153 return isom_set_fragment_last_duration( traf, sample_delta );
3155 isom_trak_t *trak = isom_get_trak( file, track_ID );
3156 if( !trak
3157 || !trak->cache
3158 || !trak->mdia
3159 || !trak->mdia->mdhd
3160 || !trak->mdia->minf
3161 || !trak->mdia->minf->stbl
3162 || !trak->mdia->minf->stbl->stsd
3163 || !trak->mdia->minf->stbl->stsz
3164 || !trak->mdia->minf->stbl->stts
3165 || !trak->mdia->minf->stbl->stts->list )
3166 return -1;
3167 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3168 isom_stts_t *stts = stbl->stts;
3169 uint32_t sample_count = isom_get_sample_count( trak );
3170 if( !stts->list->tail )
3172 if( !sample_count )
3173 return 0; /* no samples */
3174 if( sample_count > 1 )
3175 return -1; /* irregular sample_count */
3176 /* Set the duration of the first sample.
3177 * This duration is also the duration of the last sample. */
3178 if( isom_add_stts_entry( stbl, sample_delta ) )
3179 return -1;
3180 return lsmash_update_track_duration( root, track_ID, 0 );
3182 uint32_t i = 0;
3183 for( lsmash_entry_t *entry = stts->list->head; entry; entry = entry->next )
3184 i += ((isom_stts_entry_t *)entry->data)->sample_count;
3185 if( sample_count < i )
3186 return -1;
3187 int no_last = (sample_count > i);
3188 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stts->list->tail->data;
3189 if( !last_stts_data )
3190 return -1;
3191 /* Consider QuikcTime fixed compression audio. */
3192 isom_audio_entry_t *audio = (isom_audio_entry_t *)lsmash_get_entry_data( &trak->mdia->minf->stbl->stsd->list,
3193 trak->cache->chunk.sample_description_index );
3194 if( !audio )
3195 return -1;
3196 if( (audio->manager & LSMASH_AUDIO_DESCRIPTION)
3197 && (audio->manager & LSMASH_QTFF_BASE)
3198 && (audio->version == 1)
3199 && (audio->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION) )
3201 if( audio->samplesPerPacket == 0 )
3202 return -1;
3203 int exclude_last_sample = no_last ? 0 : 1;
3204 uint32_t j = audio->samplesPerPacket;
3205 for( lsmash_entry_t *entry = stts->list->tail; entry && j > 1; entry = entry->prev )
3207 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)entry->data;
3208 if( !stts_data )
3209 return -1;
3210 for( uint32_t k = exclude_last_sample; k < stts_data->sample_count && j > 1; k++ )
3212 sample_delta -= stts_data->sample_delta;
3213 --j;
3215 exclude_last_sample = 0;
3218 /* Set sample_delta. */
3219 if( no_last )
3221 /* The duration of the last sample is not set yet. */
3222 if( sample_count - i > 1 )
3223 return -1;
3224 /* Add a sample_delta. */
3225 if( sample_delta == last_stts_data->sample_delta )
3226 ++ last_stts_data->sample_count;
3227 else if( isom_add_stts_entry( stbl, sample_delta ) < 0 )
3228 return -1;
3230 /* The duration of the last sample is already set. Replace it with a new one. */
3231 else if( isom_replace_last_sample_delta( stbl, sample_delta ) < 0 )
3232 return -1;
3233 return lsmash_update_track_duration( root, track_ID, sample_delta );
3236 /*---- timeline manipulator ----*/
3238 int lsmash_modify_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t edit_number, lsmash_edit_t edit )
3240 if( !root || edit.duration == 0 || edit.start_time < -1 )
3241 return -1;
3242 lsmash_file_t *file = root->file;
3243 isom_trak_t *trak = isom_get_trak( file, track_ID );
3244 if( !trak
3245 || !trak->edts
3246 || !trak->edts->elst
3247 || !trak->edts->elst->list )
3248 return -1;
3249 isom_elst_t *elst = trak->edts->elst;
3250 isom_elst_entry_t *data = (isom_elst_entry_t *)lsmash_get_entry_data( elst->list, edit_number );
3251 if( !data )
3252 return -1;
3253 data->segment_duration = edit.duration;
3254 data->media_time = edit.start_time;
3255 data->media_rate = edit.rate;
3256 if( elst->pos == 0 || !file->fragment || file->bs->unseekable )
3257 return isom_update_tkhd_duration( trak );
3258 /* Rewrite the specified entry.
3259 * Note: we don't update the version of the Edit List Box. */
3260 lsmash_bs_t *bs = file->bs;
3261 uint64_t current_pos = bs->offset;
3262 uint64_t entry_pos = elst->pos + ISOM_LIST_FULLBOX_COMMON_SIZE + ((uint64_t)edit_number - 1) * (elst->version == 1 ? 20 : 12);
3263 lsmash_bs_write_seek( bs, entry_pos, SEEK_SET );
3264 if( elst->version )
3266 lsmash_bs_put_be64( bs, data->segment_duration );
3267 lsmash_bs_put_be64( bs, data->media_time );
3269 else
3271 lsmash_bs_put_be32( bs, (uint32_t)LSMASH_MIN( data->segment_duration, UINT32_MAX ) );
3272 lsmash_bs_put_be32( bs, (uint32_t)data->media_time );
3274 lsmash_bs_put_be32( bs, data->media_rate );
3275 int ret = lsmash_bs_flush_buffer( bs );
3276 lsmash_bs_write_seek( bs, current_pos, SEEK_SET );
3277 return ret;
3280 int lsmash_create_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, lsmash_edit_t edit )
3282 if( !root || edit.start_time < -1 )
3283 return -1;
3284 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
3285 if( !trak
3286 || !trak->tkhd )
3287 return -1;
3288 edit.duration = (edit.duration || root->file->fragment) ? edit.duration
3289 : trak->tkhd->duration ? trak->tkhd->duration
3290 : isom_update_tkhd_duration( trak ) < 0 ? 0
3291 : trak->tkhd->duration;
3292 if( (!trak->edts && !isom_add_edts( trak ))
3293 || (!trak->edts->elst && !isom_add_elst( trak->edts ))
3294 || isom_add_elst_entry( trak->edts->elst, edit.duration, edit.start_time, edit.rate ) )
3295 return -1;
3296 return isom_update_tkhd_duration( trak );
3299 int lsmash_get_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t edit_number, lsmash_edit_t *edit )
3301 if( !root || !edit )
3302 return -1;
3303 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
3304 if( !trak )
3305 return -1;
3306 if( !trak->edts
3307 || !trak->edts->elst )
3309 /* no edits */
3310 edit->duration = 0;
3311 edit->start_time = 0;
3312 edit->rate = 0;
3313 return 0;
3315 isom_elst_entry_t *elst = (isom_elst_entry_t *)lsmash_get_entry_data( trak->edts->elst->list, edit_number );
3316 if( !elst )
3317 return -1;
3318 edit->duration = elst->segment_duration;
3319 edit->start_time = elst->media_time;
3320 edit->rate = elst->media_rate;
3321 return 0;
3324 uint32_t lsmash_count_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID )
3326 if( !root )
3327 return -1;
3328 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
3329 if( !trak
3330 || !trak->edts
3331 || !trak->edts->elst
3332 || !trak->edts->elst->list )
3333 return 0;
3334 return trak->edts->elst->list->entry_count;
3337 /*---- create / modification time fields manipulators ----*/
3339 int lsmash_update_media_modification_time( lsmash_root_t *root, uint32_t track_ID )
3341 if( !root )
3342 return -1;
3343 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
3344 if( !trak
3345 || !trak->mdia
3346 || !trak->mdia->mdhd )
3347 return -1;
3348 isom_mdhd_t *mdhd = trak->mdia->mdhd;
3349 mdhd->modification_time = isom_get_current_mp4time();
3350 /* overwrite strange creation_time */
3351 if( mdhd->creation_time > mdhd->modification_time )
3352 mdhd->creation_time = mdhd->modification_time;
3353 return 0;
3356 int lsmash_update_track_modification_time( lsmash_root_t *root, uint32_t track_ID )
3358 if( !root )
3359 return -1;
3360 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
3361 if( !trak
3362 || !trak->tkhd )
3363 return -1;
3364 isom_tkhd_t *tkhd = trak->tkhd;
3365 tkhd->modification_time = isom_get_current_mp4time();
3366 /* overwrite strange creation_time */
3367 if( tkhd->creation_time > tkhd->modification_time )
3368 tkhd->creation_time = tkhd->modification_time;
3369 return 0;
3372 int lsmash_update_movie_modification_time( lsmash_root_t *root )
3374 if( !root )
3375 return -1;
3376 lsmash_file_t *file = root->file;
3377 if( !file
3378 || !file->moov
3379 || !file->moov->mvhd )
3380 return -1;
3381 isom_mvhd_t *mvhd = file->moov->mvhd;
3382 mvhd->modification_time = isom_get_current_mp4time();
3383 /* overwrite strange creation_time */
3384 if( mvhd->creation_time > mvhd->modification_time )
3385 mvhd->creation_time = mvhd->modification_time;
3386 return 0;
3389 /*---- sample manipulators ----*/
3390 lsmash_sample_t *lsmash_create_sample( uint32_t size )
3392 lsmash_sample_t *sample = lsmash_malloc_zero( sizeof(lsmash_sample_t) );
3393 if( !sample )
3394 return NULL;
3395 if( size == 0 )
3396 return sample;
3397 sample->data = lsmash_malloc( size );
3398 if( !sample->data )
3400 lsmash_free( sample );
3401 return NULL;
3403 sample->length = size;
3404 return sample;
3407 int lsmash_sample_alloc( lsmash_sample_t *sample, uint32_t size )
3409 if( !sample )
3410 return -1;
3411 if( size == 0 )
3413 if( sample->data )
3414 lsmash_free( sample->data );
3415 sample->data = NULL;
3416 sample->length = 0;
3417 return 0;
3419 if( size == sample->length )
3420 return 0;
3421 uint8_t *data;
3422 if( !sample->data )
3423 data = lsmash_malloc( size );
3424 else
3425 data = lsmash_realloc( sample->data, size );
3426 if( !data )
3427 return -1;
3428 sample->data = data;
3429 sample->length = size;
3430 return 0;
3433 void lsmash_delete_sample( lsmash_sample_t *sample )
3435 if( !sample )
3436 return;
3437 if( sample->data )
3438 lsmash_free( sample->data );
3439 lsmash_free( sample );
3442 isom_sample_pool_t *isom_create_sample_pool( uint64_t size )
3444 isom_sample_pool_t *pool = lsmash_malloc_zero( sizeof(isom_sample_pool_t) );
3445 if( !pool )
3446 return NULL;
3447 if( size == 0 )
3448 return pool;
3449 pool->data = lsmash_malloc( size );
3450 if( !pool->data )
3452 lsmash_free( pool );
3453 return NULL;
3455 pool->alloc = size;
3456 return pool;
3459 void isom_remove_sample_pool( isom_sample_pool_t *pool )
3461 if( !pool )
3462 return;
3463 if( pool->data )
3464 lsmash_free( pool->data );
3465 lsmash_free( pool );
3468 static uint32_t isom_add_size( isom_trak_t *trak, uint32_t sample_size )
3470 if( isom_add_stsz_entry( trak->mdia->minf->stbl, sample_size ) )
3471 return 0;
3472 return isom_get_sample_count( trak );
3475 static uint32_t isom_add_dts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t dts )
3477 isom_stts_t *stts = stbl->stts;
3478 if( stts->list->entry_count == 0 )
3480 if( isom_add_stts_entry( stbl, dts ) )
3481 return 0;
3482 cache->dts = dts;
3483 return dts;
3485 if( dts <= cache->dts )
3486 return 0;
3487 uint32_t sample_delta = dts - cache->dts;
3488 isom_stts_entry_t *data = (isom_stts_entry_t *)stts->list->tail->data;
3489 if( data->sample_delta == sample_delta )
3490 ++ data->sample_count;
3491 else if( isom_add_stts_entry( stbl, sample_delta ) )
3492 return 0;
3493 cache->dts = dts;
3494 return sample_delta;
3497 static int isom_add_cts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t cts )
3499 isom_ctts_t *ctts = stbl->ctts;
3500 if( !ctts )
3502 if( cts == cache->dts )
3504 cache->cts = cts;
3505 return 0;
3507 /* Add ctts box and the first ctts entry. */
3508 if( !isom_add_ctts( stbl ) || isom_add_ctts_entry( stbl, 0 ) < 0 )
3509 return -1;
3510 ctts = stbl->ctts;
3511 isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->head->data;
3512 uint32_t sample_count = stbl->stsz->sample_count;
3513 if( sample_count != 1 )
3515 data->sample_count = sample_count - 1;
3516 if( isom_add_ctts_entry( stbl, cts - cache->dts ) )
3517 return -1;
3519 else
3520 data->sample_offset = cts;
3521 cache->cts = cts;
3522 return 0;
3524 if( !ctts->list )
3525 return -1;
3526 isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->tail->data;
3527 uint32_t sample_offset = cts - cache->dts;
3528 if( data->sample_offset == sample_offset )
3529 ++ data->sample_count;
3530 else if( isom_add_ctts_entry( stbl, sample_offset ) )
3531 return -1;
3532 cache->cts = cts;
3533 return 0;
3536 static int isom_add_timestamp( isom_trak_t *trak, uint64_t dts, uint64_t cts )
3538 if( !trak->cache
3539 || !trak->mdia->minf->stbl->stts
3540 || !trak->mdia->minf->stbl->stts->list )
3541 return -1;
3542 lsmash_file_t *file = trak->file;
3543 if( file->isom_compatible && file->qt_compatible && (cts - dts) > INT32_MAX )
3544 return -1; /* sample_offset is not compatible. */
3545 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3546 isom_timestamp_t *ts_cache = &trak->cache->timestamp;
3547 uint32_t sample_count = isom_get_sample_count( trak );
3548 uint32_t sample_delta = sample_count > 1 ? isom_add_dts( stbl, ts_cache, dts ) : 0;
3549 if( sample_count > 1 && !sample_delta )
3550 return -1;
3551 if( isom_add_cts( stbl, ts_cache, cts ) )
3552 return -1;
3553 if( (cts + ts_cache->ctd_shift) < dts )
3555 if( (file->max_isom_version < 4 && !file->qt_compatible) /* Negative sample offset is not supported. */
3556 || (file->max_isom_version >= 4 && file->qt_compatible) /* ctts version 1 is not defined in QTFF. */
3557 || ((dts - cts) > INT32_MAX) ) /* Overflow */
3558 return -1;
3559 ts_cache->ctd_shift = dts - cts;
3560 if( stbl->ctts->version == 0 && !file->qt_compatible )
3561 stbl->ctts->version = 1;
3563 if( trak->cache->fragment )
3565 isom_fragment_t *fragment_cache = trak->cache->fragment;
3566 fragment_cache->last_duration = sample_delta;
3567 fragment_cache->largest_cts = LSMASH_MAX( ts_cache->cts, fragment_cache->largest_cts );
3569 return 0;
3572 static int isom_add_sync_point( isom_trak_t *trak, uint32_t sample_number, lsmash_sample_property_t *prop )
3574 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3575 isom_cache_t *cache = trak->cache;
3576 if( !(prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC) ) /* no null check for prop */
3578 if( !cache->all_sync )
3579 return 0;
3580 if( !stbl->stss && !isom_add_stss( stbl ) )
3581 return -1;
3582 if( isom_add_stss_entry( stbl, 1 ) ) /* Declare here the first sample is a sync sample. */
3583 return -1;
3584 cache->all_sync = 0;
3585 return 0;
3587 if( cache->all_sync ) /* We don't need stss box if all samples are sync sample. */
3588 return 0;
3589 if( !stbl->stss )
3591 if( isom_get_sample_count( trak ) == 1 )
3593 cache->all_sync = 1; /* Also the first sample is a sync sample. */
3594 return 0;
3596 if( !isom_add_stss( stbl ) )
3597 return -1;
3599 return isom_add_stss_entry( stbl, sample_number );
3602 static int isom_add_partial_sync( isom_trak_t *trak, uint32_t sample_number, lsmash_sample_property_t *prop )
3604 if( !trak->file->qt_compatible )
3605 return 0;
3606 if( !(prop->ra_flags & QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC) )
3607 return 0;
3608 /* This sample is a partial sync sample. */
3609 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3610 if( !stbl->stps && !isom_add_stps( stbl ) )
3611 return -1;
3612 return isom_add_stps_entry( stbl, sample_number );
3615 static int isom_add_dependency_type( isom_trak_t *trak, lsmash_sample_property_t *prop )
3617 if( !trak->file->qt_compatible && !trak->file->avc_extensions )
3618 return 0;
3619 int compatibility = trak->file->avc_extensions && trak->file->qt_compatible ? 3
3620 : trak->file->qt_compatible ? 2
3621 : trak->file->avc_extensions ? 1
3622 : 0;
3623 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3624 if( stbl->sdtp )
3625 return isom_add_sdtp_entry( (isom_box_t *)stbl, prop, compatibility );
3626 if( !prop->allow_earlier && !prop->leading && !prop->independent && !prop->disposable && !prop->redundant ) /* no null check for prop */
3627 return 0;
3628 if( !isom_add_sdtp( (isom_box_t *)stbl ) )
3629 return -1;
3630 uint32_t count = isom_get_sample_count( trak );
3631 /* fill past samples with ISOM_SAMPLE_*_UNKNOWN */
3632 lsmash_sample_property_t null_prop = { 0 };
3633 for( uint32_t i = 1; i < count; i++ )
3634 if( isom_add_sdtp_entry( (isom_box_t *)stbl, &null_prop, compatibility ) )
3635 return -1;
3636 return isom_add_sdtp_entry( (isom_box_t *)stbl, prop, compatibility );
3639 int isom_rap_grouping_established( isom_rap_group_t *group, int num_leading_samples_known, isom_sgpd_t *sgpd, int is_fragment )
3641 isom_rap_entry_t *rap = group->random_access;
3642 if( !rap )
3643 return 0;
3644 assert( rap == (isom_rap_entry_t *)sgpd->list->tail->data );
3645 rap->num_leading_samples_known = num_leading_samples_known;
3646 /* Avoid duplication of sample group descriptions. */
3647 uint32_t group_description_index = is_fragment ? 0x10001 : 1;
3648 for( lsmash_entry_t *entry = sgpd->list->head; entry != sgpd->list->tail; entry = entry->next )
3650 isom_rap_entry_t *data = (isom_rap_entry_t *)entry->data;
3651 if( !data )
3652 return -1;
3653 if( rap->num_leading_samples_known == data->num_leading_samples_known
3654 && rap->num_leading_samples == data->num_leading_samples )
3656 /* The same description already exists.
3657 * Remove the latest random access entry. */
3658 lsmash_remove_entry_tail( sgpd->list, NULL );
3659 /* Replace assigned group_description_index with the one corresponding the same description. */
3660 if( group->assignment->group_description_index == 0 )
3662 /* We don't create consecutive sample groups not assigned to 'rap '.
3663 * So the previous sample group shall be a group of 'rap ' if any. */
3664 if( group->prev_assignment )
3666 assert( group->prev_assignment->group_description_index );
3667 group->prev_assignment->group_description_index = group_description_index;
3670 else
3671 group->assignment->group_description_index = group_description_index;
3672 break;
3674 ++group_description_index;
3676 group->random_access = NULL;
3677 return 0;
3680 int isom_group_random_access( isom_box_t *parent, lsmash_sample_t *sample )
3682 if( parent->file->max_isom_version < 6 )
3683 return 0;
3684 isom_sbgp_t *sbgp;
3685 isom_sgpd_t *sgpd;
3686 isom_cache_t *cache;
3687 uint32_t sample_count;
3688 int is_fragment;
3689 if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) )
3691 isom_trak_t *trak = (isom_trak_t *)parent;
3692 sbgp = isom_get_sample_to_group ( trak->mdia->minf->stbl, ISOM_GROUP_TYPE_RAP );
3693 sgpd = isom_get_sample_group_description( trak->mdia->minf->stbl, ISOM_GROUP_TYPE_RAP );
3694 cache = trak->cache;
3695 sample_count = isom_get_sample_count( trak );
3696 is_fragment = 0;
3698 else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) )
3700 isom_traf_t *traf = (isom_traf_t *)parent;
3701 sbgp = isom_get_fragment_sample_to_group ( traf, ISOM_GROUP_TYPE_RAP );
3702 sgpd = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_RAP );
3703 cache = traf->cache;
3704 sample_count = cache->fragment->sample_count + 1;
3705 is_fragment = 1;
3707 else
3709 assert( 0 );
3710 sbgp = NULL;
3711 sgpd = NULL;
3712 /* redundant initializations to suppress warnings from unclever compilers */
3713 cache = NULL;
3714 sample_count = 0;
3715 is_fragment = 0;
3717 if( !sbgp || !sgpd )
3718 return 0;
3719 lsmash_sample_property_t *prop = &sample->prop;
3720 uint8_t is_rap = (prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC)
3721 || (prop->ra_flags & QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC)
3722 || (prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP)
3723 || (LSMASH_IS_POST_ROLL_START( prop->ra_flags ) && prop->post_roll.identifier == prop->post_roll.complete);
3724 isom_rap_group_t *group = cache->rap;
3725 if( !group )
3727 /* This sample is the first sample, create a grouping cache. */
3728 assert( sample_count == 1 );
3729 group = lsmash_malloc( sizeof(isom_rap_group_t) );
3730 if( !group )
3731 return -1;
3732 if( is_rap )
3734 group->random_access = isom_add_rap_group_entry( sgpd );
3735 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count + (is_fragment ? 0x10000 : 0) );
3737 else
3739 /* The first sample is not always a random access point. */
3740 group->random_access = NULL;
3741 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
3743 if( !group->assignment )
3745 lsmash_free( group );
3746 return -1;
3748 group->prev_assignment = NULL;
3749 group->is_prev_rap = is_rap;
3750 cache->rap = group;
3751 return 0;
3753 if( group->is_prev_rap )
3755 /* OK. here, the previous sample is a menber of 'rap '. */
3756 if( !is_rap )
3758 /* This sample isn't a member of 'rap ' and the previous sample is.
3759 * So we create a new group and set 0 on its group_description_index. */
3760 group->prev_assignment = group->assignment;
3761 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
3762 if( !group->assignment )
3764 lsmash_free( group );
3765 return -1;
3768 else if( !LSMASH_IS_CLOSED_RAP( prop->ra_flags ) )
3770 /* Create a new group since there is the possibility the next sample is a leading sample.
3771 * This sample is a member of 'rap ', so we set appropriate value on its group_description_index. */
3772 if( isom_rap_grouping_established( group, 1, sgpd, is_fragment ) < 0 )
3773 return -1;
3774 group->random_access = isom_add_rap_group_entry( sgpd );
3775 group->prev_assignment = group->assignment;
3776 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count + (is_fragment ? 0x10000 : 0) );
3777 if( !group->assignment )
3779 lsmash_free( group );
3780 return -1;
3783 else /* The previous and current sample are a member of 'rap ', and the next sample must not be a leading sample. */
3784 ++ group->assignment->sample_count;
3786 else if( is_rap )
3788 /* This sample is a member of 'rap ' and the previous sample isn't.
3789 * So we create a new group and set appropriate value on its group_description_index. */
3790 if( isom_rap_grouping_established( group, 1, sgpd, is_fragment ) < 0 )
3791 return -1;
3792 group->random_access = isom_add_rap_group_entry( sgpd );
3793 group->prev_assignment = group->assignment;
3794 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count + (is_fragment ? 0x10000 : 0) );
3795 if( !group->assignment )
3797 lsmash_free( group );
3798 return -1;
3801 else /* The previous and current sample aren't a member of 'rap '. */
3802 ++ group->assignment->sample_count;
3803 /* Obtain the property of the latest random access point group. */
3804 if( !is_rap && group->random_access )
3806 if( prop->leading == ISOM_SAMPLE_LEADING_UNKNOWN )
3808 /* We can no longer know num_leading_samples in this group. */
3809 if( isom_rap_grouping_established( group, 0, sgpd, is_fragment ) < 0 )
3810 return -1;
3812 else
3814 if( prop->leading == ISOM_SAMPLE_IS_UNDECODABLE_LEADING
3815 || prop->leading == ISOM_SAMPLE_IS_DECODABLE_LEADING )
3816 ++ group->random_access->num_leading_samples;
3817 /* no more consecutive leading samples in this group */
3818 else if( isom_rap_grouping_established( group, 1, sgpd, is_fragment ) < 0 )
3819 return -1;
3822 group->is_prev_rap = is_rap;
3823 return 0;
3826 static int isom_roll_grouping_established( isom_roll_group_t *group )
3828 /* Avoid duplication of sample group descriptions. */
3829 isom_sgpd_t *sgpd = group->sgpd;
3830 uint32_t group_description_index = group->is_fragment ? 0x10001 : 1;
3831 for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next )
3833 isom_roll_entry_t *data = (isom_roll_entry_t *)entry->data;
3834 if( !data )
3835 return -1;
3836 if( group->roll_distance == data->roll_distance )
3838 /* The same description already exists.
3839 * Set the group_description_index corresponding the same description. */
3840 group->assignment->group_description_index = group_description_index;
3841 return 0;
3843 ++group_description_index;
3845 /* Add a new roll recovery description. */
3846 if( !isom_add_roll_group_entry( sgpd, group->roll_distance ) )
3847 return -1;
3848 group->assignment->group_description_index = sgpd->list->entry_count + (group->is_fragment ? 0x10000 : 0);
3849 return 0;
3852 static int isom_deduplicate_roll_group( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool )
3854 /* Deduplication */
3855 uint32_t current_group_number = sbgp->list->entry_count - pool->entry_count + 1;
3856 isom_group_assignment_entry_t *prev_assignment = (isom_group_assignment_entry_t *)lsmash_get_entry_data( sbgp->list, current_group_number - 1 );
3857 for( lsmash_entry_t *entry = pool->head; entry; )
3859 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3860 if( !group
3861 || !group->assignment )
3862 return -1;
3863 if( !group->delimited || group->described != ROLL_DISTANCE_DETERMINED )
3864 return 0;
3865 if( prev_assignment && prev_assignment->group_description_index == group->assignment->group_description_index )
3867 /* Merge the current group with the previous. */
3868 lsmash_entry_t *next_entry = entry->next;
3869 prev_assignment->sample_count += group->assignment->sample_count;
3870 if( lsmash_remove_entry( sbgp->list, current_group_number, NULL )
3871 || lsmash_remove_entry_direct( pool, entry, NULL ) )
3872 return -1;
3873 entry = next_entry;
3875 else
3877 entry = entry->next;
3878 prev_assignment = group->assignment;
3879 ++current_group_number;
3882 return 0;
3885 /* Remove pooled caches that has become unnecessary. */
3886 static int isom_clean_roll_pool( lsmash_entry_list_t *pool )
3888 for( lsmash_entry_t *entry = pool->head; entry; entry = pool->head )
3890 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3891 if( !group )
3892 return -1;
3893 if( !group->delimited || group->described != ROLL_DISTANCE_DETERMINED )
3894 return 0;
3895 if( lsmash_remove_entry_direct( pool, entry, NULL ) )
3896 return -1;
3898 return 0;
3901 static int isom_flush_roll_pool( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool )
3903 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
3905 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3906 if( !group )
3907 return -1;
3908 if( group->delimited
3909 && group->described == ROLL_DISTANCE_DETERMINED
3910 && group->roll_distance != 0
3911 && isom_roll_grouping_established( group ) < 0 )
3912 return -1;
3914 if( isom_deduplicate_roll_group( sbgp, pool ) < 0 )
3915 return -1;
3916 return isom_clean_roll_pool( pool );
3919 static int isom_all_recovery_described( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool )
3921 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
3923 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3924 if( !group )
3925 return -1;
3926 group->described = ROLL_DISTANCE_DETERMINED;
3928 return isom_flush_roll_pool( sbgp, pool );
3931 int isom_all_recovery_completed( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool )
3933 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
3935 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3936 if( !group )
3937 return -1;
3938 group->described = ROLL_DISTANCE_DETERMINED;
3939 group->delimited = 1;
3941 return isom_flush_roll_pool( sbgp, pool );
3944 static isom_roll_entry_t *isom_get_roll_description
3946 isom_roll_group_t *group
3949 uint32_t group_description_index = group->assignment->group_description_index;
3950 if( group_description_index && group->is_fragment )
3952 assert( group_description_index > 0x10000 );
3953 group_description_index -= 0x10000;
3955 return (isom_roll_entry_t *)lsmash_get_entry_data( group->sgpd->list, group_description_index );
3958 int isom_group_roll_recovery( isom_box_t *parent, lsmash_sample_t *sample )
3960 if( !parent->file->avc_extensions
3961 && !parent->file->qt_compatible )
3962 return 0;
3963 uint32_t sample_count;
3964 int is_fragment;
3965 isom_cache_t *cache;
3966 lsmash_entry_list_t *sbgp_list;
3967 lsmash_entry_list_t *sgpd_list;
3968 if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) )
3970 isom_trak_t *trak = (isom_trak_t *)parent;
3971 cache = trak->cache;
3972 sbgp_list = &trak->mdia->minf->stbl->sbgp_list;
3973 sgpd_list = &trak->mdia->minf->stbl->sgpd_list;
3974 sample_count = isom_get_sample_count( trak );
3975 is_fragment = 0;
3977 else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) )
3979 if( parent->file->max_isom_version < 6 )
3980 return 0;
3981 isom_traf_t *traf = (isom_traf_t *)parent;
3982 cache = traf->cache;
3983 sbgp_list = &traf->sbgp_list;
3984 sgpd_list = &traf->sgpd_list;
3985 sample_count = cache->fragment->sample_count + 1;
3986 is_fragment = 1;
3988 else
3990 assert( 0 );
3991 return -1;
3993 isom_sbgp_t *sbgp = isom_get_roll_recovery_sample_to_group ( sbgp_list );
3994 isom_sgpd_t *sgpd = isom_get_roll_recovery_sample_group_description( sgpd_list );
3995 if( !sbgp || !sgpd || sbgp->grouping_type != sgpd->grouping_type )
3996 return 0;
3997 /* Check if 'roll' -> 'prol' conversion is needed. */
3998 if( cache->is_audio
3999 && sbgp->grouping_type == ISOM_GROUP_TYPE_ROLL
4000 && !(sample->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC) )
4002 /* Since not every samples is a sync sample, change grouping_type into 'prol'. */
4003 sbgp->grouping_type = ISOM_GROUP_TYPE_PROL;
4004 sgpd->grouping_type = ISOM_GROUP_TYPE_PROL;
4006 lsmash_entry_list_t *pool = cache->roll.pool;
4007 if( !pool )
4009 pool = lsmash_create_entry_list();
4010 if( !pool )
4011 return -1;
4012 cache->roll.pool = pool;
4014 lsmash_sample_property_t *prop = &sample->prop;
4015 isom_roll_group_t *group = (isom_roll_group_t *)lsmash_get_entry_data( pool, pool->entry_count );
4016 int is_recovery_start = LSMASH_IS_POST_ROLL_START( prop->ra_flags );
4017 int valid_pre_roll = !is_recovery_start
4018 && (prop->ra_flags != ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE)
4019 && (prop->pre_roll.distance > 0)
4020 && (prop->pre_roll.distance <= -INT16_MIN);
4021 int new_group = !group || is_recovery_start || (group->prev_is_recovery_start != is_recovery_start);
4022 if( !new_group )
4024 /* Check pre-roll distance. */
4025 assert( group->assignment && group->sgpd );
4026 isom_roll_entry_t *prev_roll = isom_get_roll_description( group );
4027 if( !prev_roll )
4028 new_group = valid_pre_roll;
4029 else if( !valid_pre_roll || (prop->pre_roll.distance != -prev_roll->roll_distance) )
4030 /* Pre-roll distance is different from the previous. */
4031 new_group = 1;
4033 if( new_group )
4035 if( group )
4036 group->delimited = 1;
4037 else
4038 assert( sample_count == 1 );
4039 /* Create a new group. */
4040 group = lsmash_malloc_zero( sizeof(isom_roll_group_t) );
4041 if( !group )
4042 return -1;
4043 group->sgpd = sgpd;
4044 group->prev_is_recovery_start = is_recovery_start;
4045 group->is_fragment = is_fragment;
4046 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
4047 if( !group->assignment || lsmash_add_entry( pool, group ) )
4049 lsmash_free( group );
4050 return -1;
4052 if( is_recovery_start )
4054 /* a member of non-roll or post-roll group */
4055 group->first_sample = sample_count;
4056 group->recovery_point = prop->post_roll.complete;
4058 else
4060 group->described = ROLL_DISTANCE_DETERMINED;
4061 if( valid_pre_roll )
4063 /* a member of pre-roll group */
4064 group->roll_distance = -prop->pre_roll.distance;
4065 if( isom_roll_grouping_established( group ) < 0 )
4066 return -1;
4068 else
4069 /* a member of non-roll group */
4070 group->roll_distance = 0;
4073 else
4075 group->prev_is_recovery_start = is_recovery_start;
4076 group->assignment->sample_count += 1;
4078 /* If encountered a RAP, all recovery is completed here. */
4079 if( prop->ra_flags & (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
4080 | ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
4081 | QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC) )
4082 return isom_all_recovery_described( sbgp, pool );
4083 /* Check whether this sample is a random access recovery point or not. */
4084 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
4086 group = (isom_roll_group_t *)entry->data;
4087 if( !group )
4088 return -1;
4089 if( group->described == ROLL_DISTANCE_DETERMINED )
4090 continue;
4091 if( group->described == ROLL_DISTANCE_INITIALIZED )
4093 /* Let's consider the following picture sequence.
4094 * coded order : P[0] P[1] P[2] P[3] P[4] P[5]
4095 * DTS : 0 1 2 3 4 5
4096 * CTS : 2 4 3 6 7 5
4097 * Here, P[0] conveys a recovery point SEI and P[3] is the recovery point.
4098 * Correctness of decoded pictures is specified by recovery point in output order for both AVC and HEVC.
4099 * Therefore, as follows,
4100 * output order : P[0] P[2] P[1] P[5]|P[3] P[4]
4101 * ---(incorrect?)--->|
4102 * there is no guarantee that P[5] is decoded and output correctly.
4103 * From this, it can be said that the roll_distance of this sequence is equal to 5. */
4104 isom_roll_entry_t *post_roll = isom_get_roll_description( group );
4105 if( post_roll && post_roll->roll_distance > 0 )
4107 if( group->rp_cts > sample->cts )
4108 /* Updated roll_distance for composition reordering. */
4109 post_roll->roll_distance = sample_count - group->first_sample;
4110 if( ++ group->wait_and_see_count >= MAX_ROLL_WAIT_AND_SEE_COUNT )
4111 group->described = ROLL_DISTANCE_DETERMINED;
4114 else if( prop->post_roll.identifier == group->recovery_point )
4116 int16_t distance = sample_count - group->first_sample;
4117 group->rp_cts = sample->cts;
4118 group->roll_distance = distance;
4119 /* Add a roll recovery entry only when roll_distance isn't zero since roll_distance = 0 must not be used. */
4120 if( distance )
4122 /* Now, this group is a 'roll'.
4123 * The roll_distance may be updated later because of composition reordering. */
4124 group->described = ROLL_DISTANCE_INITIALIZED;
4125 group->wait_and_see_count = 0;
4126 /* All groups with uninitialized roll_distance before the current group are described. */
4127 lsmash_entry_t *current = entry;
4128 for( entry = pool->head; entry != current; entry = entry->next )
4130 group = (isom_roll_group_t *)entry->data;
4131 if( !group || group->described != ROLL_DISTANCE_INITIALIZED )
4132 continue;
4133 group->described = ROLL_DISTANCE_DETERMINED;
4135 /* Cache the CTS of the first recovery point in a subsegment. */
4136 if( cache->fragment
4137 && cache->fragment->subsegment.first_rp_number == 0 )
4139 cache->fragment->subsegment.first_rp_number = sample_count;
4140 cache->fragment->subsegment.first_rp_cts = sample->cts;
4141 cache->fragment->subsegment.first_ed_cts = sample->cts;
4142 cache->fragment->subsegment.decodable = 1;
4145 else
4146 /* Random Accessible Point */
4147 return isom_all_recovery_described( sbgp, pool );
4150 return isom_flush_roll_pool( sbgp, pool );
4153 /* returns 1 if pooled samples must be flushed. */
4154 /* FIXME: I wonder if this function should have a extra argument which indicates force_to_flush_cached_chunk.
4155 see lsmash_append_sample for detail. */
4156 static int isom_add_chunk( isom_trak_t *trak, lsmash_sample_t *sample )
4158 if( !trak->file
4159 || !trak->cache
4160 || !trak->mdia->mdhd
4161 || trak->mdia->mdhd->timescale == 0
4162 || !trak->mdia->minf->stbl->stsc
4163 || !trak->mdia->minf->stbl->stsc->list )
4164 return -1;
4165 lsmash_file_t *file = trak->file;
4166 isom_chunk_t *current = &trak->cache->chunk;
4167 if( !current->pool )
4169 /* Very initial settings, just once per track */
4170 current->pool = isom_create_sample_pool( 0 );
4171 if( !current->pool )
4172 return -1;
4174 if( !current->pool->sample_count )
4176 /* Cannot decide whether we should flush the current sample or not here yet. */
4177 current->chunk_number += 1;
4178 current->sample_description_index = sample->index;
4179 current->first_dts = sample->dts;
4180 return 0;
4182 if( sample->dts < current->first_dts )
4183 return -1; /* easy error check. */
4184 if( (file->max_chunk_duration >= ((double)(sample->dts - current->first_dts) / trak->mdia->mdhd->timescale))
4185 && (file->max_chunk_size >= current->pool->size + sample->length)
4186 && (current->sample_description_index == sample->index) )
4187 return 0; /* No need to flush current cached chunk, the current sample must be put into that. */
4188 /* NOTE: chunk relative stuff must be pushed into file after a chunk is fully determined with its contents. */
4189 /* Now the current cached chunk is fixed, actually add the chunk relative properties to its file accordingly. */
4190 isom_stbl_t *stbl = trak->mdia->minf->stbl;
4191 isom_stsc_entry_t *last_stsc_data = stbl->stsc->list->tail ? (isom_stsc_entry_t *)stbl->stsc->list->tail->data : NULL;
4192 /* Create a new chunk sequence in this track if needed. */
4193 if( (!last_stsc_data
4194 || current->pool->sample_count != last_stsc_data->samples_per_chunk
4195 || current->sample_description_index != last_stsc_data->sample_description_index)
4196 && isom_add_stsc_entry( stbl, current->chunk_number,
4197 current->pool->sample_count,
4198 current->sample_description_index ) )
4199 return -1;
4200 /* Add a new chunk offset in this track. */
4201 uint64_t offset = file->size;
4202 if( file->fragment )
4203 offset += ISOM_BASEBOX_COMMON_SIZE + file->fragment->pool_size;
4204 if( isom_add_stco_entry( stbl, offset ) )
4205 return -1;
4206 /* Update and re-initialize cache, using the current sample */
4207 current->chunk_number += 1;
4208 current->sample_description_index = sample->index;
4209 current->first_dts = sample->dts;
4210 /* current->pool must be flushed in isom_append_sample_internal() */
4211 return 1;
4214 static int isom_write_pooled_samples( lsmash_file_t *file, isom_sample_pool_t *pool )
4216 if( !file
4217 || !file->mdat
4218 || !file->bs
4219 || !file->bs->stream )
4220 return -1;
4221 lsmash_bs_put_bytes( file->bs, pool->size, pool->data );
4222 if( lsmash_bs_flush_buffer( file->bs ) )
4223 return -1;
4224 file->mdat->media_size += pool->size;
4225 file->size += pool->size;
4226 pool->sample_count = 0;
4227 pool->size = 0;
4228 return 0;
4231 int isom_update_sample_tables( isom_trak_t *trak, lsmash_sample_t *sample, uint32_t *samples_per_packet )
4233 isom_audio_entry_t *audio = (isom_audio_entry_t *)lsmash_get_entry_data( &trak->mdia->minf->stbl->stsd->list, sample->index );
4234 if( (audio->manager & LSMASH_AUDIO_DESCRIPTION)
4235 && (audio->manager & LSMASH_QTFF_BASE)
4236 && (audio->version == 1)
4237 && (audio->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION) )
4239 /* Add entries of the sample table for each uncompressed sample. */
4240 uint64_t sample_duration = trak->mdia->mdhd->timescale / (audio->samplerate >> 16);
4241 if( audio->samplesPerPacket == 0 || sample_duration == 0 )
4242 return -1;
4243 uint64_t sample_dts = sample->dts;
4244 uint64_t sample_cts = sample->cts;
4245 for( uint32_t i = 0; i < audio->samplesPerPacket; i++ )
4247 /* Add a size of uncomressed audio and increment sample_count.
4248 * This points to individual uncompressed audio samples, each one byte in size, within the compressed frames. */
4249 uint32_t sample_count = isom_add_size( trak, 1 );
4250 if( sample_count == 0 )
4251 return -1;
4252 /* Add a decoding timestamp and a composition timestamp. */
4253 if( isom_add_timestamp( trak, sample_dts, sample_cts ) < 0 )
4254 return -1;
4255 sample_dts += sample_duration;
4256 sample_cts += sample_duration;
4258 *samples_per_packet = audio->samplesPerPacket;
4260 else
4262 /* Add a sample_size and increment sample_count. */
4263 uint32_t sample_count = isom_add_size( trak, sample->length );
4264 if( sample_count == 0 )
4265 return -1;
4266 /* Add a decoding timestamp and a composition timestamp. */
4267 if( isom_add_timestamp( trak, sample->dts, sample->cts ) < 0 )
4268 return -1;
4269 /* Add a sync point if needed. */
4270 if( isom_add_sync_point( trak, sample_count, &sample->prop ) < 0 )
4271 return -1;
4272 /* Add a partial sync point if needed. */
4273 if( isom_add_partial_sync( trak, sample_count, &sample->prop ) < 0 )
4274 return -1;
4275 /* Add leading, independent, disposable and redundant information if needed. */
4276 if( isom_add_dependency_type( trak, &sample->prop ) < 0 )
4277 return -1;
4278 /* Group samples into random access point type if needed. */
4279 if( isom_group_random_access( (isom_box_t *)trak, sample ) < 0 )
4280 return -1;
4281 /* Group samples into random access recovery point type if needed. */
4282 if( isom_group_roll_recovery( (isom_box_t *)trak, sample ) < 0 )
4283 return -1;
4284 *samples_per_packet = 1;
4286 /* Add a chunk if needed. */
4287 return isom_add_chunk( trak, sample );
4290 static int isom_output_cached_chunk( isom_trak_t *trak )
4292 isom_chunk_t *chunk = &trak->cache->chunk;
4293 isom_stbl_t *stbl = trak->mdia->minf->stbl;
4294 isom_stsc_entry_t *last_stsc_data = stbl->stsc->list->tail ? (isom_stsc_entry_t *)stbl->stsc->list->tail->data : NULL;
4295 /* Create a new chunk sequence in this track if needed. */
4296 if( (!last_stsc_data
4297 || chunk->pool->sample_count != last_stsc_data->samples_per_chunk
4298 || chunk->sample_description_index != last_stsc_data->sample_description_index)
4299 && isom_add_stsc_entry( stbl, chunk->chunk_number,
4300 chunk->pool->sample_count,
4301 chunk->sample_description_index ) )
4302 return -1;
4303 lsmash_file_t *file = trak->file;
4304 if( file->fragment )
4306 /* Add a new chunk offset in this track. */
4307 if( isom_add_stco_entry( stbl, file->size + ISOM_BASEBOX_COMMON_SIZE + file->fragment->pool_size ) )
4308 return -1;
4309 return isom_append_fragment_track_run( file, chunk );
4311 /* Add a new chunk offset in this track. */
4312 if( isom_add_stco_entry( stbl, file->size ) )
4313 return -1;
4314 /* Output pooled samples in this track. */
4315 return isom_write_pooled_samples( file, chunk->pool );
4318 int isom_pool_sample( isom_sample_pool_t *pool, lsmash_sample_t *sample, uint32_t samples_per_packet )
4320 uint64_t pool_size = pool->size + sample->length;
4321 if( pool->alloc < pool_size )
4323 uint8_t *data;
4324 uint64_t alloc = pool_size + (1<<16);
4325 if( !pool->data )
4326 data = lsmash_malloc( alloc );
4327 else
4328 data = lsmash_realloc( pool->data, alloc );
4329 if( !data )
4330 return -1;
4331 pool->data = data;
4332 pool->alloc = alloc;
4334 memcpy( pool->data + pool->size, sample->data, sample->length );
4335 pool->size = pool_size;
4336 pool->sample_count += samples_per_packet;
4337 lsmash_delete_sample( sample );
4338 return 0;
4341 static int isom_append_sample_internal( isom_trak_t *trak, lsmash_sample_t *sample )
4343 uint32_t samples_per_packet;
4344 int flush = isom_update_sample_tables( trak, sample, &samples_per_packet );
4345 if( flush < 0 )
4346 return -1;
4347 /* flush == 1 means pooled samples must be flushed. */
4348 lsmash_file_t *file = trak->file;
4349 isom_sample_pool_t *current_pool = trak->cache->chunk.pool;
4350 if( flush == 1 && isom_write_pooled_samples( file, current_pool ) )
4351 return -1;
4352 /* Arbitration system between tracks with extremely scattering dts.
4353 * Here, we check whether asynchronization between the tracks exceeds the tolerance.
4354 * If a track has too old "first DTS" in its cached chunk than current sample's DTS, then its pooled samples must be flushed.
4355 * We don't consider presentation of media since any edit can pick an arbitrary portion of media in track.
4356 * Note: you needn't read this loop until you grasp the basic handling of chunks. */
4357 double tolerance = file->max_async_tolerance;
4358 for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next )
4360 isom_trak_t *other = (isom_trak_t *)entry->data;
4361 if( trak == other )
4362 continue;
4363 if( !other
4364 || !other->cache
4365 || !other->mdia
4366 || !other->mdia->mdhd
4367 || other->mdia->mdhd->timescale == 0
4368 || !other->mdia->minf
4369 || !other->mdia->minf->stbl
4370 || !other->mdia->minf->stbl->stsc
4371 || !other->mdia->minf->stbl->stsc->list )
4372 return -1;
4373 isom_chunk_t *chunk = &other->cache->chunk;
4374 if( !chunk->pool || chunk->pool->sample_count == 0 )
4375 continue;
4376 double diff = ((double)sample->dts / trak->mdia->mdhd->timescale)
4377 - ((double)chunk->first_dts / other->mdia->mdhd->timescale);
4378 if( diff > tolerance && isom_output_cached_chunk( other ) )
4379 return -1;
4380 /* Note: we don't flush the cached chunk in the current track and the current sample here
4381 * even if the conditional expression of '-diff > tolerance' meets.
4382 * That's useless because appending a sample to another track would be a good equivalent.
4383 * It's even harmful because it causes excess chunk division by calling
4384 * isom_output_cached_chunk() which always generates a new chunk.
4385 * Anyway some excess chunk division will be there, but rather less without it.
4386 * To completely avoid this, we need to observe at least whether the current sample will be placed
4387 * right next to the previous chunk of the same track or not. */
4389 /* anyway the current sample must be pooled. */
4390 return isom_pool_sample( current_pool, sample, samples_per_packet );
4393 /* This function is for non-fragmented movie. */
4394 static int isom_append_sample( lsmash_file_t *file, uint32_t track_ID, lsmash_sample_t *sample )
4396 isom_trak_t *trak = isom_get_trak( file, track_ID );
4397 if( !trak
4398 || !trak->file
4399 || !trak->cache
4400 || !trak->mdia
4401 || !trak->mdia->mdhd
4402 || trak->mdia->mdhd->timescale == 0
4403 || !trak->mdia->minf
4404 || !trak->mdia->minf->stbl
4405 || !trak->mdia->minf->stbl->stsd
4406 || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
4407 return -1;
4408 /* If there is no available Media Data Box to write samples, add and write a new one before any chunk offset is decided. */
4409 if( !file->mdat )
4411 if( !isom_add_mdat( file ) )
4412 return -1;
4413 file->mdat->manager |= LSMASH_PLACEHOLDER;
4414 if( isom_write_box( file->bs, (isom_box_t *)file->mdat ) < 0 )
4415 return -1;
4416 assert( file->free );
4417 file->size += file->free->size + file->mdat->size;
4419 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( &trak->mdia->minf->stbl->stsd->list, sample->index );
4420 if( !sample_entry )
4421 return -1;
4422 if( isom_is_lpcm_audio( sample_entry ) )
4424 uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket;
4425 if( sample->length == frame_size )
4426 return isom_append_sample_internal( trak, sample );
4427 else if( sample->length < frame_size )
4428 return -1;
4429 /* Append samples splitted into each LPCMFrame. */
4430 uint64_t dts = sample->dts;
4431 uint64_t cts = sample->cts;
4432 for( uint32_t offset = 0; offset < sample->length; offset += frame_size )
4434 lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size );
4435 if( !lpcm_sample )
4436 return -1;
4437 memcpy( lpcm_sample->data, sample->data + offset, frame_size );
4438 lpcm_sample->dts = dts++;
4439 lpcm_sample->cts = cts++;
4440 lpcm_sample->prop = sample->prop;
4441 lpcm_sample->index = sample->index;
4442 if( isom_append_sample_internal( trak, lpcm_sample ) )
4444 lsmash_delete_sample( lpcm_sample );
4445 return -1;
4448 lsmash_delete_sample( sample );
4449 return 0;
4451 return isom_append_sample_internal( trak, sample );
4454 static int isom_output_cache( isom_trak_t *trak )
4456 isom_cache_t *cache = trak->cache;
4457 if( cache->chunk.pool
4458 && cache->chunk.pool->sample_count
4459 && isom_output_cached_chunk( trak ) < 0 )
4460 return -1;
4461 isom_stbl_t *stbl = trak->mdia->minf->stbl;
4462 for( lsmash_entry_t *entry = stbl->sgpd_list.head; entry; entry = entry->next )
4464 isom_sgpd_t *sgpd = (isom_sgpd_t *)entry->data;
4465 if( !sgpd )
4466 return -1;
4467 switch( sgpd->grouping_type )
4469 case ISOM_GROUP_TYPE_RAP :
4471 isom_rap_group_t *group = cache->rap;
4472 if( !group )
4474 if( stbl->file->fragment )
4475 continue;
4476 else
4477 return -1;
4479 if( !group->random_access )
4480 continue;
4481 group->random_access->num_leading_samples_known = 1;
4482 break;
4484 case ISOM_GROUP_TYPE_ROLL :
4485 case ISOM_GROUP_TYPE_PROL :
4486 if( !cache->roll.pool )
4488 if( stbl->file->fragment )
4489 continue;
4490 else
4491 return -1;
4493 isom_sbgp_t *sbgp = isom_get_roll_recovery_sample_to_group( &stbl->sbgp_list );
4494 if( !sbgp || isom_all_recovery_completed( sbgp, cache->roll.pool ) < 0 )
4495 return -1;
4496 break;
4497 default :
4498 break;
4501 return 0;
4504 int lsmash_flush_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta )
4506 if( !root || !root->file )
4507 return -1;
4508 lsmash_file_t *file = root->file;
4509 if( file->fragment
4510 && file->fragment->movie )
4511 return isom_flush_fragment_pooled_samples( file, track_ID, last_sample_delta );
4512 isom_trak_t *trak = isom_get_trak( file, track_ID );
4513 if( !trak
4514 || !trak->cache
4515 || !trak->mdia
4516 || !trak->mdia->minf
4517 || !trak->mdia->minf->stbl
4518 || !trak->mdia->minf->stbl->stsc
4519 || !trak->mdia->minf->stbl->stsc->list )
4520 return -1;
4521 if( isom_output_cache( trak ) < 0 )
4522 return -1;
4523 return lsmash_set_last_sample_delta( root, track_ID, last_sample_delta );
4526 int lsmash_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample )
4528 if( !root )
4529 return -1;
4530 lsmash_file_t *file = root->file;
4531 /* We think max_chunk_duration == 0, which means all samples will be cached on memory, should be prevented.
4532 * This means removal of a feature that we used to have, but anyway very alone chunk does not make sense. */
4533 if( !file
4534 || !file->bs
4535 || !sample
4536 || !sample->data
4537 || track_ID == 0
4538 || file->max_chunk_duration == 0
4539 || file->max_async_tolerance == 0 )
4540 return -1;
4541 /* Write File Type Box here if it was not written yet. */
4542 if( file->ftyp && !(file->ftyp->manager & LSMASH_WRITTEN_BOX) )
4544 if( isom_write_box( file->bs, (isom_box_t *)file->ftyp ) )
4545 return -1;
4546 file->size += file->ftyp->size;
4548 if( file->fragment
4549 && file->fragment->pool )
4550 return isom_append_fragment_sample( file, track_ID, sample );
4551 return isom_append_sample( file, track_ID, sample );
4554 /*---- misc functions ----*/
4556 int lsmash_delete_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID )
4558 if( !root )
4559 return -1;
4560 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
4561 if( !trak )
4562 return -1;
4563 isom_remove_box_by_itself( trak->edts );
4564 return isom_update_tkhd_duration( trak );
4567 void lsmash_delete_tyrant_chapter( lsmash_root_t *root )
4569 if( !root
4570 || !root->file
4571 || !root->file->moov
4572 || !root->file->moov->udta )
4573 return;
4574 isom_remove_box_by_itself( root->file->moov->udta->chpl );
4577 int lsmash_set_copyright( lsmash_root_t *root, uint32_t track_ID, uint16_t ISO_language, char *notice )
4579 if( !root )
4580 return -1;
4581 lsmash_file_t *file = root->file;
4582 if( !file
4583 || !file->moov
4584 || !file->isom_compatible
4585 || (ISO_language && ISO_language < 0x800)
4586 || !notice )
4587 return -1;
4588 isom_udta_t *udta;
4589 if( track_ID )
4591 isom_trak_t *trak = isom_get_trak( file, track_ID );
4592 if( !trak || (!trak->udta && isom_add_udta( trak )) )
4593 return -1;
4594 udta = trak->udta;
4596 else
4598 if( !file->moov->udta && isom_add_udta( file->moov ) )
4599 return -1;
4600 udta = file->moov->udta;
4602 assert( udta );
4603 for( lsmash_entry_t *entry = udta->cprt_list.head; entry; entry = entry->next )
4605 isom_cprt_t *cprt = (isom_cprt_t *)entry->data;
4606 if( !cprt || cprt->language == ISO_language )
4607 return -1;
4609 if( !isom_add_cprt( udta ) )
4610 return -1;
4611 isom_cprt_t *cprt = (isom_cprt_t *)udta->cprt_list.tail->data;
4612 cprt->language = ISO_language;
4613 cprt->notice_length = strlen( notice ) + 1;
4614 cprt->notice = lsmash_memdup( notice, cprt->notice_length );
4615 return 0;