diff --git a/NEWS b/NEWS index 697cad074..03110dc7b 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,10 @@ ver 0.23 (not yet released) ver 0.22.4 (not yet released) * decoder - dsdiff: apply padding to odd-sized chunks +* filter + - ffmpeg: detect the output sample format +* output + - moveoutput: fix always_on and tag lost on move ver 0.22.3 (2020/11/06) * playlist diff --git a/src/command/PartitionCommands.cxx b/src/command/PartitionCommands.cxx index fbcb8a754..9d260425f 100644 --- a/src/command/PartitionCommands.cxx +++ b/src/command/PartitionCommands.cxx @@ -183,9 +183,8 @@ handle_moveoutput(Client &client, Request request, Response &response) existing_output->ReplaceDummy(output->Steal(), was_enabled); else - /* add it to the output list */ - dest_partition.outputs.Add(output->Steal(), - was_enabled); + /* copy the AudioOutputControl and add it to the output list */ + dest_partition.outputs.AddCopy(output,was_enabled); instance.EmitIdle(IDLE_OUTPUT); return CommandResult::OK; diff --git a/src/filter/plugins/FfmpegFilterPlugin.cxx b/src/filter/plugins/FfmpegFilterPlugin.cxx index 168ff52bf..fcb255be0 100644 --- a/src/filter/plugins/FfmpegFilterPlugin.cxx +++ b/src/filter/plugins/FfmpegFilterPlugin.cxx @@ -23,6 +23,7 @@ #include "filter/Filter.hxx" #include "filter/Prepared.hxx" #include "lib/ffmpeg/Filter.hxx" +#include "lib/ffmpeg/DetectFilterFormat.hxx" #include "config/Block.hxx" class PreparedFfmpegFilter final : public PreparedFilter { @@ -60,7 +61,9 @@ PreparedFfmpegFilter::Open(AudioFormat &in_audio_format) graph.CheckAndConfigure(); - auto out_audio_format = in_audio_format; // TODO + const auto out_audio_format = + Ffmpeg::DetectFilterOutputFormat(in_audio_format, *buffer_src, + *buffer_sink); return std::make_unique(in_audio_format, out_audio_format, diff --git a/src/lib/ffmpeg/DetectFilterFormat.cxx b/src/lib/ffmpeg/DetectFilterFormat.cxx new file mode 100644 index 000000000..65b610024 --- /dev/null +++ b/src/lib/ffmpeg/DetectFilterFormat.cxx @@ -0,0 +1,76 @@ +/* + * Copyright 2003-2020 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 "DetectFilterFormat.hxx" +#include "Frame.hxx" +#include "SampleFormat.hxx" +#include "pcm/Silence.hxx" +#include "pcm/CheckAudioFormat.hxx" +#include "util/WritableBuffer.hxx" + +extern "C" { +#include +#include +} + +#include + +namespace Ffmpeg { + +AudioFormat +DetectFilterOutputFormat(const AudioFormat &in_audio_format, + AVFilterContext &buffer_src, + AVFilterContext &buffer_sink) +{ + uint_least64_t silence[MAX_CHANNELS]; + const size_t silence_size = in_audio_format.GetFrameSize(); + assert(sizeof(silence) >= silence_size); + + PcmSilence(WritableBuffer{&silence, silence_size}, + in_audio_format.format); + + Frame frame; + frame->format = ToFfmpegSampleFormat(in_audio_format.format); + frame->sample_rate = in_audio_format.sample_rate; + frame->channels = in_audio_format.channels; + frame->nb_samples = 1; + + frame.GetBuffer(); + + memcpy(frame.GetData(0), silence, silence_size); + + int err = av_buffersrc_add_frame(&buffer_src, frame.get()); + if (err < 0) + throw MakeFfmpegError(err, "av_buffersrc_add_frame() failed"); + + frame.Unref(); + + err = av_buffersink_get_frame(&buffer_sink, frame.get()); + if (err < 0) + throw MakeFfmpegError(err, "av_buffersink_get_frame() failed"); + + const SampleFormat sample_format = FromFfmpegSampleFormat(AVSampleFormat(frame->format)); + if (sample_format == SampleFormat::UNDEFINED) + throw std::runtime_error("Unsupported FFmpeg sample format"); + + return CheckAudioFormat(frame->sample_rate, sample_format, + frame->channels); +} + +} // namespace Ffmpeg diff --git a/src/lib/ffmpeg/DetectFilterFormat.hxx b/src/lib/ffmpeg/DetectFilterFormat.hxx new file mode 100644 index 000000000..6e9e3877c --- /dev/null +++ b/src/lib/ffmpeg/DetectFilterFormat.hxx @@ -0,0 +1,46 @@ +/* + * Copyright 2003-2020 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_FFMPEG_DETECT_FILTER_FORMAT_HXX +#define MPD_FFMPEG_DETECT_FILTER_FORMAT_HXX + +struct AVFilterContext; +struct AudioFormat; + +namespace Ffmpeg { + +/** + * Attempt to detect the output format of the given FFmpeg filter by + * sending one frame of silence and checking what format comes back + * from the filter. + * + * This is a kludge because MPD needs to know the output format of a + * filter while initializing and cannot cope with format changes in + * between. + * + * This function can throw if the FFmpeg filter fails. + */ +AudioFormat +DetectFilterOutputFormat(const AudioFormat &in_audio_format, + AVFilterContext &buffer_src, + AVFilterContext &buffer_sink); + +} // namespace Ffmpeg + +#endif diff --git a/src/lib/ffmpeg/meson.build b/src/lib/ffmpeg/meson.build index fde110edc..31eda8167 100644 --- a/src/lib/ffmpeg/meson.build +++ b/src/lib/ffmpeg/meson.build @@ -20,7 +20,10 @@ endif ffmpeg_sources = [] if libavfilter_dep.found() - ffmpeg_sources += 'Filter.cxx' + ffmpeg_sources += [ + 'Filter.cxx', + 'DetectFilterFormat.cxx', + ] endif ffmpeg = static_library( diff --git a/src/output/Control.cxx b/src/output/Control.cxx index fd4a1aa50..035739312 100644 --- a/src/output/Control.cxx +++ b/src/output/Control.cxx @@ -39,6 +39,17 @@ AudioOutputControl::AudioOutputControl(std::unique_ptr _out { } +AudioOutputControl::AudioOutputControl(AudioOutputControl *_output, + AudioOutputClient &_client) noexcept + :output(_output->Steal()), + name(output->GetName()), + client(_client), + thread(BIND_THIS_METHOD(Task)) +{ + tags =_output->tags; + always_on=_output->always_on; +} + AudioOutputControl::~AudioOutputControl() noexcept { StopThread(); diff --git a/src/output/Control.hxx b/src/output/Control.hxx index 809c65101..ab8637f47 100644 --- a/src/output/Control.hxx +++ b/src/output/Control.hxx @@ -245,6 +245,9 @@ public: AudioOutputControl(std::unique_ptr _output, AudioOutputClient &_client) noexcept; + AudioOutputControl(AudioOutputControl *_outputControl, + AudioOutputClient &_client) noexcept; + ~AudioOutputControl() noexcept; AudioOutputControl(const AudioOutputControl &) = delete; diff --git a/src/output/MultipleOutputs.cxx b/src/output/MultipleOutputs.cxx index 71014e8d1..0639d51c7 100644 --- a/src/output/MultipleOutputs.cxx +++ b/src/output/MultipleOutputs.cxx @@ -140,6 +140,19 @@ MultipleOutputs::Add(std::unique_ptr output, client.ApplyEnabled(); } +void +MultipleOutputs::AddCopy(AudioOutputControl *outputControl, + bool enable) noexcept +{ + // TODO: this operation needs to be protected with a mutex + outputs.emplace_back(std::make_unique(outputControl, + client)); + + outputs.back()->LockSetEnabled(enable); + + client.ApplyEnabled(); +} + void MultipleOutputs::EnableDisable() { diff --git a/src/output/MultipleOutputs.hxx b/src/output/MultipleOutputs.hxx index 9c885cb3a..6b3e538c2 100644 --- a/src/output/MultipleOutputs.hxx +++ b/src/output/MultipleOutputs.hxx @@ -128,6 +128,10 @@ public: void Add(std::unique_ptr output, bool enable) noexcept; + void AddCopy(AudioOutputControl *outputControl, + bool enable) noexcept; + + void SetReplayGainMode(ReplayGainMode mode) noexcept; /**