From e5426500a8e2786152edf27dde74e7647b06a056 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Sat, 10 Mar 2018 13:00:09 +0100 Subject: [PATCH] Fix CD playback. --- src/device/cddadevice.cpp | 3 +- src/engine/gstenginepipeline.cpp | 128 +++++++++---------------------- 2 files changed, 39 insertions(+), 92 deletions(-) diff --git a/src/device/cddadevice.cpp b/src/device/cddadevice.cpp index 698169d55..d7f4abc2a 100644 --- a/src/device/cddadevice.cpp +++ b/src/device/cddadevice.cpp @@ -22,6 +22,8 @@ #include +#include "core/logging.h" + #include "collection/collectionbackend.h" #include "collection/collectionmodel.h" @@ -62,4 +64,3 @@ void CddaDevice::SongsLoaded(const SongList &songs) { song_count_ = songs.size(); } - diff --git a/src/engine/gstenginepipeline.cpp b/src/engine/gstenginepipeline.cpp index fea133d68..c5e9fd90b 100644 --- a/src/engine/gstenginepipeline.cpp +++ b/src/engine/gstenginepipeline.cpp @@ -95,8 +95,6 @@ GstEnginePipeline::GstEnginePipeline(GstEngine *engine) audioscale_(nullptr), audiosink_(nullptr) { - //qLog(Info) << __PRETTY_FUNCTION__; - if (!sElementDeleter) { sElementDeleter = new GstElementDeleter; } @@ -107,8 +105,6 @@ GstEnginePipeline::GstEnginePipeline(GstEngine *engine) void GstEnginePipeline::set_output_device(const QString &sink, const QVariant &device) { - //qLog(Info) << __PRETTY_FUNCTION__ << sink << device ; - sink_ = sink; device_ = device; @@ -116,8 +112,6 @@ void GstEnginePipeline::set_output_device(const QString &sink, const QVariant &d void GstEnginePipeline::set_replaygain(bool enabled, int mode, float preamp, bool compression) { - //qLog(Info) << __PRETTY_FUNCTION__; - rg_enabled_ = enabled; rg_mode_ = mode; rg_preamp_ = preamp; @@ -138,8 +132,6 @@ void GstEnginePipeline::set_mono_playback(bool enabled) { bool GstEnginePipeline::ReplaceDecodeBin(GstElement *new_bin) { - //qLog(Info) << __PRETTY_FUNCTION__; - if (!new_bin) return false; // Destroy the old elements if they are set @@ -156,27 +148,34 @@ bool GstEnginePipeline::ReplaceDecodeBin(GstElement *new_bin) { gst_bin_add(GST_BIN(pipeline_), uridecodebin_); return true; + } -bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) { +bool GstEnginePipeline::ReplaceDecodeBin(const QUrl &url) { - //qLog(Info) << __PRETTY_FUNCTION__; + QByteArray uri; + if (url.scheme() == "cdda") { + QString str = url.toString(); + str.remove(str.lastIndexOf(QChar('a')), 1); + uri = str.toLocal8Bit();; + } + else { + uri = url.toEncoded(); + } GstElement *new_bin = nullptr; - new_bin = engine_->CreateElement("uridecodebin"); - g_object_set(G_OBJECT(new_bin), "uri", url.toEncoded().constData(), nullptr); + g_object_set(G_OBJECT(new_bin), "uri", uri.constData(), nullptr); CHECKED_GCONNECT(G_OBJECT(new_bin), "drained", &SourceDrainedCallback, this); CHECKED_GCONNECT(G_OBJECT(new_bin), "pad-added", &NewPadCallback, this); CHECKED_GCONNECT(G_OBJECT(new_bin), "notify::source", &SourceSetupCallback, this); return ReplaceDecodeBin(new_bin); + } GstElement *GstEnginePipeline::CreateDecodeBinFromString(const char *pipeline) { - //qLog(Info) << __PRETTY_FUNCTION__; - GError *error = nullptr; GstElement *bin = gst_parse_bin_from_description(pipeline, TRUE, &error); @@ -194,12 +193,11 @@ GstElement *GstEnginePipeline::CreateDecodeBinFromString(const char *pipeline) { else { return bin; } + } bool GstEnginePipeline::Init() { - //qLog(Info) << __PRETTY_FUNCTION__; - // Here we create all the parts of the gstreamer pipeline - from the source to the sink. The parts of the pipeline are split up into bins: // uri decode bin -> audio bin // The uri decode bin is a gstreamer builtin that automatically picks the right type of source and decoder for the URI. @@ -410,9 +408,7 @@ bool GstEnginePipeline::Init() { } void GstEnginePipeline::MaybeLinkDecodeToAudio() { - - //qLog(Info) << __PRETTY_FUNCTION__; - + if (!uridecodebin_ || !audiobin_) return; GstPad *pad = gst_element_get_static_pad(uridecodebin_, "src"); @@ -420,11 +416,10 @@ void GstEnginePipeline::MaybeLinkDecodeToAudio() { gst_object_unref(pad); gst_element_link(uridecodebin_, audiobin_); + } bool GstEnginePipeline::InitFromString(const QString &pipeline) { - - //qLog(Info) << __PRETTY_FUNCTION__; pipeline_ = gst_pipeline_new("pipeline"); @@ -437,20 +432,17 @@ bool GstEnginePipeline::InitFromString(const QString &pipeline) { if (!Init()) return false; return gst_element_link(new_bin, audiobin_); + } bool GstEnginePipeline::InitFromUrl(const QUrl &url, qint64 end_nanosec) { - - //qLog(Info) << __PRETTY_FUNCTION__; pipeline_ = gst_pipeline_new("pipeline"); if (url.scheme() == "cdda" && !url.path().isEmpty()) { - // 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 + // 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("/"); @@ -469,19 +461,16 @@ bool GstEnginePipeline::InitFromUrl(const QUrl &url, qint64 end_nanosec) { GstEnginePipeline::~GstEnginePipeline() { - //qLog(Info) << __PRETTY_FUNCTION__; - if (pipeline_) { gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), nullptr, nullptr, nullptr); g_source_remove(bus_cb_id_); gst_element_set_state(pipeline_, GST_STATE_NULL); gst_object_unref(GST_OBJECT(pipeline_)); } + } gboolean GstEnginePipeline::BusCallback(GstBus*, GstMessage *msg, gpointer self) { - - //qLog(Info) << __PRETTY_FUNCTION__; GstEnginePipeline *instance = reinterpret_cast(self); @@ -509,8 +498,6 @@ gboolean GstEnginePipeline::BusCallback(GstBus*, GstMessage *msg, gpointer self) } GstBusSyncReply GstEnginePipeline::BusCallbackSync(GstBus*, GstMessage *msg, gpointer self) { - - //qLog(Info) << __PRETTY_FUNCTION__; GstEnginePipeline *instance = reinterpret_cast(self); @@ -562,9 +549,7 @@ GstBusSyncReply GstEnginePipeline::BusCallbackSync(GstBus*, GstMessage *msg, gpo } void GstEnginePipeline::StreamStatusMessageReceived(GstMessage *msg) { - - //qLog(Info) << __PRETTY_FUNCTION__; - + GstStreamStatusType type; GstElement *owner; gst_message_parse_stream_status(msg, &type, &owner); @@ -581,8 +566,6 @@ void GstEnginePipeline::StreamStatusMessageReceived(GstMessage *msg) { void GstEnginePipeline::TaskEnterCallback(GstTask*, GThread*, gpointer) { - //qLog(Info) << __PRETTY_FUNCTION__; - // Bump the priority of the thread only on OS X #ifdef Q_OS_DARWIN @@ -596,8 +579,6 @@ void GstEnginePipeline::TaskEnterCallback(GstTask*, GThread*, gpointer) { } void GstEnginePipeline::ElementMessageReceived(GstMessage *msg) { - - //qLog(Info) << __PRETTY_FUNCTION__; const GstStructure *structure = gst_message_get_structure(msg); @@ -613,8 +594,6 @@ void GstEnginePipeline::ElementMessageReceived(GstMessage *msg) { } void GstEnginePipeline::ErrorMessageReceived(GstMessage *msg) { - - //qLog(Info) << __PRETTY_FUNCTION__; GError *error; gchar *debugs; @@ -628,9 +607,8 @@ void GstEnginePipeline::ErrorMessageReceived(GstMessage *msg) { free(debugs); if (!redirect_url_.isEmpty() && debugstr.contains("A redirect message was posted on the bus and should have been handled by the application.")) { - // mmssrc posts a message on the bus *and* makes an error message when it - // wants to do a redirect. We handle the message, but now we have to - // ignore the error too. + // mmssrc posts a message on the bus *and* makes an error message when it wants to do a redirect. + // We handle the message, but now we have to ignore the error too. return; } @@ -641,8 +619,6 @@ void GstEnginePipeline::ErrorMessageReceived(GstMessage *msg) { } void GstEnginePipeline::TagMessageReceived(GstMessage *msg) { - - //qLog(Info) << __PRETTY_FUNCTION__; GstTagList *taglist = nullptr; gst_message_parse_tag(msg, &taglist); @@ -663,8 +639,6 @@ void GstEnginePipeline::TagMessageReceived(GstMessage *msg) { } QString GstEnginePipeline::ParseTag(GstTagList *list, const char *tag) const { - - //qLog(Info) << __PRETTY_FUNCTION__; gchar *data = nullptr; bool success = gst_tag_list_get_string(list, tag, &data); @@ -680,8 +654,6 @@ QString GstEnginePipeline::ParseTag(GstTagList *list, const char *tag) const { void GstEnginePipeline::StateChangedMessageReceived(GstMessage *msg) { - //qLog(Info) << __PRETTY_FUNCTION__; - if (msg->src != GST_OBJECT(pipeline_)) { // We only care about state changes of the whole pipeline. return; @@ -705,8 +677,6 @@ void GstEnginePipeline::StateChangedMessageReceived(GstMessage *msg) { void GstEnginePipeline::BufferingMessageReceived(GstMessage *msg) { - //qLog(Info) << __PRETTY_FUNCTION__; - // Only handle buffering messages from the queue2 element in audiobin - not // the one that's created automatically by uridecodebin. if (GST_ELEMENT(GST_MESSAGE_SRC(msg)) != queue_) { @@ -745,8 +715,6 @@ void GstEnginePipeline::BufferingMessageReceived(GstMessage *msg) { void GstEnginePipeline::NewPadCallback(GstElement*, GstPad *pad, gpointer self) { - //qLog(Info) << __PRETTY_FUNCTION__; - GstEnginePipeline *instance = reinterpret_cast(self); GstPad *const audiopad = gst_element_get_static_pad(instance->audiobin_, "sink"); @@ -777,8 +745,6 @@ void GstEnginePipeline::NewPadCallback(GstElement*, GstPad *pad, gpointer self) } GstPadProbeReturn GstEnginePipeline::DecodebinProbe(GstPad *pad, GstPadProbeInfo *info, gpointer data) { - - //qLog(Info) << __PRETTY_FUNCTION__; GstEnginePipeline *instance = reinterpret_cast(data); const GstPadProbeType info_type = GST_PAD_PROBE_INFO_TYPE(info); @@ -822,8 +788,6 @@ GstPadProbeReturn GstEnginePipeline::DecodebinProbe(GstPad *pad, GstPadProbeInfo } GstPadProbeReturn GstEnginePipeline::HandoffCallback(GstPad*, GstPadProbeInfo *info, gpointer self) { - - //qLog(Info) << __PRETTY_FUNCTION__; GstEnginePipeline *instance = reinterpret_cast(self); GstBuffer *buf = gst_pad_probe_info_get_buffer(info); @@ -888,8 +852,6 @@ GstPadProbeReturn GstEnginePipeline::HandoffCallback(GstPad*, GstPadProbeInfo *i } GstPadProbeReturn GstEnginePipeline::EventHandoffCallback(GstPad*, GstPadProbeInfo *info, gpointer self) { - - //qLog(Info) << __PRETTY_FUNCTION__; GstEnginePipeline *instance = reinterpret_cast(self); GstEvent *e = gst_pad_probe_info_get_event(info); @@ -918,8 +880,6 @@ GstPadProbeReturn GstEnginePipeline::EventHandoffCallback(GstPad*, GstPadProbeIn void GstEnginePipeline::SourceDrainedCallback(GstURIDecodeBin *bin, gpointer self) { - //qLog(Info) << __PRETTY_FUNCTION__; - GstEnginePipeline *instance = reinterpret_cast(self); if (instance->has_next_valid_url()) { @@ -929,8 +889,6 @@ void GstEnginePipeline::SourceDrainedCallback(GstURIDecodeBin *bin, gpointer sel } void GstEnginePipeline::SourceSetupCallback(GstURIDecodeBin *bin, GParamSpec *pspec, gpointer self) { - - //qLog(Info) << __PRETTY_FUNCTION__; GstEnginePipeline *instance = reinterpret_cast(self); GstElement *element; @@ -940,10 +898,8 @@ void GstEnginePipeline::SourceSetupCallback(GstURIDecodeBin *bin, GParamSpec *ps } if (g_object_class_find_property(G_OBJECT_GET_CLASS(element), "device") && !instance->source_device().isEmpty()) { - // 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. + // 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(), nullptr); } @@ -960,8 +916,6 @@ void GstEnginePipeline::SourceSetupCallback(GstURIDecodeBin *bin, GParamSpec *ps } void GstEnginePipeline::TransitionToNext() { - - //qLog(Info) << __PRETTY_FUNCTION__; GstElement *old_decode_bin = uridecodebin_; @@ -977,13 +931,11 @@ void GstEnginePipeline::TransitionToNext() { next_beginning_offset_nanosec_ = 0; next_end_offset_nanosec_ = 0; - // This function gets called when the source has been drained, even if the - // song hasn't finished playing yet. We'll get a new stream when it really - // does finish, so emit TrackEnded then. + // This function gets called when the source has been drained, even if the song hasn't finished playing yet. + // We'll get a new stream when it really does finish, so emit TrackEnded then. emit_track_ended_on_stream_start_ = true; - // This has to happen *after* the gst_element_set_state on the new bin to - // fix an occasional race condition deadlock. + // This has to happen *after* the gst_element_set_state on the new bin to fix an occasional race condition deadlock. sElementDeleter->DeleteElementLater(old_decode_bin); ignore_tags_ = false; @@ -1041,8 +993,6 @@ bool GstEnginePipeline::Seek(qint64 nanosec) { } void GstEnginePipeline::SetEqualizerEnabled(bool enabled) { - - //qLog(Info) << __PRETTY_FUNCTION__ << enabled; eq_enabled_ = enabled; UpdateEqualizer(); @@ -1115,16 +1065,14 @@ void GstEnginePipeline::StartFader(qint64 duration_nanosec, QTimeLine::Direction const int duration_msec = duration_nanosec / kNsecPerMsec; - // If there's already another fader running then start from the same time - // that one was already at. + // If there's already another fader running then start from the same time that one was already at. int start_time = direction == QTimeLine::Forward ? 0 : duration_msec; if (fader_ && fader_->state() == QTimeLine::Running) { if (duration_msec == fader_->duration()) { start_time = fader_->currentTime(); } else { - // Calculate the position in the new fader with the same value from - // the old fader, so no volume jumps appear + // Calculate the position in the new fader with the same value from the old fader, so no volume jumps appear qreal time = qreal(duration_msec) * (qreal(fader_->currentTime()) / qreal(fader_->duration())); start_time = qRound(time); } @@ -1149,16 +1097,13 @@ void GstEnginePipeline::FaderTimelineFinished() { fader_.reset(); - // Wait a little while longer before emitting the finished signal (and - // probably distroying the pipeline) to account for delays in the audio - // server/driver. + // Wait a little while longer before emitting the finished signal (and probably distroying the pipeline) to account for delays in the audio server/driver. if (use_fudge_timer_) { fader_fudge_timer_.start(kFaderFudgeMsec, this); } else { - // Even here we cannot emit the signal directly, as it result in a - // stutter when resuming playback. So use a quest small time, so you - // won't notice the difference when resuming playback + // Even here we cannot emit the signal directly, as it result in a stutter when resuming playback. + // So use a quest small time, so you won't notice the difference when resuming playback // (You get here when the pause fading is active) fader_fudge_timer_.start(250, this); } @@ -1191,9 +1136,10 @@ void GstEnginePipeline::RemoveAllBufferConsumers() { buffer_consumers_.clear(); } -void GstEnginePipeline::SetNextUrl(const QUrl& url, qint64 beginning_nanosec, qint64 end_nanosec) { +void GstEnginePipeline::SetNextUrl(const QUrl &url, qint64 beginning_nanosec, qint64 end_nanosec) { + next_url_ = url; next_beginning_offset_nanosec_ = beginning_nanosec; next_end_offset_nanosec_ = end_nanosec; -} +}