1 /*****************************************************************************
2  * mp4.c: mp4/mov muxer
3  *****************************************************************************
4  * Copyright (C) 2001, 2002, 2003, 2006 VLC authors and VideoLAN
5  * $Id: 4c17a0e963aa43a6a42220836ec539ecd95adc58 $
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 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27 
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_sout.h>
35 #include <vlc_block.h>
36 
37 #include <assert.h>
38 #include <time.h>
39 
40 #include <vlc_iso_lang.h>
41 #include <vlc_meta.h>
42 
43 #include "../demux/mp4/libmp4.h"
44 #include "libmp4mux.h"
45 #include "../packetizer/hxxx_nal.h"
46 
47 /*****************************************************************************
48  * Module descriptor
49  *****************************************************************************/
50 #define FASTSTART_TEXT N_("Create \"Fast Start\" files")
51 #define FASTSTART_LONGTEXT N_(\
52     "Create \"Fast Start\" files. " \
53     "\"Fast Start\" files are optimized for downloads and allow the user " \
54     "to start previewing the file while it is downloading.")
55 
56 static int  Open   (vlc_object_t *);
57 static void Close  (vlc_object_t *);
58 static int  OpenFrag   (vlc_object_t *);
59 static void CloseFrag  (vlc_object_t *);
60 
61 #define SOUT_CFG_PREFIX "sout-mp4-"
62 
63 vlc_module_begin ()
64     set_description(N_("MP4/MOV muxer"))
65     set_category(CAT_SOUT)
66     set_subcategory(SUBCAT_SOUT_MUX)
67     set_shortname("MP4")
68 
69     add_bool(SOUT_CFG_PREFIX "faststart", true,
70               FASTSTART_TEXT, FASTSTART_LONGTEXT,
71               true)
72     set_capability("sout mux", 5)
73     add_shortcut("mp4", "mov", "3gp")
74     set_callbacks(Open, Close)
75 
76 add_submodule ()
77     set_description(N_("Fragmented and streamable MP4 muxer"))
78     set_category(CAT_SOUT)
79     set_subcategory(SUBCAT_SOUT_MUX)
80     set_shortname("MP4 Frag")
81     add_shortcut("mp4frag", "mp4stream")
82     set_capability("sout mux", 0)
83     set_callbacks(OpenFrag, CloseFrag)
84 
85 vlc_module_end ()
86 
87 /*****************************************************************************
88  * Exported prototypes
89  *****************************************************************************/
90 static const char *const ppsz_sout_options[] = {
91     "faststart", NULL
92 };
93 
94 static int Control(sout_mux_t *, int, va_list);
95 static int AddStream(sout_mux_t *, sout_input_t *);
96 static void DelStream(sout_mux_t *, sout_input_t *);
97 static int Mux      (sout_mux_t *);
98 static int MuxFrag  (sout_mux_t *);
99 
100 /*****************************************************************************
101  * Local prototypes
102  *****************************************************************************/
103 
104 typedef struct mp4_fragentry_t mp4_fragentry_t;
105 
106 struct mp4_fragentry_t
107 {
108     block_t  *p_block;
109     uint32_t  i_run;
110     mp4_fragentry_t *p_next;
111 };
112 
113 typedef struct mp4_fragindex_t
114 {
115     uint64_t i_moofoffset;
116     mtime_t  i_time;
117     uint8_t  i_traf;
118     uint8_t  i_trun;
119     uint32_t i_sample;
120 } mp4_fragindex_t;
121 
122 typedef struct mp4_fragqueue_t
123 {
124     mp4_fragentry_t *p_first;
125     mp4_fragentry_t *p_last;
126 } mp4_fragqueue_t;
127 
128 typedef struct
129 {
130     mp4mux_trackinfo_t mux;
131 
132     /* index */
133     int64_t      i_length_neg;
134 
135     /* applies to current segment only */
136     int64_t      i_first_dts;
137     int64_t      i_last_dts;
138     int64_t      i_last_pts;
139 
140     /*** mp4frag ***/
141     bool         b_hasiframes;
142 
143     uint32_t         i_current_run;
144     mp4_fragentry_t *p_held_entry;
145     mp4_fragqueue_t  read;
146     mp4_fragqueue_t  towrite;
147     mtime_t          i_last_iframe_time;
148     mtime_t          i_written_duration;
149     mp4_fragindex_t *p_indexentries;
150     uint32_t         i_indexentriesmax;
151     uint32_t         i_indexentries;
152 } mp4_stream_t;
153 
154 struct sout_mux_sys_t
155 {
156     bool b_mov;
157     bool b_3gp;
158     bool b_64_ext;
159     bool b_fast_start;
160 
161     /* global */
162     bool     b_header_sent;
163 
164     uint64_t i_mdat_pos;
165     uint64_t i_pos;
166     mtime_t  i_read_duration;
167     mtime_t  i_start_dts;
168 
169     unsigned int   i_nb_streams;
170     mp4_stream_t **pp_streams;
171 
172 
173     /* mp4frag */
174     bool           b_fragmented;
175     mtime_t        i_written_duration;
176     uint32_t       i_mfhd_sequence;
177 };
178 
179 static void box_send(sout_mux_t *p_mux,  bo_t *box);
180 static bo_t *BuildMoov(sout_mux_t *p_mux);
181 
182 static block_t *ConvertSUBT(block_t *);
183 static bool CreateCurrentEdit(mp4_stream_t *, mtime_t, bool);
184 static void DebugEdits(sout_mux_t *, const mp4_stream_t *);
185 
WriteSlowStartHeader(sout_mux_t * p_mux)186 static int WriteSlowStartHeader(sout_mux_t *p_mux)
187 {
188     sout_mux_sys_t *p_sys = p_mux->p_sys;
189     bo_t *box;
190 
191     if (!p_sys->b_mov) {
192         /* Now add ftyp header */
193         if(p_sys->b_3gp)
194         {
195             vlc_fourcc_t extra[] = {MAJOR_3gp4, MAJOR_avc1};
196             box = mp4mux_GetFtyp(MAJOR_3gp6, 0, extra, ARRAY_SIZE(extra));
197         }
198         else
199         {
200             vlc_fourcc_t extra[] = {MAJOR_mp41, MAJOR_avc1};
201             box = mp4mux_GetFtyp(MAJOR_isom, 0, extra, ARRAY_SIZE(extra));
202         }
203 
204         if(!box)
205             return VLC_ENOMEM;
206 
207         p_sys->i_pos += box->b->i_buffer;
208         p_sys->i_mdat_pos = p_sys->i_pos;
209         box_send(p_mux, box);
210     }
211 
212     /* Now add mdat header */
213     box = box_new("mdat");
214     if(!box)
215         return VLC_ENOMEM;
216 
217     bo_add_64be(box, 0); // enough to store an extended size
218 
219     if(box->b)
220         p_sys->i_pos += box->b->i_buffer;
221 
222     box_send(p_mux, box);
223 
224     return VLC_SUCCESS;
225 }
226 
227 /*****************************************************************************
228  * Open:
229  *****************************************************************************/
Open(vlc_object_t * p_this)230 static int Open(vlc_object_t *p_this)
231 {
232     sout_mux_t      *p_mux = (sout_mux_t*)p_this;
233     sout_mux_sys_t  *p_sys;
234 
235     msg_Dbg(p_mux, "Mp4 muxer opened");
236     config_ChainParse(p_mux, SOUT_CFG_PREFIX, ppsz_sout_options, p_mux->p_cfg);
237 
238     p_mux->pf_control   = Control;
239     p_mux->pf_addstream = AddStream;
240     p_mux->pf_delstream = DelStream;
241     p_mux->pf_mux       = Mux;
242     p_mux->p_sys        = p_sys = malloc(sizeof(sout_mux_sys_t));
243     if (!p_sys)
244         return VLC_ENOMEM;
245     p_sys->i_pos        = 0;
246     p_sys->i_nb_streams = 0;
247     p_sys->pp_streams   = NULL;
248     p_sys->i_mdat_pos   = 0;
249     p_sys->b_mov        = p_mux->psz_mux && !strcmp(p_mux->psz_mux, "mov");
250     p_sys->b_3gp        = p_mux->psz_mux && !strcmp(p_mux->psz_mux, "3gp");
251     p_sys->i_read_duration   = 0;
252     p_sys->i_start_dts = VLC_TS_INVALID;
253     p_sys->b_fragmented = false;
254     p_sys->b_header_sent = false;
255 
256     /* FIXME FIXME
257      * Quicktime actually doesn't like the 64 bits extensions !!! */
258     p_sys->b_64_ext = false;
259 
260     return VLC_SUCCESS;
261 }
262 
263 /*****************************************************************************
264  * Close:
265  *****************************************************************************/
Close(vlc_object_t * p_this)266 static void Close(vlc_object_t *p_this)
267 {
268     sout_mux_t      *p_mux = (sout_mux_t*)p_this;
269     sout_mux_sys_t  *p_sys = p_mux->p_sys;
270 
271     msg_Dbg(p_mux, "Close");
272 
273     /* Update mdat size */
274     bo_t bo;
275     if (!bo_init(&bo, 16))
276         goto cleanup;
277     if (p_sys->i_pos - p_sys->i_mdat_pos >= (((uint64_t)1)<<32)) {
278         /* Extended size */
279         bo_add_32be  (&bo, 1);
280         bo_add_fourcc(&bo, "mdat");
281         bo_add_64be  (&bo, p_sys->i_pos - p_sys->i_mdat_pos);
282     } else {
283         bo_add_32be  (&bo, 8);
284         bo_add_fourcc(&bo, "wide");
285         bo_add_32be  (&bo, p_sys->i_pos - p_sys->i_mdat_pos - 8);
286         bo_add_fourcc(&bo, "mdat");
287     }
288 
289     sout_AccessOutSeek(p_mux->p_access, p_sys->i_mdat_pos);
290     sout_AccessOutWrite(p_mux->p_access, bo.b);
291 
292     /* Create MOOV header */
293     const bool b_stco64 = (p_sys->i_pos >= (((uint64_t)0x1) << 32));
294     uint64_t i_moov_pos = p_sys->i_pos;
295     bo_t *moov = BuildMoov(p_mux);
296 
297     /* Check we need to create "fast start" files */
298     p_sys->b_fast_start = var_GetBool(p_this, SOUT_CFG_PREFIX "faststart");
299     while (p_sys->b_fast_start && moov && moov->b) {
300         /* Move data to the end of the file so we can fit the moov header
301          * at the start */
302         int64_t i_size = p_sys->i_pos - p_sys->i_mdat_pos;
303         int i_moov_size = moov->b->i_buffer;
304 
305         while (i_size > 0) {
306             int64_t i_chunk = __MIN(32768, i_size);
307             block_t *p_buf = block_Alloc(i_chunk);
308             sout_AccessOutSeek(p_mux->p_access,
309                                 p_sys->i_mdat_pos + i_size - i_chunk);
310             if (sout_AccessOutRead(p_mux->p_access, p_buf) < i_chunk) {
311                 msg_Warn(p_this, "read() not supported by access output, "
312                           "won't create a fast start file");
313                 p_sys->b_fast_start = false;
314                 block_Release(p_buf);
315                 break;
316             }
317             sout_AccessOutSeek(p_mux->p_access, p_sys->i_mdat_pos + i_size +
318                                 i_moov_size - i_chunk);
319             sout_AccessOutWrite(p_mux->p_access, p_buf);
320             i_size -= i_chunk;
321         }
322 
323         if (!p_sys->b_fast_start)
324             break;
325 
326         /* Update pos pointers */
327         i_moov_pos = p_sys->i_mdat_pos;
328         p_sys->i_mdat_pos += moov->b->i_buffer;
329 
330         /* Fix-up samples to chunks table in MOOV header */
331         for (unsigned int i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++) {
332             mp4_stream_t *p_stream = p_sys->pp_streams[i_trak];
333             unsigned i_written = 0;
334             for (unsigned i = 0; i < p_stream->mux.i_entry_count; ) {
335                 mp4mux_entry_t *entry = p_stream->mux.entry;
336                 if (b_stco64)
337                     bo_set_64be(moov, p_stream->mux.i_stco_pos + i_written++ * 8, entry[i].i_pos + p_sys->i_mdat_pos - i_moov_pos);
338                 else
339                     bo_set_32be(moov, p_stream->mux.i_stco_pos + i_written++ * 4, entry[i].i_pos + p_sys->i_mdat_pos - i_moov_pos);
340 
341                 for (; i < p_stream->mux.i_entry_count; i++)
342                     if (i >= p_stream->mux.i_entry_count - 1 ||
343                         entry[i].i_pos + entry[i].i_size != entry[i+1].i_pos) {
344                         i++;
345                         break;
346                     }
347             }
348         }
349 
350         p_sys->b_fast_start = false;
351     }
352 
353     /* Write MOOV header */
354     sout_AccessOutSeek(p_mux->p_access, i_moov_pos);
355     if (moov != NULL)
356         box_send(p_mux, moov);
357 
358 cleanup:
359     /* Clean-up */
360     for (unsigned int i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++) {
361         mp4_stream_t *p_stream = p_sys->pp_streams[i_trak];
362         mp4mux_trackinfo_Clear(&p_stream->mux);
363         free(p_stream);
364     }
365     TAB_CLEAN(p_sys->i_nb_streams, p_sys->pp_streams);
366     free(p_sys);
367 }
368 
369 /*****************************************************************************
370  * Control:
371  *****************************************************************************/
Control(sout_mux_t * p_mux,int i_query,va_list args)372 static int Control(sout_mux_t *p_mux, int i_query, va_list args)
373 {
374     VLC_UNUSED(p_mux);
375     bool *pb_bool;
376 
377     switch(i_query)
378     {
379     case MUX_CAN_ADD_STREAM_WHILE_MUXING:
380         pb_bool = va_arg(args, bool *);
381         *pb_bool = false;
382         return VLC_SUCCESS;
383 
384     case MUX_GET_ADD_STREAM_WAIT:
385         pb_bool = va_arg(args, bool *);
386         *pb_bool = true;
387         return VLC_SUCCESS;
388 
389     case MUX_GET_MIME:   /* Not needed, as not streamable */
390     default:
391         return VLC_EGENERIC;
392     }
393 }
394 
395 /*****************************************************************************
396  * AddStream:
397  *****************************************************************************/
AddStream(sout_mux_t * p_mux,sout_input_t * p_input)398 static int AddStream(sout_mux_t *p_mux, sout_input_t *p_input)
399 {
400     sout_mux_sys_t  *p_sys = p_mux->p_sys;
401     mp4_stream_t    *p_stream;
402 
403     if(!mp4mux_CanMux(VLC_OBJECT(p_mux), p_input->p_fmt))
404     {
405         msg_Err(p_mux, "unsupported codec %4.4s in mp4",
406                  (char*)&p_input->p_fmt->i_codec);
407         return VLC_EGENERIC;
408     }
409 
410     p_stream = malloc(sizeof(mp4_stream_t));
411     if (!p_stream ||
412         !mp4mux_trackinfo_Init(&p_stream->mux, p_sys->i_nb_streams + 1, CLOCK_FREQ))
413     {
414         free(p_stream);
415         return VLC_ENOMEM;
416     }
417 
418     es_format_Copy(&p_stream->mux.fmt, p_input->p_fmt);
419     p_stream->i_length_neg  = 0;
420     p_stream->i_first_dts   = VLC_TS_INVALID;
421     switch( p_stream->mux.fmt.i_cat )
422     {
423     case AUDIO_ES:
424         if(!p_stream->mux.fmt.audio.i_rate)
425         {
426             msg_Warn( p_mux, "no audio rate given for stream %d, assuming 48KHz",
427                       p_sys->i_nb_streams );
428             p_stream->mux.fmt.audio.i_rate = 48000;
429         }
430         p_stream->mux.i_timescale = p_stream->mux.fmt.audio.i_rate;
431         break;
432     case VIDEO_ES:
433         if( !p_stream->mux.fmt.video.i_frame_rate ||
434             !p_stream->mux.fmt.video.i_frame_rate_base )
435         {
436             msg_Warn( p_mux, "Missing frame rate for stream %d, assuming 25fps",
437                       p_sys->i_nb_streams );
438             p_stream->mux.fmt.video.i_frame_rate = 25;
439             p_stream->mux.fmt.video.i_frame_rate_base = 1;
440         }
441 
442         p_stream->mux.i_timescale = p_stream->mux.fmt.video.i_frame_rate *
443                                     p_stream->mux.fmt.video.i_frame_rate_base;
444 
445         if( p_stream->mux.i_timescale > CLOCK_FREQ )
446             p_stream->mux.i_timescale = CLOCK_FREQ;
447         else if( p_stream->mux.i_timescale < 90000 )
448             p_stream->mux.i_timescale = 90000;
449         break;
450     default:
451         break;
452     }
453 
454     p_stream->mux.p_edits = NULL;
455     p_stream->mux.i_edits_count = 0;
456     p_stream->mux.i_firstdts = VLC_TS_INVALID;
457     p_stream->i_last_dts    = VLC_TS_INVALID;
458     p_stream->i_last_pts    = VLC_TS_INVALID;
459 
460     p_stream->b_hasiframes  = false;
461 
462     p_stream->i_current_run = 0;
463     p_stream->read.p_first  = NULL;
464     p_stream->read.p_last   = NULL;
465     p_stream->towrite.p_first = NULL;
466     p_stream->towrite.p_last  = NULL;
467     p_stream->p_held_entry    = NULL;
468     p_stream->i_last_iframe_time = 0;
469     p_stream->i_written_duration = 0;
470     p_stream->p_indexentries     = NULL;
471     p_stream->i_indexentriesmax  = 0;
472     p_stream->i_indexentries     = 0;
473 
474     p_input->p_sys          = p_stream;
475 
476     msg_Dbg(p_mux, "adding input");
477 
478     TAB_APPEND(p_sys->i_nb_streams, p_sys->pp_streams, p_stream);
479     return VLC_SUCCESS;
480 }
481 
482 /*****************************************************************************
483  * DelStream:
484  *****************************************************************************/
DelStream(sout_mux_t * p_mux,sout_input_t * p_input)485 static void DelStream(sout_mux_t *p_mux, sout_input_t *p_input)
486 {
487     sout_mux_sys_t *p_sys = p_mux->p_sys;
488     mp4_stream_t *p_stream = (mp4_stream_t*)p_input->p_sys;
489 
490     if(!p_sys->b_fragmented &&
491         CreateCurrentEdit(p_stream, p_sys->i_start_dts, false))
492     {
493         DebugEdits(p_mux, p_stream);
494     }
495 
496     msg_Dbg(p_mux, "removing input");
497 }
498 
499 /*****************************************************************************
500  * Mux:
501  *****************************************************************************/
DebugEdits(sout_mux_t * p_mux,const mp4_stream_t * p_stream)502 static void DebugEdits(sout_mux_t *p_mux, const mp4_stream_t *p_stream)
503 {
504     for( unsigned i=0; i<p_stream->mux.i_edits_count; i++ )
505     {
506         msg_Dbg(p_mux, "tk %d elst media time %" PRId64 " duration %" PRIu64 " offset %" PRId64 ,
507                 p_stream->mux.i_track_id,
508                 p_stream->mux.p_edits[i].i_start_time,
509                 p_stream->mux.p_edits[i].i_duration,
510                 p_stream->mux.p_edits[i].i_start_offset);
511     }
512 }
513 
CreateCurrentEdit(mp4_stream_t * p_stream,mtime_t i_mux_start_dts,bool b_fragmented)514 static bool CreateCurrentEdit(mp4_stream_t *p_stream, mtime_t i_mux_start_dts,
515                               bool b_fragmented)
516 {
517     /* Never more than first empty edit for fragmented */
518     if(p_stream->mux.i_edits_count && b_fragmented)
519         return true;
520 
521     mp4mux_edit_t *p_realloc = realloc( p_stream->mux.p_edits, sizeof(mp4mux_edit_t) *
522                                        (p_stream->mux.i_edits_count + 1) );
523     if(unlikely(!p_realloc))
524         return false;
525 
526     mp4mux_edit_t *p_newedit = &p_realloc[p_stream->mux.i_edits_count];
527     if(p_stream->mux.i_edits_count == 0)
528     {
529         p_newedit->i_start_time = 0;
530         p_newedit->i_start_offset = p_stream->i_first_dts - i_mux_start_dts;
531     }
532     else
533     {
534         const mp4mux_edit_t *p_lastedit = &p_realloc[p_stream->mux.i_edits_count - 1];
535         p_newedit->i_start_time = p_lastedit->i_start_time + p_lastedit->i_duration;
536         p_newedit->i_start_offset = 0;
537     }
538 
539     if(b_fragmented)
540     {
541         p_newedit->i_duration = 0;
542     }
543     else
544     {
545         if(p_stream->i_last_pts > VLC_TS_INVALID)
546             p_newedit->i_duration = p_stream->i_last_pts - p_stream->i_first_dts;
547         else
548             p_newedit->i_duration = p_stream->i_last_dts - p_stream->i_first_dts;
549         if(p_stream->mux.i_entry_count)
550             p_newedit->i_duration += p_stream->mux.entry[p_stream->mux.i_entry_count - 1].i_length;
551     }
552 
553     p_stream->mux.p_edits = p_realloc;
554     p_stream->mux.i_edits_count++;
555 
556     return true;
557 }
558 
BlockDequeue(sout_input_t * p_input,mp4_stream_t * p_stream)559 static block_t * BlockDequeue(sout_input_t *p_input, mp4_stream_t *p_stream)
560 {
561     block_t *p_block = block_FifoGet(p_input->p_fifo);
562     if(unlikely(!p_block))
563         return NULL;
564 
565     switch(p_stream->mux.fmt.i_codec)
566     {
567         case VLC_CODEC_H264:
568         case VLC_CODEC_HEVC:
569             p_block = hxxx_AnnexB_to_xVC(p_block, 4);
570             break;
571         case VLC_CODEC_SUBT:
572             p_block = ConvertSUBT(p_block);
573             break;
574         case VLC_CODEC_A52:
575         case VLC_CODEC_EAC3:
576             if (p_stream->mux.a52_frame == NULL && p_block->i_buffer >= 8)
577                 p_stream->mux.a52_frame = block_Duplicate(p_block);
578             break;
579         default:
580             break;
581     }
582 
583     return p_block;
584 }
585 
dts_fb_pts(const block_t * p_data)586 static inline mtime_t dts_fb_pts( const block_t *p_data )
587 {
588     return p_data->i_dts > VLC_TS_INVALID ? p_data->i_dts: p_data->i_pts;
589 }
590 
Mux(sout_mux_t * p_mux)591 static int Mux(sout_mux_t *p_mux)
592 {
593     sout_mux_sys_t *p_sys = p_mux->p_sys;
594 
595     if(!p_sys->b_header_sent)
596     {
597         int i_ret = WriteSlowStartHeader(p_mux);
598         if(i_ret != VLC_SUCCESS)
599             return i_ret;
600         p_sys->b_header_sent = true;
601     }
602 
603     for (;;) {
604         int i_stream = sout_MuxGetStream(p_mux, 2, NULL);
605         if (i_stream < 0)
606             return(VLC_SUCCESS);
607 
608         sout_input_t *p_input  = p_mux->pp_inputs[i_stream];
609         mp4_stream_t *p_stream = (mp4_stream_t*)p_input->p_sys;
610 
611         block_t *p_data = BlockDequeue(p_input, p_stream);
612         if(!p_data)
613             return VLC_SUCCESS;
614 
615         /* Reset reference dts in case of discontinuity (ex: gather sout) */
616         if (p_data->i_flags & BLOCK_FLAG_DISCONTINUITY && p_stream->mux.i_entry_count)
617         {
618             if(p_stream->i_first_dts != VLC_TS_INVALID)
619             {
620                 if(!CreateCurrentEdit(p_stream, p_sys->i_start_dts, p_sys->b_fragmented))
621                 {
622                     block_Release( p_data );
623                     return VLC_ENOMEM;
624                 }
625             }
626 
627             p_stream->i_length_neg = 0;
628             p_stream->i_first_dts = VLC_TS_INVALID;
629             p_stream->i_last_dts = VLC_TS_INVALID;
630             p_stream->i_last_pts = VLC_TS_INVALID;
631         }
632 
633         /* XXX: -1 to always have 2 entry for easy adding of empty SPU */
634         if (p_stream->mux.i_entry_count >= p_stream->mux.i_entry_max - 2) {
635             p_stream->mux.i_entry_max += 1000;
636             p_stream->mux.entry = xrealloc(p_stream->mux.entry,
637                          p_stream->mux.i_entry_max * sizeof(mp4mux_entry_t));
638         }
639 
640         /* Set current segment ranges */
641         if( p_stream->i_first_dts == VLC_TS_INVALID )
642         {
643             p_stream->i_first_dts = dts_fb_pts( p_data );
644             if( p_sys->i_start_dts == VLC_TS_INVALID )
645                 p_sys->i_start_dts = p_stream->i_first_dts;
646         }
647 
648         if (p_stream->mux.fmt.i_cat != SPU_ES) {
649             /* Fix length of the sample */
650             if (block_FifoCount(p_input->p_fifo) > 0) {
651                 block_t *p_next = block_FifoShow(p_input->p_fifo);
652                 if ( p_next->i_flags & BLOCK_FLAG_DISCONTINUITY )
653                 { /* we have no way to know real length except by decoding */
654                     if ( p_stream->mux.fmt.i_cat == VIDEO_ES )
655                     {
656                         p_data->i_length = CLOCK_FREQ *
657                                            p_stream->mux.fmt.video.i_frame_rate_base /
658                                            p_stream->mux.fmt.video.i_frame_rate;
659                         if( p_data->i_flags & BLOCK_FLAG_SINGLE_FIELD )
660                             p_data->i_length >>= 1;
661                         msg_Dbg( p_mux, "video track %u fixup to %"PRId64" for sample %u",
662                                  p_stream->mux.i_track_id, p_data->i_length, p_stream->mux.i_entry_count );
663                     }
664                     else if ( p_stream->mux.fmt.i_cat == AUDIO_ES &&
665                               p_stream->mux.fmt.audio.i_rate &&
666                               p_data->i_nb_samples )
667                     {
668                         p_data->i_length = CLOCK_FREQ * p_data->i_nb_samples /
669                                            p_stream->mux.fmt.audio.i_rate;
670                         msg_Dbg( p_mux, "audio track %u fixup to %"PRId64" for sample %u",
671                                  p_stream->mux.i_track_id, p_data->i_length, p_stream->mux.i_entry_count );
672                     }
673                     else if ( p_data->i_length <= 0 )
674                     {
675                         msg_Warn( p_mux, "unknown length for track %u sample %u",
676                                   p_stream->mux.i_track_id, p_stream->mux.i_entry_count );
677                         p_data->i_length = 1;
678                     }
679                 }
680                 else
681                 {
682                     int64_t i_diff  = dts_fb_pts( p_next ) - dts_fb_pts( p_data );
683                     if (i_diff < CLOCK_FREQ) /* protection */
684                         p_data->i_length = i_diff;
685                 }
686             }
687             if (p_data->i_length <= 0) {
688                 msg_Warn(p_mux, "i_length <= 0");
689                 p_stream->i_length_neg += p_data->i_length - 1;
690                 p_data->i_length = 1;
691             } else if (p_stream->i_length_neg < 0) {
692                 int64_t i_recover = __MIN(p_data->i_length / 4, - p_stream->i_length_neg);
693 
694                 p_data->i_length -= i_recover;
695                 p_stream->i_length_neg += i_recover;
696             }
697         }
698 
699         if (p_stream->mux.fmt.i_cat == SPU_ES && p_stream->mux.i_entry_count > 0)
700         {
701              /* length of previous spu, stored in spu clearer */
702             int64_t i_length = dts_fb_pts( p_data ) - p_stream->i_last_dts;
703             if(i_length < 0)
704                 i_length = 0;
705             assert( p_stream->mux.entry[p_stream->mux.i_entry_count-1].i_length == 0 );
706             assert( p_stream->mux.entry[p_stream->mux.i_entry_count-1].i_size == 3 );
707             /* Fix entry */
708             p_stream->mux.entry[p_stream->mux.i_entry_count-1].i_length = i_length;
709             p_stream->mux.i_read_duration += i_length;
710         }
711 
712         /* Update (Not earlier for SPU!) */
713         p_stream->i_last_dts = dts_fb_pts( p_data );
714         if( p_data->i_pts > p_stream->i_last_pts )
715             p_stream->i_last_pts = p_data->i_pts;
716 
717         /* add index entry */
718         mp4mux_entry_t *e = &p_stream->mux.entry[p_stream->mux.i_entry_count++];
719         e->i_pos    = p_sys->i_pos;
720         e->i_size   = p_data->i_buffer;
721 
722         if ( p_data->i_dts > VLC_TS_INVALID && p_data->i_pts > p_data->i_dts )
723         {
724             e->i_pts_dts = p_data->i_pts - p_data->i_dts;
725             if ( !p_stream->mux.b_hasbframes )
726                 p_stream->mux.b_hasbframes = true;
727         }
728         else e->i_pts_dts = 0;
729 
730         e->i_length = p_data->i_length;
731         e->i_flags  = p_data->i_flags;
732 
733         /* update */
734         p_stream->mux.i_read_duration += __MAX( 0, p_data->i_length );
735         p_stream->i_last_dts = dts_fb_pts( p_data );
736 
737         /* write data */
738         p_sys->i_pos += p_data->i_buffer;
739         sout_AccessOutWrite(p_mux->p_access, p_data);
740 
741         /* Add SPU clearing tag (duration tb fixed on next SPU or stream end )*/
742         if (p_stream->mux.fmt.i_cat == SPU_ES)
743         {
744             block_t *p_empty = block_Alloc(3);
745             if (p_empty)
746             {
747                 /* point to start of our empty */
748                 p_stream->i_last_dts += e->i_length;
749 
750                 /* Write a " " */
751                 p_empty->p_buffer[0] = 0;
752                 p_empty->p_buffer[1] = 1;
753                 p_empty->p_buffer[2] = ' ';
754 
755                 /* Append a idx entry */
756                 /* XXX: No need to grow the entry here */
757                 mp4mux_entry_t *e_empty = &p_stream->mux.entry[p_stream->mux.i_entry_count++];
758                 e_empty->i_pos    = p_sys->i_pos;
759                 e_empty->i_size   = 3;
760                 e_empty->i_pts_dts= 0;
761                 e_empty->i_length = 0; /* will add dts diff later*/
762                 e_empty->i_flags  = 0;
763 
764                 p_sys->i_pos += p_empty->i_buffer;
765                 sout_AccessOutWrite(p_mux->p_access, p_empty);
766             }
767         }
768 
769         /* Update the global segment/media duration */
770         if( p_stream->mux.i_read_duration > p_sys->i_read_duration )
771             p_sys->i_read_duration = p_stream->mux.i_read_duration;
772     }
773 
774     return(VLC_SUCCESS);
775 }
776 
777 /*****************************************************************************
778  *
779  *****************************************************************************/
ConvertSUBT(block_t * p_block)780 static block_t *ConvertSUBT(block_t *p_block)
781 {
782     p_block = block_Realloc(p_block, 2, p_block->i_buffer);
783     if( !p_block )
784         return NULL;
785     /* No trailling '\0' */
786     if (p_block->i_buffer > 2 && p_block->p_buffer[p_block->i_buffer-1] == '\0')
787         p_block->i_buffer--;
788 
789     p_block->p_buffer[0] = ((p_block->i_buffer - 2) >> 8)&0xff;
790     p_block->p_buffer[1] = ((p_block->i_buffer - 2)     )&0xff;
791 
792     return p_block;
793 }
794 
box_send(sout_mux_t * p_mux,bo_t * box)795 static void box_send(sout_mux_t *p_mux,  bo_t *box)
796 {
797     assert(box != NULL);
798     if (box->b)
799         sout_AccessOutWrite(p_mux->p_access, box->b);
800     free(box);
801 }
802 
803 /***************************************************************************
804     MP4 Live submodule
805 ****************************************************************************/
806 #define FRAGMENT_LENGTH  (CLOCK_FREQ * 3/2)
807 
808 #define ENQUEUE_ENTRY(object, entry) \
809     do {\
810         if (object.p_last)\
811             object.p_last->p_next = entry;\
812         object.p_last = entry;\
813         if (!object.p_first)\
814             object.p_first = entry;\
815     } while(0)
816 
817 #define DEQUEUE_ENTRY(object, entry) \
818     do {\
819         entry = object.p_first;\
820         if (object.p_last == entry)\
821             object.p_last = NULL;\
822         object.p_first = object.p_first->p_next;\
823         entry->p_next = NULL;\
824     } while(0)
825 
826 /* Creates mfra/traf index entries */
AddKeyframeEntry(mp4_stream_t * p_stream,const uint64_t i_moof_pos,const uint8_t i_traf,const uint32_t i_sample,const mtime_t i_time)827 static void AddKeyframeEntry(mp4_stream_t *p_stream, const uint64_t i_moof_pos,
828                              const uint8_t i_traf, const uint32_t i_sample,
829                              const mtime_t i_time)
830 {
831     /* alloc or realloc */
832     mp4_fragindex_t *p_entries = p_stream->p_indexentries;
833     if (p_stream->i_indexentries >= p_stream->i_indexentriesmax)
834     {
835         p_stream->i_indexentriesmax += 256;
836         p_entries = xrealloc(p_stream->p_indexentries,
837                              p_stream->i_indexentriesmax * sizeof(mp4_fragindex_t));
838         if (p_entries) /* realloc can fail */
839             p_stream->p_indexentries = p_entries;
840     }
841 
842     mtime_t i_last_entry_time;
843     if (p_stream->i_indexentries)
844         i_last_entry_time = p_stream->p_indexentries[p_stream->i_indexentries - 1].i_time;
845     else
846         i_last_entry_time = 0;
847 
848     if (p_entries && i_time - i_last_entry_time >= CLOCK_FREQ * 2)
849     {
850         mp4_fragindex_t *p_indexentry = &p_stream->p_indexentries[p_stream->i_indexentries];
851         p_indexentry->i_time = i_time;
852         p_indexentry->i_moofoffset = i_moof_pos;
853         p_indexentry->i_sample = i_sample;
854         p_indexentry->i_traf = i_traf;
855         p_indexentry->i_trun = 1;
856         p_stream->i_indexentries++;
857     }
858 }
859 
860 /* Creates moof box and traf/trun information.
861  * Single run per traf is absolutely not optimal as interleaving should be done
862  * using runs and not limiting moof size, but creating an relative offset only
863  * requires base_offset_is_moof and then comply to late iso brand spec which
864  * breaks clients. */
GetMoofBox(sout_mux_t * p_mux,size_t * pi_mdat_total_size,mtime_t i_barrier_time,const uint64_t i_write_pos)865 static bo_t *GetMoofBox(sout_mux_t *p_mux, size_t *pi_mdat_total_size,
866                         mtime_t i_barrier_time, const uint64_t i_write_pos)
867 {
868     sout_mux_sys_t *p_sys = p_mux->p_sys;
869 
870     bo_t            *moof, *mfhd;
871     size_t           i_fixupoffset = 0;
872 
873     *pi_mdat_total_size = 0;
874 
875     moof = box_new("moof");
876     if(!moof)
877         return NULL;
878 
879     /* *** add /moof/mfhd *** */
880 
881     mfhd = box_full_new("mfhd", 0, 0);
882     if(!mfhd)
883     {
884         bo_free(moof);
885         return NULL;
886     }
887     bo_add_32be(mfhd, p_sys->i_mfhd_sequence++);   // sequence number
888 
889     box_gather(moof, mfhd);
890 
891     for (unsigned int i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++)
892     {
893         mp4_stream_t *p_stream = p_sys->pp_streams[i_trak];
894 
895         /* *** add /moof/traf *** */
896         bo_t *traf = box_new("traf");
897         if(!traf)
898             continue;
899         uint32_t i_sample = 0;
900         mtime_t i_time = p_stream->i_written_duration;
901         bool b_allsamesize = true;
902         bool b_allsamelength = true;
903         if ( p_stream->read.p_first )
904         {
905             mp4_fragentry_t *p_entry = p_stream->read.p_first->p_next;
906             while (p_entry && (b_allsamelength || b_allsamesize))
907             {
908                 /* compare against queue head */
909                 b_allsamelength &= ( p_entry->p_block->i_length == p_stream->read.p_first->p_block->i_length );
910                 b_allsamesize &= ( p_entry->p_block->i_buffer == p_stream->read.p_first->p_block->i_buffer );
911                 p_entry = p_entry->p_next;
912             }
913         }
914 
915         uint32_t i_tfhd_flags = 0x0;
916         if (p_stream->read.p_first)
917         {
918             /* Current segment have all same duration value, different than trex's default */
919             if (b_allsamelength &&
920                 p_stream->read.p_first->p_block->i_length != p_stream->mux.i_trex_default_length &&
921                 p_stream->read.p_first->p_block->i_length)
922                     i_tfhd_flags |= MP4_TFHD_DFLT_SAMPLE_DURATION;
923 
924             /* Current segment have all same size value, different than trex's default */
925             if (b_allsamesize &&
926                 p_stream->read.p_first->p_block->i_buffer != p_stream->mux.i_trex_default_size &&
927                 p_stream->read.p_first->p_block->i_buffer)
928                     i_tfhd_flags |= MP4_TFHD_DFLT_SAMPLE_SIZE;
929         }
930         else
931         {
932             /* We have no samples */
933             i_tfhd_flags |= MP4_TFHD_DURATION_IS_EMPTY;
934         }
935 
936         /* *** add /moof/traf/tfhd *** */
937         bo_t *tfhd = box_full_new("tfhd", 0, i_tfhd_flags);
938         if(!tfhd)
939         {
940             bo_free(traf);
941             continue;
942         }
943         bo_add_32be(tfhd, p_stream->mux.i_track_id);
944 
945         /* set the local sample duration default */
946         if (i_tfhd_flags & MP4_TFHD_DFLT_SAMPLE_DURATION)
947             bo_add_32be(tfhd, p_stream->read.p_first->p_block->i_length * p_stream->mux.i_timescale / CLOCK_FREQ);
948 
949         /* set the local sample size default */
950         if (i_tfhd_flags & MP4_TFHD_DFLT_SAMPLE_SIZE)
951             bo_add_32be(tfhd, p_stream->read.p_first->p_block->i_buffer);
952 
953         box_gather(traf, tfhd);
954 
955         /* *** add /moof/traf/tfdt *** */
956         bo_t *tfdt = box_full_new("tfdt", 1, 0);
957         if(!tfdt)
958         {
959             bo_free(traf);
960             continue;
961         }
962         bo_add_64be(tfdt, p_stream->i_written_duration * p_stream->mux.i_timescale / CLOCK_FREQ );
963         box_gather(traf, tfdt);
964 
965         /* *** add /moof/traf/trun *** */
966         if (p_stream->read.p_first)
967         {
968             uint32_t i_trun_flags = 0x0;
969 
970             if (p_stream->b_hasiframes && !(p_stream->read.p_first->p_block->i_flags & BLOCK_FLAG_TYPE_I))
971                 i_trun_flags |= MP4_TRUN_FIRST_FLAGS;
972 
973             if (!b_allsamelength ||
974                 ( !(i_tfhd_flags & MP4_TFHD_DFLT_SAMPLE_DURATION) && p_stream->mux.i_trex_default_length == 0 ))
975                 i_trun_flags |= MP4_TRUN_SAMPLE_DURATION;
976 
977             if (!b_allsamesize ||
978                 ( !(i_tfhd_flags & MP4_TFHD_DFLT_SAMPLE_SIZE) && p_stream->mux.i_trex_default_size == 0 ))
979                 i_trun_flags |= MP4_TRUN_SAMPLE_SIZE;
980 
981             if (p_stream->mux.b_hasbframes)
982                 i_trun_flags |= MP4_TRUN_SAMPLE_TIME_OFFSET;
983 
984             if (i_fixupoffset == 0)
985                 i_trun_flags |= MP4_TRUN_DATA_OFFSET;
986 
987             bo_t *trun = box_full_new("trun", 0, i_trun_flags);
988             if(!trun)
989             {
990                 bo_free(traf);
991                 continue;
992             }
993 
994             /* count entries */
995             uint32_t i_entry_count = 0;
996             mtime_t i_run_time = p_stream->i_written_duration;
997             mp4_fragentry_t *p_entry = p_stream->read.p_first;
998             while(p_entry)
999             {
1000                 if ( i_barrier_time && i_run_time + p_entry->p_block->i_length > i_barrier_time )
1001                     break;
1002                 i_entry_count++;
1003                 i_run_time += p_entry->p_block->i_length;
1004                 p_entry = p_entry->p_next;
1005             }
1006             bo_add_32be(trun, i_entry_count); // sample count
1007 
1008             if (i_trun_flags & MP4_TRUN_DATA_OFFSET)
1009             {
1010                 i_fixupoffset = moof->b->i_buffer + traf->b->i_buffer + trun->b->i_buffer;
1011                 bo_add_32be(trun, 0xdeadbeef); // data offset
1012             }
1013 
1014             if (i_trun_flags & MP4_TRUN_FIRST_FLAGS)
1015                 bo_add_32be(trun, 1<<16); // flag as non keyframe
1016 
1017             while(p_stream->read.p_first && i_entry_count)
1018             {
1019                 DEQUEUE_ENTRY(p_stream->read, p_entry);
1020 
1021                 if (i_trun_flags & MP4_TRUN_SAMPLE_DURATION)
1022                     bo_add_32be(trun, p_entry->p_block->i_length * p_stream->mux.i_timescale / CLOCK_FREQ); // sample duration
1023 
1024                 if (i_trun_flags & MP4_TRUN_SAMPLE_SIZE)
1025                     bo_add_32be(trun, p_entry->p_block->i_buffer); // sample size
1026 
1027                 if (i_trun_flags & MP4_TRUN_SAMPLE_TIME_OFFSET)
1028                 {
1029                     uint32_t i_diff = 0;
1030                     if ( p_entry->p_block->i_dts  > VLC_TS_INVALID &&
1031                          p_entry->p_block->i_pts > p_entry->p_block->i_dts )
1032                     {
1033                         i_diff = p_entry->p_block->i_pts - p_entry->p_block->i_dts;
1034                     }
1035                     bo_add_32be(trun, i_diff * p_stream->mux.i_timescale / CLOCK_FREQ); // ctts
1036                 }
1037 
1038                 *pi_mdat_total_size += p_entry->p_block->i_buffer;
1039 
1040                 ENQUEUE_ENTRY(p_stream->towrite, p_entry);
1041                 i_entry_count--;
1042                 i_sample++;
1043 
1044                 /* Add keyframe entry if needed */
1045                 if (p_stream->b_hasiframes && (p_entry->p_block->i_flags & BLOCK_FLAG_TYPE_I) &&
1046                     (p_stream->mux.fmt.i_cat == VIDEO_ES || p_stream->mux.fmt.i_cat == AUDIO_ES))
1047                 {
1048                     AddKeyframeEntry(p_stream, i_write_pos, i_trak, i_sample, i_time);
1049                 }
1050 
1051                 i_time += p_entry->p_block->i_length;
1052             }
1053 
1054             box_gather(traf, trun);
1055         }
1056 
1057         box_gather(moof, traf);
1058     }
1059 
1060     if(!moof->b)
1061     {
1062         bo_free(moof);
1063         return NULL;
1064     }
1065 
1066     box_fix(moof, moof->b->i_buffer);
1067 
1068     /* do tfhd base data offset fixup */
1069     if (i_fixupoffset)
1070     {
1071         /* mdat will follow moof */
1072         bo_set_32be(moof, i_fixupoffset, moof->b->i_buffer + 8);
1073     }
1074 
1075     /* set iframe flag, so the streaming server always starts from moof */
1076     moof->b->i_flags |= BLOCK_FLAG_TYPE_I;
1077 
1078     return moof;
1079 }
1080 
WriteFragmentMDAT(sout_mux_t * p_mux,size_t i_total_size)1081 static void WriteFragmentMDAT(sout_mux_t *p_mux, size_t i_total_size)
1082 {
1083     sout_mux_sys_t *p_sys = p_mux->p_sys;
1084 
1085     /* Now add mdat header */
1086     bo_t *mdat = box_new("mdat");
1087     if(!mdat)
1088         return;
1089     /* force update of real size */
1090     assert(mdat->b->i_buffer==8);
1091     box_fix(mdat, mdat->b->i_buffer + i_total_size);
1092     p_sys->i_pos += mdat->b->i_buffer;
1093     /* only write header */
1094     sout_AccessOutWrite(p_mux->p_access, mdat->b);
1095     free(mdat);
1096     /* Header and its size are written and good, now write content */
1097     for (unsigned int i_trak = 0; i_trak < p_sys->i_nb_streams; i_trak++)
1098     {
1099         mp4_stream_t *p_stream = p_sys->pp_streams[i_trak];
1100 
1101         while(p_stream->towrite.p_first)
1102         {
1103             mp4_fragentry_t *p_entry = p_stream->towrite.p_first;
1104             p_sys->i_pos += p_entry->p_block->i_buffer;
1105             p_stream->i_written_duration += p_entry->p_block->i_length;
1106 
1107             p_entry->p_block->i_flags &= ~BLOCK_FLAG_TYPE_I; // clear flag for http stream
1108             sout_AccessOutWrite(p_mux->p_access, p_entry->p_block);
1109 
1110             p_stream->towrite.p_first = p_entry->p_next;
1111             free(p_entry);
1112             if (!p_stream->towrite.p_first)
1113                 p_stream->towrite.p_last = NULL;
1114         }
1115     }
1116 }
1117 
GetMfraBox(sout_mux_t * p_mux)1118 static bo_t *GetMfraBox(sout_mux_t *p_mux)
1119 {
1120     sout_mux_sys_t *p_sys = (sout_mux_sys_t*) p_mux->p_sys;
1121     bo_t *mfra = NULL;
1122     for (unsigned int i = 0; i < p_sys->i_nb_streams; i++)
1123     {
1124         mp4_stream_t *p_stream = p_sys->pp_streams[i];
1125         if (p_stream->i_indexentries)
1126         {
1127             bo_t *tfra = box_full_new("tfra", 0, 0x0);
1128             if (!tfra) continue;
1129             bo_add_32be(tfra, p_stream->mux.i_track_id);
1130             bo_add_32be(tfra, 0x3); // reserved + lengths (1,1,4)=>(0,0,3)
1131             bo_add_32be(tfra, p_stream->i_indexentries);
1132             for(uint32_t i_index=0; i_index<p_stream->i_indexentries; i_index++)
1133             {
1134                 const mp4_fragindex_t *p_indexentry = &p_stream->p_indexentries[i_index];
1135                 bo_add_32be(tfra, p_indexentry->i_time);
1136                 bo_add_32be(tfra, p_indexentry->i_moofoffset);
1137                 assert(sizeof(p_indexentry->i_traf)==1); /* guard against sys changes */
1138                 assert(sizeof(p_indexentry->i_trun)==1);
1139                 assert(sizeof(p_indexentry->i_sample)==4);
1140                 bo_add_8(tfra, p_indexentry->i_traf);
1141                 bo_add_8(tfra, p_indexentry->i_trun);
1142                 bo_add_32be(tfra, p_indexentry->i_sample);
1143             }
1144 
1145             if (!mfra && !(mfra = box_new("mfra")))
1146             {
1147                 bo_free(tfra);
1148                 return NULL;
1149             }
1150 
1151             box_gather(mfra,tfra);
1152         }
1153     }
1154     return mfra;
1155 }
1156 
BuildMoov(sout_mux_t * p_mux)1157 static bo_t *BuildMoov(sout_mux_t *p_mux)
1158 {
1159     sout_mux_sys_t *p_sys = (sout_mux_sys_t*) p_mux->p_sys;
1160     const bool b_stco64 = (p_sys->i_pos >= (((uint64_t)0x1) << 32));
1161     /* map our structs */
1162     mp4mux_trackinfo_t **pp_infos = NULL;
1163     if(p_sys->i_nb_streams) /* Trackless moov ? */
1164     {
1165         pp_infos = vlc_alloc(p_sys->i_nb_streams, sizeof(mp4mux_trackinfo_t *));
1166         if(!pp_infos)
1167             return NULL;
1168         for(unsigned int i=0; i<p_sys->i_nb_streams; i++)
1169             pp_infos[i] = &p_sys->pp_streams[i]->mux;
1170     }
1171     bo_t *p_moov = mp4mux_GetMoovBox(VLC_OBJECT(p_mux), pp_infos, p_sys->i_nb_streams, 0,
1172                               p_sys->b_fragmented, p_sys->b_mov, p_sys->b_64_ext, b_stco64);
1173     free(pp_infos);
1174     return p_moov;
1175 }
1176 
FlushHeader(sout_mux_t * p_mux)1177 static void FlushHeader(sout_mux_t *p_mux)
1178 {
1179     sout_mux_sys_t *p_sys = (sout_mux_sys_t*) p_mux->p_sys;
1180 
1181     /* Now add ftyp header */
1182     bo_t *ftyp = mp4mux_GetFtyp(MAJOR_isom, 0, NULL, 0);
1183     if(!ftyp)
1184         return;
1185 
1186     bo_t *moov = BuildMoov(p_mux);
1187 
1188     /* merge into a single block */
1189     box_gather(ftyp, moov);
1190 
1191     /* add header flag for streaming server */
1192     ftyp->b->i_flags |= BLOCK_FLAG_HEADER;
1193     p_sys->i_pos += ftyp->b->i_buffer;
1194     box_send(p_mux, ftyp);
1195     p_sys->b_header_sent = true;
1196 }
1197 
OpenFrag(vlc_object_t * p_this)1198 static int OpenFrag(vlc_object_t *p_this)
1199 {
1200     sout_mux_t *p_mux = (sout_mux_t*) p_this;
1201     sout_mux_sys_t *p_sys = malloc(sizeof(sout_mux_sys_t));
1202     if (!p_sys)
1203         return VLC_ENOMEM;
1204 
1205     p_mux->p_sys = (sout_mux_sys_t *) p_sys;
1206     p_mux->pf_control   = Control;
1207     p_mux->pf_addstream = AddStream;
1208     p_mux->pf_delstream = DelStream;
1209     p_mux->pf_mux       = MuxFrag;
1210 
1211     /* unused */
1212     p_sys->b_mov        = false;
1213     p_sys->b_3gp        = false;
1214     p_sys->b_64_ext     = false;
1215     /* !unused */
1216 
1217     p_sys->i_pos        = 0;
1218     p_sys->i_nb_streams = 0;
1219     p_sys->pp_streams   = NULL;
1220     p_sys->i_mdat_pos   = 0;
1221     p_sys->i_read_duration   = 0;
1222     p_sys->i_written_duration= 0;
1223 
1224     p_sys->b_header_sent = false;
1225     p_sys->b_fragmented  = true;
1226     p_sys->i_start_dts = VLC_TS_INVALID;
1227     p_sys->i_mfhd_sequence = 1;
1228 
1229     return VLC_SUCCESS;
1230 }
1231 
WriteFragments(sout_mux_t * p_mux,bool b_flush)1232 static void WriteFragments(sout_mux_t *p_mux, bool b_flush)
1233 {
1234     sout_mux_sys_t *p_sys = (sout_mux_sys_t*) p_mux->p_sys;
1235     bo_t *moof = NULL;
1236     mtime_t i_barrier_time = p_sys->i_written_duration + FRAGMENT_LENGTH;
1237     size_t i_mdat_size = 0;
1238     bool b_has_samples = false;
1239 
1240     if(!p_sys->b_header_sent)
1241     {
1242         for (unsigned int j = 0; j < p_sys->i_nb_streams; j++)
1243         {
1244             mp4_stream_t *p_stream = p_sys->pp_streams[j];
1245             if(CreateCurrentEdit(p_stream, p_sys->i_start_dts, true))
1246                 DebugEdits(p_mux, p_stream);
1247         }
1248     }
1249 
1250     for (unsigned int i = 0; i < p_sys->i_nb_streams; i++)
1251     {
1252         const mp4_stream_t *p_stream = p_sys->pp_streams[i];
1253         if (p_stream->read.p_first)
1254         {
1255             b_has_samples = true;
1256 
1257             /* set a barrier so we try to align to keyframe */
1258             if (p_stream->b_hasiframes &&
1259                     p_stream->i_last_iframe_time > p_stream->i_written_duration &&
1260                     (p_stream->mux.fmt.i_cat == VIDEO_ES ||
1261                      p_stream->mux.fmt.i_cat == AUDIO_ES) )
1262             {
1263                 i_barrier_time = __MIN(i_barrier_time, p_stream->i_last_iframe_time);
1264             }
1265         }
1266     }
1267 
1268     if (!p_sys->b_header_sent)
1269         FlushHeader(p_mux);
1270 
1271     if (b_has_samples)
1272         moof = GetMoofBox(p_mux, &i_mdat_size, (b_flush)?0:i_barrier_time, p_sys->i_pos);
1273 
1274     if (moof && i_mdat_size == 0)
1275     {
1276         block_Release(moof->b);
1277         FREENULL(moof);
1278     }
1279 
1280     if (moof)
1281     {
1282         msg_Dbg(p_mux, "writing moof @ %"PRId64, p_sys->i_pos);
1283         p_sys->i_pos += moof->b->i_buffer;
1284         assert(moof->b->i_flags & BLOCK_FLAG_TYPE_I); /* http sout */
1285         box_send(p_mux, moof);
1286         msg_Dbg(p_mux, "writing mdat @ %"PRId64, p_sys->i_pos);
1287         WriteFragmentMDAT(p_mux, i_mdat_size);
1288 
1289         /* update iframe point */
1290         for (unsigned int i = 0; i < p_sys->i_nb_streams; i++)
1291         {
1292             mp4_stream_t *p_stream = p_sys->pp_streams[i];
1293             p_stream->i_last_iframe_time = 0;
1294         }
1295     }
1296 }
1297 
1298 /* Do an entry length fixup using only its own info.
1299  * This is the end boundary case. */
LengthLocalFixup(sout_mux_t * p_mux,const mp4_stream_t * p_stream,block_t * p_entrydata)1300 static void LengthLocalFixup(sout_mux_t *p_mux, const mp4_stream_t *p_stream, block_t *p_entrydata)
1301 {
1302     if ( p_stream->mux.fmt.i_cat == VIDEO_ES && p_stream->mux.fmt.video.i_frame_rate )
1303     {
1304         p_entrydata->i_length = CLOCK_FREQ *
1305                 p_stream->mux.fmt.video.i_frame_rate_base /
1306                 p_stream->mux.fmt.video.i_frame_rate;
1307         msg_Dbg(p_mux, "video track %d fixup to %"PRId64" for sample %u",
1308                 p_stream->mux.i_track_id, p_entrydata->i_length, p_stream->mux.i_entry_count - 1);
1309     }
1310     else if (p_stream->mux.fmt.i_cat == AUDIO_ES &&
1311              p_stream->mux.fmt.audio.i_rate &&
1312              p_entrydata->i_nb_samples && p_stream->mux.fmt.audio.i_rate)
1313     {
1314         p_entrydata->i_length = CLOCK_FREQ * p_entrydata->i_nb_samples /
1315                 p_stream->mux.fmt.audio.i_rate;
1316         msg_Dbg(p_mux, "audio track %d fixup to %"PRId64" for sample %u",
1317                 p_stream->mux.i_track_id, p_entrydata->i_length, p_stream->mux.i_entry_count - 1);
1318     }
1319     else
1320     {
1321         msg_Warn(p_mux, "unknown length for track %d sample %u",
1322                  p_stream->mux.i_track_id, p_stream->mux.i_entry_count - 1);
1323         p_entrydata->i_length = 1;
1324     }
1325 }
1326 
CleanupFrag(sout_mux_sys_t * p_sys)1327 static void CleanupFrag(sout_mux_sys_t *p_sys)
1328 {
1329     for (unsigned int i = 0; i < p_sys->i_nb_streams; i++)
1330     {
1331         mp4_stream_t *p_stream = p_sys->pp_streams[i];
1332         if (p_stream->p_held_entry)
1333         {
1334             block_Release(p_stream->p_held_entry->p_block);
1335             free(p_stream->p_held_entry);
1336         }
1337         while(p_stream->read.p_first)
1338         {
1339             mp4_fragentry_t *p_next = p_stream->read.p_first->p_next;
1340             block_Release(p_stream->read.p_first->p_block);
1341             free(p_stream->read.p_first);
1342             p_stream->read.p_first = p_next;
1343         }
1344         while(p_stream->towrite.p_first)
1345         {
1346             mp4_fragentry_t *p_next = p_stream->towrite.p_first->p_next;
1347             block_Release(p_stream->towrite.p_first->p_block);
1348             free(p_stream->towrite.p_first);
1349             p_stream->towrite.p_first = p_next;
1350         }
1351         free(p_stream->p_indexentries);
1352 
1353         mp4mux_trackinfo_Clear(&p_stream->mux);
1354         free(p_stream);
1355     }
1356     TAB_CLEAN(p_sys->i_nb_streams, p_sys->pp_streams);
1357     free(p_sys);
1358 }
1359 
CloseFrag(vlc_object_t * p_this)1360 static void CloseFrag(vlc_object_t *p_this)
1361 {
1362     sout_mux_t *p_mux = (sout_mux_t *) p_this;
1363     sout_mux_sys_t *p_sys = (sout_mux_sys_t*) p_mux->p_sys;
1364 
1365     /* Flush remaining entries */
1366     for (unsigned int i = 0; i < p_sys->i_nb_streams; i++)
1367     {
1368         mp4_stream_t *p_stream = p_sys->pp_streams[i];
1369         if (p_stream->p_held_entry)
1370         {
1371             if (p_stream->p_held_entry->p_block->i_length < 1)
1372                 LengthLocalFixup(p_mux, p_stream, p_stream->p_held_entry->p_block);
1373             ENQUEUE_ENTRY(p_stream->read, p_stream->p_held_entry);
1374             p_stream->p_held_entry = NULL;
1375         }
1376     }
1377 
1378     /* and force creating a fragment from it */
1379     WriteFragments(p_mux, true);
1380 
1381     /* Write indexes, but only for non streamed content
1382        as they refer to moof by absolute position */
1383     if (!strcmp(p_mux->psz_mux, "mp4frag"))
1384     {
1385         bo_t *mfra = GetMfraBox(p_mux);
1386         if (mfra)
1387         {
1388             bo_t *mfro = box_full_new("mfro", 0, 0x0);
1389             if (mfro)
1390             {
1391                 if (mfra->b)
1392                 {
1393                     box_fix(mfra, mfra->b->i_buffer);
1394                     bo_add_32be(mfro, mfra->b->i_buffer + MP4_MFRO_BOXSIZE);
1395                 }
1396                 box_gather(mfra, mfro);
1397             }
1398             box_send(p_mux, mfra);
1399         }
1400     }
1401 
1402     CleanupFrag(p_sys);
1403 }
1404 
MuxFrag(sout_mux_t * p_mux)1405 static int MuxFrag(sout_mux_t *p_mux)
1406 {
1407     sout_mux_sys_t *p_sys = (sout_mux_sys_t*) p_mux->p_sys;
1408 
1409     int i_stream = sout_MuxGetStream(p_mux, 1, NULL);
1410     if (i_stream < 0)
1411         return VLC_SUCCESS;
1412 
1413     sout_input_t *p_input  = p_mux->pp_inputs[i_stream];
1414     mp4_stream_t *p_stream = (mp4_stream_t*) p_input->p_sys;
1415     block_t *p_currentblock = BlockDequeue(p_input, p_stream);
1416     if( !p_currentblock )
1417         return VLC_SUCCESS;
1418 
1419     /* Set time ranges */
1420     if( p_stream->i_first_dts == VLC_TS_INVALID )
1421     {
1422         p_stream->i_first_dts = p_currentblock->i_dts;
1423         if( p_sys->i_start_dts == VLC_TS_INVALID )
1424             p_sys->i_start_dts = p_currentblock->i_dts;
1425     }
1426 
1427     /* If we have a previous entry for outgoing queue */
1428     if (p_stream->p_held_entry)
1429     {
1430         block_t *p_heldblock = p_stream->p_held_entry->p_block;
1431 
1432         /* Fix previous block length from current */
1433         if (p_heldblock->i_length < 1)
1434         {
1435 
1436             /* Fix using dts if not on a boundary */
1437             if ((p_currentblock->i_flags & BLOCK_FLAG_DISCONTINUITY) == 0)
1438                 p_heldblock->i_length = p_currentblock->i_dts - p_heldblock->i_dts;
1439 
1440             if (p_heldblock->i_length < 1)
1441                 LengthLocalFixup(p_mux, p_stream, p_heldblock);
1442         }
1443 
1444         /* enqueue */
1445         ENQUEUE_ENTRY(p_stream->read, p_stream->p_held_entry);
1446         p_stream->p_held_entry = NULL;
1447 
1448         if (p_stream->b_hasiframes && (p_heldblock->i_flags & BLOCK_FLAG_TYPE_I) &&
1449             p_stream->mux.i_read_duration - p_sys->i_written_duration < FRAGMENT_LENGTH)
1450         {
1451             /* Flag the last iframe time, we'll use it as boundary so it will start
1452                next fragment */
1453             p_stream->i_last_iframe_time = p_stream->mux.i_read_duration;
1454         }
1455 
1456         /* update buffered time */
1457         p_stream->mux.i_read_duration += __MAX(0, p_heldblock->i_length);
1458     }
1459 
1460 
1461     /* set temp entry */
1462     p_stream->p_held_entry = malloc(sizeof(mp4_fragentry_t));
1463     if (unlikely(!p_stream->p_held_entry))
1464         return VLC_ENOMEM;
1465 
1466     p_stream->p_held_entry->p_block  = p_currentblock;
1467     p_stream->p_held_entry->i_run    = p_stream->i_current_run;
1468     p_stream->p_held_entry->p_next   = NULL;
1469 
1470     if (p_stream->mux.fmt.i_cat == VIDEO_ES )
1471     {
1472         if (!p_stream->b_hasiframes && (p_currentblock->i_flags & BLOCK_FLAG_TYPE_I))
1473             p_stream->b_hasiframes = true;
1474 
1475         if (!p_stream->mux.b_hasbframes && p_currentblock->i_dts > VLC_TS_INVALID &&
1476             p_currentblock->i_pts > p_currentblock->i_dts)
1477             p_stream->mux.b_hasbframes = true;
1478     }
1479 
1480     /* Update the global fragment/media duration */
1481     mtime_t i_min_read_duration = p_stream->mux.i_read_duration;
1482     mtime_t i_min_written_duration = p_stream->i_written_duration;
1483     for (unsigned int i=0; i<p_sys->i_nb_streams; i++)
1484     {
1485         const mp4_stream_t *p_s = p_sys->pp_streams[i];
1486         if (p_s->mux.fmt.i_cat != VIDEO_ES && p_s->mux.fmt.i_cat != AUDIO_ES)
1487             continue;
1488         if (p_s->mux.i_read_duration < i_min_read_duration)
1489             i_min_read_duration = p_s->mux.i_read_duration;
1490 
1491         if (p_s->i_written_duration < i_min_written_duration)
1492             i_min_written_duration = p_s->i_written_duration;
1493     }
1494     p_sys->i_read_duration = i_min_read_duration;
1495     p_sys->i_written_duration = i_min_written_duration;
1496 
1497     /* we have prerolled enough to know all streams, and have enough date to create a fragment */
1498     if (p_stream->read.p_first && p_sys->i_read_duration - p_sys->i_written_duration >= FRAGMENT_LENGTH)
1499         WriteFragments(p_mux, false);
1500 
1501     return VLC_SUCCESS;
1502 }
1503