1/*
2 Copyright (C) 2007-2008 Tanguy Krotoff <tkrotoff@gmail.com>
3 Copyright (C) 2008 Lukas Durfina <lukas.durfina@gmail.com>
4 Copyright (C) 2009 Fathi Boudra <fabo@kde.org>
5 Copyright (C) 2009-2010 vlc-phonon AUTHORS <kde-multimedia@kde.org>
6 Copyright (C) 2011 Harald Sitter <sitter@kde.org>
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation; either
11 version 2.1 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "devicemanager.h"
23
24#include <phonon/pulsesupport.h>
25
26#include <vlc/vlc.h>
27
28#include "backend.h"
29#include "utils/debug.h"
30#include "utils/libvlc.h"
31#include "utils/vstring.h"
32
33namespace Phonon
34{
35namespace VLC
36{
37
38/*
39 * Device Info
40 */
41
42DeviceInfo::DeviceInfo(const QString &name, bool isAdvanced)
43{
44 // Get an id
45 static int counter = 0;
46 m_id = counter++;
47
48 // Get name and description for the device
49 m_name = name;
50 m_isAdvanced = isAdvanced;
51 m_capabilities = None;
52
53 // A default device should never be advanced
54 if (name.startsWith(s: QLatin1String("default"), cs: Qt::CaseInsensitive))
55 m_isAdvanced = false;
56}
57
58int DeviceInfo::id() const
59{
60 return m_id;
61}
62
63const QString& DeviceInfo::name() const
64{
65 return m_name;
66}
67
68const QString& DeviceInfo::description() const
69{
70 return m_description;
71}
72
73bool DeviceInfo::isAdvanced() const
74{
75 return m_isAdvanced;
76}
77
78void DeviceInfo::setAdvanced(bool advanced)
79{
80 m_isAdvanced = advanced;
81}
82
83const DeviceAccessList& DeviceInfo::accessList() const
84{
85 return m_accessList;
86}
87
88void DeviceInfo::addAccess(const DeviceAccess& access)
89{
90 if (m_accessList.isEmpty())
91 m_description = access.first + ": " + access.second;
92 m_accessList.append(t: access);
93}
94
95quint16 DeviceInfo::capabilities() const
96{
97 return m_capabilities;
98}
99
100void DeviceInfo::setCapabilities(quint16 cap)
101{
102 m_capabilities = cap;
103}
104
105
106/*
107 * Device Manager
108 */
109
110DeviceManager::DeviceManager(Backend *parent)
111 : QObject(parent)
112 , m_backend(parent)
113{
114 Q_ASSERT(parent);
115 updateDeviceList();
116}
117
118DeviceManager::~DeviceManager()
119{
120}
121
122QList<int> DeviceManager::deviceIds(ObjectDescriptionType type)
123{
124 DeviceInfo::Capability capability = DeviceInfo::None;
125 switch (type) {
126 case Phonon::AudioOutputDeviceType:
127 capability = DeviceInfo::AudioOutput;
128 break;
129 case Phonon::AudioCaptureDeviceType:
130 capability = DeviceInfo::AudioCapture;
131 break;
132 case Phonon::VideoCaptureDeviceType:
133 capability = DeviceInfo::VideoCapture;
134 break;
135 default: ;
136 }
137
138 QList<int> ids;
139 foreach (const DeviceInfo &device, m_devices) {
140 if (device.capabilities() & capability)
141 ids.append(t: device.id());
142 }
143
144 return ids;
145}
146
147QHash<QByteArray, QVariant> DeviceManager::deviceProperties(int id)
148{
149 QHash<QByteArray, QVariant> properties;
150
151 foreach (const DeviceInfo &device, m_devices) {
152 if (device.id() == id) {
153 properties.insert(key: "name", value: device.name());
154 properties.insert(key: "description", value: device.description());
155 properties.insert(key: "isAdvanced", value: device.isAdvanced());
156 properties.insert(key: "deviceAccessList", value: QVariant::fromValue<Phonon::DeviceAccessList>(value: device.accessList()));
157 properties.insert(key: "discovererIcon", value: "vlc");
158
159 if (device.capabilities() & DeviceInfo::AudioOutput) {
160 properties.insert(key: "icon", value: QLatin1String("audio-card"));
161 }
162
163 if (device.capabilities() & DeviceInfo::AudioCapture) {
164 properties.insert(key: "hasaudio", value: true);
165 properties.insert(key: "icon", value: QLatin1String("audio-input-microphone"));
166 }
167
168 if (device.capabilities() & DeviceInfo::VideoCapture) {
169 properties.insert(key: "hasvideo", value: true);
170 properties.insert(key: "icon", value: QLatin1String("camera-web"));
171 }
172 break;
173 }
174 }
175
176 return properties;
177}
178
179const DeviceInfo *DeviceManager::device(int id) const
180{
181 for (int i = 0; i < m_devices.size(); i ++) {
182 if (m_devices[i].id() == id)
183 return &m_devices[i];
184 }
185
186 return NULL;
187}
188
189static QList<QByteArray> vlcAudioOutBackends()
190{
191 QList<QByteArray> ret;
192
193 VLC_FOREACH_LIST(audio_output, aout) {
194 QByteArray name(aout->psz_name);
195 if (!ret.contains(t: name))
196 ret.append(t: name);
197 }
198
199 return ret;
200}
201
202void DeviceManager::updateDeviceList()
203{
204 QList<DeviceInfo> newDeviceList;
205
206 if (!LibVLC::self || !pvlc_libvlc)
207 return;
208
209 QList<QByteArray> audioOutBackends = vlcAudioOutBackends();
210
211 PulseSupport *pulse = PulseSupport::getInstance();
212 if (pulse && pulse->isUsable()) {
213 if (audioOutBackends.contains(t: "pulse")) {
214 DeviceInfo defaultAudioOutputDevice(tr(s: "Default"), false);
215 defaultAudioOutputDevice.setCapabilities(DeviceInfo::AudioOutput);
216 defaultAudioOutputDevice.addAccess(access: DeviceAccess("pulse", "default"));
217 newDeviceList.append(t: defaultAudioOutputDevice);
218 pulse->request(requested: true);
219 return;
220 } else {
221 pulse->enable(enabled: false);
222 }
223 }
224
225 QList<QByteArray> knownSoundSystems;
226 // Whitelist - Order has no particular impact.
227 // NOTE: if listing was not intercepted by the PA code above we also need
228 // to try injecting the pulse aout as otherwise the user would have to
229 // use the fake PA device in ALSA to output through PA (kind of silly).
230 knownSoundSystems << QByteArray("pulse")
231 << QByteArray("alsa")
232 << QByteArray("oss")
233 << QByteArray("jack")
234 << QByteArray("aout_directx") // Windows up to VLC 2.0
235 << QByteArray("directsound") // Windows from VLC 2.1 upwards
236 << QByteArray("auhal"); // Mac
237 foreach (const QByteArray &soundSystem, knownSoundSystems) {
238 if (!audioOutBackends.contains(t: soundSystem)) {
239 debug() << "Sound system" << soundSystem << "not supported by libvlc";
240 continue;
241 }
242
243 // FIXME: there is a rather ungodly amount of code duplication going
244 // on here.
245 bool hasDevices = false;
246 VLC_FOREACH(audio_output_device,
247 device,
248 libvlc_audio_output_device_list_get(pvlc_libvlc, soundSystem),
249 libvlc_audio_output_device_list_release) {
250 QString idName = QString::fromUtf8(utf8: device->psz_device);
251 QString longName = QString::fromUtf8(utf8: device->psz_description);
252
253 debug() << "found device" << soundSystem << idName << longName;
254
255 DeviceInfo info(longName, true);
256 info.addAccess(access: DeviceAccess(soundSystem, idName));
257 info.setCapabilities(DeviceInfo::AudioOutput);
258 newDeviceList.append(t: info);
259
260 hasDevices = true;
261 }
262
263 if (!hasDevices) {
264 debug() << "manually injecting sound system" << soundSystem;
265 DeviceInfo info(QString::fromUtf8(ba: soundSystem), false);
266 info.addAccess(access: DeviceAccess(soundSystem, ""));
267 info.setCapabilities(DeviceInfo::AudioOutput);
268 newDeviceList.append(t: info);
269 }
270 }
271
272 /*
273 * Compares the list with the devices available at the moment with the last list. If
274 * a new device is seen, a signal is emitted. If a device disappeared, another signal
275 * is emitted.
276 */
277
278 // Search for added devices
279 for (int i = 0; i < newDeviceList.count(); ++i) {
280 int id = newDeviceList[i].id();
281 if (!listContainsDevice(list: m_devices, id)) {
282 // This is a new device, add it
283 m_devices.append(t: newDeviceList[i]);
284 emit deviceAdded(id);
285
286 debug() << "Added backend device" << newDeviceList[i].name();
287 }
288 }
289
290 // Search for removed devices
291 for (int i = m_devices.count() - 1; i >= 0; --i) {
292 int id = m_devices[i].id();
293 if (!listContainsDevice(list: newDeviceList, id)) {
294 emit deviceRemoved(id);
295 m_devices.removeAt(i);
296 }
297 }
298}
299
300bool DeviceManager::listContainsDevice(const QList<DeviceInfo> &list, int id)
301{
302 foreach (const DeviceInfo &d, list) {
303 if (d.id() == id)
304 return true;
305 }
306 return false;
307}
308
309}
310}
311

source code of phonon-vlc/src/devicemanager.cpp