[vlc-devel] [PATCH] demux: ogg/opus: rewrite pts and pts computation (fix #10176)

Francois Cartegnie fcvlcdev at free.fr
Sun Mar 9 00:56:09 CET 2014


Hi,

Can't find regressions with my samples set, and fully
passes the tricky opus ones. But that's no guarantee
there will be no regression due to the amount of changes.

Tristan ?


Francois

- 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

---
 configure.ac              |   4 +
 modules/demux/Makefile.am |   4 +-
 modules/demux/ogg.c       | 610 +++++++++++++++++++++++++++-------------------
 modules/demux/ogg.h       |  51 +++-
 modules/demux/oggseek.c   | 180 +++-----------
 modules/demux/oggseek.h   |   7 -
 6 files changed, 438 insertions(+), 418 deletions(-)

diff --git a/configure.ac b/configure.ac
index ee788fc..036b56d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/modules/demux/Makefile.am b/modules/demux/Makefile.am
index 0719686..4ab22af 100644
--- a/modules/demux/Makefile.am
+++ b/modules/demux/Makefile.am
@@ -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)
 
diff --git a/modules/demux/ogg.c b/modules/demux/ogg.c
index cba2c9d..77e6118 100644
--- a/modules/demux/ogg.c
+++ b/modules/demux/ogg.c
@@ -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 */
@@ -431,101 +452,108 @@ static int Demux( demux_t * p_demux )
             if ( b_skipping && p_stream->i_skip_frames == 0
                  && p_stream->i_secondary_header_packets ) continue;
 
-            if( p_stream->b_reinit )
+            if( p_stream->b_reinit /*&& p_stream->i_pcr > VLC_TS_INVALID*/ )
             {
-                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;
         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 ) )
+        if( 0 >= p_oggpacket->granulepos )
         {
-            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 &&
         p_stream->fmt.i_codec != VLC_CODEC_SPEEX &&
@@ -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;
diff --git a/modules/demux/ogg.h b/modules/demux/ogg.h
index 84f4c68..71cf096 100644
--- a/modules/demux/ogg.h
+++ b/modules/demux/ogg.h
@@ -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; )
 };
 
 
diff --git a/modules/demux/oggseek.c b/modules/demux/oggseek.c
index 5b6830c..738a73f 100644
--- a/modules/demux/oggseek.c
+++ b/modules/demux/oggseek.c
@@ -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;
     }
-    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 */
diff --git a/modules/demux/oggseek.h b/modules/demux/oggseek.h
index ea075ab..6db6db7 100644
--- a/modules/demux/oggseek.h
+++ b/modules/demux/oggseek.h
@@ -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 * );
-- 
1.8.5.3




More information about the vlc-devel mailing list