From c0d6008781c3032145e1740cb069830a3ef76eb4 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 18 Mar 2019 18:35:23 +0100 Subject: [PATCH] filter/hdcd: move generic code to class FfmpegFilter --- src/filter/plugins/FfmpegFilter.cxx | 76 ++++++++++++++++++++++ src/filter/plugins/FfmpegFilter.hxx | 56 +++++++++++++++++ src/filter/plugins/HdcdFilterPlugin.cxx | 83 +++++-------------------- src/filter/plugins/meson.build | 1 + 4 files changed, 150 insertions(+), 66 deletions(-) create mode 100644 src/filter/plugins/FfmpegFilter.cxx create mode 100644 src/filter/plugins/FfmpegFilter.hxx diff --git a/src/filter/plugins/FfmpegFilter.cxx b/src/filter/plugins/FfmpegFilter.cxx new file mode 100644 index 000000000..bea6eec4b --- /dev/null +++ b/src/filter/plugins/FfmpegFilter.cxx @@ -0,0 +1,76 @@ +/* + * Copyright 2003-2019 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 "FfmpegFilter.hxx" +#include "lib/ffmpeg/SampleFormat.hxx" +#include "util/ConstBuffer.hxx" + +extern "C" { +#include +#include +} + +FfmpegFilter::FfmpegFilter(const AudioFormat &in_audio_format, + const AudioFormat &_out_audio_format, + Ffmpeg::FilterGraph &&_graph, + Ffmpeg::FilterContext &&_buffer_src, + Ffmpeg::FilterContext &&_buffer_sink) noexcept + :Filter(_out_audio_format), + graph(std::move(_graph)), + buffer_src(std::move(_buffer_src)), + buffer_sink(std::move(_buffer_sink)), + in_audio_frame_size(in_audio_format.GetFrameSize()), + out_audio_frame_size(_out_audio_format.GetFrameSize()) +{ + in_frame->format = Ffmpeg::ToFfmpegSampleFormat(in_audio_format.format); + in_frame->sample_rate = in_audio_format.sample_rate; + in_frame->channels = in_audio_format.channels; +} + +ConstBuffer +FfmpegFilter::FilterPCM(ConstBuffer src) +{ + /* submit source data into the FFmpeg audio buffer source */ + + in_frame->nb_samples = src.size / in_audio_frame_size; + + in_frame.GetBuffer(); + in_frame.MakeWritable(); + + memcpy(in_frame.GetData(0), src.data, src.size); + + int err = av_buffersrc_write_frame(buffer_src.get(), in_frame.get()); + if (err < 0) + throw MakeFfmpegError(err, "av_buffersrc_write_frame() failed"); + + /* collect filtered data from the FFmpeg audio buffer sink */ + + err = av_buffersink_get_frame(buffer_sink.get(), out_frame.get()); + if (err < 0) { + if (err == AVERROR(EAGAIN) || err == AVERROR_EOF) + return nullptr; + + throw MakeFfmpegError(err, "av_buffersink_get_frame() failed"); + } + + /* TODO: call av_buffersink_get_frame() repeatedly? Not + possible with MPD's current Filter API */ + + return {out_frame.GetData(0), out_frame->nb_samples * GetOutAudioFormat().GetFrameSize()}; +} diff --git a/src/filter/plugins/FfmpegFilter.hxx b/src/filter/plugins/FfmpegFilter.hxx new file mode 100644 index 000000000..640a81eb3 --- /dev/null +++ b/src/filter/plugins/FfmpegFilter.hxx @@ -0,0 +1,56 @@ +/* + * Copyright 2003-2019 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_FILTER__HXX +#define MPD_FFMPEG_FILTER__HXX + +#include "filter/Filter.hxx" +#include "lib/ffmpeg/Filter.hxx" +#include "lib/ffmpeg/Frame.hxx" + +/** + * A #Filter implementation using FFmpeg's libavfilter. + */ +class FfmpegFilter final : public Filter { + Ffmpeg::FilterGraph graph; + Ffmpeg::FilterContext buffer_src, buffer_sink; + Ffmpeg::Frame in_frame, out_frame; + + const size_t in_audio_frame_size; + const size_t out_audio_frame_size; + +public: + /** + * @param _graph a checked and configured AVFilterGraph + * @param _buffer_src an "abuffer" filter which serves as + * input + * @param _buffer_sink an "abuffersink" filter which serves as + * output + */ + FfmpegFilter(const AudioFormat &in_audio_format, + const AudioFormat &_out_audio_format, + Ffmpeg::FilterGraph &&_graph, + Ffmpeg::FilterContext &&_buffer_src, + Ffmpeg::FilterContext &&_buffer_sink) noexcept; + + /* virtual methods from class Filter */ + ConstBuffer FilterPCM(ConstBuffer src) override; +}; + +#endif diff --git a/src/filter/plugins/HdcdFilterPlugin.cxx b/src/filter/plugins/HdcdFilterPlugin.cxx index 32fa08237..33fdcf868 100644 --- a/src/filter/plugins/HdcdFilterPlugin.cxx +++ b/src/filter/plugins/HdcdFilterPlugin.cxx @@ -18,6 +18,7 @@ */ #include "HdcdFilterPlugin.hxx" +#include "FfmpegFilter.hxx" #include "filter/FilterPlugin.hxx" #include "filter/Filter.hxx" #include "filter/NullFilter.hxx" @@ -27,12 +28,6 @@ #include "lib/ffmpeg/SampleFormat.hxx" #include "config/Block.hxx" #include "AudioFormat.hxx" -#include "util/ConstBuffer.hxx" - -extern "C" { -#include -#include -} #include @@ -47,32 +42,20 @@ MaybeHdcd(const AudioFormat &audio_format) noexcept audio_format.channels == 2; } -class HdcdFilter final : public Filter { - Ffmpeg::FilterGraph graph; - Ffmpeg::FilterContext buffer_src, buffer_sink; - Ffmpeg::Frame frame, out_frame; - - size_t in_audio_frame_size; - size_t out_audio_frame_size; - -public: - explicit HdcdFilter(AudioFormat &audio_format); - - /* virtual methods from class Filter */ - ConstBuffer FilterPCM(ConstBuffer src) override; -}; - -inline -HdcdFilter::HdcdFilter(AudioFormat &audio_format) - :Filter(audio_format) +static auto +OpenHdcdFilter(AudioFormat &in_audio_format) { - buffer_src = Ffmpeg::FilterContext::MakeAudioBufferSource(audio_format, - *graph); + Ffmpeg::FilterGraph graph; - buffer_sink = Ffmpeg::FilterContext::MakeAudioBufferSink(*graph); + auto buffer_src = + Ffmpeg::FilterContext::MakeAudioBufferSource(in_audio_format, + *graph); + + auto buffer_sink = Ffmpeg::FilterContext::MakeAudioBufferSink(*graph); Ffmpeg::FilterInOut io_sink("out", *buffer_sink); Ffmpeg::FilterInOut io_src("in", *buffer_src); + auto io = graph.Parse(hdcd_graph, std::move(io_sink), std::move(io_src)); @@ -84,15 +67,15 @@ HdcdFilter::HdcdFilter(AudioFormat &audio_format) graph.CheckAndConfigure(); - frame->format = Ffmpeg::ToFfmpegSampleFormat(audio_format.format); - frame->sample_rate = audio_format.sample_rate; - frame->channels = audio_format.channels; - + auto out_audio_format = in_audio_format; // TODO: convert to 32 bit only if HDCD actually detected out_audio_format.format = SampleFormat::S32; - in_audio_frame_size = audio_format.GetFrameSize(); - out_audio_frame_size = out_audio_format.GetFrameSize(); + return std::make_unique(in_audio_format, + out_audio_format, + std::move(graph), + std::move(buffer_src), + std::move(buffer_sink)); } class PreparedHdcdFilter final : public PreparedFilter { @@ -105,45 +88,13 @@ std::unique_ptr PreparedHdcdFilter::Open(AudioFormat &audio_format) { if (MaybeHdcd(audio_format)) - return std::make_unique(audio_format); + return OpenHdcdFilter(audio_format); else /* this cannot be HDCD, so let's copy as-is using NullFilter */ return std::make_unique(audio_format); } -ConstBuffer -HdcdFilter::FilterPCM(ConstBuffer src) -{ - /* submit source data into the FFmpeg audio buffer source */ - - frame->nb_samples = src.size / in_audio_frame_size; - - frame.GetBuffer(); - frame.MakeWritable(); - - memcpy(frame.GetData(0), src.data, src.size); - - int err = av_buffersrc_write_frame(buffer_src.get(), frame.get()); - if (err < 0) - throw MakeFfmpegError(err, "av_buffersrc_write_frame() failed"); - - /* collect filtered data from the FFmpeg audio buffer sink */ - - err = av_buffersink_get_frame(buffer_sink.get(), out_frame.get()); - if (err < 0) { - if (err == AVERROR(EAGAIN) || err == AVERROR_EOF) - return nullptr; - - throw MakeFfmpegError(err, "av_buffersink_get_frame() failed"); - } - - /* TODO: call av_buffersink_get_frame() repeatedly? Not - possible with MPD's current Filter API */ - - return {out_frame.GetData(0), out_frame->nb_samples * GetOutAudioFormat().GetFrameSize()}; -} - static std::unique_ptr hdcd_filter_init(const ConfigBlock &) { diff --git a/src/filter/plugins/meson.build b/src/filter/plugins/meson.build index 371f08bfc..b9130a090 100644 --- a/src/filter/plugins/meson.build +++ b/src/filter/plugins/meson.build @@ -3,6 +3,7 @@ filter_plugins_deps = [] if libavfilter_dep.found() filter_plugins_sources += [ + 'FfmpegFilter.cxx', 'HdcdFilterPlugin.cxx', ] filter_plugins_deps += ffmpeg_dep