#include "vorbis.h"
#include "kate_categories.h"
#include "ogg.h"
+#include "oggseek.h"
/*****************************************************************************
* Module descriptor
ogg_page oggpage;
ogg_packet oggpacket;
int i_stream;
+ bool b_skipping = false;
if( p_sys->i_eos == p_sys->i_streams )
}
if( ogg_stream_pagein( &p_stream->os, &oggpage ) != 0 )
+ {
continue;
+ }
+
}
while( ogg_stream_packetout( &p_stream->os, &oggpacket ) > 0 )
{
p_stream->i_secondary_header_packets = 0;
}
+
+ /* update start of data pointer */
+ p_stream->i_data_start = stream_Tell( p_demux->s );
+
}
+ /* If any streams have i_skip_frames, only decode (pre-roll)
+ * for those streams */
+ if ( b_skipping && p_stream->i_skip_frames == 0 ) continue;
+
+
if( p_stream->b_reinit )
{
/* If synchro is re-initialized we need to drop all the packets
p_sys->i_pcr = p_stream->i_interpolated_pcr;
}
- if( p_sys->i_pcr >= 0 )
+ if( p_sys->i_pcr >= 0 && ! b_skipping )
es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_sys->i_pcr );
return 1;
if( !( p_block = block_New( p_demux, p_oggpacket->bytes ) ) ) return;
+
+ /* may need to preroll video frames after a seek */
+ if ( p_stream->i_skip_frames > 0 )
+ {
+ p_block->i_flags |= BLOCK_FLAG_PREROLL;
+ p_stream->i_skip_frames--;
+ }
+
+
/* Normalize PTS */
if( i_pts == 0 ) i_pts = VLC_TS_0;
else if( i_pts == -1 && i_interpolated_pts == 0 ) i_pts = VLC_TS_0;
p_block->i_dts = p_stream->i_pcr;
p_block->i_pts = VLC_TS_INVALID;
/* NB, OggDirac granulepos values are in units of 2*picturerate */
+
+ /* granulepos for dirac is possibly broken, this value should be ignored */
if( -1 != p_oggpacket->granulepos )
p_block->i_pts = u_pnum * INT64_C(1000000) / p_stream->f_rate / 2;
}
ogg_page oggpage;
int i_stream;
+ p_ogg->i_total_length = stream_Size ( p_demux->s );
+ msg_Dbg( p_demux, "File length is %"PRId64" bytes", p_ogg->i_total_length );
+
+
while( Ogg_ReadPage( p_demux, &oggpage ) == VLC_SUCCESS )
{
if( ogg_page_bos( &oggpage ) )
p_stream->i_secondary_header_packets = 0;
p_stream->i_keyframe_offset = 0;
+ p_stream->i_skip_frames = 0;
+
+ p_stream->i_data_start = 0;
es_format_Init( &p_stream->fmt, 0, 0 );
es_format_Init( &p_stream->fmt_old, 0, 0 );
p_stream->p_es = NULL;
+ /* initialise kframe index */
+ p_stream->idx=NULL;
+
/* Try first to reuse an old ES */
if( p_old_stream &&
p_old_stream->fmt.i_cat == p_stream->fmt.i_cat &&
Ogg_LogicalStreamDelete( p_demux, p_ogg->p_old_stream );
p_ogg->p_old_stream = NULL;
}
+
+
+ /* get total frame count for video stream; we will need this for seeking */
+ p_ogg->i_total_frames = 0;
+
return VLC_SUCCESS;
}
es_format_Clean( &p_stream->fmt_old );
es_format_Clean( &p_stream->fmt );
+ if ( p_stream->idx != NULL)
+ {
+ oggseek_index_entries_free( p_stream->idx );
+ }
+
free( p_stream );
}
/**
--- /dev/null
+/*****************************************************************************
+ * oggseek.c : ogg seeking functions for ogg demuxer vlc
+ *****************************************************************************
+ * Copyright (C) 2008 - 2010 Gabriel Finch <salsaman@gmail.com>
+ *
+ * Authors: Gabriel Finch <salsaman@gmail.com>
+ * 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
+ * (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.
+ *
+ * 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_demux.h>
+
+#include <ogg/ogg.h>
+
+#include "ogg.h"
+#include "oggseek.h"
+
+
+/************************************************************
+* index entries
+*************************************************************/
+
+/* free all entries in index list */
+
+void oggseek_index_entries_free ( demux_index_entry_t *idx )
+{
+ demux_index_entry_t *idx_next;
+
+ while ( idx != NULL )
+ {
+ idx_next = idx->p_next;
+ free( idx );
+ idx = idx_next;
+ }
+}
+
+
+/* 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 )
+{
+ demux_index_entry_t *idx = (demux_index_entry_t *)malloc( sizeof( demux_index_entry_t ) );
+ idx->p_next = idx->p_prev = NULL;
+ idx->i_pagepos_end = -1;
+ return idx;
+}
+
+
+
+
+/*********************************************************************
+ * private functions
+ **********************************************************************/
+
+/* seek in ogg file to offset i_pos and update the sync */
+
+static void seek_byte( demux_t *p_demux, int64_t i_pos )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ if ( ! stream_Seek( p_demux->s, i_pos ) )
+ {
+ ogg_sync_reset( &p_sys->oy );
+
+ p_sys->i_input_position = i_pos;
+ p_sys->b_page_waiting = false;
+ }
+}
+
+
+
+/* read bytes from the ogg file to try to find a page start */
+
+static int64_t get_data( demux_t *p_demux, int64_t i_bytes_to_read )
+{
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ char *buf;
+ int64_t i_result;
+
+ if ( p_sys->i_total_length > 0 )
+ {
+ if ( p_sys->i_input_position + i_bytes_to_read > p_sys->i_total_length )
+ {
+ i_bytes_to_read = p_sys->i_total_length - p_sys->i_input_position;
+ if ( i_bytes_to_read <= 0 ) {
+ return 0;
+ }
+ }
+ }
+
+ seek_byte ( p_demux, p_sys->i_input_position );
+
+ buf = ogg_sync_buffer( &p_sys->oy, i_bytes_to_read );
+
+ i_result = stream_Read( p_demux->s, buf, i_bytes_to_read );
+
+ p_sys->b_page_waiting = false;
+
+ ogg_sync_wrote( &p_sys->oy, i_result );
+ return i_result;
+}
+
+
+
+
+
+
+/* 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)
+{
+
+ return -1;
+}
+
+
+
+
+/* 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. */
+
+
+static int64_t ogg_seek( demux_t *p_demux, logical_stream_t *p_stream, int64_t i_tframe,
+ int64_t i_pos_lower, int64_t i_pos_upper, int64_t *pi_pagepos,
+ bool b_exact )
+{
+ /* For theora:
+ * We do two passes here, first with b_exact set, then with b_exact unset.
+ *
+ * If b_exact is set, we find the highest granulepos <= the target granulepos
+ * from this we extract an estimate of the keyframe (note that there could be other
+ * "hidden" keyframes between the found granulepos and the target).
+ *
+ * On the second pass we find the highest granulepos < target. This places us just before or
+ * at the start of the target keyframe.
+ *
+ * When we come to decode, we start from this second position, discarding any completed
+ * packets on that page, and read pages discarding packets until we get to the target frame.
+ *
+ * The function returns the granulepos which is found,
+ * sets the page offset in pi_pagepos. -1 is returned on error.
+ *
+ * for dirac:
+ *
+ * we find the highest sync frame <= target frame, and return the sync_frame number
+ * b_exact should be set to true
+ *
+ *
+ * the method used is bi-sections:
+ * - we check the lower keyframe
+ * if this is == target we return
+ * if > target, or we find no keyframes, we go to the lower segment
+ * if < target we divide the segment in two and check the upper half
+ *
+ * This is then repeated until the segment size is too small to hold a packet,
+ * at which point we return our best match
+ *
+ * Two optimisations are made: - anything we discover about keyframes is added to our index
+ * - before calling this function we get approximate bounds from the index
+ *
+ * therefore, subsequent searches become more rapid.
+ *
+ */
+
+
+ return -1;
+}
+
+
+
+
+
+
+/* find upper and lower pagepos for i_tframe; if we find an exact match, we return it */
+
+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)
+{
+
+ return NULL;
+}
+
+
+/************************************************************************
+ * public functions
+ *************************************************************************/
+
+
+
+/* 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 )
+{
+
+ /* unhandled video format */
+ return -1;
+}
+
+
+
+
+
+
+/* seek to target frame in p_stream; actually we will probably end up just before it
+ * (so we set skip)
+ *
+ * range for i_tframe is 0 -> p_sys->i_total_frames - 1
+ */
+
+int oggseek_find_frame ( demux_t *p_demux, logical_stream_t *p_stream, int64_t i_tframe )
+{
+
+ const demux_index_entry_t *fidx;
+
+ /* lower and upper bounds for search domain */
+ int64_t i_pos_lower;
+ int64_t i_pos_upper;
+
+ int64_t i_granulepos;
+ int64_t i_pagepos;
+
+ /* keyframe for i_tframe ( <= i_tframe ) */
+ int64_t i_kframe;
+
+ /* keyframe for i_kframe ( <= i_kframe ) */
+ int64_t i_xkframe;
+
+ /* next frame to be decoded ( >= i_xkframe ) */
+ int64_t i_cframe;
+
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ i_tframe += p_stream->i_keyframe_offset;
+
+ /* reduce the search domain */
+ fidx = get_bounds_for( p_stream, i_tframe, &i_pos_lower, &i_pos_upper );
+
+ if ( fidx == NULL )
+ {
+ /* no exact match found; search the domain for highest keyframe <= i_tframe */
+
+ i_granulepos = ogg_seek ( p_demux, p_stream, i_tframe, i_pos_lower, i_pos_upper,
+ &i_pagepos, true );
+ if ( i_granulepos == -1 )
+ {
+ return VLC_EGENERIC;
+ }
+
+ }
+ else {
+ i_granulepos = fidx->i_value;
+ }
+
+ return VLC_EGENERIC;
+
+}
+
+
+
+
+
+
+/****************************************************************************
+ * oggseek_read_page: Read a full Ogg page from the physical bitstream.
+ ****************************************************************************
+ * Returns number of bytes read. This should always be > 0
+ * unless we are at the end of stream.
+ *
+ ****************************************************************************/
+int64_t oggseek_read_page( demux_t *p_demux )
+{
+ demux_sys_t *p_ogg = p_demux->p_sys ;
+ uint8_t header[PAGE_HEADER_BYTES+255];
+ int i_nsegs;
+ int i_in_pos;
+ int i;
+ int64_t i_result;
+ int i_page_size;
+ char *buf;
+
+ demux_sys_t *p_sys = p_demux->p_sys;
+
+ /* store position of this page */
+ i_in_pos = p_ogg->i_input_position = stream_Tell( p_demux->s );
+
+ if ( p_sys->b_page_waiting) {
+ msg_Warn( p_demux, "Ogg page already loaded" );
+ return 0;
+ }
+
+ if ( stream_Read ( p_demux->s, header, PAGE_HEADER_BYTES ) < PAGE_HEADER_BYTES )
+ {
+ stream_Seek( p_demux->s, i_in_pos );
+ msg_Dbg ( p_demux, "Reached clean EOF in ogg file" );
+ return 0;
+ }
+
+ i_nsegs = header[ PAGE_HEADER_BYTES - 1 ];
+
+ if ( stream_Read ( p_demux->s, header+PAGE_HEADER_BYTES, i_nsegs ) < i_nsegs )
+ {
+ stream_Seek( p_demux->s, i_in_pos );
+ msg_Warn ( p_demux, "Reached broken EOF in ogg file" );
+ return 0;
+ }
+
+ i_page_size = PAGE_HEADER_BYTES + i_nsegs;
+
+ for ( i = 0; i < i_nsegs; i++ )
+ {
+ i_page_size += header[ PAGE_HEADER_BYTES + i ];
+ }
+
+ ogg_sync_reset( &p_ogg->oy );
+
+ buf = ogg_sync_buffer( &p_ogg->oy, i_page_size );
+
+ memcpy( buf, header, PAGE_HEADER_BYTES + i_nsegs );
+
+ i_result = stream_Read ( p_demux->s, (uint8_t*)buf + PAGE_HEADER_BYTES + i_nsegs,
+ i_page_size - PAGE_HEADER_BYTES - i_nsegs );
+
+ ogg_sync_wrote( &p_ogg->oy, i_result + PAGE_HEADER_BYTES + i_nsegs );
+
+
+
+
+ if ( ogg_sync_pageout( &p_ogg->oy, &p_ogg->current_page ) != 1 )
+ {
+ msg_Err( p_demux , "Got invalid packet, read %"PRId64" of %i: %s",i_result,i_page_size,
+ buf );
+ return 0;
+ }
+
+ p_sys->b_page_waiting = false;
+
+ return i_result + PAGE_HEADER_BYTES + i_nsegs;
+}
+
+
--- /dev/null
+/*****************************************************************************
+ * oggseek.h : ogg seeking functions for ogg demuxer vlc
+ *****************************************************************************
+ * Copyright (C) 2008 - 2010 Gabriel Finch <salsaman@gmail.com>
+ *
+ * Authors: Gabriel Finch <salsaman@gmail.com>
+ * 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
+ * (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.
+ *
+ * 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#define PAGE_HEADER_BYTES 27
+
+#define OGGSEEK_BYTES_TO_READ 8500
+
+/* index entries are structured as follows:
+ * - for theora, highest granulepos -> pagepos (bytes) where keyframe begins
+ * - for dirac, kframe (sync point) -> pagepos of sequence start (?)
+ */
+
+/* this is typedefed to demux_index_entry_t in ogg.h */
+struct oggseek_index_entry
+{
+ demux_index_entry_t *p_next;
+ demux_index_entry_t *p_prev;
+
+ /* value is highest granulepos for theora, sync frame for dirac */
+ int64_t i_value;
+ int64_t i_pagepos;
+
+ /* not used for theora because the granulepos tells us this */
+ int64_t i_pagepos_end;
+};
+
+
+
+
+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 );
+