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
|
- sndio: new mixer plugin
|
||||||
* require GCC 5.0
|
* 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)
|
ver 0.20.17 (2018/02/11)
|
||||||
* output
|
* output
|
||||||
- alsa: fix crash bug with 8 channels
|
- alsa: fix crash bug with 8 channels
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.musicpd"
|
package="org.musicpd"
|
||||||
android:installLocation="auto"
|
android:installLocation="auto"
|
||||||
android:versionCode="16"
|
android:versionCode="17"
|
||||||
android:versionName="0.20.17">
|
android:versionName="0.20.18">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17"/>
|
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="17"/>
|
||||||
|
|
||||||
|
@ -293,6 +293,7 @@ DecoderBridge::CommandFinished()
|
|||||||
|
|
||||||
initial_seek_running = false;
|
initial_seek_running = false;
|
||||||
timestamp = dc.start_time.ToDoubleS();
|
timestamp = dc.start_time.ToDoubleS();
|
||||||
|
absolute_frame = dc.start_time.ToScale<uint64_t>(dc.in_audio_format.sample_rate);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,6 +313,7 @@ DecoderBridge::CommandFinished()
|
|||||||
convert->Reset();
|
convert->Reset();
|
||||||
|
|
||||||
timestamp = dc.seek_time.ToDoubleS();
|
timestamp = dc.seek_time.ToDoubleS();
|
||||||
|
absolute_frame = dc.seek_time.ToScale<uint64_t>(dc.in_audio_format.sample_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
dc.command = DecoderCommand::NONE;
|
dc.command = DecoderCommand::NONE;
|
||||||
@ -420,6 +422,7 @@ DecoderBridge::SubmitTimestamp(double t)
|
|||||||
assert(t >= 0);
|
assert(t >= 0);
|
||||||
|
|
||||||
timestamp = t;
|
timestamp = t;
|
||||||
|
absolute_frame = uint64_t(t * dc.in_audio_format.sample_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
DecoderCommand
|
DecoderCommand
|
||||||
@ -455,6 +458,29 @@ DecoderBridge::SubmitData(InputStream *is,
|
|||||||
return cmd;
|
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) {
|
if (convert != nullptr) {
|
||||||
assert(dc.in_audio_format != dc.out_audio_format);
|
assert(dc.in_audio_format != dc.out_audio_format);
|
||||||
|
|
||||||
@ -512,15 +538,11 @@ DecoderBridge::SubmitData(InputStream *is,
|
|||||||
|
|
||||||
timestamp += (double)nbytes /
|
timestamp += (double)nbytes /
|
||||||
dc.out_audio_format.GetTimeToSize();
|
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
|
DecoderCommand
|
||||||
|
@ -50,6 +50,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
double timestamp = 0;
|
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)
|
* Is the initial seek (to the start position of the sub-song)
|
||||||
* pending, or has it been performed already?
|
* pending, or has it been performed already?
|
||||||
|
@ -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 <exception>
|
#include <exception>
|
||||||
@ -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 @@
|
|||||||
#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) {}
|
||||||
|
|
||||||
|
@ -139,19 +139,40 @@ flac_decoder_initialize(FlacDecoder *data, FLAC__StreamDecoder *sd)
|
|||||||
return data->initialized;
|
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
|
static void
|
||||||
flac_decoder_loop(FlacDecoder *data, FLAC__StreamDecoder *flac_dec)
|
flac_decoder_loop(FlacDecoder *data, FLAC__StreamDecoder *flac_dec)
|
||||||
{
|
{
|
||||||
DecoderClient &client = *data->GetClient();
|
DecoderClient &client = *data->GetClient();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
DecoderCommand cmd;
|
DecoderCommand cmd = FlacSubmitToClient(client, *data);
|
||||||
if (!data->tag.IsEmpty()) {
|
|
||||||
cmd = client.SubmitTag(data->GetInputStream(),
|
|
||||||
std::move(data->tag));
|
|
||||||
data->tag.Clear();
|
|
||||||
} else
|
|
||||||
cmd = client.GetCommand();
|
|
||||||
|
|
||||||
if (cmd == DecoderCommand::SEEK) {
|
if (cmd == DecoderCommand::SEEK) {
|
||||||
FLAC__uint64 seek_sample = client.GetSeekFrame();
|
FLAC__uint64 seek_sample = client.GetSeekFrame();
|
||||||
@ -160,6 +181,11 @@ flac_decoder_loop(FlacDecoder *data, FLAC__StreamDecoder *flac_dec)
|
|||||||
client.CommandFinished();
|
client.CommandFinished();
|
||||||
} else
|
} else
|
||||||
client.SeekError();
|
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)
|
} else if (cmd == DecoderCommand::STOP)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ class OpusEncoder final : public OggEncoder {
|
|||||||
|
|
||||||
ogg_int64_t packetno = 0;
|
ogg_int64_t packetno = 0;
|
||||||
|
|
||||||
ogg_int64_t granulepos;
|
ogg_int64_t granulepos = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc);
|
OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc);
|
||||||
|
Loading…
Reference in New Issue
Block a user