1 /*****************************************************************************
2 * isom.c
3 *****************************************************************************
4 * Copyright (C) 2010-2017 L-SMASH project
5 *
6 * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
7 * Contributors: Takashi Hirata <silverfilain@gmail.com>
8 *
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.
12 *
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 *****************************************************************************/
21
22 /* This file is available under an ISC license. */
23
24 #include "common/internal.h" /* must be placed first */
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <inttypes.h>
29
30 #include "box.h"
31 #include "box_default.h"
32 #include "file.h"
33 #include "fragment.h"
34 #include "read.h"
35 #include "timeline.h"
36 #include "write.h"
37
38 #include "codecs/mp4a.h"
39 #include "codecs/mp4sys.h"
40 #include "codecs/description.h"
41
42 /*---- ----*/
isom_check_initializer_present(lsmash_root_t * root)43 int isom_check_initializer_present( lsmash_root_t *root )
44 {
45 if( LSMASH_IS_NON_EXISTING_BOX( root )
46 || LSMASH_IS_NON_EXISTING_BOX( root->file )
47 || LSMASH_IS_NON_EXISTING_BOX( root->file->initializer ) )
48 return LSMASH_ERR_NAMELESS;
49 return 0;
50 }
51
isom_get_trak(lsmash_file_t * file,uint32_t track_ID)52 isom_trak_t *isom_get_trak( lsmash_file_t *file, uint32_t track_ID )
53 {
54 if( track_ID == 0
55 || LSMASH_IS_NON_EXISTING_BOX( file->moov )
56 || file != file->initializer )
57 return isom_non_existing_trak();
58 for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next )
59 {
60 isom_trak_t *trak = (isom_trak_t *)entry->data;
61 if( LSMASH_IS_NON_EXISTING_BOX( trak )
62 || LSMASH_IS_NON_EXISTING_BOX( trak->tkhd ) )
63 return isom_non_existing_trak();
64 if( trak->tkhd->track_ID == track_ID )
65 return trak;
66 }
67 return isom_non_existing_trak();
68 }
69
isom_get_trex(isom_mvex_t * mvex,uint32_t track_ID)70 isom_trex_t *isom_get_trex( isom_mvex_t *mvex, uint32_t track_ID )
71 {
72 if( track_ID == 0 || LSMASH_IS_NON_EXISTING_BOX( mvex ) )
73 return isom_non_existing_trex();
74 for( lsmash_entry_t *entry = mvex->trex_list.head; entry; entry = entry->next )
75 {
76 isom_trex_t *trex = (isom_trex_t *)entry->data;
77 if( LSMASH_IS_NON_EXISTING_BOX( trex ) )
78 return isom_non_existing_trex();
79 if( trex->track_ID == track_ID )
80 return trex;
81 }
82 return isom_non_existing_trex();
83 }
84
isom_get_traf(isom_moof_t * moof,uint32_t track_ID)85 isom_traf_t *isom_get_traf( isom_moof_t *moof, uint32_t track_ID )
86 {
87 if( track_ID == 0 || LSMASH_IS_NON_EXISTING_BOX( moof ) )
88 return isom_non_existing_traf();
89 for( lsmash_entry_t *entry = moof->traf_list.head; entry; entry = entry->next )
90 {
91 isom_traf_t *traf = (isom_traf_t *)entry->data;
92 if( LSMASH_IS_NON_EXISTING_BOX( traf )
93 || LSMASH_IS_NON_EXISTING_BOX( traf->tfhd ) )
94 return isom_non_existing_traf();
95 if( traf->tfhd->track_ID == track_ID )
96 return traf;
97 }
98 return isom_non_existing_traf();
99 }
100
isom_get_tfra(isom_mfra_t * mfra,uint32_t track_ID)101 isom_tfra_t *isom_get_tfra( isom_mfra_t *mfra, uint32_t track_ID )
102 {
103 if( track_ID == 0 || LSMASH_IS_NON_EXISTING_BOX( mfra ) )
104 return isom_non_existing_tfra();
105 for( lsmash_entry_t *entry = mfra->tfra_list.head; entry; entry = entry->next )
106 {
107 isom_tfra_t *tfra = (isom_tfra_t *)entry->data;
108 if( LSMASH_IS_NON_EXISTING_BOX( tfra ) )
109 return isom_non_existing_tfra();
110 if( tfra->track_ID == track_ID )
111 return tfra;
112 }
113 return isom_non_existing_tfra();
114 }
115
isom_add_elst_entry(isom_elst_t * elst,uint64_t segment_duration,int64_t media_time,int32_t media_rate)116 static int isom_add_elst_entry( isom_elst_t *elst, uint64_t segment_duration, int64_t media_time, int32_t media_rate )
117 {
118 assert( LSMASH_IS_EXISTING_BOX( elst->file ) );
119 isom_elst_entry_t *data = lsmash_malloc( sizeof(isom_elst_entry_t) );
120 if( !data )
121 return LSMASH_ERR_MEMORY_ALLOC;
122 data->segment_duration = segment_duration;
123 data->media_time = media_time;
124 data->media_rate = media_rate;
125 if( lsmash_list_add_entry( elst->list, data ) < 0 )
126 {
127 lsmash_free( data );
128 return LSMASH_ERR_MEMORY_ALLOC;
129 }
130 if( !elst->file->undefined_64_ver
131 && (data->segment_duration > UINT32_MAX
132 || data->media_time > INT32_MAX
133 || data->media_time < INT32_MIN) )
134 elst->version = 1;
135 return 0;
136 }
137
138 /* This function returns 0 if failed, sample_entry_number if succeeded. */
lsmash_add_sample_entry(lsmash_root_t * root,uint32_t track_ID,void * summary)139 int lsmash_add_sample_entry( lsmash_root_t *root, uint32_t track_ID, void *summary )
140 {
141 if( LSMASH_IS_NON_EXISTING_BOX( root ) || !summary
142 || ((lsmash_summary_t *)summary)->data_ref_index == 0
143 || ((lsmash_summary_t *)summary)->data_ref_index > UINT16_MAX )
144 return 0;
145 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
146 if( LSMASH_IS_NON_EXISTING_BOX( trak )
147 || LSMASH_IS_NON_EXISTING_BOX( trak->file )
148 || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->hdlr )
149 || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl->stsd ) )
150 return 0;
151 isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd;
152 lsmash_media_type media_type = trak->mdia->hdlr->componentSubtype;
153 if( isom_setup_sample_description( stsd, media_type, (lsmash_summary_t *)summary ) < 0 )
154 return 0;
155 else
156 return stsd->list.entry_count;
157 }
158
isom_add_stts_entry(isom_stbl_t * stbl,uint32_t sample_delta)159 static int isom_add_stts_entry( isom_stbl_t *stbl, uint32_t sample_delta )
160 {
161 assert( LSMASH_IS_EXISTING_BOX( stbl->stts ) );
162 if( !stbl->stts->list )
163 return LSMASH_ERR_NAMELESS;
164 isom_stts_entry_t *data = lsmash_malloc( sizeof(isom_stts_entry_t) );
165 if( !data )
166 return LSMASH_ERR_MEMORY_ALLOC;
167 data->sample_count = 1;
168 data->sample_delta = sample_delta;
169 if( lsmash_list_add_entry( stbl->stts->list, data ) < 0 )
170 {
171 lsmash_free( data );
172 return LSMASH_ERR_MEMORY_ALLOC;
173 }
174 return 0;
175 }
176
isom_add_ctts_entry(isom_stbl_t * stbl,uint32_t sample_count,uint32_t sample_offset)177 static int isom_add_ctts_entry( isom_stbl_t *stbl, uint32_t sample_count, uint32_t sample_offset )
178 {
179 assert( LSMASH_IS_EXISTING_BOX( stbl->ctts ) );
180 if( !stbl->ctts->list )
181 return LSMASH_ERR_NAMELESS;
182 isom_ctts_entry_t *data = lsmash_malloc( sizeof(isom_ctts_entry_t) );
183 if( !data )
184 return LSMASH_ERR_MEMORY_ALLOC;
185 data->sample_count = sample_count;
186 data->sample_offset = sample_offset;
187 if( lsmash_list_add_entry( stbl->ctts->list, data ) < 0 )
188 {
189 lsmash_free( data );
190 return LSMASH_ERR_MEMORY_ALLOC;
191 }
192 return 0;
193 }
194
isom_add_stsc_entry(isom_stbl_t * stbl,uint32_t first_chunk,uint32_t samples_per_chunk,uint32_t sample_description_index)195 static int isom_add_stsc_entry( isom_stbl_t *stbl, uint32_t first_chunk, uint32_t samples_per_chunk, uint32_t sample_description_index )
196 {
197 assert( LSMASH_IS_EXISTING_BOX( stbl->stsc ) );
198 if( !stbl->stsc->list )
199 return LSMASH_ERR_NAMELESS;
200 isom_stsc_entry_t *data = lsmash_malloc( sizeof(isom_stsc_entry_t) );
201 if( !data )
202 return LSMASH_ERR_MEMORY_ALLOC;
203 data->first_chunk = first_chunk;
204 data->samples_per_chunk = samples_per_chunk;
205 data->sample_description_index = sample_description_index;
206 if( lsmash_list_add_entry( stbl->stsc->list, data ) < 0 )
207 {
208 lsmash_free( data );
209 return LSMASH_ERR_MEMORY_ALLOC;
210 }
211 return 0;
212 }
213
isom_add_stsz_entry(isom_stbl_t * stbl,uint32_t entry_size)214 static int isom_add_stsz_entry( isom_stbl_t *stbl, uint32_t entry_size )
215 {
216 assert( LSMASH_IS_EXISTING_BOX( stbl ) );
217 if( LSMASH_IS_NON_EXISTING_BOX( stbl->stsz ) )
218 return LSMASH_ERR_NAMELESS;
219 isom_stsz_t *stsz = stbl->stsz;
220 /* retrieve initial sample_size */
221 if( stsz->sample_count == 0 )
222 stsz->sample_size = entry_size;
223 /* if it seems constant sample size at present, update sample_count only */
224 if( !stsz->list && stsz->sample_size == entry_size )
225 {
226 ++ stsz->sample_count;
227 return 0;
228 }
229 /* found sample_size varies, create sample_size list */
230 if( !stsz->list )
231 {
232 stsz->list = lsmash_list_create_simple();
233 if( !stsz->list )
234 return LSMASH_ERR_MEMORY_ALLOC;
235 for( uint32_t i = 0; i < stsz->sample_count; i++ )
236 {
237 isom_stsz_entry_t *data = lsmash_malloc( sizeof(isom_stsz_entry_t) );
238 if( !data )
239 return LSMASH_ERR_MEMORY_ALLOC;
240 data->entry_size = stsz->sample_size;
241 if( lsmash_list_add_entry( stsz->list, data ) < 0 )
242 {
243 lsmash_free( data );
244 return LSMASH_ERR_MEMORY_ALLOC;
245 }
246 }
247 stsz->sample_size = 0;
248 }
249 isom_stsz_entry_t *data = lsmash_malloc( sizeof(isom_stsz_entry_t) );
250 if( !data )
251 return LSMASH_ERR_MEMORY_ALLOC;
252 data->entry_size = entry_size;
253 if( lsmash_list_add_entry( stsz->list, data ) < 0 )
254 {
255 lsmash_free( data );
256 return LSMASH_ERR_MEMORY_ALLOC;
257 }
258 ++ stsz->sample_count;
259 return 0;
260 }
261
isom_add_stss_entry(isom_stbl_t * stbl,uint32_t sample_number)262 static int isom_add_stss_entry( isom_stbl_t *stbl, uint32_t sample_number )
263 {
264 assert( LSMASH_IS_EXISTING_BOX( stbl->stss ) );
265 if( !stbl->stss->list )
266 return LSMASH_ERR_NAMELESS;
267 isom_stss_entry_t *data = lsmash_malloc( sizeof(isom_stss_entry_t) );
268 if( !data )
269 return LSMASH_ERR_MEMORY_ALLOC;
270 data->sample_number = sample_number;
271 if( lsmash_list_add_entry( stbl->stss->list, data ) < 0 )
272 {
273 lsmash_free( data );
274 return LSMASH_ERR_MEMORY_ALLOC;
275 }
276 return 0;
277 }
278
isom_add_stps_entry(isom_stbl_t * stbl,uint32_t sample_number)279 static int isom_add_stps_entry( isom_stbl_t *stbl, uint32_t sample_number )
280 {
281 assert( LSMASH_IS_EXISTING_BOX( stbl->stps ) );
282 if( !stbl->stps->list )
283 return LSMASH_ERR_NAMELESS;
284 isom_stps_entry_t *data = lsmash_malloc( sizeof(isom_stps_entry_t) );
285 if( !data )
286 return LSMASH_ERR_MEMORY_ALLOC;
287 data->sample_number = sample_number;
288 if( lsmash_list_add_entry( stbl->stps->list, data ) < 0 )
289 {
290 lsmash_free( data );
291 return LSMASH_ERR_MEMORY_ALLOC;
292 }
293 return 0;
294 }
295
296 /* Between ISOBMFF and QTFF, the most significant 2-bit has different meaning.
297 * For the maximum compatibility, we set 0 to the most significant 2-bit when compatible with
298 * both ISOBMFF with AVCFF extensions and QTFF.
299 * compatibility == 0 -> neither AVCFF extensions nor QTFF compatible
300 * compatibility == 1 -> AVCFF extensions compatible
301 * compatibility == 2 -> QTFF compatible
302 * compatibility == 3 -> both AVCFF extensions and QTFF compatible */
isom_add_sdtp_entry(isom_box_t * parent,lsmash_sample_property_t * prop,int compatibility)303 static int isom_add_sdtp_entry( isom_box_t *parent, lsmash_sample_property_t *prop, int compatibility )
304 {
305 if( !prop || LSMASH_IS_NON_EXISTING_BOX( parent ) )
306 return LSMASH_ERR_NAMELESS;
307 isom_sdtp_t *sdtp = isom_non_existing_sdtp();
308 if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) )
309 sdtp = ((isom_stbl_t *)parent)->sdtp;
310 else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) )
311 sdtp = ((isom_traf_t *)parent)->sdtp;
312 else
313 assert( 0 );
314 if( LSMASH_IS_NON_EXISTING_BOX( sdtp )
315 || !sdtp->list )
316 return LSMASH_ERR_NAMELESS;
317 isom_sdtp_entry_t *data = lsmash_malloc( sizeof(isom_sdtp_entry_t) );
318 if( !data )
319 return LSMASH_ERR_MEMORY_ALLOC;
320 if( compatibility == 1 )
321 data->is_leading = prop->leading & 0x03;
322 else if( compatibility == 2 )
323 data->is_leading = prop->allow_earlier & 0x03;
324 else
325 {
326 data->is_leading = 0;
327 assert( compatibility == 3 );
328 }
329 data->sample_depends_on = prop->independent & 0x03;
330 data->sample_is_depended_on = prop->disposable & 0x03;
331 data->sample_has_redundancy = prop->redundant & 0x03;
332 if( lsmash_list_add_entry( sdtp->list, data ) < 0 )
333 {
334 lsmash_free( data );
335 return LSMASH_ERR_MEMORY_ALLOC;
336 }
337 return 0;
338 }
339
isom_add_co64_entry(isom_stbl_t * stbl,uint64_t chunk_offset)340 static int isom_add_co64_entry( isom_stbl_t *stbl, uint64_t chunk_offset )
341 {
342 assert( LSMASH_IS_EXISTING_BOX( stbl->stco ) );
343 if( !stbl->stco->list )
344 return LSMASH_ERR_NAMELESS;
345 isom_co64_entry_t *data = lsmash_malloc( sizeof(isom_co64_entry_t) );
346 if( !data )
347 return LSMASH_ERR_MEMORY_ALLOC;
348 data->chunk_offset = chunk_offset;
349 if( lsmash_list_add_entry( stbl->stco->list, data ) < 0 )
350 {
351 lsmash_free( data );
352 return LSMASH_ERR_MEMORY_ALLOC;
353 }
354 return 0;
355 }
356
isom_convert_stco_to_co64(isom_stbl_t * stbl)357 static int isom_convert_stco_to_co64( isom_stbl_t *stbl )
358 {
359 assert( LSMASH_IS_EXISTING_BOX( stbl->stco ) );
360 /* backup stco */
361 int err = 0;
362 isom_stco_t *stco = stbl->stco;
363 LSMASH_MAKE_BOX_NON_EXISTING( stbl->stco );
364 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_co64( stbl ) ) )
365 {
366 err = LSMASH_ERR_NAMELESS;
367 goto fail;
368 }
369 /* move chunk_offset to co64 from stco */
370 for( lsmash_entry_t *entry = stco->list->head; entry; entry = entry->next )
371 {
372 isom_stco_entry_t *data = (isom_stco_entry_t*)entry->data;
373 if( (err = isom_add_co64_entry( stbl, data->chunk_offset )) < 0 )
374 goto fail;
375 }
376 fail:
377 isom_remove_box_by_itself( stco );
378 return err;
379 }
380
isom_add_stco_entry(isom_stbl_t * stbl,uint64_t chunk_offset)381 static int isom_add_stco_entry( isom_stbl_t *stbl, uint64_t chunk_offset )
382 {
383 if( !stbl->stco->list )
384 return LSMASH_ERR_NAMELESS;
385 if( stbl->stco->large_presentation )
386 return isom_add_co64_entry( stbl, chunk_offset );
387 if( chunk_offset > UINT32_MAX )
388 {
389 int err = isom_convert_stco_to_co64( stbl );
390 if( err < 0 )
391 return err;
392 return isom_add_co64_entry( stbl, chunk_offset );
393 }
394 isom_stco_entry_t *data = lsmash_malloc( sizeof(isom_stco_entry_t) );
395 if( !data )
396 return LSMASH_ERR_MEMORY_ALLOC;
397 data->chunk_offset = (uint32_t)chunk_offset;
398 if( lsmash_list_add_entry( stbl->stco->list, data ) < 0 )
399 {
400 lsmash_free( data );
401 return LSMASH_ERR_MEMORY_ALLOC;
402 }
403 return 0;
404 }
405
isom_get_sample_group_description_common(lsmash_entry_list_t * list,uint32_t grouping_type)406 static isom_sgpd_t *isom_get_sample_group_description_common( lsmash_entry_list_t *list, uint32_t grouping_type )
407 {
408 for( lsmash_entry_t *entry = list->head; entry; entry = entry->next )
409 {
410 isom_sgpd_t *sgpd = (isom_sgpd_t *)entry->data;
411 if( LSMASH_IS_NON_EXISTING_BOX( sgpd )
412 || !sgpd->list )
413 return isom_non_existing_sgpd();
414 if( sgpd->grouping_type == grouping_type )
415 return sgpd;
416 }
417 return isom_non_existing_sgpd();
418 }
419
isom_get_sample_to_group_common(lsmash_entry_list_t * list,uint32_t grouping_type)420 static isom_sbgp_t *isom_get_sample_to_group_common( lsmash_entry_list_t *list, uint32_t grouping_type )
421 {
422 for( lsmash_entry_t *entry = list->head; entry; entry = entry->next )
423 {
424 isom_sbgp_t *sbgp = (isom_sbgp_t *)entry->data;
425 if( LSMASH_IS_NON_EXISTING_BOX( sbgp )
426 || !sbgp->list )
427 return isom_non_existing_sbgp();
428 if( sbgp->grouping_type == grouping_type )
429 return sbgp;
430 }
431 return isom_non_existing_sbgp();
432 }
433
isom_get_sample_group_description(isom_stbl_t * stbl,uint32_t grouping_type)434 isom_sgpd_t *isom_get_sample_group_description( isom_stbl_t *stbl, uint32_t grouping_type )
435 {
436 return isom_get_sample_group_description_common( &stbl->sgpd_list, grouping_type );
437 }
438
isom_get_sample_to_group(isom_stbl_t * stbl,uint32_t grouping_type)439 isom_sbgp_t *isom_get_sample_to_group( isom_stbl_t *stbl, uint32_t grouping_type )
440 {
441 return isom_get_sample_to_group_common( &stbl->sbgp_list, grouping_type );
442 }
443
isom_get_roll_recovery_sample_group_description(lsmash_entry_list_t * list)444 isom_sgpd_t *isom_get_roll_recovery_sample_group_description( lsmash_entry_list_t *list )
445 {
446 isom_sgpd_t *sgpd;
447 if( ((sgpd = isom_get_sample_group_description_common( list, ISOM_GROUP_TYPE_ROLL )), LSMASH_IS_EXISTING_BOX( sgpd ))
448 || ((sgpd = isom_get_sample_group_description_common( list, ISOM_GROUP_TYPE_PROL )), LSMASH_IS_EXISTING_BOX( sgpd )) )
449 return sgpd;
450 return isom_non_existing_sgpd();
451 }
452
isom_get_roll_recovery_sample_to_group(lsmash_entry_list_t * list)453 isom_sbgp_t *isom_get_roll_recovery_sample_to_group( lsmash_entry_list_t *list )
454 {
455 isom_sbgp_t *sbgp;
456 if( ((sbgp = isom_get_sample_to_group_common( list, ISOM_GROUP_TYPE_ROLL )), LSMASH_IS_EXISTING_BOX( sbgp ))
457 || ((sbgp = isom_get_sample_to_group_common( list, ISOM_GROUP_TYPE_PROL )), LSMASH_IS_EXISTING_BOX( sbgp )) )
458 return sbgp;
459 return isom_non_existing_sbgp();
460 }
461
isom_get_fragment_sample_group_description(isom_traf_t * traf,uint32_t grouping_type)462 isom_sgpd_t *isom_get_fragment_sample_group_description( isom_traf_t *traf, uint32_t grouping_type )
463 {
464 return isom_get_sample_group_description_common( &traf->sgpd_list, grouping_type );
465 }
466
isom_get_fragment_sample_to_group(isom_traf_t * traf,uint32_t grouping_type)467 isom_sbgp_t *isom_get_fragment_sample_to_group( isom_traf_t *traf, uint32_t grouping_type )
468 {
469 return isom_get_sample_to_group_common( &traf->sbgp_list, grouping_type );
470 }
471
isom_add_rap_group_entry(isom_sgpd_t * sgpd)472 static isom_rap_entry_t *isom_add_rap_group_entry( isom_sgpd_t *sgpd )
473 {
474 if( LSMASH_IS_NON_EXISTING_BOX( sgpd ) )
475 return NULL;
476 isom_rap_entry_t *data = lsmash_malloc( sizeof(isom_rap_entry_t) );
477 if( !data )
478 return NULL;
479 data->description_length = 0;
480 data->num_leading_samples_known = 0;
481 data->num_leading_samples = 0;
482 if( lsmash_list_add_entry( sgpd->list, data ) < 0 )
483 {
484 lsmash_free( data );
485 return NULL;
486 }
487 return data;
488 }
489
isom_add_roll_group_entry(isom_sgpd_t * sgpd,int16_t roll_distance)490 static isom_roll_entry_t *isom_add_roll_group_entry( isom_sgpd_t *sgpd, int16_t roll_distance )
491 {
492 if( LSMASH_IS_NON_EXISTING_BOX( sgpd ) )
493 return NULL;
494 isom_roll_entry_t *data = lsmash_malloc( sizeof(isom_roll_entry_t) );
495 if( !data )
496 return NULL;
497 data->description_length = 0;
498 data->roll_distance = roll_distance;
499 if( lsmash_list_add_entry( sgpd->list, data ) < 0 )
500 {
501 lsmash_free( data );
502 return NULL;
503 }
504 return data;
505 }
506
isom_add_group_assignment_entry(isom_sbgp_t * sbgp,uint32_t sample_count,uint32_t group_description_index)507 static isom_group_assignment_entry_t *isom_add_group_assignment_entry( isom_sbgp_t *sbgp, uint32_t sample_count, uint32_t group_description_index )
508 {
509 if( LSMASH_IS_NON_EXISTING_BOX( sbgp ) )
510 return NULL;
511 isom_group_assignment_entry_t *data = lsmash_malloc( sizeof(isom_group_assignment_entry_t) );
512 if( !data )
513 return NULL;
514 data->sample_count = sample_count;
515 data->group_description_index = group_description_index;
516 if( lsmash_list_add_entry( sbgp->list, data ) < 0 )
517 {
518 lsmash_free( data );
519 return NULL;
520 }
521 return data;
522 }
523
isom_get_sample_count_from_sample_table(isom_stbl_t * stbl)524 static uint32_t isom_get_sample_count_from_sample_table( isom_stbl_t *stbl )
525 {
526 if( LSMASH_IS_EXISTING_BOX( stbl->stsz ) )
527 return stbl->stsz->sample_count;
528 else if( LSMASH_IS_EXISTING_BOX( stbl->stz2 ) )
529 return stbl->stz2->sample_count;
530 else
531 return 0;
532 }
533
isom_get_sample_count(isom_trak_t * trak)534 uint32_t isom_get_sample_count( isom_trak_t *trak )
535 {
536 return isom_get_sample_count_from_sample_table( trak->mdia->minf->stbl );
537 }
538
isom_get_dts(isom_stts_t * stts,uint32_t sample_number)539 static uint64_t isom_get_dts( isom_stts_t *stts, uint32_t sample_number )
540 {
541 if( !stts->list )
542 return 0;
543 uint64_t dts = 0;
544 uint32_t i = 1;
545 lsmash_entry_t *entry;
546 isom_stts_entry_t *data = NULL;
547 for( entry = stts->list->head; entry; entry = entry->next )
548 {
549 data = (isom_stts_entry_t *)entry->data;
550 if( !data )
551 return 0;
552 if( i + data->sample_count > sample_number )
553 break;
554 dts += (uint64_t)data->sample_delta * data->sample_count;
555 i += data->sample_count;
556 }
557 if( !entry )
558 return 0;
559 dts += (uint64_t)data->sample_delta * (sample_number - i);
560 return dts;
561 }
562
563 #if 0
564 static uint64_t isom_get_cts( isom_stts_t *stts, isom_ctts_t *ctts, uint32_t sample_number )
565 {
566 if( !stts->list )
567 return 0;
568 if( LSMASH_IS_NON_EXISTING_BOX( ctts ) )
569 return isom_get_dts( stts, sample_number );
570 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. */
571 lsmash_entry_t *entry;
572 isom_ctts_entry_t *data = NULL;
573 if( sample_number == 0 )
574 return 0;
575 for( entry = ctts->list->head; entry; entry = entry->next )
576 {
577 data = (isom_ctts_entry_t *)entry->data;
578 if( !data )
579 return 0;
580 if( i + data->sample_count > sample_number )
581 break;
582 i += data->sample_count;
583 }
584 if( !entry )
585 return 0;
586 return isom_get_dts( stts, sample_number ) + data->sample_offset;
587 }
588 #endif
589
isom_replace_last_sample_delta(isom_stbl_t * stbl,uint32_t sample_delta)590 static int isom_replace_last_sample_delta( isom_stbl_t *stbl, uint32_t sample_delta )
591 {
592 assert( LSMASH_IS_EXISTING_BOX( stbl->stts ) );
593 if( !stbl->stts->list
594 || !stbl->stts->list->tail
595 || !stbl->stts->list->tail->data )
596 return LSMASH_ERR_NAMELESS;
597 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stbl->stts->list->tail->data;
598 if( sample_delta != last_stts_data->sample_delta )
599 {
600 if( last_stts_data->sample_count > 1 )
601 {
602 last_stts_data->sample_count -= 1;
603 int err = isom_add_stts_entry( stbl, sample_delta );
604 if( err < 0 )
605 return err;
606 }
607 else
608 last_stts_data->sample_delta = sample_delta;
609 }
610 return 0;
611 }
612
isom_update_mdhd_duration(isom_trak_t * trak,uint32_t last_sample_delta)613 static int isom_update_mdhd_duration( isom_trak_t *trak, uint32_t last_sample_delta )
614 {
615 assert( LSMASH_IS_EXISTING_BOX( trak ) );
616 if( LSMASH_IS_NON_EXISTING_BOX( trak->file )
617 || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->mdhd )
618 || !trak->cache
619 || !trak->mdia->minf->stbl->stts->list )
620 return LSMASH_ERR_INVALID_DATA;
621 lsmash_file_t *file = trak->file;
622 isom_mdhd_t *mdhd = trak->mdia->mdhd;
623 isom_stbl_t *stbl = trak->mdia->minf->stbl;
624 isom_stts_t *stts = stbl->stts;
625 isom_ctts_t *ctts = stbl->ctts;
626 isom_cslg_t *cslg = stbl->cslg;
627 mdhd->duration = 0;
628 uint32_t sample_count = isom_get_sample_count( trak );
629 if( sample_count == 0 )
630 {
631 /* Return error if non-fragmented movie has no samples. */
632 if( !file->fragment && !stts->list->entry_count )
633 return LSMASH_ERR_INVALID_DATA;
634 return 0;
635 }
636 /* Now we have at least 1 sample, so do stts_entry. */
637 lsmash_entry_t *last_stts = stts->list->tail;
638 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)last_stts->data;
639 if( sample_count == 1 )
640 mdhd->duration = last_stts_data->sample_delta;
641 /* Now we have at least 2 samples,
642 * but dunno whether 1 stts_entry which has 2 samples or 2 stts_entry which has 1 samle each. */
643 else if( LSMASH_IS_NON_EXISTING_BOX( ctts ) )
644 {
645 /* use dts instead of cts */
646 mdhd->duration = isom_get_dts( stts, sample_count );
647 int err;
648 if( last_sample_delta )
649 {
650 mdhd->duration += last_sample_delta;
651 if( (err = isom_replace_last_sample_delta( stbl, last_sample_delta )) < 0 )
652 return err;
653 }
654 else if( last_stts_data->sample_count > 1 )
655 mdhd->duration += last_stts_data->sample_delta; /* no need to update last_stts_data->sample_delta */
656 else
657 {
658 /* Remove the last entry. */
659 if( (err = lsmash_list_remove_entry_tail( stts->list )) < 0 )
660 return err;
661 /* copy the previous sample_delta. */
662 ++ ((isom_stts_entry_t *)stts->list->tail->data)->sample_count;
663 mdhd->duration += ((isom_stts_entry_t *)stts->list->tail->data)->sample_delta;
664 }
665 }
666 else
667 {
668 if( !ctts->list
669 || ctts->list->entry_count == 0 )
670 return LSMASH_ERR_INVALID_DATA;
671 uint64_t dts = 0;
672 uint64_t max_cts = 0;
673 uint64_t max2_cts = 0;
674 uint64_t min_cts = LSMASH_TIMESTAMP_UNDEFINED;
675 int64_t max_offset = 0;
676 int64_t min_offset = UINT32_MAX;
677 int32_t ctd_shift = trak->cache->timestamp.ctd_shift;
678 uint32_t j = 0;
679 uint32_t k = 0;
680 lsmash_entry_t *stts_entry = stts->list->head;
681 lsmash_entry_t *ctts_entry = ctts->list->head;
682 for( uint32_t i = 0; i < sample_count; i++ )
683 {
684 if( !ctts_entry || !stts_entry )
685 return LSMASH_ERR_INVALID_DATA;
686 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
687 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
688 if( !stts_data || !ctts_data )
689 return LSMASH_ERR_INVALID_DATA;
690 if( ctts_data->sample_offset != ISOM_NON_OUTPUT_SAMPLE_OFFSET )
691 {
692 uint64_t cts;
693 if( ctd_shift )
694 {
695 /* Anyway, add composition to decode timeline shift for calculating maximum and minimum CTS correctly. */
696 int64_t sample_offset = (int32_t)ctts_data->sample_offset;
697 cts = dts + sample_offset + ctd_shift;
698 max_offset = LSMASH_MAX( max_offset, sample_offset );
699 min_offset = LSMASH_MIN( min_offset, sample_offset );
700 }
701 else
702 {
703 cts = dts + ctts_data->sample_offset;
704 max_offset = LSMASH_MAX( max_offset, ctts_data->sample_offset );
705 min_offset = LSMASH_MIN( min_offset, ctts_data->sample_offset );
706 }
707 min_cts = LSMASH_MIN( min_cts, cts );
708 if( max_cts < cts )
709 {
710 max2_cts = max_cts;
711 max_cts = cts;
712 }
713 else if( max2_cts < cts )
714 max2_cts = cts;
715 }
716 dts += stts_data->sample_delta;
717 /* If finished sample_count of current entry, move to next. */
718 if( ++j == ctts_data->sample_count )
719 {
720 ctts_entry = ctts_entry->next;
721 j = 0;
722 }
723 if( ++k == stts_data->sample_count )
724 {
725 stts_entry = stts_entry->next;
726 k = 0;
727 }
728 }
729 dts -= last_stts_data->sample_delta;
730 if( file->fragment )
731 /* Overall presentation is extended exceeding this initial movie.
732 * So, any players shall display the movie exceeding the durations
733 * indicated in Movie Header Box, Track Header Boxes and Media Header Boxes.
734 * Samples up to the duration indicated in Movie Extends Header Box shall be displayed.
735 * In the absence of Movie Extends Header Box, all samples shall be displayed. */
736 mdhd->duration += dts + last_sample_delta;
737 else
738 {
739 if( !last_sample_delta )
740 {
741 /* The spec allows an arbitrary value for the duration of the last sample. So, we pick last-1 sample's. */
742 last_sample_delta = max_cts - max2_cts;
743 }
744 if( min_cts != LSMASH_TIMESTAMP_UNDEFINED )
745 mdhd->duration = max_cts - min_cts + last_sample_delta;
746 /* To match dts and media duration, update stts and mdhd relatively. */
747 if( mdhd->duration > dts )
748 last_sample_delta = mdhd->duration - dts;
749 else
750 mdhd->duration = dts + last_sample_delta; /* media duration must not less than last dts. */
751 }
752 int err = isom_replace_last_sample_delta( stbl, last_sample_delta );
753 if( err < 0 )
754 return err;
755 /* Explicit composition information and timeline shifting */
756 if( LSMASH_IS_EXISTING_BOX( cslg ) || file->qt_compatible || file->max_isom_version >= 4 )
757 {
758 if( ctd_shift )
759 {
760 /* Remove composition to decode timeline shift. */
761 max_cts -= ctd_shift;
762 max2_cts -= ctd_shift;
763 min_cts -= ctd_shift;
764 }
765 int64_t composition_end_time = max_cts + (max_cts - max2_cts);
766 if( !file->fragment
767 && min_cts != LSMASH_TIMESTAMP_UNDEFINED
768 && (min_offset <= INT32_MAX) && (min_offset >= INT32_MIN)
769 && (max_offset <= INT32_MAX) && (max_offset >= INT32_MIN)
770 && ((int64_t)min_cts <= INT32_MAX) && (composition_end_time <= INT32_MAX) )
771 {
772 if( LSMASH_IS_NON_EXISTING_BOX( cslg ) )
773 {
774 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_cslg( trak->mdia->minf->stbl ) ) )
775 return LSMASH_ERR_NAMELESS;
776 cslg = stbl->cslg;
777 }
778 cslg->compositionToDTSShift = ctd_shift;
779 cslg->leastDecodeToDisplayDelta = min_offset;
780 cslg->greatestDecodeToDisplayDelta = max_offset;
781 cslg->compositionStartTime = min_cts;
782 cslg->compositionEndTime = composition_end_time;
783 }
784 else
785 isom_remove_box_by_itself( cslg );
786 }
787 }
788 if( mdhd->duration > UINT32_MAX && !file->undefined_64_ver )
789 mdhd->version = 1;
790 return 0;
791 }
792
isom_update_mvhd_duration(isom_moov_t * moov)793 static int isom_update_mvhd_duration( isom_moov_t *moov )
794 {
795 assert( LSMASH_IS_EXISTING_BOX( moov ) );
796 if( LSMASH_IS_NON_EXISTING_BOX( moov->mvhd->file ) )
797 return LSMASH_ERR_INVALID_DATA;
798 isom_mvhd_t *mvhd = moov->mvhd;
799 mvhd->duration = 0;
800 for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next )
801 {
802 /* We pick maximum track duration as movie duration. */
803 isom_trak_t *trak = (isom_trak_t *)entry->data;
804 if( LSMASH_IS_NON_EXISTING_BOX( trak )
805 || LSMASH_IS_NON_EXISTING_BOX( trak->tkhd ) )
806 return LSMASH_ERR_INVALID_DATA;
807 mvhd->duration = entry != moov->trak_list.head
808 ? LSMASH_MAX( mvhd->duration, trak->tkhd->duration )
809 : trak->tkhd->duration;
810 }
811 if( mvhd->duration > UINT32_MAX && !mvhd->file->undefined_64_ver )
812 mvhd->version = 1;
813 return 0;
814 }
815
isom_update_tkhd_duration(isom_trak_t * trak)816 int isom_update_tkhd_duration( isom_trak_t *trak )
817 {
818 assert( LSMASH_IS_EXISTING_BOX( trak ) );
819 if( LSMASH_IS_NON_EXISTING_BOX( trak->tkhd )
820 || LSMASH_IS_NON_EXISTING_BOX( trak->file->moov->mvhd ) )
821 return LSMASH_ERR_INVALID_DATA;
822 lsmash_file_t *file = trak->file;
823 isom_tkhd_t *tkhd = trak->tkhd;
824 tkhd->duration = 0;
825 if( file->fragment
826 || LSMASH_IS_NON_EXISTING_BOX( trak->edts->elst ) )
827 {
828 /* If this presentation might be extended or this track doesn't have edit list, calculate track duration from media duration. */
829 if( LSMASH_IS_NON_EXISTING_BOX( trak->mdia->mdhd )
830 || trak->mdia->mdhd->timescale == 0 )
831 return LSMASH_ERR_INVALID_DATA;
832 if( trak->mdia->mdhd->duration == 0 )
833 {
834 int err = isom_update_mdhd_duration( trak, 0 );
835 if( err < 0 )
836 return err;
837 }
838 tkhd->duration = trak->mdia->mdhd->duration * ((double)file->moov->mvhd->timescale / trak->mdia->mdhd->timescale);
839 }
840 else
841 {
842 /* If the presentation won't be extended and this track has any edit, then track duration is just the sum of the segment_duartions. */
843 for( lsmash_entry_t *entry = trak->edts->elst->list->head; entry; entry = entry->next )
844 {
845 isom_elst_entry_t *data = (isom_elst_entry_t *)entry->data;
846 if( !data )
847 return LSMASH_ERR_INVALID_DATA;
848 tkhd->duration += data->segment_duration;
849 }
850 }
851 if( tkhd->duration > UINT32_MAX && !file->undefined_64_ver )
852 tkhd->version = 1;
853 if( !file->fragment && tkhd->duration == 0 )
854 tkhd->duration = tkhd->version == 1 ? 0xffffffffffffffff : 0xffffffff;
855 return isom_update_mvhd_duration( file->moov );
856 }
857
lsmash_update_track_duration(lsmash_root_t * root,uint32_t track_ID,uint32_t last_sample_delta)858 int lsmash_update_track_duration( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta )
859 {
860 if( isom_check_initializer_present( root ) < 0 )
861 return LSMASH_ERR_FUNCTION_PARAM;
862 lsmash_file_t *file = root->file;
863 isom_trak_t *trak = isom_get_trak( file, track_ID );
864 if( LSMASH_IS_NON_EXISTING_BOX( trak ) )
865 return LSMASH_ERR_NAMELESS;
866 int err = isom_update_mdhd_duration( trak, last_sample_delta );
867 if( err < 0 )
868 return err;
869 /* If the presentation won't be extended and this track has any edit, we don't change or update duration in tkhd. */
870 if( !file->fragment
871 && LSMASH_IS_EXISTING_BOX( trak->edts )
872 && LSMASH_IS_EXISTING_BOX( trak->edts->elst ) )
873 err = isom_update_mvhd_duration( file->moov ); /* Only update movie duration. */
874 else
875 err = isom_update_tkhd_duration( trak ); /* Also update movie duration internally. */
876 return err;
877 }
878
isom_increment_sample_number_in_entry(uint32_t * sample_number_in_entry,uint32_t sample_count_in_entry,lsmash_entry_t ** entry)879 static inline int isom_increment_sample_number_in_entry
880 (
881 uint32_t *sample_number_in_entry,
882 uint32_t sample_count_in_entry,
883 lsmash_entry_t **entry
884 )
885 {
886 if( *sample_number_in_entry != sample_count_in_entry )
887 {
888 *sample_number_in_entry += 1;
889 return 0;
890 }
891 /* Precede the next entry. */
892 *sample_number_in_entry = 1;
893 if( *entry )
894 {
895 *entry = (*entry)->next;
896 if( *entry && !(*entry)->data )
897 return LSMASH_ERR_INVALID_DATA;
898 }
899 return 0;
900 }
901
isom_calculate_bitrate_description(isom_stbl_t * stbl,isom_mdhd_t * mdhd,uint32_t * bufferSizeDB,uint32_t * maxBitrate,uint32_t * avgBitrate,uint32_t sample_description_index)902 int isom_calculate_bitrate_description
903 (
904 isom_stbl_t *stbl,
905 isom_mdhd_t *mdhd,
906 uint32_t *bufferSizeDB,
907 uint32_t *maxBitrate,
908 uint32_t *avgBitrate,
909 uint32_t sample_description_index
910 )
911 {
912 isom_stsz_t *stsz = stbl->stsz;
913 lsmash_entry_list_t *stsz_list = LSMASH_IS_EXISTING_BOX( stsz ) ? stsz->list : stbl->stz2->list;
914 lsmash_entry_t *stsz_entry = stsz_list ? stsz_list->head : NULL;
915 lsmash_entry_t *stts_entry = stbl->stts->list->head;
916 lsmash_entry_t *stsc_entry = NULL;
917 lsmash_entry_t *next_stsc_entry = stbl->stsc->list->head;
918 isom_stts_entry_t *stts_data = NULL;
919 isom_stsc_entry_t *stsc_data = NULL;
920 if( next_stsc_entry && !next_stsc_entry->data )
921 return LSMASH_ERR_INVALID_DATA;
922 uint32_t rate = 0;
923 uint64_t dts = 0;
924 uint32_t time_wnd = 0;
925 uint32_t chunk_number = 0;
926 uint32_t sample_number_in_stts = 1;
927 uint32_t sample_number_in_chunk = 1;
928 uint32_t constant_sample_size = LSMASH_IS_EXISTING_BOX( stsz ) ? stsz->sample_size : 0;
929 *bufferSizeDB = 0;
930 *maxBitrate = 0;
931 *avgBitrate = 0;
932 while( stts_entry )
933 {
934 int err;
935 if( !stsc_data || sample_number_in_chunk == stsc_data->samples_per_chunk )
936 {
937 /* Move the next chunk. */
938 sample_number_in_chunk = 1;
939 ++chunk_number;
940 /* Check if the next entry is broken. */
941 while( next_stsc_entry && ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk < chunk_number )
942 {
943 /* Just skip broken next entry. */
944 next_stsc_entry = next_stsc_entry->next;
945 if( next_stsc_entry && !next_stsc_entry->data )
946 return LSMASH_ERR_INVALID_DATA;
947 }
948 /* Check if the next chunk belongs to the next sequence of chunks. */
949 if( next_stsc_entry && ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk == chunk_number )
950 {
951 stsc_entry = next_stsc_entry;
952 next_stsc_entry = next_stsc_entry->next;
953 if( next_stsc_entry && !next_stsc_entry->data )
954 return LSMASH_ERR_INVALID_DATA;
955 stsc_data = (isom_stsc_entry_t *)stsc_entry->data;
956 /* Check if the next contiguous chunks belong to given sample description. */
957 if( stsc_data->sample_description_index != sample_description_index )
958 {
959 /* Skip chunks which don't belong to given sample description. */
960 uint32_t number_of_skips = 0;
961 uint32_t first_chunk = stsc_data->first_chunk;
962 uint32_t samples_per_chunk = stsc_data->samples_per_chunk;
963 while( next_stsc_entry )
964 {
965 if( ((isom_stsc_entry_t *)next_stsc_entry->data)->sample_description_index != sample_description_index )
966 {
967 stsc_data = (isom_stsc_entry_t *)next_stsc_entry->data;
968 number_of_skips += (stsc_data->first_chunk - first_chunk) * samples_per_chunk;
969 first_chunk = stsc_data->first_chunk;
970 samples_per_chunk = stsc_data->samples_per_chunk;
971 }
972 else if( ((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk <= first_chunk )
973 ; /* broken entry */
974 else
975 break;
976 /* Just skip the next entry. */
977 next_stsc_entry = next_stsc_entry->next;
978 if( next_stsc_entry && !next_stsc_entry->data )
979 return LSMASH_ERR_INVALID_DATA;
980 }
981 if( !next_stsc_entry )
982 break; /* There is no more chunks which don't belong to given sample description. */
983 number_of_skips += (((isom_stsc_entry_t *)next_stsc_entry->data)->first_chunk - first_chunk) * samples_per_chunk;
984 for( uint32_t i = 0; i < number_of_skips; i++ )
985 {
986 if( stsz_list )
987 {
988 if( !stsz_entry )
989 break;
990 stsz_entry = stsz_entry->next;
991 }
992 if( !stts_entry )
993 break;
994 if( (err = isom_increment_sample_number_in_entry( &sample_number_in_stts,
995 ((isom_stts_entry_t *)stts_entry->data)->sample_count,
996 &stts_entry )) < 0 )
997 return err;
998 }
999 if( (stsz_list && !stsz_entry) || !stts_entry )
1000 break;
1001 chunk_number = stsc_data->first_chunk;
1002 }
1003 }
1004 }
1005 else
1006 ++sample_number_in_chunk;
1007 /* Get current sample's size. */
1008 uint32_t size;
1009 if( stsz_list )
1010 {
1011 if( !stsz_entry )
1012 break;
1013 isom_stsz_entry_t *stsz_data = (isom_stsz_entry_t *)stsz_entry->data;
1014 if( !stsz_data )
1015 return LSMASH_ERR_INVALID_DATA;
1016 size = stsz_data->entry_size;
1017 stsz_entry = stsz_entry->next;
1018 }
1019 else
1020 size = constant_sample_size;
1021 /* Get current sample's DTS. */
1022 if( stts_data )
1023 dts += stts_data->sample_delta;
1024 stts_data = (isom_stts_entry_t *)stts_entry->data;
1025 if( !stts_data )
1026 return LSMASH_ERR_INVALID_DATA;
1027 if( (err = isom_increment_sample_number_in_entry( &sample_number_in_stts, stts_data->sample_count, &stts_entry )) < 0 )
1028 return err;
1029 /* Calculate bitrate description. */
1030 if( *bufferSizeDB < size )
1031 *bufferSizeDB = size;
1032 *avgBitrate += size;
1033 rate += size;
1034 if( dts > time_wnd + mdhd->timescale )
1035 {
1036 if( rate > *maxBitrate )
1037 *maxBitrate = rate;
1038 time_wnd = dts;
1039 rate = 0;
1040 }
1041 }
1042 double duration = (double)mdhd->duration / mdhd->timescale;
1043 *avgBitrate = (uint32_t)(*avgBitrate / duration);
1044 if( *maxBitrate == 0 )
1045 *maxBitrate = *avgBitrate;
1046 /* Convert to bits per second. */
1047 *maxBitrate *= 8;
1048 *avgBitrate *= 8;
1049 return 0;
1050 }
1051
isom_is_variable_size(isom_stbl_t * stbl)1052 int isom_is_variable_size( isom_stbl_t *stbl )
1053 {
1054 if( (LSMASH_IS_EXISTING_BOX( stbl->stz2 ) && stbl->stz2->sample_count > 1)
1055 || (LSMASH_IS_EXISTING_BOX( stbl->stsz ) && stbl->stsz->sample_count > 1 && stbl->stsz->sample_size == 0) )
1056 return 1;
1057 else
1058 return 0;
1059 }
1060
isom_get_first_sample_size(isom_stbl_t * stbl)1061 uint32_t isom_get_first_sample_size( isom_stbl_t *stbl )
1062 {
1063 if( LSMASH_IS_EXISTING_BOX( stbl->stsz ) )
1064 {
1065 /* 'stsz' */
1066 if( stbl->stsz->sample_size )
1067 return stbl->stsz->sample_size;
1068 else if( stbl->stsz->list && stbl->stsz->list->head && stbl->stsz->list->head->data )
1069 return ((isom_stsz_entry_t *)stbl->stsz->list->head->data)->entry_size;
1070 else
1071 return 0;
1072 }
1073 else if( LSMASH_IS_EXISTING_BOX( stbl->stz2 ) )
1074 {
1075 /* stz2 */
1076 if( stbl->stz2->list && stbl->stz2->list->head && stbl->stz2->list->head->data )
1077 return ((isom_stsz_entry_t *)stbl->stz2->list->head->data)->entry_size;
1078 else
1079 return 0;
1080 }
1081 else
1082 return 0;
1083 }
1084
isom_update_bitrate_description(isom_mdia_t * mdia)1085 int isom_update_bitrate_description( isom_mdia_t *mdia )
1086 {
1087 if( LSMASH_IS_NON_EXISTING_BOX( mdia->mdhd ) )
1088 return LSMASH_ERR_INVALID_DATA;
1089 isom_stbl_t *stbl = mdia->minf->stbl;
1090 if( LSMASH_IS_NON_EXISTING_BOX( stbl->stsd )
1091 || (LSMASH_IS_NON_EXISTING_BOX( stbl->stsz ) && LSMASH_IS_NON_EXISTING_BOX( stbl->stz2 ))
1092 || !stbl->stsc->list
1093 || !stbl->stts->list )
1094 return LSMASH_ERR_INVALID_DATA;
1095 uint32_t sample_description_index = 0;
1096 for( lsmash_entry_t *entry = stbl->stsd->list.head; entry; entry = entry->next )
1097 {
1098 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)entry->data;
1099 if( !sample_entry )
1100 return LSMASH_ERR_INVALID_DATA;
1101 ++sample_description_index;
1102 isom_bitrate_updater_t func_update_bitrate = isom_get_bitrate_updater( sample_entry );
1103 if( func_update_bitrate )
1104 {
1105 int err = func_update_bitrate( stbl, mdia->mdhd, sample_description_index );
1106 if( err < 0 )
1107 return err;
1108 }
1109 }
1110 return sample_description_index ? 0 : LSMASH_ERR_INVALID_DATA;
1111 }
1112
isom_get_current_mp4time(void)1113 static inline uint64_t isom_get_current_mp4time( void )
1114 {
1115 return (uint64_t)time( NULL ) + ISOM_MAC_EPOCH_OFFSET;
1116 }
1117
isom_set_media_creation_time(isom_trak_t * trak,uint64_t current_mp4time)1118 static int isom_set_media_creation_time( isom_trak_t *trak, uint64_t current_mp4time )
1119 {
1120 if( LSMASH_IS_NON_EXISTING_BOX( trak->mdia->mdhd ) )
1121 return LSMASH_ERR_NAMELESS;
1122 isom_mdhd_t *mdhd = trak->mdia->mdhd;
1123 if( mdhd->creation_time == 0 )
1124 mdhd->creation_time = mdhd->modification_time = current_mp4time;
1125 return 0;
1126 }
1127
isom_set_track_creation_time(isom_trak_t * trak,uint64_t current_mp4time)1128 static int isom_set_track_creation_time( isom_trak_t *trak, uint64_t current_mp4time )
1129 {
1130 assert( LSMASH_IS_EXISTING_BOX( trak ) );
1131 if( LSMASH_IS_NON_EXISTING_BOX( trak->tkhd ) )
1132 return LSMASH_ERR_NAMELESS;
1133 isom_tkhd_t *tkhd = trak->tkhd;
1134 if( tkhd->creation_time == 0 )
1135 tkhd->creation_time = tkhd->modification_time = current_mp4time;
1136 return isom_set_media_creation_time( trak, current_mp4time );
1137 }
1138
isom_set_movie_creation_time(lsmash_file_t * file)1139 static int isom_set_movie_creation_time( lsmash_file_t *file )
1140 {
1141 if( LSMASH_IS_NON_EXISTING_BOX( file->moov->mvhd ) )
1142 return LSMASH_ERR_NAMELESS;
1143 uint64_t current_mp4time = isom_get_current_mp4time();
1144 for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next )
1145 {
1146 isom_trak_t *trak = (isom_trak_t *)entry->data;
1147 if( LSMASH_IS_NON_EXISTING_BOX( trak ) )
1148 return LSMASH_ERR_INVALID_DATA;
1149 int err = isom_set_track_creation_time( trak, current_mp4time );
1150 if( err < 0 )
1151 return err;
1152 }
1153 isom_mvhd_t *mvhd = file->moov->mvhd;
1154 if( mvhd->creation_time == 0 )
1155 mvhd->creation_time = mvhd->modification_time = current_mp4time;
1156 return 0;
1157 }
1158
isom_setup_handler_reference(isom_hdlr_t * hdlr,uint32_t media_type)1159 int isom_setup_handler_reference( isom_hdlr_t *hdlr, uint32_t media_type )
1160 {
1161 assert( LSMASH_IS_EXISTING_BOX( hdlr ) );
1162 isom_box_t *parent = hdlr->parent;
1163 lsmash_file_t *file = hdlr->file;
1164 if( LSMASH_IS_NON_EXISTING_BOX( parent )
1165 || LSMASH_IS_NON_EXISTING_BOX( file ) )
1166 return LSMASH_ERR_NAMELESS;
1167 isom_mdia_t *mdia = lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_MDIA ) ? (isom_mdia_t *)parent : isom_non_existing_mdia();
1168 isom_meta_t *meta = lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_META ) ? (isom_meta_t *)parent
1169 : lsmash_check_box_type_identical( parent->type, QT_BOX_TYPE_META ) ? (isom_meta_t *)parent : isom_non_existing_meta();
1170 uint32_t type = LSMASH_IS_EXISTING_BOX( mdia ) ? (file->qt_compatible ? QT_HANDLER_TYPE_MEDIA : 0)
1171 : (LSMASH_IS_EXISTING_BOX( meta ) ? 0 : QT_HANDLER_TYPE_DATA);
1172 uint32_t subtype = media_type;
1173 hdlr->componentType = type;
1174 hdlr->componentSubtype = subtype;
1175 char *type_name = NULL;
1176 char *subtype_name = NULL;
1177 uint8_t type_name_length = 0;
1178 uint8_t subtype_name_length = 0;
1179 if( mdia )
1180 type_name = "Media ";
1181 else if( meta )
1182 type_name = "Metadata ";
1183 else /* if( minf ) */
1184 type_name = "Data ";
1185 type_name_length = strlen( type_name );
1186 struct
1187 {
1188 uint32_t subtype;
1189 char *subtype_name;
1190 uint8_t subtype_name_length;
1191 } subtype_table[] =
1192 {
1193 { ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK, "Sound ", 6 },
1194 { ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK, "Video ", 6 },
1195 { ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK, "Hint ", 5 },
1196 { ISOM_MEDIA_HANDLER_TYPE_TIMED_METADATA_TRACK, "Metadata ", 9 },
1197 { ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK, "Text ", 5 },
1198 { ISOM_META_HANDLER_TYPE_ITUNES_METADATA, "iTunes ", 7 },
1199 { QT_REFERENCE_HANDLER_TYPE_ALIAS, "Alias ", 6 },
1200 { QT_REFERENCE_HANDLER_TYPE_RESOURCE, "Resource ", 9 },
1201 { QT_REFERENCE_HANDLER_TYPE_URL, "URL ", 4 },
1202 { subtype, "Unknown ", 8 }
1203 };
1204 for( int i = 0; subtype_table[i].subtype; i++ )
1205 if( subtype == subtype_table[i].subtype )
1206 {
1207 subtype_name = subtype_table[i].subtype_name;
1208 subtype_name_length = subtype_table[i].subtype_name_length;
1209 break;
1210 }
1211 uint32_t name_length = 15 + subtype_name_length + type_name_length + file->isom_compatible + file->qt_compatible;
1212 uint8_t *name = lsmash_malloc( name_length );
1213 if( !name )
1214 return LSMASH_ERR_MEMORY_ALLOC;
1215 if( file->qt_compatible )
1216 name[0] = name_length & 0xff;
1217 memcpy( name + file->qt_compatible, "L-SMASH ", 8 );
1218 memcpy( name + file->qt_compatible + 8, subtype_name, subtype_name_length );
1219 memcpy( name + file->qt_compatible + 8 + subtype_name_length, type_name, type_name_length );
1220 memcpy( name + file->qt_compatible + 8 + subtype_name_length + type_name_length, "Handler", 7 );
1221 if( file->isom_compatible )
1222 name[name_length - 1] = 0;
1223 hdlr->componentName = name;
1224 hdlr->componentName_length = name_length;
1225 return 0;
1226 }
1227
isom_track_create(lsmash_file_t * file,lsmash_media_type media_type)1228 isom_trak_t *isom_track_create( lsmash_file_t *file, lsmash_media_type media_type )
1229 {
1230 /* Don't allow to create a new track if the initial movie is already written. */
1231 if( (file->fragment && file->fragment->movie)
1232 || (LSMASH_IS_EXISTING_BOX( file->moov ) && (file->moov->manager & LSMASH_WRITTEN_BOX)) )
1233 return isom_non_existing_trak();
1234 isom_trak_t *trak = isom_add_trak( file->moov );
1235 if( LSMASH_IS_NON_EXISTING_BOX( trak->file->moov->mvhd ) )
1236 goto fail;
1237 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_tkhd( trak ) )
1238 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_mdia( trak ) )
1239 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_mdhd( trak->mdia ) )
1240 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_minf( trak->mdia ) )
1241 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_dinf( trak->mdia->minf ) )
1242 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_dref( trak->mdia->minf->dinf ) )
1243 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stbl( trak->mdia->minf ) )
1244 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stsd( trak->mdia->minf->stbl ) )
1245 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stts( trak->mdia->minf->stbl ) )
1246 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stsc( trak->mdia->minf->stbl ) )
1247 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stco( trak->mdia->minf->stbl ) )
1248 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stsz( trak->mdia->minf->stbl ) ) )
1249 goto fail;
1250 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_hdlr( trak->mdia ) )
1251 || isom_setup_handler_reference( trak->mdia->hdlr, media_type ) < 0 )
1252 goto fail;
1253 if( file->qt_compatible )
1254 {
1255 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_hdlr( trak->mdia->minf ) )
1256 || isom_setup_handler_reference( trak->mdia->minf->hdlr, QT_REFERENCE_HANDLER_TYPE_URL ) < 0 )
1257 goto fail;
1258 }
1259 switch( media_type )
1260 {
1261 case ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK :
1262 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_vmhd( trak->mdia->minf ) ) )
1263 goto fail;
1264 trak->mdia->minf->vmhd->flags = 0x000001;
1265 break;
1266 case ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK :
1267 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_smhd( trak->mdia->minf ) ) )
1268 goto fail;
1269 trak->cache->is_audio = 1;
1270 break;
1271 case ISOM_MEDIA_HANDLER_TYPE_HINT_TRACK :
1272 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_hmhd( trak->mdia->minf ) ) )
1273 goto fail;
1274 break;
1275 case ISOM_MEDIA_HANDLER_TYPE_TEXT_TRACK :
1276 if( file->qt_compatible || file->itunes_movie )
1277 {
1278 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_gmhd( trak->mdia->minf ) )
1279 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_gmin( trak->mdia->minf->gmhd ) )
1280 || LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_text( trak->mdia->minf->gmhd ) ) )
1281 return 0;
1282 /* Default Text Media Information Box. */
1283 {
1284 isom_text_t *text = trak->mdia->minf->gmhd->text;
1285 text->matrix[0] = 0x00010000;
1286 text->matrix[4] = 0x00010000;
1287 text->matrix[8] = 0x40000000;
1288 }
1289 }
1290 else
1291 goto fail; /* We support only reference text media track for chapter yet. */
1292 break;
1293 default :
1294 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_nmhd( trak->mdia->minf ) ) )
1295 goto fail;
1296 break;
1297 }
1298 /* Default Track Header Box. */
1299 {
1300 isom_tkhd_t *tkhd = trak->tkhd;
1301 if( media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK )
1302 tkhd->volume = 0x0100;
1303 tkhd->matrix[0] = 0x00010000;
1304 tkhd->matrix[4] = 0x00010000;
1305 tkhd->matrix[8] = 0x40000000;
1306 tkhd->duration = 0xffffffff;
1307 tkhd->track_ID = trak->file->moov->mvhd->next_track_ID ++;
1308 }
1309 trak->mdia->mdhd->language = file->qt_compatible ? 0 : ISOM_LANGUAGE_CODE_UNDEFINED;
1310 return trak;
1311 fail:
1312 isom_remove_box_by_itself( trak );
1313 return isom_non_existing_trak();
1314 }
1315
isom_movie_create(lsmash_file_t * file)1316 isom_moov_t *isom_movie_create( lsmash_file_t *file )
1317 {
1318 isom_moov_t *moov = isom_add_moov( file );
1319 isom_mvhd_t *mvhd = isom_add_mvhd( moov );
1320 if( LSMASH_IS_NON_EXISTING_BOX( mvhd ) )
1321 {
1322 isom_remove_box_by_itself( moov );
1323 return isom_non_existing_moov();
1324 }
1325 /* Default Movie Header Box. */
1326 mvhd->rate = 0x00010000;
1327 mvhd->volume = 0x0100;
1328 mvhd->matrix[0] = 0x00010000;
1329 mvhd->matrix[4] = 0x00010000;
1330 mvhd->matrix[8] = 0x40000000;
1331 mvhd->next_track_ID = 1;
1332 file->initializer = file;
1333 return moov;
1334 }
1335
1336 /*******************************
1337 public interfaces
1338 *******************************/
1339
1340 /*---- track manipulators ----*/
1341
lsmash_delete_track(lsmash_root_t * root,uint32_t track_ID)1342 void lsmash_delete_track( lsmash_root_t *root, uint32_t track_ID )
1343 {
1344 if( isom_check_initializer_present( root ) < 0 )
1345 return;
1346 for( lsmash_entry_t *entry = root->file->initializer->moov->trak_list.head; entry; entry = entry->next )
1347 {
1348 isom_trak_t *trak = (isom_trak_t *)entry->data;
1349 if( LSMASH_IS_NON_EXISTING_BOX( trak )
1350 || LSMASH_IS_NON_EXISTING_BOX( trak->tkhd ) )
1351 return;
1352 if( trak->tkhd->track_ID == track_ID )
1353 {
1354 isom_remove_box_by_itself( trak );
1355 return;
1356 }
1357 }
1358 }
1359
lsmash_create_track(lsmash_root_t * root,lsmash_media_type media_type)1360 uint32_t lsmash_create_track( lsmash_root_t *root, lsmash_media_type media_type )
1361 {
1362 if( isom_check_initializer_present( root ) < 0 )
1363 return 0;
1364 isom_trak_t *trak = isom_track_create( root->file, media_type );
1365 if( LSMASH_IS_NON_EXISTING_BOX( trak )
1366 || LSMASH_IS_NON_EXISTING_BOX( trak->tkhd ) )
1367 return 0;
1368 return trak->tkhd->track_ID;
1369 }
1370
lsmash_get_track_ID(lsmash_root_t * root,uint32_t track_number)1371 uint32_t lsmash_get_track_ID( lsmash_root_t *root, uint32_t track_number )
1372 {
1373 if( isom_check_initializer_present( root ) < 0
1374 || LSMASH_IS_NON_EXISTING_BOX( root->file->initializer->moov ) )
1375 return 0;
1376 isom_trak_t *trak = (isom_trak_t *)lsmash_list_get_entry_data( &root->file->initializer->moov->trak_list, track_number );
1377 if( LSMASH_IS_NON_EXISTING_BOX( trak )
1378 || LSMASH_IS_NON_EXISTING_BOX( trak->tkhd ) )
1379 return 0;
1380 return trak->tkhd->track_ID;
1381 }
1382
lsmash_initialize_track_parameters(lsmash_track_parameters_t * param)1383 void lsmash_initialize_track_parameters( lsmash_track_parameters_t *param )
1384 {
1385 memset( param, 0, sizeof(lsmash_track_parameters_t) );
1386 param->audio_volume = 0x0100;
1387 param->matrix[0] = 0x00010000;
1388 param->matrix[4] = 0x00010000;
1389 param->matrix[8] = 0x40000000;
1390 }
1391
lsmash_set_track_parameters(lsmash_root_t * root,uint32_t track_ID,lsmash_track_parameters_t * param)1392 int lsmash_set_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param )
1393 {
1394 if( isom_check_initializer_present( root ) < 0 )
1395 return LSMASH_ERR_FUNCTION_PARAM;
1396 lsmash_file_t *file = root->file;
1397 isom_trak_t *trak = isom_get_trak( file, track_ID );
1398 if( LSMASH_IS_NON_EXISTING_BOX( trak->tkhd )
1399 || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->hdlr )
1400 || LSMASH_IS_NON_EXISTING_BOX( file->moov->mvhd ) )
1401 return LSMASH_ERR_NAMELESS;
1402 /* Prepare Track Aperture Modes if required. */
1403 if( file->qt_compatible && param->aperture_modes )
1404 {
1405 if( LSMASH_IS_NON_EXISTING_BOX( trak->tapt ) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_tapt( trak ) ) )
1406 return LSMASH_ERR_NAMELESS;
1407 isom_tapt_t *tapt = trak->tapt;
1408 if( (LSMASH_IS_NON_EXISTING_BOX( tapt->clef ) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_clef( tapt ) ))
1409 || (LSMASH_IS_NON_EXISTING_BOX( tapt->prof ) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_prof( tapt ) ))
1410 || (LSMASH_IS_NON_EXISTING_BOX( tapt->enof ) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_enof( tapt ) )) )
1411 return LSMASH_ERR_NAMELESS;
1412 }
1413 else
1414 isom_remove_box_by_itself( trak->tapt );
1415 /* Set up Track Header. */
1416 uint32_t media_type = trak->mdia->hdlr->componentSubtype;
1417 isom_tkhd_t *tkhd = trak->tkhd;
1418 tkhd->flags = param->mode;
1419 tkhd->track_ID = param->track_ID ? param->track_ID : tkhd->track_ID;
1420 tkhd->duration = LSMASH_IS_NON_EXISTING_BOX( trak->edts->elst ) ? param->duration : tkhd->duration;
1421 /* Template fields
1422 * alternate_group, layer, volume and matrix
1423 * According to 14496-14, these value are all set to defaut values in 14496-12.
1424 * And when a file is read as an MPEG-4 file, these values shall be ignored.
1425 * If a file complies with other specifications, then those fields may have non-default values
1426 * as required by those other specifications. */
1427 if( param->alternate_group )
1428 {
1429 if( file->qt_compatible || file->itunes_movie || file->max_3gpp_version >= 4 )
1430 tkhd->alternate_group = param->alternate_group;
1431 else
1432 {
1433 tkhd->alternate_group = 0;
1434 lsmash_log( NULL, LSMASH_LOG_WARNING,
1435 "alternate_group is specified but not compatible with any of the brands. It won't be set.\n" );
1436 }
1437 }
1438 else
1439 tkhd->alternate_group = 0;
1440 if( file->qt_compatible || file->itunes_movie )
1441 {
1442 tkhd->layer = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->video_layer : 0;
1443 tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? param->audio_volume : 0;
1444 if( media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK )
1445 for( int i = 0; i < 9; i++ )
1446 tkhd->matrix[i] = param->matrix[i];
1447 else
1448 for( int i = 0; i < 9; i++ )
1449 tkhd->matrix[i] = 0;
1450 }
1451 else
1452 {
1453 tkhd->layer = 0;
1454 tkhd->volume = media_type == ISOM_MEDIA_HANDLER_TYPE_AUDIO_TRACK ? 0x0100 : 0;
1455 tkhd->matrix[0] = 0x00010000;
1456 tkhd->matrix[1] = 0;
1457 tkhd->matrix[2] = 0;
1458 tkhd->matrix[3] = 0;
1459 tkhd->matrix[4] = 0x00010000;
1460 tkhd->matrix[5] = 0;
1461 tkhd->matrix[6] = 0;
1462 tkhd->matrix[7] = 0;
1463 tkhd->matrix[8] = 0x40000000;
1464 }
1465 /* visual presentation size */
1466 tkhd->width = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_width : 0;
1467 tkhd->height = media_type == ISOM_MEDIA_HANDLER_TYPE_VIDEO_TRACK ? param->display_height : 0;
1468 /* Update next_track_ID if needed. */
1469 if( file->moov->mvhd->next_track_ID <= tkhd->track_ID )
1470 file->moov->mvhd->next_track_ID = tkhd->track_ID + 1;
1471 return 0;
1472 }
1473
lsmash_get_track_parameters(lsmash_root_t * root,uint32_t track_ID,lsmash_track_parameters_t * param)1474 int lsmash_get_track_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_track_parameters_t *param )
1475 {
1476 if( isom_check_initializer_present( root ) < 0 )
1477 return LSMASH_ERR_FUNCTION_PARAM;
1478 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1479 if( LSMASH_IS_NON_EXISTING_BOX( trak->tkhd ) )
1480 return LSMASH_ERR_NAMELESS;
1481 isom_tkhd_t *tkhd = trak->tkhd;
1482 param->mode = tkhd->flags;
1483 param->track_ID = tkhd->track_ID;
1484 param->duration = tkhd->duration;
1485 param->video_layer = tkhd->layer;
1486 param->alternate_group = tkhd->alternate_group;
1487 param->audio_volume = tkhd->volume;
1488 for( int i = 0; i < 9; i++ )
1489 param->matrix[i] = tkhd->matrix[i];
1490 param->display_width = tkhd->width;
1491 param->display_height = tkhd->height;
1492 param->aperture_modes = !!trak->tapt;
1493 return 0;
1494 }
1495
check_dref_presence(isom_trak_t * trak)1496 static inline int check_dref_presence( isom_trak_t *trak )
1497 {
1498 if( LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->dinf->dref ) )
1499 return LSMASH_ERR_NAMELESS;
1500 return 0;
1501 }
1502
lsmash_count_data_reference(lsmash_root_t * root,uint32_t track_ID)1503 uint32_t lsmash_count_data_reference
1504 (
1505 lsmash_root_t *root,
1506 uint32_t track_ID
1507 )
1508 {
1509 if( isom_check_initializer_present( root ) < 0 )
1510 return 0;
1511 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1512 if( check_dref_presence( trak ) < 0 )
1513 return 0;
1514 return trak->mdia->minf->dinf->dref->list.entry_count;
1515 }
1516
lsmash_get_data_reference(lsmash_root_t * root,uint32_t track_ID,lsmash_data_reference_t * data_ref)1517 int lsmash_get_data_reference
1518 (
1519 lsmash_root_t *root,
1520 uint32_t track_ID,
1521 lsmash_data_reference_t *data_ref
1522 )
1523 {
1524 if( isom_check_initializer_present( root ) < 0 || !data_ref )
1525 return LSMASH_ERR_FUNCTION_PARAM;
1526 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1527 if( check_dref_presence( trak ) < 0 )
1528 return LSMASH_ERR_NAMELESS;
1529 isom_dref_entry_t *url = lsmash_list_get_entry_data( &trak->mdia->minf->dinf->dref->list, data_ref->index );
1530 if( LSMASH_IS_NON_EXISTING_BOX( url ) )
1531 return LSMASH_ERR_NAMELESS;
1532 if( !(url->flags & 0x000001) && url->location )
1533 {
1534 int length = strlen( url->location );
1535 char *location = lsmash_malloc( length + 1 );
1536 if( !location )
1537 return LSMASH_ERR_MEMORY_ALLOC;
1538 memcpy( location, url->location, length );
1539 location[length] = '\0';
1540 data_ref->location = location;
1541 }
1542 else
1543 data_ref->location = NULL;
1544 return 0;
1545 }
1546
lsmash_cleanup_data_reference(lsmash_data_reference_t * data_ref)1547 void lsmash_cleanup_data_reference
1548 (
1549 lsmash_data_reference_t *data_ref
1550 )
1551 {
1552 if( !data_ref )
1553 return;
1554 lsmash_freep( &data_ref->location );
1555 }
1556
lsmash_create_data_reference(lsmash_root_t * root,uint32_t track_ID,lsmash_data_reference_t * data_ref,lsmash_file_t * file)1557 int lsmash_create_data_reference
1558 (
1559 lsmash_root_t *root,
1560 uint32_t track_ID,
1561 lsmash_data_reference_t *data_ref,
1562 lsmash_file_t *file
1563 )
1564 {
1565 /* At present, we don't support external data references for movie fragments.
1566 * Note that, for external media data, default-base-is-moof is meaningless since relative
1567 * offsets from Movie Fragment Boxes make no sense.
1568 * In the future, the condition of !(file->flags & LSMASH_FILE_MODE_WRITE) may be removed
1569 * for the implementation which does not write actually and does reference read-only file. */
1570 if( LSMASH_IS_NON_EXISTING_BOX( root )
1571 || LSMASH_IS_NON_EXISTING_BOX( file )
1572 || file->root != root
1573 || (!(file->flags & LSMASH_FILE_MODE_MEDIA) && !(file->flags & LSMASH_FILE_MODE_INITIALIZATION))
1574 || !(file->flags & LSMASH_FILE_MODE_WRITE)
1575 || (root->file != file && ((file->flags & LSMASH_FILE_MODE_FRAGMENTED) || file->fragment))
1576 || !data_ref )
1577 return LSMASH_ERR_FUNCTION_PARAM;
1578 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
1579 if( check_dref_presence( trak ) < 0 )
1580 return LSMASH_ERR_NAMELESS;
1581 isom_dref_entry_t *url = isom_add_dref_entry( trak->mdia->minf->dinf->dref, ISOM_BOX_TYPE_URL );
1582 if( LSMASH_IS_NON_EXISTING_BOX( url ) )
1583 return LSMASH_ERR_NAMELESS;
1584 if( !data_ref->location || root->file == file )
1585 {
1586 /* Media data is in the same file. */
1587 url->flags = 0x000001;
1588 url->ref_file = root->file;
1589 }
1590 else
1591 {
1592 /* Set the location of the file. */
1593 int length = strlen( data_ref->location );
1594 url->location = lsmash_malloc( length + 1 );
1595 if( !url->location )
1596 {
1597 isom_remove_box_by_itself( url );
1598 return LSMASH_ERR_MEMORY_ALLOC;
1599 }
1600 memcpy( url->location, data_ref->location, length );
1601 url->location[length] = '\0';
1602 url->location_length = length + 1;
1603 url->ref_file = file;
1604 }
1605 data_ref->index = trak->mdia->minf->dinf->dref->list.entry_count;
1606 return 0;
1607 }
1608
lsmash_assign_data_reference(lsmash_root_t * root,uint32_t track_ID,uint32_t data_ref_index,lsmash_file_t * file)1609 int lsmash_assign_data_reference
1610 (
1611 lsmash_root_t *root,
1612 uint32_t track_ID,
1613 uint32_t data_ref_index,
1614 lsmash_file_t *file
1615 )
1616 {
1617 if( isom_check_initializer_present( root ) < 0
1618 || !file || file->root != root
1619 || !(file->flags & LSMASH_FILE_MODE_MEDIA)
1620 || !(file->flags & LSMASH_FILE_MODE_READ)
1621 || data_ref_index == 0 )
1622 return LSMASH_ERR_FUNCTION_PARAM;
1623 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1624 if( check_dref_presence( trak ) < 0 )
1625 return LSMASH_ERR_NAMELESS;
1626 isom_dref_entry_t *url = (isom_dref_entry_t *)lsmash_list_get_entry_data( &trak->mdia->minf->dinf->dref->list, data_ref_index );
1627 if( LSMASH_IS_NON_EXISTING_BOX( url ) )
1628 return LSMASH_ERR_NAMELESS;
1629 if( !(url->flags & 0x000001) )
1630 /* Reference an external media data. */
1631 url->ref_file = file;
1632 return 0;
1633 }
1634
isom_set_media_handler_name(lsmash_file_t * file,uint32_t track_ID,char * handler_name)1635 static int isom_set_media_handler_name( lsmash_file_t *file, uint32_t track_ID, char *handler_name )
1636 {
1637 isom_trak_t *trak = isom_get_trak( file, track_ID );
1638 if( LSMASH_IS_NON_EXISTING_BOX( trak->mdia->hdlr ) )
1639 return LSMASH_ERR_NAMELESS;
1640 isom_hdlr_t *hdlr = trak->mdia->hdlr;
1641 uint8_t *name = NULL;
1642 uint32_t name_length = strlen( handler_name ) + file->isom_compatible + file->qt_compatible;
1643 if( file->qt_compatible )
1644 name_length = LSMASH_MIN( name_length, 255 );
1645 if( name_length > hdlr->componentName_length && hdlr->componentName )
1646 name = lsmash_realloc( hdlr->componentName, name_length );
1647 else if( !hdlr->componentName )
1648 name = lsmash_malloc( name_length );
1649 else
1650 name = hdlr->componentName;
1651 if( !name )
1652 return LSMASH_ERR_MEMORY_ALLOC;
1653 if( file->qt_compatible )
1654 name[0] = name_length & 0xff;
1655 memcpy( name + file->qt_compatible, handler_name, strlen( handler_name ) );
1656 if( file->isom_compatible )
1657 name[name_length - 1] = 0;
1658 hdlr->componentName = name;
1659 hdlr->componentName_length = name_length;
1660 return 0;
1661 }
1662
isom_set_data_handler_name(lsmash_file_t * file,uint32_t track_ID,char * handler_name)1663 static int isom_set_data_handler_name( lsmash_file_t *file, uint32_t track_ID, char *handler_name )
1664 {
1665 isom_trak_t *trak = isom_get_trak( file, track_ID );
1666 if( LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->hdlr ) )
1667 return LSMASH_ERR_NAMELESS;
1668 isom_hdlr_t *hdlr = trak->mdia->minf->hdlr;
1669 uint8_t *name = NULL;
1670 uint32_t name_length = strlen( handler_name ) + file->isom_compatible + file->qt_compatible;
1671 if( file->qt_compatible )
1672 name_length = LSMASH_MIN( name_length, 255 );
1673 if( name_length > hdlr->componentName_length && hdlr->componentName )
1674 name = lsmash_realloc( hdlr->componentName, name_length );
1675 else if( !hdlr->componentName )
1676 name = lsmash_malloc( name_length );
1677 else
1678 name = hdlr->componentName;
1679 if( !name )
1680 return LSMASH_ERR_MEMORY_ALLOC;
1681 if( file->qt_compatible )
1682 name[0] = name_length & 0xff;
1683 memcpy( name + file->qt_compatible, handler_name, strlen( handler_name ) );
1684 if( file->isom_compatible )
1685 name[name_length - 1] = 0;
1686 hdlr->componentName = name;
1687 hdlr->componentName_length = name_length;
1688 return 0;
1689 }
1690
lsmash_get_media_timescale(lsmash_root_t * root,uint32_t track_ID)1691 uint32_t lsmash_get_media_timescale( lsmash_root_t *root, uint32_t track_ID )
1692 {
1693 if( isom_check_initializer_present( root ) < 0 )
1694 return 0;
1695 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1696 if( LSMASH_IS_NON_EXISTING_BOX( trak->mdia->mdhd ) )
1697 return 0;
1698 return trak->mdia->mdhd->timescale;
1699 }
1700
lsmash_get_media_duration(lsmash_root_t * root,uint32_t track_ID)1701 uint64_t lsmash_get_media_duration( lsmash_root_t *root, uint32_t track_ID )
1702 {
1703 if( isom_check_initializer_present( root ) < 0 )
1704 return 0;
1705 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1706 if( LSMASH_IS_NON_EXISTING_BOX( trak->mdia->mdhd ) )
1707 return 0;
1708 return trak->mdia->mdhd->duration;
1709 }
1710
lsmash_get_track_duration(lsmash_root_t * root,uint32_t track_ID)1711 uint64_t lsmash_get_track_duration( lsmash_root_t *root, uint32_t track_ID )
1712 {
1713 if( isom_check_initializer_present( root ) < 0 )
1714 return 0;
1715 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
1716 if( LSMASH_IS_NON_EXISTING_BOX( trak->tkhd ) )
1717 return 0;
1718 return trak->tkhd->duration;
1719 }
1720
lsmash_get_last_sample_delta(lsmash_root_t * root,uint32_t track_ID)1721 uint32_t lsmash_get_last_sample_delta( lsmash_root_t *root, uint32_t track_ID )
1722 {
1723 if( isom_check_initializer_present( root ) < 0 )
1724 return 0;
1725 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
1726 if( !trak->mdia->minf->stbl->stts->list
1727 || !trak->mdia->minf->stbl->stts->list->tail
1728 || !trak->mdia->minf->stbl->stts->list->tail->data )
1729 return 0;
1730 return ((isom_stts_entry_t *)trak->mdia->minf->stbl->stts->list->tail->data)->sample_delta;
1731 }
1732
lsmash_get_start_time_offset(lsmash_root_t * root,uint32_t track_ID)1733 uint32_t lsmash_get_start_time_offset( lsmash_root_t *root, uint32_t track_ID )
1734 {
1735 if( isom_check_initializer_present( root ) < 0 )
1736 return 0;
1737 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
1738 if( !trak->mdia->minf->stbl->ctts->list
1739 || !trak->mdia->minf->stbl->ctts->list->head
1740 || !trak->mdia->minf->stbl->ctts->list->head->data )
1741 return 0;
1742 return ((isom_ctts_entry_t *)trak->mdia->minf->stbl->ctts->list->head->data)->sample_offset;
1743 }
1744
lsmash_get_composition_to_decode_shift(lsmash_root_t * root,uint32_t track_ID)1745 uint32_t lsmash_get_composition_to_decode_shift( lsmash_root_t *root, uint32_t track_ID )
1746 {
1747 if( isom_check_initializer_present( root ) < 0 )
1748 return 0;
1749 lsmash_file_t *file = root->file->initializer;
1750 isom_trak_t *trak = isom_get_trak( file, track_ID );
1751 uint32_t sample_count = isom_get_sample_count( trak );
1752 if( sample_count == 0 )
1753 return 0;
1754 isom_stbl_t *stbl = trak->mdia->minf->stbl;
1755 if( !stbl->stts->list
1756 || !stbl->ctts->list )
1757 return 0;
1758 if( !(file->max_isom_version >= 4 && stbl->ctts->version == 1) && !file->qt_compatible )
1759 return 0; /* This movie shall not have composition to decode timeline shift. */
1760 lsmash_entry_t *stts_entry = stbl->stts->list->head;
1761 lsmash_entry_t *ctts_entry = stbl->ctts->list->head;
1762 if( !stts_entry || !ctts_entry )
1763 return 0;
1764 uint64_t dts = 0;
1765 uint64_t cts = 0;
1766 uint32_t ctd_shift = 0;
1767 uint32_t i = 0;
1768 uint32_t j = 0;
1769 for( uint32_t k = 0; k < sample_count; k++ )
1770 {
1771 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)stts_entry->data;
1772 isom_ctts_entry_t *ctts_data = (isom_ctts_entry_t *)ctts_entry->data;
1773 if( !stts_data || !ctts_data )
1774 return 0;
1775 if( ctts_data->sample_offset != ISOM_NON_OUTPUT_SAMPLE_OFFSET )
1776 {
1777 cts = dts + (int32_t)ctts_data->sample_offset;
1778 if( dts > cts + ctd_shift )
1779 ctd_shift = dts - cts;
1780 }
1781 dts += stts_data->sample_delta;
1782 if( ++i == stts_data->sample_count )
1783 {
1784 stts_entry = stts_entry->next;
1785 if( !stts_entry )
1786 return 0;
1787 i = 0;
1788 }
1789 if( ++j == ctts_data->sample_count )
1790 {
1791 ctts_entry = ctts_entry->next;
1792 if( !ctts_entry )
1793 return 0;
1794 j = 0;
1795 }
1796 }
1797 return ctd_shift;
1798 }
1799
lsmash_pack_iso_language(char * iso_language)1800 uint16_t lsmash_pack_iso_language( char *iso_language )
1801 {
1802 if( !iso_language || strlen( iso_language ) != 3 )
1803 return 0;
1804 return (uint16_t)LSMASH_PACK_ISO_LANGUAGE( iso_language[0], iso_language[1], iso_language[2] );
1805 }
1806
isom_iso2mac_language(uint16_t ISO_language,uint16_t * MAC_language)1807 static int isom_iso2mac_language( uint16_t ISO_language, uint16_t *MAC_language )
1808 {
1809 assert( MAC_language );
1810 int i = 0;
1811 for( ; isom_languages[i].iso_name; i++ )
1812 if( ISO_language == isom_languages[i].iso_name )
1813 break;
1814 if( !isom_languages[i].iso_name )
1815 return LSMASH_ERR_NAMELESS;
1816 *MAC_language = isom_languages[i].mac_value;
1817 return 0;
1818 }
1819
isom_mac2iso_language(uint16_t MAC_language,uint16_t * ISO_language)1820 static int isom_mac2iso_language( uint16_t MAC_language, uint16_t *ISO_language )
1821 {
1822 assert( ISO_language );
1823 int i = 0;
1824 for( ; isom_languages[i].iso_name; i++ )
1825 if( MAC_language == isom_languages[i].mac_value )
1826 break;
1827 *ISO_language = isom_languages[i].iso_name ? isom_languages[i].iso_name : ISOM_LANGUAGE_CODE_UNDEFINED;
1828 return 0;
1829 }
1830
isom_set_media_language(lsmash_file_t * file,uint32_t track_ID,uint16_t ISO_language,uint16_t MAC_language)1831 static int isom_set_media_language( lsmash_file_t *file, uint32_t track_ID, uint16_t ISO_language, uint16_t MAC_language )
1832 {
1833 isom_trak_t *trak = isom_get_trak( file, track_ID );
1834 if( LSMASH_IS_NON_EXISTING_BOX( trak->mdia->mdhd ) )
1835 return LSMASH_ERR_NAMELESS;
1836 uint16_t language = 0;
1837 if( file->isom_compatible )
1838 {
1839 if( ISO_language )
1840 language = ISO_language;
1841 else if( MAC_language )
1842 {
1843 int err = isom_mac2iso_language( MAC_language, &language );
1844 if( err )
1845 return err;
1846 }
1847 else
1848 language = ISOM_LANGUAGE_CODE_UNDEFINED;
1849 }
1850 else if( file->qt_compatible )
1851 {
1852 if( ISO_language )
1853 {
1854 int err = isom_iso2mac_language( ISO_language, &language );
1855 if( err )
1856 return err;
1857 }
1858 else
1859 language = MAC_language;
1860 }
1861 else
1862 return LSMASH_ERR_INVALID_DATA;
1863 trak->mdia->mdhd->language = language;
1864 return 0;
1865 }
1866
isom_add_sample_grouping(isom_box_t * parent,isom_grouping_type grouping_type)1867 int isom_add_sample_grouping( isom_box_t *parent, isom_grouping_type grouping_type )
1868 {
1869 isom_sgpd_t *sgpd;
1870 isom_sbgp_t *sbgp;
1871 if( ((sgpd = isom_add_sgpd( parent )), LSMASH_IS_NON_EXISTING_BOX( sgpd ))
1872 || ((sbgp = isom_add_sbgp( parent )), LSMASH_IS_NON_EXISTING_BOX( sbgp )) )
1873 return LSMASH_ERR_NAMELESS;
1874 sbgp->grouping_type = grouping_type;
1875 sgpd->grouping_type = grouping_type;
1876 sgpd->version = 1; /* We use version 1 for Sample Group Description Box because it is recommended in the spec. */
1877 switch( grouping_type )
1878 {
1879 case ISOM_GROUP_TYPE_RAP :
1880 sgpd->default_length = 1;
1881 break;
1882 case ISOM_GROUP_TYPE_ROLL :
1883 case ISOM_GROUP_TYPE_PROL :
1884 sgpd->default_length = 2;
1885 break;
1886 default :
1887 /* We don't consider other grouping types currently. */
1888 break;
1889 }
1890 return 0;
1891 }
1892
isom_create_sample_grouping(isom_trak_t * trak,isom_grouping_type grouping_type)1893 static int isom_create_sample_grouping( isom_trak_t *trak, isom_grouping_type grouping_type )
1894 {
1895 assert( LSMASH_IS_EXISTING_BOX( trak ) );
1896 lsmash_file_t *file = trak->file;
1897 switch( grouping_type )
1898 {
1899 case ISOM_GROUP_TYPE_RAP :
1900 assert( file->max_isom_version >= 6 );
1901 break;
1902 case ISOM_GROUP_TYPE_ROLL :
1903 case ISOM_GROUP_TYPE_PROL :
1904 assert( file->avc_extensions || file->qt_compatible );
1905 break;
1906 default :
1907 assert( 0 );
1908 break;
1909 }
1910 int err = isom_add_sample_grouping( (isom_box_t *)trak->mdia->minf->stbl, grouping_type );
1911 if( err < 0 )
1912 return err;
1913 if( trak->cache->fragment && file->max_isom_version >= 6 )
1914 switch( grouping_type )
1915 {
1916 case ISOM_GROUP_TYPE_RAP :
1917 trak->cache->fragment->rap_grouping = 1;
1918 break;
1919 case ISOM_GROUP_TYPE_ROLL :
1920 case ISOM_GROUP_TYPE_PROL :
1921 trak->cache->fragment->roll_grouping = 1;
1922 break;
1923 default :
1924 /* We don't consider other grouping types currently. */
1925 break;
1926 }
1927 return 0;
1928 }
1929
isom_compress_sample_size_table(isom_stbl_t * stbl)1930 static int isom_compress_sample_size_table( isom_stbl_t *stbl )
1931 {
1932 if( stbl->file->max_3gpp_version )
1933 /* 3GPP: Limitations to the ISO base media file format
1934 * - compact sample sizes ('stz2') shall not be used for tracks containing H.263, MPEG-4 video, AMR, AMR-WB, AAC or Timed text.
1935 * Note that 'mp4a' check is incomplete here since this restriction is not applied to Enhanced aacPlus audio (HE-AAC v2). */
1936 for( lsmash_entry_t *entry = stbl->stsd->list.head; entry; entry = entry->next )
1937 {
1938 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)entry->data;
1939 if( LSMASH_IS_NON_EXISTING_BOX( sample_entry ) )
1940 return LSMASH_ERR_INVALID_DATA;
1941 lsmash_codec_type_t sample_type = sample_entry->type;
1942 if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_S263_VIDEO )
1943 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4V_VIDEO )
1944 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO )
1945 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_SAMR_AUDIO )
1946 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_SAWB_AUDIO )
1947 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_TX3G_TEXT ) )
1948 return 0;
1949 }
1950 if( LSMASH_IS_EXISTING_BOX( stbl->stsz ) && isom_is_variable_size( stbl ) )
1951 {
1952 int max_num_bits = 0;
1953 for( lsmash_entry_t *entry = stbl->stsz->list->head; entry; entry = entry->next )
1954 {
1955 isom_stsz_entry_t *data = (isom_stsz_entry_t *)entry->data;
1956 if( !data )
1957 return LSMASH_ERR_INVALID_DATA;
1958 int num_bits;
1959 for( num_bits = 1; data->entry_size >> num_bits; num_bits++ );
1960 if( max_num_bits < num_bits )
1961 {
1962 max_num_bits = num_bits;
1963 if( max_num_bits > 16 )
1964 return 0; /* not compressible */
1965 }
1966 }
1967 if( max_num_bits <= 16 && LSMASH_IS_BOX_ADDITION_SUCCESS( isom_add_stz2( stbl ) ) )
1968 {
1969 /* The sample size table can be compressed by using 'stz2'. */
1970 isom_stsz_t *stsz = stbl->stsz;
1971 isom_stz2_t *stz2 = stbl->stz2;
1972 stz2->sample_count = stsz->sample_count;
1973 if( max_num_bits <= 4 )
1974 stz2->field_size = 4;
1975 else if( max_num_bits <= 8 )
1976 stz2->field_size = 8;
1977 else
1978 stz2->field_size = 16;
1979 lsmash_list_move_entries( stz2->list, stsz->list );
1980 isom_remove_box_by_itself( stsz );
1981 }
1982 }
1983 return 0;
1984 }
1985
isom_add_dependency_type(isom_stbl_t * stbl,lsmash_file_t * file,lsmash_sample_property_t * prop)1986 static int isom_add_dependency_type( isom_stbl_t *stbl, lsmash_file_t *file, lsmash_sample_property_t *prop )
1987 {
1988 if( !file->qt_compatible && !file->avc_extensions )
1989 return 0;
1990 int compatibility = file->avc_extensions && file->qt_compatible ? 3
1991 : file->qt_compatible ? 2
1992 : file->avc_extensions ? 1
1993 : 0;
1994 if( LSMASH_IS_EXISTING_BOX( stbl->sdtp ) )
1995 return isom_add_sdtp_entry( (isom_box_t *)stbl, prop, compatibility );
1996 /* no null check for prop */
1997 if( !prop->allow_earlier
1998 && !prop->leading
1999 && !prop->independent
2000 && !prop->disposable
2001 && !prop->redundant )
2002 return 0;
2003 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_sdtp( (isom_box_t *)stbl ) ) )
2004 return LSMASH_ERR_NAMELESS;
2005 uint32_t count = isom_get_sample_count_from_sample_table( stbl );
2006 /* fill past samples with ISOM_SAMPLE_*_UNKNOWN */
2007 lsmash_sample_property_t null_prop = { 0 };
2008 for( uint32_t i = 1; i < count; i++ )
2009 {
2010 int err = isom_add_sdtp_entry( (isom_box_t *)stbl, &null_prop, compatibility );
2011 if( err < 0 )
2012 return err;
2013 }
2014 return isom_add_sdtp_entry( (isom_box_t *)stbl, prop, compatibility );
2015 }
2016
lsmash_initialize_media_parameters(lsmash_media_parameters_t * param)2017 void lsmash_initialize_media_parameters( lsmash_media_parameters_t *param )
2018 {
2019 memset( param, 0, sizeof(lsmash_media_parameters_t) );
2020 param->timescale = 1;
2021 }
2022
lsmash_set_media_parameters(lsmash_root_t * root,uint32_t track_ID,lsmash_media_parameters_t * param)2023 int lsmash_set_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param )
2024 {
2025 if( isom_check_initializer_present( root ) < 0 )
2026 return LSMASH_ERR_FUNCTION_PARAM;
2027 lsmash_file_t *file = root->file;
2028 isom_trak_t *trak = isom_get_trak( file, track_ID );
2029 if( LSMASH_IS_NON_EXISTING_BOX( trak->mdia->mdhd )
2030 || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl ) )
2031 return LSMASH_ERR_NAMELESS;
2032 trak->mdia->mdhd->timescale = param->timescale;
2033 int err = isom_set_media_language( file, track_ID, param->ISO_language, param->MAC_language );
2034 if( err < 0 )
2035 return err;
2036 if( param->media_handler_name
2037 && (err = isom_set_media_handler_name( file, track_ID, param->media_handler_name )) < 0 )
2038 return err;
2039 if( file->qt_compatible && param->data_handler_name
2040 && (err = isom_set_data_handler_name( file, track_ID, param->data_handler_name )) < 0 )
2041 return err;
2042 if( (file->avc_extensions || file->qt_compatible) && param->roll_grouping
2043 && (err = isom_create_sample_grouping( trak, ISOM_GROUP_TYPE_ROLL )) < 0 )
2044 return err;
2045 if( (file->max_isom_version >= 6) && param->rap_grouping
2046 && (err = isom_create_sample_grouping( trak, ISOM_GROUP_TYPE_RAP )) < 0 )
2047 return err;
2048 if( !file->qt_compatible && param->compact_sample_size_table )
2049 trak->mdia->minf->stbl->compress_sample_size_table = isom_compress_sample_size_table;
2050 if( !param->no_sample_dependency_table )
2051 trak->mdia->minf->stbl->add_dependency_type = isom_add_dependency_type;
2052 return 0;
2053 }
2054
get_actual_handler_name_length(isom_hdlr_t * hdlr,lsmash_file_t * file)2055 static uint32_t get_actual_handler_name_length( isom_hdlr_t *hdlr, lsmash_file_t *file )
2056 {
2057 if( hdlr->componentName_length == 0 )
2058 return 0;
2059 uint32_t length;
2060 uint8_t *name;
2061 if( file->qt_compatible )
2062 {
2063 length = LSMASH_MIN( hdlr->componentName[0], hdlr->componentName_length - 1 );
2064 if( !file->isom_compatible )
2065 return length;
2066 name = &hdlr->componentName[1];
2067 }
2068 else
2069 {
2070 length = hdlr->componentName_length;
2071 name = hdlr->componentName;
2072 }
2073 /* Considering fool-proof such as not terminated by '\0'. */
2074 uint32_t i = 0;
2075 while( i < length && name[i] )
2076 ++i;
2077 return i;
2078 }
2079
lsmash_get_media_parameters(lsmash_root_t * root,uint32_t track_ID,lsmash_media_parameters_t * param)2080 int lsmash_get_media_parameters( lsmash_root_t *root, uint32_t track_ID, lsmash_media_parameters_t *param )
2081 {
2082 if( isom_check_initializer_present( root ) < 0 )
2083 return LSMASH_ERR_FUNCTION_PARAM;
2084 lsmash_file_t *file = root->file->initializer;
2085 isom_trak_t *trak = isom_get_trak( file, track_ID );
2086 if( LSMASH_IS_NON_EXISTING_BOX( trak->mdia->mdhd )
2087 || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->hdlr )
2088 || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl ) )
2089 return LSMASH_ERR_NAMELESS;
2090 isom_mdhd_t *mdhd = trak->mdia->mdhd;
2091 isom_stbl_t *stbl = trak->mdia->minf->stbl;
2092 param->timescale = mdhd->timescale;
2093 param->handler_type = trak->mdia->hdlr->componentSubtype;
2094 param->duration = mdhd->duration;
2095 /* Whether sample grouping present. */
2096 {
2097 isom_sbgp_t *sbgp;
2098 isom_sgpd_t *sgpd;
2099 sbgp = isom_get_sample_to_group ( stbl, ISOM_GROUP_TYPE_RAP );
2100 sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
2101 param->rap_grouping = LSMASH_IS_EXISTING_BOX( sbgp ) && LSMASH_IS_EXISTING_BOX( sgpd );
2102 sbgp = isom_get_roll_recovery_sample_to_group ( &stbl->sbgp_list );
2103 sgpd = isom_get_roll_recovery_sample_group_description( &stbl->sgpd_list );
2104 param->roll_grouping = LSMASH_IS_EXISTING_BOX( sbgp ) && LSMASH_IS_EXISTING_BOX( sgpd );
2105 }
2106 /* Get media language. */
2107 if( mdhd->language >= 0x800 )
2108 {
2109 param->MAC_language = 0;
2110 param->ISO_language = mdhd->language;
2111 }
2112 else
2113 {
2114 param->MAC_language = mdhd->language;
2115 param->ISO_language = 0;
2116 }
2117 /* Get handler name(s). */
2118 isom_hdlr_t *hdlr = trak->mdia->hdlr;
2119 uint32_t actual_length = get_actual_handler_name_length( hdlr, file );
2120 uint32_t length = LSMASH_MIN( 255, actual_length );
2121 if( length )
2122 {
2123 memcpy( param->media_handler_name_shadow, hdlr->componentName + file->qt_compatible, length );
2124 param->media_handler_name_shadow[length] = '\0';
2125 param->media_handler_name = param->media_handler_name_shadow;
2126 }
2127 else
2128 {
2129 param->media_handler_name = NULL;
2130 memset( param->media_handler_name_shadow, 0, sizeof(param->media_handler_name_shadow) );
2131 }
2132 if( LSMASH_IS_EXISTING_BOX( trak->mdia->minf->hdlr ) )
2133 {
2134 hdlr = trak->mdia->minf->hdlr;
2135 actual_length = get_actual_handler_name_length( hdlr, file );
2136 length = LSMASH_MIN( 255, actual_length );
2137 if( length )
2138 {
2139 memcpy( param->data_handler_name_shadow, hdlr->componentName + file->qt_compatible, length );
2140 param->data_handler_name_shadow[length] = '\0';
2141 param->data_handler_name = param->data_handler_name_shadow;
2142 }
2143 else
2144 {
2145 param->data_handler_name = NULL;
2146 memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) );
2147 }
2148 }
2149 else
2150 {
2151 param->data_handler_name = NULL;
2152 memset( param->data_handler_name_shadow, 0, sizeof(param->data_handler_name_shadow) );
2153 }
2154 param->compact_sample_size_table = LSMASH_IS_EXISTING_BOX( stbl->stz2 );
2155 param->no_sample_dependency_table = LSMASH_IS_NON_EXISTING_BOX( stbl->sdtp );
2156 param->reserved[0] = param->reserved[1] = 0;
2157 return 0;
2158 }
2159
2160 /*---- movie manipulators ----*/
2161
lsmash_initialize_movie_parameters(lsmash_movie_parameters_t * param)2162 void lsmash_initialize_movie_parameters( lsmash_movie_parameters_t *param )
2163 {
2164 memset( param, 0, sizeof(lsmash_movie_parameters_t) );
2165 param->timescale = 600;
2166 param->playback_rate = 0x00010000;
2167 param->playback_volume = 0x0100;
2168 }
2169
lsmash_set_movie_parameters(lsmash_root_t * root,lsmash_movie_parameters_t * param)2170 int lsmash_set_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param )
2171 {
2172 if( LSMASH_IS_NON_EXISTING_BOX( root ) )
2173 return LSMASH_ERR_FUNCTION_PARAM;
2174 lsmash_file_t *file = root->file;
2175 if( LSMASH_IS_NON_EXISTING_BOX( file->moov->mvhd ) )
2176 return LSMASH_ERR_NAMELESS;
2177 isom_mvhd_t *mvhd = file->moov->mvhd;
2178 mvhd->timescale = param->timescale;
2179 if( file->qt_compatible || file->itunes_movie )
2180 {
2181 mvhd->rate = param->playback_rate;
2182 mvhd->volume = param->playback_volume;
2183 mvhd->previewTime = param->preview_time;
2184 mvhd->previewDuration = param->preview_duration;
2185 mvhd->posterTime = param->poster_time;
2186 }
2187 else
2188 {
2189 mvhd->rate = 0x00010000;
2190 mvhd->volume = 0x0100;
2191 mvhd->previewTime = 0;
2192 mvhd->previewDuration = 0;
2193 mvhd->posterTime = 0;
2194 }
2195 return 0;
2196 }
2197
lsmash_get_movie_parameters(lsmash_root_t * root,lsmash_movie_parameters_t * param)2198 int lsmash_get_movie_parameters( lsmash_root_t *root, lsmash_movie_parameters_t *param )
2199 {
2200 if( isom_check_initializer_present( root ) < 0 )
2201 return LSMASH_ERR_FUNCTION_PARAM;
2202 lsmash_file_t *file = root->file->initializer;
2203 if( LSMASH_IS_NON_EXISTING_BOX( file->moov->mvhd ) )
2204 return LSMASH_ERR_NAMELESS;
2205 isom_mvhd_t *mvhd = file->moov->mvhd;
2206 param->timescale = mvhd->timescale;
2207 param->duration = mvhd->duration;
2208 param->playback_rate = mvhd->rate;
2209 param->playback_volume = mvhd->volume;
2210 param->preview_time = mvhd->previewTime;
2211 param->preview_duration = mvhd->previewDuration;
2212 param->poster_time = mvhd->posterTime;
2213 param->number_of_tracks = file->moov->trak_list.entry_count;
2214 return 0;
2215 }
2216
lsmash_get_movie_timescale(lsmash_root_t * root)2217 uint32_t lsmash_get_movie_timescale( lsmash_root_t *root )
2218 {
2219 if( isom_check_initializer_present( root ) < 0 )
2220 return 0;
2221 return root->file->initializer->moov->mvhd->timescale;
2222 }
2223
lsmash_reserve_media_data_size(lsmash_root_t * root,uint64_t media_data_size)2224 int lsmash_reserve_media_data_size
2225 (
2226 lsmash_root_t *root,
2227 uint64_t media_data_size
2228 )
2229 {
2230 if( isom_check_initializer_present( root ) < 0 )
2231 return LSMASH_ERR_FUNCTION_PARAM;
2232 lsmash_file_t *file = root->file->initializer;
2233 if( LSMASH_IS_EXISTING_BOX( file->mdat ) /* whether the Media Data Box is already written or not */
2234 || file->fragment ) /* For fragmented movies, this function makes no sense. */
2235 return LSMASH_ERR_NAMELESS;
2236 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_mdat( file ) ) )
2237 return LSMASH_ERR_NAMELESS;
2238 file->mdat->reserved_size = media_data_size;
2239 return 0;
2240 }
2241
isom_scan_trak_profileLevelIndication(isom_trak_t * trak,mp4a_audioProfileLevelIndication * audio_pli,mp4sys_visualProfileLevelIndication * visual_pli)2242 static int isom_scan_trak_profileLevelIndication
2243 (
2244 isom_trak_t *trak,
2245 mp4a_audioProfileLevelIndication *audio_pli,
2246 mp4sys_visualProfileLevelIndication *visual_pli
2247 )
2248 {
2249 isom_stsd_t *stsd = trak->mdia->minf->stbl->stsd;
2250 if( !stsd->list.head )
2251 return LSMASH_ERR_INVALID_DATA;
2252 for( lsmash_entry_t *entry = stsd->list.head; entry; entry = entry->next )
2253 {
2254 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)entry->data;
2255 if( LSMASH_IS_NON_EXISTING_BOX( sample_entry ) )
2256 return LSMASH_ERR_INVALID_DATA;
2257 lsmash_codec_type_t sample_type = sample_entry->type;
2258 if( LSMASH_IS_EXISTING_BOX( trak->mdia->minf->vmhd ) )
2259 {
2260 if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC1_VIDEO )
2261 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC2_VIDEO )
2262 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC3_VIDEO )
2263 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVC4_VIDEO )
2264 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_AVCP_VIDEO )
2265 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_SVC1_VIDEO )
2266 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MVC1_VIDEO )
2267 || lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MVC2_VIDEO ) )
2268 {
2269 /* FIXME: Do we have to arbitrate like audio? */
2270 if( *visual_pli == MP4SYS_VISUAL_PLI_NONE_REQUIRED )
2271 *visual_pli = MP4SYS_VISUAL_PLI_H264_AVC;
2272 }
2273 else
2274 *visual_pli = MP4SYS_VISUAL_PLI_NOT_SPECIFIED;
2275 }
2276 else if( LSMASH_IS_EXISTING_BOX( trak->mdia->minf->smhd ) )
2277 {
2278 if( lsmash_check_codec_type_identical( sample_type, ISOM_CODEC_TYPE_MP4A_AUDIO ) )
2279 {
2280 isom_audio_entry_t *audio = (isom_audio_entry_t *)sample_entry;
2281 isom_esds_t *esds = (isom_esds_t *)isom_get_extension_box_format( &audio->extensions, ISOM_BOX_TYPE_ESDS );
2282 if( LSMASH_IS_NON_EXISTING_BOX( esds ) || !esds->ES )
2283 return LSMASH_ERR_INVALID_DATA;
2284 lsmash_audio_summary_t *summary = (lsmash_audio_summary_t *)lsmash_create_summary( LSMASH_SUMMARY_TYPE_AUDIO );
2285 if( !summary )
2286 continue;
2287 if( mp4sys_setup_summary_from_DecoderSpecificInfo( summary, esds->ES ) < 0 )
2288 *audio_pli = MP4A_AUDIO_PLI_NOT_SPECIFIED;
2289 else
2290 *audio_pli = mp4a_max_audioProfileLevelIndication( *audio_pli, mp4a_get_audioProfileLevelIndication( summary ) );
2291 lsmash_cleanup_summary( (lsmash_summary_t *)summary );
2292 }
2293 else
2294 /* NOTE: Audio CODECs other than 'mp4a' does not have appropriate pli. */
2295 *audio_pli = MP4A_AUDIO_PLI_NOT_SPECIFIED;
2296 }
2297 else
2298 ; /* FIXME: Do we have to set OD_profileLevelIndication? */
2299 }
2300 return 0;
2301 }
2302
isom_setup_iods(isom_moov_t * moov)2303 int isom_setup_iods( isom_moov_t *moov )
2304 {
2305 if( LSMASH_IS_NON_EXISTING_BOX( moov->iods ) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_iods( moov ) ) )
2306 return LSMASH_ERR_NAMELESS;
2307 isom_iods_t *iods = moov->iods;
2308 int err = LSMASH_ERR_NAMELESS;
2309 iods->OD = mp4sys_create_ObjectDescriptor( 1 ); /* NOTE: Use 1 for ObjectDescriptorID of IOD. */
2310 if( !iods->OD )
2311 goto fail;
2312 mp4a_audioProfileLevelIndication audio_pli = MP4A_AUDIO_PLI_NONE_REQUIRED;
2313 mp4sys_visualProfileLevelIndication visual_pli = MP4SYS_VISUAL_PLI_NONE_REQUIRED;
2314 for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next )
2315 {
2316 isom_trak_t *trak = (isom_trak_t *)entry->data;
2317 if( LSMASH_IS_NON_EXISTING_BOX( trak )
2318 || LSMASH_IS_NON_EXISTING_BOX( trak->tkhd ) )
2319 goto fail;
2320 if( (err = isom_scan_trak_profileLevelIndication( trak, &audio_pli, &visual_pli )) < 0 )
2321 goto fail;
2322 if( (err = mp4sys_create_ES_ID_Inc( iods->OD, trak->tkhd->track_ID )) < 0 )
2323 goto fail;
2324 }
2325 if( (err = mp4sys_to_InitialObjectDescriptor( iods->OD,
2326 0, /* FIXME: I'm not quite sure what the spec says. */
2327 MP4SYS_OD_PLI_NONE_REQUIRED, MP4SYS_SCENE_PLI_NONE_REQUIRED,
2328 audio_pli, visual_pli,
2329 MP4SYS_GRAPHICS_PLI_NONE_REQUIRED )) < 0 )
2330 goto fail;
2331 return 0;
2332 fail:
2333 isom_remove_box_by_itself( iods );
2334 return err;
2335 }
2336
lsmash_create_object_descriptor(lsmash_root_t * root)2337 int lsmash_create_object_descriptor( lsmash_root_t *root )
2338 {
2339 if( isom_check_initializer_present( root ) < 0 )
2340 return LSMASH_ERR_FUNCTION_PARAM;
2341 lsmash_file_t *file = root->file;
2342 /* Return error if this file is not compatible with MP4 file format. */
2343 if( !file->mp4_version1
2344 && !file->mp4_version2 )
2345 return LSMASH_ERR_FUNCTION_PARAM;
2346 return isom_setup_iods( file->moov );
2347 }
2348
2349 /*---- finishing functions ----*/
2350
isom_complement_data_reference(isom_minf_t * minf)2351 int isom_complement_data_reference( isom_minf_t *minf )
2352 {
2353 if( LSMASH_IS_NON_EXISTING_BOX( minf->dinf->dref ) )
2354 return LSMASH_ERR_INVALID_DATA;
2355 /* Complement data referece if absent. */
2356 if( !minf->dinf->dref->list.head )
2357 {
2358 isom_dref_entry_t *url = isom_add_dref_entry( minf->dinf->dref, ISOM_BOX_TYPE_URL );
2359 if( LSMASH_IS_NON_EXISTING_BOX( url ) )
2360 return LSMASH_ERR_NAMELESS;
2361 url->flags = 0x000001; /* Media data is in the same file. */
2362 }
2363 return 0;
2364 }
2365
isom_get_written_media_file(isom_trak_t * trak,uint32_t sample_description_index)2366 static lsmash_file_t *isom_get_written_media_file
2367 (
2368 isom_trak_t *trak,
2369 uint32_t sample_description_index
2370 )
2371 {
2372 isom_minf_t *minf = trak->mdia->minf;
2373 isom_sample_entry_t *description = (isom_sample_entry_t *)lsmash_list_get_entry_data( &minf->stbl->stsd->list, sample_description_index );
2374 isom_dref_entry_t *dref_entry = (isom_dref_entry_t *)lsmash_list_get_entry_data( &minf->dinf->dref->list, description ? description->data_reference_index : 1 );
2375 lsmash_file_t *file = (!dref_entry || LSMASH_IS_NON_EXISTING_BOX( dref_entry->ref_file )) ? trak->file : dref_entry->ref_file;
2376 if( !(file->flags & LSMASH_FILE_MODE_MEDIA)
2377 || !(file->flags & LSMASH_FILE_MODE_WRITE) )
2378 return trak->file;
2379 return file;
2380 }
2381
isom_check_large_offset_requirement(isom_moov_t * moov,uint64_t meta_size)2382 int isom_check_large_offset_requirement
2383 (
2384 isom_moov_t *moov,
2385 uint64_t meta_size
2386 )
2387 {
2388 for( lsmash_entry_t *entry = moov->trak_list.head; entry; )
2389 {
2390 isom_trak_t *trak = (isom_trak_t *)entry->data;
2391 isom_stco_t *stco = trak->mdia->minf->stbl->stco;
2392 if( !stco->list->tail /* no samples */
2393 || stco->large_presentation
2394 || (((isom_stco_entry_t *)stco->list->tail->data)->chunk_offset + moov->size + meta_size) <= UINT32_MAX )
2395 {
2396 entry = entry->next;
2397 continue; /* no need to convert stco into co64 */
2398 }
2399 /* stco->co64 conversion */
2400 int err = isom_convert_stco_to_co64( trak->mdia->minf->stbl );
2401 if( err < 0 )
2402 return err;
2403 if( isom_update_box_size( moov ) == 0 )
2404 return LSMASH_ERR_INVALID_DATA;
2405 entry = moov->trak_list.head; /* whenever any conversion, re-check all traks */
2406 }
2407 return 0;
2408 }
2409
isom_add_preceding_box_size(isom_moov_t * moov,uint64_t preceding_size)2410 void isom_add_preceding_box_size
2411 (
2412 isom_moov_t *moov,
2413 uint64_t preceding_size
2414 )
2415 {
2416 for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next )
2417 {
2418 /* Apply to the chunks in the same file. */
2419 isom_trak_t *trak = (isom_trak_t *)entry->data;
2420 isom_stsc_t *stsc = trak->mdia->minf->stbl->stsc;
2421 isom_stco_t *stco = trak->mdia->minf->stbl->stco;
2422 lsmash_entry_t *stsc_entry = stsc->list->head;
2423 isom_stsc_entry_t *stsc_data = stsc_entry ? (isom_stsc_entry_t *)stsc_entry->data : NULL;
2424 uint32_t chunk_number = 1;
2425 for( lsmash_entry_t *stco_entry = stco->list->head; stco_entry; )
2426 {
2427 if( stsc_data
2428 && stsc_data->first_chunk == chunk_number )
2429 {
2430 lsmash_file_t *ref_file = isom_get_written_media_file( trak, stsc_data->sample_description_index );
2431 stsc_entry = stsc_entry->next;
2432 stsc_data = stsc_entry ? (isom_stsc_entry_t *)stsc_entry->data : NULL;
2433 if( ref_file != trak->file )
2434 {
2435 /* The chunks are not contained in the same file. Skip applying the offset.
2436 * If no more stsc entries, the rest of the chunks is not contained in the same file. */
2437 if( !stsc_entry || !stsc_data )
2438 break;
2439 while( stco_entry && chunk_number < stsc_data->first_chunk )
2440 {
2441 stco_entry = stco_entry->next;
2442 ++chunk_number;
2443 }
2444 continue;
2445 }
2446 }
2447 if( stco->large_presentation )
2448 ((isom_co64_entry_t *)stco_entry->data)->chunk_offset += preceding_size;
2449 else
2450 ((isom_stco_entry_t *)stco_entry->data)->chunk_offset += preceding_size;
2451 stco_entry = stco_entry->next;
2452 ++chunk_number;
2453 }
2454 }
2455 }
2456
isom_establish_movie(lsmash_file_t * file)2457 int isom_establish_movie( lsmash_file_t *file )
2458 {
2459 assert( file == file->initializer );
2460 int err;
2461 if( (err = isom_check_mandatory_boxes( file )) < 0
2462 || (err = isom_set_movie_creation_time( file )) < 0 )
2463 return err;
2464 if( isom_update_box_size( file->moov ) == 0 )
2465 return LSMASH_ERR_INVALID_DATA;
2466 return 0;
2467 }
2468
lsmash_finish_movie(lsmash_root_t * root,lsmash_adhoc_remux_t * remux)2469 int lsmash_finish_movie
2470 (
2471 lsmash_root_t *root,
2472 lsmash_adhoc_remux_t *remux
2473 )
2474 {
2475 if( isom_check_initializer_present( root ) < 0 )
2476 return LSMASH_ERR_FUNCTION_PARAM;
2477 lsmash_file_t *file = root->file;
2478 if( !file->bs
2479 || LSMASH_IS_NON_EXISTING_BOX( file->initializer->moov ) )
2480 return LSMASH_ERR_INVALID_DATA;
2481 if( file->fragment )
2482 return isom_finish_final_fragment_movie( file, remux );
2483 if( file != file->initializer )
2484 return LSMASH_ERR_INVALID_DATA;
2485 int err;
2486 isom_moov_t *moov = file->moov;
2487 for( lsmash_entry_t *entry = moov->trak_list.head; entry; entry = entry->next )
2488 {
2489 isom_trak_t *trak = (isom_trak_t *)entry->data;
2490 if( LSMASH_IS_NON_EXISTING_BOX( trak )
2491 || LSMASH_IS_NON_EXISTING_BOX( trak->tkhd )
2492 || !trak->cache
2493 || !trak->mdia->minf->stbl->stsd->list.head
2494 || !trak->mdia->minf->stbl->stsd->list.head->data
2495 || !trak->mdia->minf->stbl->stco->list
2496 || !trak->mdia->minf->stbl->stco->list->tail )
2497 return LSMASH_ERR_INVALID_DATA;
2498 if( (err = isom_complement_data_reference( trak->mdia->minf )) < 0 )
2499 return err;
2500 uint32_t track_ID = trak->tkhd->track_ID;
2501 uint32_t related_track_ID = trak->related_track_ID;
2502 /* Disable the track if the track is a track reference chapter. */
2503 if( trak->is_chapter )
2504 trak->tkhd->flags &= ~ISOM_TRACK_ENABLED;
2505 if( trak->is_chapter && related_track_ID )
2506 {
2507 /* In order that the track duration of the chapter track doesn't exceed that of the related track. */
2508 lsmash_edit_t edit;
2509 edit.duration = LSMASH_MIN( trak->tkhd->duration, lsmash_get_track_duration( root, related_track_ID ) );
2510 edit.start_time = 0;
2511 edit.rate = ISOM_EDIT_MODE_NORMAL;
2512 if( (err = lsmash_create_explicit_timeline_map( root, track_ID, edit )) < 0 )
2513 return err;
2514 }
2515 isom_stbl_t *stbl = trak->mdia->minf->stbl;
2516 /* Compress sample size table. */
2517 if( stbl->compress_sample_size_table
2518 && (err = stbl->compress_sample_size_table( stbl )) < 0 )
2519 return err;
2520 /* Add stss box if any samples aren't sync sample. */
2521 if( !trak->cache->all_sync && !stbl->stss && !isom_add_stss( stbl ) )
2522 return LSMASH_ERR_NAMELESS;
2523 if( (err = isom_update_tkhd_duration( trak )) < 0
2524 || (err = isom_update_bitrate_description( trak->mdia )) < 0 )
2525 return err;
2526 }
2527 if( file->mp4_version1 == 1 && (err = isom_setup_iods( moov )) < 0 )
2528 return err;
2529 if( (err = isom_establish_movie( file )) < 0 )
2530 return err;
2531 /* Write the size of Media Data Box here. */
2532 lsmash_bs_t *bs = file->bs;
2533 file->mdat->manager &= ~LSMASH_INCOMPLETE_BOX;
2534 if( (err = isom_write_box( bs, (isom_box_t *)file->mdat )) < 0 )
2535 return err;
2536 /* Write the Movie Box and a Meta Box if no optimization for progressive download. */
2537 uint64_t meta_size = LSMASH_IS_EXISTING_BOX( file->meta ) ? file->meta->size : 0;
2538 if( !remux )
2539 {
2540 if( (err = isom_write_box( bs, (isom_box_t *)file->moov )) < 0
2541 || (err = isom_write_box( bs, (isom_box_t *)file->meta )) < 0 )
2542 return err;
2543 file->size += moov->size + meta_size;
2544 return 0;
2545 }
2546 /* stco->co64 conversion, depending on last chunk's offset */
2547 if( (err = isom_check_large_offset_requirement( moov, meta_size )) < 0 )
2548 return err;
2549 /* now the amount of offset is fixed. */
2550 uint64_t mtf_size = moov->size + meta_size; /* sum of size of boxes moved to front */
2551 /* buffer size must be at least mtf_size * 2 */
2552 remux->buffer_size = LSMASH_MAX( remux->buffer_size, mtf_size * 2 );
2553 /* Split to 2 buffers. */
2554 uint8_t *buf[2] = { NULL, NULL };
2555 if( (buf[0] = (uint8_t*)lsmash_malloc( remux->buffer_size )) == NULL )
2556 return LSMASH_ERR_MEMORY_ALLOC; /* NOTE: I think we still can fallback to "return isom_write_moov();" here. */
2557 size_t size = remux->buffer_size / 2;
2558 buf[1] = buf[0] + size;
2559 /* Now, the amount of the offset is fixed. apply it to stco/co64 */
2560 isom_add_preceding_box_size( moov, mtf_size );
2561 /* Backup starting area of mdat and write moov + meta there instead. */
2562 isom_mdat_t *mdat = file->mdat;
2563 uint64_t total = file->size + mtf_size;
2564 uint64_t placeholder_pos = mdat->pos;
2565 if( (err = lsmash_bs_write_seek( bs, placeholder_pos, SEEK_SET )) < 0 )
2566 goto fail;
2567 size_t read_num = size;
2568 lsmash_bs_read_data( bs, buf[0], &read_num );
2569 uint64_t read_pos = bs->offset;
2570 /* Write moov + meta there instead. */
2571 if( (err = lsmash_bs_write_seek( bs, placeholder_pos, SEEK_SET )) < 0
2572 || (err = isom_write_box( bs, (isom_box_t *)file->moov )) < 0
2573 || (err = isom_write_box( bs, (isom_box_t *)file->meta )) < 0 )
2574 goto fail;
2575 uint64_t write_pos = bs->offset;
2576 /* Update the positions */
2577 mdat->pos += mtf_size;
2578 /* Move Media Data Box. */
2579 if( (err = isom_rearrange_data( file, remux, buf, read_num, size, read_pos, write_pos, total )) < 0 )
2580 goto fail;
2581 file->size += mtf_size;
2582 lsmash_free( buf[0] );
2583 return 0;
2584 fail:
2585 lsmash_free( buf[0] );
2586 return err;
2587 }
2588
lsmash_set_last_sample_delta(lsmash_root_t * root,uint32_t track_ID,uint32_t sample_delta)2589 int lsmash_set_last_sample_delta( lsmash_root_t *root, uint32_t track_ID, uint32_t sample_delta )
2590 {
2591 if( isom_check_initializer_present( root ) < 0 || track_ID == 0 )
2592 return LSMASH_ERR_FUNCTION_PARAM;
2593 lsmash_file_t *file = root->file;
2594 if( file->fragment
2595 && file->fragment->movie )
2596 {
2597 isom_traf_t *traf = isom_get_traf( file->fragment->movie, track_ID );
2598 if( LSMASH_IS_NON_EXISTING_BOX( traf )
2599 || LSMASH_IS_NON_EXISTING_BOX( traf->tfhd )
2600 || !traf->cache )
2601 return LSMASH_ERR_NAMELESS;
2602 return isom_set_fragment_last_duration( traf, sample_delta );
2603 }
2604 if( file != file->initializer )
2605 return LSMASH_ERR_INVALID_DATA;
2606 isom_trak_t *trak = isom_get_trak( file, track_ID );
2607 if( LSMASH_IS_NON_EXISTING_BOX( trak->mdia->mdhd )
2608 || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl->stsd )
2609 || (LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl->stsz )
2610 && LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl->stz2 ))
2611 || !trak->cache
2612 || !trak->mdia->minf->stbl->stts->list )
2613 return LSMASH_ERR_NAMELESS;
2614 isom_stbl_t *stbl = trak->mdia->minf->stbl;
2615 isom_stts_t *stts = stbl->stts;
2616 uint32_t sample_count = isom_get_sample_count( trak );
2617 int err;
2618 if( !stts->list->tail )
2619 {
2620 if( sample_count == 0 )
2621 return 0; /* no samples */
2622 if( sample_count > 1 )
2623 return LSMASH_ERR_INVALID_DATA; /* irregular sample_count */
2624 /* Set the duration of the first sample.
2625 * This duration is also the duration of the last sample. */
2626 if( (err = isom_add_stts_entry( stbl, sample_delta )) < 0 )
2627 return err;
2628 return lsmash_update_track_duration( root, track_ID, 0 );
2629 }
2630 uint32_t i = 0;
2631 for( lsmash_entry_t *entry = stts->list->head; entry; entry = entry->next )
2632 i += ((isom_stts_entry_t *)entry->data)->sample_count;
2633 if( sample_count < i )
2634 return LSMASH_ERR_INVALID_DATA;
2635 int no_last = (sample_count > i);
2636 isom_stts_entry_t *last_stts_data = (isom_stts_entry_t *)stts->list->tail->data;
2637 if( !last_stts_data )
2638 return LSMASH_ERR_INVALID_DATA;
2639 /* Consider QuikcTime fixed compression audio. */
2640 isom_audio_entry_t *audio = (isom_audio_entry_t *)lsmash_list_get_entry_data( &trak->mdia->minf->stbl->stsd->list,
2641 trak->cache->chunk.sample_description_index );
2642 if( LSMASH_IS_NON_EXISTING_BOX( audio ) )
2643 return LSMASH_ERR_INVALID_DATA;
2644 if( (audio->manager & LSMASH_AUDIO_DESCRIPTION)
2645 && (audio->manager & LSMASH_QTFF_BASE)
2646 && (audio->version == 1)
2647 && (audio->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION) )
2648 {
2649 if( audio->samplesPerPacket == 0 )
2650 return LSMASH_ERR_INVALID_DATA;
2651 int exclude_last_sample = no_last ? 0 : 1;
2652 uint32_t j = audio->samplesPerPacket;
2653 for( lsmash_entry_t *entry = stts->list->tail; entry && j > 1; entry = entry->prev )
2654 {
2655 isom_stts_entry_t *stts_data = (isom_stts_entry_t *)entry->data;
2656 if( !stts_data )
2657 return LSMASH_ERR_INVALID_DATA;
2658 for( uint32_t k = exclude_last_sample; k < stts_data->sample_count && j > 1; k++ )
2659 {
2660 sample_delta -= stts_data->sample_delta;
2661 --j;
2662 }
2663 exclude_last_sample = 0;
2664 }
2665 }
2666 /* Set sample_delta. */
2667 if( no_last )
2668 {
2669 /* The duration of the last sample is not set yet. */
2670 if( sample_count - i > 1 )
2671 return LSMASH_ERR_INVALID_DATA;
2672 /* Add a sample_delta. */
2673 if( sample_delta == last_stts_data->sample_delta )
2674 ++ last_stts_data->sample_count;
2675 else if( (err = isom_add_stts_entry( stbl, sample_delta )) < 0 )
2676 return err;
2677 }
2678 /* The duration of the last sample is already set. Replace it with a new one. */
2679 else if( (err = isom_replace_last_sample_delta( stbl, sample_delta )) < 0 )
2680 return err;
2681 return lsmash_update_track_duration( root, track_ID, sample_delta );
2682 }
2683
2684 /*---- timeline manipulator ----*/
2685
lsmash_modify_explicit_timeline_map(lsmash_root_t * root,uint32_t track_ID,uint32_t edit_number,lsmash_edit_t edit)2686 int lsmash_modify_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t edit_number, lsmash_edit_t edit )
2687 {
2688 if( isom_check_initializer_present( root ) < 0
2689 || edit.start_time < -1 )
2690 return LSMASH_ERR_FUNCTION_PARAM;
2691 lsmash_file_t *file = root->file->initializer;
2692 isom_trak_t *trak = isom_get_trak( file, track_ID );
2693 if( !trak->edts->elst->list )
2694 return LSMASH_ERR_NAMELESS;
2695 isom_elst_t *elst = trak->edts->elst;
2696 isom_elst_entry_t *data = (isom_elst_entry_t *)lsmash_list_get_entry_data( elst->list, edit_number );
2697 if( !data )
2698 return LSMASH_ERR_NAMELESS;
2699 data->segment_duration = edit.duration;
2700 data->media_time = edit.start_time;
2701 data->media_rate = edit.rate;
2702 if( elst->pos == 0 || !file->fragment || file->bs->unseekable )
2703 return isom_update_tkhd_duration( trak );
2704 /* Rewrite the specified entry.
2705 * Note: we don't update the version of the Edit List Box. */
2706 lsmash_bs_t *bs = file->bs;
2707 uint64_t current_pos = bs->offset;
2708 uint64_t entry_pos = elst->pos + ISOM_LIST_FULLBOX_COMMON_SIZE + ((uint64_t)edit_number - 1) * (elst->version == 1 ? 20 : 12);
2709 lsmash_bs_write_seek( bs, entry_pos, SEEK_SET );
2710 if( elst->version )
2711 {
2712 lsmash_bs_put_be64( bs, data->segment_duration );
2713 lsmash_bs_put_be64( bs, data->media_time );
2714 }
2715 else
2716 {
2717 lsmash_bs_put_be32( bs, (uint32_t)LSMASH_MIN( data->segment_duration, UINT32_MAX ) );
2718 lsmash_bs_put_be32( bs, (uint32_t)data->media_time );
2719 }
2720 lsmash_bs_put_be32( bs, data->media_rate );
2721 int ret = lsmash_bs_flush_buffer( bs );
2722 lsmash_bs_write_seek( bs, current_pos, SEEK_SET );
2723 return ret;
2724 }
2725
lsmash_create_explicit_timeline_map(lsmash_root_t * root,uint32_t track_ID,lsmash_edit_t edit)2726 int lsmash_create_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, lsmash_edit_t edit )
2727 {
2728 if( isom_check_initializer_present( root ) < 0 || edit.start_time < -1 )
2729 return LSMASH_ERR_FUNCTION_PARAM;
2730 isom_trak_t *trak = isom_get_trak( root->file, track_ID );
2731 if( LSMASH_IS_NON_EXISTING_BOX( trak->tkhd ) )
2732 return LSMASH_ERR_NAMELESS;
2733 edit.duration = (edit.duration || root->file->fragment) ? edit.duration
2734 : trak->tkhd->duration ? trak->tkhd->duration
2735 : isom_update_tkhd_duration( trak ) < 0 ? 0
2736 : trak->tkhd->duration;
2737 if( (LSMASH_IS_NON_EXISTING_BOX( trak->edts ) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_edts( trak ) ))
2738 || (LSMASH_IS_NON_EXISTING_BOX( trak->edts->elst ) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_elst( trak->edts ) )) )
2739 return LSMASH_ERR_NAMELESS;
2740 int err = isom_add_elst_entry( trak->edts->elst, edit.duration, edit.start_time, edit.rate );
2741 if( err < 0 )
2742 return err;
2743 return isom_update_tkhd_duration( trak );
2744 }
2745
lsmash_get_explicit_timeline_map(lsmash_root_t * root,uint32_t track_ID,uint32_t edit_number,lsmash_edit_t * edit)2746 int lsmash_get_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID, uint32_t edit_number, lsmash_edit_t *edit )
2747 {
2748 if( isom_check_initializer_present( root ) < 0 || !edit )
2749 return LSMASH_ERR_FUNCTION_PARAM;
2750 isom_elst_entry_t *data;
2751 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
2752 if( LSMASH_IS_NON_EXISTING_BOX( trak ) )
2753 data = isom_timelime_get_explicit_timeline_map( root, track_ID, edit_number );
2754 else
2755 {
2756 if( LSMASH_IS_NON_EXISTING_BOX( trak->edts->elst ) )
2757 {
2758 /* no edits */
2759 edit->duration = 0;
2760 edit->start_time = 0;
2761 edit->rate = 0;
2762 return 0;
2763 }
2764 data = (isom_elst_entry_t *)lsmash_list_get_entry_data( trak->edts->elst->list, edit_number );
2765 }
2766 if( !data )
2767 return LSMASH_ERR_NAMELESS;
2768 edit->duration = data->segment_duration;
2769 edit->start_time = data->media_time;
2770 edit->rate = data->media_rate;
2771 return 0;
2772 }
2773
lsmash_count_explicit_timeline_map(lsmash_root_t * root,uint32_t track_ID)2774 uint32_t lsmash_count_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID )
2775 {
2776 if( isom_check_initializer_present( root ) < 0 )
2777 return LSMASH_ERR_FUNCTION_PARAM;
2778 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
2779 if( LSMASH_IS_NON_EXISTING_BOX( trak ) )
2780 return isom_timelime_count_explicit_timeline_map( root, track_ID );
2781 else
2782 return trak->edts->elst->list ? trak->edts->elst->list->entry_count : 0;
2783 }
2784
2785 /*---- create / modification time fields manipulators ----*/
2786
lsmash_update_media_modification_time(lsmash_root_t * root,uint32_t track_ID)2787 int lsmash_update_media_modification_time( lsmash_root_t *root, uint32_t track_ID )
2788 {
2789 if( isom_check_initializer_present( root ) < 0 )
2790 return LSMASH_ERR_FUNCTION_PARAM;
2791 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
2792 if( LSMASH_IS_NON_EXISTING_BOX( trak->mdia->mdhd ) )
2793 return LSMASH_ERR_NAMELESS;
2794 isom_mdhd_t *mdhd = trak->mdia->mdhd;
2795 mdhd->modification_time = isom_get_current_mp4time();
2796 /* overwrite strange creation_time */
2797 if( mdhd->creation_time > mdhd->modification_time )
2798 mdhd->creation_time = mdhd->modification_time;
2799 return 0;
2800 }
2801
lsmash_update_track_modification_time(lsmash_root_t * root,uint32_t track_ID)2802 int lsmash_update_track_modification_time( lsmash_root_t *root, uint32_t track_ID )
2803 {
2804 if( isom_check_initializer_present( root ) < 0 )
2805 return LSMASH_ERR_FUNCTION_PARAM;
2806 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
2807 if( LSMASH_IS_NON_EXISTING_BOX( trak->tkhd ) )
2808 return LSMASH_ERR_NAMELESS;
2809 isom_tkhd_t *tkhd = trak->tkhd;
2810 tkhd->modification_time = isom_get_current_mp4time();
2811 /* overwrite strange creation_time */
2812 if( tkhd->creation_time > tkhd->modification_time )
2813 tkhd->creation_time = tkhd->modification_time;
2814 return 0;
2815 }
2816
lsmash_update_movie_modification_time(lsmash_root_t * root)2817 int lsmash_update_movie_modification_time( lsmash_root_t *root )
2818 {
2819 if( isom_check_initializer_present( root ) < 0 )
2820 return LSMASH_ERR_FUNCTION_PARAM;
2821 lsmash_file_t *file = root->file->initializer;
2822 if( LSMASH_IS_NON_EXISTING_BOX( file->moov->mvhd ) )
2823 return LSMASH_ERR_INVALID_DATA;
2824 isom_mvhd_t *mvhd = file->moov->mvhd;
2825 mvhd->modification_time = isom_get_current_mp4time();
2826 /* overwrite strange creation_time */
2827 if( mvhd->creation_time > mvhd->modification_time )
2828 mvhd->creation_time = mvhd->modification_time;
2829 return 0;
2830 }
2831
2832 /*---- sample manipulators ----*/
lsmash_create_sample(uint32_t size)2833 lsmash_sample_t *lsmash_create_sample( uint32_t size )
2834 {
2835 lsmash_sample_t *sample = lsmash_malloc_zero( sizeof(lsmash_sample_t) );
2836 if( !sample )
2837 return NULL;
2838 if( size == 0 )
2839 return sample;
2840 sample->data = lsmash_malloc( size );
2841 if( !sample->data )
2842 {
2843 lsmash_free( sample );
2844 return NULL;
2845 }
2846 sample->length = size;
2847 return sample;
2848 }
2849
lsmash_sample_alloc(lsmash_sample_t * sample,uint32_t size)2850 int lsmash_sample_alloc( lsmash_sample_t *sample, uint32_t size )
2851 {
2852 if( !sample )
2853 return LSMASH_ERR_FUNCTION_PARAM;
2854 if( size == 0 )
2855 {
2856 lsmash_free( sample->data );
2857 sample->data = NULL;
2858 sample->length = 0;
2859 return 0;
2860 }
2861 if( size == sample->length )
2862 return 0;
2863 uint8_t *data;
2864 if( !sample->data )
2865 data = lsmash_malloc( size );
2866 else
2867 data = lsmash_realloc( sample->data, size );
2868 if( !data )
2869 return LSMASH_ERR_MEMORY_ALLOC;
2870 sample->data = data;
2871 sample->length = size;
2872 return 0;
2873 }
2874
lsmash_delete_sample(lsmash_sample_t * sample)2875 void lsmash_delete_sample( lsmash_sample_t *sample )
2876 {
2877 if( !sample )
2878 return;
2879 lsmash_free( sample->data );
2880 lsmash_free( sample );
2881 }
2882
isom_create_sample_pool(uint64_t size)2883 isom_sample_pool_t *isom_create_sample_pool( uint64_t size )
2884 {
2885 isom_sample_pool_t *pool = lsmash_malloc_zero( sizeof(isom_sample_pool_t) );
2886 if( !pool )
2887 return NULL;
2888 if( size == 0 )
2889 return pool;
2890 pool->data = lsmash_malloc( size );
2891 if( !pool->data )
2892 {
2893 lsmash_free( pool );
2894 return NULL;
2895 }
2896 pool->alloc = size;
2897 return pool;
2898 }
2899
isom_remove_sample_pool(isom_sample_pool_t * pool)2900 void isom_remove_sample_pool( isom_sample_pool_t *pool )
2901 {
2902 if( !pool )
2903 return;
2904 lsmash_free( pool->data );
2905 lsmash_free( pool );
2906 }
2907
isom_add_size(isom_stbl_t * stbl,uint32_t sample_size)2908 static uint32_t isom_add_size( isom_stbl_t *stbl, uint32_t sample_size )
2909 {
2910 if( isom_add_stsz_entry( stbl, sample_size ) < 0 )
2911 return 0;
2912 return isom_get_sample_count_from_sample_table( stbl );
2913 }
2914
isom_add_dts(isom_stbl_t * stbl,uint64_t dts,uint64_t prev_dts)2915 static uint32_t isom_add_dts( isom_stbl_t *stbl, uint64_t dts, uint64_t prev_dts )
2916 {
2917 isom_stts_t *stts = stbl->stts;
2918 if( stts->list->entry_count == 0 )
2919 return isom_add_stts_entry( stbl, dts ) < 0 ? 0 : dts;
2920 if( dts <= prev_dts )
2921 return 0;
2922 uint32_t sample_delta = dts - prev_dts;
2923 isom_stts_entry_t *data = (isom_stts_entry_t *)stts->list->tail->data;
2924 if( data->sample_delta == sample_delta )
2925 ++ data->sample_count;
2926 else if( isom_add_stts_entry( stbl, sample_delta ) < 0 )
2927 return 0;
2928 return sample_delta;
2929 }
2930
2931 /* Add ctts box and the first ctts entry. */
isom_add_initial_sample_offset(isom_stbl_t * stbl,uint32_t sample_offset)2932 static int isom_add_initial_sample_offset( isom_stbl_t *stbl, uint32_t sample_offset )
2933 {
2934 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_ctts( stbl ) ) )
2935 return LSMASH_ERR_NAMELESS;
2936 if( sample_offset == ISOM_NON_OUTPUT_SAMPLE_OFFSET )
2937 stbl->ctts->version = 1;
2938 uint32_t sample_count = isom_get_sample_count_from_sample_table( stbl );
2939 if( sample_count > 1 )
2940 {
2941 /* Set all prior samples' sample_offset to 0. */
2942 int err = isom_add_ctts_entry( stbl, sample_count - 1, 0 );
2943 if( err < 0 )
2944 return err;
2945 }
2946 return isom_add_ctts_entry( stbl, 1, sample_offset );
2947 }
2948
isom_add_sample_offset(isom_stbl_t * stbl,uint32_t sample_offset)2949 static int isom_add_sample_offset( isom_stbl_t *stbl, uint32_t sample_offset )
2950 {
2951 if( !stbl->ctts->list )
2952 return LSMASH_ERR_INVALID_DATA;
2953 isom_ctts_entry_t *data = (isom_ctts_entry_t *)stbl->ctts->list->tail->data;
2954 if( data->sample_offset == sample_offset )
2955 ++ data->sample_count;
2956 else
2957 {
2958 int err = isom_add_ctts_entry( stbl, 1, sample_offset );
2959 if( err < 0 )
2960 return err;
2961 }
2962 return 0;
2963 }
2964
isom_add_cts(isom_stbl_t * stbl,uint64_t dts,uint64_t cts,int non_output_sample)2965 static int isom_add_cts( isom_stbl_t *stbl, uint64_t dts, uint64_t cts, int non_output_sample )
2966 {
2967 uint32_t sample_offset = !non_output_sample ? cts - dts : ISOM_NON_OUTPUT_SAMPLE_OFFSET;
2968 if( LSMASH_IS_EXISTING_BOX( stbl->ctts ) )
2969 return isom_add_sample_offset( stbl, sample_offset );
2970 return sample_offset != 0 ? isom_add_initial_sample_offset( stbl, sample_offset ) : 0;
2971 }
2972
isom_check_sample_offset_compatibility(lsmash_file_t * file,uint64_t dts,uint64_t cts,int non_output_sample)2973 static int isom_check_sample_offset_compatibility( lsmash_file_t *file, uint64_t dts, uint64_t cts, int non_output_sample )
2974 {
2975 if( non_output_sample )
2976 {
2977 if( file->min_isom_version < 4 )
2978 return LSMASH_ERR_INVALID_DATA; /* Non-output sample can be indicated under 'iso4' or later brands. */
2979 }
2980 else
2981 {
2982 if( file->isom_compatible && file->qt_compatible && (((cts >= dts) ? (cts - dts) : (dts - cts)) > INT32_MAX) )
2983 return LSMASH_ERR_INVALID_DATA; /* sample_offset is not compatible with both ISOBMFF and QTFF. */
2984 }
2985 if( non_output_sample || cts < dts )
2986 {
2987 /* Negative sample offset is required. */
2988 if( file->max_isom_version < 4 && !file->qt_compatible )
2989 return LSMASH_ERR_INVALID_DATA; /* Negative sample offset is not supported in both ISOBMFF and QTFF. */
2990 if( file->max_isom_version >= 4 && file->qt_compatible )
2991 return LSMASH_ERR_INVALID_DATA; /* ctts version 1 is not defined in QTFF. */
2992 }
2993 return 0;
2994 }
2995
isom_update_cache_timestamp(isom_cache_t * cache,uint64_t dts,uint64_t cts,int32_t ctd_shift,uint32_t sample_duration,int non_output_sample)2996 void isom_update_cache_timestamp
2997 (
2998 isom_cache_t *cache,
2999 uint64_t dts,
3000 uint64_t cts,
3001 int32_t ctd_shift,
3002 uint32_t sample_duration,
3003 int non_output_sample
3004 )
3005 {
3006 cache->timestamp.dts = dts;
3007 cache->timestamp.cts = non_output_sample ? cache->timestamp.cts : cts;
3008 cache->timestamp.ctd_shift = ctd_shift;
3009 if( cache->fragment )
3010 {
3011 cache->fragment->last_duration = sample_duration;
3012 if( !non_output_sample )
3013 cache->fragment->largest_cts
3014 = cache->fragment->largest_cts != LSMASH_TIMESTAMP_UNDEFINED
3015 ? LSMASH_MAX( cache->timestamp.cts, cache->fragment->largest_cts )
3016 : cache->timestamp.cts;
3017 }
3018 }
3019
isom_add_timestamp(isom_stbl_t * stbl,isom_cache_t * cache,lsmash_file_t * file,uint64_t dts,uint64_t cts)3020 static int isom_add_timestamp( isom_stbl_t *stbl, isom_cache_t *cache, lsmash_file_t *file, uint64_t dts, uint64_t cts )
3021 {
3022 if( !cache || !stbl->stts->list )
3023 return LSMASH_ERR_INVALID_DATA;
3024 int non_output_sample = (cts == LSMASH_TIMESTAMP_UNDEFINED);
3025 int err = isom_check_sample_offset_compatibility( file, dts, cts, non_output_sample );
3026 if( err < 0 )
3027 return err;
3028 uint32_t sample_count = isom_get_sample_count_from_sample_table( stbl );
3029 uint32_t sample_delta = sample_count > 1 ? isom_add_dts( stbl, dts, cache->timestamp.dts ) : 0;
3030 if( sample_count > 1 && sample_delta == 0 )
3031 return LSMASH_ERR_INVALID_DATA;
3032 if( (err = isom_add_cts( stbl, dts, cts, non_output_sample )) < 0 )
3033 return err;
3034 int32_t ctd_shift = cache->timestamp.ctd_shift;
3035 if( !non_output_sample && ((cts + ctd_shift) < dts) )
3036 {
3037 /* Check overflow of composition to decode timeline shift. */
3038 if( (dts - cts) > INT32_MAX )
3039 return LSMASH_ERR_INVALID_DATA;
3040 assert( LSMASH_IS_EXISTING_BOX( stbl->ctts ) );
3041 if( stbl->ctts->version == 0 && !file->qt_compatible )
3042 stbl->ctts->version = 1;
3043 ctd_shift = dts - cts;
3044 }
3045 isom_update_cache_timestamp( cache, dts, cts, ctd_shift, sample_delta, non_output_sample );
3046 return 0;
3047 }
3048
isom_add_sync_point(isom_stbl_t * stbl,isom_cache_t * cache,uint32_t sample_number,lsmash_sample_property_t * prop)3049 static int isom_add_sync_point( isom_stbl_t *stbl, isom_cache_t *cache, uint32_t sample_number, lsmash_sample_property_t *prop )
3050 {
3051 if( !(prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC) ) /* no null check for prop */
3052 {
3053 if( !cache->all_sync )
3054 return 0;
3055 if( LSMASH_IS_NON_EXISTING_BOX( stbl->stss )
3056 && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stss( stbl ) ) )
3057 return LSMASH_ERR_NAMELESS;
3058 int err = isom_add_stss_entry( stbl, 1 );
3059 if( err < 0 ) /* Declare here the first sample is a sync sample. */
3060 return err;
3061 cache->all_sync = 0;
3062 return 0;
3063 }
3064 if( cache->all_sync ) /* We don't need stss box if all samples are sync sample. */
3065 return 0;
3066 if( LSMASH_IS_NON_EXISTING_BOX( stbl->stss ) )
3067 {
3068 if( isom_get_sample_count_from_sample_table( stbl ) == 1 )
3069 {
3070 cache->all_sync = 1; /* Also the first sample is a sync sample. */
3071 return 0;
3072 }
3073 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stss( stbl ) ) )
3074 return LSMASH_ERR_NAMELESS;
3075 }
3076 return isom_add_stss_entry( stbl, sample_number );
3077 }
3078
isom_add_partial_sync(isom_stbl_t * stbl,lsmash_file_t * file,uint32_t sample_number,lsmash_sample_property_t * prop)3079 static int isom_add_partial_sync( isom_stbl_t *stbl, lsmash_file_t *file, uint32_t sample_number, lsmash_sample_property_t *prop )
3080 {
3081 if( !file->qt_compatible )
3082 return 0;
3083 if( !(prop->ra_flags & QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC) )
3084 return 0;
3085 /* This sample is a partial sync sample. */
3086 if( LSMASH_IS_NON_EXISTING_BOX( stbl->stps )
3087 && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_stps( stbl ) ) )
3088 return LSMASH_ERR_NAMELESS;
3089 return isom_add_stps_entry( stbl, sample_number );
3090 }
3091
isom_rap_grouping_established(isom_rap_group_t * group,int num_leading_samples_known,isom_sgpd_t * sgpd,int is_fragment)3092 int isom_rap_grouping_established( isom_rap_group_t *group, int num_leading_samples_known, isom_sgpd_t *sgpd, int is_fragment )
3093 {
3094 isom_rap_entry_t *rap = group->random_access;
3095 if( !rap )
3096 return 0;
3097 assert( rap == (isom_rap_entry_t *)sgpd->list->tail->data );
3098 rap->num_leading_samples_known = num_leading_samples_known;
3099 /* Avoid duplication of sample group descriptions. */
3100 uint32_t group_description_index = is_fragment ? 0x10001 : 1;
3101 for( lsmash_entry_t *entry = sgpd->list->head; entry != sgpd->list->tail; entry = entry->next )
3102 {
3103 isom_rap_entry_t *data = (isom_rap_entry_t *)entry->data;
3104 if( !data )
3105 return LSMASH_ERR_INVALID_DATA;
3106 if( rap->num_leading_samples_known == data->num_leading_samples_known
3107 && rap->num_leading_samples == data->num_leading_samples )
3108 {
3109 /* The same description already exists.
3110 * Remove the latest random access entry. */
3111 lsmash_list_remove_entry_tail( sgpd->list );
3112 /* Replace assigned group_description_index with the one corresponding the same description. */
3113 if( group->assignment->group_description_index == 0 )
3114 {
3115 /* We don't create consecutive sample groups not assigned to 'rap '.
3116 * So the previous sample group shall be a group of 'rap ' if any. */
3117 if( group->prev_assignment )
3118 {
3119 assert( group->prev_assignment->group_description_index );
3120 group->prev_assignment->group_description_index = group_description_index;
3121 }
3122 }
3123 else
3124 group->assignment->group_description_index = group_description_index;
3125 break;
3126 }
3127 ++group_description_index;
3128 }
3129 group->random_access = NULL;
3130 return 0;
3131 }
3132
isom_group_random_access(isom_box_t * parent,isom_cache_t * cache,lsmash_sample_t * sample)3133 int isom_group_random_access( isom_box_t *parent, isom_cache_t *cache, lsmash_sample_t *sample )
3134 {
3135 if( parent->file->max_isom_version < 6 )
3136 return 0;
3137 isom_sbgp_t *sbgp;
3138 isom_sgpd_t *sgpd;
3139 uint32_t sample_count;
3140 int is_fragment;
3141 if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) )
3142 {
3143 isom_stbl_t *stbl = (isom_stbl_t *)parent;
3144 sbgp = isom_get_sample_to_group ( stbl, ISOM_GROUP_TYPE_RAP );
3145 sgpd = isom_get_sample_group_description( stbl, ISOM_GROUP_TYPE_RAP );
3146 sample_count = isom_get_sample_count_from_sample_table( stbl );
3147 is_fragment = 0;
3148 }
3149 else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) )
3150 {
3151 isom_traf_t *traf = (isom_traf_t *)parent;
3152 sbgp = isom_get_fragment_sample_to_group ( traf, ISOM_GROUP_TYPE_RAP );
3153 sgpd = isom_get_fragment_sample_group_description( traf, ISOM_GROUP_TYPE_RAP );
3154 sample_count = cache->fragment->sample_count + 1; /* Cached sample_count is incremented later in isom_fragment_update_cache(). */
3155 is_fragment = 1;
3156 }
3157 else
3158 {
3159 assert( 0 );
3160 sbgp = isom_non_existing_sbgp();
3161 sgpd = isom_non_existing_sgpd();
3162 /* redundant initializations to suppress warnings from unclever compilers */
3163 sample_count = 0;
3164 is_fragment = 0;
3165 }
3166 if( LSMASH_IS_NON_EXISTING_BOX( sbgp )
3167 || LSMASH_IS_NON_EXISTING_BOX( sgpd ) )
3168 return 0;
3169 lsmash_sample_property_t *prop = &sample->prop;
3170 uint8_t is_rap = (prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC)
3171 || (prop->ra_flags & QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC)
3172 || (prop->ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP)
3173 || (LSMASH_IS_POST_ROLL_START( prop->ra_flags ) && prop->post_roll.identifier == prop->post_roll.complete);
3174 isom_rap_group_t *group = cache->rap;
3175 if( !group )
3176 {
3177 /* This sample is the first sample, create a grouping cache. */
3178 assert( sample_count == 1 );
3179 group = lsmash_malloc( sizeof(isom_rap_group_t) );
3180 if( !group )
3181 return LSMASH_ERR_MEMORY_ALLOC;
3182 if( is_rap )
3183 {
3184 group->random_access = isom_add_rap_group_entry( sgpd );
3185 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count + (is_fragment ? 0x10000 : 0) );
3186 }
3187 else
3188 {
3189 /* The first sample is not always a random access point. */
3190 group->random_access = NULL;
3191 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
3192 }
3193 if( !group->assignment )
3194 {
3195 lsmash_free( group );
3196 return LSMASH_ERR_MEMORY_ALLOC;
3197 }
3198 group->prev_assignment = NULL;
3199 group->is_prev_rap = is_rap;
3200 cache->rap = group;
3201 return 0;
3202 }
3203 int err;
3204 if( group->is_prev_rap )
3205 {
3206 /* OK. here, the previous sample is a menber of 'rap '. */
3207 if( !is_rap )
3208 {
3209 /* This sample isn't a member of 'rap ' and the previous sample is.
3210 * So we create a new group and set 0 on its group_description_index. */
3211 group->prev_assignment = group->assignment;
3212 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
3213 if( !group->assignment )
3214 {
3215 lsmash_free( group );
3216 return LSMASH_ERR_MEMORY_ALLOC;
3217 }
3218 }
3219 else if( !LSMASH_IS_CLOSED_RAP( prop->ra_flags ) )
3220 {
3221 /* Create a new group since there is the possibility the next sample is a leading sample.
3222 * This sample is a member of 'rap ', so we set appropriate value on its group_description_index. */
3223 if( (err = isom_rap_grouping_established( group, 1, sgpd, is_fragment )) < 0 )
3224 return err;
3225 group->random_access = isom_add_rap_group_entry( sgpd );
3226 group->prev_assignment = group->assignment;
3227 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count + (is_fragment ? 0x10000 : 0) );
3228 if( !group->assignment )
3229 {
3230 lsmash_free( group );
3231 return LSMASH_ERR_MEMORY_ALLOC;
3232 }
3233 }
3234 else /* The previous and current sample are a member of 'rap ', and the next sample must not be a leading sample. */
3235 ++ group->assignment->sample_count;
3236 }
3237 else if( is_rap )
3238 {
3239 /* This sample is a member of 'rap ' and the previous sample isn't.
3240 * So we create a new group and set appropriate value on its group_description_index. */
3241 if( (err = isom_rap_grouping_established( group, 1, sgpd, is_fragment )) < 0 )
3242 return err;
3243 group->random_access = isom_add_rap_group_entry( sgpd );
3244 group->prev_assignment = group->assignment;
3245 group->assignment = isom_add_group_assignment_entry( sbgp, 1, sgpd->list->entry_count + (is_fragment ? 0x10000 : 0) );
3246 if( !group->assignment )
3247 {
3248 lsmash_free( group );
3249 return LSMASH_ERR_MEMORY_ALLOC;
3250 }
3251 }
3252 else /* The previous and current sample aren't a member of 'rap '. */
3253 ++ group->assignment->sample_count;
3254 /* Obtain the property of the latest random access point group. */
3255 if( !is_rap && group->random_access )
3256 {
3257 if( prop->leading == ISOM_SAMPLE_LEADING_UNKNOWN )
3258 {
3259 /* We can no longer know num_leading_samples in this group. */
3260 if( (err = isom_rap_grouping_established( group, 0, sgpd, is_fragment )) < 0 )
3261 return err;
3262 }
3263 else
3264 {
3265 if( prop->leading == ISOM_SAMPLE_IS_UNDECODABLE_LEADING
3266 || prop->leading == ISOM_SAMPLE_IS_DECODABLE_LEADING )
3267 ++ group->random_access->num_leading_samples;
3268 /* no more consecutive leading samples in this group */
3269 else if( (err = isom_rap_grouping_established( group, 1, sgpd, is_fragment )) < 0 )
3270 return err;
3271 }
3272 }
3273 group->is_prev_rap = is_rap;
3274 return 0;
3275 }
3276
isom_roll_grouping_established(isom_roll_group_t * group)3277 static int isom_roll_grouping_established( isom_roll_group_t *group )
3278 {
3279 /* Avoid duplication of sample group descriptions. */
3280 isom_sgpd_t *sgpd = group->sgpd;
3281 uint32_t group_description_index = group->is_fragment ? 0x10001 : 1;
3282 for( lsmash_entry_t *entry = sgpd->list->head; entry; entry = entry->next )
3283 {
3284 isom_roll_entry_t *data = (isom_roll_entry_t *)entry->data;
3285 if( !data )
3286 return LSMASH_ERR_INVALID_DATA;
3287 if( group->roll_distance == data->roll_distance )
3288 {
3289 /* The same description already exists.
3290 * Set the group_description_index corresponding the same description. */
3291 group->assignment->group_description_index = group_description_index;
3292 return 0;
3293 }
3294 ++group_description_index;
3295 }
3296 /* Add a new roll recovery description. */
3297 if( !isom_add_roll_group_entry( sgpd, group->roll_distance ) )
3298 return LSMASH_ERR_MEMORY_ALLOC;
3299 group->assignment->group_description_index = sgpd->list->entry_count + (group->is_fragment ? 0x10000 : 0);
3300 return 0;
3301 }
3302
isom_deduplicate_roll_group(isom_sbgp_t * sbgp,lsmash_entry_list_t * pool)3303 static int isom_deduplicate_roll_group( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool )
3304 {
3305 /* Deduplication */
3306 uint32_t current_group_number = sbgp->list->entry_count - pool->entry_count + 1;
3307 isom_group_assignment_entry_t *prev_assignment = (isom_group_assignment_entry_t *)lsmash_list_get_entry_data( sbgp->list, current_group_number - 1 );
3308 for( lsmash_entry_t *entry = pool->head; entry; )
3309 {
3310 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3311 if( !group
3312 || !group->assignment )
3313 return LSMASH_ERR_INVALID_DATA;
3314 if( !group->delimited || group->described != ROLL_DISTANCE_DETERMINED )
3315 return 0;
3316 if( prev_assignment && prev_assignment->group_description_index == group->assignment->group_description_index )
3317 {
3318 /* Merge the current group with the previous. */
3319 lsmash_entry_t *next_entry = entry->next;
3320 prev_assignment->sample_count += group->assignment->sample_count;
3321 int err;
3322 if( (err = lsmash_list_remove_entry( sbgp->list, current_group_number )) < 0
3323 || (err = lsmash_list_remove_entry_direct( pool, entry )) < 0 )
3324 return err;
3325 entry = next_entry;
3326 }
3327 else
3328 {
3329 entry = entry->next;
3330 prev_assignment = group->assignment;
3331 ++current_group_number;
3332 }
3333 }
3334 return 0;
3335 }
3336
3337 /* Remove pooled caches that has become unnecessary. */
isom_clean_roll_pool(lsmash_entry_list_t * pool)3338 static int isom_clean_roll_pool( lsmash_entry_list_t *pool )
3339 {
3340 for( lsmash_entry_t *entry = pool->head; entry; entry = pool->head )
3341 {
3342 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3343 if( !group )
3344 return LSMASH_ERR_INVALID_DATA;
3345 if( !group->delimited || group->described != ROLL_DISTANCE_DETERMINED )
3346 return 0;
3347 int err = lsmash_list_remove_entry_direct( pool, entry );
3348 if( err < 0 )
3349 return err;
3350 }
3351 return 0;
3352 }
3353
isom_flush_roll_pool(isom_sbgp_t * sbgp,lsmash_entry_list_t * pool)3354 static int isom_flush_roll_pool( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool )
3355 {
3356 int err;
3357 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
3358 {
3359 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3360 if( !group )
3361 return LSMASH_ERR_INVALID_DATA;
3362 if( group->delimited
3363 && group->described == ROLL_DISTANCE_DETERMINED
3364 && group->roll_distance != 0
3365 && (err = isom_roll_grouping_established( group )) < 0 )
3366 return err;
3367 }
3368 if( (err = isom_deduplicate_roll_group( sbgp, pool )) < 0 )
3369 return err;
3370 return isom_clean_roll_pool( pool );
3371 }
3372
isom_all_recovery_described(isom_sbgp_t * sbgp,lsmash_entry_list_t * pool)3373 static int isom_all_recovery_described( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool )
3374 {
3375 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
3376 {
3377 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3378 if( !group )
3379 return LSMASH_ERR_INVALID_DATA;
3380 group->described = ROLL_DISTANCE_DETERMINED;
3381 }
3382 return isom_flush_roll_pool( sbgp, pool );
3383 }
3384
isom_all_recovery_completed(isom_sbgp_t * sbgp,lsmash_entry_list_t * pool)3385 int isom_all_recovery_completed( isom_sbgp_t *sbgp, lsmash_entry_list_t *pool )
3386 {
3387 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
3388 {
3389 isom_roll_group_t *group = (isom_roll_group_t *)entry->data;
3390 if( !group )
3391 return LSMASH_ERR_INVALID_DATA;
3392 group->described = ROLL_DISTANCE_DETERMINED;
3393 group->delimited = 1;
3394 }
3395 return isom_flush_roll_pool( sbgp, pool );
3396 }
3397
isom_get_roll_description(isom_roll_group_t * group)3398 static isom_roll_entry_t *isom_get_roll_description
3399 (
3400 isom_roll_group_t *group
3401 )
3402 {
3403 uint32_t group_description_index = group->assignment->group_description_index;
3404 if( group_description_index && group->is_fragment )
3405 {
3406 assert( group_description_index > 0x10000 );
3407 group_description_index -= 0x10000;
3408 }
3409 return (isom_roll_entry_t *)lsmash_list_get_entry_data( group->sgpd->list, group_description_index );
3410 }
3411
isom_group_roll_recovery(isom_box_t * parent,isom_cache_t * cache,lsmash_sample_t * sample)3412 int isom_group_roll_recovery( isom_box_t *parent, isom_cache_t *cache, lsmash_sample_t *sample )
3413 {
3414 if( !parent->file->avc_extensions
3415 && !parent->file->qt_compatible )
3416 return 0;
3417 uint32_t sample_count;
3418 int is_fragment;
3419 lsmash_entry_list_t *sbgp_list;
3420 lsmash_entry_list_t *sgpd_list;
3421 if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_STBL ) )
3422 {
3423 isom_stbl_t *stbl = (isom_stbl_t *)parent;
3424 sbgp_list = &stbl->sbgp_list;
3425 sgpd_list = &stbl->sgpd_list;
3426 sample_count = isom_get_sample_count_from_sample_table( stbl );
3427 is_fragment = 0;
3428 }
3429 else if( lsmash_check_box_type_identical( parent->type, ISOM_BOX_TYPE_TRAF ) )
3430 {
3431 if( parent->file->max_isom_version < 6 )
3432 return 0;
3433 isom_traf_t *traf = (isom_traf_t *)parent;
3434 sbgp_list = &traf->sbgp_list;
3435 sgpd_list = &traf->sgpd_list;
3436 sample_count = cache->fragment->sample_count + 1; /* Cached sample_count is incremented later in isom_fragment_update_cache(). */
3437 is_fragment = 1;
3438 }
3439 else
3440 {
3441 assert( 0 );
3442 return LSMASH_ERR_INVALID_DATA;
3443 }
3444 isom_sbgp_t *sbgp = isom_get_roll_recovery_sample_to_group ( sbgp_list );
3445 isom_sgpd_t *sgpd = isom_get_roll_recovery_sample_group_description( sgpd_list );
3446 if( LSMASH_IS_NON_EXISTING_BOX( sbgp )
3447 || LSMASH_IS_NON_EXISTING_BOX( sgpd )
3448 || sbgp->grouping_type != sgpd->grouping_type )
3449 return 0;
3450 /* Check if 'roll' -> 'prol' conversion is needed. */
3451 if( cache->is_audio
3452 && sbgp->grouping_type == ISOM_GROUP_TYPE_ROLL
3453 && !(sample->prop.ra_flags & ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC) )
3454 {
3455 /* Since not every samples is a sync sample, change grouping_type into 'prol'. */
3456 sbgp->grouping_type = ISOM_GROUP_TYPE_PROL;
3457 sgpd->grouping_type = ISOM_GROUP_TYPE_PROL;
3458 }
3459 lsmash_entry_list_t *pool = cache->roll.pool;
3460 if( !pool )
3461 {
3462 pool = lsmash_list_create_simple();
3463 if( !pool )
3464 return LSMASH_ERR_MEMORY_ALLOC;
3465 cache->roll.pool = pool;
3466 }
3467 lsmash_sample_property_t *prop = &sample->prop;
3468 isom_roll_group_t *group = (isom_roll_group_t *)lsmash_list_get_entry_data( pool, pool->entry_count );
3469 int is_recovery_start = LSMASH_IS_POST_ROLL_START( prop->ra_flags );
3470 int valid_pre_roll = !is_recovery_start
3471 && (prop->ra_flags != ISOM_SAMPLE_RANDOM_ACCESS_FLAG_NONE)
3472 && (prop->pre_roll.distance > 0)
3473 && (prop->pre_roll.distance <= -INT16_MIN);
3474 int new_group = !group || is_recovery_start || (group->prev_is_recovery_start != is_recovery_start);
3475 if( !new_group )
3476 {
3477 /* Check pre-roll distance. */
3478 assert( group->assignment && group->sgpd );
3479 isom_roll_entry_t *prev_roll = isom_get_roll_description( group );
3480 if( !prev_roll )
3481 new_group = valid_pre_roll;
3482 else if( !valid_pre_roll || (prop->pre_roll.distance != -prev_roll->roll_distance) )
3483 /* Pre-roll distance is different from the previous. */
3484 new_group = 1;
3485 }
3486 if( new_group )
3487 {
3488 if( group )
3489 group->delimited = 1;
3490 else
3491 assert( sample_count == 1 );
3492 /* Create a new group. */
3493 group = lsmash_malloc_zero( sizeof(isom_roll_group_t) );
3494 if( !group )
3495 return LSMASH_ERR_MEMORY_ALLOC;
3496 group->sgpd = sgpd;
3497 group->prev_is_recovery_start = is_recovery_start;
3498 group->is_fragment = is_fragment;
3499 group->assignment = isom_add_group_assignment_entry( sbgp, 1, 0 );
3500 if( !group->assignment || lsmash_list_add_entry( pool, group ) < 0 )
3501 {
3502 lsmash_free( group );
3503 return LSMASH_ERR_MEMORY_ALLOC;
3504 }
3505 if( is_recovery_start )
3506 {
3507 /* a member of non-roll or post-roll group */
3508 group->first_sample = sample_count;
3509 group->recovery_point = prop->post_roll.complete;
3510 }
3511 else
3512 {
3513 group->described = ROLL_DISTANCE_DETERMINED;
3514 if( valid_pre_roll )
3515 {
3516 /* a member of pre-roll group */
3517 group->roll_distance = -(signed)prop->pre_roll.distance;
3518 int err = isom_roll_grouping_established( group );
3519 if( err < 0 )
3520 return err;
3521 }
3522 else
3523 /* a member of non-roll group */
3524 group->roll_distance = 0;
3525 }
3526 }
3527 else
3528 {
3529 group->prev_is_recovery_start = is_recovery_start;
3530 group->assignment->sample_count += 1;
3531 }
3532 /* If encountered a RAP, all recovery is completed here. */
3533 if( prop->ra_flags & (ISOM_SAMPLE_RANDOM_ACCESS_FLAG_SYNC
3534 | ISOM_SAMPLE_RANDOM_ACCESS_FLAG_RAP
3535 | QT_SAMPLE_RANDOM_ACCESS_FLAG_PARTIAL_SYNC) )
3536 return isom_all_recovery_described( sbgp, pool );
3537 /* Check whether this sample is a random access recovery point or not. */
3538 for( lsmash_entry_t *entry = pool->head; entry; entry = entry->next )
3539 {
3540 group = (isom_roll_group_t *)entry->data;
3541 if( !group )
3542 return LSMASH_ERR_INVALID_DATA;
3543 if( group->described == ROLL_DISTANCE_DETERMINED )
3544 continue;
3545 if( group->described == ROLL_DISTANCE_INITIALIZED )
3546 {
3547 /* Let's consider the following picture sequence.
3548 * coded order : P[0] P[1] P[2] P[3] P[4] P[5]
3549 * DTS : 0 1 2 3 4 5
3550 * CTS : 2 4 3 6 7 5
3551 * Here, P[0] conveys a recovery point SEI and P[3] is the recovery point.
3552 * Correctness of decoded pictures is specified by recovery point in output order for both AVC and HEVC.
3553 * Therefore, as follows,
3554 * output order : P[0] P[2] P[1] P[5]|P[3] P[4]
3555 * ---(incorrect?)--->|
3556 * there is no guarantee that P[5] is decoded and output correctly.
3557 * From this, it can be said that the roll_distance of this sequence is equal to 5. */
3558 isom_roll_entry_t *post_roll = isom_get_roll_description( group );
3559 if( post_roll && post_roll->roll_distance > 0 )
3560 {
3561 if( sample->cts != LSMASH_TIMESTAMP_UNDEFINED
3562 && group->rp_cts != LSMASH_TIMESTAMP_UNDEFINED
3563 && group->rp_cts > sample->cts )
3564 /* Updated roll_distance for composition reordering. */
3565 post_roll->roll_distance = sample_count - group->first_sample;
3566 if( ++ group->wait_and_see_count >= MAX_ROLL_WAIT_AND_SEE_COUNT )
3567 group->described = ROLL_DISTANCE_DETERMINED;
3568 }
3569 }
3570 else if( prop->post_roll.identifier == group->recovery_point )
3571 {
3572 int16_t distance = sample_count - group->first_sample;
3573 group->rp_cts = sample->cts;
3574 group->roll_distance = distance;
3575 /* Add a roll recovery entry only when roll_distance isn't zero since roll_distance = 0 must not be used. */
3576 if( distance )
3577 {
3578 /* Now, this group is a 'roll'.
3579 * The roll_distance may be updated later because of composition reordering. */
3580 group->described = ROLL_DISTANCE_INITIALIZED;
3581 group->wait_and_see_count = 0;
3582 /* All groups with uninitialized roll_distance before the current group are described. */
3583 lsmash_entry_t *current = entry;
3584 for( entry = pool->head; entry != current; entry = entry->next )
3585 {
3586 group = (isom_roll_group_t *)entry->data;
3587 if( !group || group->described != ROLL_DISTANCE_INITIALIZED )
3588 continue;
3589 group->described = ROLL_DISTANCE_DETERMINED;
3590 }
3591 /* Cache the mark of the first recovery point in a subsegment. */
3592 if( cache->fragment
3593 && cache->fragment->subsegment.first_rp_number == 0 )
3594 cache->fragment->subsegment.is_first_recovery_point = 1;
3595 }
3596 else
3597 /* Random Accessible Point */
3598 return isom_all_recovery_described( sbgp, pool );
3599 }
3600 }
3601 return isom_flush_roll_pool( sbgp, pool );
3602 }
3603
isom_update_chunk_tables(isom_stbl_t * stbl,lsmash_file_t * media_file,isom_chunk_t * current)3604 static int isom_update_chunk_tables
3605 (
3606 isom_stbl_t *stbl,
3607 lsmash_file_t *media_file,
3608 isom_chunk_t *current
3609 )
3610 {
3611 isom_stsc_entry_t *last_stsc_data = stbl->stsc->list->tail ? (isom_stsc_entry_t *)stbl->stsc->list->tail->data : NULL;
3612 /* Create a new chunk sequence in this track if needed. */
3613 int err;
3614 if( (!last_stsc_data
3615 || current->pool->sample_count != last_stsc_data->samples_per_chunk
3616 || current->sample_description_index != last_stsc_data->sample_description_index)
3617 && (err = isom_add_stsc_entry( stbl, current->chunk_number,
3618 current->pool->sample_count,
3619 current->sample_description_index )) < 0 )
3620 return err;
3621 /* Add a new chunk offset in this track. */
3622 uint64_t offset = media_file->size;
3623 if( media_file->fragment )
3624 offset += ISOM_BASEBOX_COMMON_SIZE + media_file->fragment->pool_size;
3625 return isom_add_stco_entry( stbl, offset );
3626 }
3627
3628 /* This function decides to put a give sample on the current chunk or the next new one.
3629 * Returns 1 if pooled samples must be flushed.
3630 * FIXME: I wonder if this function should have a extra argument which indicates force_to_flush_cached_chunk.
3631 * see lsmash_append_sample for detail. */
isom_add_sample_to_chunk(isom_trak_t * trak,lsmash_sample_t * sample)3632 static int isom_add_sample_to_chunk
3633 (
3634 isom_trak_t *trak,
3635 lsmash_sample_t *sample
3636 )
3637 {
3638 if( LSMASH_IS_NON_EXISTING_BOX( trak->file )
3639 || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->mdhd )
3640 || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->dinf->dref )
3641 || LSMASH_IS_NON_EXISTING_BOX( trak->mdia->minf->stbl->stsd )
3642 || !trak->cache
3643 || trak->mdia->mdhd->timescale == 0
3644 || !trak->mdia->minf->stbl->stsc->list )
3645 return LSMASH_ERR_INVALID_DATA;
3646 isom_chunk_t *current = &trak->cache->chunk;
3647 if( !current->pool )
3648 {
3649 /* Very initial settings, just once per track */
3650 current->pool = isom_create_sample_pool( 0 );
3651 if( !current->pool )
3652 return LSMASH_ERR_MEMORY_ALLOC;
3653 }
3654 if( current->pool->sample_count == 0 )
3655 {
3656 /* Cannot decide whether we should flush the current sample or not here yet. */
3657 current->chunk_number += 1;
3658 current->sample_description_index = sample->index;
3659 current->first_dts = sample->dts;
3660 return 0;
3661 }
3662 if( sample->dts < current->first_dts )
3663 return LSMASH_ERR_INVALID_DATA; /* easy error check. */
3664 lsmash_file_t *media_file = isom_get_written_media_file( trak, current->sample_description_index );
3665 if( (current->sample_description_index == sample->index)
3666 && (media_file->max_chunk_duration >= ((double)(sample->dts - current->first_dts) / trak->mdia->mdhd->timescale))
3667 && (media_file->max_chunk_size >= current->pool->size + sample->length) )
3668 return 0; /* No need to flush current cached chunk, the current sample must be put into that. */
3669 /* NOTE: chunk relative stuff must be pushed into file after a chunk is fully determined with its contents.
3670 * Now the current cached chunk is fixed, actually add the chunk relative properties to its file accordingly. */
3671 int err = isom_update_chunk_tables( trak->mdia->minf->stbl, media_file, current );
3672 if( err < 0 )
3673 return err;
3674 /* Update and re-initialize cache, using the current sample */
3675 current->chunk_number += 1;
3676 current->sample_description_index = sample->index;
3677 current->first_dts = sample->dts;
3678 /* current->pool must be flushed in isom_append_sample_internal() */
3679 return 1;
3680 }
3681
isom_write_pooled_samples(lsmash_file_t * file,isom_sample_pool_t * pool)3682 static int isom_write_pooled_samples( lsmash_file_t *file, isom_sample_pool_t *pool )
3683 {
3684 if( LSMASH_IS_NON_EXISTING_BOX( file )
3685 || !file->bs
3686 || !file->bs->stream
3687 || !(file->flags & LSMASH_FILE_MODE_WRITE)
3688 || !(file->flags & LSMASH_FILE_MODE_MEDIA)
3689 || ((file->flags & LSMASH_FILE_MODE_BOX) && LSMASH_IS_NON_EXISTING_BOX( file->mdat )) )
3690 return LSMASH_ERR_INVALID_DATA;
3691 lsmash_bs_put_bytes( file->bs, pool->size, pool->data );
3692 int err = lsmash_bs_flush_buffer( file->bs );
3693 if( err < 0 )
3694 return err;
3695 if( LSMASH_IS_EXISTING_BOX( file->mdat ) )
3696 file->mdat->media_size += pool->size;
3697 file->size += pool->size;
3698 pool->sample_count = 0;
3699 pool->size = 0;
3700 return 0;
3701 }
3702
isom_update_sample_tables(isom_trak_t * trak,lsmash_sample_t * sample,uint32_t * samples_per_packet,isom_sample_entry_t * sample_entry)3703 int isom_update_sample_tables
3704 (
3705 isom_trak_t *trak,
3706 lsmash_sample_t *sample,
3707 uint32_t *samples_per_packet,
3708 isom_sample_entry_t *sample_entry
3709 )
3710 {
3711 int err;
3712 isom_audio_entry_t *audio = (isom_audio_entry_t *)sample_entry;
3713 if( (audio->manager & LSMASH_AUDIO_DESCRIPTION)
3714 && (audio->manager & LSMASH_QTFF_BASE)
3715 && (audio->version == 1)
3716 && (audio->compression_ID != QT_AUDIO_COMPRESSION_ID_VARIABLE_COMPRESSION) )
3717 {
3718 /* Add entries of the sample table for each uncompressed sample. */
3719 uint64_t sample_duration = trak->mdia->mdhd->timescale / (audio->samplerate >> 16);
3720 if( audio->samplesPerPacket == 0 || sample_duration == 0 || sample->cts == LSMASH_TIMESTAMP_UNDEFINED )
3721 return LSMASH_ERR_INVALID_DATA;
3722 uint64_t sample_dts = sample->dts;
3723 uint64_t sample_cts = sample->cts;
3724 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3725 for( uint32_t i = 0; i < audio->samplesPerPacket; i++ )
3726 {
3727 /* Add a size of uncomressed audio and increment sample_count.
3728 * This points to individual uncompressed audio samples, each one byte in size, within the compressed frames. */
3729 uint32_t sample_count = isom_add_size( stbl, 1 );
3730 if( sample_count == 0 )
3731 return LSMASH_ERR_NAMELESS;
3732 /* Add a decoding timestamp and a composition timestamp. */
3733 if( (err = isom_add_timestamp( stbl, trak->cache, trak->file, sample_dts, sample_cts )) < 0 )
3734 return err;
3735 sample_dts += sample_duration;
3736 sample_cts += sample_duration;
3737 }
3738 *samples_per_packet = audio->samplesPerPacket;
3739 }
3740 else
3741 {
3742 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3743 /* Add a sample_size and increment sample_count. */
3744 uint32_t sample_count = isom_add_size( stbl, sample->length );
3745 if( sample_count == 0 )
3746 return LSMASH_ERR_NAMELESS;
3747 /* Add a decoding timestamp and a composition timestamp. */
3748 if( (err = isom_add_timestamp( stbl, trak->cache, trak->file, sample->dts, sample->cts )) < 0 )
3749 return err;
3750 /* Add a sync point if needed. */
3751 if( (err = isom_add_sync_point( stbl, trak->cache, sample_count, &sample->prop )) < 0 )
3752 return err;
3753 /* Add a partial sync point if needed. */
3754 if( (err = isom_add_partial_sync( stbl, trak->file, sample_count, &sample->prop )) < 0 )
3755 return err;
3756 /* Add leading, independent, disposable and redundant information if needed. */
3757 if( stbl->add_dependency_type
3758 && (err = stbl->add_dependency_type( stbl, trak->file, &sample->prop )) < 0 )
3759 return err;
3760 /* Group samples into random access point type if needed. */
3761 if( (err = isom_group_random_access( (isom_box_t *)stbl, trak->cache, sample )) < 0 )
3762 return err;
3763 /* Group samples into random access recovery point type if needed. */
3764 if( (err = isom_group_roll_recovery( (isom_box_t *)stbl, trak->cache, sample )) < 0 )
3765 return err;
3766 *samples_per_packet = 1;
3767 }
3768 /* Add a chunk if needed. */
3769 return isom_add_sample_to_chunk( trak, sample );
3770 }
3771
isom_output_cached_chunk(isom_trak_t * trak)3772 static int isom_output_cached_chunk( isom_trak_t *trak )
3773 {
3774 isom_chunk_t *chunk = &trak->cache->chunk;
3775 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3776 isom_stsc_entry_t *last_stsc_data = stbl->stsc->list->tail ? (isom_stsc_entry_t *)stbl->stsc->list->tail->data : NULL;
3777 /* Create a new chunk sequence in this track if needed. */
3778 int err;
3779 if( (!last_stsc_data
3780 || chunk->pool->sample_count != last_stsc_data->samples_per_chunk
3781 || chunk->sample_description_index != last_stsc_data->sample_description_index)
3782 && (err = isom_add_stsc_entry( stbl, chunk->chunk_number,
3783 chunk->pool->sample_count,
3784 chunk->sample_description_index )) < 0 )
3785 return err;
3786 lsmash_file_t *file = isom_get_written_media_file( trak, chunk->sample_description_index );
3787 if( file->fragment )
3788 {
3789 /* Add a new chunk offset in this track. */
3790 if( (err = isom_add_stco_entry( stbl, file->size + ISOM_BASEBOX_COMMON_SIZE + file->fragment->pool_size )) < 0 )
3791 return err;
3792 return isom_append_fragment_track_run( file, chunk );
3793 }
3794 /* Add a new chunk offset in this track. */
3795 if( (err = isom_add_stco_entry( stbl, file->size )) < 0 )
3796 return err;
3797 /* Output pooled samples in this track. */
3798 return isom_write_pooled_samples( file, chunk->pool );
3799 }
3800
isom_pool_sample(isom_sample_pool_t * pool,lsmash_sample_t * sample,uint32_t samples_per_packet)3801 int isom_pool_sample( isom_sample_pool_t *pool, lsmash_sample_t *sample, uint32_t samples_per_packet )
3802 {
3803 uint64_t pool_size = pool->size + sample->length;
3804 if( pool->alloc < pool_size )
3805 {
3806 uint8_t *data;
3807 uint64_t alloc = pool_size + (1<<16);
3808 if( !pool->data )
3809 data = lsmash_malloc( alloc );
3810 else
3811 data = lsmash_realloc( pool->data, alloc );
3812 if( !data )
3813 return LSMASH_ERR_MEMORY_ALLOC;
3814 pool->data = data;
3815 pool->alloc = alloc;
3816 }
3817 memcpy( pool->data + pool->size, sample->data, sample->length );
3818 pool->size = pool_size;
3819 pool->sample_count += samples_per_packet;
3820 lsmash_delete_sample( sample );
3821 return 0;
3822 }
3823
isom_append_sample_internal(isom_trak_t * trak,lsmash_sample_t * sample,isom_sample_entry_t * sample_entry)3824 static int isom_append_sample_internal
3825 (
3826 isom_trak_t *trak,
3827 lsmash_sample_t *sample,
3828 isom_sample_entry_t *sample_entry
3829 )
3830 {
3831 uint32_t samples_per_packet;
3832 int ret = isom_update_sample_tables( trak, sample, &samples_per_packet, sample_entry );
3833 if( ret < 0 )
3834 return ret;
3835 /* ret == 1 means pooled samples must be flushed. */
3836 isom_sample_pool_t *current_pool = trak->cache->chunk.pool;
3837 if( ret == 1 )
3838 {
3839 /* The sample_description_index in the cache is one of the next written chunk.
3840 * Therefore, it cannot be referenced here. */
3841 lsmash_entry_list_t *stsc_list = trak->mdia->minf->stbl->stsc->list;
3842 isom_stsc_entry_t *last_stsc_data = (isom_stsc_entry_t *)stsc_list->tail->data;
3843 lsmash_file_t *file = isom_get_written_media_file( trak, last_stsc_data->sample_description_index );
3844 if( (ret = isom_write_pooled_samples( file, current_pool )) < 0 )
3845 return ret;
3846 }
3847 /* Arbitration system between tracks with extremely scattering dts.
3848 * Here, we check whether asynchronization between the tracks exceeds the tolerance.
3849 * If a track has too old "first DTS" in its cached chunk than current sample's DTS, then its pooled samples must be flushed.
3850 * We don't consider presentation of media since any edit can pick an arbitrary portion of media in track.
3851 * Note: you needn't read this loop until you grasp the basic handling of chunks. */
3852 lsmash_file_t *file = trak->file;
3853 double tolerance = file->max_async_tolerance;
3854 for( lsmash_entry_t *entry = file->moov->trak_list.head; entry; entry = entry->next )
3855 {
3856 isom_trak_t *other = (isom_trak_t *)entry->data;
3857 if( trak == other )
3858 continue;
3859 if( LSMASH_IS_NON_EXISTING_BOX( other )
3860 || LSMASH_IS_NON_EXISTING_BOX( other->mdia->mdhd )
3861 || !other->cache
3862 || other->mdia->mdhd->timescale == 0
3863 || !other->mdia->minf->stbl->stsc->list )
3864 return LSMASH_ERR_INVALID_DATA;
3865 isom_chunk_t *chunk = &other->cache->chunk;
3866 if( !chunk->pool || chunk->pool->sample_count == 0 )
3867 continue;
3868 double diff = ((double)sample->dts / trak->mdia->mdhd->timescale)
3869 - ((double)chunk->first_dts / other->mdia->mdhd->timescale);
3870 if( diff > tolerance && (ret = isom_output_cached_chunk( other )) < 0 )
3871 return ret;
3872 /* Note: we don't flush the cached chunk in the current track and the current sample here
3873 * even if the conditional expression of '-diff > tolerance' meets.
3874 * That's useless because appending a sample to another track would be a good equivalent.
3875 * It's even harmful because it causes excess chunk division by calling
3876 * isom_output_cached_chunk() which always generates a new chunk.
3877 * Anyway some excess chunk division will be there, but rather less without it.
3878 * To completely avoid this, we need to observe at least whether the current sample will be placed
3879 * right next to the previous chunk of the same track or not. */
3880 }
3881 /* anyway the current sample must be pooled. */
3882 return isom_pool_sample( current_pool, sample, samples_per_packet );
3883 }
3884
isom_append_sample_by_type(void * track,lsmash_sample_t * sample,isom_sample_entry_t * sample_entry,int (* func_append_sample)(void *,lsmash_sample_t *,isom_sample_entry_t *))3885 int isom_append_sample_by_type
3886 (
3887 void *track,
3888 lsmash_sample_t *sample,
3889 isom_sample_entry_t *sample_entry,
3890 int (*func_append_sample)( void *, lsmash_sample_t *, isom_sample_entry_t * )
3891 )
3892 {
3893 if( isom_is_lpcm_audio( sample_entry ) )
3894 {
3895 uint32_t frame_size = ((isom_audio_entry_t *)sample_entry)->constBytesPerAudioPacket;
3896 if( sample->length == frame_size )
3897 return func_append_sample( track, sample, sample_entry );
3898 else if( sample->length < frame_size || sample->cts == LSMASH_TIMESTAMP_UNDEFINED )
3899 return LSMASH_ERR_INVALID_DATA;
3900 /* Append samples splitted into each LPCMFrame. */
3901 uint64_t dts = sample->dts;
3902 uint64_t cts = sample->cts;
3903 for( uint32_t offset = 0; offset < sample->length; offset += frame_size )
3904 {
3905 lsmash_sample_t *lpcm_sample = lsmash_create_sample( frame_size );
3906 if( !lpcm_sample )
3907 return LSMASH_ERR_MEMORY_ALLOC;
3908 memcpy( lpcm_sample->data, sample->data + offset, frame_size );
3909 lpcm_sample->dts = dts++;
3910 lpcm_sample->cts = cts++;
3911 lpcm_sample->prop = sample->prop;
3912 lpcm_sample->index = sample->index;
3913 int err = func_append_sample( track, lpcm_sample, sample_entry );
3914 if( err < 0 )
3915 {
3916 lsmash_delete_sample( lpcm_sample );
3917 return err;
3918 }
3919 }
3920 lsmash_delete_sample( sample );
3921 return 0;
3922 }
3923 return func_append_sample( track, sample, sample_entry );
3924 }
3925
3926 /* This function is for non-fragmented movie. */
isom_append_sample(lsmash_file_t * file,isom_trak_t * trak,lsmash_sample_t * sample,isom_sample_entry_t * sample_entry)3927 static int isom_append_sample
3928 (
3929 lsmash_file_t *file,
3930 isom_trak_t *trak,
3931 lsmash_sample_t *sample,
3932 isom_sample_entry_t *sample_entry
3933 )
3934 {
3935 /* If there is no available Media Data Box to write samples, add and write a new one before any chunk offset is decided. */
3936 int err;
3937 int mdat_absent = LSMASH_IS_NON_EXISTING_BOX( file->mdat );
3938 if( mdat_absent || !(file->mdat->manager & LSMASH_INCOMPLETE_BOX) )
3939 {
3940 if( mdat_absent && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_mdat( file ) ) )
3941 return LSMASH_ERR_NAMELESS;
3942 file->mdat->manager |= LSMASH_PLACEHOLDER;
3943 if( (err = isom_write_box( file->bs, (isom_box_t *)file->mdat )) < 0 )
3944 return err;
3945 file->size += file->mdat->size;
3946 }
3947 return isom_append_sample_by_type( trak, sample, sample_entry, (int (*)( void *, lsmash_sample_t *, isom_sample_entry_t * ))isom_append_sample_internal );
3948 }
3949
isom_output_cache(isom_trak_t * trak)3950 static int isom_output_cache( isom_trak_t *trak )
3951 {
3952 int err;
3953 isom_cache_t *cache = trak->cache;
3954 if( cache->chunk.pool
3955 && cache->chunk.pool->sample_count
3956 && (err = isom_output_cached_chunk( trak )) < 0 )
3957 return err;
3958 isom_stbl_t *stbl = trak->mdia->minf->stbl;
3959 for( lsmash_entry_t *entry = stbl->sgpd_list.head; entry; entry = entry->next )
3960 {
3961 isom_sgpd_t *sgpd = (isom_sgpd_t *)entry->data;
3962 if( LSMASH_IS_NON_EXISTING_BOX( sgpd ) )
3963 return LSMASH_ERR_INVALID_DATA;
3964 switch( sgpd->grouping_type )
3965 {
3966 case ISOM_GROUP_TYPE_RAP :
3967 {
3968 isom_rap_group_t *group = cache->rap;
3969 if( !group )
3970 {
3971 if( stbl->file->fragment )
3972 continue;
3973 else
3974 return LSMASH_ERR_NAMELESS;
3975 }
3976 if( !group->random_access )
3977 continue;
3978 group->random_access->num_leading_samples_known = 1;
3979 break;
3980 }
3981 case ISOM_GROUP_TYPE_ROLL :
3982 case ISOM_GROUP_TYPE_PROL :
3983 if( !cache->roll.pool )
3984 {
3985 if( stbl->file->fragment )
3986 continue;
3987 else
3988 return LSMASH_ERR_NAMELESS;
3989 }
3990 isom_sbgp_t *sbgp = isom_get_roll_recovery_sample_to_group( &stbl->sbgp_list );
3991 if( LSMASH_IS_NON_EXISTING_BOX( sbgp ) )
3992 return LSMASH_ERR_NAMELESS;
3993 if( (err = isom_all_recovery_completed( sbgp, cache->roll.pool )) < 0 )
3994 return err;
3995 break;
3996 default :
3997 break;
3998 }
3999 }
4000 return 0;
4001 }
4002
lsmash_flush_pooled_samples(lsmash_root_t * root,uint32_t track_ID,uint32_t last_sample_delta)4003 int lsmash_flush_pooled_samples( lsmash_root_t *root, uint32_t track_ID, uint32_t last_sample_delta )
4004 {
4005 if( isom_check_initializer_present( root ) < 0 )
4006 return LSMASH_ERR_FUNCTION_PARAM;
4007 lsmash_file_t *file = root->file;
4008 if( file->fragment
4009 && file->fragment->movie )
4010 return isom_flush_fragment_pooled_samples( file, track_ID, last_sample_delta );
4011 if( file != file->initializer )
4012 return LSMASH_ERR_INVALID_DATA;
4013 isom_trak_t *trak = isom_get_trak( file, track_ID );
4014 if( LSMASH_IS_NON_EXISTING_BOX( trak )
4015 || !trak->cache
4016 || !trak->mdia->minf->stbl->stsc->list )
4017 return LSMASH_ERR_NAMELESS;
4018 int err = isom_output_cache( trak );
4019 if( err < 0 )
4020 return err;
4021 return lsmash_set_last_sample_delta( root, track_ID, last_sample_delta );
4022 }
4023
lsmash_append_sample(lsmash_root_t * root,uint32_t track_ID,lsmash_sample_t * sample)4024 int lsmash_append_sample( lsmash_root_t *root, uint32_t track_ID, lsmash_sample_t *sample )
4025 {
4026 if( isom_check_initializer_present( root ) < 0
4027 || track_ID == 0
4028 || sample == NULL
4029 || sample->data == NULL
4030 || sample->dts == LSMASH_TIMESTAMP_UNDEFINED )
4031 return LSMASH_ERR_FUNCTION_PARAM;
4032 lsmash_file_t *file = root->file;
4033 /* We think max_chunk_duration == 0, which means all samples will be cached on memory, should be prevented.
4034 * This means removal of a feature that we used to have, but anyway very alone chunk does not make sense. */
4035 if( !file->bs
4036 || !(file->flags & LSMASH_FILE_MODE_BOX)
4037 || file->max_chunk_duration == 0
4038 || file->max_async_tolerance == 0 )
4039 return LSMASH_ERR_NAMELESS;
4040 /* Write File Type Box here if it was not written yet. */
4041 if( file->flags & LSMASH_FILE_MODE_INITIALIZATION )
4042 {
4043 if( LSMASH_IS_EXISTING_BOX( file->ftyp ) && !(file->ftyp->manager & LSMASH_WRITTEN_BOX) )
4044 {
4045 int err = isom_write_box( file->bs, (isom_box_t *)file->ftyp );
4046 if( err < 0 )
4047 return err;
4048 file->size += file->ftyp->size;
4049 }
4050 }
4051 /* Get a sample initializer. */
4052 isom_trak_t *trak = isom_get_trak( file->initializer, track_ID );
4053 if( LSMASH_IS_NON_EXISTING_BOX( trak->file )
4054 || LSMASH_IS_NON_EXISTING_BOX( trak->tkhd )
4055 || trak->mdia->mdhd->timescale == 0
4056 || !trak->cache
4057 || !trak->mdia->minf->stbl->stsc->list )
4058 return LSMASH_ERR_NAMELESS;
4059 isom_sample_entry_t *sample_entry = (isom_sample_entry_t *)lsmash_list_get_entry_data( &trak->mdia->minf->stbl->stsd->list, sample->index );
4060 if( LSMASH_IS_NON_EXISTING_BOX( sample_entry ) )
4061 return LSMASH_ERR_NAMELESS;
4062 /* Append a sample. */
4063 if( (file->flags & LSMASH_FILE_MODE_FRAGMENTED)
4064 && file->fragment
4065 && file->fragment->pool )
4066 return isom_append_fragment_sample( file, trak, sample, sample_entry );
4067 if( file != file->initializer )
4068 return LSMASH_ERR_INVALID_DATA;
4069 return isom_append_sample( file, trak, sample, sample_entry );
4070 }
4071
4072 /*---- misc functions ----*/
4073
lsmash_delete_explicit_timeline_map(lsmash_root_t * root,uint32_t track_ID)4074 int lsmash_delete_explicit_timeline_map( lsmash_root_t *root, uint32_t track_ID )
4075 {
4076 if( isom_check_initializer_present( root ) < 0 )
4077 return LSMASH_ERR_FUNCTION_PARAM;
4078 isom_trak_t *trak = isom_get_trak( root->file->initializer, track_ID );
4079 if( LSMASH_IS_NON_EXISTING_BOX( trak ) )
4080 return LSMASH_ERR_NAMELESS;
4081 isom_remove_box_by_itself( trak->edts );
4082 return isom_update_tkhd_duration( trak );
4083 }
4084
lsmash_delete_tyrant_chapter(lsmash_root_t * root)4085 void lsmash_delete_tyrant_chapter( lsmash_root_t *root )
4086 {
4087 if( isom_check_initializer_present( root ) < 0
4088 || LSMASH_IS_NON_EXISTING_BOX( root->file->initializer->moov->udta ) )
4089 return;
4090 isom_remove_box_by_itself( root->file->moov->udta->chpl );
4091 }
4092
lsmash_set_copyright(lsmash_root_t * root,uint32_t track_ID,uint16_t ISO_language,char * notice)4093 int lsmash_set_copyright( lsmash_root_t *root, uint32_t track_ID, uint16_t ISO_language, char *notice )
4094 {
4095 if( isom_check_initializer_present( root ) < 0
4096 || (ISO_language && ISO_language < 0x800)
4097 || !notice )
4098 return LSMASH_ERR_FUNCTION_PARAM;
4099 lsmash_file_t *file = root->file;
4100 if( !file->isom_compatible )
4101 return LSMASH_ERR_NAMELESS;
4102 isom_udta_t *udta;
4103 if( track_ID )
4104 {
4105 isom_trak_t *trak = isom_get_trak( file, track_ID );
4106 if( LSMASH_IS_NON_EXISTING_BOX( trak->udta ) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_udta( trak ) ) )
4107 return LSMASH_ERR_NAMELESS;
4108 udta = trak->udta;
4109 }
4110 else
4111 {
4112 if( LSMASH_IS_NON_EXISTING_BOX( file->moov->udta ) && LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_udta( file->moov ) ) )
4113 return LSMASH_ERR_NAMELESS;
4114 udta = file->moov->udta;
4115 }
4116 assert( LSMASH_IS_EXISTING_BOX( udta ) );
4117 for( lsmash_entry_t *entry = udta->cprt_list.head; entry; entry = entry->next )
4118 {
4119 isom_cprt_t *cprt = (isom_cprt_t *)entry->data;
4120 if( LSMASH_IS_NON_EXISTING_BOX( cprt ) || cprt->language == ISO_language )
4121 return LSMASH_ERR_NAMELESS;
4122 }
4123 if( LSMASH_IS_BOX_ADDITION_FAILURE( isom_add_cprt( udta ) ) )
4124 return LSMASH_ERR_NAMELESS;
4125 isom_cprt_t *cprt = (isom_cprt_t *)udta->cprt_list.tail->data;
4126 cprt->language = ISO_language;
4127 cprt->notice_length = strlen( notice ) + 1;
4128 cprt->notice = lsmash_memdup( notice, cprt->notice_length );
4129 return 0;
4130 }
4131