Handle redirect messages from mmssrc properly. Fixes issue #385.

This commit is contained in:
David Sansome 2010-06-23 11:47:54 +00:00
parent f205326ff3
commit 9106abb1a0
5 changed files with 76 additions and 10 deletions

View File

@ -29,6 +29,8 @@
#include <boost/bind.hpp>
QSet<QString> SongLoader::sRawUriSchemes;
SongLoader::SongLoader(QObject *parent)
: QObject(parent),
timeout_timer_(new QTimer(this)),
@ -37,6 +39,11 @@ SongLoader::SongLoader(QObject *parent)
success_(false),
parser_(NULL)
{
if (sRawUriSchemes.isEmpty()) {
sRawUriSchemes << "udp" << "mms" << "mmsh" << "mmst" << "mmsu" << "rtsp"
<< "rtspu" << "rtspt" << "rtsph";
}
timeout_timer_->setSingleShot(true);
connect(timeout_timer_, SIGNAL(timeout()), SLOT(Timeout()));
}
@ -55,6 +62,13 @@ SongLoader::Result SongLoader::Load(const QUrl& url, int timeout_msec) {
return LoadLocal();
}
if (sRawUriSchemes.contains(url_.scheme())) {
// The URI scheme indicates that it can't possibly be a playlist, so add
// it as a raw stream.
AddAsRawStream();
return Success;
}
timeout_timer_->start(timeout_msec);
return LoadRemote();
}
@ -325,17 +339,21 @@ void SongLoader::StopTypefind() {
qDebug() << "Loading" << url_ << "as raw stream";
// It wasn't a playlist - just put the URL in as a stream
Song song;
song.set_valid(true);
song.set_filetype(Song::Type_Stream);
song.set_filename(url_.toString());
song.set_title(url_.toString());
songs_ << song;
AddAsRawStream();
}
emit LoadFinished(success_);
}
void SongLoader::AddAsRawStream() {
Song song;
song.set_valid(true);
song.set_filetype(Song::Type_Stream);
song.set_filename(url_.toString());
song.set_title(url_.toString());
songs_ << song;
}
void SongLoader::Timeout() {
state_ = Finished;
success_ = false;

View File

@ -75,7 +75,11 @@ private:
void EndOfStreamReached();
void MagicReady();
void AddAsRawStream();
private:
static QSet<QString> sRawUriSchemes;
QUrl url_;
SongList songs_;

View File

@ -474,7 +474,19 @@ void GstEngine::StartFadeout() {
bool GstEngine::Play( uint offset ) {
// Try to play input pipeline; if fails, destroy input bin
if (!current_pipeline_->SetState(GST_STATE_PLAYING)) {
forever {
if (current_pipeline_->SetState(GST_STATE_PLAYING))
break; // Success
// Failure, but we got a redirection URL - try loading that instead
QUrl redirect_url = current_pipeline_->redirect_url();
if (!redirect_url.isEmpty() && redirect_url != current_pipeline_->url()) {
qDebug() << "Redirecting to" << redirect_url;
current_pipeline_ = CreatePipeline(redirect_url);
continue;
}
// Failure - give up
qWarning() << "Could not set thread to PLAYING.";
current_pipeline_.reset();
return false;

View File

@ -213,24 +213,49 @@ GstBusSyncReply GstEnginePipeline::BusCallbackSync(GstBus*, GstMessage* msg, gpo
instance->ErrorMessageReceived(msg);
break;
case GST_MESSAGE_ELEMENT:
instance->ElementMessageReceived(msg);
break;
default:
break;
}
return GST_BUS_PASS;
}
void GstEnginePipeline::ElementMessageReceived(GstMessage* msg) {
const GstStructure* structure = gst_message_get_structure(msg);
if (gst_structure_has_name(structure, "redirect")) {
const char* uri = gst_structure_get_string(structure, "new-location");
// Set the redirect URL. In mmssrc redirect messages come during the
// initial state change to PLAYING, so callers can pick up this URL after
// the state change has failed.
redirect_url_ = QUrl::fromEncoded(uri);
}
}
void GstEnginePipeline::ErrorMessageReceived(GstMessage* msg) {
GError* error;
gchar* debugs;
gst_message_parse_error(msg, &error, &debugs);
QString message = QString::fromLocal8Bit(error->message);
qDebug() << debugs;
QString debugstr = QString::fromLocal8Bit(debugs);
g_error_free(error);
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.
return;
}
qDebug() << debugstr;
emit Error(message);
}

View File

@ -73,6 +73,8 @@ class GstEnginePipeline : public QObject {
qint64 length() const;
GstState state() const;
QUrl redirect_url() const { return redirect_url_; }
public slots:
void SetVolumeModifier(qreal mod);
@ -95,8 +97,9 @@ class GstEnginePipeline : public QObject {
static void SourceDrainedCallback(GstURIDecodeBin*, gpointer);
static bool StopUriDecodeBin(gpointer bin);
void TagMessageReceived(GstMessage*);
QString ParseTag(GstTagList* list, const char* tag) const;
void ErrorMessageReceived(GstMessage*);
void ElementMessageReceived(GstMessage*);
QString ParseTag(GstTagList* list, const char* tag) const;
void UpdateVolume();
bool ReplaceDecodeBin(const QUrl& url);
@ -130,6 +133,10 @@ class GstEnginePipeline : public QObject {
QUrl url_;
QUrl next_url_;
// When the gstreamer source requests a redirect we store the URL here and
// callers can pick it up after the state change to PLAYING fails.
QUrl redirect_url_;
int volume_percent_;
qreal volume_modifier_;