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-02-20 22:12:43 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/mpd (Old) and /work/SRC/openSUSE:Factory/.mpd.new.28504 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "mpd" Sat Feb 20 22:12:43 2021 rev:29 rq:873980 version:0.22.6 Changes: -------- --- /work/SRC/openSUSE:Factory/mpd/mpd.changes 2021-01-25 18:23:56.892457233 +0100 +++ /work/SRC/openSUSE:Factory/.mpd.new.28504/mpd.changes 2021-02-20 22:12:49.339072992 +0100 @@ -1,0 +2,13 @@ +Fri Feb 19 23:49:35 UTC 2021 - Dirk M??ller <[email protected]> + +- update to 0.22.6: + * fix missing tags on songs in queue + - error for malformed ranges instead of ignoring silently + - better error message for open-ended range with "move" + - simple: fix missing CUE sheet metadata in "addid" command + - id: translate TPE3 to Conductor, not Performer + - iso9660: another fix for unaligned reads + - httpd: error handling on Windows improved + - pulse: fix deadlock with "always_on" + +------------------------------------------------------------------- Old: ---- mpd-0.22.4.tar.xz mpd-0.22.4.tar.xz.sig New: ---- mpd-0.22.6.tar.xz mpd-0.22.6.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ mpd.spec ++++++ --- /var/tmp/diff_new_pack.YFJ4ok/_old 2021-02-20 22:12:49.911073600 +0100 +++ /var/tmp/diff_new_pack.YFJ4ok/_new 2021-02-20 22:12:49.911073600 +0100 @@ -20,7 +20,7 @@ %bcond_with faad %bcond_without mpd_iso9660 Name: mpd -Version: 0.22.4 +Version: 0.22.6 Release: 0 Summary: Music Player Daemon License: GPL-2.0-or-later @@ -36,10 +36,10 @@ BuildRequires: cmake BuildRequires: gcc BuildRequires: gcc-c++ -BuildRequires: group(audio) BuildRequires: hicolor-icon-theme BuildRequires: libboost_headers-devel BuildRequires: libcue-devel +BuildRequires: group(audio) # MPD_ENABLE_AUTO_LIB BuildRequires: libgcrypt-devel BuildRequires: libmikmod-devel ++++++ mpd-0.22.4.tar.xz -> mpd-0.22.6.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/NEWS new/mpd-0.22.6/NEWS --- old/mpd-0.22.4/NEWS 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/NEWS 2021-02-16 13:56:14.000000000 +0100 @@ -1,3 +1,24 @@ +ver 0.22.6 (2021/02/16) +* fix missing tags on songs in queue + +ver 0.22.5 (2021/02/15) +* protocol + - error for malformed ranges instead of ignoring silently + - better error message for open-ended range with "move" +* database + - simple: fix missing CUE sheet metadata in "addid" command +* tags + - id: translate TPE3 to Conductor, not Performer +* archive + - iso9660: another fix for unaligned reads +* output + - httpd: error handling on Windows improved + - pulse: fix deadlock with "always_on" +* Windows: + - enable https:// support (via Schannel) +* Android + - work around "Permission denied" on mpd.conf + ver 0.22.4 (2021/01/21) * protocol - add command "binarylimit" to allow larger chunk sizes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/android/AndroidManifest.xml new/mpd-0.22.6/android/AndroidManifest.xml --- old/mpd-0.22.4/android/AndroidManifest.xml 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/android/AndroidManifest.xml 2021-02-16 13:56:14.000000000 +0100 @@ -2,10 +2,10 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.musicpd" android:installLocation="auto" - android:versionCode="51" - android:versionName="0.22.1"> + android:versionCode="54" + android:versionName="0.22.6"> - <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/> + <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/> <uses-feature android:name="android.software.leanback" android:required="false" /> @@ -19,6 +19,7 @@ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <application android:allowBackup="true" + android:requestLegacyExternalStorage="true" android:icon="@drawable/icon" android:banner="@drawable/icon" android:label="@string/app_name"> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/android/meson.build new/mpd-0.22.6/android/meson.build --- old/mpd-0.22.4/android/meson.build 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/android/meson.build 2021-02-16 13:56:14.000000000 +0100 @@ -5,8 +5,8 @@ android_sdk = get_option('android_sdk') android_abi = get_option('android_abi') -android_sdk_build_tools_version = '27.0.0' -android_sdk_platform = 'android-23' +android_sdk_build_tools_version = '29.0.3' +android_sdk_platform = 'android-29' android_build_tools_dir = join_paths(android_sdk, 'build-tools', android_sdk_build_tools_version) android_sdk_platform_dir = join_paths(android_sdk, 'platforms', android_sdk_platform) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/doc/conf.py new/mpd-0.22.6/doc/conf.py --- old/mpd-0.22.4/doc/conf.py 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/doc/conf.py 2021-02-16 13:56:14.000000000 +0100 @@ -38,7 +38,7 @@ # built documents. # # The short X.Y version. -version = '0.22.4' +version = '0.22.6' # The full version, including alpha/beta/rc tags. release = version diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/doc/developer.rst new/mpd-0.22.6/doc/developer.rst --- old/mpd-0.22.4/doc/developer.rst 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/doc/developer.rst 2021-02-16 13:56:14.000000000 +0100 @@ -68,11 +68,11 @@ - the "unstable" branch called ``master`` where new features are merged. This will become the next major release eventually. -- the "stable" branch (currently called ``v0.21.x``) where only bug +- the "stable" branch (currently called ``v0.22.x``) where only bug fixes are merged. -Once :program:`MPD` 0.22 is released, a new branch called ``v0.22.x`` -will be created for 0.22 bug-fix releases; after that, ``v0.21.x`` +Once :program:`MPD` 0.23 is released, a new branch called ``v0.23.x`` +will be created for 0.23 bug-fix releases; after that, ``v0.22.x`` will eventually cease to be maintained. After bug fixes have been added to the "stable" branch, it will be diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/doc/protocol.rst new/mpd-0.22.6/doc/protocol.rst --- old/mpd-0.22.4/doc/protocol.rst 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/doc/protocol.rst 2021-02-16 13:56:14.000000000 +0100 @@ -677,6 +677,11 @@ (directories add recursively). ``URI`` can also be a single file. + Clients that are connected via local socket may add arbitrary + local files (URI is an absolute path). Exmaple:: + + add "/home/foo/Music/bar.ogg" + .. _command_addid: :command:`addid {URI} [POSITION]` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/meson.build new/mpd-0.22.6/meson.build --- old/mpd-0.22.4/meson.build 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/meson.build 2021-02-16 13:56:14.000000000 +0100 @@ -1,7 +1,7 @@ project( 'mpd', ['c', 'cpp'], - version: '0.22.4', + version: '0.22.6', meson_version: '>= 0.49.0', default_options: [ 'c_std=c11', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/python/build/libs.py new/mpd-0.22.6/python/build/libs.py --- old/mpd-0.22.4/python/build/libs.py 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/python/build/libs.py 2021-02-16 13:56:14.000000000 +0100 @@ -407,6 +407,9 @@ '--disable-progress-meter', '--disable-alt-svc', '--without-gnutls', '--without-nss', '--without-libssh2', + + # native Windows SSL/TLS support, option ignored on non-Windows builds + '--with-schannel', ], patches='src/lib/curl/patches', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/archive/plugins/Iso9660ArchivePlugin.cxx new/mpd-0.22.6/src/archive/plugins/Iso9660ArchivePlugin.cxx --- old/mpd-0.22.4/src/archive/plugins/Iso9660ArchivePlugin.cxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/archive/plugins/Iso9660ArchivePlugin.cxx 2021-02-16 13:56:14.000000000 +0100 @@ -221,8 +221,8 @@ if (new_offset > size) throw std::runtime_error("Invalid seek offset"); + offset = new_offset; skip = new_offset % ISO_BLOCKSIZE; - offset = new_offset - skip; buffer.Clear(); } }; @@ -260,13 +260,13 @@ if (r.empty()) { /* the buffer is empty - read more data from the ISO file */ - assert(offset % ISO_BLOCKSIZE == 0); + assert((offset - skip) % ISO_BLOCKSIZE == 0); const ScopeUnlock unlock(mutex); const lsn_t read_lsn = lsn + offset / ISO_BLOCKSIZE; - if (read_size >= ISO_BLOCKSIZE) { + if (read_size >= ISO_BLOCKSIZE && skip == 0) { /* big read - read right into the caller's buffer */ auto nbytes = iso->SeekRead(ptr, read_lsn, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/command/QueueCommands.cxx new/mpd-0.22.6/src/command/QueueCommands.cxx --- old/mpd-0.22.4/src/command/QueueCommands.cxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/command/QueueCommands.cxx 2021-02-16 13:56:14.000000000 +0100 @@ -326,6 +326,11 @@ handle_move(Client &client, Request args, [[maybe_unused]] Response &r) { RangeArg range = args.ParseRange(0); + if (range.IsOpenEnded()) { + r.Error(ACK_ERROR_ARG, "Open-ended range not supported"); + return CommandResult::ERROR; + } + int to = args.ParseInt(1); client.GetPartition().MoveRange(range.start, range.end, to); return CommandResult::OK; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/db/plugins/simple/ExportedSong.hxx new/mpd-0.22.6/src/db/plugins/simple/ExportedSong.hxx --- old/mpd-0.22.4/src/db/plugins/simple/ExportedSong.hxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/db/plugins/simple/ExportedSong.hxx 2021-02-16 13:56:14.000000000 +0100 @@ -29,6 +29,12 @@ * a #LightSong, e.g. a merged #Tag. */ class ExportedSong : public LightSong { + /** + * A reference target for LightSong::tag, but it is only used + * if this instance "owns" the #Tag. For instances referring + * to a foreign #Tag instance (e.g. a Song::tag), this field + * is not used (and empty). + */ Tag tag_buffer; public: @@ -37,6 +43,25 @@ ExportedSong(const char *_uri, Tag &&_tag) noexcept :LightSong(_uri, tag_buffer), tag_buffer(std::move(_tag)) {} + + /* this custom move constructor is necessary so LightSong::tag + points to this instance's #Tag field instead of leaving a + dangling reference to the source object's #Tag field */ + ExportedSong(ExportedSong &&src) noexcept + :LightSong(src, + /* refer to tag_buffer only if the + moved-from instance also owned the Tag + which its LightSong::tag field refers + to */ + OwnsTag() ? tag_buffer : src.tag), + tag_buffer(std::move(src.tag_buffer)) {} + + ExportedSong &operator=(ExportedSong &&) = delete; + +private: + bool OwnsTag() const noexcept { + return &tag == &tag_buffer; + } }; #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/event/BufferedSocket.cxx new/mpd-0.22.6/src/event/BufferedSocket.cxx --- old/mpd-0.22.4/src/event/BufferedSocket.cxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/event/BufferedSocket.cxx 2021-02-16 13:56:14.000000000 +0100 @@ -36,7 +36,7 @@ } const auto code = GetSocketError(); - if (IsSocketErrorAgain(code)) + if (IsSocketErrorReceiveWouldBlock(code)) return 0; if (IsSocketErrorClosed(code)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/event/FullyBufferedSocket.cxx new/mpd-0.22.6/src/event/FullyBufferedSocket.cxx --- old/mpd-0.22.4/src/event/FullyBufferedSocket.cxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/event/FullyBufferedSocket.cxx 2021-02-16 13:56:14.000000000 +0100 @@ -31,7 +31,7 @@ const auto nbytes = GetSocket().Write((const char *)data, length); if (gcc_unlikely(nbytes < 0)) { const auto code = GetSocketError(); - if (IsSocketErrorAgain(code)) + if (IsSocketErrorSendWouldBlock(code)) return 0; IdleMonitor::Cancel(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/net/SocketError.cxx new/mpd-0.22.6/src/net/SocketError.cxx --- old/mpd-0.22.4/src/net/SocketError.cxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/net/SocketError.cxx 2021-02-16 13:56:14.000000000 +0100 @@ -1,20 +1,30 @@ /* - * Copyright 2003-2021 The Music Player Daemon Project - * http://www.musicpd.org + * Copyright 2015-2021 Max Kellermann <[email protected]> * - * 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. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: * - * 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. + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. * - * 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. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "SocketError.hxx" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/net/SocketError.hxx new/mpd-0.22.6/src/net/SocketError.hxx --- old/mpd-0.22.4/src/net/SocketError.hxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/net/SocketError.hxx 2021-02-16 13:56:14.000000000 +0100 @@ -1,24 +1,34 @@ /* - * Copyright 2003-2021 The Music Player Daemon Project - * http://www.musicpd.org + * Copyright 2015-2021 Max Kellermann <[email protected]> * - * 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. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: * - * 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. + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. * - * 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. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef MPD_SOCKET_ERROR_HXX -#define MPD_SOCKET_ERROR_HXX +#ifndef SOCKET_ERROR_HXX +#define SOCKET_ERROR_HXX #include "util/Compiler.h" #include "system/Error.hxx" @@ -42,14 +52,79 @@ #endif } -gcc_const -static inline bool -IsSocketErrorAgain(socket_error_t code) noexcept +constexpr bool +IsSocketErrorInProgress(socket_error_t code) noexcept { #ifdef _WIN32 return code == WSAEINPROGRESS; #else - return code == EAGAIN; + return code == EINPROGRESS; +#endif +} + +constexpr bool +IsSocketErrorWouldBlock(socket_error_t code) noexcept +{ +#ifdef _WIN32 + return code == WSAEWOULDBLOCK; +#else + return code == EWOULDBLOCK; +#endif +} + +constexpr bool +IsSocketErrorConnectWouldBlock(socket_error_t code) noexcept +{ +#if defined(_WIN32) || defined(__linux__) + /* on Windows, WSAEINPROGRESS is for blocking sockets and + WSAEWOULDBLOCK for non-blocking sockets */ + /* on Linux, EAGAIN==EWOULDBLOCK is for local sockets and + EINPROGRESS is for all other sockets */ + return IsSocketErrorInProgress(code) || IsSocketErrorWouldBlock(code); +#else + /* on all other operating systems, there's just EINPROGRESS */ + return IsSocketErrorInProgress(code); +#endif +} + +constexpr bool +IsSocketErrorSendWouldBlock(socket_error_t code) noexcept +{ +#ifdef _WIN32 + /* on Windows, WSAEINPROGRESS is for blocking sockets and + WSAEWOULDBLOCK for non-blocking sockets */ + return IsSocketErrorInProgress(code) || IsSocketErrorWouldBlock(code); +#else + /* on all other operating systems, there's just EAGAIN==EWOULDBLOCK */ + return IsSocketErrorWouldBlock(code); +#endif +} + +constexpr bool +IsSocketErrorReceiveWouldBlock(socket_error_t code) noexcept +{ +#ifdef _WIN32 + /* on Windows, WSAEINPROGRESS is for blocking sockets and + WSAEWOULDBLOCK for non-blocking sockets */ + return IsSocketErrorInProgress(code) || IsSocketErrorWouldBlock(code); +#else + /* on all other operating systems, there's just + EAGAIN==EWOULDBLOCK */ + return IsSocketErrorWouldBlock(code); +#endif +} + +constexpr bool +IsSocketErrorAcceptWouldBlock(socket_error_t code) noexcept +{ +#ifdef _WIN32 + /* on Windows, WSAEINPROGRESS is for blocking sockets and + WSAEWOULDBLOCK for non-blocking sockets */ + return IsSocketErrorInProgress(code) || IsSocketErrorWouldBlock(code); +#else + /* on all other operating systems, there's just + EAGAIN==EWOULDBLOCK */ + return IsSocketErrorWouldBlock(code); #endif } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/output/Control.hxx new/mpd-0.22.6/src/output/Control.hxx --- old/mpd-0.22.4/src/output/Control.hxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/output/Control.hxx 2021-02-16 13:56:14.000000000 +0100 @@ -182,6 +182,14 @@ bool open = false; /** + * Is the device currently playing, i.e. is its buffer + * (likely) non-empty? If not, then it will never be drained. + * + * This field is only valid while the output is open. + */ + bool playing; + + /** * Is the device paused? i.e. the output thread is in the * ao_pause() loop. */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/output/Thread.cxx new/mpd-0.22.6/src/output/Thread.cxx --- old/mpd-0.22.4/src/output/Thread.cxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/output/Thread.cxx 2021-02-16 13:56:14.000000000 +0100 @@ -53,7 +53,7 @@ if (open && cf != output->filter_audio_format) /* if the filter's output format changes, the output must be reopened as well */ - InternalCloseOutput(true); + InternalCloseOutput(playing); output->filter_audio_format = cf; @@ -64,6 +64,7 @@ } open = true; + playing = false; } else if (in_audio_format != output->out_audio_format) { /* reconfigure the final ConvertFilter for its new input AudioFormat */ @@ -285,6 +286,9 @@ assert(nbytes % output->out_audio_format.GetFrameSize() == 0); source.ConsumeData(nbytes); + + /* there's data to be drained from now on */ + playing = true; } return true; @@ -371,6 +375,9 @@ } skip_delay = true; + + /* ignore drain commands until we got something new to play */ + playing = false; } static void @@ -390,6 +397,10 @@ inline void AudioOutputControl::InternalDrain() noexcept { + /* after this method finishes, there's nothing left to be + drained */ + playing = false; + try { /* flush the filter and play its remaining output */ @@ -518,6 +529,7 @@ source.Cancel(); if (open) { + playing = false; const ScopeUnlock unlock(mutex); output->Cancel(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/output/plugins/PulseOutputPlugin.cxx new/mpd-0.22.6/src/output/plugins/PulseOutputPlugin.cxx --- old/mpd-0.22.4/src/output/plugins/PulseOutputPlugin.cxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/output/plugins/PulseOutputPlugin.cxx 2021-02-16 13:56:14.000000000 +0100 @@ -56,8 +56,6 @@ size_t writable; - bool pause; - /** * Was Interrupt() called? This will unblock Play(). It will * be reset by Cancel() and Pause(), as documented by the @@ -113,6 +111,7 @@ [[nodiscard]] std::chrono::steady_clock::duration Delay() const noexcept override; size_t Play(const void *chunk, size_t size) override; + void Drain() override; void Cancel() noexcept override; bool Pause() override; @@ -688,7 +687,6 @@ "pa_stream_connect_playback() has failed"); } - pause = false; interrupted = false; } @@ -699,17 +697,6 @@ Pulse::LockGuard lock(mainloop); - if (pa_stream_get_state(stream) == PA_STREAM_READY) { - pa_operation *o = - pa_stream_drain(stream, - pulse_output_stream_success_cb, this); - if (o == nullptr) { - LogPulseError(context, - "pa_stream_drain() has failed"); - } else - pulse_wait_for_operation(mainloop, o); - } - DeleteStream(); if (context != nullptr && @@ -780,7 +767,7 @@ Pulse::LockGuard lock(mainloop); auto result = std::chrono::steady_clock::duration::zero(); - if (pause && pa_stream_is_corked(stream) && + if (pa_stream_is_corked(stream) && pa_stream_get_state(stream) == PA_STREAM_READY) /* idle while paused */ result = std::chrono::seconds(1); @@ -796,8 +783,6 @@ Pulse::LockGuard lock(mainloop); - pause = false; - /* check if the stream is (already) connected */ WaitStream(); @@ -841,6 +826,25 @@ } void +PulseOutput::Drain() +{ + Pulse::LockGuard lock(mainloop); + + if (pa_stream_get_state(stream) != PA_STREAM_READY || + pa_stream_is_suspended(stream) || + pa_stream_is_corked(stream)) + return; + + pa_operation *o = + pa_stream_drain(stream, + pulse_output_stream_success_cb, this); + if (o == nullptr) + throw MakePulseError(context, "pa_stream_drain() failed"); + + pulse_wait_for_operation(mainloop, o); +} + +void PulseOutput::Cancel() noexcept { assert(mainloop != nullptr); @@ -876,7 +880,6 @@ Pulse::LockGuard lock(mainloop); - pause = true; interrupted = false; /* check if the stream is (already/still) connected */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/output/plugins/WasapiOutputPlugin.cxx new/mpd-0.22.6/src/output/plugins/WasapiOutputPlugin.cxx --- old/mpd-0.22.4/src/output/plugins/WasapiOutputPlugin.cxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/output/plugins/WasapiOutputPlugin.cxx 2021-02-16 13:56:14.000000000 +0100 @@ -24,6 +24,7 @@ #include "mixer/MixerList.hxx" #include "thread/Cond.hxx" #include "thread/Mutex.hxx" +#include "thread/Name.hxx" #include "thread/Thread.hxx" #include "util/AllocatedString.hxx" #include "util/Domain.hxx" @@ -231,6 +232,7 @@ } void WasapiOutputThread::Work() noexcept { + SetThreadName("Wasapi Output Worker"); FormatDebug(wasapi_output_domain, "Working thread started"); try { com.emplace(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/output/plugins/httpd/HttpdClient.cxx new/mpd-0.22.6/src/output/plugins/httpd/HttpdClient.cxx --- old/mpd-0.22.4/src/output/plugins/httpd/HttpdClient.cxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/output/plugins/httpd/HttpdClient.cxx 2021-02-16 13:56:14.000000000 +0100 @@ -278,7 +278,7 @@ metadata_current_position); if (nbytes < 0) { auto e = GetSocketError(); - if (IsSocketErrorAgain(e)) + if (IsSocketErrorSendWouldBlock(e)) return true; if (!IsSocketErrorClosed(e)) { @@ -305,7 +305,7 @@ ssize_t nbytes = GetSocket().Write(&empty_data, 1); if (nbytes < 0) { auto e = GetSocketError(); - if (IsSocketErrorAgain(e)) + if (IsSocketErrorSendWouldBlock(e)) return true; if (!IsSocketErrorClosed(e)) { @@ -328,7 +328,7 @@ bytes_to_write); if (nbytes < 0) { auto e = GetSocketError(); - if (IsSocketErrorAgain(e)) + if (IsSocketErrorSendWouldBlock(e)) return true; if (!IsSocketErrorClosed(e)) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/protocol/ArgParser.cxx new/mpd-0.22.6/src/protocol/ArgParser.cxx --- old/mpd-0.22.4/src/protocol/ArgParser.cxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/protocol/ArgParser.cxx 2021-02-16 13:56:14.000000000 +0100 @@ -94,7 +94,7 @@ s); if (test == test2) - value = std::numeric_limits<int>::max(); + return RangeArg::OpenEnded(range.start); if (value < 0) throw FormatProtocolError(ACK_ERROR_ARG, @@ -107,9 +107,13 @@ range.end = (unsigned)value; } else { - range.end = (unsigned)value + 1; + return RangeArg::Single(range.start); } + if (!range.IsWellFormed()) + throw FormatProtocolError(ACK_ERROR_ARG, + "Malformed range: %s", s); + return range; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/protocol/RangeArg.hxx new/mpd-0.22.6/src/protocol/RangeArg.hxx --- old/mpd-0.22.4/src/protocol/RangeArg.hxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/protocol/RangeArg.hxx 2021-02-16 13:56:14.000000000 +0100 @@ -25,8 +25,22 @@ struct RangeArg { unsigned start, end; - static constexpr RangeArg All() { - return { 0, std::numeric_limits<unsigned>::max() }; + /** + * Construct an open-ended range starting at the given index. + */ + static constexpr RangeArg OpenEnded(unsigned start) noexcept { + return { start, std::numeric_limits<unsigned>::max() }; + } + + static constexpr RangeArg All() noexcept { + return OpenEnded(0); + } + + /** + * Construct an instance describing exactly one index. + */ + static constexpr RangeArg Single(unsigned i) noexcept { + return { i, i + 1 }; } constexpr bool operator==(RangeArg other) const noexcept { @@ -37,13 +51,45 @@ return !(*this == other); } + constexpr bool IsOpenEnded() const noexcept { + return end == All().end; + } + constexpr bool IsAll() const noexcept { return *this == All(); } + constexpr bool IsWellFormed() const noexcept { + return start <= end; + } + + /** + * Is this range empty? A malformed range also counts as + * "empty" for this method. + */ + constexpr bool IsEmpty() const noexcept { + return start >= end; + } + + /** + * Check if the range contains at least this number of items. + * Unlike Count(), this allows the object to be malformed. + */ + constexpr bool HasAtLeast(unsigned n) const noexcept { + return start + n <= end; + } + constexpr bool Contains(unsigned i) const noexcept { return i >= start && i < end; } + + /** + * Count the number of items covered by this range. This requires the + * object to be well-formed. + */ + constexpr unsigned Count() const noexcept { + return end - start; + } }; #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/song/LightSong.hxx new/mpd-0.22.6/src/song/LightSong.hxx --- old/mpd-0.22.4/src/song/LightSong.hxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/song/LightSong.hxx 2021-02-16 13:56:14.000000000 +0100 @@ -88,6 +88,19 @@ LightSong(const char *_uri, const Tag &_tag) noexcept :uri(_uri), tag(_tag) {} + /** + * A copy constructor which copies all fields, but only sets + * the tag to a caller-provided reference. This is used by + * the #ExportedSong move constructor. + */ + LightSong(const LightSong &src, const Tag &_tag) noexcept + :directory(src.directory), uri(src.uri), + real_uri(src.real_uri), + tag(_tag), + mtime(src.mtime), + start_time(src.start_time), end_time(src.end_time), + audio_format(src.audio_format) {} + gcc_pure std::string GetURI() const noexcept { if (directory == nullptr) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/tag/Id3Scan.cxx new/mpd-0.22.6/src/tag/Id3Scan.cxx --- old/mpd-0.22.4/src/tag/Id3Scan.cxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/tag/Id3Scan.cxx 2021-02-16 13:56:14.000000000 +0100 @@ -352,7 +352,7 @@ handler); tag_id3_import_text(tag, ID3_FRAME_COMPOSER, TAG_COMPOSER, handler); - tag_id3_import_text(tag, "TPE3", TAG_PERFORMER, + tag_id3_import_text(tag, "TPE3", TAG_CONDUCTOR, handler); tag_id3_import_text(tag, "TPE4", TAG_PERFORMER, handler); tag_id3_import_text(tag, "TIT1", TAG_GROUPING, handler); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/win32/ComPtr.hxx new/mpd-0.22.6/src/win32/ComPtr.hxx --- old/mpd-0.22.4/src/win32/ComPtr.hxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/win32/ComPtr.hxx 2021-02-16 13:56:14.000000000 +0100 @@ -112,6 +112,6 @@ void swap(ComPtr<T> &lhs, ComPtr<T> &rhs) noexcept { lhs.swap(rhs); } -} +} // namespace std #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/src/win32/HResult.hxx new/mpd-0.22.6/src/win32/HResult.hxx --- old/mpd-0.22.4/src/win32/HResult.hxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/src/win32/HResult.hxx 2021-02-16 13:56:14.000000000 +0100 @@ -59,6 +59,7 @@ C(E_INVALIDARG); C(E_OUTOFMEMORY); C(E_POINTER); + C(NO_ERROR); #undef C } return std::string_view(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mpd-0.22.4/test/run_input.cxx new/mpd-0.22.6/test/run_input.cxx --- old/mpd-0.22.4/test/run_input.cxx 2021-01-21 17:21:20.000000000 +0100 +++ new/mpd-0.22.6/test/run_input.cxx 2021-02-16 13:56:14.000000000 +0100 @@ -56,6 +56,8 @@ FromNarrowPath config_path; + std::size_t seek = 0; + std::size_t chunk_size = MAX_CHUNK_SIZE; bool verbose = false; @@ -67,6 +69,7 @@ OPTION_CONFIG, OPTION_VERBOSE, OPTION_SCAN, + OPTION_SEEK, OPTION_CHUNK_SIZE, }; @@ -74,6 +77,7 @@ {"config", 0, true, "Load a MPD configuration file"}, {"verbose", 'v', false, "Verbose logging"}, {"scan", 0, false, "Scan tags instead of reading raw data"}, + {"seek", 0, true, "Start reading at this position"}, {"chunk-size", 0, true, "Read this number of bytes at a time"}, }; @@ -108,6 +112,10 @@ c.scan = true; break; + case OPTION_SEEK: + c.seek = ParseSize(o.value); + break; + case OPTION_CHUNK_SIZE: c.chunk_size = ParseSize(o.value); if (c.chunk_size <= 0 || c.chunk_size > MAX_CHUNK_SIZE) @@ -118,7 +126,7 @@ auto args = option_parser.GetRemaining(); if (args.size != 1) - throw std::runtime_error("Usage: run_input [--verbose] [--config=FILE] URI"); + throw std::runtime_error("Usage: run_input [--verbose] [--config=FILE] [--scan] [--chunk-size=BYTES] URI"); c.uri = args.front(); return c; @@ -153,10 +161,14 @@ } static int -dump_input_stream(InputStream &is, FileDescriptor out, size_t chunk_size) +dump_input_stream(InputStream &is, FileDescriptor out, + offset_type seek, size_t chunk_size) { std::unique_lock<Mutex> lock(is.mutex); + if (seek > 0) + is.Seek(lock, seek); + /* print meta data */ if (is.HasMimeType()) @@ -256,7 +268,7 @@ Mutex mutex; auto is = InputStream::OpenReady(c.uri, mutex); return dump_input_stream(*is, FileDescriptor(STDOUT_FILENO), - c.chunk_size); + c.seek, c.chunk_size); } catch (...) { PrintException(std::current_exception()); return EXIT_FAILURE;
