diff --git a/NEWS b/NEWS index 9dcde8cfc..1297825a0 100644 --- a/NEWS +++ b/NEWS @@ -31,11 +31,18 @@ ver 0.21 (not yet released) - opus: support for sending metadata using ogg stream chaining * require GCC 5.0 -ver 0.20.19 (not yet released) +ver 0.20.19 (2018/04/26) * protocol - validate absolute seek time, reject negative values +* database + - proxy: fix "search already in progress" errors + - proxy: implement "list ... group" * input - mms: fix lockup bug and a crash bug +* decoder + - ffmpeg: fix av_register_all() deprecation warning (FFmpeg 4.0) +* player + - fix spurious "Not seekable" error when switching radio streams * macOS: fix crash bug ver 0.20.18 (2018/02/24) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index afa8c8475..16f96a9cf 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -2,8 +2,8 @@ + android:versionCode="18" + android:versionName="0.20.19"> diff --git a/python/build/libs.py b/python/build/libs.py index 1c99a37cb..c11be6188 100644 --- a/python/build/libs.py +++ b/python/build/libs.py @@ -17,8 +17,8 @@ libogg = AutotoolsProject( ) libvorbis = AutotoolsProject( - 'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.5.tar.xz', - '28cb28097c07a735d6af56e598e1c90f', + 'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.6.tar.xz', + 'af00bb5a784e7c9e69f56823de4637c350643deedaf333d0fa86ecdba6fcb415', 'lib/libvorbis.a', [ '--disable-shared', '--enable-static', @@ -105,8 +105,8 @@ liblame = AutotoolsProject( ) ffmpeg = FfmpegProject( - 'http://ffmpeg.org/releases/ffmpeg-3.4.2.tar.xz', - '2b92e9578ef8b3e49eeab229e69305f5f4cbc1fdaa22e927fc7fca18acccd740', + 'http://ffmpeg.org/releases/ffmpeg-4.0.tar.xz', + 'ed945daf40b124e77a685893cc025d086f638bc703183460aff49508edb3a43f', 'lib/libavcodec.a', [ '--disable-shared', '--enable-static', diff --git a/src/db/plugins/ProxyDatabasePlugin.cxx b/src/db/plugins/ProxyDatabasePlugin.cxx index 8a896c9e6..a33e02ca8 100644 --- a/src/db/plugins/ProxyDatabasePlugin.cxx +++ b/src/db/plugins/ProxyDatabasePlugin.cxx @@ -34,6 +34,7 @@ #include "tag/Builder.hxx" #include "tag/Tag.hxx" #include "tag/Mask.hxx" +#include "tag/ParseName.hxx" #include "util/ScopeExit.hxx" #include "util/RuntimeError.hxx" #include "protocol/Ack.hxx" @@ -335,6 +336,35 @@ SendConstraints(mpd_connection *connection, const DatabaseSelection &selection) return true; } +static bool +SendGroupMask(mpd_connection *connection, TagMask mask) +{ +#if LIBMPDCLIENT_CHECK_VERSION(2,12,0) + for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) { + const auto tag_type = TagType(i); + if (!mask.Test(tag_type)) + continue; + + const auto tag = Convert(tag_type); + if (tag == MPD_TAG_COUNT) + throw std::runtime_error("Unsupported tag"); + + if (!mpd_search_add_group_tag(connection, tag)) + return false; + } + + return true; +#else + (void)connection; + (void)mask; + + if (mask.TestAny()) + throw std::runtime_error("Grouping requires libmpdclient 2.12"); + + return true; +#endif +} + ProxyDatabase::ProxyDatabase(EventLoop &_loop, DatabaseListener &_listener, const ConfigBlock &block) :Database(proxy_db_plugin), @@ -700,7 +730,7 @@ static void SearchSongs(struct mpd_connection *connection, const DatabaseSelection &selection, VisitSong visit_song) -{ +try { assert(selection.recursive); assert(visit_song); @@ -727,6 +757,11 @@ SearchSongs(struct mpd_connection *connection, if (!mpd_response_finish(connection)) ThrowError(connection); +} catch (...) { + if (connection != nullptr) + mpd_search_cancel(connection); + + throw; } /** @@ -774,9 +809,9 @@ ProxyDatabase::Visit(const DatabaseSelection &selection, void ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection, TagType tag_type, - gcc_unused TagMask group_mask, + TagMask group_mask, VisitTag visit_tag) const -{ +try { // TODO: eliminate the const_cast const_cast(this)->EnsureConnected(); @@ -785,32 +820,47 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection, throw std::runtime_error("Unsupported tag"); if (!mpd_search_db_tags(connection, tag_type2) || - !SendConstraints(connection, selection)) + !SendConstraints(connection, selection) || + !SendGroupMask(connection, group_mask)) ThrowError(connection); - // TODO: use group_mask - if (!mpd_search_commit(connection)) ThrowError(connection); - while (auto *pair = mpd_recv_pair_tag(connection, tag_type2)) { + TagBuilder builder; + + while (auto *pair = mpd_recv_pair(connection)) { AtScopeExit(this, pair) { mpd_return_pair(connection, pair); }; - TagBuilder tag; - tag.AddItem(tag_type, pair->value); + const auto current_type = tag_name_parse_i(pair->name); + if (current_type == TAG_NUM_OF_ITEM_TYPES) + continue; - if (tag.empty()) + if (current_type == tag_type && !builder.empty()) { + try { + visit_tag(builder.Commit()); + } catch (...) { + mpd_response_finish(connection); + throw; + } + } + + builder.AddItem(current_type, pair->value); + + if (!builder.HasType(current_type)) /* if no tag item has been added, then the given value was not acceptable (e.g. empty); forcefully insert an empty tag in this case, as the caller expects the given tag type to be present */ - tag.AddEmptyItem(tag_type); + builder.AddEmptyItem(current_type); + } + if (!builder.empty()) { try { - visit_tag(tag.Commit()); + visit_tag(builder.Commit()); } catch (...) { mpd_response_finish(connection); throw; @@ -819,6 +869,11 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection, if (!mpd_response_finish(connection)) ThrowError(connection); +} catch (...) { + if (connection != nullptr) + mpd_search_cancel(connection); + + throw; } DatabaseStats diff --git a/src/decoder/DecoderControl.hxx b/src/decoder/DecoderControl.hxx index a30ebe722..1d792520d 100644 --- a/src/decoder/DecoderControl.hxx +++ b/src/decoder/DecoderControl.hxx @@ -304,6 +304,11 @@ struct DecoderControl { gcc_pure bool IsCurrentSong(const DetachedSong &_song) const noexcept; + gcc_pure + bool IsSeekableCurrentSong(const DetachedSong &_song) const noexcept { + return seekable && IsCurrentSong(_song); + } + private: /** * Wait for the command to be finished by the decoder thread. diff --git a/src/lib/ffmpeg/Init.cxx b/src/lib/ffmpeg/Init.cxx index 7f11a72d4..9a7872618 100644 --- a/src/lib/ffmpeg/Init.cxx +++ b/src/lib/ffmpeg/Init.cxx @@ -33,6 +33,9 @@ FfmpegInit() { av_log_set_callback(FfmpegLogCallback); +#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100) + /* deprecated as of FFmpeg 4.0 */ av_register_all(); +#endif } diff --git a/src/player/Thread.cxx b/src/player/Thread.cxx index 2ffa5d933..c599b1725 100644 --- a/src/player/Thread.cxx +++ b/src/player/Thread.cxx @@ -577,7 +577,7 @@ Player::SeekDecoder() noexcept pc.outputs.Cancel(); } - if (!dc.IsCurrentSong(*pc.next_song)) { + if (!dc.IsSeekableCurrentSong(*pc.next_song)) { /* the decoder is already decoding the "next" song - stop it and start the previous song again */