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;
185 struct sout_mux_sys_t
190 int i_next_serial_no;
192 /* number of logical streams pending to be added */
194 bool b_can_add_streams;
196 /* logical streams pending to be deleted */
198 ogg_stream_t **pp_del_streams;
208 /* backup values for rewriting fishead page later */
209 uint64_t i_fishead_offset; /* sout offset of the fishead page */
214 /* access position */
216 ssize_t i_data_start;
217 ssize_t i_segment_start;
220 static void OggSetDate( block_t *, mtime_t , mtime_t );
221 static block_t *OggStreamFlush( sout_mux_t *, ogg_stream_state *, mtime_t );
222 static void OggCreateStreamFooter( sout_mux_t *p_mux, ogg_stream_t *p_stream );
223 static void OggRewriteFisheadPage( sout_mux_t *p_mux );
224 static bool AllocateIndex( sout_mux_t *p_mux, sout_input_t *p_input );
226 /*****************************************************************************
228 *****************************************************************************/
229 static int Open( vlc_object_t *p_this )
231 sout_mux_t *p_mux = (sout_mux_t*)p_this;
232 sout_mux_sys_t *p_sys;
234 msg_Info( p_mux, "Open" );
236 p_sys = malloc( sizeof( sout_mux_sys_t ) );
239 p_sys->i_streams = 0;
240 p_sys->i_add_streams = 0;
241 p_sys->b_can_add_streams = true;
242 p_sys->i_del_streams = 0;
243 p_sys->pp_del_streams = 0;
245 p_sys->skeleton.b_create = false;
246 p_sys->skeleton.b_head_done = false;
247 p_sys->skeleton.i_index_intvl =
248 var_InheritInteger( p_this, SOUT_CFG_PREFIX "indexintvl" );
249 p_sys->skeleton.i_index_ratio =
250 var_InheritFloat( p_this, SOUT_CFG_PREFIX "indexratio" );
251 p_mux->p_sys = p_sys;
252 p_mux->pf_control = Control;
253 p_mux->pf_addstream = AddStream;
254 p_mux->pf_delstream = DelStream;
257 /* First serial number is random.
258 * (Done like this because on win32 you need to seed the random number
259 * generator once per thread). */
261 vlc_rand_bytes(&r, sizeof(r));
262 p_sys->i_next_serial_no = r & INT_MAX;
267 /*****************************************************************************
268 * Close: Finalize ogg bitstream and close muxer
269 *****************************************************************************/
270 static void Close( vlc_object_t * p_this )
272 sout_mux_t *p_mux = (sout_mux_t*)p_this;
273 sout_mux_sys_t *p_sys = p_mux->p_sys;
274 ogg_stream_t *p_stream;
276 msg_Info( p_mux, "Close" );
278 if( p_sys->i_del_streams )
280 /* Close the current ogg stream */
281 msg_Dbg( p_mux, "writing footers" );
283 for(int i = 0; i < p_mux->i_nb_inputs; i++ )
285 p_stream = (ogg_stream_t *) p_mux->pp_inputs[i]->p_sys;
286 OggCreateStreamFooter( p_mux, p_stream );
287 free( p_stream->skeleton.p_index );
290 /* Remove deleted logical streams */
291 for(int i = 0; i < p_sys->i_del_streams; i++ )
293 OggCreateStreamFooter( p_mux, p_sys->pp_del_streams[i] );
294 free( p_sys->pp_del_streams[i]->p_oggds_header );
295 free( p_sys->pp_del_streams[i] );
296 free( p_sys->pp_del_streams[i]->skeleton.p_index );
298 free( p_sys->pp_del_streams );
299 p_sys->i_streams -= p_sys->i_del_streams;
302 /* rewrite fishead with final values */
303 if ( p_sys->skeleton.b_create && p_sys->skeleton.b_head_done )
305 OggRewriteFisheadPage( p_mux );
311 /*****************************************************************************
313 *****************************************************************************/
314 static int Control( sout_mux_t *p_mux, int i_query, va_list args )
322 case MUX_CAN_ADD_STREAM_WHILE_MUXING:
323 pb_bool = (bool*)va_arg( args, bool * );
327 case MUX_GET_ADD_STREAM_WAIT:
328 pb_bool = (bool*)va_arg( args, bool * );
333 ppsz = (char**)va_arg( args, char ** );
334 *ppsz = strdup( "application/ogg" );
341 /*****************************************************************************
342 * AddStream: Add an elementary stream to the muxed stream
343 *****************************************************************************/
344 static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
346 sout_mux_sys_t *p_sys = p_mux->p_sys;
347 ogg_stream_t *p_stream;
350 msg_Dbg( p_mux, "adding input" );
352 p_input->p_sys = p_stream = calloc( 1, sizeof( ogg_stream_t ) );
356 p_stream->i_cat = p_input->p_fmt->i_cat;
357 p_stream->i_fourcc = p_input->p_fmt->i_codec;
358 p_stream->i_serial_no = p_sys->i_next_serial_no++;
359 p_stream->i_packet_no = 0;
360 p_stream->i_last_keyframe = 0;
361 p_stream->i_num_keyframes = 0;
362 p_stream->i_num_frames = 0;
364 p_stream->p_oggds_header = 0;
366 switch( p_input->p_fmt->i_cat )
369 if( !p_input->p_fmt->video.i_frame_rate ||
370 !p_input->p_fmt->video.i_frame_rate_base )
372 msg_Warn( p_mux, "Missing frame rate, assuming 25fps" );
373 p_input->p_fmt->video.i_frame_rate = 25;
374 p_input->p_fmt->video.i_frame_rate_base = 1;
377 switch( p_stream->i_fourcc )
386 p_stream->p_oggds_header = calloc( 1, sizeof(oggds_header_t) );
387 if( !p_stream->p_oggds_header )
392 p_stream->p_oggds_header->i_packet_type = PACKET_TYPE_HEADER;
394 memcpy( p_stream->p_oggds_header->stream_type, "video", 5 );
395 if( p_stream->i_fourcc == VLC_CODEC_MP4V )
397 memcpy( p_stream->p_oggds_header->sub_type, "XVID", 4 );
399 else if( p_stream->i_fourcc == VLC_CODEC_DIV3 )
401 memcpy( p_stream->p_oggds_header->sub_type, "DIV3", 4 );
405 memcpy( p_stream->p_oggds_header->sub_type,
406 &p_stream->i_fourcc, 4 );
408 p_stream->p_oggds_header->i_size = 0 ;
409 p_stream->p_oggds_header->i_time_unit =
410 INT64_C(10000000) * p_input->p_fmt->video.i_frame_rate_base /
411 (int64_t)p_input->p_fmt->video.i_frame_rate;
412 p_stream->p_oggds_header->i_samples_per_unit = 1;
413 p_stream->p_oggds_header->i_default_len = 1 ; /* ??? */
414 p_stream->p_oggds_header->i_buffer_size = 1024*1024;
415 p_stream->p_oggds_header->i_bits_per_sample = 0;
416 p_stream->p_oggds_header->header.video.i_width = p_input->p_fmt->video.i_width;
417 p_stream->p_oggds_header->header.video.i_height = p_input->p_fmt->video.i_height;
418 msg_Dbg( p_mux, "%4.4s stream", (char *)&p_stream->i_fourcc );
421 case VLC_CODEC_DIRAC:
422 msg_Dbg( p_mux, "dirac stream" );
425 case VLC_CODEC_THEORA:
426 msg_Dbg( p_mux, "theora stream" );
430 FREENULL( p_input->p_sys );
436 switch( p_stream->i_fourcc )
439 msg_Dbg( p_mux, "opus stream" );
442 case VLC_CODEC_VORBIS:
443 msg_Dbg( p_mux, "vorbis stream" );
446 case VLC_CODEC_SPEEX:
447 msg_Dbg( p_mux, "speex stream" );
451 msg_Dbg( p_mux, "flac stream" );
455 fourcc_to_wf_tag( p_stream->i_fourcc, &i_tag );
456 if( i_tag == WAVE_FORMAT_UNKNOWN )
458 FREENULL( p_input->p_sys );
462 p_stream->p_oggds_header =
463 malloc( sizeof(oggds_header_t) + p_input->p_fmt->i_extra );
464 if( !p_stream->p_oggds_header )
469 memset( p_stream->p_oggds_header, 0, sizeof(oggds_header_t) );
470 p_stream->p_oggds_header->i_packet_type = PACKET_TYPE_HEADER;
472 p_stream->p_oggds_header->i_size = p_input->p_fmt->i_extra;
474 if( p_input->p_fmt->i_extra )
476 memcpy( &p_stream->p_oggds_header[1],
477 p_input->p_fmt->p_extra, p_input->p_fmt->i_extra );
480 memcpy( p_stream->p_oggds_header->stream_type, "audio", 5 );
482 memset( p_stream->p_oggds_header->sub_type, 0, 4 );
484 snprintf( buf, sizeof(buf), "%"PRIx16, i_tag );
485 strncpy( p_stream->p_oggds_header->sub_type, buf, 4 );
487 p_stream->p_oggds_header->i_time_unit = INT64_C(10000000);
488 p_stream->p_oggds_header->i_default_len = 1;
489 p_stream->p_oggds_header->i_buffer_size = 30*1024 ;
490 p_stream->p_oggds_header->i_samples_per_unit = p_input->p_fmt->audio.i_rate;
491 p_stream->p_oggds_header->i_bits_per_sample = p_input->p_fmt->audio.i_bitspersample;
492 p_stream->p_oggds_header->header.audio.i_channels = p_input->p_fmt->audio.i_channels;
493 p_stream->p_oggds_header->header.audio.i_block_align = p_input->p_fmt->audio.i_blockalign;
494 p_stream->p_oggds_header->header.audio.i_avgbytespersec = p_input->p_fmt->i_bitrate / 8;
495 msg_Dbg( p_mux, "%4.4s stream", (char *)&p_stream->i_fourcc );
501 switch( p_stream->i_fourcc )
504 p_stream->p_oggds_header = calloc( 1, sizeof(oggds_header_t) );
505 if( !p_stream->p_oggds_header )
510 p_stream->p_oggds_header->i_packet_type = PACKET_TYPE_HEADER;
512 memcpy( p_stream->p_oggds_header->stream_type, "text", 4 );
513 msg_Dbg( p_mux, "subtitles stream" );
517 FREENULL( p_input->p_sys );
522 FREENULL( p_input->p_sys );
526 p_stream->b_new = true;
528 p_sys->i_add_streams++;
533 /*****************************************************************************
534 * DelStream: Delete an elementary stream from the muxed stream
535 *****************************************************************************/
536 static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
538 sout_mux_sys_t *p_sys = p_mux->p_sys;
539 ogg_stream_t *p_stream = (ogg_stream_t*)p_input->p_sys;
542 msg_Dbg( p_mux, "removing input" );
544 /* flush all remaining data */
547 if( !p_stream->b_new )
549 while( block_FifoCount( p_input->p_fifo ) )
550 MuxBlock( p_mux, p_input );
553 if( !p_stream->b_new &&
554 ( p_og = OggStreamFlush( p_mux, &p_stream->os, 0 ) ) )
556 OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
557 p_mux->p_sys->i_pos += sout_AccessOutWrite( p_mux->p_access, p_og );
560 /* move input in delete queue */
561 if( !p_stream->b_new )
563 p_sys->pp_del_streams = xrealloc( p_sys->pp_del_streams,
564 (p_sys->i_del_streams + 1) * sizeof(ogg_stream_t *) );
565 p_sys->pp_del_streams[p_sys->i_del_streams++] = p_stream;
569 /* wasn't already added so get rid of it */
570 FREENULL( p_stream->p_oggds_header );
571 FREENULL( p_stream );
572 p_sys->i_add_streams--;
576 p_input->p_sys = NULL;
581 /*****************************************************************************
582 * Ogg Skeleton helpers
583 *****************************************************************************/
584 static int WriteQWVariableLE( uint64_t i_64, uint64_t i_offset,
585 uint8_t *p_buffer, int i_buffer_size )
587 uint8_t *p_dest = p_buffer + i_offset;
592 if ( p_dest - p_buffer >= i_buffer_size ) return -1;
594 *p_dest = (uint8_t) ( i_64 & 0x7F );
608 static bool AddIndexEntry( sout_mux_t *p_mux, uint64_t i_time, sout_input_t *p_input )
610 sout_mux_sys_t *p_sys = p_mux->p_sys;
611 ogg_stream_t *p_stream = (ogg_stream_t *) p_input->p_sys;
613 uint64_t i_timedelta;
614 if ( !p_sys->skeleton.b_create || p_mux->p_sys->skeleton.i_index_intvl == 0
615 || !p_stream->skeleton.p_index )
618 if ( p_stream->skeleton.i_last_keyframe_pos == 0 )
619 p_stream->skeleton.i_last_keyframe_pos = p_sys->i_segment_start;
620 i_posdelta = p_sys->i_pos - p_stream->skeleton.i_last_keyframe_pos;
621 i_timedelta = i_time - p_stream->skeleton.i_last_keyframe_time;
623 if ( i_timedelta <= ( (uint64_t) p_mux->p_sys->skeleton.i_index_intvl * 1000 )
624 || i_posdelta <= 0xFFFF )
629 if ( !p_stream->skeleton.p_index ) return false;
630 uint64_t i_offset = p_stream->skeleton.i_index_payload;
631 i_ret = WriteQWVariableLE( i_posdelta, i_offset, p_stream->skeleton.p_index,
632 p_stream->skeleton.i_index_size );
633 if ( i_ret == -1 ) return false;
635 i_ret = WriteQWVariableLE( i_timedelta, i_offset, p_stream->skeleton.p_index,
636 p_stream->skeleton.i_index_size );
637 if ( i_ret == -1 ) return false;
638 p_stream->skeleton.i_index_payload = i_offset + i_ret;
639 p_stream->skeleton.i_index_count++;
641 /* update diff points */
642 p_stream->skeleton.i_last_keyframe_pos = p_sys->i_pos;
643 p_stream->skeleton.i_last_keyframe_time = i_time;
644 msg_Dbg( p_mux, "Added index on stream %d entry %"PRId64" %"PRId64,
645 p_stream->i_serial_no, p_sys->i_pos - p_sys->i_segment_start, i_time );
650 /*****************************************************************************
651 * Ogg bitstream manipulation routines
652 *****************************************************************************/
653 static block_t *OggStreamGetPage( sout_mux_t *p_mux,
654 ogg_stream_state *p_os, mtime_t i_pts,
658 block_t *p_og, *p_og_first = NULL;
660 int (*pager)( ogg_stream_state*, ogg_page* ) = flush ? ogg_stream_flush : ogg_stream_pageout;
662 while( pager( p_os, &og ) )
665 p_og = block_Alloc( og.header_len + og.body_len );
667 memcpy( p_og->p_buffer, og.header, og.header_len );
668 memcpy( p_og->p_buffer + og.header_len, og.body, og.body_len );
673 i_pts = 0; // write it only once
675 block_ChainAppend( &p_og_first, p_og );
681 static block_t *OggStreamFlush( sout_mux_t *p_mux,
682 ogg_stream_state *p_os, mtime_t i_pts )
684 return OggStreamGetPage( p_mux, p_os, i_pts, true );
687 static block_t *OggStreamPageOut( sout_mux_t *p_mux,
688 ogg_stream_state *p_os, mtime_t i_pts )
690 return OggStreamGetPage( p_mux, p_os, i_pts, false );
693 static void OggGetSkeletonIndex( uint8_t **pp_buffer, long *pi_size, ogg_stream_t *p_stream )
695 uint8_t *p_buffer = calloc( INDEX_BASE_SIZE + p_stream->skeleton.i_index_size, sizeof(uint8_t) );
696 if ( !p_buffer ) return;
697 *pp_buffer = p_buffer;
699 memcpy( p_buffer, "index", 6 );
700 SetDWLE( &p_buffer[6], p_stream->i_serial_no );
701 SetQWLE( &p_buffer[10], p_stream->skeleton.i_index_count ); /* num keypoints */
702 SetQWLE( &p_buffer[18], 1000000 );
703 SetQWLE( &p_buffer[34], p_stream->i_length );
704 memcpy( p_buffer + INDEX_BASE_SIZE, p_stream->skeleton.p_index, p_stream->skeleton.i_index_payload );
705 *pi_size = INDEX_BASE_SIZE + p_stream->skeleton.i_index_size;
708 static void OggGetSkeletonFisbone( uint8_t **pp_buffer, long *pi_size,
709 sout_input_t *p_input, sout_mux_t *p_mux )
713 const char *psz_value = NULL;
714 ogg_stream_t *p_stream = (ogg_stream_t *) p_input->p_sys;
717 char *psz_content_type;
720 unsigned int i_count;
721 } headers = { NULL, NULL, 0, 0 };
724 switch( p_stream->i_fourcc )
726 case VLC_CODEC_VORBIS:
727 psz_value = "audio/vorbis";
729 case VLC_CODEC_THEORA:
730 psz_value = "video/theora";
732 case VLC_CODEC_SPEEX:
733 psz_value = "audio/speex";
736 psz_value = "audio/flac";
739 psz_value = "text/cmml";
742 psz_value = "application/kate";
745 psz_value = "application/octet-stream";
746 msg_Warn( p_mux, "Unkown fourcc for stream %s, setting Content-Type to %s",
747 vlc_fourcc_GetDescription( p_stream->i_cat, p_stream->i_fourcc ),
751 /* Content Type Header */
752 if ( asprintf( &headers.psz_content_type, "Content-Type: %s\r\n", psz_value ) != -1 )
754 headers.i_size += strlen( headers.psz_content_type );
758 /* Set Role Header */
759 if ( p_input->p_fmt->i_priority > ES_PRIORITY_NOT_SELECTABLE )
761 int i_max_prio = ES_PRIORITY_MIN;
762 for ( int i=0; i< p_mux->i_nb_inputs; i++ )
764 if ( p_mux->pp_inputs[i]->p_fmt->i_cat != p_input->p_fmt->i_cat ) continue;
765 i_max_prio = __MAX( p_mux->pp_inputs[i]->p_fmt->i_priority, i_max_prio );
769 if ( p_input->p_fmt->i_cat == AUDIO_ES || p_input->p_fmt->i_cat == VIDEO_ES )
771 if ( p_input->p_fmt->i_priority == i_max_prio && i_max_prio >= ES_PRIORITY_SELECTABLE_MIN )
772 psz_value = ( p_input->p_fmt->i_cat == VIDEO_ES ) ?
773 "video/main" : "audio/main";
775 psz_value = ( p_input->p_fmt->i_cat == VIDEO_ES ) ?
776 "video/alternate" : "audio/alternate";
778 else if ( p_input->p_fmt->i_cat == SPU_ES )
780 psz_value = ( p_input->p_fmt->i_codec == VLC_CODEC_KATE ) ?
781 "text/karaoke" : "text/subtitle";
784 if ( psz_value && asprintf( &headers.psz_role, "Role: %s\r\n", psz_value ) != -1 )
786 headers.i_size += strlen( headers.psz_role );
791 *pp_buffer = calloc( FISBONE_BASE_SIZE + headers.i_size, sizeof(uint8_t) );
792 if ( !*pp_buffer ) return;
793 p_buffer = *pp_buffer;
795 memcpy( p_buffer, "fisbone", 8 );
796 SetDWLE( &p_buffer[8], FISBONE_BASE_OFFSET ); /* offset to message headers */
797 SetDWLE( &p_buffer[12], p_stream->i_serial_no );
798 SetDWLE( &p_buffer[16], headers.i_count );
800 /* granulerate den */
801 switch ( p_input->p_fmt->i_cat )
804 SetQWLE( &(*pp_buffer)[20], p_input->p_fmt->video.i_frame_rate );
805 SetQWLE( &(*pp_buffer)[28], p_input->p_fmt->video.i_frame_rate_base );
808 SetQWLE( &(*pp_buffer)[20], p_input->p_fmt->audio.i_rate );
809 SetQWLE( &(*pp_buffer)[28], 1 );
812 SetQWLE( &(*pp_buffer)[20], 1000 );
813 SetQWLE( &(*pp_buffer)[28], 1 );
817 if ( p_input->p_fmt->p_extra )
818 SetDWLE( &(*pp_buffer)[44], xiph_CountHeaders( p_input->p_fmt->p_extra, p_input->p_fmt->i_extra ) );
820 psz_header = *pp_buffer + FISBONE_BASE_SIZE;
821 memcpy( psz_header, headers.psz_content_type, strlen( headers.psz_content_type ) );
822 psz_header += strlen( headers.psz_content_type );
823 if ( headers.psz_role )
824 memcpy( psz_header, headers.psz_role, strlen( headers.psz_role ) );
826 *pi_size = FISBONE_BASE_SIZE + headers.i_size;
828 free( headers.psz_content_type );
829 free( headers.psz_role );
832 static void OggFillSkeletonFishead( uint8_t *p_buffer, sout_mux_t *p_mux )
834 memcpy( p_buffer, "fishead", 8 );
835 SetWLE( &p_buffer[8], 4 );
836 SetWLE( &p_buffer[10], 0 );
837 SetQWLE( &p_buffer[20], 1000 );
838 SetQWLE( &p_buffer[36], 1000 );
839 SetQWLE( &p_buffer[64], p_mux->p_sys->i_pos - p_mux->p_sys->i_segment_start ); /* segment length */
840 SetQWLE( &p_buffer[72], p_mux->p_sys->i_data_start - p_mux->p_sys->i_segment_start ); /* data start offset */
843 static int32_t OggFillDsHeader( uint8_t *p_buffer, oggds_header_t *p_oggds_header, int i_cat )
846 p_buffer[index] = p_oggds_header->i_packet_type;
848 memcpy( &p_buffer[index], p_oggds_header->stream_type, sizeof(p_oggds_header->stream_type) );
849 index += sizeof(p_oggds_header->stream_type);
850 memcpy(&p_buffer[index], p_oggds_header->sub_type, sizeof(p_oggds_header->sub_type) );
851 index += sizeof(p_oggds_header->sub_type);
853 /* The size is filled at the end */
854 uint8_t *p_isize = &p_buffer[index];
857 SetQWLE( &p_buffer[index], p_oggds_header->i_time_unit );
859 SetQWLE( &p_buffer[index], p_oggds_header->i_samples_per_unit );
861 SetDWLE( &p_buffer[index], p_oggds_header->i_default_len );
863 SetDWLE( &p_buffer[index], p_oggds_header->i_buffer_size );
865 SetWLE( &p_buffer[index], p_oggds_header->i_bits_per_sample );
867 SetWLE( &p_buffer[index], p_oggds_header->i_padding_0 );
873 SetDWLE( &p_buffer[index], p_oggds_header->header.video.i_width );
874 SetDWLE( &p_buffer[index+4], p_oggds_header->header.video.i_height );
877 SetWLE( &p_buffer[index], p_oggds_header->header.audio.i_channels );
878 SetWLE( &p_buffer[index+2], p_oggds_header->header.audio.i_block_align );
879 SetDWLE( &p_buffer[index+4], p_oggds_header->header.audio.i_avgbytespersec );
883 SetDWLE( &p_buffer[index], p_oggds_header->i_padding_1 );
887 if( p_oggds_header->i_size > 0 )
889 memcpy( &p_buffer[index], p_oggds_header + sizeof(*p_oggds_header), p_oggds_header->i_size );
890 index += p_oggds_header->i_size;
893 SetDWLE( p_isize, index-1 );
897 static bool OggCreateHeaders( sout_mux_t *p_mux )
899 block_t *p_hdr = NULL;
900 block_t *p_og = NULL;
902 ogg_stream_t *p_stream;
903 sout_mux_sys_t *p_sys = p_mux->p_sys;
906 if( sout_AccessOutControl( p_mux->p_access,
908 &p_sys->skeleton.b_create ) )
910 p_sys->skeleton.b_create = false;
913 p_sys->skeleton.b_create &= !! p_mux->i_nb_inputs;
915 /* no skeleton for solo vorbis/speex/opus tracks */
916 if ( p_mux->i_nb_inputs == 1 && p_mux->pp_inputs[0]->p_fmt->i_cat == AUDIO_ES )
918 p_sys->skeleton.b_create = false;
922 for ( int i=0; i< p_mux->i_nb_inputs; i++ )
924 p_stream = (ogg_stream_t*) p_mux->pp_inputs[i]->p_sys;
925 if ( p_stream->p_oggds_header )
927 /* We don't want skeleton for OggDS */
928 p_sys->skeleton.b_create = false;
934 /* Skeleton's Fishead must be the first page of the stream */
935 if ( p_sys->skeleton.b_create && !p_sys->skeleton.b_head_done )
937 msg_Dbg( p_mux, "creating header for skeleton" );
938 p_sys->skeleton.i_serial_no = p_sys->i_next_serial_no++;
939 ogg_stream_init( &p_sys->skeleton.os, p_sys->skeleton.i_serial_no );
941 op.packet = calloc( 1, op.bytes );
942 if ( op.packet == NULL ) return false;
947 OggFillSkeletonFishead( op.packet, p_mux );
948 ogg_stream_packetin( &p_sys->skeleton.os, &op );
949 p_og = OggStreamFlush( p_mux, &p_sys->skeleton.os, 0 );
950 block_ChainAppend( &p_hdr, p_og );
951 p_sys->skeleton.b_head_done = true;
952 p_sys->skeleton.i_fishead_offset = p_sys->i_pos;
955 /* Write header for each stream. All b_o_s (beginning of stream) packets
956 * must appear first in the ogg stream so we take care of them first. */
957 for( int pass = 0; pass < 2; pass++ )
959 for( i = 0; i < p_mux->i_nb_inputs; i++ )
961 sout_input_t *p_input = p_mux->pp_inputs[i];
962 p_stream = (ogg_stream_t*)p_input->p_sys;
964 bool video = ( p_stream->i_fourcc == VLC_CODEC_THEORA || p_stream->i_fourcc == VLC_CODEC_DIRAC );
965 if( ( ( pass == 0 && !video ) || ( pass == 1 && video ) ) )
968 msg_Dbg( p_mux, "creating header for %4.4s",
969 (char *)&p_stream->i_fourcc );
971 ogg_stream_init( &p_stream->os, p_stream->i_serial_no );
972 p_stream->b_new = false;
973 p_stream->i_packet_no = 0;
974 p_stream->b_started = true;
976 if( p_stream->i_fourcc == VLC_CODEC_VORBIS ||
977 p_stream->i_fourcc == VLC_CODEC_SPEEX ||
978 p_stream->i_fourcc == VLC_CODEC_OPUS ||
979 p_stream->i_fourcc == VLC_CODEC_THEORA )
981 /* First packet in order: vorbis/speex/theora info */
982 unsigned pi_size[XIPH_MAX_HEADER_COUNT];
983 void *pp_data[XIPH_MAX_HEADER_COUNT];
985 if( xiph_SplitHeaders( pi_size, pp_data, &i_count,
986 p_input->p_fmt->i_extra, p_input->p_fmt->p_extra ) )
993 op.bytes = pi_size[0];
994 op.packet = pp_data[0];
995 if( pi_size[0] <= 0 )
996 msg_Err( p_mux, "header data corrupted");
1001 op.packetno = p_stream->i_packet_no++;
1002 ogg_stream_packetin( &p_stream->os, &op );
1003 p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
1005 /* Get keyframe_granule_shift for theora granulepos calculation */
1006 if( p_stream->i_fourcc == VLC_CODEC_THEORA )
1008 p_stream->i_keyframe_granule_shift =
1009 ( (op.packet[40] & 0x03) << 3 ) | ( (op.packet[41] & 0xe0) >> 5 );
1012 else if( p_stream->i_fourcc == VLC_CODEC_DIRAC )
1014 op.packet = p_input->p_fmt->p_extra;
1015 op.bytes = p_input->p_fmt->i_extra;
1019 op.packetno = p_stream->i_packet_no++;
1020 ogg_stream_packetin( &p_stream->os, &op );
1021 p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
1023 else if( p_stream->i_fourcc == VLC_CODEC_FLAC )
1025 /* flac stream marker (yeah, only that in the 1st packet) */
1026 op.packet = (unsigned char *)"fLaC";
1031 op.packetno = p_stream->i_packet_no++;
1032 ogg_stream_packetin( &p_stream->os, &op );
1033 p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
1035 else if( p_stream->p_oggds_header )
1038 op.packet = malloc( sizeof(*p_stream->p_oggds_header) + p_stream->p_oggds_header->i_size );
1041 op.bytes = OggFillDsHeader( op.packet, p_stream->p_oggds_header, p_stream->i_cat );
1045 op.packetno = p_stream->i_packet_no++;
1046 ogg_stream_packetin( &p_stream->os, &op );
1047 p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
1051 block_ChainAppend( &p_hdr, p_og );
1055 /* Create fisbones if any */
1056 if ( p_sys->skeleton.b_create )
1058 for( i = 0; i < p_mux->i_nb_inputs; i++ )
1060 sout_input_t *p_input = p_mux->pp_inputs[i];
1061 ogg_stream_t *p_stream = (ogg_stream_t*)p_input->p_sys;
1062 if ( p_stream->skeleton.b_fisbone_done ) continue;
1063 OggGetSkeletonFisbone( &op.packet, &op.bytes, p_input, p_mux );
1064 if ( op.packet == NULL ) return false;
1068 op.packetno = p_sys->skeleton.i_packet_no++;
1069 ogg_stream_packetin( &p_sys->skeleton.os, &op );
1070 p_og = OggStreamFlush( p_mux, &p_sys->skeleton.os, 0 );
1071 block_ChainAppend( &p_hdr, p_og );
1072 p_stream->skeleton.b_fisbone_done = true;
1076 /* Write previous headers */
1077 p_mux->p_sys->i_pos += sout_AccessOutWrite( p_mux->p_access, p_hdr );
1080 /* Create indexes if any */
1081 for( i = 0; i < p_mux->i_nb_inputs; i++ )
1083 sout_input_t *p_input = p_mux->pp_inputs[i];
1084 ogg_stream_t *p_stream = (ogg_stream_t*)p_input->p_sys;
1085 /* flush stream && save offset */
1086 if ( p_sys->skeleton.b_create && !p_stream->skeleton.b_index_done )
1088 if ( !p_stream->skeleton.p_index ) AllocateIndex( p_mux, p_input );
1089 if ( p_stream->skeleton.p_index )
1091 msg_Dbg( p_mux, "Creating index for stream %d", p_stream->i_serial_no );
1092 OggGetSkeletonIndex( &op.packet, &op.bytes, p_stream );
1093 if ( op.packet == NULL ) return false;
1097 op.packetno = p_sys->skeleton.i_packet_no++;
1099 /* backup some values */
1100 p_stream->skeleton.i_index_offset = p_mux->p_sys->i_pos;
1101 p_stream->skeleton.i_index_packetno = p_sys->skeleton.os.packetno;
1102 p_stream->skeleton.i_index_pageno = p_sys->skeleton.os.pageno;
1104 ogg_stream_packetin( &p_sys->skeleton.os, &op );
1105 p_og = OggStreamFlush( p_mux, &p_sys->skeleton.os, 0 );
1106 p_mux->p_sys->i_pos += sout_AccessOutWrite( p_mux->p_access, p_og );
1108 p_stream->skeleton.b_index_done = true;
1112 /* Take care of the non b_o_s headers */
1113 for( i = 0; i < p_mux->i_nb_inputs; i++ )
1115 sout_input_t *p_input = p_mux->pp_inputs[i];
1116 ogg_stream_t *p_stream = (ogg_stream_t*)p_input->p_sys;
1118 if( p_stream->i_fourcc == VLC_CODEC_VORBIS ||
1119 p_stream->i_fourcc == VLC_CODEC_SPEEX ||
1120 p_stream->i_fourcc == VLC_CODEC_OPUS ||
1121 p_stream->i_fourcc == VLC_CODEC_THEORA )
1123 unsigned pi_size[XIPH_MAX_HEADER_COUNT];
1124 void *pp_data[XIPH_MAX_HEADER_COUNT];
1126 if( xiph_SplitHeaders( pi_size, pp_data, &i_count,
1127 p_input->p_fmt->i_extra, p_input->p_fmt->p_extra ) )
1130 /* Special case, headers are already there in the incoming stream.
1131 * We need to gather them an mark them as headers. */
1132 for( unsigned i = 1; i < i_count; i++ )
1134 op.bytes = pi_size[i];
1135 op.packet = pp_data[i];
1136 if( pi_size[i] <= 0 )
1137 msg_Err( p_mux, "header data corrupted");
1142 op.packetno = p_stream->i_packet_no++;
1143 ogg_stream_packetin( &p_stream->os, &op );
1144 msg_Dbg( p_mux, "adding non bos, secondary header" );
1145 if( i == i_count - 1 )
1146 p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
1148 p_og = OggStreamPageOut( p_mux, &p_stream->os, 0 );
1150 block_ChainAppend( &p_hdr, p_og );
1153 else if( p_stream->i_fourcc != VLC_CODEC_FLAC &&
1154 p_stream->i_fourcc != VLC_CODEC_DIRAC )
1160 com[0] = PACKET_TYPE_COMMENT;
1161 i_com = snprintf( (char *)(com+1), 127,
1162 PACKAGE_VERSION" stream output" )
1169 op.packetno = p_stream->i_packet_no++;
1170 ogg_stream_packetin( &p_stream->os, &op );
1171 p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
1172 block_ChainAppend( &p_hdr, p_og );
1175 /* Special case for mp4v and flac */
1176 if( ( p_stream->i_fourcc == VLC_CODEC_MP4V ||
1177 p_stream->i_fourcc == VLC_CODEC_FLAC ) &&
1178 p_input->p_fmt->i_extra )
1180 /* Send a packet with the VOL data for mp4v
1181 * or STREAMINFO for flac */
1182 msg_Dbg( p_mux, "writing extra data" );
1183 op.bytes = p_input->p_fmt->i_extra;
1184 op.packet = p_input->p_fmt->p_extra;
1185 uint8_t flac_streaminfo[34 + 4];
1186 if( p_stream->i_fourcc == VLC_CODEC_FLAC )
1188 if (op.bytes == 42 && !memcmp(op.packet, "fLaC", 4)) {
1190 memcpy(flac_streaminfo, op.packet + 4, 38);
1191 op.packet = flac_streaminfo;
1192 } else if (op.bytes == 34) {
1194 memcpy(flac_streaminfo + 4, op.packet, 34);
1195 flac_streaminfo[0] = 0x80; /* last block, streaminfo */
1196 flac_streaminfo[1] = 0;
1197 flac_streaminfo[2] = 0;
1198 flac_streaminfo[3] = 34; /* block size */
1199 op.packet = flac_streaminfo;
1201 msg_Err(p_mux, "Invalid FLAC streaminfo (%ld bytes)",
1208 op.packetno = p_stream->i_packet_no++;
1209 ogg_stream_packetin( &p_stream->os, &op );
1210 p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
1211 block_ChainAppend( &p_hdr, p_og );
1215 if ( p_sys->skeleton.b_create )
1217 msg_Dbg( p_mux, "ending skeleton" );
1223 op.packetno = p_sys->skeleton.i_packet_no++;
1224 ogg_stream_packetin( &p_sys->skeleton.os, &op );
1225 p_og = OggStreamFlush( p_mux, &p_sys->skeleton.os, 0 );
1226 block_ChainAppend( &p_hdr, p_og );
1229 /* set HEADER flag */
1230 for( p_og = p_hdr; p_og != NULL; p_og = p_og->p_next )
1232 p_og->i_flags |= BLOCK_FLAG_HEADER;
1235 /* Write previous headers */
1236 p_mux->p_sys->i_pos += sout_AccessOutWrite( p_mux->p_access, p_hdr );
1241 static void OggCreateStreamFooter( sout_mux_t *p_mux, ogg_stream_t *p_stream )
1245 sout_mux_sys_t *p_sys = p_mux->p_sys;
1247 /* as stream is finished, overwrite the index, if there was any */
1248 if ( p_sys->skeleton.b_create && p_stream->skeleton.p_index
1249 && p_stream->skeleton.i_index_payload )
1251 sout_AccessOutSeek( p_mux->p_access, p_stream->skeleton.i_index_offset );
1252 OggGetSkeletonIndex( &op.packet, &op.bytes, p_stream );
1253 if ( op.packet != NULL )
1255 msg_Dbg(p_mux, "Rewriting index at %"PRId64, p_stream->skeleton.i_index_offset );
1256 ogg_stream_reset_serialno( &p_sys->skeleton.os, p_sys->skeleton.i_serial_no );
1260 op.packetno = p_stream->skeleton.i_index_packetno + 1;
1261 /* fake our stream state */
1262 p_sys->skeleton.os.pageno = p_stream->skeleton.i_index_pageno;
1263 p_sys->skeleton.os.packetno = p_stream->skeleton.i_index_packetno;
1264 p_sys->skeleton.os.granulepos = 0;
1265 p_sys->skeleton.os.b_o_s = 1;
1266 p_sys->skeleton.os.e_o_s = 0;
1267 ogg_stream_packetin( &p_sys->skeleton.os, &op );
1268 p_og = OggStreamFlush( p_mux, &p_sys->skeleton.os, 0 );
1269 sout_AccessOutWrite( p_mux->p_access, p_og );
1271 sout_AccessOutSeek( p_mux->p_access, p_sys->i_pos );
1274 /* clear skeleton */
1275 p_stream->skeleton.b_fisbone_done = false;
1276 p_stream->skeleton.b_index_done = false;
1277 p_stream->skeleton.i_index_offset = 0;
1278 p_stream->skeleton.i_index_payload = 0;
1279 p_stream->skeleton.i_last_keyframe_pos = 0;
1280 p_stream->skeleton.i_last_keyframe_time = 0;
1281 /* clear accounting */
1282 p_stream->i_num_frames = 0;
1283 p_stream->i_num_keyframes = 0;
1285 /* Write eos packet for stream. */
1290 op.granulepos = p_stream->u_last_granulepos;
1291 op.packetno = p_stream->i_packet_no++;
1292 ogg_stream_packetin( &p_stream->os, &op );
1294 /* flush it with all remaining data */
1295 if( ( p_og = OggStreamFlush( p_mux, &p_stream->os, 0 ) ) )
1298 OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
1299 p_mux->p_sys->i_pos += sout_AccessOutWrite( p_mux->p_access, p_og );
1302 ogg_stream_clear( &p_stream->os );
1305 static void OggSetDate( block_t *p_og, mtime_t i_dts, mtime_t i_length )
1311 for( p_tmp = p_og, i_count = 0; p_tmp != NULL; p_tmp = p_tmp->p_next )
1316 if( i_count == 0 ) return; /* ignore. */
1318 i_delta = i_length / i_count;
1320 for( p_tmp = p_og; p_tmp != NULL; p_tmp = p_tmp->p_next )
1322 p_tmp->i_dts = i_dts;
1323 p_tmp->i_length = i_delta;
1329 static void OggRewriteFisheadPage( sout_mux_t *p_mux )
1331 sout_mux_sys_t *p_sys = p_mux->p_sys;
1334 op.packet = calloc( 1, op.bytes );
1335 if ( op.packet != NULL )
1341 ogg_stream_reset_serialno( &p_sys->skeleton.os, p_sys->skeleton.i_serial_no );
1342 OggFillSkeletonFishead( op.packet, p_mux );
1343 ogg_stream_packetin( &p_sys->skeleton.os, &op );
1344 msg_Dbg( p_mux, "rewriting fishead at %"PRId64, p_mux->p_sys->skeleton.i_fishead_offset );
1345 sout_AccessOutSeek( p_mux->p_access, p_mux->p_sys->skeleton.i_fishead_offset );
1346 sout_AccessOutWrite( p_mux->p_access,
1347 OggStreamFlush( p_mux, &p_sys->skeleton.os, 0 ) );
1348 sout_AccessOutSeek( p_mux->p_access, p_mux->p_sys->i_pos );
1352 static bool AllocateIndex( sout_mux_t *p_mux, sout_input_t *p_input )
1354 ogg_stream_t *p_stream = (ogg_stream_t *) p_input->p_sys;
1357 if ( p_stream->i_length )
1359 uint64_t i_interval = (uint64_t)p_mux->p_sys->skeleton.i_index_intvl * 1000;
1362 if( p_input->p_fmt->i_cat == VIDEO_ES &&
1363 p_input->p_fmt->video.i_frame_rate )
1365 /* optimize for fps < 1 */
1366 i_interval= __MAX( p_mux->p_sys->skeleton.i_index_intvl * 1000,
1368 p_input->p_fmt->video.i_frame_rate_base /
1369 p_input->p_fmt->video.i_frame_rate );
1372 size_t i_tuple_size = 0;
1373 /* estimate length of pos value */
1374 if ( p_input->p_fmt->i_bitrate )
1376 i = i_interval * p_input->p_fmt->i_bitrate / 1000000;
1377 while ( i <<= 1 ) i_tuple_size++;
1381 /* Likely 64KB<<keyframe interval<<16MB */
1382 /* We can't really guess due to muxing */
1383 i_tuple_size = 24 / 8;
1386 /* add length of interval value */
1388 while ( i <<= 1 ) i_tuple_size++;
1390 i_size = i_tuple_size * ( p_stream->i_length / i_interval + 2 );
1394 i_size = ( INT64_C(3600) * 11.2 * 1000 / p_mux->p_sys->skeleton.i_index_intvl )
1395 * p_mux->p_sys->skeleton.i_index_ratio;
1396 msg_Dbg( p_mux, "No stream length, using default allocation for index" );
1398 i_size *= ( 8.0 / 7 ); /* 7bits encoding overhead */
1399 msg_Dbg( p_mux, "allocating %"PRId64" bytes for index", i_size );
1400 p_stream->skeleton.p_index = calloc( i_size, sizeof(uint8_t) );
1401 if ( !p_stream->skeleton.p_index ) return false;
1402 p_stream->skeleton.i_index_size = i_size;
1403 p_stream->skeleton.i_index_payload = 0;
1407 /*****************************************************************************
1408 * Mux: multiplex available data in input fifos into the Ogg bitstream
1409 *****************************************************************************/
1410 static int Mux( sout_mux_t *p_mux )
1412 sout_mux_sys_t *p_sys = p_mux->p_sys;
1415 /* End any stream that ends in that group */
1416 if ( p_sys->i_del_streams )
1418 /* Remove deleted logical streams */
1419 for( int i = 0; i < p_sys->i_del_streams; i++ )
1421 OggCreateStreamFooter( p_mux, p_sys->pp_del_streams[i] );
1422 FREENULL( p_sys->pp_del_streams[i]->p_oggds_header );
1423 FREENULL( p_sys->pp_del_streams[i] );
1425 FREENULL( p_sys->pp_del_streams );
1426 p_sys->i_del_streams = 0;
1429 if ( p_sys->i_streams == 0 )
1431 /* All streams have been deleted, or none have ever been created
1432 From this point, we are allowed to start a new group of logical streams */
1433 p_sys->skeleton.b_head_done = false;
1434 p_sys->b_can_add_streams = true;
1435 p_sys->i_segment_start = p_sys->i_pos;
1438 if ( p_sys->i_add_streams )
1440 if ( !p_sys->b_can_add_streams )
1442 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);
1443 msg_Warn( p_mux, "Resetting and setting new identity to current streams");
1445 /* resetting all active streams */
1446 for ( int i=0; i < p_mux->p_sys->i_streams; i++ )
1448 ogg_stream_t * p_stream = (ogg_stream_t *) p_mux->pp_inputs[i]->p_sys;
1449 if ( p_stream->b_finished || !p_stream->b_started ) continue;
1450 OggCreateStreamFooter( p_mux, p_stream );
1451 p_stream->i_serial_no = p_sys->i_next_serial_no++;
1452 p_stream->i_packet_no = 0;
1453 p_stream->b_finished = true;
1456 /* rewrite fishead with final values */
1457 if ( p_sys->skeleton.b_head_done )
1459 OggRewriteFisheadPage( p_mux );
1462 p_sys->b_can_add_streams = true;
1463 p_sys->skeleton.b_head_done = false;
1464 p_sys->i_segment_start = p_sys->i_pos;
1467 /* Open new ogg stream */
1468 if( sout_MuxGetStream( p_mux, 1, &i_dts) < 0 )
1470 msg_Dbg( p_mux, "waiting for data..." );
1473 msg_Dbg( p_mux, "writing streams headers" );
1474 p_sys->i_start_dts = i_dts;
1475 p_sys->i_streams = p_mux->i_nb_inputs;
1476 p_sys->i_del_streams = 0;
1477 p_sys->i_add_streams = 0;
1478 p_sys->skeleton.b_create = true;
1480 if ( ! OggCreateHeaders( p_mux ) )
1483 /* If we're switching to end of headers, then that's data start */
1484 if ( p_sys->b_can_add_streams )
1486 msg_Dbg( p_mux, "data starts from %"PRId64, p_sys->i_pos );
1487 p_sys->i_data_start = p_sys->i_pos;
1490 /* Since we started sending secondaryheader or data pages,
1491 * we're no longer allowed to create new streams, until all streams end */
1492 p_sys->b_can_add_streams = false;
1495 /* Do the regular data mux thing */
1498 int i_stream = sout_MuxGetStream( p_mux, 1, NULL );
1501 MuxBlock( p_mux, p_mux->pp_inputs[i_stream] );
1507 static int MuxBlock( sout_mux_t *p_mux, sout_input_t *p_input )
1509 sout_mux_sys_t *p_sys = p_mux->p_sys;
1510 ogg_stream_t *p_stream = (ogg_stream_t*)p_input->p_sys;
1511 block_t *p_data = block_FifoGet( p_input->p_fifo );
1512 block_t *p_og = NULL;
1516 if( p_stream->i_fourcc != VLC_CODEC_VORBIS &&
1517 p_stream->i_fourcc != VLC_CODEC_FLAC &&
1518 p_stream->i_fourcc != VLC_CODEC_SPEEX &&
1519 p_stream->i_fourcc != VLC_CODEC_OPUS &&
1520 p_stream->i_fourcc != VLC_CODEC_THEORA &&
1521 p_stream->i_fourcc != VLC_CODEC_DIRAC )
1523 p_data = block_Realloc( p_data, 1, p_data->i_buffer );
1524 p_data->p_buffer[0] = PACKET_IS_SYNCPOINT; // FIXME
1527 op.packet = p_data->p_buffer;
1528 op.bytes = p_data->i_buffer;
1531 op.packetno = p_stream->i_packet_no++;
1534 if( p_stream->i_cat == AUDIO_ES )
1536 if( p_stream->i_fourcc == VLC_CODEC_VORBIS ||
1537 p_stream->i_fourcc == VLC_CODEC_FLAC ||
1538 p_stream->i_fourcc == VLC_CODEC_OPUS ||
1539 p_stream->i_fourcc == VLC_CODEC_SPEEX )
1541 /* number of sample from begining + current packet */
1543 ( p_data->i_dts - p_sys->i_start_dts + p_data->i_length ) *
1544 (mtime_t)p_input->p_fmt->audio.i_rate / INT64_C(1000000);
1546 i_time = p_data->i_dts - p_sys->i_start_dts;
1547 AddIndexEntry( p_mux, i_time, p_input );
1549 else if( p_stream->p_oggds_header )
1551 /* number of sample from begining */
1552 op.granulepos = ( p_data->i_dts - p_sys->i_start_dts ) *
1553 p_stream->p_oggds_header->i_samples_per_unit / INT64_C(1000000);
1556 else if( p_stream->i_cat == VIDEO_ES )
1558 if( p_stream->i_fourcc == VLC_CODEC_THEORA )
1560 p_stream->i_num_frames++;
1561 if( p_data->i_flags & BLOCK_FLAG_TYPE_I )
1563 p_stream->i_num_keyframes++;
1564 p_stream->i_last_keyframe = p_stream->i_num_frames;
1566 /* presentation time */
1567 i_time = INT64_C(1000000) * ( p_stream->i_num_frames - 1 ) *
1568 p_input->p_fmt->video.i_frame_rate_base / p_input->p_fmt->video.i_frame_rate;
1569 AddIndexEntry( p_mux, i_time, p_input );
1572 op.granulepos = (p_stream->i_last_keyframe << p_stream->i_keyframe_granule_shift )
1573 | (p_stream->i_num_frames-p_stream->i_last_keyframe);
1575 else if( p_stream->i_fourcc == VLC_CODEC_DIRAC )
1577 mtime_t dt = (p_data->i_dts - p_sys->i_start_dts + 1)
1578 * p_input->p_fmt->video.i_frame_rate *2
1579 / p_input->p_fmt->video.i_frame_rate_base
1581 mtime_t delay = (p_data->i_pts - p_data->i_dts + 1)
1582 * p_input->p_fmt->video.i_frame_rate *2
1583 / p_input->p_fmt->video.i_frame_rate_base
1585 if( p_data->i_flags & BLOCK_FLAG_TYPE_I )
1586 p_stream->i_last_keyframe = dt;
1587 mtime_t dist = dt - p_stream->i_last_keyframe;
1588 op.granulepos = dt << 31 | (dist&0xff00) << 14
1589 | (delay&0x1fff) << 9 | (dist&0xff);
1590 AddIndexEntry( p_mux, dt, p_input );
1592 else if( p_stream->p_oggds_header )
1593 op.granulepos = ( p_data->i_dts - p_sys->i_start_dts ) * INT64_C(10) /
1594 p_stream->p_oggds_header->i_time_unit;
1596 else if( p_stream->i_cat == SPU_ES )
1598 /* granulepos is in millisec */
1599 op.granulepos = ( p_data->i_dts - p_sys->i_start_dts ) / 1000;
1602 return VLC_EGENERIC;
1604 p_stream->u_last_granulepos = op.granulepos;
1605 ogg_stream_packetin( &p_stream->os, &op );
1607 if( p_stream->i_cat == SPU_ES ||
1608 p_stream->i_fourcc == VLC_CODEC_SPEEX ||
1609 p_stream->i_fourcc == VLC_CODEC_DIRAC )
1611 /* Subtitles or Speex packets are quite small so they
1612 * need to be flushed to be sent on time */
1613 /* The OggDirac mapping suggests ever so strongly that a
1614 * page flush occurs after each OggDirac packet, so to make
1615 * the timestamps unambiguous */
1616 p_og = OggStreamFlush( p_mux, &p_stream->os, p_data->i_dts );
1620 p_og = OggStreamPageOut( p_mux, &p_stream->os, p_data->i_dts );
1625 OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
1626 p_stream->i_dts = -1;
1627 p_stream->i_length = 0;
1629 p_mux->p_sys->i_pos += sout_AccessOutWrite( p_mux->p_access, p_og );
1633 if( p_stream->i_dts < 0 )
1635 p_stream->i_dts = p_data->i_dts;
1637 p_stream->i_length += p_data->i_length;
1640 block_Release( p_data );