diff --git a/src/devices/cddadevice.cpp b/src/devices/cddadevice.cpp index e60ef2e4e..736647494 100644 --- a/src/devices/cddadevice.cpp +++ b/src/devices/cddadevice.cpp @@ -47,12 +47,14 @@ void CddaDevice::Init() { return; } // Create gstreamer cdda element - cdda_ = gst_element_make_from_uri (GST_URI_SRC, "cdda://", unique_id_.toLocal8Bit().constData()); + cdda_ = gst_element_make_from_uri (GST_URI_SRC, "cdda://", NULL); if (cdda_ == NULL) { model_->Reset(); return; } + GST_CDDA_BASE_SRC(cdda_)->device = g_strdup (unique_id_.toLocal8Bit().constData()); + // Change the element's state to ready and paused, to be able to query it if (gst_element_set_state(cdda_, GST_STATE_READY) == GST_STATE_CHANGE_FAILURE || gst_element_set_state(cdda_, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) { @@ -86,7 +88,7 @@ void CddaDevice::Init() { song.set_id(track_number); song.set_valid(true); song.set_filetype(Song::Type_Cdda); - song.set_url(QUrl(QString("cdda://%1").arg(track_number))); + song.set_url(QUrl(QString("cdda://%1/%2").arg(unique_id()).arg(track_number))); song.set_title(QString("Track %1").arg(track_number)); song.set_track(track_number); songs << song; @@ -146,7 +148,7 @@ void CddaDevice::AudioCDTagsLoaded(const QString& artist, const QString& album, song.set_id(track_number); song.set_track(track_number); // We need to set url: that's how playlist will find the correct item to update - song.set_url(QUrl(QString("cdda://%1").arg(track_number++))); + song.set_url(QUrl(QString("cdda://%1/%2").arg(unique_id()).arg(track_number++))); songs << song; } connect(this, SIGNAL(SongsDiscovered(const SongList&)), model_, SLOT(SongsDiscovered(const SongList&))); diff --git a/src/engines/gstenginepipeline.cpp b/src/engines/gstenginepipeline.cpp index 63c50b34b..c2b3e7d5c 100644 --- a/src/engines/gstenginepipeline.cpp +++ b/src/engines/gstenginepipeline.cpp @@ -153,6 +153,7 @@ bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) { g_object_set(G_OBJECT(new_bin), "use-buffering", true, NULL); g_signal_connect(G_OBJECT(new_bin), "drained", G_CALLBACK(SourceDrainedCallback), this); g_signal_connect(G_OBJECT(new_bin), "pad-added", G_CALLBACK(NewPadCallback), this); + g_signal_connect(G_OBJECT(new_bin), "notify::source", G_CALLBACK(SourceSetupCallback), this); return ReplaceDecodeBin(new_bin); } } @@ -288,12 +289,23 @@ bool GstEnginePipeline::InitFromString(const QString& pipeline) { bool GstEnginePipeline::InitFromUrl(const QUrl &url, qint64 end_nanosec) { pipeline_ = gst_pipeline_new("pipeline"); - - url_ = url; + + if (url.scheme() == "cdda") { + // Currently, Gstreamer can't handle input CD devices inside cdda URL. So + // we handle them ourselve: we extract the track number and re-create an + // URL with only cdda:// + the track number (which can be handled by + // Gstreamer). We keep the device in mind, and we will set it later using + // SourceSetupCallback + QStringList path = url.path().split('/'); + url_ = QUrl(QString("cdda://%1").arg(path.takeLast())); + source_device_ = path.join("/"); + } else { + url_ = url; + } end_offset_nanosec_ = end_nanosec; // Decode bin - if (!ReplaceDecodeBin(url)) return false; + if (!ReplaceDecodeBin(url_)) return false; return Init(); } @@ -560,6 +572,20 @@ void GstEnginePipeline::SourceDrainedCallback(GstURIDecodeBin* bin, gpointer sel } } +void GstEnginePipeline::SourceSetupCallback(GstURIDecodeBin* bin, GParamSpec *pspec, gpointer self) { + GstEnginePipeline* instance = reinterpret_cast(self); + GstElement* element; + g_object_get(bin, "source", &element, NULL); + if (element && + g_object_class_find_property(G_OBJECT_GET_CLASS(element), "device")) { + // Gstreamer is not able to handle device in URL (refering to Gstreamer + // documentation, this might be added in the future). Despite that, for now + // we include device inside URL: we decompose it during Init and set device + // here, when this callback is called. + g_object_set(element, "device", instance->source_device().toLocal8Bit().constData(), NULL); + } +} + void GstEnginePipeline::TransitionToNext() { GstElement* old_decode_bin = uridecodebin_; GstElement* old_tcpsrc = tcpsrc_; diff --git a/src/engines/gstenginepipeline.h b/src/engines/gstenginepipeline.h index ff7a01bc2..967e2fdf0 100644 --- a/src/engines/gstenginepipeline.h +++ b/src/engines/gstenginepipeline.h @@ -91,6 +91,8 @@ class GstEnginePipeline : public QObject { QUrl redirect_url() const { return redirect_url_; } + QString source_device() const { return source_device_; } + public slots: void SetVolumeModifier(qreal mod); @@ -114,6 +116,7 @@ class GstEnginePipeline : public QObject { static bool HandoffCallback(GstPad*, GstBuffer*, gpointer); static bool EventHandoffCallback(GstPad*, GstEvent*, gpointer); static void SourceDrainedCallback(GstURIDecodeBin*, gpointer); + static void SourceSetupCallback(GstURIDecodeBin*, GParamSpec *pspec, gpointer); void TagMessageReceived(GstMessage*); void ErrorMessageReceived(GstMessage*); @@ -204,6 +207,9 @@ class GstEnginePipeline : public QObject { // callers can pick it up after the state change to PLAYING fails. QUrl redirect_url_; + // When we need to specify the device to use as source (for CD device) + QString source_device_; + // Seeking while the pipeline is in the READY state doesn't work, so we have // to wait until it goes to PAUSED or PLAYING. // Also we have to wait for the decodebin to be connected.