1 /*****************************************************************************
2 * ogg.c: ogg muxer module for vlc
3 *****************************************************************************
4 * Copyright (C) 2001, 2002, 2006 VLC authors and VideoLAN
6 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
7 * Gildas Bazin <gbazin@videolan.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
35 #include <vlc_block.h>
36 #include <vlc_codecs.h>
39 #include "../demux/xiph.h"
43 /*****************************************************************************
45 *****************************************************************************/
46 #define INDEXINTVL_TEXT N_("Index interval")
47 #define INDEXINTVL_LONGTEXT N_("Minimal index interval, in milliseconds. " \
48 "Set to 0 to disable index creation.")
49 #define INDEXRATIO_TEXT N_("Index size ratio")
50 #define INDEXRATIO_LONGTEXT N_(\
51 "Set index size ratio. Alters default (60min content) or estimated size." )
53 static int Open ( vlc_object_t
* );
54 static void Close ( vlc_object_t
* );
56 #define SOUT_CFG_PREFIX "sout-ogg-"
59 set_description( N_("Ogg/OGM muxer") )
60 set_capability( "sout mux", 10 )
61 set_category( CAT_SOUT
)
62 set_subcategory( SUBCAT_SOUT_MUX
)
63 add_shortcut( "ogg", "ogm" )
64 add_integer_with_range( SOUT_CFG_PREFIX
"indexintvl", 1000, 0, INT_MAX
,
65 INDEXINTVL_TEXT
, INDEXINTVL_LONGTEXT
, true )
66 add_float_with_range( SOUT_CFG_PREFIX
"indexratio", 1.0, 1.0, 1000,
67 INDEXRATIO_TEXT
, INDEXRATIO_LONGTEXT
, true )
68 set_callbacks( Open
, Close
)
72 /*****************************************************************************
74 *****************************************************************************/
75 static int Control ( sout_mux_t
*, int, va_list );
76 static int AddStream( sout_mux_t
*, sout_input_t
* );
77 static void DelStream( sout_mux_t
*, sout_input_t
* );
78 static int Mux ( sout_mux_t
* );
79 static int MuxBlock ( sout_mux_t
*, sout_input_t
* );
81 static bool OggCreateHeaders( sout_mux_t
* );
82 static void OggFillSkeletonFishead( uint8_t *p_buffer
, sout_mux_t
*p_mux
);
84 /*****************************************************************************
86 *****************************************************************************/
89 #define FISBONE_BASE_SIZE 52
90 #define FISBONE_BASE_OFFSET 44
91 #define INDEX_BASE_SIZE 42
93 /* Structures used for OggDS headers used in ogm files */
95 #define PACKET_TYPE_HEADER 0x01
96 #define PACKET_TYPE_COMMENT 0x03
97 #define PACKET_IS_SYNCPOINT 0x08
103 } oggds_header_video_t
;
108 int16_t i_block_align
;
109 int32_t i_avgbytespersec
;
110 } oggds_header_audio_t
;
114 uint8_t i_packet_type
;
122 int64_t i_samples_per_unit
;
123 int32_t i_default_len
;
125 int32_t i_buffer_size
;
126 int16_t i_bits_per_sample
;
128 int16_t i_padding_0
; /* Because the original is using MSVC packing style */
132 oggds_header_video_t video
;
133 oggds_header_audio_t audio
;
136 int32_t i_padding_1
; /* Because the original is using MSVC packing style */
140 /*****************************************************************************
141 * Definitions of structures and functions used by this plugins
142 *****************************************************************************/
153 int i_keyframe_granule_shift
; /* Theora and Daala only */
154 int i_last_keyframe
; /* dirac and theora */
155 int i_num_frames
; /* Theora only */
156 uint64_t u_last_granulepos
; /* Used for correct EOS page */
157 int64_t i_num_keyframes
;
160 oggds_header_t
*p_oggds_header
;
168 /* Skeleton index storage */
169 unsigned char *p_index
;
170 uint64_t i_index_size
;
171 uint64_t i_index_payload
; /* real index size */
172 uint64_t i_index_count
;
173 /* backup values for rewriting index page later */
174 uint64_t i_index_offset
; /* sout offset of the index page */
175 int64_t i_index_packetno
; /* index packet no */
177 /* index creation tracking values */
178 uint64_t i_last_keyframe_pos
;
179 vlc_tick_t i_last_keyframe_time
;
184 vlc_tick_t i_baseptsdelay
;
192 vlc_tick_t i_start_dts
;
193 int i_next_serial_no
;
195 /* number of logical streams pending to be added */
197 bool b_can_add_streams
;
199 /* logical streams pending to be deleted */
201 ogg_stream_t
**pp_del_streams
;
211 /* backup values for rewriting fishead page later */
212 uint64_t i_fishead_offset
; /* sout offset of the fishead page */
213 vlc_tick_t i_index_intvl
;
217 /* access position */
219 ssize_t i_data_start
;
220 ssize_t i_segment_start
;
223 static void OggSetDate( block_t
*, vlc_tick_t
, vlc_tick_t
);
224 static block_t
*OggStreamFlush( sout_mux_t
*, ogg_stream_state
*, vlc_tick_t
);
225 static void OggCreateStreamFooter( sout_mux_t
*p_mux
, ogg_stream_t
*p_stream
);
226 static void OggRewriteFisheadPage( sout_mux_t
*p_mux
);
227 static bool AllocateIndex( sout_mux_t
*p_mux
, sout_input_t
*p_input
);
229 /*****************************************************************************
231 *****************************************************************************/
232 static int Open( vlc_object_t
*p_this
)
234 sout_mux_t
*p_mux
= (sout_mux_t
*)p_this
;
235 sout_mux_sys_t
*p_sys
;
237 msg_Info( p_mux
, "Open" );
239 p_sys
= malloc( sizeof( sout_mux_sys_t
) );
242 p_sys
->i_streams
= 0;
243 p_sys
->i_add_streams
= 0;
244 p_sys
->b_can_add_streams
= true;
245 p_sys
->i_del_streams
= 0;
246 p_sys
->pp_del_streams
= 0;
248 p_sys
->skeleton
.b_create
= false;
249 p_sys
->skeleton
.b_head_done
= false;
250 p_sys
->skeleton
.i_index_intvl
=
251 VLC_TICK_FROM_MS(var_InheritInteger( p_this
, SOUT_CFG_PREFIX
"indexintvl" ));
252 p_sys
->skeleton
.i_index_ratio
=
253 var_InheritFloat( p_this
, SOUT_CFG_PREFIX
"indexratio" );
254 p_sys
->i_data_start
= 0;
255 p_sys
->i_segment_start
= 0;
256 p_mux
->p_sys
= p_sys
;
257 p_mux
->pf_control
= Control
;
258 p_mux
->pf_addstream
= AddStream
;
259 p_mux
->pf_delstream
= DelStream
;
262 /* First serial number is random.
263 * (Done like this because on win32 you need to seed the random number
264 * generator once per thread). */
266 vlc_rand_bytes(&r
, sizeof(r
));
267 p_sys
->i_next_serial_no
= r
& INT_MAX
;
272 /*****************************************************************************
273 * Close: Finalize ogg bitstream and close muxer
274 *****************************************************************************/
275 static void Close( vlc_object_t
* p_this
)
277 sout_mux_t
*p_mux
= (sout_mux_t
*)p_this
;
278 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
280 msg_Info( p_mux
, "Close" );
282 if( p_sys
->i_del_streams
)
284 /* Close the current ogg stream */
285 msg_Dbg( p_mux
, "writing footers" );
287 /* Remove deleted logical streams */
288 for(int i
= 0; i
< p_sys
->i_del_streams
; i
++ )
290 ogg_stream_t
*p_stream
= p_sys
->pp_del_streams
[i
];
292 es_format_Clean( &p_stream
->fmt
);
293 OggCreateStreamFooter( p_mux
, p_stream
);
294 free( p_stream
->p_oggds_header
);
295 free( p_stream
->skeleton
.p_index
);
298 free( p_sys
->pp_del_streams
);
299 p_sys
->i_streams
-= p_sys
->i_del_streams
;
302 /* rewrite fishead with final values */
303 if ( p_sys
->skeleton
.b_create
&& p_sys
->skeleton
.b_head_done
)
305 OggRewriteFisheadPage( p_mux
);
311 /*****************************************************************************
313 *****************************************************************************/
314 static int Control( sout_mux_t
*p_mux
, int i_query
, va_list args
)
322 case MUX_CAN_ADD_STREAM_WHILE_MUXING
:
323 pb_bool
= va_arg( args
, bool * );
328 ppsz
= va_arg( args
, char ** );
329 *ppsz
= strdup( "application/ogg" );
336 /*****************************************************************************
337 * AddStream: Add an elementary stream to the muxed stream
338 *****************************************************************************/
339 static int AddStream( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
341 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
342 ogg_stream_t
*p_stream
;
345 msg_Dbg( p_mux
, "adding input" );
347 p_input
->p_sys
= p_stream
= calloc( 1, sizeof( ogg_stream_t
) );
351 if( es_format_Copy( &p_stream
->fmt
, &p_input
->fmt
) != VLC_SUCCESS
)
357 p_stream
->i_serial_no
= p_sys
->i_next_serial_no
++;
358 p_stream
->i_packet_no
= 0;
359 p_stream
->i_last_keyframe
= 0;
360 p_stream
->i_num_keyframes
= 0;
361 p_stream
->i_num_frames
= 0;
363 p_stream
->p_oggds_header
= 0;
365 p_stream
->i_baseptsdelay
= -1;
366 p_stream
->i_dirac_last_pt
= -1;
367 p_stream
->i_dirac_last_dt
= -1;
369 switch( p_input
->p_fmt
->i_cat
)
373 if( !p_stream
->fmt
.video
.i_frame_rate
||
374 !p_stream
->fmt
.video
.i_frame_rate_base
)
376 msg_Warn( p_mux
, "Missing frame rate, assuming 25fps" );
377 p_stream
->fmt
.video
.i_frame_rate
= 25;
378 p_stream
->fmt
.video
.i_frame_rate_base
= 1;
381 switch( p_stream
->fmt
.i_codec
)
392 p_stream
->p_oggds_header
= calloc( 1, sizeof(oggds_header_t
) );
393 if( !p_stream
->p_oggds_header
)
398 p_stream
->p_oggds_header
->i_packet_type
= PACKET_TYPE_HEADER
;
400 memcpy( p_stream
->p_oggds_header
->stream_type
, "video", 5 );
401 if( p_stream
->fmt
.i_codec
== VLC_CODEC_MP4V
)
403 memcpy( p_stream
->p_oggds_header
->sub_type
, "XVID", 4 );
405 else if( p_stream
->fmt
.i_codec
== VLC_CODEC_DIV3
)
407 memcpy( p_stream
->p_oggds_header
->sub_type
, "DIV3", 4 );
411 memcpy( p_stream
->p_oggds_header
->sub_type
,
412 &p_stream
->fmt
.i_codec
, 4 );
414 p_stream
->p_oggds_header
->i_size
= 0 ;
415 p_stream
->p_oggds_header
->i_time_unit
=
416 MSFTIME_FROM_SEC(1) * p_stream
->fmt
.video
.i_frame_rate_base
/
417 (int64_t)p_stream
->fmt
.video
.i_frame_rate
;
418 p_stream
->p_oggds_header
->i_samples_per_unit
= 1;
419 p_stream
->p_oggds_header
->i_default_len
= 1 ; /* ??? */
420 p_stream
->p_oggds_header
->i_buffer_size
= 1024*1024;
421 p_stream
->p_oggds_header
->i_bits_per_sample
= 0;
422 p_stream
->p_oggds_header
->header
.video
.i_width
= p_input
->p_fmt
->video
.i_width
;
423 p_stream
->p_oggds_header
->header
.video
.i_height
= p_input
->p_fmt
->video
.i_height
;
424 msg_Dbg( p_mux
, "%4.4s stream", (char *)&p_stream
->fmt
.i_codec
);
427 case VLC_CODEC_DIRAC
:
428 msg_Dbg( p_mux
, "dirac stream" );
431 case VLC_CODEC_THEORA
:
432 msg_Dbg( p_mux
, "theora stream" );
435 case VLC_CODEC_DAALA
:
436 msg_Dbg( p_mux
, "daala stream" );
440 msg_Dbg( p_mux
, "VP8 stream" );
444 FREENULL( p_input
->p_sys
);
451 switch( p_stream
->fmt
.i_codec
)
454 msg_Dbg( p_mux
, "opus stream" );
457 case VLC_CODEC_VORBIS
:
458 msg_Dbg( p_mux
, "vorbis stream" );
461 case VLC_CODEC_SPEEX
:
462 msg_Dbg( p_mux
, "speex stream" );
466 msg_Dbg( p_mux
, "flac stream" );
470 fourcc_to_wf_tag( p_stream
->fmt
.i_codec
, &i_tag
);
471 if( i_tag
== WAVE_FORMAT_UNKNOWN
)
473 FREENULL( p_input
->p_sys
);
477 p_stream
->p_oggds_header
=
478 malloc( sizeof(oggds_header_t
) + p_input
->p_fmt
->i_extra
);
479 if( !p_stream
->p_oggds_header
)
484 memset( p_stream
->p_oggds_header
, 0, sizeof(oggds_header_t
) );
485 p_stream
->p_oggds_header
->i_packet_type
= PACKET_TYPE_HEADER
;
487 p_stream
->p_oggds_header
->i_size
= p_input
->p_fmt
->i_extra
;
489 if( p_input
->p_fmt
->i_extra
)
491 memcpy( &p_stream
->p_oggds_header
[1],
492 p_input
->p_fmt
->p_extra
, p_input
->p_fmt
->i_extra
);
495 memcpy( p_stream
->p_oggds_header
->stream_type
, "audio", 5 );
498 memset( buf
, 0, sizeof(buf
) );
499 snprintf( buf
, sizeof(buf
), "%"PRIx16
, i_tag
);
501 memcpy( p_stream
->p_oggds_header
->sub_type
, buf
, 4 );
503 p_stream
->p_oggds_header
->i_time_unit
= MSFTIME_FROM_SEC(1);
504 p_stream
->p_oggds_header
->i_default_len
= 1;
505 p_stream
->p_oggds_header
->i_buffer_size
= 30*1024 ;
506 p_stream
->p_oggds_header
->i_samples_per_unit
= p_input
->p_fmt
->audio
.i_rate
;
507 p_stream
->p_oggds_header
->i_bits_per_sample
= p_input
->p_fmt
->audio
.i_bitspersample
;
508 p_stream
->p_oggds_header
->header
.audio
.i_channels
= p_input
->p_fmt
->audio
.i_channels
;
509 p_stream
->p_oggds_header
->header
.audio
.i_block_align
= p_input
->p_fmt
->audio
.i_blockalign
;
510 p_stream
->p_oggds_header
->header
.audio
.i_avgbytespersec
= p_input
->p_fmt
->i_bitrate
/ 8;
511 msg_Dbg( p_mux
, "%4.4s stream", (char *)&p_stream
->fmt
.i_codec
);
517 switch( p_stream
->fmt
.i_codec
)
520 p_stream
->p_oggds_header
= calloc( 1, sizeof(oggds_header_t
) );
521 if( !p_stream
->p_oggds_header
)
526 p_stream
->p_oggds_header
->i_packet_type
= PACKET_TYPE_HEADER
;
528 memcpy( p_stream
->p_oggds_header
->stream_type
, "text", 4 );
529 msg_Dbg( p_mux
, "subtitles stream" );
533 FREENULL( p_input
->p_sys
);
538 FREENULL( p_input
->p_sys
);
542 p_stream
->b_new
= true;
544 p_sys
->i_add_streams
++;
549 /*****************************************************************************
550 * DelStream: Delete an elementary stream from the muxed stream
551 *****************************************************************************/
552 static void DelStream( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
554 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
555 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
558 msg_Dbg( p_mux
, "removing input" );
560 /* flush all remaining data */
563 if( !p_stream
->b_new
)
565 while( block_FifoCount( p_input
->p_fifo
) )
566 MuxBlock( p_mux
, p_input
);
569 if( !p_stream
->b_new
&&
570 ( p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 ) ) )
572 OggSetDate( p_og
, p_stream
->i_dts
, p_stream
->i_length
);
573 p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
576 /* move input in delete queue */
577 if( !p_stream
->b_new
)
579 p_sys
->pp_del_streams
= xrealloc( p_sys
->pp_del_streams
,
580 (p_sys
->i_del_streams
+ 1) * sizeof(ogg_stream_t
*) );
581 p_sys
->pp_del_streams
[p_sys
->i_del_streams
++] = p_stream
;
585 /* wasn't already added so get rid of it */
586 FREENULL( p_stream
->p_oggds_header
);
587 FREENULL( p_stream
);
588 p_sys
->i_add_streams
--;
592 p_input
->p_sys
= NULL
;
595 /*****************************************************************************
596 * Ogg Skeleton helpers
597 *****************************************************************************/
598 static int WriteQWVariableLE( uint64_t i_64
, uint64_t i_offset
,
599 uint8_t *p_buffer
, int i_buffer_size
)
601 uint8_t *p_dest
= p_buffer
+ i_offset
;
606 if ( p_dest
- p_buffer
>= i_buffer_size
) return -1;
608 *p_dest
= (uint8_t) ( i_64
& 0x7F );
622 static bool AddIndexEntry( sout_mux_t
*p_mux
, vlc_tick_t i_time
, sout_input_t
*p_input
)
624 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
625 ogg_stream_t
*p_stream
= (ogg_stream_t
*) p_input
->p_sys
;
627 vlc_tick_t i_timedelta
;
628 if ( !p_sys
->skeleton
.b_create
|| p_sys
->skeleton
.i_index_intvl
== 0
629 || !p_stream
->skeleton
.p_index
)
632 if ( p_stream
->skeleton
.i_last_keyframe_pos
== 0 )
633 p_stream
->skeleton
.i_last_keyframe_pos
= p_sys
->i_segment_start
;
634 i_posdelta
= p_sys
->i_pos
- p_stream
->skeleton
.i_last_keyframe_pos
;
635 i_timedelta
= i_time
- p_stream
->skeleton
.i_last_keyframe_time
;
637 if ( i_timedelta
<= p_sys
->skeleton
.i_index_intvl
|| i_posdelta
<= 0xFFFF )
642 if ( !p_stream
->skeleton
.p_index
) return false;
643 uint64_t i_offset
= p_stream
->skeleton
.i_index_payload
;
644 i_ret
= WriteQWVariableLE( i_posdelta
, i_offset
, p_stream
->skeleton
.p_index
,
645 p_stream
->skeleton
.i_index_size
);
646 if ( i_ret
== -1 ) return false;
648 i_ret
= WriteQWVariableLE( i_timedelta
, i_offset
, p_stream
->skeleton
.p_index
,
649 p_stream
->skeleton
.i_index_size
);
650 if ( i_ret
== -1 ) return false;
651 p_stream
->skeleton
.i_index_payload
= i_offset
+ i_ret
;
652 p_stream
->skeleton
.i_index_count
++;
654 /* update diff points */
655 p_stream
->skeleton
.i_last_keyframe_pos
= p_sys
->i_pos
;
656 p_stream
->skeleton
.i_last_keyframe_time
= i_time
;
657 msg_Dbg( p_mux
, "Added index on stream %d entry %zd %"PRIu64
,
658 p_stream
->i_serial_no
, p_sys
->i_pos
- p_sys
->i_segment_start
, i_time
);
663 /*****************************************************************************
664 * Ogg bitstream manipulation routines
665 *****************************************************************************/
666 static block_t
*OggStreamGetPage( sout_mux_t
*p_mux
,
667 ogg_stream_state
*p_os
, vlc_tick_t i_pts
,
671 block_t
*p_og
, *p_og_first
= NULL
;
673 int (*pager
)( ogg_stream_state
*, ogg_page
* ) = flush
? ogg_stream_flush
: ogg_stream_pageout
;
675 while( pager( p_os
, &og
) )
678 p_og
= block_Alloc( og
.header_len
+ og
.body_len
);
680 memcpy( p_og
->p_buffer
, og
.header
, og
.header_len
);
681 memcpy( p_og
->p_buffer
+ og
.header_len
, og
.body
, og
.body_len
);
686 i_pts
= 0; // write it only once
688 block_ChainAppend( &p_og_first
, p_og
);
694 static block_t
*OggStreamFlush( sout_mux_t
*p_mux
,
695 ogg_stream_state
*p_os
, vlc_tick_t i_pts
)
697 return OggStreamGetPage( p_mux
, p_os
, i_pts
, true );
700 static block_t
*OggStreamPageOut( sout_mux_t
*p_mux
,
701 ogg_stream_state
*p_os
, vlc_tick_t i_pts
)
703 return OggStreamGetPage( p_mux
, p_os
, i_pts
, false );
706 static void OggGetSkeletonIndex( uint8_t **pp_buffer
, long *pi_size
, ogg_stream_t
*p_stream
)
708 uint8_t *p_buffer
= calloc( INDEX_BASE_SIZE
+ p_stream
->skeleton
.i_index_size
, sizeof(uint8_t) );
709 if ( !p_buffer
) return;
710 *pp_buffer
= p_buffer
;
712 memcpy( p_buffer
, "index", 6 );
713 SetDWLE( &p_buffer
[6], p_stream
->i_serial_no
);
714 SetQWLE( &p_buffer
[10], p_stream
->skeleton
.i_index_count
); /* num keypoints */
715 SetQWLE( &p_buffer
[18], CLOCK_FREQ
);
716 SetQWLE( &p_buffer
[34], p_stream
->i_length
);
717 memcpy( p_buffer
+ INDEX_BASE_SIZE
, p_stream
->skeleton
.p_index
, p_stream
->skeleton
.i_index_payload
);
718 *pi_size
= INDEX_BASE_SIZE
+ p_stream
->skeleton
.i_index_size
;
721 static void OggGetSkeletonFisbone( uint8_t **pp_buffer
, long *pi_size
,
722 sout_input_t
*p_input
, sout_mux_t
*p_mux
)
726 const char *psz_value
= NULL
;
727 ogg_stream_t
*p_stream
= (ogg_stream_t
*) p_input
->p_sys
;
730 char *psz_content_type
;
733 unsigned int i_count
;
734 } headers
= { NULL
, NULL
, 0, 0 };
737 switch( p_stream
->fmt
.i_codec
)
739 case VLC_CODEC_VORBIS
:
740 psz_value
= "audio/vorbis";
742 case VLC_CODEC_THEORA
:
743 psz_value
= "video/theora";
745 case VLC_CODEC_DAALA
:
746 psz_value
= "video/daala";
748 case VLC_CODEC_SPEEX
:
749 psz_value
= "audio/speex";
752 psz_value
= "audio/flac";
755 psz_value
= "text/cmml";
758 psz_value
= "application/kate";
761 psz_value
= "video/x-vp8";
764 psz_value
= "application/octet-stream";
765 msg_Warn( p_mux
, "Unknown fourcc for stream %s, setting Content-Type to %s",
766 vlc_fourcc_GetDescription( p_stream
->fmt
.i_cat
, p_stream
->fmt
.i_codec
),
770 /* Content Type Header */
771 if ( asprintf( &headers
.psz_content_type
, "Content-Type: %s\r\n", psz_value
) != -1 )
773 headers
.i_size
+= strlen( headers
.psz_content_type
);
777 /* Set Role Header */
778 if ( p_input
->p_fmt
->i_priority
> ES_PRIORITY_NOT_SELECTABLE
)
780 int i_max_prio
= ES_PRIORITY_MIN
;
781 for ( int i
=0; i
< p_mux
->i_nb_inputs
; i
++ )
783 if ( p_mux
->pp_inputs
[i
]->p_fmt
->i_cat
!= p_input
->p_fmt
->i_cat
) continue;
784 i_max_prio
= __MAX( p_mux
->pp_inputs
[i
]->p_fmt
->i_priority
, i_max_prio
);
788 if ( p_input
->p_fmt
->i_cat
== AUDIO_ES
|| p_input
->p_fmt
->i_cat
== VIDEO_ES
)
790 if ( p_input
->p_fmt
->i_priority
== i_max_prio
&& i_max_prio
>= ES_PRIORITY_SELECTABLE_MIN
)
791 psz_value
= ( p_input
->p_fmt
->i_cat
== VIDEO_ES
) ?
792 "video/main" : "audio/main";
794 psz_value
= ( p_input
->p_fmt
->i_cat
== VIDEO_ES
) ?
795 "video/alternate" : "audio/alternate";
797 else if ( p_input
->p_fmt
->i_cat
== SPU_ES
)
799 psz_value
= ( p_input
->p_fmt
->i_codec
== VLC_CODEC_KATE
) ?
800 "text/karaoke" : "text/subtitle";
803 if ( psz_value
&& asprintf( &headers
.psz_role
, "Role: %s\r\n", psz_value
) != -1 )
805 headers
.i_size
+= strlen( headers
.psz_role
);
810 *pp_buffer
= calloc( FISBONE_BASE_SIZE
+ headers
.i_size
, sizeof(uint8_t) );
811 if ( !*pp_buffer
) return;
812 p_buffer
= *pp_buffer
;
814 memcpy( p_buffer
, "fisbone", 8 );
815 SetDWLE( &p_buffer
[8], FISBONE_BASE_OFFSET
); /* offset to message headers */
816 SetDWLE( &p_buffer
[12], p_stream
->i_serial_no
);
817 SetDWLE( &p_buffer
[16], headers
.i_count
);
819 /* granulerate den */
820 switch ( p_input
->p_fmt
->i_cat
)
823 SetQWLE( &(*pp_buffer
)[20], p_stream
->fmt
.video
.i_frame_rate
);
824 SetQWLE( &(*pp_buffer
)[28], p_stream
->fmt
.video
.i_frame_rate_base
);
827 SetQWLE( &(*pp_buffer
)[20], p_input
->p_fmt
->audio
.i_rate
);
828 SetQWLE( &(*pp_buffer
)[28], 1 );
831 SetQWLE( &(*pp_buffer
)[20], 1000 );
832 SetQWLE( &(*pp_buffer
)[28], 1 );
836 if ( p_input
->p_fmt
->p_extra
)
837 SetDWLE( &(*pp_buffer
)[44],
838 xiph_CountUnknownHeaders( p_input
->p_fmt
->p_extra
,
839 p_input
->p_fmt
->i_extra
,
840 p_input
->p_fmt
->i_codec
) );
842 if ( headers
.i_size
> 0 )
844 psz_header
= *pp_buffer
+ FISBONE_BASE_SIZE
;
845 memcpy( psz_header
, headers
.psz_content_type
, strlen( headers
.psz_content_type
) );
846 psz_header
+= strlen( headers
.psz_content_type
);
847 if ( headers
.psz_role
)
848 memcpy( psz_header
, headers
.psz_role
, strlen( headers
.psz_role
) );
850 *pi_size
= FISBONE_BASE_SIZE
+ headers
.i_size
;
852 free( headers
.psz_content_type
);
853 free( headers
.psz_role
);
856 static void OggFillSkeletonFishead( uint8_t *p_buffer
, sout_mux_t
*p_mux
)
858 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
859 memcpy( p_buffer
, "fishead", 8 );
860 SetWLE( &p_buffer
[8], 4 );
861 SetWLE( &p_buffer
[10], 0 );
862 SetQWLE( &p_buffer
[20], 1000 );
863 SetQWLE( &p_buffer
[36], 1000 );
864 SetQWLE( &p_buffer
[64], p_sys
->i_pos
- p_sys
->i_segment_start
); /* segment length */
865 SetQWLE( &p_buffer
[72], p_sys
->i_data_start
- p_sys
->i_segment_start
); /* data start offset */
868 static int32_t OggFillDsHeader( uint8_t *p_buffer
, oggds_header_t
*p_oggds_header
, int i_cat
)
871 p_buffer
[index
] = p_oggds_header
->i_packet_type
;
873 memcpy( &p_buffer
[index
], p_oggds_header
->stream_type
, sizeof(p_oggds_header
->stream_type
) );
874 index
+= sizeof(p_oggds_header
->stream_type
);
875 memcpy(&p_buffer
[index
], p_oggds_header
->sub_type
, sizeof(p_oggds_header
->sub_type
) );
876 index
+= sizeof(p_oggds_header
->sub_type
);
878 /* The size is filled at the end */
879 uint8_t *p_isize
= &p_buffer
[index
];
882 SetQWLE( &p_buffer
[index
], p_oggds_header
->i_time_unit
);
884 SetQWLE( &p_buffer
[index
], p_oggds_header
->i_samples_per_unit
);
886 SetDWLE( &p_buffer
[index
], p_oggds_header
->i_default_len
);
888 SetDWLE( &p_buffer
[index
], p_oggds_header
->i_buffer_size
);
890 SetWLE( &p_buffer
[index
], p_oggds_header
->i_bits_per_sample
);
892 SetWLE( &p_buffer
[index
], p_oggds_header
->i_padding_0
);
898 SetDWLE( &p_buffer
[index
], p_oggds_header
->header
.video
.i_width
);
899 SetDWLE( &p_buffer
[index
+4], p_oggds_header
->header
.video
.i_height
);
902 SetWLE( &p_buffer
[index
], p_oggds_header
->header
.audio
.i_channels
);
903 SetWLE( &p_buffer
[index
+2], p_oggds_header
->header
.audio
.i_block_align
);
904 SetDWLE( &p_buffer
[index
+4], p_oggds_header
->header
.audio
.i_avgbytespersec
);
908 SetDWLE( &p_buffer
[index
], p_oggds_header
->i_padding_1
);
912 if( p_oggds_header
->i_size
> 0 )
914 memcpy( &p_buffer
[index
], (uint8_t *) p_oggds_header
+ sizeof(*p_oggds_header
), p_oggds_header
->i_size
);
915 index
+= p_oggds_header
->i_size
;
918 SetDWLE( p_isize
, index
-1 );
922 static void OggFillVP8Header( uint8_t *p_buffer
, sout_input_t
*p_input
)
924 ogg_stream_t
*p_stream
= (ogg_stream_t
*) p_input
->p_sys
;
926 memcpy( p_buffer
, "OVP80\x01\x01\x00", 8 );
927 SetWBE( &p_buffer
[8], p_input
->p_fmt
->video
.i_width
);
928 SetDWBE( &p_buffer
[14], p_input
->p_fmt
->video
.i_sar_den
);/* 24 bits, 15~ */
929 SetDWBE( &p_buffer
[11], p_input
->p_fmt
->video
.i_sar_num
);/* 24 bits, 12~ */
930 SetWBE( &p_buffer
[10], p_input
->p_fmt
->video
.i_height
);
931 SetDWBE( &p_buffer
[18], p_stream
->fmt
.video
.i_frame_rate
);
932 SetDWBE( &p_buffer
[22], p_stream
->fmt
.video
.i_frame_rate_base
);
935 static bool OggCreateHeaders( sout_mux_t
*p_mux
)
937 block_t
*p_hdr
= NULL
;
938 block_t
*p_og
= NULL
;
940 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
942 if( sout_AccessOutControl( p_mux
->p_access
,
944 &p_sys
->skeleton
.b_create
) )
946 p_sys
->skeleton
.b_create
= false;
949 p_sys
->skeleton
.b_create
&= !! p_mux
->i_nb_inputs
;
951 /* no skeleton for solo vorbis/speex/opus tracks */
952 if ( p_mux
->i_nb_inputs
== 1 && p_mux
->pp_inputs
[0]->p_fmt
->i_cat
== AUDIO_ES
)
954 p_sys
->skeleton
.b_create
= false;
958 for ( int i
=0; i
< p_mux
->i_nb_inputs
; i
++ )
960 ogg_stream_t
*p_stream
= p_mux
->pp_inputs
[i
]->p_sys
;
961 if( p_stream
->p_oggds_header
)
963 /* We don't want skeleton for OggDS */
964 p_sys
->skeleton
.b_create
= false;
970 /* Skeleton's Fishead must be the first page of the stream */
971 if ( p_sys
->skeleton
.b_create
&& !p_sys
->skeleton
.b_head_done
)
973 msg_Dbg( p_mux
, "creating header for skeleton" );
974 p_sys
->skeleton
.i_serial_no
= p_sys
->i_next_serial_no
++;
975 ogg_stream_init( &p_sys
->skeleton
.os
, p_sys
->skeleton
.i_serial_no
);
977 op
.packet
= calloc( 1, op
.bytes
);
978 if ( op
.packet
== NULL
) return false;
983 OggFillSkeletonFishead( op
.packet
, p_mux
);
984 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
985 ogg_packet_clear( &op
);
986 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
987 block_ChainAppend( &p_hdr
, p_og
);
988 p_sys
->skeleton
.b_head_done
= true;
989 p_sys
->skeleton
.i_fishead_offset
= p_sys
->i_pos
;
992 /* Write header for each stream. All b_o_s (beginning of stream) packets
993 * must appear first in the ogg stream so we take care of them first. */
994 for( int pass
= 0; pass
< 2; pass
++ )
996 for( int i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
998 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
999 ogg_stream_t
*p_stream
= p_input
->p_sys
;
1001 bool video
= ( p_stream
->fmt
.i_codec
== VLC_CODEC_THEORA
||
1002 p_stream
->fmt
.i_codec
== VLC_CODEC_DIRAC
||
1003 p_stream
->fmt
.i_codec
== VLC_CODEC_DAALA
);
1004 if( ( ( pass
== 0 && !video
) || ( pass
== 1 && video
) ) )
1007 msg_Dbg( p_mux
, "creating header for %4.4s",
1008 (char *)&p_stream
->fmt
.i_codec
);
1010 ogg_stream_init( &p_stream
->os
, p_stream
->i_serial_no
);
1011 p_stream
->b_new
= false;
1012 p_stream
->i_packet_no
= 0;
1013 p_stream
->b_started
= true;
1015 if( p_stream
->fmt
.i_codec
== VLC_CODEC_VORBIS
||
1016 p_stream
->fmt
.i_codec
== VLC_CODEC_SPEEX
||
1017 p_stream
->fmt
.i_codec
== VLC_CODEC_OPUS
||
1018 p_stream
->fmt
.i_codec
== VLC_CODEC_THEORA
||
1019 p_stream
->fmt
.i_codec
== VLC_CODEC_DAALA
)
1021 /* First packet in order: vorbis/speex/opus/theora/daala info */
1022 unsigned pi_size
[XIPH_MAX_HEADER_COUNT
];
1023 const void *pp_data
[XIPH_MAX_HEADER_COUNT
];
1026 if( xiph_SplitHeaders( pi_size
, pp_data
, &i_count
,
1027 p_input
->p_fmt
->i_extra
, p_input
->p_fmt
->p_extra
) )
1034 op
.bytes
= pi_size
[0];
1035 op
.packet
= (void *)pp_data
[0];
1036 if( pi_size
[0] <= 0 )
1037 msg_Err( p_mux
, "header data corrupted");
1042 op
.packetno
= p_stream
->i_packet_no
++;
1043 ogg_stream_packetin( &p_stream
->os
, &op
);
1044 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1046 /* Get keyframe_granule_shift for theora or daala granulepos calculation */
1047 if( p_stream
->fmt
.i_codec
== VLC_CODEC_THEORA
||
1048 p_stream
->fmt
.i_codec
== VLC_CODEC_DAALA
)
1050 p_stream
->i_keyframe_granule_shift
=
1051 ( (op
.packet
[40] & 0x03) << 3 ) | ( (op
.packet
[41] & 0xe0) >> 5 );
1054 else if( p_stream
->fmt
.i_codec
== VLC_CODEC_DIRAC
)
1056 op
.packet
= p_input
->p_fmt
->p_extra
;
1057 op
.bytes
= p_input
->p_fmt
->i_extra
;
1061 op
.packetno
= p_stream
->i_packet_no
++;
1062 ogg_stream_packetin( &p_stream
->os
, &op
);
1063 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1065 else if( p_stream
->fmt
.i_codec
== VLC_CODEC_FLAC
)
1067 /* flac stream marker (yeah, only that in the 1st packet) */
1068 op
.packet
= (unsigned char *)"fLaC";
1073 op
.packetno
= p_stream
->i_packet_no
++;
1074 ogg_stream_packetin( &p_stream
->os
, &op
);
1075 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1077 else if( p_stream
->fmt
.i_codec
== VLC_CODEC_VP8
)
1080 op
.packet
= malloc( 26 );
1084 OggFillVP8Header( op
.packet
, p_input
);
1088 op
.packetno
= p_stream
->i_packet_no
++;
1089 ogg_stream_packetin( &p_stream
->os
, &op
);
1090 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1093 else if( p_stream
->p_oggds_header
)
1096 op
.packet
= malloc( sizeof(*p_stream
->p_oggds_header
) + p_stream
->p_oggds_header
->i_size
);
1099 op
.bytes
= OggFillDsHeader( op
.packet
, p_stream
->p_oggds_header
, p_stream
->fmt
.i_cat
);
1103 op
.packetno
= p_stream
->i_packet_no
++;
1104 ogg_stream_packetin( &p_stream
->os
, &op
);
1105 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1109 block_ChainAppend( &p_hdr
, p_og
);
1113 /* Create fisbones if any */
1114 if ( p_sys
->skeleton
.b_create
)
1116 for( int i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1118 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1119 ogg_stream_t
*p_stream
= p_input
->p_sys
;
1120 if ( p_stream
->skeleton
.b_fisbone_done
) continue;
1121 OggGetSkeletonFisbone( &op
.packet
, &op
.bytes
, p_input
, p_mux
);
1122 if ( op
.packet
== NULL
) return false;
1126 op
.packetno
= p_sys
->skeleton
.i_packet_no
++;
1127 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1128 ogg_packet_clear( &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_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_hdr
);
1144 /* Create indexes if any */
1145 for( int 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_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 ogg_packet_clear( &op
);
1170 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1171 p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1173 p_stream
->skeleton
.b_index_done
= true;
1177 /* Take care of the non b_o_s headers */
1178 for( int i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1180 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1181 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1183 if( p_stream
->fmt
.i_codec
== VLC_CODEC_VORBIS
||
1184 p_stream
->fmt
.i_codec
== VLC_CODEC_SPEEX
||
1185 p_stream
->fmt
.i_codec
== VLC_CODEC_OPUS
||
1186 p_stream
->fmt
.i_codec
== VLC_CODEC_THEORA
||
1187 p_stream
->fmt
.i_codec
== VLC_CODEC_DAALA
)
1189 unsigned pi_size
[XIPH_MAX_HEADER_COUNT
];
1190 const void *pp_data
[XIPH_MAX_HEADER_COUNT
];
1193 if( xiph_SplitHeaders( pi_size
, pp_data
, &i_count
,
1194 p_input
->p_fmt
->i_extra
, p_input
->p_fmt
->p_extra
) )
1197 /* Special case, headers are already there in the incoming stream.
1198 * We need to gather them an mark them as headers. */
1199 for( unsigned j
= 1; j
< i_count
; j
++ )
1201 op
.bytes
= pi_size
[j
];
1202 op
.packet
= (void *)pp_data
[j
];
1203 if( pi_size
[j
] <= 0 )
1204 msg_Err( p_mux
, "header data corrupted");
1209 op
.packetno
= p_stream
->i_packet_no
++;
1210 ogg_stream_packetin( &p_stream
->os
, &op
);
1211 msg_Dbg( p_mux
, "adding non bos, secondary header" );
1212 if( j
== i_count
- 1 )
1213 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1215 p_og
= OggStreamPageOut( p_mux
, &p_stream
->os
, 0 );
1217 block_ChainAppend( &p_hdr
, p_og
);
1220 else if( p_stream
->fmt
.i_codec
!= VLC_CODEC_FLAC
&&
1221 p_stream
->fmt
.i_codec
!= VLC_CODEC_DIRAC
)
1227 com
[0] = PACKET_TYPE_COMMENT
;
1228 i_com
= snprintf( (char *)(com
+1), 127,
1229 PACKAGE_VERSION
" stream output" )
1236 op
.packetno
= p_stream
->i_packet_no
++;
1237 ogg_stream_packetin( &p_stream
->os
, &op
);
1238 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1239 block_ChainAppend( &p_hdr
, p_og
);
1242 /* Special case for mp4v and flac */
1243 if( ( p_stream
->fmt
.i_codec
== VLC_CODEC_MP4V
||
1244 p_stream
->fmt
.i_codec
== VLC_CODEC_FLAC
) &&
1245 p_input
->p_fmt
->i_extra
)
1247 /* Send a packet with the VOL data for mp4v
1248 * or STREAMINFO for flac */
1249 msg_Dbg( p_mux
, "writing extra data" );
1250 op
.bytes
= p_input
->p_fmt
->i_extra
;
1251 op
.packet
= p_input
->p_fmt
->p_extra
;
1252 uint8_t flac_streaminfo
[34 + 4];
1253 if( p_stream
->fmt
.i_codec
== VLC_CODEC_FLAC
)
1255 if (op
.bytes
== 42 && !memcmp(op
.packet
, "fLaC", 4)) {
1257 memcpy(flac_streaminfo
, op
.packet
+ 4, 38);
1258 op
.packet
= flac_streaminfo
;
1259 } else if (op
.bytes
== 34) {
1261 memcpy(flac_streaminfo
+ 4, op
.packet
, 34);
1262 flac_streaminfo
[0] = 0x80; /* last block, streaminfo */
1263 flac_streaminfo
[1] = 0;
1264 flac_streaminfo
[2] = 0;
1265 flac_streaminfo
[3] = 34; /* block size */
1266 op
.packet
= flac_streaminfo
;
1268 msg_Err(p_mux
, "Invalid FLAC streaminfo (%ld bytes)",
1275 op
.packetno
= p_stream
->i_packet_no
++;
1276 ogg_stream_packetin( &p_stream
->os
, &op
);
1277 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1278 block_ChainAppend( &p_hdr
, p_og
);
1282 if ( p_sys
->skeleton
.b_create
)
1284 msg_Dbg( p_mux
, "ending skeleton" );
1290 op
.packetno
= p_sys
->skeleton
.i_packet_no
++;
1291 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1292 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1293 block_ChainAppend( &p_hdr
, p_og
);
1296 /* set HEADER flag */
1297 /* flag headers to be resent for streaming clients */
1298 for( p_og
= p_hdr
; p_og
!= NULL
; p_og
= p_og
->p_next
)
1300 p_og
->i_flags
|= BLOCK_FLAG_HEADER
;
1303 /* Write previous headers */
1304 p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_hdr
);
1309 static void OggCreateStreamFooter( sout_mux_t
*p_mux
, ogg_stream_t
*p_stream
)
1313 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1315 /* as stream is finished, overwrite the index, if there was any */
1316 if ( p_sys
->skeleton
.b_create
&& p_stream
->skeleton
.p_index
1317 && p_stream
->skeleton
.i_index_payload
)
1319 sout_AccessOutSeek( p_mux
->p_access
, p_stream
->skeleton
.i_index_offset
);
1320 OggGetSkeletonIndex( &op
.packet
, &op
.bytes
, p_stream
);
1321 if ( op
.packet
!= NULL
)
1323 msg_Dbg(p_mux
, "Rewriting index at %"PRId64
, p_stream
->skeleton
.i_index_offset
);
1324 ogg_stream_reset_serialno( &p_sys
->skeleton
.os
, p_sys
->skeleton
.i_serial_no
);
1328 op
.packetno
= p_stream
->skeleton
.i_index_packetno
+ 1;
1329 /* fake our stream state */
1330 p_sys
->skeleton
.os
.pageno
= p_stream
->skeleton
.i_index_pageno
;
1331 p_sys
->skeleton
.os
.packetno
= p_stream
->skeleton
.i_index_packetno
;
1332 p_sys
->skeleton
.os
.granulepos
= 0;
1333 p_sys
->skeleton
.os
.b_o_s
= 1;
1334 p_sys
->skeleton
.os
.e_o_s
= 0;
1335 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1336 ogg_packet_clear( &op
);
1337 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1338 sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1340 sout_AccessOutSeek( p_mux
->p_access
, p_sys
->i_pos
);
1343 /* clear skeleton */
1344 p_stream
->skeleton
.b_fisbone_done
= false;
1345 p_stream
->skeleton
.b_index_done
= false;
1346 p_stream
->skeleton
.i_index_offset
= 0;
1347 p_stream
->skeleton
.i_index_payload
= 0;
1348 p_stream
->skeleton
.i_last_keyframe_pos
= 0;
1349 p_stream
->skeleton
.i_last_keyframe_time
= 0;
1350 /* clear accounting */
1351 p_stream
->i_num_frames
= 0;
1352 p_stream
->i_num_keyframes
= 0;
1354 /* Write eos packet for stream. */
1359 op
.granulepos
= p_stream
->u_last_granulepos
;
1360 op
.packetno
= p_stream
->i_packet_no
++;
1361 ogg_stream_packetin( &p_stream
->os
, &op
);
1363 /* flush it with all remaining data */
1364 if( ( p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 ) ) )
1367 OggSetDate( p_og
, p_stream
->i_dts
, p_stream
->i_length
);
1368 p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1371 ogg_stream_clear( &p_stream
->os
);
1374 static void OggSetDate( block_t
*p_og
, vlc_tick_t i_dts
, vlc_tick_t i_length
)
1380 for( p_tmp
= p_og
, i_count
= 0; p_tmp
!= NULL
; p_tmp
= p_tmp
->p_next
)
1385 if( i_count
== 0 ) return; /* ignore. */
1387 i_delta
= i_length
/ i_count
;
1389 for( p_tmp
= p_og
; p_tmp
!= NULL
; p_tmp
= p_tmp
->p_next
)
1391 p_tmp
->i_dts
= i_dts
;
1392 p_tmp
->i_length
= i_delta
;
1398 static void OggRewriteFisheadPage( sout_mux_t
*p_mux
)
1400 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1403 op
.packet
= calloc( 1, op
.bytes
);
1404 if ( op
.packet
!= NULL
)
1410 ogg_stream_reset_serialno( &p_sys
->skeleton
.os
, p_sys
->skeleton
.i_serial_no
);
1411 OggFillSkeletonFishead( op
.packet
, p_mux
);
1412 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1413 ogg_packet_clear( &op
);
1414 msg_Dbg( p_mux
, "rewriting fishead at %"PRId64
, p_sys
->skeleton
.i_fishead_offset
);
1415 sout_AccessOutSeek( p_mux
->p_access
, p_sys
->skeleton
.i_fishead_offset
);
1416 sout_AccessOutWrite( p_mux
->p_access
,
1417 OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 ) );
1418 sout_AccessOutSeek( p_mux
->p_access
, p_sys
->i_pos
);
1422 static bool AllocateIndex( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
1424 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1425 ogg_stream_t
*p_stream
= (ogg_stream_t
*) p_input
->p_sys
;
1428 if ( p_stream
->i_length
)
1430 vlc_tick_t i_interval
= p_sys
->skeleton
.i_index_intvl
;
1433 if( p_input
->p_fmt
->i_cat
== VIDEO_ES
&&
1434 p_stream
->fmt
.video
.i_frame_rate
)
1436 /* optimize for fps < 1 */
1437 i_interval
= __MAX( i_interval
,
1438 VLC_TICK_FROM_SEC(10) *
1439 p_stream
->fmt
.video
.i_frame_rate_base
/
1440 p_stream
->fmt
.video
.i_frame_rate
);
1443 size_t i_tuple_size
= 0;
1444 /* estimate length of pos value */
1445 if ( p_input
->p_fmt
->i_bitrate
)
1447 i
= samples_from_vlc_tick(i_interval
, p_input
->p_fmt
->i_bitrate
);
1448 while ( i
<<= 1 ) i_tuple_size
++;
1452 /* Likely 64KB<<keyframe interval<<16MB */
1453 /* We can't really guess due to muxing */
1454 i_tuple_size
= 24 / 8;
1457 /* add length of interval value */
1459 while ( i
<<= 1 ) i_tuple_size
++;
1461 i_size
= i_tuple_size
* ( p_stream
->i_length
/ i_interval
+ 2 );
1465 i_size
= ( INT64_C(3600) * 11.2 * CLOCK_FREQ
/ p_sys
->skeleton
.i_index_intvl
)
1466 * p_sys
->skeleton
.i_index_ratio
;
1467 msg_Dbg( p_mux
, "No stream length, using default allocation for index" );
1469 i_size
*= ( 8.0 / 7 ); /* 7bits encoding overhead */
1470 msg_Dbg( p_mux
, "allocating %zu bytes for index", i_size
);
1471 p_stream
->skeleton
.p_index
= calloc( i_size
, sizeof(uint8_t) );
1472 if ( !p_stream
->skeleton
.p_index
) return false;
1473 p_stream
->skeleton
.i_index_size
= i_size
;
1474 p_stream
->skeleton
.i_index_payload
= 0;
1478 /*****************************************************************************
1479 * Mux: multiplex available data in input fifos into the Ogg bitstream
1480 *****************************************************************************/
1481 static int Mux( sout_mux_t
*p_mux
)
1483 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1486 /* End any stream that ends in that group */
1487 if ( p_sys
->i_del_streams
)
1489 /* Remove deleted logical streams */
1490 for( int i
= 0; i
< p_sys
->i_del_streams
; i
++ )
1492 OggCreateStreamFooter( p_mux
, p_sys
->pp_del_streams
[i
] );
1493 FREENULL( p_sys
->pp_del_streams
[i
]->p_oggds_header
);
1494 FREENULL( p_sys
->pp_del_streams
[i
] );
1496 FREENULL( p_sys
->pp_del_streams
);
1497 p_sys
->i_del_streams
= 0;
1500 if ( p_sys
->i_streams
== 0 )
1502 /* All streams have been deleted, or none have ever been created
1503 From this point, we are allowed to start a new group of logical streams */
1504 p_sys
->skeleton
.b_head_done
= false;
1505 p_sys
->b_can_add_streams
= true;
1506 p_sys
->i_segment_start
= p_sys
->i_pos
;
1509 if ( p_sys
->i_add_streams
)
1511 if ( !p_sys
->b_can_add_streams
)
1513 msg_Warn( p_mux
, "Can't add new stream %d/%d: Considerer increasing sout-mux-caching variable", p_sys
->i_del_streams
, p_sys
->i_streams
);
1514 msg_Warn( p_mux
, "Resetting and setting new identity to current streams");
1516 /* resetting all active streams */
1517 for ( int i
=0; i
< p_sys
->i_streams
; i
++ )
1519 ogg_stream_t
* p_stream
= (ogg_stream_t
*) p_mux
->pp_inputs
[i
]->p_sys
;
1520 if ( p_stream
->b_finished
|| !p_stream
->b_started
) continue;
1521 OggCreateStreamFooter( p_mux
, p_stream
);
1522 p_stream
->i_serial_no
= p_sys
->i_next_serial_no
++;
1523 p_stream
->i_packet_no
= 0;
1524 p_stream
->b_finished
= true;
1527 /* rewrite fishead with final values */
1528 if ( p_sys
->skeleton
.b_head_done
)
1530 OggRewriteFisheadPage( p_mux
);
1533 p_sys
->b_can_add_streams
= true;
1534 p_sys
->skeleton
.b_head_done
= false;
1535 p_sys
->i_segment_start
= p_sys
->i_pos
;
1538 /* Open new ogg stream */
1539 if( sout_MuxGetStream( p_mux
, 1, &i_dts
) < 0 )
1541 msg_Dbg( p_mux
, "waiting for data..." );
1544 msg_Dbg( p_mux
, "writing streams headers" );
1545 p_sys
->i_start_dts
= i_dts
;
1546 p_sys
->i_streams
= p_mux
->i_nb_inputs
;
1547 p_sys
->i_del_streams
= 0;
1548 p_sys
->i_add_streams
= 0;
1549 p_sys
->skeleton
.b_create
= true;
1551 if ( ! OggCreateHeaders( p_mux
) )
1554 /* If we're switching to end of headers, then that's data start */
1555 if ( p_sys
->b_can_add_streams
)
1557 msg_Dbg( p_mux
, "data starts from %zu", p_sys
->i_pos
);
1558 p_sys
->i_data_start
= p_sys
->i_pos
;
1561 /* Since we started sending secondaryheader or data pages,
1562 * we're no longer allowed to create new streams, until all streams end */
1563 p_sys
->b_can_add_streams
= false;
1566 /* Do the regular data mux thing */
1569 int i_stream
= sout_MuxGetStream( p_mux
, 1, NULL
);
1572 MuxBlock( p_mux
, p_mux
->pp_inputs
[i_stream
] );
1578 static int MuxBlock( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
1580 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1581 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1582 block_t
*p_data
= block_FifoGet( p_input
->p_fifo
);
1583 block_t
*p_og
= NULL
;
1587 if( p_stream
->fmt
.i_codec
!= VLC_CODEC_VORBIS
&&
1588 p_stream
->fmt
.i_codec
!= VLC_CODEC_FLAC
&&
1589 p_stream
->fmt
.i_codec
!= VLC_CODEC_SPEEX
&&
1590 p_stream
->fmt
.i_codec
!= VLC_CODEC_OPUS
&&
1591 p_stream
->fmt
.i_codec
!= VLC_CODEC_THEORA
&&
1592 p_stream
->fmt
.i_codec
!= VLC_CODEC_DAALA
&&
1593 p_stream
->fmt
.i_codec
!= VLC_CODEC_VP8
&&
1594 p_stream
->fmt
.i_codec
!= VLC_CODEC_DIRAC
)
1596 p_data
= block_Realloc( p_data
, 1, p_data
->i_buffer
);
1597 p_data
->p_buffer
[0] = PACKET_IS_SYNCPOINT
; // FIXME
1600 if ( p_stream
->fmt
.i_codec
== VLC_CODEC_DIRAC
&& p_stream
->i_baseptsdelay
< 0 )
1601 p_stream
->i_baseptsdelay
= p_data
->i_pts
- p_data
->i_dts
;
1603 op
.packet
= p_data
->p_buffer
;
1604 op
.bytes
= p_data
->i_buffer
;
1607 op
.packetno
= p_stream
->i_packet_no
++;
1610 if( p_stream
->fmt
.i_cat
== AUDIO_ES
)
1612 if( p_stream
->fmt
.i_codec
== VLC_CODEC_VORBIS
||
1613 p_stream
->fmt
.i_codec
== VLC_CODEC_FLAC
||
1614 p_stream
->fmt
.i_codec
== VLC_CODEC_OPUS
||
1615 p_stream
->fmt
.i_codec
== VLC_CODEC_SPEEX
)
1617 /* number of sample from begining + current packet */
1619 samples_from_vlc_tick( p_data
->i_dts
- p_sys
->i_start_dts
+ p_data
->i_length
,
1620 p_input
->p_fmt
->audio
.i_rate
);
1622 i_time
= p_data
->i_dts
- p_sys
->i_start_dts
;
1623 AddIndexEntry( p_mux
, i_time
, p_input
);
1625 else if( p_stream
->p_oggds_header
)
1627 /* number of sample from begining */
1628 op
.granulepos
= samples_from_vlc_tick( p_data
->i_dts
- p_sys
->i_start_dts
,
1629 p_stream
->p_oggds_header
->i_samples_per_unit
);
1632 else if( p_stream
->fmt
.i_cat
== VIDEO_ES
)
1634 if( p_stream
->fmt
.i_codec
== VLC_CODEC_THEORA
||
1635 p_stream
->fmt
.i_codec
== VLC_CODEC_DAALA
)
1637 p_stream
->i_num_frames
++;
1638 if( p_data
->i_flags
& BLOCK_FLAG_TYPE_I
)
1640 p_stream
->i_num_keyframes
++;
1641 p_stream
->i_last_keyframe
= p_stream
->i_num_frames
;
1643 /* presentation time */
1644 i_time
= vlc_tick_from_samples( (p_stream
->i_num_frames
- 1 ) *
1645 p_stream
->fmt
.video
.i_frame_rate_base
,
1646 p_stream
->fmt
.video
.i_frame_rate
);
1647 AddIndexEntry( p_mux
, i_time
, p_input
);
1650 op
.granulepos
= (p_stream
->i_last_keyframe
<< p_stream
->i_keyframe_granule_shift
)
1651 | (p_stream
->i_num_frames
-p_stream
->i_last_keyframe
);
1653 else if( p_stream
->fmt
.i_codec
== VLC_CODEC_DIRAC
)
1656 #define FRAME_ROUND(a) \
1657 if ( ( a + 5000 / CLOCK_FREQ ) > ( a / CLOCK_FREQ ) )\
1661 int64_t dt
= (p_data
->i_dts
- p_sys
->i_start_dts
) * p_stream
->fmt
.video
.i_frame_rate
/
1662 p_stream
->fmt
.video
.i_frame_rate_base
;
1665 int64_t pt
= (p_data
->i_pts
- p_sys
->i_start_dts
- p_stream
->i_baseptsdelay
) *
1666 p_stream
->fmt
.video
.i_frame_rate
/ p_stream
->fmt
.video
.i_frame_rate_base
;
1669 /* (shro) some PTS could be repeated within 1st frames */
1670 if ( pt
== p_stream
->i_dirac_last_pt
)
1673 p_stream
->i_dirac_last_pt
= pt
;
1675 /* (shro) some DTS could be repeated within 1st frames */
1676 if ( dt
== p_stream
->i_dirac_last_dt
)
1679 p_stream
->i_dirac_last_dt
= dt
;
1681 if( p_data
->i_flags
& BLOCK_FLAG_TYPE_I
)
1682 p_stream
->i_last_keyframe
= dt
;
1683 int64_t dist
= dt
- p_stream
->i_last_keyframe
;
1685 /* Everything increments by two for progressive */
1692 int64_t delay
= llabs(pt
- dt
);
1694 op
.granulepos
= (pt
- delay
) << 31 | (dist
&0xff00) << 14
1695 | (delay
&0x1fff) << 9 | (dist
&0xff);
1697 msg_Dbg( p_mux
, "dts %"PRId64
" pts %"PRId64
" dt %"PRId64
" pt %"PRId64
" delay %"PRId64
" granule %"PRId64
,
1698 (p_data
->i_dts
- p_sys
->i_start_dts
),
1699 (p_data
->i_pts
- p_sys
->i_start_dts
),
1700 dt
, pt
, delay
, op
.granulepos
);
1703 AddIndexEntry( p_mux
, dt
, p_input
);
1705 else if( p_stream
->fmt
.i_codec
== VLC_CODEC_VP8
)
1707 p_stream
->i_num_frames
++;
1708 if( p_data
->i_flags
& BLOCK_FLAG_TYPE_I
)
1710 p_stream
->i_num_keyframes
++;
1711 p_stream
->i_last_keyframe
= p_stream
->i_num_frames
;
1713 /* presentation time */
1714 i_time
= vlc_tick_from_samples( ( p_stream
->i_num_frames
- 1 ) *
1715 p_stream
->fmt
.video
.i_frame_rate_base
, p_stream
->fmt
.video
.i_frame_rate
);
1716 AddIndexEntry( p_mux
, i_time
, p_input
);
1718 op
.granulepos
= ( ((int64_t)p_stream
->i_num_frames
) << 32 ) |
1719 ( ( ( p_stream
->i_num_frames
- p_stream
->i_last_keyframe
) & 0x07FFFFFF ) << 3 );
1721 else if( p_stream
->p_oggds_header
)
1722 op
.granulepos
= MSFTIME_FROM_VLC_TICK( p_data
->i_dts
- p_sys
->i_start_dts
) /
1723 p_stream
->p_oggds_header
->i_time_unit
;
1725 else if( p_stream
->fmt
.i_cat
== SPU_ES
)
1727 /* granulepos is in millisec */
1728 op
.granulepos
= MS_FROM_VLC_TICK( p_data
->i_dts
- p_sys
->i_start_dts
);
1731 return VLC_EGENERIC
;
1733 p_stream
->u_last_granulepos
= op
.granulepos
;
1734 ogg_stream_packetin( &p_stream
->os
, &op
);
1736 if( p_stream
->fmt
.i_cat
== SPU_ES
||
1737 p_stream
->fmt
.i_codec
== VLC_CODEC_SPEEX
||
1738 p_stream
->fmt
.i_codec
== VLC_CODEC_DIRAC
)
1740 /* Subtitles or Speex packets are quite small so they
1741 * need to be flushed to be sent on time */
1742 /* The OggDirac mapping suggests ever so strongly that a
1743 * page flush occurs after each OggDirac packet, so to make
1744 * the timestamps unambiguous */
1745 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, p_data
->i_dts
);
1749 p_og
= OggStreamPageOut( p_mux
, &p_stream
->os
, p_data
->i_dts
);
1754 OggSetDate( p_og
, p_stream
->i_dts
, p_stream
->i_length
);
1755 p_stream
->i_dts
= -1;
1756 p_stream
->i_length
= 0;
1757 p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1761 if( p_stream
->i_dts
< 0 )
1763 p_stream
->i_dts
= p_data
->i_dts
;
1765 p_stream
->i_length
+= p_data
->i_length
;
1768 block_Release( p_data
);