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