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