From ca2e80223930ebd992f4fc7bed635bd96024089b Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Thu, 18 Dec 2025 00:21:00 +0100 Subject: [PATCH] GstEngine: Make sure device is set for pipeline Fixes #1852 --- src/engine/gstengine.cpp | 40 ++++++++++++++++++++++++---------------- src/engine/gstengine.h | 3 ++- src/engine/gsturl.h | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 17 deletions(-) create mode 100644 src/engine/gsturl.h diff --git a/src/engine/gstengine.cpp b/src/engine/gstengine.cpp index 916564660..9d1f9c051 100644 --- a/src/engine/gstengine.cpp +++ b/src/engine/gstengine.cpp @@ -57,6 +57,7 @@ #include "core/enginemetadata.h" #include "constants/timeconstants.h" #include "enginebase.h" +#include "gsturl.h" #include "gstengine.h" #include "gstenginepipeline.h" #include "gstbufferconsumer.h" @@ -179,15 +180,18 @@ EngineBase::State GstEngine::state() const { void GstEngine::StartPreloading(const QUrl &media_url, const QUrl &stream_url, const bool force_stop_at_end, const qint64 beginning_offset_nanosec, const qint64 end_offset_nanosec) { - const QByteArray gst_url = FixupUrl(stream_url); + const GstUrl gst_url = FixupUrl(stream_url); // No crossfading, so we can just queue the new URL in the existing pipeline and get gapless playback (hopefully) if (current_pipeline_) { - current_pipeline_->PrepareNextUrl(media_url, stream_url, gst_url, beginning_offset_nanosec, force_stop_at_end ? end_offset_nanosec : 0); + if (!gst_url.source_device.isEmpty()) { + current_pipeline_->SetSourceDevice(gst_url.source_device); + } + current_pipeline_->PrepareNextUrl(media_url, stream_url, gst_url.url, beginning_offset_nanosec, force_stop_at_end ? end_offset_nanosec : 0); // Add request to discover the stream if (discoverer_ && media_url.scheme() != u"spotify"_s) { - if (!gst_discoverer_discover_uri_async(discoverer_, gst_url.constData())) { - qLog(Error) << "Failed to start stream discovery for" << gst_url; + if (!gst_discoverer_discover_uri_async(discoverer_, gst_url.url.constData())) { + qLog(Error) << "Failed to start stream discovery for" << gst_url.url; } } } @@ -198,7 +202,7 @@ bool GstEngine::Load(const QUrl &media_url, const QUrl &stream_url, const Engine EngineBase::Load(media_url, stream_url, change, force_stop_at_end, beginning_offset_nanosec, end_offset_nanosec, ebur128_integrated_loudness_lufs); - const QByteArray gst_url = FixupUrl(stream_url); + const GstUrl gst_url = FixupUrl(stream_url); bool crossfade = current_pipeline_ && ((crossfade_enabled_ && change & EngineBase::TrackChangeType::Manual) || (autocrossfade_enabled_ && change & EngineBase::TrackChangeType::Auto) || ((crossfade_enabled_ || autocrossfade_enabled_) && change & EngineBase::TrackChangeType::Intro)); @@ -215,9 +219,14 @@ bool GstEngine::Load(const QUrl &media_url, const QUrl &stream_url, const Engine } } - GstEnginePipelinePtr pipeline = CreatePipeline(media_url, stream_url, gst_url, static_cast(beginning_offset_nanosec), force_stop_at_end ? end_offset_nanosec : 0, ebur128_loudness_normalizing_gain_db_); + GstEnginePipelinePtr pipeline = CreatePipeline(media_url, stream_url, gst_url.url, static_cast(beginning_offset_nanosec), force_stop_at_end ? end_offset_nanosec : 0, ebur128_loudness_normalizing_gain_db_); if (!pipeline) return false; + // Set the source device if one was extracted from the URL + if (!gst_url.source_device.isEmpty()) { + pipeline->SetSourceDevice(gst_url.source_device); + } + GstEnginePipelinePtr old_pipeline = current_pipeline_; current_pipeline_ = pipeline; @@ -253,8 +262,8 @@ bool GstEngine::Load(const QUrl &media_url, const QUrl &stream_url, const Engine // Add request to discover the stream if (discoverer_ && media_url.scheme() != u"spotify"_s) { - if (!gst_discoverer_discover_uri_async(discoverer_, gst_url.constData())) { - qLog(Error) << "Failed to start stream discovery for" << gst_url; + if (!gst_discoverer_discover_uri_async(discoverer_, gst_url.url.constData())) { + qLog(Error) << "Failed to start stream discovery for" << gst_url.url; } } @@ -814,16 +823,16 @@ void GstEngine::BufferingFinished() { } -QByteArray GstEngine::FixupUrl(const QUrl &url) { +GstUrl GstEngine::FixupUrl(const QUrl &url) { - QByteArray uri; + GstUrl gst_url; // It's a file:// url with a hostname set. // QUrl::fromLocalFile does this when given a \\host\share\file path on Windows. // Munge it back into a path that gstreamer will recognise. if (url.isLocalFile() && !url.host().isEmpty()) { QString str = "file:////"_L1 + url.host() + url.path(); - uri = str.toUtf8(); + gst_url.url = str.toUtf8(); } else if (url.scheme() == "cdda"_L1) { QString str; @@ -837,16 +846,15 @@ QByteArray GstEngine::FixupUrl(const QUrl &url) { // We keep the device in mind, and we will set it later using SourceSetupCallback QStringList path = url.path().split(u'/'); str = QStringLiteral("cdda://%1").arg(path.takeLast()); - QString device = path.join(u'/'); - if (current_pipeline_) current_pipeline_->SetSourceDevice(device); + gst_url.source_device = path.join(u'/'); } - uri = str.toUtf8(); + gst_url.url = str.toUtf8(); } else { - uri = url.toEncoded(); + gst_url.url = url.toEncoded(); } - return uri; + return gst_url; } diff --git a/src/engine/gstengine.h b/src/engine/gstengine.h index a64234d4f..41b3de4a3 100644 --- a/src/engine/gstengine.h +++ b/src/engine/gstengine.h @@ -41,6 +41,7 @@ #include "includes/shared_ptr.h" #include "enginebase.h" +#include "gsturl.h" #include "gstenginepipeline.h" #include "gstbufferconsumer.h" @@ -123,7 +124,7 @@ class GstEngine : public EngineBase, public GstBufferConsumer { void PipelineFinished(const int pipeline_id); private: - QByteArray FixupUrl(const QUrl &url); + GstUrl FixupUrl(const QUrl &url); void StartFadeout(GstEnginePipelinePtr pipeline); void StartFadeoutPause(); diff --git a/src/engine/gsturl.h b/src/engine/gsturl.h new file mode 100644 index 000000000..e9ea4fa13 --- /dev/null +++ b/src/engine/gsturl.h @@ -0,0 +1,32 @@ +/* + * Strawberry Music Player + * Copyright 2025, Jonas Kvinge + * + * Strawberry 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 3 of the License, or + * (at your option) any later version. + * + * Strawberry 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 Strawberry. If not, see . + * + */ + +#ifndef GSTURL_H +#define GSTURL_H + +#include +#include + +class GstUrl { + public: + QByteArray url; + QString source_device; +}; + +#endif // GSTURL_H