Allow songs from the spotify service to be added to the playlist, and bodge a tcpserversrc into GstEnginePipeline

This commit is contained in:
David Sansome 2011-04-26 18:39:38 +00:00
parent 232e1c2335
commit 6f1f4484f4
7 changed files with 101 additions and 19 deletions

View File

@ -402,7 +402,7 @@ int SpotifyClient::MusicDeliveryCallback(
const int bytes_per_sample = 2;
const int byte_rate = format->sample_rate * format->channels * bytes_per_sample;
const quint32 data_size = me->media_length_msec_ * byte_rate / 1000;
const quint32 data_size = me->media_length_msec_ * byte_rate;
// RIFF header
s.writeRawData("RIFF", 4);
@ -479,13 +479,15 @@ void SpotifyClient::StartPlayback(const protobuf::PlaybackRequest& req) {
}
// Create the media socket
if (media_socket_) {
media_socket_->close();
}
QTcpSocket* old_media_socket = media_socket_;
media_socket_ = new QTcpSocket(this);
media_socket_->connectToHost(QHostAddress::LocalHost, req.media_port());
connect(media_socket_, SIGNAL(disconnected()), SLOT(MediaSocketDisconnected()));
if (old_media_socket) {
old_media_socket->close();
}
qLog(Info) << "Starting playback of uri" << req.track_uri().c_str()
<< "to port" << req.media_port();

View File

@ -64,6 +64,7 @@ GstEnginePipeline::GstEnginePipeline(GstEngine* engine)
volume_modifier_(1.0),
fader_(NULL),
pipeline_(NULL),
tcpsrc_(NULL),
uridecodebin_(NULL),
audiobin_(NULL),
queue_(NULL),
@ -101,35 +102,59 @@ void GstEnginePipeline::set_buffer_duration_nanosec(qint64 buffer_duration_nanos
buffer_duration_nanosec_ = buffer_duration_nanosec;
}
bool GstEnginePipeline::ReplaceDecodeBin(GstElement* new_bin) {
bool GstEnginePipeline::ReplaceDecodeBin(GstElement* new_bin,
GstElement* new_tcpsrc) {
if (!new_bin) return false;
// Destroy the old one, if any
// Destroy the old elements if they are set
// Note that the caller to this function MUST schedule the old uridecodebin_
// or tcpsrc_ for deletion in the main thread.
if (uridecodebin_) {
gst_bin_remove(GST_BIN(pipeline_), uridecodebin_);
// Note that the caller to this function MUST schedule the old bin for
// deletion in the main thread
}
if (tcpsrc_) {
gst_bin_remove(GST_BIN(pipeline_), tcpsrc_);
}
uridecodebin_ = new_bin;
tcpsrc_ = new_tcpsrc;
segment_start_ = 0;
segment_start_received_ = false;
pipeline_is_connected_ = false;
gst_bin_add(GST_BIN(pipeline_), uridecodebin_);
if (new_tcpsrc) {
gst_bin_add(GST_BIN(pipeline_), tcpsrc_);
gst_element_link(tcpsrc_, uridecodebin_);
}
return true;
}
bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) {
GstElement* new_bin = engine_->CreateElement("uridecodebin");
g_object_set(G_OBJECT(new_bin), "uri", url.toEncoded().constData(), NULL);
g_object_set(G_OBJECT(new_bin), "buffer-duration", buffer_duration_nanosec_, NULL);
g_object_set(G_OBJECT(new_bin), "download", true, NULL);
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);
return ReplaceDecodeBin(new_bin);
if (url.scheme() == "tcp") {
qLog(Info) << "Listening on TCP port" << url.port();
// Hackity hack
GstElement* src = engine_->CreateElement("tcpserversrc");
g_object_set(G_OBJECT(src), "host", url.host().toUtf8().constData(), NULL);
g_object_set(G_OBJECT(src), "port", url.port(), NULL);
GstElement* decodebin = engine_->CreateElement("decodebin2");
g_signal_connect(G_OBJECT(decodebin), "drained", G_CALLBACK(SourceDrainedCallback), this);
g_signal_connect(G_OBJECT(decodebin), "pad-added", G_CALLBACK(NewPadCallback), this);
return ReplaceDecodeBin(decodebin, src);
} else {
GstElement* new_bin = engine_->CreateElement("uridecodebin");
g_object_set(G_OBJECT(new_bin), "uri", url.toEncoded().constData(), NULL);
g_object_set(G_OBJECT(new_bin), "buffer-duration", buffer_duration_nanosec_, NULL);
g_object_set(G_OBJECT(new_bin), "download", true, NULL);
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);
return ReplaceDecodeBin(new_bin);
}
}
GstElement* GstEnginePipeline::CreateDecodeBinFromString(const char* pipeline) {
@ -537,6 +562,7 @@ void GstEnginePipeline::SourceDrainedCallback(GstURIDecodeBin* bin, gpointer sel
void GstEnginePipeline::TransitionToNext() {
GstElement* old_decode_bin = uridecodebin_;
GstElement* old_tcpsrc = tcpsrc_;
ignore_tags_ = true;
@ -558,6 +584,10 @@ void GstEnginePipeline::TransitionToNext() {
// fix an occasional race condition deadlock.
sElementDeleter->DeleteElementLater(old_decode_bin);
if (old_tcpsrc) {
sElementDeleter->DeleteElementLater(old_tcpsrc);
}
ignore_tags_ = false;
}

View File

@ -127,7 +127,7 @@ class GstEnginePipeline : public QObject {
void UpdateVolume();
void UpdateEqualizer();
bool ReplaceDecodeBin(GstElement* new_bin);
bool ReplaceDecodeBin(GstElement* new_bin, GstElement* new_tcpsrc = NULL);
bool ReplaceDecodeBin(const QUrl& url);
void TransitionToNext();
@ -220,7 +220,8 @@ class GstEnginePipeline : public QObject {
GstElement* pipeline_;
// Bins
// uridecodebin ! audiobin
// [tcpsrc !] uridecodebin ! audiobin
GstElement* tcpsrc_;
GstElement* uridecodebin_;
GstElement* audiobin_;

View File

@ -145,3 +145,12 @@ void SpotifyServer::LoadStarred() {
void SpotifyServer::LoadUserPlaylist(int index) {
LoadPlaylist(protobuf::LoadPlaylistRequest_Type_UserPlaylist, index);
}
void SpotifyServer::StartPlayback(const QString& uri, quint16 port) {
protobuf::SpotifyMessage message;
protobuf::PlaybackRequest* req = message.mutable_playback_request();
req->set_track_uri(DataCommaSizeFromQString(uri));
req->set_media_port(port);
SendMessage(message);
}

View File

@ -39,6 +39,8 @@ public:
void LoadInbox();
void LoadUserPlaylist(int index);
void StartPlayback(const QString& uri, quint16 port);
int server_port() const;
signals:

View File

@ -8,6 +8,7 @@
#include <QCoreApplication>
#include <QProcess>
#include <QSettings>
#include <QTcpServer>
const char* SpotifyService::kServiceName = "Spotify";
const char* SpotifyService::kSettingsGroup = "Spotify";
@ -194,6 +195,8 @@ void SpotifyService::FillPlaylist(QStandardItem* item, const protobuf::LoadPlayl
QStandardItem* child = new QStandardItem(song.PrettyTitleWithArtist());
child->setData(Type_Track, RadioModel::Role_Type);
child->setData(QVariant::fromValue(song), Role_Metadata);
child->setData(RadioModel::PlayBehaviour_SingleItem, RadioModel::Role_PlayBehaviour);
child->setData(QUrl(song.filename()), RadioModel::Role_Url);
item->appendRow(child);
}
@ -220,3 +223,35 @@ void SpotifyService::SongFromProtobuf(const protobuf::Track& track, Song* song)
song->set_filetype(Song::Type_Stream);
song->set_valid(true);
}
PlaylistItem::Options SpotifyService::playlistitem_options() const {
return PlaylistItem::SpecialPlayBehaviour |
PlaylistItem::PauseDisabled;
}
PlaylistItem::SpecialLoadResult SpotifyService::StartLoading(const QUrl& url) {
// Pick an unused local port. There's a possible race condition here -
// something else might grab the port before gstreamer does.
quint16 port = 0;
{
QTcpServer server;
server.listen(QHostAddress::LocalHost);
port = server.serverPort();
}
if (port == 0) {
qLog(Warning) << "Couldn't pick an unused port";
return PlaylistItem::SpecialLoadResult();
}
// Tell Spotify to start sending to this port
EnsureServerCreated();
server_->StartPlayback(url.toString(), port);
// Tell gstreamer to listen on this port
return PlaylistItem::SpecialLoadResult(
PlaylistItem::SpecialLoadResult::TrackAvailable,
url,
QUrl("tcp://localhost:" + QString::number(port)));
}

View File

@ -34,6 +34,9 @@ public:
void Login(const QString& username, const QString& password);
PlaylistItem::SpecialLoadResult StartLoading(const QUrl& url);
PlaylistItem::Options playlistitem_options() const;
static const char* kServiceName;
static const char* kSettingsGroup;