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 int DelStream( sout_mux_t
*, sout_input_t
* );
79 static int Mux ( sout_mux_t
* );
80 static int MuxBlock ( sout_mux_t
*, sout_input_t
* );
82 static bool OggCreateHeaders( sout_mux_t
* );
83 static void OggFillSkeletonFishead( uint8_t *p_buffer
, sout_mux_t
*p_mux
);
85 /*****************************************************************************
87 *****************************************************************************/
90 #define FISBONE_BASE_SIZE 52
91 #define FISBONE_BASE_OFFSET 44
92 #define INDEX_BASE_SIZE 42
94 /* Structures used for OggDS headers used in ogm files */
96 #define PACKET_TYPE_HEADER 0x01
97 #define PACKET_TYPE_COMMENT 0x03
98 #define PACKET_IS_SYNCPOINT 0x08
104 } oggds_header_video_t
;
109 int16_t i_block_align
;
110 int32_t i_avgbytespersec
;
111 } oggds_header_audio_t
;
115 uint8_t i_packet_type
;
123 int64_t i_samples_per_unit
;
124 int32_t i_default_len
;
126 int32_t i_buffer_size
;
127 int16_t i_bits_per_sample
;
129 int16_t i_padding_0
; /* Because the original is using MSVC packing style */
133 oggds_header_video_t video
;
134 oggds_header_audio_t audio
;
137 int32_t i_padding_1
; /* Because the original is using MSVC packing style */
141 /*****************************************************************************
142 * Definitions of structures and functions used by this plugins
143 *****************************************************************************/
147 vlc_fourcc_t i_fourcc
;
155 int i_keyframe_granule_shift
; /* Theora only */
156 int i_last_keyframe
; /* dirac and theora */
157 int i_num_frames
; /* Theora only */
158 uint64_t u_last_granulepos
; /* Used for correct EOS page */
159 int64_t i_num_keyframes
;
162 oggds_header_t
*p_oggds_header
;
170 /* Skeleton index storage */
171 unsigned char *p_index
;
172 uint64_t i_index_size
;
173 uint64_t i_index_payload
; /* real index size */
174 uint64_t i_index_count
;
175 /* backup values for rewriting index page later */
176 uint64_t i_index_offset
; /* sout offset of the index page */
177 int64_t i_index_packetno
; /* index packet no */
179 /* index creation tracking values */
180 uint64_t i_last_keyframe_pos
;
181 uint64_t i_last_keyframe_time
;
186 mtime_t i_baseptsdelay
;
190 struct sout_mux_sys_t
195 int i_next_serial_no
;
197 /* number of logical streams pending to be added */
199 bool b_can_add_streams
;
201 /* logical streams pending to be deleted */
203 ogg_stream_t
**pp_del_streams
;
213 /* backup values for rewriting fishead page later */
214 uint64_t i_fishead_offset
; /* sout offset of the fishead page */
219 /* access position */
221 ssize_t i_data_start
;
222 ssize_t i_segment_start
;
225 static void OggSetDate( block_t
*, mtime_t
, mtime_t
);
226 static block_t
*OggStreamFlush( sout_mux_t
*, ogg_stream_state
*, mtime_t
);
227 static void OggCreateStreamFooter( sout_mux_t
*p_mux
, ogg_stream_t
*p_stream
);
228 static void OggRewriteFisheadPage( sout_mux_t
*p_mux
);
229 static bool AllocateIndex( sout_mux_t
*p_mux
, sout_input_t
*p_input
);
231 /*****************************************************************************
233 *****************************************************************************/
234 static int Open( vlc_object_t
*p_this
)
236 sout_mux_t
*p_mux
= (sout_mux_t
*)p_this
;
237 sout_mux_sys_t
*p_sys
;
239 msg_Info( p_mux
, "Open" );
241 p_sys
= malloc( sizeof( sout_mux_sys_t
) );
244 p_sys
->i_streams
= 0;
245 p_sys
->i_add_streams
= 0;
246 p_sys
->b_can_add_streams
= true;
247 p_sys
->i_del_streams
= 0;
248 p_sys
->pp_del_streams
= 0;
250 p_sys
->skeleton
.b_create
= false;
251 p_sys
->skeleton
.b_head_done
= false;
252 p_sys
->skeleton
.i_index_intvl
=
253 var_InheritInteger( p_this
, SOUT_CFG_PREFIX
"indexintvl" );
254 p_sys
->skeleton
.i_index_ratio
=
255 var_InheritFloat( p_this
, SOUT_CFG_PREFIX
"indexratio" );
256 p_sys
->i_data_start
= 0;
257 p_sys
->i_segment_start
= 0;
258 p_mux
->p_sys
= p_sys
;
259 p_mux
->pf_control
= Control
;
260 p_mux
->pf_addstream
= AddStream
;
261 p_mux
->pf_delstream
= DelStream
;
264 /* First serial number is random.
265 * (Done like this because on win32 you need to seed the random number
266 * generator once per thread). */
268 vlc_rand_bytes(&r
, sizeof(r
));
269 p_sys
->i_next_serial_no
= r
& INT_MAX
;
274 /*****************************************************************************
275 * Close: Finalize ogg bitstream and close muxer
276 *****************************************************************************/
277 static void Close( vlc_object_t
* p_this
)
279 sout_mux_t
*p_mux
= (sout_mux_t
*)p_this
;
280 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
281 ogg_stream_t
*p_stream
;
283 msg_Info( p_mux
, "Close" );
285 if( p_sys
->i_del_streams
)
287 /* Close the current ogg stream */
288 msg_Dbg( p_mux
, "writing footers" );
290 for(int i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
292 p_stream
= (ogg_stream_t
*) p_mux
->pp_inputs
[i
]->p_sys
;
293 OggCreateStreamFooter( p_mux
, p_stream
);
294 free( p_stream
->skeleton
.p_index
);
297 /* Remove deleted logical streams */
298 for(int i
= 0; i
< p_sys
->i_del_streams
; i
++ )
300 OggCreateStreamFooter( p_mux
, p_sys
->pp_del_streams
[i
] );
301 free( p_sys
->pp_del_streams
[i
]->p_oggds_header
);
302 free( p_sys
->pp_del_streams
[i
]->skeleton
.p_index
);
303 free( p_sys
->pp_del_streams
[i
] );
305 free( p_sys
->pp_del_streams
);
306 p_sys
->i_streams
-= p_sys
->i_del_streams
;
309 /* rewrite fishead with final values */
310 if ( p_sys
->skeleton
.b_create
&& p_sys
->skeleton
.b_head_done
)
312 OggRewriteFisheadPage( p_mux
);
318 /*****************************************************************************
320 *****************************************************************************/
321 static int Control( sout_mux_t
*p_mux
, int i_query
, va_list args
)
329 case MUX_CAN_ADD_STREAM_WHILE_MUXING
:
330 pb_bool
= (bool*)va_arg( args
, bool * );
334 case MUX_GET_ADD_STREAM_WAIT
:
335 pb_bool
= (bool*)va_arg( args
, bool * );
340 ppsz
= (char**)va_arg( args
, char ** );
341 *ppsz
= strdup( "application/ogg" );
348 /*****************************************************************************
349 * AddStream: Add an elementary stream to the muxed stream
350 *****************************************************************************/
351 static int AddStream( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
353 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
354 ogg_stream_t
*p_stream
;
357 msg_Dbg( p_mux
, "adding input" );
359 p_input
->p_sys
= p_stream
= calloc( 1, sizeof( ogg_stream_t
) );
363 p_stream
->i_cat
= p_input
->p_fmt
->i_cat
;
364 p_stream
->i_fourcc
= p_input
->p_fmt
->i_codec
;
365 p_stream
->i_serial_no
= p_sys
->i_next_serial_no
++;
366 p_stream
->i_packet_no
= 0;
367 p_stream
->i_last_keyframe
= 0;
368 p_stream
->i_num_keyframes
= 0;
369 p_stream
->i_num_frames
= 0;
371 p_stream
->p_oggds_header
= 0;
373 p_stream
->i_baseptsdelay
= -1;
374 p_stream
->i_dirac_last_pt
= -1;
375 p_stream
->i_dirac_last_dt
= -1;
377 switch( p_input
->p_fmt
->i_cat
)
380 if( !p_input
->p_fmt
->video
.i_frame_rate
||
381 !p_input
->p_fmt
->video
.i_frame_rate_base
)
383 msg_Warn( p_mux
, "Missing frame rate, assuming 25fps" );
384 p_input
->p_fmt
->video
.i_frame_rate
= 25;
385 p_input
->p_fmt
->video
.i_frame_rate_base
= 1;
388 switch( p_stream
->i_fourcc
)
399 p_stream
->p_oggds_header
= calloc( 1, sizeof(oggds_header_t
) );
400 if( !p_stream
->p_oggds_header
)
405 p_stream
->p_oggds_header
->i_packet_type
= PACKET_TYPE_HEADER
;
407 memcpy( p_stream
->p_oggds_header
->stream_type
, "video", 5 );
408 if( p_stream
->i_fourcc
== VLC_CODEC_MP4V
)
410 memcpy( p_stream
->p_oggds_header
->sub_type
, "XVID", 4 );
412 else if( p_stream
->i_fourcc
== VLC_CODEC_DIV3
)
414 memcpy( p_stream
->p_oggds_header
->sub_type
, "DIV3", 4 );
418 memcpy( p_stream
->p_oggds_header
->sub_type
,
419 &p_stream
->i_fourcc
, 4 );
421 p_stream
->p_oggds_header
->i_size
= 0 ;
422 p_stream
->p_oggds_header
->i_time_unit
=
423 INT64_C(10000000) * p_input
->p_fmt
->video
.i_frame_rate_base
/
424 (int64_t)p_input
->p_fmt
->video
.i_frame_rate
;
425 p_stream
->p_oggds_header
->i_samples_per_unit
= 1;
426 p_stream
->p_oggds_header
->i_default_len
= 1 ; /* ??? */
427 p_stream
->p_oggds_header
->i_buffer_size
= 1024*1024;
428 p_stream
->p_oggds_header
->i_bits_per_sample
= 0;
429 p_stream
->p_oggds_header
->header
.video
.i_width
= p_input
->p_fmt
->video
.i_width
;
430 p_stream
->p_oggds_header
->header
.video
.i_height
= p_input
->p_fmt
->video
.i_height
;
431 msg_Dbg( p_mux
, "%4.4s stream", (char *)&p_stream
->i_fourcc
);
434 case VLC_CODEC_DIRAC
:
435 msg_Dbg( p_mux
, "dirac stream" );
438 case VLC_CODEC_THEORA
:
439 msg_Dbg( p_mux
, "theora stream" );
443 msg_Dbg( p_mux
, "VP8 stream" );
447 FREENULL( p_input
->p_sys
);
453 switch( p_stream
->i_fourcc
)
456 msg_Dbg( p_mux
, "opus stream" );
459 case VLC_CODEC_VORBIS
:
460 msg_Dbg( p_mux
, "vorbis stream" );
463 case VLC_CODEC_SPEEX
:
464 msg_Dbg( p_mux
, "speex stream" );
468 msg_Dbg( p_mux
, "flac stream" );
472 fourcc_to_wf_tag( p_stream
->i_fourcc
, &i_tag
);
473 if( i_tag
== WAVE_FORMAT_UNKNOWN
)
475 FREENULL( p_input
->p_sys
);
479 p_stream
->p_oggds_header
=
480 malloc( sizeof(oggds_header_t
) + p_input
->p_fmt
->i_extra
);
481 if( !p_stream
->p_oggds_header
)
486 memset( p_stream
->p_oggds_header
, 0, sizeof(oggds_header_t
) );
487 p_stream
->p_oggds_header
->i_packet_type
= PACKET_TYPE_HEADER
;
489 p_stream
->p_oggds_header
->i_size
= p_input
->p_fmt
->i_extra
;
491 if( p_input
->p_fmt
->i_extra
)
493 memcpy( &p_stream
->p_oggds_header
[1],
494 p_input
->p_fmt
->p_extra
, p_input
->p_fmt
->i_extra
);
497 memcpy( p_stream
->p_oggds_header
->stream_type
, "audio", 5 );
499 memset( p_stream
->p_oggds_header
->sub_type
, 0, 4 );
501 snprintf( buf
, sizeof(buf
), "%"PRIx16
, i_tag
);
502 strncpy( p_stream
->p_oggds_header
->sub_type
, buf
, 4 );
504 p_stream
->p_oggds_header
->i_time_unit
= INT64_C(10000000);
505 p_stream
->p_oggds_header
->i_default_len
= 1;
506 p_stream
->p_oggds_header
->i_buffer_size
= 30*1024 ;
507 p_stream
->p_oggds_header
->i_samples_per_unit
= p_input
->p_fmt
->audio
.i_rate
;
508 p_stream
->p_oggds_header
->i_bits_per_sample
= p_input
->p_fmt
->audio
.i_bitspersample
;
509 p_stream
->p_oggds_header
->header
.audio
.i_channels
= p_input
->p_fmt
->audio
.i_channels
;
510 p_stream
->p_oggds_header
->header
.audio
.i_block_align
= p_input
->p_fmt
->audio
.i_blockalign
;
511 p_stream
->p_oggds_header
->header
.audio
.i_avgbytespersec
= p_input
->p_fmt
->i_bitrate
/ 8;
512 msg_Dbg( p_mux
, "%4.4s stream", (char *)&p_stream
->i_fourcc
);
518 switch( p_stream
->i_fourcc
)
521 p_stream
->p_oggds_header
= calloc( 1, sizeof(oggds_header_t
) );
522 if( !p_stream
->p_oggds_header
)
527 p_stream
->p_oggds_header
->i_packet_type
= PACKET_TYPE_HEADER
;
529 memcpy( p_stream
->p_oggds_header
->stream_type
, "text", 4 );
530 msg_Dbg( p_mux
, "subtitles stream" );
534 FREENULL( p_input
->p_sys
);
539 FREENULL( p_input
->p_sys
);
543 p_stream
->b_new
= true;
545 p_sys
->i_add_streams
++;
550 /*****************************************************************************
551 * DelStream: Delete an elementary stream from the muxed stream
552 *****************************************************************************/
553 static int DelStream( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
555 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
556 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
559 msg_Dbg( p_mux
, "removing input" );
561 /* flush all remaining data */
564 if( !p_stream
->b_new
)
566 while( block_FifoCount( p_input
->p_fifo
) )
567 MuxBlock( p_mux
, p_input
);
570 if( !p_stream
->b_new
&&
571 ( p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 ) ) )
573 OggSetDate( p_og
, p_stream
->i_dts
, p_stream
->i_length
);
574 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
577 /* move input in delete queue */
578 if( !p_stream
->b_new
)
580 p_sys
->pp_del_streams
= xrealloc( p_sys
->pp_del_streams
,
581 (p_sys
->i_del_streams
+ 1) * sizeof(ogg_stream_t
*) );
582 p_sys
->pp_del_streams
[p_sys
->i_del_streams
++] = p_stream
;
586 /* wasn't already added so get rid of it */
587 FREENULL( p_stream
->p_oggds_header
);
588 FREENULL( p_stream
);
589 p_sys
->i_add_streams
--;
593 p_input
->p_sys
= NULL
;
598 /*****************************************************************************
599 * Ogg Skeleton helpers
600 *****************************************************************************/
601 static int WriteQWVariableLE( uint64_t i_64
, uint64_t i_offset
,
602 uint8_t *p_buffer
, int i_buffer_size
)
604 uint8_t *p_dest
= p_buffer
+ i_offset
;
609 if ( p_dest
- p_buffer
>= i_buffer_size
) return -1;
611 *p_dest
= (uint8_t) ( i_64
& 0x7F );
625 static bool AddIndexEntry( sout_mux_t
*p_mux
, uint64_t i_time
, sout_input_t
*p_input
)
627 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
628 ogg_stream_t
*p_stream
= (ogg_stream_t
*) p_input
->p_sys
;
630 uint64_t i_timedelta
;
631 if ( !p_sys
->skeleton
.b_create
|| p_mux
->p_sys
->skeleton
.i_index_intvl
== 0
632 || !p_stream
->skeleton
.p_index
)
635 if ( p_stream
->skeleton
.i_last_keyframe_pos
== 0 )
636 p_stream
->skeleton
.i_last_keyframe_pos
= p_sys
->i_segment_start
;
637 i_posdelta
= p_sys
->i_pos
- p_stream
->skeleton
.i_last_keyframe_pos
;
638 i_timedelta
= i_time
- p_stream
->skeleton
.i_last_keyframe_time
;
640 if ( i_timedelta
<= ( (uint64_t) p_mux
->p_sys
->skeleton
.i_index_intvl
* 1000 )
641 || i_posdelta
<= 0xFFFF )
646 if ( !p_stream
->skeleton
.p_index
) return false;
647 uint64_t i_offset
= p_stream
->skeleton
.i_index_payload
;
648 i_ret
= WriteQWVariableLE( i_posdelta
, i_offset
, p_stream
->skeleton
.p_index
,
649 p_stream
->skeleton
.i_index_size
);
650 if ( i_ret
== -1 ) return false;
652 i_ret
= WriteQWVariableLE( i_timedelta
, i_offset
, p_stream
->skeleton
.p_index
,
653 p_stream
->skeleton
.i_index_size
);
654 if ( i_ret
== -1 ) return false;
655 p_stream
->skeleton
.i_index_payload
= i_offset
+ i_ret
;
656 p_stream
->skeleton
.i_index_count
++;
658 /* update diff points */
659 p_stream
->skeleton
.i_last_keyframe_pos
= p_sys
->i_pos
;
660 p_stream
->skeleton
.i_last_keyframe_time
= i_time
;
661 msg_Dbg( p_mux
, "Added index on stream %d entry %zd %"PRIu64
,
662 p_stream
->i_serial_no
, p_sys
->i_pos
- p_sys
->i_segment_start
, i_time
);
667 /*****************************************************************************
668 * Ogg bitstream manipulation routines
669 *****************************************************************************/
670 static block_t
*OggStreamGetPage( sout_mux_t
*p_mux
,
671 ogg_stream_state
*p_os
, mtime_t i_pts
,
675 block_t
*p_og
, *p_og_first
= NULL
;
677 int (*pager
)( ogg_stream_state
*, ogg_page
* ) = flush
? ogg_stream_flush
: ogg_stream_pageout
;
679 while( pager( p_os
, &og
) )
682 p_og
= block_Alloc( og
.header_len
+ og
.body_len
);
684 memcpy( p_og
->p_buffer
, og
.header
, og
.header_len
);
685 memcpy( p_og
->p_buffer
+ og
.header_len
, og
.body
, og
.body_len
);
690 i_pts
= 0; // write it only once
692 block_ChainAppend( &p_og_first
, p_og
);
698 static block_t
*OggStreamFlush( sout_mux_t
*p_mux
,
699 ogg_stream_state
*p_os
, mtime_t i_pts
)
701 return OggStreamGetPage( p_mux
, p_os
, i_pts
, true );
704 static block_t
*OggStreamPageOut( sout_mux_t
*p_mux
,
705 ogg_stream_state
*p_os
, mtime_t i_pts
)
707 return OggStreamGetPage( p_mux
, p_os
, i_pts
, false );
710 static void OggGetSkeletonIndex( uint8_t **pp_buffer
, long *pi_size
, ogg_stream_t
*p_stream
)
712 uint8_t *p_buffer
= calloc( INDEX_BASE_SIZE
+ p_stream
->skeleton
.i_index_size
, sizeof(uint8_t) );
713 if ( !p_buffer
) return;
714 *pp_buffer
= p_buffer
;
716 memcpy( p_buffer
, "index", 6 );
717 SetDWLE( &p_buffer
[6], p_stream
->i_serial_no
);
718 SetQWLE( &p_buffer
[10], p_stream
->skeleton
.i_index_count
); /* num keypoints */
719 SetQWLE( &p_buffer
[18], 1000000 );
720 SetQWLE( &p_buffer
[34], p_stream
->i_length
);
721 memcpy( p_buffer
+ INDEX_BASE_SIZE
, p_stream
->skeleton
.p_index
, p_stream
->skeleton
.i_index_payload
);
722 *pi_size
= INDEX_BASE_SIZE
+ p_stream
->skeleton
.i_index_size
;
725 static void OggGetSkeletonFisbone( uint8_t **pp_buffer
, long *pi_size
,
726 sout_input_t
*p_input
, sout_mux_t
*p_mux
)
730 const char *psz_value
= NULL
;
731 ogg_stream_t
*p_stream
= (ogg_stream_t
*) p_input
->p_sys
;
734 char *psz_content_type
;
737 unsigned int i_count
;
738 } headers
= { NULL
, NULL
, 0, 0 };
741 switch( p_stream
->i_fourcc
)
743 case VLC_CODEC_VORBIS
:
744 psz_value
= "audio/vorbis";
746 case VLC_CODEC_THEORA
:
747 psz_value
= "video/theora";
749 case VLC_CODEC_SPEEX
:
750 psz_value
= "audio/speex";
753 psz_value
= "audio/flac";
756 psz_value
= "text/cmml";
759 psz_value
= "application/kate";
762 psz_value
= "video/x-vp8";
765 psz_value
= "application/octet-stream";
766 msg_Warn( p_mux
, "Unkown fourcc for stream %s, setting Content-Type to %s",
767 vlc_fourcc_GetDescription( p_stream
->i_cat
, p_stream
->i_fourcc
),
771 /* Content Type Header */
772 if ( asprintf( &headers
.psz_content_type
, "Content-Type: %s\r\n", psz_value
) != -1 )
774 headers
.i_size
+= strlen( headers
.psz_content_type
);
778 /* Set Role Header */
779 if ( p_input
->p_fmt
->i_priority
> ES_PRIORITY_NOT_SELECTABLE
)
781 int i_max_prio
= ES_PRIORITY_MIN
;
782 for ( int i
=0; i
< p_mux
->i_nb_inputs
; i
++ )
784 if ( p_mux
->pp_inputs
[i
]->p_fmt
->i_cat
!= p_input
->p_fmt
->i_cat
) continue;
785 i_max_prio
= __MAX( p_mux
->pp_inputs
[i
]->p_fmt
->i_priority
, i_max_prio
);
789 if ( p_input
->p_fmt
->i_cat
== AUDIO_ES
|| p_input
->p_fmt
->i_cat
== VIDEO_ES
)
791 if ( p_input
->p_fmt
->i_priority
== i_max_prio
&& i_max_prio
>= ES_PRIORITY_SELECTABLE_MIN
)
792 psz_value
= ( p_input
->p_fmt
->i_cat
== VIDEO_ES
) ?
793 "video/main" : "audio/main";
795 psz_value
= ( p_input
->p_fmt
->i_cat
== VIDEO_ES
) ?
796 "video/alternate" : "audio/alternate";
798 else if ( p_input
->p_fmt
->i_cat
== SPU_ES
)
800 psz_value
= ( p_input
->p_fmt
->i_codec
== VLC_CODEC_KATE
) ?
801 "text/karaoke" : "text/subtitle";
804 if ( psz_value
&& asprintf( &headers
.psz_role
, "Role: %s\r\n", psz_value
) != -1 )
806 headers
.i_size
+= strlen( headers
.psz_role
);
811 *pp_buffer
= calloc( FISBONE_BASE_SIZE
+ headers
.i_size
, sizeof(uint8_t) );
812 if ( !*pp_buffer
) return;
813 p_buffer
= *pp_buffer
;
815 memcpy( p_buffer
, "fisbone", 8 );
816 SetDWLE( &p_buffer
[8], FISBONE_BASE_OFFSET
); /* offset to message headers */
817 SetDWLE( &p_buffer
[12], p_stream
->i_serial_no
);
818 SetDWLE( &p_buffer
[16], headers
.i_count
);
820 /* granulerate den */
821 switch ( p_input
->p_fmt
->i_cat
)
824 SetQWLE( &(*pp_buffer
)[20], p_input
->p_fmt
->video
.i_frame_rate
);
825 SetQWLE( &(*pp_buffer
)[28], p_input
->p_fmt
->video
.i_frame_rate_base
);
828 SetQWLE( &(*pp_buffer
)[20], p_input
->p_fmt
->audio
.i_rate
);
829 SetQWLE( &(*pp_buffer
)[28], 1 );
832 SetQWLE( &(*pp_buffer
)[20], 1000 );
833 SetQWLE( &(*pp_buffer
)[28], 1 );
837 if ( p_input
->p_fmt
->p_extra
)
838 SetDWLE( &(*pp_buffer
)[44], xiph_CountHeaders( p_input
->p_fmt
->p_extra
, p_input
->p_fmt
->i_extra
) );
840 if ( headers
.i_size
> 0 )
842 psz_header
= *pp_buffer
+ FISBONE_BASE_SIZE
;
843 memcpy( psz_header
, headers
.psz_content_type
, strlen( headers
.psz_content_type
) );
844 psz_header
+= strlen( headers
.psz_content_type
);
845 if ( headers
.psz_role
)
846 memcpy( psz_header
, headers
.psz_role
, strlen( headers
.psz_role
) );
848 *pi_size
= FISBONE_BASE_SIZE
+ headers
.i_size
;
850 free( headers
.psz_content_type
);
851 free( headers
.psz_role
);
854 static void OggFillSkeletonFishead( uint8_t *p_buffer
, sout_mux_t
*p_mux
)
856 memcpy( p_buffer
, "fishead", 8 );
857 SetWLE( &p_buffer
[8], 4 );
858 SetWLE( &p_buffer
[10], 0 );
859 SetQWLE( &p_buffer
[20], 1000 );
860 SetQWLE( &p_buffer
[36], 1000 );
861 SetQWLE( &p_buffer
[64], p_mux
->p_sys
->i_pos
- p_mux
->p_sys
->i_segment_start
); /* segment length */
862 SetQWLE( &p_buffer
[72], p_mux
->p_sys
->i_data_start
- p_mux
->p_sys
->i_segment_start
); /* data start offset */
865 static int32_t OggFillDsHeader( uint8_t *p_buffer
, oggds_header_t
*p_oggds_header
, int i_cat
)
868 p_buffer
[index
] = p_oggds_header
->i_packet_type
;
870 memcpy( &p_buffer
[index
], p_oggds_header
->stream_type
, sizeof(p_oggds_header
->stream_type
) );
871 index
+= sizeof(p_oggds_header
->stream_type
);
872 memcpy(&p_buffer
[index
], p_oggds_header
->sub_type
, sizeof(p_oggds_header
->sub_type
) );
873 index
+= sizeof(p_oggds_header
->sub_type
);
875 /* The size is filled at the end */
876 uint8_t *p_isize
= &p_buffer
[index
];
879 SetQWLE( &p_buffer
[index
], p_oggds_header
->i_time_unit
);
881 SetQWLE( &p_buffer
[index
], p_oggds_header
->i_samples_per_unit
);
883 SetDWLE( &p_buffer
[index
], p_oggds_header
->i_default_len
);
885 SetDWLE( &p_buffer
[index
], p_oggds_header
->i_buffer_size
);
887 SetWLE( &p_buffer
[index
], p_oggds_header
->i_bits_per_sample
);
889 SetWLE( &p_buffer
[index
], p_oggds_header
->i_padding_0
);
895 SetDWLE( &p_buffer
[index
], p_oggds_header
->header
.video
.i_width
);
896 SetDWLE( &p_buffer
[index
+4], p_oggds_header
->header
.video
.i_height
);
899 SetWLE( &p_buffer
[index
], p_oggds_header
->header
.audio
.i_channels
);
900 SetWLE( &p_buffer
[index
+2], p_oggds_header
->header
.audio
.i_block_align
);
901 SetDWLE( &p_buffer
[index
+4], p_oggds_header
->header
.audio
.i_avgbytespersec
);
905 SetDWLE( &p_buffer
[index
], p_oggds_header
->i_padding_1
);
909 if( p_oggds_header
->i_size
> 0 )
911 memcpy( &p_buffer
[index
], p_oggds_header
+ sizeof(*p_oggds_header
), p_oggds_header
->i_size
);
912 index
+= p_oggds_header
->i_size
;
915 SetDWLE( p_isize
, index
-1 );
919 static void OggFillVP8Header( uint8_t *p_buffer
, sout_input_t
*p_input
)
921 memcpy( p_buffer
, "OVP80\x01\x01\x00", 8 );
922 SetWBE( &p_buffer
[8], p_input
->p_fmt
->video
.i_width
);
923 SetDWBE( &p_buffer
[14], p_input
->p_fmt
->video
.i_sar_den
);/* 24 bits, 15~ */
924 SetDWBE( &p_buffer
[11], p_input
->p_fmt
->video
.i_sar_num
);/* 24 bits, 12~ */
925 SetWBE( &p_buffer
[10], p_input
->p_fmt
->video
.i_height
);
926 SetDWBE( &p_buffer
[18], p_input
->p_fmt
->video
.i_frame_rate
);
927 SetDWBE( &p_buffer
[22], p_input
->p_fmt
->video
.i_frame_rate_base
);
930 static bool OggCreateHeaders( sout_mux_t
*p_mux
)
932 block_t
*p_hdr
= NULL
;
933 block_t
*p_og
= NULL
;
935 ogg_stream_t
*p_stream
;
936 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
939 if( sout_AccessOutControl( p_mux
->p_access
,
941 &p_sys
->skeleton
.b_create
) )
943 p_sys
->skeleton
.b_create
= false;
946 p_sys
->skeleton
.b_create
&= !! p_mux
->i_nb_inputs
;
948 /* no skeleton for solo vorbis/speex/opus tracks */
949 if ( p_mux
->i_nb_inputs
== 1 && p_mux
->pp_inputs
[0]->p_fmt
->i_cat
== AUDIO_ES
)
951 p_sys
->skeleton
.b_create
= false;
955 for ( int i
=0; i
< p_mux
->i_nb_inputs
; i
++ )
957 p_stream
= (ogg_stream_t
*) p_mux
->pp_inputs
[i
]->p_sys
;
958 if ( p_stream
->p_oggds_header
)
960 /* We don't want skeleton for OggDS */
961 p_sys
->skeleton
.b_create
= false;
967 /* Skeleton's Fishead must be the first page of the stream */
968 if ( p_sys
->skeleton
.b_create
&& !p_sys
->skeleton
.b_head_done
)
970 msg_Dbg( p_mux
, "creating header for skeleton" );
971 p_sys
->skeleton
.i_serial_no
= p_sys
->i_next_serial_no
++;
972 ogg_stream_init( &p_sys
->skeleton
.os
, p_sys
->skeleton
.i_serial_no
);
974 op
.packet
= calloc( 1, op
.bytes
);
975 if ( op
.packet
== NULL
) return false;
980 OggFillSkeletonFishead( op
.packet
, p_mux
);
981 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
982 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
983 block_ChainAppend( &p_hdr
, p_og
);
984 p_sys
->skeleton
.b_head_done
= true;
985 p_sys
->skeleton
.i_fishead_offset
= p_sys
->i_pos
;
988 /* Write header for each stream. All b_o_s (beginning of stream) packets
989 * must appear first in the ogg stream so we take care of them first. */
990 for( int pass
= 0; pass
< 2; pass
++ )
992 for( i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
994 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
995 p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
997 bool video
= ( p_stream
->i_fourcc
== VLC_CODEC_THEORA
|| p_stream
->i_fourcc
== VLC_CODEC_DIRAC
);
998 if( ( ( pass
== 0 && !video
) || ( pass
== 1 && video
) ) )
1001 msg_Dbg( p_mux
, "creating header for %4.4s",
1002 (char *)&p_stream
->i_fourcc
);
1004 ogg_stream_init( &p_stream
->os
, p_stream
->i_serial_no
);
1005 p_stream
->b_new
= false;
1006 p_stream
->i_packet_no
= 0;
1007 p_stream
->b_started
= true;
1009 if( p_stream
->i_fourcc
== VLC_CODEC_VORBIS
||
1010 p_stream
->i_fourcc
== VLC_CODEC_SPEEX
||
1011 p_stream
->i_fourcc
== VLC_CODEC_OPUS
||
1012 p_stream
->i_fourcc
== VLC_CODEC_THEORA
)
1014 /* First packet in order: vorbis/speex/theora info */
1015 unsigned pi_size
[XIPH_MAX_HEADER_COUNT
];
1016 void *pp_data
[XIPH_MAX_HEADER_COUNT
];
1018 if( xiph_SplitHeaders( pi_size
, pp_data
, &i_count
,
1019 p_input
->p_fmt
->i_extra
, p_input
->p_fmt
->p_extra
) )
1026 op
.bytes
= pi_size
[0];
1027 op
.packet
= pp_data
[0];
1028 if( pi_size
[0] <= 0 )
1029 msg_Err( p_mux
, "header data corrupted");
1034 op
.packetno
= p_stream
->i_packet_no
++;
1035 ogg_stream_packetin( &p_stream
->os
, &op
);
1036 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1038 /* Get keyframe_granule_shift for theora granulepos calculation */
1039 if( p_stream
->i_fourcc
== VLC_CODEC_THEORA
)
1041 p_stream
->i_keyframe_granule_shift
=
1042 ( (op
.packet
[40] & 0x03) << 3 ) | ( (op
.packet
[41] & 0xe0) >> 5 );
1045 else if( p_stream
->i_fourcc
== VLC_CODEC_DIRAC
)
1047 op
.packet
= p_input
->p_fmt
->p_extra
;
1048 op
.bytes
= p_input
->p_fmt
->i_extra
;
1052 op
.packetno
= p_stream
->i_packet_no
++;
1053 ogg_stream_packetin( &p_stream
->os
, &op
);
1054 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1056 else if( p_stream
->i_fourcc
== VLC_CODEC_FLAC
)
1058 /* flac stream marker (yeah, only that in the 1st packet) */
1059 op
.packet
= (unsigned char *)"fLaC";
1064 op
.packetno
= p_stream
->i_packet_no
++;
1065 ogg_stream_packetin( &p_stream
->os
, &op
);
1066 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1068 else if( p_stream
->i_fourcc
== VLC_CODEC_VP8
)
1071 op
.packet
= malloc( 26 );
1075 OggFillVP8Header( op
.packet
, p_input
);
1079 op
.packetno
= p_stream
->i_packet_no
++;
1080 ogg_stream_packetin( &p_stream
->os
, &op
);
1081 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1084 else if( p_stream
->p_oggds_header
)
1087 op
.packet
= malloc( sizeof(*p_stream
->p_oggds_header
) + p_stream
->p_oggds_header
->i_size
);
1090 op
.bytes
= OggFillDsHeader( op
.packet
, p_stream
->p_oggds_header
, p_stream
->i_cat
);
1094 op
.packetno
= p_stream
->i_packet_no
++;
1095 ogg_stream_packetin( &p_stream
->os
, &op
);
1096 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1100 block_ChainAppend( &p_hdr
, p_og
);
1104 /* Create fisbones if any */
1105 if ( p_sys
->skeleton
.b_create
)
1107 for( i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1109 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1110 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1111 if ( p_stream
->skeleton
.b_fisbone_done
) continue;
1112 OggGetSkeletonFisbone( &op
.packet
, &op
.bytes
, p_input
, p_mux
);
1113 if ( op
.packet
== NULL
) return false;
1117 op
.packetno
= p_sys
->skeleton
.i_packet_no
++;
1118 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1119 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1120 block_ChainAppend( &p_hdr
, p_og
);
1121 p_stream
->skeleton
.b_fisbone_done
= true;
1125 /* Write previous headers */
1126 for( p_og
= p_hdr
; p_og
!= NULL
; p_og
= p_og
->p_next
)
1128 /* flag headers to be resent for streaming clients */
1129 p_og
->i_flags
|= BLOCK_FLAG_HEADER
;
1131 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_hdr
);
1134 /* Create indexes if any */
1135 for( i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1137 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1138 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1139 /* flush stream && save offset */
1140 if ( p_sys
->skeleton
.b_create
&& !p_stream
->skeleton
.b_index_done
)
1142 if ( !p_stream
->skeleton
.p_index
) AllocateIndex( p_mux
, p_input
);
1143 if ( p_stream
->skeleton
.p_index
)
1145 msg_Dbg( p_mux
, "Creating index for stream %d", p_stream
->i_serial_no
);
1146 OggGetSkeletonIndex( &op
.packet
, &op
.bytes
, p_stream
);
1147 if ( op
.packet
== NULL
) return false;
1151 op
.packetno
= p_sys
->skeleton
.i_packet_no
++;
1153 /* backup some values */
1154 p_stream
->skeleton
.i_index_offset
= p_mux
->p_sys
->i_pos
;
1155 p_stream
->skeleton
.i_index_packetno
= p_sys
->skeleton
.os
.packetno
;
1156 p_stream
->skeleton
.i_index_pageno
= p_sys
->skeleton
.os
.pageno
;
1158 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1159 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1160 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1162 p_stream
->skeleton
.b_index_done
= true;
1166 /* Take care of the non b_o_s headers */
1167 for( i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1169 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1170 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1172 if( p_stream
->i_fourcc
== VLC_CODEC_VORBIS
||
1173 p_stream
->i_fourcc
== VLC_CODEC_SPEEX
||
1174 p_stream
->i_fourcc
== VLC_CODEC_OPUS
||
1175 p_stream
->i_fourcc
== VLC_CODEC_THEORA
)
1177 unsigned pi_size
[XIPH_MAX_HEADER_COUNT
];
1178 void *pp_data
[XIPH_MAX_HEADER_COUNT
];
1180 if( xiph_SplitHeaders( pi_size
, pp_data
, &i_count
,
1181 p_input
->p_fmt
->i_extra
, p_input
->p_fmt
->p_extra
) )
1184 /* Special case, headers are already there in the incoming stream.
1185 * We need to gather them an mark them as headers. */
1186 for( unsigned i
= 1; i
< i_count
; i
++ )
1188 op
.bytes
= pi_size
[i
];
1189 op
.packet
= pp_data
[i
];
1190 if( pi_size
[i
] <= 0 )
1191 msg_Err( p_mux
, "header data corrupted");
1196 op
.packetno
= p_stream
->i_packet_no
++;
1197 ogg_stream_packetin( &p_stream
->os
, &op
);
1198 msg_Dbg( p_mux
, "adding non bos, secondary header" );
1199 if( i
== i_count
- 1 )
1200 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1202 p_og
= OggStreamPageOut( p_mux
, &p_stream
->os
, 0 );
1204 block_ChainAppend( &p_hdr
, p_og
);
1207 else if( p_stream
->i_fourcc
!= VLC_CODEC_FLAC
&&
1208 p_stream
->i_fourcc
!= VLC_CODEC_DIRAC
)
1214 com
[0] = PACKET_TYPE_COMMENT
;
1215 i_com
= snprintf( (char *)(com
+1), 127,
1216 PACKAGE_VERSION
" stream output" )
1223 op
.packetno
= p_stream
->i_packet_no
++;
1224 ogg_stream_packetin( &p_stream
->os
, &op
);
1225 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1226 block_ChainAppend( &p_hdr
, p_og
);
1229 /* Special case for mp4v and flac */
1230 if( ( p_stream
->i_fourcc
== VLC_CODEC_MP4V
||
1231 p_stream
->i_fourcc
== VLC_CODEC_FLAC
) &&
1232 p_input
->p_fmt
->i_extra
)
1234 /* Send a packet with the VOL data for mp4v
1235 * or STREAMINFO for flac */
1236 msg_Dbg( p_mux
, "writing extra data" );
1237 op
.bytes
= p_input
->p_fmt
->i_extra
;
1238 op
.packet
= p_input
->p_fmt
->p_extra
;
1239 uint8_t flac_streaminfo
[34 + 4];
1240 if( p_stream
->i_fourcc
== VLC_CODEC_FLAC
)
1242 if (op
.bytes
== 42 && !memcmp(op
.packet
, "fLaC", 4)) {
1244 memcpy(flac_streaminfo
, op
.packet
+ 4, 38);
1245 op
.packet
= flac_streaminfo
;
1246 } else if (op
.bytes
== 34) {
1248 memcpy(flac_streaminfo
+ 4, op
.packet
, 34);
1249 flac_streaminfo
[0] = 0x80; /* last block, streaminfo */
1250 flac_streaminfo
[1] = 0;
1251 flac_streaminfo
[2] = 0;
1252 flac_streaminfo
[3] = 34; /* block size */
1253 op
.packet
= flac_streaminfo
;
1255 msg_Err(p_mux
, "Invalid FLAC streaminfo (%ld bytes)",
1262 op
.packetno
= p_stream
->i_packet_no
++;
1263 ogg_stream_packetin( &p_stream
->os
, &op
);
1264 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1265 block_ChainAppend( &p_hdr
, p_og
);
1269 if ( p_sys
->skeleton
.b_create
)
1271 msg_Dbg( p_mux
, "ending skeleton" );
1277 op
.packetno
= p_sys
->skeleton
.i_packet_no
++;
1278 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1279 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1280 block_ChainAppend( &p_hdr
, p_og
);
1283 /* set HEADER flag */
1284 /* flag headers to be resent for streaming clients */
1285 for( p_og
= p_hdr
; p_og
!= NULL
; p_og
= p_og
->p_next
)
1287 p_og
->i_flags
|= BLOCK_FLAG_HEADER
;
1290 /* Write previous headers */
1291 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_hdr
);
1296 static void OggCreateStreamFooter( sout_mux_t
*p_mux
, ogg_stream_t
*p_stream
)
1300 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1302 /* as stream is finished, overwrite the index, if there was any */
1303 if ( p_sys
->skeleton
.b_create
&& p_stream
->skeleton
.p_index
1304 && p_stream
->skeleton
.i_index_payload
)
1306 sout_AccessOutSeek( p_mux
->p_access
, p_stream
->skeleton
.i_index_offset
);
1307 OggGetSkeletonIndex( &op
.packet
, &op
.bytes
, p_stream
);
1308 if ( op
.packet
!= NULL
)
1310 msg_Dbg(p_mux
, "Rewriting index at %"PRId64
, p_stream
->skeleton
.i_index_offset
);
1311 ogg_stream_reset_serialno( &p_sys
->skeleton
.os
, p_sys
->skeleton
.i_serial_no
);
1315 op
.packetno
= p_stream
->skeleton
.i_index_packetno
+ 1;
1316 /* fake our stream state */
1317 p_sys
->skeleton
.os
.pageno
= p_stream
->skeleton
.i_index_pageno
;
1318 p_sys
->skeleton
.os
.packetno
= p_stream
->skeleton
.i_index_packetno
;
1319 p_sys
->skeleton
.os
.granulepos
= 0;
1320 p_sys
->skeleton
.os
.b_o_s
= 1;
1321 p_sys
->skeleton
.os
.e_o_s
= 0;
1322 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1323 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1324 sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1326 sout_AccessOutSeek( p_mux
->p_access
, p_sys
->i_pos
);
1329 /* clear skeleton */
1330 p_stream
->skeleton
.b_fisbone_done
= false;
1331 p_stream
->skeleton
.b_index_done
= false;
1332 p_stream
->skeleton
.i_index_offset
= 0;
1333 p_stream
->skeleton
.i_index_payload
= 0;
1334 p_stream
->skeleton
.i_last_keyframe_pos
= 0;
1335 p_stream
->skeleton
.i_last_keyframe_time
= 0;
1336 /* clear accounting */
1337 p_stream
->i_num_frames
= 0;
1338 p_stream
->i_num_keyframes
= 0;
1340 /* Write eos packet for stream. */
1345 op
.granulepos
= p_stream
->u_last_granulepos
;
1346 op
.packetno
= p_stream
->i_packet_no
++;
1347 ogg_stream_packetin( &p_stream
->os
, &op
);
1349 /* flush it with all remaining data */
1350 if( ( p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 ) ) )
1353 OggSetDate( p_og
, p_stream
->i_dts
, p_stream
->i_length
);
1354 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1357 ogg_stream_clear( &p_stream
->os
);
1360 static void OggSetDate( block_t
*p_og
, mtime_t i_dts
, mtime_t i_length
)
1366 for( p_tmp
= p_og
, i_count
= 0; p_tmp
!= NULL
; p_tmp
= p_tmp
->p_next
)
1371 if( i_count
== 0 ) return; /* ignore. */
1373 i_delta
= i_length
/ i_count
;
1375 for( p_tmp
= p_og
; p_tmp
!= NULL
; p_tmp
= p_tmp
->p_next
)
1377 p_tmp
->i_dts
= i_dts
;
1378 p_tmp
->i_length
= i_delta
;
1384 static void OggRewriteFisheadPage( sout_mux_t
*p_mux
)
1386 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1389 op
.packet
= calloc( 1, op
.bytes
);
1390 if ( op
.packet
!= NULL
)
1396 ogg_stream_reset_serialno( &p_sys
->skeleton
.os
, p_sys
->skeleton
.i_serial_no
);
1397 OggFillSkeletonFishead( op
.packet
, p_mux
);
1398 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1399 msg_Dbg( p_mux
, "rewriting fishead at %"PRId64
, p_mux
->p_sys
->skeleton
.i_fishead_offset
);
1400 sout_AccessOutSeek( p_mux
->p_access
, p_mux
->p_sys
->skeleton
.i_fishead_offset
);
1401 sout_AccessOutWrite( p_mux
->p_access
,
1402 OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 ) );
1403 sout_AccessOutSeek( p_mux
->p_access
, p_mux
->p_sys
->i_pos
);
1407 static bool AllocateIndex( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
1409 ogg_stream_t
*p_stream
= (ogg_stream_t
*) p_input
->p_sys
;
1412 if ( p_stream
->i_length
)
1414 uint64_t i_interval
= (uint64_t)p_mux
->p_sys
->skeleton
.i_index_intvl
* 1000;
1417 if( p_input
->p_fmt
->i_cat
== VIDEO_ES
&&
1418 p_input
->p_fmt
->video
.i_frame_rate
)
1420 /* optimize for fps < 1 */
1421 i_interval
= __MAX( p_mux
->p_sys
->skeleton
.i_index_intvl
* 1000,
1423 p_input
->p_fmt
->video
.i_frame_rate_base
/
1424 p_input
->p_fmt
->video
.i_frame_rate
);
1427 size_t i_tuple_size
= 0;
1428 /* estimate length of pos value */
1429 if ( p_input
->p_fmt
->i_bitrate
)
1431 i
= i_interval
* p_input
->p_fmt
->i_bitrate
/ 1000000;
1432 while ( i
<<= 1 ) i_tuple_size
++;
1436 /* Likely 64KB<<keyframe interval<<16MB */
1437 /* We can't really guess due to muxing */
1438 i_tuple_size
= 24 / 8;
1441 /* add length of interval value */
1443 while ( i
<<= 1 ) i_tuple_size
++;
1445 i_size
= i_tuple_size
* ( p_stream
->i_length
/ i_interval
+ 2 );
1449 i_size
= ( INT64_C(3600) * 11.2 * 1000 / p_mux
->p_sys
->skeleton
.i_index_intvl
)
1450 * p_mux
->p_sys
->skeleton
.i_index_ratio
;
1451 msg_Dbg( p_mux
, "No stream length, using default allocation for index" );
1453 i_size
*= ( 8.0 / 7 ); /* 7bits encoding overhead */
1454 msg_Dbg( p_mux
, "allocating %zu bytes for index", i_size
);
1455 p_stream
->skeleton
.p_index
= calloc( i_size
, sizeof(uint8_t) );
1456 if ( !p_stream
->skeleton
.p_index
) return false;
1457 p_stream
->skeleton
.i_index_size
= i_size
;
1458 p_stream
->skeleton
.i_index_payload
= 0;
1462 /*****************************************************************************
1463 * Mux: multiplex available data in input fifos into the Ogg bitstream
1464 *****************************************************************************/
1465 static int Mux( sout_mux_t
*p_mux
)
1467 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1470 /* End any stream that ends in that group */
1471 if ( p_sys
->i_del_streams
)
1473 /* Remove deleted logical streams */
1474 for( int i
= 0; i
< p_sys
->i_del_streams
; i
++ )
1476 OggCreateStreamFooter( p_mux
, p_sys
->pp_del_streams
[i
] );
1477 FREENULL( p_sys
->pp_del_streams
[i
]->p_oggds_header
);
1478 FREENULL( p_sys
->pp_del_streams
[i
] );
1480 FREENULL( p_sys
->pp_del_streams
);
1481 p_sys
->i_del_streams
= 0;
1484 if ( p_sys
->i_streams
== 0 )
1486 /* All streams have been deleted, or none have ever been created
1487 From this point, we are allowed to start a new group of logical streams */
1488 p_sys
->skeleton
.b_head_done
= false;
1489 p_sys
->b_can_add_streams
= true;
1490 p_sys
->i_segment_start
= p_sys
->i_pos
;
1493 if ( p_sys
->i_add_streams
)
1495 if ( !p_sys
->b_can_add_streams
)
1497 msg_Warn( p_mux
, "Can't add new stream %d/%d: Considerer increasing sout-mux-caching variable", p_sys
->i_del_streams
, p_mux
->p_sys
->i_streams
);
1498 msg_Warn( p_mux
, "Resetting and setting new identity to current streams");
1500 /* resetting all active streams */
1501 for ( int i
=0; i
< p_mux
->p_sys
->i_streams
; i
++ )
1503 ogg_stream_t
* p_stream
= (ogg_stream_t
*) p_mux
->pp_inputs
[i
]->p_sys
;
1504 if ( p_stream
->b_finished
|| !p_stream
->b_started
) continue;
1505 OggCreateStreamFooter( p_mux
, p_stream
);
1506 p_stream
->i_serial_no
= p_sys
->i_next_serial_no
++;
1507 p_stream
->i_packet_no
= 0;
1508 p_stream
->b_finished
= true;
1511 /* rewrite fishead with final values */
1512 if ( p_sys
->skeleton
.b_head_done
)
1514 OggRewriteFisheadPage( p_mux
);
1517 p_sys
->b_can_add_streams
= true;
1518 p_sys
->skeleton
.b_head_done
= false;
1519 p_sys
->i_segment_start
= p_sys
->i_pos
;
1522 /* Open new ogg stream */
1523 if( sout_MuxGetStream( p_mux
, 1, &i_dts
) < 0 )
1525 msg_Dbg( p_mux
, "waiting for data..." );
1528 msg_Dbg( p_mux
, "writing streams headers" );
1529 p_sys
->i_start_dts
= i_dts
;
1530 p_sys
->i_streams
= p_mux
->i_nb_inputs
;
1531 p_sys
->i_del_streams
= 0;
1532 p_sys
->i_add_streams
= 0;
1533 p_sys
->skeleton
.b_create
= true;
1535 if ( ! OggCreateHeaders( p_mux
) )
1538 /* If we're switching to end of headers, then that's data start */
1539 if ( p_sys
->b_can_add_streams
)
1541 msg_Dbg( p_mux
, "data starts from %zu", p_sys
->i_pos
);
1542 p_sys
->i_data_start
= p_sys
->i_pos
;
1545 /* Since we started sending secondaryheader or data pages,
1546 * we're no longer allowed to create new streams, until all streams end */
1547 p_sys
->b_can_add_streams
= false;
1550 /* Do the regular data mux thing */
1553 int i_stream
= sout_MuxGetStream( p_mux
, 1, NULL
);
1556 MuxBlock( p_mux
, p_mux
->pp_inputs
[i_stream
] );
1562 static int MuxBlock( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
1564 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1565 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1566 block_t
*p_data
= block_FifoGet( p_input
->p_fifo
);
1567 block_t
*p_og
= NULL
;
1571 if( p_stream
->i_fourcc
!= VLC_CODEC_VORBIS
&&
1572 p_stream
->i_fourcc
!= VLC_CODEC_FLAC
&&
1573 p_stream
->i_fourcc
!= VLC_CODEC_SPEEX
&&
1574 p_stream
->i_fourcc
!= VLC_CODEC_OPUS
&&
1575 p_stream
->i_fourcc
!= VLC_CODEC_THEORA
&&
1576 p_stream
->i_fourcc
!= VLC_CODEC_VP8
&&
1577 p_stream
->i_fourcc
!= VLC_CODEC_DIRAC
)
1579 p_data
= block_Realloc( p_data
, 1, p_data
->i_buffer
);
1580 p_data
->p_buffer
[0] = PACKET_IS_SYNCPOINT
; // FIXME
1583 if ( p_stream
->i_fourcc
== VLC_CODEC_DIRAC
&& p_stream
->i_baseptsdelay
< 0 )
1584 p_stream
->i_baseptsdelay
= p_data
->i_pts
- p_data
->i_dts
;
1586 op
.packet
= p_data
->p_buffer
;
1587 op
.bytes
= p_data
->i_buffer
;
1590 op
.packetno
= p_stream
->i_packet_no
++;
1593 if( p_stream
->i_cat
== AUDIO_ES
)
1595 if( p_stream
->i_fourcc
== VLC_CODEC_VORBIS
||
1596 p_stream
->i_fourcc
== VLC_CODEC_FLAC
||
1597 p_stream
->i_fourcc
== VLC_CODEC_OPUS
||
1598 p_stream
->i_fourcc
== VLC_CODEC_SPEEX
)
1600 /* number of sample from begining + current packet */
1602 ( p_data
->i_dts
- p_sys
->i_start_dts
+ p_data
->i_length
) *
1603 (mtime_t
)p_input
->p_fmt
->audio
.i_rate
/ CLOCK_FREQ
;
1605 i_time
= p_data
->i_dts
- p_sys
->i_start_dts
;
1606 AddIndexEntry( p_mux
, i_time
, p_input
);
1608 else if( p_stream
->p_oggds_header
)
1610 /* number of sample from begining */
1611 op
.granulepos
= ( p_data
->i_dts
- p_sys
->i_start_dts
) *
1612 p_stream
->p_oggds_header
->i_samples_per_unit
/ CLOCK_FREQ
;
1615 else if( p_stream
->i_cat
== VIDEO_ES
)
1617 if( p_stream
->i_fourcc
== VLC_CODEC_THEORA
)
1619 p_stream
->i_num_frames
++;
1620 if( p_data
->i_flags
& BLOCK_FLAG_TYPE_I
)
1622 p_stream
->i_num_keyframes
++;
1623 p_stream
->i_last_keyframe
= p_stream
->i_num_frames
;
1625 /* presentation time */
1626 i_time
= CLOCK_FREQ
* ( p_stream
->i_num_frames
- 1 ) *
1627 p_input
->p_fmt
->video
.i_frame_rate_base
/ p_input
->p_fmt
->video
.i_frame_rate
;
1628 AddIndexEntry( p_mux
, i_time
, p_input
);
1631 op
.granulepos
= (p_stream
->i_last_keyframe
<< p_stream
->i_keyframe_granule_shift
)
1632 | (p_stream
->i_num_frames
-p_stream
->i_last_keyframe
);
1634 else if( p_stream
->i_fourcc
== VLC_CODEC_DIRAC
)
1637 #define FRAME_ROUND(a) \
1638 if ( ( a + 5000 / CLOCK_FREQ ) > ( a / CLOCK_FREQ ) )\
1642 mtime_t dt
= (p_data
->i_dts
- p_sys
->i_start_dts
) * p_input
->p_fmt
->video
.i_frame_rate
/ p_input
->p_fmt
->video
.i_frame_rate_base
;
1645 mtime_t pt
= (p_data
->i_pts
- p_sys
->i_start_dts
- p_stream
->i_baseptsdelay
) * p_input
->p_fmt
->video
.i_frame_rate
/ p_input
->p_fmt
->video
.i_frame_rate_base
;
1648 /* (shro) some PTS could be repeated within 1st frames */
1649 if ( pt
== p_stream
->i_dirac_last_pt
)
1652 p_stream
->i_dirac_last_pt
= pt
;
1654 /* (shro) some DTS could be repeated within 1st frames */
1655 if ( dt
== p_stream
->i_dirac_last_dt
)
1658 p_stream
->i_dirac_last_dt
= dt
;
1660 if( p_data
->i_flags
& BLOCK_FLAG_TYPE_I
)
1661 p_stream
->i_last_keyframe
= dt
;
1662 mtime_t dist
= dt
- p_stream
->i_last_keyframe
;
1664 /* Everything increments by two for progressive */
1671 mtime_t delay
= pt
- dt
;
1672 if ( delay
< 0 ) delay
*= -1;
1674 op
.granulepos
= (pt
- delay
) << 31 | (dist
&0xff00) << 14
1675 | (delay
&0x1fff) << 9 | (dist
&0xff);
1677 msg_Dbg( p_mux
, "dts %"PRId64
" pts %"PRId64
" dt %"PRId64
" pt %"PRId64
" delay %"PRId64
" granule %"PRId64
,
1678 (p_data
->i_dts
- p_sys
->i_start_dts
),
1679 (p_data
->i_pts
- p_sys
->i_start_dts
),
1680 dt
, pt
, delay
, op
.granulepos
);
1683 AddIndexEntry( p_mux
, dt
, p_input
);
1685 else if( p_stream
->i_fourcc
== VLC_CODEC_VP8
)
1687 p_stream
->i_num_frames
++;
1688 if( p_data
->i_flags
& BLOCK_FLAG_TYPE_I
)
1690 p_stream
->i_num_keyframes
++;
1691 p_stream
->i_last_keyframe
= p_stream
->i_num_frames
;
1693 /* presentation time */
1694 i_time
= CLOCK_FREQ
* ( p_stream
->i_num_frames
- 1 ) *
1695 p_input
->p_fmt
->video
.i_frame_rate_base
/ p_input
->p_fmt
->video
.i_frame_rate
;
1696 AddIndexEntry( p_mux
, i_time
, p_input
);
1698 op
.granulepos
= ( ((int64_t)p_stream
->i_num_frames
) << 32 ) |
1699 ( ( ( p_stream
->i_num_frames
- p_stream
->i_last_keyframe
) & 0x07FFFFFF ) << 3 );
1701 else if( p_stream
->p_oggds_header
)
1702 op
.granulepos
= ( p_data
->i_dts
- p_sys
->i_start_dts
) * INT64_C(10) /
1703 p_stream
->p_oggds_header
->i_time_unit
;
1705 else if( p_stream
->i_cat
== SPU_ES
)
1707 /* granulepos is in millisec */
1708 op
.granulepos
= ( p_data
->i_dts
- p_sys
->i_start_dts
) / 1000;
1711 return VLC_EGENERIC
;
1713 p_stream
->u_last_granulepos
= op
.granulepos
;
1714 ogg_stream_packetin( &p_stream
->os
, &op
);
1716 if( p_stream
->i_cat
== SPU_ES
||
1717 p_stream
->i_fourcc
== VLC_CODEC_SPEEX
||
1718 p_stream
->i_fourcc
== VLC_CODEC_DIRAC
)
1720 /* Subtitles or Speex packets are quite small so they
1721 * need to be flushed to be sent on time */
1722 /* The OggDirac mapping suggests ever so strongly that a
1723 * page flush occurs after each OggDirac packet, so to make
1724 * the timestamps unambiguous */
1725 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, p_data
->i_dts
);
1729 p_og
= OggStreamPageOut( p_mux
, &p_stream
->os
, p_data
->i_dts
);
1734 OggSetDate( p_og
, p_stream
->i_dts
, p_stream
->i_length
);
1735 p_stream
->i_dts
= -1;
1736 p_stream
->i_length
= 0;
1737 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1741 if( p_stream
->i_dts
< 0 )
1743 p_stream
->i_dts
= p_data
->i_dts
;
1745 p_stream
->i_length
+= p_data
->i_length
;
1748 block_Release( p_data
);