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
dnl ogg demux plugin
dnl
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
PKG_ENABLE_MODULES_VLC([MUX_OGG], [], [ogg >= 1.0], [Ogg mux support], [auto])
dnl Check for libshout
......
......@@ -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 \
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_LIBADD = $(LIBS_ogg)
libogg_plugin_la_LIBADD = $(LIBS_ogg) $(LIBVORBIS_LIBS)
EXTRA_LTLIBRARIES += libogg_plugin.la
demux_LTLIBRARIES += $(LTLIBogg)
......
......@@ -105,6 +105,10 @@ typedef struct
} sh;
} stream_header_t;
#define VORBIS_HEADER_IDENTIFICATION 1
#define VORBIS_HEADER_COMMENT 2
#define VORBIS_HEADER_SETUP 3
/*****************************************************************************
* Local prototypes
*****************************************************************************/
......@@ -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_DecodePacket ( demux_t *, 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 int Ogg_BeginningOfStream( 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 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 );
......@@ -147,6 +153,12 @@ static void Ogg_ReadSkeletonIndex( demux_t *, ogg_packet * );
static void Ogg_FreeSkeleton( ogg_skeleton_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 const int pi_channels_map[9] =
......@@ -369,13 +381,8 @@ static int Demux( demux_t * p_demux )
ogg_page_serialno( &p_sys->current_page ) != p_stream->os.serialno )
{
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 ) );
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 */
......@@ -386,7 +393,8 @@ static int Demux( demux_t * p_demux )
}
/* 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(
if ( p_stream->fmt.i_cat == VIDEO_ES )
......@@ -398,6 +406,19 @@ static int Demux( demux_t * p_demux )
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 )
{
/* Read info from any secondary header packets, if there are any */
......@@ -433,99 +454,106 @@ static int Demux( demux_t * p_demux )
if( p_stream->b_reinit )
{
if ( Oggseek_PacketPCRFixup( p_stream, &p_sys->current_page,
&oggpacket ) )
p_stream->b_reinit = false;
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 ) ) );
p_stream->i_skip_frames = p_stream->i_pre_skip;
}
else
{
/* If synchro is re-initialized we need to drop all the packets
* until we find a new dated one. */
Ogg_UpdatePCR( p_demux, p_stream, &oggpacket );
}
if( p_stream->i_pcr >= 0 )
Ogg_DecodePacket( p_demux, p_stream, &oggpacket );
}
if ( p_stream->p_prepcr_blocks )
{
p_stream->b_reinit = false;
/* 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. */
if( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
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++ )
{
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 )
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;
switch( p_stream->fmt.i_codec )
{
start_time = start_time > duration ?
start_time - duration : 0;
}
if( p_stream->i_pre_skip > start_time )
case VLC_CODEC_OPUS:
i_duration = Ogg_OpusPacketDuration( p_stream, &dumb_packet );
p_block->i_nb_samples = i_duration;
break;
#ifdef HAVE_LIBVORBIS
case VLC_CODEC_VORBIS:
{
p_stream->i_skip_frames +=
p_stream->i_pre_skip - start_time;
}
long i_blocksize = vorbis_packet_blocksize(
p_stream->special.vorbis.p_info, &dumb_packet );
if ( i_prev_blocksize )
i_duration = ( i_blocksize + i_prev_blocksize ) / 4;
else
i_duration = i_blocksize / 2;
i_prev_blocksize = i_blocksize;
p_block->i_nb_samples = i_duration;
}
#endif
}
else
{
DemuxDebug(
msg_Dbg(p_demux, "DEMUX DROPS PACKET (? / %d) pageno %ld granule %"PRId64,
ogg_page_packets( &p_sys->current_page ),
ogg_page_pageno( &p_sys->current_page ), oggpacket.granulepos );
);
p_stream->i_interpolated_pcr = -1;
p_stream->i_previous_granulepos = -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_OPUS ||
p_stream->fmt.i_codec == VLC_CODEC_VP8 ||
p_stream->fmt.i_codec == VLC_CODEC_FLAC )
// PASS 2
bool b_fixed = false;
for( int i=p_stream->i_prepcr_blocks - 1; i>=0; i-- )
{
if( ogg_stream_packetout( &p_stream->os, &oggpacket ) > 0 )
block_t *p_block = p_stream->p_prepcr_blocks[i];
switch( p_stream->fmt.i_codec )
{
Ogg_DecodePacket( p_demux, p_stream, &oggpacket );
case VLC_CODEC_OPUS:
case VLC_CODEC_VORBIS:
pagestamp -= CLOCK_FREQ * p_block->i_nb_samples / p_stream->f_rate;
if ( pagestamp < 0 )
{
p_block->i_pts = VLC_TS_INVALID;
p_block->i_flags |= BLOCK_FLAG_PREROLL;
}
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,
VLC_TS_0 + p_stream->i_pcr );
pagestamp = pagestamp - ( CLOCK_FREQ / p_stream->f_rate );
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 ) )
msg_Dbg(p_demux, "** DEMUX ON KEYFRAME **" );
if ( pagestamp < 0 ) pagestamp = 0;
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;
ogg_int64_t pframe = ogg_page_granulepos( &p_sys->current_page ) - ( iframe << p_stream->i_granule_shift );
FREENULL( p_stream->p_prepcr_blocks );
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_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_SendOrQueueBlocks( p_demux, p_stream, NULL );
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 )
break;
......@@ -555,15 +583,16 @@ static int Demux( demux_t * p_demux )
if( p_stream->fmt.i_cat == SPU_ES )
continue;
if( p_stream->i_interpolated_pcr < VLC_TS_0 )
if( p_stream->i_pcr < VLC_TS_0 )
continue;
if ( p_stream->b_finished || p_stream->b_initializing )
continue;
if ( p_stream->p_preparse_block )
continue;
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 )
p_sys->i_access_delay / 1000, i_pcr_jitter / 1000 );
}
}
if( ! b_skipping && p_sys->b_preparsing_done )
{
p_sys->i_pcr = i_pcr_candidate;
if( ! b_skipping )
es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_pcr );
}
}
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;
}
#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_interpolated_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 );
p_sys->i_pcr = VLC_TS_UNKNOWN;
}
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 )
stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &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,
VLC_TS_0 + i64 );
return VLC_SUCCESS;
......@@ -734,14 +777,14 @@ static int Control( demux_t *p_demux, int i_query, va_list args )
f = (double)va_arg( args, double );
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 );
return VLC_SUCCESS;
}
assert( p_sys->i_length > 0 );
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 )
{
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 )
stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &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,
VLC_TS_0 + i64 );
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,
if ( p_oggpacket->granulepos == 0 )
{
/* We're in headers, and we haven't parsed 1st data packet yet */
p_stream->i_pcr = VLC_TS_UNKNOWN;
p_stream->i_interpolated_pcr = VLC_TS_UNKNOWN;
// p_stream->i_pcr = VLC_TS_UNKNOWN;
}
else if( p_oggpacket->granulepos > 0 )
{
if( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
p_stream->fmt.i_codec == VLC_CODEC_KATE ||
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_stream->i_pcr += p_ogg->i_nzpcr_offset;
}
else
else if ( p_stream->i_previous_granulepos > 0 )
{
ogg_int64_t sample;
sample = p_oggpacket->granulepos;
if( p_oggpacket->e_o_s &&
p_stream->fmt.i_codec == VLC_CODEC_OPUS &&
p_stream->i_previous_granulepos >= 0 )
ogg_int64_t sample = p_stream->i_previous_granulepos;
if( p_stream->fmt.i_codec == VLC_CODEC_OPUS && p_oggpacket->e_o_s )
{
int duration;
duration = Ogg_OpusPacketDuration( p_stream, p_oggpacket );
int duration = Ogg_OpusPacketDuration( p_stream, p_oggpacket );
if( duration > 0 )
{
ogg_int64_t end_sample;
end_sample = p_stream->i_previous_granulepos + duration;
if( end_sample > sample )
p_stream->i_end_trim = (int)(end_sample - sample);
ogg_int64_t end_sample = p_oggpacket->granulepos;
if( end_sample < ( sample + duration ) )
p_stream->i_end_trim = sample + duration - end_sample;
}
}
if (sample >= p_stream->i_pre_skip)
sample -= p_stream->i_pre_skip;
else
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;
p_stream->i_pcr = VLC_TS_INVALID;
int i_duration;
/* no granulepos available, try to interpolate the pcr.
* If we can't then don't touch the old value. */
if( p_stream->fmt.i_cat == VIDEO_ES )
/* 1 frame per packet */
p_stream->i_interpolated_pcr += (CLOCK_FREQ / p_stream->f_rate);
if( p_stream->fmt.i_cat == VIDEO_ES && p_stream->i_pcr > VLC_TS_INVALID )
{
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 &&
p_stream->i_previous_granulepos > 0 &&
( duration =
( i_duration =
Ogg_OpusPacketDuration( p_stream, p_oggpacket ) ) > 0 )
{
ogg_int64_t sample;
p_oggpacket->granulepos =
p_stream->i_previous_granulepos + duration;
sample = p_oggpacket->granulepos;
p_oggpacket->granulepos = p_stream->i_previous_granulepos + i_duration;
sample = p_stream->i_previous_granulepos;
if (sample >= p_stream->i_pre_skip)
sample -= p_stream->i_pre_skip;
else
sample = 0;
p_stream->i_interpolated_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 = VLC_TS_0 + sample * CLOCK_FREQ / p_stream->f_rate;
p_stream->i_pcr += p_ogg->i_nzpcr_offset;
}
else if( p_stream->fmt.i_bitrate && p_stream->i_pcr > VLC_TS_UNKNOWN )
{
p_stream->i_interpolated_pcr +=
( p_oggpacket->bytes * CLOCK_FREQ /
p_stream->i_pcr += ( CLOCK_FREQ * p_oggpacket->bytes /
p_stream->fmt.i_bitrate / 8 );
p_stream->i_interpolated_pcr += p_ogg->i_pcr_offset;
}
}
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.
****************************************************************************/
......@@ -962,8 +1071,6 @@ static void Ogg_DecodePacket( demux_t *p_demux,
block_t *p_block;
bool b_selected;
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 &&
! memcmp ( p_oggpacket->packet, "Annodex", 7 ) )
......@@ -1014,6 +1121,10 @@ static void Ogg_DecodePacket( demux_t *p_demux,
switch( p_stream->fmt.i_codec )
{
case VLC_CODEC_VORBIS:
#ifdef HAVE_LIBVORBIS
Ogg_DecodeVorbisHeader( p_stream, p_oggpacket, p_stream->i_packets_backup );
#endif
//ft
case VLC_CODEC_THEORA:
if( p_stream->i_packets_backup == 3 )
p_stream->b_force_backup = false;
......@@ -1051,7 +1162,7 @@ static void Ogg_DecodePacket( demux_t *p_demux,
break;
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;
b_xiph = true;
break;
......@@ -1114,39 +1225,13 @@ static void Ogg_DecodePacket( demux_t *p_demux,
b_selected = false; /* Discard the header packet */
}
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->i_previous_pcr = p_stream->i_pcr;
/* The granulepos is the end date of the sample */
i_pts = p_stream->i_pcr;
}
p_stream->b_initializing = false;
}
/* Convert the granulepos into the next pcr */
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 )
{
/* 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,
}
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); )
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 */
/* may need to preroll after a seek or in case of preskip */
if ( p_stream->i_skip_frames > 0 )
{
if( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
{
int duration;
duration = Ogg_OpusPacketDuration( p_stream, p_oggpacket );
if( p_stream->i_skip_frames > duration )
if( p_stream->i_skip_frames >= p_block->i_nb_samples )
{
p_block->i_flags |= BLOCK_FLAG_PREROLL;
p_stream->i_skip_frames -= p_block->i_nb_samples;
p_block->i_nb_samples = 0;
p_stream->i_skip_frames -= duration;
}
else
{
p_block->i_nb_samples = duration - 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_block->i_nb_samples -= p_stream->i_skip_frames;
p_stream->i_skip_frames = 0;
}
}
......@@ -1189,60 +1271,34 @@ static void Ogg_DecodePacket( demux_t *p_demux,
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 */
if( i_pts == VLC_TS_UNKNOWN )
i_pts = VLC_TS_INVALID;
if( p_stream->fmt.i_cat == AUDIO_ES )
/* Conditional block fixes */
if ( p_stream->fmt.i_cat == VIDEO_ES &&
Ogg_IsKeyFrame( p_stream, p_oggpacket ) )
{
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. */
p_block->i_length = p_stream->i_end_trim;
}
else if( p_stream->fmt.i_cat == SPU_ES )
{
p_block->i_dts = p_block->i_pts = i_pts;
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 )
{
ogg_int64_t dts = p_oggpacket->granulepos >> 31;
ogg_int64_t delay = (p_oggpacket->granulepos >> 9) & 0x1fff;
uint64_t u_pnum = dts + delay;
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 */
ogg_int64_t nzdts = Oggseek_GranuleToAbsTimestamp( p_stream, p_oggpacket->granulepos, false );
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;
p_block->i_pts = ( nzpts > VLC_TS_INVALID ) ? VLC_TS_0 + nzpts : nzpts;
/* granulepos for dirac is possibly broken, this value should be ignored */
if( -1 != 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;
}
}
else
if( 0 >= p_oggpacket->granulepos )
{
p_block->i_dts = i_pts;
p_block->i_pts = VLC_TS_INVALID;
p_block->i_dts = p_stream->i_pcr;
}
}
if( p_stream->fmt.i_codec != VLC_CODEC_VORBIS &&
......@@ -1308,29 +1364,12 @@ static void Ogg_DecodePacket( demux_t *p_demux,
memcpy( p_block->p_buffer, p_oggpacket->packet + 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 )
{
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 );
Ogg_SendOrQueueBlocks( p_demux, p_stream, p_block );
}
/* Re-implemented to avoid linking against libopus from the demuxer. */
static int Ogg_OpusPacketDuration( logical_stream_t *p_stream,
ogg_packet *p_oggpacket )
static int Ogg_OpusDataDuration( logical_stream_t *p_stream,
unsigned char *data, long i_datalen )
{
static const int silk_fs_div[4] = { 6000, 3000, 1500, 1000 };
int toc;
......@@ -1338,9 +1377,9 @@ static int Ogg_OpusPacketDuration( logical_stream_t *p_stream,
int frame_size;
int nsamples;
int i_rate;
if( p_oggpacket->bytes < 1 )
if( i_datalen < 1 )
return VLC_EGENERIC;
toc = p_oggpacket->packet[0];
toc = data[0];
switch( toc&3 )
{
case 0:
......@@ -1351,9 +1390,9 @@ static int Ogg_OpusPacketDuration( logical_stream_t *p_stream,
nframes = 2;
break;
default:
if( p_oggpacket->bytes < 2 )
if( i_datalen < 2 )
return VLC_EGENERIC;
nframes = p_oggpacket->packet[1]&0x3F;
nframes = data[1]&0x3F;
break;
}
i_rate = (int)p_stream->fmt.audio.i_rate;
......@@ -1369,6 +1408,12 @@ static int Ogg_OpusPacketDuration( logical_stream_t *p_stream,
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
* stream and fill p_ogg.
......@@ -1468,7 +1513,6 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
* important info in the second header packet!!!
* (STREAMINFO metadata is in the following packet) */
p_stream->b_force_backup = true;
p_stream->fmt.i_cat = AUDIO_ES;
p_stream->fmt.i_codec = VLC_CODEC_FLAC;
}
......@@ -1496,7 +1540,6 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
! memcmp( oggpacket.packet, "\x80theora", 7 ) )
{
Ogg_ReadTheoraHeader( p_stream, &oggpacket );
msg_Dbg( p_demux,
"found theora header, bitrate: %i, rate: %f",
p_stream->fmt.i_bitrate, p_stream->f_rate );
......@@ -1889,6 +1932,7 @@ static void Ogg_CreateES( demux_t *p_demux )
p_stream->b_finished = false;
p_stream->b_reinit = false;
p_stream->b_initializing = false;
p_stream->i_pre_skip = 0;
bool b_resetdecoder = Ogg_LogicalStreamResetEsFormat( p_demux, p_stream );
es_format_Copy( &p_stream->fmt_old, &p_old_stream->fmt );
......@@ -1958,8 +2002,8 @@ static int Ogg_BeginningOfStream( demux_t *p_demux )
else
p_ogg->i_bitrate += p_stream->fmt.i_bitrate;
p_stream->i_pcr = p_stream->i_previous_pcr =
p_stream->i_interpolated_pcr = VLC_TS_UNKNOWN;
p_stream->i_pcr = p_stream->i_previous_pcr = VLC_TS_UNKNOWN;
p_stream->i_previous_granulepos = -1;
p_stream->b_reinit = false;
}
......@@ -1989,7 +2033,8 @@ static void Ogg_EndOfStream( demux_t *p_demux )
p_ogg->skeleton.minor = 0;
p_ogg->b_preparsing_done = 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 )
......@@ -2005,6 +2050,20 @@ static void Ogg_EndOfStream( demux_t *p_demux )
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
*/
......@@ -2016,6 +2075,8 @@ static void Ogg_LogicalStreamDelete( demux_t *p_demux, logical_stream_t *p_strea
ogg_stream_clear( &p_stream->os );
free( p_stream->p_headers );
Ogg_CleanSpecificData( p_stream );
es_format_Clean( &p_stream->fmt_old );
es_format_Clean( &p_stream->fmt );
......@@ -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->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,
ogg_packet *p_oggpacket )
......@@ -2426,6 +2520,12 @@ static void Ogg_ReadOpusHeader( logical_stream_t *p_stream,
p_stream->fmt.audio.i_channels = oggpack_read( &opb, 8 );
fill_channels_info(&p_stream->fmt.audio);
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,
......@@ -2482,7 +2582,7 @@ static void Ogg_ReadKateHeader( logical_stream_t *p_stream,
/* Cheat and get additionnal info ;) */
oggpack_readinit( &opb, p_oggpacket->packet, p_oggpacket->bytes);
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 );
p_stream->i_granule_shift = oggpack_read( &opb, 8 );
oggpack_adv( &opb, 8*8 ); /* reserved */
......@@ -2990,8 +3090,10 @@ static bool Ogg_ReadDiracHeader( logical_stream_t *p_stream,
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_d = p_dirac_frate_tbl[pu_dirac_vidfmt_frate[u_video_format]].u_d;
......
......@@ -22,6 +22,14 @@
* 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
*****************************************************************************/
......@@ -43,6 +51,12 @@
typedef struct oggseek_index_entry demux_index_entry_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
{
ogg_stream_state os; /* logical stream of packets */
......@@ -63,11 +77,11 @@ typedef struct logical_stream_s
void *p_headers;
int i_headers;
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
* granulepos */
mtime_t i_pcr;
mtime_t i_interpolated_pcr;
mtime_t i_previous_pcr;
/* Misc */
......@@ -92,18 +106,42 @@ typedef struct logical_stream_s
ogg_skeleton_t *p_skel;
/* skip some frames after a seek */
int i_skip_frames;
unsigned int i_skip_frames;
/* data start offset (absolute) in bytes */
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 */
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;
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;
struct ogg_skeleton_t
......@@ -131,7 +169,7 @@ struct demux_sys_t
/* program clock reference (in units of 90kHz) derived from the pcr of
* the sub-streams */
mtime_t i_pcr;
mtime_t i_pcr_offset;
mtime_t i_nzpcr_offset;
/* informative only */
mtime_t i_pcr_jitter;
int64_t i_access_delay;
......@@ -181,7 +219,6 @@ struct demux_sys_t
/* Length, if available. */
int64_t i_length;
DemuxDebug( bool b_seeked; )
};
......
......@@ -93,96 +93,6 @@ static demux_index_entry_t *index_entry_new( void )
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
time stamps) */
const demux_index_entry_t *OggSeek_IndexAdd ( logical_stream_t *p_stream,
......@@ -414,19 +324,6 @@ clean:
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,
int64_t i_pos1, int64_t i_pos2,
......@@ -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 );
}
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 )
return false;
else
return !( p_packet->packet[0] & THEORA_FTYPE_INTERFRAME );
}
else if ( p_stream->fmt.i_codec == VLC_CODEC_VP8 )
{
case VLC_CODEC_VP8:
return ( ( ( p_packet->granulepos >> 3 ) & 0x07FFFFFF ) == 0 );
}
case VLC_CODEC_DIRAC:
return ( p_packet->granulepos & 0xFF8000FF );
default:
return true;
}
}
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,
p_lastpacketcoords->i_pos = p_sys->i_input_position;
p_lastpacketcoords->i_skip = i;
}
return true;
}
......@@ -649,6 +547,7 @@ static bool OggSeekToPacket( demux_t *p_demux, logical_stream_t *p_stream,
/* remove that packet and go sync to next */
ogg_stream_packetout( &p_stream->os, &op );
}
return false;
}
......@@ -779,75 +678,60 @@ int64_t Oggseek_GranuleToAbsTimestamp( logical_stream_t *p_stream,
int64_t i_granule, bool b_presentation )
{
int64_t i_timestamp = -1;
if ( i_granule < 0 )
if ( i_granule < 1 )
return -1;
if ( p_stream->b_oggds )
{
if ( b_presentation ) i_granule--;
i_timestamp = i_granule * CLOCK_FREQ / p_stream->f_rate;
}
else if( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
p_stream->fmt.i_codec == VLC_CODEC_KATE )
else switch( p_stream->fmt.i_codec )
{
case VLC_CODEC_THEORA:
case VLC_CODEC_KATE:
{
ogg_int64_t iframe = i_granule >> p_stream->i_granule_shift;
ogg_int64_t pframe = i_granule - ( iframe << p_stream->i_granule_shift );
/* See Theora A.2.3 */
if ( b_presentation ) pframe -= p_stream->i_keyframe_offset;
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;
if ( b_presentation ) frame--;
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 delay = (i_granule >> 9) & 0x1fff;
/* 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;
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;
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 */
......
......@@ -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,
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_BlindSeektoPosition ( demux_t *, logical_stream_t *, double f, bool );
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 );
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 * );
int oggseek_find_frame ( demux_t *, logical_stream_t *, int64_t i_tframe );
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