1
/* This file is part of the KDE project.
3
Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
Copyright (C) 2008 Matthias Kretz <kretz@kde.org>
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.
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.
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/>.
20
#include "audiooutput.h"
22
#include "devicemanager.h"
23
#include "mediaobject.h"
24
#include "medianodeevent.h"
25
#include "gsthelper.h"
26
#include <phonon/audiooutput.h>
28
#include <gst/gstbin.h>
29
#include <gst/gstghostpad.h>
30
#include <gst/gstutils.h>
38
AudioOutput::AudioOutput(Backend *backend, QObject *parent)
40
, MediaNode(backend, AudioSink)
42
, m_device(0) // ### get from backend
49
m_name = "AudioOutput" + QString::number(count++);
50
if (m_backend->isValid()) {
51
m_audioBin = gst_bin_new (NULL);
52
gst_object_ref (GST_OBJECT (m_audioBin));
53
gst_object_sink (GST_OBJECT (m_audioBin));
55
m_conv = gst_element_factory_make ("audioconvert", NULL);
57
// Get category from parent
58
Phonon::Category category = Phonon::NoCategory;
59
if (Phonon::AudioOutput *audioOutput = qobject_cast<Phonon::AudioOutput *>(parent))
60
category = audioOutput->category();
62
m_audioSink = m_backend->deviceManager()->createAudioSink(category);
63
m_volumeElement = gst_element_factory_make ("volume", NULL);
64
GstElement *queue = gst_element_factory_make ("queue", NULL);
65
GstElement *audioresample = gst_element_factory_make ("audioresample", NULL);
67
if (queue && m_audioBin && m_conv && audioresample && m_audioSink && m_volumeElement) {
68
gst_bin_add_many (GST_BIN (m_audioBin), queue, m_conv, audioresample, m_volumeElement, m_audioSink, (const char*)NULL);
70
if (gst_element_link_many (queue, m_conv, audioresample, m_volumeElement, m_audioSink, (const char*)NULL)) {
71
// Add ghost sink for audiobin
72
GstPad *audiopad = gst_element_get_pad (queue, "sink");
73
gst_element_add_pad (m_audioBin, gst_ghost_pad_new ("sink", audiopad));
74
gst_object_unref (audiopad);
75
m_isValid = true; // Initialization ok, accept input
81
void AudioOutput::mediaNodeEvent(const MediaNodeEvent *event)
86
switch (event->type()) {
94
AudioOutput::~AudioOutput()
97
gst_element_set_state (m_audioBin, GST_STATE_NULL);
98
gst_object_unref (m_audioBin);
102
qreal AudioOutput::volume() const
104
return m_volumeLevel;
107
int AudioOutput::outputDevice() const
112
void AudioOutput::setVolume(qreal newVolume)
114
if (newVolume > 2.0 )
116
else if (newVolume < 0.0)
119
if (newVolume == m_volumeLevel)
122
m_volumeLevel = newVolume;
124
if (m_volumeElement) {
125
g_object_set(G_OBJECT(m_volumeElement), "volume", newVolume, (const char*)NULL);
128
emit volumeChanged(newVolume);
131
bool AudioOutput::setOutputDevice(int newDevice)
133
m_backend->logMessage(Q_FUNC_INFO + QString::number(newDevice), Backend::Info, this);
135
if (newDevice == m_device)
140
if (gst_element_set_state(root()->pipeline(), GST_STATE_READY) == GST_STATE_CHANGE_FAILURE)
144
bool success = false;
145
if (m_audioSink && newDevice >= 0) {
146
// Save previous state
147
GstState oldState = GST_STATE(m_audioSink);
148
const QByteArray oldDeviceValue = GstHelper::property(m_audioSink, "device");
149
const QByteArray deviceId = m_backend->deviceManager()->gstId(newDevice);
150
m_device = newDevice;
152
// We test if the device can be opened by checking if it can go from NULL to READY state
153
gst_element_set_state(m_audioSink, GST_STATE_NULL);
154
success = GstHelper::setProperty(m_audioSink, "device", deviceId);
156
success = (gst_element_set_state(m_audioSink, oldState) == GST_STATE_CHANGE_SUCCESS);
158
if (!success) { // Revert state
159
m_backend->logMessage(Q_FUNC_INFO +
160
QLatin1String(" Failed to change device ") +
161
deviceId, Backend::Info, this);
163
GstHelper::setProperty(m_audioSink, "device", oldDeviceValue);
164
gst_element_set_state(m_audioSink, oldState);
166
m_backend->logMessage(Q_FUNC_INFO +
167
QLatin1String(" Successfully changed device ") +
168
deviceId, Backend::Info, this);
171
// Note the stopped state should not really be necessary, but seems to be required to
172
// properly reset after changing the audio state
174
QMetaObject::invokeMethod(root(), "setState", Qt::QueuedConnection, Q_ARG(State, StoppedState));
175
root()->resumeState();
181
#if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 2, 0))
182
bool AudioOutput::setOutputDevice(const AudioOutputDevice &newDevice)
184
m_backend->logMessage(Q_FUNC_INFO, Backend::Info, this);
185
if (!m_audioSink || !newDevice.isValid()) {
188
const QVariant driver = newDevice.property("driver");
189
if (!driver.isValid()) {
190
return setOutputDevice(newDevice.index());
192
if (newDevice.index() == m_device) {
198
if (gst_element_set_state(root()->pipeline(), GST_STATE_READY) == GST_STATE_CHANGE_FAILURE)
202
// Save previous state
203
const GstState oldState = GST_STATE(m_audioSink);
204
const QByteArray oldDeviceValue = GstHelper::property(m_audioSink, "device");
206
const QByteArray sinkName = GstHelper::property(m_audioSink, "name");
207
if (sinkName == "alsasink" || sinkName == "alsasink2") {
208
if (driver.toByteArray() != "alsa") {
213
const QVariant deviceIdsProperty = newDevice.property("deviceIds");
214
QStringList deviceIds;
215
if (deviceIdsProperty.type() == QVariant::StringList) {
216
deviceIds = deviceIdsProperty.toStringList();
217
} else if (deviceIdsProperty.type() == QVariant::String) {
218
deviceIds += deviceIdsProperty.toString();
221
// We test if the device can be opened by checking if it can go from NULL to READY state
222
foreach (const QString &deviceId, deviceIds) {
223
gst_element_set_state(m_audioSink, GST_STATE_NULL);
224
if (GstHelper::setProperty(m_audioSink, "device", deviceId.toUtf8())) {
225
m_backend->logMessage(Q_FUNC_INFO + QLatin1String("setProperty(device,") +
226
deviceId + QLatin1String(") succeeded"), Backend::Info, this);
227
if (gst_element_set_state(m_audioSink, oldState) == GST_STATE_CHANGE_SUCCESS) {
228
m_backend->logMessage(Q_FUNC_INFO + QLatin1String("go to old state on device") +
229
deviceId + QLatin1String(" succeeded"), Backend::Info, this);
230
m_device = newDevice.index();
232
QMetaObject::invokeMethod(root(), "setState", Qt::QueuedConnection, Q_ARG(State, StoppedState));
233
root()->resumeState();
237
m_backend->logMessage(Q_FUNC_INFO + QLatin1String("go to old state on device") +
238
deviceId + QLatin1String(" failed"), Backend::Info, this);
241
m_backend->logMessage(Q_FUNC_INFO + QLatin1String("setProperty(device,") +
242
deviceId + QLatin1String(") failed"), Backend::Info, this);
246
GstHelper::setProperty(m_audioSink, "device", oldDeviceValue);
247
gst_element_set_state(m_audioSink, oldState);
250
QMetaObject::invokeMethod(root(), "setState", Qt::QueuedConnection, Q_ARG(State, StoppedState));
251
root()->resumeState();
259
} //namespace Phonon::Gstreamer
262
#include "moc_audiooutput.cpp"