[vlc-commits] demux: ogg: rework and simplify

Francois Cartegnie git at videolan.org
Thu May 31 18:47:54 CEST 2018


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Thu May 24 18:40:26 2018 +0200| [bc9f6bb1ce26c07fded6b1065cff4ac194b68f28] | committer: Francois Cartegnie

demux: ogg: rework and simplify

Was no longer understandable mess

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=bc9f6bb1ce26c07fded6b1065cff4ac194b68f28
---

 modules/demux/Makefile.am   |   6 +-
 modules/demux/ogg.c         | 447 ++++++++++++++++++++++----------------------
 modules/demux/ogg.h         |  17 +-
 modules/demux/ogg_granule.c | 187 ++++++++++++++++++
 modules/demux/ogg_granule.h |  27 +++
 modules/demux/oggseek.c     | 149 ++-------------
 modules/demux/oggseek.h     |  14 +-
 7 files changed, 467 insertions(+), 380 deletions(-)

diff --git a/modules/demux/Makefile.am b/modules/demux/Makefile.am
index 6f799e84c4..28e32f29d6 100644
--- a/modules/demux/Makefile.am
+++ b/modules/demux/Makefile.am
@@ -10,8 +10,10 @@ libflacsys_plugin_la_CPPFLAGS = $(AM_CPPFLAGS)
 libflacsys_plugin_la_LIBADD = libxiph_metadata.la
 demux_LTLIBRARIES += libflacsys_plugin.la
 
-libogg_plugin_la_SOURCES = demux/ogg.c demux/ogg.h demux/oggseek.c demux/oggseek.h \
-	demux/xiph.h demux/opus.h
+libogg_plugin_la_SOURCES = demux/ogg.c demux/ogg.h \
+                           demux/oggseek.c demux/oggseek.h \
+                           demux/ogg_granule.c demux/ogg_granule.h \
+                           demux/xiph.h demux/opus.h
 libogg_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBVORBIS_CFLAGS) $(OGG_CFLAGS)
 libogg_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(demuxdir)'
 libogg_plugin_la_LIBADD = $(LIBVORBIS_LIBS) $(OGG_LIBS) libxiph_metadata.la
diff --git a/modules/demux/ogg.c b/modules/demux/ogg.c
index eeaf09acaa..4d52b3e194 100644
--- a/modules/demux/ogg.c
+++ b/modules/demux/ogg.c
@@ -29,6 +29,10 @@
 # include "config.h"
 #endif
 
+#ifdef HAVE_LIBVORBIS
+  #include <vorbis/codec.h>
+#endif
+
 #include <vlc_common.h>
 #include <vlc_plugin.h>
 #include <vlc_access.h>
@@ -44,6 +48,7 @@
 #include "xiph_metadata.h"
 #include "ogg.h"
 #include "oggseek.h"
+#include "ogg_granule.h"
 #include "opus.h"
 
 /*****************************************************************************
@@ -121,7 +126,6 @@ static int  Control( demux_t *, int, va_list );
 
 /* Bitstream manipulation */
 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 unsigned Ogg_OpusPacketDuration( ogg_packet * );
 static void Ogg_SendOrQueueBlocks( demux_t *, logical_stream_t *, block_t * );
@@ -580,8 +584,11 @@ static int Demux( demux_t * p_demux )
 
         if ( p_stream->prepcr.pp_blocks )
         {
-            mtime_t pagestamp = Oggseek_GranuleToAbsTimestamp( p_stream, ogg_page_granulepos(  &p_sys->current_page ), false );
-            p_stream->i_previous_pcr = pagestamp;
+            mtime_t i_end = Ogg_GranuleToTime( p_stream,
+                                               ogg_page_granulepos(  &p_sys->current_page ),
+                                               false, false );
+            mtime_t i_end_backup = i_end;
+
 #ifdef HAVE_LIBVORBIS
             int i_prev_blocksize = 0;
 #endif
@@ -624,9 +631,8 @@ static int Demux( demux_t * p_demux )
 
             // PASS 2
             bool b_fixed = false;
-            date_t d;
-            date_Init( &d, p_stream->f_rate, 1 );
-            date_Set( &d, p_sys->i_nzpcr_offset + pagestamp - VLC_TS_0 );
+            date_t d = p_stream->dts;
+            date_Set( &d, p_sys->i_nzpcr_offset + i_end - VLC_TS_0 );
             for( int i=p_stream->prepcr.i_used - 1; i>=0; i-- )
             {
                 block_t *p_block = p_stream->prepcr.pp_blocks[i];
@@ -635,7 +641,7 @@ static int Demux( demux_t * p_demux )
                 case VLC_CODEC_SPEEX:
                 case VLC_CODEC_OPUS:
                 case VLC_CODEC_VORBIS:
-                    if( pagestamp != VLC_TS_INVALID )
+                    if( i_end != VLC_TS_INVALID )
                     {
                         date_Decrement( &d, p_block->i_nb_samples );
                         p_block->i_pts = date_Get( &d ) + VLC_TS_0;
@@ -651,7 +657,7 @@ static int Demux( demux_t * p_demux )
                 default:
                     if ( p_stream->fmt.i_cat == VIDEO_ES )
                     {
-                        if( pagestamp != VLC_TS_INVALID )
+                        if( i_end != VLC_TS_INVALID )
                         {
                             date_Decrement( &d, 1 );
                             p_block->i_pts = date_Get( &d ) + VLC_TS_0;
@@ -663,26 +669,22 @@ static int Demux( demux_t * p_demux )
 
             if ( b_fixed )
             {
-                pagestamp = p_stream->i_previous_pcr; /* as set above */
-                p_stream->i_pcr = pagestamp;
-                p_stream->i_pcr += p_sys->i_nzpcr_offset;
-                p_stream->i_previous_granulepos = ogg_page_granulepos( &p_sys->current_page );
+                i_end = i_end_backup; /* as set above */
+                if( i_end != VLC_TS_INVALID )
+                    p_stream->i_pcr = i_end + p_sys->i_nzpcr_offset;
             }
 
             FREENULL(p_stream->prepcr.pp_blocks);
             p_stream->prepcr.i_used = 0;
 
             Ogg_SendOrQueueBlocks( p_demux, p_stream, NULL );
-
         }
 
-        mtime_t i_pagestamp = Oggseek_GranuleToAbsTimestamp( p_stream,
-                              ogg_page_granulepos( &p_sys->current_page ), false );
-        if ( i_pagestamp != VLC_TS_INVALID )
-        {
-            p_stream->i_pcr = i_pagestamp;
-            p_stream->i_pcr += p_sys->i_nzpcr_offset;
-        }
+        mtime_t i_pcr = Ogg_GranuleToTime( p_stream,
+                                           ogg_page_granulepos( &p_sys->current_page ),
+                                           !p_stream->b_contiguous, false );
+        if ( i_pcr != VLC_TS_INVALID )
+            p_stream->i_pcr = p_sys->i_nzpcr_offset + i_pcr;
 
         if( !p_sys->b_page_waiting )
             break;
@@ -727,8 +729,7 @@ static void Ogg_ResetStream( logical_stream_t *p_stream )
     /* 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;
+    date_Set( &p_stream->dts, VLC_TS_INVALID );
     ogg_stream_reset( &p_stream->os );
     FREENULL( p_stream->prepcr.pp_blocks );
     p_stream->prepcr.i_size = 0;
@@ -1026,133 +1027,69 @@ static int Ogg_ReadPage( demux_t *p_demux, ogg_page *p_oggpage )
     return VLC_SUCCESS;
 }
 
-/****************************************************************************
- * Ogg_UpdatePCR: update the PCR (90kHz program clock reference) for the
- *                current stream.
- ****************************************************************************/
-static void Ogg_UpdatePCR( demux_t *p_demux, logical_stream_t *p_stream,
-                           ogg_packet *p_oggpacket )
+static void Ogg_SetNextFrame( demux_t *p_demux, logical_stream_t *p_stream,
+                              ogg_packet *p_oggpacket )
 {
-    demux_sys_t *p_ogg = p_demux->p_sys;
-    p_stream->i_end_trim = 0;
+    VLC_UNUSED(p_demux);
+    ogg_int64_t i_granule = p_oggpacket->granulepos;
 
-    /* Convert the granulepos into a pcr */
-    if ( p_oggpacket->granulepos == 0 )
+    if( Ogg_GranuleIsValid( p_stream, i_granule ) )
     {
-        /* We're in headers, and we haven't parsed 1st data packet yet */
-//        p_stream->i_pcr = VLC_TS_UNKNOWN;
-        if( p_stream->b_oggds && p_oggpacket->bytes > 0 &&
-            (p_oggpacket->packet[0] & PACKET_TYPE_HEADER) == 0 )
+        mtime_t i_endtime = Ogg_GranuleToTime( p_stream, i_granule, false, false );
+        assert( !p_stream->b_contiguous || i_endtime != VLC_TS_INVALID );
+        if( i_endtime != VLC_TS_INVALID )
         {
-            p_stream->i_pcr = VLC_TS_0 + p_ogg->i_nzpcr_offset;
+            date_Set( &p_stream->dts, i_endtime );
+            return;
         }
     }
-    else if( p_oggpacket->granulepos > 0 )
+
+    /* Do Interpolation if can't compute directly from granule */
+    if( date_Get( &p_stream->dts ) != VLC_TS_INVALID )
     {
-        if( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
-            p_stream->fmt.i_codec == VLC_CODEC_DAALA ||
-            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_SPEEX ||
-            p_stream->fmt.i_codec == VLC_CODEC_OGGSPOTS ||
-            (p_stream->b_oggds && p_stream->fmt.i_cat == VIDEO_ES) )
+        if( p_stream->fmt.i_cat == VIDEO_ES )
         {
-            p_stream->i_pcr = Oggseek_GranuleToAbsTimestamp( p_stream,
-                                         p_oggpacket->granulepos, true );
-            p_stream->i_pcr += p_ogg->i_nzpcr_offset;
+            date_Increment( &p_stream->dts, 1 );
         }
-        else if ( p_stream->i_previous_granulepos > 0 )
+        else if( p_stream->fmt.i_cat == AUDIO_ES )
         {
-            ogg_int64_t sample = p_stream->i_previous_granulepos;
-
-            if( p_stream->fmt.i_codec == VLC_CODEC_OPUS && p_oggpacket->e_o_s )
-                p_stream->i_end_trim = p_oggpacket->granulepos - sample;
-
-            sample -= p_stream->i_pre_skip;
-
-            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_oggpacket->granulepos == -1 )
-    {
-        unsigned i_duration;
-        /* no granulepos available, try to interpolate the pcr.
-         * If we can't then don't touch the old value. */
-        if( p_stream->b_oggds && p_stream->fmt.i_cat == VIDEO_ES )
-        {
-            if( p_stream->i_previous_granulepos > 0 )
-            {
-                p_stream->i_pcr = Oggseek_GranuleToAbsTimestamp( p_stream, ++p_stream->i_previous_granulepos, false );
-                p_stream->i_pcr += p_ogg->i_nzpcr_offset;
-            }
-            /* First frame in ogm can be -1 (0 0 -1 2 3 -1 5 ...) */
-            else if( p_stream->i_previous_granulepos == 0 )
-            {
-                p_stream->i_pcr = VLC_TS_0 + p_ogg->i_nzpcr_offset;
-            }
-            else
+            int64_t i_samples = 0;
+            switch( p_stream->fmt.i_codec )
             {
-                p_stream->i_pcr += (CLOCK_FREQ / p_stream->f_rate);
-            }
-        }
+                case VLC_CODEC_OPUS:
+                    i_samples = Ogg_OpusPacketDuration( p_oggpacket );
+                    break;
+                case VLC_CODEC_SPEEX:
+                    i_samples = p_stream->special.speex.i_framesize *
+                                p_stream->special.speex.i_framesperpacket;
+                    break;
 #ifdef HAVE_LIBVORBIS
-        else if ( p_stream->fmt.i_codec == VLC_CODEC_VORBIS &&
-                  p_stream->special.vorbis.p_info &&
-                  VORBIS_HEADERS_VALID(p_stream) &&
-                  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;
-        }
+                case VLC_CODEC_VORBIS:
+                    if( p_stream->special.vorbis.p_info &&
+                        VORBIS_HEADERS_VALID(p_stream) )
+                    {
+                        long i_blocksize = vorbis_packet_blocksize(
+                                    p_stream->special.vorbis.p_info, p_oggpacket );
+                        /* duration in samples per channel */
+                        if ( p_stream->special.vorbis.i_prev_blocksize )
+                            i_samples = ( i_blocksize + p_stream->special.vorbis.i_prev_blocksize ) / 4;
+                        else
+                            i_samples = i_blocksize / 2;
+                        p_stream->special.vorbis.i_prev_blocksize = i_blocksize;
+                    }
+                    break;
 #endif
-        else if ( p_stream->fmt.i_codec == VLC_CODEC_SPEEX &&
-                  p_stream->i_previous_granulepos > 0 )
-        {
-            i_duration = p_stream->special.speex.i_framesize *
-                         p_stream->special.speex.i_framesperpacket;
-            p_oggpacket->granulepos = p_stream->i_previous_granulepos + i_duration;
-            p_stream->i_pcr = Oggseek_GranuleToAbsTimestamp( p_stream,
-                                    p_stream->i_previous_granulepos, false );
-            p_stream->i_pcr += p_ogg->i_nzpcr_offset;
-        }
-        else if( p_stream->fmt.i_codec == VLC_CODEC_OPUS &&
-                 p_stream->i_previous_granulepos > 0 &&
-                 ( i_duration =
-                     Ogg_OpusPacketDuration( p_oggpacket ) ) > 0 )
-        {
-            ogg_int64_t sample;
-            p_oggpacket->granulepos = p_stream->i_previous_granulepos + i_duration;
-            sample = p_stream->i_previous_granulepos;
-            sample -= p_stream->i_pre_skip;
-
-            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_cat == VIDEO_ES && p_stream->i_pcr > VLC_TS_UNKNOWN )
-        {
-            p_stream->i_pcr += (CLOCK_FREQ / p_stream->f_rate);
-        }
-        else if( p_stream->fmt.i_bitrate && p_stream->i_pcr > VLC_TS_UNKNOWN )
-        {
-            p_stream->i_pcr += ( CLOCK_FREQ * p_oggpacket->bytes /
-                                 p_stream->fmt.i_bitrate / 8 );
+                default:
+                    if( p_stream->fmt.i_bitrate )
+                    {
+                        i_samples = 8 * p_oggpacket->bytes * p_stream->dts.i_divider_num;
+                        i_samples /= p_stream->fmt.i_bitrate / p_stream->dts.i_divider_den;
+                    }
+                    break;
+            }
+            date_Increment( &p_stream->dts, i_samples );
         }
     }
-
-    p_stream->i_previous_granulepos = p_oggpacket->granulepos;
 }
 
 static void Ogg_SendOrQueueBlocks( demux_t *p_demux, logical_stream_t *p_stream,
@@ -1223,6 +1160,17 @@ static void Ogg_SendOrQueueBlocks( demux_t *p_demux, logical_stream_t *p_stream,
     }
 }
 
+static bool Ogg_IsHeaderPacket( const logical_stream_t *p_stream,
+                                const ogg_packet *p_oggpacket )
+{
+    if ( p_stream->b_oggds )
+    {
+        return p_oggpacket->bytes > 0 &&
+               (p_oggpacket->packet[0] & PACKET_TYPE_HEADER);
+    }
+    else return (p_oggpacket->granulepos == 0);
+}
+
 /****************************************************************************
  * Ogg_DecodePacket: Decode an Ogg packet.
  ****************************************************************************/
@@ -1398,8 +1346,16 @@ static void Ogg_DecodePacket( demux_t *p_demux,
         p_stream->b_initializing = false;
     }
 
-    /* Convert the granulepos into the next pcr */
-    Ogg_UpdatePCR( p_demux, p_stream, p_oggpacket );
+    mtime_t i_dts = Ogg_GranuleToTime( p_stream, p_oggpacket->granulepos, true, false );
+    mtime_t i_expected_dts = date_Get( &p_stream->dts ); /* Interpolated or previous end time */
+    if( i_dts == VLC_TS_INVALID )
+        i_dts = i_expected_dts;
+    else
+        date_Set( &p_stream->dts, i_dts );
+
+    /* Write end granule as next start, or do interpolation */
+    if( !Ogg_IsHeaderPacket( p_stream, p_oggpacket ) )
+        Ogg_SetNextFrame( p_demux, p_stream, p_oggpacket );
 
     if( !b_selected )
     {
@@ -1408,14 +1364,30 @@ static void Ogg_DecodePacket( demux_t *p_demux,
         return;
     }
 
-    if( !( p_block = block_Alloc( p_oggpacket->bytes ) ) ) return;
+    if( !( p_block = block_Alloc( p_oggpacket->bytes ) ) )
+        return;
 
-    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); )
+    /* Set effective timestamp */
+    if( i_dts != VLC_TS_INVALID )
+        p_block->i_dts = p_sys->i_nzpcr_offset + i_dts;
 
-    if( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
+    /* Vorbis and Opus can trim the end of a stream using granule positions. */
+    if( p_oggpacket->e_o_s )
+    {
+        mtime_t i_endtime = Ogg_GranuleToTime( p_stream, p_oggpacket->granulepos, false, false );
+        if( i_endtime != VLC_TS_INVALID && i_expected_dts != VLC_TS_INVALID )
+        {
+                p_block->i_length = i_endtime - i_expected_dts;
+                p_block->i_flags |= BLOCK_FLAG_END_OF_SEQUENCE;
+        }
+    }
+
+    if( p_stream->fmt.i_codec == VLC_CODEC_OPUS ) /* also required for trimming */
         p_block->i_nb_samples = Ogg_OpusPacketDuration( p_oggpacket );
 
+    DemuxDebug( msg_Dbg(p_demux, "block set from granule %"PRId64" to pts/pcr %"PRId64" skip %d",
+                        p_oggpacket->granulepos, p_block->i_dts, p_stream->i_skip_frames); )
+
     /* may need to preroll after a seek or in case of preskip */
     if ( p_stream->i_skip_frames > 0 )
     {
@@ -1450,40 +1422,22 @@ static void Ogg_DecodePacket( demux_t *p_demux,
 
         if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC )
         {
-            p_block->i_dts = Oggseek_GranuleToAbsTimestamp( p_stream, p_oggpacket->granulepos, false );
-            p_block->i_pts = Oggseek_GranuleToAbsTimestamp( p_stream, p_oggpacket->granulepos, true );
-            /* granulepos for dirac is possibly broken, this value should be ignored */
-            if( 0 >= p_oggpacket->granulepos )
-            {
-                p_block->i_pts = VLC_TS_INVALID;
-                p_block->i_dts = p_stream->i_pcr;
-            }
+            if( p_oggpacket->granulepos > 0 )
+                p_block->i_pts = Ogg_GranuleToTime( p_stream, p_oggpacket->granulepos, true, true );
         }
         else if( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
         {
-            p_block->i_pts =
-            p_block->i_dts = p_stream->i_pcr;
-        }
-        else
-        {
-            p_block->i_pts = VLC_TS_INVALID;
-            p_block->i_dts = p_stream->i_pcr;
+            p_block->i_pts = p_block->i_dts;
         }
     }
     else if( p_stream->fmt.i_cat == AUDIO_ES )
     {
-        /* Blatant abuse of the i_length field. */
-        if( p_stream->i_end_trim > 0 )
-        {
-            p_block->i_length = p_stream->i_end_trim * CLOCK_FREQ / p_stream->f_rate;
-            p_block->i_flags |= BLOCK_FLAG_END_OF_SEQUENCE;
-        }
-        p_block->i_pts = p_block->i_dts = p_stream->i_pcr;
+        p_block->i_pts = p_block->i_dts;
     }
     else if( p_stream->fmt.i_cat == SPU_ES )
     {
         p_block->i_length = 0;
-        p_block->i_pts = p_block->i_dts = p_stream->i_pcr;
+        p_block->i_pts = p_block->i_dts;
     }
 
     if( p_stream->fmt.i_codec != VLC_CODEC_VORBIS &&
@@ -1567,7 +1521,7 @@ static unsigned Ogg_OpusPacketDuration( ogg_packet *p_oggpacket )
  ****************************************************************************/
 static int Ogg_FindLogicalStreams( demux_t *p_demux )
 {
-    demux_sys_t *p_ogg = p_demux->p_sys  ;
+    demux_sys_t *p_ogg = p_demux->p_sys;
     ogg_packet oggpacket;
 
     p_ogg->i_total_length = stream_Size ( p_demux->s );
@@ -1593,6 +1547,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                 es_format_Init( &p_stream->fmt, UNKNOWN_ES, 0 );
                 es_format_Init( &p_stream->fmt_old, UNKNOWN_ES, 0 );
                 p_stream->b_initializing = true;
+                p_stream->b_contiguous = true; /* default */
 
                 /* Setup the logical stream */
                 p_stream->i_serial_no = ogg_page_serialno( &p_ogg->current_page );
@@ -1633,9 +1588,10 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                     es_format_Change( &p_stream->fmt, AUDIO_ES, VLC_CODEC_SPEEX );
                     if ( Ogg_ReadSpeexHeader( p_stream, &oggpacket ) )
                         msg_Dbg( p_demux, "found speex header, channels: %i, "
-                                "rate: %i,  bitrate: %i, frames: %i group %i",
+                                "rate: %"PRIu32"/%"PRIu32",  bitrate: %i, frames: %i group %i",
                                 p_stream->fmt.audio.i_channels,
-                                (int)p_stream->f_rate, p_stream->fmt.i_bitrate,
+                                p_stream->dts.i_divider_num, p_stream->dts.i_divider_den,
+                                p_stream->fmt.i_bitrate,
                                 p_stream->special.speex.i_framesize,
                                 p_stream->special.speex.i_framesperpacket );
                     else
@@ -1707,8 +1663,9 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                     es_format_Change( &p_stream->fmt, VIDEO_ES, VLC_CODEC_THEORA );
                     if ( 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 );
+                                 "found theora header, bitrate: %i, rate: %"PRIu32"/%"PRIu32,
+                                 p_stream->fmt.i_bitrate,
+                                 p_stream->dts.i_divider_num, p_stream->dts.i_divider_den );
                     else
                     {
                         msg_Dbg( p_demux, "found invalid Theora header" );
@@ -1724,8 +1681,9 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                     es_format_Change( &p_stream->fmt, VIDEO_ES, VLC_CODEC_DAALA );
                     if ( Ogg_ReadDaalaHeader( p_stream, &oggpacket ) )
                         msg_Dbg( p_demux,
-                                 "found daala header, bitrate: %i, rate: %f",
-                                 p_stream->fmt.i_bitrate, p_stream->f_rate );
+                                 "found daala header, bitrate: %i, rate: %"PRIu32"/%"PRIu32,
+                                 p_stream->fmt.i_bitrate,
+                                 p_stream->dts.i_divider_num, p_stream->dts.i_divider_den );
                     else
                     {
                         msg_Dbg( p_demux, "found invalid Daala header" );
@@ -1758,8 +1716,8 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                     es_format_Change( &p_stream->fmt, VIDEO_ES, VLC_CODEC_VP8 );
                     if ( Ogg_ReadVP8Header( p_demux, p_stream, &oggpacket ) )
                         msg_Dbg( p_demux, "found VP8 header "
-                             "fps: %f, width:%i; height:%i",
-                             p_stream->f_rate,
+                             "fps: %"PRIu32"/%"PRIu32", width:%i; height:%i",
+                             p_stream->dts.i_divider_num, p_stream->dts.i_divider_den,
                              p_stream->fmt.video.i_width,
                              p_stream->fmt.video.i_height );
                     else
@@ -1807,6 +1765,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                 {
                     /* Old header type */
                     p_stream->b_oggds = true;
+                    p_stream->b_contiguous = false;
                     /* Check for video header (old format) */
                     if( GetDWLE((oggpacket.packet+96)) == 0x05589f80 &&
                         oggpacket.bytes >= 184 )
@@ -1819,13 +1778,12 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                         msg_Dbg( p_demux, "found video header of type: %.4s",
                                  (char *)&p_stream->fmt.i_codec );
 
-                        p_stream->fmt.video.i_frame_rate = 10000000;
-                        p_stream->fmt.video.i_frame_rate_base =
-                            GetQWLE((oggpacket.packet+164));
-                        p_stream->fmt.video.i_frame_rate_base =
-                            __MAX( p_stream->fmt.video.i_frame_rate_base, 1 );
-                        p_stream->f_rate = 10000000.0 /
-                            p_stream->fmt.video.i_frame_rate_base;
+                        unsigned num = OGGDS_RESOLUTION;
+                        unsigned den = GetQWLE(oggpacket.packet+164);
+                        vlc_ureduce( &num, &den, num, den > 0 ? den : 1, OGGDS_RESOLUTION );
+                        p_stream->fmt.video.i_frame_rate = num;
+                        p_stream->fmt.video.i_frame_rate_base = den;
+                        date_Init( &p_stream->dts, num, den );
                         p_stream->fmt.video.i_bits_per_pixel =
                             GetWLE((oggpacket.packet+182));
                         if( !p_stream->fmt.video.i_bits_per_pixel )
@@ -1841,12 +1799,20 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                             p_stream->fmt.video.i_height;
 
                         msg_Dbg( p_demux,
-                                 "fps: %f, width:%i; height:%i, bitcount:%i",
-                                 p_stream->f_rate,
+                                 "fps: %u/%u, width:%i; height:%i, bitcount:%i",
+                                 p_stream->fmt.video.i_frame_rate,
+                                 p_stream->fmt.video.i_frame_rate_base,
                                  p_stream->fmt.video.i_width,
                                  p_stream->fmt.video.i_height,
                                  p_stream->fmt.video.i_bits_per_pixel);
 
+                        if ( !p_stream->fmt.video.i_frame_rate ||
+                             !p_stream->fmt.video.i_frame_rate_base )
+                        {
+                            Ogg_LogicalStreamDelete( p_demux, p_stream );
+                            p_stream = NULL;
+                            p_ogg->i_streams--;
+                        }
                     }
                     /* Check for audio header (old format) */
                     else if( GetDWLE((oggpacket.packet+96)) == 0x05589F81 )
@@ -1872,7 +1838,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                         p_stream->fmt.audio.i_channels =
                             GetWLE((oggpacket.packet+126));
                         fill_channels_info(&p_stream->fmt.audio);
-                        p_stream->f_rate = p_stream->fmt.audio.i_rate =
+                        p_stream->fmt.audio.i_rate =
                             GetDWLE((oggpacket.packet+128));
                         p_stream->fmt.i_bitrate =
                             GetDWLE((oggpacket.packet+132)) * 8;
@@ -1881,6 +1847,8 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                         p_stream->fmt.audio.i_bitspersample =
                             GetWLE((oggpacket.packet+138));
 
+                        date_Init( &p_stream->dts, p_stream->fmt.audio.i_rate, 1 );
+
                         wf_tag_to_fourcc( i_format_tag,
                                           &p_stream->fmt.i_codec, 0 );
 
@@ -1900,7 +1868,8 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                                  p_stream->fmt.audio.i_rate,
                                  p_stream->fmt.audio.i_bitspersample,
                                  p_stream->fmt.i_bitrate / 1024 );
-                        if ( p_stream->f_rate == 0 )
+
+                        if ( p_stream->fmt.audio.i_rate == 0 )
                         {
                             msg_Dbg( p_demux, "invalid oggds audio header" );
                             Ogg_LogicalStreamDelete( p_demux, p_stream );
@@ -1924,6 +1893,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                     stream_header_t *st = &tmp;
 
                     p_stream->b_oggds = true;
+                    p_stream->b_contiguous = false;
 
                     memcpy( st->streamtype, &oggpacket.packet[1+0], 8 );
                     memcpy( st->subtype, &oggpacket.packet[1+8], 4 );
@@ -1952,11 +1922,14 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                         msg_Dbg( p_demux, "found video header of type: %.4s",
                                  (char *)&p_stream->fmt.i_codec );
 
-                        p_stream->fmt.video.i_frame_rate = 10000000;
-                        p_stream->fmt.video.i_frame_rate_base = st->time_unit;
                         if( st->time_unit <= 0 )
                             st->time_unit = 400000;
-                        p_stream->f_rate = 10000000.0 / st->time_unit;
+                        unsigned num,den;
+                        vlc_ureduce( &num, &den, OGGDS_RESOLUTION, st->time_unit,
+                                     OGGDS_RESOLUTION );
+                        date_Init( &p_stream->dts, num, den );
+                        p_stream->fmt.video.i_frame_rate = num;
+                        p_stream->fmt.video.i_frame_rate_base = den;
                         p_stream->fmt.video.i_bits_per_pixel = st->bits_per_sample;
                         p_stream->fmt.video.i_width = st->sh.video.width;
                         p_stream->fmt.video.i_height = st->sh.video.height;
@@ -1966,8 +1939,9 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                             p_stream->fmt.video.i_height;
 
                         msg_Dbg( p_demux,
-                                 "fps: %f, width:%i; height:%i, bitcount:%i",
-                                 p_stream->f_rate,
+                                 "fps: %u/%u, width:%i; height:%i, bitcount:%i",
+                                 p_stream->fmt.video.i_frame_rate,
+                                 p_stream->fmt.video.i_frame_rate_base,
                                  p_stream->fmt.video.i_width,
                                  p_stream->fmt.video.i_height,
                                  p_stream->fmt.video.i_bits_per_pixel );
@@ -2008,9 +1982,13 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                         i_format_tag = strtol(p_buffer,NULL,16);
                         p_stream->fmt.audio.i_channels = st->sh.audio.channels;
                         fill_channels_info(&p_stream->fmt.audio);
-                        if( st->time_unit <= 0 )
-                            st->time_unit = 10000000;
-                        p_stream->f_rate = p_stream->fmt.audio.i_rate = st->samples_per_unit * 10000000 / st->time_unit;
+                        unsigned num,den;
+                        vlc_ureduce( &num, &den,
+                                     st->samples_per_unit * OGGDS_RESOLUTION,
+                                     st->time_unit > 0 ? st->time_unit : OGGDS_RESOLUTION,
+                                     OGGDS_RESOLUTION );
+                        date_Init( &p_stream->dts, num, den );
+                        p_stream->fmt.audio.i_rate = num / den;
                         p_stream->fmt.i_bitrate = st->sh.audio.avgbytespersec * 8;
                         p_stream->fmt.audio.i_blockalign = st->sh.audio.blockalign;
                         p_stream->fmt.audio.i_bitspersample = st->bits_per_sample;
@@ -2034,7 +2012,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                                  p_stream->fmt.audio.i_rate,
                                  p_stream->fmt.audio.i_bitspersample,
                                  p_stream->fmt.i_bitrate / 1024 );
-                        if ( p_stream->f_rate == 0 )
+                        if ( p_stream->fmt.audio.i_rate == 0 )
                         {
                             msg_Dbg( p_demux, "invalid oggds audio header" );
                             Ogg_LogicalStreamDelete( p_demux, p_stream );
@@ -2050,7 +2028,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
 
                         msg_Dbg( p_demux, "found text subtitle header" );
                         es_format_Change( &p_stream->fmt, SPU_ES, VLC_CODEC_SUBT );
-                        p_stream->f_rate = 1000; /* granulepos is in millisec */
+                        date_Init( &p_stream->dts, 1000, 1 ); /* granulepos is in millisec */
                     }
                     else
                     {
@@ -2075,8 +2053,9 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
                 {
                     if ( Ogg_ReadOggSpotsHeader( p_stream, &oggpacket ) )
                         msg_Dbg( p_demux,
-                                 "found OggSpots header, time resolution: %f",
-                                 p_stream->f_rate );
+                                 "found OggSpots header, time resolution: %u/%u",
+                                 p_stream->fmt.video.i_frame_rate,
+                                 p_stream->fmt.video.i_frame_rate_base );
                     else
                     {
                         msg_Err( p_demux, "found invalid OggSpots header" );
@@ -2219,8 +2198,7 @@ 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 = VLC_TS_UNKNOWN;
-        p_stream->i_previous_granulepos = -1;
+        p_stream->i_pcr = VLC_TS_UNKNOWN;
         p_stream->b_reinit = false;
     }
 
@@ -2644,8 +2622,9 @@ static bool Ogg_ReadTheoraHeader( logical_stream_t *p_stream,
 
     i_version = i_major * 1000000 + i_minor * 1000 + i_subminor;
     p_stream->i_keyframe_offset = 0;
-    p_stream->f_rate = ((double)i_fps_numerator) / i_fps_denominator;
-    if ( p_stream->f_rate == 0 ) return false;
+    if ( !i_fps_denominator || !i_fps_numerator )
+        return false;
+    date_Init( &p_stream->dts, i_fps_numerator, i_fps_denominator );
 
     if ( i_version >= 3002001 )
     {
@@ -2708,8 +2687,9 @@ static bool Ogg_ReadDaalaHeader( logical_stream_t *p_stream,
     i_version = i_major * 1000000 + i_minor * 1000 + i_subminor;
     VLC_UNUSED(i_version);
     p_stream->i_keyframe_offset = 0;
-    p_stream->f_rate = ((double)i_timebase_numerator) / i_timebase_denominator;
-    if ( p_stream->f_rate == 0 ) return false;
+    if ( !i_timebase_numerator || !i_timebase_denominator )
+        return false;
+    date_Init( &p_stream->dts, i_timebase_numerator, i_timebase_denominator );
 
     return true;
 }
@@ -2729,12 +2709,14 @@ static bool Ogg_ReadVorbisHeader( logical_stream_t *p_stream,
     oggpack_adv( &opb, 88 );
     p_stream->fmt.audio.i_channels = oggpack_read( &opb, 8 );
     fill_channels_info(&p_stream->fmt.audio);
-    p_stream->f_rate = p_stream->fmt.audio.i_rate =
-        oggpack_read( &opb, 32 );
+    p_stream->fmt.audio.i_rate = oggpack_read( &opb, 32 );
+    if( p_stream->fmt.audio.i_rate == 0 )
+        return false;
+    date_Init( &p_stream->dts, p_stream->fmt.audio.i_rate, 1 );
+
     oggpack_adv( &opb, 32 );
     p_stream->fmt.i_bitrate = oggpack_read( &opb, 32 ); /* is signed 32 */
     if( p_stream->fmt.i_bitrate > INT32_MAX ) p_stream->fmt.i_bitrate = 0;
-    if ( p_stream->f_rate == 0 ) return false;
     return true;
 }
 #ifdef HAVE_LIBVORBIS
@@ -2788,8 +2770,10 @@ static bool Ogg_ReadSpeexHeader( logical_stream_t *p_stream,
     oggpack_adv( &opb, 224 );
     oggpack_adv( &opb, 32 ); /* speex_version_id */
     oggpack_adv( &opb, 32 ); /* header_size */
-    p_stream->f_rate = p_stream->fmt.audio.i_rate = oggpack_read( &opb, 32 );
-    if ( p_stream->f_rate == 0 ) return false;
+    p_stream->fmt.audio.i_rate = oggpack_read( &opb, 32 );
+    if ( !p_stream->fmt.audio.i_rate )
+        return false;
+    date_Init( &p_stream->dts, p_stream->fmt.audio.i_rate, 1 );
     oggpack_adv( &opb, 32 ); /* mode */
     oggpack_adv( &opb, 32 ); /* mode_bitstream_version */
     p_stream->fmt.audio.i_channels = oggpack_read( &opb, 32 );
@@ -2816,7 +2800,8 @@ static void Ogg_ReadOpusHeader( logical_stream_t *p_stream,
 
     /* All OggOpus streams are timestamped at 48kHz and
      * can be played at 48kHz. */
-    p_stream->f_rate = p_stream->fmt.audio.i_rate = 48000;
+    p_stream->fmt.audio.i_rate = 48000;
+    date_Init( &p_stream->dts, p_stream->fmt.audio.i_rate, 1 );
     p_stream->fmt.i_bitrate = 0;
 
     /* Cheat and get additional info ;) */
@@ -2852,13 +2837,15 @@ static bool Ogg_ReadFlacStreamInfo( demux_t *p_demux, logical_stream_t *p_stream
     if( bs_read( &s, 24 ) >= 34 /*size STREAMINFO*/ )
     {
         bs_skip( &s, 80 );
-        p_stream->f_rate = p_stream->fmt.audio.i_rate = bs_read( &s, 20 );
+        p_stream->fmt.audio.i_rate = bs_read( &s, 20 );
         p_stream->fmt.audio.i_channels = bs_read( &s, 3 ) + 1;
         fill_channels_info(&p_stream->fmt.audio);
 
-        msg_Dbg( p_demux, "FLAC header, channels: %i, rate: %i",
-                 p_stream->fmt.audio.i_channels, (int)p_stream->f_rate );
-        if ( p_stream->f_rate == 0 ) return false;
+        msg_Dbg( p_demux, "FLAC header, channels: %"PRIu8", rate: %u",
+                 p_stream->fmt.audio.i_channels, p_stream->fmt.audio.i_rate );
+        if ( p_stream->fmt.audio.i_rate == 0 )
+            return false;
+        date_Init( &p_stream->dts, p_stream->fmt.audio.i_rate, 1 );
     }
     else
     {
@@ -2894,8 +2881,9 @@ static bool Ogg_ReadKateHeader( logical_stream_t *p_stream,
     gnum = oggpack_read( &opb, 32 );
     gden = oggpack_read( &opb, 32 );
     gden = __MAX( gden, 1 );
-    p_stream->f_rate = (double)gnum/gden;
-    if ( p_stream->f_rate == 0 ) return false;
+    if ( !gnum || !gden )
+        return false;
+    date_Init( &p_stream->dts, gnum, gden );
 
     p_stream->fmt.psz_language = malloc(16);
     if( p_stream->fmt.psz_language )
@@ -2952,10 +2940,10 @@ static bool Ogg_ReadVP8Header( demux_t *p_demux, logical_stream_t *p_stream,
         p_stream->fmt.video.i_sar_den = GetDWBE( &p_oggpacket->packet[15 - 1] ) & 0x0FFF;
         p_stream->fmt.video.i_frame_rate = GetDWBE( &p_oggpacket->packet[18] );
         p_stream->fmt.video.i_frame_rate_base = GetDWBE( &p_oggpacket->packet[22] );
-        p_stream->fmt.video.i_frame_rate_base =
-            __MAX( p_stream->fmt.video.i_frame_rate_base, 1 );
-        p_stream->f_rate = (double) p_stream->fmt.video.i_frame_rate / p_stream->fmt.video.i_frame_rate_base;
-        if ( p_stream->f_rate == 0 ) return false;
+        if ( !p_stream->fmt.video.i_frame_rate || !p_stream->fmt.video.i_frame_rate_base )
+            return false;
+        date_Init( &p_stream->dts, p_stream->fmt.video.i_frame_rate,
+                                   p_stream->fmt.video.i_frame_rate_base );
         return true;
     /* METADATA */
     case 0x02:
@@ -3095,8 +3083,8 @@ static void Ogg_ReadAnnodexHeader( demux_t *p_demux,
                  granule_rate_numerator, granule_rate_denominator,
                  p_stream->i_secondary_header_packets, content_type_string );
 
-        p_stream->f_rate = (float) granule_rate_numerator /
-            (float) granule_rate_denominator;
+        if( granule_rate_numerator && granule_rate_denominator )
+            date_Init( &p_stream->dts, granule_rate_numerator, granule_rate_denominator );
 
         /* What type of file do we have?
          * strcmp is safe to use here because we've extracted
@@ -3425,8 +3413,17 @@ static bool Ogg_ReadDiracHeader( logical_stream_t *p_stream,
             u_d = dirac_uint( &bs ); /* frame_rate_denominator */
         }
     }
-    p_stream->f_rate = (float) u_n / u_d;
-    if ( p_stream->f_rate == 0 ) return false;
+
+    if( !u_n || !u_d )
+        return false;
+
+    /*
+     * NB, OggDirac granulepos values are in units of 2*picturerate
+     * When picture_coding_mode = 0 (progressive),
+     * pt increments by two for each picture in display order.
+     * When picture_coding_mode = 1 (interlace),
+     * pt increments by one for each field in display order. */
+    date_Init( &p_stream->dts, 2 * u_n, u_d );
 
     return true;
 }
@@ -3477,17 +3474,17 @@ static bool Ogg_ReadOggSpotsHeader( logical_stream_t *p_stream,
         i_granulerate_denominator = 1;
     }
 
-    p_stream->f_rate = ((double)i_granulerate_numerator) / i_granulerate_denominator;
-    if ( p_stream->f_rate == 0 )
-    {
+    if ( !i_granulerate_numerator || !i_granulerate_denominator )
         return false;
-    }
 
     /* Normalize granulerate */
     vlc_ureduce(&p_stream->fmt.video.i_frame_rate,
                 &p_stream->fmt.video.i_frame_rate_base,
                 i_granulerate_numerator, i_granulerate_denominator, 0);
 
+    date_Init( &p_stream->dts, p_stream->fmt.video.i_frame_rate,
+                               p_stream->fmt.video.i_frame_rate_base );
+
     p_stream->i_granule_shift = p_oggpacket->packet[28];
 
     return true;
diff --git a/modules/demux/ogg.h b/modules/demux/ogg.h
index 13c1c6d4d6..1914762e41 100644
--- a/modules/demux/ogg.h
+++ b/modules/demux/ogg.h
@@ -22,14 +22,6 @@
  * 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
  *****************************************************************************/
@@ -48,6 +40,8 @@
 #define PACKET_LEN_BITS2     0x02
 #define PACKET_IS_SYNCPOINT  0x08
 
+#define OGGDS_RESOLUTION     10000000
+
 typedef struct oggseek_index_entry demux_index_entry_t;
 typedef struct ogg_skeleton_t ogg_skeleton_t;
 
@@ -64,7 +58,8 @@ typedef struct logical_stream_s
     es_format_t      fmt;
     es_format_t      fmt_old;                  /* format of old ES is reused */
     es_out_id_t      *p_es;
-    double           f_rate;
+    date_t           dts;
+    bool             b_contiguous;              /* Granule is end of packet */
 
     int              i_serial_no;
 
@@ -76,13 +71,11 @@ typedef struct logical_stream_s
     int32_t          i_extra_headers_packets;
     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_previous_pcr;
 
     /* Misc */
     bool b_initializing;
@@ -93,8 +86,6 @@ typedef struct logical_stream_s
 
     /* Opus has a starting offset in the headers. */
     int i_pre_skip;
-    /* Vorbis and Opus can trim the end of a stream using granule positions. */
-    int i_end_trim; /* number of samples to keep */
 
     /* offset of first keyframe for theora; can be 0 or 1 depending on version number */
     int8_t i_keyframe_offset;
diff --git a/modules/demux/ogg_granule.c b/modules/demux/ogg_granule.c
new file mode 100644
index 0000000000..524533dd87
--- /dev/null
+++ b/modules/demux/ogg_granule.c
@@ -0,0 +1,187 @@
+/*****************************************************************************
+ * ogg_granule.c : ogg granule functions
+ *****************************************************************************
+ * Copyright (C) 2008 - 2018 VideoLAN Authors and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * 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
+
+#include <ogg/ogg.h>
+
+#include <vlc_common.h>
+#include <vlc_codecs.h>
+#include <vlc_es.h>
+
+#include "ogg.h"
+#include "ogg_granule.h"
+
+/* Theora spec 7.1 */
+#define THEORA_FTYPE_NOTDATA       0x80
+#define THEORA_FTYPE_INTERFRAME    0x40
+
+/* Checks if current packet matches codec keyframe */
+bool Ogg_IsKeyFrame( const logical_stream_t *p_stream, const ogg_packet *p_packet )
+{
+    if ( p_stream->b_oggds )
+    {
+        return ( p_packet->bytes > 0 && p_packet->packet[0] & PACKET_IS_SYNCPOINT );
+    }
+    else switch ( p_stream->fmt.i_codec )
+    {
+        case VLC_CODEC_THEORA:
+        case VLC_CODEC_DAALA: /* Same convention used in daala */
+            if ( p_packet->bytes <= 0 || p_packet->packet[0] & THEORA_FTYPE_NOTDATA )
+                return false;
+            else
+                return !( p_packet->packet[0] & THEORA_FTYPE_INTERFRAME );
+        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( const logical_stream_t *p_stream, int64_t i_granule )
+{
+    if ( p_stream->b_oggds )
+    {
+           return -1; /* We have no way to know */
+    }
+    else switch( p_stream->fmt.i_codec )
+    {
+        case VLC_CODEC_THEORA:
+        case VLC_CODEC_DAALA:
+            return ( i_granule >> p_stream->i_granule_shift ) << p_stream->i_granule_shift;
+        case VLC_CODEC_DIRAC:
+            return ( i_granule >> 31 ) << 31;
+        default:
+            /* No change, that's keyframe */
+            return i_granule;
+    }
+}
+
+static int64_t Ogg_GranuleToSampleDelta( const logical_stream_t *p_stream, int64_t i_granule )
+{
+    if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC )
+        return (i_granule >> 9) & 0x1fff;
+    else
+        return -1;
+}
+
+static int64_t Ogg_GranuleToSample( const logical_stream_t *p_stream, int64_t i_granule )
+{
+    switch( p_stream->fmt.i_codec )
+    {
+        case VLC_CODEC_THEORA:
+        case VLC_CODEC_DAALA:
+        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 );
+            return iframe + pframe;
+        }
+        case VLC_CODEC_VP8:
+        case VLC_CODEC_OGGSPOTS:
+            return i_granule >> p_stream->i_granule_shift;
+        case VLC_CODEC_DIRAC:
+            return (i_granule >> 31);
+        case VLC_CODEC_OPUS:
+        case VLC_CODEC_VORBIS:
+        case VLC_CODEC_SPEEX:
+        case VLC_CODEC_FLAC:
+            return i_granule/* - p_stream->i_pre_skip*/;
+        default:
+            return i_granule;
+    }
+}
+
+static int64_t Ogg_ShiftPacketSample( const logical_stream_t *p_stream,
+                                      int64_t i_sample, bool b_start )
+{
+    /* /!\ Packet Granule as sample value ! */
+
+    /* granule always point to end time of packet
+       Except with OggDS where it is reversed */
+    int64_t i_endtostartoffset = 0; /* in interval # */
+    if( p_stream->b_oggds )
+        i_endtostartoffset = (b_start ? 0 : 1);
+    else
+        i_endtostartoffset = (b_start ? -1 : 0);
+
+    if( p_stream->fmt.i_cat == VIDEO_ES )
+    {
+        if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC ) /* points to start */
+            i_sample += (p_stream->special.dirac.b_interlaced ? 1 : 2) * (i_endtostartoffset + 1);
+        else
+            i_sample += i_endtostartoffset * 1;
+    }
+    else if( p_stream->fmt.i_cat == AUDIO_ES )
+    {
+        if( p_stream->fmt.i_codec == VLC_CODEC_SPEEX )
+        {
+            i_sample += i_endtostartoffset *
+                        p_stream->special.speex.i_framesize *
+                        p_stream->special.speex.i_framesperpacket;
+        }
+        else /* we can't tell */
+        {
+            if( i_endtostartoffset != 0 )
+                return -1;
+        }
+    }
+    return i_sample;
+}
+
+mtime_t Ogg_SampleToTime( const logical_stream_t *p_stream, int64_t i_sample, bool b_start )
+{
+    i_sample = Ogg_ShiftPacketSample( p_stream, i_sample, b_start );
+    if( i_sample < 0 )
+        return VLC_TS_INVALID;
+
+    date_t d = p_stream->dts;
+    date_Set(&d, VLC_TS_0);
+    return date_Increment( &d, i_sample );
+}
+
+bool Ogg_GranuleIsValid( const logical_stream_t *p_stream, int64_t i_granule )
+{
+    /* First frame in ogm is 0 (0[header] 0[frame] -1 2 3 -1 5 ...) */
+    return !( i_granule < 1 - !!p_stream->b_oggds );
+}
+
+mtime_t Ogg_GranuleToTime( const logical_stream_t *p_stream, int64_t i_granule,
+                           bool b_start, bool b_pts )
+{
+    if( !Ogg_GranuleIsValid( p_stream, i_granule ) )
+        return VLC_TS_INVALID;
+
+    int64_t i_sample = Ogg_GranuleToSample( p_stream, i_granule );
+    if( b_pts )
+    {
+        int64_t i_delta = Ogg_GranuleToSampleDelta( p_stream, i_granule );
+        if( i_delta != -1 )
+            i_sample += i_delta;
+    }
+    return Ogg_SampleToTime( p_stream, i_sample, b_start );
+}
diff --git a/modules/demux/ogg_granule.h b/modules/demux/ogg_granule.h
new file mode 100644
index 0000000000..2bdf409e29
--- /dev/null
+++ b/modules/demux/ogg_granule.h
@@ -0,0 +1,27 @@
+/*****************************************************************************
+ * ogg_granule.h : ogg granule functions
+ *****************************************************************************
+ * Copyright (C) 2008 - 2018 VideoLAN Authors and VideoLabs
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+int64_t Ogg_GetKeyframeGranule ( const logical_stream_t *, int64_t i_granule );
+bool    Ogg_IsKeyFrame ( const logical_stream_t *, const ogg_packet * );
+mtime_t Ogg_GranuleToTime( const logical_stream_t *, int64_t i_granule,
+                           bool b_packetstart, bool b_pts );
+mtime_t Ogg_SampleToTime( const logical_stream_t *, int64_t i_sample,
+                          bool b_packetstart );
+bool    Ogg_GranuleIsValid( const logical_stream_t *, int64_t i_granule );
diff --git a/modules/demux/oggseek.c b/modules/demux/oggseek.c
index ff4c5f7f7b..3e774c79ea 100644
--- a/modules/demux/oggseek.c
+++ b/modules/demux/oggseek.c
@@ -30,6 +30,10 @@
 # include "config.h"
 #endif
 
+#ifdef HAVE_LIBVORBIS
+  #include <vorbis/codec.h>
+#endif
+
 #include <vlc_common.h>
 #include <vlc_demux.h>
 
@@ -40,10 +44,7 @@
 
 #include "ogg.h"
 #include "oggseek.h"
-
-/* Theora spec 7.1 */
-#define THEORA_FTYPE_NOTDATA       0x80
-#define THEORA_FTYPE_INTERFRAME    0x40
+#include "ogg_granule.h"
 
 #define SEGMENT_NOT_FOUND -1
 
@@ -55,14 +56,6 @@ typedef struct packetStartCoordinates
     int64_t i_skip;
 } packetStartCoordinates;
 
-//#define OGG_SEEK_DEBUG 1
-#ifdef OGG_SEEK_DEBUG
-  #define OggDebug(code) code
-  #define OggNoDebug(code)
-#else
-  #define OggDebug(code)
-  #define OggNoDebug(code) code
-#endif
 /************************************************************
 * index entries
 *************************************************************/
@@ -289,7 +282,8 @@ void Oggseek_ProbeEnd( demux_t *p_demux )
                     if ( p_sys->pp_stream[i]->i_serial_no != ogg_page_serialno( &page ) )
                         continue;
 
-                    i_length = Oggseek_GranuleToAbsTimestamp( p_sys->pp_stream[i], i_granule, false );
+                    i_length = Ogg_GranuleToTime( p_sys->pp_stream[i], i_granule,
+                                                  !p_sys->pp_stream[i]->b_contiguous, false );
                     if( i_length > VLC_TS_INVALID )
                         p_sys->i_length = __MAX( p_sys->i_length, (i_length - VLC_TS_0) / 1000000 );
                     break;
@@ -441,49 +435,6 @@ static int64_t find_first_page_granule( demux_t *p_demux,
     }
 }
 
-/* Checks if current packet matches codec keyframe */
-bool Ogg_IsKeyFrame( logical_stream_t *p_stream, ogg_packet *p_packet )
-{
-    if ( p_stream->b_oggds )
-    {
-        return ( p_packet->bytes > 0 && p_packet->packet[0] & PACKET_IS_SYNCPOINT );
-    }
-    else switch ( p_stream->fmt.i_codec )
-    {
-    case VLC_CODEC_THEORA:
-    case VLC_CODEC_DAALA: /* Same convention used in daala */
-        if ( p_packet->bytes <= 0 || p_packet->packet[0] & THEORA_FTYPE_NOTDATA )
-            return false;
-        else
-            return !( p_packet->packet[0] & THEORA_FTYPE_INTERFRAME );
-    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 )
-{
-    if ( p_stream->b_oggds )
-    {
-           return -1; /* We have no way to know */
-    }
-    else if( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
-             p_stream->fmt.i_codec == VLC_CODEC_DAALA )
-    {
-        return ( i_granule >> p_stream->i_granule_shift ) << p_stream->i_granule_shift;
-    }
-    else if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC )
-    {
-        return ( i_granule >> 31 ) << 31;
-    }
-    /* No change, that's keyframe or it can't be shifted out (oggds) */
-    return i_granule;
-}
-
 static bool OggSeekToPacket( demux_t *p_demux, logical_stream_t *p_stream,
             int64_t i_granulepos, packetStartCoordinates *p_lastpacketcoords,
             bool b_exact )
@@ -674,82 +625,6 @@ restart:
     return i_result;
 }
 
-/* Dont use b_presentation with frames granules ! */
-mtime_t Oggseek_GranuleToAbsTimestamp( logical_stream_t *p_stream,
-                                       int64_t i_granule, bool b_presentation )
-{
-    mtime_t i_timestamp = VLC_TS_INVALID;
-    if ( i_granule < 1 - !!p_stream->b_oggds )
-        return VLC_TS_INVALID;
-
-    if ( p_stream->b_oggds )
-    {
-        i_timestamp = i_granule * CLOCK_FREQ / p_stream->f_rate;
-    }
-    else  switch( p_stream->fmt.i_codec )
-    {
-    case VLC_CODEC_THEORA:
-    case VLC_CODEC_DAALA:
-    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;
-    }
-    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;
-    }
-    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 */
-        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;
-    }
-    case VLC_CODEC_OPUS:
-    {
-        if ( b_presentation ) return VLC_TS_INVALID;
-        i_timestamp = ( i_granule - p_stream->i_pre_skip ) * CLOCK_FREQ / 48000;
-        break;
-    }
-    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;
-    }
-    case VLC_CODEC_SPEEX:
-    {
-        if ( b_presentation )
-            i_granule -= p_stream->special.speex.i_framesize *
-                         p_stream->special.speex.i_framesperpacket;
-        i_timestamp = i_granule * CLOCK_FREQ / p_stream->f_rate;
-        break;
-    }
-    case VLC_CODEC_OGGSPOTS:
-    {
-        if ( b_presentation ) return VLC_TS_INVALID;
-        i_timestamp = ( i_granule >> p_stream->i_granule_shift )
-                * CLOCK_FREQ / p_stream->f_rate;
-        break;
-    }
-    }
-
-    return i_timestamp != VLC_TS_INVALID ? i_timestamp + VLC_TS_0 : VLC_TS_INVALID;
-}
-
 /* returns pos */
 static int64_t OggBisectSearchByTime( demux_t *p_demux, logical_stream_t *p_stream,
             mtime_t i_targettime, int64_t i_pos_lower, int64_t i_pos_upper)
@@ -803,14 +678,18 @@ static int64_t OggBisectSearchByTime( demux_t *p_demux, logical_stream_t *p_stre
                                                  p_stream,
                                                  &current.i_granule );
 
-        current.i_timestamp = Oggseek_GranuleToAbsTimestamp( p_stream,
-                                                             current.i_granule, false );
+        current.i_timestamp = Ogg_GranuleToTime( p_stream, current.i_granule,
+                                                 !p_stream->b_contiguous, false );
 
         if ( current.i_timestamp == VLC_TS_INVALID && current.i_granule > 0 )
         {
             msg_Err( p_demux, "Unmatched granule. New codec ?" );
             return -1;
         }
+        else if ( current.i_timestamp < 0 )  /* due to preskip with some codecs */
+        {
+            current.i_timestamp = 0;
+        }
 
         if ( current.i_pos != -1 && current.i_granule != -1 )
         {
@@ -874,7 +753,7 @@ static int64_t OggBisectSearchByTime( demux_t *p_demux, logical_stream_t *p_stre
                            i_keyframegranule >> p_stream->i_granule_shift,
                            bestlower.i_granule,
                            i_pos_upper,
-                           Oggseek_GranuleToAbsTimestamp( p_stream, i_keyframegranule, false ) ) );
+                           Ogg_GranuleToTime( p_stream, i_keyframegranule, !p_stream->b_contiguous, false ) ) );
 
         OggDebug( msg_Dbg( p_demux, "Seeking back to %"PRId64, __MAX ( bestlower.i_pos - OGGSEEK_BYTES_TO_READ, p_stream->i_data_start ) ) );
 
diff --git a/modules/demux/oggseek.h b/modules/demux/oggseek.h
index 4225a13472..93755555ee 100644
--- a/modules/demux/oggseek.h
+++ b/modules/demux/oggseek.h
@@ -26,6 +26,15 @@
  * Preamble
  *****************************************************************************/
 
+//#define OGG_SEEK_DEBUG 1
+#ifdef OGG_SEEK_DEBUG
+  #define OggDebug(code) code
+  #define OggNoDebug(code)
+#else
+  #define OggDebug(code)
+  #define OggNoDebug(code) code
+#endif
+
 #define PAGE_HEADER_BYTES 27
 
 #define OGGSEEK_BYTES_TO_READ 8500
@@ -49,11 +58,6 @@ struct oggseek_index_entry
     int64_t i_pagepos_end;
 };
 
-int64_t Ogg_GetKeyframeGranule ( logical_stream_t *p_stream, int64_t i_granule );
-bool    Ogg_IsKeyFrame ( logical_stream_t *, ogg_packet * );
-
-mtime_t Oggseek_GranuleToAbsTimestamp ( logical_stream_t *p_stream, int64_t i_granule,
-                                        bool b_presentation );
 int     Oggseek_BlindSeektoAbsoluteTime ( demux_t *, logical_stream_t *, mtime_t, bool );
 int     Oggseek_BlindSeektoPosition ( demux_t *, logical_stream_t *, double f, bool );
 int     Oggseek_SeektoAbsolutetime ( demux_t *, logical_stream_t *, mtime_t );



More information about the vlc-commits mailing list