Commit ff681fc7 authored by Francois Cartegnie's avatar Francois Cartegnie

demux: ogg: rewrite pts and pts computation (fix #9828)

- Previous algorithm was unable to guess the first packet
pts because of ogg design (some codecs/opus/vorbis have total
sample at page granule and only granule on last packet. You
then need to compute first packet pts backward using packets
duration).
So we were stuck either assuming a start at pts/pcr zero, or
be unable to handle ogg streaming (non zero start) correctly,
or had to always drop the 0..N-1 packets for first page.
We need libvorbis to correcly compute those durations (code
is far more complex than opus's one).
- Refactorized to discard the tons of cases appended
with each new codec.
- lots of other fixes in many places
parent 5008e1c3
...@@ -1999,6 +1999,10 @@ dnl ...@@ -1999,6 +1999,10 @@ dnl
dnl ogg demux plugin dnl ogg demux plugin
dnl dnl
PKG_ENABLE_MODULES_VLC([OGG], [], [ogg >= 1.0], [Ogg demux support], [auto]) PKG_ENABLE_MODULES_VLC([OGG], [], [ogg >= 1.0], [Ogg demux support], [auto])
PKG_CHECK_MODULES(LIBVORBIS, [vorbis >= 1.1], [
AC_DEFINE(HAVE_LIBVORBIS, 1, [Define to 1 if you have the libvorbis])
],[])
PKG_ENABLE_MODULES_VLC([OGG], [], [ogg >= 1.0], [Ogg demux support], [auto], [${LIBVORBIS_CFLAGS}], [${LIBVORBIS_LIBS}])
if test "${enable_sout}" != "no"; then if test "${enable_sout}" != "no"; then
PKG_ENABLE_MODULES_VLC([MUX_OGG], [], [ogg >= 1.0], [Ogg mux support], [auto]) PKG_ENABLE_MODULES_VLC([MUX_OGG], [], [ogg >= 1.0], [Ogg mux support], [auto])
dnl Check for libshout dnl Check for libshout
......
...@@ -7,9 +7,9 @@ demux_LTLIBRARIES += libflacsys_plugin.la ...@@ -7,9 +7,9 @@ demux_LTLIBRARIES += libflacsys_plugin.la
libogg_plugin_la_SOURCES = demux/ogg.c demux/ogg.h demux/oggseek.c demux/oggseek.h \ libogg_plugin_la_SOURCES = demux/ogg.c demux/ogg.h demux/oggseek.c demux/oggseek.h \
demux/xiph_metadata.h demux/xiph.h demux/xiph_metadata.c demux/xiph_metadata.h demux/xiph.h demux/xiph_metadata.c
libogg_plugin_la_CFLAGS = $(AM_CFLAGS) $(CFLAGS_ogg) libogg_plugin_la_CFLAGS = $(AM_CFLAGS) $(CFLAGS_ogg) $(LIBVORBIS_CFLAGS)
libogg_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(demuxdir)' libogg_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(demuxdir)'
libogg_plugin_la_LIBADD = $(LIBS_ogg) libogg_plugin_la_LIBADD = $(LIBS_ogg) $(LIBVORBIS_LIBS)
EXTRA_LTLIBRARIES += libogg_plugin.la EXTRA_LTLIBRARIES += libogg_plugin.la
demux_LTLIBRARIES += $(LTLIBogg) demux_LTLIBRARIES += $(LTLIBogg)
......
...@@ -105,6 +105,10 @@ typedef struct ...@@ -105,6 +105,10 @@ typedef struct
} sh; } sh;
} stream_header_t; } stream_header_t;
#define VORBIS_HEADER_IDENTIFICATION 1
#define VORBIS_HEADER_COMMENT 2
#define VORBIS_HEADER_SETUP 3
/***************************************************************************** /*****************************************************************************
* Local prototypes * Local prototypes
*****************************************************************************/ *****************************************************************************/
...@@ -116,6 +120,7 @@ static int Ogg_ReadPage ( demux_t *, ogg_page * ); ...@@ -116,6 +120,7 @@ static int Ogg_ReadPage ( demux_t *, ogg_page * );
static void Ogg_UpdatePCR ( demux_t *, logical_stream_t *, ogg_packet * ); static void Ogg_UpdatePCR ( demux_t *, logical_stream_t *, ogg_packet * );
static void Ogg_DecodePacket ( demux_t *, logical_stream_t *, ogg_packet * ); static void Ogg_DecodePacket ( demux_t *, logical_stream_t *, ogg_packet * );
static int Ogg_OpusPacketDuration( logical_stream_t *, ogg_packet * ); static int Ogg_OpusPacketDuration( logical_stream_t *, ogg_packet * );
static void Ogg_SendOrQueueBlocks( demux_t *, logical_stream_t *, block_t * );
static void Ogg_CreateES( demux_t *p_demux ); static void Ogg_CreateES( demux_t *p_demux );
static int Ogg_BeginningOfStream( demux_t *p_demux ); static int Ogg_BeginningOfStream( demux_t *p_demux );
...@@ -125,6 +130,7 @@ static void Ogg_EndOfStream( demux_t *p_demux ); ...@@ -125,6 +130,7 @@ static void Ogg_EndOfStream( demux_t *p_demux );
/* */ /* */
static void Ogg_LogicalStreamDelete( demux_t *p_demux, logical_stream_t *p_stream ); static void Ogg_LogicalStreamDelete( demux_t *p_demux, logical_stream_t *p_stream );
static bool Ogg_LogicalStreamResetEsFormat( demux_t *p_demux, logical_stream_t *p_stream ); static bool Ogg_LogicalStreamResetEsFormat( demux_t *p_demux, logical_stream_t *p_stream );
static void Ogg_ResetStream( logical_stream_t *p_stream );
/* */ /* */
static void Ogg_ExtractMeta( demux_t *p_demux, es_format_t *p_fmt, const uint8_t *p_headers, int i_headers ); static void Ogg_ExtractMeta( demux_t *p_demux, es_format_t *p_fmt, const uint8_t *p_headers, int i_headers );
...@@ -147,6 +153,12 @@ static void Ogg_ReadSkeletonIndex( demux_t *, ogg_packet * ); ...@@ -147,6 +153,12 @@ static void Ogg_ReadSkeletonIndex( demux_t *, ogg_packet * );
static void Ogg_FreeSkeleton( ogg_skeleton_t * ); static void Ogg_FreeSkeleton( ogg_skeleton_t * );
static void Ogg_ApplySkeleton( logical_stream_t * ); static void Ogg_ApplySkeleton( logical_stream_t * );
/* Special decoding */
static void Ogg_CleanSpecificData( logical_stream_t * );
#ifdef HAVE_LIBVORBIS
static void Ogg_DecodeVorbisHeader( logical_stream_t *, ogg_packet *, int );
#endif
static void fill_channels_info(audio_format_t *audio) static void fill_channels_info(audio_format_t *audio)
{ {
static const int pi_channels_map[9] = static const int pi_channels_map[9] =
...@@ -369,13 +381,8 @@ static int Demux( demux_t * p_demux ) ...@@ -369,13 +381,8 @@ static int Demux( demux_t * p_demux )
ogg_page_serialno( &p_sys->current_page ) != p_stream->os.serialno ) ogg_page_serialno( &p_sys->current_page ) != p_stream->os.serialno )
{ {
msg_Err( p_demux, "Broken Ogg stream (serialno) mismatch" ); msg_Err( p_demux, "Broken Ogg stream (serialno) mismatch" );
Ogg_ResetStream( p_stream );
ogg_stream_reset_serialno( &p_stream->os, ogg_page_serialno( &p_sys->current_page ) ); ogg_stream_reset_serialno( &p_stream->os, ogg_page_serialno( &p_sys->current_page ) );
p_stream->b_reinit = true;
p_stream->i_pcr = VLC_TS_UNKNOWN;
p_stream->i_interpolated_pcr = VLC_TS_UNKNOWN;
p_stream->i_previous_granulepos = -1;
es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0);
} }
/* Does fail if serialno differs */ /* Does fail if serialno differs */
...@@ -386,7 +393,8 @@ static int Demux( demux_t * p_demux ) ...@@ -386,7 +393,8 @@ static int Demux( demux_t * p_demux )
} }
/* clear the finished flag if pages after eos (ex: after a seek) */ /* clear the finished flag if pages after eos (ex: after a seek) */
if ( ! ogg_page_eos( &p_sys->current_page ) ) p_stream->b_finished = false; if ( ! ogg_page_eos( &p_sys->current_page ) && p_sys->p_skelstream != p_stream )
p_stream->b_finished = false;
DemuxDebug( DemuxDebug(
if ( p_stream->fmt.i_cat == VIDEO_ES ) if ( p_stream->fmt.i_cat == VIDEO_ES )
...@@ -398,6 +406,19 @@ static int Demux( demux_t * p_demux ) ...@@ -398,6 +406,19 @@ static int Demux( demux_t * p_demux )
p_sys->current_page.body_len ) p_sys->current_page.body_len )
); );
if ( p_stream->i_pcr < VLC_TS_0 && ogg_page_granulepos( &p_sys->current_page ) > 0 )
{
// PASS 0
if ( p_stream->fmt.i_codec == VLC_CODEC_OPUS ||
p_stream->fmt.i_codec == VLC_CODEC_VORBIS ||
p_stream->fmt.i_cat == VIDEO_ES )
{
assert( p_stream->p_prepcr_blocks == NULL );
p_stream->i_prepcr_blocks = 0;
p_stream->p_prepcr_blocks = malloc( sizeof(block_t) * ogg_page_packets( &p_sys->current_page ) );
}
}
while( ogg_stream_packetout( &p_stream->os, &oggpacket ) > 0 ) while( ogg_stream_packetout( &p_stream->os, &oggpacket ) > 0 )
{ {
/* Read info from any secondary header packets, if there are any */ /* Read info from any secondary header packets, if there are any */
...@@ -433,99 +454,106 @@ static int Demux( demux_t * p_demux ) ...@@ -433,99 +454,106 @@ static int Demux( demux_t * p_demux )
if( p_stream->b_reinit ) if( p_stream->b_reinit )
{ {
if ( Oggseek_PacketPCRFixup( p_stream, &p_sys->current_page, p_stream->b_reinit = false;
&oggpacket ) ) if( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
{
DemuxDebug( msg_Dbg( p_demux, "PCR fixup for %"PRId64,
ogg_page_granulepos( &p_sys->current_page ) ) );
}
else
{ {
/* If synchro is re-initialized we need to drop all the packets p_stream->i_skip_frames = p_stream->i_pre_skip;
* until we find a new dated one. */
Ogg_UpdatePCR( p_demux, p_stream, &oggpacket );
} }
}
Ogg_DecodePacket( p_demux, p_stream, &oggpacket );
}
if ( p_stream->p_prepcr_blocks )
{
int64_t pagestamp = Oggseek_GranuleToAbsTimestamp( p_stream, ogg_page_granulepos( &p_sys->current_page ), false );
p_stream->i_previous_pcr = pagestamp;
#ifdef HAVE_LIBVORBIS
int i_prev_blocksize = 0;
#endif
// PASS 1
for( int i=0; i<p_stream->i_prepcr_blocks; i++ )
{
block_t *p_block = p_stream->p_prepcr_blocks[i];
ogg_packet dumb_packet;
dumb_packet.bytes = p_block->i_buffer;
dumb_packet.packet = p_block->p_buffer;
int i_duration;
if( p_stream->i_pcr >= 0 ) switch( p_stream->fmt.i_codec )
{ {
p_stream->b_reinit = false; case VLC_CODEC_OPUS:
/* For Opus, trash the first 80 ms of decoded output as i_duration = Ogg_OpusPacketDuration( p_stream, &dumb_packet );
well, to avoid blowing out speakers if we get unlucky. p_block->i_nb_samples = i_duration;
Opus predicts content from prior frames, which can go break;
badly if we seek right where the stream goes from very #ifdef HAVE_LIBVORBIS
quiet to very loud. It will converge after a bit. */ case VLC_CODEC_VORBIS:
if( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
{
ogg_int64_t start_time;
int duration;
p_stream->i_skip_frames = 80*48;
/* Make sure we never play audio from within the
pre-skip at the beginning of the stream. */
duration =
Ogg_OpusPacketDuration( p_stream, &oggpacket );
start_time = p_stream->i_previous_granulepos;
if( duration > 0 )
{
start_time = start_time > duration ?
start_time - duration : 0;
}
if( p_stream->i_pre_skip > start_time )
{
p_stream->i_skip_frames +=
p_stream->i_pre_skip - start_time;
}
}
}
else
{ {
DemuxDebug( long i_blocksize = vorbis_packet_blocksize(
msg_Dbg(p_demux, "DEMUX DROPS PACKET (? / %d) pageno %ld granule %"PRId64, p_stream->special.vorbis.p_info, &dumb_packet );
ogg_page_packets( &p_sys->current_page ), if ( i_prev_blocksize )
ogg_page_pageno( &p_sys->current_page ), oggpacket.granulepos ); i_duration = ( i_blocksize + i_prev_blocksize ) / 4;
); else
i_duration = i_blocksize / 2;
p_stream->i_interpolated_pcr = -1; i_prev_blocksize = i_blocksize;
p_stream->i_previous_granulepos = -1; p_block->i_nb_samples = i_duration;
continue; }
#endif
} }
}
/* An Ogg/vorbis packet contains an end date granulepos */ // PASS 2
if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS || bool b_fixed = false;
p_stream->fmt.i_codec == VLC_CODEC_SPEEX || for( int i=p_stream->i_prepcr_blocks - 1; i>=0; i-- )
p_stream->fmt.i_codec == VLC_CODEC_OPUS || {
p_stream->fmt.i_codec == VLC_CODEC_VP8 || block_t *p_block = p_stream->p_prepcr_blocks[i];
p_stream->fmt.i_codec == VLC_CODEC_FLAC ) switch( p_stream->fmt.i_codec )
{ {
if( ogg_stream_packetout( &p_stream->os, &oggpacket ) > 0 ) case VLC_CODEC_OPUS:
case VLC_CODEC_VORBIS:
pagestamp -= CLOCK_FREQ * p_block->i_nb_samples / p_stream->f_rate;
if ( pagestamp < 0 )
{ {
Ogg_DecodePacket( p_demux, p_stream, &oggpacket ); p_block->i_pts = VLC_TS_INVALID;
p_block->i_flags |= BLOCK_FLAG_PREROLL;
} }
else else
p_block->i_pts = VLC_TS_0 + p_sys->i_nzpcr_offset + pagestamp;
b_fixed = true;
break;
default:
if ( p_stream->fmt.i_cat == VIDEO_ES )
{ {
es_out_Control( p_demux->out, ES_OUT_SET_PCR, pagestamp = pagestamp - ( CLOCK_FREQ / p_stream->f_rate );
VLC_TS_0 + p_stream->i_pcr ); p_block->i_pts = p_sys->i_nzpcr_offset + pagestamp;
b_fixed = true;
} }
continue;
} }
} }
DemuxDebug( if ( p_sys->b_seeked ) if ( b_fixed )
{ {
if ( Ogg_IsKeyFrame( p_stream, &oggpacket ) ) if ( pagestamp < 0 ) pagestamp = 0;
msg_Dbg(p_demux, "** DEMUX ON KEYFRAME **" ); p_stream->i_pcr = VLC_TS_0 + pagestamp;
p_stream->i_pcr += p_sys->i_nzpcr_offset;
p_stream->i_previous_granulepos = ogg_page_granulepos( &p_sys->current_page );
}
ogg_int64_t iframe = ogg_page_granulepos( &p_sys->current_page ) >> p_stream->i_granule_shift; FREENULL( p_stream->p_prepcr_blocks );
ogg_int64_t pframe = ogg_page_granulepos( &p_sys->current_page ) - ( iframe << p_stream->i_granule_shift ); p_stream->i_prepcr_blocks = 0;
msg_Dbg(p_demux, "DEMUX PACKET (size %d) IS at iframe %"PRId64" pageno %ld pframe %"PRId64" OFFSET %"PRId64" PACKET NO %"PRId64" skipleft=%d", Ogg_SendOrQueueBlocks( p_demux, p_stream, NULL );
ogg_page_packets( &p_sys->current_page ),
iframe, ogg_page_pageno( &p_sys->current_page ), pframe, p_sys->i_input_position, oggpacket.packetno, p_stream->i_skip_frames );
})
Ogg_DecodePacket( p_demux, p_stream, &oggpacket );
} }
DemuxDebug( p_sys->b_seeked = false; ) int64_t i_pagestamp = Oggseek_GranuleToAbsTimestamp( p_stream,
ogg_page_granulepos( &p_sys->current_page ), false );
if ( i_pagestamp > -1 )
{
p_stream->i_pcr = VLC_TS_0 + i_pagestamp;
p_stream->i_pcr += p_sys->i_nzpcr_offset;
}
if( !p_sys->b_page_waiting ) if( !p_sys->b_page_waiting )
break; break;
...@@ -555,15 +583,16 @@ static int Demux( demux_t * p_demux ) ...@@ -555,15 +583,16 @@ static int Demux( demux_t * p_demux )
if( p_stream->fmt.i_cat == SPU_ES ) if( p_stream->fmt.i_cat == SPU_ES )
continue; continue;
if( p_stream->i_interpolated_pcr < VLC_TS_0 ) if( p_stream->i_pcr < VLC_TS_0 )
continue; continue;
if ( p_stream->b_finished || p_stream->b_initializing ) if ( p_stream->b_finished || p_stream->b_initializing )
continue; continue;
if ( p_stream->p_preparse_block )
continue;
if( i_pcr_candidate < VLC_TS_0 if( i_pcr_candidate < VLC_TS_0
|| p_stream->i_interpolated_pcr <= i_pcr_candidate ) || p_stream->i_pcr <= i_pcr_candidate )
{ {
i_pcr_candidate = p_stream->i_interpolated_pcr; i_pcr_candidate = p_stream->i_pcr;
} }
} }
...@@ -580,28 +609,42 @@ static int Demux( demux_t * p_demux ) ...@@ -580,28 +609,42 @@ static int Demux( demux_t * p_demux )
p_sys->i_access_delay / 1000, i_pcr_jitter / 1000 ); p_sys->i_access_delay / 1000, i_pcr_jitter / 1000 );
} }
} }
p_sys->i_pcr = i_pcr_candidate;
if( ! b_skipping ) if( ! b_skipping && p_sys->b_preparsing_done )
{
p_sys->i_pcr = i_pcr_candidate;
es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_pcr ); es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_pcr );
}
} }
return 1; return 1;
} }
static void Ogg_ResetStreamHelper( demux_sys_t *p_sys ) static void Ogg_ResetStream( logical_stream_t *p_stream )
{ {
for( int i = 0; i < p_sys->i_streams; i++ ) #ifdef HAVE_LIBVORBIS
if ( p_stream->fmt.i_codec == VLC_CODEC_VORBIS )
{ {
logical_stream_t *p_stream = p_sys->pp_stream[i]; p_stream->special.vorbis.i_prev_blocksize = 0;
/* we'll trash all the data until we find the next pcr */
p_stream->b_reinit = true;
p_stream->i_pcr = VLC_TS_UNKNOWN;
p_stream->i_interpolated_pcr = VLC_TS_UNKNOWN;
p_stream->i_previous_granulepos = -1;
ogg_stream_reset( &p_stream->os );
} }
#endif
/* we'll trash all the data until we find the next pcr */
p_stream->b_reinit = true;
p_stream->i_pcr = VLC_TS_UNKNOWN;
p_stream->i_previous_granulepos = -1;
p_stream->i_previous_pcr = VLC_TS_UNKNOWN;
ogg_stream_reset( &p_stream->os );
FREENULL( p_stream->p_prepcr_blocks );
p_stream->i_prepcr_blocks = 0;
}
static void Ogg_ResetStreamsHelper( demux_sys_t *p_sys )
{
for( int i = 0; i < p_sys->i_streams; i++ )
Ogg_ResetStream( p_sys->pp_stream[i] );
ogg_sync_reset( &p_sys->oy ); ogg_sync_reset( &p_sys->oy );
p_sys->i_pcr = VLC_TS_UNKNOWN;
} }
static logical_stream_t * Ogg_GetSelectedStream( demux_t *p_demux ) static logical_stream_t * Ogg_GetSelectedStream( demux_t *p_demux )
...@@ -673,7 +716,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) ...@@ -673,7 +716,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b ); stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b );
if ( Oggseek_BlindSeektoAbsoluteTime( p_demux, p_stream, i64, b ) ) if ( Oggseek_BlindSeektoAbsoluteTime( p_demux, p_stream, i64, b ) )
{ {
Ogg_ResetStreamHelper( p_sys ); Ogg_ResetStreamsHelper( p_sys );
es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
VLC_TS_0 + i64 ); VLC_TS_0 + i64 );
return VLC_SUCCESS; return VLC_SUCCESS;
...@@ -734,14 +777,14 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) ...@@ -734,14 +777,14 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
f = (double)va_arg( args, double ); f = (double)va_arg( args, double );
if ( p_sys->i_length <= 0 || !b /* || ! ACCESS_CAN_FASTSEEK */ ) if ( p_sys->i_length <= 0 || !b /* || ! ACCESS_CAN_FASTSEEK */ )
{ {
Ogg_ResetStreamHelper( p_sys ); Ogg_ResetStreamsHelper( p_sys );
Oggseek_BlindSeektoPosition( p_demux, p_stream, f, b ); Oggseek_BlindSeektoPosition( p_demux, p_stream, f, b );
return VLC_SUCCESS; return VLC_SUCCESS;
} }
assert( p_sys->i_length > 0 ); assert( p_sys->i_length > 0 );
i64 = CLOCK_FREQ * p_sys->i_length * f; i64 = CLOCK_FREQ * p_sys->i_length * f;
Ogg_ResetStreamHelper( p_sys ); Ogg_ResetStreamsHelper( p_sys );
if ( Oggseek_SeektoAbsolutetime( p_demux, p_stream, i64 ) >= 0 ) if ( Oggseek_SeektoAbsolutetime( p_demux, p_stream, i64 ) >= 0 )
{ {
es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
...@@ -813,7 +856,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) ...@@ -813,7 +856,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b ); stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b );
if ( Oggseek_BlindSeektoAbsoluteTime( p_demux, p_stream, i64, b ) ) if ( Oggseek_BlindSeektoAbsoluteTime( p_demux, p_stream, i64, b ) )
{ {
Ogg_ResetStreamHelper( p_sys ); Ogg_ResetStreamsHelper( p_sys );
es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME,
VLC_TS_0 + i64 ); VLC_TS_0 + i64 );
p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT; p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT;
...@@ -870,88 +913,154 @@ static void Ogg_UpdatePCR( demux_t *p_demux, logical_stream_t *p_stream, ...@@ -870,88 +913,154 @@ static void Ogg_UpdatePCR( demux_t *p_demux, logical_stream_t *p_stream,
if ( p_oggpacket->granulepos == 0 ) if ( p_oggpacket->granulepos == 0 )
{ {
/* We're in headers, and we haven't parsed 1st data packet yet */ /* We're in headers, and we haven't parsed 1st data packet yet */
p_stream->i_pcr = VLC_TS_UNKNOWN; // p_stream->i_pcr = VLC_TS_UNKNOWN;
p_stream->i_interpolated_pcr = VLC_TS_UNKNOWN;
} }
else if( p_oggpacket->granulepos > 0 ) else if( p_oggpacket->granulepos > 0 )
{ {
if( p_stream->fmt.i_codec == VLC_CODEC_THEORA || if( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
p_stream->fmt.i_codec == VLC_CODEC_KATE || p_stream->fmt.i_codec == VLC_CODEC_KATE ||
p_stream->fmt.i_codec == VLC_CODEC_VP8 || p_stream->fmt.i_codec == VLC_CODEC_VP8 ||
p_stream->fmt.i_codec == VLC_CODEC_DIRAC ) p_stream->fmt.i_codec == VLC_CODEC_DIRAC ||
(p_stream->b_oggds && p_stream->fmt.i_cat == VIDEO_ES) )
{ {
p_stream->i_pcr = Oggseek_GranuleToAbsTimestamp( p_stream, p_stream->i_pcr = VLC_TS_0 + Oggseek_GranuleToAbsTimestamp( p_stream,
p_oggpacket->granulepos, true ); p_oggpacket->granulepos, true );
p_stream->i_pcr += p_ogg->i_nzpcr_offset;
} }
else else if ( p_stream->i_previous_granulepos > 0 )
{ {
ogg_int64_t sample; ogg_int64_t sample = p_stream->i_previous_granulepos;
sample = p_oggpacket->granulepos;
if( p_oggpacket->e_o_s && if( p_stream->fmt.i_codec == VLC_CODEC_OPUS && p_oggpacket->e_o_s )
p_stream->fmt.i_codec == VLC_CODEC_OPUS &&
p_stream->i_previous_granulepos >= 0 )
{ {
int duration; int duration = Ogg_OpusPacketDuration( p_stream, p_oggpacket );
duration = Ogg_OpusPacketDuration( p_stream, p_oggpacket );
if( duration > 0 ) if( duration > 0 )
{ {
ogg_int64_t end_sample; ogg_int64_t end_sample = p_oggpacket->granulepos;
end_sample = p_stream->i_previous_granulepos + duration; if( end_sample < ( sample + duration ) )
if( end_sample > sample ) p_stream->i_end_trim = sample + duration - end_sample;
p_stream->i_end_trim = (int)(end_sample - sample);
} }
} }
if (sample >= p_stream->i_pre_skip) if (sample >= p_stream->i_pre_skip)
sample -= p_stream->i_pre_skip; sample -= p_stream->i_pre_skip;
else else
sample = 0; sample = 0;
p_stream->i_pcr = sample * CLOCK_FREQ / p_stream->f_rate;
p_stream->i_pcr = VLC_TS_0 + sample * CLOCK_FREQ / p_stream->f_rate;
p_stream->i_pcr += p_ogg->i_nzpcr_offset;
} }
if ( !p_ogg->i_pcr_offset )
p_stream->i_pcr += VLC_TS_0;
else
p_stream->i_pcr += p_ogg->i_pcr_offset;
p_stream->i_interpolated_pcr = p_stream->i_pcr;
} }
else else if ( p_oggpacket->granulepos == -1 )
{ {
int duration; int i_duration;
p_stream->i_pcr = VLC_TS_INVALID;
/* no granulepos available, try to interpolate the pcr. /* no granulepos available, try to interpolate the pcr.
* If we can't then don't touch the old value. */ * If we can't then don't touch the old value. */
if( p_stream->fmt.i_cat == VIDEO_ES ) if( p_stream->fmt.i_cat == VIDEO_ES && p_stream->i_pcr > VLC_TS_INVALID )
/* 1 frame per packet */ {
p_stream->i_interpolated_pcr += (CLOCK_FREQ / p_stream->f_rate); p_stream->i_pcr += (CLOCK_FREQ / p_stream->f_rate);
}
#ifdef HAVE_LIBVORBIS
else if ( p_stream->fmt.i_codec == VLC_CODEC_VORBIS &&
p_stream->special.vorbis.p_info &&
!p_stream->special.vorbis.b_invalid &&
p_stream->i_previous_granulepos > 0 )
{
long i_blocksize = vorbis_packet_blocksize(
p_stream->special.vorbis.p_info, p_oggpacket );
if ( p_stream->special.vorbis.i_prev_blocksize )
i_duration = ( i_blocksize + p_stream->special.vorbis.i_prev_blocksize ) / 4;
else
i_duration = i_blocksize / 2;
p_stream->special.vorbis.i_prev_blocksize = i_blocksize;
/* duration in samples per channel */
p_oggpacket->granulepos = p_stream->i_previous_granulepos + i_duration;
p_stream->i_pcr = p_stream->i_previous_granulepos *
CLOCK_FREQ / p_stream->special.vorbis.p_info->rate;
p_stream->i_pcr += p_ogg->i_nzpcr_offset;
}
#endif
else if( p_stream->fmt.i_codec == VLC_CODEC_OPUS && else if( p_stream->fmt.i_codec == VLC_CODEC_OPUS &&
p_stream->i_previous_granulepos > 0 && p_stream->i_previous_granulepos > 0 &&
( duration = ( i_duration =
Ogg_OpusPacketDuration( p_stream, p_oggpacket ) ) > 0 ) Ogg_OpusPacketDuration( p_stream, p_oggpacket ) ) > 0 )
{ {
ogg_int64_t sample; ogg_int64_t sample;
p_oggpacket->granulepos = p_oggpacket->granulepos = p_stream->i_previous_granulepos + i_duration;
p_stream->i_previous_granulepos + duration; sample = p_stream->i_previous_granulepos;
sample = p_oggpacket->granulepos;
if (sample >= p_stream->i_pre_skip) if (sample >= p_stream->i_pre_skip)
sample -= p_stream->i_pre_skip; sample -= p_stream->i_pre_skip;
else else
sample = 0; sample = 0;
p_stream->i_interpolated_pcr =
VLC_TS_0 + sample * CLOCK_FREQ / p_stream->f_rate; p_stream->i_pcr = VLC_TS_0 + sample * CLOCK_FREQ / p_stream->f_rate;
p_stream->i_interpolated_pcr += p_ogg->i_pcr_offset; p_stream->i_pcr += p_ogg->i_nzpcr_offset;
} }
else if( p_stream->fmt.i_bitrate && p_stream->i_pcr > VLC_TS_UNKNOWN ) else if( p_stream->fmt.i_bitrate && p_stream->i_pcr > VLC_TS_UNKNOWN )
{ {
p_stream->i_interpolated_pcr += p_stream->i_pcr += ( CLOCK_FREQ * p_oggpacket->bytes /
( p_oggpacket->bytes * CLOCK_FREQ / p_stream->fmt.i_bitrate / 8 );
p_stream->fmt.i_bitrate / 8 );
p_stream->i_interpolated_pcr += p_ogg->i_pcr_offset;
} }
} }
p_stream->i_previous_granulepos = p_oggpacket->granulepos; p_stream->i_previous_granulepos = p_oggpacket->granulepos;
} }
static void Ogg_SendOrQueueBlocks( demux_t *p_demux, logical_stream_t *p_stream,
block_t *p_block )
{
demux_sys_t *p_ogg = p_demux->p_sys;
if ( !p_stream->p_es || p_stream->p_prepcr_blocks || p_stream->i_pcr == VLC_TS_UNKNOWN )
{
if ( !p_block ) return;
if ( p_stream->p_prepcr_blocks )
{
assert( p_stream->p_prepcr_blocks );
p_stream->p_prepcr_blocks[p_stream->i_prepcr_blocks++] = p_block;
}
DemuxDebug( msg_Dbg( p_demux, "block prepcr append > pts %"PRId64" spcr %"PRId64" pcr %"PRId64,
p_block->i_pts, p_stream->i_pcr, p_ogg->i_pcr ); )
block_ChainAppend( & p_stream->p_preparse_block, p_block );
}
else
{
/* Because ES creation is delayed for preparsing */
mtime_t i_firstpts = VLC_TS_UNKNOWN;
if ( p_stream->p_preparse_block )
{
block_t *temp = p_stream->p_preparse_block;
while ( temp )
{
if ( temp && i_firstpts < VLC_TS_0 )
i_firstpts = temp->i_pts;
block_t *tosend = temp;
temp = temp->p_next;
tosend->p_next = NULL;
DemuxDebug( msg_Dbg( p_demux, "block sent from preparse > pts %"PRId64" spcr %"PRId64" pcr %"PRId64,
tosend->i_pts, p_stream->i_pcr, p_ogg->i_pcr ); )
es_out_Send( p_demux->out, p_stream->p_es, tosend );
if ( p_ogg->i_pcr < VLC_TS_0 && i_firstpts > VLC_TS_INVALID )
{
p_ogg->i_pcr = i_firstpts;
es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_ogg->i_pcr );
}
}
p_stream->p_preparse_block = NULL;
}
if ( p_block )
{
DemuxDebug( msg_Dbg( p_demux, "block sent directly > pts %"PRId64" spcr %"PRId64" pcr %"PRId64,
p_block->i_pts, p_stream->i_pcr, p_ogg->i_pcr ) );
es_out_Send( p_demux->out, p_stream->p_es, p_block );
}
}
}
/**************************************************************************** /****************************************************************************
* Ogg_DecodePacket: Decode an Ogg packet. * Ogg_DecodePacket: Decode an Ogg packet.
****************************************************************************/ ****************************************************************************/
...@@ -962,8 +1071,6 @@ static void Ogg_DecodePacket( demux_t *p_demux, ...@@ -962,8 +1071,6 @@ static void Ogg_DecodePacket( demux_t *p_demux,
block_t *p_block; block_t *p_block;
bool b_selected; bool b_selected;
int i_header_len = 0; int i_header_len = 0;
mtime_t i_pts = VLC_TS_UNKNOWN;
demux_sys_t *p_ogg = p_demux->p_sys;
if( p_oggpacket->bytes >= 7 && if( p_oggpacket->bytes >= 7 &&
! memcmp ( p_oggpacket->packet, "Annodex", 7 ) ) ! memcmp ( p_oggpacket->packet, "Annodex", 7 ) )
...@@ -1014,6 +1121,10 @@ static void Ogg_DecodePacket( demux_t *p_demux, ...@@ -1014,6 +1121,10 @@ static void Ogg_DecodePacket( demux_t *p_demux,
switch( p_stream->fmt.i_codec ) switch( p_stream->fmt.i_codec )
{ {
case VLC_CODEC_VORBIS: case VLC_CODEC_VORBIS:
#ifdef HAVE_LIBVORBIS
Ogg_DecodeVorbisHeader( p_stream, p_oggpacket, p_stream->i_packets_backup );
#endif
//ft
case VLC_CODEC_THEORA: case VLC_CODEC_THEORA:
if( p_stream->i_packets_backup == 3 ) if( p_stream->i_packets_backup == 3 )
p_stream->b_force_backup = false; p_stream->b_force_backup = false;
...@@ -1051,7 +1162,7 @@ static void Ogg_DecodePacket( demux_t *p_demux, ...@@ -1051,7 +1162,7 @@ static void Ogg_DecodePacket( demux_t *p_demux,
break; break;
case VLC_CODEC_KATE: case VLC_CODEC_KATE:
if( p_stream->i_packets_backup == p_stream->i_kate_num_headers ) if( p_stream->i_packets_backup == p_stream->special.kate.i_num_headers )
p_stream->b_force_backup = false; p_stream->b_force_backup = false;
b_xiph = true; b_xiph = true;
break; break;
...@@ -1114,39 +1225,13 @@ static void Ogg_DecodePacket( demux_t *p_demux, ...@@ -1114,39 +1225,13 @@ static void Ogg_DecodePacket( demux_t *p_demux,
b_selected = false; /* Discard the header packet */ b_selected = false; /* Discard the header packet */
} }
else else
p_stream->b_initializing = false;
/* Convert the pcr into a pts */
if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS ||
p_stream->fmt.i_codec == VLC_CODEC_SPEEX ||
p_stream->fmt.i_codec == VLC_CODEC_OPUS ||
p_stream->fmt.i_codec == VLC_CODEC_VP8 ||
p_stream->fmt.i_codec == VLC_CODEC_FLAC )
{ {
if( p_stream->i_pcr > VLC_TS_INVALID ) p_stream->b_initializing = false;
{
p_stream->i_previous_pcr = p_stream->i_pcr;
/* The granulepos is the end date of the sample */
i_pts = p_stream->i_pcr;
}
} }
/* Convert the granulepos into the next pcr */ /* Convert the granulepos into the next pcr */
Ogg_UpdatePCR( p_demux, p_stream, p_oggpacket ); Ogg_UpdatePCR( p_demux, p_stream, p_oggpacket );
if( p_stream->fmt.i_codec != VLC_CODEC_VORBIS &&
p_stream->fmt.i_codec != VLC_CODEC_SPEEX &&
p_stream->fmt.i_codec != VLC_CODEC_OPUS &&
p_stream->fmt.i_codec != VLC_CODEC_VP8 &&
p_stream->fmt.i_codec != VLC_CODEC_FLAC &&
p_stream->i_pcr >= 0 )
{
p_stream->i_previous_pcr = p_stream->i_pcr;
/* The granulepos is the start date of the sample */
i_pts = p_stream->i_pcr;
}
if( !b_selected ) if( !b_selected )
{ {
/* This stream isn't currently selected so we don't need to decode it, /* This stream isn't currently selected so we don't need to decode it,
...@@ -1155,31 +1240,28 @@ static void Ogg_DecodePacket( demux_t *p_demux, ...@@ -1155,31 +1240,28 @@ static void Ogg_DecodePacket( demux_t *p_demux,
} }
if( !( p_block = block_Alloc( p_oggpacket->bytes ) ) ) return; if( !( p_block = block_Alloc( p_oggpacket->bytes ) ) ) return;
p_block->i_pts = p_stream->i_pcr;
DemuxDebug( msg_Dbg(p_demux, "block set from granule %"PRId64" to pts/pcr %"PRId64" skip %d",
p_oggpacket->granulepos, p_stream->i_pcr, p_stream->i_skip_frames); )
/* may need to preroll after a seek */ if( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
p_block->i_nb_samples = Ogg_OpusPacketDuration( p_stream, p_oggpacket );
/* may need to preroll after a seek or in case of preskip */
if ( p_stream->i_skip_frames > 0 ) if ( p_stream->i_skip_frames > 0 )
{ {
if( p_stream->fmt.i_codec == VLC_CODEC_OPUS ) if( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
{ {
int duration; if( p_stream->i_skip_frames >= p_block->i_nb_samples )
duration = Ogg_OpusPacketDuration( p_stream, p_oggpacket );
if( p_stream->i_skip_frames > duration )
{ {
p_block->i_flags |= BLOCK_FLAG_PREROLL; p_block->i_flags |= BLOCK_FLAG_PREROLL;
p_stream->i_skip_frames -= p_block->i_nb_samples;
p_block->i_nb_samples = 0; p_block->i_nb_samples = 0;
p_stream->i_skip_frames -= duration;
} }
else else
{ {
p_block->i_nb_samples = duration - p_stream->i_skip_frames; p_block->i_nb_samples -= p_stream->i_skip_frames;
if( p_stream->i_previous_granulepos >=
p_block->i_nb_samples + p_stream->i_pre_skip )
{
i_pts = VLC_TS_0 + (p_stream->i_previous_granulepos
- p_block->i_nb_samples - p_stream->i_pre_skip) *
CLOCK_FREQ / p_stream->f_rate;
}
p_stream->i_skip_frames = 0; p_stream->i_skip_frames = 0;
} }
} }
...@@ -1189,61 +1271,35 @@ static void Ogg_DecodePacket( demux_t *p_demux, ...@@ -1189,61 +1271,35 @@ static void Ogg_DecodePacket( demux_t *p_demux,
p_stream->i_skip_frames--; p_stream->i_skip_frames--;
} }
} }
else if( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
p_block->i_nb_samples = Ogg_OpusPacketDuration( p_stream, p_oggpacket );
/* Normalize PTS */ /* Conditional block fixes */
if( i_pts == VLC_TS_UNKNOWN ) if ( p_stream->fmt.i_cat == VIDEO_ES &&
i_pts = VLC_TS_INVALID; Ogg_IsKeyFrame( p_stream, p_oggpacket ) )
{
if( p_stream->fmt.i_cat == AUDIO_ES ) p_block->i_flags |= BLOCK_FLAG_TYPE_I;
}
else if( p_stream->fmt.i_cat == AUDIO_ES )
{ {
p_block->i_dts = p_block->i_pts = i_pts;
/* Blatant abuse of the i_length field. */ /* Blatant abuse of the i_length field. */
p_block->i_length = p_stream->i_end_trim; p_block->i_length = p_stream->i_end_trim;
} }
else if( p_stream->fmt.i_cat == SPU_ES ) else if( p_stream->fmt.i_cat == SPU_ES )
{ {
p_block->i_dts = p_block->i_pts = i_pts;
p_block->i_length = 0; p_block->i_length = 0;
} }
else if( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
{
p_block->i_dts = p_block->i_pts = i_pts;
if( (p_oggpacket->granulepos & ((1<<p_stream->i_granule_shift)-1)) == 0 )
{
p_block->i_flags |= BLOCK_FLAG_TYPE_I;
}
}
else if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC ) else if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC )
{ {
ogg_int64_t dts = p_oggpacket->granulepos >> 31; ogg_int64_t nzdts = Oggseek_GranuleToAbsTimestamp( p_stream, p_oggpacket->granulepos, false );
ogg_int64_t delay = (p_oggpacket->granulepos >> 9) & 0x1fff; ogg_int64_t nzpts = Oggseek_GranuleToAbsTimestamp( p_stream, p_oggpacket->granulepos, true );
p_block->i_dts = ( nzdts > VLC_TS_INVALID ) ? VLC_TS_0 + nzdts : nzdts;
uint64_t u_pnum = dts + delay; p_block->i_pts = ( nzpts > VLC_TS_INVALID ) ? VLC_TS_0 + nzpts : nzpts;
p_block->i_dts = p_stream->i_pcr;
p_block->i_pts = VLC_TS_INVALID;
/* NB, OggDirac granulepos values are in units of 2*picturerate */
/* granulepos for dirac is possibly broken, this value should be ignored */ /* granulepos for dirac is possibly broken, this value should be ignored */
if( -1 != p_oggpacket->granulepos ) if( 0 >= p_oggpacket->granulepos )
p_block->i_pts = u_pnum * CLOCK_FREQ / p_stream->f_rate / 2;
}
else if( p_stream->fmt.i_codec == VLC_CODEC_VP8 )
{
p_block->i_pts = p_stream->i_interpolated_pcr;
p_block->i_dts = p_block->i_pts;
if( p_oggpacket->granulepos > 0 && Ogg_IsKeyFrame( p_stream, p_oggpacket ) )
{ {
p_block->i_flags |= BLOCK_FLAG_TYPE_I; p_block->i_pts = VLC_TS_INVALID;
p_block->i_dts = p_stream->i_pcr;
} }
} }
else
{
p_block->i_dts = i_pts;
p_block->i_pts = VLC_TS_INVALID;
}
if( p_stream->fmt.i_codec != VLC_CODEC_VORBIS && if( p_stream->fmt.i_codec != VLC_CODEC_VORBIS &&
p_stream->fmt.i_codec != VLC_CODEC_SPEEX && p_stream->fmt.i_codec != VLC_CODEC_SPEEX &&
...@@ -1308,29 +1364,12 @@ static void Ogg_DecodePacket( demux_t *p_demux, ...@@ -1308,29 +1364,12 @@ static void Ogg_DecodePacket( demux_t *p_demux,
memcpy( p_block->p_buffer, p_oggpacket->packet + i_header_len, memcpy( p_block->p_buffer, p_oggpacket->packet + i_header_len,
p_oggpacket->bytes - i_header_len ); p_oggpacket->bytes - i_header_len );
if ( p_ogg->i_streams == 1 && p_block->i_pts > VLC_TS_INVALID && p_ogg->i_pcr < VLC_TS_0 ) Ogg_SendOrQueueBlocks( p_demux, p_stream, p_block );
{
p_ogg->i_pcr = p_block->i_pts;
es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_ogg->i_pcr );
}
if ( p_stream->p_es )
{
/* Because ES creation is delayed for preparsing */
if ( p_stream->p_preparse_block )
{
es_out_Send( p_demux->out, p_stream->p_es, p_stream->p_preparse_block );
p_stream->p_preparse_block = NULL;
}
es_out_Send( p_demux->out, p_stream->p_es, p_block );
}
else
block_ChainAppend( & p_stream->p_preparse_block, p_block );
} }
/* Re-implemented to avoid linking against libopus from the demuxer. */ /* Re-implemented to avoid linking against libopus from the demuxer. */
static int Ogg_OpusPacketDuration( logical_stream_t *p_stream, static int Ogg_OpusDataDuration( logical_stream_t *p_stream,
ogg_packet *p_oggpacket ) unsigned char *data, long i_datalen )
{ {
static const int silk_fs_div[4] = { 6000, 3000, 1500, 1000 }; static const int silk_fs_div[4] = { 6000, 3000, 1500, 1000 };
int toc; int toc;
...@@ -1338,9 +1377,9 @@ static int Ogg_OpusPacketDuration( logical_stream_t *p_stream, ...@@ -1338,9 +1377,9 @@ static int Ogg_OpusPacketDuration( logical_stream_t *p_stream,
int frame_size; int frame_size;
int nsamples; int nsamples;
int i_rate; int i_rate;
if( p_oggpacket->bytes < 1 ) if( i_datalen < 1 )
return VLC_EGENERIC; return VLC_EGENERIC;
toc = p_oggpacket->packet[0]; toc = data[0];
switch( toc&3 ) switch( toc&3 )
{ {
case 0: case 0:
...@@ -1351,9 +1390,9 @@ static int Ogg_OpusPacketDuration( logical_stream_t *p_stream, ...@@ -1351,9 +1390,9 @@ static int Ogg_OpusPacketDuration( logical_stream_t *p_stream,
nframes = 2; nframes = 2;
break; break;
default: default:
if( p_oggpacket->bytes < 2 ) if( i_datalen < 2 )
return VLC_EGENERIC; return VLC_EGENERIC;
nframes = p_oggpacket->packet[1]&0x3F; nframes = data[1]&0x3F;
break; break;
} }
i_rate = (int)p_stream->fmt.audio.i_rate; i_rate = (int)p_stream->fmt.audio.i_rate;
...@@ -1369,6 +1408,12 @@ static int Ogg_OpusPacketDuration( logical_stream_t *p_stream, ...@@ -1369,6 +1408,12 @@ static int Ogg_OpusPacketDuration( logical_stream_t *p_stream,
return nsamples; return nsamples;
} }
static int Ogg_OpusPacketDuration( logical_stream_t *p_stream,
ogg_packet *p_oggpacket )
{
return Ogg_OpusDataDuration( p_stream, p_oggpacket->packet, p_oggpacket->bytes );
}
/**************************************************************************** /****************************************************************************
* Ogg_FindLogicalStreams: Find the logical streams embedded in the physical * Ogg_FindLogicalStreams: Find the logical streams embedded in the physical
* stream and fill p_ogg. * stream and fill p_ogg.
...@@ -1468,7 +1513,6 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) ...@@ -1468,7 +1513,6 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
* important info in the second header packet!!! * important info in the second header packet!!!
* (STREAMINFO metadata is in the following packet) */ * (STREAMINFO metadata is in the following packet) */
p_stream->b_force_backup = true; p_stream->b_force_backup = true;
p_stream->fmt.i_cat = AUDIO_ES; p_stream->fmt.i_cat = AUDIO_ES;
p_stream->fmt.i_codec = VLC_CODEC_FLAC; p_stream->fmt.i_codec = VLC_CODEC_FLAC;
} }
...@@ -1496,7 +1540,6 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) ...@@ -1496,7 +1540,6 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
! memcmp( oggpacket.packet, "\x80theora", 7 ) ) ! memcmp( oggpacket.packet, "\x80theora", 7 ) )
{ {
Ogg_ReadTheoraHeader( p_stream, &oggpacket ); Ogg_ReadTheoraHeader( p_stream, &oggpacket );
msg_Dbg( p_demux, msg_Dbg( p_demux,
"found theora header, bitrate: %i, rate: %f", "found theora header, bitrate: %i, rate: %f",
p_stream->fmt.i_bitrate, p_stream->f_rate ); p_stream->fmt.i_bitrate, p_stream->f_rate );
...@@ -1889,6 +1932,7 @@ static void Ogg_CreateES( demux_t *p_demux ) ...@@ -1889,6 +1932,7 @@ static void Ogg_CreateES( demux_t *p_demux )
p_stream->b_finished = false; p_stream->b_finished = false;
p_stream->b_reinit = false; p_stream->b_reinit = false;
p_stream->b_initializing = false; p_stream->b_initializing = false;
p_stream->i_pre_skip = 0;
bool b_resetdecoder = Ogg_LogicalStreamResetEsFormat( p_demux, p_stream ); bool b_resetdecoder = Ogg_LogicalStreamResetEsFormat( p_demux, p_stream );
es_format_Copy( &p_stream->fmt_old, &p_old_stream->fmt ); es_format_Copy( &p_stream->fmt_old, &p_old_stream->fmt );
...@@ -1958,8 +2002,8 @@ static int Ogg_BeginningOfStream( demux_t *p_demux ) ...@@ -1958,8 +2002,8 @@ static int Ogg_BeginningOfStream( demux_t *p_demux )
else else
p_ogg->i_bitrate += p_stream->fmt.i_bitrate; p_ogg->i_bitrate += p_stream->fmt.i_bitrate;
p_stream->i_pcr = p_stream->i_previous_pcr = p_stream->i_pcr = p_stream->i_previous_pcr = VLC_TS_UNKNOWN;
p_stream->i_interpolated_pcr = VLC_TS_UNKNOWN; p_stream->i_previous_granulepos = -1;
p_stream->b_reinit = false; p_stream->b_reinit = false;
} }
...@@ -1989,7 +2033,8 @@ static void Ogg_EndOfStream( demux_t *p_demux ) ...@@ -1989,7 +2033,8 @@ static void Ogg_EndOfStream( demux_t *p_demux )
p_ogg->skeleton.minor = 0; p_ogg->skeleton.minor = 0;
p_ogg->b_preparsing_done = false; p_ogg->b_preparsing_done = false;
p_ogg->b_es_created = false; p_ogg->b_es_created = false;
p_ogg->i_pcr_offset = p_ogg->i_pcr; p_ogg->i_nzpcr_offset = (p_ogg->i_pcr >= VLC_TS_INVALID) ?
p_ogg->i_pcr - VLC_TS_0 : 0;
/* */ /* */
if( p_ogg->p_meta ) if( p_ogg->p_meta )
...@@ -2005,6 +2050,20 @@ static void Ogg_EndOfStream( demux_t *p_demux ) ...@@ -2005,6 +2050,20 @@ static void Ogg_EndOfStream( demux_t *p_demux )
p_ogg->i_seekpoints = 0; p_ogg->i_seekpoints = 0;
} }
static void Ogg_CleanSpecificData( logical_stream_t *p_stream )
{
#ifdef HAVE_LIBVORBIS
if ( p_stream->fmt.i_codec == VLC_CODEC_VORBIS )
{
FREENULL( p_stream->special.vorbis.p_info );
FREENULL( p_stream->special.vorbis.p_comment );
p_stream->special.vorbis.b_invalid = false;
}
#else
VLC_UNUSED( p_stream );
#endif
}
/** /**
* This function delete and release all data associated to a logical_stream_t * This function delete and release all data associated to a logical_stream_t
*/ */
...@@ -2016,6 +2075,8 @@ static void Ogg_LogicalStreamDelete( demux_t *p_demux, logical_stream_t *p_strea ...@@ -2016,6 +2075,8 @@ static void Ogg_LogicalStreamDelete( demux_t *p_demux, logical_stream_t *p_strea
ogg_stream_clear( &p_stream->os ); ogg_stream_clear( &p_stream->os );
free( p_stream->p_headers ); free( p_stream->p_headers );
Ogg_CleanSpecificData( p_stream );
es_format_Clean( &p_stream->fmt_old ); es_format_Clean( &p_stream->fmt_old );
es_format_Clean( &p_stream->fmt ); es_format_Clean( &p_stream->fmt );
...@@ -2370,6 +2431,39 @@ static void Ogg_ReadVorbisHeader( logical_stream_t *p_stream, ...@@ -2370,6 +2431,39 @@ static void Ogg_ReadVorbisHeader( logical_stream_t *p_stream,
if( p_stream->fmt.i_bitrate > INT32_MAX ) p_stream->fmt.i_bitrate = 0; if( p_stream->fmt.i_bitrate > INT32_MAX ) p_stream->fmt.i_bitrate = 0;
if ( p_stream->f_rate == 0 ) p_stream->fmt.i_cat = UNKNOWN_ES; if ( p_stream->f_rate == 0 ) p_stream->fmt.i_cat = UNKNOWN_ES;
} }
#ifdef HAVE_LIBVORBIS
static void Ogg_DecodeVorbisHeader( logical_stream_t *p_stream,
ogg_packet *p_oggpacket, int i_number )
{
switch( i_number )
{
case VORBIS_HEADER_IDENTIFICATION:
p_stream->special.vorbis.p_info = malloc( sizeof(vorbis_info) );
p_stream->special.vorbis.p_comment = malloc( sizeof(vorbis_comment) );
if ( !p_stream->special.vorbis.p_info || !p_stream->special.vorbis.p_comment )
{
free( p_stream->special.vorbis.p_info );
free( p_stream->special.vorbis.p_comment );
}
vorbis_info_init( p_stream->special.vorbis.p_info );
vorbis_comment_init( p_stream->special.vorbis.p_comment );
// ft
case VORBIS_HEADER_COMMENT:
case VORBIS_HEADER_SETUP:
if ( p_stream->special.vorbis.p_info && ! p_stream->special.vorbis.b_invalid )
{
p_stream->special.vorbis.b_invalid = ( 0 != vorbis_synthesis_headerin(
p_stream->special.vorbis.p_info,
p_stream->special.vorbis.p_comment, p_oggpacket ) );
}
// ft
default:
break;
}
}
#endif
static void Ogg_ReadSpeexHeader( logical_stream_t *p_stream, static void Ogg_ReadSpeexHeader( logical_stream_t *p_stream,
ogg_packet *p_oggpacket ) ogg_packet *p_oggpacket )
...@@ -2426,6 +2520,12 @@ static void Ogg_ReadOpusHeader( logical_stream_t *p_stream, ...@@ -2426,6 +2520,12 @@ static void Ogg_ReadOpusHeader( logical_stream_t *p_stream,
p_stream->fmt.audio.i_channels = oggpack_read( &opb, 8 ); p_stream->fmt.audio.i_channels = oggpack_read( &opb, 8 );
fill_channels_info(&p_stream->fmt.audio); fill_channels_info(&p_stream->fmt.audio);
p_stream->i_pre_skip = oggpack_read( &opb, 16 ); p_stream->i_pre_skip = oggpack_read( &opb, 16 );
/* For Opus, trash the first 80 ms of decoded output as
well, to avoid blowing out speakers if we get unlucky.
Opus predicts content from prior frames, which can go
badly if we seek right where the stream goes from very
quiet to very loud. It will converge after a bit. */
p_stream->i_pre_skip = __MAX( 80*48, p_stream->i_pre_skip );
} }
static void Ogg_ReadFlacHeader( demux_t *p_demux, logical_stream_t *p_stream, static void Ogg_ReadFlacHeader( demux_t *p_demux, logical_stream_t *p_stream,
...@@ -2482,7 +2582,7 @@ static void Ogg_ReadKateHeader( logical_stream_t *p_stream, ...@@ -2482,7 +2582,7 @@ static void Ogg_ReadKateHeader( logical_stream_t *p_stream,
/* Cheat and get additionnal info ;) */ /* Cheat and get additionnal info ;) */
oggpack_readinit( &opb, p_oggpacket->packet, p_oggpacket->bytes); oggpack_readinit( &opb, p_oggpacket->packet, p_oggpacket->bytes);
oggpack_adv( &opb, 11*8 ); /* packet type, kate magic, version */ oggpack_adv( &opb, 11*8 ); /* packet type, kate magic, version */
p_stream->i_kate_num_headers = oggpack_read( &opb, 8 ); p_stream->special.kate.i_num_headers = oggpack_read( &opb, 8 );
oggpack_adv( &opb, 3*8 ); oggpack_adv( &opb, 3*8 );
p_stream->i_granule_shift = oggpack_read( &opb, 8 ); p_stream->i_granule_shift = oggpack_read( &opb, 8 );
oggpack_adv( &opb, 8*8 ); /* reserved */ oggpack_adv( &opb, 8*8 ); /* reserved */
...@@ -2990,8 +3090,10 @@ static bool Ogg_ReadDiracHeader( logical_stream_t *p_stream, ...@@ -2990,8 +3090,10 @@ static bool Ogg_ReadDiracHeader( logical_stream_t *p_stream,
if( dirac_bool( &bs ) ) if( dirac_bool( &bs ) )
{ {
dirac_uint( &bs ); /* scan_format */ p_stream->special.dirac.b_interlaced = dirac_uint( &bs ); /* scan_format */
} }
else
p_stream->special.dirac.b_interlaced = false;
uint32_t u_n = p_dirac_frate_tbl[pu_dirac_vidfmt_frate[u_video_format]].u_n; uint32_t u_n = p_dirac_frate_tbl[pu_dirac_vidfmt_frate[u_video_format]].u_n;
uint32_t u_d = p_dirac_frate_tbl[pu_dirac_vidfmt_frate[u_video_format]].u_d; uint32_t u_d = p_dirac_frate_tbl[pu_dirac_vidfmt_frate[u_video_format]].u_d;
......
...@@ -22,6 +22,14 @@ ...@@ -22,6 +22,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/ *****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_LIBVORBIS
#include <vorbis/codec.h>
#endif
/***************************************************************************** /*****************************************************************************
* Definitions of structures and functions used by this plugin * Definitions of structures and functions used by this plugin
*****************************************************************************/ *****************************************************************************/
...@@ -43,6 +51,12 @@ ...@@ -43,6 +51,12 @@
typedef struct oggseek_index_entry demux_index_entry_t; typedef struct oggseek_index_entry demux_index_entry_t;
typedef struct ogg_skeleton_t ogg_skeleton_t; typedef struct ogg_skeleton_t ogg_skeleton_t;
typedef struct backup_queue
{
block_t *p_block;
mtime_t i_duration;
} backup_queue_t;
typedef struct logical_stream_s typedef struct logical_stream_s
{ {
ogg_stream_state os; /* logical stream of packets */ ogg_stream_state os; /* logical stream of packets */
...@@ -63,11 +77,11 @@ typedef struct logical_stream_s ...@@ -63,11 +77,11 @@ typedef struct logical_stream_s
void *p_headers; void *p_headers;
int i_headers; int i_headers;
ogg_int64_t i_previous_granulepos; ogg_int64_t i_previous_granulepos;
ogg_int64_t i_granulepos_offset;/* first granule offset */
/* program clock reference (in units of 90kHz) derived from the previous /* program clock reference (in units of 90kHz) derived from the previous
* granulepos */ * granulepos */
mtime_t i_pcr; mtime_t i_pcr;
mtime_t i_interpolated_pcr;
mtime_t i_previous_pcr; mtime_t i_previous_pcr;
/* Misc */ /* Misc */
...@@ -92,18 +106,42 @@ typedef struct logical_stream_s ...@@ -92,18 +106,42 @@ typedef struct logical_stream_s
ogg_skeleton_t *p_skel; ogg_skeleton_t *p_skel;
/* skip some frames after a seek */ /* skip some frames after a seek */
int i_skip_frames; unsigned int i_skip_frames;
/* data start offset (absolute) in bytes */ /* data start offset (absolute) in bytes */
int64_t i_data_start; int64_t i_data_start;
/* kate streams have the number of headers in the ID header */
int i_kate_num_headers;
/* for Annodex logical bitstreams */ /* for Annodex logical bitstreams */
int i_secondary_header_packets; int i_secondary_header_packets;
/* All blocks which can't be sent because track PCR isn't known yet */
block_t **p_prepcr_blocks;
int i_prepcr_blocks;
/* All blocks that are queued because ES isn't created yet */
block_t *p_preparse_block; block_t *p_preparse_block;
union
{
#ifdef HAVE_LIBVORBIS
struct
{
vorbis_info *p_info;
vorbis_comment *p_comment;
bool b_invalid;
int i_prev_blocksize;
} vorbis;
#endif
struct
{
/* kate streams have the number of headers in the ID header */
int i_num_headers;
} kate;
struct
{
bool b_interlaced;
} dirac;
} special;
} logical_stream_t; } logical_stream_t;
struct ogg_skeleton_t struct ogg_skeleton_t
...@@ -131,7 +169,7 @@ struct demux_sys_t ...@@ -131,7 +169,7 @@ struct demux_sys_t
/* program clock reference (in units of 90kHz) derived from the pcr of /* program clock reference (in units of 90kHz) derived from the pcr of
* the sub-streams */ * the sub-streams */
mtime_t i_pcr; mtime_t i_pcr;
mtime_t i_pcr_offset; mtime_t i_nzpcr_offset;
/* informative only */ /* informative only */
mtime_t i_pcr_jitter; mtime_t i_pcr_jitter;
int64_t i_access_delay; int64_t i_access_delay;
...@@ -181,7 +219,6 @@ struct demux_sys_t ...@@ -181,7 +219,6 @@ struct demux_sys_t
/* Length, if available. */ /* Length, if available. */
int64_t i_length; int64_t i_length;
DemuxDebug( bool b_seeked; )
}; };
......
...@@ -93,96 +93,6 @@ static demux_index_entry_t *index_entry_new( void ) ...@@ -93,96 +93,6 @@ static demux_index_entry_t *index_entry_new( void )
return idx; return idx;
} }
/* add a theora entry to our list; format is highest granulepos -> page offset of
keyframe start */
const demux_index_entry_t *oggseek_theora_index_entry_add ( logical_stream_t *p_stream,
int64_t i_granule,
int64_t i_pagepos)
{
/* add or update entry for keyframe */
demux_index_entry_t *idx;
demux_index_entry_t *oidx;
demux_index_entry_t *last_idx = NULL;
int64_t i_gpos;
int64_t i_frame;
int64_t i_kframe;
int64_t i_tframe;
int64_t i_tkframe;
if ( p_stream == NULL ) return NULL;
oidx = idx = p_stream->idx;
i_tkframe = i_granule >> p_stream->i_granule_shift;
i_tframe = i_tkframe + i_granule - ( i_tkframe << p_stream->i_granule_shift );
if ( i_tkframe < 1 ) return NULL;
if ( idx == NULL )
{
demux_index_entry_t *ie = index_entry_new();
ie->i_value = i_granule;
ie->i_pagepos = i_pagepos;
p_stream->idx = ie;
return ie;
}
while ( idx != NULL )
{
i_gpos = idx->i_value;
i_kframe = i_gpos >> p_stream->i_granule_shift;
if ( i_kframe > i_tframe ) break;
if ( i_kframe == i_tkframe )
{
/* entry exists, update it if applicable, and return it */
i_frame = i_kframe + i_gpos - ( i_kframe << p_stream->i_granule_shift );
if ( i_frame < i_tframe )
{
idx->i_value = i_granule;
idx->i_pagepos = i_pagepos;
}
return idx;
}
last_idx = idx;
idx = idx->p_next;
}
/* new entry; insert after last_idx */
idx = index_entry_new();
if ( last_idx != NULL )
{
idx->p_next = last_idx->p_next;
last_idx->p_next = idx;
idx->p_prev = last_idx;
}
else
{
idx->p_next = oidx;
oidx = idx;
}
if ( idx->p_next != NULL )
{
idx->p_next->p_prev = idx;
}
idx->i_value = i_granule;
idx->i_pagepos = i_pagepos;
return idx;
}
/* We insert into index, sorting by pagepos (as a page can match multiple /* We insert into index, sorting by pagepos (as a page can match multiple
time stamps) */ time stamps) */
const demux_index_entry_t *OggSeek_IndexAdd ( logical_stream_t *p_stream, const demux_index_entry_t *OggSeek_IndexAdd ( logical_stream_t *p_stream,
...@@ -414,19 +324,6 @@ clean: ...@@ -414,19 +324,6 @@ clean:
ogg_stream_clear( &os ); ogg_stream_clear( &os );
} }
/* convert a theora frame to a granulepos */
static inline int64_t frame_to_gpos( logical_stream_t *p_stream, int64_t i_kframe,
int64_t i_frame )
{
if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
{
return ( i_kframe << p_stream->i_granule_shift ) + ( i_frame - i_kframe );
}
return i_kframe;
}
static int64_t find_first_page_granule( demux_t *p_demux, static int64_t find_first_page_granule( demux_t *p_demux,
int64_t i_pos1, int64_t i_pos2, int64_t i_pos1, int64_t i_pos2,
...@@ -552,18 +449,20 @@ bool Ogg_IsKeyFrame( logical_stream_t *p_stream, ogg_packet *p_packet ) ...@@ -552,18 +449,20 @@ bool Ogg_IsKeyFrame( logical_stream_t *p_stream, ogg_packet *p_packet )
{ {
return ( p_packet->bytes > 0 && p_packet->packet[0] & PACKET_IS_SYNCPOINT ); return ( p_packet->bytes > 0 && p_packet->packet[0] & PACKET_IS_SYNCPOINT );
} }
else if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA ) else switch ( p_stream->fmt.i_codec )
{ {
case VLC_CODEC_THEORA:
if ( p_packet->bytes <= 0 || p_packet->packet[0] & THEORA_FTYPE_NOTDATA ) if ( p_packet->bytes <= 0 || p_packet->packet[0] & THEORA_FTYPE_NOTDATA )
return false; return false;
else else
return !( p_packet->packet[0] & THEORA_FTYPE_INTERFRAME ); return !( p_packet->packet[0] & THEORA_FTYPE_INTERFRAME );
} case VLC_CODEC_VP8:
else if ( p_stream->fmt.i_codec == VLC_CODEC_VP8 )
{
return ( ( ( p_packet->granulepos >> 3 ) & 0x07FFFFFF ) == 0 ); return ( ( ( p_packet->granulepos >> 3 ) & 0x07FFFFFF ) == 0 );
case VLC_CODEC_DIRAC:
return ( p_packet->granulepos & 0xFF8000FF );
default:
return true;
} }
return true;
} }
int64_t Ogg_GetKeyframeGranule( logical_stream_t *p_stream, int64_t i_granule ) int64_t Ogg_GetKeyframeGranule( logical_stream_t *p_stream, int64_t i_granule )
...@@ -638,7 +537,6 @@ static bool OggSeekToPacket( demux_t *p_demux, logical_stream_t *p_stream, ...@@ -638,7 +537,6 @@ static bool OggSeekToPacket( demux_t *p_demux, logical_stream_t *p_stream,
p_lastpacketcoords->i_pos = p_sys->i_input_position; p_lastpacketcoords->i_pos = p_sys->i_input_position;
p_lastpacketcoords->i_skip = i; p_lastpacketcoords->i_skip = i;
} }
return true; return true;
} }
...@@ -649,6 +547,7 @@ static bool OggSeekToPacket( demux_t *p_demux, logical_stream_t *p_stream, ...@@ -649,6 +547,7 @@ static bool OggSeekToPacket( demux_t *p_demux, logical_stream_t *p_stream,
/* remove that packet and go sync to next */ /* remove that packet and go sync to next */
ogg_stream_packetout( &p_stream->os, &op ); ogg_stream_packetout( &p_stream->os, &op );
} }
return false; return false;
} }
...@@ -779,75 +678,60 @@ int64_t Oggseek_GranuleToAbsTimestamp( logical_stream_t *p_stream, ...@@ -779,75 +678,60 @@ int64_t Oggseek_GranuleToAbsTimestamp( logical_stream_t *p_stream,
int64_t i_granule, bool b_presentation ) int64_t i_granule, bool b_presentation )
{ {
int64_t i_timestamp = -1; int64_t i_timestamp = -1;
if ( i_granule < 0 ) if ( i_granule < 1 )
return -1; return -1;
if ( p_stream->b_oggds ) if ( p_stream->b_oggds )
{ {
if ( b_presentation ) i_granule--;
i_timestamp = i_granule * CLOCK_FREQ / p_stream->f_rate; i_timestamp = i_granule * CLOCK_FREQ / p_stream->f_rate;
} }
else if( p_stream->fmt.i_codec == VLC_CODEC_THEORA || else switch( p_stream->fmt.i_codec )
p_stream->fmt.i_codec == VLC_CODEC_KATE ) {
case VLC_CODEC_THEORA:
case VLC_CODEC_KATE:
{ {
ogg_int64_t iframe = i_granule >> p_stream->i_granule_shift; ogg_int64_t iframe = i_granule >> p_stream->i_granule_shift;
ogg_int64_t pframe = i_granule - ( iframe << p_stream->i_granule_shift ); ogg_int64_t pframe = i_granule - ( iframe << p_stream->i_granule_shift );
/* See Theora A.2.3 */ /* See Theora A.2.3 */
if ( b_presentation ) pframe -= p_stream->i_keyframe_offset; if ( b_presentation ) pframe -= p_stream->i_keyframe_offset;
i_timestamp = ( iframe + pframe ) * CLOCK_FREQ / p_stream->f_rate; i_timestamp = ( iframe + pframe ) * CLOCK_FREQ / p_stream->f_rate;
break;
} }
else if ( p_stream->fmt.i_codec == VLC_CODEC_VP8 ) case VLC_CODEC_VP8:
{ {
ogg_int64_t frame = i_granule >> p_stream->i_granule_shift; ogg_int64_t frame = i_granule >> p_stream->i_granule_shift;
if ( b_presentation ) frame--; if ( b_presentation ) frame--;
i_timestamp = frame * CLOCK_FREQ / p_stream->f_rate; i_timestamp = frame * CLOCK_FREQ / p_stream->f_rate;
break;
} }
else if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC ) case VLC_CODEC_DIRAC:
{ {
ogg_int64_t i_dts = i_granule >> 31; ogg_int64_t i_dts = i_granule >> 31;
ogg_int64_t delay = (i_granule >> 9) & 0x1fff;
/* NB, OggDirac granulepos values are in units of 2*picturerate */ /* NB, OggDirac granulepos values are in units of 2*picturerate */
i_timestamp = (i_dts/2) * CLOCK_FREQ / p_stream->f_rate; double f_rate = p_stream->f_rate;
if ( !p_stream->special.dirac.b_interlaced ) f_rate *= 2;
if ( b_presentation ) i_dts += delay;
i_timestamp = i_dts * CLOCK_FREQ / f_rate;
break;
} }
else if( p_stream->fmt.i_codec == VLC_CODEC_OPUS ) case VLC_CODEC_OPUS:
{ {
if ( b_presentation ) return VLC_TS_INVALID;
i_timestamp = ( i_granule - p_stream->i_pre_skip ) * CLOCK_FREQ / 48000; i_timestamp = ( i_granule - p_stream->i_pre_skip ) * CLOCK_FREQ / 48000;
break;
} }
else if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS ) case VLC_CODEC_VORBIS:
case VLC_CODEC_FLAC:
{ {
if ( b_presentation ) return VLC_TS_INVALID;
i_timestamp = i_granule * CLOCK_FREQ / p_stream->f_rate; i_timestamp = i_granule * CLOCK_FREQ / p_stream->f_rate;
break;
} }
return i_timestamp;
}
bool Oggseek_PacketPCRFixup( logical_stream_t *p_stream, ogg_page *p_page,
ogg_packet *p_packet )
{
if ( p_packet->granulepos != -1 )
return false;
else
if ( p_stream->b_oggds )
{
p_stream->i_pcr = Oggseek_GranuleToAbsTimestamp( p_stream,
ogg_page_granulepos( p_page ), true );
return true;
}
else if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
{
p_stream->i_pcr = Oggseek_GranuleToAbsTimestamp( p_stream,
ogg_page_granulepos( p_page ), false );
/* Computes the presentation time of the first packet on page */
p_stream->i_pcr -= CLOCK_FREQ *
ogg_page_packets( p_page ) / p_stream->f_rate;
return true;
}
else if ( p_stream->fmt.i_codec == VLC_CODEC_VP8 )
{
ogg_int64_t frame = ogg_page_granulepos( p_page ) >> p_stream->i_granule_shift;
frame -= ogg_page_packets( p_page );
p_stream->i_pcr = frame * CLOCK_FREQ / p_stream->f_rate;
} }
return false; return i_timestamp;
} }
/* returns pos */ /* returns pos */
......
...@@ -54,19 +54,12 @@ bool Ogg_IsKeyFrame ( logical_stream_t *, ogg_packet * ); ...@@ -54,19 +54,12 @@ bool Ogg_IsKeyFrame ( logical_stream_t *, ogg_packet * );
int64_t Oggseek_GranuleToAbsTimestamp ( logical_stream_t *p_stream, int64_t i_granule, int64_t Oggseek_GranuleToAbsTimestamp ( logical_stream_t *p_stream, int64_t i_granule,
bool b_presentation ); bool b_presentation );
bool Oggseek_PacketPCRFixup ( logical_stream_t *p_stream, ogg_page *, ogg_packet * );
int Oggseek_BlindSeektoAbsoluteTime ( demux_t *, logical_stream_t *, int64_t, bool ); int Oggseek_BlindSeektoAbsoluteTime ( demux_t *, logical_stream_t *, int64_t, bool );
int Oggseek_BlindSeektoPosition ( demux_t *, logical_stream_t *, double f, bool ); int Oggseek_BlindSeektoPosition ( demux_t *, logical_stream_t *, double f, bool );
int Oggseek_SeektoAbsolutetime ( demux_t *, logical_stream_t *, int64_t i_granulepos ); int Oggseek_SeektoAbsolutetime ( demux_t *, logical_stream_t *, int64_t i_granulepos );
const demux_index_entry_t *OggSeek_IndexAdd ( logical_stream_t *, int64_t, int64_t ); const demux_index_entry_t *OggSeek_IndexAdd ( logical_stream_t *, int64_t, int64_t );
void Oggseek_ProbeEnd( demux_t * ); void Oggseek_ProbeEnd( demux_t * );
const demux_index_entry_t *oggseek_theora_index_entry_add ( logical_stream_t *,
int64_t i_granule,
int64_t i_pagepos );
void oggseek_index_entries_free ( demux_index_entry_t * ); void oggseek_index_entries_free ( demux_index_entry_t * );
int oggseek_find_frame ( demux_t *, logical_stream_t *, int64_t i_tframe );
int64_t oggseek_read_page ( demux_t * ); int64_t oggseek_read_page ( demux_t * );
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment