File indexing completed on 2024-12-01 04:21:25

0001 /*
0002     Copyright (C) 2007-2008 Tanguy Krotoff <tkrotoff@gmail.com>
0003     Copyright (C) 2008 Lukas Durfina <lukas.durfina@gmail.com>
0004     Copyright (C) 2009 Fathi Boudra <fabo@kde.org>
0005     Copyright (C) 2009-2010 vlc-phonon AUTHORS <kde-multimedia@kde.org>
0006     Copyright (C) 2011 Harald Sitter <sitter@kde.org>
0007 
0008     This library is free software; you can redistribute it and/or
0009     modify it under the terms of the GNU Lesser General Public
0010     License as published by the Free Software Foundation; either
0011     version 2.1 of the License, or (at your option) any later version.
0012 
0013     This library is distributed in the hope that it will be useful,
0014     but WITHOUT ANY WARRANTY; without even the implied warranty of
0015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016     Lesser General Public License for more details.
0017 
0018     You should have received a copy of the GNU Lesser General Public
0019     License along with this library.  If not, see <http://www.gnu.org/licenses/>.
0020 */
0021 
0022 #include "devicemanager.h"
0023 
0024 #include <phonon/pulsesupport.h>
0025 
0026 #include <vlc/vlc.h>
0027 
0028 #include "backend.h"
0029 #include "utils/debug.h"
0030 #include "utils/libvlc.h"
0031 #include "utils/vstring.h"
0032 
0033 namespace Phonon
0034 {
0035 namespace VLC
0036 {
0037 
0038 /*
0039  * Device Info
0040  */
0041 
0042 DeviceInfo::DeviceInfo(const QString &name, bool isAdvanced)
0043 {
0044     // Get an id
0045     static int counter = 0;
0046     m_id = counter++;
0047 
0048     // Get name and description for the device
0049     m_name = name;
0050     m_isAdvanced = isAdvanced;
0051     m_capabilities = None;
0052 
0053     // A default device should never be advanced
0054     if (name.startsWith(QLatin1String("default"), Qt::CaseInsensitive))
0055         m_isAdvanced = false;
0056 }
0057 
0058 int DeviceInfo::id() const
0059 {
0060     return m_id;
0061 }
0062 
0063 const QString& DeviceInfo::name() const
0064 {
0065     return m_name;
0066 }
0067 
0068 const QString& DeviceInfo::description() const
0069 {
0070     return m_description;
0071 }
0072 
0073 bool DeviceInfo::isAdvanced() const
0074 {
0075     return m_isAdvanced;
0076 }
0077 
0078 void DeviceInfo::setAdvanced(bool advanced)
0079 {
0080     m_isAdvanced = advanced;
0081 }
0082 
0083 const DeviceAccessList& DeviceInfo::accessList() const
0084 {
0085     return m_accessList;
0086 }
0087 
0088 void DeviceInfo::addAccess(const DeviceAccess& access)
0089 {
0090     if (m_accessList.isEmpty())
0091         m_description = access.first + ": " + access.second;
0092     m_accessList.append(access);
0093 }
0094 
0095 quint16 DeviceInfo::capabilities() const
0096 {
0097     return m_capabilities;
0098 }
0099 
0100 void DeviceInfo::setCapabilities(quint16 cap)
0101 {
0102     m_capabilities = cap;
0103 }
0104 
0105 
0106 /*
0107  * Device Manager
0108  */
0109 
0110 DeviceManager::DeviceManager(Backend *parent)
0111     : QObject(parent)
0112     , m_backend(parent)
0113 {
0114     Q_ASSERT(parent);
0115     updateDeviceList();
0116 }
0117 
0118 DeviceManager::~DeviceManager()
0119 {
0120 }
0121 
0122 QList<int> DeviceManager::deviceIds(ObjectDescriptionType type)
0123 {
0124     DeviceInfo::Capability capability = DeviceInfo::None;
0125     switch (type) {
0126     case Phonon::AudioOutputDeviceType:
0127         capability = DeviceInfo::AudioOutput;
0128         break;
0129     case Phonon::AudioCaptureDeviceType:
0130         capability = DeviceInfo::AudioCapture;
0131         break;
0132     case Phonon::VideoCaptureDeviceType:
0133         capability = DeviceInfo::VideoCapture;
0134         break;
0135     default: ;
0136     }
0137 
0138     QList<int> ids;
0139     foreach (const DeviceInfo &device, m_devices) {
0140         if (device.capabilities() & capability)
0141             ids.append(device.id());
0142     }
0143 
0144     return ids;
0145 }
0146 
0147 QHash<QByteArray, QVariant> DeviceManager::deviceProperties(int id)
0148 {
0149     QHash<QByteArray, QVariant> properties;
0150 
0151     foreach (const DeviceInfo &device, m_devices) {
0152         if (device.id() == id) {
0153             properties.insert("name", device.name());
0154             properties.insert("description", device.description());
0155             properties.insert("isAdvanced", device.isAdvanced());
0156             properties.insert("deviceAccessList", QVariant::fromValue<Phonon::DeviceAccessList>(device.accessList()));
0157             properties.insert("discovererIcon", "vlc");
0158 
0159             if (device.capabilities() & DeviceInfo::AudioOutput) {
0160                 properties.insert("icon", QLatin1String("audio-card"));
0161             }
0162 
0163             if (device.capabilities() & DeviceInfo::AudioCapture) {
0164                 properties.insert("hasaudio", true);
0165                 properties.insert("icon", QLatin1String("audio-input-microphone"));
0166             }
0167 
0168             if (device.capabilities() & DeviceInfo::VideoCapture) {
0169                 properties.insert("hasvideo", true);
0170                 properties.insert("icon", QLatin1String("camera-web"));
0171             }
0172             break;
0173         }
0174     }
0175 
0176     return properties;
0177 }
0178 
0179 const DeviceInfo *DeviceManager::device(int id) const
0180 {
0181     for (int i = 0; i < m_devices.size(); i ++) {
0182         if (m_devices[i].id() == id)
0183             return &m_devices[i];
0184     }
0185 
0186     return NULL;
0187 }
0188 
0189 static QList<QByteArray> vlcAudioOutBackends()
0190 {
0191     QList<QByteArray> ret;
0192 
0193     VLC_FOREACH_LIST(audio_output, aout) {
0194         QByteArray name(aout->psz_name);
0195         if (!ret.contains(name))
0196             ret.append(name);
0197     }
0198 
0199     return ret;
0200 }
0201 
0202 void DeviceManager::updateDeviceList()
0203 {
0204     QList<DeviceInfo> newDeviceList;
0205 
0206     if (!LibVLC::self || !pvlc_libvlc)
0207         return;
0208 
0209     QList<QByteArray> audioOutBackends = vlcAudioOutBackends();
0210 
0211     PulseSupport *pulse = PulseSupport::getInstance();
0212     if (pulse && pulse->isUsable()) {
0213         if (audioOutBackends.contains("pulse")) {
0214             DeviceInfo defaultAudioOutputDevice(tr("Default"), false);
0215             defaultAudioOutputDevice.setCapabilities(DeviceInfo::AudioOutput);
0216             defaultAudioOutputDevice.addAccess(DeviceAccess("pulse", "default"));
0217             newDeviceList.append(defaultAudioOutputDevice);
0218             pulse->request(true);
0219             return;
0220         } else {
0221             pulse->enable(false);
0222         }
0223     }
0224 
0225     QList<QByteArray> knownSoundSystems;
0226     // Whitelist - Order has no particular impact.
0227     // NOTE: if listing was not intercepted by the PA code above we also need
0228     //       to try injecting the pulse aout as otherwise the user would have to
0229     //       use the fake PA device in ALSA to output through PA (kind of silly).
0230     knownSoundSystems << QByteArray("pulse")
0231                       << QByteArray("alsa")
0232                       << QByteArray("oss")
0233                       << QByteArray("jack")
0234                       << QByteArray("aout_directx") // Windows up to VLC 2.0
0235                       << QByteArray("directsound") // Windows from VLC 2.1 upwards
0236                       << QByteArray("auhal"); // Mac
0237     foreach (const QByteArray &soundSystem, knownSoundSystems) {
0238         if (!audioOutBackends.contains(soundSystem)) {
0239             debug() << "Sound system" << soundSystem << "not supported by libvlc";
0240             continue;
0241         }
0242 
0243         // FIXME: there is a rather ungodly amount of code duplication going
0244         //        on here.
0245         bool hasDevices = false;
0246         VLC_FOREACH(audio_output_device,
0247                     device,
0248                     libvlc_audio_output_device_list_get(pvlc_libvlc, soundSystem),
0249                     libvlc_audio_output_device_list_release) {
0250             QString idName = QString::fromUtf8(device->psz_device);
0251             QString longName = QString::fromUtf8(device->psz_description);
0252 
0253             debug() << "found device" << soundSystem << idName << longName;
0254 
0255             DeviceInfo info(longName, true);
0256             info.addAccess(DeviceAccess(soundSystem, idName));
0257             info.setCapabilities(DeviceInfo::AudioOutput);
0258             newDeviceList.append(info);
0259 
0260             hasDevices = true;
0261         }
0262 
0263         if (!hasDevices) {
0264             debug() << "manually injecting sound system" << soundSystem;
0265             DeviceInfo info(QString::fromUtf8(soundSystem), false);
0266             info.addAccess(DeviceAccess(soundSystem, ""));
0267             info.setCapabilities(DeviceInfo::AudioOutput);
0268             newDeviceList.append(info);
0269         }
0270     }
0271 
0272     /*
0273      * Compares the list with the devices available at the moment with the last list. If
0274      * a new device is seen, a signal is emitted. If a device disappeared, another signal
0275      * is emitted.
0276      */
0277 
0278     // Search for added devices
0279     for (int i = 0; i < newDeviceList.count(); ++i) {
0280         int id = newDeviceList[i].id();
0281         if (!listContainsDevice(m_devices, id)) {
0282             // This is a new device, add it
0283             m_devices.append(newDeviceList[i]);
0284             emit deviceAdded(id);
0285 
0286             debug() << "Added backend device" << newDeviceList[i].name();
0287         }
0288     }
0289 
0290     // Search for removed devices
0291     for (int i = m_devices.count() - 1; i >= 0; --i) {
0292         int id = m_devices[i].id();
0293         if (!listContainsDevice(newDeviceList, id)) {
0294             emit deviceRemoved(id);
0295             m_devices.removeAt(i);
0296         }
0297     }
0298 }
0299 
0300 bool DeviceManager::listContainsDevice(const QList<DeviceInfo> &list, int id)
0301 {
0302     foreach (const DeviceInfo &d, list) {
0303         if (d.id() == id)
0304             return true;
0305     }
0306     return false;
0307 }
0308 
0309 }
0310 }