decoder/flac: move the SubmitData() call out of the callback

This addresses two problems:

1. the libFLAC write callback had to send an error status to its
caller when SubmitData() returned a command; this disrupted libFLAC
and the resulting command could not be used for anything;

2. the libFLAC function FLAC__stream_decoder_seek_absolute() also
calls the write callback, but its result cannot be used, because
seeking is still in progress, so we lose all data from one FLAC frame.
By moving the SubmitData() call until after CommandFinished(), we
avoid losing this data.  This fixes another part of #113
This commit is contained in:
Max Kellermann 2018-02-17 13:10:04 +01:00
parent b53a23b51b
commit 026aef7465
4 changed files with 36 additions and 21 deletions

2
NEWS
View File

@ -1,4 +1,6 @@
ver 0.20.18 (not yet released) ver 0.20.18 (not yet released)
* decoder
- flac: improve seeking precision
* fix gapless CUE song transitions * fix gapless CUE song transitions
ver 0.20.17 (2018/02/11) ver 0.20.17 (2018/02/11)

View File

@ -24,7 +24,6 @@
#include "config.h" #include "config.h"
#include "FlacCommon.hxx" #include "FlacCommon.hxx"
#include "FlacMetadata.hxx" #include "FlacMetadata.hxx"
#include "util/ConstBuffer.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <stdexcept> #include <stdexcept>
@ -143,25 +142,10 @@ FlacDecoder::OnWrite(const FLAC__Frame &frame,
if (!initialized && !OnFirstFrame(frame.header)) if (!initialized && !OnFirstFrame(frame.header))
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
const auto data = pcm_import.Import(buf, frame.header.blocksize); chunk = pcm_import.Import(buf, frame.header.blocksize);
unsigned bit_rate = nbytes * 8 * frame.header.sample_rate / kbit_rate = nbytes * 8 * frame.header.sample_rate /
(1000 * frame.header.blocksize); (1000 * frame.header.blocksize);
auto cmd = GetClient()->SubmitData(GetInputStream(),
data.data, data.size,
bit_rate);
switch (cmd) {
case DecoderCommand::NONE:
case DecoderCommand::START:
break;
case DecoderCommand::STOP:
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
case DecoderCommand::SEEK:
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
} }

View File

@ -27,6 +27,7 @@
#include "FlacInput.hxx" #include "FlacInput.hxx"
#include "FlacPcm.hxx" #include "FlacPcm.hxx"
#include "../DecoderAPI.hxx" #include "../DecoderAPI.hxx"
#include "util/ConstBuffer.hxx"
#include <FLAC/stream_decoder.h> #include <FLAC/stream_decoder.h>
@ -41,6 +42,12 @@ struct FlacDecoder : public FlacInput {
*/ */
bool unsupported = false; bool unsupported = false;
/**
* The kbit_rate parameter for the next
* DecoderBridge::SubmitData() call.
*/
uint16_t kbit_rate;
FlacPcmImport pcm_import; FlacPcmImport pcm_import;
/** /**
@ -51,6 +58,13 @@ struct FlacDecoder : public FlacInput {
Tag tag; Tag tag;
/**
* Decoded PCM data obtained by our libFLAC write callback.
* If this is non-empty, then DecoderBridge::SubmitData()
* should be called.
*/
ConstBuffer<void> chunk = nullptr;
FlacDecoder(DecoderClient &_client, InputStream &_input_stream) FlacDecoder(DecoderClient &_client, InputStream &_input_stream)
:FlacInput(_input_stream, &_client) {} :FlacInput(_input_stream, &_client) {}

View File

@ -142,13 +142,28 @@ flac_decoder_initialize(FlacDecoder *data, FLAC__StreamDecoder *sd)
static DecoderCommand static DecoderCommand
FlacSubmitToClient(DecoderClient &client, FlacDecoder &d) noexcept FlacSubmitToClient(DecoderClient &client, FlacDecoder &d) noexcept
{ {
if (d.tag.IsEmpty() && d.chunk.IsEmpty())
return client.GetCommand();
if (!d.tag.IsEmpty()) { if (!d.tag.IsEmpty()) {
auto cmd = client.SubmitTag(d.GetInputStream(), auto cmd = client.SubmitTag(d.GetInputStream(),
std::move(d.tag)); std::move(d.tag));
d.tag.Clear(); d.tag.Clear();
return cmd; if (cmd != DecoderCommand::NONE)
} else return cmd;
return client.GetCommand(); }
if (!d.chunk.IsEmpty()) {
auto cmd = client.SubmitData(d.GetInputStream(),
d.chunk.data,
d.chunk.size,
d.kbit_rate);
d.chunk = nullptr;
if (cmd != DecoderCommand::NONE)
return cmd;
}
return DecoderCommand::NONE;
} }
static void static void