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