source: trunk/src/3rdparty/phonon/gstreamer/audiooutput.cpp@ 728

Last change on this file since 728 was 561, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 9.1 KB
Line 
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 <[email protected]>
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
26QT_BEGIN_NAMESPACE
27
28namespace Phonon
29{
30namespace Gstreamer
31{
32AudioOutput::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 m_audioBin = gst_bin_new (NULL);
46 gst_object_ref (GST_OBJECT (m_audioBin));
47 gst_object_sink (GST_OBJECT (m_audioBin));
48
49 m_conv = gst_element_factory_make ("audioconvert", NULL);
50
51 // Get category from parent
52 Phonon::Category category = Phonon::NoCategory;
53 if (Phonon::AudioOutput *audioOutput = qobject_cast<Phonon::AudioOutput *>(parent))
54 category = audioOutput->category();
55
56 m_audioSink = m_backend->deviceManager()->createAudioSink(category);
57 m_volumeElement = gst_element_factory_make ("volume", NULL);
58 GstElement *queue = gst_element_factory_make ("queue", NULL);
59 GstElement *audioresample = gst_element_factory_make ("audioresample", NULL);
60
61 if (queue && m_audioBin && m_conv && audioresample && m_audioSink && m_volumeElement) {
62 gst_bin_add_many (GST_BIN (m_audioBin), queue, m_conv, audioresample, m_volumeElement, m_audioSink, (const char*)NULL);
63
64 if (gst_element_link_many (queue, m_conv, audioresample, m_volumeElement, m_audioSink, (const char*)NULL)) {
65 // Add ghost sink for audiobin
66 GstPad *audiopad = gst_element_get_pad (queue, "sink");
67 gst_element_add_pad (m_audioBin, gst_ghost_pad_new ("sink", audiopad));
68 gst_object_unref (audiopad);
69 m_isValid = true; // Initialization ok, accept input
70 }
71 }
72 }
73}
74
75void AudioOutput::mediaNodeEvent(const MediaNodeEvent *event)
76{
77 if (!m_audioBin)
78 return;
79
80 switch (event->type()) {
81
82 default:
83 break;
84 }
85}
86
87
88AudioOutput::~AudioOutput()
89{
90 if (m_audioBin) {
91 gst_element_set_state (m_audioBin, GST_STATE_NULL);
92 gst_object_unref (m_audioBin);
93 }
94}
95
96qreal AudioOutput::volume() const
97{
98 return m_volumeLevel;
99}
100
101int AudioOutput::outputDevice() const
102{
103 return m_device;
104}
105
106void AudioOutput::setVolume(qreal newVolume)
107{
108 if (newVolume > 2.0 )
109 newVolume = 2.0;
110 else if (newVolume < 0.0)
111 newVolume = 0.0;
112
113 if (newVolume == m_volumeLevel)
114 return;
115
116 m_volumeLevel = newVolume;
117
118 if (m_volumeElement) {
119 g_object_set(G_OBJECT(m_volumeElement), "volume", newVolume, (const char*)NULL);
120 }
121
122 emit volumeChanged(newVolume);
123}
124
125bool AudioOutput::setOutputDevice(int newDevice)
126{
127 m_backend->logMessage(Q_FUNC_INFO + QString::number(newDevice), Backend::Info, this);
128 if (newDevice == m_device)
129 return true;
130
131 if (root()) {
132 root()->saveState();
133 if (gst_element_set_state(root()->pipeline(), GST_STATE_READY) == GST_STATE_CHANGE_FAILURE)
134 return false;
135 }
136
137 bool success = false;
138 const QList<AudioDevice> deviceList = m_backend->deviceManager()->audioOutputDevices();
139 int deviceIdx = -1;
140 for (int i=0; i<deviceList.size(); i++) {
141 if (deviceList.at(i).id == newDevice) {
142 deviceIdx = i;
143 break;
144 }
145 }
146
147 if (m_audioSink && deviceIdx >= 0) {
148 // Save previous state
149 GstState oldState = GST_STATE(m_audioSink);
150 const QByteArray oldDeviceValue = GstHelper::property(m_audioSink, "device");
151 const QByteArray deviceId = deviceList.at(deviceIdx).gstId;
152 m_device = newDevice;
153
154 // We test if the device can be opened by checking if it can go from NULL to READY state
155 gst_element_set_state(m_audioSink, GST_STATE_NULL);
156 success = GstHelper::setProperty(m_audioSink, "device", deviceId);
157 if (success) {
158 success = (gst_element_set_state(m_audioSink, oldState) == GST_STATE_CHANGE_SUCCESS);
159 }
160 if (!success) { // Revert state
161 m_backend->logMessage(Q_FUNC_INFO +
162 QLatin1String(" Failed to change device ") +
163 deviceId, Backend::Info, this);
164
165 GstHelper::setProperty(m_audioSink, "device", oldDeviceValue);
166 gst_element_set_state(m_audioSink, oldState);
167 } else {
168 m_backend->logMessage(Q_FUNC_INFO +
169 QLatin1String(" Successfully changed device ") +
170 deviceId, Backend::Info, this);
171 }
172
173 // Note the stopped state should not really be neccessary, but seems to be required to
174 // properly reset after changing the audio state
175 if (root()) {
176 QMetaObject::invokeMethod(root(), "setState", Qt::QueuedConnection, Q_ARG(State, StoppedState));
177 root()->resumeState();
178 }
179 }
180 return success;
181}
182
183#if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 2, 0))
184bool AudioOutput::setOutputDevice(const AudioOutputDevice &newDevice)
185{
186 m_backend->logMessage(Q_FUNC_INFO, Backend::Info, this);
187 if (!m_audioSink || !newDevice.isValid()) {
188 return false;
189 }
190 const QVariant driver = newDevice.property("driver");
191 if (!driver.isValid()) {
192 return setOutputDevice(newDevice.index());
193 }
194 if (newDevice.index() == m_device) {
195 return true;
196 }
197
198 if (root()) {
199 root()->saveState();
200 if (gst_element_set_state(root()->pipeline(), GST_STATE_READY) == GST_STATE_CHANGE_FAILURE)
201 return false;
202 }
203
204 // Save previous state
205 const GstState oldState = GST_STATE(m_audioSink);
206 const QByteArray oldDeviceValue = GstHelper::property(m_audioSink, "device");
207
208 const QByteArray sinkName = GstHelper::property(m_audioSink, "name");
209 if (sinkName == "alsasink" || sinkName == "alsasink2") {
210 if (driver.toByteArray() != "alsa") {
211 return false;
212 }
213 }
214
215 const QVariant deviceIdsProperty = newDevice.property("deviceIds");
216 QStringList deviceIds;
217 if (deviceIdsProperty.type() == QVariant::StringList) {
218 deviceIds = deviceIdsProperty.toStringList();
219 } else if (deviceIdsProperty.type() == QVariant::String) {
220 deviceIds += deviceIdsProperty.toString();
221 }
222
223 // We test if the device can be opened by checking if it can go from NULL to READY state
224 foreach (const QString &deviceId, deviceIds) {
225 gst_element_set_state(m_audioSink, GST_STATE_NULL);
226 if (GstHelper::setProperty(m_audioSink, "device", deviceId.toUtf8())) {
227 m_backend->logMessage(Q_FUNC_INFO + QLatin1String("setProperty(device,") +
228 deviceId + QLatin1String(") succeeded"), Backend::Info, this);
229 if (gst_element_set_state(m_audioSink, oldState) == GST_STATE_CHANGE_SUCCESS) {
230 m_backend->logMessage(Q_FUNC_INFO + QLatin1String("go to old state on device") +
231 deviceId + QLatin1String(" succeeded"), Backend::Info, this);
232 m_device = newDevice.index();
233 if (root()) {
234 QMetaObject::invokeMethod(root(), "setState", Qt::QueuedConnection, Q_ARG(State, StoppedState));
235 root()->resumeState();
236 }
237 return true;
238 } else {
239 m_backend->logMessage(Q_FUNC_INFO + QLatin1String("go to old state on device") +
240 deviceId + QLatin1String(" failed"), Backend::Info, this);