decoder/Opus: implement seeking
This commit is contained in:
parent
f0060718de
commit
c76952534e
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user