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