src/3rdparty/phonon/gstreamer/devicemanager.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 <gst/interfaces/propertyprobe.h>
       
    19 #include "devicemanager.h"
       
    20 #include "backend.h"
       
    21 #include "gsthelper.h"
       
    22 #include "videowidget.h"
       
    23 #include "glrenderer.h"
       
    24 #include "widgetrenderer.h"
       
    25 #include "x11renderer.h"
       
    26 #include "artssink.h"
       
    27 
       
    28 #ifdef USE_ALSASINK2
       
    29 #include "alsasink2.h"
       
    30 #endif
       
    31 
       
    32 /*
       
    33  * This class manages the list of currently
       
    34  * active output devices
       
    35  */
       
    36 
       
    37 QT_BEGIN_NAMESPACE
       
    38 
       
    39 namespace Phonon
       
    40 {
       
    41 namespace Gstreamer
       
    42 {
       
    43 
       
    44 AudioDevice::AudioDevice(DeviceManager *manager, const QByteArray &gstId)
       
    45         : gstId(gstId)
       
    46 {
       
    47     //get an id
       
    48     static int counter = 0;
       
    49     id = counter++;
       
    50     //get name from device
       
    51     if (gstId == "default") {
       
    52         description = "Default audio device";
       
    53     } else {
       
    54         GstElement *aSink= manager->createAudioSink();
       
    55 
       
    56         if (aSink) {
       
    57             gchar *deviceDescription = NULL;
       
    58 
       
    59             if (GST_IS_PROPERTY_PROBE(aSink) && gst_property_probe_get_property( GST_PROPERTY_PROBE(aSink), "device" ) ) {
       
    60                 g_object_set (G_OBJECT(aSink), "device", gstId.constData(), (const char*)NULL);
       
    61                 g_object_get (G_OBJECT(aSink), "device-name", &deviceDescription, (const char*)NULL);
       
    62                 description = QByteArray(deviceDescription);
       
    63                 g_free (deviceDescription);
       
    64                 gst_element_set_state(aSink, GST_STATE_NULL);
       
    65                 gst_object_unref (aSink);
       
    66             }
       
    67         }
       
    68     }
       
    69 }
       
    70 
       
    71 DeviceManager::DeviceManager(Backend *backend)
       
    72         : QObject(backend)
       
    73         , m_backend(backend)
       
    74 {
       
    75     QSettings settings(QLatin1String("Trolltech"));
       
    76     settings.beginGroup(QLatin1String("Qt"));
       
    77 
       
    78     m_audioSink = qgetenv("PHONON_GST_AUDIOSINK");
       
    79     if (m_audioSink.isEmpty()) {
       
    80         m_audioSink = settings.value(QLatin1String("audiosink"), "Auto").toByteArray().toLower();
       
    81     }
       
    82 
       
    83     m_videoSinkWidget = qgetenv("PHONON_GST_VIDEOMODE");
       
    84     if (m_videoSinkWidget.isEmpty()) {
       
    85         m_videoSinkWidget = settings.value(QLatin1String("videomode"), "Auto").toByteArray().toLower();
       
    86     }
       
    87 
       
    88     if (m_backend->isValid())
       
    89         updateDeviceList();
       
    90 }
       
    91 
       
    92 DeviceManager::~DeviceManager()
       
    93 {
       
    94     m_audioDeviceList.clear();
       
    95 }
       
    96 
       
    97 /***
       
    98 * Returns a Gst Audiosink based on GNOME configuration settings,
       
    99 * or 0 if the element is not available.
       
   100 */
       
   101 GstElement *DeviceManager::createGNOMEAudioSink(Category category)
       
   102 {
       
   103     GstElement *sink = gst_element_factory_make ("gconfaudiosink", NULL);
       
   104 
       
   105     if (sink) {
       
   106 
       
   107         // set profile property on the gconfaudiosink to "music and movies"
       
   108         if (g_object_class_find_property (G_OBJECT_GET_CLASS (sink), "profile")) {
       
   109             switch (category) {
       
   110             case NotificationCategory:
       
   111                 g_object_set (G_OBJECT (sink), "profile", 0, (const char*)NULL); // 0 = 'sounds'
       
   112                 break;
       
   113             case CommunicationCategory:
       
   114                 g_object_set (G_OBJECT (sink), "profile", 2, (const char*)NULL); // 2 = 'chat'
       
   115                 break;
       
   116             default:
       
   117                 g_object_set (G_OBJECT (sink), "profile", 1, (const char*)NULL); // 1 = 'music and movies'
       
   118                 break;
       
   119             }
       
   120         }
       
   121     }
       
   122     return sink;
       
   123 }
       
   124 
       
   125 
       
   126 bool DeviceManager::canOpenDevice(GstElement *element) const
       
   127 {
       
   128     if (!element)
       
   129         return false;
       
   130 
       
   131     if (gst_element_set_state(element, GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS)
       
   132         return true;
       
   133 
       
   134     const QList<QByteArray> &list = GstHelper::extractProperties(element, "device");
       
   135     foreach (const QByteArray &gstId, list) {
       
   136         GstHelper::setProperty(element, "device", gstId);
       
   137         if (gst_element_set_state(element, GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS) {
       
   138             return true;
       
   139         }
       
   140     }
       
   141     // FIXME: the above can still fail for a valid alsasink because list only contains entries of
       
   142     // the form "hw:X,Y". Would be better to use "default:X" or "dmix:X,Y"
       
   143 
       
   144     gst_element_set_state(element, GST_STATE_NULL);
       
   145     return false;
       
   146 }
       
   147 
       
   148 /*
       
   149 *
       
   150 * Returns a GstElement with a valid audio sink
       
   151 * based on the current value of PHONON_GSTREAMER_DRIVER
       
   152 *
       
   153 * Allowed values are auto (default), alsa, oss, arts and ess
       
   154 * does not exist
       
   155 *
       
   156 * If no real sound sink is available a fakesink will be returned
       
   157 */
       
   158 GstElement *DeviceManager::createAudioSink(Category category)
       
   159 {
       
   160     GstElement *sink = 0;
       
   161 
       
   162     if (m_backend && m_backend->isValid())
       
   163     {
       
   164         if (m_audioSink == "auto") //this is the default value
       
   165         {
       
   166             //### TODO : get equivalent KDE settings here
       
   167 
       
   168             if (!qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) {
       
   169                 sink = createGNOMEAudioSink(category);
       
   170                 if (canOpenDevice(sink))
       
   171                     m_backend->logMessage("AudioOutput using gconf audio sink");
       
   172                 else if (sink) {
       
   173                     gst_object_unref(sink);
       
   174                     sink = 0;
       
   175                 }
       
   176             }
       
   177 
       
   178 #ifdef USE_ALSASINK2
       
   179             if (!sink) {
       
   180                 sink = gst_element_factory_make ("_k_alsasink", NULL);
       
   181                 if (canOpenDevice(sink))
       
   182                     m_backend->logMessage("AudioOutput using alsa2 audio sink");
       
   183                 else if (sink) {
       
   184                     gst_object_unref(sink);
       
   185                     sink = 0;
       
   186                 }
       
   187             }
       
   188 #endif
       
   189 
       
   190             if (!sink) {
       
   191                 sink = gst_element_factory_make ("alsasink", NULL);
       
   192                 if (canOpenDevice(sink))
       
   193                     m_backend->logMessage("AudioOutput using alsa audio sink");
       
   194                 else if (sink) {
       
   195                     gst_object_unref(sink);
       
   196                     sink = 0;
       
   197                 }
       
   198             }
       
   199 
       
   200             if (!sink) {
       
   201                 sink = gst_element_factory_make ("autoaudiosink", NULL);
       
   202                 if (canOpenDevice(sink))
       
   203                     m_backend->logMessage("AudioOutput using auto audio sink");
       
   204                 else if (sink) {
       
   205                     gst_object_unref(sink);
       
   206                     sink = 0;
       
   207                 }
       
   208             }
       
   209 
       
   210             if (!sink) {
       
   211                 sink = gst_element_factory_make ("osssink", NULL);
       
   212                 if (canOpenDevice(sink))
       
   213                     m_backend->logMessage("AudioOutput using oss audio sink");
       
   214                 else if (sink) {
       
   215                     gst_object_unref(sink);
       
   216                     sink = 0;
       
   217                 }
       
   218             }
       
   219         } else if (m_audioSink == "fake") {
       
   220             //do nothing as a fakesink will be created by default
       
   221         } else if (m_audioSink == "artssink") {
       
   222             sink = GST_ELEMENT(g_object_new(arts_sink_get_type(), NULL));
       
   223         } else if (!m_audioSink.isEmpty()) { //Use a custom sink
       
   224             sink = gst_element_factory_make (m_audioSink, NULL);
       
   225             if (canOpenDevice(sink))
       
   226                 m_backend->logMessage(QString("AudioOutput using %0").arg(QString::fromUtf8(m_audioSink)));
       
   227             else if (sink) {
       
   228                 gst_object_unref(sink);
       
   229                 sink = 0;
       
   230             }
       
   231         }
       
   232     }
       
   233 
       
   234     if (!sink) { //no suitable sink found so we'll make a fake one
       
   235         sink = gst_element_factory_make("fakesink", NULL);
       
   236         if (sink) {
       
   237             m_backend->logMessage("AudioOutput Using fake audio sink");
       
   238             //without sync the sink will pull the pipeline as fast as the CPU allows
       
   239             g_object_set (G_OBJECT (sink), "sync", TRUE, (const char*)NULL);
       
   240         }
       
   241     }
       
   242     Q_ASSERT(sink);
       
   243     return sink;
       
   244 }
       
   245 
       
   246 AbstractRenderer *DeviceManager::createVideoRenderer(VideoWidget *parent)
       
   247 {
       
   248 #if !defined(QT_NO_OPENGL) && !defined(QT_OPENGL_ES)
       
   249     if (m_videoSinkWidget == "opengl") {
       
   250         return new GLRenderer(parent);
       
   251     } else
       
   252 #endif
       
   253     if (m_videoSinkWidget == "software") {
       
   254         return new WidgetRenderer(parent);
       
   255     }
       
   256 #ifndef Q_WS_QWS
       
   257     else if (m_videoSinkWidget == "xwindow") {
       
   258         return new X11Renderer(parent);
       
   259     } else {
       
   260         GstElementFactory *srcfactory = gst_element_factory_find("ximagesink");
       
   261         if (srcfactory) {
       
   262             return new X11Renderer(parent);
       
   263         }
       
   264     }
       
   265 #endif
       
   266     return new WidgetRenderer(parent);
       
   267 }
       
   268 
       
   269 /*
       
   270  * Returns a positive device id or -1 if device
       
   271  * does not exist
       
   272  *
       
   273  * The gstId is typically in the format hw:1,0
       
   274  */
       
   275 int DeviceManager::deviceId(const QByteArray &gstId) const
       
   276 {
       
   277     for (int i = 0 ; i < m_audioDeviceList.size() ; ++i) {
       
   278         if (m_audioDeviceList[i].gstId == gstId) {
       
   279             return m_audioDeviceList[i].id;
       
   280         }
       
   281     }
       
   282     return -1;
       
   283 }
       
   284 
       
   285 /**
       
   286  * Get a human-readable description from a device id
       
   287  */
       
   288 QByteArray DeviceManager::deviceDescription(int id) const
       
   289 {
       
   290     for (int i = 0 ; i < m_audioDeviceList.size() ; ++i) {
       
   291         if (m_audioDeviceList[i].id == id) {
       
   292             return m_audioDeviceList[i].description;
       
   293         }
       
   294     }
       
   295     return QByteArray();
       
   296 }
       
   297 
       
   298 /**
       
   299  * Updates the current list of active devices
       
   300  */
       
   301 void DeviceManager::updateDeviceList()
       
   302 {
       
   303     //fetch list of current devices
       
   304     GstElement *audioSink= createAudioSink();
       
   305 
       
   306     QList<QByteArray> list;
       
   307 
       
   308     if (audioSink) {
       
   309         list = GstHelper::extractProperties(audioSink, "device");
       
   310         list.prepend("default");
       
   311 
       
   312         for (int i = 0 ; i < list.size() ; ++i) {
       
   313             QByteArray gstId = list.at(i);
       
   314             if (deviceId(gstId) == -1) {
       
   315                 // This is a new device, add it
       
   316                 m_audioDeviceList.append(AudioDevice(this, gstId));
       
   317                 emit deviceAdded(deviceId(gstId));
       
   318                 m_backend->logMessage(QString("Found new audio device %0").arg(QString::fromUtf8(gstId)), Backend::Debug, this);
       
   319             }
       
   320         }
       
   321 
       
   322         if (list.size() < m_audioDeviceList.size()) {
       
   323             //a device was removed
       
   324             for (int i = m_audioDeviceList.size() -1 ; i >= 0 ; --i) {
       
   325                 QByteArray currId = m_audioDeviceList[i].gstId;
       
   326                 bool found = false;
       
   327                 for (int k = list.size() -1  ; k >= 0 ; --k) {
       
   328                     if (currId == list[k]) {
       
   329                         found = true;
       
   330                         break;
       
   331                     }
       
   332                 }
       
   333                 if (!found) {
       
   334                     m_backend->logMessage(QString("Audio device lost %0").arg(QString::fromUtf8(currId)), Backend::Debug, this);
       
   335                     emit deviceRemoved(deviceId(currId));
       
   336                     m_audioDeviceList.removeAt(i);
       
   337                 }
       
   338             }
       
   339         }
       
   340     }
       
   341 
       
   342     gst_element_set_state (audioSink, GST_STATE_NULL);
       
   343     gst_object_unref (audioSink);
       
   344 }
       
   345 
       
   346 /**
       
   347   * Returns a list of hardware id usable by gstreamer [i.e hw:1,0]
       
   348   */
       
   349 const QList<AudioDevice> DeviceManager::audioOutputDevices() const
       
   350 {
       
   351     return m_audioDeviceList;
       
   352 }
       
   353 
       
   354 }
       
   355 }
       
   356 
       
   357 QT_END_NAMESPACE