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 {
AudioOutput(Backend * backend,QObject * parent)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 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
mediaNodeEvent(const MediaNodeEvent * event)75 void 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
~AudioOutput()88 AudioOutput::~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
volume() const96 qreal AudioOutput::volume() const
97 {
98 return m_volumeLevel;
99 }
100
outputDevice() const101 int AudioOutput::outputDevice() const
102 {
103 return m_device;
104 }
105
setVolume(qreal newVolume)106 void 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
setOutputDevice(int newDevice)125 bool AudioOutput::setOutputDevice(int newDevice)
126 {
127 m_backend->logMessage(Q_FUNC_INFO + QString::number(newDevice), Backend::Info, this);
128
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 if (m_audioSink && newDevice >= 0) {
140 // Save previous state
141 GstState oldState = GST_STATE(m_audioSink);
142 const QByteArray oldDeviceValue = GstHelper::property(m_audioSink, "device");
143 const QByteArray deviceId = m_backend->deviceManager()->gstId(newDevice);
144 m_device = newDevice;
145
146 // We test if the device can be opened by checking if it can go from NULL to READY state
147 gst_element_set_state(m_audioSink, GST_STATE_NULL);
148 success = GstHelper::setProperty(m_audioSink, "device", deviceId);
149 if (success) {
150 success = (gst_element_set_state(m_audioSink, oldState) == GST_STATE_CHANGE_SUCCESS);
151 }
152 if (!success) { // Revert state
153 m_backend->logMessage(Q_FUNC_INFO +
154 QLatin1String(" Failed to change device ") +
155 deviceId, Backend::Info, this);
156
157 GstHelper::setProperty(m_audioSink, "device", oldDeviceValue);
158 gst_element_set_state(m_audioSink, oldState);
159 } else {
160 m_backend->logMessage(Q_FUNC_INFO +
161 QLatin1String(" Successfully changed device ") +
162 deviceId, Backend::Info, this);
163 }
164
165 // Note the stopped state should not really be necessary, but seems to be required to
166 // properly reset after changing the audio state
167 if (root()) {
168 QMetaObject::invokeMethod(root(), "setState", Qt::QueuedConnection, Q_ARG(State, StoppedState));
169 root()->resumeState();
170 }
171 }
172 return success;
173 }
174
175 #if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 2, 0))
setOutputDevice(const AudioOutputDevice & newDevice)176 bool AudioOutput::setOutputDevice(const AudioOutputDevice &newDevice)
177 {
178 m_backend->logMessage(Q_FUNC_INFO, Backend::Info, this);
179 if (!m_audioSink || !newDevice.isValid()) {
180 return false;
181 }
182 const QVariant driver = newDevice.property("driver");
183 if (!driver.isValid()) {
184 return setOutputDevice(newDevice.index());
185 }
186 if (newDevice.index() == m_device) {
187 return true;
188 }
189
190 if (root()) {
191 root()->saveState();
192 if (gst_element_set_state(root()->pipeline(), GST_STATE_READY) == GST_STATE_CHANGE_FAILURE)
193 return false;
194 }
195
196 // Save previous state
197 const GstState oldState = GST_STATE(m_audioSink);
198 const QByteArray oldDeviceValue = GstHelper::property(m_audioSink, "device");
199
200 const QByteArray sinkName = GstHelper::property(m_audioSink, "name");
201 if (sinkName == "alsasink" || sinkName == "alsasink2") {
202 if (driver.toByteArray() != "alsa") {
203 return false;
204 }
205 }
206
207 const QVariant deviceIdsProperty = newDevice.property("deviceIds");
208 QStringList deviceIds;
209 if (deviceIdsProperty.type() == QVariant::StringList) {
210 deviceIds = deviceIdsProperty.toStringList();
211 } else if (deviceIdsProperty.type() == QVariant::String) {
212 deviceIds += deviceIdsProperty.toString();
213 }
214
215 // We test if the device can be opened by checking if it can go from NULL to READY state
216 foreach (const QString &deviceId, deviceIds) {
217 gst_element_set_state(m_audioSink, GST_STATE_NULL);
218 if (GstHelper::setProperty(m_audioSink, "device", deviceId.toUtf8())) {
219 m_backend->logMessage(Q_FUNC_INFO + QLatin1String("setProperty(device,") +
220 deviceId + QLatin1String(") succeeded"), Backend::Info, this);
221 if (gst_element_set_state(m_audioSink, oldState) == GST_STATE_CHANGE_SUCCESS) {
222 m_backend->logMessage(Q_FUNC_INFO + QLatin1String("go to old state on device") +
223 deviceId + QLatin1String(" succeeded"), Backend::Info, this);
224 m_device = newDevice.index();
225 if (root()) {
226 QMetaObject::invokeMethod(root(), "setState", Qt::QueuedConnection, Q_ARG(State, StoppedState));
227 root()->resumeState();
228 }
229 return true;
230 } else {
231 m_backend->logMessage(Q_FUNC_INFO + QLatin1String("go to old state on device") +
232 deviceId + QLatin1String(" failed"), Backend::Info, this);
233 }
234 } else {
235 m_backend->logMessage(Q_FUNC_INFO + QLatin1String("setProperty(device,") +
236 deviceId + QLatin1String(") failed"), Backend::Info, this);
237 }
238 }
239 // Revert state
240 GstHelper::setProperty(m_audioSink, "device", oldDeviceValue);
241 gst_element_set_state(m_audioSink, oldState);
242
243 if (root()) {
244 QMetaObject::invokeMethod(root(), "setState", Qt::QueuedConnection, Q_ARG(State, StoppedState));
245 root()->resumeState();
246 }
247
248 return false;
249 }
250 #endif
251
252 }
253 } //namespace Phonon::Gstreamer
254
255 QT_END_NAMESPACE
256 #include "moc_audiooutput.cpp"
257