Commit 4654ab23 authored by G Finch's avatar G Finch Committed by Ilkka Ollakka

Ogg-seek-new-logic-generic-changes

Signed-off-by: default avatarIlkka Ollakka <ileoo@videolan.org>
parent f90b7fec
SUBDIRS = asf avformat avi mkv mp4 mpeg playlist
SOURCES_flacsys = flac.c
SOURCES_ogg = ogg.c ogg.h vorbis.h kate_categories.c kate_categories.h xiph.h
SOURCES_ogg = ogg.c ogg.h oggseek.c oggseek.h vorbis.h kate_categories.c \
kate_categories.h xiph.h
SOURCES_demuxdump = demuxdump.c
SOURCES_rawdv = rawdv.c
SOURCES_rawvid = rawvid.c
......
......@@ -43,6 +43,7 @@
#include "vorbis.h"
#include "kate_categories.h"
#include "ogg.h"
#include "oggseek.h"
/*****************************************************************************
* Module descriptor
......@@ -217,6 +218,7 @@ static int Demux( demux_t * p_demux )
ogg_page oggpage;
ogg_packet oggpacket;
int i_stream;
bool b_skipping = false;
if( p_sys->i_eos == p_sys->i_streams )
......@@ -290,9 +292,12 @@ static int Demux( demux_t * p_demux )
}
if( ogg_stream_pagein( &p_stream->os, &oggpage ) != 0 )
{
continue;
}
}
while( ogg_stream_packetout( &p_stream->os, &oggpacket ) > 0 )
{
/* Read info from any secondary header packets, if there are any */
......@@ -316,8 +321,17 @@ static int Demux( demux_t * p_demux )
{
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
......@@ -376,7 +390,7 @@ static int Demux( demux_t * p_demux )
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;
......@@ -729,6 +743,15 @@ static void Ogg_DecodePacket( demux_t *p_demux,
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;
......@@ -759,6 +782,8 @@ static void Ogg_DecodePacket( demux_t *p_demux,
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;
}
......@@ -842,6 +867,10 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
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 ) )
......@@ -864,6 +893,9 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux )
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 );
......@@ -1314,6 +1346,9 @@ static int Ogg_BeginningOfStream( demux_t *p_demux )
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 &&
......@@ -1359,6 +1394,11 @@ static int Ogg_BeginningOfStream( demux_t *p_demux )
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;
}
......@@ -1399,6 +1439,11 @@ static void Ogg_LogicalStreamDelete( demux_t *p_demux, logical_stream_t *p_strea
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 );
}
/**
......
......@@ -69,6 +69,15 @@ typedef struct logical_stream_s
/* offset of first keyframe for theora; can be 0 or 1 depending on version number */
int64_t i_keyframe_offset;
/* keyframe index for seeking, created as we discover keyframes */
demux_index_entry_t *idx;
/* skip some frames after a seek */
int i_skip_frames;
/* data start offset (absolute) in bytes */
int64_t i_data_start;
/* kate streams have the number of headers in the ID header */
int i_kate_num_headers;
......@@ -105,6 +114,18 @@ struct demux_sys_t
/* after reading all headers, the first data page is stuffed into the relevant stream, ready to use */
bool b_page_waiting;
/* count of total frames in video stream */
int64_t i_total_frames;
/* length of file in bytes */
int64_t i_total_length;
/* offset position in file (for reading) */
int64_t i_input_position;
/* current page being parsed */
ogg_page current_page;
mtime_t i_st_pts;
......
/*****************************************************************************
* 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;
}
/*****************************************************************************
* 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 );
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