diff --git a/NEWS b/NEWS index 89dcf7526d..b26e356a28 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,12 @@ ver 0.25 (not yet released) * switch to C++23 * require Meson 1.2 +ver 0.24.10 (not yet released) +* database + - upnp: allow building with libupnp 1.14.30 which has fixed the API breakage +* Windows + - work around build failure due to zlib bug + ver 0.24.9 (2026/03/11) * input - curl: fix build failure after CURL 8.19 API change diff --git a/python/build/libs.py b/python/build/libs.py index e3e786fc0f..6cb19360ed 100644 --- a/python/build/libs.py +++ b/python/build/libs.py @@ -26,6 +26,7 @@ '-DZLIB_BUILD_TESTING=OFF', '-DZLIB_BUILD_SHARED=OFF', ], + patches='src/lib/zlib/patches', ) libmodplug = AutotoolsProject( @@ -82,8 +83,8 @@ ) ffmpeg = FfmpegProject( - 'http://ffmpeg.org/releases/ffmpeg-8.0.1.tar.xz', - '05ee0b03119b45c0bdb4df654b96802e909e0a752f72e4fe3794f487229e5a41', + 'http://ffmpeg.org/releases/ffmpeg-8.1.tar.xz', + 'b072aed6871998cce9b36e7774033105ca29e33632be5b6347f3206898e0756a', 'lib/libavcodec.a', [ '--disable-shared', '--enable-static', @@ -514,7 +515,6 @@ '--disable-decoder=vp9_qsv', '--disable-decoder=vp9_rkmpp', '--disable-decoder=vp9_v4l2m2m', - '--disable-decoder=vp9_vucid', '--disable-decoder=vplayer', '--disable-decoder=vqa', '--disable-decoder=webvtt', @@ -554,7 +554,6 @@ '--disable-bsf=mjpeg2jpeg', '--disable-bsf=opus_metadata', '--disable-bsf=pgs_frame_merge', - '--disable-bsf=prores', '--disable-bsf=text2movsub', '--disable-bsf=vp9_metadata', '--disable-bsf=vp9_raw_reorder', diff --git a/src/decoder/Control.hxx b/src/decoder/Control.hxx index 70013e4f7d..ca1e2c169a 100644 --- a/src/decoder/Control.hxx +++ b/src/decoder/Control.hxx @@ -261,25 +261,17 @@ public: bool _seekable, SignedSongTime _duration) noexcept; /** - * Checks whether an error has occurred, and if so, rethrows + * Checks whether an error has occurred, and if so, returns * it. * * Caller must lock the object. */ - void CheckRethrowError() const { + std::exception_ptr GetError() const noexcept { assert(command == DecoderCommand::NONE); - assert(state != DecoderState::ERROR || error); + assert(state == DecoderState::ERROR); + assert(error); - if (state == DecoderState::ERROR) - std::rethrow_exception(error); - } - - /** - * Like CheckRethrowError(), but locks and unlocks the object. - */ - void LockCheckRethrowError() const { - const std::scoped_lock protect{mutex}; - CheckRethrowError(); + return error; } /** diff --git a/src/input/plugins/CurlInputPlugin.cxx b/src/input/plugins/CurlInputPlugin.cxx index b3a4e45b22..7512d5b563 100644 --- a/src/input/plugins/CurlInputPlugin.cxx +++ b/src/input/plugins/CurlInputPlugin.cxx @@ -58,7 +58,7 @@ static const size_t CURL_RESUME_AT = 384 * 1024; class CurlInputStream final : public AsyncInputStream, CurlResponseHandler { /* some buffers which were passed to libcurl, which we have - too free */ + to free */ CurlSlist request_headers; CurlRequest *request = nullptr; @@ -348,7 +348,11 @@ void CurlInputStream::OnEnd() { const std::scoped_lock protect{mutex}; - InvokeOnAvailable(); + + if (IsSeekPending()) + SeekDone(); + else + InvokeOnAvailable(); AsyncInputStream::SetClosed(); } @@ -384,12 +388,9 @@ input_curl_init(EventLoop &event_loop, const ConfigBlock &block) } const auto version_info = curl_version_info(CURLVERSION_FIRST); - if (version_info != nullptr) { - FmtDebug(curl_domain, "version {}", version_info->version); - if (version_info->features & CURL_VERSION_SSL) - FmtDebug(curl_domain, "with {}", - version_info->ssl_version); - } + FmtDebug(curl_domain, "version {}", version_info->version); + if (version_info->features & CURL_VERSION_SSL) + FmtDebug(curl_domain, "with {}", version_info->ssl_version); http_200_aliases = curl_slist_append(http_200_aliases, "ICY 200 OK"); @@ -420,11 +421,11 @@ input_curl_init(EventLoop &event_loop, const ConfigBlock &block) low_speed_time = block.GetBlockValue("low_speed_time", default_low_speed_time); - tcp_keepalive = block.GetBlockValue("tcp_keepalive",default_tcp_keepalive); + tcp_keepalive = block.GetBlockValue("tcp_keepalive", default_tcp_keepalive); - tcp_keepidle = block.GetBlockValue("tcp_keepidle",default_tcp_keepidle); + tcp_keepidle = block.GetBlockValue("tcp_keepidle", default_tcp_keepidle); - tcp_keepintvl = block.GetBlockValue("tcp_keepintvl",default_tcp_keepintvl); + tcp_keepintvl = block.GetBlockValue("tcp_keepintvl", default_tcp_keepintvl); } static void @@ -509,8 +510,7 @@ CreateEasy(const char *url, struct curl_slist *headers) if (proxy_user != nullptr && proxy_password != nullptr) easy.SetOption(CURLOPT_PROXYUSERPWD, - FmtBuffer<1024>("{}:{}", proxy_user, - proxy_password).c_str()); + fmt::format("{}:{}"sv, proxy_user, proxy_password).c_str()); if (cacert != nullptr) easy.SetOption(CURLOPT_CAINFO, cacert); diff --git a/src/lib/upnp/meson.build b/src/lib/upnp/meson.build index 9c2c7bac1f..fa4886a115 100644 --- a/src/lib/upnp/meson.build +++ b/src/lib/upnp/meson.build @@ -13,7 +13,10 @@ endif if upnp_option == 'auto' upnp_dep = dependency('libupnp', version: '>= 1.8', required: false) - if upnp_dep.found() and upnp_dep.version().version_compare('>= 1.14.26') + if upnp_dep.found() and ( + ( upnp_dep.version().version_compare('>= 1.14.26') and upnp_dep.version().version_compare('< 1.14.30') ) + or upnp_dep.version().version_compare('>= 1.18.0') + ) warning('Your libupnp version is known to be broken, see https://github.com/pupnp/pupnp/issues/528 - disabling') upnp_dep = dependency('', required: false) endif @@ -25,7 +28,10 @@ if upnp_option == 'auto' elif upnp_option == 'pupnp' upnp_dep = dependency('libupnp', version: '>= 1.8', required: true) - if upnp_dep.found() and upnp_dep.version().version_compare('>= 1.14.26') + if upnp_dep.found() and ( + ( upnp_dep.version().version_compare('>= 1.14.26') and upnp_dep.version().version_compare('< 1.14.30') ) + or upnp_dep.version().version_compare('>= 1.18.0') + ) error('Your libupnp version is known to be broken, see https://github.com/pupnp/pupnp/issues/528') endif diff --git a/src/lib/zlib/patches/no_static_suffix b/src/lib/zlib/patches/no_static_suffix new file mode 100644 index 0000000000..d95ba7a2db --- /dev/null +++ b/src/lib/zlib/patches/no_static_suffix @@ -0,0 +1,12 @@ +Index: zlib-1.3.2/CMakeLists.txt +=================================================================== +--- zlib-1.3.2.orig/CMakeLists.txt ++++ zlib-1.3.2/CMakeLists.txt +@@ -150,7 +150,6 @@ set(ZLIB_SRCS + zutil.c) + + if(WIN32) +- set(zlib_static_suffix "s") + set(CMAKE_DEBUG_POSTFIX "d") + endif(WIN32) + diff --git a/src/lib/zlib/patches/series b/src/lib/zlib/patches/series new file mode 100644 index 0000000000..38339d9f63 --- /dev/null +++ b/src/lib/zlib/patches/series @@ -0,0 +1 @@ +no_static_suffix diff --git a/src/player/Thread.cxx b/src/player/Thread.cxx index 5a8ecc4d32..0d19439da7 100644 --- a/src/player/Thread.cxx +++ b/src/player/Thread.cxx @@ -15,7 +15,7 @@ * thread and sends it commands. * * The player thread itself does not do any I/O. It synchronizes with - * other threads via #GMutex and #GCond objects, and passes + * other threads via #Mutex and #Cond objects, and passes * #MusicChunk instances around in #MusicPipe objects. */ @@ -404,13 +404,11 @@ Player::StopDecoder(std::unique_lock &lock) noexcept decoder_starting = false; } -bool +inline bool Player::ForwardDecoderError() noexcept { - try { - dc.CheckRethrowError(); - } catch (...) { - pc.SetError(PlayerError::DECODER, std::current_exception()); + if (dc.HasFailed()) { + pc.SetError(PlayerError::DECODER, dc.GetError()); return false; } @@ -550,7 +548,8 @@ Player::OpenOutput() noexcept const ScopeUnlock unlock(pc.mutex); pc.outputs.Open(play_audio_format); } catch (...) { - LogError(std::current_exception()); + auto error = std::current_exception(); + LogError(error); output_open = false; @@ -558,7 +557,7 @@ Player::OpenOutput() noexcept audio output becomes available */ paused = true; - pc.SetOutputError(std::current_exception()); + pc.SetOutputError(std::move(error)); return false; } @@ -585,7 +584,7 @@ Player::CheckDecoderStartup(std::unique_lock &lock) noexcept if (output_open && !pc.WaitOutputConsumed(lock, 1)) - /* the output devices havn't finished playing + /* the output devices haven't finished playing all chunks yet - wait for that */ return true; @@ -837,9 +836,11 @@ Player::ProcessCommand(std::unique_lock &lock) noexcept pc.outputs.CheckPipe(); } - pc.elapsed_time = !pc.outputs.GetElapsedTime().IsNegative() - ? SongTime(pc.outputs.GetElapsedTime()) - : elapsed_time; + if (const auto outputs_time = pc.outputs.GetElapsedTime(); + !outputs_time.IsNegative()) + pc.elapsed_time = static_cast(outputs_time); + else + pc.elapsed_time = elapsed_time; pc.CommandFinished(); break; @@ -858,7 +859,6 @@ Player::CheckCrossFade() noexcept if (pc.border_pause) { /* no cross-fading if MPD is going to pause at the end of the current song */ - xfade_state = CrossFadeState::UNKNOWN; return; } @@ -866,8 +866,8 @@ Player::CheckCrossFade() noexcept /* we need information about the next song before we can decide */ /* the "pipe.empty" check is here so we wait for all - (ReplayGain/MixRamp) metadata to appear, which some - decoders parse only after reporting readiness */ + (ReplayGain/MixRamp) metadata to appear, which some + decoders parse only after reporting readiness */ return; if (!pc.cross_fade.CanCrossFade(pc.total_time, dc.total_time, @@ -986,7 +986,7 @@ Player::PlayNextChunk() noexcept std::move(other_chunk->tag)); if (pc.cross_fade.mixramp_delay <= FloatDuration::zero()) { - chunk->mix_ratio = ((float)cross_fade_position) + chunk->mix_ratio = static_cast(cross_fade_position) / cross_fade_chunks; } else { chunk->mix_ratio = -1; @@ -1042,7 +1042,8 @@ Player::PlayNextChunk() noexcept pc.PlayChunk(*song, std::move(chunk), play_audio_format); } catch (...) { - LogError(std::current_exception()); + auto error = std::current_exception(); + LogError(error); chunk.reset(); @@ -1050,7 +1051,7 @@ Player::PlayNextChunk() noexcept audio output becomes available */ paused = true; - pc.LockSetOutputError(std::current_exception()); + pc.LockSetOutputError(std::move(error)); return false; }