diff options
author | Max Kellermann <max@duempel.org> | 2016-07-09 00:46:09 +0200 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2016-07-09 00:46:09 +0200 |
commit | b67e7df38e852f6008a6cb26abcf149faa5e837a (patch) | |
tree | e3db5b1e76c0919e855d72da8cf7e88052b7c73c /src | |
parent | e068d62ac65bacd69de84a237e622a2a762e1d5d (diff) | |
parent | f28c746b6bcfadb6df8abbe5d1ae9f418568ef2b (diff) |
Merge tag 'v0.19.17'
release v0.19.17
Diffstat (limited to 'src')
-rw-r--r-- | src/client/ClientRead.cxx | 2 | ||||
-rw-r--r-- | src/decoder/DecoderAPI.cxx | 3 | ||||
-rw-r--r-- | src/decoder/plugins/FlacCommon.cxx | 90 | ||||
-rw-r--r-- | src/decoder/plugins/FlacCommon.hxx | 24 | ||||
-rw-r--r-- | src/decoder/plugins/FlacDecoderPlugin.cxx | 100 | ||||
-rw-r--r-- | src/event/BufferedSocket.cxx | 8 | ||||
-rw-r--r-- | src/filter/plugins/ReplayGainFilterPlugin.cxx | 9 | ||||
-rw-r--r-- | src/filter/plugins/RouteFilterPlugin.cxx | 7 | ||||
-rw-r--r-- | src/output/plugins/ShoutOutputPlugin.cxx | 1 | ||||
-rw-r--r-- | src/pcm/Silence.cxx | 35 | ||||
-rw-r--r-- | src/pcm/Silence.hxx | 36 | ||||
-rw-r--r-- | src/pcm/Volume.cxx | 6 | ||||
-rw-r--r-- | src/player/Thread.cxx | 11 |
13 files changed, 210 insertions, 122 deletions
diff --git a/src/client/ClientRead.cxx b/src/client/ClientRead.cxx index 60fa42516..fdd8b341a 100644 --- a/src/client/ClientRead.cxx +++ b/src/client/ClientRead.cxx @@ -52,8 +52,8 @@ Client::OnSocketInput(void *data, size_t length) break; case CommandResult::KILL: - Close(); partition.instance.Shutdown(); + Close(); return InputResult::CLOSED; case CommandResult::FINISH: diff --git a/src/decoder/DecoderAPI.cxx b/src/decoder/DecoderAPI.cxx index a8ea57bf1..401be32f2 100644 --- a/src/decoder/DecoderAPI.cxx +++ b/src/decoder/DecoderAPI.cxx @@ -301,7 +301,8 @@ decoder_check_cancel_read(const Decoder *decoder) /* ignore the SEEK command during initialization, the plugin should handle that after it has initialized successfully */ if (dc.command == DecoderCommand::SEEK && - (dc.state == DecoderState::START || decoder->seeking)) + (dc.state == DecoderState::START || decoder->seeking || + decoder->initial_seek_running)) return false; return true; diff --git a/src/decoder/plugins/FlacCommon.cxx b/src/decoder/plugins/FlacCommon.cxx index 0c98786c3..783a3ccf4 100644 --- a/src/decoder/plugins/FlacCommon.cxx +++ b/src/decoder/plugins/FlacCommon.cxx @@ -33,7 +33,7 @@ flac_data::flac_data(Decoder &_decoder, InputStream &_input_stream) :FlacInput(_input_stream, &_decoder), initialized(false), unsupported(false), - total_frames(0), first_frame(0), next_frame(0), position(0), + position(0), decoder(_decoder), input_stream(_input_stream) { } @@ -59,6 +59,38 @@ flac_sample_format(unsigned bits_per_sample) } } +bool +flac_data::Initialize(unsigned sample_rate, unsigned bits_per_sample, + unsigned channels, FLAC__uint64 total_frames) +{ + assert(!initialized); + assert(!unsupported); + + ::Error error; + if (!audio_format_init_checked(audio_format, + sample_rate, + flac_sample_format(bits_per_sample), + channels, error)) { + LogError(error); + unsupported = true; + return false; + } + + frame_size = audio_format.GetFrameSize(); + + const auto duration = total_frames > 0 + ? SignedSongTime::FromScale<uint64_t>(total_frames, + audio_format.sample_rate) + : SignedSongTime::Negative(); + + decoder_initialized(decoder, audio_format, + input_stream.IsSeekable(), + duration); + + initialized = true; + return true; +} + static void flac_got_stream_info(struct flac_data *data, const FLAC__StreamMetadata_StreamInfo *stream_info) @@ -66,22 +98,10 @@ flac_got_stream_info(struct flac_data *data, if (data->initialized || data->unsupported) return; - Error error; - if (!audio_format_init_checked(data->audio_format, - stream_info->sample_rate, - flac_sample_format(stream_info->bits_per_sample), - stream_info->channels, error)) { - LogError(error); - data->unsupported = true; - return; - } - - data->frame_size = data->audio_format.GetFrameSize(); - - if (data->total_frames == 0) - data->total_frames = stream_info->total_samples; - - data->initialized = true; + data->Initialize(stream_info->sample_rate, + stream_info->bits_per_sample, + stream_info->channels, + stream_info->total_samples); } void flac_metadata_common_cb(const FLAC__StreamMetadata * block, @@ -125,28 +145,11 @@ flac_got_first_frame(struct flac_data *data, const FLAC__FrameHeader *header) if (data->unsupported) return false; - Error error; - if (!audio_format_init_checked(data->audio_format, - header->sample_rate, - flac_sample_format(header->bits_per_sample), - header->channels, error)) { - LogError(error); - data->unsupported = true; - return false; - } - - data->frame_size = data->audio_format.GetFrameSize(); - - const auto duration = SongTime::FromScale<uint64_t>(data->total_frames, - data->audio_format.sample_rate); - - decoder_initialized(data->decoder, data->audio_format, - data->input_stream.IsSeekable(), - duration); - - data->initialized = true; - - return true; + return data->Initialize(header->sample_rate, + header->bits_per_sample, + header->channels, + /* unknown duration */ + 0); } FLAC__StreamDecoderWriteStatus @@ -155,7 +158,6 @@ flac_common_write(struct flac_data *data, const FLAC__Frame * frame, FLAC__uint64 nbytes) { void *buffer; - unsigned bit_rate; if (!data->initialized && !flac_got_first_frame(data, &frame->header)) return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; @@ -167,16 +169,12 @@ flac_common_write(struct flac_data *data, const FLAC__Frame * frame, data->audio_format.format, buf, 0, frame->header.blocksize); - if (nbytes > 0) - bit_rate = nbytes * 8 * frame->header.sample_rate / - (1000 * frame->header.blocksize); - else - bit_rate = 0; + unsigned bit_rate = nbytes * 8 * frame->header.sample_rate / + (1000 * frame->header.blocksize); auto cmd = decoder_data(data->decoder, data->input_stream, buffer, buffer_size, bit_rate); - data->next_frame += frame->header.blocksize; switch (cmd) { case DecoderCommand::NONE: case DecoderCommand::START: diff --git a/src/decoder/plugins/FlacCommon.hxx b/src/decoder/plugins/FlacCommon.hxx index 4232cd949..0059a9cee 100644 --- a/src/decoder/plugins/FlacCommon.hxx +++ b/src/decoder/plugins/FlacCommon.hxx @@ -55,23 +55,9 @@ struct flac_data : public FlacInput { AudioFormat audio_format; /** - * The total number of frames in this song. The decoder - * plugin may initialize this attribute to override the value - * provided by libFLAC (e.g. for sub songs from a CUE sheet). + * End of last frame's position within the stream. This is + * used for bit rate calculations. */ - FLAC__uint64 total_frames; - - /** - * The number of the first frame in this song. This is only - * non-zero if playing sub songs from a CUE sheet. - */ - FLAC__uint64 first_frame; - - /** - * The number of the next frame which is going to be decoded. - */ - FLAC__uint64 next_frame; - FLAC__uint64 position; Decoder &decoder; @@ -80,6 +66,12 @@ struct flac_data : public FlacInput { Tag tag; flac_data(Decoder &decoder, InputStream &input_stream); + + /** + * Wrapper for decoder_initialized(). + */ + bool Initialize(unsigned sample_rate, unsigned bits_per_sample, + unsigned channels, FLAC__uint64 total_frames); }; void flac_metadata_common_cb(const FLAC__StreamMetadata * block, diff --git a/src/decoder/plugins/FlacDecoderPlugin.cxx b/src/decoder/plugins/FlacDecoderPlugin.cxx index 6ff9abb0a..24a758db9 100644 --- a/src/decoder/plugins/FlacDecoderPlugin.cxx +++ b/src/decoder/plugins/FlacDecoderPlugin.cxx @@ -133,26 +133,16 @@ flac_decoder_new(void) } static bool -flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd, - FLAC__uint64 duration) +flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd) { - data->total_frames = duration; - if (!FLAC__stream_decoder_process_until_end_of_metadata(sd)) { - LogWarning(flac_domain, "problem reading metadata"); + if (FLAC__stream_decoder_get_state(sd) != FLAC__STREAM_DECODER_END_OF_STREAM) + LogWarning(flac_domain, "problem reading metadata"); return false; } if (data->initialized) { /* done */ - - const auto duration2 = - SongTime::FromScale<uint64_t>(data->total_frames, - data->audio_format.sample_rate); - - decoder_initialized(data->decoder, data->audio_format, - data->input_stream.IsSeekable(), - duration2); return true; } @@ -168,13 +158,10 @@ flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd, } static void -flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec, - FLAC__uint64 t_start, FLAC__uint64 t_end) +flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec) { Decoder &decoder = data->decoder; - data->first_frame = t_start; - while (true) { DecoderCommand cmd; if (!data->tag.IsEmpty()) { @@ -185,24 +172,49 @@ flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec, cmd = decoder_get_command(decoder); if (cmd == DecoderCommand::SEEK) { - FLAC__uint64 seek_sample = t_start + + FLAC__uint64 seek_sample = decoder_seek_where_frame(decoder); - if (seek_sample >= t_start && - (t_end == 0 || seek_sample <= t_end) && - FLAC__stream_decoder_seek_absolute(flac_dec, seek_sample)) { - data->next_frame = seek_sample; + if (FLAC__stream_decoder_seek_absolute(flac_dec, seek_sample)) { data->position = 0; decoder_command_finished(decoder); } else decoder_seek_error(decoder); - } else if (cmd == DecoderCommand::STOP || - FLAC__stream_decoder_get_state(flac_dec) == FLAC__STREAM_DECODER_END_OF_STREAM) + } else if (cmd == DecoderCommand::STOP) break; - if (t_end != 0 && data->next_frame >= t_end) - /* end of this sub track */ + switch (FLAC__stream_decoder_get_state(flac_dec)) { + case FLAC__STREAM_DECODER_SEARCH_FOR_METADATA: + case FLAC__STREAM_DECODER_READ_METADATA: + case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: + case FLAC__STREAM_DECODER_READ_FRAME: + /* continue decoding */ + break; + + case FLAC__STREAM_DECODER_END_OF_STREAM: + /* regular end of stream */ + return; + + case FLAC__STREAM_DECODER_SEEK_ERROR: + /* try to recover from seek error */ + if (!FLAC__stream_decoder_flush(flac_dec)) { + LogError(flac_domain, "FLAC__stream_decoder_flush() failed"); + return; + } + break; + case FLAC__STREAM_DECODER_OGG_ERROR: + case FLAC__STREAM_DECODER_ABORTED: + case FLAC__STREAM_DECODER_MEMORY_ALLOCATION_ERROR: + /* an error, fatal enough for us to abort the + decoder */ + return; + + case FLAC__STREAM_DECODER_UNINITIALIZED: + /* we shouldn't see this, ever - bail out */ + return; + } + if (!FLAC__stream_decoder_process_single(flac_dec) && decoder_get_command(decoder) == DecoderCommand::NONE) { /* a failure that was not triggered by a @@ -251,6 +263,24 @@ stream_init(FLAC__StreamDecoder *flac_dec, struct flac_data *data, bool is_ogg) : stream_init_flac(flac_dec, data); } +static bool +FlacInitAndDecode(struct flac_data &data, FLAC__StreamDecoder *sd, bool is_ogg) +{ + auto init_status = stream_init(sd, &data, is_ogg); + if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + LogWarning(flac_domain, + FLAC__StreamDecoderInitStatusString[init_status]); + return false; + } + + bool result = flac_decoder_initialize(&data, sd); + if (result) + flac_decoder_loop(&data, sd); + + FLAC__stream_decoder_finish(sd); + return result; +} + static void flac_decode_internal(Decoder &decoder, InputStream &input_stream, @@ -264,24 +294,8 @@ flac_decode_internal(Decoder &decoder, struct flac_data data(decoder, input_stream); - FLAC__StreamDecoderInitStatus status = - stream_init(flac_dec, &data, is_ogg); - if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK) { - FLAC__stream_decoder_delete(flac_dec); - LogWarning(flac_domain, - FLAC__StreamDecoderInitStatusString[status]); - return; - } - - if (!flac_decoder_initialize(&data, flac_dec, 0)) { - FLAC__stream_decoder_finish(flac_dec); - FLAC__stream_decoder_delete(flac_dec); - return; - } - - flac_decoder_loop(&data, flac_dec, 0, 0); + FlacInitAndDecode(data, flac_dec, is_ogg); - FLAC__stream_decoder_finish(flac_dec); FLAC__stream_decoder_delete(flac_dec); } diff --git a/src/event/BufferedSocket.cxx b/src/event/BufferedSocket.cxx index b9f5d9189..f4a5ef287 100644 --- a/src/event/BufferedSocket.cxx +++ b/src/event/BufferedSocket.cxx @@ -118,9 +118,15 @@ BufferedSocket::OnSocketReady(unsigned flags) if (flags & READ) { assert(!input.IsFull()); - if (!ReadToBuffer() || !ResumeInput()) + if (!ReadToBuffer()) return false; + if (!ResumeInput()) + /* we must return "true" here or + SocketMonitor::Dispatch() will call + Cancel() on a freed object */ + return true; + if (!input.IsFull()) ScheduleRead(); } diff --git a/src/filter/plugins/ReplayGainFilterPlugin.cxx b/src/filter/plugins/ReplayGainFilterPlugin.cxx index 7512c180d..49b7674c4 100644 --- a/src/filter/plugins/ReplayGainFilterPlugin.cxx +++ b/src/filter/plugins/ReplayGainFilterPlugin.cxx @@ -152,8 +152,6 @@ ReplayGainFilter::Update() volume = pcm_float_to_volume(scale); } - pv.SetVolume(volume); - if (mixer != nullptr) { /* update the hardware mixer volume */ @@ -164,7 +162,8 @@ ReplayGainFilter::Update() Error error; if (!mixer_set_volume(mixer, _volume, error)) LogError(error, "Failed to update hardware mixer"); - } + } else + pv.SetVolume(volume); } static PreparedFilter * @@ -189,7 +188,9 @@ PreparedReplayGainFilter::Open(AudioFormat &af, gcc_unused Error &error) ConstBuffer<void> ReplayGainFilter::FilterPCM(ConstBuffer<void> src, gcc_unused Error &error) { - return pv.Apply(src); + return mixer != nullptr + ? src + : pv.Apply(src); } const struct filter_plugin replay_gain_filter_plugin = { diff --git a/src/filter/plugins/RouteFilterPlugin.cxx b/src/filter/plugins/RouteFilterPlugin.cxx index 39986a376..b04362c90 100644 --- a/src/filter/plugins/RouteFilterPlugin.cxx +++ b/src/filter/plugins/RouteFilterPlugin.cxx @@ -47,9 +47,11 @@ #include "filter/FilterInternal.hxx" #include "filter/FilterRegistry.hxx" #include "pcm/PcmBuffer.hxx" +#include "pcm/Silence.hxx" #include "util/StringUtil.hxx" #include "util/Error.hxx" #include "util/ConstBuffer.hxx" +#include "util/WritableBuffer.hxx" #include <algorithm> #include <array> @@ -272,9 +274,8 @@ RouteFilter::FilterPCM(ConstBuffer<void> src, gcc_unused Error &error) (unsigned)sources[c] >= input_format.channels) { // No source for this destination output, // give it zeroes as input - memset(chan_destination, - 0x00, - bytes_per_frame_per_channel); + PcmSilence({chan_destination, bytes_per_frame_per_channel}, + input_format.format); } else { // Get the data from channel sources[c] // and copy it to the output diff --git a/src/output/plugins/ShoutOutputPlugin.cxx b/src/output/plugins/ShoutOutputPlugin.cxx index 25ae4440e..2fee9c83b 100644 --- a/src/output/plugins/ShoutOutputPlugin.cxx +++ b/src/output/plugins/ShoutOutputPlugin.cxx @@ -266,7 +266,6 @@ ShoutOutput::Configure(const ConfigBlock &block, Error &error) { char temp[11]; - memset(temp, 0, sizeof(temp)); snprintf(temp, sizeof(temp), "%u", audio_format.channels); shout_set_audio_info(shout_conn, SHOUT_AI_CHANNELS, temp); diff --git a/src/pcm/Silence.cxx b/src/pcm/Silence.cxx new file mode 100644 index 000000000..c8f67f2b0 --- /dev/null +++ b/src/pcm/Silence.cxx @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2003-2016 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "Silence.hxx" +#include "AudioFormat.hxx" +#include "util/WritableBuffer.hxx" + +#include <string.h> + +void +PcmSilence(WritableBuffer<void> dest, SampleFormat format) +{ + uint8_t pattern = 0; + if (format == SampleFormat::DSD) + pattern = 0x69; + + memset(dest.data, pattern, dest.size); +} diff --git a/src/pcm/Silence.hxx b/src/pcm/Silence.hxx new file mode 100644 index 000000000..5fcf99316 --- /dev/null +++ b/src/pcm/Silence.hxx @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2003-2016 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_PCM_SILENCE_HXX +#define MPD_PCM_SILENCE_HXX + +#include "check.h" + +#include <stdint.h> + +template<typename T> struct WritableBuffer; +enum class SampleFormat : uint8_t; + +/** + * Fill the given buffer with the format-specific silence pattern. + */ +void +PcmSilence(WritableBuffer<void> dest, SampleFormat format); + +#endif diff --git a/src/pcm/Volume.cxx b/src/pcm/Volume.cxx index d8f83ad60..5e09fc3be 100644 --- a/src/pcm/Volume.cxx +++ b/src/pcm/Volume.cxx @@ -19,9 +19,11 @@ #include "config.h" #include "Volume.hxx" +#include "Silence.hxx" #include "Domain.hxx" #include "Traits.hxx" #include "util/ConstBuffer.hxx" +#include "util/WritableBuffer.hxx" #include "util/Error.hxx" #include "PcmDither.cxx" // including the .cxx file to get inlined templates @@ -133,9 +135,7 @@ PcmVolume::Apply(ConstBuffer<void> src) if (volume == 0) { /* optimized special case: 0% volume = memset(0) */ - /* TODO: is this valid for all sample formats? What - about floating point? */ - memset(data, 0, src.size); + PcmSilence({data, src.size}, format); return { data, src.size }; } diff --git a/src/player/Thread.cxx b/src/player/Thread.cxx index 836075e01..4458ba671 100644 --- a/src/player/Thread.cxx +++ b/src/player/Thread.cxx @@ -25,6 +25,7 @@ #include "MusicPipe.hxx" #include "MusicBuffer.hxx" #include "MusicChunk.hxx" +#include "pcm/Silence.hxx" #include "DetachedSong.hxx" #include "CrossFade.hxx" #include "Control.hxx" @@ -532,8 +533,12 @@ Player::SendSilence() MusicChunk *chunk = buffer.Allocate(); if (chunk == nullptr) { - LogError(player_domain, "Failed to allocate silence buffer"); - return false; + /* this is non-fatal, because this means that the + decoder has filled to buffer completely meanwhile; + by ignoring the error, we work around this race + condition */ + LogDebug(player_domain, "Failed to allocate silence buffer"); + return true; } #ifndef NDEBUG @@ -547,7 +552,7 @@ Player::SendSilence() chunk->time = SignedSongTime::Negative(); /* undefined time stamp */ chunk->length = num_frames * frame_size; - memset(chunk->data, 0, chunk->length); + PcmSilence({chunk->data, chunk->length}, play_audio_format.format); Error error; if (!pc.outputs.Play(chunk, error)) { |