* adapted from: http://lives.svn.sourceforge.net/viewvc/lives/trunk/lives-plugins
* /plugins/decoders/ogg_theora_decoder.c
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * 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 General Public License for more details.
+ * 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 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.
+ * 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.
*****************************************************************************/
/*****************************************************************************
}
-/* unlink and free idx. If idx is head of list, return new head */
-
-static demux_index_entry_t *index_entry_delete( demux_index_entry_t *idx )
-{
- demux_index_entry_t *xidx = idx;
-
- if ( idx->p_prev != NULL ) idx->p_prev->p_next = idx->p_next;
- else xidx = idx->p_next;
-
- if ( idx->p_next != NULL ) idx->p_next->p_prev = idx->p_prev;
- free( idx );
-
- return xidx;
-}
-
-
/* internal function to create a new list member */
static demux_index_entry_t *index_entry_new( void )
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;
}
-/* get highest frame in theora stream */
+/* get highest frame in theora and opus streams */
-static int64_t find_last_theora_frame ( demux_t *p_demux, logical_stream_t *p_stream )
+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 );
seek_byte( p_demux, 0 );
/* Reset stream states */
- p_stream->i_serial_no = ogg_page_serialno( &p_demux->p_sys->current_page );
- ogg_stream_init( &p_stream->os, p_stream->i_serial_no );
- ogg_stream_pagein( &p_stream->os, &p_demux->p_sys->current_page );
+ p_sys->i_streams = 0;
+ ogg_stream_clear( &p_stream->os );
+ memcpy( &p_stream->os, &os, sizeof(os) );
return i_frame;
}
-/* 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, 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 )
+ 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 = find_last_theora_frame ( p_demux, p_stream );
+ i_frame = get_last_frame ( p_demux, p_stream );
if ( i_frame < 0 ) return -1;
return i_frame;
i_tframe += p_stream->i_keyframe_offset;
+ i_cframe = i_tframe;
+ /* For Opus, seek back 80 ms before the target playback position. */
+ if ( p_stream->fmt.i_codec == VLC_CODEC_OPUS )
+ {
+ if ( i_tframe <= p_stream->i_pre_skip )
+ i_cframe = 0;
+ else if ( i_tframe < 80*48 )
+ i_cframe = 0;
+ else
+ i_cframe = i_tframe - 80*48;
+ }
+
/* reduce the search domain */
- fidx = get_bounds_for( p_stream, i_tframe, &i_pos_lower, &i_pos_upper );
+ fidx = get_bounds_for( p_stream, i_cframe, &i_pos_lower, &i_pos_upper );
if ( fidx == NULL )
{
- /* no exact match found; search the domain for highest keyframe <= i_tframe */
+ /* no exact match found; search the domain for highest keyframe <= i_cframe */
- i_granulepos = ogg_seek ( p_demux, p_stream, i_tframe, i_pos_lower, i_pos_upper,
+ i_granulepos = ogg_seek ( p_demux, p_stream, i_cframe, i_pos_lower, i_pos_upper,
&i_pagepos, true );
if ( i_granulepos == -1 )
{