Merge branch 'v0.20.x'
This commit is contained in:
commit
60efdce5ff
5
NEWS
5
NEWS
|
@ -26,6 +26,11 @@ ver 0.21 (not yet released)
|
|||
- sndio: new mixer plugin
|
||||
* require GCC 5.0
|
||||
|
||||
ver 0.20.18 (not yet released)
|
||||
* decoder
|
||||
- flac: improve seeking precision
|
||||
* fix gapless CUE song transitions
|
||||
|
||||
ver 0.20.17 (2018/02/11)
|
||||
* output
|
||||
- alsa: fix crash bug with 8 channels
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.musicpd"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="16"
|
||||
android:versionName="0.20.17">
|
||||
android:versionCode="17"
|
||||
android:versionName="0.20.18">
|
||||
|
||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17"/>
|
||||
|
||||
|
|
|
@ -293,6 +293,7 @@ DecoderBridge::CommandFinished()
|
|||
|
||||
initial_seek_running = false;
|
||||
timestamp = dc.start_time.ToDoubleS();
|
||||
absolute_frame = dc.start_time.ToScale<uint64_t>(dc.in_audio_format.sample_rate);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -312,6 +313,7 @@ DecoderBridge::CommandFinished()
|
|||
convert->Reset();
|
||||
|
||||
timestamp = dc.seek_time.ToDoubleS();
|
||||
absolute_frame = dc.seek_time.ToScale<uint64_t>(dc.in_audio_format.sample_rate);
|
||||
}
|
||||
|
||||
dc.command = DecoderCommand::NONE;
|
||||
|
@ -420,6 +422,7 @@ DecoderBridge::SubmitTimestamp(double t)
|
|||
assert(t >= 0);
|
||||
|
||||
timestamp = t;
|
||||
absolute_frame = uint64_t(t * dc.in_audio_format.sample_rate);
|
||||
}
|
||||
|
||||
DecoderCommand
|
||||
|
@ -455,6 +458,29 @@ DecoderBridge::SubmitData(InputStream *is,
|
|||
return cmd;
|
||||
}
|
||||
|
||||
cmd = DecoderCommand::NONE;
|
||||
|
||||
const size_t frame_size = dc.in_audio_format.GetFrameSize();
|
||||
size_t data_frames = length / frame_size;
|
||||
|
||||
if (dc.end_time.IsPositive()) {
|
||||
/* enforce the given end time */
|
||||
|
||||
const uint64_t end_frame =
|
||||
dc.end_time.ToScale<uint64_t>(dc.in_audio_format.sample_rate);
|
||||
if (absolute_frame >= end_frame)
|
||||
return DecoderCommand::STOP;
|
||||
|
||||
const uint64_t remaining_frames = end_frame - absolute_frame;
|
||||
if (data_frames >= remaining_frames) {
|
||||
/* past the end of the range: truncate this
|
||||
data submission and stop the decoder */
|
||||
data_frames = remaining_frames;
|
||||
length = data_frames * frame_size;
|
||||
cmd = DecoderCommand::STOP;
|
||||
}
|
||||
}
|
||||
|
||||
if (convert != nullptr) {
|
||||
assert(dc.in_audio_format != dc.out_audio_format);
|
||||
|
||||
|
@ -512,15 +538,11 @@ DecoderBridge::SubmitData(InputStream *is,
|
|||
|
||||
timestamp += (double)nbytes /
|
||||
dc.out_audio_format.GetTimeToSize();
|
||||
|
||||
if (dc.end_time.IsPositive() &&
|
||||
timestamp >= dc.end_time.ToDoubleS())
|
||||
/* the end of this range has been reached:
|
||||
stop decoding */
|
||||
return DecoderCommand::STOP;
|
||||
}
|
||||
|
||||
return DecoderCommand::NONE;
|
||||
absolute_frame += data_frames;
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
DecoderCommand
|
||||
|
|
|
@ -50,6 +50,11 @@ public:
|
|||
*/
|
||||
double timestamp = 0;
|
||||
|
||||
/**
|
||||
* The time stamp of the next data chunk, in PCM frames.
|
||||
*/
|
||||
uint64_t absolute_frame = 0;
|
||||
|
||||
/**
|
||||
* Is the initial seek (to the start position of the sub-song)
|
||||
* pending, or has it been performed already?
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include "config.h"
|
||||
#include "FlacCommon.hxx"
|
||||
#include "FlacMetadata.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <exception>
|
||||
|
@ -143,25 +142,10 @@ FlacDecoder::OnWrite(const FLAC__Frame &frame,
|
|||
if (!initialized && !OnFirstFrame(frame.header))
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "FlacInput.hxx"
|
||||
#include "FlacPcm.hxx"
|
||||
#include "../DecoderAPI.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
|
||||
#include <FLAC/stream_decoder.h>
|
||||
|
||||
|
@ -41,6 +42,12 @@ struct FlacDecoder : public FlacInput {
|
|||
*/
|
||||
bool unsupported = false;
|
||||
|
||||
/**
|
||||
* The kbit_rate parameter for the next
|
||||
* DecoderBridge::SubmitData() call.
|
||||
*/
|
||||
uint16_t kbit_rate;
|
||||
|
||||
FlacPcmImport pcm_import;
|
||||
|
||||
/**
|
||||
|
@ -51,6 +58,13 @@ struct FlacDecoder : public FlacInput {
|
|||
|
||||
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)
|
||||
:FlacInput(_input_stream, &_client) {}
|
||||
|
||||
|
|
|
@ -139,19 +139,40 @@ flac_decoder_initialize(FlacDecoder *data, FLAC__StreamDecoder *sd)
|
|||
return data->initialized;
|
||||
}
|
||||
|
||||
static DecoderCommand
|
||||
FlacSubmitToClient(DecoderClient &client, FlacDecoder &d) noexcept
|
||||
{
|
||||
if (d.tag.IsEmpty() && d.chunk.empty())
|
||||
return client.GetCommand();
|
||||
|
||||
if (!d.tag.IsEmpty()) {
|
||||
auto cmd = client.SubmitTag(d.GetInputStream(),
|
||||
std::move(d.tag));
|
||||
d.tag.Clear();
|
||||
if (cmd != DecoderCommand::NONE)
|
||||
return cmd;
|
||||
}
|
||||
|
||||
if (!d.chunk.empty()) {
|
||||
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
|
||||
flac_decoder_loop(FlacDecoder *data, FLAC__StreamDecoder *flac_dec)
|
||||
{
|
||||
DecoderClient &client = *data->GetClient();
|
||||
|
||||
while (true) {
|
||||
DecoderCommand cmd;
|
||||
if (!data->tag.IsEmpty()) {
|
||||
cmd = client.SubmitTag(data->GetInputStream(),
|
||||
std::move(data->tag));
|
||||
data->tag.Clear();
|
||||
} else
|
||||
cmd = client.GetCommand();
|
||||
DecoderCommand cmd = FlacSubmitToClient(client, *data);
|
||||
|
||||
if (cmd == DecoderCommand::SEEK) {
|
||||
FLAC__uint64 seek_sample = client.GetSeekFrame();
|
||||
|
@ -160,6 +181,11 @@ flac_decoder_loop(FlacDecoder *data, FLAC__StreamDecoder *flac_dec)
|
|||
client.CommandFinished();
|
||||
} else
|
||||
client.SeekError();
|
||||
|
||||
/* FLAC__stream_decoder_seek_absolute()
|
||||
decodes one frame and may have provided
|
||||
data to be submitted to the client */
|
||||
continue;
|
||||
} else if (cmd == DecoderCommand::STOP)
|
||||
break;
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ class OpusEncoder final : public OggEncoder {
|
|||
|
||||
ogg_int64_t packetno = 0;
|
||||
|
||||
ogg_int64_t granulepos;
|
||||
ogg_int64_t granulepos = 0;
|
||||
|
||||
public:
|
||||
OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc);
|
||||
|
|
Loading…
Reference in New Issue