| 1 | /*  This file is part of the KDE project.
 | 
|---|
| 2 | 
 | 
|---|
| 3 |     Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
 | 
|---|
| 4 | 
 | 
|---|
| 5 |     This library is free software: you can redistribute it and/or modify
 | 
|---|
| 6 |     it under the terms of the GNU Lesser General Public License as published by
 | 
|---|
| 7 |     the Free Software Foundation, either version 2.1 or 3 of the License.
 | 
|---|
| 8 | 
 | 
|---|
| 9 |     This library is distributed in the hope that it will be useful,
 | 
|---|
| 10 |     but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
|---|
| 11 |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
|---|
| 12 |     GNU Lesser General Public License for more details.
 | 
|---|
| 13 | 
 | 
|---|
| 14 |     You should have received a copy of the GNU Lesser General Public License
 | 
|---|
| 15 |     along with this library.  If not, see <http://www.gnu.org/licenses/>.
 | 
|---|
| 16 | */
 | 
|---|
| 17 | 
 | 
|---|
| 18 | #include "common.h"
 | 
|---|
| 19 | #include "backend.h"
 | 
|---|
| 20 | #include "audiooutput.h"
 | 
|---|
| 21 | #include "audiodataoutput.h"
 | 
|---|
| 22 | #include "audioeffect.h"
 | 
|---|
| 23 | #include "mediaobject.h"
 | 
|---|
| 24 | #include "videowidget.h"
 | 
|---|
| 25 | #include "devicemanager.h"
 | 
|---|
| 26 | #include "effectmanager.h"
 | 
|---|
| 27 | #include "message.h"
 | 
|---|
| 28 | #include "volumefadereffect.h"
 | 
|---|
| 29 | #include <gst/interfaces/propertyprobe.h>
 | 
|---|
| 30 | #include <phonon/pulsesupport.h>
 | 
|---|
| 31 | 
 | 
|---|
| 32 | #include <QtCore/QSet>
 | 
|---|
| 33 | #include <QtCore/QVariant>
 | 
|---|
| 34 | #include <QtCore/QtPlugin>
 | 
|---|
| 35 | 
 | 
|---|
| 36 | QT_BEGIN_NAMESPACE
 | 
|---|
| 37 | 
 | 
|---|
| 38 | Q_EXPORT_PLUGIN2(phonon_gstreamer, Phonon::Gstreamer::Backend)
 | 
|---|
| 39 | 
 | 
|---|
| 40 | namespace Phonon
 | 
|---|
| 41 | {
 | 
|---|
| 42 | namespace Gstreamer
 | 
|---|
| 43 | {
 | 
|---|
| 44 | 
 | 
|---|
| 45 | class MediaNode;
 | 
|---|
| 46 | 
 | 
|---|
| 47 | Backend::Backend(QObject *parent, const QVariantList &)
 | 
|---|
| 48 |         : QObject(parent)
 | 
|---|
| 49 |         , m_deviceManager(0)
 | 
|---|
| 50 |         , m_effectManager(0)
 | 
|---|
| 51 |         , m_debugLevel(Warning)
 | 
|---|
| 52 |         , m_isValid(false)
 | 
|---|
| 53 | {
 | 
|---|
| 54 |     // Initialise PulseAudio support
 | 
|---|
| 55 |     PulseSupport *pulse = PulseSupport::getInstance();
 | 
|---|
| 56 |     pulse->enable();
 | 
|---|
| 57 |     connect(pulse, SIGNAL(objectDescriptionChanged(ObjectDescriptionType)), SIGNAL(objectDescriptionChanged(ObjectDescriptionType)));
 | 
|---|
| 58 | 
 | 
|---|
| 59 |     // In order to support reloading, we only set the app name once...
 | 
|---|
| 60 |     static bool first = true;
 | 
|---|
| 61 |     if (first) {
 | 
|---|
| 62 |         first = false;
 | 
|---|
| 63 |         g_set_application_name(qApp->applicationName().toUtf8());
 | 
|---|
| 64 |     }
 | 
|---|
| 65 |     GError *err = 0;
 | 
|---|
| 66 |     bool wasInit = gst_init_check(0, 0, &err);  //init gstreamer: must be called before any gst-related functions
 | 
|---|
| 67 |     if (err)
 | 
|---|
| 68 |         g_error_free(err);
 | 
|---|
| 69 | 
 | 
|---|
| 70 |     qRegisterMetaType<Message>("Message");
 | 
|---|
| 71 | #ifndef QT_NO_PROPERTIES
 | 
|---|
| 72 |     setProperty("identifier",     QLatin1String("phonon_gstreamer"));
 | 
|---|
| 73 |     setProperty("backendName",    QLatin1String("Gstreamer"));
 | 
|---|
| 74 |     setProperty("backendComment", QLatin1String("Gstreamer plugin for Phonon"));
 | 
|---|
| 75 |     setProperty("backendVersion", QLatin1String("0.2"));
 | 
|---|
| 76 |     setProperty("backendWebsite", QLatin1String("http://qt.nokia.com/"));
 | 
|---|
| 77 | #endif //QT_NO_PROPERTIES
 | 
|---|
| 78 | 
 | 
|---|
| 79 |     //check if we should enable debug output
 | 
|---|
| 80 |     QString debugLevelString = qgetenv("PHONON_GST_DEBUG");
 | 
|---|
| 81 |     int debugLevel = debugLevelString.toInt();
 | 
|---|
| 82 |     if (debugLevel > 3) //3 is maximum
 | 
|---|
| 83 |         debugLevel = 3;
 | 
|---|
| 84 |     m_debugLevel = (DebugLevel)debugLevel;
 | 
|---|
| 85 | 
 | 
|---|
| 86 |     if (wasInit) {
 | 
|---|
| 87 |         m_isValid = checkDependencies();
 | 
|---|
| 88 |         gchar *versionString = gst_version_string();
 | 
|---|
| 89 |         logMessage(QString("Using %0").arg(versionString));
 | 
|---|
| 90 |         g_free(versionString);
 | 
|---|
| 91 |     }
 | 
|---|
| 92 |     if (!m_isValid)
 | 
|---|
| 93 |         qWarning("Phonon::GStreamer::Backend: Failed to initialize GStreamer");
 | 
|---|
| 94 | 
 | 
|---|
| 95 |     m_deviceManager = new DeviceManager(this);
 | 
|---|
| 96 |     m_effectManager = new EffectManager(this);
 | 
|---|
| 97 | }
 | 
|---|
| 98 | 
 | 
|---|
| 99 | Backend::~Backend() 
 | 
|---|
| 100 | {
 | 
|---|
| 101 |     delete m_effectManager;
 | 
|---|
| 102 |     delete m_deviceManager;
 | 
|---|
| 103 |     PulseSupport::shutdown();
 | 
|---|
| 104 | }
 | 
|---|
| 105 | 
 | 
|---|
| 106 | gboolean Backend::busCall(GstBus *bus, GstMessage *msg, gpointer data)
 | 
|---|
| 107 | {
 | 
|---|
| 108 |     Q_UNUSED(bus);
 | 
|---|
| 109 |     Q_ASSERT(msg);
 | 
|---|
| 110 | 
 | 
|---|
| 111 |     MediaObject *mediaObject = static_cast<MediaObject*>(data);
 | 
|---|
| 112 |     Q_ASSERT(mediaObject);
 | 
|---|
| 113 | 
 | 
|---|
| 114 |     Message message(msg, mediaObject);
 | 
|---|
| 115 |     QMetaObject::invokeMethod(mediaObject->backend(), "handleBusMessage", Qt::QueuedConnection, Q_ARG(Message, message));
 | 
|---|
| 116 | 
 | 
|---|
| 117 |     return true;
 | 
|---|
| 118 | }
 | 
|---|
| 119 | 
 | 
|---|
| 120 | /***
 | 
|---|
| 121 |  * !reimp
 | 
|---|
| 122 |  */
 | 
|---|
| 123 | QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args)
 | 
|---|
| 124 | {
 | 
|---|
| 125 |     // Return nothing if dependencies are not met
 | 
|---|
| 126 | 
 | 
|---|
| 127 |     switch (c) {
 | 
|---|
| 128 |     case MediaObjectClass:
 | 
|---|
| 129 |         return new MediaObject(this, parent);
 | 
|---|
| 130 | 
 | 
|---|
| 131 |     case AudioOutputClass:
 | 
|---|
| 132 |         return new AudioOutput(this, parent);
 | 
|---|
| 133 | 
 | 
|---|
| 134 | #ifndef QT_NO_PHONON_EFFECT
 | 
|---|
| 135 |     case EffectClass:
 | 
|---|
| 136 |         return new AudioEffect(this, args[0].toInt(), parent);
 | 
|---|
| 137 | #endif //QT_NO_PHONON_EFFECT
 | 
|---|
| 138 |     case AudioDataOutputClass:
 | 
|---|
| 139 |         return new AudioDataOutput(this, parent);
 | 
|---|
| 140 | 
 | 
|---|
| 141 | #ifndef QT_NO_PHONON_VIDEO
 | 
|---|
| 142 |     case VideoDataOutputClass:
 | 
|---|
| 143 |         logMessage("createObject() : VideoDataOutput not implemented");
 | 
|---|
| 144 |         break;
 | 
|---|
| 145 | 
 | 
|---|
| 146 |     case VideoWidgetClass: {
 | 
|---|
| 147 |             QWidget *widget =  qobject_cast<QWidget*>(parent);
 | 
|---|
| 148 |             return new VideoWidget(this, widget);
 | 
|---|
| 149 |         }
 | 
|---|
| 150 | #endif //QT_NO_PHONON_VIDEO
 | 
|---|
| 151 | #ifndef QT_NO_PHONON_VOLUMEFADEREFFECT
 | 
|---|
| 152 |     case VolumeFaderEffectClass:
 | 
|---|
| 153 |         return new VolumeFaderEffect(this, parent);
 | 
|---|
| 154 | #endif //QT_NO_PHONON_VOLUMEFADEREFFECT
 | 
|---|
| 155 | 
 | 
|---|
| 156 |     case VisualizationClass:  //Fall through
 | 
|---|
| 157 |     default:
 | 
|---|
| 158 |         logMessage("createObject() : Backend object not available");
 | 
|---|
| 159 |     }
 | 
|---|
| 160 |     return 0;
 | 
|---|
| 161 | }
 | 
|---|
| 162 | 
 | 
|---|
| 163 | // Returns true if all dependencies are met
 | 
|---|
| 164 | // and gstreamer is usable, otherwise false
 | 
|---|
| 165 | bool Backend::isValid() const
 | 
|---|
| 166 | {
 | 
|---|
| 167 |     return m_isValid;
 | 
|---|
| 168 | }
 | 
|---|
| 169 | 
 | 
|---|
| 170 | bool Backend::supportsVideo() const
 | 
|---|
| 171 | {
 | 
|---|
| 172 |     return isValid();
 | 
|---|
| 173 | }
 | 
|---|
| 174 | 
 | 
|---|
| 175 | bool Backend::checkDependencies() const
 | 
|---|
| 176 | {
 | 
|---|
| 177 |     bool success = false;
 | 
|---|
| 178 |     // Verify that gst-plugins-base is installed
 | 
|---|
| 179 |     GstElementFactory *acFactory = gst_element_factory_find ("audioconvert");
 | 
|---|
| 180 |     if (acFactory) {
 | 
|---|
| 181 |         gst_object_unref(acFactory);
 | 
|---|
| 182 |         success = true;
 | 
|---|
| 183 |         // Check if gst-plugins-good is installed
 | 
|---|
| 184 |         GstElementFactory *csFactory = gst_element_factory_find ("videobalance");
 | 
|---|
| 185 |         if (csFactory) {
 | 
|---|
| 186 |             gst_object_unref(csFactory);
 | 
|---|
| 187 |         } else {
 | 
|---|
| 188 |             QString message = tr("Warning: You do not seem to have the package gstreamer0.10-plugins-good installed.\n"
 | 
|---|
| 189 |                                  "          Some video features have been disabled.");
 | 
|---|
| 190 |             qDebug() << message;
 | 
|---|
| 191 |         }
 | 
|---|
| 192 |     } else {
 | 
|---|
| 193 |         qWarning() << tr("Warning: You do not seem to have the base GStreamer plugins installed.\n"
 | 
|---|
| 194 |                          "          All audio and video support has been disabled");
 | 
|---|
| 195 |     }
 | 
|---|
| 196 |     return success;
 | 
|---|
| 197 | }
 | 
|---|
| 198 | 
 | 
|---|
| 199 | /***
 | 
|---|
| 200 |  * !reimp
 | 
|---|
| 201 |  */
 | 
|---|
| 202 | QStringList Backend::availableMimeTypes() const
 | 
|---|
| 203 | {
 | 
|---|
| 204 |     QStringList availableMimeTypes;
 | 
|---|
| 205 | 
 | 
|---|
| 206 |     if (!isValid())
 | 
|---|
| 207 |         return availableMimeTypes;
 | 
|---|
| 208 | 
 | 
|---|
| 209 |     GstElementFactory *mpegFactory;
 | 
|---|
| 210 |     // Add mp3 as a separate mime type as people are likely to look for it.
 | 
|---|
| 211 |     if ((mpegFactory = gst_element_factory_find ("ffmpeg")) || 
 | 
|---|
| 212 |         (mpegFactory = gst_element_factory_find ("mad"))) {
 | 
|---|
| 213 |         availableMimeTypes << QLatin1String("audio/x-mp3");
 | 
|---|
| 214 |         gst_object_unref(GST_OBJECT(mpegFactory));
 | 
|---|
| 215 |     }
 | 
|---|
| 216 | 
 | 
|---|
| 217 |     // Iterate over all audio and video decoders and extract mime types from sink caps
 | 
|---|
| 218 |     GList* factoryList = gst_registry_get_feature_list(gst_registry_get_default (), GST_TYPE_ELEMENT_FACTORY);
 | 
|---|
| 219 |     for (GList* iter = g_list_first(factoryList) ; iter != NULL ; iter = g_list_next(iter)) {
 | 
|---|
| 220 |         GstPluginFeature *feature = GST_PLUGIN_FEATURE(iter->data);
 | 
|---|
| 221 |         QString klass = gst_element_factory_get_klass(GST_ELEMENT_FACTORY(feature));
 | 
|---|
| 222 | 
 | 
|---|
| 223 |         if (klass == QLatin1String("Codec/Decoder") || 
 | 
|---|
| 224 |             klass == QLatin1String("Codec/Decoder/Audio") || 
 | 
|---|
| 225 |             klass == QLatin1String("Codec/Decoder/Video") || 
 | 
|---|
| 226 |             klass == QLatin1String("Codec/Demuxer") || 
 | 
|---|
| 227 |             klass == QLatin1String("Codec/Demuxer/Audio") || 
 | 
|---|
| 228 |             klass == QLatin1String("Codec/Demuxer/Video") || 
 | 
|---|
| 229 |             klass == QLatin1String("Codec/Parser") || 
 | 
|---|
| 230 |             klass == QLatin1String("Codec/Parser/Audio") || 
 | 
|---|
| 231 |             klass == QLatin1String("Codec/Parser/Video")) {
 | 
|---|
| 232 | 
 | 
|---|
| 233 |             const GList *static_templates;
 | 
|---|
| 234 |             GstElementFactory *factory = GST_ELEMENT_FACTORY(feature);
 | 
|---|
| 235 |             static_templates = gst_element_factory_get_static_pad_templates(factory);
 | 
|---|
| 236 | 
 | 
|---|
| 237 |             for (; static_templates != NULL ; static_templates = static_templates->next) {
 | 
|---|
| 238 |                 GstStaticPadTemplate *padTemplate = (GstStaticPadTemplate *) static_templates->data;
 | 
|---|
| 239 |                 if (padTemplate && padTemplate->direction == GST_PAD_SINK) {
 | 
|---|
| 240 |                     GstCaps *caps = gst_static_pad_template_get_caps (padTemplate);
 | 
|---|
| 241 | 
 | 
|---|
| 242 |                     if (caps) {
 | 
|---|
| 243 |                         for (unsigned int struct_idx = 0; struct_idx < gst_caps_get_size (caps); struct_idx++) {
 | 
|---|
| 244 | 
 | 
|---|
| 245 |                             const GstStructure* capsStruct = gst_caps_get_structure (caps, struct_idx);
 | 
|---|
| 246 |                             QString mime = QString::fromUtf8(gst_structure_get_name (capsStruct));
 | 
|---|
| 247 |                             if (!availableMimeTypes.contains(mime))
 | 
|---|
| 248 |                                 availableMimeTypes.append(mime);
 | 
|---|
| 249 |                         }
 | 
|---|
| 250 |                     }
 | 
|---|
| 251 |                 }
 | 
|---|
| 252 |             }
 | 
|---|
| 253 |         }
 | 
|---|
| 254 |     }
 | 
|---|
| 255 |     g_list_free(factoryList);
 | 
|---|
| 256 |     if (availableMimeTypes.contains("audio/x-vorbis")
 | 
|---|
| 257 |         && availableMimeTypes.contains("application/x-ogm-audio")) {
 | 
|---|
| 258 |         if (!availableMimeTypes.contains("audio/x-vorbis+ogg"))
 | 
|---|
| 259 |             availableMimeTypes.append("audio/x-vorbis+ogg");
 | 
|---|
| 260 |         if (!availableMimeTypes.contains("application/ogg"))  /* *.ogg */
 | 
|---|
| 261 |             availableMimeTypes.append("application/ogg");
 | 
|---|
| 262 |         if (!availableMimeTypes.contains("audio/ogg")) /* *.oga */
 | 
|---|
| 263 |             availableMimeTypes.append("audio/ogg");
 | 
|---|
| 264 |     }
 | 
|---|
| 265 |     availableMimeTypes.sort();
 | 
|---|
| 266 |     return availableMimeTypes;
 | 
|---|
| 267 | }
 | 
|---|
| 268 | 
 | 
|---|
| 269 | /***
 | 
|---|
| 270 |  * !reimp
 | 
|---|
| 271 |  */
 | 
|---|
| 272 | QList<int> Backend::objectDescriptionIndexes(ObjectDescriptionType type) const
 | 
|---|
| 273 | {
 | 
|---|
| 274 |     QList<int> list;
 | 
|---|
| 275 | 
 | 
|---|
| 276 |     if (!isValid())
 | 
|---|
| 277 |         return list;
 | 
|---|
| 278 | 
 | 
|---|
| 279 |     switch (type) {
 | 
|---|
| 280 |     case Phonon::AudioOutputDeviceType: {
 | 
|---|
| 281 |             QList<AudioDevice> deviceList = deviceManager()->audioOutputDevices();
 | 
|---|
| 282 |             for (int dev = 0 ; dev < deviceList.size() ; ++dev)
 | 
|---|
| 283 |                 list.append(deviceList[dev].id);
 | 
|---|
| 284 |             break;
 | 
|---|
| 285 |         }
 | 
|---|
| 286 |         break;
 | 
|---|
| 287 | 
 | 
|---|
| 288 |     case Phonon::EffectType: {
 | 
|---|
| 289 |             QList<EffectInfo*> effectList = effectManager()->audioEffects();
 | 
|---|
| 290 |             for (int eff = 0 ; eff < effectList.size() ; ++eff)
 | 
|---|
| 291 |                 list.append(eff);
 | 
|---|
| 292 |             break;
 | 
|---|
| 293 |         }
 | 
|---|
| 294 |         break;
 | 
|---|
| 295 |     default:
 | 
|---|
| 296 |         break;
 | 
|---|
| 297 |     }
 | 
|---|
| 298 |     return list;
 | 
|---|
| 299 | }
 | 
|---|
| 300 | 
 | 
|---|
| 301 | /***
 | 
|---|
| 302 |  * !reimp
 | 
|---|
| 303 |  */
 | 
|---|
| 304 | QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(ObjectDescriptionType type, int index) const
 | 
|---|
| 305 | {
 | 
|---|
| 306 | 
 | 
|---|
| 307 |     QHash<QByteArray, QVariant> ret;
 | 
|---|
| 308 | 
 | 
|---|
| 309 |     if (!isValid())
 | 
|---|
| 310 |         return ret;
 | 
|---|
| 311 | 
 | 
|---|
| 312 |     switch (type) {
 | 
|---|
| 313 |     case Phonon::AudioOutputDeviceType: {
 | 
|---|
| 314 |             AudioDevice* ad;
 | 
|---|
| 315 |             if ((ad = deviceManager()->audioDevice(index))) {
 | 
|---|
| 316 |                 ret.insert("name", ad->gstId);
 | 
|---|
| 317 |                 ret.insert("description", ad->description);
 | 
|---|
| 318 |                 ret.insert("icon", ad->icon);
 | 
|---|
| 319 |             }
 | 
|---|
| 320 |         }
 | 
|---|
| 321 |         break;
 | 
|---|
| 322 | 
 | 
|---|
| 323 |     case Phonon::EffectType: {
 | 
|---|
| 324 |             QList<EffectInfo*> effectList = effectManager()->audioEffects();
 | 
|---|
| 325 |             if (index >= 0 && index <= effectList.size()) {
 | 
|---|
| 326 |                 const EffectInfo *effect = effectList[index];
 | 
|---|
| 327 |                 ret.insert("name", effect->name());
 | 
|---|
| 328 |                 ret.insert("description", effect->description());
 | 
|---|
| 329 |                 ret.insert("author", effect->author());
 | 
|---|
| 330 |             } else
 | 
|---|
| 331 |                 Q_ASSERT(1); // Since we use list position as ID, this should not happen
 | 
|---|
| 332 |         }
 | 
|---|
| 333 |     default:
 | 
|---|
| 334 |         break;
 | 
|---|
| 335 |     }
 | 
|---|
| 336 |     return ret;
 | 
|---|
| 337 | }
 | 
|---|
| 338 | 
 | 
|---|
| 339 | /***
 | 
|---|
| 340 |  * !reimp
 | 
|---|
| 341 |  */
 | 
|---|
| 342 | bool Backend::startConnectionChange(QSet<QObject *> objects)
 | 
|---|
| 343 | {
 | 
|---|
| 344 |     foreach (QObject *object, objects) {
 | 
|---|
| 345 |         MediaNode *sourceNode = qobject_cast<MediaNode *>(object);
 | 
|---|
| 346 |         MediaObject *media = sourceNode->root();
 | 
|---|
| 347 |         if (media) {
 | 
|---|
| 348 |             media->saveState();
 | 
|---|
| 349 |             return true;
 | 
|---|
| 350 |         }
 | 
|---|
| 351 |     }
 | 
|---|
| 352 |     return true;
 | 
|---|
| 353 | }
 | 
|---|
| 354 | 
 | 
|---|
| 355 | /***
 | 
|---|
| 356 |  * !reimp
 | 
|---|
| 357 |  */
 | 
|---|
| 358 | bool Backend::connectNodes(QObject *source, QObject *sink)
 | 
|---|
| 359 | {
 | 
|---|
| 360 |     if (isValid()) {
 | 
|---|
| 361 |         MediaNode *sourceNode = qobject_cast<MediaNode *>(source);
 | 
|---|
| 362 |         MediaNode *sinkNode = qobject_cast<MediaNode *>(sink);
 | 
|---|
| 363 |         if (sourceNode && sinkNode) {
 | 
|---|
| 364 |             if (sourceNode->connectNode(sink)) {
 | 
|---|
| 365 |                 sourceNode->root()->invalidateGraph();
 | 
|---|
| 366 |                 logMessage(QString("Backend connected %0 to %1").arg(source->metaObject()->className()).arg(sink->metaObject()->className()));
 | 
|---|
| 367 |                 return true;
 | 
|---|
| 368 |             }
 | 
|---|
| 369 |         }
 | 
|---|
| 370 |     }
 | 
|---|
| 371 |     logMessage(QString("Linking %0 to %1 failed").arg(source->metaObject()->className()).arg(sink->metaObject()->className()), Warning);
 | 
|---|
| 372 |     return false;
 | 
|---|
| 373 | }
 | 
|---|
| 374 | 
 | 
|---|
| 375 | /***
 | 
|---|
| 376 |  * !reimp
 | 
|---|
| 377 |  */
 | 
|---|
| 378 | bool Backend::disconnectNodes(QObject *source, QObject *sink)
 | 
|---|
| 379 | {
 | 
|---|
| 380 |     MediaNode *sourceNode = qobject_cast<MediaNode *>(source);
 | 
|---|
| 381 |     MediaNode *sinkNode = qobject_cast<MediaNode *>(sink);
 | 
|---|
| 382 | 
 | 
|---|
| 383 |     if (sourceNode && sinkNode)
 | 
|---|
| 384 |         return sourceNode->disconnectNode(sink);
 | 
|---|
| 385 |     else
 | 
|---|
| 386 |         return false;
 | 
|---|
| 387 | }
 | 
|---|
| 388 | 
 | 
|---|
| 389 | /***
 | 
|---|
| 390 |  * !reimp
 | 
|---|
| 391 |  */
 | 
|---|
| 392 | bool Backend::endConnectionChange(QSet<QObject *> objects)
 | 
|---|
| 393 | {
 | 
|---|
| 394 |     foreach (QObject *object, objects) {
 | 
|---|
| 395 |         MediaNode *sourceNode = qobject_cast<MediaNode *>(object);
 | 
|---|
| 396 |         MediaObject *media = sourceNode->root();
 | 
|---|
| 397 |         if (media) {
 | 
|---|
| 398 |             media->resumeState();
 | 
|---|
| 399 |             return true;
 | 
|---|
| 400 |         }
 | 
|---|
| 401 |     }
 | 
|---|
| 402 |     return true;
 | 
|---|
| 403 | }
 | 
|---|
| 404 | 
 | 
|---|
| 405 | /***
 | 
|---|
| 406 |  * Request bus messages for this mediaobject
 | 
|---|
| 407 |  */
 | 
|---|
| 408 | void Backend::addBusWatcher(MediaObject* node)
 | 
|---|
| 409 | {
 | 
|---|
| 410 |     Q_ASSERT(node);
 | 
|---|
| 411 |     GstBus *bus = gst_pipeline_get_bus (GST_PIPELINE(node->pipeline()));
 | 
|---|
| 412 |     gst_bus_add_watch (bus, busCall, node);
 | 
|---|
| 413 |     gst_object_unref(bus);
 | 
|---|
| 414 | }
 | 
|---|
| 415 | 
 | 
|---|
| 416 | /***
 | 
|---|
| 417 |  * Ignore bus messages for this mediaobject
 | 
|---|
| 418 |  */
 | 
|---|
| 419 | void Backend::removeBusWatcher(MediaObject* node)
 | 
|---|
| 420 | {
 | 
|---|
| 421 |     Q_ASSERT(node);
 | 
|---|
| 422 |     g_source_remove_by_user_data(node);
 | 
|---|
| 423 | }
 | 
|---|
| 424 | 
 | 
|---|
| 425 | /***
 | 
|---|
| 426 |  * Polls each mediaobject's pipeline and delivers
 | 
|---|
| 427 |  * pending any pending messages
 | 
|---|
| 428 |  */
 | 
|---|
| 429 | void Backend::handleBusMessage(Message message)
 | 
|---|
| 430 | {
 | 
|---|
| 431 |     MediaObject *mediaObject = message.source();
 | 
|---|
| 432 |     mediaObject->handleBusMessage(message);
 | 
|---|
| 433 | }
 | 
|---|
| 434 | 
 | 
|---|
| 435 | DeviceManager* Backend::deviceManager() const
 | 
|---|
| 436 | {
 | 
|---|
| 437 |     return m_deviceManager;
 | 
|---|
| 438 | }
 | 
|---|
| 439 | 
 | 
|---|
| 440 | EffectManager* Backend::effectManager() const
 | 
|---|
| 441 | {
 | 
|---|
| 442 |     return m_effectManager;
 | 
|---|
| 443 | }
 | 
|---|
| 444 | 
 | 
|---|
| 445 | /**
 | 
|---|
| 446 |  * Returns a debuglevel that is determined by the
 | 
|---|
| 447 |  * PHONON_GST_DEBUG environment variable.
 | 
|---|
| 448 |  *
 | 
|---|
| 449 |  *  Warning - important warnings
 | 
|---|
| 450 |  *  Info    - general info
 | 
|---|
| 451 |  *  Debug   - gives extra info
 | 
|---|
| 452 |  */
 | 
|---|
| 453 | Backend::DebugLevel Backend::debugLevel() const
 | 
|---|
| 454 | {
 | 
|---|
| 455 |     return m_debugLevel;
 | 
|---|
| 456 | }
 | 
|---|
| 457 | 
 | 
|---|
| 458 | /***
 | 
|---|
| 459 |  * Prints a conditional debug message based on the current debug level
 | 
|---|
| 460 |  * If obj is provided, classname and objectname will be printed as well
 | 
|---|
| 461 |  *
 | 
|---|
| 462 |  * see debugLevel()
 | 
|---|
| 463 |  */
 | 
|---|
| 464 | void Backend::logMessage(const QString &message, int priority, QObject *obj) const
 | 
|---|
| 465 | {
 | 
|---|
| 466 |     if (debugLevel() > 0) {
 | 
|---|
| 467 |         QString output;
 | 
|---|
| 468 |         if (obj) {
 | 
|---|
| 469 |             // Strip away namespace from className
 | 
|---|
| 470 |             QString className(obj->metaObject()->className());
 | 
|---|
| 471 |             int nameLength = className.length() - className.lastIndexOf(':') - 1;
 | 
|---|
| 472 |             className = className.right(nameLength);
 | 
|---|
| 473 |             output.sprintf("%s %s (%s %p)", message.toLatin1().constData(), 
 | 
|---|
| 474 |                                           obj->objectName().toLatin1().constData(), 
 | 
|---|
| 475 |                                           className.toLatin1().constData(), obj);
 | 
|---|
| 476 |         }
 | 
|---|
| 477 |         else {
 | 
|---|
| 478 |             output = message;
 | 
|---|
| 479 |         }
 | 
|---|
| 480 |         if (priority <= (int)debugLevel()) {
 | 
|---|
| 481 |             qDebug() << QString("PGST(%1): %2").arg(priority).arg(output);
 | 
|---|
| 482 |         }
 | 
|---|
| 483 |     }
 | 
|---|
| 484 | }
 | 
|---|
| 485 | 
 | 
|---|
| 486 | }
 | 
|---|
| 487 | }
 | 
|---|
| 488 | 
 | 
|---|
| 489 | QT_END_NAMESPACE
 | 
|---|
| 490 | 
 | 
|---|
| 491 | #include "moc_backend.cpp"
 | 
|---|