1 /*
2 Copyright (C) 2010 Collabora Ltd. <info@collabora.co.uk>
3 @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk>
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
7 by the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program 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 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 program. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "device-element-factory.h"
19 #include "phonon-integration.h"
20 #include "../libktpcall_debug.h"
21 #include <QGst/Bin>
22 #include <QGst/ElementFactory>
23 #include <QGst/Structure>
24
25 #include <KSharedConfig>
26 #include <KConfigGroup>
27
28 namespace KTpCallPrivate {
29
makeAudioCaptureElement()30 QGst::ElementPtr DeviceElementFactory::makeAudioCaptureElement()
31 {
32 QGst::ElementPtr element;
33
34 //allow overrides from the application's configuration file
35 element = tryOverrideForKey("audiosrc");
36 if (element) {
37 return element;
38 }
39
40 //use gconf on non-kde environments
41 if (qgetenv("KDE_FULL_SESSION").isEmpty()) {
42 element = tryElement("gconfaudiosrc");
43 return element;
44 }
45
46 //for kde environments try to do what phonon does:
47 //first try pulseaudio,
48 element = tryElement("pulsesrc");
49 if (element) {
50 addStreamProperties(element);
51 return element;
52 }
53
54 //then try alsa/oss devices reported by phononserver
55 //in the order that they are configured in phonon
56 QList<Phonon::DeviceAccessList> phononDeviceLists
57 = PhononIntegration::readDevices(Phonon::AudioCaptureDeviceType, Phonon::CommunicationCategory);
58
59 Q_FOREACH (const Phonon::DeviceAccessList & deviceList, phononDeviceLists) {
60 Q_FOREACH (const Phonon::DeviceAccess & device, deviceList) {
61 if (device.first == "alsa") {
62 //skip x-phonon devices, we will use plughw which is always second in the list
63 if (!device.second.startsWith("x-phonon")) {
64 element = tryElement("alsasrc", device.second);
65 }
66 } else if (device.first == "oss") {
67 element = tryElement("osssrc", device.second);
68 }
69
70 if (element) {
71 return element;
72 }
73 }
74 }
75
76 //as a last resort, try gstreamer's autodetection
77 element = tryElement("autoaudiosrc");
78 return element;
79 }
80
makeAudioOutputElement()81 QGst::ElementPtr DeviceElementFactory::makeAudioOutputElement()
82 {
83 QGst::ElementPtr element;
84
85 //allow overrides from the application's configuration file
86 element = tryOverrideForKey("audiosink");
87 if (element) {
88 return element;
89 }
90
91 //use gconf on non-kde environments
92 if (qgetenv("KDE_FULL_SESSION").isEmpty()) {
93 element = tryElement("gconfaudiosink");
94 if (element) {
95 element->setProperty("profile", 2 /*chat*/);
96 }
97 return element;
98 }
99
100 //for kde environments try to do what phonon does:
101 //first try pulseaudio,
102 element = tryElement("pulsesink");
103 if (element) {
104 addStreamProperties(element);
105 return element;
106 }
107
108 //then try alsa/oss devices reported by phononserver
109 //in the order that they are configured in phonon
110 QList<Phonon::DeviceAccessList> phononDeviceLists
111 = PhononIntegration::readDevices(Phonon::AudioOutputDeviceType, Phonon::CommunicationCategory);
112
113 Q_FOREACH (const Phonon::DeviceAccessList & deviceList, phononDeviceLists) {
114 Q_FOREACH (const Phonon::DeviceAccess & device, deviceList) {
115 if (device.first == "alsa") {
116 //use dmix instead of x-phonon, since we don't have phonon's alsa configuration file
117 QString deviceString = device.second;
118 deviceString.replace("x-phonon", "dmix");
119 element = tryElement("alsasink", deviceString);
120 } else if (device.first == "oss") {
121 element = tryElement("osssink", device.second);
122 }
123
124 if (element) {
125 return element;
126 }
127 }
128 }
129
130 //as a last resort, try gstreamer's autodetection
131 element = tryElement("autoaudiosink");
132 return element;
133 }
134
makeVideoCaptureElement()135 QGst::ElementPtr DeviceElementFactory::makeVideoCaptureElement()
136 {
137 QGst::ElementPtr element;
138
139 //allow overrides from the application's configuration file
140 element = tryOverrideForKey("videosrc");
141 if (element) {
142 return element;
143 }
144
145 //use gconf on non-kde environments
146 if (qgetenv("KDE_FULL_SESSION").isEmpty()) {
147 element = tryElement("gconfvideosrc");
148 return element;
149 }
150
151
152 //Phonon integration
153 QList<Phonon::DeviceAccessList> phononDeviceLists
154 = PhononIntegration::readDevices(Phonon::VideoCaptureDeviceType, Phonon::CommunicationCategory);
155
156 Q_FOREACH (const Phonon::DeviceAccessList & deviceList, phononDeviceLists) {
157 Q_FOREACH (const Phonon::DeviceAccess & device, deviceList) {
158 if(device.first == "v4l2") {
159 element = tryElement("v4l2src", device.second);
160 } else if (device.first == "v4l1") {
161 element = tryElement("v4lsrc", device.second);
162 }
163 }
164
165 if (element) {
166 return element;
167 }
168 }
169
170 //as a last resort, try gstreamer's autodetection
171 element = tryElement("autovideosrc");
172 return element;
173 }
174
tryElement(const char * name,const QString & device)175 QGst::ElementPtr DeviceElementFactory::tryElement(const char *name, const QString & device)
176 {
177 QGst::ElementPtr element = QGst::ElementFactory::make(name);
178 if (!element) {
179 qCDebug(LIBKTPCALL) << "Could not make element" << name;
180 return element;
181 }
182
183 if (!device.isEmpty()) {
184 try {
185 element->setProperty("device", device);
186 } catch (const std::logic_error & error) {
187 qCDebug(LIBKTPCALL) << "FIXME: Element" << name << "doesn't support string device property";
188 }
189 }
190
191 if (!element->setState(QGst::StateReady)) {
192 qCDebug(LIBKTPCALL) << "Element" << name << "with device string" << device << "doesn't want to become ready";
193 return QGst::ElementPtr();
194 }
195
196 qCDebug(LIBKTPCALL) << "Using element" << name << "with device string" << device;
197 return element;
198 }
199
tryOverrideForKey(const char * keyName)200 QGst::ElementPtr DeviceElementFactory::tryOverrideForKey(const char *keyName)
201 {
202 QGst::ElementPtr element;
203 const KConfigGroup configGroup = KSharedConfig::openConfig()->group("GStreamer");
204
205 if (configGroup.hasKey(keyName)) {
206 QString binDescription = configGroup.readEntry(keyName);
207 element = QGst::Bin::fromDescription(binDescription);
208
209 if (!element) {
210 qCDebug(LIBKTPCALL) << "Could not construct bin" << binDescription;
211 return element;
212 }
213
214 if (!element->setState(QGst::StateReady)) {
215 qCDebug(LIBKTPCALL) << "Custom bin" << binDescription << "doesn't want to become ready";
216 return QGst::ElementPtr();
217 }
218
219 qCDebug(LIBKTPCALL) << "Using custom bin" << binDescription;
220 }
221
222 return element;
223 }
224
addStreamProperties(QGst::ElementPtr element)225 void DeviceElementFactory::addStreamProperties (QGst::ElementPtr element)
226 {
227 // Echo cancellation magic
228 QGst::Structure streamProperties("stream-properties");
229 streamProperties.setValue("media.role", "phone");
230 streamProperties.setValue("filter.want", "echo-cancel");
231 element->setProperty("stream-properties", streamProperties);
232 }
233
234 } // KTpCallPrivate
235