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 *****************************************************************************/
154 int i_keyframe_granule_shift
; /* Theora and Daala only */
155 int i_last_keyframe
; /* dirac and theora */
156 int i_num_frames
; /* Theora only */
157 uint64_t u_last_granulepos
; /* Used for correct EOS page */
158 int64_t i_num_keyframes
;
161 oggds_header_t
*p_oggds_header
;
169 /* Skeleton index storage */
170 unsigned char *p_index
;
171 uint64_t i_index_size
;
172 uint64_t i_index_payload
; /* real index size */
173 uint64_t i_index_count
;
174 /* backup values for rewriting index page later */
175 uint64_t i_index_offset
; /* sout offset of the index page */
176 int64_t i_index_packetno
; /* index packet no */
178 /* index creation tracking values */
179 uint64_t i_last_keyframe_pos
;
180 uint64_t i_last_keyframe_time
;
185 mtime_t i_baseptsdelay
;
194 int i_next_serial_no
;
196 /* number of logical streams pending to be added */
198 bool b_can_add_streams
;
200 /* logical streams pending to be deleted */
202 ogg_stream_t
**pp_del_streams
;
212 /* backup values for rewriting fishead page later */
213 uint64_t i_fishead_offset
; /* sout offset of the fishead page */
218 /* access position */
220 ssize_t i_data_start
;
221 ssize_t i_segment_start
;
224 static void OggSetDate( block_t
*, mtime_t
, mtime_t
);
225 static block_t
*OggStreamFlush( sout_mux_t
*, ogg_stream_state
*, mtime_t
);
226 static void OggCreateStreamFooter( sout_mux_t
*p_mux
, ogg_stream_t
*p_stream
);
227 static void OggRewriteFisheadPage( sout_mux_t
*p_mux
);
228 static bool AllocateIndex( sout_mux_t
*p_mux
, sout_input_t
*p_input
);
230 /*****************************************************************************
232 *****************************************************************************/
233 static int Open( vlc_object_t
*p_this
)
235 sout_mux_t
*p_mux
= (sout_mux_t
*)p_this
;
236 sout_mux_sys_t
*p_sys
;
238 msg_Info( p_mux
, "Open" );
240 p_sys
= malloc( sizeof( sout_mux_sys_t
) );
243 p_sys
->i_streams
= 0;
244 p_sys
->i_add_streams
= 0;
245 p_sys
->b_can_add_streams
= true;
246 p_sys
->i_del_streams
= 0;
247 p_sys
->pp_del_streams
= 0;
249 p_sys
->skeleton
.b_create
= false;
250 p_sys
->skeleton
.b_head_done
= false;
251 p_sys
->skeleton
.i_index_intvl
=
252 var_InheritInteger( p_this
, SOUT_CFG_PREFIX
"indexintvl" );
253 p_sys
->skeleton
.i_index_ratio
=
254 var_InheritFloat( p_this
, SOUT_CFG_PREFIX
"indexratio" );
255 p_sys
->i_data_start
= 0;
256 p_sys
->i_segment_start
= 0;
257 p_mux
->p_sys
= p_sys
;
258 p_mux
->pf_control
= Control
;
259 p_mux
->pf_addstream
= AddStream
;
260 p_mux
->pf_delstream
= DelStream
;
263 /* First serial number is random.
264 * (Done like this because on win32 you need to seed the random number
265 * generator once per thread). */
267 vlc_rand_bytes(&r
, sizeof(r
));
268 p_sys
->i_next_serial_no
= r
& INT_MAX
;
273 /*****************************************************************************
274 * Close: Finalize ogg bitstream and close muxer
275 *****************************************************************************/
276 static void Close( vlc_object_t
* p_this
)
278 sout_mux_t
*p_mux
= (sout_mux_t
*)p_this
;
279 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
281 msg_Info( p_mux
, "Close" );
283 if( p_sys
->i_del_streams
)
285 /* Close the current ogg stream */
286 msg_Dbg( p_mux
, "writing footers" );
288 /* Remove deleted logical streams */
289 for(int i
= 0; i
< p_sys
->i_del_streams
; i
++ )
291 ogg_stream_t
*p_stream
= p_sys
->pp_del_streams
[i
];
293 es_format_Clean( &p_stream
->fmt
);
294 OggCreateStreamFooter( p_mux
, p_stream
);
295 free( p_stream
->p_oggds_header
);
296 free( p_stream
->skeleton
.p_index
);
299 free( p_sys
->pp_del_streams
);
300 p_sys
->i_streams
-= p_sys
->i_del_streams
;
303 /* rewrite fishead with final values */
304 if ( p_sys
->skeleton
.b_create
&& p_sys
->skeleton
.b_head_done
)
306 OggRewriteFisheadPage( p_mux
);
312 /*****************************************************************************
314 *****************************************************************************/
315 static int Control( sout_mux_t
*p_mux
, int i_query
, va_list args
)
323 case MUX_CAN_ADD_STREAM_WHILE_MUXING
:
324 pb_bool
= va_arg( args
, bool * );
328 case MUX_GET_ADD_STREAM_WAIT
:
329 pb_bool
= va_arg( args
, bool * );
334 ppsz
= va_arg( args
, char ** );
335 *ppsz
= strdup( "application/ogg" );
342 /*****************************************************************************
343 * AddStream: Add an elementary stream to the muxed stream
344 *****************************************************************************/
345 static int AddStream( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
347 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
348 ogg_stream_t
*p_stream
;
351 msg_Dbg( p_mux
, "adding input" );
353 p_input
->p_sys
= p_stream
= calloc( 1, sizeof( ogg_stream_t
) );
357 if( es_format_Copy( &p_stream
->fmt
, &p_input
->fmt
) != VLC_SUCCESS
)
363 p_stream
->i_serial_no
= p_sys
->i_next_serial_no
++;
364 p_stream
->i_packet_no
= 0;
365 p_stream
->i_last_keyframe
= 0;
366 p_stream
->i_num_keyframes
= 0;
367 p_stream
->i_num_frames
= 0;
369 p_stream
->p_oggds_header
= 0;
371 p_stream
->i_baseptsdelay
= -1;
372 p_stream
->i_dirac_last_pt
= -1;
373 p_stream
->i_dirac_last_dt
= -1;
375 switch( p_input
->p_fmt
->i_cat
)
379 if( !p_stream
->fmt
.video
.i_frame_rate
||
380 !p_stream
->fmt
.video
.i_frame_rate_base
)
382 msg_Warn( p_mux
, "Missing frame rate, assuming 25fps" );
383 p_stream
->fmt
.video
.i_frame_rate
= 25;
384 p_stream
->fmt
.video
.i_frame_rate_base
= 1;
387 switch( p_stream
->fmt
.i_codec
)
398 p_stream
->p_oggds_header
= calloc( 1, sizeof(oggds_header_t
) );
399 if( !p_stream
->p_oggds_header
)
404 p_stream
->p_oggds_header
->i_packet_type
= PACKET_TYPE_HEADER
;
406 memcpy( p_stream
->p_oggds_header
->stream_type
, "video", 5 );
407 if( p_stream
->fmt
.i_codec
== VLC_CODEC_MP4V
)
409 memcpy( p_stream
->p_oggds_header
->sub_type
, "XVID", 4 );
411 else if( p_stream
->fmt
.i_codec
== VLC_CODEC_DIV3
)
413 memcpy( p_stream
->p_oggds_header
->sub_type
, "DIV3", 4 );
417 memcpy( p_stream
->p_oggds_header
->sub_type
,
418 &p_stream
->fmt
.i_codec
, 4 );
420 p_stream
->p_oggds_header
->i_size
= 0 ;
421 p_stream
->p_oggds_header
->i_time_unit
=
422 INT64_C(10000000) * p_stream
->fmt
.video
.i_frame_rate_base
/
423 (int64_t)p_stream
->fmt
.video
.i_frame_rate
;
424 p_stream
->p_oggds_header
->i_samples_per_unit
= 1;
425 p_stream
->p_oggds_header
->i_default_len
= 1 ; /* ??? */
426 p_stream
->p_oggds_header
->i_buffer_size
= 1024*1024;
427 p_stream
->p_oggds_header
->i_bits_per_sample
= 0;
428 p_stream
->p_oggds_header
->header
.video
.i_width
= p_input
->p_fmt
->video
.i_width
;
429 p_stream
->p_oggds_header
->header
.video
.i_height
= p_input
->p_fmt
->video
.i_height
;
430 msg_Dbg( p_mux
, "%4.4s stream", (char *)&p_stream
->fmt
.i_codec
);
433 case VLC_CODEC_DIRAC
:
434 msg_Dbg( p_mux
, "dirac stream" );
437 case VLC_CODEC_THEORA
:
438 msg_Dbg( p_mux
, "theora stream" );
441 case VLC_CODEC_DAALA
:
442 msg_Dbg( p_mux
, "daala stream" );
446 msg_Dbg( p_mux
, "VP8 stream" );
450 FREENULL( p_input
->p_sys
);
457 switch( p_stream
->fmt
.i_codec
)
460 msg_Dbg( p_mux
, "opus stream" );
463 case VLC_CODEC_VORBIS
:
464 msg_Dbg( p_mux
, "vorbis stream" );
467 case VLC_CODEC_SPEEX
:
468 msg_Dbg( p_mux
, "speex stream" );
472 msg_Dbg( p_mux
, "flac stream" );
476 fourcc_to_wf_tag( p_stream
->fmt
.i_codec
, &i_tag
);
477 if( i_tag
== WAVE_FORMAT_UNKNOWN
)
479 FREENULL( p_input
->p_sys
);
483 p_stream
->p_oggds_header
=
484 malloc( sizeof(oggds_header_t
) + p_input
->p_fmt
->i_extra
);
485 if( !p_stream
->p_oggds_header
)
490 memset( p_stream
->p_oggds_header
, 0, sizeof(oggds_header_t
) );
491 p_stream
->p_oggds_header
->i_packet_type
= PACKET_TYPE_HEADER
;
493 p_stream
->p_oggds_header
->i_size
= p_input
->p_fmt
->i_extra
;
495 if( p_input
->p_fmt
->i_extra
)
497 memcpy( &p_stream
->p_oggds_header
[1],
498 p_input
->p_fmt
->p_extra
, p_input
->p_fmt
->i_extra
);
501 memcpy( p_stream
->p_oggds_header
->stream_type
, "audio", 5 );
503 memset( p_stream
->p_oggds_header
->sub_type
, 0, 4 );
505 snprintf( buf
, sizeof(buf
), "%"PRIx16
, i_tag
);
506 strncpy( p_stream
->p_oggds_header
->sub_type
, buf
, 4 );
508 p_stream
->p_oggds_header
->i_time_unit
= INT64_C(10000000);
509 p_stream
->p_oggds_header
->i_default_len
= 1;
510 p_stream
->p_oggds_header
->i_buffer_size
= 30*1024 ;
511 p_stream
->p_oggds_header
->i_samples_per_unit
= p_input
->p_fmt
->audio
.i_rate
;
512 p_stream
->p_oggds_header
->i_bits_per_sample
= p_input
->p_fmt
->audio
.i_bitspersample
;
513 p_stream
->p_oggds_header
->header
.audio
.i_channels
= p_input
->p_fmt
->audio
.i_channels
;
514 p_stream
->p_oggds_header
->header
.audio
.i_block_align
= p_input
->p_fmt
->audio
.i_blockalign
;
515 p_stream
->p_oggds_header
->header
.audio
.i_avgbytespersec
= p_input
->p_fmt
->i_bitrate
/ 8;
516 msg_Dbg( p_mux
, "%4.4s stream", (char *)&p_stream
->fmt
.i_codec
);
522 switch( p_stream
->fmt
.i_codec
)
525 p_stream
->p_oggds_header
= calloc( 1, sizeof(oggds_header_t
) );
526 if( !p_stream
->p_oggds_header
)
531 p_stream
->p_oggds_header
->i_packet_type
= PACKET_TYPE_HEADER
;
533 memcpy( p_stream
->p_oggds_header
->stream_type
, "text", 4 );
534 msg_Dbg( p_mux
, "subtitles stream" );
538 FREENULL( p_input
->p_sys
);
543 FREENULL( p_input
->p_sys
);
547 p_stream
->b_new
= true;
549 p_sys
->i_add_streams
++;
554 /*****************************************************************************
555 * DelStream: Delete an elementary stream from the muxed stream
556 *****************************************************************************/
557 static void DelStream( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
559 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
560 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
563 msg_Dbg( p_mux
, "removing input" );
565 /* flush all remaining data */
568 if( !p_stream
->b_new
)
570 while( block_FifoCount( p_input
->p_fifo
) )
571 MuxBlock( p_mux
, p_input
);
574 if( !p_stream
->b_new
&&
575 ( p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 ) ) )
577 OggSetDate( p_og
, p_stream
->i_dts
, p_stream
->i_length
);
578 p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
581 /* move input in delete queue */
582 if( !p_stream
->b_new
)
584 p_sys
->pp_del_streams
= xrealloc( p_sys
->pp_del_streams
,
585 (p_sys
->i_del_streams
+ 1) * sizeof(ogg_stream_t
*) );
586 p_sys
->pp_del_streams
[p_sys
->i_del_streams
++] = p_stream
;
590 /* wasn't already added so get rid of it */
591 FREENULL( p_stream
->p_oggds_header
);
592 FREENULL( p_stream
);
593 p_sys
->i_add_streams
--;
597 p_input
->p_sys
= NULL
;
600 /*****************************************************************************
601 * Ogg Skeleton helpers
602 *****************************************************************************/
603 static int WriteQWVariableLE( uint64_t i_64
, uint64_t i_offset
,
604 uint8_t *p_buffer
, int i_buffer_size
)
606 uint8_t *p_dest
= p_buffer
+ i_offset
;
611 if ( p_dest
- p_buffer
>= i_buffer_size
) return -1;
613 *p_dest
= (uint8_t) ( i_64
& 0x7F );
627 static bool AddIndexEntry( sout_mux_t
*p_mux
, uint64_t i_time
, sout_input_t
*p_input
)
629 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
630 ogg_stream_t
*p_stream
= (ogg_stream_t
*) p_input
->p_sys
;
632 uint64_t i_timedelta
;
633 if ( !p_sys
->skeleton
.b_create
|| p_sys
->skeleton
.i_index_intvl
== 0
634 || !p_stream
->skeleton
.p_index
)
637 if ( p_stream
->skeleton
.i_last_keyframe_pos
== 0 )
638 p_stream
->skeleton
.i_last_keyframe_pos
= p_sys
->i_segment_start
;
639 i_posdelta
= p_sys
->i_pos
- p_stream
->skeleton
.i_last_keyframe_pos
;
640 i_timedelta
= i_time
- p_stream
->skeleton
.i_last_keyframe_time
;
642 if ( i_timedelta
<= ( (uint64_t) p_sys
->skeleton
.i_index_intvl
* 1000 )
643 || i_posdelta
<= 0xFFFF )
648 if ( !p_stream
->skeleton
.p_index
) return false;
649 uint64_t i_offset
= p_stream
->skeleton
.i_index_payload
;
650 i_ret
= WriteQWVariableLE( i_posdelta
, i_offset
, p_stream
->skeleton
.p_index
,
651 p_stream
->skeleton
.i_index_size
);
652 if ( i_ret
== -1 ) return false;
654 i_ret
= WriteQWVariableLE( i_timedelta
, i_offset
, p_stream
->skeleton
.p_index
,
655 p_stream
->skeleton
.i_index_size
);
656 if ( i_ret
== -1 ) return false;
657 p_stream
->skeleton
.i_index_payload
= i_offset
+ i_ret
;
658 p_stream
->skeleton
.i_index_count
++;
660 /* update diff points */
661 p_stream
->skeleton
.i_last_keyframe_pos
= p_sys
->i_pos
;
662 p_stream
->skeleton
.i_last_keyframe_time
= i_time
;
663 msg_Dbg( p_mux
, "Added index on stream %d entry %zd %"PRIu64
,
664 p_stream
->i_serial_no
, p_sys
->i_pos
- p_sys
->i_segment_start
, i_time
);
669 /*****************************************************************************
670 * Ogg bitstream manipulation routines
671 *****************************************************************************/
672 static block_t
*OggStreamGetPage( sout_mux_t
*p_mux
,
673 ogg_stream_state
*p_os
, mtime_t i_pts
,
677 block_t
*p_og
, *p_og_first
= NULL
;
679 int (*pager
)( ogg_stream_state
*, ogg_page
* ) = flush
? ogg_stream_flush
: ogg_stream_pageout
;
681 while( pager( p_os
, &og
) )
684 p_og
= block_Alloc( og
.header_len
+ og
.body_len
);
686 memcpy( p_og
->p_buffer
, og
.header
, og
.header_len
);
687 memcpy( p_og
->p_buffer
+ og
.header_len
, og
.body
, og
.body_len
);
692 i_pts
= 0; // write it only once
694 block_ChainAppend( &p_og_first
, p_og
);
700 static block_t
*OggStreamFlush( sout_mux_t
*p_mux
,
701 ogg_stream_state
*p_os
, mtime_t i_pts
)
703 return OggStreamGetPage( p_mux
, p_os
, i_pts
, true );
706 static block_t
*OggStreamPageOut( sout_mux_t
*p_mux
,
707 ogg_stream_state
*p_os
, mtime_t i_pts
)
709 return OggStreamGetPage( p_mux
, p_os
, i_pts
, false );
712 static void OggGetSkeletonIndex( uint8_t **pp_buffer
, long *pi_size
, ogg_stream_t
*p_stream
)
714 uint8_t *p_buffer
= calloc( INDEX_BASE_SIZE
+ p_stream
->skeleton
.i_index_size
, sizeof(uint8_t) );
715 if ( !p_buffer
) return;
716 *pp_buffer
= p_buffer
;
718 memcpy( p_buffer
, "index", 6 );
719 SetDWLE( &p_buffer
[6], p_stream
->i_serial_no
);
720 SetQWLE( &p_buffer
[10], p_stream
->skeleton
.i_index_count
); /* num keypoints */
721 SetQWLE( &p_buffer
[18], 1000000 );
722 SetQWLE( &p_buffer
[34], p_stream
->i_length
);
723 memcpy( p_buffer
+ INDEX_BASE_SIZE
, p_stream
->skeleton
.p_index
, p_stream
->skeleton
.i_index_payload
);
724 *pi_size
= INDEX_BASE_SIZE
+ p_stream
->skeleton
.i_index_size
;
727 static void OggGetSkeletonFisbone( uint8_t **pp_buffer
, long *pi_size
,
728 sout_input_t
*p_input
, sout_mux_t
*p_mux
)
732 const char *psz_value
= NULL
;
733 ogg_stream_t
*p_stream
= (ogg_stream_t
*) p_input
->p_sys
;
736 char *psz_content_type
;
739 unsigned int i_count
;
740 } headers
= { NULL
, NULL
, 0, 0 };
743 switch( p_stream
->fmt
.i_codec
)
745 case VLC_CODEC_VORBIS
:
746 psz_value
= "audio/vorbis";
748 case VLC_CODEC_THEORA
:
749 psz_value
= "video/theora";
751 case VLC_CODEC_DAALA
:
752 psz_value
= "video/daala";
754 case VLC_CODEC_SPEEX
:
755 psz_value
= "audio/speex";
758 psz_value
= "audio/flac";
761 psz_value
= "text/cmml";
764 psz_value
= "application/kate";
767 psz_value
= "video/x-vp8";
770 psz_value
= "application/octet-stream";
771 msg_Warn( p_mux
, "Unknown fourcc for stream %s, setting Content-Type to %s",
772 vlc_fourcc_GetDescription( p_stream
->fmt
.i_cat
, p_stream
->fmt
.i_codec
),
776 /* Content Type Header */
777 if ( asprintf( &headers
.psz_content_type
, "Content-Type: %s\r\n", psz_value
) != -1 )
779 headers
.i_size
+= strlen( headers
.psz_content_type
);
783 /* Set Role Header */
784 if ( p_input
->p_fmt
->i_priority
> ES_PRIORITY_NOT_SELECTABLE
)
786 int i_max_prio
= ES_PRIORITY_MIN
;
787 for ( int i
=0; i
< p_mux
->i_nb_inputs
; i
++ )
789 if ( p_mux
->pp_inputs
[i
]->p_fmt
->i_cat
!= p_input
->p_fmt
->i_cat
) continue;
790 i_max_prio
= __MAX( p_mux
->pp_inputs
[i
]->p_fmt
->i_priority
, i_max_prio
);
794 if ( p_input
->p_fmt
->i_cat
== AUDIO_ES
|| p_input
->p_fmt
->i_cat
== VIDEO_ES
)
796 if ( p_input
->p_fmt
->i_priority
== i_max_prio
&& i_max_prio
>= ES_PRIORITY_SELECTABLE_MIN
)
797 psz_value
= ( p_input
->p_fmt
->i_cat
== VIDEO_ES
) ?
798 "video/main" : "audio/main";
800 psz_value
= ( p_input
->p_fmt
->i_cat
== VIDEO_ES
) ?
801 "video/alternate" : "audio/alternate";
803 else if ( p_input
->p_fmt
->i_cat
== SPU_ES
)
805 psz_value
= ( p_input
->p_fmt
->i_codec
== VLC_CODEC_KATE
) ?
806 "text/karaoke" : "text/subtitle";
809 if ( psz_value
&& asprintf( &headers
.psz_role
, "Role: %s\r\n", psz_value
) != -1 )
811 headers
.i_size
+= strlen( headers
.psz_role
);
816 *pp_buffer
= calloc( FISBONE_BASE_SIZE
+ headers
.i_size
, sizeof(uint8_t) );
817 if ( !*pp_buffer
) return;
818 p_buffer
= *pp_buffer
;
820 memcpy( p_buffer
, "fisbone", 8 );
821 SetDWLE( &p_buffer
[8], FISBONE_BASE_OFFSET
); /* offset to message headers */
822 SetDWLE( &p_buffer
[12], p_stream
->i_serial_no
);
823 SetDWLE( &p_buffer
[16], headers
.i_count
);
825 /* granulerate den */
826 switch ( p_input
->p_fmt
->i_cat
)
829 SetQWLE( &(*pp_buffer
)[20], p_stream
->fmt
.video
.i_frame_rate
);
830 SetQWLE( &(*pp_buffer
)[28], p_stream
->fmt
.video
.i_frame_rate_base
);
833 SetQWLE( &(*pp_buffer
)[20], p_input
->p_fmt
->audio
.i_rate
);
834 SetQWLE( &(*pp_buffer
)[28], 1 );
837 SetQWLE( &(*pp_buffer
)[20], 1000 );
838 SetQWLE( &(*pp_buffer
)[28], 1 );
842 if ( p_input
->p_fmt
->p_extra
)
843 SetDWLE( &(*pp_buffer
)[44], xiph_CountHeaders( p_input
->p_fmt
->p_extra
, p_input
->p_fmt
->i_extra
) );
845 if ( headers
.i_size
> 0 )
847 psz_header
= *pp_buffer
+ FISBONE_BASE_SIZE
;
848 memcpy( psz_header
, headers
.psz_content_type
, strlen( headers
.psz_content_type
) );
849 psz_header
+= strlen( headers
.psz_content_type
);
850 if ( headers
.psz_role
)
851 memcpy( psz_header
, headers
.psz_role
, strlen( headers
.psz_role
) );
853 *pi_size
= FISBONE_BASE_SIZE
+ headers
.i_size
;
855 free( headers
.psz_content_type
);
856 free( headers
.psz_role
);
859 static void OggFillSkeletonFishead( uint8_t *p_buffer
, sout_mux_t
*p_mux
)
861 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
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_sys
->i_pos
- p_sys
->i_segment_start
); /* segment length */
868 SetQWLE( &p_buffer
[72], p_sys
->i_data_start
- 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 ogg_stream_t
*p_stream
= (ogg_stream_t
*) p_input
->p_sys
;
929 memcpy( p_buffer
, "OVP80\x01\x01\x00", 8 );
930 SetWBE( &p_buffer
[8], p_input
->p_fmt
->video
.i_width
);
931 SetDWBE( &p_buffer
[14], p_input
->p_fmt
->video
.i_sar_den
);/* 24 bits, 15~ */
932 SetDWBE( &p_buffer
[11], p_input
->p_fmt
->video
.i_sar_num
);/* 24 bits, 12~ */
933 SetWBE( &p_buffer
[10], p_input
->p_fmt
->video
.i_height
);
934 SetDWBE( &p_buffer
[18], p_stream
->fmt
.video
.i_frame_rate
);
935 SetDWBE( &p_buffer
[22], p_stream
->fmt
.video
.i_frame_rate_base
);
938 static bool OggCreateHeaders( sout_mux_t
*p_mux
)
940 block_t
*p_hdr
= NULL
;
941 block_t
*p_og
= NULL
;
943 ogg_stream_t
*p_stream
;
944 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
947 if( sout_AccessOutControl( p_mux
->p_access
,
949 &p_sys
->skeleton
.b_create
) )
951 p_sys
->skeleton
.b_create
= false;
954 p_sys
->skeleton
.b_create
&= !! p_mux
->i_nb_inputs
;
956 /* no skeleton for solo vorbis/speex/opus tracks */
957 if ( p_mux
->i_nb_inputs
== 1 && p_mux
->pp_inputs
[0]->p_fmt
->i_cat
== AUDIO_ES
)
959 p_sys
->skeleton
.b_create
= false;
963 for ( int i
=0; i
< p_mux
->i_nb_inputs
; i
++ )
965 p_stream
= (ogg_stream_t
*) p_mux
->pp_inputs
[i
]->p_sys
;
966 if ( p_stream
->p_oggds_header
)
968 /* We don't want skeleton for OggDS */
969 p_sys
->skeleton
.b_create
= false;
975 /* Skeleton's Fishead must be the first page of the stream */
976 if ( p_sys
->skeleton
.b_create
&& !p_sys
->skeleton
.b_head_done
)
978 msg_Dbg( p_mux
, "creating header for skeleton" );
979 p_sys
->skeleton
.i_serial_no
= p_sys
->i_next_serial_no
++;
980 ogg_stream_init( &p_sys
->skeleton
.os
, p_sys
->skeleton
.i_serial_no
);
982 op
.packet
= calloc( 1, op
.bytes
);
983 if ( op
.packet
== NULL
) return false;
988 OggFillSkeletonFishead( op
.packet
, p_mux
);
989 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
990 ogg_packet_clear( &op
);
991 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
992 block_ChainAppend( &p_hdr
, p_og
);
993 p_sys
->skeleton
.b_head_done
= true;
994 p_sys
->skeleton
.i_fishead_offset
= p_sys
->i_pos
;
997 /* Write header for each stream. All b_o_s (beginning of stream) packets
998 * must appear first in the ogg stream so we take care of them first. */
999 for( int pass
= 0; pass
< 2; pass
++ )
1001 for( i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1003 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1004 p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1006 bool video
= ( p_stream
->fmt
.i_codec
== VLC_CODEC_THEORA
||
1007 p_stream
->fmt
.i_codec
== VLC_CODEC_DIRAC
||
1008 p_stream
->fmt
.i_codec
== VLC_CODEC_DAALA
);
1009 if( ( ( pass
== 0 && !video
) || ( pass
== 1 && video
) ) )
1012 msg_Dbg( p_mux
, "creating header for %4.4s",
1013 (char *)&p_stream
->fmt
.i_codec
);
1015 ogg_stream_init( &p_stream
->os
, p_stream
->i_serial_no
);
1016 p_stream
->b_new
= false;
1017 p_stream
->i_packet_no
= 0;
1018 p_stream
->b_started
= true;
1020 if( p_stream
->fmt
.i_codec
== VLC_CODEC_VORBIS
||
1021 p_stream
->fmt
.i_codec
== VLC_CODEC_SPEEX
||
1022 p_stream
->fmt
.i_codec
== VLC_CODEC_OPUS
||
1023 p_stream
->fmt
.i_codec
== VLC_CODEC_THEORA
||
1024 p_stream
->fmt
.i_codec
== VLC_CODEC_DAALA
)
1026 /* First packet in order: vorbis/speex/opus/theora/daala info */
1027 unsigned pi_size
[XIPH_MAX_HEADER_COUNT
];
1028 void *pp_data
[XIPH_MAX_HEADER_COUNT
];
1030 if( xiph_SplitHeaders( pi_size
, pp_data
, &i_count
,
1031 p_input
->p_fmt
->i_extra
, p_input
->p_fmt
->p_extra
) )
1038 op
.bytes
= pi_size
[0];
1039 op
.packet
= pp_data
[0];
1040 if( pi_size
[0] <= 0 )
1041 msg_Err( p_mux
, "header data corrupted");
1046 op
.packetno
= p_stream
->i_packet_no
++;
1047 ogg_stream_packetin( &p_stream
->os
, &op
);
1048 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1050 /* Get keyframe_granule_shift for theora or daala granulepos calculation */
1051 if( p_stream
->fmt
.i_codec
== VLC_CODEC_THEORA
||
1052 p_stream
->fmt
.i_codec
== VLC_CODEC_DAALA
)
1054 p_stream
->i_keyframe_granule_shift
=
1055 ( (op
.packet
[40] & 0x03) << 3 ) | ( (op
.packet
[41] & 0xe0) >> 5 );
1058 else if( p_stream
->fmt
.i_codec
== VLC_CODEC_DIRAC
)
1060 op
.packet
= p_input
->p_fmt
->p_extra
;
1061 op
.bytes
= p_input
->p_fmt
->i_extra
;
1065 op
.packetno
= p_stream
->i_packet_no
++;
1066 ogg_stream_packetin( &p_stream
->os
, &op
);
1067 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1069 else if( p_stream
->fmt
.i_codec
== VLC_CODEC_FLAC
)
1071 /* flac stream marker (yeah, only that in the 1st packet) */
1072 op
.packet
= (unsigned char *)"fLaC";
1077 op
.packetno
= p_stream
->i_packet_no
++;
1078 ogg_stream_packetin( &p_stream
->os
, &op
);
1079 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1081 else if( p_stream
->fmt
.i_codec
== VLC_CODEC_VP8
)
1084 op
.packet
= malloc( 26 );
1088 OggFillVP8Header( op
.packet
, p_input
);
1092 op
.packetno
= p_stream
->i_packet_no
++;
1093 ogg_stream_packetin( &p_stream
->os
, &op
);
1094 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1097 else if( p_stream
->p_oggds_header
)
1100 op
.packet
= malloc( sizeof(*p_stream
->p_oggds_header
) + p_stream
->p_oggds_header
->i_size
);
1103 op
.bytes
= OggFillDsHeader( op
.packet
, p_stream
->p_oggds_header
, p_stream
->fmt
.i_cat
);
1107 op
.packetno
= p_stream
->i_packet_no
++;
1108 ogg_stream_packetin( &p_stream
->os
, &op
);
1109 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1113 block_ChainAppend( &p_hdr
, p_og
);
1117 /* Create fisbones if any */
1118 if ( p_sys
->skeleton
.b_create
)
1120 for( i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1122 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1123 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1124 if ( p_stream
->skeleton
.b_fisbone_done
) continue;
1125 OggGetSkeletonFisbone( &op
.packet
, &op
.bytes
, p_input
, p_mux
);
1126 if ( op
.packet
== NULL
) return false;
1130 op
.packetno
= p_sys
->skeleton
.i_packet_no
++;
1131 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1132 ogg_packet_clear( &op
);
1133 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1134 block_ChainAppend( &p_hdr
, p_og
);
1135 p_stream
->skeleton
.b_fisbone_done
= true;
1139 /* Write previous headers */
1140 for( p_og
= p_hdr
; p_og
!= NULL
; p_og
= p_og
->p_next
)
1142 /* flag headers to be resent for streaming clients */
1143 p_og
->i_flags
|= BLOCK_FLAG_HEADER
;
1145 p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_hdr
);
1148 /* Create indexes if any */
1149 for( i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1151 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1152 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1153 /* flush stream && save offset */
1154 if ( p_sys
->skeleton
.b_create
&& !p_stream
->skeleton
.b_index_done
)
1156 if ( !p_stream
->skeleton
.p_index
) AllocateIndex( p_mux
, p_input
);
1157 if ( p_stream
->skeleton
.p_index
)
1159 msg_Dbg( p_mux
, "Creating index for stream %d", p_stream
->i_serial_no
);
1160 OggGetSkeletonIndex( &op
.packet
, &op
.bytes
, p_stream
);
1161 if ( op
.packet
== NULL
) return false;
1165 op
.packetno
= p_sys
->skeleton
.i_packet_no
++;
1167 /* backup some values */
1168 p_stream
->skeleton
.i_index_offset
= p_sys
->i_pos
;
1169 p_stream
->skeleton
.i_index_packetno
= p_sys
->skeleton
.os
.packetno
;
1170 p_stream
->skeleton
.i_index_pageno
= p_sys
->skeleton
.os
.pageno
;
1172 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1173 ogg_packet_clear( &op
);
1174 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1175 p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1177 p_stream
->skeleton
.b_index_done
= true;
1181 /* Take care of the non b_o_s headers */
1182 for( i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1184 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1185 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1187 if( p_stream
->fmt
.i_codec
== VLC_CODEC_VORBIS
||
1188 p_stream
->fmt
.i_codec
== VLC_CODEC_SPEEX
||
1189 p_stream
->fmt
.i_codec
== VLC_CODEC_OPUS
||
1190 p_stream
->fmt
.i_codec
== VLC_CODEC_THEORA
||
1191 p_stream
->fmt
.i_codec
== VLC_CODEC_DAALA
)
1193 unsigned pi_size
[XIPH_MAX_HEADER_COUNT
];
1194 void *pp_data
[XIPH_MAX_HEADER_COUNT
];
1196 if( xiph_SplitHeaders( pi_size
, pp_data
, &i_count
,
1197 p_input
->p_fmt
->i_extra
, p_input
->p_fmt
->p_extra
) )
1200 /* Special case, headers are already there in the incoming stream.
1201 * We need to gather them an mark them as headers. */
1202 for( unsigned i
= 1; i
< i_count
; i
++ )
1204 op
.bytes
= pi_size
[i
];
1205 op
.packet
= pp_data
[i
];
1206 if( pi_size
[i
] <= 0 )
1207 msg_Err( p_mux
, "header data corrupted");
1212 op
.packetno
= p_stream
->i_packet_no
++;
1213 ogg_stream_packetin( &p_stream
->os
, &op
);
1214 msg_Dbg( p_mux
, "adding non bos, secondary header" );
1215 if( i
== i_count
- 1 )
1216 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1218 p_og
= OggStreamPageOut( p_mux
, &p_stream
->os
, 0 );
1220 block_ChainAppend( &p_hdr
, p_og
);
1223 else if( p_stream
->fmt
.i_codec
!= VLC_CODEC_FLAC
&&
1224 p_stream
->fmt
.i_codec
!= VLC_CODEC_DIRAC
)
1230 com
[0] = PACKET_TYPE_COMMENT
;
1231 i_com
= snprintf( (char *)(com
+1), 127,
1232 PACKAGE_VERSION
" stream output" )
1239 op
.packetno
= p_stream
->i_packet_no
++;
1240 ogg_stream_packetin( &p_stream
->os
, &op
);
1241 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1242 block_ChainAppend( &p_hdr
, p_og
);
1245 /* Special case for mp4v and flac */
1246 if( ( p_stream
->fmt
.i_codec
== VLC_CODEC_MP4V
||
1247 p_stream
->fmt
.i_codec
== VLC_CODEC_FLAC
) &&
1248 p_input
->p_fmt
->i_extra
)
1250 /* Send a packet with the VOL data for mp4v
1251 * or STREAMINFO for flac */
1252 msg_Dbg( p_mux
, "writing extra data" );
1253 op
.bytes
= p_input
->p_fmt
->i_extra
;
1254 op
.packet
= p_input
->p_fmt
->p_extra
;
1255 uint8_t flac_streaminfo
[34 + 4];
1256 if( p_stream
->fmt
.i_codec
== VLC_CODEC_FLAC
)
1258 if (op
.bytes
== 42 && !memcmp(op
.packet
, "fLaC", 4)) {
1260 memcpy(flac_streaminfo
, op
.packet
+ 4, 38);
1261 op
.packet
= flac_streaminfo
;
1262 } else if (op
.bytes
== 34) {
1264 memcpy(flac_streaminfo
+ 4, op
.packet
, 34);
1265 flac_streaminfo
[0] = 0x80; /* last block, streaminfo */
1266 flac_streaminfo
[1] = 0;
1267 flac_streaminfo
[2] = 0;
1268 flac_streaminfo
[3] = 34; /* block size */
1269 op
.packet
= flac_streaminfo
;
1271 msg_Err(p_mux
, "Invalid FLAC streaminfo (%ld bytes)",
1278 op
.packetno
= p_stream
->i_packet_no
++;
1279 ogg_stream_packetin( &p_stream
->os
, &op
);
1280 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1281 block_ChainAppend( &p_hdr
, p_og
);
1285 if ( p_sys
->skeleton
.b_create
)
1287 msg_Dbg( p_mux
, "ending skeleton" );
1293 op
.packetno
= p_sys
->skeleton
.i_packet_no
++;
1294 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1295 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1296 block_ChainAppend( &p_hdr
, p_og
);
1299 /* set HEADER flag */
1300 /* flag headers to be resent for streaming clients */
1301 for( p_og
= p_hdr
; p_og
!= NULL
; p_og
= p_og
->p_next
)
1303 p_og
->i_flags
|= BLOCK_FLAG_HEADER
;
1306 /* Write previous headers */
1307 p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_hdr
);
1312 static void OggCreateStreamFooter( sout_mux_t
*p_mux
, ogg_stream_t
*p_stream
)
1316 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1318 /* as stream is finished, overwrite the index, if there was any */
1319 if ( p_sys
->skeleton
.b_create
&& p_stream
->skeleton
.p_index
1320 && p_stream
->skeleton
.i_index_payload
)
1322 sout_AccessOutSeek( p_mux
->p_access
, p_stream
->skeleton
.i_index_offset
);
1323 OggGetSkeletonIndex( &op
.packet
, &op
.bytes
, p_stream
);
1324 if ( op
.packet
!= NULL
)
1326 msg_Dbg(p_mux
, "Rewriting index at %"PRId64
, p_stream
->skeleton
.i_index_offset
);
1327 ogg_stream_reset_serialno( &p_sys
->skeleton
.os
, p_sys
->skeleton
.i_serial_no
);
1331 op
.packetno
= p_stream
->skeleton
.i_index_packetno
+ 1;
1332 /* fake our stream state */
1333 p_sys
->skeleton
.os
.pageno
= p_stream
->skeleton
.i_index_pageno
;
1334 p_sys
->skeleton
.os
.packetno
= p_stream
->skeleton
.i_index_packetno
;
1335 p_sys
->skeleton
.os
.granulepos
= 0;
1336 p_sys
->skeleton
.os
.b_o_s
= 1;
1337 p_sys
->skeleton
.os
.e_o_s
= 0;
1338 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1339 ogg_packet_clear( &op
);
1340 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1341 sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1343 sout_AccessOutSeek( p_mux
->p_access
, p_sys
->i_pos
);
1346 /* clear skeleton */
1347 p_stream
->skeleton
.b_fisbone_done
= false;
1348 p_stream
->skeleton
.b_index_done
= false;
1349 p_stream
->skeleton
.i_index_offset
= 0;
1350 p_stream
->skeleton
.i_index_payload
= 0;
1351 p_stream
->skeleton
.i_last_keyframe_pos
= 0;
1352 p_stream
->skeleton
.i_last_keyframe_time
= 0;
1353 /* clear accounting */
1354 p_stream
->i_num_frames
= 0;
1355 p_stream
->i_num_keyframes
= 0;
1357 /* Write eos packet for stream. */
1362 op
.granulepos
= p_stream
->u_last_granulepos
;
1363 op
.packetno
= p_stream
->i_packet_no
++;
1364 ogg_stream_packetin( &p_stream
->os
, &op
);
1366 /* flush it with all remaining data */
1367 if( ( p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 ) ) )
1370 OggSetDate( p_og
, p_stream
->i_dts
, p_stream
->i_length
);
1371 p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1374 ogg_stream_clear( &p_stream
->os
);
1377 static void OggSetDate( block_t
*p_og
, mtime_t i_dts
, mtime_t i_length
)
1383 for( p_tmp
= p_og
, i_count
= 0; p_tmp
!= NULL
; p_tmp
= p_tmp
->p_next
)
1388 if( i_count
== 0 ) return; /* ignore. */
1390 i_delta
= i_length
/ i_count
;
1392 for( p_tmp
= p_og
; p_tmp
!= NULL
; p_tmp
= p_tmp
->p_next
)
1394 p_tmp
->i_dts
= i_dts
;
1395 p_tmp
->i_length
= i_delta
;
1401 static void OggRewriteFisheadPage( sout_mux_t
*p_mux
)
1403 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1406 op
.packet
= calloc( 1, op
.bytes
);
1407 if ( op
.packet
!= NULL
)
1413 ogg_stream_reset_serialno( &p_sys
->skeleton
.os
, p_sys
->skeleton
.i_serial_no
);
1414 OggFillSkeletonFishead( op
.packet
, p_mux
);
1415 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1416 ogg_packet_clear( &op
);
1417 msg_Dbg( p_mux
, "rewriting fishead at %"PRId64
, p_sys
->skeleton
.i_fishead_offset
);
1418 sout_AccessOutSeek( p_mux
->p_access
, p_sys
->skeleton
.i_fishead_offset
);
1419 sout_AccessOutWrite( p_mux
->p_access
,
1420 OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 ) );
1421 sout_AccessOutSeek( p_mux
->p_access
, p_sys
->i_pos
);
1425 static bool AllocateIndex( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
1427 sout_mux_sys_t
*p_sys
= p_mux
;
1428 ogg_stream_t
*p_stream
= (ogg_stream_t
*) p_input
->p_sys
;
1431 if ( p_stream
->i_length
)
1433 uint64_t i_interval
= (uint64_t)p_sys
->skeleton
.i_index_intvl
* 1000;
1436 if( p_input
->p_fmt
->i_cat
== VIDEO_ES
&&
1437 p_stream
->fmt
.video
.i_frame_rate
)
1439 /* optimize for fps < 1 */
1440 i_interval
= __MAX( p_sys
->skeleton
.i_index_intvl
* 1000,
1442 p_stream
->fmt
.video
.i_frame_rate_base
/
1443 p_stream
->fmt
.video
.i_frame_rate
);
1446 size_t i_tuple_size
= 0;
1447 /* estimate length of pos value */
1448 if ( p_input
->p_fmt
->i_bitrate
)
1450 i
= i_interval
* p_input
->p_fmt
->i_bitrate
/ 1000000;
1451 while ( i
<<= 1 ) i_tuple_size
++;
1455 /* Likely 64KB<<keyframe interval<<16MB */
1456 /* We can't really guess due to muxing */
1457 i_tuple_size
= 24 / 8;
1460 /* add length of interval value */
1462 while ( i
<<= 1 ) i_tuple_size
++;
1464 i_size
= i_tuple_size
* ( p_stream
->i_length
/ i_interval
+ 2 );
1468 i_size
= ( INT64_C(3600) * 11.2 * 1000 / p_sys
->skeleton
.i_index_intvl
)
1469 * p_sys
->skeleton
.i_index_ratio
;
1470 msg_Dbg( p_mux
, "No stream length, using default allocation for index" );
1472 i_size
*= ( 8.0 / 7 ); /* 7bits encoding overhead */
1473 msg_Dbg( p_mux
, "allocating %zu bytes for index", i_size
);
1474 p_stream
->skeleton
.p_index
= calloc( i_size
, sizeof(uint8_t) );
1475 if ( !p_stream
->skeleton
.p_index
) return false;
1476 p_stream
->skeleton
.i_index_size
= i_size
;
1477 p_stream
->skeleton
.i_index_payload
= 0;
1481 /*****************************************************************************
1482 * Mux: multiplex available data in input fifos into the Ogg bitstream
1483 *****************************************************************************/
1484 static int Mux( sout_mux_t
*p_mux
)
1486 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1489 /* End any stream that ends in that group */
1490 if ( p_sys
->i_del_streams
)
1492 /* Remove deleted logical streams */
1493 for( int i
= 0; i
< p_sys
->i_del_streams
; i
++ )
1495 OggCreateStreamFooter( p_mux
, p_sys
->pp_del_streams
[i
] );
1496 FREENULL( p_sys
->pp_del_streams
[i
]->p_oggds_header
);
1497 FREENULL( p_sys
->pp_del_streams
[i
] );
1499 FREENULL( p_sys
->pp_del_streams
);
1500 p_sys
->i_del_streams
= 0;
1503 if ( p_sys
->i_streams
== 0 )
1505 /* All streams have been deleted, or none have ever been created
1506 From this point, we are allowed to start a new group of logical streams */
1507 p_sys
->skeleton
.b_head_done
= false;
1508 p_sys
->b_can_add_streams
= true;
1509 p_sys
->i_segment_start
= p_sys
->i_pos
;
1512 if ( p_sys
->i_add_streams
)
1514 if ( !p_sys
->b_can_add_streams
)
1516 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
);
1517 msg_Warn( p_mux
, "Resetting and setting new identity to current streams");
1519 /* resetting all active streams */
1520 for ( int i
=0; i
< p_sys
->i_streams
; i
++ )
1522 ogg_stream_t
* p_stream
= (ogg_stream_t
*) p_mux
->pp_inputs
[i
]->p_sys
;
1523 if ( p_stream
->b_finished
|| !p_stream
->b_started
) continue;
1524 OggCreateStreamFooter( p_mux
, p_stream
);
1525 p_stream
->i_serial_no
= p_sys
->i_next_serial_no
++;
1526 p_stream
->i_packet_no
= 0;
1527 p_stream
->b_finished
= true;
1530 /* rewrite fishead with final values */
1531 if ( p_sys
->skeleton
.b_head_done
)
1533 OggRewriteFisheadPage( p_mux
);
1536 p_sys
->b_can_add_streams
= true;
1537 p_sys
->skeleton
.b_head_done
= false;
1538 p_sys
->i_segment_start
= p_sys
->i_pos
;
1541 /* Open new ogg stream */
1542 if( sout_MuxGetStream( p_mux
, 1, &i_dts
) < 0 )
1544 msg_Dbg( p_mux
, "waiting for data..." );
1547 msg_Dbg( p_mux
, "writing streams headers" );
1548 p_sys
->i_start_dts
= i_dts
;
1549 p_sys
->i_streams
= p_mux
->i_nb_inputs
;
1550 p_sys
->i_del_streams
= 0;
1551 p_sys
->i_add_streams
= 0;
1552 p_sys
->skeleton
.b_create
= true;
1554 if ( ! OggCreateHeaders( p_mux
) )
1557 /* If we're switching to end of headers, then that's data start */
1558 if ( p_sys
->b_can_add_streams
)
1560 msg_Dbg( p_mux
, "data starts from %zu", p_sys
->i_pos
);
1561 p_sys
->i_data_start
= p_sys
->i_pos
;
1564 /* Since we started sending secondaryheader or data pages,
1565 * we're no longer allowed to create new streams, until all streams end */
1566 p_sys
->b_can_add_streams
= false;
1569 /* Do the regular data mux thing */
1572 int i_stream
= sout_MuxGetStream( p_mux
, 1, NULL
);
1575 MuxBlock( p_mux
, p_mux
->pp_inputs
[i_stream
] );
1581 static int MuxBlock( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
1583 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1584 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1585 block_t
*p_data
= block_FifoGet( p_input
->p_fifo
);
1586 block_t
*p_og
= NULL
;
1590 if( p_stream
->fmt
.i_codec
!= VLC_CODEC_VORBIS
&&
1591 p_stream
->fmt
.i_codec
!= VLC_CODEC_FLAC
&&
1592 p_stream
->fmt
.i_codec
!= VLC_CODEC_SPEEX
&&
1593 p_stream
->fmt
.i_codec
!= VLC_CODEC_OPUS
&&
1594 p_stream
->fmt
.i_codec
!= VLC_CODEC_THEORA
&&
1595 p_stream
->fmt
.i_codec
!= VLC_CODEC_DAALA
&&
1596 p_stream
->fmt
.i_codec
!= VLC_CODEC_VP8
&&
1597 p_stream
->fmt
.i_codec
!= VLC_CODEC_DIRAC
)
1599 p_data
= block_Realloc( p_data
, 1, p_data
->i_buffer
);
1600 p_data
->p_buffer
[0] = PACKET_IS_SYNCPOINT
; // FIXME
1603 if ( p_stream
->fmt
.i_codec
== VLC_CODEC_DIRAC
&& p_stream
->i_baseptsdelay
< 0 )
1604 p_stream
->i_baseptsdelay
= p_data
->i_pts
- p_data
->i_dts
;
1606 op
.packet
= p_data
->p_buffer
;
1607 op
.bytes
= p_data
->i_buffer
;
1610 op
.packetno
= p_stream
->i_packet_no
++;
1613 if( p_stream
->fmt
.i_cat
== AUDIO_ES
)
1615 if( p_stream
->fmt
.i_codec
== VLC_CODEC_VORBIS
||
1616 p_stream
->fmt
.i_codec
== VLC_CODEC_FLAC
||
1617 p_stream
->fmt
.i_codec
== VLC_CODEC_OPUS
||
1618 p_stream
->fmt
.i_codec
== VLC_CODEC_SPEEX
)
1620 /* number of sample from begining + current packet */
1622 ( p_data
->i_dts
- p_sys
->i_start_dts
+ p_data
->i_length
) *
1623 (mtime_t
)p_input
->p_fmt
->audio
.i_rate
/ CLOCK_FREQ
;
1625 i_time
= p_data
->i_dts
- p_sys
->i_start_dts
;
1626 AddIndexEntry( p_mux
, i_time
, p_input
);
1628 else if( p_stream
->p_oggds_header
)
1630 /* number of sample from begining */
1631 op
.granulepos
= ( p_data
->i_dts
- p_sys
->i_start_dts
) *
1632 p_stream
->p_oggds_header
->i_samples_per_unit
/ CLOCK_FREQ
;
1635 else if( p_stream
->fmt
.i_cat
== VIDEO_ES
)
1637 if( p_stream
->fmt
.i_codec
== VLC_CODEC_THEORA
||
1638 p_stream
->fmt
.i_codec
== VLC_CODEC_DAALA
)
1640 p_stream
->i_num_frames
++;
1641 if( p_data
->i_flags
& BLOCK_FLAG_TYPE_I
)
1643 p_stream
->i_num_keyframes
++;
1644 p_stream
->i_last_keyframe
= p_stream
->i_num_frames
;
1646 /* presentation time */
1647 i_time
= CLOCK_FREQ
* ( p_stream
->i_num_frames
- 1 ) *
1648 p_stream
->fmt
.video
.i_frame_rate_base
/
1649 p_stream
->fmt
.video
.i_frame_rate
;
1650 AddIndexEntry( p_mux
, i_time
, p_input
);
1653 op
.granulepos
= (p_stream
->i_last_keyframe
<< p_stream
->i_keyframe_granule_shift
)
1654 | (p_stream
->i_num_frames
-p_stream
->i_last_keyframe
);
1656 else if( p_stream
->fmt
.i_codec
== VLC_CODEC_DIRAC
)
1659 #define FRAME_ROUND(a) \
1660 if ( ( a + 5000 / CLOCK_FREQ ) > ( a / CLOCK_FREQ ) )\
1664 mtime_t dt
= (p_data
->i_dts
- p_sys
->i_start_dts
) * p_stream
->fmt
.video
.i_frame_rate
/
1665 p_stream
->fmt
.video
.i_frame_rate_base
;
1668 mtime_t pt
= (p_data
->i_pts
- p_sys
->i_start_dts
- p_stream
->i_baseptsdelay
) *
1669 p_stream
->fmt
.video
.i_frame_rate
/ p_stream
->fmt
.video
.i_frame_rate_base
;
1672 /* (shro) some PTS could be repeated within 1st frames */
1673 if ( pt
== p_stream
->i_dirac_last_pt
)
1676 p_stream
->i_dirac_last_pt
= pt
;
1678 /* (shro) some DTS could be repeated within 1st frames */
1679 if ( dt
== p_stream
->i_dirac_last_dt
)
1682 p_stream
->i_dirac_last_dt
= dt
;
1684 if( p_data
->i_flags
& BLOCK_FLAG_TYPE_I
)
1685 p_stream
->i_last_keyframe
= dt
;
1686 mtime_t dist
= dt
- p_stream
->i_last_keyframe
;
1688 /* Everything increments by two for progressive */
1695 mtime_t delay
= pt
- dt
;
1696 if ( delay
< 0 ) delay
*= -1;
1698 op
.granulepos
= (pt
- delay
) << 31 | (dist
&0xff00) << 14
1699 | (delay
&0x1fff) << 9 | (dist
&0xff);
1701 msg_Dbg( p_mux
, "dts %"PRId64
" pts %"PRId64
" dt %"PRId64
" pt %"PRId64
" delay %"PRId64
" granule %"PRId64
,
1702 (p_data
->i_dts
- p_sys
->i_start_dts
),
1703 (p_data
->i_pts
- p_sys
->i_start_dts
),
1704 dt
, pt
, delay
, op
.granulepos
);
1707 AddIndexEntry( p_mux
, dt
, p_input
);
1709 else if( p_stream
->fmt
.i_codec
== VLC_CODEC_VP8
)
1711 p_stream
->i_num_frames
++;
1712 if( p_data
->i_flags
& BLOCK_FLAG_TYPE_I
)
1714 p_stream
->i_num_keyframes
++;
1715 p_stream
->i_last_keyframe
= p_stream
->i_num_frames
;
1717 /* presentation time */
1718 i_time
= CLOCK_FREQ
* ( p_stream
->i_num_frames
- 1 ) *
1719 p_stream
->fmt
.video
.i_frame_rate_base
/ p_stream
->fmt
.video
.i_frame_rate
;
1720 AddIndexEntry( p_mux
, i_time
, p_input
);
1722 op
.granulepos
= ( ((int64_t)p_stream
->i_num_frames
) << 32 ) |
1723 ( ( ( p_stream
->i_num_frames
- p_stream
->i_last_keyframe
) & 0x07FFFFFF ) << 3 );
1725 else if( p_stream
->p_oggds_header
)
1726 op
.granulepos
= ( p_data
->i_dts
- p_sys
->i_start_dts
) * INT64_C(10) /
1727 p_stream
->p_oggds_header
->i_time_unit
;
1729 else if( p_stream
->fmt
.i_cat
== SPU_ES
)
1731 /* granulepos is in millisec */
1732 op
.granulepos
= ( p_data
->i_dts
- p_sys
->i_start_dts
) / 1000;
1735 return VLC_EGENERIC
;
1737 p_stream
->u_last_granulepos
= op
.granulepos
;
1738 ogg_stream_packetin( &p_stream
->os
, &op
);
1740 if( p_stream
->fmt
.i_cat
== SPU_ES
||
1741 p_stream
->fmt
.i_codec
== VLC_CODEC_SPEEX
||
1742 p_stream
->fmt
.i_codec
== VLC_CODEC_DIRAC
)
1744 /* Subtitles or Speex packets are quite small so they
1745 * need to be flushed to be sent on time */
1746 /* The OggDirac mapping suggests ever so strongly that a
1747 * page flush occurs after each OggDirac packet, so to make
1748 * the timestamps unambiguous */
1749 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, p_data
->i_dts
);
1753 p_og
= OggStreamPageOut( p_mux
, &p_stream
->os
, p_data
->i_dts
);
1758 OggSetDate( p_og
, p_stream
->i_dts
, p_stream
->i_length
);
1759 p_stream
->i_dts
= -1;
1760 p_stream
->i_length
= 0;
1761 p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1765 if( p_stream
->i_dts
< 0 )
1767 p_stream
->i_dts
= p_data
->i_dts
;
1769 p_stream
->i_length
+= p_data
->i_length
;
1772 block_Release( p_data
);