Commit 002b786f authored by Harald Sitter's avatar Harald Sitter 🏳️‍🌈
Browse files

don't construct objects when gst init failed

what happend is that we continued constructing backend objects even when
gst init failed (supposedly that is only the case since the pipeline
refactor) which in turn lead to crashing as it would have required just
about every function to do validity checking before doing anything at all.
the good news is that libphonon already does that as part of its own
validity system. whenver the backend returns 0 on an object construction
request libphonon's corresponding frontend object will simply ignore all
calls thus allowing a scenario in which the backend cannot construct
any valid backend objects (such as when gst init fails).

consequently also remove pointless checks for the validity in the
AO, MO and devicemanager ctors. also the managers are now only constructed
if gst_init was successful.

ultimate result of this is that when gst_init failed or our plugin
requirements are not met (i.e. gst-base is not present) no object will
get constructed other than the Backend object itself.

BUG: 310394
parent 8bbfc734
......@@ -48,35 +48,34 @@ AudioOutput::AudioOutput(Backend *backend, QObject *parent)
{
static int count = 0;
m_name = "AudioOutput" + QString::number(count++);
if (m_backend->isValid()) {
m_audioBin = gst_bin_new (NULL);
gst_object_ref (GST_OBJECT (m_audioBin));
gst_object_sink (GST_OBJECT (m_audioBin));
m_conv = gst_element_factory_make ("audioconvert", NULL);
// Get category from parent
Phonon::Category category = Phonon::NoCategory;
if (Phonon::AudioOutput *audioOutput = qobject_cast<Phonon::AudioOutput *>(parent))
category = audioOutput->category();
m_audioSink = m_backend->deviceManager()->createAudioSink(category);
m_volumeElement = gst_element_factory_make ("volume", NULL);
GstElement *queue = gst_element_factory_make ("queue", NULL);
GstElement *audioresample = gst_element_factory_make ("audioresample", NULL);
if (queue && m_audioBin && m_conv && audioresample && m_audioSink && m_volumeElement) {
gst_bin_add_many(GST_BIN(m_audioBin), queue, m_conv,
audioresample, m_volumeElement, m_audioSink, NULL);
if (gst_element_link_many(queue, m_conv, audioresample, m_volumeElement,
m_audioSink, NULL)) {
// Add ghost sink for audiobin
GstPad *audiopad = gst_element_get_static_pad (queue, "sink");
gst_element_add_pad (m_audioBin, gst_ghost_pad_new ("sink", audiopad));
gst_object_unref (audiopad);
m_isValid = true; // Initialization ok, accept input
}
m_audioBin = gst_bin_new (NULL);
gst_object_ref (GST_OBJECT (m_audioBin));
gst_object_sink (GST_OBJECT (m_audioBin));
m_conv = gst_element_factory_make ("audioconvert", NULL);
// Get category from parent
Phonon::Category category = Phonon::NoCategory;
if (Phonon::AudioOutput *audioOutput = qobject_cast<Phonon::AudioOutput *>(parent))
category = audioOutput->category();
m_audioSink = m_backend->deviceManager()->createAudioSink(category);
m_volumeElement = gst_element_factory_make ("volume", NULL);
GstElement *queue = gst_element_factory_make ("queue", NULL);
GstElement *audioresample = gst_element_factory_make ("audioresample", NULL);
if (queue && m_audioBin && m_conv && audioresample && m_audioSink && m_volumeElement) {
gst_bin_add_many(GST_BIN(m_audioBin), queue, m_conv,
audioresample, m_volumeElement, m_audioSink, NULL);
if (gst_element_link_many(queue, m_conv, audioresample, m_volumeElement,
m_audioSink, NULL)) {
// Add ghost sink for audiobin
GstPad *audiopad = gst_element_get_static_pad (queue, "sink");
gst_element_add_pad (m_audioBin, gst_ghost_pad_new ("sink", audiopad));
gst_object_unref (audiopad);
m_isValid = true; // Initialization ok, accept input
}
}
}
......
......@@ -82,7 +82,7 @@ Backend::Backend(QObject *parent, const QVariantList &)
int argc = sizeof(args) / sizeof(*args);
char **argv = const_cast<char**>(args);
GError *err = 0;
bool wasInit = gst_init_check(&argc, &argv, &err); //init gstreamer: must be called before any gst-related functions
bool wasInit = gst_init_check(&argc, &argv, &err); //init gstreamer: must be called before any gst-related functions
if (err)
g_error_free(err);
......@@ -107,11 +107,13 @@ Backend::Backend(QObject *parent, const QVariantList &)
debug() << "Using" << versionString;
g_free(versionString);
}
if (!m_isValid)
qWarning("Phonon::GStreamer::Backend: Failed to initialize GStreamer");
m_deviceManager = new DeviceManager(this);
m_effectManager = new EffectManager(this);
if (!isValid()) {
qWarning("Phonon::GStreamer::Backend: Failed to initialize GStreamer");
} else {
m_deviceManager = new DeviceManager(this);
m_effectManager = new EffectManager(this);
}
}
Backend::~Backend()
......@@ -132,6 +134,10 @@ Backend::~Backend()
QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args)
{
// Return nothing if dependencies are not met
if (!isValid()) {
warning() << "Backend class" << c << "is not going to be created because GStreamer init failed.";
return 0;
}
switch (c) {
case MediaObjectClass:
......
......@@ -50,8 +50,6 @@ public:
QObject *createObject(BackendInterface::Class, QObject *parent, const QList<QVariant> &args);
bool isValid() const;
bool supportsVideo() const;
QStringList availableMimeTypes() const;
QList<int> objectDescriptionIndexes(ObjectDescriptionType type) const;
......@@ -69,6 +67,9 @@ Q_SIGNALS:
void objectDescriptionChanged(ObjectDescriptionType);
private:
bool isValid() const;
bool supportsVideo() const;
DeviceManager *m_deviceManager;
EffectManager *m_effectManager;
bool m_isValid;
......
......@@ -190,8 +190,7 @@ DeviceManager::DeviceManager(Backend *backend)
m_videoSinkWidget = settings.value(QLatin1String("videomode"), "Auto").toByteArray().toLower();
}
if (m_backend->isValid())
updateDeviceList();
updateDeviceList();
}
DeviceManager::~DeviceManager()
......@@ -263,70 +262,67 @@ GstElement *DeviceManager::createAudioSink(Category category)
{
GstElement *sink = 0;
if (m_backend && m_backend->isValid())
if (m_audioSink == "auto") //this is the default value
{
if (m_audioSink == "auto") //this is the default value
{
//### TODO : get equivalent KDE settings here
if (!qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) {
sink = createGNOMEAudioSink(category);
if (canOpenDevice(sink))
debug() << "AudioOutput using gconf audio sink";
else if (sink) {
gst_object_unref(sink);
sink = 0;
}
}
//### TODO : get equivalent KDE settings here
if (!sink) {
sink = gst_element_factory_make ("alsasink", NULL);
if (canOpenDevice(sink))
debug() << "AudioOutput using alsa audio sink";
else if (sink) {
gst_object_unref(sink);
sink = 0;
}
if (!qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) {
sink = createGNOMEAudioSink(category);
if (canOpenDevice(sink))
debug() << "AudioOutput using gconf audio sink";
else if (sink) {
gst_object_unref(sink);
sink = 0;
}
}
if (!sink) {
sink = gst_element_factory_make ("autoaudiosink", NULL);
if (canOpenDevice(sink))
debug() << "AudioOutput using auto audio sink";
else if (sink) {
gst_object_unref(sink);
sink = 0;
}
if (!sink) {
sink = gst_element_factory_make ("alsasink", NULL);
if (canOpenDevice(sink))
debug() << "AudioOutput using alsa audio sink";
else if (sink) {
gst_object_unref(sink);
sink = 0;
}
}
if (!sink) {
sink = gst_element_factory_make ("osssink", NULL);
if (canOpenDevice(sink))
debug() << "AudioOutput using oss audio sink";
else if (sink) {
gst_object_unref(sink);
sink = 0;
}
if (!sink) {
sink = gst_element_factory_make ("autoaudiosink", NULL);
if (canOpenDevice(sink))
debug() << "AudioOutput using auto audio sink";
else if (sink) {
gst_object_unref(sink);
sink = 0;
}
} else if (m_audioSink == "fake") {
//do nothing as a fakesink will be created by default
} else if (!m_audioSink.isEmpty()) { //Use a custom sink
sink = gst_element_factory_make (m_audioSink, NULL);
}
if (!sink) {
sink = gst_element_factory_make ("osssink", NULL);
if (canOpenDevice(sink))
debug() << "AudioOutput using" << QString::fromUtf8(m_audioSink);
else {
if (sink) {
gst_object_unref(sink);
sink = 0;
}
if ("pulsesink" == m_audioSink) {
// We've tried to use PulseAudio support, but the GST plugin
// doesn't exits. Let's try again, but not use PA support this time.
warning() << "PulseAudio support failed. Falling back to 'auto'";
PulseSupport::getInstance()->enable(false);
m_audioSink = "auto";
sink = createAudioSink();
}
debug() << "AudioOutput using oss audio sink";
else if (sink) {
gst_object_unref(sink);
sink = 0;
}
}
} else if (m_audioSink == "fake") {
//do nothing as a fakesink will be created by default
} else if (!m_audioSink.isEmpty()) { //Use a custom sink
sink = gst_element_factory_make (m_audioSink, NULL);
if (canOpenDevice(sink))
debug() << "AudioOutput using" << QString::fromUtf8(m_audioSink);
else {
if (sink) {
gst_object_unref(sink);
sink = 0;
}
if ("pulsesink" == m_audioSink) {
// We've tried to use PulseAudio support, but the GST plugin
// doesn't exits. Let's try again, but not use PA support this time.
warning() << "PulseAudio support failed. Falling back to 'auto'";
PulseSupport::getInstance()->enable(false);
m_audioSink = "auto";
sink = createAudioSink();
}
}
}
......
......@@ -88,50 +88,45 @@ MediaObject::MediaObject(Backend *backend, QObject *parent)
static int count = 0;
m_name = "MediaObject" + QString::number(count++);
if (!m_backend->isValid()) {
setError(tr("Cannot start playback. \n\nCheck your GStreamer installation and make sure you "
"\nhave libgstreamer-plugins-base installed."), Phonon::FatalError);
} else {
m_root = this;
m_pipeline = new Pipeline(this);
m_isValid = true;
GlobalSubtitles::instance()->register_(this);
GlobalAudioChannels::instance()->register_(this);
connect(m_pipeline, SIGNAL(aboutToFinish()),
this, SLOT(handleAboutToFinish()), Qt::DirectConnection);
connect(m_pipeline, SIGNAL(eos()),
this, SLOT(handleEndOfStream()));
connect(m_pipeline, SIGNAL(warning(QString)),
this, SLOT(logWarning(QString)));
connect(m_pipeline, SIGNAL(durationChanged(qint64)),
this, SLOT(handleDurationChange(qint64)));
connect(m_pipeline, SIGNAL(buffering(int)),
this, SIGNAL(bufferStatus(int)));
connect(m_pipeline, SIGNAL(stateChanged(GstState,GstState)),
this, SLOT(handleStateChange(GstState,GstState)));
connect(m_pipeline, SIGNAL(errorMessage(QString,Phonon::ErrorType)),
this, SLOT(setError(QString,Phonon::ErrorType)));
connect(m_pipeline, SIGNAL(metaDataChanged(QMultiMap<QString,QString>)),
this, SIGNAL(metaDataChanged(QMultiMap<QString,QString>)));
connect(m_pipeline, SIGNAL(availableMenusChanged(QList<MediaController::NavigationMenu>)),
this, SIGNAL(availableMenusChanged(QList<MediaController::NavigationMenu>)));
connect(m_pipeline, SIGNAL(videoAvailabilityChanged(bool)),
this, SIGNAL(hasVideoChanged(bool)));
connect(m_pipeline, SIGNAL(seekableChanged(bool)),
this, SIGNAL(seekableChanged(bool)));
connect(m_pipeline, SIGNAL(streamChanged()),
this, SLOT(handleStreamChange()));
connect(m_pipeline, SIGNAL(textTagChanged(int)),
this, SLOT(getSubtitleInfo(int)));
connect(m_pipeline, SIGNAL(audioTagChanged(int)),
this, SLOT(getAudioChannelInfo(int)));
connect(m_pipeline, SIGNAL(trackCountChanged(int)),
this, SLOT(handleTrackCountChange(int)));
connect(m_tickTimer, SIGNAL(timeout()), SLOT(emitTick()));
}
m_isValid = true;
m_root = this;
m_pipeline = new Pipeline(this);
GlobalSubtitles::instance()->register_(this);
GlobalAudioChannels::instance()->register_(this);
connect(m_pipeline, SIGNAL(aboutToFinish()),
this, SLOT(handleAboutToFinish()), Qt::DirectConnection);
connect(m_pipeline, SIGNAL(eos()),
this, SLOT(handleEndOfStream()));
connect(m_pipeline, SIGNAL(warning(QString)),
this, SLOT(logWarning(QString)));
connect(m_pipeline, SIGNAL(durationChanged(qint64)),
this, SLOT(handleDurationChange(qint64)));
connect(m_pipeline, SIGNAL(buffering(int)),
this, SIGNAL(bufferStatus(int)));
connect(m_pipeline, SIGNAL(stateChanged(GstState,GstState)),
this, SLOT(handleStateChange(GstState,GstState)));
connect(m_pipeline, SIGNAL(errorMessage(QString,Phonon::ErrorType)),
this, SLOT(setError(QString,Phonon::ErrorType)));
connect(m_pipeline, SIGNAL(metaDataChanged(QMultiMap<QString,QString>)),
this, SIGNAL(metaDataChanged(QMultiMap<QString,QString>)));
connect(m_pipeline, SIGNAL(availableMenusChanged(QList<MediaController::NavigationMenu>)),
this, SIGNAL(availableMenusChanged(QList<MediaController::NavigationMenu>)));
connect(m_pipeline, SIGNAL(videoAvailabilityChanged(bool)),
this, SIGNAL(hasVideoChanged(bool)));
connect(m_pipeline, SIGNAL(seekableChanged(bool)),
this, SIGNAL(seekableChanged(bool)));
connect(m_pipeline, SIGNAL(streamChanged()),
this, SLOT(handleStreamChange()));
connect(m_pipeline, SIGNAL(textTagChanged(int)),
this, SLOT(getSubtitleInfo(int)));
connect(m_pipeline, SIGNAL(audioTagChanged(int)),
this, SLOT(getAudioChannelInfo(int)));
connect(m_pipeline, SIGNAL(trackCountChanged(int)),
this, SLOT(handleTrackCountChange(int)));
connect(m_tickTimer, SIGNAL(timeout()), SLOT(emitTick()));
}
MediaObject::~MediaObject()
......@@ -382,9 +377,6 @@ qint64 MediaObject::getPipelinePos() const
*/
void MediaObject::setSource(const MediaSource &source)
{
if (!isValid())
return;
DEBUG_BLOCK;
if (source.type() == Phonon::MediaSource::Invalid) {
......@@ -487,9 +479,6 @@ void MediaObject::stop()
void MediaObject::seek(qint64 time)
{
if (!isValid())
return;
DEBUG_BLOCK;
if (m_waitingForNextSource) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment