src/3rdparty/phonon/gstreamer/audiooutput.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     Copyright (C) 2008 Matthias Kretz <kretz@kde.org>
       
     5 
       
     6     This library is free software: you can redistribute it and/or modify
       
     7     it under the terms of the GNU Lesser General Public License as published by
       
     8     the Free Software Foundation, either version 2.1 or 3 of the License.
       
     9 
       
    10     This library is distributed in the hope that it will be useful,
       
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    13     GNU Lesser General Public License for more details.
       
    14 
       
    15     You should have received a copy of the GNU Lesser General Public License
       
    16     along with this library.  If not, see <http://www.gnu.org/licenses/>.
       
    17 */
       
    18 
       
    19 #include "common.h"
       
    20 #include "audiooutput.h"
       
    21 #include "backend.h"
       
    22 #include "mediaobject.h"
       
    23 #include "gsthelper.h"
       
    24 #include <phonon/audiooutput.h>
       
    25 
       
    26 QT_BEGIN_NAMESPACE
       
    27 
       
    28 namespace Phonon
       
    29 {
       
    30 namespace Gstreamer
       
    31 {
       
    32 AudioOutput::AudioOutput(Backend *backend, QObject *parent)
       
    33         : QObject(parent)
       
    34         , MediaNode(backend, AudioSink)
       
    35         , m_volumeLevel(1.0)
       
    36         , m_device(0) // ### get from backend
       
    37         , m_volumeElement(0)
       
    38         , m_audioBin(0)
       
    39         , m_audioSink(0)
       
    40         , m_conv(0)
       
    41 {
       
    42     static int count = 0;
       
    43     m_name = "AudioOutput" + QString::number(count++);
       
    44     if (m_backend->isValid()) {
       
    45         g_set_application_name(qApp->applicationName().toUtf8());
       
    46         m_audioBin = gst_bin_new (NULL);
       
    47         gst_object_ref (GST_OBJECT (m_audioBin));
       
    48         gst_object_sink (GST_OBJECT (m_audioBin));     
       
    49 
       
    50         m_conv = gst_element_factory_make ("audioconvert", NULL);
       
    51 
       
    52         // Get category from parent
       
    53         Phonon::Category category = Phonon::NoCategory;
       
    54         if (Phonon::AudioOutput *audioOutput = qobject_cast<Phonon::AudioOutput *>(parent))
       
    55             category = audioOutput->category();
       
    56     
       
    57         m_audioSink = m_backend->deviceManager()->createAudioSink(category);
       
    58         m_volumeElement = gst_element_factory_make ("volume", NULL);
       
    59         GstElement *queue = gst_element_factory_make ("queue", NULL);
       
    60         GstElement *audioresample = gst_element_factory_make ("audioresample", NULL);
       
    61     
       
    62         if (queue && m_audioBin && m_conv && audioresample && m_audioSink && m_volumeElement) {
       
    63             gst_bin_add_many (GST_BIN (m_audioBin), queue, m_conv, audioresample, m_volumeElement, m_audioSink, (const char*)NULL);
       
    64     
       
    65             if (gst_element_link_many (queue, m_conv, audioresample, m_volumeElement, m_audioSink, (const char*)NULL)) {
       
    66                 // Add ghost sink for audiobin
       
    67                 GstPad *audiopad = gst_element_get_pad (queue, "sink");
       
    68                 gst_element_add_pad (m_audioBin, gst_ghost_pad_new ("sink", audiopad));
       
    69                 gst_object_unref (audiopad);
       
    70                 m_isValid = true; // Initialization ok, accept input
       
    71             }
       
    72         }
       
    73     }
       
    74 }
       
    75 
       
    76 void AudioOutput::mediaNodeEvent(const MediaNodeEvent *event)
       
    77 {
       
    78     if (!m_audioBin)
       
    79         return;
       
    80 
       
    81     switch (event->type()) {
       
    82 
       
    83     default:
       
    84         break;
       
    85     }
       
    86 }
       
    87 
       
    88 
       
    89 AudioOutput::~AudioOutput()
       
    90 {
       
    91     if (m_audioBin) {
       
    92         gst_element_set_state (m_audioBin, GST_STATE_NULL);
       
    93         gst_object_unref (m_audioBin);
       
    94     }
       
    95 }
       
    96 
       
    97 qreal AudioOutput::volume() const
       
    98 {
       
    99     return m_volumeLevel;
       
   100 }
       
   101 
       
   102 int AudioOutput::outputDevice() const
       
   103 {
       
   104     return m_device;
       
   105 }
       
   106 
       
   107 void AudioOutput::setVolume(qreal newVolume)
       
   108 {
       
   109     if (newVolume > 2.0 )
       
   110         newVolume = 2.0;
       
   111     else if (newVolume < 0.0)
       
   112         newVolume = 0.0;
       
   113 
       
   114     if (newVolume == m_volumeLevel)
       
   115         return;
       
   116 
       
   117     m_volumeLevel = newVolume;
       
   118 
       
   119     if (m_volumeElement) {
       
   120         g_object_set(G_OBJECT(m_volumeElement), "volume", newVolume, (const char*)NULL);
       
   121     }
       
   122 
       
   123     emit volumeChanged(newVolume);
       
   124 }
       
   125 
       
   126 bool AudioOutput::setOutputDevice(int newDevice)
       
   127 {
       
   128     m_backend->logMessage(Q_FUNC_INFO + QString::number(newDevice), Backend::Info, this);
       
   129     if (newDevice == m_device)
       
   130         return true;
       
   131 
       
   132     if (root()) {
       
   133         root()->saveState();
       
   134         if (gst_element_set_state(root()->pipeline(), GST_STATE_READY) == GST_STATE_CHANGE_FAILURE)
       
   135             return false;
       
   136     }
       
   137 
       
   138     bool success = false;
       
   139     const QList<AudioDevice> deviceList = m_backend->deviceManager()->audioOutputDevices();
       
   140     if (m_audioSink &&  newDevice >= 0 && newDevice < deviceList.size()) {
       
   141         // Save previous state
       
   142         GstState oldState = GST_STATE(m_audioSink);
       
   143         const QByteArray oldDeviceValue = GstHelper::property(m_audioSink, "device");
       
   144         const QByteArray deviceId = deviceList.at(newDevice).gstId;
       
   145         m_device = newDevice;
       
   146 
       
   147         // We test if the device can be opened by checking if it can go from NULL to READY state
       
   148         gst_element_set_state(m_audioSink, GST_STATE_NULL);
       
   149         success = GstHelper::setProperty(m_audioSink, "device", deviceId);
       
   150         if (success) {
       
   151             success = (gst_element_set_state(m_audioSink, oldState) == GST_STATE_CHANGE_SUCCESS);
       
   152         }
       
   153         if (!success) { // Revert state
       
   154             m_backend->logMessage(Q_FUNC_INFO +
       
   155                                   QLatin1String(" Failed to change device ") +
       
   156                                   deviceId, Backend::Info, this);
       
   157 
       
   158             GstHelper::setProperty(m_audioSink, "device", oldDeviceValue);
       
   159             gst_element_set_state(m_audioSink, oldState);
       
   160         } else {
       
   161             m_backend->logMessage(Q_FUNC_INFO +
       
   162                                   QLatin1String(" Successfully changed device ") +
       
   163                                   deviceId, Backend::Info, this);
       
   164         }
       
   165 
       
   166         // Note the stopped state should not really be neccessary, but seems to be required to 
       
   167         // properly reset after changing the audio state
       
   168         if (root()) {
       
   169             QMetaObject::invokeMethod(root(), "setState", Qt::QueuedConnection, Q_ARG(State, StoppedState));
       
   170             root()->resumeState();
       
   171         }
       
   172     }
       
   173     return success;
       
   174 }
       
   175 
       
   176 #if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 2, 0))
       
   177 bool AudioOutput::setOutputDevice(const AudioOutputDevice &newDevice)
       
   178 {
       
   179     m_backend->logMessage(Q_FUNC_INFO, Backend::Info, this);
       
   180     if (!m_audioSink || !newDevice.isValid()) {
       
   181         return false;
       
   182     }
       
   183     const QVariant driver = newDevice.property("driver");
       
   184     if (!driver.isValid()) {
       
   185         return setOutputDevice(newDevice.index());
       
   186     }
       
   187     if (newDevice.index() == m_device) {
       
   188         return true;
       
   189     }
       
   190 
       
   191     if (root()) {
       
   192         root()->saveState();
       
   193         if (gst_element_set_state(root()->pipeline(), GST_STATE_READY) == GST_STATE_CHANGE_FAILURE)
       
   194             return false;
       
   195     }
       
   196 
       
   197     // Save previous state
       
   198     const GstState oldState = GST_STATE(m_audioSink);
       
   199     const QByteArray oldDeviceValue = GstHelper::property(m_audioSink, "device");
       
   200 
       
   201     const QByteArray sinkName = GstHelper::property(m_audioSink, "name");
       
   202     if (sinkName == "alsasink" || sinkName == "alsasink2") {
       
   203         if (driver.toByteArray() != "alsa") {
       
   204             return false;
       
   205         }
       
   206     }
       
   207 
       
   208     const QVariant deviceIdsProperty = newDevice.property("deviceIds");
       
   209     QStringList deviceIds;
       
   210     if (deviceIdsProperty.type() == QVariant::StringList) {
       
   211         deviceIds = deviceIdsProperty.toStringList();
       
   212     } else if (deviceIdsProperty.type() == QVariant::String) {
       
   213         deviceIds += deviceIdsProperty.toString();
       
   214     }
       
   215 
       
   216     // We test if the device can be opened by checking if it can go from NULL to READY state
       
   217     foreach (const QString &deviceId, deviceIds) {
       
   218         gst_element_set_state(m_audioSink, GST_STATE_NULL);
       
   219         if (GstHelper::setProperty(m_audioSink, "device", deviceId.toUtf8())) {
       
   220             m_backend->logMessage(Q_FUNC_INFO + QLatin1String("setProperty(device,") +
       
   221                                   deviceId + QLatin1String(") succeeded"), Backend::Info, this);
       
   222             if (gst_element_set_state(m_audioSink, oldState) == GST_STATE_CHANGE_SUCCESS) {
       
   223                 m_backend->logMessage(Q_FUNC_INFO + QLatin1String("go to old state on device") +
       
   224                                       deviceId + QLatin1String(" succeeded"), Backend::Info, this);
       
   225                 m_device = newDevice.index();
       
   226                 if (root()) {
       
   227                     QMetaObject::invokeMethod(root(), "setState", Qt::QueuedConnection, Q_ARG(State, StoppedState));
       
   228                     root()->resumeState();
       
   229                 }
       
   230                 return true;
       
   231             } else {
       
   232                 m_backend->logMessage(Q_FUNC_INFO + QLatin1String("go to old state on device") +
       
   233                                       deviceId + QLatin1String(" failed"), Backend::Info, this);
       
   234             }
       
   235         } else {
       
   236             m_backend->logMessage(Q_FUNC_INFO + QLatin1String("setProperty(device,") +
       
   237                                   deviceId + QLatin1String(") failed"), Backend::Info, this);
       
   238         }
       
   239     }
       
   240     // Revert state
       
   241     GstHelper::setProperty(m_audioSink, "device", oldDeviceValue);
       
   242     gst_element_set_state(m_audioSink, oldState);
       
   243 
       
   244     if (root()) {
       
   245         QMetaObject::invokeMethod(root(), "setState", Qt::QueuedConnection, Q_ARG(State, StoppedState));
       
   246         root()->resumeState();
       
   247     }
       
   248 
       
   249     return false;
       
   250 }
       
   251 #endif
       
   252 
       
   253 }
       
   254 } //namespace Phonon::Gstreamer
       
   255 
       
   256 QT_END_NAMESPACE
       
   257 #include "moc_audiooutput.cpp"