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