void GstEnginePipeline::TransitionToNext() { GstElement* old_decode_bin = uridecodebin_; ignore_tags_ = true; ReplaceDecodeBin(next_url_); gst_element_set_state(uridecodebin_, GST_STATE_PLAYING); MaybeLinkDecodeToAudio(); url_ = next_url_; end_offset_nanosec_ = next_end_offset_nanosec_; next_url_ = QUrl(); 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 segment when it really // does finish, so emit TrackEnded then. emit_track_ended_on_segment_start_ = true; // 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; }
bool GstEnginePipeline::InitFromString(const QString& pipeline) { pipeline_ = gst_pipeline_new("pipeline"); GstElement* new_bin = CreateDecodeBinFromString(pipeline.toAscii().constData()); if (!new_bin) { return false; } if (!ReplaceDecodeBin(new_bin)) return false; if (!Init()) return false; return gst_element_link(new_bin, audiobin_); }
bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) { GstElement* new_bin = nullptr; #ifdef HAVE_SPOTIFY if (url.scheme() == "spotify") { new_bin = gst_bin_new("spotify_bin"); // Create elements GstElement* src = engine_->CreateElement("tcpserversrc", new_bin); if (!src) return false; GstElement* gdp = engine_->CreateElement("gdpdepay", new_bin); if (!gdp) return false; // Pick a port number const int port = Utilities::PickUnusedPort(); g_object_set(G_OBJECT(src), "host", "127.0.0.1", nullptr); g_object_set(G_OBJECT(src), "port", port, nullptr); // Link the elements gst_element_link(src, gdp); // Add a ghost pad GstPad* pad = gst_element_get_static_pad(gdp, "src"); gst_element_add_pad(GST_ELEMENT(new_bin), gst_ghost_pad_new("src", pad)); gst_object_unref(GST_OBJECT(pad)); // Tell spotify to start sending data to us. SpotifyServer* spotify_server = InternetModel::Service<SpotifyService>()->server(); // Need to schedule this in the spotify server's thread QMetaObject::invokeMethod( spotify_server, "StartPlayback", Qt::QueuedConnection, Q_ARG(QString, url.toString()), Q_ARG(quint16, port)); } else { #endif new_bin = engine_->CreateElement("uridecodebin"); if (!new_bin) return false; g_object_set(G_OBJECT(new_bin), "uri", url.toEncoded().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); #ifdef HAVE_SPOTIFY } #endif return ReplaceDecodeBin(new_bin); }
bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) { GstElement* new_bin = NULL; if (url.scheme() == "spotify") { #ifdef HAVE_SPOTIFY new_bin = gst_bin_new("spotify_bin"); // Create elements GstElement* src = engine_->CreateElement("tcpserversrc", new_bin); GstElement* gdp = engine_->CreateElement("gdpdepay", new_bin); if (!src || !gdp) return false; // Pick a port number const int port = Utilities::PickUnusedPort(); g_object_set(G_OBJECT(src), "host", "127.0.0.1", NULL); g_object_set(G_OBJECT(src), "port", port, NULL); // Link the elements gst_element_link(src, gdp); // Add a ghost pad GstPad* pad = gst_element_get_static_pad(gdp, "src"); gst_element_add_pad(GST_ELEMENT(new_bin), gst_ghost_pad_new("src", pad)); gst_object_unref(GST_OBJECT(pad)); // Tell spotify to start sending data to us. InternetModel::Service<SpotifyService>()->server()->StartPlaybackLater(url.toString(), port); #else // HAVE_SPOTIFY qLog(Error) << "Tried to play a spotify:// url, but spotify support is not compiled in"; return false; #endif } else { new_bin = engine_->CreateElement("uridecodebin"); g_object_set(G_OBJECT(new_bin), "uri", url.toEncoded().constData(), NULL); 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); }
bool GstEnginePipeline::InitFromUrl(const QUrl& url, qint64 end_nanosec) { 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 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; return Init(); }