decoder/Client: add DecoderCommand/seek virtual methods

This commit is contained in:
Max Kellermann 2016-11-18 08:15:07 +01:00
parent 66fb352cca
commit 47a0f46ce8
24 changed files with 156 additions and 172 deletions

View File

@ -21,7 +21,11 @@
#define MPD_DECODER_CLIENT_HXX
#include "check.h"
#include "DecoderCommand.hxx"
#include "Chrono.hxx"
#include "Compiler.h"
#include <stdint.h>
struct AudioFormat;
@ -42,6 +46,44 @@ public:
*/
virtual void Ready(AudioFormat audio_format,
bool seekable, SignedSongTime duration) = 0;
/**
* Determines the pending decoder command.
*
* @return the current command, or DecoderCommand::NONE if there is no
* command pending
*/
gcc_pure
virtual DecoderCommand GetCommand() = 0;
/**
* Called by the decoder when it has performed the requested command
* (dc->command). This function resets dc->command and wakes up the
* player thread.
*/
virtual void CommandFinished() = 0;
/**
* Call this when you have received the DecoderCommand::SEEK command.
*
* @return the destination position for the seek in milliseconds
*/
gcc_pure
virtual SongTime GetSeekTime() = 0;
/**
* Call this when you have received the DecoderCommand::SEEK command.
*
* @return the destination position for the seek in frames
*/
gcc_pure
virtual uint64_t GetSeekFrame() = 0;
/**
* Call this instead of CommandFinished() when seeking has
* failed.
*/
virtual void SeekError() = 0;
};
#endif

View File

@ -164,50 +164,45 @@ decoder_lock_get_virtual_command(DecoderClient &client)
}
DecoderCommand
decoder_get_command(DecoderClient &client)
Decoder::GetCommand()
{
auto &decoder = (Decoder &)client;
return decoder_lock_get_virtual_command(decoder);
return decoder_lock_get_virtual_command(*this);
}
void
decoder_command_finished(DecoderClient &client)
Decoder::CommandFinished()
{
auto &decoder = (Decoder &)client;
DecoderControl &dc = decoder.dc;
const ScopeLock protect(dc.mutex);
assert(dc.command != DecoderCommand::NONE ||
decoder.initial_seek_running);
assert(dc.command != DecoderCommand::NONE || initial_seek_running);
assert(dc.command != DecoderCommand::SEEK ||
decoder.initial_seek_running ||
dc.seek_error || decoder.seeking);
initial_seek_running ||
dc.seek_error || seeking);
assert(dc.pipe != nullptr);
if (decoder.initial_seek_running) {
assert(!decoder.seeking);
assert(decoder.current_chunk == nullptr);
if (initial_seek_running) {
assert(!seeking);
assert(current_chunk == nullptr);
assert(dc.pipe->IsEmpty());
decoder.initial_seek_running = false;
decoder.timestamp = dc.start_time.ToDoubleS();
initial_seek_running = false;
timestamp = dc.start_time.ToDoubleS();
return;
}
if (decoder.seeking) {
decoder.seeking = false;
if (seeking) {
seeking = false;
/* delete frames from the old song position */
if (decoder.current_chunk != nullptr) {
dc.buffer->Return(decoder.current_chunk);
decoder.current_chunk = nullptr;
if (current_chunk != nullptr) {
dc.buffer->Return(current_chunk);
current_chunk = nullptr;
}
dc.pipe->Clear(*dc.buffer);
decoder.timestamp = dc.seek_time.ToDoubleS();
timestamp = dc.seek_time.ToDoubleS();
}
dc.command = DecoderCommand::NONE;
@ -215,53 +210,44 @@ decoder_command_finished(DecoderClient &client)
}
SongTime
decoder_seek_time(DecoderClient &client)
Decoder::GetSeekTime()
{
auto &decoder = (Decoder &)client;
const DecoderControl &dc = decoder.dc;
assert(dc.pipe != nullptr);
if (decoder.initial_seek_running)
if (initial_seek_running)
return dc.start_time;
assert(dc.command == DecoderCommand::SEEK);
decoder.seeking = true;
seeking = true;
return dc.seek_time;
}
uint64_t
decoder_seek_where_frame(DecoderClient &client)
Decoder::GetSeekFrame()
{
auto &decoder = (Decoder &)client;
const DecoderControl &dc = decoder.dc;
return decoder_seek_time(decoder).ToScale<uint64_t>(dc.in_audio_format.sample_rate);
return GetSeekTime().ToScale<uint64_t>(dc.in_audio_format.sample_rate);
}
void
decoder_seek_error(DecoderClient &client)
Decoder::SeekError()
{
auto &decoder = (Decoder &)client;
DecoderControl &dc = decoder.dc;
assert(dc.pipe != nullptr);
if (decoder.initial_seek_running) {
if (initial_seek_running) {
/* d'oh, we can't seek to the sub-song start position,
what now? - no idea, ignoring the problem for now. */
decoder.initial_seek_running = false;
initial_seek_running = false;
return;
}
assert(dc.command == DecoderCommand::SEEK);
dc.seek_error = true;
decoder.seeking = false;
seeking = false;
decoder_command_finished(decoder);
CommandFinished();
}
InputStreamPtr

View File

@ -54,56 +54,6 @@ class DecoderClient;
*/
class StopDecoder {};
/**
* Determines the pending decoder command.
*
* @param decoder the decoder object
* @return the current command, or DecoderCommand::NONE if there is no
* command pending
*/
gcc_pure
DecoderCommand
decoder_get_command(DecoderClient &decoder);
/**
* Called by the decoder when it has performed the requested command
* (dc->command). This function resets dc->command and wakes up the
* player thread.
*
* @param decoder the decoder object
*/
void
decoder_command_finished(DecoderClient &decoder);
/**
* Call this when you have received the DecoderCommand::SEEK command.
*
* @param decoder the decoder object
* @return the destination position for the seek in milliseconds
*/
gcc_pure
SongTime
decoder_seek_time(DecoderClient &decoder);
/**
* Call this when you have received the DecoderCommand::SEEK command.
*
* @param decoder the decoder object
* @return the destination position for the seek in frames
*/
gcc_pure
uint64_t
decoder_seek_where_frame(DecoderClient &decoder);
/**
* Call this instead of decoder_command_finished() when seeking has
* failed.
*
* @param decoder the decoder object
*/
void
decoder_seek_error(DecoderClient &decoder);
/**
* Open a new #InputStream and wait until it's ready.
*

View File

@ -59,9 +59,8 @@ struct Decoder final : DecoderClient {
bool initial_seek_running = false;
/**
* This flag is set by decoder_seek_time(), and checked by
* decoder_command_finished(). It is used to clean up after
* seeking.
* This flag is set by GetSeekTime(), and checked by
* CommandFinished(). It is used to clean up after seeking.
*/
bool seeking = false;
@ -120,6 +119,11 @@ struct Decoder final : DecoderClient {
/* virtual methods from DecoderClient */
void Ready(AudioFormat audio_format,
bool seekable, SignedSongTime duration) override;
DecoderCommand GetCommand() override;
void CommandFinished() override;
SongTime GetSeekTime() override;
uint64_t GetSeekFrame() override;
void SeekError() override;
};
#endif

View File

@ -228,10 +228,10 @@ audiofile_stream_decode(DecoderClient &client, InputStream &is)
kbit_rate);
if (cmd == DecoderCommand::SEEK) {
AFframecount frame = decoder_seek_where_frame(client);
AFframecount frame = client.GetSeekFrame();
afSeekFrame(fh, AF_DEFAULT_TRACK, frame);
decoder_command_finished(client);
client.CommandFinished();
cmd = DecoderCommand::NONE;
}
} while (cmd == DecoderCommand::NONE);

View File

@ -372,23 +372,23 @@ dsdiff_decode_chunk(DecoderClient &client, InputStream &is,
const unsigned buffer_frames = sizeof(buffer) / frame_size;
const size_t buffer_size = buffer_frames * frame_size;
auto cmd = decoder_get_command(client);
auto cmd = client.GetCommand();
for (offset_type remaining_bytes = total_bytes;
remaining_bytes >= frame_size && cmd != DecoderCommand::STOP;) {
if (cmd == DecoderCommand::SEEK) {
uint64_t frame = decoder_seek_where_frame(client);
uint64_t frame = client.GetSeekFrame();
offset_type offset = FrameToOffset(frame, channels);
if (offset >= total_bytes) {
decoder_command_finished(client);
client.CommandFinished();
break;
}
if (dsdlib_skip_to(&client, is,
start_offset + offset)) {
decoder_command_finished(client);
client.CommandFinished();
remaining_bytes = total_bytes - offset;
} else
decoder_seek_error(client);
client.SeekError();
}
/* see how much aligned data from the remaining chunk

View File

@ -259,23 +259,23 @@ dsf_decode_chunk(DecoderClient &client, InputStream &is,
const size_t block_size = channels * DSF_BLOCK_SIZE;
const offset_type start_offset = is.GetOffset();
auto cmd = decoder_get_command(client);
auto cmd = client.GetCommand();
for (offset_type i = 0; i < n_blocks && cmd != DecoderCommand::STOP;) {
if (cmd == DecoderCommand::SEEK) {
uint64_t frame = decoder_seek_where_frame(client);
uint64_t frame = client.GetSeekFrame();
offset_type block = FrameToBlock(frame);
if (block >= n_blocks) {
decoder_command_finished(client);
client.CommandFinished();
break;
}
offset_type offset =
start_offset + block * block_size;
if (dsdlib_skip_to(&client, is, offset)) {
decoder_command_finished(client);
client.CommandFinished();
i = block;
} else
decoder_seek_error(client);
client.SeekError();
}
/* worst-case buffer size */

View File

@ -717,11 +717,11 @@ FfmpegDecode(DecoderClient &client, InputStream &input,
uint64_t min_frame = 0;
DecoderCommand cmd = decoder_get_command(client);
DecoderCommand cmd = client.GetCommand();
while (cmd != DecoderCommand::STOP) {
if (cmd == DecoderCommand::SEEK) {
int64_t where =
ToFfmpegTime(decoder_seek_time(client),
ToFfmpegTime(client.GetSeekTime(),
av_stream.time_base) +
start_time_fallback(av_stream);
@ -730,11 +730,11 @@ FfmpegDecode(DecoderClient &client, InputStream &input,
stamp, not after */
if (av_seek_frame(&format_context, audio_stream, where,
AVSEEK_FLAG_ANY|AVSEEK_FLAG_BACKWARD) < 0)
decoder_seek_error(client);
client.SeekError();
else {
avcodec_flush_buffers(codec_context);
min_frame = decoder_seek_where_frame(client);
decoder_command_finished(client);
min_frame = client.GetSeekFrame();
client.CommandFinished();
}
}
@ -757,7 +757,7 @@ FfmpegDecode(DecoderClient &client, InputStream &input,
interleaved_buffer);
min_frame = 0;
} else
cmd = decoder_get_command(client);
cmd = client.GetCommand();
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56, 25, 100)
av_packet_unref(&packet);

View File

@ -151,16 +151,15 @@ flac_decoder_loop(FlacDecoder *data, FLAC__StreamDecoder *flac_dec)
std::move(data->tag));
data->tag.Clear();
} else
cmd = decoder_get_command(client);
cmd = client.GetCommand();
if (cmd == DecoderCommand::SEEK) {
FLAC__uint64 seek_sample =
decoder_seek_where_frame(client);
FLAC__uint64 seek_sample = client.GetSeekFrame();
if (FLAC__stream_decoder_seek_absolute(flac_dec, seek_sample)) {
data->position = 0;
decoder_command_finished(client);
client.CommandFinished();
} else
decoder_seek_error(client);
client.SeekError();
} else if (cmd == DecoderCommand::STOP)
break;
@ -198,7 +197,7 @@ flac_decoder_loop(FlacDecoder *data, FLAC__StreamDecoder *flac_dec)
}
if (!FLAC__stream_decoder_process_single(flac_dec) &&
decoder_get_command(client) == DecoderCommand::NONE) {
client.GetCommand() == DecoderCommand::NONE) {
/* a failure that was not triggered by a
decoder command */
flacPrintErroredState(FLAC__stream_decoder_get_state(flac_dec));

View File

@ -36,7 +36,7 @@ FlacInput::Read(FLAC__byte buffer[], size_t *bytes)
if (r == 0) {
if (input_stream.LockIsEOF() ||
(client != nullptr &&
decoder_get_command(*client) != DecoderCommand::NONE))
client->GetCommand() != DecoderCommand::NONE))
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
else
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
@ -84,8 +84,8 @@ FLAC__bool
FlacInput::Eof()
{
return (client != nullptr &&
decoder_get_command(*client) != DecoderCommand::NONE &&
decoder_get_command(*client) != DecoderCommand::SEEK) ||
client->GetCommand() != DecoderCommand::NONE &&
client->GetCommand() != DecoderCommand::SEEK) ||
input_stream.LockIsEOF();
}
@ -93,7 +93,7 @@ void
FlacInput::Error(FLAC__StreamDecoderErrorStatus status)
{
if (client == nullptr ||
decoder_get_command(*client) != DecoderCommand::STOP)
client->GetCommand() != DecoderCommand::STOP)
LogWarning(flac_domain,
FLAC__StreamDecoderErrorStatusString[status]);
}

View File

@ -192,13 +192,13 @@ gme_file_decode(DecoderClient &client, Path path_fs)
cmd = decoder_data(client, nullptr, buf, sizeof(buf), 0);
if (cmd == DecoderCommand::SEEK) {
unsigned where = decoder_seek_time(client).ToMS();
unsigned where = client.GetSeekTime().ToMS();
gme_err = gme_seek(emu, where);
if (gme_err != nullptr) {
LogWarning(gme_domain, gme_err);
decoder_seek_error(client);
client.SeekError();
} else
decoder_command_finished(client);
client.CommandFinished();
}
if (gme_track_ended(emu))

View File

@ -985,18 +985,18 @@ MadDecoder::Read()
if (cmd == DecoderCommand::SEEK) {
assert(input_stream.IsSeekable());
unsigned long j =
TimeToFrame(decoder_seek_time(*client));
const auto t = client->GetSeekTime();
unsigned long j = TimeToFrame(t);
if (j < highest_frame) {
if (Seek(frame_offsets[j])) {
current_frame = j;
decoder_command_finished(*client);
client->CommandFinished();
} else
decoder_seek_error(*client);
client->SeekError();
} else {
seek_time = decoder_seek_time(*client);
seek_time = t;
mute_frame = MUTEFRAME_SEEK;
decoder_command_finished(*client);
client->CommandFinished();
}
} else if (cmd != DecoderCommand::NONE)
return false;
@ -1042,7 +1042,7 @@ mp3_decode(DecoderClient &client, InputStream &input_stream)
if (!data.DecodeFirstFrame(&tag)) {
delete tag;
if (decoder_get_command(client) == DecoderCommand::NONE)
if (client.GetCommand() == DecoderCommand::NONE)
LogError(mad_domain,
"input/Input does not appear to be a mp3 bit stream");
return;

View File

@ -165,8 +165,8 @@ mod_decode(DecoderClient &client, InputStream &is)
0);
if (cmd == DecoderCommand::SEEK) {
ModPlug_Seek(f, decoder_seek_time(client).ToMS());
decoder_command_finished(client);
ModPlug_Seek(f, client.GetSeekTime().ToMS());
client.CommandFinished();
}
} while (cmd != DecoderCommand::STOP);

View File

@ -152,7 +152,7 @@ mpcdec_decode(DecoderClient &client, InputStream &is)
mpc_demux *demux = mpc_demux_init(&reader);
if (demux == nullptr) {
if (decoder_get_command(client) != DecoderCommand::STOP)
if (client.GetCommand() != DecoderCommand::STOP)
LogWarning(mpcdec_domain,
"Not a valid musepack stream");
return;
@ -180,16 +180,15 @@ mpcdec_decode(DecoderClient &client, InputStream &is)
DecoderCommand cmd = DecoderCommand::NONE;
do {
if (cmd == DecoderCommand::SEEK) {
mpc_int64_t where =
decoder_seek_where_frame(client);
mpc_int64_t where = client.GetSeekFrame();
bool success;
success = mpc_demux_seek_sample(demux, where)
== MPC_STATUS_OK;
if (success)
decoder_command_finished(client);
client.CommandFinished();
else
decoder_seek_error(client);
client.SeekError();
}
MPC_SAMPLE_FORMAT sample_buffer[MPC_DECODER_BUFFER_LENGTH];

View File

@ -260,12 +260,12 @@ mpd_mpg123_file_decode(DecoderClient &client, Path path_fs)
cmd = decoder_data(client, nullptr, buffer, nbytes, info.bitrate);
if (cmd == DecoderCommand::SEEK) {
off_t c = decoder_seek_where_frame(client);
off_t c = client.GetSeekFrame();
c = mpg123_seek(handle, c, SEEK_SET);
if (c < 0)
decoder_seek_error(client);
client.SeekError();
else {
decoder_command_finished(client);
client.CommandFinished();
decoder_timestamp(client, c/(double)audio_format.sample_rate);
}

View File

@ -181,7 +181,7 @@ MPDOpusDecoder::OnOggBeginning(const ogg_packet &packet)
output_buffer = new opus_int16[opus_output_buffer_frames
* audio_format.channels];
auto cmd = decoder_get_command(client);
auto cmd = client.GetCommand();
if (cmd != DecoderCommand::NONE)
throw cmd;
}
@ -291,10 +291,10 @@ mpd_opus_stream_decode(DecoderClient &client,
break;
} catch (DecoderCommand cmd) {
if (cmd == DecoderCommand::SEEK) {
if (d.Seek(decoder_seek_where_frame(client)))
decoder_command_finished(client);
if (d.Seek(client.GetSeekFrame()))
client.CommandFinished();
else
decoder_seek_error(client);
client.SeekError();
} else if (cmd != DecoderCommand::NONE)
break;
}

View File

@ -166,18 +166,18 @@ pcm_stream_decode(DecoderClient &client, InputStream &is)
cmd = !r.IsEmpty()
? decoder_data(client, is, r.data, r.size, 0)
: decoder_get_command(client);
: client.GetCommand();
if (cmd == DecoderCommand::SEEK) {
uint64_t frame = decoder_seek_where_frame(client);
uint64_t frame = client.GetSeekFrame();
offset_type offset = frame * frame_size;
try {
is.LockSeek(offset);
buffer.Clear();
decoder_command_finished(client);
client.CommandFinished();
} catch (const std::runtime_error &e) {
LogError(e);
decoder_seek_error(client);
client.SeekError();
}
cmd = DecoderCommand::NONE;

View File

@ -373,7 +373,7 @@ sidplay_file_decode(DecoderClient &client, Path path_fs)
if (cmd == DecoderCommand::SEEK) {
unsigned data_time = player.time();
unsigned target_time =
decoder_seek_time(client).ToScale(timebase);
client.GetSeekTime().ToScale(timebase);
/* can't rewind so return to zero and seek forward */
if(target_time<data_time) {
@ -386,7 +386,7 @@ sidplay_file_decode(DecoderClient &client, Path path_fs)
player.play(buffer, ARRAY_SIZE(buffer)) > 0)
data_time = player.time();
decoder_command_finished(client);
client.CommandFinished();
}
if (end > 0 && player.time() >= end)

View File

@ -225,12 +225,12 @@ sndfile_stream_decode(DecoderClient &client, InputStream &is)
buffer, num_frames * frame_size,
0);
if (cmd == DecoderCommand::SEEK) {
sf_count_t c = decoder_seek_where_frame(client);
sf_count_t c = client.GetSeekFrame();
c = sf_seek(sf, c, SEEK_SET);
if (c < 0)
decoder_seek_error(client);
client.SeekError();
else
decoder_command_finished(client);
client.CommandFinished();
cmd = DecoderCommand::NONE;
}
} while (cmd == DecoderCommand::NONE);

View File

@ -264,7 +264,7 @@ VorbisDecoder::OnOggPacket(const ogg_packet &_packet)
if (vorbis_synthesis(&block, &packet) != 0) {
/* ignore bad packets, but give the MPD core a
chance to stop us */
auto cmd = decoder_get_command(client);
auto cmd = client.GetCommand();
if (cmd != DecoderCommand::NONE)
throw cmd;
return;
@ -322,10 +322,10 @@ vorbis_stream_decode(DecoderClient &client,
break;
} catch (DecoderCommand cmd) {
if (cmd == DecoderCommand::SEEK) {
if (d.Seek(decoder_seek_where_frame(client)))
decoder_command_finished(client);
if (d.Seek(client.GetSeekFrame()))
client.CommandFinished();
else
decoder_seek_error(client);
client.SeekError();
} else if (cmd != DecoderCommand::NONE)
break;
}

View File

@ -170,19 +170,19 @@ wavpack_decode(DecoderClient &client, WavpackContext *wpc, bool can_seek)
client.Ready(audio_format, can_seek, total_time);
DecoderCommand cmd = decoder_get_command(client);
DecoderCommand cmd = client.GetCommand();
while (cmd != DecoderCommand::STOP) {
if (cmd == DecoderCommand::SEEK) {
if (can_seek) {
auto where = decoder_seek_where_frame(client);
auto where = client.GetSeekFrame();
if (WavpackSeekSample(wpc, where)) {
decoder_command_finished(client);
client.CommandFinished();
} else {
decoder_seek_error(client);
client.SeekError();
}
} else {
decoder_seek_error(client);
client.SeekError();
}
}

View File

@ -114,11 +114,10 @@ wildmidi_file_decode(DecoderClient &client, Path path_fs)
cmd = wildmidi_output(client, wm);
if (cmd == DecoderCommand::SEEK) {
unsigned long seek_where =
decoder_seek_where_frame(client);
unsigned long seek_where = client.GetSeekFrame();
WildMidi_FastSeek(wm, &seek_where);
decoder_command_finished(client);
client.CommandFinished();
cmd = DecoderCommand::NONE;
}

View File

@ -46,30 +46,30 @@ FakeDecoder::Ready(const AudioFormat audio_format,
}
DecoderCommand
decoder_get_command(gcc_unused DecoderClient &client)
FakeDecoder::GetCommand()
{
return DecoderCommand::NONE;
}
void
decoder_command_finished(gcc_unused DecoderClient &client)
FakeDecoder::CommandFinished()
{
}
SongTime
decoder_seek_time(gcc_unused DecoderClient &client)
FakeDecoder::GetSeekTime()
{
return SongTime();
}
uint64_t
decoder_seek_where_frame(gcc_unused DecoderClient &client)
FakeDecoder::GetSeekFrame()
{
return 1;
}
void
decoder_seek_error(gcc_unused DecoderClient &client)
FakeDecoder::SeekError()
{
}

View File

@ -34,6 +34,11 @@ struct FakeDecoder final : DecoderClient {
/* virtual methods from DecoderClient */
void Ready(AudioFormat audio_format,
bool seekable, SignedSongTime duration) override;
DecoderCommand GetCommand() override;
void CommandFinished() override;
SongTime GetSeekTime() override;
uint64_t GetSeekFrame() override;
void SeekError() override;
};
#endif