--- /dev/null
+/*****************************************************************************
+ * vorbis.c: vorbis decoder module making use of libvorbis.
+ *****************************************************************************
+ * Copyright (C) 1999-2001 VideoLAN
+ * $Id: vorbis.c,v 1.1 2002/10/24 09:30:47 gbazin Exp $
+ *
+ * Authors: Gildas Bazin <gbazin@netcourrier.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <stdlib.h> /* malloc(), free() */
+#include <string.h> /* memcpy(), memset() */
+
+#include <vlc/vlc.h>
+#include <vlc/aout.h>
+#include <vlc/decoder.h>
+#include <input_ext-dec.h>
+
+#include <vlc/input.h>
+
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+
+/*****************************************************************************
+ * dec_thread_t : vorbis decoder thread descriptor
+ *****************************************************************************/
+typedef struct dec_thread_t
+{
+ /*
+ * Thread properties
+ */
+ vlc_thread_t thread_id; /* id for thread functions */
+
+ /*
+ * Vorbis properties
+ */
+ vorbis_info vi; /* struct that stores all the static vorbis bitstream
+ settings */
+ vorbis_comment vc; /* struct that stores all the bitstream user
+ * comments */
+ vorbis_dsp_state vd; /* central working state for the packet->PCM
+ * decoder */
+ vorbis_block vb; /* local working space for packet->PCM decode */
+
+ /*
+ * Input properties
+ */
+ decoder_fifo_t * p_fifo; /* stores the PES stream data */
+
+ /*
+ * Output properties
+ */
+ aout_instance_t *p_aout;
+ aout_input_t *p_aout_input;
+ audio_sample_format_t output_format;
+ audio_date_t end_date;
+
+} dec_thread_t;
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int OpenDecoder ( vlc_object_t * );
+static int RunDecoder ( decoder_fifo_t * );
+static void CloseDecoder ( dec_thread_t * );
+
+static void DecodePacket ( dec_thread_t * );
+static int GetOggPacket ( dec_thread_t *, ogg_packet *, mtime_t *, int );
+
+static void Interleave ( float *, const float **, int, int );
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+vlc_module_begin();
+ set_description( _("Vorbis decoder module") );
+ set_capability( "decoder", 100 );
+ set_callbacks( OpenDecoder, NULL );
+vlc_module_end();
+
+/*****************************************************************************
+ * OpenDecoder: probe the decoder and return score
+ *****************************************************************************/
+static int OpenDecoder( vlc_object_t *p_this )
+{
+ decoder_fifo_t *p_fifo = (decoder_fifo_t*) p_this;
+
+ if( p_fifo->i_fourcc != VLC_FOURCC('v','o','r','b') )
+ {
+ return VLC_EGENERIC;
+ }
+
+ p_fifo->pf_run = RunDecoder;
+ return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * RunDecoder: the vorbis decoder
+ *****************************************************************************/
+static int RunDecoder( decoder_fifo_t * p_fifo )
+{
+ dec_thread_t *p_dec;
+ ogg_packet oggpacket;
+ mtime_t i_pts;
+
+ /* Allocate the memory needed to store the thread's structure */
+ if( (p_dec = (dec_thread_t *)malloc (sizeof(dec_thread_t)) )
+ == NULL)
+ {
+ msg_Err( p_fifo, "out of memory" );
+ goto error;
+ }
+
+ /* Initialize the thread properties */
+ p_dec->p_fifo = p_fifo;
+
+ /* Take care of the initial Vorbis header */
+ vorbis_info_init( &p_dec->vi );
+ vorbis_comment_init( &p_dec->vc );
+
+ if( GetOggPacket( p_dec, &oggpacket, &i_pts, 0 ) != VLC_SUCCESS )
+ goto error;
+
+ oggpacket.b_o_s = 1; /* yes this actually is a b_o_s packet :) */
+ if( vorbis_synthesis_headerin( &p_dec->vi, &p_dec->vc, &oggpacket ) < 0 )
+ {
+ msg_Err( p_dec->p_fifo, "This bitstream does not contain Vorbis "
+ "audio data");
+ goto error;
+ }
+
+ /* The next two packets in order are the comment and codebook headers.
+ We need to watch out that these packets are not missing as a
+ missing or corrupted header is fatal. */
+ if( GetOggPacket( p_dec, &oggpacket, &i_pts, 1 ) != VLC_SUCCESS )
+ goto error;
+
+ if( vorbis_synthesis_headerin( &p_dec->vi, &p_dec->vc, &oggpacket ) < 0 )
+ {
+ msg_Err( p_dec->p_fifo, "2nd Vorbis header is corrupted" );
+ goto error;
+ }
+
+ if( GetOggPacket( p_dec, &oggpacket, &i_pts, 1 ) != VLC_SUCCESS )
+ goto error;
+
+ if( vorbis_synthesis_headerin( &p_dec->vi, &p_dec->vc, &oggpacket ) < 0 )
+ {
+ msg_Err( p_dec->p_fifo, "3rd Vorbis header is corrupted" );
+ goto error;
+ }
+
+ /* Initialize the Vorbis packet->PCM decoder */
+ vorbis_synthesis_init( &p_dec->vd, &p_dec->vi );
+ vorbis_block_init( &p_dec->vd, &p_dec->vb );
+
+ p_dec->output_format.i_format = VLC_FOURCC('f','l','3','2');
+ p_dec->output_format.i_channels = p_dec->vi.channels;
+ p_dec->output_format.i_rate = p_dec->vi.rate;
+
+ aout_DateInit( &p_dec->end_date, p_dec->vi.rate );
+ p_dec->p_aout = NULL;
+ p_dec->p_aout_input = aout_DecNew( p_dec->p_fifo,
+ &p_dec->p_aout,
+ &p_dec->output_format );
+
+ if( p_dec->p_aout_input == NULL )
+ {
+ msg_Err( p_dec->p_fifo, "failed to create aout fifo" );
+ goto error;
+ }
+
+ /* Take care of the first pts we receive. We need to be careful as a pts
+ * in vorbis language does in fact correspond to the presentation time of
+ * the _next_ packet to receive */
+ if( i_pts > 0 && i_pts != aout_DateGet( &p_dec->end_date ) )
+ {
+ aout_DateSet( &p_dec->end_date, i_pts );
+ }
+
+ /* vorbis decoder thread's main loop */
+ while( (!p_dec->p_fifo->b_die) && (!p_dec->p_fifo->b_error) )
+ {
+ DecodePacket( p_dec );
+ }
+
+ /* If b_error is set, the vorbis decoder thread enters the error loop */
+ if( p_dec->p_fifo->b_error )
+ {
+ DecoderError( p_dec->p_fifo );
+ }
+
+ /* End of the vorbis decoder thread */
+ CloseDecoder( p_dec );
+
+ return 0;
+
+ error:
+ if( p_dec )
+ {
+ if( p_dec->p_fifo )
+ p_dec->p_fifo->b_error = 1;
+ free( p_dec );
+ }
+
+ DecoderError( p_fifo );
+ return -1;
+
+}
+
+/*****************************************************************************
+ * DecodePacket: decodes a Vorbis packet.
+ *****************************************************************************/
+static void DecodePacket( dec_thread_t *p_dec )
+{
+ aout_buffer_t *p_aout_buffer;
+ ogg_packet oggpacket;
+ float **pp_pcm;
+ int i_samples;
+ mtime_t i_pts;
+
+ if( GetOggPacket( p_dec, &oggpacket, &i_pts, 1 ) != VLC_SUCCESS )
+ {
+ /* This should mean an eos */
+ return;
+ }
+
+ if( vorbis_synthesis( &p_dec->vb, &oggpacket ) == 0 )
+ vorbis_synthesis_blockin( &p_dec->vd, &p_dec->vb );
+ else
+ msg_Err( p_dec->p_fifo, "vorbis_synthesis error" );
+
+ /* **pp_pcm is a multichannel float vector. In stereo, for
+ * example, pp_pcm[0] is left, and pp_pcm[1] is right. i_samples is
+ * the size of each channel. Convert the float values
+ * (-1.<=range<=1.) to whatever PCM format and write it out */
+
+ while( ( i_samples = vorbis_synthesis_pcmout( &p_dec->vd, &pp_pcm ) ) > 0 )
+ {
+
+ p_aout_buffer = aout_DecNewBuffer( p_dec->p_aout, p_dec->p_aout_input,
+ i_samples );
+ if( !p_aout_buffer )
+ {
+ msg_Err( p_dec->p_fifo, "cannot get aout buffer" );
+ p_dec->p_fifo->b_error = 1;
+ return;
+ }
+
+ /* Interleave the samples */
+ Interleave( (float *)p_aout_buffer->p_buffer, (const float **)pp_pcm,
+ p_dec->vi.channels, i_samples );
+
+ /* Tell libvorbis how many samples we actually consumed */
+ vorbis_synthesis_read( &p_dec->vd, i_samples );
+
+ /* Date management */
+ p_aout_buffer->start_date = aout_DateGet( &p_dec->end_date );
+ p_aout_buffer->end_date = aout_DateIncrement( &p_dec->end_date,
+ i_samples );
+
+ if( i_pts > 0 && i_pts != aout_DateGet( &p_dec->end_date ) )
+ {
+ aout_DateSet( &p_dec->end_date, i_pts );
+ p_aout_buffer->end_date = aout_DateGet( &p_dec->end_date );
+ }
+ else
+ {
+ aout_DateSet( &p_dec->end_date, p_aout_buffer->end_date );
+ }
+
+ aout_DecPlay( p_dec->p_aout, p_dec->p_aout_input, p_aout_buffer );
+ }
+
+}
+
+/*****************************************************************************
+ * GetOggPacket: get the following vorbis packet from the stream and send back
+ * the result in an ogg packet (for easy decoding by libvorbis).
+ *****************************************************************************
+ * Returns VLC_EGENERIC in case of eof.
+ *****************************************************************************/
+static int GetOggPacket( dec_thread_t *p_dec, ogg_packet *p_oggpacket,
+ mtime_t *p_pts, int b_next )
+{
+ pes_packet_t *p_pes;
+
+ if( b_next ) NextPES( p_dec->p_fifo );
+ p_pes = GetPES( p_dec->p_fifo );
+
+ p_oggpacket->packet = p_pes->p_first->p_payload_start;
+ p_oggpacket->bytes = p_pes->i_pes_size;
+ p_oggpacket->granulepos = p_pes->i_dts;
+ p_oggpacket->b_o_s = 0;
+ p_oggpacket->e_o_s = 0;
+ p_oggpacket->packetno = 0;
+
+ *p_pts = p_pes->i_pts;
+
+ return p_pes ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * Interleave: helper function to interleave channels
+ *****************************************************************************/
+static void Interleave( float *p_out, const float **pp_in, int i_channels,
+ int i_samples )
+{
+ int i, j;
+
+ for ( j = 0; j < i_samples; j++ )
+ {
+ for ( i = 0; i < i_channels; i++ )
+ {
+ p_out[j * i_channels + i] = pp_in[i][j];
+ }
+ }
+}
+
+/*****************************************************************************
+ * CloseDecoder: vorbis decoder destruction
+ *****************************************************************************/
+static void CloseDecoder( dec_thread_t * p_dec )
+{
+ if( p_dec->p_aout_input != NULL )
+ {
+ aout_DecDelete( p_dec->p_aout, p_dec->p_aout_input );
+ }
+
+ if( p_dec )
+ {
+ vorbis_block_clear( &p_dec->vb );
+ vorbis_dsp_clear( &p_dec->vd );
+ vorbis_comment_clear( &p_dec->vc );
+ vorbis_info_clear( &p_dec->vi ); /* must be called last */
+ free( p_dec );
+ }
+}
--- /dev/null
+/*****************************************************************************
+ * ogg.c : ogg stream input module for vlc
+ *****************************************************************************
+ * Copyright (C) 2001 VideoLAN
+ * $Id: ogg.c,v 1.1 2002/10/24 09:30:48 gbazin Exp $
+ *
+ * Authors: Gildas Bazin <gbazin@netcourrier.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#include <stdlib.h> /* malloc(), free() */
+#include <string.h>
+
+#include <vlc/vlc.h>
+#include <vlc/input.h>
+
+#include <sys/types.h>
+
+#include <ogg/ogg.h>
+
+#define OGG_BLOCK_SIZE 4096
+#define PAGES_READ_ONCE 1
+
+/*****************************************************************************
+ * Definitions of structures and functions used by this plugins
+ *****************************************************************************/
+typedef struct logical_stream_s
+{
+ ogg_stream_state os; /* logical stream of packets */
+
+ int i_serial_no;
+ int i_pages_read;
+ int i_cat; /* AUDIO_ES, VIDEO_ES */
+ int i_activated;
+ vlc_fourcc_t i_fourcc;
+ vlc_fourcc_t i_codec;
+
+ es_descriptor_t *p_es;
+ int b_selected; /* newly selected */
+
+ /* info for vorbis logical streams */
+ int i_rate;
+ int i_channels;
+ int i_bitrate;
+
+} logical_stream_t;
+
+struct demux_sys_t
+{
+ ogg_sync_state oy; /* sync and verify incoming physical bitstream */
+
+ int i_streams; /* number of logical bitstreams */
+ logical_stream_t **pp_stream; /* pointer to an array of logical streams */
+
+ /* current audio and video es */
+ logical_stream_t *p_stream_video;
+ logical_stream_t *p_stream_audio;
+
+ mtime_t i_time;
+ mtime_t i_length;
+ mtime_t i_pcr;
+ int b_seekable;
+};
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static int Activate ( vlc_object_t * );
+static void Deactivate( vlc_object_t * );
+static int Demux ( input_thread_t * );
+
+/* Stream managment */
+static int Ogg_StreamStart ( input_thread_t *, demux_sys_t *, int );
+static int Ogg_StreamSeek ( input_thread_t *, demux_sys_t *, int, mtime_t );
+static void Ogg_StreamStop ( input_thread_t *, demux_sys_t *, int );
+
+/* Bitstream manipulation */
+static int Ogg_Check ( input_thread_t *p_input );
+static int Ogg_ReadPage ( input_thread_t *, demux_sys_t *, ogg_page * );
+static void Ogg_DecodePacket ( input_thread_t *p_input,
+ logical_stream_t *p_stream,
+ ogg_packet *p_oggpacket );
+static int Ogg_FindLogicalStreams( input_thread_t *p_input,
+ demux_sys_t *p_ogg );
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+vlc_module_begin();
+ set_description( _("ogg stream demux" ) );
+ set_capability( "demux", 50 );
+ set_callbacks( Activate, Deactivate );
+ add_shortcut( "ogg" );
+vlc_module_end();
+
+/*****************************************************************************
+ * Stream managment
+ *****************************************************************************/
+static int Ogg_StreamStart( input_thread_t *p_input,
+ demux_sys_t *p_ogg, int i_stream )
+{
+#define p_stream p_ogg->pp_stream[i_stream]
+ if( !p_stream->p_es )
+ {
+ msg_Warn( p_input, "stream[%d] unselectable", i_stream );
+ return( 0 );
+ }
+ if( p_stream->i_activated )
+ {
+ msg_Warn( p_input, "stream[%d] already selected", i_stream );
+ return( 1 );
+ }
+
+ if( !p_stream->p_es->p_decoder_fifo )
+ {
+ vlc_mutex_lock( &p_input->stream.stream_lock );
+ input_SelectES( p_input, p_stream->p_es );
+ vlc_mutex_unlock( &p_input->stream.stream_lock );
+ }
+ p_stream->i_activated = p_stream->p_es->p_decoder_fifo ? 1 : 0;
+
+ //Ogg_StreamSeek( p_input, p_ogg, i_stream, p_ogg->i_time );
+
+ return( p_stream->i_activated );
+#undef p_stream
+}
+
+static void Ogg_StreamStop( input_thread_t *p_input,
+ demux_sys_t *p_ogg, int i_stream )
+{
+#define p_stream p_ogg->pp_stream[i_stream]
+
+ if( !p_stream->i_activated )
+ {
+ msg_Warn( p_input, "stream[%d] already unselected", i_stream );
+ return;
+ }
+
+ if( p_stream->p_es->p_decoder_fifo )
+ {
+ vlc_mutex_lock( &p_input->stream.stream_lock );
+ input_UnselectES( p_input, p_stream->p_es );
+ vlc_mutex_unlock( &p_input->stream.stream_lock );
+ }
+
+ p_stream->i_activated = 0;
+
+#undef p_stream
+}
+
+static int Ogg_StreamSeek( input_thread_t *p_input, demux_sys_t *p_ogg,
+ int i_stream, mtime_t i_date )
+{
+#define p_stream p_ogg->pp_stream[i_stream]
+
+ /* FIXME: todo */
+
+ return 1;
+#undef p_stream
+}
+
+/****************************************************************************
+ * Ogg_Check: Check we are dealing with an ogg stream.
+ ****************************************************************************/
+static int Ogg_Check( input_thread_t *p_input )
+{
+ u8 *p_peek;
+ int i_size = input_Peek( p_input, &p_peek, 4 );
+
+ /* Check for the Ogg capture pattern */
+ if( !(i_size>3) || !(p_peek[0] == 'O') || !(p_peek[1] == 'g') ||
+ !(p_peek[2] == 'g') || !(p_peek[3] == 'S') )
+ return VLC_EGENERIC;
+
+ /* FIXME: Capture pattern might not be enough so we can also check for the
+ * the first complete page */
+
+ return VLC_SUCCESS;
+}
+
+/****************************************************************************
+ * Ogg_ReadPage: Read a full Ogg page from the physical bitstream.
+ ****************************************************************************
+ * Returns VLC_SUCCESS if a page has been read. An error might happen if we
+ * are at the end of stream.
+ ****************************************************************************/
+static int Ogg_ReadPage( input_thread_t *p_input, demux_sys_t *p_ogg,
+ ogg_page *p_oggpage )
+{
+ int i_read = 0;
+ data_packet_t *p_data;
+ byte_t *p_buffer;
+
+ while( ogg_sync_pageout( &p_ogg->oy, p_oggpage ) != 1 )
+ {
+ i_read = input_SplitBuffer( p_input, &p_data, OGG_BLOCK_SIZE );
+ if( i_read <= 0 )
+ return VLC_EGENERIC;
+
+ p_buffer = ogg_sync_buffer( &p_ogg->oy, i_read );
+ p_input->p_vlc->pf_memcpy( p_buffer, p_data->p_payload_start, i_read );
+ ogg_sync_wrote( &p_ogg->oy, i_read );
+ input_DeletePacket( p_input->p_method_data, p_data );
+ }
+
+ return VLC_SUCCESS;
+}
+
+/****************************************************************************
+ * Ogg_DecodePacket: Decode an Ogg packet.
+ ****************************************************************************/
+static void Ogg_DecodePacket( input_thread_t *p_input,
+ logical_stream_t *p_stream,
+ ogg_packet *p_oggpacket )
+{
+ pes_packet_t *p_pes;
+ data_packet_t *p_data;
+
+ if( !( p_pes = input_NewPES( p_input->p_method_data ) ) )
+ {
+ return;
+ }
+ if( !( p_data = input_NewPacket( p_input->p_method_data,
+ p_oggpacket->bytes ) ) )
+ {
+ input_DeletePES( p_input->p_method_data, p_pes );
+ return;
+ }
+
+ p_pes->p_first = p_pes->p_last = p_data;
+ p_pes->i_nb_data = 1;
+ p_pes->i_pes_size = p_oggpacket->bytes;
+ p_pes->i_dts = p_oggpacket->granulepos;
+
+ /* Convert the granule into a pts */
+ p_pes->i_pts = (p_oggpacket->granulepos < 0) ? 0 :
+ p_oggpacket->granulepos * 90000 / p_stream->i_rate;
+ p_pes->i_pts = (p_oggpacket->granulepos < 0) ? 0 :
+ input_ClockGetTS( p_input, p_input->stream.p_selected_program,
+ p_pes->i_pts );
+
+ memcpy( p_data->p_payload_start, p_oggpacket->packet, p_oggpacket->bytes );
+
+ input_DecodePES( p_stream->p_es->p_decoder_fifo, p_pes );
+}
+
+/****************************************************************************
+ * Ogg_FindLogicalStreams: Find the logical streams embedded in the physical
+ * stream and fill p_ogg.
+ *****************************************************************************
+ * The initial page of a logical stream is marked as a 'bos' page.
+ * Furthermore, the Ogg specification mandates that grouped bitstreams begin
+ * together and all of the initial pages must appear before any data pages.
+ *
+ * On success this function returns VLC_SUCCESS.
+ ****************************************************************************/
+static int Ogg_FindLogicalStreams( input_thread_t *p_input, demux_sys_t *p_ogg)
+{
+ ogg_packet oggpacket;
+ ogg_page oggpage;
+ int i_stream;
+
+ while( Ogg_ReadPage( p_input, p_ogg, &oggpage ) == VLC_SUCCESS )
+ {
+ if( ogg_page_bos( &oggpage ) )
+ {
+
+ /* All is wonderful in our fine fine little world.
+ * We found the beginning of our first logical stream. */
+ while( ogg_page_bos( &oggpage ) )
+ {
+ p_ogg->i_streams++;
+ p_ogg->pp_stream =
+ realloc( p_ogg->pp_stream, p_ogg->i_streams *
+ sizeof(logical_stream_t *) );
+
+#define p_stream p_ogg->pp_stream[p_ogg->i_streams - 1]
+
+ p_stream = malloc( sizeof(logical_stream_t) );
+ memset( p_stream, 0, sizeof(logical_stream_t) );
+
+ /* Setup the logical stream */
+ p_stream->i_pages_read++;
+ p_stream->i_serial_no = ogg_page_serialno( &oggpage );
+ ogg_stream_init( &p_stream->os, p_stream->i_serial_no );
+
+ /* Extract the initial header from the first page and verify
+ * the codec type of tis Ogg bitstream */
+ if( ogg_stream_pagein( &p_stream->os, &oggpage ) < 0 )
+ {
+ /* error. stream version mismatch perhaps */
+ msg_Err( p_input, "Error reading first page of "
+ "Ogg bitstream data" );
+ return VLC_EGENERIC;
+ }
+
+ /* FIXME: check return value */
+ ogg_stream_packetpeek( &p_stream->os, &oggpacket );
+
+ /* Check for Vorbis header */
+ if( oggpacket.bytes >= 7 &&
+ ! strncmp( &oggpacket.packet[1], "vorbis", 6 ) )
+ {
+ oggpack_buffer opb;
+
+ msg_Dbg( p_input, "found vorbis header" );
+ p_stream->i_cat = AUDIO_ES;
+ p_stream->i_fourcc = VLC_FOURCC( 'v','o','r','b' );
+
+ /* Cheat and get additionnal info ;) */
+ oggpack_readinit( &opb, oggpacket.packet, oggpacket.bytes);
+ oggpack_adv( &opb, 88 );
+ p_stream->i_channels = oggpack_read( &opb, 8 );
+ p_stream->i_rate = oggpack_read( &opb, 32 );
+ oggpack_adv( &opb, 32 );
+ p_stream->i_bitrate = oggpack_read( &opb, 32 );
+ }
+ else
+ {
+ msg_Dbg( p_input, "found unknown codec" );
+ ogg_stream_destroy( &p_stream->os );
+ free( p_stream );
+ p_ogg->i_streams--;
+ }
+
+#undef p_stream
+
+ if( Ogg_ReadPage( p_input, p_ogg, &oggpage ) != VLC_SUCCESS )
+ return VLC_EGENERIC;
+ }
+
+ /* This is the first data page, which means we are now finished
+ * with the initial pages. We just need to store it in the relevant
+ * bitstream. */
+ for( i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ )
+ {
+ if( ogg_stream_pagein( &p_ogg->pp_stream[i_stream]->os,
+ &oggpage ) == 0 )
+ {
+ p_ogg->pp_stream[i_stream]->i_pages_read++;
+ break;
+ }
+ }
+ return VLC_SUCCESS;
+ }
+ }
+ return VLC_EGENERIC;
+}
+
+/*****************************************************************************
+ * Activate: initializes ogg demux structures
+ *****************************************************************************/
+static int Activate( vlc_object_t * p_this )
+{
+ int i_stream, b_forced;
+ demux_sys_t *p_ogg;
+ input_thread_t *p_input = (input_thread_t *)p_this;
+
+ p_input->p_demux_data = NULL;
+ b_forced = ( ( *p_input->psz_demux )&&
+ ( !strncmp( p_input->psz_demux, "ogg", 10 ) ) ) ? 1 : 0;
+
+ /* Check if we are dealing with an ogg stream */
+ if( !b_forced && ( Ogg_Check( p_input ) != VLC_SUCCESS ) )
+ return -1;
+
+ /* Allocate p_ogg */
+ if( !( p_ogg = malloc( sizeof( demux_sys_t ) ) ) )
+ {
+ msg_Err( p_input, "out of memory" );
+ goto error;
+ }
+ memset( p_ogg, 0, sizeof( demux_sys_t ) );
+ p_input->p_demux_data = p_ogg;
+
+ p_ogg->i_time = 0;
+ p_ogg->i_pcr = 0;
+ p_ogg->b_seekable = ( ( p_input->stream.b_seekable )
+ &&( p_input->stream.i_method == INPUT_METHOD_FILE ) );
+
+ /* Initialize the Ogg physical bitstream parser */
+ ogg_sync_init( &p_ogg->oy );
+
+ /* Find the logical streams embedded in the physical stream and
+ * initialize our p_ogg structure. */
+ if( Ogg_FindLogicalStreams( p_input, p_ogg ) != VLC_SUCCESS )
+ {
+ msg_Err( p_input, "couldn't find an ogg logical stream" );
+ goto error;
+ }
+
+ /* Set the demux function */
+ p_input->pf_demux = Demux;
+
+ /* Initialize access plug-in structures. */
+ if( p_input->i_mtu == 0 )
+ {
+ /* Improve speed. */
+ p_input->i_bufsize = INPUT_DEFAULT_BUFSIZE;
+ }
+
+ /* Create one program */
+ vlc_mutex_lock( &p_input->stream.stream_lock );
+ if( input_InitStream( p_input, 0 ) == -1)
+ {
+ vlc_mutex_unlock( &p_input->stream.stream_lock );
+ msg_Err( p_input, "cannot init stream" );
+ goto error;
+ }
+ if( input_AddProgram( p_input, 0, 0) == NULL )
+ {
+ vlc_mutex_unlock( &p_input->stream.stream_lock );
+ msg_Err( p_input, "cannot add program" );
+ goto error;
+ }
+ p_input->stream.p_selected_program = p_input->stream.pp_programs[0];
+ vlc_mutex_unlock( &p_input->stream.stream_lock );
+
+ for( i_stream = 0 ; i_stream < p_ogg->i_streams; i_stream++ )
+ {
+#define p_stream p_ogg->pp_stream[i_stream]
+ vlc_mutex_lock( &p_input->stream.stream_lock );
+ p_stream->p_es = input_AddES( p_input,
+ p_input->stream.p_selected_program,
+ p_ogg->i_streams + 1, 0 );
+ vlc_mutex_unlock( &p_input->stream.stream_lock );
+ p_stream->p_es->i_stream_id = i_stream;
+ p_stream->p_es->i_fourcc = p_stream->i_fourcc;
+ p_stream->p_es->i_cat = p_stream->i_cat;
+#undef p_stream
+ }
+
+ vlc_mutex_lock( &p_input->stream.stream_lock );
+ p_input->stream.i_mux_rate = 0;
+ vlc_mutex_unlock( &p_input->stream.stream_lock );
+
+ for( i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ )
+ {
+#define p_stream p_ogg->pp_stream[i_stream]
+ switch( p_stream->p_es->i_cat )
+ {
+ case( VIDEO_ES ):
+
+ if( (p_ogg->p_stream_video == NULL) )
+ {
+ p_ogg->p_stream_video = p_stream;
+ /* TODO add test to see if a decoder has been found */
+ Ogg_StreamStart( p_input, p_ogg, i_stream );
+ }
+ break;
+
+ case( AUDIO_ES ):
+ if( (p_ogg->p_stream_audio == NULL) )
+ {
+ p_ogg->p_stream_audio = p_stream;
+ Ogg_StreamStart( p_input, p_ogg, i_stream );
+ }
+ break;
+
+ default:
+ break;
+ }
+#undef p_stream
+ }
+
+ /* we select the first audio and video ES */
+ vlc_mutex_lock( &p_input->stream.stream_lock );
+ if( !p_ogg->p_stream_video )
+ {
+ msg_Warn( p_input, "no video stream found" );
+ }
+ if( !p_ogg->p_stream_audio )
+ {
+ msg_Warn( p_input, "no audio stream found!" );
+ }
+ p_input->stream.p_selected_program->b_is_ok = 1;
+ vlc_mutex_unlock( &p_input->stream.stream_lock );
+
+ return 0;
+
+ error:
+ Deactivate( (vlc_object_t *)p_input );
+ return -1;
+
+}
+
+/*****************************************************************************
+ * Deactivate: frees unused data
+ *****************************************************************************/
+static void Deactivate( vlc_object_t *p_this )
+{
+ input_thread_t *p_input = (input_thread_t *)p_this;
+ demux_sys_t *p_ogg = (demux_sys_t *)p_input->p_demux_data ;
+ int i;
+
+ if( p_ogg )
+ {
+ /* Cleanup the bitstream parser */
+ ogg_sync_clear( &p_ogg->oy );
+
+ for( i = 0; i < p_ogg->i_streams; i++ )
+ {
+ ogg_stream_clear( &p_ogg->pp_stream[i]->os );
+ free( p_ogg->pp_stream[i] );
+ }
+ if( p_ogg->pp_stream ) free( p_ogg->pp_stream );
+
+ free( p_ogg );
+ }
+}
+
+/*****************************************************************************
+ * Demux: reads and demuxes data packets
+ *****************************************************************************
+ * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
+ *****************************************************************************/
+static int Demux( input_thread_t * p_input )
+{
+ int i, i_stream, b_eos;
+ ogg_page oggpage;
+ ogg_packet oggpacket;
+ demux_sys_t *p_ogg = (demux_sys_t *)p_input->p_demux_data;
+
+#define p_stream p_ogg->pp_stream[i_stream]
+ /* detect new selected/unselected streams */
+ for( i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ )
+ {
+ if( p_stream->p_es )
+ {
+ if( p_stream->p_es->p_decoder_fifo &&
+ !p_stream->i_activated )
+ {
+ Ogg_StreamStart( p_input, p_ogg, i_stream );
+ }
+ else
+ if( !p_stream->p_es->p_decoder_fifo &&
+ p_stream->i_activated )
+ {
+ Ogg_StreamStop( p_input, p_ogg, i_stream );
+ }
+ }
+ }
+
+ /* search new video and audio stream selected
+ * if current have been unselected */
+ if( ( !p_ogg->p_stream_video )
+ || ( !p_ogg->p_stream_video->p_es->p_decoder_fifo ) )
+ {
+ p_ogg->p_stream_video = NULL;
+ for( i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ )
+ {
+ if( ( p_stream->i_cat == VIDEO_ES )
+ &&( p_stream->p_es->p_decoder_fifo ) )
+ {
+ p_ogg->p_stream_video = p_stream;
+ break;
+ }
+ }
+ }
+ if( ( !p_ogg->p_stream_audio )
+ ||( !p_ogg->p_stream_audio->p_es->p_decoder_fifo ) )
+ {
+ p_ogg->p_stream_audio = NULL;
+ for( i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ )
+ {
+ if( ( p_stream->i_cat == AUDIO_ES )
+ &&( p_stream->p_es->p_decoder_fifo ) )
+ {
+ p_ogg->p_stream_audio = p_stream;
+ break;
+ }
+ }
+ }
+
+ /* Update program clock reference */
+ p_ogg->i_pcr = p_ogg->i_time * 9 / 100;
+
+ if( (p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT)
+ || (input_ClockManageControl( p_input,
+ p_input->stream.p_selected_program,
+ (mtime_t)0 ) == PAUSE_S) )
+ {
+ msg_Warn( p_input, "synchro reinit" );
+ p_input->stream.p_selected_program->i_synchro_state = SYNCHRO_OK;
+ }
+
+ /* Call the pace control. */
+ input_ClockManageRef( p_input,
+ p_input->stream.p_selected_program,
+ p_ogg->i_pcr );
+
+ /* Demux ogg pages from the stream */
+ b_eos = 0;
+ for( i = 0; i < PAGES_READ_ONCE; i++ )
+ {
+ if( Ogg_ReadPage(p_input, p_ogg, &oggpage ) != VLC_SUCCESS )
+ {
+ b_eos = 1;
+ break;
+ }
+
+ for( i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ )
+ {
+ /* FIXME: we already read the header */
+
+ if( ogg_stream_pagein( &p_stream->os, &oggpage ) != 0 )
+ continue;
+
+ while( ogg_stream_packetout( &p_stream->os, &oggpacket ) > 0 )
+ {
+ /* FIXME: handle discontinuity */
+
+ if( !p_stream->p_es ||
+ !p_stream->p_es->p_decoder_fifo )
+ {
+ break;
+ }
+
+ if( oggpacket.granulepos >= 0 )
+ p_ogg->i_time = oggpacket.granulepos * 1000000
+ / p_stream->i_rate;
+ else
+ p_ogg->i_time += (oggpacket.bytes * 1000000
+ / p_stream->i_bitrate);
+
+ Ogg_DecodePacket( p_input, p_stream, &oggpacket );
+ }
+ }
+#undef p_stream
+ }
+
+ /* Did we reach the end of stream ? */
+ return( b_eos ? 0 : 1 );
+}