Commit ff681fc7 authored by Francois Cartegnie's avatar Francois Cartegnie

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

- Previous algorithm was unable to guess the first packet
pts because of ogg design (some codecs/opus/vorbis have total
sample at page granule and only granule on last packet. You
then need to compute first packet pts backward using packets
duration).
So we were stuck either assuming a start at pts/pcr zero, or
be unable to handle ogg streaming (non zero start) correctly,
or had to always drop the 0..N-1 packets for first page.
We need libvorbis to correcly compute those durations (code
is far more complex than opus's one).
- Refactorized to discard the tons of cases appended
with each new codec.
- lots of other fixes in many places
parent 5008e1c3
......@@ -1999,6 +1999,10 @@ dnl
dnl ogg demux plugin
dnl
PKG_ENABLE_MODULES_VLC([OGG], [], [ogg >= 1.0], [Ogg demux support], [auto])
PKG_CHECK_MODULES(LIBVORBIS, [vorbis >= 1.1], [
AC_DEFINE(HAVE_LIBVORBIS, 1, [Define to 1 if you have the libvorbis])
],[])
PKG_ENABLE_MODULES_VLC([OGG], [], [ogg >= 1.0], [Ogg demux support], [auto], [${LIBVORBIS_CFLAGS}], [${LIBVORBIS_LIBS}])
if test "${enable_sout}" != "no"; then
PKG_ENABLE_MODULES_VLC([MUX_OGG], [], [ogg >= 1.0], [Ogg mux support], [auto])
dnl Check for libshout
......
......@@ -7,9 +7,9 @@ demux_LTLIBRARIES += libflacsys_plugin.la
libogg_plugin_la_SOURCES = demux/ogg.c demux/ogg.h demux/oggseek.c demux/oggseek.h \
demux/xiph_metadata.h demux/xiph.h demux/xiph_metadata.c
libogg_plugin_la_CFLAGS = $(AM_CFLAGS) $(CFLAGS_ogg)
libogg_plugin_la_CFLAGS = $(AM_CFLAGS) $(CFLAGS_ogg) $(LIBVORBIS_CFLAGS)
libogg_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(demuxdir)'
libogg_plugin_la_LIBADD = $(LIBS_ogg)
libogg_plugin_la_LIBADD = $(LIBS_ogg) $(LIBVORBIS_LIBS)
EXTRA_LTLIBRARIES += libogg_plugin.la
demux_LTLIBRARIES += $(LTLIBogg)
......
This diff is collapsed.
......@@ -22,6 +22,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_LIBVORBIS
#include <vorbis/codec.h>
#endif
/*****************************************************************************
* Definitions of structures and functions used by this plugin
*****************************************************************************/
......@@ -43,6 +51,12 @@
typedef struct oggseek_index_entry demux_index_entry_t;
typedef struct ogg_skeleton_t ogg_skeleton_t;
typedef struct backup_queue
{
block_t *p_block;
mtime_t i_duration;
} backup_queue_t;
typedef struct logical_stream_s
{
ogg_stream_state os; /* logical stream of packets */
......@@ -63,11 +77,11 @@ typedef struct logical_stream_s
void *p_headers;
int i_headers;
ogg_int64_t i_previous_granulepos;
ogg_int64_t i_granulepos_offset;/* first granule offset */
/* program clock reference (in units of 90kHz) derived from the previous
* granulepos */
mtime_t i_pcr;
mtime_t i_interpolated_pcr;
mtime_t i_previous_pcr;
/* Misc */
......@@ -92,18 +106,42 @@ typedef struct logical_stream_s
ogg_skeleton_t *p_skel;
/* skip some frames after a seek */
int i_skip_frames;
unsigned int i_skip_frames;
/* data start offset (absolute) in bytes */
int64_t i_data_start;
/* kate streams have the number of headers in the ID header */
int i_kate_num_headers;
/* for Annodex logical bitstreams */
int i_secondary_header_packets;
/* All blocks which can't be sent because track PCR isn't known yet */
block_t **p_prepcr_blocks;
int i_prepcr_blocks;
/* All blocks that are queued because ES isn't created yet */
block_t *p_preparse_block;
union
{
#ifdef HAVE_LIBVORBIS
struct
{
vorbis_info *p_info;
vorbis_comment *p_comment;
bool b_invalid;
int i_prev_blocksize;
} vorbis;
#endif
struct
{
/* kate streams have the number of headers in the ID header */
int i_num_headers;
} kate;
struct
{
bool b_interlaced;
} dirac;
} special;
} logical_stream_t;
struct ogg_skeleton_t
......@@ -131,7 +169,7 @@ struct demux_sys_t
/* program clock reference (in units of 90kHz) derived from the pcr of
* the sub-streams */
mtime_t i_pcr;
mtime_t i_pcr_offset;
mtime_t i_nzpcr_offset;
/* informative only */
mtime_t i_pcr_jitter;
int64_t i_access_delay;
......@@ -181,7 +219,6 @@ struct demux_sys_t
/* Length, if available. */
int64_t i_length;
DemuxDebug( bool b_seeked; )
};
......
......@@ -93,96 +93,6 @@ static demux_index_entry_t *index_entry_new( void )
return idx;
}
/* add a theora entry to our list; format is highest granulepos -> page offset of
keyframe start */
const demux_index_entry_t *oggseek_theora_index_entry_add ( logical_stream_t *p_stream,
int64_t i_granule,
int64_t i_pagepos)
{
/* add or update entry for keyframe */
demux_index_entry_t *idx;
demux_index_entry_t *oidx;
demux_index_entry_t *last_idx = NULL;
int64_t i_gpos;
int64_t i_frame;
int64_t i_kframe;
int64_t i_tframe;
int64_t i_tkframe;
if ( p_stream == NULL ) return NULL;
oidx = idx = p_stream->idx;
i_tkframe = i_granule >> p_stream->i_granule_shift;
i_tframe = i_tkframe + i_granule - ( i_tkframe << p_stream->i_granule_shift );
if ( i_tkframe < 1 ) return NULL;
if ( idx == NULL )
{
demux_index_entry_t *ie = index_entry_new();
ie->i_value = i_granule;
ie->i_pagepos = i_pagepos;
p_stream->idx = ie;
return ie;
}
while ( idx != NULL )
{
i_gpos = idx->i_value;
i_kframe = i_gpos >> p_stream->i_granule_shift;
if ( i_kframe > i_tframe ) break;
if ( i_kframe == i_tkframe )
{
/* entry exists, update it if applicable, and return it */
i_frame = i_kframe + i_gpos - ( i_kframe << p_stream->i_granule_shift );
if ( i_frame < i_tframe )
{
idx->i_value = i_granule;
idx->i_pagepos = i_pagepos;
}
return idx;
}
last_idx = idx;
idx = idx->p_next;
}
/* new entry; insert after last_idx */
idx = index_entry_new();
if ( last_idx != NULL )
{
idx->p_next = last_idx->p_next;
last_idx->p_next = idx;
idx->p_prev = last_idx;
}
else
{
idx->p_next = oidx;
oidx = idx;
}
if ( idx->p_next != NULL )
{
idx->p_next->p_prev = idx;
}
idx->i_value = i_granule;
idx->i_pagepos = i_pagepos;
return idx;
}
/* We insert into index, sorting by pagepos (as a page can match multiple
time stamps) */
const demux_index_entry_t *OggSeek_IndexAdd ( logical_stream_t *p_stream,
......@@ -414,19 +324,6 @@ clean:
ogg_stream_clear( &os );
}
/* convert a theora frame to a granulepos */
static inline int64_t frame_to_gpos( logical_stream_t *p_stream, int64_t i_kframe,
int64_t i_frame )
{
if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
{
return ( i_kframe << p_stream->i_granule_shift ) + ( i_frame - i_kframe );
}
return i_kframe;
}
static int64_t find_first_page_granule( demux_t *p_demux,
int64_t i_pos1, int64_t i_pos2,
......@@ -552,18 +449,20 @@ bool Ogg_IsKeyFrame( logical_stream_t *p_stream, ogg_packet *p_packet )
{
return ( p_packet->bytes > 0 && p_packet->packet[0] & PACKET_IS_SYNCPOINT );
}
else if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
else switch ( p_stream->fmt.i_codec )
{
case VLC_CODEC_THEORA:
if ( p_packet->bytes <= 0 || p_packet->packet[0] & THEORA_FTYPE_NOTDATA )
return false;
else
return !( p_packet->packet[0] & THEORA_FTYPE_INTERFRAME );
}
else if ( p_stream->fmt.i_codec == VLC_CODEC_VP8 )
{
case VLC_CODEC_VP8:
return ( ( ( p_packet->granulepos >> 3 ) & 0x07FFFFFF ) == 0 );
case VLC_CODEC_DIRAC:
return ( p_packet->granulepos & 0xFF8000FF );
default:
return true;
}
return true;
}
int64_t Ogg_GetKeyframeGranule( logical_stream_t *p_stream, int64_t i_granule )
......@@ -638,7 +537,6 @@ static bool OggSeekToPacket( demux_t *p_demux, logical_stream_t *p_stream,
p_lastpacketcoords->i_pos = p_sys->i_input_position;
p_lastpacketcoords->i_skip = i;
}
return true;
}
......@@ -649,6 +547,7 @@ static bool OggSeekToPacket( demux_t *p_demux, logical_stream_t *p_stream,
/* remove that packet and go sync to next */
ogg_stream_packetout( &p_stream->os, &op );
}
return false;
}
......@@ -779,75 +678,60 @@ int64_t Oggseek_GranuleToAbsTimestamp( logical_stream_t *p_stream,
int64_t i_granule, bool b_presentation )
{
int64_t i_timestamp = -1;
if ( i_granule < 0 )
if ( i_granule < 1 )
return -1;
if ( p_stream->b_oggds )
{
if ( b_presentation ) i_granule--;
i_timestamp = i_granule * CLOCK_FREQ / p_stream->f_rate;
}
else if( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
p_stream->fmt.i_codec == VLC_CODEC_KATE )
else switch( p_stream->fmt.i_codec )
{
case VLC_CODEC_THEORA:
case VLC_CODEC_KATE:
{
ogg_int64_t iframe = i_granule >> p_stream->i_granule_shift;
ogg_int64_t pframe = i_granule - ( iframe << p_stream->i_granule_shift );
/* See Theora A.2.3 */
if ( b_presentation ) pframe -= p_stream->i_keyframe_offset;
i_timestamp = ( iframe + pframe ) * CLOCK_FREQ / p_stream->f_rate;
break;
}
else if ( p_stream->fmt.i_codec == VLC_CODEC_VP8 )
case VLC_CODEC_VP8:
{
ogg_int64_t frame = i_granule >> p_stream->i_granule_shift;
if ( b_presentation ) frame--;
i_timestamp = frame * CLOCK_FREQ / p_stream->f_rate;
break;
}
else if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC )
case VLC_CODEC_DIRAC:
{
ogg_int64_t i_dts = i_granule >> 31;
ogg_int64_t delay = (i_granule >> 9) & 0x1fff;
/* NB, OggDirac granulepos values are in units of 2*picturerate */
i_timestamp = (i_dts/2) * CLOCK_FREQ / p_stream->f_rate;
double f_rate = p_stream->f_rate;
if ( !p_stream->special.dirac.b_interlaced ) f_rate *= 2;
if ( b_presentation ) i_dts += delay;
i_timestamp = i_dts * CLOCK_FREQ / f_rate;
break;
}
else if( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
case VLC_CODEC_OPUS:
{
if ( b_presentation ) return VLC_TS_INVALID;
i_timestamp = ( i_granule - p_stream->i_pre_skip ) * CLOCK_FREQ / 48000;
break;
}
else if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS )
case VLC_CODEC_VORBIS:
case VLC_CODEC_FLAC:
{
if ( b_presentation ) return VLC_TS_INVALID;
i_timestamp = i_granule * CLOCK_FREQ / p_stream->f_rate;
break;
}
return i_timestamp;
}
bool Oggseek_PacketPCRFixup( logical_stream_t *p_stream, ogg_page *p_page,
ogg_packet *p_packet )
{
if ( p_packet->granulepos != -1 )
return false;
else
if ( p_stream->b_oggds )
{
p_stream->i_pcr = Oggseek_GranuleToAbsTimestamp( p_stream,
ogg_page_granulepos( p_page ), true );
return true;
}
else if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
{
p_stream->i_pcr = Oggseek_GranuleToAbsTimestamp( p_stream,
ogg_page_granulepos( p_page ), false );
/* Computes the presentation time of the first packet on page */
p_stream->i_pcr -= CLOCK_FREQ *
ogg_page_packets( p_page ) / p_stream->f_rate;
return true;
}
else if ( p_stream->fmt.i_codec == VLC_CODEC_VP8 )
{
ogg_int64_t frame = ogg_page_granulepos( p_page ) >> p_stream->i_granule_shift;
frame -= ogg_page_packets( p_page );
p_stream->i_pcr = frame * CLOCK_FREQ / p_stream->f_rate;
}
return false;
return i_timestamp;
}
/* returns pos */
......
......@@ -54,19 +54,12 @@ bool Ogg_IsKeyFrame ( logical_stream_t *, ogg_packet * );
int64_t Oggseek_GranuleToAbsTimestamp ( logical_stream_t *p_stream, int64_t i_granule,
bool b_presentation );
bool Oggseek_PacketPCRFixup ( logical_stream_t *p_stream, ogg_page *, ogg_packet * );
int Oggseek_BlindSeektoAbsoluteTime ( demux_t *, logical_stream_t *, int64_t, bool );
int Oggseek_BlindSeektoPosition ( demux_t *, logical_stream_t *, double f, bool );
int Oggseek_SeektoAbsolutetime ( demux_t *, logical_stream_t *, int64_t i_granulepos );
const demux_index_entry_t *OggSeek_IndexAdd ( logical_stream_t *, int64_t, int64_t );
void Oggseek_ProbeEnd( demux_t * );
const demux_index_entry_t *oggseek_theora_index_entry_add ( logical_stream_t *,
int64_t i_granule,
int64_t i_pagepos );
void oggseek_index_entries_free ( demux_index_entry_t * );
int oggseek_find_frame ( demux_t *, logical_stream_t *, int64_t i_tframe );
int64_t oggseek_read_page ( demux_t * );
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment