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