Commit 0ba592f9 authored by G Finch's avatar G Finch Committed by Ilkka Ollakka

New-ogg-seek-logic-theora-specific-changes

Signed-off-by: default avatarIlkka Ollakka <ileoo@videolan.org>
parent eea1cddc
...@@ -86,6 +86,96 @@ static demux_index_entry_t *index_entry_new( void ) ...@@ -86,6 +86,96 @@ static demux_index_entry_t *index_entry_new( void )
/* 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;
}
/********************************************************************* /*********************************************************************
* private functions * private functions
...@@ -144,6 +234,149 @@ static int64_t get_data( demux_t *p_demux, int64_t i_bytes_to_read ) ...@@ -144,6 +234,149 @@ static int64_t get_data( demux_t *p_demux, int64_t i_bytes_to_read )
/* Find the first first ogg page for p_stream between offsets i_pos1 and i_pos2,
return file offset in bytes; -1 is returned on failure */
static int64_t find_first_page( demux_t *p_demux, int64_t i_pos1, int64_t i_pos2,
logical_stream_t *p_stream,
int64_t *pi_kframe, int64_t *pi_frame )
{
int64_t i_result;
int64_t i_granulepos;
int64_t i_bytes_to_read = i_pos2 - i_pos1 + 1;
int64_t i_bytes_read;
int64_t i_pages_checked = 0;
int64_t i_packets_checked;
demux_sys_t *p_sys = p_demux->p_sys;
ogg_packet op;
seek_byte( p_demux, i_pos1 );
if ( i_pos1 == p_stream->i_data_start )
{
/* set a dummy granulepos at data_start */
*pi_kframe = p_stream->i_keyframe_offset;
*pi_frame = p_stream->i_keyframe_offset;
p_sys->b_page_waiting = true;
return p_sys->i_input_position;
}
if ( i_bytes_to_read > OGGSEEK_BYTES_TO_READ ) i_bytes_to_read = OGGSEEK_BYTES_TO_READ;
while ( 1 )
{
if ( p_sys->i_input_position >= i_pos2 )
{
/* we reached the end and found no pages */
*pi_frame=-1;
return -1;
}
/* read next chunk */
if ( ! ( i_bytes_read = get_data( p_demux, i_bytes_to_read ) ) )
{
/* EOF */
*pi_frame = -1;
return -1;
}
i_bytes_to_read = OGGSEEK_BYTES_TO_READ;
i_result = ogg_sync_pageseek( &p_sys->oy, &p_sys->current_page );
if ( i_result < 0 )
{
/* found a page, sync to page start */
p_sys->i_input_position -= i_result;
i_pos1 = p_sys->i_input_position;
continue;
}
if ( i_result > 0 || ( i_result == 0 && p_sys->oy.fill > 3 &&
! strncmp( (char *)p_sys->oy.data, "OggS" , 4 ) ) )
{
i_pos1 = p_sys->i_input_position;
break;
}
p_sys->i_input_position += i_bytes_read;
};
seek_byte( p_demux, p_sys->i_input_position );
ogg_stream_reset( &p_stream->os );
while( 1 )
{
if ( p_sys->i_input_position >= i_pos2 )
{
/* reached the end of the search region and nothing was found */
*pi_frame = -1;
return p_sys->i_input_position;
}
p_sys->b_page_waiting = false;
if ( ! ( i_result = oggseek_read_page( p_demux ) ) )
{
/* EOF */
*pi_frame = -1;
return p_sys->i_input_position;
}
// found a page
if ( p_stream->os.serialno != ogg_page_serialno( &p_sys->current_page ) )
{
/* page is not for this stream */
p_sys->i_input_position += i_result;
if ( ! i_pages_checked ) i_pos1 = p_sys->i_input_position;
continue;
}
ogg_stream_pagein( &p_stream->os, &p_sys->current_page );
i_pages_checked++;
i_packets_checked = 0;
if ( ogg_stream_packetout( &p_stream->os, &op ) > 0 )
{
i_packets_checked++;
}
if ( i_packets_checked )
{
i_granulepos = ogg_page_granulepos( &p_sys->current_page );
oggseek_theora_index_entry_add( p_stream, i_granulepos, i_pos1 );
*pi_kframe =
i_granulepos >> p_stream->i_granule_shift;
*pi_frame = *pi_kframe +
i_granulepos - ( *pi_kframe << p_stream->i_granule_shift );
p_sys->b_page_waiting = true;
return i_pos1;
}
/* -> start of next page */
p_sys->i_input_position += i_result;
}
}
/* Find the last frame for p_stream, /* Find the last frame for p_stream,
-1 is returned on failure */ -1 is returned on failure */
...@@ -151,12 +384,81 @@ static int64_t get_data( demux_t *p_demux, int64_t i_bytes_to_read ) ...@@ -151,12 +384,81 @@ static int64_t get_data( demux_t *p_demux, int64_t i_bytes_to_read )
static int64_t find_last_frame (demux_t *p_demux, logical_stream_t *p_stream) static int64_t find_last_frame (demux_t *p_demux, logical_stream_t *p_stream)
{ {
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;
int64_t i_serialno;
demux_sys_t *p_sys = p_demux->p_sys;
i_pos1 = p_stream->i_data_start;
i_pos2 = p_sys->i_total_length;
i_serialno = p_stream->os.serialno;
i_start_pos = i_pos2 - OGGSEEK_BYTES_TO_READ;
while( 1 )
{
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 );
if ( i_frame == -1 )
{
/* no pages found in range */
if ( i_last_frame >= 0 )
{
/* No more pages in range -> return last one */
return i_last_frame;
}
if ( i_start_pos <= i_pos1 )
{
return -1;
}
/* 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;
}
else
{
/* found a page, see if we can find another one */
i_last_frame = i_frame;
i_start_pos = i_page_pos + 1;
}
}
return -1; return -1;
} }
/* 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;
}
/* seek to a suitable point to begin decoding for i_tframe. We can pre-set bounding positions /* seek to a suitable point to begin decoding for i_tframe. We can pre-set bounding positions
i_pos_lower and i_pos_higher to narrow the search domain. */ i_pos_lower and i_pos_higher to narrow the search domain. */
...@@ -203,6 +505,130 @@ static int64_t ogg_seek( demux_t *p_demux, logical_stream_t *p_stream, int64_t i ...@@ -203,6 +505,130 @@ static int64_t ogg_seek( demux_t *p_demux, logical_stream_t *p_stream, int64_t i
* *
*/ */
int64_t i_start_pos;
int64_t i_end_pos;
int64_t i_pagepos;
int64_t i_segsize;
int64_t i_frame;
int64_t i_kframe;
int64_t i_best_kframe = -1;
int64_t i_best_frame = -1;
int64_t i_best_pagepos = -1;
demux_sys_t *p_sys = p_demux->p_sys;
if ( i_tframe < p_stream->i_keyframe_offset )
{
*pi_pagepos = p_stream->i_data_start;
if ( ! b_exact ) {
seek_byte( p_demux, p_stream->i_data_start );
return frame_to_gpos( p_stream, p_stream->i_keyframe_offset, 1 );
}
return frame_to_gpos( p_stream, p_stream->i_keyframe_offset, 0 );
}
if ( i_pos_lower < p_stream->i_data_start )
{
i_pos_lower = p_stream->i_data_start;
}
if ( i_pos_upper < 0 )
{
i_pos_upper = p_sys->i_total_length;
}
if ( i_pos_upper > p_sys->i_total_length )
{
i_pos_upper = p_sys->i_total_length;
}
i_start_pos = i_pos_lower;
i_end_pos = i_pos_upper;
i_segsize = ( i_end_pos - i_start_pos + 1 ) >> 1;
do
{
/* see if the frame lies in current segment */
if ( i_start_pos < i_pos_lower )
{
i_start_pos = i_pos_lower;
}
if ( i_end_pos > i_pos_upper )
{
i_end_pos = i_pos_upper;
}
if ( i_start_pos >= i_end_pos )
{
if ( i_start_pos == i_pos_lower)
{
if ( ! b_exact ) seek_byte( p_demux, i_start_pos );
*pi_pagepos = i_start_pos;
return frame_to_gpos( p_stream, p_stream->i_keyframe_offset, 1 );
}
break;
}
if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
{
i_pagepos = find_first_page( p_demux, i_start_pos, i_end_pos, p_stream,
&i_kframe, &i_frame );
}
else return -1;
if ( i_pagepos != -1 && i_kframe != -1 )
{
/* found a page */
if ( b_exact && i_frame >= i_tframe && i_kframe <= i_tframe )
{
/* got it ! */
*pi_pagepos = i_start_pos;
return frame_to_gpos( p_stream, i_kframe, i_frame );
}
if ( ( i_kframe < i_tframe || ( b_exact && i_kframe == i_tframe ) )
&& i_kframe > i_best_kframe )
{
i_best_kframe = i_kframe;
i_best_frame = i_frame;
i_best_pagepos = i_pagepos;
}
if ( i_frame >= i_tframe )
{
/* check lower half of segment */
i_start_pos -= i_segsize;
i_end_pos -= i_segsize;
}
else i_start_pos = i_pagepos;
}
else
{
/* no keyframe found, check lower segment */
i_end_pos -= i_segsize;
i_start_pos -= i_segsize;
}
i_segsize = ( i_end_pos - i_start_pos + 1 ) >> 1;
i_start_pos += i_segsize;
} while ( i_segsize > 64 );
if ( i_best_kframe >- 1 )
{
if ( !b_exact )
{
seek_byte( p_demux, i_best_pagepos );
}
*pi_pagepos = i_best_pagepos;
return frame_to_gpos( p_stream, i_best_kframe, i_best_frame );
}
return -1; return -1;
} }
...@@ -217,21 +643,97 @@ static int64_t ogg_seek( demux_t *p_demux, logical_stream_t *p_stream, int64_t i ...@@ -217,21 +643,97 @@ static int64_t ogg_seek( demux_t *p_demux, logical_stream_t *p_stream, int64_t i
static demux_index_entry_t *get_bounds_for ( logical_stream_t *p_stream, int64_t i_tframe, static demux_index_entry_t *get_bounds_for ( logical_stream_t *p_stream, int64_t i_tframe,
int64_t *pi_pos_lower, int64_t *pi_pos_upper) int64_t *pi_pos_lower, int64_t *pi_pos_upper)
{ {
int64_t i_kframe;
int64_t i_frame;
int64_t i_gpos;
demux_index_entry_t *idx = p_stream->idx;
*pi_pos_lower = *pi_pos_upper = -1;
while ( idx != NULL )
{
if ( idx-> i_pagepos < 0 )
{
/* kframe was found to be invalid */
idx = idx->p_next;
continue;
}
if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
{
i_gpos = idx->i_value;
i_kframe = i_gpos >> p_stream->i_granule_shift;
i_frame = i_kframe + i_gpos - ( i_kframe << p_stream->i_granule_shift );
}
else return NULL;
if ( i_kframe > i_tframe )
{
*pi_pos_upper = idx->i_pagepos;
return NULL;
}
if ( i_frame < i_tframe )
{
if ( pi_pos_lower != NULL )
{
*pi_pos_lower = idx->i_pagepos;
idx = idx->p_next;
continue;
}
}
return idx;
}
return NULL; return NULL;
} }
/* get highest frame in theora stream */
static int64_t find_last_theora_frame ( demux_t *p_demux, logical_stream_t *p_stream )
{
int64_t i_frame;
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 );
return i_frame;
}
/************************************************************************ /************************************************************************
* public functions * public functions
*************************************************************************/ *************************************************************************/
/* return highest frame number for p_stream (which must be a theora or dirac video stream) */ /* return highest frame number for p_stream (which must be a theora or dirac video stream) */
int64_t oggseek_get_last_frame ( demux_t *p_demux, logical_stream_t *p_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 )
{
i_frame = find_last_theora_frame ( p_demux, p_stream );
if ( i_frame < 0 ) return -1;
return i_frame;
}
/* unhandled video format */ /* unhandled video format */
return -1; return -1;
...@@ -292,8 +794,44 @@ int oggseek_find_frame ( demux_t *p_demux, logical_stream_t *p_stream, int64_t i ...@@ -292,8 +794,44 @@ int oggseek_find_frame ( demux_t *p_demux, logical_stream_t *p_stream, int64_t i
i_granulepos = fidx->i_value; i_granulepos = fidx->i_value;
} }
return VLC_EGENERIC; if ( p_stream->fmt.i_codec == VLC_CODEC_THEORA )
{
i_kframe = i_granulepos >> p_stream->i_granule_shift;
if ( i_kframe < p_stream->i_keyframe_offset )
{
i_kframe = p_stream->i_keyframe_offset;
}
/* we found a keyframe, but we don't know where its packet starts, so search for a
frame just before it */
/* reduce search domain */
get_bounds_for( p_stream, i_kframe-1, &i_pos_lower, &i_pos_upper );
i_granulepos = ogg_seek( p_demux, p_stream, i_kframe-1, i_pos_lower, i_pos_upper,
&i_pagepos, false );
/* i_cframe will be the next frame we decode */
i_xkframe = i_granulepos >> p_stream->i_granule_shift;
i_cframe = i_xkframe + i_granulepos - ( i_xkframe << p_stream->i_granule_shift) + 1;
if ( p_sys->i_input_position == p_stream->i_data_start )
{
i_cframe = i_kframe = p_stream->i_keyframe_offset;
}
else
{
oggseek_theora_index_entry_add( p_stream, i_granulepos, p_sys->i_input_position );
}
}
else return VLC_EGENERIC;
p_stream->i_skip_frames = i_tframe - i_cframe;
ogg_stream_reset( &p_stream->os );
return VLC_SUCCESS;
} }
......
...@@ -52,9 +52,14 @@ struct oggseek_index_entry ...@@ -52,9 +52,14 @@ struct oggseek_index_entry
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 * ); void oggseek_index_entries_free ( demux_index_entry_t * );
int64_t oggseek_get_last_frame ( demux_t *, logical_stream_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 * );
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