importer: Add fake movie maker.
[L-SMASH.git] / core / isom.c
blob8574603f412e994734c2e24a750645c0dcf98ec4
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>
30 #include "box.h"
31 #include "file.h"
32 #include "write.h"
33 #include "fragment.h"
34 #include "read.h"
36 #include "codecs/mp4a.h"
37 #include "codecs/mp4sys.h"
38 #include "codecs/description.h"
40 /*---- ----*/
41 int isom_check_initializer_present( lsmash_root_t *root )
43 if( !root
44 || !root->file
45 || !root->file->initializer )
46 return LSMASH_ERR_NAMELESS;
47 return 0;
50 isom_trak_t *isom_get_trak( lsmash_file_t *file, uint32_t track_ID )
52 if( track_ID == 0
53 || !file
54 || file != file->initializer
55 || !file->moov )
56 return NULL;
57 for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next )
59 isom_trak_t *trak = (isom_trak_t *)entry->data;
60 if( !trak
61 || !trak->tkhd )
62 return NULL;
63 if( trak->tkhd->track_ID == track_ID )
64 return trak;
66 return NULL;
69 isom_trex_t *isom_get_trex( isom_mvex_t *mvex, uint32_t track_ID )
71 if( track_ID == 0 || !mvex )
72 return NULL;
73 for( lsmash_entry_t *entry = mvex->trex_list.head; entry; entry = entry->next )
75 isom_trex_t *trex = (isom_trex_t *)entry->data;
76 if( !trex )
77 return NULL;
78 if( trex->track_ID == track_ID )
79 return trex;
81 return NULL;
84 isom_traf_t *isom_get_traf( isom_moof_t *moof, uint32_t track_ID )
86 if( track_ID == 0 || !moof )
87 return NULL;
88 for( lsmash_entry_t *entry = moof->traf_list.head; entry; entry = entry->next )
90 isom_traf_t *traf = (isom_traf_t *)entry->data;
91 if( !traf
92 || !traf->tfhd )
93 return NULL;
94 if( traf->tfhd->track_ID == track_ID )
95 return traf;
97 return NULL;
100 isom_tfra_t *isom_get_tfra( isom_mfra_t *mfra, uint32_t track_ID )
102 if( track_ID == 0 || !mfra )
103 return NULL;
104 for( lsmash_entry_t *entry = mfra->tfra_list.head; entry; entry = entry->next )
106 isom_tfra_t *tfra = (isom_tfra_t *)entry->data;
107 if( !tfra )
108 return NULL;
109 if( tfra->track_ID == track_ID )
110 return tfra;
112 return NULL;
115 static int isom_add_elst_entry( isom_elst_t *elst, uint64_t segment_duration, int64_t media_time, int32_t media_rate )
117 assert( elst->file );
118 isom_elst_entry_t *data = lsmash_malloc( sizeof(isom_elst_entry_t) );
119 if( !data )
120 return LSMASH_ERR_MEMORY_ALLOC;
121 data->segment_duration = segment_duration;
122 data->media_time = media_time;
123 data->media_rate = media_rate;
124 if( lsmash_add_entry( elst->list, data ) < 0 )
126 lsmash_free( data );
127 return LSMASH_ERR_MEMORY_ALLOC;
129 if( !elst->file->undefined_64_ver
130 && (data->segment_duration > UINT32_MAX
131 || data->media_time > INT32_MAX
132 || data->media_time < INT32_MIN) )
133 elst->version = 1;
134 return 0;
137 isom_dcr_ps_entry_t *isom_create_ps_entry( uint8_t *ps, uint32_t ps_size )
139 isom_dcr_ps_entry_t *entry = lsmash_malloc( sizeof(isom_dcr_ps_entry_t) );
140 if( !entry )
141 return NULL;
142 entry->nalUnit = lsmash_memdup( ps, ps_size );
143 if( !entry->nalUnit )
145 lsmash_free( entry );
146 return NULL;
148 entry->nalUnitLength = ps_size;
149 entry->unused = 0;
150 return entry;
153 void isom_remove_dcr_ps( isom_dcr_ps_entry_t *ps )
155 if( !ps )
156 return;
157 lsmash_free( ps->nalUnit );
158 lsmash_free( ps );
161 /* This function returns 0 if failed, sample_entry_number if succeeded. */
162 int lsmash_add_sample_entry( lsmash_root_t *root, uint32_t track_ID, void *summary )
164 if( !root || !summary
165 || ((lsmash_summary_t *)summary)->data_ref_index == 0
166 || ((lsmash_summary_t *)summary)->data_ref_index > UINT16_MAX )
167 return 0;
168 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
169 if( !trak
170 || !trak->file
171 || !trak->mdia
172 || !trak->mdia->minf
173 || !trak->mdia->minf->stbl
174 || !trak->mdia->minf->stbl->stsd )
175 return 0;
176 isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd;
177 lsmash_entry_list_t *list = &stsd->list;
178 int ret = LSMASH_ERR_NAMELESS;
179 lsmash_codec_type_t sample_type = ((lsmash_summary_t *)summary)->sample_type;
180 if( lsmash_check_codec_type_identical( sample_type, LSMASH_CODEC_TYPE_RAW ) )
182 if( trak->mdia->minf->vmhd )
183 ret = isom_setup_visual_description( stsd, sample_type, (lsmash_video_summary_t *)summary );
184 else if( trak->mdia->minf->smhd )
185 ret = isom_setup_audio_description( stsd, sample_type, (lsmash_audio_summary_t *)summary );
186 return ret < 0 ? 0 : list->entry_count;
188 typedef void (*opaque_func_t)( void );
189 static struct description_setup_table_tag
191 lsmash_codec_type_t type;
192 opaque_func_t func;
193 } description_setup_table[160] = { { LSMASH_CODEC_TYPE_INITIALIZER, NULL } };
194 if( !description_setup_table[0].func )
196 int i = 0;
197 #define ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( type, func ) \
198 description_setup_table[i++] = (struct description_setup_table_tag){ type, (opaque_func_t)func }
199 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC1_VIDEO, isom_setup_visual_description );
200 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC2_VIDEO, isom_setup_visual_description );
201 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC3_VIDEO, isom_setup_visual_description );
202 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVC4_VIDEO, isom_setup_visual_description );
203 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_HVC1_VIDEO, isom_setup_visual_description );
204 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_HEV1_VIDEO, isom_setup_visual_description );
205 #if 0
206 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AVCP_VIDEO, isom_setup_visual_description );
207 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SVC1_VIDEO, isom_setup_visual_description );
208 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC1_VIDEO, isom_setup_visual_description );
209 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MVC2_VIDEO, isom_setup_visual_description );
210 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4V_VIDEO, isom_setup_visual_description );
211 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRAC_VIDEO, isom_setup_visual_description );
212 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCV_VIDEO, isom_setup_visual_description );
213 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MJP2_VIDEO, isom_setup_visual_description );
214 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_S263_VIDEO, isom_setup_visual_description );
215 #endif
216 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_VC_1_VIDEO, isom_setup_visual_description );
217 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_2VUY_VIDEO, isom_setup_visual_description );
218 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCH_VIDEO, isom_setup_visual_description );
219 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCN_VIDEO, isom_setup_visual_description );
220 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCS_VIDEO, isom_setup_visual_description );
221 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_APCO_VIDEO, isom_setup_visual_description );
222 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_AP4H_VIDEO, isom_setup_visual_description );
223 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVC_VIDEO, isom_setup_visual_description );
224 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVCP_VIDEO, isom_setup_visual_description );
225 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVPP_VIDEO, isom_setup_visual_description );
226 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DV5N_VIDEO, isom_setup_visual_description );
227 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DV5P_VIDEO, isom_setup_visual_description );
228 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH2_VIDEO, isom_setup_visual_description );
229 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH3_VIDEO, isom_setup_visual_description );
230 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH5_VIDEO, isom_setup_visual_description );
231 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVH6_VIDEO, isom_setup_visual_description );
232 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVHP_VIDEO, isom_setup_visual_description );
233 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_DVHQ_VIDEO, isom_setup_visual_description );
234 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULRA_VIDEO, isom_setup_visual_description );
235 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULRG_VIDEO, isom_setup_visual_description );
236 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULY2_VIDEO, isom_setup_visual_description );
237 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULY0_VIDEO, isom_setup_visual_description );
238 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULH2_VIDEO, isom_setup_visual_description );
239 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULH0_VIDEO, isom_setup_visual_description );
240 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V210_VIDEO, isom_setup_visual_description );
241 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V216_VIDEO, isom_setup_visual_description );
242 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V308_VIDEO, isom_setup_visual_description );
243 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V408_VIDEO, isom_setup_visual_description );
244 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_V410_VIDEO, isom_setup_visual_description );
245 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_YUV2_VIDEO, isom_setup_visual_description );
246 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4A_AUDIO, isom_setup_audio_description );
247 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_AC_3_AUDIO, isom_setup_audio_description );
248 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ALAC_AUDIO, isom_setup_audio_description );
249 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_EC_3_AUDIO, isom_setup_audio_description );
250 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAMR_AUDIO, isom_setup_audio_description );
251 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWB_AUDIO, isom_setup_audio_description );
252 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSC_AUDIO, isom_setup_audio_description );
253 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSE_AUDIO, isom_setup_audio_description );
254 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSH_AUDIO, isom_setup_audio_description );
255 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DTSL_AUDIO, isom_setup_audio_description );
256 #if 0
257 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_DRA1_AUDIO, isom_setup_audio_description );
258 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_ENCA_AUDIO, isom_setup_audio_description );
259 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_G719_AUDIO, isom_setup_audio_description );
260 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_G726_AUDIO, isom_setup_audio_description );
261 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_M4AE_AUDIO, isom_setup_audio_description );
262 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MLPA_AUDIO, isom_setup_audio_description );
263 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_RAW_AUDIO, isom_setup_audio_description );
264 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SAWP_AUDIO, isom_setup_audio_description );
265 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SEVC_AUDIO, isom_setup_audio_description );
266 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SQCP_AUDIO, isom_setup_audio_description );
267 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_SSMV_AUDIO, isom_setup_audio_description );
268 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_TWOS_AUDIO, isom_setup_audio_description );
269 #endif
270 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MP4A_AUDIO, isom_setup_audio_description );
271 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MAC3_AUDIO, isom_setup_audio_description );
272 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_MAC6_AUDIO, isom_setup_audio_description );
273 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_AGSM_AUDIO, isom_setup_audio_description );
274 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ALAW_AUDIO, isom_setup_audio_description );
275 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ULAW_AUDIO, isom_setup_audio_description );
276 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FULLMP3_AUDIO, isom_setup_audio_description );
277 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM2_AUDIO, isom_setup_audio_description );
278 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_ADPCM17_AUDIO, isom_setup_audio_description );
279 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_GSM49_AUDIO, isom_setup_audio_description );
280 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_NONE_AUDIO, isom_setup_audio_description );
281 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_LPCM_AUDIO, isom_setup_audio_description );
282 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_SOWT_AUDIO, isom_setup_audio_description );
283 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_TWOS_AUDIO, isom_setup_audio_description );
284 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FL32_AUDIO, isom_setup_audio_description );
285 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_FL64_AUDIO, isom_setup_audio_description );
286 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_IN24_AUDIO, isom_setup_audio_description );
287 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_IN32_AUDIO, isom_setup_audio_description );
288 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_23NI_AUDIO, isom_setup_audio_description );
289 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_NOT_SPECIFIED, isom_setup_audio_description );
290 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_TX3G_TEXT, isom_add_tx3g_description );
291 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( QT_CODEC_TYPE_TEXT_TEXT, isom_add_qt_text_description );
292 #if 0
293 ADD_DESCRIPTION_SETUP_TABLE_ELEMENT( ISOM_CODEC_TYPE_MP4S_SYSTEM, isom_add_mp4s_entry );
294 #endif
296 for( int i = 0; description_setup_table[i].func; i++ )
297 if( lsmash_check_codec_type_identical( sample_type, description_setup_table[i].type ) )
299 if( (opaque_func_t)isom_setup_visual_description == description_setup_table[i].func )
300 ret = isom_setup_visual_description( stsd, sample_type, (lsmash_video_summary_t *)summary );
301 else if( (opaque_func_t)isom_setup_audio_description == description_setup_table[i].func )
302 ret = isom_setup_audio_description( stsd, sample_type, (lsmash_audio_summary_t *)summary );
303 else if( (opaque_func_t)isom_add_tx3g_description == description_setup_table[i].func )
304 ret = isom_setup_tx3g_description( stsd, (lsmash_summary_t *)summary );
305 else if( (opaque_func_t)isom_add_qt_text_description == description_setup_table[i].func )
307 isom_qt_text_entry_t *text = isom_add_qt_text_description( stsd );
308 if( text )
310 text->data_reference_index = ((lsmash_summary_t *)summary)->data_ref_index;
311 ret = 0;
314 break;
316 return ret < 0 ? 0 : list->entry_count;
319 static int isom_add_stts_entry( isom_stbl_t *stbl, uint32_t sample_delta )
321 if( !stbl
322 || !stbl->stts
323 || !stbl->stts->list )
324 return LSMASH_ERR_NAMELESS;
325 isom_stts_entry_t *data = lsmash_malloc( sizeof(isom_stts_entry_t) );
326 if( !data )
327 return LSMASH_ERR_MEMORY_ALLOC;
328 data->sample_count = 1;
329 data->sample_delta = sample_delta;
330 if( lsmash_add_entry( stbl->stts->list, data ) < 0 )
332 lsmash_free( data );
333 return LSMASH_ERR_MEMORY_ALLOC;
335 return 0;
338 static int isom_add_ctts_entry( isom_stbl_t *stbl, uint32_t sample_offset )
340 if( !stbl
341 || !stbl->ctts
342 || !stbl->ctts->list )
343 return LSMASH_ERR_NAMELESS;
344 isom_ctts_entry_t *data = lsmash_malloc( sizeof(isom_ctts_entry_t) );
345 if( !data )
346 return LSMASH_ERR_MEMORY_ALLOC;
347 data->sample_count = 1;
348 data->sample_offset = sample_offset;
349 if( lsmash_add_entry( stbl->ctts->list, data ) < 0 )
351 lsmash_free( data );
352 return LSMASH_ERR_MEMORY_ALLOC;
354 return 0;
357 static int isom_add_stsc_entry( isom_stbl_t *stbl, uint32_t first_chunk, uint32_t samples_per_chunk, uint32_t sample_description_index )
359 if( !stbl
360 || !stbl->stsc
361 || !stbl->stsc->list )
362 return LSMASH_ERR_NAMELESS;
363 isom_stsc_entry_t *data = lsmash_malloc( sizeof(isom_stsc_entry_t) );
364 if( !data )
365 return LSMASH_ERR_MEMORY_ALLOC;
366 data->first_chunk = first_chunk;
367 data->samples_per_chunk = samples_per_chunk;
368 data->sample_description_index = sample_description_index;
369 if( lsmash_add_entry( stbl->stsc->list, data ) < 0 )
371 lsmash_free( data );
372 return LSMASH_ERR_MEMORY_ALLOC;
374 return 0;
377 static int isom_add_stsz_entry( isom_stbl_t *stbl, uint32_t entry_size )
379 if( !stbl
380 || !stbl->stsz )
381 return LSMASH_ERR_NAMELESS;
382 isom_stsz_t *stsz = stbl->stsz;
383 /* retrieve initial sample_size */
384 if( stsz->sample_count == 0 )
385 stsz->sample_size = entry_size;
386 /* if it seems constant access_unit size at present, update sample_count only */
387 if( !stsz->list && stsz->sample_size == entry_size )
389 ++ stsz->sample_count;
390 return 0;
392 /* found sample_size varies, create sample_size list */
393 if( !stsz->list )
395 stsz->list = lsmash_create_entry_list();
396 if( !stsz->list )
397 return LSMASH_ERR_MEMORY_ALLOC;
398 for( uint32_t i = 0; i < stsz->sample_count; i++ )
400 isom_stsz_entry_t *data = lsmash_malloc( sizeof(isom_stsz_entry_t) );
401 if( !data )
402 return LSMASH_ERR_MEMORY_ALLOC;
403 data->entry_size = stsz->sample_size;
404 if( lsmash_add_entry( stsz->list, data ) < 0 )
406 lsmash_free( data );
407 return LSMASH_ERR_MEMORY_ALLOC;
410 stsz->sample_size = 0;
412 isom_stsz_entry_t *data = lsmash_malloc( sizeof(isom_stsz_entry_t) );
413 if( !data )
414 return LSMASH_ERR_MEMORY_ALLOC;
415 data->entry_size = entry_size;
416 if( lsmash_add_entry( stsz->list, data ) < 0 )
418 lsmash_free( data );
419 return LSMASH_ERR_MEMORY_ALLOC;
421 ++ stsz->sample_count;
422 return 0;
425 static int isom_add_stss_entry( isom_stbl_t *stbl, uint32_t sample_number )
427 if( !stbl
428 || !stbl->stss
429 || !stbl->stss->list )
430 return LSMASH_ERR_NAMELESS;
431 isom_stss_entry_t *data = lsmash_malloc( sizeof(isom_stss_entry_t) );
432 if( !data )
433 return LSMASH_ERR_MEMORY_ALLOC;
434 data->sample_number = sample_number;
435 if( lsmash_add_entry( stbl->stss->list, data ) < 0 )
437 lsmash_free( data );
438 return LSMASH_ERR_MEMORY_ALLOC;
440 return 0;
443 static int isom_add_stps_entry( isom_stbl_t *stbl, uint32_t sample_number )
445 if( !stbl
446 || !stbl->stps
447 || !stbl->stps->list )
448 return LSMASH_ERR_NAMELESS;
449 isom_stps_entry_t *data = lsmash_malloc( sizeof(isom_stps_entry_t) );
450 if( !data )
451 return LSMASH_ERR_MEMORY_ALLOC;
452 data->sample_number = sample_number;
453 if( lsmash_add_entry( stbl->stps->list, data ) < 0 )
455 lsmash_free( data );
456 return LSMASH_ERR_MEMORY_ALLOC;
458 return 0;
461 /* Between ISOBMFF and QTFF, the most significant 2-bit has different meaning.
462 * For the maximum compatibility, we set 0 to the most significant 2-bit when compatible with
463 * both ISOBMFF with AVCFF extensions and QTFF.
464 * compatibility == 0 -> neither AVCFF extensions nor QTFF compatible
465 * compatibility == 1 -> AVCFF extensions compatible
466 * compatibility == 2 -> QTFF compatible
467 * compatibility == 3 -> both AVCFF extensions and QTFF compatible */
468 static int isom_add_sdtp_entry( isom_box_t *parent, lsmash_sample_property_t *prop, int compatibility )
470 if( !prop || !parent )
471 return LSMASH_ERR_NAMELESS;
472 isom_sdtp_t *sdtp = NULL;
473 if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) )
474 sdtp = ((isom_stbl_t *)parent)->sdtp;
475 else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) )
476 sdtp = ((isom_traf_t *)parent)->sdtp;
477 else
478 assert( 0 );
479 if( !sdtp
480 || !sdtp->list )
481 return LSMASH_ERR_NAMELESS;
482 isom_sdtp_entry_t *data = lsmash_malloc( sizeof(isom_sdtp_entry_t) );
483 if( !data )
484 return LSMASH_ERR_MEMORY_ALLOC;
485 if( compatibility == 1 )
486 data->is_leading = prop->leading & 0x03;
487 else if( compatibility == 2 )
488 data->is_leading = prop->allow_earlier & 0x03;
489 else
491 data->is_leading = 0;
492 assert( compatibility == 3 );
494 data->sample_depends_on = prop->independent & 0x03;
495 data->sample_is_depended_on = prop->disposable & 0x03;
496 data->sample_has_redundancy = prop->redundant & 0x03;
497 if( lsmash_add_entry( sdtp->list, data ) < 0 )
499 lsmash_free( data );
500 return LSMASH_ERR_MEMORY_ALLOC;
502 return 0;
505 static int isom_add_co64_entry( isom_stbl_t *stbl, uint64_t chunk_offset )
507 if( !stbl
508 || !stbl->stco
509 || !stbl->stco->list )
510 return LSMASH_ERR_NAMELESS;
511 isom_co64_entry_t *data = lsmash_malloc( sizeof(isom_co64_entry_t) );
512 if( !data )
513 return LSMASH_ERR_MEMORY_ALLOC;
514 data->chunk_offset = chunk_offset;
515 if( lsmash_add_entry( stbl->stco->list, data ) < 0 )
517 lsmash_free( data );
518 return LSMASH_ERR_MEMORY_ALLOC;
520 return 0;
523 static int isom_convert_stco_to_co64( isom_stbl_t *stbl )
525 /* backup stco */
526 int err = 0;
527 isom_stco_t *stco = stbl->stco;
528 stbl->stco = NULL;
529 if( !isom_add_co64( stbl ) )
531 err = LSMASH_ERR_NAMELESS;
532 goto fail;
534 /* move chunk_offset to co64 from stco */
535 for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next )
537 isom_stco_entry_t *data = (isom_stco_entry_t*)entry->data;
538 if( (err = isom_add_co64_entry( stbl, data->chunk_offset )) < 0 )
539 goto fail;
541 fail:
542 isom_remove_box_by_itself( stco );
543 return err;
546 static int isom_add_stco_entry( isom_stbl_t *stbl, uint64_t chunk_offset )
548 if( !stbl
549 || !stbl->stco
550 || !stbl->stco->list )
551 return LSMASH_ERR_NAMELESS;
552 if( stbl->stco->large_presentation )
553 return isom_add_co64_entry( stbl, chunk_offset );
554 if( chunk_offset > UINT32_MAX )
556 int err = isom_convert_stco_to_co64( stbl );
557 if( err < 0 )
558 return err;
559 return isom_add_co64_entry( stbl, chunk_offset );
561 isom_stco_entry_t *data = lsmash_malloc( sizeof(isom_stco_entry_t) );
562 if( !data )
563 return LSMASH_ERR_MEMORY_ALLOC;
564 data->chunk_offset = (uint32_t)chunk_offset;
565 if( lsmash_add_entry( stbl->stco->list, data ) < 0 )
567 lsmash_free( data );
568 return LSMASH_ERR_MEMORY_ALLOC;
570 return 0;
573 static isom_sgpd_t *isom_get_sample_group_description_common( lsmash_entry_list_t *list, uint32_t grouping_type )
575 for( lsmash_entry_t *entry = list->head; entry; entry = entry->next )
577 isom_sgpd_t *sgpd = (isom_sgpd_t *)entry->data;
578 if( !sgpd
579 || !sgpd->list )
580 return NULL;
581 if( sgpd->grouping_type == grouping_type )
582 return sgpd;
584 return NULL;
587 static isom_sbgp_t *isom_get_sample_to_group_common( lsmash_entry_list_t *list, uint32_t grouping_type )
589 for( lsmash_entry_t *entry = list->head; entry; entry = entry->next )
591 isom_sbgp_t *sbgp = (isom_sbgp_t *)entry->data;
592 if( !sbgp
593 || !sbgp->list )
594 return NULL;
595 if( sbgp->grouping_type == grouping_type )
596 return sbgp;
598 return NULL;
601 isom_sgpd_t *isom_get_sample_group_description( isom_stbl_t *stbl, uint32_t grouping_type )
603 return isom_get_sample_group_description_common( &stbl->sgpd_list, grouping_type );
606 isom_sbgp_t *isom_get_sample_to_group( isom_stbl_t *stbl, uint32_t grouping_type )
608 return isom_get_sample_to_group_common( &stbl->sbgp_list, grouping_type );
611 isom_sgpd_t *isom_get_roll_recovery_sample_group_description( lsmash_entry_list_t *list )
613 isom_sgpd_t *sgpd;
614 if( (sgpd = isom_get_sample_group_description_common( list, ISOM_GROUP_TYPE_ROLL ))
615 || (sgpd = isom_get_sample_group_description_common( list, ISOM_GROUP_TYPE_PROL )) )
616 return sgpd;
617 return NULL;
620 isom_sbgp_t *isom_get_roll_recovery_sample_to_group( lsmash_entry_list_t *list )
622 isom_sbgp_t *sbgp;
623 if( (sbgp = isom_get_sample_to_group_common( list, ISOM_GROUP_TYPE_ROLL ))
624 || (sbgp = isom_get_sample_to_group_common( list, ISOM_GROUP_TYPE_PROL )) )
625 return sbgp;
626 return NULL;
629 isom_sgpd_t *isom_get_fragment_sample_group_description( isom_traf_t *traf, uint32_t grouping_type )
631 return isom_get_sample_group_description_common( &traf->sgpd_list, grouping_type );
634 isom_sbgp_t *isom_get_fragment_sample_to_group( isom_traf_t *traf, uint32_t grouping_type )
636 return isom_get_sample_to_group_common( &traf->sbgp_list, grouping_type );
639 static isom_rap_entry_t *isom_add_rap_group_entry( isom_sgpd_t *sgpd )
641 if( !sgpd )
642 return NULL;
643 isom_rap_entry_t *data = lsmash_malloc( sizeof(isom_rap_entry_t) );
644 if( !data )
645 return NULL;
646 data->description_length = 0;
647 data->num_leading_samples_known = 0;
648 data->num_leading_samples = 0;
649 if( lsmash_add_entry( sgpd->list, data ) < 0 )
651 lsmash_free( data );
652 return NULL;
654 return data;
657 static isom_roll_entry_t *isom_add_roll_group_entry( isom_sgpd_t *sgpd, int16_t roll_distance )
659 if( !sgpd )
660 return NULL;
661 isom_roll_entry_t *data = lsmash_malloc( sizeof(isom_roll_entry_t) );
662 if( !data )
663 return NULL;
664 data->description_length = 0;
665 data->roll_distance = roll_distance;
666 if( lsmash_add_entry( sgpd->list, data ) < 0 )
668 lsmash_free( data );
669 return NULL;
671 return data;
674 static isom_group_assignment_entry_t *isom_add_group_assignment_entry( isom_sbgp_t *sbgp, uint32_t sample_count, uint32_t group_description_index )
676 if( !sbgp )
677 return NULL;
678 isom_group_assignment_entry_t *data = lsmash_malloc( sizeof(isom_group_assignment_entry_t) );
679 if( !data )
680 return NULL;
681 data->sample_count = sample_count;
682 data->group_description_index = group_description_index;
683 if( lsmash_add_entry( sbgp->list, data ) < 0 )
685 lsmash_free( data );
686 return NULL;
688 return data;
691 uint32_t isom_get_sample_count( isom_trak_t *trak )
693 if( !trak
694 || !trak->mdia
695 || !trak->mdia->minf
696 || !trak->mdia->minf->stbl
697 || !trak->mdia->minf->stbl->stsz )
698 return 0;
699 return trak->mdia->minf->stbl->stsz->sample_count;
702 static uint64_t isom_get_dts( isom_stts_t *stts, uint32_t sample_number )
704 if( !stts
705 || !stts->list )
706 return 0;
707 uint64_t dts = 0;
708 uint32_t i = 1;
709 lsmash_entry_t *entry;
710 isom_stts_entry_t *data;
711 for( entry = stts->list->head; entry; entry = entry->next )
713 data = (isom_stts_entry_t *)entry->data;
714 if( !data )
715 return 0;
716 if( i + data->sample_count > sample_number )
717 break;
718 dts += (uint64_t)data->sample_delta * data->sample_count;
719 i += data->sample_count;
721 if( !entry )
722 return 0;
723 dts += (uint64_t)data->sample_delta * (sample_number - i);
724 return dts;
727 #if 0
728 static uint64_t isom_get_cts( isom_stts_t *stts, isom_ctts_t *ctts, uint32_t sample_number )
730 if( !stts
731 || !stts->list )
732 return 0;
733 if( !ctts )
734 return isom_get_dts( stts, sample_number );
735 uint32_t i = 1; /* This can be 0 (and then condition below shall be changed) but I dare use same algorithm with isom_get_dts. */
736 lsmash_entry_t *entry;
737 isom_ctts_entry_t *data;
738 if( sample_number == 0 )
739 return 0;
740 for( entry = ctts->list->head; entry; entry = entry->next )
742 data = (isom_ctts_entry_t *)entry->data;
743 if( !data )
744 return 0;
745 if( i + data->sample_count > sample_number )
746 break;
747 i += data->sample_count;
749 if( !entry )
750 return 0;
751 return isom_get_dts( stts, sample_number ) + data->sample_offset;
753 #endif
755 static int isom_replace_last_sample_delta( isom_stbl_t *stbl, uint32_t sample_delta )
757 if( !stbl
758 || !stbl->stts
759 || !stbl->stts->list
760 || !stbl->stts->list->tail
761 || !stbl->stts->list->tail->data )
762 return LSMASH_ERR_NAMELESS;
763 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stbl->stts->list->tail->data;
764 if( sample_delta != last_stts_data->sample_delta )
766 if( last_stts_data->sample_count > 1 )
768 last_stts_data->sample_count -= 1;
769 int err = isom_add_stts_entry( stbl, sample_delta );
770 if( err < 0 )
771 return err;
773 else
774 last_stts_data->sample_delta = sample_delta;
776 return 0;
779 static int isom_update_mdhd_duration( isom_trak_t *trak, uint32_t last_sample_delta )
781 if( !trak
782 || !trak->file
783 || !trak->cache
784 || !trak->mdia
785 || !trak->mdia->mdhd
786 || !trak->mdia->minf
787 || !trak->mdia->minf->stbl
788 || !trak->mdia->minf->stbl->stts
789 || !trak->mdia->minf->stbl->stts->list )
790 return LSMASH_ERR_INVALID_DATA;
791 lsmash_file_t *file = trak->file;
792 isom_mdhd_t *mdhd = trak->mdia->mdhd;
793 isom_stbl_t *stbl = trak->mdia->minf->stbl;
794 isom_stts_t *stts = stbl->stts;
795 isom_ctts_t *ctts = stbl->ctts;
796 isom_cslg_t *cslg = stbl->cslg;
797 mdhd->duration = 0;
798 uint32_t sample_count = isom_get_sample_count( trak );
799 if( sample_count == 0 )
801 /* Return error if non-fragmented movie has no samples. */
802 if( !file->fragment && !stts->list->entry_count )
803 return LSMASH_ERR_INVALID_DATA;
804 return 0;
806 /* Now we have at least 1 sample, so do stts_entry. */
807 lsmash_entry_t *last_stts = stts->list->tail;
808 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)last_stts->data;
809 if( sample_count == 1 )
810 mdhd->duration = last_stts_data->sample_delta;
811 /* Now we have at least 2 samples,
812 * but dunno whether 1 stts_entry which has 2 samples or 2 stts_entry which has 1 samle each. */
813 else if( !ctts )
815 /* use dts instead of cts */
816 mdhd->duration = isom_get_dts( stts, sample_count );
817 int err;
818 if( last_sample_delta )
820 mdhd->duration += last_sample_delta;
821 if( (err = isom_replace_last_sample_delta( stbl, last_sample_delta )) < 0 )
822 return err;
824 else if( last_stts_data->sample_count > 1 )
825 mdhd->duration += last_stts_data->sample_delta; /* no need to update last_stts_data->sample_delta */
826 else
828 /* Remove the last entry. */
829 if( (err = lsmash_remove_entry_tail( stts->list, NULL )) < 0 )
830 return err;
831 /* copy the previous sample_delta. */
832 ++ ((isom_stts_entry_t *)stts->list->tail->data)->sample_count;
833 mdhd->duration += ((isom_stts_entry_t *)stts->list->tail->data)->sample_delta;
836 else
838 if( !ctts->list
839 || ctts->list->entry_count == 0 )
840 return LSMASH_ERR_INVALID_DATA;
841 uint64_t dts = 0;
842 uint64_t max_cts = 0;
843 uint64_t max2_cts = 0;
844 uint64_t min_cts = UINT64_MAX;
845 uint32_t max_offset = 0;
846 uint32_t min_offset = UINT32_MAX;
847 int32_t ctd_shift = trak->cache->timestamp.ctd_shift;
848 uint32_t j = 0;
849 uint32_t k = 0;
850 lsmash_entry_t *stts_entry = stts->list->head;
851 lsmash_entry_t *ctts_entry = ctts->list->head;
852 for( uint32_t i = 0; i < sample_count; i++ )
854 if( !ctts_entry || !stts_entry )
855 return LSMASH_ERR_INVALID_DATA;
856 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
857 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
858 if( !stts_data || !ctts_data )
859 return LSMASH_ERR_INVALID_DATA;
860 uint64_t cts;
861 if( ctd_shift )
863 /* Anyway, add composition to decode timeline shift for calculating maximum and minimum CTS correctly. */
864 int32_t sample_offset = (int32_t)ctts_data->sample_offset;
865 cts = dts + sample_offset + ctd_shift;
866 max_offset = LSMASH_MAX( (int32_t)max_offset, sample_offset );
867 min_offset = LSMASH_MIN( (int32_t)min_offset, sample_offset );
869 else
871 cts = dts + ctts_data->sample_offset;
872 max_offset = LSMASH_MAX( max_offset, ctts_data->sample_offset );
873 min_offset = LSMASH_MIN( min_offset, ctts_data->sample_offset );
875 min_cts = LSMASH_MIN( min_cts, cts );
876 if( max_cts < cts )
878 max2_cts = max_cts;
879 max_cts = cts;
881 else if( max2_cts < cts )
882 max2_cts = cts;
883 dts += stts_data->sample_delta;
884 /* If finished sample_count of current entry, move to next. */
885 if( ++j == ctts_data->sample_count )
887 ctts_entry = ctts_entry->next;
888 j = 0;
890 if( ++k == stts_data->sample_count )
892 stts_entry = stts_entry->next;
893 k = 0;
896 dts -= last_stts_data->sample_delta;
897 if( file->fragment )
898 /* Overall presentation is extended exceeding this initial movie.
899 * So, any players shall display the movie exceeding the durations
900 * indicated in Movie Header Box, Track Header Boxes and Media Header Boxes.
901 * Samples up to the duration indicated in Movie Extends Header Box shall be displayed.
902 * In the absence of Movie Extends Header Box, all samples shall be displayed. */
903 mdhd->duration += dts + last_sample_delta;
904 else
906 if( !last_sample_delta )
908 /* The spec allows an arbitrary value for the duration of the last sample. So, we pick last-1 sample's. */
909 last_sample_delta = max_cts - max2_cts;
911 mdhd->duration = max_cts - min_cts + last_sample_delta;
912 /* To match dts and media duration, update stts and mdhd relatively. */
913 if( mdhd->duration > dts )
914 last_sample_delta = mdhd->duration - dts;
915 else
916 mdhd->duration = dts + last_sample_delta; /* media duration must not less than last dts. */
918 int err = isom_replace_last_sample_delta( stbl, last_sample_delta );
919 if( err < 0 )
920 return err;
921 /* Explicit composition information and timeline shifting */
922 if( cslg || file->qt_compatible || file->max_isom_version >= 4 )
924 if( ctd_shift )
926 /* Remove composition to decode timeline shift. */
927 max_cts -= ctd_shift;
928 max2_cts -= ctd_shift;
929 min_cts -= ctd_shift;
931 int64_t composition_end_time = max_cts + (max_cts - max2_cts);
932 if( !file->fragment
933 && ((int32_t)min_offset <= INT32_MAX) && ((int32_t)max_offset <= INT32_MAX)
934 && ((int64_t)min_cts <= INT32_MAX) && (composition_end_time <= INT32_MAX) )
936 if( !cslg )
938 if( !isom_add_cslg( trak->mdia->minf->stbl ) )
939 return LSMASH_ERR_NAMELESS;
940 cslg = stbl->cslg;
942 cslg->compositionToDTSShift = ctd_shift;
943 cslg->leastDecodeToDisplayDelta = min_offset;
944 cslg->greatestDecodeToDisplayDelta = max_offset;
945 cslg->compositionStartTime = min_cts;
946 cslg->compositionEndTime = composition_end_time;
948 else
949 isom_remove_box_by_itself( cslg );
952 if( mdhd->duration > UINT32_MAX && !file->undefined_64_ver )
953 mdhd->version = 1;
954 return 0;
957 static int isom_update_mvhd_duration( isom_moov_t *moov )
959 if( !moov
960 || !moov->mvhd
961 || !moov->mvhd->file )
962 return LSMASH_ERR_INVALID_DATA;
963 isom_mvhd_t *mvhd = moov->mvhd;
964 mvhd->duration = 0;
965 for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next )
967 /* We pick maximum track duration as movie duration. */
968 isom_trak_t *data = (isom_trak_t *)entry->data;
969 if( !data
970 || !data->tkhd )
971 return LSMASH_ERR_INVALID_DATA;
972 mvhd->duration = entry != moov->trak_list.head
973 ? LSMASH_MAX( mvhd->duration, data->tkhd->duration )
974 : data->tkhd->duration;
976 if( mvhd->duration > UINT32_MAX && !mvhd->file->undefined_64_ver )
977 mvhd->version = 1;
978 return 0;
981 int isom_update_tkhd_duration( isom_trak_t *trak )
983 if( !trak
984 || !trak->tkhd
985 || !trak->file
986 || !trak->file->moov )
987 return LSMASH_ERR_INVALID_DATA;
988 lsmash_file_t *file = trak->file;
989 isom_tkhd_t *tkhd = trak->tkhd;
990 tkhd->duration = 0;
991 if( file->fragment
992 || !trak->edts
993 || !trak->edts->elst )
995 /* If this presentation might be extended or this track doesn't have edit list, calculate track duration from media duration. */
996 if( !trak->mdia
997 || !trak->mdia->mdhd
998 || !file->moov->mvhd
999 || trak->mdia->mdhd->timescale == 0 )
1000 return LSMASH_ERR_INVALID_DATA;
1001 if( trak->mdia->mdhd->duration == 0 )
1003 int err = isom_update_mdhd_duration( trak, 0 );
1004 if( err < 0 )
1005 return err;
1007 tkhd->duration = trak->mdia->mdhd->duration * ((double)file->moov->mvhd->timescale / trak->mdia->mdhd->timescale);
1009 else
1011 /* If the presentation won't be extended and this track has any edit, then track duration is just the sum of the segment_duartions. */
1012 for( lsmash_entry_t *entry = trak->edts->elst->list->head; entry; entry = entry->next )
1014 isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data;
1015 if( !data )
1016 return LSMASH_ERR_INVALID_DATA;
1017 tkhd->duration += data->segment_duration;
1020 if( tkhd->duration > UINT32_MAX && !file->undefined_64_ver )
1021 tkhd->version = 1;
1022 if( !file->fragment && tkhd->duration == 0 )
1023 tkhd->duration = tkhd->version == 1 ? 0xffffffffffffffff : 0xffffffff;
1024 return isom_update_mvhd_duration( file->moov );
1027 int lsmash_update_track_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta )
1029 if( isom_check_initializer_present( root ) < 0 )
1030 return LSMASH_ERR_FUNCTION_PARAM;
1031 lsmash_file_t *file = root->file;
1032 isom_trak_t *trak = isom_get_trak( file, track_ID );
1033 if( !trak )
1034 return LSMASH_ERR_NAMELESS;
1035 int err = isom_update_mdhd_duration( trak, last_sample_delta );
1036 if( err < 0 )
1037 return err;
1038 /* If the presentation won't be extended and this track has any edit, we don't change or update duration in tkhd. */
1039 if( !file->fragment && trak->edts && trak->edts->elst )
1040 err = isom_update_mvhd_duration( file->moov ); /* Only update movie duration. */
1041 else
1042 err = isom_update_tkhd_duration( trak ); /* Also update movie duration internally. */
1043 return err;
1046 static inline int isom_increment_sample_number_in_entry( uint32_t *sample_number_in_entry, uint32_t sample_count_in_entry, lsmash_entry_t **entry )
1048 if( *sample_number_in_entry != sample_count_in_entry )
1050 *sample_number_in_entry += 1;
1051 return 0;
1053 /* Precede the next entry. */
1054 *sample_number_in_entry = 1;
1055 if( *entry )
1057 *entry = (*entry)->next;
1058 if( *entry && !(*entry)->data )
1059 return LSMASH_ERR_INVALID_DATA;
1061 return 0;
1064 static int isom_calculate_bitrate_description( isom_mdia_t *mdia, uint32_t *bufferSizeDB, uint32_t *maxBitrate, uint32_t *avgBitrate, uint32_t sample_description_index )
1066 isom_stsz_t *stsz = mdia->minf->stbl->stsz;
1067 lsmash_entry_t *stsz_entry = stsz->list ? stsz->list->head : NULL;
1068 lsmash_entry_t *stts_entry = mdia->minf->stbl->stts->list->head;
1069 lsmash_entry_t *stsc_entry = NULL;
1070 lsmash_entry_t *next_stsc_entry = mdia->minf->stbl->stsc->list->head;
1071 isom_stts_entry_t *stts_data = NULL;
1072 isom_stsc_entry_t *stsc_data = NULL;
1073 if( next_stsc_entry && !next_stsc_entry->data )
1074 return LSMASH_ERR_INVALID_DATA;
1075 uint32_t rate = 0;
1076 uint64_t dts = 0;
1077 uint32_t time_wnd = 0;
1078 uint32_t timescale = mdia->mdhd->timescale;
1079 uint32_t chunk_number = 0;
1080 uint32_t sample_number_in_stts = 1;
1081 uint32_t sample_number_in_chunk = 1;
1082 *bufferSizeDB = 0;
1083 *maxBitrate = 0;
1084 *avgBitrate = 0;
1085 while( stts_entry )
1087 int err;
1088 if( !stsc_data || sample_number_in_chunk == stsc_data->samples_per_chunk )
1090 /* Move the next chunk. */
1091 sample_number_in_chunk = 1;
1092 ++chunk_number;
1093 /* Check if the next entry is broken. */
1094 while( next_stsc_entry && ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk < chunk_number )
1096 /* Just skip broken next entry. */
1097 next_stsc_entry = next_stsc_entry->next;
1098 if( next_stsc_entry && !next_stsc_entry->data )
1099 return LSMASH_ERR_INVALID_DATA;
1101 /* Check if the next chunk belongs to the next sequence of chunks. */
1102 if( next_stsc_entry && ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk == chunk_number )
1104 stsc_entry = next_stsc_entry;
1105 next_stsc_entry = next_stsc_entry->next;
1106 if( next_stsc_entry && !next_stsc_entry->data )
1107 return LSMASH_ERR_INVALID_DATA;
1108 stsc_data = (isom_stsc_entry_t *)stsc_entry->data;
1109 /* Check if the next contiguous chunks belong to given sample description. */
1110 if( stsc_data->sample_description_index != sample_description_index )
1112 /* Skip chunks which don't belong to given sample description. */
1113 uint32_t number_of_skips = 0;
1114 uint32_t first_chunk = stsc_data->first_chunk;
1115 uint32_t samples_per_chunk = stsc_data->samples_per_chunk;
1116 while( next_stsc_entry )
1118 if( ((isom_stsc_entry_t *)next_stsc_entry->data)->sample_description_index != sample_description_index )
1120 stsc_data = (isom_stsc_entry_t *)next_stsc_entry->data;
1121 number_of_skips += (stsc_data->first_chunk - first_chunk) * samples_per_chunk;
1122 first_chunk = stsc_data->first_chunk;
1123 samples_per_chunk = stsc_data->samples_per_chunk;
1125 else if( ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk <= first_chunk )
1126 ; /* broken entry */
1127 else
1128 break;
1129 /* Just skip the next entry. */
1130 next_stsc_entry = next_stsc_entry->next;
1131 if( next_stsc_entry && !next_stsc_entry->data )
1132 return LSMASH_ERR_INVALID_DATA;
1134 if( !next_stsc_entry )
1135 break; /* There is no more chunks which don't belong to given sample description. */
1136 number_of_skips += (((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk - first_chunk) * samples_per_chunk;
1137 for( uint32_t i = 0; i < number_of_skips; i++ )
1139 if( stsz->list )
1141 if( !stsz_entry )
1142 break;
1143 stsz_entry = stsz_entry->next;
1145 if( !stts_entry )
1146 break;
1147 if( (err = isom_increment_sample_number_in_entry( &sample_number_in_stts,
1148 ((isom_stts_entry_t *)stts_entry->data)->sample_count,
1149 &stts_entry )) < 0 )
1150 return err;
1152 if( (stsz->list && !stsz_entry) || !stts_entry )
1153 break;
1154 chunk_number = stsc_data->first_chunk;
1158 else
1159 ++sample_number_in_chunk;
1160 /* Get current sample's size. */
1161 uint32_t size;
1162 if( stsz->list )
1164 if( !stsz_entry )
1165 break;
1166 isom_stsz_entry_t *stsz_data = (isom_stsz_entry_t *)stsz_entry->data;
1167 if( !stsz_data )
1168 return LSMASH_ERR_INVALID_DATA;
1169 size = stsz_data->entry_size;
1170 stsz_entry = stsz_entry->next;
1172 else
1173 size = stsz->sample_size;
1174 /* Get current sample's DTS. */
1175 if( stts_data )
1176 dts += stts_data->sample_delta;
1177 stts_data = (isom_stts_entry_t *)stts_entry->data;
1178 if( !stts_data )
1179 return LSMASH_ERR_INVALID_DATA;
1180 if( (err = isom_increment_sample_number_in_entry( &sample_number_in_stts, stts_data->sample_count, &stts_entry )) < 0 )
1181 return err;
1182 /* Calculate bitrate description. */
1183 if( *bufferSizeDB < size )
1184 *bufferSizeDB = size;
1185 *avgBitrate += size;
1186 rate += size;
1187 if( dts > time_wnd + timescale )
1189 if( rate > *maxBitrate )
1190 *maxBitrate = rate;
1191 time_wnd = dts;
1192 rate = 0;
1195 double duration = (double)mdia->mdhd->duration / timescale;
1196 *avgBitrate = (uint32_t)(*avgBitrate / duration);
1197 if( *maxBitrate == 0 )
1198 *maxBitrate = *avgBitrate;
1199 /* Convert to bits per second. */
1200 *maxBitrate *= 8;
1201 *avgBitrate *= 8;
1202 return 0;
1205 int isom_update_bitrate_description( isom_mdia_t *mdia )
1207 if( !mdia
1208 || !mdia->mdhd
1209 || !mdia->minf
1210 || !mdia->minf->stbl )
1211 return LSMASH_ERR_INVALID_DATA;
1212 isom_stbl_t *stbl = mdia->minf->stbl;
1213 if( !stbl->stsd
1214 || !stbl->stsz
1215 || !stbl->stsc || !stbl->stsc->list
1216 || !stbl->stts || !stbl->stts->list )
1217 return LSMASH_ERR_INVALID_DATA;
1218 uint32_t sample_description_index = 0;
1219 for( lsmash_entry_t *entry = stbl->stsd->list.head; entry; entry = entry->next )
1221 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)entry->data;
1222 if( !sample_entry )
1223 return LSMASH_ERR_INVALID_DATA;
1224 ++sample_description_index;
1225 int err;
1226 uint32_t bufferSizeDB;
1227 uint32_t maxBitrate;
1228 uint32_t avgBitrate;
1229 /* set bitrate info */
1230 lsmash_codec_type_t sample_type = sample_entry->type;
1231 if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO )
1232 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC2_VIDEO )
1233 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC3_VIDEO )
1234 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC4_VIDEO )
1235 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_HVC1_VIDEO )
1236 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_HEV1_VIDEO ) )
1238 isom_visual_entry_t *stsd_data = (isom_visual_entry_t *)sample_entry;
1239 if( !stsd_data )
1240 return LSMASH_ERR_INVALID_DATA;
1241 isom_btrt_t *btrt = (isom_btrt_t *)isom_get_extension_box_format( &stsd_data->extensions, ISOM_BOX_TYPE_BTRT );
1242 if( btrt )
1244 if( (err = isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index )) < 0 )
1245 return err;
1246 btrt->bufferSizeDB = bufferSizeDB;
1247 btrt->maxBitrate = maxBitrate;
1248 btrt->avgBitrate = avgBitrate;
1251 else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4V_VIDEO ) )
1253 isom_visual_entry_t *stsd_data = (isom_visual_entry_t *)sample_entry;
1254 if( !stsd_data )
1255 return LSMASH_ERR_INVALID_DATA;
1256 isom_esds_t *esds = (isom_esds_t *)isom_get_extension_box_format( &stsd_data->extensions, ISOM_BOX_TYPE_ESDS );
1257 if( !esds || !esds->ES )
1258 return LSMASH_ERR_INVALID_DATA;
1259 if( (err = isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index )) < 0 )
1260 return err;
1261 /* FIXME: avgBitrate is 0 only if VBR in proper. */
1262 if( (err = mp4sys_update_DecoderConfigDescriptor( esds->ES, bufferSizeDB, maxBitrate, 0 )) < 0 )
1263 return err;
1265 else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) )
1267 isom_audio_entry_t *stsd_data = (isom_audio_entry_t *)sample_entry;
1268 if( !stsd_data )
1269 return LSMASH_ERR_INVALID_DATA;
1270 isom_esds_t *esds = NULL;
1271 if( ((isom_audio_entry_t *)sample_entry)->version )
1273 /* MPEG-4 Audio in QTFF */
1274 isom_wave_t *wave = (isom_wave_t *)isom_get_extension_box_format( &stsd_data->extensions, QT_BOX_TYPE_WAVE );
1275 if( !wave )
1276 return LSMASH_ERR_INVALID_DATA;
1277 esds = (isom_esds_t *)isom_get_extension_box_format( &wave->extensions, ISOM_BOX_TYPE_ESDS );
1279 else
1280 esds = (isom_esds_t *)isom_get_extension_box_format( &stsd_data->extensions, ISOM_BOX_TYPE_ESDS );
1281 if( !esds || !esds->ES )
1282 return LSMASH_ERR_INVALID_DATA;
1283 if( (err = isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index )) < 0 )
1284 return err;
1285 /* FIXME: avgBitrate is 0 only if VBR in proper. */
1286 if( (err = mp4sys_update_DecoderConfigDescriptor( esds->ES, bufferSizeDB, maxBitrate, 0 )) < 0 )
1287 return err;
1289 else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_ALAC_AUDIO )
1290 || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_ALAC_AUDIO ) )
1292 isom_audio_entry_t *alac = (isom_audio_entry_t *)sample_entry;
1293 if( !alac )
1294 return LSMASH_ERR_INVALID_DATA;
1295 uint8_t *exdata = NULL;
1296 uint32_t exdata_size = 0;
1297 isom_box_t *alac_ext = isom_get_extension_box( &alac->extensions, QT_BOX_TYPE_WAVE );
1298 if( alac_ext )
1300 /* Apple Lossless Audio inside QuickTime file format
1301 * Though average bitrate field we found is always set to 0 apparently,
1302 * we set up maxFrameBytes and avgBitRate fields. */
1303 if( alac_ext->manager & LSMASH_BINARY_CODED_BOX )
1304 exdata = isom_get_child_box_position( alac_ext->binary, alac_ext->size, QT_BOX_TYPE_ALAC, &exdata_size );
1305 else
1307 isom_wave_t *wave = (isom_wave_t *)alac_ext;
1308 isom_box_t *wave_ext = isom_get_extension_box( &wave->extensions, QT_BOX_TYPE_ALAC );
1309 if( !wave_ext || !(wave_ext->manager & LSMASH_BINARY_CODED_BOX) )
1310 return LSMASH_ERR_INVALID_DATA;
1311 exdata = wave_ext->binary;
1312 exdata_size = wave_ext->size;
1315 else
1317 /* Apple Lossless Audio inside ISO Base Media file format */
1318 isom_box_t *ext = isom_get_extension_box( &alac->extensions, ISOM_BOX_TYPE_ALAC );
1319 if( !ext || !(ext->manager & LSMASH_BINARY_CODED_BOX) )
1320 return LSMASH_ERR_INVALID_DATA;
1321 exdata = ext->binary;
1322 exdata_size = ext->size;
1324 if( !exdata || exdata_size < 36 )
1325 return LSMASH_ERR_INVALID_DATA;
1326 if( (err = isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index )) < 0 )
1327 return err;
1328 exdata += 24;
1329 /* maxFrameBytes */
1330 LSMASH_SET_BE32( &exdata[0], bufferSizeDB );
1331 /* avgBitRate */
1332 LSMASH_SET_BE32( &exdata[4], avgBitrate );
1334 else if( isom_is_waveform_audio( sample_type ) )
1336 isom_box_t *ext = isom_get_extension_box( &sample_entry->extensions, QT_BOX_TYPE_WAVE );
1337 if( !ext )
1338 return LSMASH_ERR_INVALID_DATA;
1339 uint8_t *exdata = NULL;
1340 uint32_t exdata_size = 0;
1341 if( ext->manager & LSMASH_BINARY_CODED_BOX )
1342 exdata = isom_get_child_box_position( ext->binary, ext->size, sample_type, &exdata_size );
1343 else
1345 isom_wave_t *wave = (isom_wave_t *)ext;
1346 isom_box_t *wave_ext = isom_get_extension_box( &wave->extensions, sample_type );
1347 if( !wave_ext || !(wave_ext->manager & LSMASH_BINARY_CODED_BOX) )
1348 return LSMASH_ERR_INVALID_DATA;
1349 exdata = wave_ext->binary;
1350 exdata_size = wave_ext->size;
1352 /* Check whether exdata is valid or not. */
1353 if( !exdata || exdata_size < ISOM_BASEBOX_COMMON_SIZE + 18 )
1354 return LSMASH_ERR_INVALID_DATA;
1355 exdata += ISOM_BASEBOX_COMMON_SIZE;
1356 uint16_t cbSize = LSMASH_GET_LE16( &exdata[16] );
1357 if( exdata_size < ISOM_BASEBOX_COMMON_SIZE + 18 + cbSize )
1358 return LSMASH_ERR_INVALID_DATA;
1359 /* WAVEFORMATEX.nAvgBytesPerSec */
1360 if( (err = isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index )) < 0 )
1361 return err;
1362 uint32_t nAvgBytesPerSec = avgBitrate / 8;
1363 LSMASH_SET_LE32( &exdata[8], nAvgBytesPerSec );
1364 if( lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_FULLMP3_AUDIO )
1365 || lsmash_check_codec_type_identical( sample_type, QT_CODEC_TYPE_MP3_AUDIO ) )
1367 /* MPEGLAYER3WAVEFORMAT.nBlockSize */
1368 uint32_t nSamplesPerSec = LSMASH_GET_LE32( &exdata[ 4] );
1369 uint16_t nFramesPerBlock = LSMASH_GET_LE16( &exdata[26] );
1370 uint16_t padding = 0; /* FIXME? */
1371 uint16_t nBlockSize = (144 * (avgBitrate / nSamplesPerSec) + padding) * nFramesPerBlock;
1372 LSMASH_SET_LE16( &exdata[24], nBlockSize );
1375 else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSC_AUDIO )
1376 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSE_AUDIO )
1377 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSH_AUDIO )
1378 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_DTSL_AUDIO ) )
1380 isom_audio_entry_t *dts_audio = (isom_audio_entry_t *)sample_entry;
1381 if( !dts_audio )
1382 return LSMASH_ERR_INVALID_DATA;
1383 isom_box_t *ext = isom_get_extension_box( &dts_audio->extensions, ISOM_BOX_TYPE_DDTS );
1384 if( !(ext && (ext->manager & LSMASH_BINARY_CODED_BOX) && ext->binary && ext->size >= 28) )
1385 return LSMASH_ERR_INVALID_DATA;
1386 if( (err = isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index )) < 0 )
1387 return err;
1388 if( !stbl->stsz->list )
1389 maxBitrate = avgBitrate;
1390 uint8_t *exdata = ext->binary + 12;
1391 LSMASH_SET_BE32( &exdata[0], maxBitrate );
1392 LSMASH_SET_BE32( &exdata[4], avgBitrate );
1394 else if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_EC_3_AUDIO ) )
1396 isom_audio_entry_t *eac3 = (isom_audio_entry_t *)sample_entry;
1397 if( !eac3 )
1398 return LSMASH_ERR_INVALID_DATA;
1399 isom_box_t *ext = isom_get_extension_box( &eac3->extensions, ISOM_BOX_TYPE_DEC3 );
1400 if( !(ext && (ext->manager & LSMASH_BINARY_CODED_BOX) && ext->binary && ext->size >= 10) )
1401 return LSMASH_ERR_INVALID_DATA;
1402 uint16_t bitrate;
1403 if( stbl->stsz->list )
1405 if( (err = isom_calculate_bitrate_description( mdia, &bufferSizeDB, &maxBitrate, &avgBitrate, sample_description_index )) < 0 )
1406 return err;
1407 bitrate = maxBitrate / 1000; /* Use maximum bitrate if VBR. */
1409 else
1410 bitrate = stbl->stsz->sample_size * (eac3->samplerate >> 16) / 192000; /* 192000 == 1536 * 1000 / 8 */
1411 uint8_t *exdata = ext->binary + 8;
1412 exdata[0] = (bitrate >> 5) & 0xff;
1413 exdata[1] = (bitrate & 0x1f) << 3;
1416 return sample_description_index ? 0 : LSMASH_ERR_INVALID_DATA;
1419 static inline uint64_t isom_get_current_mp4time( void )
1421 return (uint64_t)time( NULL ) + ISOM_MAC_EPOCH_OFFSET;
1424 static int isom_set_media_creation_time( isom_trak_t *trak, uint64_t current_mp4time )
1426 if( !trak->mdia
1427 || !trak->mdia->mdhd )
1428 return LSMASH_ERR_NAMELESS;
1429 isom_mdhd_t *mdhd = trak->mdia->mdhd;
1430 if( mdhd->creation_time == 0 )
1431 mdhd->creation_time = mdhd->modification_time = current_mp4time;
1432 return 0;
1435 static int isom_set_track_creation_time( isom_trak_t *trak, uint64_t current_mp4time )
1437 if( !trak
1438 || !trak->tkhd )
1439 return LSMASH_ERR_NAMELESS;
1440 isom_tkhd_t *tkhd = trak->tkhd;
1441 if( tkhd->creation_time == 0 )
1442 tkhd->creation_time = tkhd->modification_time = current_mp4time;
1443 return isom_set_media_creation_time( trak, current_mp4time );
1446 static int isom_set_movie_creation_time( lsmash_file_t *file )
1448 if( !file
1449 || !file->moov
1450 || !file->moov->mvhd )
1451 return LSMASH_ERR_NAMELESS;
1452 uint64_t current_mp4time = isom_get_current_mp4time();
1453 for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next )
1455 int err = isom_set_track_creation_time( (isom_trak_t *)entry->data, current_mp4time );
1456 if( err < 0 )
1457 return err;
1459 isom_mvhd_t *mvhd = file->moov->mvhd;
1460 if( mvhd->creation_time == 0 )
1461 mvhd->creation_time = mvhd->modification_time = current_mp4time;
1462 return 0;
1465 int isom_setup_handler_reference( isom_hdlr_t *hdlr, uint32_t media_type )
1467 isom_box_t *parent = hdlr->parent;
1468 lsmash_file_t *file = hdlr->file;
1469 if( !parent || !file )
1470 return LSMASH_ERR_NAMELESS;
1471 isom_mdia_t *mdia = lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MDIA ) ? (isom_mdia_t *)parent : NULL;
1472 isom_meta_t *meta = lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) ? (isom_meta_t *)parent
1473 : lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) ? (isom_meta_t *)parent : NULL;
1474 uint32_t type = mdia ? (file->qt_compatible ? QT_HANDLER_TYPE_MEDIA : 0) : (meta ? 0 : QT_HANDLER_TYPE_DATA);
1475 uint32_t subtype = media_type;
1476 hdlr->componentType = type;
1477 hdlr->componentSubtype = subtype;
1478 char *type_name = NULL;
1479 char *subtype_name = NULL;
1480 uint8_t type_name_length = 0;
1481 uint8_t subtype_name_length = 0;
1482 if( mdia )
1483 type_name = "Media ";
1484 else if( meta )
1485 type_name = "Metadata ";
1486 else /* if( minf ) */
1487 type_name = "Data ";
1488 type_name_length = strlen( type_name );
1489 struct
1491 uint32_t subtype;
1492 char *subtype_name;
1493 uint8_t subtype_name_length;
1494 } subtype_table[] =
1496 { ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK, "Sound ", 6 },
1497 { ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK, "Video ", 6 },
1498 { ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK, "Hint ", 5 },
1499 { ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK, "Metadata ", 9 },
1500 { ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK, "Text ", 5 },
1501 { ISOM_META_HANDLER_TYPE_ITUNES_METADATA, "iTunes ", 7 },
1502 { QT_REFERENCE_HANDLER_TYPE_ALIAS, "Alias ", 6 },
1503 { QT_REFERENCE_HANDLER_TYPE_RESOURCE, "Resource ", 9 },
1504 { QT_REFERENCE_HANDLER_TYPE_URL, "URL ", 4 },
1505 { subtype, "Unknown ", 8 }
1507 for( int i = 0; subtype_table[i].subtype; i++ )
1508 if( subtype == subtype_table[i].subtype )
1510 subtype_name = subtype_table[i].subtype_name;
1511 subtype_name_length = subtype_table[i].subtype_name_length;
1512 break;
1514 uint32_t name_length = 15 + subtype_name_length + type_name_length + file->isom_compatible + file->qt_compatible;
1515 uint8_t *name = lsmash_malloc( name_length );
1516 if( !name )
1517 return LSMASH_ERR_MEMORY_ALLOC;
1518 if( file->qt_compatible )
1519 name[0] = name_length & 0xff;
1520 memcpy( name + file->qt_compatible, "L-SMASH ", 8 );
1521 memcpy( name + file->qt_compatible + 8, subtype_name, subtype_name_length );
1522 memcpy( name + file->qt_compatible + 8 + subtype_name_length, type_name, type_name_length );
1523 memcpy( name + file->qt_compatible + 8 + subtype_name_length + type_name_length, "Handler", 7 );
1524 if( file->isom_compatible )
1525 name[name_length - 1] = 0;
1526 hdlr->componentName = name;
1527 hdlr->componentName_length = name_length;
1528 return 0;
1531 isom_trak_t *isom_track_create( lsmash_file_t *file, lsmash_media_type media_type )
1533 /* Don't allow to create a new track if the initial movie is already written. */
1534 if( (file->fragment && file->fragment->movie)
1535 || (file->moov && (file->moov->manager & LSMASH_WRITTEN_BOX)) )
1536 return NULL;
1537 isom_trak_t *trak = isom_add_trak( file->moov );
1538 if( !trak
1539 || !trak->file
1540 || !trak->file->moov
1541 || !trak->file->moov->mvhd )
1542 goto fail;
1543 if( !isom_add_tkhd( trak )
1544 || !isom_add_mdia( trak )
1545 || !isom_add_mdhd( trak->mdia )
1546 || !isom_add_minf( trak->mdia )
1547 || !isom_add_dinf( trak->mdia->minf )
1548 || !isom_add_dref( trak->mdia->minf->dinf )
1549 || !isom_add_stbl( trak->mdia->minf )
1550 || !isom_add_stsd( trak->mdia->minf->stbl )
1551 || !isom_add_stts( trak->mdia->minf->stbl )
1552 || !isom_add_stsc( trak->mdia->minf->stbl )
1553 || !isom_add_stco( trak->mdia->minf->stbl )
1554 || !isom_add_stsz( trak->mdia->minf->stbl ) )
1555 goto fail;
1556 if( !isom_add_hdlr( trak->mdia )
1557 || isom_setup_handler_reference( trak->mdia->hdlr, media_type ) < 0 )
1558 goto fail;
1559 if( file->qt_compatible )
1561 if( !isom_add_hdlr( trak->mdia->minf )
1562 || isom_setup_handler_reference( trak->mdia->minf->hdlr, QT_REFERENCE_HANDLER_TYPE_URL ) < 0 )
1563 goto fail;
1565 switch( media_type )
1567 case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK :
1568 if( !isom_add_vmhd( trak->mdia->minf ) )
1569 goto fail;
1570 trak->mdia->minf->vmhd->flags = 0x000001;
1571 break;
1572 case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK :
1573 if( !isom_add_smhd( trak->mdia->minf ) )
1574 goto fail;
1575 trak->cache->is_audio = 1;
1576 break;
1577 case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK :
1578 if( !isom_add_hmhd( trak->mdia->minf ) )
1579 goto fail;
1580 break;
1581 case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK :
1582 if( file->qt_compatible || file->itunes_movie )
1584 if( !isom_add_gmhd( trak->mdia->minf )
1585 || !isom_add_gmin( trak->mdia->minf->gmhd )
1586 || !isom_add_text( trak->mdia->minf->gmhd ) )
1587 return 0;
1588 /* Default Text Media Information Box. */
1590 isom_text_t *text = trak->mdia->minf->gmhd->text;
1591 text->matrix[0] = 0x00010000;
1592 text->matrix[4] = 0x00010000;
1593 text->matrix[8] = 0x40000000;
1596 else
1597 goto fail; /* We support only reference text media track for chapter yet. */
1598 break;
1599 default :
1600 if( !isom_add_nmhd( trak->mdia->minf ) )
1601 goto fail;
1602 break;
1604 /* Default Track Header Box. */
1606 isom_tkhd_t *tkhd = trak->tkhd;
1607 if( media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK )
1608 tkhd->volume = 0x0100;
1609 tkhd->matrix[0] = 0x00010000;
1610 tkhd->matrix[4] = 0x00010000;
1611 tkhd->matrix[8] = 0x40000000;
1612 tkhd->duration = 0xffffffff;
1613 tkhd->track_ID = trak->file->moov->mvhd->next_track_ID ++;
1615 trak->mdia->mdhd->language = file->qt_compatible ? 0 : ISOM_LANGUAGE_CODE_UNDEFINED;
1616 return trak;
1617 fail:
1618 isom_remove_box_by_itself( trak );
1619 return NULL;
1622 isom_moov_t *isom_movie_create( lsmash_file_t *file )
1624 isom_moov_t *moov = isom_add_moov( file );
1625 isom_mvhd_t *mvhd = isom_add_mvhd( moov );
1626 if( !mvhd )
1628 isom_remove_box_by_itself( moov );
1629 return NULL;
1631 /* Default Movie Header Box. */
1632 mvhd->rate = 0x00010000;
1633 mvhd->volume = 0x0100;
1634 mvhd->matrix[0] = 0x00010000;
1635 mvhd->matrix[4] = 0x00010000;
1636 mvhd->matrix[8] = 0x40000000;
1637 mvhd->next_track_ID = 1;
1638 file->initializer = file;
1639 return moov;
1642 /*******************************
1643 public interfaces
1644 *******************************/
1646 /*---- track manipulators ----*/
1648 void lsmash_delete_track( lsmash_root_t *root, uint32_t track_ID )
1650 if( isom_check_initializer_present( root ) < 0
1651 || !root->file->initializer->moov )
1652 return;
1653 for( lsmash_entry_t *entry = root->file->initializer->moov->trak_list.head; entry; entry = entry->next )
1655 isom_trak_t *trak = (isom_trak_t *)entry->data;
1656 if( !trak
1657 || !trak->tkhd )
1658 return;
1659 if( trak->tkhd->track_ID == track_ID )
1661 isom_remove_box_by_itself( trak );
1662 return;
1667 uint32_t lsmash_create_track( lsmash_root_t *root, lsmash_media_type media_type )
1669 if( isom_check_initializer_present( root ) < 0 )
1670 return 0;
1671 isom_trak_t *trak = isom_track_create( root->file, media_type );
1672 if( !trak
1673 || !trak->tkhd )
1674 return 0;
1675 return trak->tkhd->track_ID;
1678 uint32_t lsmash_get_track_ID( lsmash_root_t *root, uint32_t track_number )
1680 if( isom_check_initializer_present( root ) < 0
1681 || !root->file->initializer->moov )
1682 return 0;
1683 isom_trak_t *trak = (isom_trak_t *)lsmash_get_entry_data( &root->file->initializer->moov->trak_list, track_number );
1684 if( !trak
1685 || !trak->tkhd )
1686 return 0;
1687 return trak->tkhd->track_ID;
1690 void lsmash_initialize_track_parameters( lsmash_track_parameters_t *param )
1692 memset( param, 0, sizeof(lsmash_track_parameters_t) );
1693 param->audio_volume = 0x0100;
1694 param->matrix[0] = 0x00010000;
1695 param->matrix[4] = 0x00010000;
1696 param->matrix[8] = 0x40000000;
1699 int lsmash_set_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param )
1701 if( isom_check_initializer_present( root ) < 0 )
1702 return LSMASH_ERR_FUNCTION_PARAM;
1703 lsmash_file_t *file = root->file;
1704 isom_trak_t *trak = isom_get_trak( file, track_ID );
1705 if( !trak
1706 || !trak->mdia
1707 || !trak->mdia->hdlr
1708 || !file->moov->mvhd )
1709 return LSMASH_ERR_NAMELESS;
1710 /* Prepare Track Aperture Modes if required. */
1711 if( file->qt_compatible && param->aperture_modes )
1713 if( !trak->tapt && !isom_add_tapt( trak ) )
1714 return LSMASH_ERR_NAMELESS;
1715 isom_tapt_t *tapt = trak->tapt;
1716 if( (!tapt->clef && !isom_add_clef( tapt ))
1717 || (!tapt->prof && !isom_add_prof( tapt ))
1718 || (!tapt->enof && !isom_add_enof( tapt )) )
1719 return LSMASH_ERR_NAMELESS;
1721 else
1722 isom_remove_box_by_itself( trak->tapt );
1723 /* Set up Track Header. */
1724 uint32_t media_type = trak->mdia->hdlr->componentSubtype;
1725 isom_tkhd_t *tkhd = trak->tkhd;
1726 tkhd->flags = param->mode;
1727 tkhd->track_ID = param->track_ID ? param->track_ID : tkhd->track_ID;
1728 tkhd->duration = !trak->edts || !trak->edts->elst ? param->duration : tkhd->duration;
1729 /* Template fields
1730 * alternate_group, layer, volume and matrix
1731 * According to 14496-14, these value are all set to defaut values in 14496-12.
1732 * And when a file is read as an MPEG-4 file, these values shall be ignored.
1733 * If a file complies with other specifications, then those fields may have non-default values
1734 * as required by those other specifications. */
1735 if( param->alternate_group )
1737 if( file->qt_compatible || file->itunes_movie || file->max_3gpp_version >= 4 )
1738 tkhd->alternate_group = param->alternate_group;
1739 else
1741 tkhd->alternate_group = 0;
1742 lsmash_log( NULL, LSMASH_LOG_WARNING,
1743 "alternate_group is specified but not compatible with any of the brands. It won't be set.\n" );
1746 else
1747 tkhd->alternate_group = 0;
1748 if( file->qt_compatible || file->itunes_movie )
1750 tkhd->layer = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->video_layer : 0;
1751 tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? param->audio_volume : 0;
1752 if( media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK )
1753 for( int i = 0; i < 9; i++ )
1754 tkhd->matrix[i] = param->matrix[i];
1755 else
1756 for( int i = 0; i < 9; i++ )
1757 tkhd->matrix[i] = 0;
1759 else
1761 tkhd->layer = 0;
1762 tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? 0x0100 : 0;
1763 tkhd->matrix[0] = 0x00010000;
1764 tkhd->matrix[1] = 0;
1765 tkhd->matrix[2] = 0;
1766 tkhd->matrix[3] = 0;
1767 tkhd->matrix[4] = 0x00010000;
1768 tkhd->matrix[5] = 0;
1769 tkhd->matrix[6] = 0;
1770 tkhd->matrix[7] = 0;
1771 tkhd->matrix[8] = 0x40000000;
1773 /* visual presentation size */
1774 tkhd->width = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_width : 0;
1775 tkhd->height = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_height : 0;
1776 /* Update next_track_ID if needed. */
1777 if( file->moov->mvhd->next_track_ID <= tkhd->track_ID )
1778 file->moov->mvhd->next_track_ID = tkhd->track_ID + 1;
1779 return 0;
1782 int lsmash_get_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param )
1784 if( isom_check_initializer_present( root ) < 0 )
1785 return LSMASH_ERR_FUNCTION_PARAM;
1786 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1787 if( !trak )
1788 return LSMASH_ERR_NAMELESS;
1789 isom_tkhd_t *tkhd = trak->tkhd;
1790 param->mode = tkhd->flags;
1791 param->track_ID = tkhd->track_ID;
1792 param->duration = tkhd->duration;
1793 param->video_layer = tkhd->layer;
1794 param->alternate_group = tkhd->alternate_group;
1795 param->audio_volume = tkhd->volume;
1796 for( int i = 0; i < 9; i++ )
1797 param->matrix[i] = tkhd->matrix[i];
1798 param->display_width = tkhd->width;
1799 param->display_height = tkhd->height;
1800 param->aperture_modes = !!trak->tapt;
1801 return 0;
1804 static inline int check_dref_presence( isom_trak_t *trak )
1806 if( !trak
1807 || !trak->mdia
1808 || !trak->mdia->minf
1809 || !trak->mdia->minf->dinf
1810 || !trak->mdia->minf->dinf->dref )
1811 return LSMASH_ERR_NAMELESS;
1812 return 0;
1815 uint32_t lsmash_count_data_reference
1817 lsmash_root_t *root,
1818 uint32_t track_ID
1821 if( isom_check_initializer_present( root ) < 0 )
1822 return 0;
1823 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1824 if( check_dref_presence( trak ) < 0 )
1825 return 0;
1826 return trak->mdia->minf->dinf->dref->list.entry_count;
1829 int lsmash_get_data_reference
1831 lsmash_root_t *root,
1832 uint32_t track_ID,
1833 lsmash_data_reference_t *data_ref
1836 if( isom_check_initializer_present( root ) < 0 || !data_ref )
1837 return LSMASH_ERR_FUNCTION_PARAM;
1838 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1839 if( check_dref_presence( trak ) < 0 )
1840 return LSMASH_ERR_NAMELESS;
1841 isom_dref_entry_t *url = lsmash_get_entry_data( &trak->mdia->minf->dinf->dref->list, data_ref->index );
1842 if( !url )
1843 return LSMASH_ERR_NAMELESS;
1844 if( !(url->flags & 0x000001) && url->location )
1846 int length = strlen( url->location );
1847 char *location = lsmash_malloc( length + 1 );
1848 if( !location )
1849 return LSMASH_ERR_MEMORY_ALLOC;
1850 memcpy( location, url->location, length );
1851 location[length] = '\0';
1852 data_ref->location = location;
1854 else
1855 data_ref->location = NULL;
1856 return 0;
1859 void lsmash_cleanup_data_reference
1861 lsmash_data_reference_t *data_ref
1864 if( !data_ref )
1865 return;
1866 lsmash_freep( &data_ref->location );
1869 int lsmash_create_data_reference
1871 lsmash_root_t *root,
1872 uint32_t track_ID,
1873 lsmash_data_reference_t *data_ref,
1874 lsmash_file_t *file
1877 /* At present, we don't support external data references for movie fragments.
1878 * Note that, for external media data, default-base-is-moof is meaningless since relative
1879 * offsets from Movie Fragment Boxes make no sense.
1880 * In the future, the condition of !(file->flags & LSMASH_FILE_MODE_WRITE) may be removed
1881 * for the implementation which does not write actually and does reference read-only file. */
1882 if( !root || !file || file->root != root
1883 || (!(file->flags & LSMASH_FILE_MODE_MEDIA) && !(file->flags & LSMASH_FILE_MODE_INITIALIZATION))
1884 || !(file->flags & LSMASH_FILE_MODE_WRITE)
1885 || (root->file != file && ((file->flags & LSMASH_FILE_MODE_FRAGMENTED) || file->fragment))
1886 || !data_ref )
1887 return LSMASH_ERR_FUNCTION_PARAM;
1888 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
1889 if( check_dref_presence( trak ) < 0 )
1890 return LSMASH_ERR_NAMELESS;
1891 isom_dref_entry_t *url = isom_add_dref_entry( trak->mdia->minf->dinf->dref, ISOM_BOX_TYPE_URL );
1892 if( !url )
1893 return LSMASH_ERR_NAMELESS;
1894 if( !data_ref->location || root->file == file )
1896 /* Media data is in the same file. */
1897 url->flags = 0x000001;
1898 url->ref_file = root->file;
1900 else
1902 /* Set the location of the file. */
1903 int length = strlen( data_ref->location );
1904 url->location = lsmash_malloc( length + 1 );
1905 if( !url->location )
1907 isom_remove_box_by_itself( url );
1908 return LSMASH_ERR_MEMORY_ALLOC;
1910 memcpy( url->location, data_ref->location, length );
1911 url->location[length] = '\0';
1912 url->location_length = length + 1;
1913 url->ref_file = file;
1915 data_ref->index = trak->mdia->minf->dinf->dref->list.entry_count;
1916 return 0;
1919 int lsmash_assign_data_reference
1921 lsmash_root_t *root,
1922 uint32_t track_ID,
1923 uint32_t data_ref_index,
1924 lsmash_file_t *file
1927 if( isom_check_initializer_present( root ) < 0
1928 || !file || file->root != root
1929 || !(file->flags & LSMASH_FILE_MODE_MEDIA)
1930 || !(file->flags & LSMASH_FILE_MODE_READ)
1931 || data_ref_index == 0 )
1932 return LSMASH_ERR_FUNCTION_PARAM;
1933 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1934 if( check_dref_presence( trak ) < 0 )
1935 return LSMASH_ERR_NAMELESS;
1936 isom_dref_entry_t *url = (isom_dref_entry_t *)lsmash_get_entry_data( &trak->mdia->minf->dinf->dref->list, data_ref_index );
1937 if( !url )
1938 return LSMASH_ERR_NAMELESS;
1939 if( !(url->flags & 0x000001) )
1940 /* Reference an external media data. */
1941 url->ref_file = file;
1942 return 0;
1945 static int isom_set_media_handler_name( lsmash_file_t *file, uint32_t track_ID, char *handler_name )
1947 isom_trak_t *trak = isom_get_trak( file, track_ID );
1948 if( !trak
1949 || !trak->mdia
1950 || !trak->mdia->hdlr )
1951 return LSMASH_ERR_NAMELESS;
1952 isom_hdlr_t *hdlr = trak->mdia->hdlr;
1953 uint8_t *name = NULL;
1954 uint32_t name_length = strlen( handler_name ) + file->isom_compatible + file->qt_compatible;
1955 if( file->qt_compatible )
1956 name_length = LSMASH_MIN( name_length, 255 );
1957 if( name_length > hdlr->componentName_length && hdlr->componentName )
1958 name = lsmash_realloc( hdlr->componentName, name_length );
1959 else if( !hdlr->componentName )
1960 name = lsmash_malloc( name_length );
1961 else
1962 name = hdlr->componentName;
1963 if( !name )
1964 return LSMASH_ERR_MEMORY_ALLOC;
1965 if( file->qt_compatible )
1966 name[0] = name_length & 0xff;
1967 memcpy( name + file->qt_compatible, handler_name, strlen( handler_name ) );
1968 if( file->isom_compatible )
1969 name[name_length - 1] = 0;
1970 hdlr->componentName = name;
1971 hdlr->componentName_length = name_length;
1972 return 0;
1975 static int isom_set_data_handler_name( lsmash_file_t *file, uint32_t track_ID, char *handler_name )
1977 isom_trak_t *trak = isom_get_trak( file, track_ID );
1978 if( !trak
1979 || !trak->mdia
1980 || !trak->mdia->minf
1981 || !trak->mdia->minf->hdlr )
1982 return LSMASH_ERR_NAMELESS;
1983 isom_hdlr_t *hdlr = trak->mdia->minf->hdlr;
1984 uint8_t *name = NULL;
1985 uint32_t name_length = strlen( handler_name ) + file->isom_compatible + file->qt_compatible;
1986 if( file->qt_compatible )
1987 name_length = LSMASH_MIN( name_length, 255 );
1988 if( name_length > hdlr->componentName_length && hdlr->componentName )
1989 name = lsmash_realloc( hdlr->componentName, name_length );
1990 else if( !hdlr->componentName )
1991 name = lsmash_malloc( name_length );
1992 else
1993 name = hdlr->componentName;
1994 if( !name )
1995 return LSMASH_ERR_MEMORY_ALLOC;
1996 if( file->qt_compatible )
1997 name[0] = name_length & 0xff;
1998 memcpy( name + file->qt_compatible, handler_name, strlen( handler_name ) );
1999 if( file->isom_compatible )
2000 name[name_length - 1] = 0;
2001 hdlr->componentName = name;
2002 hdlr->componentName_length = name_length;
2003 return 0;
2006 uint32_t lsmash_get_media_timescale( lsmash_root_t *root, uint32_t track_ID )
2008 if( isom_check_initializer_present( root ) < 0 )
2009 return 0;
2010 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
2011 if( !trak
2012 || !trak->mdia
2013 || !trak->mdia->mdhd )
2014 return 0;
2015 return trak->mdia->mdhd->timescale;
2018 uint64_t lsmash_get_media_duration( lsmash_root_t *root, uint32_t track_ID )
2020 if( isom_check_initializer_present( root ) < 0 )
2021 return 0;
2022 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
2023 if( !trak
2024 || !trak->mdia
2025 || !trak->mdia->mdhd )
2026 return 0;
2027 return trak->mdia->mdhd->duration;
2030 uint64_t lsmash_get_track_duration( lsmash_root_t *root, uint32_t track_ID )
2032 if( isom_check_initializer_present( root ) < 0 )
2033 return 0;
2034 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
2035 if( !trak
2036 || !trak->tkhd )
2037 return 0;
2038 return trak->tkhd->duration;
2041 uint32_t lsmash_get_last_sample_delta( lsmash_root_t *root, uint32_t track_ID )
2043 if( isom_check_initializer_present( root ) < 0 )
2044 return 0;
2045 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
2046 if( !trak
2047 || !trak->mdia
2048 || !trak->mdia->minf
2049 || !trak->mdia->minf->stbl
2050 || !trak->mdia->minf->stbl->stts
2051 || !trak->mdia->minf->stbl->stts->list
2052 || !trak->mdia->minf->stbl->stts->list->tail
2053 || !trak->mdia->minf->stbl->stts->list->tail->data )
2054 return 0;
2055 return ((isom_stts_entry_t *)trak->mdia->minf->stbl->stts->list->tail->data)->sample_delta;
2058 uint32_t lsmash_get_start_time_offset( lsmash_root_t *root, uint32_t track_ID )
2060 if( isom_check_initializer_present( root ) < 0 )
2061 return 0;
2062 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
2063 if( !trak
2064 || !trak->mdia
2065 || !trak->mdia->minf
2066 || !trak->mdia->minf->stbl
2067 || !trak->mdia->minf->stbl->ctts
2068 || !trak->mdia->minf->stbl->ctts->list
2069 || !trak->mdia->minf->stbl->ctts->list->head
2070 || !trak->mdia->minf->stbl->ctts->list->head->data )
2071 return 0;
2072 return ((isom_ctts_entry_t *)trak->mdia->minf->stbl->ctts->list->head->data)->sample_offset;
2075 uint32_t lsmash_get_composition_to_decode_shift( lsmash_root_t *root, uint32_t track_ID )
2077 if( isom_check_initializer_present( root ) < 0 )
2078 return 0;
2079 lsmash_file_t *file = root->file->initializer;
2080 isom_trak_t *trak = isom_get_trak( file, track_ID );
2081 if( !trak
2082 || !trak->mdia
2083 || !trak->mdia->minf
2084 || !trak->mdia->minf->stbl )
2085 return 0;
2086 uint32_t sample_count = isom_get_sample_count( trak );
2087 if( sample_count == 0 )
2088 return 0;
2089 isom_stbl_t *stbl = trak->mdia->minf->stbl;
2090 if( !stbl->stts || !stbl->stts->list
2091 || !stbl->ctts || !stbl->ctts->list )
2092 return 0;
2093 if( !(file->max_isom_version >= 4 && stbl->ctts->version == 1) && !file->qt_compatible )
2094 return 0; /* This movie shall not have composition to decode timeline shift. */
2095 lsmash_entry_t *stts_entry = stbl->stts->list->head;
2096 lsmash_entry_t *ctts_entry = stbl->ctts->list->head;
2097 if( !stts_entry || !ctts_entry )
2098 return 0;
2099 uint64_t dts = 0;
2100 uint64_t cts = 0;
2101 uint32_t ctd_shift = 0;
2102 uint32_t i = 0;
2103 uint32_t j = 0;
2104 for( uint32_t k = 0; k < sample_count; k++ )
2106 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
2107 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
2108 if( !stts_data || !ctts_data )
2109 return 0;
2110 cts = dts + (int32_t)ctts_data->sample_offset;
2111 if( dts > cts + ctd_shift )
2112 ctd_shift = dts - cts;
2113 dts += stts_data->sample_delta;
2114 if( ++i == stts_data->sample_count )
2116 stts_entry = stts_entry->next;
2117 if( !stts_entry )
2118 return 0;
2119 i = 0;
2121 if( ++j == ctts_data->sample_count )
2123 ctts_entry = ctts_entry->next;
2124 if( !ctts_entry )
2125 return 0;
2126 j = 0;
2129 return ctd_shift;
2132 uint16_t lsmash_pack_iso_language( char *iso_language )
2134 if( !iso_language || strlen( iso_language ) != 3 )
2135 return 0;
2136 return (uint16_t)LSMASH_PACK_ISO_LANGUAGE( iso_language[0], iso_language[1], iso_language[2] );
2139 static int isom_iso2mac_language( uint16_t ISO_language, uint16_t *MAC_language )
2141 assert( MAC_language );
2142 int i = 0;
2143 for( ; isom_languages[i].iso_name; i++ )
2144 if( ISO_language == isom_languages[i].iso_name )
2145 break;
2146 if( !isom_languages[i].iso_name )
2147 return LSMASH_ERR_NAMELESS;
2148 *MAC_language = isom_languages[i].mac_value;
2149 return 0;
2152 static int isom_mac2iso_language( uint16_t MAC_language, uint16_t *ISO_language )
2154 assert( ISO_language );
2155 int i = 0;
2156 for( ; isom_languages[i].iso_name; i++ )
2157 if( MAC_language == isom_languages[i].mac_value )
2158 break;
2159 *ISO_language = isom_languages[i].iso_name ? isom_languages[i].iso_name : ISOM_LANGUAGE_CODE_UNDEFINED;
2160 return 0;
2163 static int isom_set_media_language( lsmash_file_t *file, uint32_t track_ID, uint16_t ISO_language, uint16_t MAC_language )
2165 isom_trak_t *trak = isom_get_trak( file, track_ID );
2166 if( !trak
2167 || !trak->mdia
2168 || !trak->mdia->mdhd )
2169 return LSMASH_ERR_NAMELESS;
2170 uint16_t language = 0;
2171 if( file->isom_compatible )
2173 if( ISO_language )
2174 language = ISO_language;
2175 else if( MAC_language )
2177 int err = isom_mac2iso_language( MAC_language, &language );
2178 if( err )
2179 return err;
2181 else
2182 language = ISOM_LANGUAGE_CODE_UNDEFINED;
2184 else if( file->qt_compatible )
2186 if( ISO_language )
2188 int err = isom_iso2mac_language( ISO_language, &language );
2189 if( err )
2190 return err;
2192 else
2193 language = MAC_language;
2195 else
2196 return LSMASH_ERR_INVALID_DATA;
2197 trak->mdia->mdhd->language = language;
2198 return 0;
2201 int isom_add_sample_grouping( isom_box_t *parent, isom_grouping_type grouping_type )
2203 isom_sgpd_t *sgpd;
2204 isom_sbgp_t *sbgp;
2205 if( NULL == (sgpd = isom_add_sgpd( parent ))
2206 || NULL == (sbgp = isom_add_sbgp( parent )) )
2207 return LSMASH_ERR_NAMELESS;
2208 sbgp->grouping_type = grouping_type;
2209 sgpd->grouping_type = grouping_type;
2210 sgpd->version = 1; /* We use version 1 for Sample Group Description Box because it is recommended in the spec. */
2211 switch( grouping_type )
2213 case ISOM_GROUP_TYPE_RAP :
2214 sgpd->default_length = 1;
2215 break;
2216 case ISOM_GROUP_TYPE_ROLL :
2217 case ISOM_GROUP_TYPE_PROL :
2218 sgpd->default_length = 2;
2219 break;
2220 default :
2221 /* We don't consider other grouping types currently. */
2222 break;
2224 return 0;
2227 static int isom_create_sample_grouping( isom_trak_t *trak, isom_grouping_type grouping_type )
2229 lsmash_file_t *file = trak->file;
2230 switch( grouping_type )
2232 case ISOM_GROUP_TYPE_RAP :
2233 assert( file->max_isom_version >= 6 );
2234 break;
2235 case ISOM_GROUP_TYPE_ROLL :
2236 case ISOM_GROUP_TYPE_PROL :
2237 assert( file->avc_extensions || file->qt_compatible );
2238 break;
2239 default :
2240 assert( 0 );
2241 break;
2243 int err = isom_add_sample_grouping( (isom_box_t *)trak->mdia->minf->stbl, grouping_type );
2244 if( err < 0 )
2245 return err;
2246 if( trak->cache->fragment && file->max_isom_version >= 6 )
2247 switch( grouping_type )
2249 case ISOM_GROUP_TYPE_RAP :
2250 trak->cache->fragment->rap_grouping = 1;
2251 break;
2252 case ISOM_GROUP_TYPE_ROLL :
2253 case ISOM_GROUP_TYPE_PROL :
2254 trak->cache->fragment->roll_grouping = 1;
2255 break;
2256 default :
2257 /* We don't consider other grouping types currently. */
2258 break;
2260 return 0;
2263 void lsmash_initialize_media_parameters( lsmash_media_parameters_t *param )
2265 memset( param, 0, sizeof(lsmash_media_parameters_t) );
2266 param->timescale = 1;
2269 int lsmash_set_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param )
2271 if( isom_check_initializer_present( root ) < 0 )
2272 return LSMASH_ERR_FUNCTION_PARAM;
2273 lsmash_file_t *file = root->file;
2274 isom_trak_t *trak = isom_get_trak( file, track_ID );
2275 if( !trak
2276 || !trak->mdia
2277 || !trak->mdia->mdhd
2278 || !trak->mdia->minf
2279 || !trak->mdia->minf->stbl )
2280 return LSMASH_ERR_NAMELESS;
2281 trak->mdia->mdhd->timescale = param->timescale;
2282 int err = isom_set_media_language( file, track_ID, param->ISO_language, param->MAC_language );
2283 if( err < 0 )
2284 return err;
2285 if( param->media_handler_name
2286 && (err = isom_set_media_handler_name( file, track_ID, param->media_handler_name )) < 0 )
2287 return err;
2288 if( file->qt_compatible && param->data_handler_name
2289 && (err = isom_set_data_handler_name( file, track_ID, param->data_handler_name )) < 0 )
2290 return err;
2291 if( (file->avc_extensions || file->qt_compatible) && param->roll_grouping
2292 && (err = isom_create_sample_grouping( trak, ISOM_GROUP_TYPE_ROLL )) < 0 )
2293 return err;
2294 if( (file->max_isom_version >= 6) && param->rap_grouping
2295 && (err = isom_create_sample_grouping( trak, ISOM_GROUP_TYPE_RAP )) < 0 )
2296 return err;
2297 return 0;
2300 int lsmash_get_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param )
2302 if( isom_check_initializer_present( root ) < 0 )
2303 return LSMASH_ERR_FUNCTION_PARAM;
2304 lsmash_file_t *file = root->file->initializer;
2305 isom_trak_t *trak = isom_get_trak( file, track_ID );
2306 if( !trak
2307 || !trak->mdia
2308 || !trak->mdia->mdhd
2309 || !trak->mdia->hdlr
2310 || !trak->mdia->minf
2311 || !trak->mdia->minf->stbl )
2312 return LSMASH_ERR_NAMELESS;
2313 isom_mdhd_t *mdhd = trak->mdia->mdhd;
2314 isom_stbl_t *stbl = trak->mdia->minf->stbl;
2315 param->timescale = mdhd->timescale;
2316 param->handler_type = trak->mdia->hdlr->componentSubtype;
2317 param->duration = mdhd->duration;
2318 /* Whether sample grouping present. */
2320 isom_sbgp_t *sbgp;
2321 isom_sgpd_t *sgpd;
2322 sbgp = isom_get_sample_to_group ( stbl, ISOM_GROUP_TYPE_RAP );
2323 sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
2324 param->rap_grouping = sbgp && sgpd;
2325 sbgp = isom_get_roll_recovery_sample_to_group ( &stbl->sbgp_list );
2326 sgpd = isom_get_roll_recovery_sample_group_description( &stbl->sgpd_list );
2327 param->roll_grouping = sbgp && sgpd;
2329 /* Get media language. */
2330 if( mdhd->language >= 0x800 )
2332 param->MAC_language = 0;
2333 param->ISO_language = mdhd->language;
2335 else
2337 param->MAC_language = mdhd->language;
2338 param->ISO_language = 0;
2340 /* Get handler name(s). */
2341 isom_hdlr_t *hdlr = trak->mdia->hdlr;
2342 int length = LSMASH_MIN( 255, hdlr->componentName_length );
2343 if( length )
2345 memcpy( param->media_handler_name_shadow, hdlr->componentName + file->qt_compatible, length );
2346 param->media_handler_name_shadow[length - 2 + file->isom_compatible + file->qt_compatible] = '\0';
2347 param->media_handler_name = param->media_handler_name_shadow;
2349 else
2351 param->media_handler_name = NULL;
2352 memset( param->media_handler_name_shadow, 0, sizeof(param->media_handler_name_shadow) );
2354 if( trak->mdia->minf->hdlr )
2356 hdlr = trak->mdia->minf->hdlr;
2357 length = LSMASH_MIN( 255, hdlr->componentName_length );
2358 if( length )
2360 memcpy( param->data_handler_name_shadow, hdlr->componentName + file->qt_compatible, length );
2361 param->data_handler_name_shadow[length - 2 + file->isom_compatible + file->qt_compatible] = '\0';
2362 param->data_handler_name = param->data_handler_name_shadow;
2364 else
2366 param->data_handler_name = NULL;
2367 memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) );
2370 else
2372 param->data_handler_name = NULL;
2373 memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) );
2375 return 0;
2378 /*---- movie manipulators ----*/
2380 void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t *param )
2382 memset( param, 0, sizeof(lsmash_movie_parameters_t) );
2383 param->timescale = 600;
2384 param->playback_rate = 0x00010000;
2385 param->playback_volume = 0x0100;
2388 int lsmash_set_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param )
2390 if( !root )
2391 return LSMASH_ERR_FUNCTION_PARAM;
2392 lsmash_file_t *file = root->file;
2393 if( !file
2394 || !file->moov
2395 || !file->moov->mvhd )
2396 return LSMASH_ERR_NAMELESS;
2397 isom_mvhd_t *mvhd = file->moov->mvhd;
2398 mvhd->timescale = param->timescale;
2399 if( file->qt_compatible || file->itunes_movie )
2401 mvhd->rate = param->playback_rate;
2402 mvhd->volume = param->playback_volume;
2403 mvhd->previewTime = param->preview_time;
2404 mvhd->previewDuration = param->preview_duration;
2405 mvhd->posterTime = param->poster_time;
2407 else
2409 mvhd->rate = 0x00010000;
2410 mvhd->volume = 0x0100;
2411 mvhd->previewTime = 0;
2412 mvhd->previewDuration = 0;
2413 mvhd->posterTime = 0;
2415 return 0;
2418 int lsmash_get_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param )
2420 if( isom_check_initializer_present( root ) < 0 )
2421 return LSMASH_ERR_FUNCTION_PARAM;
2422 lsmash_file_t *file = root->file->initializer;
2423 if( !file->moov
2424 || !file->moov->mvhd )
2425 return LSMASH_ERR_NAMELESS;
2426 isom_mvhd_t *mvhd = file->moov->mvhd;
2427 param->timescale = mvhd->timescale;
2428 param->duration = mvhd->duration;
2429 param->playback_rate = mvhd->rate;
2430 param->playback_volume = mvhd->volume;
2431 param->preview_time = mvhd->previewTime;
2432 param->preview_duration = mvhd->previewDuration;
2433 param->poster_time = mvhd->posterTime;
2434 param->number_of_tracks = file->moov->trak_list.entry_count;
2435 return 0;
2438 uint32_t lsmash_get_movie_timescale( lsmash_root_t *root )
2440 if( isom_check_initializer_present( root ) < 0
2441 || !root->file->initializer->moov
2442 || !root->file->initializer->moov->mvhd )
2443 return 0;
2444 return root->file->initializer->moov->mvhd->timescale;
2447 static int isom_scan_trak_profileLevelIndication
2449 isom_trak_t *trak,
2450 mp4a_audioProfileLevelIndication *audio_pli,
2451 mp4sys_visualProfileLevelIndication *visual_pli
2454 if( !trak
2455 || !trak->mdia
2456 || !trak->mdia->minf
2457 || !trak->mdia->minf->stbl )
2458 return LSMASH_ERR_INVALID_DATA;
2459 isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd;
2460 if( !stsd
2461 || !stsd->list.head )
2462 return LSMASH_ERR_INVALID_DATA;
2463 for( lsmash_entry_t *entry = stsd->list.head; entry; entry = entry->next )
2465 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)entry->data;
2466 if( !sample_entry )
2467 return LSMASH_ERR_INVALID_DATA;
2468 lsmash_codec_type_t sample_type = sample_entry->type;
2469 if( trak->mdia->minf->vmhd )
2471 if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO )
2472 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC2_VIDEO )
2473 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC3_VIDEO )
2474 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC4_VIDEO )
2475 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVCP_VIDEO )
2476 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_SVC1_VIDEO )
2477 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MVC1_VIDEO )
2478 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MVC2_VIDEO ) )
2480 /* FIXME: Do we have to arbitrate like audio? */
2481 if( *visual_pli == MP4SYS_VISUAL_PLI_NONE_REQUIRED )
2482 *visual_pli = MP4SYS_VISUAL_PLI_H264_AVC;
2484 else
2485 *visual_pli = MP4SYS_VISUAL_PLI_NOT_SPECIFIED;
2487 else if( trak->mdia->minf->smhd )
2489 if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) )
2491 isom_audio_entry_t *audio = (isom_audio_entry_t *)sample_entry;
2492 isom_esds_t *esds = (isom_esds_t *)isom_get_extension_box_format( &audio->extensions, ISOM_BOX_TYPE_ESDS );
2493 if( !esds || !esds->ES )
2494 return LSMASH_ERR_INVALID_DATA;
2495 lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO );
2496 if( !summary )
2497 continue;
2498 if( mp4sys_setup_summary_from_DecoderSpecificInfo( summary, esds->ES ) < 0 )
2499 *audio_pli = MP4A_AUDIO_PLI_NOT_SPECIFIED;
2500 else
2501 *audio_pli = mp4a_max_audioProfileLevelIndication( *audio_pli, mp4a_get_audioProfileLevelIndication( summary ) );
2502 lsmash_cleanup_summary( (lsmash_summary_t *)summary );
2504 else
2505 /* NOTE: Audio CODECs other than 'mp4a' does not have appropriate pli. */
2506 *audio_pli = MP4A_AUDIO_PLI_NOT_SPECIFIED;
2508 else
2509 ; /* FIXME: Do we have to set OD_profileLevelIndication? */
2511 return 0;
2514 int isom_setup_iods( isom_moov_t *moov )
2516 if( !moov->iods && !isom_add_iods( moov ) )
2517 return LSMASH_ERR_NAMELESS;
2518 isom_iods_t *iods = moov->iods;
2519 int err = LSMASH_ERR_NAMELESS;
2520 iods->OD = mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */
2521 if( !iods->OD )
2522 goto fail;
2523 mp4a_audioProfileLevelIndication audio_pli = MP4A_AUDIO_PLI_NONE_REQUIRED;
2524 mp4sys_visualProfileLevelIndication visual_pli = MP4SYS_VISUAL_PLI_NONE_REQUIRED;
2525 for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next )
2527 isom_trak_t *trak = (isom_trak_t *)entry->data;
2528 if( !trak
2529 || !trak->tkhd )
2530 goto fail;
2531 if( (err = isom_scan_trak_profileLevelIndication( trak, &audio_pli, &visual_pli )) < 0 )
2532 goto fail;
2533 if( (err = mp4sys_create_ES_ID_Inc( iods->OD, trak->tkhd->track_ID )) < 0 )
2534 goto fail;
2536 if( (err = mp4sys_to_InitialObjectDescriptor( iods->OD,
2537 0, /* FIXME: I'm not quite sure what the spec says. */
2538 MP4SYS_OD_PLI_NONE_REQUIRED, MP4SYS_SCENE_PLI_NONE_REQUIRED,
2539 audio_pli, visual_pli,
2540 MP4SYS_GRAPHICS_PLI_NONE_REQUIRED )) < 0 )
2541 goto fail;
2542 return 0;
2543 fail:
2544 isom_remove_box_by_itself( iods );
2545 return err;
2548 int lsmash_create_object_descriptor( lsmash_root_t *root )
2550 if( isom_check_initializer_present( root ) < 0 )
2551 return LSMASH_ERR_FUNCTION_PARAM;
2552 lsmash_file_t *file = root->file;
2553 /* Return error if this file is not compatible with MP4 file format. */
2554 if( !file->mp4_version1
2555 && !file->mp4_version2 )
2556 return LSMASH_ERR_FUNCTION_PARAM;
2557 return isom_setup_iods( file->moov );
2560 /*---- finishing functions ----*/
2562 int isom_complement_data_reference( isom_minf_t *minf )
2564 if( !minf->dinf
2565 || !minf->dinf->dref )
2566 return LSMASH_ERR_INVALID_DATA;
2567 /* Complement data referece if absent. */
2568 if( !minf->dinf->dref->list.head )
2570 isom_dref_entry_t *url = isom_add_dref_entry( minf->dinf->dref, ISOM_BOX_TYPE_URL );
2571 if( !url )
2572 return LSMASH_ERR_NAMELESS;
2573 url->flags = 0x000001; /* Media data is in the same file. */
2575 return 0;
2578 static lsmash_file_t *isom_get_written_media_file
2580 isom_trak_t *trak,
2581 uint32_t sample_description_index
2584 isom_minf_t *minf = trak->mdia->minf;
2585 isom_sample_entry_t *description = (isom_sample_entry_t *)lsmash_get_entry_data( &minf->stbl->stsd->list, sample_description_index );
2586 isom_dref_entry_t *dref_entry = (isom_dref_entry_t *)lsmash_get_entry_data( &minf->dinf->dref->list, description->data_reference_index );
2587 lsmash_file_t *file = (!dref_entry || !dref_entry->ref_file) ? trak->file : dref_entry->ref_file;
2588 if( !(file->flags & LSMASH_FILE_MODE_MEDIA)
2589 || !(file->flags & LSMASH_FILE_MODE_WRITE) )
2590 return trak->file;
2591 return file;
2594 int isom_check_large_offset_requirement
2596 isom_moov_t *moov,
2597 uint64_t meta_size
2600 for( lsmash_entry_t *entry = moov->trak_list.head; entry; )
2602 isom_trak_t *trak = (isom_trak_t *)entry->data;
2603 isom_stco_t *stco = trak->mdia->minf->stbl->stco;
2604 if( !stco->list->tail /* no samples */
2605 || stco->large_presentation
2606 || (((isom_stco_entry_t *)stco->list->tail->data)->chunk_offset + moov->size + meta_size) <= UINT32_MAX )
2608 entry = entry->next;
2609 continue; /* no need to convert stco into co64 */
2611 /* stco->co64 conversion */
2612 int err = isom_convert_stco_to_co64( trak->mdia->minf->stbl );
2613 if( err < 0 )
2614 return err;
2615 if( isom_update_box_size( moov ) == 0 )
2616 return LSMASH_ERR_INVALID_DATA;
2617 entry = moov->trak_list.head; /* whenever any conversion, re-check all traks */
2619 return 0;
2622 void isom_add_preceding_box_size
2624 isom_moov_t *moov,
2625 uint64_t preceding_size
2628 for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next )
2630 /* Apply to the chunks in the same file. */
2631 isom_trak_t *trak = (isom_trak_t *)entry->data;
2632 isom_stsc_t *stsc = trak->mdia->minf->stbl->stsc;
2633 isom_stco_t *stco = trak->mdia->minf->stbl->stco;
2634 lsmash_entry_t *stsc_entry = stsc->list->head;
2635 isom_stsc_entry_t *stsc_data = stsc_entry ? (isom_stsc_entry_t *)stsc_entry->data : NULL;
2636 uint32_t chunk_number = 1;
2637 for( lsmash_entry_t *stco_entry = stco->list->head; stco_entry; )
2639 if( stsc_data
2640 && stsc_data->first_chunk == chunk_number )
2642 lsmash_file_t *ref_file = isom_get_written_media_file( trak, stsc_data->sample_description_index );
2643 stsc_entry = stsc_entry->next;
2644 stsc_data = stsc_entry ? (isom_stsc_entry_t *)stsc_entry->data : NULL;
2645 if( ref_file != trak->file )
2647 /* The chunks are not contained in the same file. Skip applying the offset.
2648 * If no more stsc entries, the rest of the chunks is not contained in the same file. */
2649 if( !stsc_entry || !stsc_data )
2650 break;
2651 while( stco_entry && chunk_number < stsc_data->first_chunk )
2653 stco_entry = stco_entry->next;
2654 ++chunk_number;
2656 continue;
2659 if( stco->large_presentation )
2660 ((isom_co64_entry_t *)stco_entry->data)->chunk_offset += preceding_size;
2661 else
2662 ((isom_stco_entry_t *)stco_entry->data)->chunk_offset += preceding_size;
2663 stco_entry = stco_entry->next;
2664 ++chunk_number;
2669 int isom_establish_movie( lsmash_file_t *file )
2671 assert( file == file->initializer );
2672 int err;
2673 if( (err = isom_check_mandatory_boxes( file )) < 0
2674 || (err = isom_set_movie_creation_time( file )) < 0 )
2675 return err;
2676 if( isom_update_box_size( file->moov ) == 0 )
2677 return LSMASH_ERR_INVALID_DATA;
2678 return 0;
2681 int lsmash_finish_movie
2683 lsmash_root_t *root,
2684 lsmash_adhoc_remux_t *remux
2687 if( isom_check_initializer_present( root ) < 0 )
2688 return LSMASH_ERR_FUNCTION_PARAM;
2689 lsmash_file_t *file = root->file;
2690 if( !file
2691 || !file->bs
2692 || !file->initializer->moov )
2693 return LSMASH_ERR_INVALID_DATA;
2694 if( file->fragment )
2695 return isom_finish_final_fragment_movie( file, remux );
2696 if( file != file->initializer )
2697 return LSMASH_ERR_INVALID_DATA;
2698 int err;
2699 isom_moov_t *moov = file->moov;
2700 for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next )
2702 isom_trak_t *trak = (isom_trak_t *)entry->data;
2703 if( !trak
2704 || !trak->cache
2705 || !trak->tkhd
2706 || !trak->mdia
2707 || !trak->mdia->minf
2708 || !trak->mdia->minf->stbl
2709 || !trak->mdia->minf->stbl->stco
2710 || !trak->mdia->minf->stbl->stco->list
2711 || !trak->mdia->minf->stbl->stco->list->tail )
2712 return LSMASH_ERR_INVALID_DATA;
2713 if( (err = isom_complement_data_reference( trak->mdia->minf )) < 0 )
2714 return err;
2715 uint32_t track_ID = trak->tkhd->track_ID;
2716 uint32_t related_track_ID = trak->related_track_ID;
2717 /* Disable the track if the track is a track reference chapter. */
2718 if( trak->is_chapter )
2719 trak->tkhd->flags &= ~ISOM_TRACK_ENABLED;
2720 if( trak->is_chapter && related_track_ID )
2722 /* In order that the track duration of the chapter track doesn't exceed that of the related track. */
2723 lsmash_edit_t edit;
2724 edit.duration = LSMASH_MIN( trak->tkhd->duration, lsmash_get_track_duration( root, related_track_ID ) );
2725 edit.start_time = 0;
2726 edit.rate = ISOM_EDIT_MODE_NORMAL;
2727 if( (err = lsmash_create_explicit_timeline_map( root, track_ID, edit )) < 0 )
2728 return err;
2730 /* Add stss box if any samples aren't sync sample. */
2731 isom_stbl_t *stbl = trak->mdia->minf->stbl;
2732 if( !trak->cache->all_sync && !stbl->stss && !isom_add_stss( stbl ) )
2733 return LSMASH_ERR_NAMELESS;
2734 if( (err = isom_update_tkhd_duration( trak )) < 0
2735 || (err = isom_update_bitrate_description( trak->mdia )) < 0 )
2736 return err;
2738 if( file->mp4_version1 == 1 && (err = isom_setup_iods( moov )) < 0 )
2739 return err;
2740 if( (err = isom_establish_movie( file )) < 0 )
2741 return err;
2742 /* Write the size of Media Data Box here. */
2743 lsmash_bs_t *bs = file->bs;
2744 file->mdat->manager &= ~LSMASH_INCOMPLETE_BOX;
2745 if( (err = isom_write_box( bs, (isom_box_t *)file->mdat )) < 0 )
2746 return err;
2747 /* Write the Movie Box and a Meta Box if no optimization for progressive download. */
2748 uint64_t meta_size = file->meta ? file->meta->size : 0;
2749 if( !remux )
2751 if( (err = isom_write_box( bs, (isom_box_t *)file->moov )) < 0
2752 || (err = isom_write_box( bs, (isom_box_t *)file->meta )) < 0 )
2753 return err;
2754 file->size += moov->size + meta_size;
2755 return 0;
2757 /* stco->co64 conversion, depending on last chunk's offset */
2758 if( (err = isom_check_large_offset_requirement( moov, meta_size )) < 0 )
2759 return err;
2760 /* now the amount of offset is fixed. */
2761 uint64_t mtf_size = moov->size + meta_size; /* sum of size of boxes moved to front */
2762 /* buffer size must be at least mtf_size * 2 */
2763 remux->buffer_size = LSMASH_MAX( remux->buffer_size, mtf_size * 2 );
2764 /* Split to 2 buffers. */
2765 uint8_t *buf[2] = { NULL, NULL };
2766 if( (buf[0] = (uint8_t*)lsmash_malloc( remux->buffer_size )) == NULL )
2767 return LSMASH_ERR_MEMORY_ALLOC; /* NOTE: I think we still can fallback to "return isom_write_moov();" here. */
2768 size_t size = remux->buffer_size / 2;
2769 buf[1] = buf[0] + size;
2770 /* Now, the amount of the offset is fixed. apply it to stco/co64 */
2771 isom_add_preceding_box_size( moov, mtf_size );
2772 /* Backup starting area of mdat and write moov + meta there instead. */
2773 isom_mdat_t *mdat = file->mdat;
2774 uint64_t total = file->size + mtf_size;
2775 uint64_t placeholder_pos = file->free ? file->free->pos : mdat->pos;
2776 if( (err = lsmash_bs_write_seek( bs, placeholder_pos, SEEK_SET )) < 0 )
2777 goto fail;
2778 size_t read_num = size;
2779 lsmash_bs_read_data( bs, buf[0], &read_num );
2780 uint64_t read_pos = bs->offset;
2781 /* Write moov + meta there instead. */
2782 if( (err = lsmash_bs_write_seek( bs, placeholder_pos, SEEK_SET )) < 0
2783 || (err = isom_write_box( bs, (isom_box_t *)file->moov )) < 0
2784 || (err = isom_write_box( bs, (isom_box_t *)file->meta )) < 0 )
2785 goto fail;
2786 uint64_t write_pos = bs->offset;
2787 /* Update the positions */
2788 mdat->pos += mtf_size;
2789 if( file->free )
2790 file->free->pos += mtf_size;
2791 /* Move Media Data Box. */
2792 if( (err = isom_rearrange_data( file, remux, buf, read_num, size, read_pos, write_pos, total )) < 0 )
2793 goto fail;
2794 file->size += mtf_size;
2795 lsmash_free( buf[0] );
2796 return 0;
2797 fail:
2798 lsmash_free( buf[0] );
2799 return err;
2802 int lsmash_set_last_sample_delta( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_delta )
2804 if( isom_check_initializer_present( root ) < 0 || track_ID == 0 )
2805 return LSMASH_ERR_FUNCTION_PARAM;
2806 lsmash_file_t *file = root->file;
2807 if( file->fragment
2808 && file->fragment->movie )
2810 isom_traf_t *traf = isom_get_traf( file->fragment->movie, track_ID );
2811 if( !traf
2812 || !traf->cache
2813 || !traf->tfhd )
2814 return LSMASH_ERR_NAMELESS;
2815 return isom_set_fragment_last_duration( traf, sample_delta );
2817 if( file != file->initializer )
2818 return LSMASH_ERR_INVALID_DATA;
2819 isom_trak_t *trak = isom_get_trak( file, track_ID );
2820 if( !trak
2821 || !trak->cache
2822 || !trak->mdia
2823 || !trak->mdia->mdhd
2824 || !trak->mdia->minf
2825 || !trak->mdia->minf->stbl
2826 || !trak->mdia->minf->stbl->stsd
2827 || !trak->mdia->minf->stbl->stsz
2828 || !trak->mdia->minf->stbl->stts
2829 || !trak->mdia->minf->stbl->stts->list )
2830 return LSMASH_ERR_NAMELESS;
2831 isom_stbl_t *stbl = trak->mdia->minf->stbl;
2832 isom_stts_t *stts = stbl->stts;
2833 uint32_t sample_count = isom_get_sample_count( trak );
2834 int err;
2835 if( !stts->list->tail )
2837 if( !sample_count )
2838 return 0; /* no samples */
2839 if( sample_count > 1 )
2840 return LSMASH_ERR_INVALID_DATA; /* irregular sample_count */
2841 /* Set the duration of the first sample.
2842 * This duration is also the duration of the last sample. */
2843 if( (err = isom_add_stts_entry( stbl, sample_delta )) < 0 )
2844 return err;
2845 return lsmash_update_track_duration( root, track_ID, 0 );
2847 uint32_t i = 0;
2848 for( lsmash_entry_t *entry = stts->list->head; entry; entry = entry->next )
2849 i += ((isom_stts_entry_t *)entry->data)->sample_count;
2850 if( sample_count < i )
2851 return LSMASH_ERR_INVALID_DATA;
2852 int no_last = (sample_count > i);
2853 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stts->list->tail->data;
2854 if( !last_stts_data )
2855 return LSMASH_ERR_INVALID_DATA;
2856 /* Consider QuikcTime fixed compression audio. */
2857 isom_audio_entry_t *audio = (isom_audio_entry_t *)lsmash_get_entry_data( &trak->mdia->minf->stbl->stsd->list,
2858 trak->cache->chunk.sample_description_index );
2859 if( !audio )
2860 return LSMASH_ERR_INVALID_DATA;
2861 if( (audio->manager & LSMASH_AUDIO_DESCRIPTION)
2862 && (audio->manager & LSMASH_QTFF_BASE)
2863 && (audio->version == 1)
2864 && (audio->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION) )
2866 if( audio->samplesPerPacket == 0 )
2867 return LSMASH_ERR_INVALID_DATA;
2868 int exclude_last_sample = no_last ? 0 : 1;
2869 uint32_t j = audio->samplesPerPacket;
2870 for( lsmash_entry_t *entry = stts->list->tail; entry && j > 1; entry = entry->prev )
2872 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)entry->data;
2873 if( !stts_data )
2874 return LSMASH_ERR_INVALID_DATA;
2875 for( uint32_t k = exclude_last_sample; k < stts_data->sample_count && j > 1; k++ )
2877 sample_delta -= stts_data->sample_delta;
2878 --j;
2880 exclude_last_sample = 0;
2883 /* Set sample_delta. */
2884 if( no_last )
2886 /* The duration of the last sample is not set yet. */
2887 if( sample_count - i > 1 )
2888 return LSMASH_ERR_INVALID_DATA;
2889 /* Add a sample_delta. */
2890 if( sample_delta == last_stts_data->sample_delta )
2891 ++ last_stts_data->sample_count;
2892 else if( (err = isom_add_stts_entry( stbl, sample_delta )) < 0 )
2893 return err;
2895 /* The duration of the last sample is already set. Replace it with a new one. */
2896 else if( (err = isom_replace_last_sample_delta( stbl, sample_delta )) < 0 )
2897 return err;
2898 return lsmash_update_track_duration( root, track_ID, sample_delta );
2901 /*---- timeline manipulator ----*/
2903 int lsmash_modify_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t edit_number, lsmash_edit_t edit )
2905 if( isom_check_initializer_present( root ) < 0
2906 || edit.start_time < -1 )
2907 return LSMASH_ERR_FUNCTION_PARAM;
2908 lsmash_file_t *file = root->file->initializer;
2909 isom_trak_t *trak = isom_get_trak( file, track_ID );
2910 if( !trak
2911 || !trak->edts
2912 || !trak->edts->elst
2913 || !trak->edts->elst->list )
2914 return LSMASH_ERR_NAMELESS;
2915 isom_elst_t *elst = trak->edts->elst;
2916 isom_elst_entry_t *data = (isom_elst_entry_t *)lsmash_get_entry_data( elst->list, edit_number );
2917 if( !data )
2918 return LSMASH_ERR_NAMELESS;
2919 data->segment_duration = edit.duration;
2920 data->media_time = edit.start_time;
2921 data->media_rate = edit.rate;
2922 if( elst->pos == 0 || !file->fragment || file->bs->unseekable )
2923 return isom_update_tkhd_duration( trak );
2924 /* Rewrite the specified entry.
2925 * Note: we don't update the version of the Edit List Box. */
2926 lsmash_bs_t *bs = file->bs;
2927 uint64_t current_pos = bs->offset;
2928 uint64_t entry_pos = elst->pos + ISOM_LIST_FULLBOX_COMMON_SIZE + ((uint64_t)edit_number - 1) * (elst->version == 1 ? 20 : 12);
2929 lsmash_bs_write_seek( bs, entry_pos, SEEK_SET );
2930 if( elst->version )
2932 lsmash_bs_put_be64( bs, data->segment_duration );
2933 lsmash_bs_put_be64( bs, data->media_time );
2935 else
2937 lsmash_bs_put_be32( bs, (uint32_t)LSMASH_MIN( data->segment_duration, UINT32_MAX ) );
2938 lsmash_bs_put_be32( bs, (uint32_t)data->media_time );
2940 lsmash_bs_put_be32( bs, data->media_rate );
2941 int ret = lsmash_bs_flush_buffer( bs );
2942 lsmash_bs_write_seek( bs, current_pos, SEEK_SET );
2943 return ret;
2946 int lsmash_create_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, lsmash_edit_t edit )
2948 if( isom_check_initializer_present( root ) < 0 || edit.start_time < -1 )
2949 return LSMASH_ERR_FUNCTION_PARAM;
2950 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
2951 if( !trak
2952 || !trak->tkhd )
2953 return LSMASH_ERR_NAMELESS;
2954 edit.duration = (edit.duration || root->file->fragment) ? edit.duration
2955 : trak->tkhd->duration ? trak->tkhd->duration
2956 : isom_update_tkhd_duration( trak ) < 0 ? 0
2957 : trak->tkhd->duration;
2958 if( (!trak->edts && !isom_add_edts( trak ))
2959 || (!trak->edts->elst && !isom_add_elst( trak->edts )) )
2960 return LSMASH_ERR_NAMELESS;
2961 int err = isom_add_elst_entry( trak->edts->elst, edit.duration, edit.start_time, edit.rate );
2962 if( err < 0 )
2963 return err;
2964 return isom_update_tkhd_duration( trak );
2967 int lsmash_get_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t edit_number, lsmash_edit_t *edit )
2969 if( isom_check_initializer_present( root ) < 0 || !edit )
2970 return LSMASH_ERR_FUNCTION_PARAM;
2971 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
2972 if( !trak )
2973 return LSMASH_ERR_NAMELESS;
2974 if( !trak->edts
2975 || !trak->edts->elst )
2977 /* no edits */
2978 edit->duration = 0;
2979 edit->start_time = 0;
2980 edit->rate = 0;
2981 return 0;
2983 isom_elst_entry_t *elst = (isom_elst_entry_t *)lsmash_get_entry_data( trak->edts->elst->list, edit_number );
2984 if( !elst )
2985 return LSMASH_ERR_NAMELESS;
2986 edit->duration = elst->segment_duration;
2987 edit->start_time = elst->media_time;
2988 edit->rate = elst->media_rate;
2989 return 0;
2992 uint32_t lsmash_count_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID )
2994 if( isom_check_initializer_present( root ) < 0 )
2995 return LSMASH_ERR_FUNCTION_PARAM;
2996 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
2997 if( !trak
2998 || !trak->edts
2999 || !trak->edts->elst
3000 || !trak->edts->elst->list )
3001 return 0;
3002 return trak->edts->elst->list->entry_count;
3005 /*---- create / modification time fields manipulators ----*/
3007 int lsmash_update_media_modification_time( lsmash_root_t *root, uint32_t track_ID )
3009 if( isom_check_initializer_present( root ) < 0 )
3010 return LSMASH_ERR_FUNCTION_PARAM;
3011 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
3012 if( !trak
3013 || !trak->mdia
3014 || !trak->mdia->mdhd )
3015 return LSMASH_ERR_NAMELESS;
3016 isom_mdhd_t *mdhd = trak->mdia->mdhd;
3017 mdhd->modification_time = isom_get_current_mp4time();
3018 /* overwrite strange creation_time */
3019 if( mdhd->creation_time > mdhd->modification_time )
3020 mdhd->creation_time = mdhd->modification_time;
3021 return 0;
3024 int lsmash_update_track_modification_time( lsmash_root_t *root, uint32_t track_ID )
3026 if( isom_check_initializer_present( root ) < 0 )
3027 return LSMASH_ERR_FUNCTION_PARAM;
3028 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
3029 if( !trak
3030 || !trak->tkhd )
3031 return LSMASH_ERR_NAMELESS;
3032 isom_tkhd_t *tkhd = trak->tkhd;
3033 tkhd->modification_time = isom_get_current_mp4time();
3034 /* overwrite strange creation_time */
3035 if( tkhd->creation_time > tkhd->modification_time )
3036 tkhd->creation_time = tkhd->modification_time;
3037 return 0;
3040 int lsmash_update_movie_modification_time( lsmash_root_t *root )
3042 if( isom_check_initializer_present( root ) < 0 )
3043 return LSMASH_ERR_FUNCTION_PARAM;
3044 lsmash_file_t *file = root->file->initializer;
3045 if( !file->moov
3046 || !file->moov->mvhd )
3047 return LSMASH_ERR_INVALID_DATA;
3048 isom_mvhd_t *mvhd = file->moov->mvhd;
3049 mvhd->modification_time = isom_get_current_mp4time();
3050 /* overwrite strange creation_time */
3051 if( mvhd->creation_time > mvhd->modification_time )
3052 mvhd->creation_time = mvhd->modification_time;
3053 return 0;
3056 /*---- sample manipulators ----*/
3057 lsmash_sample_t *lsmash_create_sample( uint32_t size )
3059 lsmash_sample_t *sample = lsmash_malloc_zero( sizeof(lsmash_sample_t) );
3060 if( !sample )
3061 return NULL;
3062 if( size == 0 )
3063 return sample;
3064 sample->data = lsmash_malloc( size );
3065 if( !sample->data )
3067 lsmash_free( sample );
3068 return NULL;
3070 sample->length = size;
3071 return sample;
3074 int lsmash_sample_alloc( lsmash_sample_t *sample, uint32_t size )
3076 if( !sample )
3077 return LSMASH_ERR_FUNCTION_PARAM;
3078 if( size == 0 )
3080 lsmash_free( sample->data );
3081 sample->data = NULL;
3082 sample->length = 0;
3083 return 0;
3085 if( size == sample->length )
3086 return 0;
3087 uint8_t *data;
3088 if( !sample->data )
3089 data = lsmash_malloc( size );
3090 else
3091 data = lsmash_realloc( sample->data, size );
3092 if( !data )
3093 return LSMASH_ERR_MEMORY_ALLOC;
3094 sample->data = data;
3095 sample->length = size;
3096 return 0;
3099 void lsmash_delete_sample( lsmash_sample_t *sample )
3101 if( !sample )
3102 return;
3103 lsmash_free( sample->data );
3104 lsmash_free( sample );
3107 isom_sample_pool_t *isom_create_sample_pool( uint64_t size )
3109 isom_sample_pool_t *pool = lsmash_malloc_zero( sizeof(isom_sample_pool_t) );
3110 if( !pool )
3111 return NULL;
3112 if( size == 0 )
3113 return pool;
3114 pool->data = lsmash_malloc( size );
3115 if( !pool->data )
3117 lsmash_free( pool );
3118 return NULL;
3120 pool->alloc = size;
3121 return pool;
3124 void isom_remove_sample_pool( isom_sample_pool_t *pool )
3126 if( !pool )
3127 return;
3128 lsmash_free( pool->data );
3129 lsmash_free( pool );
3132 static uint32_t isom_add_size( isom_trak_t *trak, uint32_t sample_size )
3134 if( isom_add_stsz_entry( trak->mdia->minf->stbl, sample_size ) < 0 )
3135 return 0;
3136 return isom_get_sample_count( trak );
3139 static uint32_t isom_add_dts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t dts )
3141 isom_stts_t *stts = stbl->stts;
3142 if( stts->list->entry_count == 0 )
3144 if( isom_add_stts_entry( stbl, dts ) < 0 )
3145 return 0;
3146 cache->dts = dts;
3147 return dts;
3149 if( dts <= cache->dts )
3150 return 0;
3151 uint32_t sample_delta = dts - cache->dts;
3152 isom_stts_entry_t *data = (isom_stts_entry_t *)stts->list->tail->data;
3153 if( data->sample_delta == sample_delta )
3154 ++ data->sample_count;
3155 else if( isom_add_stts_entry( stbl, sample_delta ) < 0 )
3156 return 0;
3157 cache->dts = dts;
3158 return sample_delta;
3161 static int isom_add_cts( isom_stbl_t *stbl, isom_timestamp_t *cache, uint64_t cts )
3163 int err;
3164 isom_ctts_t *ctts = stbl->ctts;
3165 if( !ctts )
3167 if( cts == cache->dts )
3169 cache->cts = cts;
3170 return 0;
3172 /* Add ctts box and the first ctts entry. */
3173 if( !isom_add_ctts( stbl ) )
3174 return LSMASH_ERR_NAMELESS;
3175 if( (err = isom_add_ctts_entry( stbl, 0 )) < 0 )
3176 return err;
3177 ctts = stbl->ctts;
3178 isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->head->data;
3179 uint32_t sample_count = stbl->stsz->sample_count;
3180 if( sample_count != 1 )
3182 data->sample_count = sample_count - 1;
3183 if( (err = isom_add_ctts_entry( stbl, cts - cache->dts )) < 0 )
3184 return err;
3186 else
3187 data->sample_offset = cts;
3188 cache->cts = cts;
3189 return 0;
3191 if( !ctts->list )
3192 return LSMASH_ERR_INVALID_DATA;
3193 isom_ctts_entry_t *data = (isom_ctts_entry_t *)ctts->list->tail->data;
3194 uint32_t sample_offset = cts - cache->dts;
3195 if( data->sample_offset == sample_offset )
3196 ++ data->sample_count;
3197 else if( (err = isom_add_ctts_entry( stbl, sample_offset )) < 0 )
3198 return err;
3199 cache->cts = cts;
3200 return 0;
3203 static int isom_add_timestamp( isom_trak_t *trak, uint64_t dts, uint64_t cts )
3205 if( !trak->cache
3206 || !trak->mdia->minf->stbl->stts
3207 || !trak->mdia->minf->stbl->stts->list )
3208 return LSMASH_ERR_INVALID_DATA;
3209 lsmash_file_t *file = trak->file;
3210 if( file->isom_compatible && file->qt_compatible && (cts - dts) > INT32_MAX )
3211 return LSMASH_ERR_INVALID_DATA; /* sample_offset is not compatible. */
3212 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3213 isom_timestamp_t *ts_cache = &trak->cache->timestamp;
3214 uint32_t sample_count = isom_get_sample_count( trak );
3215 uint32_t sample_delta = sample_count > 1 ? isom_add_dts( stbl, ts_cache, dts ) : 0;
3216 if( sample_count > 1 && sample_delta == 0 )
3217 return LSMASH_ERR_INVALID_DATA;
3218 int err = isom_add_cts( stbl, ts_cache, cts );
3219 if( err < 0 )
3220 return err;
3221 if( (cts + ts_cache->ctd_shift) < dts )
3223 if( (file->max_isom_version < 4 && !file->qt_compatible) /* Negative sample offset is not supported. */
3224 || (file->max_isom_version >= 4 && file->qt_compatible) /* ctts version 1 is not defined in QTFF. */
3225 || ((dts - cts) > INT32_MAX) ) /* Overflow */
3226 return LSMASH_ERR_INVALID_DATA;
3227 ts_cache->ctd_shift = dts - cts;
3228 if( stbl->ctts->version == 0 && !file->qt_compatible )
3229 stbl->ctts->version = 1;
3231 if( trak->cache->fragment )
3233 isom_fragment_t *fragment_cache = trak->cache->fragment;
3234 fragment_cache->last_duration = sample_delta;
3235 fragment_cache->largest_cts = LSMASH_MAX( ts_cache->cts, fragment_cache->largest_cts );
3237 return 0;
3240 static int isom_add_sync_point( isom_trak_t *trak, uint32_t sample_number, lsmash_sample_property_t *prop )
3242 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3243 isom_cache_t *cache = trak->cache;
3244 if( !(prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC) ) /* no null check for prop */
3246 if( !cache->all_sync )
3247 return 0;
3248 if( !stbl->stss && !isom_add_stss( stbl ) )
3249 return LSMASH_ERR_NAMELESS;
3250 int err = isom_add_stss_entry( stbl, 1 );
3251 if( err < 0 ) /* Declare here the first sample is a sync sample. */
3252 return err;
3253 cache->all_sync = 0;
3254 return 0;
3256 if( cache->all_sync ) /* We don't need stss box if all samples are sync sample. */
3257 return 0;
3258 if( !stbl->stss )
3260 if( isom_get_sample_count( trak ) == 1 )
3262 cache->all_sync = 1; /* Also the first sample is a sync sample. */
3263 return 0;
3265 if( !isom_add_stss( stbl ) )
3266 return LSMASH_ERR_NAMELESS;
3268 return isom_add_stss_entry( stbl, sample_number );
3271 static int isom_add_partial_sync( isom_trak_t *trak, uint32_t sample_number, lsmash_sample_property_t *prop )
3273 if( !trak->file->qt_compatible )
3274 return 0;
3275 if( !(prop->ra_flags & QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC) )
3276 return 0;
3277 /* This sample is a partial sync sample. */
3278 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3279 if( !stbl->stps && !isom_add_stps( stbl ) )
3280 return LSMASH_ERR_NAMELESS;
3281 return isom_add_stps_entry( stbl, sample_number );
3284 static int isom_add_dependency_type( isom_trak_t *trak, lsmash_sample_property_t *prop )
3286 if( !trak->file->qt_compatible && !trak->file->avc_extensions )
3287 return 0;
3288 int compatibility = trak->file->avc_extensions && trak->file->qt_compatible ? 3
3289 : trak->file->qt_compatible ? 2
3290 : trak->file->avc_extensions ? 1
3291 : 0;
3292 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3293 if( stbl->sdtp )
3294 return isom_add_sdtp_entry( (isom_box_t *)stbl, prop, compatibility );
3295 /* no null check for prop */
3296 if( !prop->allow_earlier
3297 && !prop->leading
3298 && !prop->independent
3299 && !prop->disposable
3300 && !prop->redundant )
3301 return 0;
3302 if( !isom_add_sdtp( (isom_box_t *)stbl ) )
3303 return LSMASH_ERR_NAMELESS;
3304 uint32_t count = isom_get_sample_count( trak );
3305 /* fill past samples with ISOM_SAMPLE_*_UNKNOWN */
3306 lsmash_sample_property_t null_prop = { 0 };
3307 for( uint32_t i = 1; i < count; i++ )
3309 int err = isom_add_sdtp_entry( (isom_box_t *)stbl, &null_prop, compatibility );
3310 if( err < 0 )
3311 return err;
3313 return isom_add_sdtp_entry( (isom_box_t *)stbl, prop, compatibility );
3316 int isom_rap_grouping_established( isom_rap_group_t *group, int num_leading_samples_known, isom_sgpd_t *sgpd, int is_fragment )
3318 isom_rap_entry_t *rap = group->random_access;
3319 if( !rap )
3320 return 0;
3321 assert( rap == (isom_rap_entry_t *)sgpd->list->tail->data );
3322 rap->num_leading_samples_known = num_leading_samples_known;
3323 /* Avoid duplication of sample group descriptions. */
3324 uint32_t group_description_index = is_fragment ? 0x10001 : 1;
3325 for( lsmash_entry_t *entry = sgpd->list->head; entry != sgpd->list->tail; entry = entry->next )
3327 isom_rap_entry_t *data = (isom_rap_entry_t *)entry->data;
3328 if( !data )
3329 return LSMASH_ERR_INVALID_DATA;
3330 if( rap->num_leading_samples_known == data->num_leading_samples_known
3331 && rap->num_leading_samples == data->num_leading_samples )
3333 /* The same description already exists.
3334 * Remove the latest random access entry. */
3335 lsmash_remove_entry_tail( sgpd->list, NULL );
3336 /* Replace assigned group_description_index with the one corresponding the same description. */
3337 if( group->assignment->group_description_index == 0 )
3339 /* We don't create consecutive sample groups not assigned to 'rap '.
3340 * So the previous sample group shall be a group of 'rap ' if any. */
3341 if( group->prev_assignment )
3343 assert( group->prev_assignment->group_description_index );
3344 group->prev_assignment->group_description_index = group_description_index;
3347 else
3348 group->assignment->group_description_index = group_description_index;
3349 break;
3351 ++group_description_index;
3353 group->random_access = NULL;
3354 return 0;
3357 int isom_group_random_access( isom_box_t *parent, lsmash_sample_t *sample )
3359 if( parent->file->max_isom_version < 6 )
3360 return 0;
3361 isom_sbgp_t *sbgp;
3362 isom_sgpd_t *sgpd;
3363 isom_cache_t *cache;
3364 uint32_t sample_count;
3365 int is_fragment;
3366 if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) )
3368 isom_trak_t *trak = (isom_trak_t *)parent;
3369 sbgp = isom_get_sample_to_group ( trak->mdia->minf->stbl, ISOM_GROUP_TYPE_RAP );
3370 sgpd = isom_get_sample_group_description( trak->mdia->minf->stbl, ISOM_GROUP_TYPE_RAP );
3371 cache = trak->cache;
3372 sample_count = isom_get_sample_count( trak );
3373 is_fragment = 0;
3375 else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) )
3377 isom_traf_t *traf = (isom_traf_t *)parent;
3378 sbgp = isom_get_fragment_sample_to_group ( traf, ISOM_GROUP_TYPE_RAP );
3379 sgpd = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_RAP );
3380 cache = traf->cache;
3381 sample_count = cache->fragment->sample_count + 1;
3382 is_fragment = 1;
3384 else
3386 assert( 0 );
3387 sbgp = NULL;
3388 sgpd = NULL;
3389 /* redundant initializations to suppress warnings from unclever compilers */
3390 cache = NULL;
3391 sample_count = 0;
3392 is_fragment = 0;
3394 if( !sbgp || !sgpd )
3395 return 0;
3396 lsmash_sample_property_t *prop = &sample->prop;
3397 uint8_t is_rap = (prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC)
3398 || (prop->ra_flags & QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC)
3399 || (prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP)
3400 || (LSMASH_IS_POST_ROLL_START( prop->ra_flags ) && prop->post_roll.identifier == prop->post_roll.complete);
3401 isom_rap_group_t *group = cache->rap;
3402 if( !group )
3404 /* This sample is the first sample, create a grouping cache. */
3405 assert( sample_count == 1 );
3406 group = lsmash_malloc( sizeof(isom_rap_group_t) );
3407 if( !group )
3408 return LSMASH_ERR_MEMORY_ALLOC;
3409 if( is_rap )
3411 group->random_access = isom_add_rap_group_entry( sgpd );
3412 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count + (is_fragment ? 0x10000 : 0) );
3414 else
3416 /* The first sample is not always a random access point. */
3417 group->random_access = NULL;
3418 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
3420 if( !group->assignment )
3422 lsmash_free( group );
3423 return LSMASH_ERR_MEMORY_ALLOC;
3425 group->prev_assignment = NULL;
3426 group->is_prev_rap = is_rap;
3427 cache->rap = group;
3428 return 0;
3430 int err;
3431 if( group->is_prev_rap )
3433 /* OK. here, the previous sample is a menber of 'rap '. */
3434 if( !is_rap )
3436 /* This sample isn't a member of 'rap ' and the previous sample is.
3437 * So we create a new group and set 0 on its group_description_index. */
3438 group->prev_assignment = group->assignment;
3439 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
3440 if( !group->assignment )
3442 lsmash_free( group );
3443 return LSMASH_ERR_MEMORY_ALLOC;
3446 else if( !LSMASH_IS_CLOSED_RAP( prop->ra_flags ) )
3448 /* Create a new group since there is the possibility the next sample is a leading sample.
3449 * This sample is a member of 'rap ', so we set appropriate value on its group_description_index. */
3450 if( (err = isom_rap_grouping_established( group, 1, sgpd, is_fragment )) < 0 )
3451 return err;
3452 group->random_access = isom_add_rap_group_entry( sgpd );
3453 group->prev_assignment = group->assignment;
3454 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count + (is_fragment ? 0x10000 : 0) );
3455 if( !group->assignment )
3457 lsmash_free( group );
3458 return LSMASH_ERR_MEMORY_ALLOC;
3461 else /* The previous and current sample are a member of 'rap ', and the next sample must not be a leading sample. */
3462 ++ group->assignment->sample_count;
3464 else if( is_rap )
3466 /* This sample is a member of 'rap ' and the previous sample isn't.
3467 * So we create a new group and set appropriate value on its group_description_index. */
3468 if( (err = isom_rap_grouping_established( group, 1, sgpd, is_fragment )) < 0 )
3469 return err;
3470 group->random_access = isom_add_rap_group_entry( sgpd );
3471 group->prev_assignment = group->assignment;
3472 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count + (is_fragment ? 0x10000 : 0) );
3473 if( !group->assignment )
3475 lsmash_free( group );
3476 return LSMASH_ERR_MEMORY_ALLOC;
3479 else /* The previous and current sample aren't a member of 'rap '. */
3480 ++ group->assignment->sample_count;
3481 /* Obtain the property of the latest random access point group. */
3482 if( !is_rap && group->random_access )
3484 if( prop->leading == ISOM_SAMPLE_LEADING_UNKNOWN )
3486 /* We can no longer know num_leading_samples in this group. */
3487 if( (err = isom_rap_grouping_established( group, 0, sgpd, is_fragment )) < 0 )
3488 return err;
3490 else
3492 if( prop->leading == ISOM_SAMPLE_IS_UNDECODABLE_LEADING
3493 || prop->leading == ISOM_SAMPLE_IS_DECODABLE_LEADING )
3494 ++ group->random_access->num_leading_samples;
3495 /* no more consecutive leading samples in this group */
3496 else if( (err = isom_rap_grouping_established( group, 1, sgpd, is_fragment )) < 0 )
3497 return err;
3500 group->is_prev_rap = is_rap;
3501 return 0;
3504 static int isom_roll_grouping_established( isom_roll_group_t *group )
3506 /* Avoid duplication of sample group descriptions. */
3507 isom_sgpd_t *sgpd = group->sgpd;
3508 uint32_t group_description_index = group->is_fragment ? 0x10001 : 1;
3509 for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next )
3511 isom_roll_entry_t *data = (isom_roll_entry_t *)entry->data;
3512 if( !data )
3513 return LSMASH_ERR_INVALID_DATA;
3514 if( group->roll_distance == data->roll_distance )
3516 /* The same description already exists.
3517 * Set the group_description_index corresponding the same description. */
3518 group->assignment->group_description_index = group_description_index;
3519 return 0;
3521 ++group_description_index;
3523 /* Add a new roll recovery description. */
3524 if( !isom_add_roll_group_entry( sgpd, group->roll_distance ) )
3525 return LSMASH_ERR_MEMORY_ALLOC;
3526 group->assignment->group_description_index = sgpd->list->entry_count + (group->is_fragment ? 0x10000 : 0);
3527 return 0;
3530 static int isom_deduplicate_roll_group( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool )
3532 /* Deduplication */
3533 uint32_t current_group_number = sbgp->list->entry_count - pool->entry_count + 1;
3534 isom_group_assignment_entry_t *prev_assignment = (isom_group_assignment_entry_t *)lsmash_get_entry_data( sbgp->list, current_group_number - 1 );
3535 for( lsmash_entry_t *entry = pool->head; entry; )
3537 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3538 if( !group
3539 || !group->assignment )
3540 return LSMASH_ERR_INVALID_DATA;
3541 if( !group->delimited || group->described != ROLL_DISTANCE_DETERMINED )
3542 return 0;
3543 if( prev_assignment && prev_assignment->group_description_index == group->assignment->group_description_index )
3545 /* Merge the current group with the previous. */
3546 lsmash_entry_t *next_entry = entry->next;
3547 prev_assignment->sample_count += group->assignment->sample_count;
3548 int err;
3549 if( (err = lsmash_remove_entry( sbgp->list, current_group_number, NULL )) < 0
3550 || (err = lsmash_remove_entry_direct( pool, entry, NULL )) < 0 )
3551 return err;
3552 entry = next_entry;
3554 else
3556 entry = entry->next;
3557 prev_assignment = group->assignment;
3558 ++current_group_number;
3561 return 0;
3564 /* Remove pooled caches that has become unnecessary. */
3565 static int isom_clean_roll_pool( lsmash_entry_list_t *pool )
3567 for( lsmash_entry_t *entry = pool->head; entry; entry = pool->head )
3569 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3570 if( !group )
3571 return LSMASH_ERR_INVALID_DATA;
3572 if( !group->delimited || group->described != ROLL_DISTANCE_DETERMINED )
3573 return 0;
3574 int err = lsmash_remove_entry_direct( pool, entry, NULL );
3575 if( err < 0 )
3576 return err;
3578 return 0;
3581 static int isom_flush_roll_pool( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool )
3583 int err;
3584 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
3586 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3587 if( !group )
3588 return LSMASH_ERR_INVALID_DATA;
3589 if( group->delimited
3590 && group->described == ROLL_DISTANCE_DETERMINED
3591 && group->roll_distance != 0
3592 && (err = isom_roll_grouping_established( group )) < 0 )
3593 return err;
3595 if( (err = isom_deduplicate_roll_group( sbgp, pool )) < 0 )
3596 return err;
3597 return isom_clean_roll_pool( pool );
3600 static int isom_all_recovery_described( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool )
3602 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
3604 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3605 if( !group )
3606 return LSMASH_ERR_INVALID_DATA;
3607 group->described = ROLL_DISTANCE_DETERMINED;
3609 return isom_flush_roll_pool( sbgp, pool );
3612 int isom_all_recovery_completed( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool )
3614 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
3616 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3617 if( !group )
3618 return LSMASH_ERR_INVALID_DATA;
3619 group->described = ROLL_DISTANCE_DETERMINED;
3620 group->delimited = 1;
3622 return isom_flush_roll_pool( sbgp, pool );
3625 static isom_roll_entry_t *isom_get_roll_description
3627 isom_roll_group_t *group
3630 uint32_t group_description_index = group->assignment->group_description_index;
3631 if( group_description_index && group->is_fragment )
3633 assert( group_description_index > 0x10000 );
3634 group_description_index -= 0x10000;
3636 return (isom_roll_entry_t *)lsmash_get_entry_data( group->sgpd->list, group_description_index );
3639 int isom_group_roll_recovery( isom_box_t *parent, lsmash_sample_t *sample )
3641 if( !parent->file->avc_extensions
3642 && !parent->file->qt_compatible )
3643 return 0;
3644 uint32_t sample_count;
3645 int is_fragment;
3646 isom_cache_t *cache;
3647 lsmash_entry_list_t *sbgp_list;
3648 lsmash_entry_list_t *sgpd_list;
3649 if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAK ) )
3651 isom_trak_t *trak = (isom_trak_t *)parent;
3652 cache = trak->cache;
3653 sbgp_list = &trak->mdia->minf->stbl->sbgp_list;
3654 sgpd_list = &trak->mdia->minf->stbl->sgpd_list;
3655 sample_count = isom_get_sample_count( trak );
3656 is_fragment = 0;
3658 else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) )
3660 if( parent->file->max_isom_version < 6 )
3661 return 0;
3662 isom_traf_t *traf = (isom_traf_t *)parent;
3663 cache = traf->cache;
3664 sbgp_list = &traf->sbgp_list;
3665 sgpd_list = &traf->sgpd_list;
3666 sample_count = cache->fragment->sample_count + 1;
3667 is_fragment = 1;
3669 else
3671 assert( 0 );
3672 return LSMASH_ERR_INVALID_DATA;
3674 isom_sbgp_t *sbgp = isom_get_roll_recovery_sample_to_group ( sbgp_list );
3675 isom_sgpd_t *sgpd = isom_get_roll_recovery_sample_group_description( sgpd_list );
3676 if( !sbgp || !sgpd || sbgp->grouping_type != sgpd->grouping_type )
3677 return 0;
3678 /* Check if 'roll' -> 'prol' conversion is needed. */
3679 if( cache->is_audio
3680 && sbgp->grouping_type == ISOM_GROUP_TYPE_ROLL
3681 && !(sample->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC) )
3683 /* Since not every samples is a sync sample, change grouping_type into 'prol'. */
3684 sbgp->grouping_type = ISOM_GROUP_TYPE_PROL;
3685 sgpd->grouping_type = ISOM_GROUP_TYPE_PROL;
3687 lsmash_entry_list_t *pool = cache->roll.pool;
3688 if( !pool )
3690 pool = lsmash_create_entry_list();
3691 if( !pool )
3692 return LSMASH_ERR_MEMORY_ALLOC;
3693 cache->roll.pool = pool;
3695 lsmash_sample_property_t *prop = &sample->prop;
3696 isom_roll_group_t *group = (isom_roll_group_t *)lsmash_get_entry_data( pool, pool->entry_count );
3697 int is_recovery_start = LSMASH_IS_POST_ROLL_START( prop->ra_flags );
3698 int valid_pre_roll = !is_recovery_start
3699 && (prop->ra_flags != ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE)
3700 && (prop->pre_roll.distance > 0)
3701 && (prop->pre_roll.distance <= -INT16_MIN);
3702 int new_group = !group || is_recovery_start || (group->prev_is_recovery_start != is_recovery_start);
3703 if( !new_group )
3705 /* Check pre-roll distance. */
3706 assert( group->assignment && group->sgpd );
3707 isom_roll_entry_t *prev_roll = isom_get_roll_description( group );
3708 if( !prev_roll )
3709 new_group = valid_pre_roll;
3710 else if( !valid_pre_roll || (prop->pre_roll.distance != -prev_roll->roll_distance) )
3711 /* Pre-roll distance is different from the previous. */
3712 new_group = 1;
3714 if( new_group )
3716 if( group )
3717 group->delimited = 1;
3718 else
3719 assert( sample_count == 1 );
3720 /* Create a new group. */
3721 group = lsmash_malloc_zero( sizeof(isom_roll_group_t) );
3722 if( !group )
3723 return LSMASH_ERR_MEMORY_ALLOC;
3724 group->sgpd = sgpd;
3725 group->prev_is_recovery_start = is_recovery_start;
3726 group->is_fragment = is_fragment;
3727 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
3728 if( !group->assignment || lsmash_add_entry( pool, group ) < 0 )
3730 lsmash_free( group );
3731 return LSMASH_ERR_MEMORY_ALLOC;
3733 if( is_recovery_start )
3735 /* a member of non-roll or post-roll group */
3736 group->first_sample = sample_count;
3737 group->recovery_point = prop->post_roll.complete;
3739 else
3741 group->described = ROLL_DISTANCE_DETERMINED;
3742 if( valid_pre_roll )
3744 /* a member of pre-roll group */
3745 group->roll_distance = -prop->pre_roll.distance;
3746 int err = isom_roll_grouping_established( group );
3747 if( err < 0 )
3748 return err;
3750 else
3751 /* a member of non-roll group */
3752 group->roll_distance = 0;
3755 else
3757 group->prev_is_recovery_start = is_recovery_start;
3758 group->assignment->sample_count += 1;
3760 /* If encountered a RAP, all recovery is completed here. */
3761 if( prop->ra_flags & (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
3762 | ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
3763 | QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC) )
3764 return isom_all_recovery_described( sbgp, pool );
3765 /* Check whether this sample is a random access recovery point or not. */
3766 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
3768 group = (isom_roll_group_t *)entry->data;
3769 if( !group )
3770 return LSMASH_ERR_INVALID_DATA;
3771 if( group->described == ROLL_DISTANCE_DETERMINED )
3772 continue;
3773 if( group->described == ROLL_DISTANCE_INITIALIZED )
3775 /* Let's consider the following picture sequence.
3776 * coded order : P[0] P[1] P[2] P[3] P[4] P[5]
3777 * DTS : 0 1 2 3 4 5
3778 * CTS : 2 4 3 6 7 5
3779 * Here, P[0] conveys a recovery point SEI and P[3] is the recovery point.
3780 * Correctness of decoded pictures is specified by recovery point in output order for both AVC and HEVC.
3781 * Therefore, as follows,
3782 * output order : P[0] P[2] P[1] P[5]|P[3] P[4]
3783 * ---(incorrect?)--->|
3784 * there is no guarantee that P[5] is decoded and output correctly.
3785 * From this, it can be said that the roll_distance of this sequence is equal to 5. */
3786 isom_roll_entry_t *post_roll = isom_get_roll_description( group );
3787 if( post_roll && post_roll->roll_distance > 0 )
3789 if( group->rp_cts > sample->cts )
3790 /* Updated roll_distance for composition reordering. */
3791 post_roll->roll_distance = sample_count - group->first_sample;
3792 if( ++ group->wait_and_see_count >= MAX_ROLL_WAIT_AND_SEE_COUNT )
3793 group->described = ROLL_DISTANCE_DETERMINED;
3796 else if( prop->post_roll.identifier == group->recovery_point )
3798 int16_t distance = sample_count - group->first_sample;
3799 group->rp_cts = sample->cts;
3800 group->roll_distance = distance;
3801 /* Add a roll recovery entry only when roll_distance isn't zero since roll_distance = 0 must not be used. */
3802 if( distance )
3804 /* Now, this group is a 'roll'.
3805 * The roll_distance may be updated later because of composition reordering. */
3806 group->described = ROLL_DISTANCE_INITIALIZED;
3807 group->wait_and_see_count = 0;
3808 /* All groups with uninitialized roll_distance before the current group are described. */
3809 lsmash_entry_t *current = entry;
3810 for( entry = pool->head; entry != current; entry = entry->next )
3812 group = (isom_roll_group_t *)entry->data;
3813 if( !group || group->described != ROLL_DISTANCE_INITIALIZED )
3814 continue;
3815 group->described = ROLL_DISTANCE_DETERMINED;
3817 /* Cache the CTS of the first recovery point in a subsegment. */
3818 if( cache->fragment
3819 && cache->fragment->subsegment.first_rp_number == 0 )
3821 cache->fragment->subsegment.first_rp_number = sample_count;
3822 cache->fragment->subsegment.first_rp_cts = sample->cts;
3823 cache->fragment->subsegment.first_ed_cts = sample->cts;
3824 cache->fragment->subsegment.decodable = 1;
3827 else
3828 /* Random Accessible Point */
3829 return isom_all_recovery_described( sbgp, pool );
3832 return isom_flush_roll_pool( sbgp, pool );
3835 /* returns 1 if pooled samples must be flushed. */
3836 /* FIXME: I wonder if this function should have a extra argument which indicates force_to_flush_cached_chunk.
3837 see lsmash_append_sample for detail. */
3838 static int isom_add_chunk( isom_trak_t *trak, lsmash_sample_t *sample )
3840 if( !trak->file
3841 || !trak->cache
3842 || !trak->mdia->mdhd
3843 || trak->mdia->mdhd->timescale == 0
3844 || !trak->mdia->minf->dinf
3845 || !trak->mdia->minf->dinf->dref
3846 || !trak->mdia->minf->stbl->stsd
3847 || !trak->mdia->minf->stbl->stsc
3848 || !trak->mdia->minf->stbl->stsc->list )
3849 return LSMASH_ERR_INVALID_DATA;
3850 isom_chunk_t *current = &trak->cache->chunk;
3851 if( !current->pool )
3853 /* Very initial settings, just once per track */
3854 current->pool = isom_create_sample_pool( 0 );
3855 if( !current->pool )
3856 return LSMASH_ERR_MEMORY_ALLOC;
3858 if( current->pool->sample_count == 0 )
3860 /* Cannot decide whether we should flush the current sample or not here yet. */
3861 current->chunk_number += 1;
3862 current->sample_description_index = sample->index;
3863 current->first_dts = sample->dts;
3864 return 0;
3866 if( sample->dts < current->first_dts )
3867 return LSMASH_ERR_INVALID_DATA; /* easy error check. */
3868 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3869 lsmash_file_t *file = isom_get_written_media_file( trak, current->sample_description_index );
3870 if( (current->sample_description_index == sample->index)
3871 && (file->max_chunk_duration >= ((double)(sample->dts - current->first_dts) / trak->mdia->mdhd->timescale))
3872 && (file->max_chunk_size >= current->pool->size + sample->length) )
3873 return 0; /* No need to flush current cached chunk, the current sample must be put into that. */
3874 /* NOTE: chunk relative stuff must be pushed into file after a chunk is fully determined with its contents. */
3875 /* Now the current cached chunk is fixed, actually add the chunk relative properties to its file accordingly. */
3876 isom_stsc_entry_t *last_stsc_data = stbl->stsc->list->tail ? (isom_stsc_entry_t *)stbl->stsc->list->tail->data : NULL;
3877 /* Create a new chunk sequence in this track if needed. */
3878 int err;
3879 if( (!last_stsc_data
3880 || current->pool->sample_count != last_stsc_data->samples_per_chunk
3881 || current->sample_description_index != last_stsc_data->sample_description_index)
3882 && (err = isom_add_stsc_entry( stbl, current->chunk_number,
3883 current->pool->sample_count,
3884 current->sample_description_index )) < 0 )
3885 return err;
3886 /* Add a new chunk offset in this track. */
3887 uint64_t offset = file->size;
3888 if( file->fragment )
3889 offset += ISOM_BASEBOX_COMMON_SIZE + file->fragment->pool_size;
3890 if( (err = isom_add_stco_entry( stbl, offset )) < 0 )
3891 return err;
3892 /* Update and re-initialize cache, using the current sample */
3893 current->chunk_number += 1;
3894 current->sample_description_index = sample->index;
3895 current->first_dts = sample->dts;
3896 /* current->pool must be flushed in isom_append_sample_internal() */
3897 return 1;
3900 static int isom_write_pooled_samples( lsmash_file_t *file, isom_sample_pool_t *pool )
3902 if( !file
3903 || !file->bs
3904 || !file->bs->stream
3905 || !(file->flags & LSMASH_FILE_MODE_WRITE)
3906 || !(file->flags & LSMASH_FILE_MODE_MEDIA)
3907 || ((file->flags & LSMASH_FILE_MODE_BOX) && !file->mdat) )
3908 return LSMASH_ERR_INVALID_DATA;
3909 lsmash_bs_put_bytes( file->bs, pool->size, pool->data );
3910 int err = lsmash_bs_flush_buffer( file->bs );
3911 if( err < 0 )
3912 return err;
3913 if( file->mdat )
3914 file->mdat->media_size += pool->size;
3915 file->size += pool->size;
3916 pool->sample_count = 0;
3917 pool->size = 0;
3918 return 0;
3921 int isom_update_sample_tables( isom_trak_t *trak, lsmash_sample_t *sample, uint32_t *samples_per_packet )
3923 int err;
3924 isom_audio_entry_t *audio = (isom_audio_entry_t *)lsmash_get_entry_data( &trak->mdia->minf->stbl->stsd->list, sample->index );
3925 if( (audio->manager & LSMASH_AUDIO_DESCRIPTION)
3926 && (audio->manager & LSMASH_QTFF_BASE)
3927 && (audio->version == 1)
3928 && (audio->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION) )
3930 /* Add entries of the sample table for each uncompressed sample. */
3931 uint64_t sample_duration = trak->mdia->mdhd->timescale / (audio->samplerate >> 16);
3932 if( audio->samplesPerPacket == 0 || sample_duration == 0 )
3933 return LSMASH_ERR_INVALID_DATA;
3934 uint64_t sample_dts = sample->dts;
3935 uint64_t sample_cts = sample->cts;
3936 for( uint32_t i = 0; i < audio->samplesPerPacket; i++ )
3938 /* Add a size of uncomressed audio and increment sample_count.
3939 * This points to individual uncompressed audio samples, each one byte in size, within the compressed frames. */
3940 uint32_t sample_count = isom_add_size( trak, 1 );
3941 if( sample_count == 0 )
3942 return LSMASH_ERR_NAMELESS;
3943 /* Add a decoding timestamp and a composition timestamp. */
3944 if( (err = isom_add_timestamp( trak, sample_dts, sample_cts )) < 0 )
3945 return err;
3946 sample_dts += sample_duration;
3947 sample_cts += sample_duration;
3949 *samples_per_packet = audio->samplesPerPacket;
3951 else
3953 /* Add a sample_size and increment sample_count. */
3954 uint32_t sample_count = isom_add_size( trak, sample->length );
3955 if( sample_count == 0 )
3956 return LSMASH_ERR_NAMELESS;
3957 /* Add a decoding timestamp and a composition timestamp. */
3958 if( (err = isom_add_timestamp( trak, sample->dts, sample->cts )) < 0 )
3959 return err;
3960 /* Add a sync point if needed. */
3961 if( (err = isom_add_sync_point( trak, sample_count, &sample->prop )) < 0 )
3962 return err;
3963 /* Add a partial sync point if needed. */
3964 if( (err = isom_add_partial_sync( trak, sample_count, &sample->prop )) < 0 )
3965 return err;
3966 /* Add leading, independent, disposable and redundant information if needed. */
3967 if( (err = isom_add_dependency_type( trak, &sample->prop )) < 0 )
3968 return err;
3969 /* Group samples into random access point type if needed. */
3970 if( (err = isom_group_random_access( (isom_box_t *)trak, sample )) < 0 )
3971 return err;
3972 /* Group samples into random access recovery point type if needed. */
3973 if( (err = isom_group_roll_recovery( (isom_box_t *)trak, sample )) < 0 )
3974 return err;
3975 *samples_per_packet = 1;
3977 /* Add a chunk if needed. */
3978 return isom_add_chunk( trak, sample );
3981 static int isom_output_cached_chunk( isom_trak_t *trak )
3983 isom_chunk_t *chunk = &trak->cache->chunk;
3984 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3985 isom_stsc_entry_t *last_stsc_data = stbl->stsc->list->tail ? (isom_stsc_entry_t *)stbl->stsc->list->tail->data : NULL;
3986 /* Create a new chunk sequence in this track if needed. */
3987 int err;
3988 if( (!last_stsc_data
3989 || chunk->pool->sample_count != last_stsc_data->samples_per_chunk
3990 || chunk->sample_description_index != last_stsc_data->sample_description_index)
3991 && (err = isom_add_stsc_entry( stbl, chunk->chunk_number,
3992 chunk->pool->sample_count,
3993 chunk->sample_description_index )) < 0 )
3994 return err;
3995 lsmash_file_t *file = isom_get_written_media_file( trak, chunk->sample_description_index );
3996 if( file->fragment )
3998 /* Add a new chunk offset in this track. */
3999 if( (err = isom_add_stco_entry( stbl, file->size + ISOM_BASEBOX_COMMON_SIZE + file->fragment->pool_size )) < 0 )
4000 return err;
4001 return isom_append_fragment_track_run( file, chunk );
4003 /* Add a new chunk offset in this track. */
4004 if( (err = isom_add_stco_entry( stbl, file->size )) < 0 )
4005 return err;
4006 /* Output pooled samples in this track. */
4007 return isom_write_pooled_samples( file, chunk->pool );
4010 int isom_pool_sample( isom_sample_pool_t *pool, lsmash_sample_t *sample, uint32_t samples_per_packet )
4012 uint64_t pool_size = pool->size + sample->length;
4013 if( pool->alloc < pool_size )
4015 uint8_t *data;
4016 uint64_t alloc = pool_size + (1<<16);
4017 if( !pool->data )
4018 data = lsmash_malloc( alloc );
4019 else
4020 data = lsmash_realloc( pool->data, alloc );
4021 if( !data )
4022 return LSMASH_ERR_MEMORY_ALLOC;
4023 pool->data = data;
4024 pool->alloc = alloc;
4026 memcpy( pool->data + pool->size, sample->data, sample->length );
4027 pool->size = pool_size;
4028 pool->sample_count += samples_per_packet;
4029 lsmash_delete_sample( sample );
4030 return 0;
4033 static int isom_append_sample_internal( isom_trak_t *trak, lsmash_sample_t *sample )
4035 uint32_t samples_per_packet;
4036 int ret = isom_update_sample_tables( trak, sample, &samples_per_packet );
4037 if( ret < 0 )
4038 return ret;
4039 /* ret == 1 means pooled samples must be flushed. */
4040 isom_sample_pool_t *current_pool = trak->cache->chunk.pool;
4041 if( ret == 1 )
4043 /* The sample_description_index in the cache is one of the next written chunk.
4044 * Therefore, it cannot be referenced here. */
4045 lsmash_entry_list_t *stsc_list = trak->mdia->minf->stbl->stsc->list;
4046 isom_stsc_entry_t *last_stsc_data = (isom_stsc_entry_t *)stsc_list->tail->data;
4047 lsmash_file_t *file = isom_get_written_media_file( trak, last_stsc_data->sample_description_index );
4048 if( (ret = isom_write_pooled_samples( file, current_pool )) < 0 )
4049 return ret;
4051 /* Arbitration system between tracks with extremely scattering dts.
4052 * Here, we check whether asynchronization between the tracks exceeds the tolerance.
4053 * If a track has too old "first DTS" in its cached chunk than current sample's DTS, then its pooled samples must be flushed.
4054 * We don't consider presentation of media since any edit can pick an arbitrary portion of media in track.
4055 * Note: you needn't read this loop until you grasp the basic handling of chunks. */
4056 lsmash_file_t *file = trak->file;
4057 double tolerance = file->max_async_tolerance;
4058 for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next )
4060 isom_trak_t *other = (isom_trak_t *)entry->data;
4061 if( trak == other )
4062 continue;
4063 if( !other
4064 || !other->cache
4065 || !other->mdia
4066 || !other->mdia->mdhd
4067 || other->mdia->mdhd->timescale == 0
4068 || !other->mdia->minf
4069 || !other->mdia->minf->stbl
4070 || !other->mdia->minf->stbl->stsc
4071 || !other->mdia->minf->stbl->stsc->list )
4072 return LSMASH_ERR_INVALID_DATA;
4073 isom_chunk_t *chunk = &other->cache->chunk;
4074 if( !chunk->pool || chunk->pool->sample_count == 0 )
4075 continue;
4076 double diff = ((double)sample->dts / trak->mdia->mdhd->timescale)
4077 - ((double)chunk->first_dts / other->mdia->mdhd->timescale);
4078 if( diff > tolerance && (ret = isom_output_cached_chunk( other )) < 0 )
4079 return ret;
4080 /* Note: we don't flush the cached chunk in the current track and the current sample here
4081 * even if the conditional expression of '-diff > tolerance' meets.
4082 * That's useless because appending a sample to another track would be a good equivalent.
4083 * It's even harmful because it causes excess chunk division by calling
4084 * isom_output_cached_chunk() which always generates a new chunk.
4085 * Anyway some excess chunk division will be there, but rather less without it.
4086 * To completely avoid this, we need to observe at least whether the current sample will be placed
4087 * right next to the previous chunk of the same track or not. */
4089 /* anyway the current sample must be pooled. */
4090 return isom_pool_sample( current_pool, sample, samples_per_packet );
4093 /* This function is for non-fragmented movie. */
4094 static int isom_append_sample( lsmash_file_t *file, uint32_t track_ID, lsmash_sample_t *sample )
4096 isom_trak_t *trak = isom_get_trak( file, track_ID );
4097 if( !trak
4098 || !trak->file
4099 || !trak->cache
4100 || !trak->mdia
4101 || !trak->mdia->mdhd
4102 || trak->mdia->mdhd->timescale == 0
4103 || !trak->mdia->minf
4104 || !trak->mdia->minf->stbl
4105 || !trak->mdia->minf->stbl->stsd
4106 || !trak->mdia->minf->stbl->stsc || !trak->mdia->minf->stbl->stsc->list )
4107 return LSMASH_ERR_NAMELESS;
4108 /* If there is no available Media Data Box to write samples, add and write a new one before any chunk offset is decided. */
4109 int err;
4110 if( !file->mdat )
4112 if( !isom_add_mdat( file ) )
4113 return LSMASH_ERR_NAMELESS;
4114 file->mdat->manager |= LSMASH_PLACEHOLDER;
4115 if( (err = isom_write_box( file->bs, (isom_box_t *)file->mdat )) < 0 )
4116 return err;
4117 assert( file->free );
4118 file->size += file->free->size + file->mdat->size;
4120 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_get_entry_data( &trak->mdia->minf->stbl->stsd->list, sample->index );
4121 if( !sample_entry )
4122 return LSMASH_ERR_NAMELESS;
4123 if( isom_is_lpcm_audio( sample_entry ) )
4125 uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket;
4126 if( sample->length == frame_size )
4127 return isom_append_sample_internal( trak, sample );
4128 else if( sample->length < frame_size )
4129 return LSMASH_ERR_INVALID_DATA;
4130 /* Append samples splitted into each LPCMFrame. */
4131 uint64_t dts = sample->dts;
4132 uint64_t cts = sample->cts;
4133 for( uint32_t offset = 0; offset < sample->length; offset += frame_size )
4135 lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size );
4136 if( !lpcm_sample )
4137 return LSMASH_ERR_MEMORY_ALLOC;
4138 memcpy( lpcm_sample->data, sample->data + offset, frame_size );
4139 lpcm_sample->dts = dts++;
4140 lpcm_sample->cts = cts++;
4141 lpcm_sample->prop = sample->prop;
4142 lpcm_sample->index = sample->index;
4143 if( (err = isom_append_sample_internal( trak, lpcm_sample )) < 0 )
4145 lsmash_delete_sample( lpcm_sample );
4146 return err;
4149 lsmash_delete_sample( sample );
4150 return 0;
4152 return isom_append_sample_internal( trak, sample );
4155 static int isom_output_cache( isom_trak_t *trak )
4157 int err;
4158 isom_cache_t *cache = trak->cache;
4159 if( cache->chunk.pool
4160 && cache->chunk.pool->sample_count
4161 && (err = isom_output_cached_chunk( trak )) < 0 )
4162 return err;
4163 isom_stbl_t *stbl = trak->mdia->minf->stbl;
4164 for( lsmash_entry_t *entry = stbl->sgpd_list.head; entry; entry = entry->next )
4166 isom_sgpd_t *sgpd = (isom_sgpd_t *)entry->data;
4167 if( !sgpd )
4168 return LSMASH_ERR_INVALID_DATA;
4169 switch( sgpd->grouping_type )
4171 case ISOM_GROUP_TYPE_RAP :
4173 isom_rap_group_t *group = cache->rap;
4174 if( !group )
4176 if( stbl->file->fragment )
4177 continue;
4178 else
4179 return LSMASH_ERR_NAMELESS;
4181 if( !group->random_access )
4182 continue;
4183 group->random_access->num_leading_samples_known = 1;
4184 break;
4186 case ISOM_GROUP_TYPE_ROLL :
4187 case ISOM_GROUP_TYPE_PROL :
4188 if( !cache->roll.pool )
4190 if( stbl->file->fragment )
4191 continue;
4192 else
4193 return LSMASH_ERR_NAMELESS;
4195 isom_sbgp_t *sbgp = isom_get_roll_recovery_sample_to_group( &stbl->sbgp_list );
4196 if( !sbgp )
4197 return LSMASH_ERR_NAMELESS;
4198 if( (err = isom_all_recovery_completed( sbgp, cache->roll.pool )) < 0 )
4199 return err;
4200 break;
4201 default :
4202 break;
4205 return 0;
4208 int lsmash_flush_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta )
4210 if( isom_check_initializer_present( root ) < 0 )
4211 return LSMASH_ERR_FUNCTION_PARAM;
4212 lsmash_file_t *file = root->file;
4213 if( file->fragment
4214 && file->fragment->movie )
4215 return isom_flush_fragment_pooled_samples( file, track_ID, last_sample_delta );
4216 if( file != file->initializer )
4217 return LSMASH_ERR_INVALID_DATA;
4218 isom_trak_t *trak = isom_get_trak( file, track_ID );
4219 if( !trak
4220 || !trak->cache
4221 || !trak->mdia
4222 || !trak->mdia->minf
4223 || !trak->mdia->minf->stbl
4224 || !trak->mdia->minf->stbl->stsc
4225 || !trak->mdia->minf->stbl->stsc->list )
4226 return LSMASH_ERR_NAMELESS;
4227 int err = isom_output_cache( trak );
4228 if( err < 0 )
4229 return err;
4230 return lsmash_set_last_sample_delta( root, track_ID, last_sample_delta );
4233 int lsmash_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample )
4235 if( isom_check_initializer_present( root ) < 0
4236 || track_ID == 0
4237 || !sample
4238 || !sample->data )
4239 return LSMASH_ERR_FUNCTION_PARAM;
4240 lsmash_file_t *file = root->file;
4241 /* We think max_chunk_duration == 0, which means all samples will be cached on memory, should be prevented.
4242 * This means removal of a feature that we used to have, but anyway very alone chunk does not make sense. */
4243 if( !file
4244 || !file->bs
4245 || !(file->flags & LSMASH_FILE_MODE_BOX)
4246 || file->max_chunk_duration == 0
4247 || file->max_async_tolerance == 0 )
4248 return LSMASH_ERR_NAMELESS;
4249 /* Write File Type Box here if it was not written yet. */
4250 if( file->flags & LSMASH_FILE_MODE_INITIALIZATION )
4252 if( file->ftyp && !(file->ftyp->manager & LSMASH_WRITTEN_BOX) )
4254 int err = isom_write_box( file->bs, (isom_box_t *)file->ftyp );
4255 if( err < 0 )
4256 return err;
4257 file->size += file->ftyp->size;
4260 if( (file->flags & LSMASH_FILE_MODE_FRAGMENTED)
4261 && file->fragment
4262 && file->fragment->pool )
4263 return isom_append_fragment_sample( file, track_ID, sample );
4264 if( file != file->initializer )
4265 return LSMASH_ERR_INVALID_DATA;
4266 return isom_append_sample( file, track_ID, sample );
4269 /*---- misc functions ----*/
4271 int lsmash_delete_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID )
4273 if( isom_check_initializer_present( root ) < 0 )
4274 return LSMASH_ERR_FUNCTION_PARAM;
4275 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
4276 if( !trak )
4277 return LSMASH_ERR_NAMELESS;
4278 isom_remove_box_by_itself( trak->edts );
4279 return isom_update_tkhd_duration( trak );
4282 void lsmash_delete_tyrant_chapter( lsmash_root_t *root )
4284 if( isom_check_initializer_present( root ) < 0
4285 || !root->file->initializer->moov
4286 || !root->file->initializer->moov->udta )
4287 return;
4288 isom_remove_box_by_itself( root->file->moov->udta->chpl );
4291 int lsmash_set_copyright( lsmash_root_t *root, uint32_t track_ID, uint16_t ISO_language, char *notice )
4293 if( isom_check_initializer_present( root ) < 0
4294 || (ISO_language && ISO_language < 0x800)
4295 || !notice )
4296 return LSMASH_ERR_FUNCTION_PARAM;
4297 lsmash_file_t *file = root->file;
4298 if( !file->moov
4299 || !file->isom_compatible )
4300 return LSMASH_ERR_NAMELESS;
4301 isom_udta_t *udta;
4302 if( track_ID )
4304 isom_trak_t *trak = isom_get_trak( file, track_ID );
4305 if( !trak || (!trak->udta && !isom_add_udta( trak )) )
4306 return LSMASH_ERR_NAMELESS;
4307 udta = trak->udta;
4309 else
4311 if( !file->moov->udta && !isom_add_udta( file->moov ) )
4312 return LSMASH_ERR_NAMELESS;
4313 udta = file->moov->udta;
4315 assert( udta );
4316 for( lsmash_entry_t *entry = udta->cprt_list.head; entry; entry = entry->next )
4318 isom_cprt_t *cprt = (isom_cprt_t *)entry->data;
4319 if( !cprt || cprt->language == ISO_language )
4320 return LSMASH_ERR_NAMELESS;
4322 if( !isom_add_cprt( udta ) )
4323 return LSMASH_ERR_NAMELESS;
4324 isom_cprt_t *cprt = (isom_cprt_t *)udta->cprt_list.tail->data;
4325 cprt->language = ISO_language;
4326 cprt->notice_length = strlen( notice ) + 1;
4327 cprt->notice = lsmash_memdup( notice, cprt->notice_length );
4328 return 0;