1 /*****************************************************************************
2 * ogg.c: ogg muxer module for vlc
3 *****************************************************************************
4 * Copyright (C) 2001, 2002, 2006 VLC authors and VideoLAN
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8 * Gildas Bazin <gbazin@videolan.org>
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.
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.
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 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
36 #include <vlc_block.h>
37 #include <vlc_codecs.h>
40 #include "../demux/xiph.h"
44 /*****************************************************************************
46 *****************************************************************************/
47 #define INDEXINTVL_TEXT N_("Index interval")
48 #define INDEXINTVL_LONGTEXT N_("Minimal index interval, in microseconds. " \
49 "Set to 0 to disable index creation.")
50 #define INDEXRATIO_TEXT N_("Index size ratio")
51 #define INDEXRATIO_LONGTEXT N_(\
52 "Set index size ratio. Alters default (60min content) or estimated size." )
54 static int Open ( vlc_object_t
* );
55 static void Close ( vlc_object_t
* );
57 #define SOUT_CFG_PREFIX "sout-ogg-"
60 set_description( N_("Ogg/OGM muxer") )
61 set_capability( "sout mux", 10 )
62 set_category( CAT_SOUT
)
63 set_subcategory( SUBCAT_SOUT_MUX
)
64 add_shortcut( "ogg", "ogm" )
65 add_integer_with_range( SOUT_CFG_PREFIX
"indexintvl", 1000, 0, INT_MAX
,
66 INDEXINTVL_TEXT
, INDEXINTVL_LONGTEXT
, true )
67 add_float_with_range( SOUT_CFG_PREFIX
"indexratio", 1.0, 1.0, 1000,
68 INDEXRATIO_TEXT
, INDEXRATIO_LONGTEXT
, true )
69 set_callbacks( Open
, Close
)
73 /*****************************************************************************
75 *****************************************************************************/
76 static int Control ( sout_mux_t
*, int, va_list );
77 static int AddStream( sout_mux_t
*, sout_input_t
* );
78 static void DelStream( sout_mux_t
*, sout_input_t
* );
79 static int Mux ( sout_mux_t
* );
80 static int MuxBlock ( sout_mux_t
*, sout_input_t
* );
82 static bool OggCreateHeaders( sout_mux_t
* );
83 static void OggFillSkeletonFishead( uint8_t *p_buffer
, sout_mux_t
*p_mux
);
85 /*****************************************************************************
87 *****************************************************************************/
90 #define FISBONE_BASE_SIZE 52
91 #define FISBONE_BASE_OFFSET 44
92 #define INDEX_BASE_SIZE 42
94 /* Structures used for OggDS headers used in ogm files */
96 #define PACKET_TYPE_HEADER 0x01
97 #define PACKET_TYPE_COMMENT 0x03
98 #define PACKET_IS_SYNCPOINT 0x08
104 } oggds_header_video_t
;
109 int16_t i_block_align
;
110 int32_t i_avgbytespersec
;
111 } oggds_header_audio_t
;
115 uint8_t i_packet_type
;
123 int64_t i_samples_per_unit
;
124 int32_t i_default_len
;
126 int32_t i_buffer_size
;
127 int16_t i_bits_per_sample
;
129 int16_t i_padding_0
; /* Because the original is using MSVC packing style */
133 oggds_header_video_t video
;
134 oggds_header_audio_t audio
;
137 int32_t i_padding_1
; /* Because the original is using MSVC packing style */
141 /*****************************************************************************
142 * Definitions of structures and functions used by this plugins
143 *****************************************************************************/
147 vlc_fourcc_t i_fourcc
;
155 int i_keyframe_granule_shift
; /* Theora and Daala only */
156 int i_last_keyframe
; /* dirac and theora */
157 int i_num_frames
; /* Theora only */
158 uint64_t u_last_granulepos
; /* Used for correct EOS page */
159 int64_t i_num_keyframes
;
162 oggds_header_t
*p_oggds_header
;
170 /* Skeleton index storage */
171 unsigned char *p_index
;
172 uint64_t i_index_size
;
173 uint64_t i_index_payload
; /* real index size */
174 uint64_t i_index_count
;
175 /* backup values for rewriting index page later */
176 uint64_t i_index_offset
; /* sout offset of the index page */
177 int64_t i_index_packetno
; /* index packet no */
179 /* index creation tracking values */
180 uint64_t i_last_keyframe_pos
;
181 uint64_t i_last_keyframe_time
;
186 mtime_t i_baseptsdelay
;
190 struct sout_mux_sys_t
195 int i_next_serial_no
;
197 /* number of logical streams pending to be added */
199 bool b_can_add_streams
;
201 /* logical streams pending to be deleted */
203 ogg_stream_t
**pp_del_streams
;
213 /* backup values for rewriting fishead page later */
214 uint64_t i_fishead_offset
; /* sout offset of the fishead page */
219 /* access position */
221 ssize_t i_data_start
;
222 ssize_t i_segment_start
;
225 static void OggSetDate( block_t
*, mtime_t
, mtime_t
);
226 static block_t
*OggStreamFlush( sout_mux_t
*, ogg_stream_state
*, mtime_t
);
227 static void OggCreateStreamFooter( sout_mux_t
*p_mux
, ogg_stream_t
*p_stream
);
228 static void OggRewriteFisheadPage( sout_mux_t
*p_mux
);
229 static bool AllocateIndex( sout_mux_t
*p_mux
, sout_input_t
*p_input
);
231 /*****************************************************************************
233 *****************************************************************************/
234 static int Open( vlc_object_t
*p_this
)
236 sout_mux_t
*p_mux
= (sout_mux_t
*)p_this
;
237 sout_mux_sys_t
*p_sys
;
239 msg_Info( p_mux
, "Open" );
241 p_sys
= malloc( sizeof( sout_mux_sys_t
) );
244 p_sys
->i_streams
= 0;
245 p_sys
->i_add_streams
= 0;
246 p_sys
->b_can_add_streams
= true;
247 p_sys
->i_del_streams
= 0;
248 p_sys
->pp_del_streams
= 0;
250 p_sys
->skeleton
.b_create
= false;
251 p_sys
->skeleton
.b_head_done
= false;
252 p_sys
->skeleton
.i_index_intvl
=
253 var_InheritInteger( p_this
, SOUT_CFG_PREFIX
"indexintvl" );
254 p_sys
->skeleton
.i_index_ratio
=
255 var_InheritFloat( p_this
, SOUT_CFG_PREFIX
"indexratio" );
256 p_sys
->i_data_start
= 0;
257 p_sys
->i_segment_start
= 0;
258 p_mux
->p_sys
= p_sys
;
259 p_mux
->pf_control
= Control
;
260 p_mux
->pf_addstream
= AddStream
;
261 p_mux
->pf_delstream
= DelStream
;
264 /* First serial number is random.
265 * (Done like this because on win32 you need to seed the random number
266 * generator once per thread). */
268 vlc_rand_bytes(&r
, sizeof(r
));
269 p_sys
->i_next_serial_no
= r
& INT_MAX
;
274 /*****************************************************************************
275 * Close: Finalize ogg bitstream and close muxer
276 *****************************************************************************/
277 static void Close( vlc_object_t
* p_this
)
279 sout_mux_t
*p_mux
= (sout_mux_t
*)p_this
;
280 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
281 ogg_stream_t
*p_stream
;
283 msg_Info( p_mux
, "Close" );
285 if( p_sys
->i_del_streams
)
287 /* Close the current ogg stream */
288 msg_Dbg( p_mux
, "writing footers" );
290 for(int i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
292 p_stream
= (ogg_stream_t
*) p_mux
->pp_inputs
[i
]->p_sys
;
293 OggCreateStreamFooter( p_mux
, p_stream
);
294 free( p_stream
->skeleton
.p_index
);
297 /* Remove deleted logical streams */
298 for(int i
= 0; i
< p_sys
->i_del_streams
; i
++ )
300 OggCreateStreamFooter( p_mux
, p_sys
->pp_del_streams
[i
] );
301 free( p_sys
->pp_del_streams
[i
]->p_oggds_header
);
302 free( p_sys
->pp_del_streams
[i
]->skeleton
.p_index
);
303 free( p_sys
->pp_del_streams
[i
] );
305 free( p_sys
->pp_del_streams
);
306 p_sys
->i_streams
-= p_sys
->i_del_streams
;
309 /* rewrite fishead with final values */
310 if ( p_sys
->skeleton
.b_create
&& p_sys
->skeleton
.b_head_done
)
312 OggRewriteFisheadPage( p_mux
);
318 /*****************************************************************************
320 *****************************************************************************/
321 static int Control( sout_mux_t
*p_mux
, int i_query
, va_list args
)
329 case MUX_CAN_ADD_STREAM_WHILE_MUXING
:
330 pb_bool
= (bool*)va_arg( args
, bool * );
334 case MUX_GET_ADD_STREAM_WAIT
:
335 pb_bool
= (bool*)va_arg( args
, bool * );
340 ppsz
= (char**)va_arg( args
, char ** );
341 *ppsz
= strdup( "application/ogg" );
348 /*****************************************************************************
349 * AddStream: Add an elementary stream to the muxed stream
350 *****************************************************************************/
351 static int AddStream( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
353 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
354 ogg_stream_t
*p_stream
;
357 msg_Dbg( p_mux
, "adding input" );
359 p_input
->p_sys
= p_stream
= calloc( 1, sizeof( ogg_stream_t
) );
363 p_stream
->i_cat
= p_input
->p_fmt
->i_cat
;
364 p_stream
->i_fourcc
= p_input
->p_fmt
->i_codec
;
365 p_stream
->i_serial_no
= p_sys
->i_next_serial_no
++;
366 p_stream
->i_packet_no
= 0;
367 p_stream
->i_last_keyframe
= 0;
368 p_stream
->i_num_keyframes
= 0;
369 p_stream
->i_num_frames
= 0;
371 p_stream
->p_oggds_header
= 0;
373 p_stream
->i_baseptsdelay
= -1;
374 p_stream
->i_dirac_last_pt
= -1;
375 p_stream
->i_dirac_last_dt
= -1;
377 switch( p_input
->p_fmt
->i_cat
)
380 if( !p_input
->p_fmt
->video
.i_frame_rate
||
381 !p_input
->p_fmt
->video
.i_frame_rate_base
)
383 msg_Warn( p_mux
, "Missing frame rate, assuming 25fps" );
384 assert(p_input
->p_fmt
== &p_input
->fmt
);
385 p_input
->fmt
.video
.i_frame_rate
= 25;
386 p_input
->fmt
.video
.i_frame_rate_base
= 1;
389 switch( p_stream
->i_fourcc
)
400 p_stream
->p_oggds_header
= calloc( 1, sizeof(oggds_header_t
) );
401 if( !p_stream
->p_oggds_header
)
406 p_stream
->p_oggds_header
->i_packet_type
= PACKET_TYPE_HEADER
;
408 memcpy( p_stream
->p_oggds_header
->stream_type
, "video", 5 );
409 if( p_stream
->i_fourcc
== VLC_CODEC_MP4V
)
411 memcpy( p_stream
->p_oggds_header
->sub_type
, "XVID", 4 );
413 else if( p_stream
->i_fourcc
== VLC_CODEC_DIV3
)
415 memcpy( p_stream
->p_oggds_header
->sub_type
, "DIV3", 4 );
419 memcpy( p_stream
->p_oggds_header
->sub_type
,
420 &p_stream
->i_fourcc
, 4 );
422 p_stream
->p_oggds_header
->i_size
= 0 ;
423 p_stream
->p_oggds_header
->i_time_unit
=
424 INT64_C(10000000) * p_input
->p_fmt
->video
.i_frame_rate_base
/
425 (int64_t)p_input
->p_fmt
->video
.i_frame_rate
;
426 p_stream
->p_oggds_header
->i_samples_per_unit
= 1;
427 p_stream
->p_oggds_header
->i_default_len
= 1 ; /* ??? */
428 p_stream
->p_oggds_header
->i_buffer_size
= 1024*1024;
429 p_stream
->p_oggds_header
->i_bits_per_sample
= 0;
430 p_stream
->p_oggds_header
->header
.video
.i_width
= p_input
->p_fmt
->video
.i_width
;
431 p_stream
->p_oggds_header
->header
.video
.i_height
= p_input
->p_fmt
->video
.i_height
;
432 msg_Dbg( p_mux
, "%4.4s stream", (char *)&p_stream
->i_fourcc
);
435 case VLC_CODEC_DIRAC
:
436 msg_Dbg( p_mux
, "dirac stream" );
439 case VLC_CODEC_THEORA
:
440 msg_Dbg( p_mux
, "theora stream" );
443 case VLC_CODEC_DAALA
:
444 msg_Dbg( p_mux
, "daala stream" );
448 msg_Dbg( p_mux
, "VP8 stream" );
452 FREENULL( p_input
->p_sys
);
458 switch( p_stream
->i_fourcc
)
461 msg_Dbg( p_mux
, "opus stream" );
464 case VLC_CODEC_VORBIS
:
465 msg_Dbg( p_mux
, "vorbis stream" );
468 case VLC_CODEC_SPEEX
:
469 msg_Dbg( p_mux
, "speex stream" );
473 msg_Dbg( p_mux
, "flac stream" );
477 fourcc_to_wf_tag( p_stream
->i_fourcc
, &i_tag
);
478 if( i_tag
== WAVE_FORMAT_UNKNOWN
)
480 FREENULL( p_input
->p_sys
);
484 p_stream
->p_oggds_header
=
485 malloc( sizeof(oggds_header_t
) + p_input
->p_fmt
->i_extra
);
486 if( !p_stream
->p_oggds_header
)
491 memset( p_stream
->p_oggds_header
, 0, sizeof(oggds_header_t
) );
492 p_stream
->p_oggds_header
->i_packet_type
= PACKET_TYPE_HEADER
;
494 p_stream
->p_oggds_header
->i_size
= p_input
->p_fmt
->i_extra
;
496 if( p_input
->p_fmt
->i_extra
)
498 memcpy( &p_stream
->p_oggds_header
[1],
499 p_input
->p_fmt
->p_extra
, p_input
->p_fmt
->i_extra
);
502 memcpy( p_stream
->p_oggds_header
->stream_type
, "audio", 5 );
504 memset( p_stream
->p_oggds_header
->sub_type
, 0, 4 );
506 snprintf( buf
, sizeof(buf
), "%"PRIx16
, i_tag
);
507 strncpy( p_stream
->p_oggds_header
->sub_type
, buf
, 4 );
509 p_stream
->p_oggds_header
->i_time_unit
= INT64_C(10000000);
510 p_stream
->p_oggds_header
->i_default_len
= 1;
511 p_stream
->p_oggds_header
->i_buffer_size
= 30*1024 ;
512 p_stream
->p_oggds_header
->i_samples_per_unit
= p_input
->p_fmt
->audio
.i_rate
;
513 p_stream
->p_oggds_header
->i_bits_per_sample
= p_input
->p_fmt
->audio
.i_bitspersample
;
514 p_stream
->p_oggds_header
->header
.audio
.i_channels
= p_input
->p_fmt
->audio
.i_channels
;
515 p_stream
->p_oggds_header
->header
.audio
.i_block_align
= p_input
->p_fmt
->audio
.i_blockalign
;
516 p_stream
->p_oggds_header
->header
.audio
.i_avgbytespersec
= p_input
->p_fmt
->i_bitrate
/ 8;
517 msg_Dbg( p_mux
, "%4.4s stream", (char *)&p_stream
->i_fourcc
);
523 switch( p_stream
->i_fourcc
)
526 p_stream
->p_oggds_header
= calloc( 1, sizeof(oggds_header_t
) );
527 if( !p_stream
->p_oggds_header
)
532 p_stream
->p_oggds_header
->i_packet_type
= PACKET_TYPE_HEADER
;
534 memcpy( p_stream
->p_oggds_header
->stream_type
, "text", 4 );
535 msg_Dbg( p_mux
, "subtitles stream" );
539 FREENULL( p_input
->p_sys
);
544 FREENULL( p_input
->p_sys
);
548 p_stream
->b_new
= true;
550 p_sys
->i_add_streams
++;
555 /*****************************************************************************
556 * DelStream: Delete an elementary stream from the muxed stream
557 *****************************************************************************/
558 static void DelStream( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
560 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
561 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
564 msg_Dbg( p_mux
, "removing input" );
566 /* flush all remaining data */
569 if( !p_stream
->b_new
)
571 while( block_FifoCount( p_input
->p_fifo
) )
572 MuxBlock( p_mux
, p_input
);
575 if( !p_stream
->b_new
&&
576 ( p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 ) ) )
578 OggSetDate( p_og
, p_stream
->i_dts
, p_stream
->i_length
);
579 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
582 /* move input in delete queue */
583 if( !p_stream
->b_new
)
585 p_sys
->pp_del_streams
= xrealloc( p_sys
->pp_del_streams
,
586 (p_sys
->i_del_streams
+ 1) * sizeof(ogg_stream_t
*) );
587 p_sys
->pp_del_streams
[p_sys
->i_del_streams
++] = p_stream
;
591 /* wasn't already added so get rid of it */
592 FREENULL( p_stream
->p_oggds_header
);
593 FREENULL( p_stream
);
594 p_sys
->i_add_streams
--;
598 p_input
->p_sys
= NULL
;
601 /*****************************************************************************
602 * Ogg Skeleton helpers
603 *****************************************************************************/
604 static int WriteQWVariableLE( uint64_t i_64
, uint64_t i_offset
,
605 uint8_t *p_buffer
, int i_buffer_size
)
607 uint8_t *p_dest
= p_buffer
+ i_offset
;
612 if ( p_dest
- p_buffer
>= i_buffer_size
) return -1;
614 *p_dest
= (uint8_t) ( i_64
& 0x7F );
628 static bool AddIndexEntry( sout_mux_t
*p_mux
, uint64_t i_time
, sout_input_t
*p_input
)
630 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
631 ogg_stream_t
*p_stream
= (ogg_stream_t
*) p_input
->p_sys
;
633 uint64_t i_timedelta
;
634 if ( !p_sys
->skeleton
.b_create
|| p_mux
->p_sys
->skeleton
.i_index_intvl
== 0
635 || !p_stream
->skeleton
.p_index
)
638 if ( p_stream
->skeleton
.i_last_keyframe_pos
== 0 )
639 p_stream
->skeleton
.i_last_keyframe_pos
= p_sys
->i_segment_start
;
640 i_posdelta
= p_sys
->i_pos
- p_stream
->skeleton
.i_last_keyframe_pos
;
641 i_timedelta
= i_time
- p_stream
->skeleton
.i_last_keyframe_time
;
643 if ( i_timedelta
<= ( (uint64_t) p_mux
->p_sys
->skeleton
.i_index_intvl
* 1000 )
644 || i_posdelta
<= 0xFFFF )
649 if ( !p_stream
->skeleton
.p_index
) return false;
650 uint64_t i_offset
= p_stream
->skeleton
.i_index_payload
;
651 i_ret
= WriteQWVariableLE( i_posdelta
, i_offset
, p_stream
->skeleton
.p_index
,
652 p_stream
->skeleton
.i_index_size
);
653 if ( i_ret
== -1 ) return false;
655 i_ret
= WriteQWVariableLE( i_timedelta
, i_offset
, p_stream
->skeleton
.p_index
,
656 p_stream
->skeleton
.i_index_size
);
657 if ( i_ret
== -1 ) return false;
658 p_stream
->skeleton
.i_index_payload
= i_offset
+ i_ret
;
659 p_stream
->skeleton
.i_index_count
++;
661 /* update diff points */
662 p_stream
->skeleton
.i_last_keyframe_pos
= p_sys
->i_pos
;
663 p_stream
->skeleton
.i_last_keyframe_time
= i_time
;
664 msg_Dbg( p_mux
, "Added index on stream %d entry %zd %"PRIu64
,
665 p_stream
->i_serial_no
, p_sys
->i_pos
- p_sys
->i_segment_start
, i_time
);
670 /*****************************************************************************
671 * Ogg bitstream manipulation routines
672 *****************************************************************************/
673 static block_t
*OggStreamGetPage( sout_mux_t
*p_mux
,
674 ogg_stream_state
*p_os
, mtime_t i_pts
,
678 block_t
*p_og
, *p_og_first
= NULL
;
680 int (*pager
)( ogg_stream_state
*, ogg_page
* ) = flush
? ogg_stream_flush
: ogg_stream_pageout
;
682 while( pager( p_os
, &og
) )
685 p_og
= block_Alloc( og
.header_len
+ og
.body_len
);
687 memcpy( p_og
->p_buffer
, og
.header
, og
.header_len
);
688 memcpy( p_og
->p_buffer
+ og
.header_len
, og
.body
, og
.body_len
);
693 i_pts
= 0; // write it only once
695 block_ChainAppend( &p_og_first
, p_og
);
701 static block_t
*OggStreamFlush( sout_mux_t
*p_mux
,
702 ogg_stream_state
*p_os
, mtime_t i_pts
)
704 return OggStreamGetPage( p_mux
, p_os
, i_pts
, true );
707 static block_t
*OggStreamPageOut( sout_mux_t
*p_mux
,
708 ogg_stream_state
*p_os
, mtime_t i_pts
)
710 return OggStreamGetPage( p_mux
, p_os
, i_pts
, false );
713 static void OggGetSkeletonIndex( uint8_t **pp_buffer
, long *pi_size
, ogg_stream_t
*p_stream
)
715 uint8_t *p_buffer
= calloc( INDEX_BASE_SIZE
+ p_stream
->skeleton
.i_index_size
, sizeof(uint8_t) );
716 if ( !p_buffer
) return;
717 *pp_buffer
= p_buffer
;
719 memcpy( p_buffer
, "index", 6 );
720 SetDWLE( &p_buffer
[6], p_stream
->i_serial_no
);
721 SetQWLE( &p_buffer
[10], p_stream
->skeleton
.i_index_count
); /* num keypoints */
722 SetQWLE( &p_buffer
[18], 1000000 );
723 SetQWLE( &p_buffer
[34], p_stream
->i_length
);
724 memcpy( p_buffer
+ INDEX_BASE_SIZE
, p_stream
->skeleton
.p_index
, p_stream
->skeleton
.i_index_payload
);
725 *pi_size
= INDEX_BASE_SIZE
+ p_stream
->skeleton
.i_index_size
;
728 static void OggGetSkeletonFisbone( uint8_t **pp_buffer
, long *pi_size
,
729 sout_input_t
*p_input
, sout_mux_t
*p_mux
)
733 const char *psz_value
= NULL
;
734 ogg_stream_t
*p_stream
= (ogg_stream_t
*) p_input
->p_sys
;
737 char *psz_content_type
;
740 unsigned int i_count
;
741 } headers
= { NULL
, NULL
, 0, 0 };
744 switch( p_stream
->i_fourcc
)
746 case VLC_CODEC_VORBIS
:
747 psz_value
= "audio/vorbis";
749 case VLC_CODEC_THEORA
:
750 psz_value
= "video/theora";
752 case VLC_CODEC_DAALA
:
753 psz_value
= "video/daala";
755 case VLC_CODEC_SPEEX
:
756 psz_value
= "audio/speex";
759 psz_value
= "audio/flac";
762 psz_value
= "text/cmml";
765 psz_value
= "application/kate";
768 psz_value
= "video/x-vp8";
771 psz_value
= "application/octet-stream";
772 msg_Warn( p_mux
, "Unknown fourcc for stream %s, setting Content-Type to %s",
773 vlc_fourcc_GetDescription( p_stream
->i_cat
, p_stream
->i_fourcc
),
777 /* Content Type Header */
778 if ( asprintf( &headers
.psz_content_type
, "Content-Type: %s\r\n", psz_value
) != -1 )
780 headers
.i_size
+= strlen( headers
.psz_content_type
);
784 /* Set Role Header */
785 if ( p_input
->p_fmt
->i_priority
> ES_PRIORITY_NOT_SELECTABLE
)
787 int i_max_prio
= ES_PRIORITY_MIN
;
788 for ( int i
=0; i
< p_mux
->i_nb_inputs
; i
++ )
790 if ( p_mux
->pp_inputs
[i
]->p_fmt
->i_cat
!= p_input
->p_fmt
->i_cat
) continue;
791 i_max_prio
= __MAX( p_mux
->pp_inputs
[i
]->p_fmt
->i_priority
, i_max_prio
);
795 if ( p_input
->p_fmt
->i_cat
== AUDIO_ES
|| p_input
->p_fmt
->i_cat
== VIDEO_ES
)
797 if ( p_input
->p_fmt
->i_priority
== i_max_prio
&& i_max_prio
>= ES_PRIORITY_SELECTABLE_MIN
)
798 psz_value
= ( p_input
->p_fmt
->i_cat
== VIDEO_ES
) ?
799 "video/main" : "audio/main";
801 psz_value
= ( p_input
->p_fmt
->i_cat
== VIDEO_ES
) ?
802 "video/alternate" : "audio/alternate";
804 else if ( p_input
->p_fmt
->i_cat
== SPU_ES
)
806 psz_value
= ( p_input
->p_fmt
->i_codec
== VLC_CODEC_KATE
) ?
807 "text/karaoke" : "text/subtitle";
810 if ( psz_value
&& asprintf( &headers
.psz_role
, "Role: %s\r\n", psz_value
) != -1 )
812 headers
.i_size
+= strlen( headers
.psz_role
);
817 *pp_buffer
= calloc( FISBONE_BASE_SIZE
+ headers
.i_size
, sizeof(uint8_t) );
818 if ( !*pp_buffer
) return;
819 p_buffer
= *pp_buffer
;
821 memcpy( p_buffer
, "fisbone", 8 );
822 SetDWLE( &p_buffer
[8], FISBONE_BASE_OFFSET
); /* offset to message headers */
823 SetDWLE( &p_buffer
[12], p_stream
->i_serial_no
);
824 SetDWLE( &p_buffer
[16], headers
.i_count
);
826 /* granulerate den */
827 switch ( p_input
->p_fmt
->i_cat
)
830 SetQWLE( &(*pp_buffer
)[20], p_input
->p_fmt
->video
.i_frame_rate
);
831 SetQWLE( &(*pp_buffer
)[28], p_input
->p_fmt
->video
.i_frame_rate_base
);
834 SetQWLE( &(*pp_buffer
)[20], p_input
->p_fmt
->audio
.i_rate
);
835 SetQWLE( &(*pp_buffer
)[28], 1 );
838 SetQWLE( &(*pp_buffer
)[20], 1000 );
839 SetQWLE( &(*pp_buffer
)[28], 1 );
843 if ( p_input
->p_fmt
->p_extra
)
844 SetDWLE( &(*pp_buffer
)[44], xiph_CountHeaders( p_input
->p_fmt
->p_extra
, p_input
->p_fmt
->i_extra
) );
846 if ( headers
.i_size
> 0 )
848 psz_header
= *pp_buffer
+ FISBONE_BASE_SIZE
;
849 memcpy( psz_header
, headers
.psz_content_type
, strlen( headers
.psz_content_type
) );
850 psz_header
+= strlen( headers
.psz_content_type
);
851 if ( headers
.psz_role
)
852 memcpy( psz_header
, headers
.psz_role
, strlen( headers
.psz_role
) );
854 *pi_size
= FISBONE_BASE_SIZE
+ headers
.i_size
;
856 free( headers
.psz_content_type
);
857 free( headers
.psz_role
);
860 static void OggFillSkeletonFishead( uint8_t *p_buffer
, sout_mux_t
*p_mux
)
862 memcpy( p_buffer
, "fishead", 8 );
863 SetWLE( &p_buffer
[8], 4 );
864 SetWLE( &p_buffer
[10], 0 );
865 SetQWLE( &p_buffer
[20], 1000 );
866 SetQWLE( &p_buffer
[36], 1000 );
867 SetQWLE( &p_buffer
[64], p_mux
->p_sys
->i_pos
- p_mux
->p_sys
->i_segment_start
); /* segment length */
868 SetQWLE( &p_buffer
[72], p_mux
->p_sys
->i_data_start
- p_mux
->p_sys
->i_segment_start
); /* data start offset */
871 static int32_t OggFillDsHeader( uint8_t *p_buffer
, oggds_header_t
*p_oggds_header
, int i_cat
)
874 p_buffer
[index
] = p_oggds_header
->i_packet_type
;
876 memcpy( &p_buffer
[index
], p_oggds_header
->stream_type
, sizeof(p_oggds_header
->stream_type
) );
877 index
+= sizeof(p_oggds_header
->stream_type
);
878 memcpy(&p_buffer
[index
], p_oggds_header
->sub_type
, sizeof(p_oggds_header
->sub_type
) );
879 index
+= sizeof(p_oggds_header
->sub_type
);
881 /* The size is filled at the end */
882 uint8_t *p_isize
= &p_buffer
[index
];
885 SetQWLE( &p_buffer
[index
], p_oggds_header
->i_time_unit
);
887 SetQWLE( &p_buffer
[index
], p_oggds_header
->i_samples_per_unit
);
889 SetDWLE( &p_buffer
[index
], p_oggds_header
->i_default_len
);
891 SetDWLE( &p_buffer
[index
], p_oggds_header
->i_buffer_size
);
893 SetWLE( &p_buffer
[index
], p_oggds_header
->i_bits_per_sample
);
895 SetWLE( &p_buffer
[index
], p_oggds_header
->i_padding_0
);
901 SetDWLE( &p_buffer
[index
], p_oggds_header
->header
.video
.i_width
);
902 SetDWLE( &p_buffer
[index
+4], p_oggds_header
->header
.video
.i_height
);
905 SetWLE( &p_buffer
[index
], p_oggds_header
->header
.audio
.i_channels
);
906 SetWLE( &p_buffer
[index
+2], p_oggds_header
->header
.audio
.i_block_align
);
907 SetDWLE( &p_buffer
[index
+4], p_oggds_header
->header
.audio
.i_avgbytespersec
);
911 SetDWLE( &p_buffer
[index
], p_oggds_header
->i_padding_1
);
915 if( p_oggds_header
->i_size
> 0 )
917 memcpy( &p_buffer
[index
], (uint8_t *) p_oggds_header
+ sizeof(*p_oggds_header
), p_oggds_header
->i_size
);
918 index
+= p_oggds_header
->i_size
;
921 SetDWLE( p_isize
, index
-1 );
925 static void OggFillVP8Header( uint8_t *p_buffer
, sout_input_t
*p_input
)
927 memcpy( p_buffer
, "OVP80\x01\x01\x00", 8 );
928 SetWBE( &p_buffer
[8], p_input
->p_fmt
->video
.i_width
);
929 SetDWBE( &p_buffer
[14], p_input
->p_fmt
->video
.i_sar_den
);/* 24 bits, 15~ */
930 SetDWBE( &p_buffer
[11], p_input
->p_fmt
->video
.i_sar_num
);/* 24 bits, 12~ */
931 SetWBE( &p_buffer
[10], p_input
->p_fmt
->video
.i_height
);
932 SetDWBE( &p_buffer
[18], p_input
->p_fmt
->video
.i_frame_rate
);
933 SetDWBE( &p_buffer
[22], p_input
->p_fmt
->video
.i_frame_rate_base
);
936 static bool OggCreateHeaders( sout_mux_t
*p_mux
)
938 block_t
*p_hdr
= NULL
;
939 block_t
*p_og
= NULL
;
941 ogg_stream_t
*p_stream
;
942 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
945 if( sout_AccessOutControl( p_mux
->p_access
,
947 &p_sys
->skeleton
.b_create
) )
949 p_sys
->skeleton
.b_create
= false;
952 p_sys
->skeleton
.b_create
&= !! p_mux
->i_nb_inputs
;
954 /* no skeleton for solo vorbis/speex/opus tracks */
955 if ( p_mux
->i_nb_inputs
== 1 && p_mux
->pp_inputs
[0]->p_fmt
->i_cat
== AUDIO_ES
)
957 p_sys
->skeleton
.b_create
= false;
961 for ( int i
=0; i
< p_mux
->i_nb_inputs
; i
++ )
963 p_stream
= (ogg_stream_t
*) p_mux
->pp_inputs
[i
]->p_sys
;
964 if ( p_stream
->p_oggds_header
)
966 /* We don't want skeleton for OggDS */
967 p_sys
->skeleton
.b_create
= false;
973 /* Skeleton's Fishead must be the first page of the stream */
974 if ( p_sys
->skeleton
.b_create
&& !p_sys
->skeleton
.b_head_done
)
976 msg_Dbg( p_mux
, "creating header for skeleton" );
977 p_sys
->skeleton
.i_serial_no
= p_sys
->i_next_serial_no
++;
978 ogg_stream_init( &p_sys
->skeleton
.os
, p_sys
->skeleton
.i_serial_no
);
980 op
.packet
= calloc( 1, op
.bytes
);
981 if ( op
.packet
== NULL
) return false;
986 OggFillSkeletonFishead( op
.packet
, p_mux
);
987 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
988 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
989 block_ChainAppend( &p_hdr
, p_og
);
990 p_sys
->skeleton
.b_head_done
= true;
991 p_sys
->skeleton
.i_fishead_offset
= p_sys
->i_pos
;
994 /* Write header for each stream. All b_o_s (beginning of stream) packets
995 * must appear first in the ogg stream so we take care of them first. */
996 for( int pass
= 0; pass
< 2; pass
++ )
998 for( i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1000 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1001 p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1003 bool video
= ( p_stream
->i_fourcc
== VLC_CODEC_THEORA
||
1004 p_stream
->i_fourcc
== VLC_CODEC_DIRAC
||
1005 p_stream
->i_fourcc
== VLC_CODEC_DAALA
);
1006 if( ( ( pass
== 0 && !video
) || ( pass
== 1 && video
) ) )
1009 msg_Dbg( p_mux
, "creating header for %4.4s",
1010 (char *)&p_stream
->i_fourcc
);
1012 ogg_stream_init( &p_stream
->os
, p_stream
->i_serial_no
);
1013 p_stream
->b_new
= false;
1014 p_stream
->i_packet_no
= 0;
1015 p_stream
->b_started
= true;
1017 if( p_stream
->i_fourcc
== VLC_CODEC_VORBIS
||
1018 p_stream
->i_fourcc
== VLC_CODEC_SPEEX
||
1019 p_stream
->i_fourcc
== VLC_CODEC_OPUS
||
1020 p_stream
->i_fourcc
== VLC_CODEC_THEORA
||
1021 p_stream
->i_fourcc
== VLC_CODEC_DAALA
)
1023 /* First packet in order: vorbis/speex/opus/theora/daala info */
1024 unsigned pi_size
[XIPH_MAX_HEADER_COUNT
];
1025 void *pp_data
[XIPH_MAX_HEADER_COUNT
];
1027 if( xiph_SplitHeaders( pi_size
, pp_data
, &i_count
,
1028 p_input
->p_fmt
->i_extra
, p_input
->p_fmt
->p_extra
) )
1035 op
.bytes
= pi_size
[0];
1036 op
.packet
= pp_data
[0];
1037 if( pi_size
[0] <= 0 )
1038 msg_Err( p_mux
, "header data corrupted");
1043 op
.packetno
= p_stream
->i_packet_no
++;
1044 ogg_stream_packetin( &p_stream
->os
, &op
);
1045 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1047 /* Get keyframe_granule_shift for theora or daala granulepos calculation */
1048 if( p_stream
->i_fourcc
== VLC_CODEC_THEORA
||
1049 p_stream
->i_fourcc
== VLC_CODEC_DAALA
)
1051 p_stream
->i_keyframe_granule_shift
=
1052 ( (op
.packet
[40] & 0x03) << 3 ) | ( (op
.packet
[41] & 0xe0) >> 5 );
1055 else if( p_stream
->i_fourcc
== VLC_CODEC_DIRAC
)
1057 op
.packet
= p_input
->p_fmt
->p_extra
;
1058 op
.bytes
= p_input
->p_fmt
->i_extra
;
1062 op
.packetno
= p_stream
->i_packet_no
++;
1063 ogg_stream_packetin( &p_stream
->os
, &op
);
1064 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1066 else if( p_stream
->i_fourcc
== VLC_CODEC_FLAC
)
1068 /* flac stream marker (yeah, only that in the 1st packet) */
1069 op
.packet
= (unsigned char *)"fLaC";
1074 op
.packetno
= p_stream
->i_packet_no
++;
1075 ogg_stream_packetin( &p_stream
->os
, &op
);
1076 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1078 else if( p_stream
->i_fourcc
== VLC_CODEC_VP8
)
1081 op
.packet
= malloc( 26 );
1085 OggFillVP8Header( op
.packet
, p_input
);
1089 op
.packetno
= p_stream
->i_packet_no
++;
1090 ogg_stream_packetin( &p_stream
->os
, &op
);
1091 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1094 else if( p_stream
->p_oggds_header
)
1097 op
.packet
= malloc( sizeof(*p_stream
->p_oggds_header
) + p_stream
->p_oggds_header
->i_size
);
1100 op
.bytes
= OggFillDsHeader( op
.packet
, p_stream
->p_oggds_header
, p_stream
->i_cat
);
1104 op
.packetno
= p_stream
->i_packet_no
++;
1105 ogg_stream_packetin( &p_stream
->os
, &op
);
1106 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1110 block_ChainAppend( &p_hdr
, p_og
);
1114 /* Create fisbones if any */
1115 if ( p_sys
->skeleton
.b_create
)
1117 for( i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1119 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1120 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1121 if ( p_stream
->skeleton
.b_fisbone_done
) continue;
1122 OggGetSkeletonFisbone( &op
.packet
, &op
.bytes
, p_input
, p_mux
);
1123 if ( op
.packet
== NULL
) return false;
1127 op
.packetno
= p_sys
->skeleton
.i_packet_no
++;
1128 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1129 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1130 block_ChainAppend( &p_hdr
, p_og
);
1131 p_stream
->skeleton
.b_fisbone_done
= true;
1135 /* Write previous headers */
1136 for( p_og
= p_hdr
; p_og
!= NULL
; p_og
= p_og
->p_next
)
1138 /* flag headers to be resent for streaming clients */
1139 p_og
->i_flags
|= BLOCK_FLAG_HEADER
;
1141 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_hdr
);
1144 /* Create indexes if any */
1145 for( i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1147 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1148 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1149 /* flush stream && save offset */
1150 if ( p_sys
->skeleton
.b_create
&& !p_stream
->skeleton
.b_index_done
)
1152 if ( !p_stream
->skeleton
.p_index
) AllocateIndex( p_mux
, p_input
);
1153 if ( p_stream
->skeleton
.p_index
)
1155 msg_Dbg( p_mux
, "Creating index for stream %d", p_stream
->i_serial_no
);
1156 OggGetSkeletonIndex( &op
.packet
, &op
.bytes
, p_stream
);
1157 if ( op
.packet
== NULL
) return false;
1161 op
.packetno
= p_sys
->skeleton
.i_packet_no
++;
1163 /* backup some values */
1164 p_stream
->skeleton
.i_index_offset
= p_mux
->p_sys
->i_pos
;
1165 p_stream
->skeleton
.i_index_packetno
= p_sys
->skeleton
.os
.packetno
;
1166 p_stream
->skeleton
.i_index_pageno
= p_sys
->skeleton
.os
.pageno
;
1168 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1169 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1170 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1172 p_stream
->skeleton
.b_index_done
= true;
1176 /* Take care of the non b_o_s headers */
1177 for( i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1179 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1180 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1182 if( p_stream
->i_fourcc
== VLC_CODEC_VORBIS
||
1183 p_stream
->i_fourcc
== VLC_CODEC_SPEEX
||
1184 p_stream
->i_fourcc
== VLC_CODEC_OPUS
||
1185 p_stream
->i_fourcc
== VLC_CODEC_THEORA
||
1186 p_stream
->i_fourcc
== VLC_CODEC_DAALA
)
1188 unsigned pi_size
[XIPH_MAX_HEADER_COUNT
];
1189 void *pp_data
[XIPH_MAX_HEADER_COUNT
];
1191 if( xiph_SplitHeaders( pi_size
, pp_data
, &i_count
,
1192 p_input
->p_fmt
->i_extra
, p_input
->p_fmt
->p_extra
) )
1195 /* Special case, headers are already there in the incoming stream.
1196 * We need to gather them an mark them as headers. */
1197 for( unsigned i
= 1; i
< i_count
; i
++ )
1199 op
.bytes
= pi_size
[i
];
1200 op
.packet
= pp_data
[i
];
1201 if( pi_size
[i
] <= 0 )
1202 msg_Err( p_mux
, "header data corrupted");
1207 op
.packetno
= p_stream
->i_packet_no
++;
1208 ogg_stream_packetin( &p_stream
->os
, &op
);
1209 msg_Dbg( p_mux
, "adding non bos, secondary header" );
1210 if( i
== i_count
- 1 )
1211 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1213 p_og
= OggStreamPageOut( p_mux
, &p_stream
->os
, 0 );
1215 block_ChainAppend( &p_hdr
, p_og
);
1218 else if( p_stream
->i_fourcc
!= VLC_CODEC_FLAC
&&
1219 p_stream
->i_fourcc
!= VLC_CODEC_DIRAC
)
1225 com
[0] = PACKET_TYPE_COMMENT
;
1226 i_com
= snprintf( (char *)(com
+1), 127,
1227 PACKAGE_VERSION
" stream output" )
1234 op
.packetno
= p_stream
->i_packet_no
++;
1235 ogg_stream_packetin( &p_stream
->os
, &op
);
1236 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1237 block_ChainAppend( &p_hdr
, p_og
);
1240 /* Special case for mp4v and flac */
1241 if( ( p_stream
->i_fourcc
== VLC_CODEC_MP4V
||
1242 p_stream
->i_fourcc
== VLC_CODEC_FLAC
) &&
1243 p_input
->p_fmt
->i_extra
)
1245 /* Send a packet with the VOL data for mp4v
1246 * or STREAMINFO for flac */
1247 msg_Dbg( p_mux
, "writing extra data" );
1248 op
.bytes
= p_input
->p_fmt
->i_extra
;
1249 op
.packet
= p_input
->p_fmt
->p_extra
;
1250 uint8_t flac_streaminfo
[34 + 4];
1251 if( p_stream
->i_fourcc
== VLC_CODEC_FLAC
)
1253 if (op
.bytes
== 42 && !memcmp(op
.packet
, "fLaC", 4)) {
1255 memcpy(flac_streaminfo
, op
.packet
+ 4, 38);
1256 op
.packet
= flac_streaminfo
;
1257 } else if (op
.bytes
== 34) {
1259 memcpy(flac_streaminfo
+ 4, op
.packet
, 34);
1260 flac_streaminfo
[0] = 0x80; /* last block, streaminfo */
1261 flac_streaminfo
[1] = 0;
1262 flac_streaminfo
[2] = 0;
1263 flac_streaminfo
[3] = 34; /* block size */
1264 op
.packet
= flac_streaminfo
;
1266 msg_Err(p_mux
, "Invalid FLAC streaminfo (%ld bytes)",
1273 op
.packetno
= p_stream
->i_packet_no
++;
1274 ogg_stream_packetin( &p_stream
->os
, &op
);
1275 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1276 block_ChainAppend( &p_hdr
, p_og
);
1280 if ( p_sys
->skeleton
.b_create
)
1282 msg_Dbg( p_mux
, "ending skeleton" );
1288 op
.packetno
= p_sys
->skeleton
.i_packet_no
++;
1289 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1290 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1291 block_ChainAppend( &p_hdr
, p_og
);
1294 /* set HEADER flag */
1295 /* flag headers to be resent for streaming clients */
1296 for( p_og
= p_hdr
; p_og
!= NULL
; p_og
= p_og
->p_next
)
1298 p_og
->i_flags
|= BLOCK_FLAG_HEADER
;
1301 /* Write previous headers */
1302 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_hdr
);
1307 static void OggCreateStreamFooter( sout_mux_t
*p_mux
, ogg_stream_t
*p_stream
)
1311 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1313 /* as stream is finished, overwrite the index, if there was any */
1314 if ( p_sys
->skeleton
.b_create
&& p_stream
->skeleton
.p_index
1315 && p_stream
->skeleton
.i_index_payload
)
1317 sout_AccessOutSeek( p_mux
->p_access
, p_stream
->skeleton
.i_index_offset
);
1318 OggGetSkeletonIndex( &op
.packet
, &op
.bytes
, p_stream
);
1319 if ( op
.packet
!= NULL
)
1321 msg_Dbg(p_mux
, "Rewriting index at %"PRId64
, p_stream
->skeleton
.i_index_offset
);
1322 ogg_stream_reset_serialno( &p_sys
->skeleton
.os
, p_sys
->skeleton
.i_serial_no
);
1326 op
.packetno
= p_stream
->skeleton
.i_index_packetno
+ 1;
1327 /* fake our stream state */
1328 p_sys
->skeleton
.os
.pageno
= p_stream
->skeleton
.i_index_pageno
;
1329 p_sys
->skeleton
.os
.packetno
= p_stream
->skeleton
.i_index_packetno
;
1330 p_sys
->skeleton
.os
.granulepos
= 0;
1331 p_sys
->skeleton
.os
.b_o_s
= 1;
1332 p_sys
->skeleton
.os
.e_o_s
= 0;
1333 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1334 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1335 sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1337 sout_AccessOutSeek( p_mux
->p_access
, p_sys
->i_pos
);
1340 /* clear skeleton */
1341 p_stream
->skeleton
.b_fisbone_done
= false;
1342 p_stream
->skeleton
.b_index_done
= false;
1343 p_stream
->skeleton
.i_index_offset
= 0;
1344 p_stream
->skeleton
.i_index_payload
= 0;
1345 p_stream
->skeleton
.i_last_keyframe_pos
= 0;
1346 p_stream
->skeleton
.i_last_keyframe_time
= 0;
1347 /* clear accounting */
1348 p_stream
->i_num_frames
= 0;
1349 p_stream
->i_num_keyframes
= 0;
1351 /* Write eos packet for stream. */
1356 op
.granulepos
= p_stream
->u_last_granulepos
;
1357 op
.packetno
= p_stream
->i_packet_no
++;
1358 ogg_stream_packetin( &p_stream
->os
, &op
);
1360 /* flush it with all remaining data */
1361 if( ( p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 ) ) )
1364 OggSetDate( p_og
, p_stream
->i_dts
, p_stream
->i_length
);
1365 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1368 ogg_stream_clear( &p_stream
->os
);
1371 static void OggSetDate( block_t
*p_og
, mtime_t i_dts
, mtime_t i_length
)
1377 for( p_tmp
= p_og
, i_count
= 0; p_tmp
!= NULL
; p_tmp
= p_tmp
->p_next
)
1382 if( i_count
== 0 ) return; /* ignore. */
1384 i_delta
= i_length
/ i_count
;
1386 for( p_tmp
= p_og
; p_tmp
!= NULL
; p_tmp
= p_tmp
->p_next
)
1388 p_tmp
->i_dts
= i_dts
;
1389 p_tmp
->i_length
= i_delta
;
1395 static void OggRewriteFisheadPage( sout_mux_t
*p_mux
)
1397 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1400 op
.packet
= calloc( 1, op
.bytes
);
1401 if ( op
.packet
!= NULL
)
1407 ogg_stream_reset_serialno( &p_sys
->skeleton
.os
, p_sys
->skeleton
.i_serial_no
);
1408 OggFillSkeletonFishead( op
.packet
, p_mux
);
1409 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1410 msg_Dbg( p_mux
, "rewriting fishead at %"PRId64
, p_mux
->p_sys
->skeleton
.i_fishead_offset
);
1411 sout_AccessOutSeek( p_mux
->p_access
, p_mux
->p_sys
->skeleton
.i_fishead_offset
);
1412 sout_AccessOutWrite( p_mux
->p_access
,
1413 OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 ) );
1414 sout_AccessOutSeek( p_mux
->p_access
, p_mux
->p_sys
->i_pos
);
1418 static bool AllocateIndex( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
1420 ogg_stream_t
*p_stream
= (ogg_stream_t
*) p_input
->p_sys
;
1423 if ( p_stream
->i_length
)
1425 uint64_t i_interval
= (uint64_t)p_mux
->p_sys
->skeleton
.i_index_intvl
* 1000;
1428 if( p_input
->p_fmt
->i_cat
== VIDEO_ES
&&
1429 p_input
->p_fmt
->video
.i_frame_rate
)
1431 /* optimize for fps < 1 */
1432 i_interval
= __MAX( p_mux
->p_sys
->skeleton
.i_index_intvl
* 1000,
1434 p_input
->p_fmt
->video
.i_frame_rate_base
/
1435 p_input
->p_fmt
->video
.i_frame_rate
);
1438 size_t i_tuple_size
= 0;
1439 /* estimate length of pos value */
1440 if ( p_input
->p_fmt
->i_bitrate
)
1442 i
= i_interval
* p_input
->p_fmt
->i_bitrate
/ 1000000;
1443 while ( i
<<= 1 ) i_tuple_size
++;
1447 /* Likely 64KB<<keyframe interval<<16MB */
1448 /* We can't really guess due to muxing */
1449 i_tuple_size
= 24 / 8;
1452 /* add length of interval value */
1454 while ( i
<<= 1 ) i_tuple_size
++;
1456 i_size
= i_tuple_size
* ( p_stream
->i_length
/ i_interval
+ 2 );
1460 i_size
= ( INT64_C(3600) * 11.2 * 1000 / p_mux
->p_sys
->skeleton
.i_index_intvl
)
1461 * p_mux
->p_sys
->skeleton
.i_index_ratio
;
1462 msg_Dbg( p_mux
, "No stream length, using default allocation for index" );
1464 i_size
*= ( 8.0 / 7 ); /* 7bits encoding overhead */
1465 msg_Dbg( p_mux
, "allocating %zu bytes for index", i_size
);
1466 p_stream
->skeleton
.p_index
= calloc( i_size
, sizeof(uint8_t) );
1467 if ( !p_stream
->skeleton
.p_index
) return false;
1468 p_stream
->skeleton
.i_index_size
= i_size
;
1469 p_stream
->skeleton
.i_index_payload
= 0;
1473 /*****************************************************************************
1474 * Mux: multiplex available data in input fifos into the Ogg bitstream
1475 *****************************************************************************/
1476 static int Mux( sout_mux_t
*p_mux
)
1478 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1481 /* End any stream that ends in that group */
1482 if ( p_sys
->i_del_streams
)
1484 /* Remove deleted logical streams */
1485 for( int i
= 0; i
< p_sys
->i_del_streams
; i
++ )
1487 OggCreateStreamFooter( p_mux
, p_sys
->pp_del_streams
[i
] );
1488 FREENULL( p_sys
->pp_del_streams
[i
]->p_oggds_header
);
1489 FREENULL( p_sys
->pp_del_streams
[i
] );
1491 FREENULL( p_sys
->pp_del_streams
);
1492 p_sys
->i_del_streams
= 0;
1495 if ( p_sys
->i_streams
== 0 )
1497 /* All streams have been deleted, or none have ever been created
1498 From this point, we are allowed to start a new group of logical streams */
1499 p_sys
->skeleton
.b_head_done
= false;
1500 p_sys
->b_can_add_streams
= true;
1501 p_sys
->i_segment_start
= p_sys
->i_pos
;
1504 if ( p_sys
->i_add_streams
)
1506 if ( !p_sys
->b_can_add_streams
)
1508 msg_Warn( p_mux
, "Can't add new stream %d/%d: Considerer increasing sout-mux-caching variable", p_sys
->i_del_streams
, p_mux
->p_sys
->i_streams
);
1509 msg_Warn( p_mux
, "Resetting and setting new identity to current streams");
1511 /* resetting all active streams */
1512 for ( int i
=0; i
< p_mux
->p_sys
->i_streams
; i
++ )
1514 ogg_stream_t
* p_stream
= (ogg_stream_t
*) p_mux
->pp_inputs
[i
]->p_sys
;
1515 if ( p_stream
->b_finished
|| !p_stream
->b_started
) continue;
1516 OggCreateStreamFooter( p_mux
, p_stream
);
1517 p_stream
->i_serial_no
= p_sys
->i_next_serial_no
++;
1518 p_stream
->i_packet_no
= 0;
1519 p_stream
->b_finished
= true;
1522 /* rewrite fishead with final values */
1523 if ( p_sys
->skeleton
.b_head_done
)
1525 OggRewriteFisheadPage( p_mux
);
1528 p_sys
->b_can_add_streams
= true;
1529 p_sys
->skeleton
.b_head_done
= false;
1530 p_sys
->i_segment_start
= p_sys
->i_pos
;
1533 /* Open new ogg stream */
1534 if( sout_MuxGetStream( p_mux
, 1, &i_dts
) < 0 )
1536 msg_Dbg( p_mux
, "waiting for data..." );
1539 msg_Dbg( p_mux
, "writing streams headers" );
1540 p_sys
->i_start_dts
= i_dts
;
1541 p_sys
->i_streams
= p_mux
->i_nb_inputs
;
1542 p_sys
->i_del_streams
= 0;
1543 p_sys
->i_add_streams
= 0;
1544 p_sys
->skeleton
.b_create
= true;
1546 if ( ! OggCreateHeaders( p_mux
) )
1549 /* If we're switching to end of headers, then that's data start */
1550 if ( p_sys
->b_can_add_streams
)
1552 msg_Dbg( p_mux
, "data starts from %zu", p_sys
->i_pos
);
1553 p_sys
->i_data_start
= p_sys
->i_pos
;
1556 /* Since we started sending secondaryheader or data pages,
1557 * we're no longer allowed to create new streams, until all streams end */
1558 p_sys
->b_can_add_streams
= false;
1561 /* Do the regular data mux thing */
1564 int i_stream
= sout_MuxGetStream( p_mux
, 1, NULL
);
1567 MuxBlock( p_mux
, p_mux
->pp_inputs
[i_stream
] );
1573 static int MuxBlock( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
1575 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1576 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1577 block_t
*p_data
= block_FifoGet( p_input
->p_fifo
);
1578 block_t
*p_og
= NULL
;
1582 if( p_stream
->i_fourcc
!= VLC_CODEC_VORBIS
&&
1583 p_stream
->i_fourcc
!= VLC_CODEC_FLAC
&&
1584 p_stream
->i_fourcc
!= VLC_CODEC_SPEEX
&&
1585 p_stream
->i_fourcc
!= VLC_CODEC_OPUS
&&
1586 p_stream
->i_fourcc
!= VLC_CODEC_THEORA
&&
1587 p_stream
->i_fourcc
!= VLC_CODEC_DAALA
&&
1588 p_stream
->i_fourcc
!= VLC_CODEC_VP8
&&
1589 p_stream
->i_fourcc
!= VLC_CODEC_DIRAC
)
1591 p_data
= block_Realloc( p_data
, 1, p_data
->i_buffer
);
1592 p_data
->p_buffer
[0] = PACKET_IS_SYNCPOINT
; // FIXME
1595 if ( p_stream
->i_fourcc
== VLC_CODEC_DIRAC
&& p_stream
->i_baseptsdelay
< 0 )
1596 p_stream
->i_baseptsdelay
= p_data
->i_pts
- p_data
->i_dts
;
1598 op
.packet
= p_data
->p_buffer
;
1599 op
.bytes
= p_data
->i_buffer
;
1602 op
.packetno
= p_stream
->i_packet_no
++;
1605 if( p_stream
->i_cat
== AUDIO_ES
)
1607 if( p_stream
->i_fourcc
== VLC_CODEC_VORBIS
||
1608 p_stream
->i_fourcc
== VLC_CODEC_FLAC
||
1609 p_stream
->i_fourcc
== VLC_CODEC_OPUS
||
1610 p_stream
->i_fourcc
== VLC_CODEC_SPEEX
)
1612 /* number of sample from begining + current packet */
1614 ( p_data
->i_dts
- p_sys
->i_start_dts
+ p_data
->i_length
) *
1615 (mtime_t
)p_input
->p_fmt
->audio
.i_rate
/ CLOCK_FREQ
;
1617 i_time
= p_data
->i_dts
- p_sys
->i_start_dts
;
1618 AddIndexEntry( p_mux
, i_time
, p_input
);
1620 else if( p_stream
->p_oggds_header
)
1622 /* number of sample from begining */
1623 op
.granulepos
= ( p_data
->i_dts
- p_sys
->i_start_dts
) *
1624 p_stream
->p_oggds_header
->i_samples_per_unit
/ CLOCK_FREQ
;
1627 else if( p_stream
->i_cat
== VIDEO_ES
)
1629 if( p_stream
->i_fourcc
== VLC_CODEC_THEORA
||
1630 p_stream
->i_fourcc
== VLC_CODEC_DAALA
)
1632 p_stream
->i_num_frames
++;
1633 if( p_data
->i_flags
& BLOCK_FLAG_TYPE_I
)
1635 p_stream
->i_num_keyframes
++;
1636 p_stream
->i_last_keyframe
= p_stream
->i_num_frames
;
1638 /* presentation time */
1639 i_time
= CLOCK_FREQ
* ( p_stream
->i_num_frames
- 1 ) *
1640 p_input
->p_fmt
->video
.i_frame_rate_base
/ p_input
->p_fmt
->video
.i_frame_rate
;
1641 AddIndexEntry( p_mux
, i_time
, p_input
);
1644 op
.granulepos
= (p_stream
->i_last_keyframe
<< p_stream
->i_keyframe_granule_shift
)
1645 | (p_stream
->i_num_frames
-p_stream
->i_last_keyframe
);
1647 else if( p_stream
->i_fourcc
== VLC_CODEC_DIRAC
)
1650 #define FRAME_ROUND(a) \
1651 if ( ( a + 5000 / CLOCK_FREQ ) > ( a / CLOCK_FREQ ) )\
1655 mtime_t dt
= (p_data
->i_dts
- p_sys
->i_start_dts
) * p_input
->p_fmt
->video
.i_frame_rate
/ p_input
->p_fmt
->video
.i_frame_rate_base
;
1658 mtime_t pt
= (p_data
->i_pts
- p_sys
->i_start_dts
- p_stream
->i_baseptsdelay
) * p_input
->p_fmt
->video
.i_frame_rate
/ p_input
->p_fmt
->video
.i_frame_rate_base
;
1661 /* (shro) some PTS could be repeated within 1st frames */
1662 if ( pt
== p_stream
->i_dirac_last_pt
)
1665 p_stream
->i_dirac_last_pt
= pt
;
1667 /* (shro) some DTS could be repeated within 1st frames */
1668 if ( dt
== p_stream
->i_dirac_last_dt
)
1671 p_stream
->i_dirac_last_dt
= dt
;
1673 if( p_data
->i_flags
& BLOCK_FLAG_TYPE_I
)
1674 p_stream
->i_last_keyframe
= dt
;
1675 mtime_t dist
= dt
- p_stream
->i_last_keyframe
;
1677 /* Everything increments by two for progressive */
1684 mtime_t delay
= pt
- dt
;
1685 if ( delay
< 0 ) delay
*= -1;
1687 op
.granulepos
= (pt
- delay
) << 31 | (dist
&0xff00) << 14
1688 | (delay
&0x1fff) << 9 | (dist
&0xff);
1690 msg_Dbg( p_mux
, "dts %"PRId64
" pts %"PRId64
" dt %"PRId64
" pt %"PRId64
" delay %"PRId64
" granule %"PRId64
,
1691 (p_data
->i_dts
- p_sys
->i_start_dts
),
1692 (p_data
->i_pts
- p_sys
->i_start_dts
),
1693 dt
, pt
, delay
, op
.granulepos
);
1696 AddIndexEntry( p_mux
, dt
, p_input
);
1698 else if( p_stream
->i_fourcc
== VLC_CODEC_VP8
)
1700 p_stream
->i_num_frames
++;
1701 if( p_data
->i_flags
& BLOCK_FLAG_TYPE_I
)
1703 p_stream
->i_num_keyframes
++;
1704 p_stream
->i_last_keyframe
= p_stream
->i_num_frames
;
1706 /* presentation time */
1707 i_time
= CLOCK_FREQ
* ( p_stream
->i_num_frames
- 1 ) *
1708 p_input
->p_fmt
->video
.i_frame_rate_base
/ p_input
->p_fmt
->video
.i_frame_rate
;
1709 AddIndexEntry( p_mux
, i_time
, p_input
);
1711 op
.granulepos
= ( ((int64_t)p_stream
->i_num_frames
) << 32 ) |
1712 ( ( ( p_stream
->i_num_frames
- p_stream
->i_last_keyframe
) & 0x07FFFFFF ) << 3 );
1714 else if( p_stream
->p_oggds_header
)
1715 op
.granulepos
= ( p_data
->i_dts
- p_sys
->i_start_dts
) * INT64_C(10) /
1716 p_stream
->p_oggds_header
->i_time_unit
;
1718 else if( p_stream
->i_cat
== SPU_ES
)
1720 /* granulepos is in millisec */
1721 op
.granulepos
= ( p_data
->i_dts
- p_sys
->i_start_dts
) / 1000;
1724 return VLC_EGENERIC
;
1726 p_stream
->u_last_granulepos
= op
.granulepos
;
1727 ogg_stream_packetin( &p_stream
->os
, &op
);
1729 if( p_stream
->i_cat
== SPU_ES
||
1730 p_stream
->i_fourcc
== VLC_CODEC_SPEEX
||
1731 p_stream
->i_fourcc
== VLC_CODEC_DIRAC
)
1733 /* Subtitles or Speex packets are quite small so they
1734 * need to be flushed to be sent on time */
1735 /* The OggDirac mapping suggests ever so strongly that a
1736 * page flush occurs after each OggDirac packet, so to make
1737 * the timestamps unambiguous */
1738 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, p_data
->i_dts
);
1742 p_og
= OggStreamPageOut( p_mux
, &p_stream
->os
, p_data
->i_dts
);
1747 OggSetDate( p_og
, p_stream
->i_dts
, p_stream
->i_length
);
1748 p_stream
->i_dts
= -1;
1749 p_stream
->i_length
= 0;
1750 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1754 if( p_stream
->i_dts
< 0 )
1756 p_stream
->i_dts
= p_data
->i_dts
;
1758 p_stream
->i_length
+= p_data
->i_length
;
1761 block_Release( p_data
);