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 *****************************************************************************/
146 enum es_format_category_e i_cat
;
147 vlc_fourcc_t i_fourcc
;
155 int i_keyframe_granule_shift
; /* Theora and Daala only */
156 int i_last_keyframe
; /* dirac and theora */
157 int i_num_frames
; /* Theora only */
158 uint64_t u_last_granulepos
; /* Used for correct EOS page */
159 int64_t i_num_keyframes
;
162 oggds_header_t
*p_oggds_header
;
170 /* Skeleton index storage */
171 unsigned char *p_index
;
172 uint64_t i_index_size
;
173 uint64_t i_index_payload
; /* real index size */
174 uint64_t i_index_count
;
175 /* backup values for rewriting index page later */
176 uint64_t i_index_offset
; /* sout offset of the index page */
177 int64_t i_index_packetno
; /* index packet no */
179 /* index creation tracking values */
180 uint64_t i_last_keyframe_pos
;
181 uint64_t i_last_keyframe_time
;
186 mtime_t i_baseptsdelay
;
190 struct sout_mux_sys_t
195 int i_next_serial_no
;
197 /* number of logical streams pending to be added */
199 bool b_can_add_streams
;
201 /* logical streams pending to be deleted */
203 ogg_stream_t
**pp_del_streams
;
213 /* backup values for rewriting fishead page later */
214 uint64_t i_fishead_offset
; /* sout offset of the fishead page */
219 /* access position */
221 ssize_t i_data_start
;
222 ssize_t i_segment_start
;
225 static void OggSetDate( block_t
*, mtime_t
, mtime_t
);
226 static block_t
*OggStreamFlush( sout_mux_t
*, ogg_stream_state
*, mtime_t
);
227 static void OggCreateStreamFooter( sout_mux_t
*p_mux
, ogg_stream_t
*p_stream
);
228 static void OggRewriteFisheadPage( sout_mux_t
*p_mux
);
229 static bool AllocateIndex( sout_mux_t
*p_mux
, sout_input_t
*p_input
);
231 /*****************************************************************************
233 *****************************************************************************/
234 static int Open( vlc_object_t
*p_this
)
236 sout_mux_t
*p_mux
= (sout_mux_t
*)p_this
;
237 sout_mux_sys_t
*p_sys
;
239 msg_Info( p_mux
, "Open" );
241 p_sys
= malloc( sizeof( sout_mux_sys_t
) );
244 p_sys
->i_streams
= 0;
245 p_sys
->i_add_streams
= 0;
246 p_sys
->b_can_add_streams
= true;
247 p_sys
->i_del_streams
= 0;
248 p_sys
->pp_del_streams
= 0;
250 p_sys
->skeleton
.b_create
= false;
251 p_sys
->skeleton
.b_head_done
= false;
252 p_sys
->skeleton
.i_index_intvl
=
253 var_InheritInteger( p_this
, SOUT_CFG_PREFIX
"indexintvl" );
254 p_sys
->skeleton
.i_index_ratio
=
255 var_InheritFloat( p_this
, SOUT_CFG_PREFIX
"indexratio" );
256 p_sys
->i_data_start
= 0;
257 p_sys
->i_segment_start
= 0;
258 p_mux
->p_sys
= p_sys
;
259 p_mux
->pf_control
= Control
;
260 p_mux
->pf_addstream
= AddStream
;
261 p_mux
->pf_delstream
= DelStream
;
264 /* First serial number is random.
265 * (Done like this because on win32 you need to seed the random number
266 * generator once per thread). */
268 vlc_rand_bytes(&r
, sizeof(r
));
269 p_sys
->i_next_serial_no
= r
& INT_MAX
;
274 /*****************************************************************************
275 * Close: Finalize ogg bitstream and close muxer
276 *****************************************************************************/
277 static void Close( vlc_object_t
* p_this
)
279 sout_mux_t
*p_mux
= (sout_mux_t
*)p_this
;
280 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
281 ogg_stream_t
*p_stream
;
283 msg_Info( p_mux
, "Close" );
285 if( p_sys
->i_del_streams
)
287 /* Close the current ogg stream */
288 msg_Dbg( p_mux
, "writing footers" );
290 for(int i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
292 p_stream
= (ogg_stream_t
*) p_mux
->pp_inputs
[i
]->p_sys
;
293 OggCreateStreamFooter( p_mux
, p_stream
);
294 free( p_stream
->skeleton
.p_index
);
297 /* Remove deleted logical streams */
298 for(int i
= 0; i
< p_sys
->i_del_streams
; i
++ )
300 OggCreateStreamFooter( p_mux
, p_sys
->pp_del_streams
[i
] );
301 free( p_sys
->pp_del_streams
[i
]->p_oggds_header
);
302 free( p_sys
->pp_del_streams
[i
]->skeleton
.p_index
);
303 free( p_sys
->pp_del_streams
[i
] );
305 free( p_sys
->pp_del_streams
);
306 p_sys
->i_streams
-= p_sys
->i_del_streams
;
309 /* rewrite fishead with final values */
310 if ( p_sys
->skeleton
.b_create
&& p_sys
->skeleton
.b_head_done
)
312 OggRewriteFisheadPage( p_mux
);
318 /*****************************************************************************
320 *****************************************************************************/
321 static int Control( sout_mux_t
*p_mux
, int i_query
, va_list args
)
329 case MUX_CAN_ADD_STREAM_WHILE_MUXING
:
330 pb_bool
= va_arg( args
, bool * );
334 case MUX_GET_ADD_STREAM_WAIT
:
335 pb_bool
= va_arg( args
, bool * );
340 ppsz
= 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
)
381 unsigned int i_frame_rate
= p_input
->p_fmt
->video
.i_frame_rate
;
382 unsigned int i_frame_rate_base
= p_input
->p_fmt
->video
.i_frame_rate_base
;
383 if( !p_input
->p_fmt
->video
.i_frame_rate
||
384 !p_input
->p_fmt
->video
.i_frame_rate_base
)
386 msg_Warn( p_mux
, "Missing frame rate, assuming 25fps" );
388 i_frame_rate_base
= 1;
391 switch( p_stream
->i_fourcc
)
402 p_stream
->p_oggds_header
= calloc( 1, sizeof(oggds_header_t
) );
403 if( !p_stream
->p_oggds_header
)
408 p_stream
->p_oggds_header
->i_packet_type
= PACKET_TYPE_HEADER
;
410 memcpy( p_stream
->p_oggds_header
->stream_type
, "video", 5 );
411 if( p_stream
->i_fourcc
== VLC_CODEC_MP4V
)
413 memcpy( p_stream
->p_oggds_header
->sub_type
, "XVID", 4 );
415 else if( p_stream
->i_fourcc
== VLC_CODEC_DIV3
)
417 memcpy( p_stream
->p_oggds_header
->sub_type
, "DIV3", 4 );
421 memcpy( p_stream
->p_oggds_header
->sub_type
,
422 &p_stream
->i_fourcc
, 4 );
424 p_stream
->p_oggds_header
->i_size
= 0 ;
425 p_stream
->p_oggds_header
->i_time_unit
=
426 INT64_C(10000000) * i_frame_rate_base
/
427 (int64_t)i_frame_rate
;
428 p_stream
->p_oggds_header
->i_samples_per_unit
= 1;
429 p_stream
->p_oggds_header
->i_default_len
= 1 ; /* ??? */
430 p_stream
->p_oggds_header
->i_buffer_size
= 1024*1024;
431 p_stream
->p_oggds_header
->i_bits_per_sample
= 0;
432 p_stream
->p_oggds_header
->header
.video
.i_width
= p_input
->p_fmt
->video
.i_width
;
433 p_stream
->p_oggds_header
->header
.video
.i_height
= p_input
->p_fmt
->video
.i_height
;
434 msg_Dbg( p_mux
, "%4.4s stream", (char *)&p_stream
->i_fourcc
);
437 case VLC_CODEC_DIRAC
:
438 msg_Dbg( p_mux
, "dirac stream" );
441 case VLC_CODEC_THEORA
:
442 msg_Dbg( p_mux
, "theora stream" );
445 case VLC_CODEC_DAALA
:
446 msg_Dbg( p_mux
, "daala stream" );
450 msg_Dbg( p_mux
, "VP8 stream" );
454 FREENULL( p_input
->p_sys
);
461 switch( p_stream
->i_fourcc
)
464 msg_Dbg( p_mux
, "opus stream" );
467 case VLC_CODEC_VORBIS
:
468 msg_Dbg( p_mux
, "vorbis stream" );
471 case VLC_CODEC_SPEEX
:
472 msg_Dbg( p_mux
, "speex stream" );
476 msg_Dbg( p_mux
, "flac stream" );
480 fourcc_to_wf_tag( p_stream
->i_fourcc
, &i_tag
);
481 if( i_tag
== WAVE_FORMAT_UNKNOWN
)
483 FREENULL( p_input
->p_sys
);
487 p_stream
->p_oggds_header
=
488 malloc( sizeof(oggds_header_t
) + p_input
->p_fmt
->i_extra
);
489 if( !p_stream
->p_oggds_header
)
494 memset( p_stream
->p_oggds_header
, 0, sizeof(oggds_header_t
) );
495 p_stream
->p_oggds_header
->i_packet_type
= PACKET_TYPE_HEADER
;
497 p_stream
->p_oggds_header
->i_size
= p_input
->p_fmt
->i_extra
;
499 if( p_input
->p_fmt
->i_extra
)
501 memcpy( &p_stream
->p_oggds_header
[1],
502 p_input
->p_fmt
->p_extra
, p_input
->p_fmt
->i_extra
);
505 memcpy( p_stream
->p_oggds_header
->stream_type
, "audio", 5 );
507 memset( p_stream
->p_oggds_header
->sub_type
, 0, 4 );
509 snprintf( buf
, sizeof(buf
), "%"PRIx16
, i_tag
);
510 strncpy( p_stream
->p_oggds_header
->sub_type
, buf
, 4 );
512 p_stream
->p_oggds_header
->i_time_unit
= INT64_C(10000000);
513 p_stream
->p_oggds_header
->i_default_len
= 1;
514 p_stream
->p_oggds_header
->i_buffer_size
= 30*1024 ;
515 p_stream
->p_oggds_header
->i_samples_per_unit
= p_input
->p_fmt
->audio
.i_rate
;
516 p_stream
->p_oggds_header
->i_bits_per_sample
= p_input
->p_fmt
->audio
.i_bitspersample
;
517 p_stream
->p_oggds_header
->header
.audio
.i_channels
= p_input
->p_fmt
->audio
.i_channels
;
518 p_stream
->p_oggds_header
->header
.audio
.i_block_align
= p_input
->p_fmt
->audio
.i_blockalign
;
519 p_stream
->p_oggds_header
->header
.audio
.i_avgbytespersec
= p_input
->p_fmt
->i_bitrate
/ 8;
520 msg_Dbg( p_mux
, "%4.4s stream", (char *)&p_stream
->i_fourcc
);
526 switch( p_stream
->i_fourcc
)
529 p_stream
->p_oggds_header
= calloc( 1, sizeof(oggds_header_t
) );
530 if( !p_stream
->p_oggds_header
)
535 p_stream
->p_oggds_header
->i_packet_type
= PACKET_TYPE_HEADER
;
537 memcpy( p_stream
->p_oggds_header
->stream_type
, "text", 4 );
538 msg_Dbg( p_mux
, "subtitles stream" );
542 FREENULL( p_input
->p_sys
);
547 FREENULL( p_input
->p_sys
);
551 p_stream
->b_new
= true;
553 p_sys
->i_add_streams
++;
558 /*****************************************************************************
559 * DelStream: Delete an elementary stream from the muxed stream
560 *****************************************************************************/
561 static void DelStream( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
563 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
564 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
567 msg_Dbg( p_mux
, "removing input" );
569 /* flush all remaining data */
572 if( !p_stream
->b_new
)
574 while( block_FifoCount( p_input
->p_fifo
) )
575 MuxBlock( p_mux
, p_input
);
578 if( !p_stream
->b_new
&&
579 ( p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 ) ) )
581 OggSetDate( p_og
, p_stream
->i_dts
, p_stream
->i_length
);
582 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
585 /* move input in delete queue */
586 if( !p_stream
->b_new
)
588 p_sys
->pp_del_streams
= xrealloc( p_sys
->pp_del_streams
,
589 (p_sys
->i_del_streams
+ 1) * sizeof(ogg_stream_t
*) );
590 p_sys
->pp_del_streams
[p_sys
->i_del_streams
++] = p_stream
;
594 /* wasn't already added so get rid of it */
595 FREENULL( p_stream
->p_oggds_header
);
596 FREENULL( p_stream
);
597 p_sys
->i_add_streams
--;
601 p_input
->p_sys
= NULL
;
604 /*****************************************************************************
605 * Ogg Skeleton helpers
606 *****************************************************************************/
607 static int WriteQWVariableLE( uint64_t i_64
, uint64_t i_offset
,
608 uint8_t *p_buffer
, int i_buffer_size
)
610 uint8_t *p_dest
= p_buffer
+ i_offset
;
615 if ( p_dest
- p_buffer
>= i_buffer_size
) return -1;
617 *p_dest
= (uint8_t) ( i_64
& 0x7F );
631 static bool AddIndexEntry( sout_mux_t
*p_mux
, uint64_t i_time
, sout_input_t
*p_input
)
633 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
634 ogg_stream_t
*p_stream
= (ogg_stream_t
*) p_input
->p_sys
;
636 uint64_t i_timedelta
;
637 if ( !p_sys
->skeleton
.b_create
|| p_mux
->p_sys
->skeleton
.i_index_intvl
== 0
638 || !p_stream
->skeleton
.p_index
)
641 if ( p_stream
->skeleton
.i_last_keyframe_pos
== 0 )
642 p_stream
->skeleton
.i_last_keyframe_pos
= p_sys
->i_segment_start
;
643 i_posdelta
= p_sys
->i_pos
- p_stream
->skeleton
.i_last_keyframe_pos
;
644 i_timedelta
= i_time
- p_stream
->skeleton
.i_last_keyframe_time
;
646 if ( i_timedelta
<= ( (uint64_t) p_mux
->p_sys
->skeleton
.i_index_intvl
* 1000 )
647 || i_posdelta
<= 0xFFFF )
652 if ( !p_stream
->skeleton
.p_index
) return false;
653 uint64_t i_offset
= p_stream
->skeleton
.i_index_payload
;
654 i_ret
= WriteQWVariableLE( i_posdelta
, i_offset
, p_stream
->skeleton
.p_index
,
655 p_stream
->skeleton
.i_index_size
);
656 if ( i_ret
== -1 ) return false;
658 i_ret
= WriteQWVariableLE( i_timedelta
, i_offset
, p_stream
->skeleton
.p_index
,
659 p_stream
->skeleton
.i_index_size
);
660 if ( i_ret
== -1 ) return false;
661 p_stream
->skeleton
.i_index_payload
= i_offset
+ i_ret
;
662 p_stream
->skeleton
.i_index_count
++;
664 /* update diff points */
665 p_stream
->skeleton
.i_last_keyframe_pos
= p_sys
->i_pos
;
666 p_stream
->skeleton
.i_last_keyframe_time
= i_time
;
667 msg_Dbg( p_mux
, "Added index on stream %d entry %zd %"PRIu64
,
668 p_stream
->i_serial_no
, p_sys
->i_pos
- p_sys
->i_segment_start
, i_time
);
673 /*****************************************************************************
674 * Ogg bitstream manipulation routines
675 *****************************************************************************/
676 static block_t
*OggStreamGetPage( sout_mux_t
*p_mux
,
677 ogg_stream_state
*p_os
, mtime_t i_pts
,
681 block_t
*p_og
, *p_og_first
= NULL
;
683 int (*pager
)( ogg_stream_state
*, ogg_page
* ) = flush
? ogg_stream_flush
: ogg_stream_pageout
;
685 while( pager( p_os
, &og
) )
688 p_og
= block_Alloc( og
.header_len
+ og
.body_len
);
690 memcpy( p_og
->p_buffer
, og
.header
, og
.header_len
);
691 memcpy( p_og
->p_buffer
+ og
.header_len
, og
.body
, og
.body_len
);
696 i_pts
= 0; // write it only once
698 block_ChainAppend( &p_og_first
, p_og
);
704 static block_t
*OggStreamFlush( sout_mux_t
*p_mux
,
705 ogg_stream_state
*p_os
, mtime_t i_pts
)
707 return OggStreamGetPage( p_mux
, p_os
, i_pts
, true );
710 static block_t
*OggStreamPageOut( sout_mux_t
*p_mux
,
711 ogg_stream_state
*p_os
, mtime_t i_pts
)
713 return OggStreamGetPage( p_mux
, p_os
, i_pts
, false );
716 static void OggGetSkeletonIndex( uint8_t **pp_buffer
, long *pi_size
, ogg_stream_t
*p_stream
)
718 uint8_t *p_buffer
= calloc( INDEX_BASE_SIZE
+ p_stream
->skeleton
.i_index_size
, sizeof(uint8_t) );
719 if ( !p_buffer
) return;
720 *pp_buffer
= p_buffer
;
722 memcpy( p_buffer
, "index", 6 );
723 SetDWLE( &p_buffer
[6], p_stream
->i_serial_no
);
724 SetQWLE( &p_buffer
[10], p_stream
->skeleton
.i_index_count
); /* num keypoints */
725 SetQWLE( &p_buffer
[18], 1000000 );
726 SetQWLE( &p_buffer
[34], p_stream
->i_length
);
727 memcpy( p_buffer
+ INDEX_BASE_SIZE
, p_stream
->skeleton
.p_index
, p_stream
->skeleton
.i_index_payload
);
728 *pi_size
= INDEX_BASE_SIZE
+ p_stream
->skeleton
.i_index_size
;
731 static void OggGetSkeletonFisbone( uint8_t **pp_buffer
, long *pi_size
,
732 sout_input_t
*p_input
, sout_mux_t
*p_mux
)
736 const char *psz_value
= NULL
;
737 ogg_stream_t
*p_stream
= (ogg_stream_t
*) p_input
->p_sys
;
740 char *psz_content_type
;
743 unsigned int i_count
;
744 } headers
= { NULL
, NULL
, 0, 0 };
747 switch( p_stream
->i_fourcc
)
749 case VLC_CODEC_VORBIS
:
750 psz_value
= "audio/vorbis";
752 case VLC_CODEC_THEORA
:
753 psz_value
= "video/theora";
755 case VLC_CODEC_DAALA
:
756 psz_value
= "video/daala";
758 case VLC_CODEC_SPEEX
:
759 psz_value
= "audio/speex";
762 psz_value
= "audio/flac";
765 psz_value
= "text/cmml";
768 psz_value
= "application/kate";
771 psz_value
= "video/x-vp8";
774 psz_value
= "application/octet-stream";
775 msg_Warn( p_mux
, "Unknown fourcc for stream %s, setting Content-Type to %s",
776 vlc_fourcc_GetDescription( p_stream
->i_cat
, p_stream
->i_fourcc
),
780 /* Content Type Header */
781 if ( asprintf( &headers
.psz_content_type
, "Content-Type: %s\r\n", psz_value
) != -1 )
783 headers
.i_size
+= strlen( headers
.psz_content_type
);
787 /* Set Role Header */
788 if ( p_input
->p_fmt
->i_priority
> ES_PRIORITY_NOT_SELECTABLE
)
790 int i_max_prio
= ES_PRIORITY_MIN
;
791 for ( int i
=0; i
< p_mux
->i_nb_inputs
; i
++ )
793 if ( p_mux
->pp_inputs
[i
]->p_fmt
->i_cat
!= p_input
->p_fmt
->i_cat
) continue;
794 i_max_prio
= __MAX( p_mux
->pp_inputs
[i
]->p_fmt
->i_priority
, i_max_prio
);
798 if ( p_input
->p_fmt
->i_cat
== AUDIO_ES
|| p_input
->p_fmt
->i_cat
== VIDEO_ES
)
800 if ( p_input
->p_fmt
->i_priority
== i_max_prio
&& i_max_prio
>= ES_PRIORITY_SELECTABLE_MIN
)
801 psz_value
= ( p_input
->p_fmt
->i_cat
== VIDEO_ES
) ?
802 "video/main" : "audio/main";
804 psz_value
= ( p_input
->p_fmt
->i_cat
== VIDEO_ES
) ?
805 "video/alternate" : "audio/alternate";
807 else if ( p_input
->p_fmt
->i_cat
== SPU_ES
)
809 psz_value
= ( p_input
->p_fmt
->i_codec
== VLC_CODEC_KATE
) ?
810 "text/karaoke" : "text/subtitle";
813 if ( psz_value
&& asprintf( &headers
.psz_role
, "Role: %s\r\n", psz_value
) != -1 )
815 headers
.i_size
+= strlen( headers
.psz_role
);
820 *pp_buffer
= calloc( FISBONE_BASE_SIZE
+ headers
.i_size
, sizeof(uint8_t) );
821 if ( !*pp_buffer
) return;
822 p_buffer
= *pp_buffer
;
824 memcpy( p_buffer
, "fisbone", 8 );
825 SetDWLE( &p_buffer
[8], FISBONE_BASE_OFFSET
); /* offset to message headers */
826 SetDWLE( &p_buffer
[12], p_stream
->i_serial_no
);
827 SetDWLE( &p_buffer
[16], headers
.i_count
);
829 /* granulerate den */
830 switch ( p_input
->p_fmt
->i_cat
)
833 SetQWLE( &(*pp_buffer
)[20], p_input
->p_fmt
->video
.i_frame_rate
);
834 SetQWLE( &(*pp_buffer
)[28], p_input
->p_fmt
->video
.i_frame_rate_base
);
837 SetQWLE( &(*pp_buffer
)[20], p_input
->p_fmt
->audio
.i_rate
);
838 SetQWLE( &(*pp_buffer
)[28], 1 );
841 SetQWLE( &(*pp_buffer
)[20], 1000 );
842 SetQWLE( &(*pp_buffer
)[28], 1 );
846 if ( p_input
->p_fmt
->p_extra
)
847 SetDWLE( &(*pp_buffer
)[44], xiph_CountHeaders( p_input
->p_fmt
->p_extra
, p_input
->p_fmt
->i_extra
) );
849 if ( headers
.i_size
> 0 )
851 psz_header
= *pp_buffer
+ FISBONE_BASE_SIZE
;
852 memcpy( psz_header
, headers
.psz_content_type
, strlen( headers
.psz_content_type
) );
853 psz_header
+= strlen( headers
.psz_content_type
);
854 if ( headers
.psz_role
)
855 memcpy( psz_header
, headers
.psz_role
, strlen( headers
.psz_role
) );
857 *pi_size
= FISBONE_BASE_SIZE
+ headers
.i_size
;
859 free( headers
.psz_content_type
);
860 free( headers
.psz_role
);
863 static void OggFillSkeletonFishead( uint8_t *p_buffer
, sout_mux_t
*p_mux
)
865 memcpy( p_buffer
, "fishead", 8 );
866 SetWLE( &p_buffer
[8], 4 );
867 SetWLE( &p_buffer
[10], 0 );
868 SetQWLE( &p_buffer
[20], 1000 );
869 SetQWLE( &p_buffer
[36], 1000 );
870 SetQWLE( &p_buffer
[64], p_mux
->p_sys
->i_pos
- p_mux
->p_sys
->i_segment_start
); /* segment length */
871 SetQWLE( &p_buffer
[72], p_mux
->p_sys
->i_data_start
- p_mux
->p_sys
->i_segment_start
); /* data start offset */
874 static int32_t OggFillDsHeader( uint8_t *p_buffer
, oggds_header_t
*p_oggds_header
, int i_cat
)
877 p_buffer
[index
] = p_oggds_header
->i_packet_type
;
879 memcpy( &p_buffer
[index
], p_oggds_header
->stream_type
, sizeof(p_oggds_header
->stream_type
) );
880 index
+= sizeof(p_oggds_header
->stream_type
);
881 memcpy(&p_buffer
[index
], p_oggds_header
->sub_type
, sizeof(p_oggds_header
->sub_type
) );
882 index
+= sizeof(p_oggds_header
->sub_type
);
884 /* The size is filled at the end */
885 uint8_t *p_isize
= &p_buffer
[index
];
888 SetQWLE( &p_buffer
[index
], p_oggds_header
->i_time_unit
);
890 SetQWLE( &p_buffer
[index
], p_oggds_header
->i_samples_per_unit
);
892 SetDWLE( &p_buffer
[index
], p_oggds_header
->i_default_len
);
894 SetDWLE( &p_buffer
[index
], p_oggds_header
->i_buffer_size
);
896 SetWLE( &p_buffer
[index
], p_oggds_header
->i_bits_per_sample
);
898 SetWLE( &p_buffer
[index
], p_oggds_header
->i_padding_0
);
904 SetDWLE( &p_buffer
[index
], p_oggds_header
->header
.video
.i_width
);
905 SetDWLE( &p_buffer
[index
+4], p_oggds_header
->header
.video
.i_height
);
908 SetWLE( &p_buffer
[index
], p_oggds_header
->header
.audio
.i_channels
);
909 SetWLE( &p_buffer
[index
+2], p_oggds_header
->header
.audio
.i_block_align
);
910 SetDWLE( &p_buffer
[index
+4], p_oggds_header
->header
.audio
.i_avgbytespersec
);
914 SetDWLE( &p_buffer
[index
], p_oggds_header
->i_padding_1
);
918 if( p_oggds_header
->i_size
> 0 )
920 memcpy( &p_buffer
[index
], (uint8_t *) p_oggds_header
+ sizeof(*p_oggds_header
), p_oggds_header
->i_size
);
921 index
+= p_oggds_header
->i_size
;
924 SetDWLE( p_isize
, index
-1 );
928 static void OggFillVP8Header( uint8_t *p_buffer
, sout_input_t
*p_input
)
930 memcpy( p_buffer
, "OVP80\x01\x01\x00", 8 );
931 SetWBE( &p_buffer
[8], p_input
->p_fmt
->video
.i_width
);
932 SetDWBE( &p_buffer
[14], p_input
->p_fmt
->video
.i_sar_den
);/* 24 bits, 15~ */
933 SetDWBE( &p_buffer
[11], p_input
->p_fmt
->video
.i_sar_num
);/* 24 bits, 12~ */
934 SetWBE( &p_buffer
[10], p_input
->p_fmt
->video
.i_height
);
935 SetDWBE( &p_buffer
[18], p_input
->p_fmt
->video
.i_frame_rate
);
936 SetDWBE( &p_buffer
[22], p_input
->p_fmt
->video
.i_frame_rate_base
);
939 static bool OggCreateHeaders( sout_mux_t
*p_mux
)
941 block_t
*p_hdr
= NULL
;
942 block_t
*p_og
= NULL
;
944 ogg_stream_t
*p_stream
;
945 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
948 if( sout_AccessOutControl( p_mux
->p_access
,
950 &p_sys
->skeleton
.b_create
) )
952 p_sys
->skeleton
.b_create
= false;
955 p_sys
->skeleton
.b_create
&= !! p_mux
->i_nb_inputs
;
957 /* no skeleton for solo vorbis/speex/opus tracks */
958 if ( p_mux
->i_nb_inputs
== 1 && p_mux
->pp_inputs
[0]->p_fmt
->i_cat
== AUDIO_ES
)
960 p_sys
->skeleton
.b_create
= false;
964 for ( int i
=0; i
< p_mux
->i_nb_inputs
; i
++ )
966 p_stream
= (ogg_stream_t
*) p_mux
->pp_inputs
[i
]->p_sys
;
967 if ( p_stream
->p_oggds_header
)
969 /* We don't want skeleton for OggDS */
970 p_sys
->skeleton
.b_create
= false;
976 /* Skeleton's Fishead must be the first page of the stream */
977 if ( p_sys
->skeleton
.b_create
&& !p_sys
->skeleton
.b_head_done
)
979 msg_Dbg( p_mux
, "creating header for skeleton" );
980 p_sys
->skeleton
.i_serial_no
= p_sys
->i_next_serial_no
++;
981 ogg_stream_init( &p_sys
->skeleton
.os
, p_sys
->skeleton
.i_serial_no
);
983 op
.packet
= calloc( 1, op
.bytes
);
984 if ( op
.packet
== NULL
) return false;
989 OggFillSkeletonFishead( op
.packet
, p_mux
);
990 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
991 ogg_packet_clear( &op
);
992 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
993 block_ChainAppend( &p_hdr
, p_og
);
994 p_sys
->skeleton
.b_head_done
= true;
995 p_sys
->skeleton
.i_fishead_offset
= p_sys
->i_pos
;
998 /* Write header for each stream. All b_o_s (beginning of stream) packets
999 * must appear first in the ogg stream so we take care of them first. */
1000 for( int pass
= 0; pass
< 2; pass
++ )
1002 for( i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1004 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1005 p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1007 bool video
= ( p_stream
->i_fourcc
== VLC_CODEC_THEORA
||
1008 p_stream
->i_fourcc
== VLC_CODEC_DIRAC
||
1009 p_stream
->i_fourcc
== VLC_CODEC_DAALA
);
1010 if( ( ( pass
== 0 && !video
) || ( pass
== 1 && video
) ) )
1013 msg_Dbg( p_mux
, "creating header for %4.4s",
1014 (char *)&p_stream
->i_fourcc
);
1016 ogg_stream_init( &p_stream
->os
, p_stream
->i_serial_no
);
1017 p_stream
->b_new
= false;
1018 p_stream
->i_packet_no
= 0;
1019 p_stream
->b_started
= true;
1021 if( p_stream
->i_fourcc
== VLC_CODEC_VORBIS
||
1022 p_stream
->i_fourcc
== VLC_CODEC_SPEEX
||
1023 p_stream
->i_fourcc
== VLC_CODEC_OPUS
||
1024 p_stream
->i_fourcc
== VLC_CODEC_THEORA
||
1025 p_stream
->i_fourcc
== VLC_CODEC_DAALA
)
1027 /* First packet in order: vorbis/speex/opus/theora/daala info */
1028 unsigned pi_size
[XIPH_MAX_HEADER_COUNT
];
1029 void *pp_data
[XIPH_MAX_HEADER_COUNT
];
1031 if( xiph_SplitHeaders( pi_size
, pp_data
, &i_count
,
1032 p_input
->p_fmt
->i_extra
, p_input
->p_fmt
->p_extra
) )
1039 op
.bytes
= pi_size
[0];
1040 op
.packet
= pp_data
[0];
1041 if( pi_size
[0] <= 0 )
1042 msg_Err( p_mux
, "header data corrupted");
1047 op
.packetno
= p_stream
->i_packet_no
++;
1048 ogg_stream_packetin( &p_stream
->os
, &op
);
1049 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1051 /* Get keyframe_granule_shift for theora or daala granulepos calculation */
1052 if( p_stream
->i_fourcc
== VLC_CODEC_THEORA
||
1053 p_stream
->i_fourcc
== VLC_CODEC_DAALA
)
1055 p_stream
->i_keyframe_granule_shift
=
1056 ( (op
.packet
[40] & 0x03) << 3 ) | ( (op
.packet
[41] & 0xe0) >> 5 );
1059 else if( p_stream
->i_fourcc
== VLC_CODEC_DIRAC
)
1061 op
.packet
= p_input
->p_fmt
->p_extra
;
1062 op
.bytes
= p_input
->p_fmt
->i_extra
;
1066 op
.packetno
= p_stream
->i_packet_no
++;
1067 ogg_stream_packetin( &p_stream
->os
, &op
);
1068 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1070 else if( p_stream
->i_fourcc
== VLC_CODEC_FLAC
)
1072 /* flac stream marker (yeah, only that in the 1st packet) */
1073 op
.packet
= (unsigned char *)"fLaC";
1078 op
.packetno
= p_stream
->i_packet_no
++;
1079 ogg_stream_packetin( &p_stream
->os
, &op
);
1080 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1082 else if( p_stream
->i_fourcc
== VLC_CODEC_VP8
)
1085 op
.packet
= malloc( 26 );
1089 OggFillVP8Header( op
.packet
, p_input
);
1093 op
.packetno
= p_stream
->i_packet_no
++;
1094 ogg_stream_packetin( &p_stream
->os
, &op
);
1095 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1098 else if( p_stream
->p_oggds_header
)
1101 op
.packet
= malloc( sizeof(*p_stream
->p_oggds_header
) + p_stream
->p_oggds_header
->i_size
);
1104 op
.bytes
= OggFillDsHeader( op
.packet
, p_stream
->p_oggds_header
, p_stream
->i_cat
);
1108 op
.packetno
= p_stream
->i_packet_no
++;
1109 ogg_stream_packetin( &p_stream
->os
, &op
);
1110 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1114 block_ChainAppend( &p_hdr
, p_og
);
1118 /* Create fisbones if any */
1119 if ( p_sys
->skeleton
.b_create
)
1121 for( i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1123 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1124 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1125 if ( p_stream
->skeleton
.b_fisbone_done
) continue;
1126 OggGetSkeletonFisbone( &op
.packet
, &op
.bytes
, p_input
, p_mux
);
1127 if ( op
.packet
== NULL
) return false;
1131 op
.packetno
= p_sys
->skeleton
.i_packet_no
++;
1132 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1133 ogg_packet_clear( &op
);
1134 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1135 block_ChainAppend( &p_hdr
, p_og
);
1136 p_stream
->skeleton
.b_fisbone_done
= true;
1140 /* Write previous headers */
1141 for( p_og
= p_hdr
; p_og
!= NULL
; p_og
= p_og
->p_next
)
1143 /* flag headers to be resent for streaming clients */
1144 p_og
->i_flags
|= BLOCK_FLAG_HEADER
;
1146 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_hdr
);
1149 /* Create indexes if any */
1150 for( i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1152 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1153 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1154 /* flush stream && save offset */
1155 if ( p_sys
->skeleton
.b_create
&& !p_stream
->skeleton
.b_index_done
)
1157 if ( !p_stream
->skeleton
.p_index
) AllocateIndex( p_mux
, p_input
);
1158 if ( p_stream
->skeleton
.p_index
)
1160 msg_Dbg( p_mux
, "Creating index for stream %d", p_stream
->i_serial_no
);
1161 OggGetSkeletonIndex( &op
.packet
, &op
.bytes
, p_stream
);
1162 if ( op
.packet
== NULL
) return false;
1166 op
.packetno
= p_sys
->skeleton
.i_packet_no
++;
1168 /* backup some values */
1169 p_stream
->skeleton
.i_index_offset
= p_mux
->p_sys
->i_pos
;
1170 p_stream
->skeleton
.i_index_packetno
= p_sys
->skeleton
.os
.packetno
;
1171 p_stream
->skeleton
.i_index_pageno
= p_sys
->skeleton
.os
.pageno
;
1173 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1174 ogg_packet_clear( &op
);
1175 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1176 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1178 p_stream
->skeleton
.b_index_done
= true;
1182 /* Take care of the non b_o_s headers */
1183 for( i
= 0; i
< p_mux
->i_nb_inputs
; i
++ )
1185 sout_input_t
*p_input
= p_mux
->pp_inputs
[i
];
1186 ogg_stream_t
*p_stream
= (ogg_stream_t
*)p_input
->p_sys
;
1188 if( p_stream
->i_fourcc
== VLC_CODEC_VORBIS
||
1189 p_stream
->i_fourcc
== VLC_CODEC_SPEEX
||
1190 p_stream
->i_fourcc
== VLC_CODEC_OPUS
||
1191 p_stream
->i_fourcc
== VLC_CODEC_THEORA
||
1192 p_stream
->i_fourcc
== VLC_CODEC_DAALA
)
1194 unsigned pi_size
[XIPH_MAX_HEADER_COUNT
];
1195 void *pp_data
[XIPH_MAX_HEADER_COUNT
];
1197 if( xiph_SplitHeaders( pi_size
, pp_data
, &i_count
,
1198 p_input
->p_fmt
->i_extra
, p_input
->p_fmt
->p_extra
) )
1201 /* Special case, headers are already there in the incoming stream.
1202 * We need to gather them an mark them as headers. */
1203 for( unsigned i
= 1; i
< i_count
; i
++ )
1205 op
.bytes
= pi_size
[i
];
1206 op
.packet
= pp_data
[i
];
1207 if( pi_size
[i
] <= 0 )
1208 msg_Err( p_mux
, "header data corrupted");
1213 op
.packetno
= p_stream
->i_packet_no
++;
1214 ogg_stream_packetin( &p_stream
->os
, &op
);
1215 msg_Dbg( p_mux
, "adding non bos, secondary header" );
1216 if( i
== i_count
- 1 )
1217 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1219 p_og
= OggStreamPageOut( p_mux
, &p_stream
->os
, 0 );
1221 block_ChainAppend( &p_hdr
, p_og
);
1224 else if( p_stream
->i_fourcc
!= VLC_CODEC_FLAC
&&
1225 p_stream
->i_fourcc
!= VLC_CODEC_DIRAC
)
1231 com
[0] = PACKET_TYPE_COMMENT
;
1232 i_com
= snprintf( (char *)(com
+1), 127,
1233 PACKAGE_VERSION
" stream output" )
1240 op
.packetno
= p_stream
->i_packet_no
++;
1241 ogg_stream_packetin( &p_stream
->os
, &op
);
1242 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1243 block_ChainAppend( &p_hdr
, p_og
);
1246 /* Special case for mp4v and flac */
1247 if( ( p_stream
->i_fourcc
== VLC_CODEC_MP4V
||
1248 p_stream
->i_fourcc
== VLC_CODEC_FLAC
) &&
1249 p_input
->p_fmt
->i_extra
)
1251 /* Send a packet with the VOL data for mp4v
1252 * or STREAMINFO for flac */
1253 msg_Dbg( p_mux
, "writing extra data" );
1254 op
.bytes
= p_input
->p_fmt
->i_extra
;
1255 op
.packet
= p_input
->p_fmt
->p_extra
;
1256 uint8_t flac_streaminfo
[34 + 4];
1257 if( p_stream
->i_fourcc
== VLC_CODEC_FLAC
)
1259 if (op
.bytes
== 42 && !memcmp(op
.packet
, "fLaC", 4)) {
1261 memcpy(flac_streaminfo
, op
.packet
+ 4, 38);
1262 op
.packet
= flac_streaminfo
;
1263 } else if (op
.bytes
== 34) {
1265 memcpy(flac_streaminfo
+ 4, op
.packet
, 34);
1266 flac_streaminfo
[0] = 0x80; /* last block, streaminfo */
1267 flac_streaminfo
[1] = 0;
1268 flac_streaminfo
[2] = 0;
1269 flac_streaminfo
[3] = 34; /* block size */
1270 op
.packet
= flac_streaminfo
;
1272 msg_Err(p_mux
, "Invalid FLAC streaminfo (%ld bytes)",
1279 op
.packetno
= p_stream
->i_packet_no
++;
1280 ogg_stream_packetin( &p_stream
->os
, &op
);
1281 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 );
1282 block_ChainAppend( &p_hdr
, p_og
);
1286 if ( p_sys
->skeleton
.b_create
)
1288 msg_Dbg( p_mux
, "ending skeleton" );
1294 op
.packetno
= p_sys
->skeleton
.i_packet_no
++;
1295 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1296 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1297 block_ChainAppend( &p_hdr
, p_og
);
1300 /* set HEADER flag */
1301 /* flag headers to be resent for streaming clients */
1302 for( p_og
= p_hdr
; p_og
!= NULL
; p_og
= p_og
->p_next
)
1304 p_og
->i_flags
|= BLOCK_FLAG_HEADER
;
1307 /* Write previous headers */
1308 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_hdr
);
1313 static void OggCreateStreamFooter( sout_mux_t
*p_mux
, ogg_stream_t
*p_stream
)
1317 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1319 /* as stream is finished, overwrite the index, if there was any */
1320 if ( p_sys
->skeleton
.b_create
&& p_stream
->skeleton
.p_index
1321 && p_stream
->skeleton
.i_index_payload
)
1323 sout_AccessOutSeek( p_mux
->p_access
, p_stream
->skeleton
.i_index_offset
);
1324 OggGetSkeletonIndex( &op
.packet
, &op
.bytes
, p_stream
);
1325 if ( op
.packet
!= NULL
)
1327 msg_Dbg(p_mux
, "Rewriting index at %"PRId64
, p_stream
->skeleton
.i_index_offset
);
1328 ogg_stream_reset_serialno( &p_sys
->skeleton
.os
, p_sys
->skeleton
.i_serial_no
);
1332 op
.packetno
= p_stream
->skeleton
.i_index_packetno
+ 1;
1333 /* fake our stream state */
1334 p_sys
->skeleton
.os
.pageno
= p_stream
->skeleton
.i_index_pageno
;
1335 p_sys
->skeleton
.os
.packetno
= p_stream
->skeleton
.i_index_packetno
;
1336 p_sys
->skeleton
.os
.granulepos
= 0;
1337 p_sys
->skeleton
.os
.b_o_s
= 1;
1338 p_sys
->skeleton
.os
.e_o_s
= 0;
1339 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1340 ogg_packet_clear( &op
);
1341 p_og
= OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 );
1342 sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1344 sout_AccessOutSeek( p_mux
->p_access
, p_sys
->i_pos
);
1347 /* clear skeleton */
1348 p_stream
->skeleton
.b_fisbone_done
= false;
1349 p_stream
->skeleton
.b_index_done
= false;
1350 p_stream
->skeleton
.i_index_offset
= 0;
1351 p_stream
->skeleton
.i_index_payload
= 0;
1352 p_stream
->skeleton
.i_last_keyframe_pos
= 0;
1353 p_stream
->skeleton
.i_last_keyframe_time
= 0;
1354 /* clear accounting */
1355 p_stream
->i_num_frames
= 0;
1356 p_stream
->i_num_keyframes
= 0;
1358 /* Write eos packet for stream. */
1363 op
.granulepos
= p_stream
->u_last_granulepos
;
1364 op
.packetno
= p_stream
->i_packet_no
++;
1365 ogg_stream_packetin( &p_stream
->os
, &op
);
1367 /* flush it with all remaining data */
1368 if( ( p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, 0 ) ) )
1371 OggSetDate( p_og
, p_stream
->i_dts
, p_stream
->i_length
);
1372 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1375 ogg_stream_clear( &p_stream
->os
);
1378 static void OggSetDate( block_t
*p_og
, mtime_t i_dts
, mtime_t i_length
)
1384 for( p_tmp
= p_og
, i_count
= 0; p_tmp
!= NULL
; p_tmp
= p_tmp
->p_next
)
1389 if( i_count
== 0 ) return; /* ignore. */
1391 i_delta
= i_length
/ i_count
;
1393 for( p_tmp
= p_og
; p_tmp
!= NULL
; p_tmp
= p_tmp
->p_next
)
1395 p_tmp
->i_dts
= i_dts
;
1396 p_tmp
->i_length
= i_delta
;
1402 static void OggRewriteFisheadPage( sout_mux_t
*p_mux
)
1404 sout_mux_sys_t
*p_sys
= p_mux
->p_sys
;
1407 op
.packet
= calloc( 1, op
.bytes
);
1408 if ( op
.packet
!= NULL
)
1414 ogg_stream_reset_serialno( &p_sys
->skeleton
.os
, p_sys
->skeleton
.i_serial_no
);
1415 OggFillSkeletonFishead( op
.packet
, p_mux
);
1416 ogg_stream_packetin( &p_sys
->skeleton
.os
, &op
);
1417 ogg_packet_clear( &op
);
1418 msg_Dbg( p_mux
, "rewriting fishead at %"PRId64
, p_mux
->p_sys
->skeleton
.i_fishead_offset
);
1419 sout_AccessOutSeek( p_mux
->p_access
, p_mux
->p_sys
->skeleton
.i_fishead_offset
);
1420 sout_AccessOutWrite( p_mux
->p_access
,
1421 OggStreamFlush( p_mux
, &p_sys
->skeleton
.os
, 0 ) );
1422 sout_AccessOutSeek( p_mux
->p_access
, p_mux
->p_sys
->i_pos
);
1426 static bool AllocateIndex( sout_mux_t
*p_mux
, sout_input_t
*p_input
)
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_mux
->p_sys
->skeleton
.i_index_intvl
* 1000;
1436 if( p_input
->p_fmt
->i_cat
== VIDEO_ES
&&
1437 p_input
->p_fmt
->video
.i_frame_rate
)
1439 /* optimize for fps < 1 */
1440 i_interval
= __MAX( p_mux
->p_sys
->skeleton
.i_index_intvl
* 1000,
1442 p_input
->p_fmt
->video
.i_frame_rate_base
/
1443 p_input
->p_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_mux
->p_sys
->skeleton
.i_index_intvl
)
1469 * p_mux
->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_mux
->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_mux
->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
->i_fourcc
!= VLC_CODEC_VORBIS
&&
1591 p_stream
->i_fourcc
!= VLC_CODEC_FLAC
&&
1592 p_stream
->i_fourcc
!= VLC_CODEC_SPEEX
&&
1593 p_stream
->i_fourcc
!= VLC_CODEC_OPUS
&&
1594 p_stream
->i_fourcc
!= VLC_CODEC_THEORA
&&
1595 p_stream
->i_fourcc
!= VLC_CODEC_DAALA
&&
1596 p_stream
->i_fourcc
!= VLC_CODEC_VP8
&&
1597 p_stream
->i_fourcc
!= 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
->i_fourcc
== 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
->i_cat
== AUDIO_ES
)
1615 if( p_stream
->i_fourcc
== VLC_CODEC_VORBIS
||
1616 p_stream
->i_fourcc
== VLC_CODEC_FLAC
||
1617 p_stream
->i_fourcc
== VLC_CODEC_OPUS
||
1618 p_stream
->i_fourcc
== 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
->i_cat
== VIDEO_ES
)
1637 if( p_stream
->i_fourcc
== VLC_CODEC_THEORA
||
1638 p_stream
->i_fourcc
== 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_input
->p_fmt
->video
.i_frame_rate_base
/ p_input
->p_fmt
->video
.i_frame_rate
;
1649 AddIndexEntry( p_mux
, i_time
, p_input
);
1652 op
.granulepos
= (p_stream
->i_last_keyframe
<< p_stream
->i_keyframe_granule_shift
)
1653 | (p_stream
->i_num_frames
-p_stream
->i_last_keyframe
);
1655 else if( p_stream
->i_fourcc
== VLC_CODEC_DIRAC
)
1658 #define FRAME_ROUND(a) \
1659 if ( ( a + 5000 / CLOCK_FREQ ) > ( a / CLOCK_FREQ ) )\
1663 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
;
1666 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
;
1669 /* (shro) some PTS could be repeated within 1st frames */
1670 if ( pt
== p_stream
->i_dirac_last_pt
)
1673 p_stream
->i_dirac_last_pt
= pt
;
1675 /* (shro) some DTS could be repeated within 1st frames */
1676 if ( dt
== p_stream
->i_dirac_last_dt
)
1679 p_stream
->i_dirac_last_dt
= dt
;
1681 if( p_data
->i_flags
& BLOCK_FLAG_TYPE_I
)
1682 p_stream
->i_last_keyframe
= dt
;
1683 mtime_t dist
= dt
- p_stream
->i_last_keyframe
;
1685 /* Everything increments by two for progressive */
1692 mtime_t delay
= pt
- dt
;
1693 if ( delay
< 0 ) delay
*= -1;
1695 op
.granulepos
= (pt
- delay
) << 31 | (dist
&0xff00) << 14
1696 | (delay
&0x1fff) << 9 | (dist
&0xff);
1698 msg_Dbg( p_mux
, "dts %"PRId64
" pts %"PRId64
" dt %"PRId64
" pt %"PRId64
" delay %"PRId64
" granule %"PRId64
,
1699 (p_data
->i_dts
- p_sys
->i_start_dts
),
1700 (p_data
->i_pts
- p_sys
->i_start_dts
),
1701 dt
, pt
, delay
, op
.granulepos
);
1704 AddIndexEntry( p_mux
, dt
, p_input
);
1706 else if( p_stream
->i_fourcc
== VLC_CODEC_VP8
)
1708 p_stream
->i_num_frames
++;
1709 if( p_data
->i_flags
& BLOCK_FLAG_TYPE_I
)
1711 p_stream
->i_num_keyframes
++;
1712 p_stream
->i_last_keyframe
= p_stream
->i_num_frames
;
1714 /* presentation time */
1715 i_time
= CLOCK_FREQ
* ( p_stream
->i_num_frames
- 1 ) *
1716 p_input
->p_fmt
->video
.i_frame_rate_base
/ p_input
->p_fmt
->video
.i_frame_rate
;
1717 AddIndexEntry( p_mux
, i_time
, p_input
);
1719 op
.granulepos
= ( ((int64_t)p_stream
->i_num_frames
) << 32 ) |
1720 ( ( ( p_stream
->i_num_frames
- p_stream
->i_last_keyframe
) & 0x07FFFFFF ) << 3 );
1722 else if( p_stream
->p_oggds_header
)
1723 op
.granulepos
= ( p_data
->i_dts
- p_sys
->i_start_dts
) * INT64_C(10) /
1724 p_stream
->p_oggds_header
->i_time_unit
;
1726 else if( p_stream
->i_cat
== SPU_ES
)
1728 /* granulepos is in millisec */
1729 op
.granulepos
= ( p_data
->i_dts
- p_sys
->i_start_dts
) / 1000;
1732 return VLC_EGENERIC
;
1734 p_stream
->u_last_granulepos
= op
.granulepos
;
1735 ogg_stream_packetin( &p_stream
->os
, &op
);
1737 if( p_stream
->i_cat
== SPU_ES
||
1738 p_stream
->i_fourcc
== VLC_CODEC_SPEEX
||
1739 p_stream
->i_fourcc
== VLC_CODEC_DIRAC
)
1741 /* Subtitles or Speex packets are quite small so they
1742 * need to be flushed to be sent on time */
1743 /* The OggDirac mapping suggests ever so strongly that a
1744 * page flush occurs after each OggDirac packet, so to make
1745 * the timestamps unambiguous */
1746 p_og
= OggStreamFlush( p_mux
, &p_stream
->os
, p_data
->i_dts
);
1750 p_og
= OggStreamPageOut( p_mux
, &p_stream
->os
, p_data
->i_dts
);
1755 OggSetDate( p_og
, p_stream
->i_dts
, p_stream
->i_length
);
1756 p_stream
->i_dts
= -1;
1757 p_stream
->i_length
= 0;
1758 p_mux
->p_sys
->i_pos
+= sout_AccessOutWrite( p_mux
->p_access
, p_og
);
1762 if( p_stream
->i_dts
< 0 )
1764 p_stream
->i_dts
= p_data
->i_dts
;
1766 p_stream
->i_length
+= p_data
->i_length
;
1769 block_Release( p_data
);