]> git.sesse.net Git - vlc/blob - modules/mux/mp4.c
mp4 mux: simplify box*new
[vlc] / modules / mux / mp4.c
1 /*****************************************************************************
2  * mp4.c: mp4/mov muxer
3  *****************************************************************************
4  * Copyright (C) 2001, 2002, 2003, 2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Gildas Bazin <gbazin at videolan dot org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_sout.h>
36 #include <vlc_block.h>
37
38 #include <time.h>
39
40 #include <vlc_iso_lang.h>
41 #include <vlc_meta.h>
42
43 /*****************************************************************************
44  * Module descriptor
45  *****************************************************************************/
46 #define FASTSTART_TEXT N_("Create \"Fast Start\" files")
47 #define FASTSTART_LONGTEXT N_( \
48     "Create \"Fast Start\" files. " \
49     "\"Fast Start\" files are optimized for downloads and allow the user " \
50     "to start previewing the file while it is downloading.")
51
52 static int  Open   ( vlc_object_t * );
53 static void Close  ( vlc_object_t * );
54
55 #define SOUT_CFG_PREFIX "sout-mp4-"
56
57 vlc_module_begin ()
58     set_description( N_("MP4/MOV muxer") )
59     set_category( CAT_SOUT )
60     set_subcategory( SUBCAT_SOUT_MUX )
61     set_shortname( "MP4" )
62
63     add_bool( SOUT_CFG_PREFIX "faststart", true,
64               FASTSTART_TEXT, FASTSTART_LONGTEXT,
65               true )
66     set_capability( "sout mux", 5 )
67     add_shortcut( "mp4", "mov", "3gp" )
68     set_callbacks( Open, Close )
69 vlc_module_end ()
70
71 /*****************************************************************************
72  * Exported prototypes
73  *****************************************************************************/
74 static const char *const ppsz_sout_options[] = {
75     "faststart", NULL
76 };
77
78 static int Control( sout_mux_t *, int, va_list );
79 static int AddStream( sout_mux_t *, sout_input_t * );
80 static int DelStream( sout_mux_t *, sout_input_t * );
81 static int Mux      ( sout_mux_t * );
82
83 /*****************************************************************************
84  * Local prototypes
85  *****************************************************************************/
86 typedef struct
87 {
88     uint64_t i_pos;
89     int      i_size;
90
91     mtime_t  i_pts_dts;
92     mtime_t  i_length;
93     unsigned int i_flags;
94
95 } mp4_entry_t;
96
97 typedef struct
98 {
99     es_format_t   fmt;
100     int           i_track_id;
101
102     /* index */
103     unsigned int i_entry_count;
104     unsigned int i_entry_max;
105     mp4_entry_t  *entry;
106     int64_t      i_length_neg;
107
108     /* stats */
109     int64_t      i_dts_start;
110     int64_t      i_duration;
111
112     /* for later stco fix-up (fast start files) */
113     uint64_t i_stco_pos;
114     bool b_stco64;
115
116     /* for spu */
117     int64_t i_last_dts;
118
119 } mp4_stream_t;
120
121 struct sout_mux_sys_t
122 {
123     bool b_mov;
124     bool b_3gp;
125     bool b_64_ext;
126     bool b_fast_start;
127
128     uint64_t i_mdat_pos;
129     uint64_t i_pos;
130
131     int64_t  i_dts_start;
132
133     int          i_nb_streams;
134     mp4_stream_t **pp_streams;
135 };
136
137 typedef struct bo_t
138 {
139     block_t    *b;
140     size_t     len;
141 } bo_t;
142
143 static void bo_init     ( bo_t * );
144 static void bo_add_8    ( bo_t *, uint8_t );
145 static void bo_add_16be ( bo_t *, uint16_t );
146 static void bo_add_24be ( bo_t *, uint32_t );
147 static void bo_add_32be ( bo_t *, uint32_t );
148 static void bo_add_64be ( bo_t *, uint64_t );
149 static void bo_add_fourcc(bo_t *, const char * );
150 static void bo_add_mem  ( bo_t *, int , uint8_t * );
151 static void bo_add_descr( bo_t *, uint8_t , uint32_t );
152
153 static void bo_fix_32be ( bo_t *, int , uint32_t );
154
155 static bo_t *box_new     ( const char *fcc );
156 static bo_t *box_full_new( const char *fcc, uint8_t v, uint32_t f );
157 static void  box_fix     ( bo_t *box );
158 static void  box_gather  ( bo_t *box, bo_t *box2 );
159
160 static void box_send( sout_mux_t *p_mux,  bo_t *box );
161
162 static bo_t *GetMoovBox( sout_mux_t *p_mux );
163
164 static block_t *ConvertSUBT( block_t *);
165 static block_t *ConvertAVC1( block_t * );
166
167 /*****************************************************************************
168  * Open:
169  *****************************************************************************/
170 static int Open( vlc_object_t *p_this )
171 {
172     sout_mux_t      *p_mux = (sout_mux_t*)p_this;
173     sout_mux_sys_t  *p_sys;
174     bo_t            *box;
175
176     msg_Dbg( p_mux, "Mp4 muxer opened" );
177     config_ChainParse( p_mux, SOUT_CFG_PREFIX, ppsz_sout_options, p_mux->p_cfg );
178
179     p_mux->pf_control   = Control;
180     p_mux->pf_addstream = AddStream;
181     p_mux->pf_delstream = DelStream;
182     p_mux->pf_mux       = Mux;
183     p_mux->p_sys        = p_sys = malloc( sizeof( sout_mux_sys_t ) );
184     if( !p_sys )
185         return VLC_ENOMEM;
186     p_sys->i_pos        = 0;
187     p_sys->i_nb_streams = 0;
188     p_sys->pp_streams   = NULL;
189     p_sys->i_mdat_pos   = 0;
190     p_sys->b_mov        = p_mux->psz_mux && !strcmp( p_mux->psz_mux, "mov" );
191     p_sys->b_3gp        = p_mux->psz_mux && !strcmp( p_mux->psz_mux, "3gp" );
192     p_sys->i_dts_start  = 0;
193
194
195     if( !p_sys->b_mov )
196     {
197         /* Now add ftyp header */
198         box = box_new( "ftyp" );
199         if( p_sys->b_3gp ) bo_add_fourcc( box, "3gp6" );
200         else bo_add_fourcc( box, "isom" );
201         bo_add_32be  ( box, 0 );
202         if( p_sys->b_3gp ) bo_add_fourcc( box, "3gp4" );
203         else bo_add_fourcc( box, "mp41" );
204         bo_add_fourcc( box, "avc1" );
205         bo_add_fourcc( box, "qt  " );
206         box_fix( box );
207
208         p_sys->i_pos += box->len;
209         p_sys->i_mdat_pos = p_sys->i_pos;
210
211         box_send( p_mux, box );
212     }
213
214     /* FIXME FIXME
215      * Quicktime actually doesn't like the 64 bits extensions !!! */
216     p_sys->b_64_ext = false;
217
218     /* Now add mdat header */
219     box = box_new( "mdat" );
220     bo_add_64be  ( box, 0 ); // enough to store an extended size
221
222     p_sys->i_pos += box->len;
223
224     box_send( p_mux, box );
225
226     return VLC_SUCCESS;
227 }
228
229 /*****************************************************************************
230  * Close:
231  *****************************************************************************/
232 static void Close( vlc_object_t * p_this )
233 {
234     sout_mux_t      *p_mux = (sout_mux_t*)p_this;
235     sout_mux_sys_t  *p_sys = p_mux->p_sys;
236     block_t   *p_hdr;
237     bo_t            bo, *moov;
238     vlc_value_t     val;
239
240     int             i_trak;
241     uint64_t        i_moov_pos;
242
243     msg_Dbg( p_mux, "Close" );
244
245     /* Update mdat size */
246     bo_init( &bo );
247     if( p_sys->i_pos - p_sys->i_mdat_pos >= (((uint64_t)1)<<32) )
248     {
249         /* Extended size */
250         bo_add_32be  ( &bo, 1 );
251         bo_add_fourcc( &bo, "mdat" );
252         bo_add_64be  ( &bo, p_sys->i_pos - p_sys->i_mdat_pos );
253     }
254     else
255     {
256         bo_add_32be  ( &bo, 8 );
257         bo_add_fourcc( &bo, "wide" );
258         bo_add_32be  ( &bo, p_sys->i_pos - p_sys->i_mdat_pos - 8 );
259         bo_add_fourcc( &bo, "mdat" );
260     }
261     p_hdr = bo.b;
262     p_hdr->i_buffer = bo.len;
263
264     sout_AccessOutSeek( p_mux->p_access, p_sys->i_mdat_pos );
265     sout_AccessOutWrite( p_mux->p_access, p_hdr );
266
267     /* Create MOOV header */
268     i_moov_pos = p_sys->i_pos;
269     moov = GetMoovBox( p_mux );
270
271     /* Check we need to create "fast start" files */
272     var_Get( p_this, SOUT_CFG_PREFIX "faststart", &val );
273     p_sys->b_fast_start = val.b_bool;
274     while( p_sys->b_fast_start )
275     {
276         /* Move data to the end of the file so we can fit the moov header
277          * at the start */
278         block_t *p_buf;
279         int64_t i_chunk, i_size = p_sys->i_pos - p_sys->i_mdat_pos;
280         int i_moov_size = moov->len;
281
282         while( i_size > 0 )
283         {
284             i_chunk = __MIN( 32768, i_size );
285             p_buf = block_Alloc( i_chunk );
286             sout_AccessOutSeek( p_mux->p_access,
287                                 p_sys->i_mdat_pos + i_size - i_chunk );
288             if( sout_AccessOutRead( p_mux->p_access, p_buf ) < i_chunk )
289             {
290                 msg_Warn( p_this, "read() not supported by access output, "
291                           "won't create a fast start file" );
292                 p_sys->b_fast_start = false;
293                 block_Release( p_buf );
294                 break;
295             }
296             sout_AccessOutSeek( p_mux->p_access, p_sys->i_mdat_pos + i_size +
297                                 i_moov_size - i_chunk );
298             sout_AccessOutWrite( p_mux->p_access, p_buf );
299             i_size -= i_chunk;
300         }
301
302         if( !p_sys->b_fast_start ) break;
303
304         /* Fix-up samples to chunks table in MOOV header */
305         for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ )
306         {
307             mp4_stream_t *p_stream = p_sys->pp_streams[i_trak];
308             unsigned int i;
309             int i_chunk;
310
311             moov->len = p_stream->i_stco_pos;
312             for( i_chunk = 0, i = 0; i < p_stream->i_entry_count; i_chunk++ )
313             {
314                 if( p_stream->b_stco64 )
315                     bo_add_64be( moov, p_stream->entry[i].i_pos + i_moov_size);
316                 else
317                     bo_add_32be( moov, p_stream->entry[i].i_pos + i_moov_size);
318
319                 while( i < p_stream->i_entry_count )
320                 {
321                     if( i + 1 < p_stream->i_entry_count &&
322                         p_stream->entry[i].i_pos + p_stream->entry[i].i_size
323                         != p_stream->entry[i + 1].i_pos )
324                     {
325                         i++;
326                         break;
327                     }
328
329                     i++;
330                 }
331             }
332         }
333
334         moov->len = i_moov_size;
335         i_moov_pos = p_sys->i_mdat_pos;
336         p_sys->b_fast_start = false;
337     }
338
339     /* Write MOOV header */
340     sout_AccessOutSeek( p_mux->p_access, i_moov_pos );
341     box_send( p_mux, moov );
342
343     /* Clean-up */
344     for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ )
345     {
346         mp4_stream_t *p_stream = p_sys->pp_streams[i_trak];
347
348         es_format_Clean( &p_stream->fmt );
349         free( p_stream->entry );
350         free( p_stream );
351     }
352     if( p_sys->i_nb_streams ) free( p_sys->pp_streams );
353     free( p_sys );
354 }
355
356 /*****************************************************************************
357  * Control:
358  *****************************************************************************/
359 static int Control( sout_mux_t *p_mux, int i_query, va_list args )
360 {
361     VLC_UNUSED(p_mux);
362     bool *pb_bool;
363
364     switch( i_query )
365     {
366         case MUX_CAN_ADD_STREAM_WHILE_MUXING:
367             pb_bool = (bool*)va_arg( args, bool * );
368             *pb_bool = false;
369             return VLC_SUCCESS;
370
371         case MUX_GET_ADD_STREAM_WAIT:
372             pb_bool = (bool*)va_arg( args, bool * );
373             *pb_bool = true;
374             return VLC_SUCCESS;
375
376         case MUX_GET_MIME:   /* Not needed, as not streamable */
377         default:
378             return VLC_EGENERIC;
379     }
380 }
381
382 /*****************************************************************************
383  * AddStream:
384  *****************************************************************************/
385 static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
386 {
387     sout_mux_sys_t  *p_sys = p_mux->p_sys;
388     mp4_stream_t    *p_stream;
389
390     switch( p_input->p_fmt->i_codec )
391     {
392         case VLC_CODEC_MP4A:
393         case VLC_CODEC_MP4V:
394         case VLC_CODEC_MPGA:
395         case VLC_CODEC_MPGV:
396         case VLC_CODEC_MJPG:
397         case VLC_CODEC_MJPGB:
398         case VLC_CODEC_SVQ1:
399         case VLC_CODEC_SVQ3:
400         case VLC_CODEC_H263:
401         case VLC_CODEC_H264:
402         case VLC_CODEC_AMR_NB:
403         case VLC_CODEC_AMR_WB:
404         case VLC_CODEC_YV12:
405         case VLC_CODEC_YUYV:
406             break;
407         case VLC_CODEC_SUBT:
408             msg_Warn( p_mux, "subtitle track added like in .mov (even when creating .mp4)" );
409             break;
410         default:
411             msg_Err( p_mux, "unsupported codec %4.4s in mp4",
412                      (char*)&p_input->p_fmt->i_codec );
413             return VLC_EGENERIC;
414     }
415
416     p_stream                = malloc( sizeof( mp4_stream_t ) );
417     if( !p_stream )
418         return VLC_ENOMEM;
419     es_format_Copy( &p_stream->fmt, p_input->p_fmt );
420     p_stream->i_track_id    = p_sys->i_nb_streams + 1;
421     p_stream->i_length_neg  = 0;
422     p_stream->i_entry_count = 0;
423     p_stream->i_entry_max   = 1000;
424     p_stream->entry         =
425         calloc( p_stream->i_entry_max, sizeof( mp4_entry_t ) );
426     p_stream->i_dts_start   = 0;
427     p_stream->i_duration    = 0;
428
429     p_input->p_sys          = p_stream;
430
431     msg_Dbg( p_mux, "adding input" );
432
433     TAB_APPEND( p_sys->i_nb_streams, p_sys->pp_streams, p_stream );
434     return VLC_SUCCESS;
435 }
436
437 /*****************************************************************************
438  * DelStream:
439  *****************************************************************************/
440 static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
441 {
442     VLC_UNUSED(p_input);
443     msg_Dbg( p_mux, "removing input" );
444     return VLC_SUCCESS;
445 }
446
447 /*****************************************************************************
448  * Mux:
449  *****************************************************************************/
450 static int Mux( sout_mux_t *p_mux )
451 {
452     sout_mux_sys_t *p_sys = p_mux->p_sys;
453
454     for( ;; )
455     {
456         sout_input_t    *p_input;
457         mp4_stream_t    *p_stream;
458         block_t         *p_data;
459         mtime_t         i_dts;
460
461         int i_stream = sout_MuxGetStream( p_mux, 2, &i_dts);
462         if( i_stream < 0 )
463         {
464             return( VLC_SUCCESS );
465         }
466
467         p_input  = p_mux->pp_inputs[i_stream];
468         p_stream = (mp4_stream_t*)p_input->p_sys;
469
470 again:
471         p_data  = block_FifoGet( p_input->p_fifo );
472         if( p_stream->fmt.i_codec == VLC_CODEC_H264 )
473         {
474             p_data = ConvertAVC1( p_data );
475         }
476         else if( p_stream->fmt.i_codec == VLC_CODEC_SUBT )
477         {
478             p_data = ConvertSUBT( p_data );
479         }
480         if( p_data == NULL ) goto again;
481
482         if( p_stream->fmt.i_cat != SPU_ES )
483         {
484             /* Fix length of the sample */
485             if( block_FifoCount( p_input->p_fifo ) > 0 )
486             {
487                 block_t *p_next = block_FifoShow( p_input->p_fifo );
488                 int64_t       i_diff  = p_next->i_dts - p_data->i_dts;
489
490                 if( i_diff < INT64_C(1000000 ) )   /* protection */
491                 {
492                     p_data->i_length = i_diff;
493                 }
494             }
495             if( p_data->i_length <= 0 )
496             {
497                 msg_Warn( p_mux, "i_length <= 0" );
498                 p_stream->i_length_neg += p_data->i_length - 1;
499                 p_data->i_length = 1;
500             }
501             else if( p_stream->i_length_neg < 0 )
502             {
503                 int64_t i_recover = __MIN( p_data->i_length / 4, - p_stream->i_length_neg );
504
505                 p_data->i_length -= i_recover;
506                 p_stream->i_length_neg += i_recover;
507             }
508         }
509
510         /* Save starting time */
511         if( p_stream->i_entry_count == 0 )
512         {
513             p_stream->i_dts_start = p_data->i_dts;
514
515             /* Update global dts_start */
516             if( p_sys->i_dts_start <= 0 ||
517                 p_stream->i_dts_start < p_sys->i_dts_start )
518             {
519                 p_sys->i_dts_start = p_stream->i_dts_start;
520             }
521         }
522
523         if( p_stream->fmt.i_cat == SPU_ES && p_stream->i_entry_count > 0 )
524         {
525             int64_t i_length = p_data->i_dts - p_stream->i_last_dts;
526
527             if( i_length <= 0 )
528             {
529                 /* FIXME handle this broken case */
530                 i_length = 1;
531             }
532
533             /* Fix last entry */
534             if( p_stream->entry[p_stream->i_entry_count-1].i_length <= 0 )
535             {
536                 p_stream->entry[p_stream->i_entry_count-1].i_length = i_length;
537             }
538         }
539
540
541         /* add index entry */
542         p_stream->entry[p_stream->i_entry_count].i_pos    = p_sys->i_pos;
543         p_stream->entry[p_stream->i_entry_count].i_size   = p_data->i_buffer;
544         p_stream->entry[p_stream->i_entry_count].i_pts_dts=
545             __MAX( p_data->i_pts - p_data->i_dts, 0 );
546         p_stream->entry[p_stream->i_entry_count].i_length = p_data->i_length;
547         p_stream->entry[p_stream->i_entry_count].i_flags  = p_data->i_flags;
548
549         p_stream->i_entry_count++;
550         /* XXX: -1 to always have 2 entry for easy adding of empty SPU */
551         if( p_stream->i_entry_count >= p_stream->i_entry_max - 1 )
552         {
553             p_stream->i_entry_max += 1000;
554             p_stream->entry = xrealloc( p_stream->entry,
555                          p_stream->i_entry_max * sizeof( mp4_entry_t ) );
556         }
557
558         /* update */
559         p_stream->i_duration = p_stream->i_last_dts - p_stream->i_dts_start + p_data->i_length;
560         p_sys->i_pos += p_data->i_buffer;
561
562         /* Save the DTS */
563         p_stream->i_last_dts = p_data->i_dts;
564
565         /* write data */
566         sout_AccessOutWrite( p_mux->p_access, p_data );
567
568         if( p_stream->fmt.i_cat == SPU_ES )
569         {
570             int64_t i_length = p_stream->entry[p_stream->i_entry_count-1].i_length;
571
572             if( i_length != 0 )
573             {
574                 /* TODO */
575                 msg_Dbg( p_mux, "writing an empty sub" ) ;
576
577                 /* Append a idx entry */
578                 p_stream->entry[p_stream->i_entry_count].i_pos    = p_sys->i_pos;
579                 p_stream->entry[p_stream->i_entry_count].i_size   = 3;
580                 p_stream->entry[p_stream->i_entry_count].i_pts_dts= 0;
581                 p_stream->entry[p_stream->i_entry_count].i_length = 0;
582                 p_stream->entry[p_stream->i_entry_count].i_flags  = 0;
583
584                 /* XXX: No need to grow the entry here */
585                 p_stream->i_entry_count++;
586
587                 /* Fix last dts */
588                 p_stream->i_last_dts += i_length;
589
590                 /* Write a " " */
591                 p_data = block_Alloc( 3 );
592                 p_data->p_buffer[0] = 0;
593                 p_data->p_buffer[1] = 1;
594                 p_data->p_buffer[2] = ' ';
595
596                 p_sys->i_pos += p_data->i_buffer;
597
598                 sout_AccessOutWrite( p_mux->p_access, p_data );
599             }
600
601             /* Fix duration */
602             p_stream->i_duration = p_stream->i_last_dts - p_stream->i_dts_start;
603         }
604     }
605
606     return( VLC_SUCCESS );
607 }
608
609 /*****************************************************************************
610  *
611  *****************************************************************************/
612 static block_t *ConvertSUBT( block_t *p_block )
613 {
614     p_block = block_Realloc( p_block, 2, p_block->i_buffer );
615
616     /* No trailling '\0' */
617     if( p_block->i_buffer > 2 && p_block->p_buffer[p_block->i_buffer-1] == '\0' )
618         p_block->i_buffer--;
619
620     p_block->p_buffer[0] = ( (p_block->i_buffer - 2) >> 8 )&0xff;
621     p_block->p_buffer[1] = ( (p_block->i_buffer - 2)      )&0xff;
622
623     return p_block;
624 }
625
626 static block_t *ConvertAVC1( block_t *p_block )
627 {
628     uint8_t *last = p_block->p_buffer;  /* Assume it starts with 0x00000001 */
629     uint8_t *dat  = &p_block->p_buffer[4];
630     uint8_t *end = &p_block->p_buffer[p_block->i_buffer];
631
632
633     /* Replace the 4 bytes start code with 4 bytes size,
634      * FIXME are all startcodes 4 bytes ? (I don't think :( */
635     while( dat < end )
636     {
637         int i_size;
638
639         while( dat < end - 4 )
640         {
641             if( dat[0] == 0x00 && dat[1] == 0x00  &&
642                 dat[2] == 0x00 && dat[3] == 0x01 )
643             {
644                 break;
645             }
646             dat++;
647         }
648         if( dat >= end - 4 )
649         {
650             dat = end;
651         }
652
653         /* Fix size */
654         i_size = dat - &last[4];
655         last[0] = ( i_size >> 24 )&0xff;
656         last[1] = ( i_size >> 16 )&0xff;
657         last[2] = ( i_size >>  8 )&0xff;
658         last[3] = ( i_size       )&0xff;
659
660         /* Skip blocks with SPS/PPS */
661         if( (last[4]&0x1f) == 7 || (last[4]&0x1f) == 8 )
662         {
663             ; // FIXME Find a way to skip dat without frelling everything
664         }
665         last = dat;
666         dat += 4;
667     }
668     return p_block;
669 }
670
671 static bo_t *GetESDS( mp4_stream_t *p_stream )
672 {
673     bo_t *esds;
674     int  i_stream_type;
675     int  i_object_type_indication;
676     int  i_decoder_specific_info_size;
677     unsigned int i;
678     int64_t i_bitrate_avg = 0;
679     int64_t i_bitrate_max = 0;
680
681     /* Compute avg/max bitrate */
682     for( i = 0; i < p_stream->i_entry_count; i++ )
683     {
684         i_bitrate_avg += p_stream->entry[i].i_size;
685         if( p_stream->entry[i].i_length > 0)
686         {
687             int64_t i_bitrate = INT64_C(8000000) * p_stream->entry[i].i_size / p_stream->entry[i].i_length;
688             if( i_bitrate > i_bitrate_max )
689                 i_bitrate_max = i_bitrate;
690         }
691     }
692
693     if( p_stream->i_duration > 0 )
694         i_bitrate_avg = INT64_C(8000000) * i_bitrate_avg / p_stream->i_duration;
695     else
696         i_bitrate_avg = 0;
697     if( i_bitrate_max <= 1 )
698         i_bitrate_max = 0x7fffffff;
699
700     /* */
701     i_decoder_specific_info_size = ( p_stream->fmt.i_extra > 0 ) ? 5 + p_stream->fmt.i_extra : 0;
702
703     esds = box_full_new( "esds", 0, 0 );
704
705     /* ES_Descr */
706     bo_add_descr( esds, 0x03, 3 + 5 + 13 + i_decoder_specific_info_size + 5 + 1 );
707     bo_add_16be( esds, p_stream->i_track_id );
708     bo_add_8   ( esds, 0x1f );      // flags=0|streamPriority=0x1f
709
710     /* DecoderConfigDescr */
711     bo_add_descr( esds, 0x04, 13 + i_decoder_specific_info_size );
712
713     switch( p_stream->fmt.i_codec )
714     {
715         case VLC_CODEC_MP4V:
716             i_object_type_indication = 0x20;
717             break;
718         case VLC_CODEC_MPGV:
719             /* FIXME MPEG-I=0x6b, MPEG-II = 0x60 -> 0x65 */
720             i_object_type_indication = 0x60;
721             break;
722         case VLC_CODEC_MP4A:
723             /* FIXME for mpeg2-aac == 0x66->0x68 */
724             i_object_type_indication = 0x40;
725             break;
726         case VLC_CODEC_MPGA:
727             i_object_type_indication =
728                 p_stream->fmt.audio.i_rate < 32000 ? 0x69 : 0x6b;
729             break;
730         default:
731             i_object_type_indication = 0x00;
732             break;
733     }
734     i_stream_type = p_stream->fmt.i_cat == VIDEO_ES ? 0x04 : 0x05;
735
736     bo_add_8   ( esds, i_object_type_indication );
737     bo_add_8   ( esds, ( i_stream_type << 2 ) | 1 );
738     bo_add_24be( esds, 1024 * 1024 );       // bufferSizeDB
739     bo_add_32be( esds, i_bitrate_max );     // maxBitrate
740     bo_add_32be( esds, i_bitrate_avg );     // avgBitrate
741
742     if( p_stream->fmt.i_extra > 0 )
743     {
744         int i;
745
746         /* DecoderSpecificInfo */
747         bo_add_descr( esds, 0x05, p_stream->fmt.i_extra );
748
749         for( i = 0; i < p_stream->fmt.i_extra; i++ )
750         {
751             bo_add_8( esds, ((uint8_t*)p_stream->fmt.p_extra)[i] );
752         }
753     }
754
755     /* SL_Descr mandatory */
756     bo_add_descr( esds, 0x06, 1 );
757     bo_add_8    ( esds, 0x02 );  // sl_predefined
758
759     box_fix( esds );
760
761     return esds;
762 }
763
764 static bo_t *GetWaveTag( mp4_stream_t *p_stream )
765 {
766     bo_t *wave;
767     bo_t *box;
768
769     wave = box_new( "wave" );
770
771     box = box_new( "frma" );
772     bo_add_fourcc( box, "mp4a" );
773     box_fix( box );
774     box_gather( wave, box );
775
776     box = box_new( "mp4a" );
777     bo_add_32be( box, 0 );
778     box_fix( box );
779     box_gather( wave, box );
780
781     box = GetESDS( p_stream );
782     box_fix( box );
783     box_gather( wave, box );
784
785     box = box_new( "srcq" );
786     bo_add_32be( box, 0x40 );
787     box_fix( box );
788     box_gather( wave, box );
789
790     /* wazza ? */
791     bo_add_32be( wave, 8 ); /* new empty box */
792     bo_add_32be( wave, 0 ); /* box label */
793
794     box_fix( wave );
795
796     return wave;
797 }
798
799 static bo_t *GetDamrTag( mp4_stream_t *p_stream )
800 {
801     bo_t *damr;
802
803     damr = box_new( "damr" );
804
805     bo_add_fourcc( damr, "REFC" );
806     bo_add_8( damr, 0 );
807
808     if( p_stream->fmt.i_codec == VLC_CODEC_AMR_NB )
809         bo_add_16be( damr, 0x81ff ); /* Mode set (all modes for AMR_NB) */
810     else
811         bo_add_16be( damr, 0x83ff ); /* Mode set (all modes for AMR_WB) */
812     bo_add_16be( damr, 0x1 ); /* Mode change period (no restriction) */
813
814     box_fix( damr );
815
816     return damr;
817 }
818
819 static bo_t *GetD263Tag( void )
820 {
821     bo_t *d263;
822
823     d263 = box_new( "d263" );
824
825     bo_add_fourcc( d263, "VLC " );
826     bo_add_16be( d263, 0xa );
827     bo_add_8( d263, 0 );
828
829     box_fix( d263 );
830
831     return d263;
832 }
833
834 static bo_t *GetAvcCTag( mp4_stream_t *p_stream )
835 {
836     bo_t    *avcC = NULL;
837     uint8_t *p_sps = NULL;
838     uint8_t *p_pps = NULL;
839     int     i_sps_size = 0;
840     int     i_pps_size = 0;
841
842     if( p_stream->fmt.i_extra > 0 )
843     {
844         /* FIXME: take into account multiple sps/pps */
845         uint8_t *p_buffer = p_stream->fmt.p_extra;
846         int     i_buffer = p_stream->fmt.i_extra;
847
848         while( i_buffer > 3 )
849         {
850             /* seek startcode */
851             while( p_buffer[0] != 0 || p_buffer[1] != 0 ||
852                    p_buffer[2] != 1 )
853             {
854                  i_buffer--;
855                  p_buffer++;
856             }
857             const int i_nal_type = p_buffer[3]&0x1f;
858             int i_offset    = 1;
859             int i_size      = 0;
860             int i_startcode = 0;
861  
862  
863             for( i_offset = 1; i_offset+2 < i_buffer ; i_offset++)
864             {
865                 if( p_buffer[i_offset] == 0 && p_buffer[i_offset+1] == 0 &&
866                     p_buffer[i_offset+2] == 1 )
867                 {
868                     /* we found another startcode */
869                     i_startcode = i_offset;
870                     while( p_buffer[i_startcode-1] == 0 && i_startcode > 0 )
871                         i_startcode--;
872                     break;
873                 }
874             }
875             i_size = i_startcode ? i_startcode : i_buffer;
876
877             if( i_nal_type == 7 )
878             {
879                 p_sps = &p_buffer[3];
880                 i_sps_size = i_size - 3;
881             }
882             if( i_nal_type == 8 )
883             {
884                 p_pps = &p_buffer[3];
885                 i_pps_size = i_size - 3;
886             }
887             i_buffer -= i_size;
888             p_buffer += i_size;
889         }
890     }
891  
892     /* FIXME use better value */
893     avcC = box_new( "avcC" );
894     bo_add_8( avcC, 1 );      /* configuration version */
895     bo_add_8( avcC, i_sps_size ? p_sps[1] : 77 );
896     bo_add_8( avcC, i_sps_size ? p_sps[2] : 64 );
897     bo_add_8( avcC, i_sps_size ? p_sps[3] : 30 );       /* level, 5.1 */
898     bo_add_8( avcC, 0xff );   /* 0b11111100 | lengthsize = 0x11 */
899
900     bo_add_8( avcC, 0xe0 | (i_sps_size > 0 ? 1 : 0) );   /* 0b11100000 | sps_count */
901     if( i_sps_size > 0 )
902     {
903         bo_add_16be( avcC, i_sps_size );
904         bo_add_mem( avcC, i_sps_size, p_sps );
905     }
906
907     bo_add_8( avcC, (i_pps_size > 0 ? 1 : 0) );   /* pps_count */
908     if( i_pps_size > 0 )
909     {
910         bo_add_16be( avcC, i_pps_size );
911         bo_add_mem( avcC, i_pps_size, p_pps );
912     }
913     box_fix( avcC );
914
915     return avcC;
916 }
917
918 /* TODO: No idea about these values */
919 static bo_t *GetSVQ3Tag( mp4_stream_t *p_stream )
920 {
921     bo_t *smi = box_new( "SMI " );
922
923     if( p_stream->fmt.i_extra > 0x4e )
924     {
925         uint8_t *p_end = &((uint8_t*)p_stream->fmt.p_extra)[p_stream->fmt.i_extra];
926         uint8_t *p     = &((uint8_t*)p_stream->fmt.p_extra)[0x46];
927
928         while( p + 8 < p_end )
929         {
930             int i_size = GetDWBE( p );
931             if( i_size <= 1 )
932             {
933                 /* FIXME handle 1 as long size */
934                 break;
935             }
936             if( !strncmp( (const char *)&p[4], "SMI ", 4 ) )
937             {
938                 bo_add_mem( smi, p_end - p - 8, &p[8] );
939                 return smi;
940             }
941             p += i_size;
942         }
943     }
944
945     /* Create a dummy one in fallback */
946     bo_add_fourcc( smi, "SEQH" );
947     bo_add_32be( smi, 0x5 );
948     bo_add_32be( smi, 0xe2c0211d );
949     bo_add_8( smi, 0xc0 );
950     box_fix( smi );
951
952     return smi;
953 }
954
955 static bo_t *GetUdtaTag( sout_mux_t *p_mux )
956 {
957     sout_mux_sys_t *p_sys = p_mux->p_sys;
958     bo_t *udta = box_new( "udta" );
959     int i_track;
960
961     /* Requirements */
962     for( i_track = 0; i_track < p_sys->i_nb_streams; i_track++ )
963     {
964         mp4_stream_t *p_stream = p_sys->pp_streams[i_track];
965
966         if( p_stream->fmt.i_codec == VLC_CODEC_MP4V ||
967             p_stream->fmt.i_codec == VLC_CODEC_MP4A )
968         {
969             bo_t *box = box_new( "\251req" );
970             /* String length */
971             bo_add_16be( box, sizeof("QuickTime 6.0 or greater") - 1);
972             bo_add_16be( box, 0 );
973             bo_add_mem( box, sizeof("QuickTime 6.0 or greater") - 1,
974                         (uint8_t *)"QuickTime 6.0 or greater" );
975             box_fix( box );
976             box_gather( udta, box );
977             break;
978         }
979     }
980
981     /* Encoder */
982     {
983         bo_t *box = box_new( "\251enc" );
984         /* String length */
985         bo_add_16be( box, sizeof(PACKAGE_STRING " stream output") - 1);
986         bo_add_16be( box, 0 );
987         bo_add_mem( box, sizeof(PACKAGE_STRING " stream output") - 1,
988                     (uint8_t*)PACKAGE_STRING " stream output" );
989         box_fix( box );
990         box_gather( udta, box );
991     }
992 #if 0
993     /* Misc atoms */
994     vlc_meta_t *p_meta = p_mux->p_sout->p_meta;
995     if( p_meta )
996     {
997 #define ADD_META_BOX( type, box_string ) { \
998         bo_t *box = NULL;  \
999         if( vlc_meta_Get( p_meta, vlc_meta_##type ) ) box = box_new( "\251" box_string ); \
1000         if( box ) \
1001         { \
1002             bo_add_16be( box, strlen( vlc_meta_Get( p_meta, vlc_meta_##type ) )); \
1003             bo_add_16be( box, 0 ); \
1004             bo_add_mem( box, strlen( vlc_meta_Get( p_meta, vlc_meta_##type ) ), \
1005                         (uint8_t*)(vlc_meta_Get( p_meta, vlc_meta_##type ) ) ); \
1006             box_fix( box ); \
1007             box_gather( udta, box ); \
1008         } }
1009
1010         ADD_META_BOX( Title, "nam" );
1011         ADD_META_BOX( Artist, "ART" );
1012         ADD_META_BOX( Genre, "gen" );
1013         ADD_META_BOX( Copyright, "cpy" );
1014         ADD_META_BOX( Description, "des" );
1015         ADD_META_BOX( Date, "day" );
1016         ADD_META_BOX( URL, "url" );
1017 #undef ADD_META_BOX
1018     }
1019 #endif
1020     box_fix( udta );
1021     return udta;
1022 }
1023
1024 static bo_t *GetSounBox( sout_mux_t *p_mux, mp4_stream_t *p_stream )
1025 {
1026     sout_mux_sys_t *p_sys = p_mux->p_sys;
1027     bool b_descr = false;
1028     bo_t *soun;
1029     char fcc[4] = "    ";
1030     int  i;
1031
1032     switch( p_stream->fmt.i_codec )
1033     {
1034     case VLC_CODEC_MP4A:
1035         memcpy( fcc, "mp4a", 4 );
1036         b_descr = true;
1037         break;
1038
1039     case VLC_CODEC_AMR_NB:
1040     case VLC_CODEC_AMR_WB:
1041         memcpy( fcc, (char*)&p_stream->fmt.i_codec, 4 );
1042         b_descr = true;
1043         break;
1044
1045     case VLC_CODEC_MPGA:
1046         if( p_sys->b_mov )
1047             memcpy( fcc, ".mp3", 4 );
1048         else
1049         {
1050             memcpy( fcc, "mp4a", 4 );
1051             b_descr = true;
1052         }
1053         break;
1054
1055     default:
1056         memcpy( fcc, (char*)&p_stream->fmt.i_codec, 4 );
1057         break;
1058     }
1059
1060     soun = box_new( fcc );
1061     for( i = 0; i < 6; i++ )
1062     {
1063         bo_add_8( soun, 0 );        // reserved;
1064     }
1065     bo_add_16be( soun, 1 );         // data-reference-index
1066
1067     /* SoundDescription */
1068     if( p_sys->b_mov &&
1069         p_stream->fmt.i_codec == VLC_CODEC_MP4A )
1070     {
1071         bo_add_16be( soun, 1 );     // version 1;
1072     }
1073     else
1074     {
1075         bo_add_16be( soun, 0 );     // version 0;
1076     }
1077     bo_add_16be( soun, 0 );         // revision level (0)
1078     bo_add_32be( soun, 0 );         // vendor
1079     // channel-count
1080     bo_add_16be( soun, p_stream->fmt.audio.i_channels );
1081     // sample size
1082     bo_add_16be( soun, p_stream->fmt.audio.i_bitspersample ?
1083                  p_stream->fmt.audio.i_bitspersample : 16 );
1084     bo_add_16be( soun, -2 );        // compression id
1085     bo_add_16be( soun, 0 );         // packet size (0)
1086     bo_add_16be( soun, p_stream->fmt.audio.i_rate ); // sampleratehi
1087     bo_add_16be( soun, 0 );                             // sampleratelo
1088
1089     /* Extended data for SoundDescription V1 */
1090     if( p_sys->b_mov &&
1091         p_stream->fmt.i_codec == VLC_CODEC_MP4A )
1092     {
1093         /* samples per packet */
1094         bo_add_32be( soun, p_stream->fmt.audio.i_frame_length );
1095         bo_add_32be( soun, 1536 ); /* bytes per packet */
1096         bo_add_32be( soun, 2 );    /* bytes per frame */
1097         /* bytes per sample */
1098         bo_add_32be( soun, 2 /*p_stream->fmt.audio.i_bitspersample/8 */);
1099     }
1100
1101     /* Add an ES Descriptor */
1102     if( b_descr )
1103     {
1104         bo_t *box;
1105
1106         if( p_sys->b_mov &&
1107             p_stream->fmt.i_codec == VLC_CODEC_MP4A )
1108         {
1109             box = GetWaveTag( p_stream );
1110         }
1111         else if( p_stream->fmt.i_codec == VLC_CODEC_AMR_NB )
1112         {
1113             box = GetDamrTag( p_stream );
1114         }
1115         else
1116         {
1117             box = GetESDS( p_stream );
1118         }
1119         box_fix( box );
1120         box_gather( soun, box );
1121     }
1122
1123     box_fix( soun );
1124
1125     return soun;
1126 }
1127
1128 static bo_t *GetVideBox( mp4_stream_t *p_stream )
1129 {
1130
1131     bo_t *vide;
1132     char fcc[4] = "    ";
1133     int  i;
1134
1135     switch( p_stream->fmt.i_codec )
1136     {
1137     case VLC_CODEC_MP4V:
1138     case VLC_CODEC_MPGV:
1139         memcpy( fcc, "mp4v", 4 );
1140         break;
1141
1142     case VLC_CODEC_MJPG:
1143         memcpy( fcc, "mjpa", 4 );
1144         break;
1145
1146     case VLC_CODEC_SVQ1:
1147         memcpy( fcc, "SVQ1", 4 );
1148         break;
1149
1150     case VLC_CODEC_SVQ3:
1151         memcpy( fcc, "SVQ3", 4 );
1152         break;
1153
1154     case VLC_CODEC_H263:
1155         memcpy( fcc, "s263", 4 );
1156         break;
1157
1158     case VLC_CODEC_H264:
1159         memcpy( fcc, "avc1", 4 );
1160         break;
1161
1162     case VLC_CODEC_YV12:
1163         memcpy( fcc, "yv12", 4 );
1164         break;
1165
1166     case VLC_CODEC_YUYV:
1167         memcpy( fcc, "yuy2", 4 );
1168         break;
1169
1170     default:
1171         memcpy( fcc, (char*)&p_stream->fmt.i_codec, 4 );
1172         break;
1173     }
1174
1175     vide = box_new( fcc );
1176     for( i = 0; i < 6; i++ )
1177     {
1178         bo_add_8( vide, 0 );        // reserved;
1179     }
1180     bo_add_16be( vide, 1 );         // data-reference-index
1181
1182     bo_add_16be( vide, 0 );         // predefined;
1183     bo_add_16be( vide, 0 );         // reserved;
1184     for( i = 0; i < 3; i++ )
1185     {
1186         bo_add_32be( vide, 0 );     // predefined;
1187     }
1188
1189     bo_add_16be( vide, p_stream->fmt.video.i_width );  // i_width
1190     bo_add_16be( vide, p_stream->fmt.video.i_height ); // i_height
1191
1192     bo_add_32be( vide, 0x00480000 );                // h 72dpi
1193     bo_add_32be( vide, 0x00480000 );                // v 72dpi
1194
1195     bo_add_32be( vide, 0 );         // data size, always 0
1196     bo_add_16be( vide, 1 );         // frames count per sample
1197
1198     // compressor name;
1199     for( i = 0; i < 32; i++ )
1200     {
1201         bo_add_8( vide, 0 );
1202     }
1203
1204     bo_add_16be( vide, 0x18 );      // depth
1205     bo_add_16be( vide, 0xffff );    // predefined
1206
1207     /* add an ES Descriptor */
1208     switch( p_stream->fmt.i_codec )
1209     {
1210     case VLC_CODEC_MP4V:
1211     case VLC_CODEC_MPGV:
1212         {
1213             bo_t *esds = GetESDS( p_stream );
1214
1215             box_fix( esds );
1216             box_gather( vide, esds );
1217         }
1218         break;
1219
1220     case VLC_CODEC_H263:
1221         {
1222             bo_t *d263 = GetD263Tag();
1223
1224             box_fix( d263 );
1225             box_gather( vide, d263 );
1226         }
1227         break;
1228
1229     case VLC_CODEC_SVQ3:
1230         {
1231             bo_t *esds = GetSVQ3Tag( p_stream );
1232
1233             box_fix( esds );
1234             box_gather( vide, esds );
1235         }
1236         break;
1237
1238     case VLC_CODEC_H264:
1239         box_gather( vide, GetAvcCTag( p_stream ) );
1240         break;
1241
1242     default:
1243         break;
1244     }
1245
1246     box_fix( vide );
1247
1248     return vide;
1249 }
1250
1251 static bo_t *GetTextBox( void )
1252 {
1253
1254     bo_t *text = box_new( "text" );
1255     int  i;
1256
1257     for( i = 0; i < 6; i++ )
1258     {
1259         bo_add_8( text, 0 );        // reserved;
1260     }
1261     bo_add_16be( text, 1 );         // data-reference-index
1262
1263     bo_add_32be( text, 0 );         // display flags
1264     bo_add_32be( text, 0 );         // justification
1265     for( i = 0; i < 3; i++ )
1266     {
1267         bo_add_16be( text, 0 );     // back ground color
1268     }
1269
1270     bo_add_16be( text, 0 );         // box text
1271     bo_add_16be( text, 0 );         // box text
1272     bo_add_16be( text, 0 );         // box text
1273     bo_add_16be( text, 0 );         // box text
1274
1275     bo_add_64be( text, 0 );         // reserved
1276     for( i = 0; i < 3; i++ )
1277     {
1278         bo_add_16be( text, 0xff );  // foreground color
1279     }
1280
1281     bo_add_8 ( text, 9 );
1282     bo_add_mem( text, 9, (uint8_t*)"Helvetica" );
1283
1284     box_fix( text );
1285
1286     return text;
1287 }
1288
1289 static bo_t *GetStblBox( sout_mux_t *p_mux, mp4_stream_t *p_stream )
1290 {
1291     sout_mux_sys_t *p_sys = p_mux->p_sys;
1292     unsigned int i_chunk, i_stsc_last_val, i_stsc_entries, i, i_index;
1293     bo_t *stbl, *stsd, *stts, *stco, *stsc, *stsz, *stss;
1294     uint32_t i_timescale;
1295     int64_t i_dts, i_dts_q;
1296
1297     stbl = box_new( "stbl" );
1298
1299     /* sample description */
1300     stsd = box_full_new( "stsd", 0, 0 );
1301     bo_add_32be( stsd, 1 );
1302     if( p_stream->fmt.i_cat == AUDIO_ES )
1303     {
1304         bo_t *soun = GetSounBox( p_mux, p_stream );
1305         box_gather( stsd, soun );
1306     }
1307     else if( p_stream->fmt.i_cat == VIDEO_ES )
1308     {
1309         bo_t *vide = GetVideBox( p_stream );
1310         box_gather( stsd, vide );
1311     }
1312     else if( p_stream->fmt.i_cat == SPU_ES )
1313     {
1314         box_gather( stsd, GetTextBox() );
1315     }
1316     box_fix( stsd );
1317
1318     /* chunk offset table */
1319     if( p_sys->i_pos >= (((uint64_t)0x1) << 32) )
1320     {
1321         /* 64 bits version */
1322         p_stream->b_stco64 = true;
1323         stco = box_full_new( "co64", 0, 0 );
1324     }
1325     else
1326     {
1327         /* 32 bits version */
1328         p_stream->b_stco64 = false;
1329         stco = box_full_new( "stco", 0, 0 );
1330     }
1331     bo_add_32be( stco, 0 );     // entry-count (fixed latter)
1332
1333     /* sample to chunk table */
1334     stsc = box_full_new( "stsc", 0, 0 );
1335     bo_add_32be( stsc, 0 );     // entry-count (fixed latter)
1336
1337     for( i_chunk = 0, i_stsc_last_val = 0, i_stsc_entries = 0, i = 0;
1338          i < p_stream->i_entry_count; i_chunk++ )
1339     {
1340         int i_first = i;
1341
1342         if( p_stream->b_stco64 )
1343             bo_add_64be( stco, p_stream->entry[i].i_pos );
1344         else
1345             bo_add_32be( stco, p_stream->entry[i].i_pos );
1346
1347         while( i < p_stream->i_entry_count )
1348         {
1349             if( i + 1 < p_stream->i_entry_count &&
1350                 p_stream->entry[i].i_pos + p_stream->entry[i].i_size
1351                 != p_stream->entry[i + 1].i_pos )
1352             {
1353                 i++;
1354                 break;
1355             }
1356
1357             i++;
1358         }
1359
1360         /* Add entry to the stsc table */
1361         if( i_stsc_last_val != i - i_first )
1362         {
1363             bo_add_32be( stsc, 1 + i_chunk );   // first-chunk
1364             bo_add_32be( stsc, i - i_first ) ;  // samples-per-chunk
1365             bo_add_32be( stsc, 1 );             // sample-descr-index
1366             i_stsc_last_val = i - i_first;
1367             i_stsc_entries++;
1368         }
1369     }
1370
1371     /* Fix stco entry count */
1372     bo_fix_32be( stco, 12, i_chunk );
1373     msg_Dbg( p_mux, "created %d chunks (stco)", i_chunk );
1374     box_fix( stco );
1375
1376     /* Fix stsc entry count */
1377     bo_fix_32be( stsc, 12, i_stsc_entries  );
1378     box_fix( stsc );
1379
1380     /* add stts */
1381     stts = box_full_new( "stts", 0, 0 );
1382     bo_add_32be( stts, 0 );     // entry-count (fixed latter)
1383
1384     if( p_stream->fmt.i_cat == AUDIO_ES )
1385         i_timescale = p_stream->fmt.audio.i_rate;
1386     else
1387         i_timescale = 1001;
1388
1389     /* first, create quantified length */
1390     for( i = 0, i_dts = 0, i_dts_q = 0; i < p_stream->i_entry_count; i++ )
1391     {
1392         int64_t i_dts_deq = i_dts_q * INT64_C(1000000) / (int64_t)i_timescale;
1393         int64_t i_delta = p_stream->entry[i].i_length + i_dts - i_dts_deq;
1394
1395         i_dts += p_stream->entry[i].i_length;
1396
1397         p_stream->entry[i].i_length =
1398             i_delta * (int64_t)i_timescale / INT64_C(1000000);
1399
1400         i_dts_q += p_stream->entry[i].i_length;
1401     }
1402     /* then write encoded table */
1403     for( i = 0, i_index = 0; i < p_stream->i_entry_count; i_index++)
1404     {
1405         int     i_first = i;
1406         int64_t i_delta = p_stream->entry[i].i_length;
1407
1408         while( i < p_stream->i_entry_count )
1409         {
1410             i++;
1411             if( i >= p_stream->i_entry_count ||
1412                 p_stream->entry[i].i_length != i_delta )
1413             {
1414                 break;
1415             }
1416         }
1417
1418         bo_add_32be( stts, i - i_first ); // sample-count
1419         bo_add_32be( stts, i_delta );     // sample-delta
1420     }
1421     bo_fix_32be( stts, 12, i_index );
1422     box_fix( stts );
1423
1424     /* FIXME add ctts ?? FIXME */
1425
1426     stsz = box_full_new( "stsz", 0, 0 );
1427     bo_add_32be( stsz, 0 );                             // sample-size
1428     bo_add_32be( stsz, p_stream->i_entry_count );       // sample-count
1429     for( i = 0; i < p_stream->i_entry_count; i++ )
1430     {
1431         bo_add_32be( stsz, p_stream->entry[i].i_size ); // sample-size
1432     }
1433     box_fix( stsz );
1434
1435     /* create stss table */
1436     stss = NULL;
1437     for( i = 0, i_index = 0; i < p_stream->i_entry_count; i++ )
1438     {
1439         if( p_stream->entry[i].i_flags & BLOCK_FLAG_TYPE_I )
1440         {
1441             if( stss == NULL )
1442             {
1443                 stss = box_full_new( "stss", 0, 0 );
1444                 bo_add_32be( stss, 0 ); /* fixed later */
1445             }
1446             bo_add_32be( stss, 1 + i );
1447             i_index++;
1448         }
1449     }
1450     if( stss )
1451     {
1452         bo_fix_32be( stss, 12, i_index );
1453         box_fix( stss );
1454     }
1455
1456     /* Now gather all boxes into stbl */
1457     box_gather( stbl, stsd );
1458     box_gather( stbl, stts );
1459     if( stss )
1460     {
1461         box_gather( stbl, stss );
1462     }
1463     box_gather( stbl, stsc );
1464     box_gather( stbl, stsz );
1465     p_stream->i_stco_pos = stbl->len + 16;
1466     box_gather( stbl, stco );
1467
1468     /* finish stbl */
1469     box_fix( stbl );
1470
1471     return stbl;
1472 }
1473
1474 static int64_t get_timestamp(void);
1475
1476 static const uint32_t mvhd_matrix[9] =
1477     { 0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000 };
1478
1479 static bo_t *GetMoovBox( sout_mux_t *p_mux )
1480 {
1481     sout_mux_sys_t *p_sys = p_mux->p_sys;
1482
1483     bo_t            *moov, *mvhd;
1484     int             i_trak, i;
1485
1486     uint32_t        i_movie_timescale = 90000;
1487     int64_t         i_movie_duration  = 0;
1488     int64_t         i_timestamp = get_timestamp();
1489
1490     moov = box_new( "moov" );
1491
1492     /* Create general info */
1493     for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ )
1494     {
1495         mp4_stream_t *p_stream = p_sys->pp_streams[i_trak];
1496         i_movie_duration = __MAX( i_movie_duration, p_stream->i_duration );
1497     }
1498     msg_Dbg( p_mux, "movie duration %ds",
1499              (uint32_t)( i_movie_duration / (mtime_t)1000000 ) );
1500
1501     i_movie_duration = i_movie_duration * i_movie_timescale / 1000000;
1502
1503     /* *** add /moov/mvhd *** */
1504     if( !p_sys->b_64_ext )
1505     {
1506         mvhd = box_full_new( "mvhd", 0, 0 );
1507         bo_add_32be( mvhd, i_timestamp );   // creation time
1508         bo_add_32be( mvhd, i_timestamp );   // modification time
1509         bo_add_32be( mvhd, i_movie_timescale);  // timescale
1510         bo_add_32be( mvhd, i_movie_duration );  // duration
1511     }
1512     else
1513     {
1514         mvhd = box_full_new( "mvhd", 1, 0 );
1515         bo_add_64be( mvhd, i_timestamp );   // creation time
1516         bo_add_64be( mvhd, i_timestamp );   // modification time
1517         bo_add_32be( mvhd, i_movie_timescale);  // timescale
1518         bo_add_64be( mvhd, i_movie_duration );  // duration
1519     }
1520     bo_add_32be( mvhd, 0x10000 );           // rate
1521     bo_add_16be( mvhd, 0x100 );             // volume
1522     bo_add_16be( mvhd, 0 );                 // reserved
1523     for( i = 0; i < 2; i++ )
1524     {
1525         bo_add_32be( mvhd, 0 );             // reserved
1526     }
1527     for( i = 0; i < 9; i++ )
1528     {
1529         bo_add_32be( mvhd, mvhd_matrix[i] );// matrix
1530     }
1531     for( i = 0; i < 6; i++ )
1532     {
1533         bo_add_32be( mvhd, 0 );             // pre-defined
1534     }
1535
1536     /* Next available track id */
1537     bo_add_32be( mvhd, p_sys->i_nb_streams + 1 ); // next-track-id
1538
1539     box_fix( mvhd );
1540     box_gather( moov, mvhd );
1541
1542     for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ )
1543     {
1544         mp4_stream_t *p_stream;
1545         uint32_t     i_timescale;
1546
1547         bo_t *trak, *tkhd, *edts, *elst, *mdia, *mdhd, *hdlr;
1548         bo_t *minf, *dinf, *dref, *url, *stbl;
1549
1550         p_stream = p_sys->pp_streams[i_trak];
1551
1552         if( p_stream->fmt.i_cat == AUDIO_ES )
1553             i_timescale = p_stream->fmt.audio.i_rate;
1554         else
1555             i_timescale = 1001;
1556
1557         /* *** add /moov/trak *** */
1558         trak = box_new( "trak" );
1559
1560         /* *** add /moov/trak/tkhd *** */
1561         if( !p_sys->b_64_ext )
1562         {
1563             if( p_sys->b_mov )
1564                 tkhd = box_full_new( "tkhd", 0, 0x0f );
1565             else
1566                 tkhd = box_full_new( "tkhd", 0, 1 );
1567
1568             bo_add_32be( tkhd, i_timestamp );       // creation time
1569             bo_add_32be( tkhd, i_timestamp );       // modification time
1570             bo_add_32be( tkhd, p_stream->i_track_id );
1571             bo_add_32be( tkhd, 0 );                     // reserved 0
1572             bo_add_32be( tkhd, p_stream->i_duration *
1573                          (int64_t)i_movie_timescale /
1574                          (mtime_t)1000000 );            // duration
1575         }
1576         else
1577         {
1578             if( p_sys->b_mov )
1579                 tkhd = box_full_new( "tkhd", 1, 0x0f );
1580             else
1581                 tkhd = box_full_new( "tkhd", 1, 1 );
1582
1583             bo_add_64be( tkhd, i_timestamp );       // creation time
1584             bo_add_64be( tkhd, i_timestamp );       // modification time
1585             bo_add_32be( tkhd, p_stream->i_track_id );
1586             bo_add_32be( tkhd, 0 );                     // reserved 0
1587             bo_add_64be( tkhd, p_stream->i_duration *
1588                          (int64_t)i_movie_timescale /
1589                          (mtime_t)1000000 );            // duration
1590         }
1591
1592         for( i = 0; i < 2; i++ )
1593         {
1594             bo_add_32be( tkhd, 0 );                 // reserved
1595         }
1596         bo_add_16be( tkhd, 0 );                     // layer
1597         bo_add_16be( tkhd, 0 );                     // pre-defined
1598         // volume
1599         bo_add_16be( tkhd, p_stream->fmt.i_cat == AUDIO_ES ? 0x100 : 0 );
1600         bo_add_16be( tkhd, 0 );                     // reserved
1601         for( i = 0; i < 9; i++ )
1602         {
1603             bo_add_32be( tkhd, mvhd_matrix[i] );    // matrix
1604         }
1605         if( p_stream->fmt.i_cat == AUDIO_ES )
1606         {
1607             bo_add_32be( tkhd, 0 );                 // width (presentation)
1608             bo_add_32be( tkhd, 0 );                 // height(presentation)
1609         }
1610         else if( p_stream->fmt.i_cat == VIDEO_ES )
1611         {
1612             int i_width = p_stream->fmt.video.i_width << 16;
1613             if( p_stream->fmt.video.i_sar_num > 0 &&
1614                 p_stream->fmt.video.i_sar_den > 0 )
1615             {
1616                 i_width = (int64_t)p_stream->fmt.video.i_sar_num *
1617                           ((int64_t)p_stream->fmt.video.i_width << 16) /
1618                           p_stream->fmt.video.i_sar_den;
1619             }
1620             // width (presentation)
1621             bo_add_32be( tkhd, i_width );
1622             // height(presentation)
1623             bo_add_32be( tkhd, p_stream->fmt.video.i_height << 16 );
1624         }
1625         else
1626         {
1627             int i_width = 320 << 16;
1628             int i_height = 200;
1629             int i;
1630             for( i = 0; i < p_sys->i_nb_streams; i++ )
1631             {
1632                 mp4_stream_t *tk = p_sys->pp_streams[i];
1633                 if( tk->fmt.i_cat == VIDEO_ES )
1634                 {
1635                     if( tk->fmt.video.i_sar_num > 0 &&
1636                         tk->fmt.video.i_sar_den > 0 )
1637                         i_width = (int64_t)tk->fmt.video.i_sar_num *
1638                                   ((int64_t)tk->fmt.video.i_width << 16) /
1639                                   tk->fmt.video.i_sar_den;
1640                     else
1641                         i_width = tk->fmt.video.i_width << 16;
1642                     i_height = tk->fmt.video.i_height;
1643                     break;
1644                 }
1645             }
1646             bo_add_32be( tkhd, i_width );     // width (presentation)
1647             bo_add_32be( tkhd, i_height << 16 );    // height(presentation)
1648         }
1649
1650         box_fix( tkhd );
1651         box_gather( trak, tkhd );
1652
1653         /* *** add /moov/trak/edts and elst */
1654         edts = box_new( "edts" );
1655         elst = box_full_new( "elst", p_sys->b_64_ext ? 1 : 0, 0 );
1656         if( p_stream->i_dts_start > p_sys->i_dts_start )
1657         {
1658             bo_add_32be( elst, 2 );
1659
1660             if( p_sys->b_64_ext )
1661             {
1662                 bo_add_64be( elst, (p_stream->i_dts_start-p_sys->i_dts_start) *
1663                              i_movie_timescale / INT64_C(1000000) );
1664                 bo_add_64be( elst, -1 );
1665             }
1666             else
1667             {
1668                 bo_add_32be( elst, (p_stream->i_dts_start-p_sys->i_dts_start) *
1669                              i_movie_timescale / INT64_C(1000000) );
1670                 bo_add_32be( elst, -1 );
1671             }
1672             bo_add_16be( elst, 1 );
1673             bo_add_16be( elst, 0 );
1674         }
1675         else
1676         {
1677             bo_add_32be( elst, 1 );
1678         }
1679         if( p_sys->b_64_ext )
1680         {
1681             bo_add_64be( elst, p_stream->i_duration *
1682                          i_movie_timescale / INT64_C(1000000) );
1683             bo_add_64be( elst, 0 );
1684         }
1685         else
1686         {
1687             bo_add_32be( elst, p_stream->i_duration *
1688                          i_movie_timescale / INT64_C(1000000) );
1689             bo_add_32be( elst, 0 );
1690         }
1691         bo_add_16be( elst, 1 );
1692         bo_add_16be( elst, 0 );
1693
1694         box_fix( elst );
1695         box_gather( edts, elst );
1696         box_fix( edts );
1697         box_gather( trak, edts );
1698
1699         /* *** add /moov/trak/mdia *** */
1700         mdia = box_new( "mdia" );
1701
1702         /* media header */
1703         if( !p_sys->b_64_ext )
1704         {
1705             mdhd = box_full_new( "mdhd", 0, 0 );
1706             bo_add_32be( mdhd, i_timestamp );   // creation time
1707             bo_add_32be( mdhd, i_timestamp );   // modification time
1708             bo_add_32be( mdhd, i_timescale);        // timescale
1709             bo_add_32be( mdhd, p_stream->i_duration * (int64_t)i_timescale /
1710                                (mtime_t)1000000 );  // duration
1711         }
1712         else
1713         {
1714             mdhd = box_full_new( "mdhd", 1, 0 );
1715             bo_add_64be( mdhd, i_timestamp );   // creation time
1716             bo_add_64be( mdhd, i_timestamp );   // modification time
1717             bo_add_32be( mdhd, i_timescale);        // timescale
1718             bo_add_64be( mdhd, p_stream->i_duration * (int64_t)i_timescale /
1719                                (mtime_t)1000000 );  // duration
1720         }
1721
1722         if( p_stream->fmt.psz_language )
1723         {
1724             char *psz = p_stream->fmt.psz_language;
1725             const iso639_lang_t *pl = NULL;
1726             uint16_t lang = 0x0;
1727
1728             if( strlen( psz ) == 2 )
1729             {
1730                 pl = GetLang_1( psz );
1731             }
1732             else if( strlen( psz ) == 3 )
1733             {
1734                 pl = GetLang_2B( psz );
1735                 if( !strcmp( pl->psz_iso639_1, "??" ) )
1736                 {
1737                     pl = GetLang_2T( psz );
1738                 }
1739             }
1740             if( pl && strcmp( pl->psz_iso639_1, "??" ) )
1741             {
1742                 lang = ( ( pl->psz_iso639_2T[0] - 0x60 ) << 10 ) |
1743                        ( ( pl->psz_iso639_2T[1] - 0x60 ) <<  5 ) |
1744                        ( ( pl->psz_iso639_2T[2] - 0x60 ) );
1745             }
1746             bo_add_16be( mdhd, lang );          // language
1747         }
1748         else
1749         {
1750             bo_add_16be( mdhd, 0    );          // language
1751         }
1752         bo_add_16be( mdhd, 0    );              // predefined
1753         box_fix( mdhd );
1754         box_gather( mdia, mdhd );
1755
1756         /* handler reference */
1757         hdlr = box_full_new( "hdlr", 0, 0 );
1758
1759         if( p_sys->b_mov )
1760             bo_add_fourcc( hdlr, "mhlr" );         // media handler
1761         else
1762             bo_add_32be( hdlr, 0 );
1763
1764         if( p_stream->fmt.i_cat == AUDIO_ES )
1765             bo_add_fourcc( hdlr, "soun" );
1766         else if( p_stream->fmt.i_cat == VIDEO_ES )
1767             bo_add_fourcc( hdlr, "vide" );
1768         else if( p_stream->fmt.i_cat == SPU_ES )
1769             bo_add_fourcc( hdlr, "text" );
1770
1771         bo_add_32be( hdlr, 0 );         // reserved
1772         bo_add_32be( hdlr, 0 );         // reserved
1773         bo_add_32be( hdlr, 0 );         // reserved
1774
1775         if( p_sys->b_mov )
1776             bo_add_8( hdlr, 12 );   /* Pascal string for .mov */
1777
1778         if( p_stream->fmt.i_cat == AUDIO_ES )
1779             bo_add_mem( hdlr, 12, (uint8_t*)"SoundHandler" );
1780         else if( p_stream->fmt.i_cat == VIDEO_ES )
1781             bo_add_mem( hdlr, 12, (uint8_t*)"VideoHandler" );
1782         else
1783             bo_add_mem( hdlr, 12, (uint8_t*)"Text Handler" );
1784
1785         if( !p_sys->b_mov )
1786             bo_add_8( hdlr, 0 );   /* asciiz string for .mp4, yes that's BRAIN DAMAGED F**K MP4 */
1787
1788         box_fix( hdlr );
1789         box_gather( mdia, hdlr );
1790
1791         /* minf*/
1792         minf = box_new( "minf" );
1793
1794         /* add smhd|vmhd */
1795         if( p_stream->fmt.i_cat == AUDIO_ES )
1796         {
1797             bo_t *smhd;
1798
1799             smhd = box_full_new( "smhd", 0, 0 );
1800             bo_add_16be( smhd, 0 );     // balance
1801             bo_add_16be( smhd, 0 );     // reserved
1802             box_fix( smhd );
1803
1804             box_gather( minf, smhd );
1805         }
1806         else if( p_stream->fmt.i_cat == VIDEO_ES )
1807         {
1808             bo_t *vmhd;
1809
1810             vmhd = box_full_new( "vmhd", 0, 1 );
1811             bo_add_16be( vmhd, 0 );     // graphicsmode
1812             for( i = 0; i < 3; i++ )
1813             {
1814                 bo_add_16be( vmhd, 0 ); // opcolor
1815             }
1816             box_fix( vmhd );
1817
1818             box_gather( minf, vmhd );
1819         }
1820         else if( p_stream->fmt.i_cat == SPU_ES )
1821         {
1822             bo_t *gmhd = box_new( "gmhd" );
1823             bo_t *gmin = box_full_new( "gmin", 0, 1 );
1824
1825             bo_add_16be( gmin, 0 );     // graphicsmode
1826             for( i = 0; i < 3; i++ )
1827             {
1828                 bo_add_16be( gmin, 0 ); // opcolor
1829             }
1830             bo_add_16be( gmin, 0 );     // balance
1831             bo_add_16be( gmin, 0 );     // reserved
1832             box_fix( gmin );
1833
1834             box_gather( gmhd, gmin );
1835             box_fix( gmhd );
1836
1837             box_gather( minf, gmhd );
1838         }
1839
1840         /* dinf */
1841         dinf = box_new( "dinf" );
1842         dref = box_full_new( "dref", 0, 0 );
1843         bo_add_32be( dref, 1 );
1844         url = box_full_new( "url ", 0, 0x01 );
1845         box_fix( url );
1846         box_gather( dref, url );
1847         box_fix( dref );
1848         box_gather( dinf, dref );
1849
1850         /* append dinf to mdia */
1851         box_fix( dinf );
1852         box_gather( minf, dinf );
1853
1854         /* add stbl */
1855         stbl = GetStblBox( p_mux, p_stream );
1856
1857         /* append stbl to minf */
1858         p_stream->i_stco_pos += minf->len;
1859         box_gather( minf, stbl );
1860
1861         /* append minf to mdia */
1862         box_fix( minf );
1863         p_stream->i_stco_pos += mdia->len;
1864         box_gather( mdia, minf );
1865
1866         /* append mdia to trak */
1867         box_fix( mdia );
1868         p_stream->i_stco_pos += trak->len;
1869         box_gather( trak, mdia );
1870
1871         /* append trak to moov */
1872         box_fix( trak );
1873         p_stream->i_stco_pos += moov->len;
1874         box_gather( moov, trak );
1875     }
1876
1877     /* Add user data tags */
1878     box_gather( moov, GetUdtaTag( p_mux ) );
1879
1880     box_fix( moov );
1881     return moov;
1882 }
1883
1884 /****************************************************************************/
1885
1886 static void bo_init( bo_t *p_bo )
1887 {
1888     p_bo->len = 0;
1889     p_bo->b = block_Alloc( 1024 );
1890 }
1891
1892 static void bo_add_8( bo_t *p_bo, uint8_t i )
1893 {
1894     if( p_bo->len >= p_bo->b->i_buffer)
1895         p_bo->b = block_Realloc(p_bo->b, 0, p_bo->b->i_buffer + 1024);
1896
1897     p_bo->b->p_buffer[p_bo->len++] = i;
1898 }
1899
1900 static void bo_add_16be( bo_t *p_bo, uint16_t i )
1901 {
1902     bo_add_8( p_bo, ( ( i >> 8) &0xff ) );
1903     bo_add_8( p_bo, i &0xff );
1904 }
1905
1906 static void bo_add_24be( bo_t *p_bo, uint32_t i )
1907 {
1908     bo_add_8( p_bo, ( ( i >> 16) &0xff ) );
1909     bo_add_8( p_bo, ( ( i >> 8) &0xff ) );
1910     bo_add_8( p_bo, (   i &0xff ) );
1911 }
1912 static void bo_add_32be( bo_t *p_bo, uint32_t i )
1913 {
1914     bo_add_16be( p_bo, ( ( i >> 16) &0xffff ) );
1915     bo_add_16be( p_bo, i &0xffff );
1916 }
1917
1918 static void bo_fix_32be ( bo_t *p_bo, int i_pos, uint32_t i)
1919 {
1920     p_bo->b->p_buffer[i_pos    ] = ( i >> 24 )&0xff;
1921     p_bo->b->p_buffer[i_pos + 1] = ( i >> 16 )&0xff;
1922     p_bo->b->p_buffer[i_pos + 2] = ( i >>  8 )&0xff;
1923     p_bo->b->p_buffer[i_pos + 3] = ( i       )&0xff;
1924 }
1925
1926 static void bo_add_64be( bo_t *p_bo, uint64_t i )
1927 {
1928     bo_add_32be( p_bo, ( ( i >> 32) &0xffffffff ) );
1929     bo_add_32be( p_bo, i &0xffffffff );
1930 }
1931
1932 static void bo_add_fourcc( bo_t *p_bo, const char *fcc )
1933 {
1934     bo_add_8( p_bo, fcc[0] );
1935     bo_add_8( p_bo, fcc[1] );
1936     bo_add_8( p_bo, fcc[2] );
1937     bo_add_8( p_bo, fcc[3] );
1938 }
1939
1940 static void bo_add_mem( bo_t *p_bo, int i_size, uint8_t *p_mem )
1941 {
1942     for( int i = 0; i < i_size; i++ )
1943         bo_add_8( p_bo, p_mem[i] );
1944 }
1945
1946 static void bo_add_descr( bo_t *p_bo, uint8_t tag, uint32_t size )
1947 {
1948     bo_add_8( p_bo, tag );
1949     for(int i = 3; i>0; i--)
1950         bo_add_8( p_bo, (size>>(7*i)) | 0x80 );
1951     bo_add_8(p_bo, size & 0x7F);
1952 }
1953
1954 static bo_t * box_new( const char *fcc )
1955 {
1956     bo_t *box = malloc( sizeof( *box ) );
1957     if (!box)
1958         return NULL;
1959
1960     bo_init( box );
1961
1962     bo_add_32be  ( box, 0 );
1963     bo_add_fourcc( box, fcc );
1964
1965     return box;
1966 }
1967
1968 static bo_t * box_full_new( const char *fcc, uint8_t v, uint32_t f )
1969 {
1970     bo_t *box = box_new( fcc );
1971     if (!box)
1972         return NULL;
1973
1974     bo_add_8     ( box, v );
1975     bo_add_24be  ( box, f );
1976
1977     return box;
1978 }
1979
1980 static void box_fix( bo_t *box )
1981 {
1982     box->b->p_buffer[0] = box->len >> 24;
1983     box->b->p_buffer[1] = box->len >> 16;
1984     box->b->p_buffer[2] = box->len >>  8;
1985     box->b->p_buffer[3] = box->len;
1986 }
1987
1988 static void box_gather ( bo_t *box, bo_t *box2 )
1989 {
1990     box->b = block_Realloc(box->b, 0, box->len + box2->len);
1991     memcpy(&box->b->p_buffer[box->len], box2->b->p_buffer, box2->len);
1992     box->len += box2->len;
1993     block_Release(box2->b);
1994     free( box2 );
1995 }
1996
1997 static void box_send( sout_mux_t *p_mux,  bo_t *box )
1998 {
1999     box->b->i_buffer = box->len;
2000     sout_AccessOutWrite( p_mux->p_access, box->b );
2001     free(box);
2002 }
2003
2004 static int64_t get_timestamp(void)
2005 {
2006     int64_t i_timestamp = time(NULL);
2007
2008     i_timestamp += 2082844800; // MOV/MP4 start date is 1/1/1904
2009     // 208284480 is (((1970 - 1904) * 365) + 17) * 24 * 60 * 60
2010
2011     return i_timestamp;
2012 }