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