]> git.sesse.net Git - vlc/blob - modules/mux/mp4.c
* mp4: use sout_ParseCfg and rename option to --sout-mp4-faststart.
[vlc] / modules / mux / mp4.c
1 /*****************************************************************************
2  * mp4.c: mp4/mov muxer
3  *****************************************************************************
4  * Copyright (C) 2001, 2002, 2003 VideoLAN
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>
29 #include <string.h>
30
31 #include <vlc/vlc.h>
32 #include <vlc/input.h>
33 #include <vlc/sout.h>
34
35 #ifdef HAVE_TIME_H
36 #include <time.h>
37 #endif
38
39 #include "iso_lang.h"
40 #include "vlc_meta.h"
41
42 /*****************************************************************************
43  * Module descriptor
44  *****************************************************************************/
45 #define FASTSTART_TEXT N_("Create \"Fast start\" files")
46 #define FASTSTART_LONGTEXT N_( \
47     "When this option is turned on, \"Fast start\" files will be created. " \
48     "(\"Fast start\" files are optimized for download, allowing the user " \
49     "to start previewing the file while it is downloading).")
50 static int  Open   ( vlc_object_t * );
51 static void Close  ( vlc_object_t * );
52
53 #define SOUT_CFG_PREFIX "sout-mp4-"
54
55 vlc_module_begin();
56     set_description( _("MP4/MOV muxer") );
57
58     add_bool( SOUT_CFG_PREFIX "faststart", 1, NULL, FASTSTART_TEXT, FASTSTART_LONGTEXT,
59               VLC_TRUE );
60     set_capability( "sout mux", 5 );
61     add_shortcut( "mp4" );
62     add_shortcut( "mov" );
63     set_callbacks( Open, Close );
64 vlc_module_end();
65
66 /*****************************************************************************
67  * Exported prototypes
68  *****************************************************************************/
69 static const char *ppsz_sout_options[] = {
70     "faststart", NULL
71 };
72
73 static int Capability(sout_mux_t *, int, void *, void * );
74 static int AddStream( sout_mux_t *, sout_input_t * );
75 static int DelStream( sout_mux_t *, sout_input_t * );
76 static int Mux      ( sout_mux_t * );
77
78 /*****************************************************************************
79  * Local prototypes
80  *****************************************************************************/
81 typedef struct
82 {
83     uint64_t i_pos;
84     int      i_size;
85
86     mtime_t  i_pts_dts;
87     mtime_t  i_length;
88     unsigned int i_flags;
89
90 } mp4_entry_t;
91
92 typedef struct
93 {
94     es_format_t   fmt;
95     int           i_track_id;
96
97     /* index */
98     unsigned int i_entry_count;
99     unsigned int i_entry_max;
100     mp4_entry_t  *entry;
101     int64_t      i_length_neg;
102
103     /* stats */
104     int64_t      i_dts_start;
105     int64_t      i_duration;
106
107     /* for later stco fix-up (fast start files) */
108     uint64_t i_stco_pos;
109     vlc_bool_t b_stco64;
110
111 } mp4_stream_t;
112
113 struct sout_mux_sys_t
114 {
115     vlc_bool_t b_mov;
116     vlc_bool_t b_64_ext;
117     vlc_bool_t b_fast_start;
118
119     uint64_t i_mdat_pos;
120     uint64_t i_pos;
121
122     int64_t  i_dts_start;
123
124     int          i_nb_streams;
125     mp4_stream_t **pp_streams;
126 };
127
128 typedef struct bo_t
129 {
130     vlc_bool_t b_grow;
131
132     int        i_buffer_size;
133     int        i_buffer;
134     uint8_t    *p_buffer;
135
136 } bo_t;
137
138 static void bo_init     ( bo_t *, int , uint8_t *, vlc_bool_t  );
139 static void bo_add_8    ( bo_t *, uint8_t );
140 static void bo_add_16be ( bo_t *, uint16_t );
141 static void bo_add_24be ( bo_t *, uint32_t );
142 static void bo_add_32be ( bo_t *, uint32_t );
143 static void bo_add_64be ( bo_t *, uint64_t );
144 static void bo_add_fourcc(bo_t *, char * );
145 static void bo_add_bo   ( bo_t *, bo_t * );
146 static void bo_add_mem  ( bo_t *, int , uint8_t * );
147 static void bo_add_descr( bo_t *, uint8_t , uint32_t );
148
149 static void bo_fix_32be ( bo_t *, int , uint32_t );
150
151 static bo_t *box_new     ( char *fcc );
152 static bo_t *box_full_new( char *fcc, uint8_t v, uint32_t f );
153 static void  box_fix     ( bo_t *box );
154 static void  box_free    ( bo_t *box );
155 static void  box_gather  ( bo_t *box, bo_t *box2 );
156
157 static void box_send( sout_mux_t *p_mux,  bo_t *box );
158
159 static block_t *bo_to_sout( sout_instance_t *p_sout,  bo_t *box );
160
161 static bo_t *GetMoovBox( sout_mux_t *p_mux );
162
163 /*****************************************************************************
164  * Open:
165  *****************************************************************************/
166 static int Open( vlc_object_t *p_this )
167 {
168     sout_mux_t      *p_mux = (sout_mux_t*)p_this;
169     sout_mux_sys_t  *p_sys;
170     bo_t            *box;
171
172     msg_Dbg( p_mux, "Mp4 muxer opend" );
173     sout_ParseCfg( p_mux, SOUT_CFG_PREFIX, ppsz_sout_options, p_mux->p_cfg );
174
175     p_mux->pf_capacity  = Capability;
176     p_mux->pf_addstream = AddStream;
177     p_mux->pf_delstream = DelStream;
178     p_mux->pf_mux       = Mux;
179     p_mux->p_sys        = p_sys = malloc( sizeof( sout_mux_sys_t ) );
180     p_sys->i_pos        = 0;
181     p_sys->i_nb_streams = 0;
182     p_sys->pp_streams   = NULL;
183     p_sys->i_mdat_pos   = 0;
184     p_sys->b_mov        = p_mux->psz_mux && !strcmp( p_mux->psz_mux, "mov" );
185     p_sys->i_dts_start  = 0;
186
187
188     if( !p_sys->b_mov )
189     {
190         /* Now add ftyp header */
191         box = box_new( "ftyp" );
192         bo_add_fourcc( box, "isom" );
193         bo_add_32be  ( box, 0 );
194         bo_add_fourcc( box, "mp41" );
195         box_fix( box );
196
197         p_sys->i_pos += box->i_buffer;
198         p_sys->i_mdat_pos = p_sys->i_pos;
199
200         box_send( p_mux, box );
201     }
202
203     /* FIXME FIXME
204      * Quicktime actually doesn't like the 64 bits extensions !!! */
205     p_sys->b_64_ext = VLC_FALSE;
206
207     /* Now add mdat header */
208     box = box_new( "mdat" );
209     bo_add_64be  ( box, 0 ); // enough to store an extended size
210
211     p_sys->i_pos += box->i_buffer;
212
213     box_send( p_mux, box );
214
215     return VLC_SUCCESS;
216 }
217
218 /*****************************************************************************
219  * Close:
220  *****************************************************************************/
221 static void Close( vlc_object_t * p_this )
222 {
223     sout_mux_t      *p_mux = (sout_mux_t*)p_this;
224     sout_mux_sys_t  *p_sys = p_mux->p_sys;
225     block_t   *p_hdr;
226     bo_t            bo, *moov;
227     vlc_value_t     val;
228
229     int             i_trak;
230     uint64_t        i_moov_pos;
231
232     msg_Dbg( p_mux, "Close" );
233
234     /* Update mdat size */
235     bo_init( &bo, 0, NULL, VLC_TRUE );
236     if( p_sys->i_pos - p_sys->i_mdat_pos >= (((uint64_t)1)<<32) )
237     {
238         /* Extended size */
239         bo_add_32be  ( &bo, 1 );
240         bo_add_fourcc( &bo, "mdat" );
241         bo_add_64be  ( &bo, p_sys->i_pos - p_sys->i_mdat_pos );
242     }
243     else
244     {
245         bo_add_32be  ( &bo, 8 );
246         bo_add_fourcc( &bo, "wide" );
247         bo_add_32be  ( &bo, p_sys->i_pos - p_sys->i_mdat_pos - 8 );
248         bo_add_fourcc( &bo, "mdat" );
249     }
250     p_hdr = bo_to_sout( p_mux->p_sout, &bo );
251     free( bo.p_buffer );
252
253     sout_AccessOutSeek( p_mux->p_access, p_sys->i_mdat_pos );
254     sout_AccessOutWrite( p_mux->p_access, p_hdr );
255
256     /* Create MOOV header */
257     i_moov_pos = p_sys->i_pos;
258     moov = GetMoovBox( p_mux );
259
260     /* Check we need to create "fast start" files */
261     var_Get( p_this, SOUT_CFG_PREFIX "faststart", &val );
262     p_sys->b_fast_start = val.b_bool;
263     while( p_sys->b_fast_start )
264     {
265         /* Move data to the end of the file so we can fit the moov header
266          * at the start */
267         block_t *p_buf;
268         int64_t i_chunk, i_size = p_sys->i_pos - p_sys->i_mdat_pos;
269         int i_moov_size = moov->i_buffer;
270
271         while( i_size > 0 )
272         {
273             i_chunk = __MIN( 32768, i_size );
274             p_buf = block_New( p_mux, i_chunk );
275             sout_AccessOutSeek( p_mux->p_access,
276                                 p_sys->i_mdat_pos + i_size - i_chunk );
277             if( sout_AccessOutRead( p_mux->p_access, p_buf ) < i_chunk )
278             {
279                 msg_Warn( p_this, "read() not supported by acces output, "
280                           "won't create a fast start file" );
281                 p_sys->b_fast_start = VLC_FALSE;
282                 break;
283             }
284             sout_AccessOutSeek( p_mux->p_access, p_sys->i_mdat_pos + i_size +
285                                 i_moov_size - i_chunk );
286             sout_AccessOutWrite( p_mux->p_access, p_buf );
287             i_size -= i_chunk;
288         }
289
290         if( !p_sys->b_fast_start ) break;
291
292         /* Fix-up samples to chunks table in MOOV header */
293         for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ )
294         {
295             mp4_stream_t *p_stream = p_sys->pp_streams[i_trak];
296             unsigned int i;
297             int i_chunk;
298
299             moov->i_buffer = p_stream->i_stco_pos;
300             for( i_chunk = 0, i = 0; i < p_stream->i_entry_count; i_chunk++ )
301             {
302                 if( p_stream->b_stco64 )
303                     bo_add_64be( moov, p_stream->entry[i].i_pos + i_moov_size);
304                 else
305                     bo_add_32be( moov, p_stream->entry[i].i_pos + i_moov_size);
306
307                 while( i < p_stream->i_entry_count )
308                 {
309                     if( i + 1 < p_stream->i_entry_count &&
310                         p_stream->entry[i].i_pos + p_stream->entry[i].i_size
311                         != p_stream->entry[i + 1].i_pos )
312                     {
313                         i++;
314                         break;
315                     }
316
317                     i++;
318                 }
319             }
320         }
321
322         moov->i_buffer = i_moov_size;
323         i_moov_pos = p_sys->i_mdat_pos;
324         p_sys->b_fast_start = VLC_FALSE;
325     }
326
327     /* Write MOOV header */
328     sout_AccessOutSeek( p_mux->p_access, i_moov_pos );
329     box_send( p_mux, moov );
330
331     /* Clean-up */
332     for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ )
333     {
334         mp4_stream_t *p_stream = p_sys->pp_streams[i_trak];
335
336         es_format_Clean( &p_stream->fmt );
337         free( p_stream->entry );
338         free( p_stream );
339     }
340     if( p_sys->i_nb_streams ) free( p_sys->pp_streams );
341     free( p_sys );
342 }
343
344 /*****************************************************************************
345  * Capability:
346  *****************************************************************************/
347 static int Capability( sout_mux_t *p_mux, int i_query, void *p_args,
348                        void *p_answer )
349 {
350    switch( i_query )
351    {
352         case SOUT_MUX_CAP_GET_ADD_STREAM_ANY_TIME:
353             *(vlc_bool_t*)p_answer = VLC_TRUE;
354             return SOUT_MUX_CAP_ERR_OK;
355
356         case SOUT_MUX_CAP_GET_ADD_STREAM_WAIT:
357             *(vlc_bool_t*)p_answer = VLC_TRUE;
358             return( SOUT_MUX_CAP_ERR_OK );
359
360         default:
361             return SOUT_MUX_CAP_ERR_UNIMPLEMENTED;
362    }
363 }
364
365 /*****************************************************************************
366  * AddStream:
367  *****************************************************************************/
368 static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
369 {
370     sout_mux_sys_t  *p_sys = p_mux->p_sys;
371     mp4_stream_t    *p_stream;
372
373     switch( p_input->p_fmt->i_codec )
374     {
375         case VLC_FOURCC( 'm', 'p', '4', 'a' ):
376         case VLC_FOURCC( 'm', 'p', '4', 'v' ):
377         case VLC_FOURCC( 'm', 'p', 'g', 'a' ):
378         case VLC_FOURCC( 'm', 'p', 'g', 'v' ):
379         case VLC_FOURCC( 'M', 'J', 'P', 'G' ):
380         case VLC_FOURCC( 'm', 'j', 'p', 'b' ):
381         case VLC_FOURCC( 'S', 'V', 'Q', '1' ):
382         case VLC_FOURCC( 'S', 'V', 'Q', '3' ):
383         case VLC_FOURCC( 'h', '2', '6', '4' ):
384             break;
385         default:
386             msg_Err( p_mux, "unsupported codec %4.4s in mp4",
387                      (char*)&p_input->p_fmt->i_codec );
388             return VLC_EGENERIC;
389     }
390
391     p_stream                = malloc( sizeof( mp4_stream_t ) );
392     es_format_Copy( &p_stream->fmt, p_input->p_fmt );
393     p_stream->i_track_id    = p_sys->i_nb_streams + 1;
394     p_stream->i_length_neg  = 0;
395     p_stream->i_entry_count = 0;
396     p_stream->i_entry_max   = 1000;
397     p_stream->entry         =
398         calloc( p_stream->i_entry_max, sizeof( mp4_entry_t ) );
399     p_stream->i_dts_start   = 0;
400     p_stream->i_duration    = 0;
401
402     p_input->p_sys          = p_stream;
403
404     msg_Dbg( p_mux, "adding input" );
405
406     TAB_APPEND( p_sys->i_nb_streams, p_sys->pp_streams, p_stream );
407     return VLC_SUCCESS;
408 }
409
410 /*****************************************************************************
411  * DelStream:
412  *****************************************************************************/
413 static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
414 {
415     msg_Dbg( p_mux, "removing input" );
416     return VLC_SUCCESS;
417 }
418
419 static int MuxGetStream( sout_mux_t *p_mux, int *pi_stream, mtime_t *pi_dts )
420 {
421     mtime_t i_dts;
422     int     i_stream;
423     int     i;
424
425     for( i = 0, i_dts = 0, i_stream = -1; i < p_mux->i_nb_inputs; i++ )
426     {
427         block_fifo_t   *p_fifo = p_mux->pp_inputs[i]->p_fifo;
428         block_t *p_buf;
429
430         if( p_fifo->i_depth <= 1 )
431         {
432             return -1; // wait that all fifo have at least 2 packets
433         }
434
435         p_buf = block_FifoShow( p_fifo );
436         if( i_stream < 0 || p_buf->i_dts < i_dts )
437         {
438             i_dts = p_buf->i_dts;
439             i_stream = i;
440         }
441     }
442     if( pi_stream )
443     {
444         *pi_stream = i_stream;
445     }
446     if( pi_dts )
447     {
448         *pi_dts = i_dts;
449     }
450     return i_stream;
451 }
452
453 /*****************************************************************************
454  * Mux:
455  *****************************************************************************/
456 static int Mux( sout_mux_t *p_mux )
457 {
458     sout_mux_sys_t *p_sys = p_mux->p_sys;
459
460     for( ;; )
461     {
462         sout_input_t    *p_input;
463         int             i_stream;
464         mp4_stream_t    *p_stream;
465         block_t   *p_data;
466         mtime_t         i_dts;
467
468         if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
469         {
470             return( VLC_SUCCESS );
471         }
472
473         p_input  = p_mux->pp_inputs[i_stream];
474         p_stream = (mp4_stream_t*)p_input->p_sys;
475
476         p_data  = block_FifoGet( p_input->p_fifo );
477         if( p_input->p_fifo->i_depth > 0 )
478         {
479             block_t *p_next = block_FifoShow( p_input->p_fifo );
480             int64_t       i_diff  = p_next->i_dts - p_data->i_dts;
481
482             if( i_diff < I64C(1000000 ) )   /* protection */
483             {
484                 p_data->i_length = i_diff;
485             }
486         }
487         if( p_data->i_length <= 0 )
488         {
489             msg_Warn( p_mux, "i_length <= 0" );
490             p_stream->i_length_neg += p_data->i_length - 1;
491             p_data->i_length = 1;
492         }
493         else if( p_stream->i_length_neg < 0 )
494         {
495             int64_t i_recover = __MIN( p_data->i_length / 4, - p_stream->i_length_neg );
496
497             p_data->i_length -= i_recover;
498             p_stream->i_length_neg += i_recover;
499         }
500
501         /* Save starting time */
502         if( p_stream->i_entry_count == 0 )
503         {
504             p_stream->i_dts_start = p_data->i_dts;
505
506             /* Update global dts_start */
507             if( p_sys->i_dts_start <= 0 ||
508                 p_stream->i_dts_start < p_sys->i_dts_start )
509             {
510                 p_sys->i_dts_start = p_stream->i_dts_start;
511             }
512         }
513
514         /* add index entry */
515         p_stream->entry[p_stream->i_entry_count].i_pos    = p_sys->i_pos;
516         p_stream->entry[p_stream->i_entry_count].i_size   = p_data->i_buffer;
517         p_stream->entry[p_stream->i_entry_count].i_pts_dts=
518             __MAX( p_data->i_pts - p_data->i_dts, 0 );
519         p_stream->entry[p_stream->i_entry_count].i_length = p_data->i_length;
520         p_stream->entry[p_stream->i_entry_count].i_flags  = p_data->i_flags;
521
522         p_stream->i_entry_count++;
523         if( p_stream->i_entry_count >= p_stream->i_entry_max )
524         {
525             p_stream->i_entry_max += 1000;
526             p_stream->entry =
527                 realloc( p_stream->entry,
528                          p_stream->i_entry_max * sizeof( mp4_entry_t ) );
529         }
530
531         /* update */
532         p_stream->i_duration += p_data->i_length;
533         p_sys->i_pos += p_data->i_buffer;
534
535         /* write data */
536         sout_AccessOutWrite( p_mux->p_access, p_data );
537     }
538
539     return( VLC_SUCCESS );
540 }
541
542 /*****************************************************************************
543  *
544  *****************************************************************************/
545 static int GetDescrLength( int i_size )
546 {
547     if( i_size < 0x00000080 )
548         return 2 + i_size;
549     else if( i_size < 0x00004000 )
550         return 3 + i_size;
551     else if( i_size < 0x00200000 )
552         return 4 + i_size;
553     else
554         return 5 + i_size;
555 }
556
557 static bo_t *GetESDS( mp4_stream_t *p_stream )
558 {
559     bo_t *esds;
560     int  i_stream_type;
561     int  i_object_type_indication;
562     int  i_decoder_specific_info_size;
563
564     if( p_stream->fmt.i_extra > 0 )
565     {
566         i_decoder_specific_info_size =
567             GetDescrLength( p_stream->fmt.i_extra );
568     }
569     else
570     {
571         i_decoder_specific_info_size = 0;
572     }
573
574     esds = box_full_new( "esds", 0, 0 );
575
576     /* ES_Descr */
577     bo_add_descr( esds, 0x03, 3 +
578                   GetDescrLength( 13 + i_decoder_specific_info_size ) +
579                   GetDescrLength( 1 ) );
580     bo_add_16be( esds, p_stream->i_track_id );
581     bo_add_8   ( esds, 0x1f );      // flags=0|streamPriority=0x1f
582
583     /* DecoderConfigDescr */
584     bo_add_descr( esds, 0x04, 13 + i_decoder_specific_info_size );
585
586     switch( p_stream->fmt.i_codec )
587     {
588         case VLC_FOURCC( 'm', 'p', '4', 'v' ):
589             i_object_type_indication = 0x20;
590             break;
591         case VLC_FOURCC( 'm', 'p', 'g', 'v' ):
592             /* FIXME MPEG-I=0x6b, MPEG-II = 0x60 -> 0x65 */
593             i_object_type_indication = 0x60;
594             break;
595         case VLC_FOURCC( 'm', 'p', '4', 'a' ):
596             /* FIXME for mpeg2-aac == 0x66->0x68 */
597             i_object_type_indication = 0x40;
598             break;
599         case VLC_FOURCC( 'm', 'p', 'g', 'a' ):
600             i_object_type_indication =
601                 p_stream->fmt.audio.i_rate < 32000 ? 0x69 : 0x6b;
602             break;
603         default:
604             i_object_type_indication = 0x00;
605             break;
606     }
607     i_stream_type = p_stream->fmt.i_cat == VIDEO_ES ? 0x04 : 0x05;
608
609     bo_add_8   ( esds, i_object_type_indication );
610     bo_add_8   ( esds, ( i_stream_type << 2 ) | 1 );
611     bo_add_24be( esds, 1024 * 1024 );       // bufferSizeDB
612     bo_add_32be( esds, 0x7fffffff );        // maxBitrate
613     bo_add_32be( esds, 0 );                 // avgBitrate
614
615     if( p_stream->fmt.i_extra > 0 )
616     {
617         int i;
618
619         /* DecoderSpecificInfo */
620         bo_add_descr( esds, 0x05, p_stream->fmt.i_extra );
621
622         for( i = 0; i < p_stream->fmt.i_extra; i++ )
623         {
624             bo_add_8( esds, ((uint8_t*)p_stream->fmt.p_extra)[i] );
625         }
626     }
627
628     /* SL_Descr mandatory */
629     bo_add_descr( esds, 0x06, 1 );
630     bo_add_8    ( esds, 0x02 );  // sl_predefined
631
632     box_fix( esds );
633
634     return esds;
635 }
636
637 static bo_t *GetWaveTag( mp4_stream_t *p_stream )
638 {
639     bo_t *wave;
640     bo_t *box;
641
642     wave = box_new( "wave" );
643
644     box = box_new( "frma" );
645     bo_add_fourcc( box, "mp4a" );
646     box_fix( box );
647     box_gather( wave, box );
648
649     box = box_new( "mp4a" );
650     bo_add_32be( box, 0 );
651     box_fix( box );
652     box_gather( wave, box );
653
654     box = GetESDS( p_stream );
655     box_fix( box );
656     box_gather( wave, box );
657
658     box = box_new( "srcq" );
659     bo_add_32be( box, 0x40 );
660     box_fix( box );
661     box_gather( wave, box );
662
663     /* wazza ? */
664     bo_add_32be( wave, 8 ); /* new empty box */
665     bo_add_32be( wave, 0 ); /* box label */
666
667     box_fix( wave );
668
669     return wave;
670 }
671
672 /* TODO: No idea about these values */
673 static bo_t *GetSVQ3Tag( mp4_stream_t *p_stream )
674 {
675     bo_t *smi = box_new( "SMI " );
676
677     if( p_stream->fmt.i_extra > 0x4e )
678     {
679         uint8_t *p_end = &((uint8_t*)p_stream->fmt.p_extra)[p_stream->fmt.i_extra];
680         uint8_t *p     = &((uint8_t*)p_stream->fmt.p_extra)[0x46];
681
682         while( p + 8 < p_end )
683         {
684             int i_size = GetDWBE( p );
685             if( i_size <= 1 )
686             {
687                 /* FIXME handle 1 as long size */
688                 break;
689             }
690             if( !strncmp( &p[4], "SMI ", 4 ) )
691             {
692                 bo_add_mem( smi, p_end - p - 8, &p[8] );
693                 return smi;
694             }
695             p += i_size;
696         }
697     }
698
699     /* Create a dummy one in fallback */
700     bo_add_fourcc( smi, "SEQH" );
701     bo_add_32be( smi, 0x5 );
702     bo_add_32be( smi, 0xe2c0211d );
703     bo_add_8( smi, 0xc0 );
704     box_fix( smi );
705
706     return smi;
707 }
708
709 static bo_t *GetUdtaTag( sout_mux_t *p_mux )
710 {
711     sout_mux_sys_t *p_sys = p_mux->p_sys;
712     bo_t *udta = box_new( "udta" );
713     vlc_meta_t *p_meta = p_mux->p_sout->p_meta;
714     int i_track;
715
716     /* Requirements */
717     for( i_track = 0; i_track < p_sys->i_nb_streams; i_track++ )
718     {
719         mp4_stream_t *p_stream = p_sys->pp_streams[i_track];
720
721         if( p_stream->fmt.i_codec == VLC_FOURCC('m','p','4','v') ||
722             p_stream->fmt.i_codec == VLC_FOURCC('m','p','4','a') )
723         {
724             bo_t *box = box_new( "\251req" );
725             /* String length */
726             bo_add_16be( box, sizeof("QuickTime 6.0 or greater") - 1);
727             bo_add_16be( box, 0 );
728             bo_add_mem( box, sizeof("QuickTime 6.0 or greater") - 1,
729                         "QuickTime 6.0 or greater" );
730             box_fix( box );
731             box_gather( udta, box );
732             break;
733         }
734     }
735
736     /* Encoder */
737     {
738         bo_t *box = box_new( "\251enc" );
739         /* String length */
740         bo_add_16be( box, sizeof(PACKAGE_STRING " stream output") - 1);
741         bo_add_16be( box, 0 );
742         bo_add_mem( box, sizeof(PACKAGE_STRING " stream output") - 1,
743                     PACKAGE_STRING " stream output" );
744         box_fix( box );
745         box_gather( udta, box );
746     }
747
748     /* Misc atoms */
749     if( p_meta )
750     {
751         int i;
752         for( i = 0; i < p_meta->i_meta; i++ )
753         {
754             bo_t *box = NULL;
755
756             if( !strcmp( p_meta->name[i], VLC_META_TITLE ) )
757                 box = box_new( "\251nam" );
758             else if( !strcmp( p_meta->name[i], VLC_META_AUTHOR ) )
759                 box = box_new( "\251aut" );
760             else if( !strcmp( p_meta->name[i], VLC_META_ARTIST ) )
761                 box = box_new( "\251ART" );
762             else if( !strcmp( p_meta->name[i], VLC_META_GENRE ) )
763                 box = box_new( "\251gen" );
764             else if( !strcmp( p_meta->name[i], VLC_META_COPYRIGHT ) )
765                 box = box_new( "\251cpy" );
766             else if( !strcmp( p_meta->name[i], VLC_META_DESCRIPTION ) )
767                 box = box_new( "\251des" );
768             else if( !strcmp( p_meta->name[i], VLC_META_DATE ) )
769                 box = box_new( "\251day" );
770             else if( !strcmp( p_meta->name[i], VLC_META_URL ) )
771                 box = box_new( "\251url" );
772
773             if( box )
774             {
775                 bo_add_16be( box, strlen( p_meta->value[i] ) );
776                 bo_add_16be( box, 0 );
777                 bo_add_mem( box, strlen( p_meta->value[i] ),
778                             p_meta->value[i] );
779                 box_fix( box );
780                 box_gather( udta, box );
781             }
782         }
783     }
784
785     box_fix( udta );
786     return udta;
787 }
788
789 static bo_t *GetSounBox( sout_mux_t *p_mux, mp4_stream_t *p_stream )
790 {
791     sout_mux_sys_t *p_sys = p_mux->p_sys;
792     vlc_bool_t b_descr = VLC_FALSE;
793     bo_t *soun;
794     char fcc[4] = "    ";
795     int  i;
796
797     switch( p_stream->fmt.i_codec )
798     {
799     case VLC_FOURCC('m','p','4','a'):
800         memcpy( fcc, "mp4a", 4 );
801         b_descr = VLC_TRUE;
802         break;
803
804     case VLC_FOURCC('m','p','g','a'):
805         if( p_sys->b_mov )
806             memcpy( fcc, ".mp3", 4 );
807         else
808         {
809             memcpy( fcc, "mp4a", 4 );
810             b_descr = VLC_TRUE;
811         }
812         break;
813
814     default:
815         memcpy( fcc, (char*)&p_stream->fmt.i_codec, 4 );
816         break;
817     }
818
819     soun = box_new( fcc );
820     for( i = 0; i < 6; i++ )
821     {
822         bo_add_8( soun, 0 );        // reserved;
823     }
824     bo_add_16be( soun, 1 );         // data-reference-index
825
826     /* SoundDescription */
827     if( p_sys->b_mov &&
828         p_stream->fmt.i_codec == VLC_FOURCC('m','p','4','a') )
829     {
830         bo_add_16be( soun, 1 );     // version 1;
831     }
832     else
833     {
834         bo_add_16be( soun, 0 );     // version 0;
835     }
836     bo_add_16be( soun, 0 );         // revision level (0)
837     bo_add_32be( soun, 0 );         // vendor
838     // channel-count
839     bo_add_16be( soun, p_stream->fmt.audio.i_channels );
840     // sample size
841     bo_add_16be( soun, p_stream->fmt.audio.i_bitspersample ?
842                  p_stream->fmt.audio.i_bitspersample : 16 );
843     bo_add_16be( soun, -2 );        // compression id
844     bo_add_16be( soun, 0 );         // packet size (0)
845     bo_add_16be( soun, p_stream->fmt.audio.i_rate ); // sampleratehi
846     bo_add_16be( soun, 0 );                             // sampleratelo
847
848     /* Extended data for SoundDescription V1 */
849     if( p_sys->b_mov &&
850         p_stream->fmt.i_codec == VLC_FOURCC('m','p','4','a') )
851     {
852         /* samples per packet */
853         bo_add_32be( soun, p_stream->fmt.audio.i_frame_length );
854         bo_add_32be( soun, 1536 ); /* bytes per packet */
855         bo_add_32be( soun, 2 );    /* bytes per frame */
856         /* bytes per sample */
857         bo_add_32be( soun, 2 /*p_stream->fmt.audio.i_bitspersample/8 */);
858     }
859
860     /* Add an ES Descriptor */
861     if( b_descr )
862     {
863         bo_t *box;
864
865         if( p_sys->b_mov &&
866             p_stream->fmt.i_codec == VLC_FOURCC('m','p','4','a') )
867         {
868             box = GetWaveTag( p_stream );
869         }
870         else
871         {
872             box = GetESDS( p_stream );
873         }
874         box_fix( box );
875         box_gather( soun, box );
876     }
877
878     box_fix( soun );
879
880     return soun;
881 }
882
883 static bo_t *GetVideBox( sout_mux_t *p_mux, mp4_stream_t *p_stream )
884 {
885
886     bo_t *vide;
887     char fcc[4] = "    ";
888     int  i;
889
890     switch( p_stream->fmt.i_codec )
891     {
892     case VLC_FOURCC('m','p','4','v'):
893     case VLC_FOURCC('m','p','g','v'):
894         memcpy( fcc, "mp4v", 4 );
895         break;
896
897     case VLC_FOURCC('M','J','P','G'):
898         memcpy( fcc, "mjpa", 4 );
899         break;
900
901     case VLC_FOURCC('S','V','Q','1'):
902         memcpy( fcc, "SVQ1", 4 );
903         break;
904
905     case VLC_FOURCC('S','V','Q','3'):
906         memcpy( fcc, "SVQ3", 4 );
907         break;
908
909     default:
910         memcpy( fcc, (char*)&p_stream->fmt.i_codec, 4 );
911         break;
912     }
913
914     vide = box_new( fcc );
915     for( i = 0; i < 6; i++ )
916     {
917         bo_add_8( vide, 0 );        // reserved;
918     }
919     bo_add_16be( vide, 1 );         // data-reference-index
920
921     bo_add_16be( vide, 0 );         // predefined;
922     bo_add_16be( vide, 0 );         // reserved;
923     for( i = 0; i < 3; i++ )
924     {
925         bo_add_32be( vide, 0 );     // predefined;
926     }
927
928     bo_add_16be( vide, p_stream->fmt.video.i_width );  // i_width
929     bo_add_16be( vide, p_stream->fmt.video.i_height ); // i_height
930
931     bo_add_32be( vide, 0x00480000 );                // h 72dpi
932     bo_add_32be( vide, 0x00480000 );                // v 72dpi
933
934     bo_add_32be( vide, 0 );         // data size, always 0
935     bo_add_16be( vide, 1 );         // frames count per sample
936
937     // compressor name;
938     for( i = 0; i < 32; i++ )
939     {
940         bo_add_8( vide, 0 );
941     }
942
943     bo_add_16be( vide, 0x18 );      // depth
944     bo_add_16be( vide, 0xffff );    // predefined
945
946     /* add an ES Descriptor */
947     switch( p_stream->fmt.i_codec )
948     {
949     case VLC_FOURCC('m','p','4','v'):
950     case VLC_FOURCC('m','p','g','v'):
951         {
952             bo_t *esds = GetESDS( p_stream );
953
954             box_fix( esds );
955             box_gather( vide, esds );
956         }
957         break;
958
959     case VLC_FOURCC('S','V','Q','3'):
960         {
961             bo_t *esds = GetSVQ3Tag( p_stream );
962
963             box_fix( esds );
964             box_gather( vide, esds );
965         }
966         break;
967
968     default:
969         break;
970     }
971
972     box_fix( vide );
973
974     return vide;
975 }
976
977 static bo_t *GetStblBox( sout_mux_t *p_mux, mp4_stream_t *p_stream )
978 {
979     sout_mux_sys_t *p_sys = p_mux->p_sys;
980     unsigned int i_chunk, i_stsc_last_val, i_stsc_entries, i, i_index;
981     bo_t *stbl, *stsd, *stts, *stco, *stsc, *stsz, *stss;
982     uint32_t i_timescale;
983     int64_t i_dts, i_dts_q;
984
985     stbl = box_new( "stbl" );
986
987     /* sample description */
988     stsd = box_full_new( "stsd", 0, 0 );
989     bo_add_32be( stsd, 1 );
990     if( p_stream->fmt.i_cat == AUDIO_ES )
991     {
992         bo_t *soun = GetSounBox( p_mux, p_stream );
993         box_gather( stsd, soun );
994     }
995     else if( p_stream->fmt.i_cat == VIDEO_ES )
996     {
997         bo_t *vide = GetVideBox( p_mux, p_stream );
998         box_gather( stsd, vide );
999     }
1000     box_fix( stsd );
1001
1002     /* chunk offset table */
1003     if( p_sys->i_pos >= (((uint64_t)0x1) << 32) )
1004     {
1005         /* 64 bits version */
1006         p_stream->b_stco64 = VLC_TRUE;
1007         stco = box_full_new( "co64", 0, 0 );
1008     }
1009     else
1010     {
1011         /* 32 bits version */
1012         p_stream->b_stco64 = VLC_FALSE;
1013         stco = box_full_new( "stco", 0, 0 );
1014     }
1015     bo_add_32be( stco, 0 );     // entry-count (fixed latter)
1016
1017     /* sample to chunk table */
1018     stsc = box_full_new( "stsc", 0, 0 );
1019     bo_add_32be( stsc, 0 );     // entry-count (fixed latter)
1020
1021     for( i_chunk = 0, i_stsc_last_val = 0, i_stsc_entries = 0, i = 0;
1022          i < p_stream->i_entry_count; i_chunk++ )
1023     {
1024         int i_first = i;
1025
1026         if( p_stream->b_stco64 )
1027             bo_add_64be( stco, p_stream->entry[i].i_pos );
1028         else
1029             bo_add_32be( stco, p_stream->entry[i].i_pos );
1030
1031         while( i < p_stream->i_entry_count )
1032         {
1033             if( i + 1 < p_stream->i_entry_count &&
1034                 p_stream->entry[i].i_pos + p_stream->entry[i].i_size
1035                 != p_stream->entry[i + 1].i_pos )
1036             {
1037                 i++;
1038                 break;
1039             }
1040
1041             i++;
1042         }
1043
1044         /* Add entry to the stsc table */
1045         if( i_stsc_last_val != i - i_first )
1046         {
1047             bo_add_32be( stsc, 1 + i_chunk );   // first-chunk
1048             bo_add_32be( stsc, i - i_first ) ;  // samples-per-chunk
1049             bo_add_32be( stsc, 1 );             // sample-descr-index
1050             i_stsc_last_val = i - i_first;
1051             i_stsc_entries++;
1052         }
1053     }
1054
1055     /* Fix stco entry count */
1056     bo_fix_32be( stco, 12, i_chunk );
1057     msg_Dbg( p_mux, "created %d chunks (stco)", i_chunk );
1058     box_fix( stco );
1059
1060     /* Fix stsc entry count */
1061     bo_fix_32be( stsc, 12, i_stsc_entries  );
1062     box_fix( stsc );
1063
1064     /* add stts */
1065     stts = box_full_new( "stts", 0, 0 );
1066     bo_add_32be( stts, 0 );     // entry-count (fixed latter)
1067
1068     if( p_stream->fmt.i_cat == AUDIO_ES )
1069         i_timescale = p_stream->fmt.audio.i_rate;
1070     else
1071         i_timescale = 1001;
1072
1073     /* first, create quantified length */
1074     for( i = 0, i_dts = 0, i_dts_q = 0; i < p_stream->i_entry_count; i++ )
1075     {
1076         int64_t i_dts_deq = i_dts_q * I64C(1000000) / (int64_t)i_timescale;
1077         int64_t i_delta = p_stream->entry[i].i_length + i_dts - i_dts_deq;
1078
1079         i_dts += p_stream->entry[i].i_length;
1080
1081         p_stream->entry[i].i_length =
1082             i_delta * (int64_t)i_timescale / I64C(1000000);
1083
1084         i_dts_q += p_stream->entry[i].i_length;
1085     }
1086     /* then write encoded table */
1087     for( i = 0, i_index = 0; i < p_stream->i_entry_count; i_index++)
1088     {
1089         int     i_first = i;
1090         int64_t i_delta = p_stream->entry[i].i_length;
1091
1092         while( i < p_stream->i_entry_count )
1093         {
1094             i++;
1095             if( i >= p_stream->i_entry_count ||
1096                 p_stream->entry[i].i_length != i_delta )
1097             {
1098                 break;
1099             }
1100         }
1101
1102         bo_add_32be( stts, i - i_first ); // sample-count
1103         bo_add_32be( stts, i_delta );     // sample-delta
1104     }
1105     bo_fix_32be( stts, 12, i_index );
1106     box_fix( stts );
1107
1108     /* FIXME add ctts ?? FIXME */
1109
1110     stsz = box_full_new( "stsz", 0, 0 );
1111     bo_add_32be( stsz, 0 );                             // sample-size
1112     bo_add_32be( stsz, p_stream->i_entry_count );       // sample-count
1113     for( i = 0; i < p_stream->i_entry_count; i++ )
1114     {
1115         bo_add_32be( stsz, p_stream->entry[i].i_size ); // sample-size
1116     }
1117     box_fix( stsz );
1118
1119     /* create stss table */
1120     stss = NULL;
1121     for( i = 0, i_index = 0; i < p_stream->i_entry_count; i++ )
1122     {
1123         if( p_stream->entry[i].i_flags & BLOCK_FLAG_TYPE_I )
1124         {
1125             if( stss == NULL )
1126             {
1127                 stss = box_full_new( "stss", 0, 0 );
1128                 bo_add_32be( stss, 0 ); /* fixed later */
1129             }
1130             bo_add_32be( stss, 1 + i );
1131             i_index++;
1132         }
1133     }
1134     if( stss )
1135     {
1136         bo_fix_32be( stss, 12, i_index );
1137         box_fix( stss );
1138     }
1139
1140     /* Now gather all boxes into stbl */
1141     box_gather( stbl, stsd );
1142     box_gather( stbl, stts );
1143     if( stss )
1144     {
1145         box_gather( stbl, stss );
1146     }
1147     box_gather( stbl, stsc );
1148     box_gather( stbl, stsz );
1149     p_stream->i_stco_pos = stbl->i_buffer + 16;
1150     box_gather( stbl, stco );
1151
1152     /* finish stbl */
1153     box_fix( stbl );
1154
1155     return stbl;
1156 }
1157
1158 static int64_t get_timestamp();
1159
1160 static uint32_t mvhd_matrix[9] =
1161     { 0x10000, 0, 0, 0, 0x10000, 0, 0, 0, 0x40000000 };
1162
1163 static bo_t *GetMoovBox( sout_mux_t *p_mux )
1164 {
1165     sout_mux_sys_t *p_sys = p_mux->p_sys;
1166
1167     bo_t            *moov, *mvhd;
1168     int             i_trak, i;
1169
1170     uint32_t        i_movie_timescale = 90000;
1171     int64_t         i_movie_duration  = 0;
1172
1173     moov = box_new( "moov" );
1174
1175     /* Create general info */
1176     for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ )
1177     {
1178         mp4_stream_t *p_stream = p_sys->pp_streams[i_trak];
1179         i_movie_duration = __MAX( i_movie_duration, p_stream->i_duration );
1180     }
1181     msg_Dbg( p_mux, "movie duration %ds",
1182              (uint32_t)( i_movie_duration / (mtime_t)1000000 ) );
1183
1184     i_movie_duration = i_movie_duration * i_movie_timescale / 1000000;
1185
1186     /* *** add /moov/mvhd *** */
1187     if( !p_sys->b_64_ext )
1188     {
1189         mvhd = box_full_new( "mvhd", 0, 0 );
1190         bo_add_32be( mvhd, get_timestamp() );   // creation time
1191         bo_add_32be( mvhd, get_timestamp() );   // modification time
1192         bo_add_32be( mvhd, i_movie_timescale);  // timescale
1193         bo_add_32be( mvhd, i_movie_duration );  // duration
1194     }
1195     else
1196     {
1197         mvhd = box_full_new( "mvhd", 1, 0 );
1198         bo_add_64be( mvhd, get_timestamp() );   // creation time
1199         bo_add_64be( mvhd, get_timestamp() );   // modification time
1200         bo_add_32be( mvhd, i_movie_timescale);  // timescale
1201         bo_add_64be( mvhd, i_movie_duration );  // duration
1202     }
1203     bo_add_32be( mvhd, 0x10000 );           // rate
1204     bo_add_16be( mvhd, 0x100 );             // volume
1205     bo_add_16be( mvhd, 0 );                 // reserved
1206     for( i = 0; i < 2; i++ )
1207     {
1208         bo_add_32be( mvhd, 0 );             // reserved
1209     }
1210     for( i = 0; i < 9; i++ )
1211     {
1212         bo_add_32be( mvhd, mvhd_matrix[i] );// matrix
1213     }
1214     for( i = 0; i < 6; i++ )
1215     {
1216         bo_add_32be( mvhd, 0 );             // pre-defined
1217     }
1218
1219     /* Next available track id */
1220     bo_add_32be( mvhd, p_sys->i_nb_streams + 1 ); // next-track-id
1221
1222     box_fix( mvhd );
1223     box_gather( moov, mvhd );
1224
1225     for( i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++ )
1226     {
1227         mp4_stream_t *p_stream;
1228         uint32_t     i_timescale;
1229
1230         bo_t *trak, *tkhd, *edts, *elst, *mdia, *mdhd, *hdlr;
1231         bo_t *minf, *dinf, *dref, *url, *stbl;
1232
1233         p_stream = p_sys->pp_streams[i_trak];
1234
1235         if( p_stream->fmt.i_cat != AUDIO_ES &&
1236             p_stream->fmt.i_cat != VIDEO_ES )
1237         {
1238             msg_Err( p_mux, "FIXME ignoring trak (noaudio&&novideo)" );
1239             continue;
1240         }
1241
1242         if( p_stream->fmt.i_cat == AUDIO_ES )
1243             i_timescale = p_stream->fmt.audio.i_rate;
1244         else
1245             i_timescale = 1001;
1246
1247         /* *** add /moov/trak *** */
1248         trak = box_new( "trak" );
1249
1250         /* *** add /moov/trak/tkhd *** */
1251         if( !p_sys->b_64_ext )
1252         {
1253             if( p_sys->b_mov )
1254                 tkhd = box_full_new( "tkhd", 0, 0x0f );
1255             else
1256                 tkhd = box_full_new( "tkhd", 0, 1 );
1257
1258             bo_add_32be( tkhd, get_timestamp() );       // creation time
1259             bo_add_32be( tkhd, get_timestamp() );       // modification time
1260             bo_add_32be( tkhd, p_stream->i_track_id );
1261             bo_add_32be( tkhd, 0 );                     // reserved 0
1262             bo_add_32be( tkhd, p_stream->i_duration *
1263                          (int64_t)i_movie_timescale /
1264                          (mtime_t)1000000 );            // duration
1265         }
1266         else
1267         {
1268             if( p_sys->b_mov )
1269                 tkhd = box_full_new( "tkhd", 1, 0x0f );
1270             else
1271                 tkhd = box_full_new( "tkhd", 1, 1 );
1272
1273             bo_add_64be( tkhd, get_timestamp() );       // creation time
1274             bo_add_64be( tkhd, get_timestamp() );       // modification time
1275             bo_add_32be( tkhd, p_stream->i_track_id );
1276             bo_add_32be( tkhd, 0 );                     // reserved 0
1277             bo_add_64be( tkhd, p_stream->i_duration *
1278                          (int64_t)i_movie_timescale /
1279                          (mtime_t)1000000 );            // duration
1280         }
1281
1282         for( i = 0; i < 2; i++ )
1283         {
1284             bo_add_32be( tkhd, 0 );                 // reserved
1285         }
1286         bo_add_16be( tkhd, 0 );                     // layer
1287         bo_add_16be( tkhd, 0 );                     // pre-defined
1288         // volume
1289         bo_add_16be( tkhd, p_stream->fmt.i_cat == AUDIO_ES ? 0x100 : 0 );
1290         bo_add_16be( tkhd, 0 );                     // reserved
1291         for( i = 0; i < 9; i++ )
1292         {
1293             bo_add_32be( tkhd, mvhd_matrix[i] );    // matrix
1294         }
1295         if( p_stream->fmt.i_cat == AUDIO_ES )
1296         {
1297             bo_add_32be( tkhd, 0 );                 // width (presentation)
1298             bo_add_32be( tkhd, 0 );                 // height(presentation)
1299         }
1300         else
1301         {
1302             // width (presentation)
1303             bo_add_32be( tkhd, p_stream->fmt.video.i_aspect *
1304                          p_stream->fmt.video.i_height /
1305                          VOUT_ASPECT_FACTOR << 16 );
1306             // height(presentation)
1307             bo_add_32be( tkhd, p_stream->fmt.video.i_height << 16 );
1308         }
1309         box_fix( tkhd );
1310         box_gather( trak, tkhd );
1311
1312         /* *** add /moov/trak/edts and elst */
1313         edts = box_new( "edts" );
1314         elst = box_full_new( "elst", p_sys->b_64_ext ? 1 : 0, 0 );
1315         if( p_stream->i_dts_start > p_sys->i_dts_start )
1316         {
1317             bo_add_32be( elst, 2 );
1318
1319             if( p_sys->b_64_ext )
1320             {
1321                 bo_add_64be( elst, (p_stream->i_dts_start-p_sys->i_dts_start) *
1322                              i_movie_timescale / I64C(1000000) );
1323                 bo_add_64be( elst, -1 );
1324             }
1325             else
1326             {
1327                 bo_add_32be( elst, (p_stream->i_dts_start-p_sys->i_dts_start) *
1328                              i_movie_timescale / I64C(1000000) );
1329                 bo_add_32be( elst, -1 );
1330             }
1331             bo_add_16be( elst, 1 );
1332             bo_add_16be( elst, 0 );
1333         }
1334         else
1335         {
1336             bo_add_32be( elst, 1 );
1337         }
1338         if( p_sys->b_64_ext )
1339         {
1340             bo_add_64be( elst, p_stream->i_duration *
1341                          i_movie_timescale / I64C(1000000) );
1342             bo_add_64be( elst, 0 );
1343         }
1344         else
1345         {
1346             bo_add_32be( elst, p_stream->i_duration *
1347                          i_movie_timescale / I64C(1000000) );
1348             bo_add_32be( elst, 0 );
1349         }
1350         bo_add_16be( elst, 1 );
1351         bo_add_16be( elst, 0 );
1352
1353         box_fix( elst );
1354         box_gather( edts, elst );
1355         box_fix( edts );
1356         box_gather( trak, edts );
1357
1358         /* *** add /moov/trak/mdia *** */
1359         mdia = box_new( "mdia" );
1360
1361         /* media header */
1362         if( !p_sys->b_64_ext )
1363         {
1364             mdhd = box_full_new( "mdhd", 0, 0 );
1365             bo_add_32be( mdhd, get_timestamp() );   // creation time
1366             bo_add_32be( mdhd, get_timestamp() );   // modification time
1367             bo_add_32be( mdhd, i_timescale);        // timescale
1368             bo_add_32be( mdhd, p_stream->i_duration * (int64_t)i_timescale /
1369                                (mtime_t)1000000 );  // duration
1370         }
1371         else
1372         {
1373             mdhd = box_full_new( "mdhd", 1, 0 );
1374             bo_add_64be( mdhd, get_timestamp() );   // creation time
1375             bo_add_64be( mdhd, get_timestamp() );   // modification time
1376             bo_add_32be( mdhd, i_timescale);        // timescale
1377             bo_add_64be( mdhd, p_stream->i_duration * (int64_t)i_timescale /
1378                                (mtime_t)1000000 );  // duration
1379         }
1380
1381         if( p_stream->fmt.psz_language )
1382         {
1383             char *psz = p_stream->fmt.psz_language;
1384             const iso639_lang_t *pl = NULL;
1385             uint16_t lang = 0x0;
1386
1387             if( strlen( psz ) == 2 )
1388             {
1389                 pl = GetLang_1( psz );
1390             }
1391             else if( strlen( psz ) == 3 )
1392             {
1393                 pl = GetLang_2B( psz );
1394                 if( !strcmp( pl->psz_iso639_1, "??" ) )
1395                 {
1396                     pl = GetLang_2T( psz );
1397                 }
1398             }
1399             if( pl && strcmp( pl->psz_iso639_1, "??" ) )
1400             {
1401                 lang = ( ( pl->psz_iso639_2T[0] - 0x60 ) << 10 ) |
1402                        ( ( pl->psz_iso639_2T[1] - 0x60 ) <<  5 ) |
1403                        ( ( pl->psz_iso639_2T[2] - 0x60 ) );
1404             }
1405             bo_add_16be( mdhd, lang );          // language
1406         }
1407         else
1408         {
1409             bo_add_16be( mdhd, 0    );          // language
1410         }
1411         bo_add_16be( mdhd, 0    );              // predefined
1412         box_fix( mdhd );
1413         box_gather( mdia, mdhd );
1414
1415         /* handler reference */
1416         hdlr = box_full_new( "hdlr", 0, 0 );
1417
1418         bo_add_fourcc( hdlr, "mhlr" );         // media handler
1419         if( p_stream->fmt.i_cat == AUDIO_ES )
1420         {
1421             bo_add_fourcc( hdlr, "soun" );
1422         }
1423         else
1424         {
1425             bo_add_fourcc( hdlr, "vide" );
1426         }
1427
1428         bo_add_32be( hdlr, 0 );         // reserved
1429         bo_add_32be( hdlr, 0 );         // reserved
1430         bo_add_32be( hdlr, 0 );         // reserved
1431
1432         bo_add_8( hdlr, 12 );
1433         if( p_stream->fmt.i_cat == AUDIO_ES )
1434             bo_add_mem( hdlr, 12, "SoundHandler" );
1435         else
1436             bo_add_mem( hdlr, 12, "VideoHandler" );
1437
1438         box_fix( hdlr );
1439         box_gather( mdia, hdlr );
1440
1441         /* minf*/
1442         minf = box_new( "minf" );
1443
1444         /* add smhd|vmhd */
1445         if( p_stream->fmt.i_cat == AUDIO_ES )
1446         {
1447             bo_t *smhd;
1448
1449             smhd = box_full_new( "smhd", 0, 0 );
1450             bo_add_16be( smhd, 0 );     // balance
1451             bo_add_16be( smhd, 0 );     // reserved
1452             box_fix( smhd );
1453
1454             box_gather( minf, smhd );
1455         }
1456         else if( p_stream->fmt.i_cat == VIDEO_ES )
1457         {
1458             bo_t *vmhd;
1459
1460             vmhd = box_full_new( "vmhd", 0, 1 );
1461             bo_add_16be( vmhd, 0 );     // graphicsmode
1462             for( i = 0; i < 3; i++ )
1463             {
1464                 bo_add_16be( vmhd, 0 ); // opcolor
1465             }
1466             box_fix( vmhd );
1467
1468             box_gather( minf, vmhd );
1469         }
1470
1471         /* dinf */
1472         dinf = box_new( "dinf" );
1473         dref = box_full_new( "dref", 0, 0 );
1474         bo_add_32be( dref, 1 );
1475         url = box_full_new( "url ", 0, 0x01 );
1476         box_fix( url );
1477         box_gather( dref, url );
1478         box_fix( dref );
1479         box_gather( dinf, dref );
1480
1481         /* append dinf to mdia */
1482         box_fix( dinf );
1483         box_gather( minf, dinf );
1484
1485         /* add stbl */
1486         stbl = GetStblBox( p_mux, p_stream );
1487
1488         /* append stbl to minf */
1489         p_stream->i_stco_pos += minf->i_buffer;
1490         box_gather( minf, stbl );
1491
1492         /* append minf to mdia */
1493         box_fix( minf );
1494         p_stream->i_stco_pos += mdia->i_buffer;
1495         box_gather( mdia, minf );
1496
1497         /* append mdia to trak */
1498         box_fix( mdia );
1499         p_stream->i_stco_pos += trak->i_buffer;
1500         box_gather( trak, mdia );
1501
1502         /* append trak to moov */
1503         box_fix( trak );
1504         p_stream->i_stco_pos += moov->i_buffer;
1505         box_gather( moov, trak );
1506     }
1507
1508     /* Add user data tags */
1509     box_gather( moov, GetUdtaTag( p_mux ) );
1510
1511     box_fix( moov );
1512     return moov;
1513 }
1514
1515 /****************************************************************************/
1516
1517 static void bo_init( bo_t *p_bo, int i_size, uint8_t *p_buffer,
1518                      vlc_bool_t b_grow )
1519 {
1520     if( !p_buffer )
1521     {
1522         p_bo->i_buffer_size = __MAX( i_size, 1024 );
1523         p_bo->p_buffer = malloc( p_bo->i_buffer_size );
1524     }
1525     else
1526     {
1527         p_bo->i_buffer_size = i_size;
1528         p_bo->p_buffer = p_buffer;
1529     }
1530
1531     p_bo->b_grow = b_grow;
1532     p_bo->i_buffer = 0;
1533 }
1534
1535 static void bo_add_8( bo_t *p_bo, uint8_t i )
1536 {
1537     if( p_bo->i_buffer < p_bo->i_buffer_size )
1538     {
1539         p_bo->p_buffer[p_bo->i_buffer] = i;
1540     }
1541     else if( p_bo->b_grow )
1542     {
1543         p_bo->i_buffer_size += 1024;
1544         p_bo->p_buffer = realloc( p_bo->p_buffer, p_bo->i_buffer_size );
1545
1546         p_bo->p_buffer[p_bo->i_buffer] = i;
1547     }
1548
1549     p_bo->i_buffer++;
1550 }
1551
1552 static void bo_add_16be( bo_t *p_bo, uint16_t i )
1553 {
1554     bo_add_8( p_bo, ( ( i >> 8) &0xff ) );
1555     bo_add_8( p_bo, i &0xff );
1556 }
1557
1558 static void bo_add_24be( bo_t *p_bo, uint32_t i )
1559 {
1560     bo_add_8( p_bo, ( ( i >> 16) &0xff ) );
1561     bo_add_8( p_bo, ( ( i >> 8) &0xff ) );
1562     bo_add_8( p_bo, (   i &0xff ) );
1563 }
1564 static void bo_add_32be( bo_t *p_bo, uint32_t i )
1565 {
1566     bo_add_16be( p_bo, ( ( i >> 16) &0xffff ) );
1567     bo_add_16be( p_bo, i &0xffff );
1568 }
1569
1570 static void bo_fix_32be ( bo_t *p_bo, int i_pos, uint32_t i)
1571 {
1572     p_bo->p_buffer[i_pos    ] = ( i >> 24 )&0xff;
1573     p_bo->p_buffer[i_pos + 1] = ( i >> 16 )&0xff;
1574     p_bo->p_buffer[i_pos + 2] = ( i >>  8 )&0xff;
1575     p_bo->p_buffer[i_pos + 3] = ( i       )&0xff;
1576 }
1577
1578 static void bo_add_64be( bo_t *p_bo, uint64_t i )
1579 {
1580     bo_add_32be( p_bo, ( ( i >> 32) &0xffffffff ) );
1581     bo_add_32be( p_bo, i &0xffffffff );
1582 }
1583
1584 static void bo_add_fourcc( bo_t *p_bo, char *fcc )
1585 {
1586     bo_add_8( p_bo, fcc[0] );
1587     bo_add_8( p_bo, fcc[1] );
1588     bo_add_8( p_bo, fcc[2] );
1589     bo_add_8( p_bo, fcc[3] );
1590 }
1591
1592 static void bo_add_mem( bo_t *p_bo, int i_size, uint8_t *p_mem )
1593 {
1594     int i;
1595
1596     for( i = 0; i < i_size; i++ )
1597     {
1598         bo_add_8( p_bo, p_mem[i] );
1599     }
1600 }
1601
1602 static void bo_add_descr( bo_t *p_bo, uint8_t tag, uint32_t i_size )
1603 {
1604     uint32_t i_length;
1605     uint8_t  vals[4];
1606
1607     i_length = i_size;
1608     vals[3] = (unsigned char)(i_length & 0x7f);
1609     i_length >>= 7;
1610     vals[2] = (unsigned char)((i_length & 0x7f) | 0x80); 
1611     i_length >>= 7;
1612     vals[1] = (unsigned char)((i_length & 0x7f) | 0x80); 
1613     i_length >>= 7;
1614     vals[0] = (unsigned char)((i_length & 0x7f) | 0x80);
1615
1616     bo_add_8( p_bo, tag );
1617
1618     if( i_size < 0x00000080 )
1619     {
1620         bo_add_8( p_bo, vals[3] );
1621     }
1622     else if( i_size < 0x00004000 )
1623     {
1624         bo_add_8( p_bo, vals[2] );
1625         bo_add_8( p_bo, vals[3] );
1626     }
1627     else if( i_size < 0x00200000 )
1628     {
1629         bo_add_8( p_bo, vals[1] );
1630         bo_add_8( p_bo, vals[2] );
1631         bo_add_8( p_bo, vals[3] );
1632     }
1633     else if( i_size < 0x10000000 )
1634     {
1635         bo_add_8( p_bo, vals[0] );
1636         bo_add_8( p_bo, vals[1] );
1637         bo_add_8( p_bo, vals[2] );
1638         bo_add_8( p_bo, vals[3] );
1639     }
1640 }
1641
1642 static void bo_add_bo( bo_t *p_bo, bo_t *p_bo2 )
1643 {
1644     int i;
1645
1646     for( i = 0; i < p_bo2->i_buffer; i++ )
1647     {
1648         bo_add_8( p_bo, p_bo2->p_buffer[i] );
1649     }
1650 }
1651
1652 static bo_t * box_new( char *fcc )
1653 {
1654     bo_t *box;
1655
1656     if( ( box = malloc( sizeof( bo_t ) ) ) )
1657     {
1658         bo_init( box, 0, NULL, VLC_TRUE );
1659
1660         bo_add_32be  ( box, 0 );
1661         bo_add_fourcc( box, fcc );
1662     }
1663
1664     return box;
1665 }
1666
1667 static bo_t * box_full_new( char *fcc, uint8_t v, uint32_t f )
1668 {
1669     bo_t *box;
1670
1671     if( ( box = malloc( sizeof( bo_t ) ) ) )
1672     {
1673         bo_init( box, 0, NULL, VLC_TRUE );
1674
1675         bo_add_32be  ( box, 0 );
1676         bo_add_fourcc( box, fcc );
1677         bo_add_8     ( box, v );
1678         bo_add_24be  ( box, f );
1679     }
1680
1681     return box;
1682 }
1683
1684 static void box_fix( bo_t *box )
1685 {
1686     bo_t box_tmp;
1687
1688     memcpy( &box_tmp, box, sizeof( bo_t ) );
1689
1690     box_tmp.i_buffer = 0;
1691     bo_add_32be( &box_tmp, box->i_buffer );
1692 }
1693
1694 static void box_free( bo_t *box )
1695 {
1696     if( box->p_buffer )
1697     {
1698         free( box->p_buffer );
1699     }
1700
1701     free( box );
1702 }
1703
1704 static void box_gather ( bo_t *box, bo_t *box2 )
1705 {
1706     bo_add_bo( box, box2 );
1707     box_free( box2 );
1708 }
1709
1710 static block_t * bo_to_sout( sout_instance_t *p_sout,  bo_t *box )
1711 {
1712     block_t *p_buf;
1713
1714     p_buf = block_New( p_sout, box->i_buffer );
1715     if( box->i_buffer > 0 )
1716     {
1717         memcpy( p_buf->p_buffer, box->p_buffer, box->i_buffer );
1718     }
1719
1720     return p_buf;
1721 }
1722
1723 static void box_send( sout_mux_t *p_mux,  bo_t *box )
1724 {
1725     block_t *p_buf;
1726
1727     p_buf = bo_to_sout( p_mux->p_sout, box );
1728     box_free( box );
1729
1730     sout_AccessOutWrite( p_mux->p_access, p_buf );
1731 }
1732
1733 static int64_t get_timestamp()
1734 {
1735     int64_t i_timestamp = 0;
1736
1737 #ifdef HAVE_TIME_H
1738     i_timestamp = time(NULL);
1739     i_timestamp += 2082844800; // MOV/MP4 start date is 1/1/1904
1740     // 208284480 is (((1970 - 1904) * 365) + 17) * 24 * 60 * 60
1741 #endif
1742
1743     return i_timestamp;
1744 }