Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package mpd for openSUSE:Factory checked in at 2021-09-01 21:37:07 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/mpd (Old) and /work/SRC/openSUSE:Factory/.mpd.new.1899 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "mpd" Wed Sep 1 21:37:07 2021 rev:34 rq:915422 version:0.22.11 Changes: -------- --- /work/SRC/openSUSE:Factory/mpd/mpd.changes 2021-08-18 08:57:00.878897109 +0200 +++ /work/SRC/openSUSE:Factory/.mpd.new.1899/mpd.changes 2021-09-01 21:37:29.368899168 +0200 @@ -1,0 +2,11 @@ +Wed Sep 1 07:24:21 UTC 2021 - ???????? ???????????? <[email protected]> + +- Updated to 0.22.11 + * https://github.com/MusicPlayerDaemon/MPD/blob/v0.22.11/NEWS + * protocol: fix "albumart" crash. + * filter: ffmpeg: + * pass "channel_layout" instead of "channels" to buffersrc + * fix "av_buffersink_get_frame() failed: Resource temporarily unavailable" + * support double-precision samples (by converting to single precision) + +------------------------------------------------------------------- Old: ---- mpd-0.22.10.tar.xz mpd-0.22.10.tar.xz.sig New: ---- mpd-0.22.11.tar.xz mpd-0.22.11.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ mpd.spec ++++++ --- /var/tmp/diff_new_pack.3sLbma/_old 2021-09-01 21:37:29.872899775 +0200 +++ /var/tmp/diff_new_pack.3sLbma/_new 2021-09-01 21:37:29.876899779 +0200 @@ -20,7 +20,7 @@ %bcond_with faad %bcond_without mpd_iso9660 Name: mpd -Version: 0.22.10 +Version: 0.22.11 Release: 0 Summary: Music Player Daemon License: GPL-2.0-or-later ++++++ mpd-0.22.10.tar.xz -> mpd-0.22.11.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/NEWS new/mpd-0.22.11/NEWS --- old/mpd-0.22.10/NEWS 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/NEWS 2021-08-24 22:15:22.000000000 +0200 @@ -1,3 +1,14 @@ +ver 0.22.11 (2021/08/24) +* protocol + - fix "albumart" crash +* filter + - ffmpeg: pass "channel_layout" instead of "channels" to buffersrc + - ffmpeg: fix "av_buffersink_get_frame() failed: Resource temporarily unavailable" + - ffmpeg: support double-precision samples (by converting to single precision) +* Android + - build with NDK r23 + - playlist_directory defaults to "/sdcard/Android/data/org.musicpd/files/playlists" + ver 0.22.10 (2021/08/06) * protocol - support "albumart" for virtual tracks in CUE sheets diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/android/AndroidManifest.xml new/mpd-0.22.11/android/AndroidManifest.xml --- old/mpd-0.22.10/android/AndroidManifest.xml 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/android/AndroidManifest.xml 2021-08-24 22:15:22.000000000 +0200 @@ -2,8 +2,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.musicpd" android:installLocation="auto" - android:versionCode="58" - android:versionName="0.22.10"> + android:versionCode="59" + android:versionName="0.22.11"> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/android/build.py new/mpd-0.22.11/android/build.py --- old/mpd-0.22.10/android/build.py 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/android/build.py 2021-08-24 22:15:22.000000000 +0200 @@ -24,15 +24,13 @@ 'armeabi-v7a': { 'arch': 'arm-linux-androideabi', 'ndk_arch': 'arm', - 'toolchain_arch': 'arm-linux-androideabi', 'llvm_triple': 'armv7-linux-androideabi', - 'cflags': '-fpic -march=armv7-a -mfpu=vfpv3-d16 -mfloat-abi=softfp', + 'cflags': '-fpic -mfpu=neon -mfloat-abi=softfp', }, 'arm64-v8a': { 'arch': 'aarch64-linux-android', 'ndk_arch': 'arm64', - 'toolchain_arch': 'aarch64-linux-android', 'llvm_triple': 'aarch64-linux-android', 'cflags': '-fpic', }, @@ -40,7 +38,6 @@ 'x86': { 'arch': 'i686-linux-android', 'ndk_arch': 'x86', - 'toolchain_arch': 'x86', 'llvm_triple': 'i686-linux-android', 'cflags': '-fPIC -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32', }, @@ -48,7 +45,6 @@ 'x86_64': { 'arch': 'x86_64-linux-android', 'ndk_arch': 'x86_64', - 'toolchain_arch': 'x86_64', 'llvm_triple': 'x86_64-linux-android', 'cflags': '-fPIC -m64', }, @@ -84,37 +80,28 @@ ndk_arch = abi_info['ndk_arch'] android_api_level = '21' - # select the NDK compiler - gcc_version = '4.9' - install_prefix = os.path.join(arch_path, 'root') self.arch = arch self.install_prefix = install_prefix - self.toolchain_arch = abi_info['toolchain_arch'] - toolchain_path = os.path.join(ndk_path, 'toolchains', self.toolchain_arch + '-' + gcc_version, 'prebuilt', build_arch) llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch) llvm_triple = abi_info['llvm_triple'] + android_api_level common_flags = '-Os -g' common_flags += ' ' + abi_info['cflags'] - toolchain_bin = os.path.join(toolchain_path, 'bin') llvm_bin = os.path.join(llvm_path, 'bin') self.cc = os.path.join(llvm_bin, 'clang') self.cxx = os.path.join(llvm_bin, 'clang++') - common_flags += ' -target ' + llvm_triple + ' -gcc-toolchain ' + toolchain_path + common_flags += ' -target ' + llvm_triple common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections' - # required flags from https://android.googlesource.com/platform/ndk/+/ndk-release-r20/docs/BuildSystemMaintainers.md#additional-required-arguments - common_flags += ' -fno-addrsig' - - self.ar = os.path.join(toolchain_bin, arch + '-ar') - self.ranlib = os.path.join(toolchain_bin, arch + '-ranlib') - self.nm = os.path.join(toolchain_bin, arch + '-nm') - self.strip = os.path.join(toolchain_bin, arch + '-strip') + self.ar = os.path.join(llvm_bin, 'llvm-ar') + self.ranlib = os.path.join(llvm_bin, 'llvm-ranlib') + self.nm = os.path.join(llvm_bin, 'llvm-nm') + self.strip = os.path.join(llvm_bin, 'llvm-strip') self.cflags = common_flags self.cxxflags = common_flags diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/doc/conf.py new/mpd-0.22.11/doc/conf.py --- old/mpd-0.22.10/doc/conf.py 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/doc/conf.py 2021-08-24 22:15:22.000000000 +0200 @@ -38,7 +38,7 @@ # built documents. # # The short X.Y version. -version = '0.22.10' +version = '0.22.11' # The full version, including alpha/beta/rc tags. release = version diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/doc/user.rst new/mpd-0.22.11/doc/user.rst --- old/mpd-0.22.10/doc/user.rst 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/doc/user.rst 2021-08-24 22:15:22.000000000 +0200 @@ -176,7 +176,7 @@ You need: * Android SDK -* `Android NDK r22 <https://developer.android.com/ndk/downloads>`_ +* `Android NDK r23 <https://developer.android.com/ndk/downloads>`_ * `Meson 0.49.0 <http://mesonbuild.com/>`__ and `Ninja <https://ninja-build.org/>`__ * cmake diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/meson.build new/mpd-0.22.11/meson.build --- old/mpd-0.22.10/meson.build 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/meson.build 2021-08-24 22:15:22.000000000 +0200 @@ -1,7 +1,7 @@ project( 'mpd', ['c', 'cpp'], - version: '0.22.10', + version: '0.22.11', meson_version: '>= 0.49.0', default_options: [ 'c_std=c11', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/python/build/openssl.py new/mpd-0.22.11/python/build/openssl.py --- old/mpd-0.22.10/python/build/openssl.py 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/python/build/openssl.py 2021-08-24 22:15:22.000000000 +0200 @@ -48,7 +48,6 @@ } openssl_arch = openssl_archs[toolchain.arch] - cross_compile_prefix = toolchain.toolchain_arch + '-' subprocess.check_call(['./Configure', 'no-shared', @@ -57,7 +56,6 @@ 'no-tests', 'no-asm', # "asm" causes build failures on Windows openssl_arch, - '--cross-compile-prefix=' + cross_compile_prefix, '--prefix=' + toolchain.install_prefix], cwd=src, env=toolchain.env) MakeProject.build(self, toolchain, src) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/src/Main.cxx new/mpd-0.22.11/src/Main.cxx --- old/mpd-0.22.10/src/Main.cxx 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/src/Main.cxx 2021-08-24 22:15:22.000000000 +0200 @@ -157,7 +157,17 @@ static void glue_mapper_init(const ConfigData &config) { - mapper_init(config.GetPath(ConfigOption::PLAYLIST_DIR)); + auto playlist_directory = config.GetPath(ConfigOption::PLAYLIST_DIR); + +#ifdef ANDROID + /* if there is no explicit configuration, store playlists in + "/sdcard/Android/data/org.musicpd/files/playlists" */ + if (playlist_directory.IsNull()) + playlist_directory = context->GetExternalFilesDir(Java::GetEnv(), + "playlists"); +#endif + + mapper_init(std::move(playlist_directory)); } #ifdef ENABLE_DATABASE diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/src/android/Context.cxx new/mpd-0.22.11/src/android/Context.cxx --- old/mpd-0.22.10/src/android/Context.cxx 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/src/android/Context.cxx 2021-08-24 22:15:22.000000000 +0200 @@ -27,6 +27,25 @@ #include "AudioManager.hxx" AllocatedPath +Context::GetExternalFilesDir(JNIEnv *env, const char *_type) noexcept +{ + assert(_type != nullptr); + + Java::Class cls{env, env->GetObjectClass(Get())}; + jmethodID method = env->GetMethodID(cls, "getExternalFilesDir", + "(Ljava/lang/String;)Ljava/io/File;"); + assert(method); + + Java::String type{env, _type}; + + jobject file = env->CallObjectMethod(Get(), method, type.Get()); + if (Java::DiscardException(env) || file == nullptr) + return nullptr; + + return Java::File::ToAbsolutePath(env, file); +} + +AllocatedPath Context::GetCacheDir(JNIEnv *env) const noexcept { assert(env != nullptr); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/src/android/Context.hxx new/mpd-0.22.11/src/android/Context.hxx --- old/mpd-0.22.10/src/android/Context.hxx 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/src/android/Context.hxx 2021-08-24 22:15:22.000000000 +0200 @@ -30,10 +30,14 @@ Context(JNIEnv *env, jobject obj) noexcept :Java::GlobalObject(env, obj) {} - gcc_pure + [[gnu::pure]] + AllocatedPath GetExternalFilesDir(JNIEnv *env, + const char *type) noexcept; + + [[gnu::pure]] AllocatedPath GetCacheDir(JNIEnv *env) const noexcept; - gcc_pure + [[gnu::pure]] AudioManager *GetAudioManager(JNIEnv *env) noexcept; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/src/android/Environment.hxx new/mpd-0.22.11/src/android/Environment.hxx --- old/mpd-0.22.10/src/android/Environment.hxx 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/src/android/Environment.hxx 2021-08-24 22:15:22.000000000 +0200 @@ -33,10 +33,10 @@ /** * Determine the mount point of the external SD card. */ - gcc_pure + [[gnu::pure]] AllocatedPath getExternalStorageDirectory() noexcept; - gcc_pure + [[gnu::pure]] AllocatedPath getExternalStoragePublicDirectory(const char *type) noexcept; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/src/command/FileCommands.cxx new/mpd-0.22.11/src/command/FileCommands.cxx --- old/mpd-0.22.10/src/command/FileCommands.cxx 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/src/command/FileCommands.cxx 2021-08-24 22:15:22.000000000 +0200 @@ -267,6 +267,9 @@ AtScopeExit(db, song) { db->ReturnSong(song); }; + if (song->real_uri == nullptr) + return directory_uri; + const char *real_uri = song->real_uri; /* this is a simplification which is just enough for CUE diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/src/filter/plugins/FfmpegFilter.cxx new/mpd-0.22.11/src/filter/plugins/FfmpegFilter.cxx --- old/mpd-0.22.10/src/filter/plugins/FfmpegFilter.cxx 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/src/filter/plugins/FfmpegFilter.cxx 2021-08-24 22:15:22.000000000 +0200 @@ -32,12 +32,12 @@ FfmpegFilter::FfmpegFilter(const AudioFormat &in_audio_format, const AudioFormat &_out_audio_format, Ffmpeg::FilterGraph &&_graph, - Ffmpeg::FilterContext &&_buffer_src, - Ffmpeg::FilterContext &&_buffer_sink) noexcept + AVFilterContext &_buffer_src, + AVFilterContext &_buffer_sink) noexcept :Filter(_out_audio_format), graph(std::move(_graph)), - buffer_src(std::move(_buffer_src)), - buffer_sink(std::move(_buffer_sink)), + buffer_src(_buffer_src), + buffer_sink(_buffer_sink), in_format(Ffmpeg::ToFfmpegSampleFormat(in_audio_format.format)), in_sample_rate(in_audio_format.sample_rate), in_channels(in_audio_format.channels), @@ -61,7 +61,7 @@ memcpy(frame.GetData(0), src.data, src.size); - int err = av_buffersrc_add_frame(buffer_src.get(), frame.get()); + int err = av_buffersrc_add_frame(&buffer_src, frame.get()); if (err < 0) throw MakeFfmpegError(err, "av_buffersrc_write_frame() failed"); @@ -69,7 +69,7 @@ frame.Unref(); - err = av_buffersink_get_frame(buffer_sink.get(), frame.get()); + err = av_buffersink_get_frame(&buffer_sink, frame.get()); if (err < 0) { if (err == AVERROR(EAGAIN) || err == AVERROR_EOF) return nullptr; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/src/filter/plugins/FfmpegFilter.hxx new/mpd-0.22.11/src/filter/plugins/FfmpegFilter.hxx --- old/mpd-0.22.10/src/filter/plugins/FfmpegFilter.hxx 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/src/filter/plugins/FfmpegFilter.hxx 2021-08-24 22:15:22.000000000 +0200 @@ -30,7 +30,7 @@ */ class FfmpegFilter final : public Filter { Ffmpeg::FilterGraph graph; - Ffmpeg::FilterContext buffer_src, buffer_sink; + AVFilterContext &buffer_src, &buffer_sink; Ffmpeg::Frame frame; FfmpegBuffer interleave_buffer; @@ -51,8 +51,8 @@ FfmpegFilter(const AudioFormat &in_audio_format, const AudioFormat &_out_audio_format, Ffmpeg::FilterGraph &&_graph, - Ffmpeg::FilterContext &&_buffer_src, - Ffmpeg::FilterContext &&_buffer_sink) noexcept; + AVFilterContext &_buffer_src, + AVFilterContext &_buffer_sink) noexcept; /* virtual methods from class Filter */ ConstBuffer<void> FilterPCM(ConstBuffer<void> src) override; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/src/filter/plugins/FfmpegFilterPlugin.cxx new/mpd-0.22.11/src/filter/plugins/FfmpegFilterPlugin.cxx --- old/mpd-0.22.10/src/filter/plugins/FfmpegFilterPlugin.cxx 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/src/filter/plugins/FfmpegFilterPlugin.cxx 2021-08-24 22:15:22.000000000 +0200 @@ -37,39 +37,79 @@ std::unique_ptr<Filter> Open(AudioFormat &af) override; }; +/** + * Fallback for PreparedFfmpegFilter::Open() just in case the filter's + * native output format could not be determined. + * + * TODO: improve the MPD filter API to allow returning the output + * format later, and eliminate this kludge + */ +static auto +OpenWithAformat(const char *graph_string, AudioFormat &in_audio_format) +{ + Ffmpeg::FilterGraph graph; + + auto &buffer_src = + Ffmpeg::MakeAudioBufferSource(in_audio_format, *graph); + + auto &buffer_sink = Ffmpeg::MakeAudioBufferSink(*graph); + + AudioFormat out_audio_format = in_audio_format; + auto &aformat = Ffmpeg::MakeAformat(out_audio_format, *graph); + + int error = avfilter_link(&aformat, 0, &buffer_sink, 0); + if (error < 0) + throw MakeFfmpegError(error, "avfilter_link() failed"); + + graph.ParseSingleInOut(graph_string, aformat, buffer_src); + graph.CheckAndConfigure(); + + return std::make_unique<FfmpegFilter>(in_audio_format, + out_audio_format, + std::move(graph), + buffer_src, + buffer_sink); +} + std::unique_ptr<Filter> PreparedFfmpegFilter::Open(AudioFormat &in_audio_format) { Ffmpeg::FilterGraph 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(graph_string, std::move(io_sink), - std::move(io_src)); + auto &buffer_src = + Ffmpeg::MakeAudioBufferSource(in_audio_format, *graph); - if (io.first.get() != nullptr) - throw std::runtime_error("FFmpeg filter has an open input"); + auto &buffer_sink = Ffmpeg::MakeAudioBufferSink(*graph); - if (io.second.get() != nullptr) - throw std::runtime_error("FFmpeg filter has an open output"); + /* if the filter's output format is not supported by MPD, this + "aformat" filter is inserted at the end and takes care for + the required conversion */ + auto &aformat = Ffmpeg::MakeAutoAformat(*graph); + int error = avfilter_link(&aformat, 0, &buffer_sink, 0); + if (error < 0) + throw MakeFfmpegError(error, "avfilter_link() failed"); + + graph.ParseSingleInOut(graph_string, aformat, buffer_src); graph.CheckAndConfigure(); const auto out_audio_format = - Ffmpeg::DetectFilterOutputFormat(in_audio_format, *buffer_src, - *buffer_sink); + Ffmpeg::DetectFilterOutputFormat(in_audio_format, buffer_src, + buffer_sink); + + if (!out_audio_format.IsDefined()) + /* the filter's native output format could not be + determined yet, but we need to know it now; as a + workaround for this MPD API deficiency, try again + with an "aformat" filter which forces a specific + output format */ + return OpenWithAformat(graph_string, in_audio_format); return std::make_unique<FfmpegFilter>(in_audio_format, out_audio_format, std::move(graph), - std::move(buffer_src), - std::move(buffer_sink)); + buffer_src, + buffer_sink); } static std::unique_ptr<PreparedFilter> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/src/filter/plugins/HdcdFilterPlugin.cxx new/mpd-0.22.11/src/filter/plugins/HdcdFilterPlugin.cxx --- old/mpd-0.22.10/src/filter/plugins/HdcdFilterPlugin.cxx 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/src/filter/plugins/HdcdFilterPlugin.cxx 2021-08-24 22:15:22.000000000 +0200 @@ -42,24 +42,13 @@ { Ffmpeg::FilterGraph graph; - auto buffer_src = - Ffmpeg::FilterContext::MakeAudioBufferSource(in_audio_format, - *graph); + auto &buffer_src = + Ffmpeg::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)); - - if (io.first.get() != nullptr) - throw std::runtime_error("FFmpeg filter has an open input"); - - if (io.second.get() != nullptr) - throw std::runtime_error("FFmpeg filter has an open output"); + auto &buffer_sink = Ffmpeg::MakeAudioBufferSink(*graph); + graph.ParseSingleInOut(hdcd_graph, buffer_sink, buffer_src); graph.CheckAndConfigure(); auto out_audio_format = in_audio_format; @@ -69,8 +58,8 @@ return std::make_unique<FfmpegFilter>(in_audio_format, out_audio_format, std::move(graph), - std::move(buffer_src), - std::move(buffer_sink)); + buffer_src, + buffer_sink); } class PreparedHdcdFilter final : public PreparedFilter { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/src/lib/ffmpeg/ChannelLayout.hxx new/mpd-0.22.11/src/lib/ffmpeg/ChannelLayout.hxx --- old/mpd-0.22.10/src/lib/ffmpeg/ChannelLayout.hxx 1970-01-01 01:00:00.000000000 +0100 +++ new/mpd-0.22.11/src/lib/ffmpeg/ChannelLayout.hxx 2021-08-24 22:15:22.000000000 +0200 @@ -0,0 +1,66 @@ +/* + * Copyright 2003-2021 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_CHANNEL_LAYOUT_HXX +#define MPD_FFMPEG_CHANNEL_LAYOUT_HXX + +extern "C" { +#include <libavutil/channel_layout.h> +} + +/** + * Convert a MPD channel count to a libavutil channel_layout bit mask. + */ +static constexpr uint64_t +ToFfmpegChannelLayout(unsigned channels) noexcept +{ + switch (channels) { + case 1: + return AV_CH_LAYOUT_MONO; + + case 2: + return AV_CH_LAYOUT_STEREO; + + case 3: + return AV_CH_LAYOUT_SURROUND; + + case 4: + // TODO is this AV_CH_LAYOUT_2_2? + return AV_CH_LAYOUT_QUAD; + + case 5: + // TODO is this AV_CH_LAYOUT_5POINT0_BACK? + return AV_CH_LAYOUT_5POINT0; + + case 6: + return AV_CH_LAYOUT_5POINT1; + + case 7: + return AV_CH_LAYOUT_6POINT1; + + case 8: + return AV_CH_LAYOUT_7POINT1; + + default: + /* unreachable */ + return 0; + } +} + +#endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/src/lib/ffmpeg/DetectFilterFormat.cxx new/mpd-0.22.11/src/lib/ffmpeg/DetectFilterFormat.cxx --- old/mpd-0.22.10/src/lib/ffmpeg/DetectFilterFormat.cxx 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/src/lib/ffmpeg/DetectFilterFormat.cxx 2021-08-24 22:15:22.000000000 +0200 @@ -62,8 +62,14 @@ frame.Unref(); err = av_buffersink_get_frame(&buffer_sink, frame.get()); - if (err < 0) + if (err < 0) { + if (err == AVERROR(EAGAIN)) + /* one sample was not enough input data for + the given filter graph */ + return AudioFormat::Undefined(); + throw MakeFfmpegError(err, "av_buffersink_get_frame() failed"); + } const SampleFormat sample_format = FromFfmpegSampleFormat(AVSampleFormat(frame->format)); if (sample_format == SampleFormat::UNDEFINED) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/src/lib/ffmpeg/DetectFilterFormat.hxx new/mpd-0.22.11/src/lib/ffmpeg/DetectFilterFormat.hxx --- old/mpd-0.22.10/src/lib/ffmpeg/DetectFilterFormat.hxx 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/src/lib/ffmpeg/DetectFilterFormat.hxx 2021-08-24 22:15:22.000000000 +0200 @@ -35,6 +35,9 @@ * between. * * This function can throw if the FFmpeg filter fails. + * + * @return the output format or AudioFormat::Undefined() if it was not + * possible to determine the format */ AudioFormat DetectFilterOutputFormat(const AudioFormat &in_audio_format, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/src/lib/ffmpeg/Filter.cxx new/mpd-0.22.11/src/lib/ffmpeg/Filter.cxx --- old/mpd-0.22.10/src/lib/ffmpeg/Filter.cxx 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/src/lib/ffmpeg/Filter.cxx 2021-08-24 22:15:22.000000000 +0200 @@ -18,10 +18,13 @@ */ #include "Filter.hxx" +#include "ChannelLayout.hxx" #include "SampleFormat.hxx" #include "pcm/AudioFormat.hxx" #include "util/RuntimeError.hxx" +#include <cinttypes> + #include <stdio.h> namespace Ffmpeg { @@ -36,9 +39,32 @@ return *filter; } -FilterContext -FilterContext::MakeAudioBufferSource(AudioFormat &audio_format, - AVFilterGraph &graph_ctx) +static AVFilterContext & +CreateFilter(const AVFilter &filt, + const char *name, const char *args, void *opaque, + AVFilterGraph &graph_ctx) +{ + AVFilterContext *context = nullptr; + int err = avfilter_graph_create_filter(&context, &filt, + name, args, opaque, + &graph_ctx); + if (err < 0) + throw MakeFfmpegError(err, "avfilter_graph_create_filter() failed"); + + return *context; +} + +static AVFilterContext & +CreateFilter(const AVFilter &filt, + const char *name, + AVFilterGraph &graph_ctx) +{ + return CreateFilter(filt, name, nullptr, nullptr, graph_ctx); +} + +AVFilterContext & +MakeAudioBufferSource(AudioFormat &audio_format, + AVFilterGraph &graph_ctx) { AVSampleFormat src_format = ToFfmpegSampleFormat(audio_format.format); if (src_format == AV_SAMPLE_FMT_NONE) { @@ -57,19 +83,72 @@ char abuffer_args[256]; sprintf(abuffer_args, - "sample_rate=%u:sample_fmt=%s:channels=%u:time_base=1/%u", + "sample_rate=%u:sample_fmt=%s:channel_layout=0x%" PRIx64 ":time_base=1/%u", audio_format.sample_rate, av_get_sample_fmt_name(src_format), - audio_format.channels, + ToFfmpegChannelLayout(audio_format.channels), audio_format.sample_rate); - return {RequireFilterByName("abuffer"), "abuffer", abuffer_args, nullptr, graph_ctx}; + return CreateFilter(RequireFilterByName("abuffer"), "abuffer", + abuffer_args, nullptr, graph_ctx); +} + +AVFilterContext & +MakeAudioBufferSink(AVFilterGraph &graph_ctx) +{ + return CreateFilter(RequireFilterByName("abuffersink"), "abuffersink", + graph_ctx); +} + +AVFilterContext & +MakeAformat(AudioFormat &audio_format, + AVFilterGraph &graph_ctx) +{ + AVSampleFormat dest_format = ToFfmpegSampleFormat(audio_format.format); + if (dest_format == AV_SAMPLE_FMT_NONE) { + switch (audio_format.format) { + case SampleFormat::S24_P32: + audio_format.format = SampleFormat::S32; + dest_format = AV_SAMPLE_FMT_S32; + break; + + default: + audio_format.format = SampleFormat::S16; + dest_format = AV_SAMPLE_FMT_S16; + break; + } + } + + char args[256]; + sprintf(args, + "sample_rates=%u:sample_fmts=%s:channel_layouts=0x%" PRIx64, + audio_format.sample_rate, + av_get_sample_fmt_name(dest_format), + ToFfmpegChannelLayout(audio_format.channels)); + + return CreateFilter(RequireFilterByName("aformat"), "aformat", + args, nullptr, graph_ctx); +} + +AVFilterContext & +MakeAutoAformat(AVFilterGraph &graph_ctx) +{ + return CreateFilter(RequireFilterByName("aformat"), "aformat", + "sample_fmts=flt|s32|s16", + nullptr, graph_ctx); } -FilterContext -FilterContext::MakeAudioBufferSink(AVFilterGraph &graph_ctx) +void +FilterGraph::ParseSingleInOut(const char *filters, AVFilterContext &in, + AVFilterContext &out) { - return {RequireFilterByName("abuffersink"), "abuffersink", graph_ctx}; + auto [inputs, outputs] = Parse(filters, {"out", in}, {"in", out}); + + if (inputs.get() != nullptr) + throw std::runtime_error("FFmpeg filter has an open input"); + + if (outputs.get() != nullptr) + throw std::runtime_error("FFmpeg filter has an open output"); } } // namespace Ffmpeg diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.10/src/lib/ffmpeg/Filter.hxx new/mpd-0.22.11/src/lib/ffmpeg/Filter.hxx --- old/mpd-0.22.10/src/lib/ffmpeg/Filter.hxx 2021-08-06 18:16:59.000000000 +0200 +++ new/mpd-0.22.11/src/lib/ffmpeg/Filter.hxx 2021-08-24 22:15:22.000000000 +0200 @@ -77,63 +77,38 @@ } }; -class FilterContext { - AVFilterContext *context = nullptr; - -public: - FilterContext() = default; - - FilterContext(const AVFilter &filt, - const char *name, const char *args, void *opaque, - AVFilterGraph &graph_ctx) { - int err = avfilter_graph_create_filter(&context, &filt, - name, args, opaque, - &graph_ctx); - if (err < 0) - throw MakeFfmpegError(err, "avfilter_graph_create_filter() failed"); - } - - FilterContext(const AVFilter &filt, - const char *name, - AVFilterGraph &graph_ctx) - :FilterContext(filt, name, nullptr, nullptr, graph_ctx) {} - - FilterContext(FilterContext &&src) noexcept - :context(std::exchange(src.context, nullptr)) {} - - ~FilterContext() noexcept { - if (context != nullptr) - avfilter_free(context); - } - - FilterContext &operator=(FilterContext &&src) noexcept { - using std::swap; - swap(context, src.context); - return *this; - } - - /** - * Create an "abuffer" filter. - * - * @param the input audio format; may be modified by the - * function to ask the caller to do format conversion - */ - static FilterContext MakeAudioBufferSource(AudioFormat &audio_format, - AVFilterGraph &graph_ctx); - - /** - * Create an "abuffersink" filter. - */ - static FilterContext MakeAudioBufferSink(AVFilterGraph &graph_ctx); - - auto &operator*() noexcept { - return *context; - } - - auto *get() noexcept { - return context; - } -}; +/** + * Create an "abuffer" filter. + * + * @param the input audio format; may be modified by the + * function to ask the caller to do format conversion + */ +AVFilterContext & +MakeAudioBufferSource(AudioFormat &audio_format, + AVFilterGraph &graph_ctx); + +/** + * Create an "abuffersink" filter. + */ +AVFilterContext & +MakeAudioBufferSink(AVFilterGraph &graph_ctx); + +/** + * Create an "aformat" filter. + * + * @param the output audio format; may be modified by the function if + * the given format is not supported by libavfilter + */ +AVFilterContext & +MakeAformat(AudioFormat &audio_format, + AVFilterGraph &graph_ctx); + +/** + * Create an "aformat" filter which automatically converts the output + * to a format supported by MPD. + */ +AVFilterContext & +MakeAutoAformat(AVFilterGraph &graph_ctx); class FilterGraph { AVFilterGraph *graph = nullptr; @@ -180,6 +155,9 @@ return std::make_pair(std::move(inputs), std::move(outputs)); } + void ParseSingleInOut(const char *filters, AVFilterContext &in, + AVFilterContext &out); + std::pair<FilterInOut, FilterInOut> Parse(const char *filters) { AVFilterInOut *inputs, *outputs; int err = avfilter_graph_parse2(graph, filters,
