diff --git a/NEWS b/NEWS index 41e926619..00248d505 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,8 @@ 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 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(