Commit 4dde2b7a authored by Francois Cartegnie's avatar Francois Cartegnie

demux: ogg: fix probing duration (fix #9591, #9649)

parent d1c8352d
......@@ -126,13 +126,12 @@ static bool Ogg_LogicalStreamResetEsFormat( demux_t *p_demux, logical_stream_t *
/* */
static void Ogg_ExtractMeta( demux_t *p_demux, es_format_t *p_fmt, const uint8_t *p_headers, int i_headers );
static int64_t Ogg_GetLastPacket( demux_t *p_demux, logical_stream_t *p_stream, double f_rate );
/* Logical bitstream headers */
static void Ogg_ReadTheoraHeader( demux_t *, logical_stream_t *, ogg_packet * );
static void Ogg_ReadVorbisHeader( demux_t *, logical_stream_t *, ogg_packet * );
static void Ogg_ReadTheoraHeader( logical_stream_t *, ogg_packet * );
static void Ogg_ReadVorbisHeader( logical_stream_t *, ogg_packet * );
static void Ogg_ReadSpeexHeader( logical_stream_t *, ogg_packet * );
static void Ogg_ReadOpusHeader( demux_t *, logical_stream_t *, ogg_packet * );
static void Ogg_ReadOpusHeader( logical_stream_t *, ogg_packet * );
static void Ogg_ReadKateHeader( logical_stream_t *, ogg_packet * );
static void Ogg_ReadFlacHeader( demux_t *, logical_stream_t *, ogg_packet * );
static void Ogg_ReadAnnodexHeader( demux_t *, logical_stream_t *, ogg_packet * );
......@@ -234,6 +233,7 @@ static int Demux( demux_t * p_demux )
ogg_packet oggpacket;
int i_stream;
bool b_skipping = false;
bool b_canseek;
if( p_sys->i_eos == p_sys->i_streams )
......@@ -255,6 +255,11 @@ static int Demux( demux_t * p_demux )
if( Ogg_BeginningOfStream( p_demux ) != VLC_SUCCESS )
return 0;
/* Find the real duration */
stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_canseek );
if ( b_canseek )
Oggseek_ProbeEnd( p_demux );
msg_Dbg( p_demux, "beginning of a group of logical streams" );
es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 );
}
......@@ -339,14 +344,14 @@ static int Demux( demux_t * p_demux )
oggpacket.bytes >= 7 &&
! memcmp( oggpacket.packet, "\x80theora", 7 ) )
{
Ogg_ReadTheoraHeader( p_demux, p_stream, &oggpacket );
Ogg_ReadTheoraHeader( p_stream, &oggpacket );
p_stream->i_secondary_header_packets = 0;
}
else if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS &&
oggpacket.bytes >= 7 &&
! memcmp( oggpacket.packet, "\x01vorbis", 7 ) )
{
Ogg_ReadVorbisHeader( p_demux, p_stream, &oggpacket );
Ogg_ReadVorbisHeader( p_stream, &oggpacket );
p_stream->i_secondary_header_packets = 0;
}
else if( p_stream->fmt.i_codec == VLC_CODEC_CMML )
......@@ -1287,7 +1292,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
if( oggpacket.bytes >= 7 &&
! memcmp( oggpacket.packet, "\x01vorbis", 7 ) )
{
Ogg_ReadVorbisHeader( p_demux, p_stream, &oggpacket );
Ogg_ReadVorbisHeader( p_stream, &oggpacket );
msg_Dbg( p_demux, "found vorbis header" );
}
/* Check for Speex header */
......@@ -1304,7 +1309,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
else if( oggpacket.bytes >= 8 &&
! memcmp( oggpacket.packet, "OpusHead", 8 ) )
{
Ogg_ReadOpusHeader( p_demux, p_stream, &oggpacket );
Ogg_ReadOpusHeader( p_stream, &oggpacket );
msg_Dbg( p_demux, "found opus header, channels: %i, "
"pre-skip: %i",
p_stream->fmt.audio.i_channels,
......@@ -1348,7 +1353,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
else if( oggpacket.bytes >= 7 &&
! memcmp( oggpacket.packet, "\x80theora", 7 ) )
{
Ogg_ReadTheoraHeader( p_demux, p_stream, &oggpacket );
Ogg_ReadTheoraHeader( p_stream, &oggpacket );
msg_Dbg( p_demux,
"found theora header, bitrate: %i, rate: %f",
......@@ -2071,14 +2076,7 @@ static void Ogg_ExtractMeta( demux_t *p_demux, es_format_t *p_fmt, const uint8_t
p_demux->info.i_update |= INPUT_UPDATE_META;
}
static int64_t Ogg_GetLastPacket( demux_t *p_demux, logical_stream_t *p_stream,
double f_rate )
{
int64_t last_packet = oggseek_get_last_frame( p_demux, p_stream );
return ( last_packet >= 0 ) ? last_packet / f_rate : -1;
}
static void Ogg_ReadTheoraHeader( demux_t *p_demux, logical_stream_t *p_stream,
static void Ogg_ReadTheoraHeader( logical_stream_t *p_stream,
ogg_packet *p_oggpacket )
{
bs_t bitstream;
......@@ -2144,16 +2142,9 @@ static void Ogg_ReadTheoraHeader( demux_t *p_demux, logical_stream_t *p_stream,
{
p_stream->i_keyframe_offset = 1;
}
if ( p_demux->p_sys->i_length < 0 )
{
int64_t last_packet = Ogg_GetLastPacket( p_demux, p_stream, p_stream->f_rate );
if ( last_packet >= 0 )
p_demux->p_sys->i_length = last_packet;
}
}
static void Ogg_ReadVorbisHeader( demux_t *p_demux, logical_stream_t *p_stream,
static void Ogg_ReadVorbisHeader( logical_stream_t *p_stream,
ogg_packet *p_oggpacket )
{
oggpack_buffer opb;
......@@ -2175,13 +2166,6 @@ static void Ogg_ReadVorbisHeader( demux_t *p_demux, logical_stream_t *p_stream,
oggpack_read( &opb, 32 );
oggpack_adv( &opb, 32 );
p_stream->fmt.i_bitrate = oggpack_read( &opb, 32 );
if ( p_demux->p_sys->i_length < 0 )
{
int64_t last_packet = Ogg_GetLastPacket( p_demux, p_stream, p_stream->f_rate );
if ( last_packet >= 0 )
p_demux->p_sys->i_length = last_packet;
}
}
static void Ogg_ReadSpeexHeader( logical_stream_t *p_stream,
......@@ -2214,8 +2198,7 @@ static void Ogg_ReadSpeexHeader( logical_stream_t *p_stream,
p_stream->i_extra_headers_packets = oggpack_read( &opb, 32 ); /* extra_headers */
}
static void Ogg_ReadOpusHeader( demux_t *p_demux,
logical_stream_t *p_stream,
static void Ogg_ReadOpusHeader( logical_stream_t *p_stream,
ogg_packet *p_oggpacket )
{
oggpack_buffer opb;
......@@ -2240,13 +2223,6 @@ static void Ogg_ReadOpusHeader( demux_t *p_demux,
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 );
if ( p_demux->p_sys->i_length < 0 )
{
int64_t last_packet = Ogg_GetLastPacket( p_demux, p_stream, p_stream->f_rate );
if ( last_packet >= 0 )
p_demux->p_sys->i_length = last_packet;
}
}
static void Ogg_ReadFlacHeader( demux_t *p_demux, logical_stream_t *p_stream,
......@@ -2359,7 +2335,7 @@ static void Ogg_ReadAnnodexHeader( demux_t *p_demux,
uint64_t timebase_numerator;
uint64_t timebase_denominator;
Ogg_ReadTheoraHeader( p_demux, p_stream, p_oggpacket );
Ogg_ReadTheoraHeader( p_stream, p_oggpacket );
oggpack_readinit( &opb, p_oggpacket->packet, p_oggpacket->bytes);
oggpack_adv( &opb, 8*8 ); /* "Annodex\0" header */
......
......@@ -36,6 +36,8 @@
#include <ogg/ogg.h>
#include <limits.h>
#include <assert.h>
#include "ogg.h"
#include "oggseek.h"
......@@ -460,68 +462,96 @@ static int64_t find_first_page( demux_t *p_demux, int64_t i_pos1, int64_t i_pos2
}
}
/* Find the last frame for p_stream,
-1 is returned on failure */
static int64_t find_last_frame (demux_t *p_demux, logical_stream_t *p_stream)
void Oggseek_ProbeEnd( demux_t *p_demux )
{
/* Temporary state */
ogg_stream_state os;
ogg_sync_state oy;
ogg_page page;
demux_sys_t *p_sys = p_demux->p_sys;
int64_t i_pos, i_startpos, i_result, i_granule, i_lowerbound;
int64_t i_length = 0;
int64_t i_backup_pos = stream_Tell( p_demux->s );
int64_t i_upperbound = stream_Size( p_demux->s );
unsigned int i_backoffset = OGGSEEK_BYTES_TO_READ;
assert( OGGSEEK_BYTES_TO_READ < UINT_MAX );
int64_t i_page_pos;
int64_t i_start_pos;
int64_t i_frame = -1;
int64_t i_last_frame = -1;
int64_t i_kframe = 0;
int64_t i_pos1;
int64_t i_pos2;
const char *buffer;
demux_sys_t *p_sys = p_demux->p_sys;
ogg_stream_init( &os, -1 );
ogg_sync_init( &oy );
i_pos1 = p_stream->i_data_start;
i_pos2 = p_sys->i_total_length;
/* Try to lookup last granule from each logical stream */
i_lowerbound = stream_Size( p_demux->s ) - p_sys->i_streams * MAX_PAGE_SIZE * 2;
i_lowerbound = __MAX( 0, i_lowerbound );
i_start_pos = i_pos2 - OGGSEEK_BYTES_TO_READ;
i_pos = i_startpos = __MAX( i_lowerbound, i_upperbound - i_backoffset );
if ( stream_Seek( p_demux->s, i_pos ) )
{
ogg_sync_clear( &oy );
ogg_stream_clear( &os );
return;
}
while( 1 )
while( i_pos >= i_lowerbound )
{
if ( i_start_pos < i_pos1 ) i_start_pos = i_pos1;
i_page_pos = find_first_page( p_demux, i_start_pos, i_pos2, p_stream, &i_kframe, &i_frame );
while( i_pos < i_upperbound )
{
if ( oy.unsynced )
i_result = ogg_sync_pageseek( &oy, &page );
buffer = ogg_sync_buffer( &oy, OGGSEEK_BYTES_TO_READ );
if ( buffer == NULL ) goto clean;
i_result = stream_Read( p_demux->s, (void*) buffer, OGGSEEK_BYTES_TO_READ );
if ( i_result < 1 ) goto clean;
i_pos += i_result;
ogg_sync_wrote( &oy, i_result );
if ( i_frame == -1 )
while( ogg_sync_pageout( &oy, &page ) == 1 )
{
/* no pages found in range */
if ( i_last_frame >= 0 )
i_granule = ogg_page_granulepos( &page );
if ( i_granule == -1 ) continue;
for ( int i=0; i< p_sys->i_streams; i++ )
{
/* No more pages in range -> return last one */
return i_last_frame;
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 );
p_sys->i_length = __MAX( p_sys->i_length, i_length / 1000000 );
break;
}
if ( i_start_pos <= i_pos1 )
{
return -1;
}
if ( i_length > 0 ) break;
}
/* We found at least a page with valid granule */
if ( i_length > 0 ) break;
/* Go back a bit */
i_pos2 -= i_start_pos;
i_start_pos -= OGGSEEK_BYTES_TO_READ;
if ( i_start_pos < i_pos1 ) i_start_pos = i_pos1;
i_pos2 += i_start_pos;
/* Otherwise increase read size, starting earlier */
if ( i_backoffset <= ( UINT_MAX >> 1 ) )
{
i_backoffset <<= 1;
i_startpos = i_upperbound - i_backoffset;
}
else
{
/* found a page, see if we can find another one */
i_last_frame = i_frame;
i_start_pos = i_page_pos + 1;
}
i_startpos -= i_backoffset;
}
return -1;
}
i_pos = i_startpos;
if ( stream_Seek( p_demux->s, i_pos ) )
break;
}
clean:
stream_Seek( p_demux->s, i_backup_pos );
ogg_sync_clear( &oy );
ogg_stream_clear( &os );
}
/* convert a theora frame to a granulepos */
......@@ -1238,67 +1268,12 @@ static int64_t OggBisectSearchByTime( demux_t *p_demux, logical_stream_t *p_stre
return bestlower.i_pos;
}
/* get highest frame in theora and opus streams */
static int64_t get_last_frame ( demux_t *p_demux, logical_stream_t *p_stream )
{
demux_sys_t *p_sys = p_demux->p_sys;
int64_t i_frame;
ogg_stream_state os;
/* Backup the stream state. We get called during header processing, and our
* caller expects its header packet to remain valid after the call. If we
* let find_last_frame() reuse the same stream state, then it will
* invalidate the pointers in that packet. */
memcpy(&os, &p_stream->os, sizeof(os));
ogg_stream_init( &p_stream->os, p_stream->i_serial_no );
i_frame = find_last_frame ( p_demux, p_stream );
/* We need to reset back to the start here, otherwise packets cannot be decoded.
* I think this is due to the fact that we seek to the end and then we must reset
* all logical streams, which causes remaining headers not to be read correctly.
* Seeking to 0 is the only value which seems to work, and it appears to have no
* adverse effects. */
seek_byte( p_demux, 0 );
/* Reset stream states */
p_sys->i_streams = 0;
ogg_stream_clear( &p_stream->os );
memcpy( &p_stream->os, &os, sizeof(os) );
return i_frame;
}
/************************************************************************
* public functions
*************************************************************************/
/* return highest frame number for p_stream (which must be a theora, dirac or opus stream) */
int64_t oggseek_get_last_frame ( demux_t *p_demux, logical_stream_t *p_stream )
{
int64_t i_frame = -1;
if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
p_stream->fmt.i_codec == VLC_CODEC_VORBIS ||
p_stream->fmt.i_codec == VLC_CODEC_OPUS )
{
i_frame = get_last_frame ( p_demux, p_stream );
if ( i_frame < 0 ) return -1;
return i_frame;
}
/* unhandled video format */
return -1;
}
int Oggseek_BlindSeektoPosition( demux_t *p_demux, logical_stream_t *p_stream,
double f, bool b_canfastseek )
{
......
......@@ -58,6 +58,7 @@ bool Oggseek_PacketPCRFixup ( logical_stream_t *p_stream, ogg_page *, ogg_pac
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,
......@@ -65,8 +66,6 @@ const demux_index_entry_t *oggseek_theora_index_entry_add ( logical_stream_t *,
void oggseek_index_entries_free ( demux_index_entry_t * );
int64_t oggseek_get_last_frame ( demux_t *, logical_stream_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