decoder/Opus: implement seeking

This commit is contained in:
Max Kellermann 2013-01-07 23:05:33 +01:00
parent f0060718de
commit c76952534e
3 changed files with 49 additions and 6 deletions

View File

@ -20,7 +20,6 @@
#include "config.h" #include "config.h"
#include "OggFind.hxx" #include "OggFind.hxx"
#include "OggSyncState.hxx" #include "OggSyncState.hxx"
#include "InputStream.hxx"
#include "util/Error.hxx" #include "util/Error.hxx"
bool bool
@ -38,7 +37,7 @@ OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet)
} }
} }
static bool bool
OggSeekPageAtOffset(OggSyncState &oy, ogg_stream_state &os, InputStream &is, OggSeekPageAtOffset(OggSyncState &oy, ogg_stream_state &os, InputStream &is,
InputStream::offset_type offset, int whence) InputStream::offset_type offset, int whence)
{ {

View File

@ -21,6 +21,7 @@
#define MPD_OGG_FIND_HXX #define MPD_OGG_FIND_HXX
#include "check.h" #include "check.h"
#include "InputStream.hxx"
#include <ogg/ogg.h> #include <ogg/ogg.h>
@ -36,6 +37,13 @@ class OggSyncState;
bool bool
OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet); OggFindEOS(OggSyncState &oy, ogg_stream_state &os, ogg_packet &packet);
/**
* Seek the #InputStream and find the next Ogg page.
*/
bool
OggSeekPageAtOffset(OggSyncState &oy, ogg_stream_state &os, InputStream &is,
InputStream::offset_type offset, int whence);
/** /**
* Try to find the end-of-stream (EOS) packet. Seek to the end of the * Try to find the end-of-stream (EOS) packet. Seek to the end of the
* file if necessary. * file if necessary.

View File

@ -80,6 +80,8 @@ class MPDOpusDecoder {
int opus_serialno; int opus_serialno;
ogg_int64_t eos_granulepos;
size_t frame_size; size_t frame_size;
public: public:
@ -99,6 +101,8 @@ public:
DecoderCommand HandleBOS(const ogg_packet &packet); DecoderCommand HandleBOS(const ogg_packet &packet);
DecoderCommand HandleTags(const ogg_packet &packet); DecoderCommand HandleTags(const ogg_packet &packet);
DecoderCommand HandleAudio(const ogg_packet &packet); DecoderCommand HandleAudio(const ogg_packet &packet);
bool Seek(OggSyncState &oy, double where);
}; };
MPDOpusDecoder::~MPDOpusDecoder() MPDOpusDecoder::~MPDOpusDecoder()
@ -252,15 +256,16 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet)
return DecoderCommand::STOP; return DecoderCommand::STOP;
} }
const ogg_int64_t eos_granulepos = eos_granulepos = LoadEOSGranulePos(input_stream, &decoder,
LoadEOSGranulePos(input_stream, &decoder, opus_serialno); opus_serialno);
const double duration = eos_granulepos >= 0 const double duration = eos_granulepos >= 0
? double(eos_granulepos) / opus_sample_rate ? double(eos_granulepos) / opus_sample_rate
: -1.0; : -1.0;
const AudioFormat audio_format(opus_sample_rate, const AudioFormat audio_format(opus_sample_rate,
SampleFormat::S16, channels); SampleFormat::S16, channels);
decoder_initialized(decoder, audio_format, false, duration); decoder_initialized(decoder, audio_format,
eos_granulepos > 0, duration);
frame_size = audio_format.GetFrameSize(); frame_size = audio_format.GetFrameSize();
/* allocate an output buffer for 16 bit PCM samples big enough /* allocate an output buffer for 16 bit PCM samples big enough
@ -324,6 +329,29 @@ MPDOpusDecoder::HandleAudio(const ogg_packet &packet)
return DecoderCommand::NONE; return DecoderCommand::NONE;
} }
bool
MPDOpusDecoder::Seek(OggSyncState &oy, double where_s)
{
assert(eos_granulepos > 0);
assert(input_stream.seekable);
assert(input_stream.size > 0);
assert(input_stream.offset >= 0);
const ogg_int64_t where_granulepos(where_s * opus_sample_rate);
/* interpolate the file offset where we expect to find the
given granule position */
/* TODO: implement binary search */
InputStream::offset_type offset(where_granulepos * input_stream.size
/ eos_granulepos);
if (!OggSeekPageAtOffset(oy, os, input_stream, offset, SEEK_SET))
return false;
decoder_timestamp(decoder, where_s);
return true;
}
static void static void
mpd_opus_stream_decode(Decoder &decoder, mpd_opus_stream_decode(Decoder &decoder,
InputStream &input_stream) InputStream &input_stream)
@ -343,12 +371,20 @@ mpd_opus_stream_decode(Decoder &decoder,
while (true) { while (true) {
auto cmd = d.HandlePackets(); auto cmd = d.HandlePackets();
if (cmd == DecoderCommand::SEEK) {
if (d.Seek(oy, decoder_seek_where(decoder)))
decoder_command_finished(decoder);
else
decoder_seek_error(decoder);
continue;
}
if (cmd != DecoderCommand::NONE) if (cmd != DecoderCommand::NONE)
break; break;
if (!d.ReadNextPage(oy)) if (!d.ReadNextPage(oy))
break; break;
} }
} }