Commit ff681fc7 authored by François Cartegnie's avatar François 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 ) )
{
DemuxDebug( msg_Dbg( p_demux, "PCR fixup for %"PRId64,
ogg_page_granulepos( &p_sys->current_page ) ) );
}
else
p_stream->b_reinit = false;
if( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
{
/* 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 );
p_stream->i_skip_frames = p_stream->i_pre_skip;
}
}
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;
/* 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 )
{
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
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:
{
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;
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
}
}
/* 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-- )
{
block_t *p_block = p_stream->p_prepcr_blocks[i];
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
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 );
}
}
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 );
}
}
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];
/* 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 );
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_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_oggpacket->granulepos, true );
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->fmt.i_bitrate / 8 );
p_stream->i_interpolated_pcr += p_ogg->i_pcr_offset;
p_stream->i_pcr += ( CLOCK_FREQ * p_oggpacket->bytes /
p_stream->fmt.i_bitrate / 8 );
}
}
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); )
/* 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->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,61 +1271,35 @@ 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;