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 * ...@@ -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 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 */ /* Logical bitstream headers */
static void Ogg_ReadTheoraHeader( demux_t *, logical_stream_t *, ogg_packet * ); static void Ogg_ReadTheoraHeader( logical_stream_t *, ogg_packet * );
static void Ogg_ReadVorbisHeader( demux_t *, 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_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_ReadKateHeader( logical_stream_t *, ogg_packet * );
static void Ogg_ReadFlacHeader( demux_t *, 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 * ); static void Ogg_ReadAnnodexHeader( demux_t *, logical_stream_t *, ogg_packet * );
...@@ -234,6 +233,7 @@ static int Demux( demux_t * p_demux ) ...@@ -234,6 +233,7 @@ static int Demux( demux_t * p_demux )
ogg_packet oggpacket; ogg_packet oggpacket;
int i_stream; int i_stream;
bool b_skipping = false; bool b_skipping = false;
bool b_canseek;
if( p_sys->i_eos == p_sys->i_streams ) if( p_sys->i_eos == p_sys->i_streams )
...@@ -255,6 +255,11 @@ static int Demux( demux_t * p_demux ) ...@@ -255,6 +255,11 @@ static int Demux( demux_t * p_demux )
if( Ogg_BeginningOfStream( p_demux ) != VLC_SUCCESS ) if( Ogg_BeginningOfStream( p_demux ) != VLC_SUCCESS )
return 0; 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" ); msg_Dbg( p_demux, "beginning of a group of logical streams" );
es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 ); es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 );
} }
...@@ -339,14 +344,14 @@ static int Demux( demux_t * p_demux ) ...@@ -339,14 +344,14 @@ static int Demux( demux_t * p_demux )
oggpacket.bytes >= 7 && oggpacket.bytes >= 7 &&
! memcmp( oggpacket.packet, "\x80theora", 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; p_stream->i_secondary_header_packets = 0;
} }
else if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS && else if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS &&
oggpacket.bytes >= 7 && oggpacket.bytes >= 7 &&
! memcmp( oggpacket.packet, "\x01vorbis", 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; p_stream->i_secondary_header_packets = 0;
} }
else if( p_stream->fmt.i_codec == VLC_CODEC_CMML ) else if( p_stream->fmt.i_codec == VLC_CODEC_CMML )
...@@ -1287,7 +1292,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) ...@@ -1287,7 +1292,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
if( oggpacket.bytes >= 7 && if( oggpacket.bytes >= 7 &&
! memcmp( oggpacket.packet, "\x01vorbis", 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" ); msg_Dbg( p_demux, "found vorbis header" );
} }
/* Check for Speex header */ /* Check for Speex header */
...@@ -1304,7 +1309,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) ...@@ -1304,7 +1309,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
else if( oggpacket.bytes >= 8 && else if( oggpacket.bytes >= 8 &&
! memcmp( oggpacket.packet, "OpusHead", 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, " msg_Dbg( p_demux, "found opus header, channels: %i, "
"pre-skip: %i", "pre-skip: %i",
p_stream->fmt.audio.i_channels, p_stream->fmt.audio.i_channels,
...@@ -1348,7 +1353,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) ...@@ -1348,7 +1353,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
else if( oggpacket.bytes >= 7 && else if( oggpacket.bytes >= 7 &&
! memcmp( oggpacket.packet, "\x80theora", 7 ) ) ! memcmp( oggpacket.packet, "\x80theora", 7 ) )
{ {
Ogg_ReadTheoraHeader( p_demux, p_stream, &oggpacket ); Ogg_ReadTheoraHeader( p_stream, &oggpacket );
msg_Dbg( p_demux, msg_Dbg( p_demux,
"found theora header, bitrate: %i, rate: %f", "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 ...@@ -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; p_demux->info.i_update |= INPUT_UPDATE_META;
} }
static int64_t Ogg_GetLastPacket( demux_t *p_demux, logical_stream_t *p_stream, static void Ogg_ReadTheoraHeader( 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,
ogg_packet *p_oggpacket ) ogg_packet *p_oggpacket )
{ {
bs_t bitstream; bs_t bitstream;
...@@ -2144,16 +2142,9 @@ static void Ogg_ReadTheoraHeader( demux_t *p_demux, logical_stream_t *p_stream, ...@@ -2144,16 +2142,9 @@ static void Ogg_ReadTheoraHeader( demux_t *p_demux, logical_stream_t *p_stream,
{ {
p_stream->i_keyframe_offset = 1; 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 ) ogg_packet *p_oggpacket )
{ {
oggpack_buffer opb; oggpack_buffer opb;
...@@ -2175,13 +2166,6 @@ static void Ogg_ReadVorbisHeader( demux_t *p_demux, logical_stream_t *p_stream, ...@@ -2175,13 +2166,6 @@ static void Ogg_ReadVorbisHeader( demux_t *p_demux, logical_stream_t *p_stream,
oggpack_read( &opb, 32 ); oggpack_read( &opb, 32 );
oggpack_adv( &opb, 32 ); oggpack_adv( &opb, 32 );
p_stream->fmt.i_bitrate = oggpack_read( &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, static void Ogg_ReadSpeexHeader( logical_stream_t *p_stream,
...@@ -2214,8 +2198,7 @@ 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 */ p_stream->i_extra_headers_packets = oggpack_read( &opb, 32 ); /* extra_headers */
} }
static void Ogg_ReadOpusHeader( demux_t *p_demux, static void Ogg_ReadOpusHeader( logical_stream_t *p_stream,
logical_stream_t *p_stream,
ogg_packet *p_oggpacket ) ogg_packet *p_oggpacket )
{ {
oggpack_buffer opb; oggpack_buffer opb;
...@@ -2240,13 +2223,6 @@ static void Ogg_ReadOpusHeader( demux_t *p_demux, ...@@ -2240,13 +2223,6 @@ static void Ogg_ReadOpusHeader( demux_t *p_demux,
p_stream->fmt.audio.i_channels = oggpack_read( &opb, 8 ); p_stream->fmt.audio.i_channels = oggpack_read( &opb, 8 );
fill_channels_info(&p_stream->fmt.audio); fill_channels_info(&p_stream->fmt.audio);
p_stream->i_pre_skip = oggpack_read( &opb, 16 ); 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, 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, ...@@ -2359,7 +2335,7 @@ static void Ogg_ReadAnnodexHeader( demux_t *p_demux,
uint64_t timebase_numerator; uint64_t timebase_numerator;
uint64_t timebase_denominator; 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_readinit( &opb, p_oggpacket->packet, p_oggpacket->bytes);
oggpack_adv( &opb, 8*8 ); /* "Annodex\0" header */ oggpack_adv( &opb, 8*8 ); /* "Annodex\0" header */
......
...@@ -36,6 +36,8 @@ ...@@ -36,6 +36,8 @@
#include <ogg/ogg.h> #include <ogg/ogg.h>
#include <limits.h> #include <limits.h>
#include <assert.h>
#include "ogg.h" #include "ogg.h"
#include "oggseek.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 ...@@ -460,68 +462,96 @@ static int64_t find_first_page( demux_t *p_demux, int64_t i_pos1, int64_t i_pos2
} }
} }
void Oggseek_ProbeEnd( demux_t *p_demux )
/* 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)
{ {
/* 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; const char *buffer;
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;
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; /* Try to lookup last granule from each logical stream */
i_pos2 = p_sys->i_total_length; 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 */ i_granule = ogg_page_granulepos( &page );
if ( i_last_frame >= 0 ) if ( i_granule == -1 ) continue;
for ( int i=0; i< p_sys->i_streams; i++ )
{ {
/* No more pages in range -> return last one */ if ( p_sys->pp_stream[i]->i_serial_no != ogg_page_serialno( &page ) )
return i_last_frame; 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 */ /* Otherwise increase read size, starting earlier */
i_pos2 -= i_start_pos; if ( i_backoffset <= ( UINT_MAX >> 1 ) )
i_start_pos -= OGGSEEK_BYTES_TO_READ; {
if ( i_start_pos < i_pos1 ) i_start_pos = i_pos1; i_backoffset <<= 1;
i_pos2 += i_start_pos; i_startpos = i_upperbound - i_backoffset;
} }
else else
{ {
/* found a page, see if we can find another one */ i_startpos -= i_backoffset;
i_last_frame = i_frame;
i_start_pos = i_page_pos + 1;
}
} }
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 */ /* convert a theora frame to a granulepos */
...@@ -1238,67 +1268,12 @@ static int64_t OggBisectSearchByTime( demux_t *p_demux, logical_stream_t *p_stre ...@@ -1238,67 +1268,12 @@ static int64_t OggBisectSearchByTime( demux_t *p_demux, logical_stream_t *p_stre
return bestlower.i_pos; 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 * 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, int Oggseek_BlindSeektoPosition( demux_t *p_demux, logical_stream_t *p_stream,
double f, bool b_canfastseek ) double f, bool b_canfastseek )
{ {
......
...@@ -58,6 +58,7 @@ bool Oggseek_PacketPCRFixup ( logical_stream_t *p_stream, ogg_page *, ogg_pac ...@@ -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_BlindSeektoPosition ( demux_t *, logical_stream_t *, double f, bool );
int Oggseek_SeektoAbsolutetime ( demux_t *, logical_stream_t *, int64_t i_granulepos ); 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 ); 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 *, const demux_index_entry_t *oggseek_theora_index_entry_add ( logical_stream_t *,
int64_t i_granule, int64_t i_granule,
...@@ -65,8 +66,6 @@ const demux_index_entry_t *oggseek_theora_index_entry_add ( logical_stream_t *, ...@@ -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 * ); 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 ); int oggseek_find_frame ( demux_t *, logical_stream_t *, int64_t i_tframe );
int64_t oggseek_read_page ( demux_t * ); 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