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