1 /*****************************************************************************
2 * ogg.c: ogg muxer module for vlc
3 *****************************************************************************
4 * Copyright (C) 2001, 2002 VideoLAN
5 * $Id: ogg.c,v 1.17 2003/10/09 19:31:38 gbazin Exp $
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8 * Gildas Bazin <gbazin@netcourrier.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 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 General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
30 #include <sys/types.h>
33 #include <vlc/input.h>
40 /*****************************************************************************
42 *****************************************************************************/
43 static int Open ( vlc_object_t * );
44 static void Close ( vlc_object_t * );
46 static int Capability(sout_mux_t *, int, void *, void * );
47 static int AddStream( sout_mux_t *, sout_input_t * );
48 static int DelStream( sout_mux_t *, sout_input_t * );
49 static int Mux ( sout_mux_t * );
51 static sout_buffer_t *OggCreateHeader( sout_mux_t *, mtime_t );
52 static sout_buffer_t *OggCreateFooter( sout_mux_t *, mtime_t );
54 /*****************************************************************************
56 *****************************************************************************/
58 set_description( _("Ogg/ogm muxer") );
59 set_capability( "sout mux", 10 );
60 add_shortcut( "ogg" );
61 add_shortcut( "ogm" );
62 set_callbacks( Open, Close );
65 /*****************************************************************************
67 *****************************************************************************/
68 #define FREE( p ) if( p ) { free( p ); (p) = NULL; }
70 /* Structures used for OggDS headers used in ogm files */
72 #define PACKET_TYPE_HEADER 0x01
73 #define PACKET_TYPE_COMMENT 0x03
74 #define PACKET_IS_SYNCPOINT 0x08
77 #ifdef HAVE_ATTRIBUTE_PACKED
78 __attribute__((__packed__))
83 } oggds_header_video_t;
86 #ifdef HAVE_ATTRIBUTE_PACKED
87 __attribute__((__packed__))
91 int16_t i_block_align;
92 int32_t i_avgbytespersec;
93 } oggds_header_audio_t;
96 #ifdef HAVE_ATTRIBUTE_PACKED
97 __attribute__((__packed__))
100 uint8_t i_packet_type;
108 int64_t i_samples_per_unit;
109 int32_t i_default_len;
111 int32_t i_buffer_size;
112 int16_t i_bits_per_sample;
114 int16_t i_padding_0; /* Because the original is using MSVC packing style */
118 oggds_header_video_t video;
119 oggds_header_audio_t audio;
122 int32_t i_padding_1; /* Because the original is using MSVC packing style */
126 /* Helper writer functions */
128 #define SetWLE( p, v ) _SetWLE( (uint8_t*)p, v)
129 static void _SetWLE( uint8_t *p, uint16_t i_dw )
131 p[1] = ( i_dw >> 8 )&0xff;
132 p[0] = ( i_dw )&0xff;
135 #define SetDWLE( p, v ) _SetDWLE( (uint8_t*)p, v)
136 static void _SetDWLE( uint8_t *p, uint32_t i_dw )
138 p[3] = ( i_dw >> 24 )&0xff;
139 p[2] = ( i_dw >> 16 )&0xff;
140 p[1] = ( i_dw >> 8 )&0xff;
141 p[0] = ( i_dw )&0xff;
143 #define SetQWLE( p, v ) _SetQWLE( (uint8_t*)p, v)
144 static void _SetQWLE( uint8_t *p, uint64_t i_qw )
146 SetDWLE( p, i_qw&0xffffffff );
147 SetDWLE( p+4, ( i_qw >> 32)&0xffffffff );
151 * TODO move this function to src/stream_output.c (used by nearly all muxers)
153 static int MuxGetStream( sout_mux_t *p_mux, int *pi_stream, mtime_t *pi_dts )
159 for( i = 0, i_dts = 0, i_stream = -1; i < p_mux->i_nb_inputs; i++ )
163 p_fifo = p_mux->pp_inputs[i]->p_fifo;
165 /* We don't really need to have anything in the SPU fifo */
166 if( p_mux->pp_inputs[i]->p_fmt->i_cat == SPU_ES &&
167 p_fifo->i_depth == 0 ) continue;
169 if( p_fifo->i_depth > 2 ||
170 /* Special case for SPUs */
171 ( p_mux->pp_inputs[i]->p_fmt->i_cat == SPU_ES &&
172 p_fifo->i_depth > 0 ) )
174 sout_buffer_t *p_buf;
176 p_buf = sout_FifoShow( p_fifo );
177 if( i_stream < 0 || p_buf->i_dts < i_dts )
179 i_dts = p_buf->i_dts;
185 // wait that all fifo have at least 3 packets (3 vorbis headers)
191 *pi_stream = i_stream;
200 /*****************************************************************************
201 * Definitions of structures and functions used by this plugins
202 *****************************************************************************/
214 int i_keyframe_granule_shift; /* Theora only */
217 oggds_header_t oggds_header;
219 sout_buffer_t *pp_sout_headers[3];
224 struct sout_mux_sys_t
230 /* number of logical streams pending to be added */
233 /* logical streams pending to be deleted */
235 ogg_stream_t **pp_del_streams;
238 static void OggSetDate( sout_buffer_t *, mtime_t , mtime_t );
239 static sout_buffer_t *OggStreamFlush( sout_mux_t *, ogg_stream_state *,
242 /*****************************************************************************
244 *****************************************************************************/
245 static int Open( vlc_object_t *p_this )
247 sout_mux_t *p_mux = (sout_mux_t*)p_this;
248 sout_mux_sys_t *p_sys;
250 msg_Info( p_mux, "Open" );
252 p_sys = malloc( sizeof( sout_mux_sys_t ) );
253 p_sys->i_streams = 0;
254 p_sys->i_add_streams = 0;
255 p_sys->i_del_streams = 0;
256 p_sys->pp_del_streams = 0;
258 p_mux->p_sys = p_sys;
259 p_mux->pf_capacity = Capability;
260 p_mux->pf_addstream = AddStream;
261 p_mux->pf_delstream = DelStream;
263 p_mux->i_preheader = 1;
268 /*****************************************************************************
269 * Close: Finalize ogg bitstream and close muxer
270 *****************************************************************************/
271 static void Close( vlc_object_t * p_this )
273 sout_mux_t *p_mux = (sout_mux_t*)p_this;
274 sout_mux_sys_t *p_sys = p_mux->p_sys;
276 msg_Info( p_mux, "Close" );
278 if( p_sys->i_del_streams )
280 sout_buffer_t *p_og = NULL;
284 /* Close the current ogg stream */
285 msg_Dbg( p_mux, "writing footer" );
286 sout_BufferChain( &p_og, OggCreateFooter( p_mux, 0 ) );
288 /* Remove deleted logical streams */
289 for( i = 0; i < p_sys->i_del_streams; i++ )
291 i_dts = p_sys->pp_del_streams[i]->i_dts;
292 ogg_stream_clear( &p_sys->pp_del_streams[i]->os );
293 FREE( p_sys->pp_del_streams[i] );
295 FREE( p_sys->pp_del_streams );
296 p_sys->i_streams -= p_sys->i_del_streams;
299 OggSetDate( p_og, i_dts, 0 );
300 sout_AccessOutWrite( p_mux->p_access, p_og );
306 static int Capability( sout_mux_t *p_mux, int i_query, void *p_args,
311 case SOUT_MUX_CAP_GET_ADD_STREAM_ANY_TIME:
312 *(vlc_bool_t*)p_answer = VLC_TRUE;
313 return( SOUT_MUX_CAP_ERR_OK );
315 return( SOUT_MUX_CAP_ERR_UNIMPLEMENTED );
319 /*****************************************************************************
320 * AddStream: Add an elementary stream to the muxed stream
321 *****************************************************************************/
322 static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
324 sout_mux_sys_t *p_sys = p_mux->p_sys;
325 ogg_stream_t *p_stream;
327 msg_Dbg( p_mux, "adding input" );
329 p_input->p_sys = (void *)p_stream = malloc( sizeof( ogg_stream_t ) );
331 p_stream->i_cat = p_input->p_fmt->i_cat;
332 p_stream->i_fourcc = p_input->p_fmt->i_fourcc;
333 p_stream->i_serial_no = rand();
334 p_stream->i_packet_no = 0;
336 p_stream->i_sout_headers = 0;
338 memset( &p_stream->oggds_header, 0, sizeof(p_stream->oggds_header) );
339 p_stream->oggds_header.i_packet_type = PACKET_TYPE_HEADER;
340 switch( p_input->p_fmt->i_cat )
343 switch( p_stream->i_fourcc )
345 case VLC_FOURCC( 'm', 'p','4', 'v' ):
346 case VLC_FOURCC( 'D', 'I','V', '3' ):
347 memcpy( p_stream->oggds_header.stream_type, "video", 5 );
348 if( p_stream->i_fourcc == VLC_FOURCC( 'm', 'p','4', 'v' ) )
350 memcpy( p_stream->oggds_header.sub_type, "XVID", 4 );
352 else if( p_stream->i_fourcc == VLC_FOURCC( 'D', 'I','V', '3' ) )
354 memcpy( p_stream->oggds_header.sub_type, "DIV3", 4 );
356 SetDWLE( &p_stream->oggds_header.i_size,
357 sizeof( oggds_header_t ) - 1);
358 SetQWLE( &p_stream->oggds_header.i_time_unit,
359 I64C(10000000)/(int64_t)25 ); // FIXME (25fps)
360 SetQWLE( &p_stream->oggds_header.i_samples_per_unit, 1 );
361 SetDWLE( &p_stream->oggds_header.i_default_len, 1 ); /* ??? */
362 SetDWLE( &p_stream->oggds_header.i_buffer_size, 1024*1024 );
363 SetWLE( &p_stream->oggds_header.i_bits_per_sample, 0 );
364 SetDWLE( &p_stream->oggds_header.header.video.i_width,
365 p_input->p_fmt->i_width );
366 SetDWLE( &p_stream->oggds_header.header.video.i_height,
367 p_input->p_fmt->i_height );
368 msg_Dbg( p_mux, "mp4v/div3 stream" );
371 case VLC_FOURCC( 't', 'h', 'e', 'o' ):
372 msg_Dbg( p_mux, "theora stream" );
376 FREE( p_input->p_sys );
377 return( VLC_EGENERIC );
382 switch( p_stream->i_fourcc )
384 case VLC_FOURCC( 'm', 'p','g', 'a' ):
385 case VLC_FOURCC( 'a', '5','2', ' ' ):
386 memcpy( p_stream->oggds_header.stream_type, "audio", 5 );
387 if( p_stream->i_fourcc == VLC_FOURCC( 'm', 'p','g', 'a' ) )
389 memcpy( p_stream->oggds_header.sub_type, "55 ", 4 );
391 else if( p_stream->i_fourcc == VLC_FOURCC( 'a', '5','2', ' ' ) )
393 memcpy( p_stream->oggds_header.sub_type, "2000", 4 );
395 SetDWLE( &p_stream->oggds_header.i_size,
396 sizeof( oggds_header_t ) - 1);
397 SetQWLE( &p_stream->oggds_header.i_time_unit, 0 /* not used */ );
398 SetDWLE( &p_stream->oggds_header.i_default_len, 1 );
399 SetDWLE( &p_stream->oggds_header.i_buffer_size, 30*1024 );
400 SetQWLE( &p_stream->oggds_header.i_samples_per_unit,
401 p_input->p_fmt->i_sample_rate );
402 SetWLE( &p_stream->oggds_header.i_bits_per_sample, 0 );
403 SetDWLE( &p_stream->oggds_header.header.audio.i_channels,
404 p_input->p_fmt->i_channels );
405 SetDWLE( &p_stream->oggds_header.header.audio.i_block_align,
406 p_input->p_fmt->i_block_align );
407 SetDWLE( &p_stream->oggds_header.header.audio.i_avgbytespersec, 0);
408 msg_Dbg( p_mux, "mpga/a52 stream" );
411 case VLC_FOURCC( 'v', 'o', 'r', 'b' ):
412 msg_Dbg( p_mux, "vorbis stream" );
416 FREE( p_input->p_sys );
417 return( VLC_EGENERIC );
422 switch( p_stream->i_fourcc )
424 case VLC_FOURCC( 's', 'u','b', 't' ):
425 memcpy( p_stream->oggds_header.stream_type, "text", 4 );
426 msg_Dbg( p_mux, "subtitles stream" );
430 FREE( p_input->p_sys );
431 return( VLC_EGENERIC );
435 FREE( p_input->p_sys );
436 return( VLC_EGENERIC );
439 p_stream->b_new = VLC_TRUE;
441 p_sys->i_add_streams++;
443 return( VLC_SUCCESS );
446 /*****************************************************************************
447 * DelStream: Delete an elementary stream from the muxed stream
448 *****************************************************************************/
449 static int DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
451 sout_mux_sys_t *p_sys = p_mux->p_sys;
452 ogg_stream_t *p_stream = (ogg_stream_t*)p_input->p_sys;
455 msg_Dbg( p_mux, "removing input" );
457 /* flush all remaining data */
462 if( ( p_og = OggStreamFlush( p_mux, &p_stream->os, 0 ) ) )
464 OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
465 sout_AccessOutWrite( p_mux->p_access, p_og );
468 for( i = 0; i < p_stream->i_sout_headers; i++ )
470 sout_BufferDelete( p_mux->p_sout, p_stream->pp_sout_headers[i] );
471 p_stream->i_sout_headers = 0;
474 /* move input in delete queue */
475 if( !p_stream->b_new )
477 p_sys->pp_del_streams = realloc( p_sys->pp_del_streams,
478 (p_sys->i_del_streams + 1) *
479 sizeof(ogg_stream_t *) );
480 p_sys->pp_del_streams[p_sys->i_del_streams++] = p_stream;
484 /* Wasn't already added so get rid of it */
485 ogg_stream_clear( &p_stream->os );
487 p_sys->i_add_streams--;
491 p_input->p_sys = NULL;
496 /*****************************************************************************
497 * Ogg bitstream manipulation routines
498 *****************************************************************************/
499 static sout_buffer_t *OggStreamFlush( sout_mux_t *p_mux,
500 ogg_stream_state *p_os, mtime_t i_pts )
502 sout_buffer_t *p_og, *p_og_first = NULL;
510 if( ( i_result = ogg_stream_flush( p_os, &og ) ) == 0 )
515 i_size = og.header_len + og.body_len;
516 p_og = sout_BufferNew( p_mux->p_sout, i_size);
518 memcpy( p_og->p_buffer, og.header, og.header_len );
519 memcpy( p_og->p_buffer + og.header_len, og.body, og.body_len );
520 p_og->i_size = i_size;
525 i_pts = 0; // write it only once
527 sout_BufferChain( &p_og_first, p_og );
530 return( p_og_first );
533 static sout_buffer_t *OggStreamPageOut( sout_mux_t *p_mux,
534 ogg_stream_state *p_os, mtime_t i_pts )
536 sout_buffer_t *p_og, *p_og_first = NULL;
544 if( ( i_result = ogg_stream_pageout( p_os, &og ) ) == 0 )
549 i_size = og.header_len + og.body_len;
550 p_og = sout_BufferNew( p_mux->p_sout, i_size);
552 memcpy( p_og->p_buffer, og.header, og.header_len );
553 memcpy( p_og->p_buffer + og.header_len, og.body, og.body_len );
554 p_og->i_size = i_size;
559 i_pts = 0; // write them only once
561 sout_BufferChain( &p_og_first, p_og );
564 return( p_og_first );
567 static sout_buffer_t *OggCreateHeader( sout_mux_t *p_mux, mtime_t i_dts )
569 sout_buffer_t *p_hdr = NULL;
574 /* Write header for each stream. All b_o_s (beginning of stream) packets
575 * must appear first in the ogg stream so we take care of them first. */
576 for( i = 0; i < p_mux->i_nb_inputs; i++ )
578 ogg_stream_t *p_stream = (ogg_stream_t*)p_mux->pp_inputs[i]->p_sys;
579 p_stream->b_new = VLC_FALSE;
581 msg_Dbg( p_mux, "creating header for %4.4s",
582 (char *)&p_stream->i_fourcc );
584 ogg_stream_init( &p_stream->os, p_stream->i_serial_no );
585 p_stream->i_packet_no = 0;
587 if( p_stream->i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) ||
588 p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
590 /* Special case, headers are already there in the
591 * incoming stream or we backed them up earlier */
593 /* first packet in order: vorbis/theora info */
594 if( !p_stream->i_sout_headers )
596 p_og = sout_FifoGet( p_mux->pp_inputs[i]->p_fifo );
597 op.packet = p_og->p_buffer;
598 op.bytes = p_og->i_size;
602 op.packetno = p_stream->i_packet_no++;
603 ogg_stream_packetin( &p_stream->os, &op );
604 p_stream->pp_sout_headers[0] =
605 OggStreamFlush( p_mux, &p_stream->os, 0 );
606 p_stream->i_sout_headers++;
608 p_og = sout_BufferDuplicate( p_mux->p_sout,
609 p_stream->pp_sout_headers[0] );
611 /* Get keyframe_granule_shift for theora granulepos calculation */
612 if( p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
614 int i_keyframe_frequency_force = 1 << (op.packet[36] >> 3);
616 /* granule_shift = i_log( frequency_force -1 ) */
617 p_stream->i_keyframe_granule_shift = 0;
618 i_keyframe_frequency_force--;
619 while( i_keyframe_frequency_force )
621 p_stream->i_keyframe_granule_shift++;
622 i_keyframe_frequency_force >>= 1;
629 op.packet = (uint8_t*)&p_stream->oggds_header;
630 op.bytes = sizeof( oggds_header_t );
634 op.packetno = p_stream->i_packet_no++;
635 ogg_stream_packetin( &p_stream->os, &op );
636 p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
639 sout_BufferChain( &p_hdr, p_og );
642 /* Take care of the non b_o_s headers */
643 for( i = 0; i < p_mux->i_nb_inputs; i++ )
645 ogg_stream_t *p_stream = (ogg_stream_t*)p_mux->pp_inputs[i]->p_sys;
647 if( p_stream->i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) ||
648 p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
650 /* Special case, headers are already there in the incoming stream.
651 * We need to gather them an mark them as headers. */
653 for( j = 0; j < 2; j++ )
655 if( p_stream->i_sout_headers < j + 2 )
657 /* next packets in order: comments and codebooks */
658 p_og = sout_FifoGet( p_mux->pp_inputs[i]->p_fifo );
659 op.packet = p_og->p_buffer;
660 op.bytes = p_og->i_size;
664 op.packetno = p_stream->i_packet_no++;
665 ogg_stream_packetin( &p_stream->os, &op );
666 p_stream->pp_sout_headers[j+1] =
667 OggStreamFlush( p_mux, &p_stream->os, 0 );
668 p_stream->i_sout_headers++;
671 p_og = sout_BufferDuplicate( p_mux->p_sout,
672 p_stream->pp_sout_headers[j+1] );
673 sout_BufferChain( &p_hdr, p_og );
682 com[0] = PACKET_TYPE_COMMENT;
683 i_com = snprintf( &com[1], 128, VERSION" stream output" ) + 1;
689 op.packetno = p_stream->i_packet_no++;
690 ogg_stream_packetin( &p_stream->os, &op );
691 p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
692 sout_BufferChain( &p_hdr, p_og );
696 /* set HEADER flag */
697 for( p_og = p_hdr; p_og != NULL; p_og = p_og->p_next )
699 p_og->i_flags |= SOUT_BUFFER_FLAGS_HEADER;
704 static sout_buffer_t *OggCreateFooter( sout_mux_t *p_mux, mtime_t i_dts )
706 sout_mux_sys_t *p_sys = p_mux->p_sys;
707 sout_buffer_t *p_hdr = NULL;
712 /* flush all remaining data */
713 for( i = 0; i < p_mux->i_nb_inputs; i++ )
715 ogg_stream_t *p_stream = p_mux->pp_inputs[i]->p_sys;
717 /* skip newly added streams */
718 if( p_stream->b_new ) continue;
720 if( ( p_og = OggStreamFlush( p_mux, &p_stream->os, 0 ) ) )
722 OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
723 sout_AccessOutWrite( p_mux->p_access, p_og );
727 /* Write eos packets for each stream. */
728 for( i = 0; i < p_mux->i_nb_inputs; i++ )
730 ogg_stream_t *p_stream = p_mux->pp_inputs[i]->p_sys;
732 /* skip newly added streams */
733 if( p_stream->b_new ) continue;
740 op.packetno = p_stream->i_packet_no++;
741 ogg_stream_packetin( &p_stream->os, &op );
743 p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
744 sout_BufferChain( &p_hdr, p_og );
745 ogg_stream_clear( &p_stream->os );
748 for( i = 0; i < p_sys->i_del_streams; i++ )
755 op.packetno = p_sys->pp_del_streams[i]->i_packet_no++;
756 ogg_stream_packetin( &p_sys->pp_del_streams[i]->os, &op );
758 p_og = OggStreamFlush( p_mux, &p_sys->pp_del_streams[i]->os, 0 );
759 sout_BufferChain( &p_hdr, p_og );
760 ogg_stream_clear( &p_sys->pp_del_streams[i]->os );
766 static void OggSetDate( sout_buffer_t *p_og, mtime_t i_dts, mtime_t i_length )
769 sout_buffer_t *p_tmp;
772 for( p_tmp = p_og, i_count = 0; p_tmp != NULL; p_tmp = p_tmp->p_next )
776 i_delta = i_length / i_count;
778 for( p_tmp = p_og; p_tmp != NULL; p_tmp = p_tmp->p_next )
780 p_tmp->i_dts = i_dts;
781 p_tmp->i_length = i_delta;
787 /*****************************************************************************
788 * Mux: multiplex available data in input fifos into the Ogg bitstream
789 *****************************************************************************/
790 static int Mux( sout_mux_t *p_mux )
792 sout_mux_sys_t *p_sys = p_mux->p_sys;
793 sout_buffer_t *p_og = NULL;
797 if( p_sys->i_add_streams || p_sys->i_del_streams )
799 /* Open new ogg stream */
800 if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
802 msg_Dbg( p_mux, "waiting for data..." );
803 return( VLC_SUCCESS );
806 if( p_sys->i_streams )
808 /* Close current ogg stream */
811 msg_Dbg( p_mux, "writing footer" );
812 sout_BufferChain( &p_og, OggCreateFooter( p_mux, 0 ) );
814 /* Remove deleted logical streams */
815 for( i = 0; i < p_sys->i_del_streams; i++ )
817 FREE( p_sys->pp_del_streams[i] );
819 FREE( p_sys->pp_del_streams );
820 p_sys->i_streams = 0;
823 msg_Dbg( p_mux, "writing header" );
824 p_sys->i_start_dts = i_dts;
825 p_sys->i_streams = p_mux->i_nb_inputs;
826 p_sys->i_del_streams = 0;
827 p_sys->i_add_streams = 0;
828 sout_BufferChain( &p_og, OggCreateHeader( p_mux, i_dts ) );
830 /* Write header and/or footer */
831 OggSetDate( p_og, i_dts, 0 );
832 sout_AccessOutWrite( p_mux->p_access, p_og );
838 sout_input_t *p_input;
839 ogg_stream_t *p_stream;
840 sout_buffer_t *p_data;
843 if( MuxGetStream( p_mux, &i_stream, &i_dts) < 0 )
845 return( VLC_SUCCESS );
848 p_input = p_mux->pp_inputs[i_stream];
849 p_stream = (ogg_stream_t*)p_input->p_sys;
850 p_data = sout_FifoGet( p_input->p_fifo );
852 if( p_stream->i_fourcc != VLC_FOURCC( 'v', 'o', 'r', 'b' ) &&
853 p_stream->i_fourcc != VLC_FOURCC( 't', 'h', 'e', 'o' ) )
855 sout_BufferReallocFromPreHeader( p_mux->p_sout, p_data, 1 );
856 p_data->p_buffer[0] = PACKET_IS_SYNCPOINT; // FIXME
859 op.packet = p_data->p_buffer;
860 op.bytes = p_data->i_size;
863 op.packetno = p_stream->i_packet_no++;
865 if( p_stream->i_cat == AUDIO_ES )
867 if( p_stream->i_fourcc == VLC_FOURCC( 'v', 'o', 'r', 'b' ) )
869 /* number of sample from begining + current packet */
871 ( i_dts + p_data->i_length - p_sys->i_start_dts ) *
872 p_input->p_fmt->i_sample_rate / I64C(1000000);
876 /* number of sample from begining */
877 op.granulepos = ( i_dts - p_sys->i_start_dts ) *
878 p_stream->oggds_header.i_samples_per_unit / I64C(1000000);
881 else if( p_stream->i_cat == VIDEO_ES )
883 if( p_stream->i_fourcc == VLC_FOURCC( 't', 'h', 'e', 'o' ) )
885 /* FIXME, we assume only keyframes and 25fps */
886 op.granulepos = ( ( i_dts - p_sys->i_start_dts ) * I64C(25)
887 / I64C(1000000) ) << p_stream->i_keyframe_granule_shift;
890 op.granulepos = ( i_dts - p_sys->i_start_dts ) * I64C(10) /
891 p_stream->oggds_header.i_time_unit;
893 else if( p_stream->i_cat == SPU_ES )
895 /* granulepos is in milisec */
896 op.granulepos = ( i_dts - p_sys->i_start_dts ) / 1000;
899 ogg_stream_packetin( &p_stream->os, &op );
901 if( p_stream->i_cat == SPU_ES )
903 /* Subtitles need to be flushed to be sent on time */
904 sout_BufferChain( &p_og, OggStreamFlush( p_mux, &p_stream->os,
909 sout_BufferChain( &p_og, OggStreamPageOut( p_mux, &p_stream->os,
915 OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
916 p_stream->i_dts = -1;
917 p_stream->i_length = 0;
919 sout_AccessOutWrite( p_mux->p_access, p_og );
925 if( p_stream->i_dts < 0 )
927 p_stream->i_dts = p_data->i_dts;
929 p_stream->i_length += p_data->i_length;
932 sout_BufferDelete( p_mux->p_sout, p_data );
935 return( VLC_SUCCESS );