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