+/*****************************************************************************
+ * Close: frees unused data
+ *****************************************************************************/
+static void Close( vlc_object_t *p_this )
+{
+ demux_t *p_demux = (demux_t *)p_this;
+ demux_sys_t *p_sys = p_demux->p_sys ;
+
+ /* Cleanup the bitstream parser */
+ ogg_sync_clear( &p_sys->oy );
+
+ Ogg_EndOfStream( p_demux );
+
+ if( p_sys->p_old_stream )
+ Ogg_LogicalStreamDelete( p_demux, p_sys->p_old_stream );
+
+ free( p_sys );
+}
+
+/*****************************************************************************
+ * Demux: reads and demuxes data packets
+ *****************************************************************************
+ * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
+ *****************************************************************************/
+static int Demux( demux_t * p_demux )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ ogg_packet oggpacket;
+ int i_stream;
+ bool b_skipping = false;
+
+
+ if( p_sys->i_eos == p_sys->i_streams )
+ {
+ if( p_sys->i_eos )
+ {
+ msg_Dbg( p_demux, "end of a group of logical streams" );
+ /* We keep the ES to try reusing it in Ogg_BeginningOfStream
+ * only 1 ES is supported (common case for ogg web radio) */
+ if( p_sys->i_streams == 1 )
+ {
+ p_sys->p_old_stream = p_sys->pp_stream[0];
+ TAB_CLEAN( p_sys->i_streams, p_sys->pp_stream );
+ }
+ Ogg_EndOfStream( p_demux );
+ }
+
+ p_sys->i_eos = 0;
+ if( Ogg_BeginningOfStream( p_demux ) != VLC_SUCCESS )
+ return 0;
+
+ msg_Dbg( p_demux, "beginning of a group of logical streams" );
+ es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
+ }
+
+ /*
+ * The first data page of a physical stream is stored in the relevant logical stream
+ * in Ogg_FindLogicalStreams. Therefore, we must not read a page and only update the
+ * stream it belongs to if we haven't processed this first page yet. If we do, we
+ * will only process that first page whenever we find the second page for this stream.
+ * While this is fine for Vorbis and Theora, which are continuous codecs, which means
+ * the second page will arrive real quick, this is not fine for Kate, whose second
+ * data page will typically arrive much later.
+ * This means it is now possible to seek right at the start of a stream where the last
+ * logical stream is Kate, without having to wait for the second data page to unblock
+ * the first one, which is the one that triggers the 'no more headers to backup' code.
+ * And, as we all know, seeking without having backed up all headers is bad, since the
+ * codec will fail to initialize if it's missing its headers.
+ */
+ if( !p_sys->b_page_waiting)
+ {
+ /*
+ * Demux an ogg page from the stream
+ */
+ if( Ogg_ReadPage( p_demux, &p_sys->current_page ) != VLC_SUCCESS )
+ return 0; /* EOF */
+
+ /* Test for End of Stream */
+ if( ogg_page_eos( &p_sys->current_page ) )
+ p_sys->i_eos++;
+ }
+
+
+ for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
+ {
+ logical_stream_t *p_stream = p_sys->pp_stream[i_stream];
+
+ /* if we've just pulled page, look for the right logical stream */
+ if( !p_sys->b_page_waiting )
+ {
+ if( p_sys->i_streams == 1 &&
+ ogg_page_serialno( &p_sys->current_page ) != p_stream->os.serialno )
+ {
+ msg_Err( p_demux, "Broken Ogg stream (serialno) mismatch" );
+ ogg_stream_reset_serialno( &p_stream->os, ogg_page_serialno( &p_sys->current_page ) );
+
+ p_stream->b_reinit = true;
+ p_stream->i_pcr = -1;
+ p_stream->i_interpolated_pcr = -1;
+ es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
+ }
+
+ if( ogg_stream_pagein( &p_stream->os, &p_sys->current_page ) != 0 )
+ {
+ continue;
+ }
+
+ }
+
+ while( ogg_stream_packetout( &p_stream->os, &oggpacket ) > 0 )
+ {
+ /* Read info from any secondary header packets, if there are any */
+ if( p_stream->i_secondary_header_packets > 0 )
+ {
+ if( p_stream->fmt.i_codec == VLC_CODEC_THEORA &&
+ oggpacket.bytes >= 7 &&
+ ! memcmp( oggpacket.packet, "\x80theora", 7 ) )
+ {
+ Ogg_ReadTheoraHeader( p_stream, &oggpacket );
+ p_stream->i_secondary_header_packets = 0;
+ }
+ else if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS &&
+ oggpacket.bytes >= 7 &&
+ ! memcmp( oggpacket.packet, "\x01vorbis", 7 ) )
+ {
+ Ogg_ReadVorbisHeader( p_stream, &oggpacket );
+ p_stream->i_secondary_header_packets = 0;
+ }
+ else if( p_stream->fmt.i_codec == VLC_CODEC_CMML )
+ {
+ p_stream->i_secondary_header_packets = 0;
+ }
+
+ /* update start of data pointer */
+ p_stream->i_data_start = stream_Tell( p_demux->s );
+
+ }
+
+ /* If any streams have i_skip_frames, only decode (pre-roll)
+ * for those streams */
+ if ( b_skipping && p_stream->i_skip_frames == 0 ) continue;
+
+
+ if( p_stream->b_reinit )
+ {
+ /* If synchro is re-initialized we need to drop all the packets
+ * until we find a new dated one. */
+ Ogg_UpdatePCR( p_stream, &oggpacket );
+
+ if( p_stream->i_pcr >= 0 )
+ {
+ p_stream->b_reinit = false;
+ }
+ else
+ {
+ p_stream->i_interpolated_pcr = -1;
+ continue;
+ }
+
+ /* An Ogg/vorbis packet contains an end date granulepos */
+ if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS ||
+ p_stream->fmt.i_codec == VLC_CODEC_SPEEX ||
+ p_stream->fmt.i_codec == VLC_CODEC_FLAC )
+ {
+ if( ogg_stream_packetout( &p_stream->os, &oggpacket ) > 0 )
+ {
+ Ogg_DecodePacket( p_demux, p_stream, &oggpacket );
+ }
+ else
+ {
+ es_out_Control( p_demux->out, ES_OUT_SET_PCR,
+ VLC_TS_0 + p_stream->i_pcr );
+ }
+ continue;
+ }
+ }
+
+ Ogg_DecodePacket( p_demux, p_stream, &oggpacket );
+ }
+
+ if( !p_sys->b_page_waiting )
+ break;
+ }
+
+ /* if a page was waiting, it's now processed */
+ p_sys->b_page_waiting = false;
+
+ p_sys->i_pcr = -1;
+ for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ )
+ {
+ logical_stream_t *p_stream = p_sys->pp_stream[i_stream];
+
+ if( p_stream->fmt.i_cat == SPU_ES )
+ continue;
+ if( p_stream->i_interpolated_pcr < 0 )
+ continue;
+
+ if( p_sys->i_pcr < 0 || p_stream->i_interpolated_pcr < p_sys->i_pcr )
+ p_sys->i_pcr = p_stream->i_interpolated_pcr;
+ }
+
+ if( p_sys->i_pcr >= 0 && ! b_skipping )
+ es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_sys->i_pcr );
+
+ return 1;
+}
+
+/*****************************************************************************
+ * Control:
+ *****************************************************************************/
+static int Control( demux_t *p_demux, int i_query, va_list args )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+ vlc_meta_t *p_meta;
+ int64_t *pi64;
+ bool *pb_bool;
+ int i;
+
+ switch( i_query )
+ {
+ case DEMUX_GET_META:
+ p_meta = (vlc_meta_t *)va_arg( args, vlc_meta_t* );
+ if( p_sys->p_meta )
+ vlc_meta_Merge( p_meta, p_sys->p_meta );
+ return VLC_SUCCESS;
+
+ case DEMUX_HAS_UNSUPPORTED_META:
+ pb_bool = (bool*)va_arg( args, bool* );
+ *pb_bool = true;
+ return VLC_SUCCESS;
+
+ case DEMUX_GET_TIME:
+ pi64 = (int64_t*)va_arg( args, int64_t * );
+ *pi64 = p_sys->i_pcr;
+ return VLC_SUCCESS;
+
+ case DEMUX_SET_TIME:
+ return VLC_EGENERIC;
+
+ case DEMUX_SET_POSITION:
+ /* forbid seeking if we haven't initialized all logical bitstreams yet;
+ if we allowed, some headers would not get backed up and decoder init
+ would fail, making that logical stream unusable */
+ if( p_sys->i_bos > 0 )
+ {
+ return VLC_EGENERIC;
+ }
+
+ for( i = 0; i < p_sys->i_streams; i++ )
+ {
+ logical_stream_t *p_stream = p_sys->pp_stream[i];
+
+ /* we'll trash all the data until we find the next pcr */
+ p_stream->b_reinit = true;
+ p_stream->i_pcr = -1;
+ p_stream->i_interpolated_pcr = -1;
+ ogg_stream_reset( &p_stream->os );
+ }
+ ogg_sync_reset( &p_sys->oy );
+ /* XXX The break/return is missing on purpose as
+ * demux_vaControlHelper will do the last part of the job */
+
+ default:
+ return demux_vaControlHelper( p_demux->s, 0, -1, p_sys->i_bitrate,
+ 1, i_query, args );
+ }
+}
+