Merge branch 'v0.20.x'
This commit is contained in:
commit
523051132d
10
NEWS
10
NEWS
@ -14,6 +14,16 @@ ver 0.21 (not yet released)
|
|||||||
* mixer
|
* mixer
|
||||||
- sndio: new mixer plugin
|
- sndio: new mixer plugin
|
||||||
|
|
||||||
|
ver 0.20.12 (not yet released)
|
||||||
|
* input
|
||||||
|
- curl: fix seeking
|
||||||
|
* decoder
|
||||||
|
- vorbis: fix Tremor support
|
||||||
|
* player
|
||||||
|
- log message when decoder is too slow
|
||||||
|
* output
|
||||||
|
- fix hanging playback with soxr resampler
|
||||||
|
|
||||||
ver 0.20.11 (2017/10/18)
|
ver 0.20.11 (2017/10/18)
|
||||||
* storage
|
* storage
|
||||||
- curl: support Content-Type application/xml
|
- curl: support Content-Type application/xml
|
||||||
|
@ -178,6 +178,20 @@ VorbisDecoder::SubmitInit()
|
|||||||
client.Ready(audio_format, eos_granulepos > 0, duration);
|
client.Ready(audio_format, eos_granulepos > 0, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_TREMOR
|
||||||
|
static inline int16_t tremor_clip_sample(int32_t x)
|
||||||
|
{
|
||||||
|
x >>= 9;
|
||||||
|
|
||||||
|
if (x < INT16_MIN)
|
||||||
|
return INT16_MIN;
|
||||||
|
if (x > INT16_MAX)
|
||||||
|
return INT16_MAX;
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool
|
bool
|
||||||
VorbisDecoder::SubmitSomePcm()
|
VorbisDecoder::SubmitSomePcm()
|
||||||
{
|
{
|
||||||
@ -197,7 +211,7 @@ VorbisDecoder::SubmitSomePcm()
|
|||||||
auto *dest = &buffer[c];
|
auto *dest = &buffer[c];
|
||||||
|
|
||||||
for (size_t i = 0; i < n_frames; ++i) {
|
for (size_t i = 0; i < n_frames; ++i) {
|
||||||
*dest = *src++;
|
*dest = tremor_clip_sample(*src++);
|
||||||
dest += channels;
|
dest += channels;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,6 @@ static const size_t CURL_RESUME_AT = 384 * 1024;
|
|||||||
struct CurlInputStream final : public AsyncInputStream, CurlResponseHandler {
|
struct CurlInputStream final : public AsyncInputStream, CurlResponseHandler {
|
||||||
/* some buffers which were passed to libcurl, which we have
|
/* some buffers which were passed to libcurl, which we have
|
||||||
too free */
|
too free */
|
||||||
char range[32];
|
|
||||||
CurlSlist request_headers;
|
CurlSlist request_headers;
|
||||||
|
|
||||||
CurlRequest *request = nullptr;
|
CurlRequest *request = nullptr;
|
||||||
@ -89,8 +88,19 @@ struct CurlInputStream final : public AsyncInputStream, CurlResponseHandler {
|
|||||||
|
|
||||||
static InputStream *Open(const char *url, Mutex &mutex, Cond &cond);
|
static InputStream *Open(const char *url, Mutex &mutex, Cond &cond);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and initialize a new #CurlRequest instance. After
|
||||||
|
* this, you may add more request headers and set options. To
|
||||||
|
* actually start the request, call StartRequest().
|
||||||
|
*/
|
||||||
void InitEasy();
|
void InitEasy();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the request after having called InitEasy(). After
|
||||||
|
* this, you must not set any CURL options.
|
||||||
|
*/
|
||||||
|
void StartRequest();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Frees the current "libcurl easy" handle, and everything
|
* Frees the current "libcurl easy" handle, and everything
|
||||||
* associated with it.
|
* associated with it.
|
||||||
@ -365,6 +375,11 @@ CurlInputStream::InitEasy()
|
|||||||
|
|
||||||
request_headers.Clear();
|
request_headers.Clear();
|
||||||
request_headers.Append("Icy-Metadata: 1");
|
request_headers.Append("Icy-Metadata: 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CurlInputStream::StartRequest()
|
||||||
|
{
|
||||||
request->SetOption(CURLOPT_HTTPHEADER, request_headers.Get());
|
request->SetOption(CURLOPT_HTTPHEADER, request_headers.Get());
|
||||||
|
|
||||||
request->Start();
|
request->Start();
|
||||||
@ -391,6 +406,7 @@ CurlInputStream::SeekInternal(offset_type new_offset)
|
|||||||
/* send the "Range" header */
|
/* send the "Range" header */
|
||||||
|
|
||||||
if (offset > 0) {
|
if (offset > 0) {
|
||||||
|
char range[32];
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
// TODO: what can we use on Windows to format 64 bit?
|
// TODO: what can we use on Windows to format 64 bit?
|
||||||
sprintf(range, "%lu-", (long)offset);
|
sprintf(range, "%lu-", (long)offset);
|
||||||
@ -399,6 +415,8 @@ CurlInputStream::SeekInternal(offset_type new_offset)
|
|||||||
#endif
|
#endif
|
||||||
request->SetOption(CURLOPT_RANGE, range);
|
request->SetOption(CURLOPT_RANGE, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StartRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -422,6 +440,7 @@ CurlInputStream::Open(const char *url, Mutex &mutex, Cond &cond)
|
|||||||
try {
|
try {
|
||||||
BlockingCall(c->GetEventLoop(), [c](){
|
BlockingCall(c->GetEventLoop(), [c](){
|
||||||
c->InitEasy();
|
c->InitEasy();
|
||||||
|
c->StartRequest();
|
||||||
});
|
});
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
delete c;
|
delete c;
|
||||||
|
@ -245,16 +245,15 @@ try {
|
|||||||
inline bool
|
inline bool
|
||||||
AudioOutputControl::PlayChunk() noexcept
|
AudioOutputControl::PlayChunk() noexcept
|
||||||
{
|
{
|
||||||
if (tags) {
|
// ensure pending tags are flushed in all cases
|
||||||
const auto *tag = source.ReadTag();
|
const auto *tag = source.ReadTag();
|
||||||
if (tag != nullptr) {
|
if (tags && tag != nullptr) {
|
||||||
const ScopeUnlock unlock(mutex);
|
const ScopeUnlock unlock(mutex);
|
||||||
try {
|
try {
|
||||||
output->SendTag(*tag);
|
output->SendTag(*tag);
|
||||||
} catch (const std::runtime_error &e) {
|
} catch (const std::runtime_error &e) {
|
||||||
FormatError(e, "Failed to send tag to %s",
|
FormatError(e, "Failed to send tag to %s",
|
||||||
GetLogName());
|
GetLogName());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include "output/MultipleOutputs.hxx"
|
#include "output/MultipleOutputs.hxx"
|
||||||
#include "tag/Tag.hxx"
|
#include "tag/Tag.hxx"
|
||||||
#include "Idle.hxx"
|
#include "Idle.hxx"
|
||||||
|
#include "system/PeriodClock.hxx"
|
||||||
#include "util/Domain.hxx"
|
#include "util/Domain.hxx"
|
||||||
#include "thread/Name.hxx"
|
#include "thread/Name.hxx"
|
||||||
#include "Log.hxx"
|
#include "Log.hxx"
|
||||||
@ -146,6 +147,8 @@ class Player {
|
|||||||
*/
|
*/
|
||||||
SongTime elapsed_time;
|
SongTime elapsed_time;
|
||||||
|
|
||||||
|
PeriodClock throttle_silence_log;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Player(PlayerControl &_pc, DecoderControl &_dc,
|
Player(PlayerControl &_pc, DecoderControl &_dc,
|
||||||
MusicBuffer &_buffer)
|
MusicBuffer &_buffer)
|
||||||
@ -934,6 +937,8 @@ Player::SongBorder()
|
|||||||
{
|
{
|
||||||
FormatDefault(player_domain, "played \"%s\"", song->GetURI());
|
FormatDefault(player_domain, "played \"%s\"", song->GetURI());
|
||||||
|
|
||||||
|
throttle_silence_log.Reset();
|
||||||
|
|
||||||
ReplacePipe(dc.pipe);
|
ReplacePipe(dc.pipe);
|
||||||
|
|
||||||
pc.outputs.SongBorder();
|
pc.outputs.SongBorder();
|
||||||
@ -1095,6 +1100,10 @@ Player::Run()
|
|||||||
/* the decoder is too busy and hasn't provided
|
/* the decoder is too busy and hasn't provided
|
||||||
new PCM data in time: send silence (if the
|
new PCM data in time: send silence (if the
|
||||||
output pipe is empty) */
|
output pipe is empty) */
|
||||||
|
|
||||||
|
if (throttle_silence_log.CheckUpdate(std::chrono::seconds(5)))
|
||||||
|
FormatWarning(player_domain, "Decoder is too slow; playing silence to avoid xrun");
|
||||||
|
|
||||||
if (!SendSilence())
|
if (!SendSilence())
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user