Commit 026aef74 authored by Max Kellermann's avatar Max Kellermann

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
parent b53a23b5
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)
......
...@@ -24,7 +24,6 @@ ...@@ -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, ...@@ -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;
} }
...@@ -27,6 +27,7 @@ ...@@ -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 { ...@@ -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 { ...@@ -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) {}
......
...@@ -142,13 +142,28 @@ flac_decoder_initialize(FlacDecoder *data, FLAC__StreamDecoder *sd) ...@@ -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
......
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