diff --git a/src/audio/audiooutput.cpp b/src/audio/audiooutput.cpp index af1a4a4..e81cd87 100644 --- a/src/audio/audiooutput.cpp +++ b/src/audio/audiooutput.cpp @@ -1,263 +1,250 @@ /* Copyright (C) 2007-2008 Tanguy Krotoff Copyright (C) 2008 Lukas Durfina Copyright (C) 2009 Fathi Boudra Copyright (C) 2013-2018 Harald Sitter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "audiooutput.h" #include #include #include "backend.h" #include "utils/debug.h" #include "devicemanager.h" #include "mediaobject.h" #include "media.h" namespace Phonon { namespace VLC { AudioOutput::AudioOutput(QObject *parent) : QObject(parent) , m_volume(0.75) , m_explicitVolume(false) , m_muted(false) , m_category(Phonon::NoCategory) { } AudioOutput::~AudioOutput() { } #if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(3, 0, 0, 0)) static libvlc_media_player_role categoryToRole(Category category) { switch(category) { case NoCategory: return libvlc_role_None; case NotificationCategory: return libvlc_role_Notification; case MusicCategory: return libvlc_role_Music; case VideoCategory: return libvlc_role_Video; case CommunicationCategory: return libvlc_role_Communication; case GameCategory: return libvlc_role_Game; case AccessibilityCategory: return libvlc_role_Accessibility; } return libvlc_role_None; } #endif void AudioOutput::handleConnectToMediaObject(MediaObject *mediaObject) { Q_UNUSED(mediaObject); setOutputDeviceImplementation(); if (!PulseSupport::getInstance()->isActive()) { // Rely on libvlc for updates if PASupport is not active connect(m_player, SIGNAL(mutedChanged(bool)), this, SLOT(onMutedChanged(bool))); connect(m_player, SIGNAL(volumeChanged(float)), this, SLOT(onVolumeChanged(float))); applyVolume(); } #if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(3, 0, 0, 0)) libvlc_media_player_set_role(*m_player, categoryToRole(m_category)); #endif } void AudioOutput::handleAddToMedia(Media *media) { media->addOption(":audio"); -#if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 6, 50)) PulseSupport *pulse = PulseSupport::getInstance(); if (pulse && pulse->isActive()) { pulse->setupStreamEnvironment(m_streamUuid); } -#endif } qreal AudioOutput::volume() const { return m_volume; } void AudioOutput::setVolume(qreal volume) { if (m_player) { debug() << "async setting of volume to" << volume; m_volume = volume; m_explicitVolume = true; applyVolume(); #if (LIBVLC_VERSION_INT < LIBVLC_VERSION(2, 2, 2, 0)) emit volumeChanged(m_volume); #endif } } -#if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 8, 50)) void AudioOutput::setMuted(bool mute) { if (mute == m_player->mute()) { // Make sure we actually have propagated the mutness into the frontend. onMutedChanged(mute); return; } m_player->setMute(mute); } -#endif void AudioOutput::setCategory(Category category) { m_category = category; } int AudioOutput::outputDevice() const { return m_device.index(); } bool AudioOutput::setOutputDevice(int deviceIndex) { const AudioOutputDevice device = AudioOutputDevice::fromIndex(deviceIndex); if (!device.isValid()) { error() << Q_FUNC_INFO << "Unable to find the output device with index" << deviceIndex; return false; } return setOutputDevice(device); } -#if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 2, 0)) bool AudioOutput::setOutputDevice(const AudioOutputDevice &newDevice) { debug() << Q_FUNC_INFO; if (!newDevice.isValid()) { error() << "Invalid audio output device"; return false; } if (newDevice == m_device) return true; m_device = newDevice; if (m_player) { setOutputDeviceImplementation(); } return true; } -#endif -#if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 6, 50)) void AudioOutput::setStreamUuid(QString uuid) { DEBUG_BLOCK; debug() << uuid; m_streamUuid = uuid; } -#endif void AudioOutput::setOutputDeviceImplementation() { Q_ASSERT(m_player); #if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 0, 0)) // VLC 2.2 has the PulseSupport overrides always disabled because of // incompatibility. Also see backend.cpp for more detals. // To get access to the correct activity state we need to temporarily // enable pulse and then disable it again. This is necessary because isActive // is in fact isActive&isEnabled............. PulseSupport::getInstance()->enable(true); const bool pulseActive = PulseSupport::getInstance()->isActive(); PulseSupport::getInstance()->enable(false); #else const bool pulseActive = PulseSupport::getInstance()->isActive(); #endif if (pulseActive) { m_player->setAudioOutput("pulse"); debug() << "Setting aout to pulse"; return; } const QVariant dalProperty = m_device.property("deviceAccessList"); if (!dalProperty.isValid()) { error() << "Device" << m_device.property("name") << "has no access list"; return; } const DeviceAccessList deviceAccessList = dalProperty.value(); if (deviceAccessList.isEmpty()) { error() << "Device" << m_device.property("name") << "has an empty access list"; return; } // ### we're not trying the whole access list (could mean same device on different soundsystems) const DeviceAccess &firstDeviceAccess = deviceAccessList.first(); QByteArray soundSystem = firstDeviceAccess.first; debug() << "Setting output soundsystem to" << soundSystem; m_player->setAudioOutput(soundSystem); QByteArray deviceName = firstDeviceAccess.second.toLatin1(); if (!deviceName.isEmpty()) { // print the name as possibly messed up by toLatin1() to see conversion problems debug() << "Setting output device to" << deviceName << '(' << m_device.property("name") << ')'; m_player->setAudioOutputDevice(soundSystem, deviceName); } } void AudioOutput::applyVolume() { if (m_player && m_explicitVolume) { const int preVolume = m_player->audioVolume(); const int newVolume = m_volume * 100; m_player->setAudioVolume(newVolume); #if (LIBVLC_VERSION_INT < LIBVLC_VERSION(2, 2, 2, 0)) onMutedChanged(m_volume == 0.0); onVolumeChanged(newVolume); #endif debug() << "Volume changed from" << preVolume << "to" << newVolume; } } void AudioOutput::onMutedChanged(bool mute) { m_muted = mute; emit mutedChanged(mute); -#if (PHONON_VERSION < PHONON_VERSION_CHECK(4, 8, 51)) - // Previously we had no interface signal to communicate mutness, so instead - // emit volume. - mute ? emit volumeChanged(0.0) : emit volumeChanged(volume()); -#endif } void AudioOutput::onVolumeChanged(float volume) { m_volume = volume; emit volumeChanged(volume); } } } // Namespace Phonon::VLC diff --git a/src/audio/audiooutput.h b/src/audio/audiooutput.h index f29cc22..7165495 100644 --- a/src/audio/audiooutput.h +++ b/src/audio/audiooutput.h @@ -1,144 +1,136 @@ /* Copyright (C) 2007-2008 Tanguy Krotoff Copyright (C) 2008 Lukas Durfina Copyright (C) 2009 Fathi Boudra Copyright (C) 2013 Harald Sitter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef PHONON_VLC_AUDIOOUTPUT_H #define PHONON_VLC_AUDIOOUTPUT_H #include #include #include "sinknode.h" namespace Phonon { namespace VLC { /** \brief AudioOutput implementation for Phonon-VLC * * This class is a SinkNode that implements the AudioOutputInterface from Phonon. It * supports setting the volume and the audio output device. * * There are signals for the change of the volume or for when an audio device failed. * * See the Phonon::AudioOutputInterface documentation for details. * * \see AudioDataOutput */ class AudioOutput : public QObject, public SinkNode, public AudioOutputInterface { Q_OBJECT Q_INTERFACES(Phonon::AudioOutputInterface) public: /** * Creates an AudioOutput with the given backend object. The volume is set to 1.0 * * \param p_back Parent backend * \param p_parent A parent object */ explicit AudioOutput(QObject *parent); ~AudioOutput(); /** \reimp */ void handleConnectToMediaObject(MediaObject *mediaObject); /** \reimp */ void handleAddToMedia(Media *media); /** * \return The current volume for this audio output. */ qreal volume() const; /** * Sets the volume of the audio output. See the Phonon::AudioOutputInterface::setVolume() documentation * for details. */ void setVolume(qreal volume); /** * \return The index of the current audio output device from the list obtained from the backend object. */ int outputDevice() const; /** * Sets the current output device for this audio output. The validity of the device index * is verified before attempting to change the device. * * \param device The index of the device, obtained from the backend's audio device list * \return \c true if succeeded, or no change was made * \return \c false if failed */ bool setOutputDevice(int); /** * Sets the current output device for this audio output. * * \param device The device to set; it should be valid and contain an usable deviceAccessList property * \return \c true if succeeded, or no change was made * \return \c false if failed */ -#if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 2, 0)) bool setOutputDevice(const AudioOutputDevice &newDevice); -#endif - -#if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 6, 50)) void setStreamUuid(QString uuid); -#endif - -#if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 8, 50)) void setMuted(bool mute); -#endif virtual void setCategory(Phonon::Category category); signals: void volumeChanged(qreal volume); void audioDeviceFailed(); void mutedChanged(bool mute); private slots: /** * Sets the volume to m_volume. */ void applyVolume(); void onMutedChanged(bool mute); void onVolumeChanged(float volume); private: /** * We can only really set the output device once we have a libvlc_media_player, which comes * from our SinkNode. */ void setOutputDeviceImplementation(); qreal m_volume; // Set after first setVolume to indicate volume was set manually. bool m_explicitVolume; bool m_muted; AudioOutputDevice m_device; QString m_streamUuid; Category m_category; }; } // namespace VLC } // namespace Phonon #endif // PHONON_VLC_AUDIOOUTPUT_H diff --git a/src/backend.cpp b/src/backend.cpp index 75e91c4..80f0f92 100644 --- a/src/backend.cpp +++ b/src/backend.cpp @@ -1,371 +1,365 @@ /* Copyright (C) 2007-2008 Tanguy Krotoff Copyright (C) 2008 Lukas Durfina Copyright (C) 2009 Fathi Boudra Copyright (C) 2009-2011 vlc-phonon AUTHORS Copyright (C) 2011-2013 Harald Sitter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "backend.h" #include #include #include #include #include #include #include #include #include #include "audio/audiooutput.h" #include "audio/audiodataoutput.h" #include "audio/volumefadereffect.h" #include "config.h" #include "devicemanager.h" #include "effect.h" #include "effectmanager.h" #include "mediaobject.h" #include "sinknode.h" #include "utils/debug.h" #include "utils/libvlc.h" #include "utils/mime.h" #ifdef PHONON_EXPERIMENTAL #include "video/videodataoutput.h" #endif #include "video/videowidget.h" -#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) -Q_EXPORT_PLUGIN2(phonon_vlc, Phonon::VLC::Backend) -#endif - namespace Phonon { namespace VLC { Backend *Backend::self; Backend::Backend(QObject *parent, const QVariantList &) : QObject(parent) , m_deviceManager(0) , m_effectManager(0) { self = this; // Backend information properties setProperty("identifier", QLatin1String("phonon_vlc")); setProperty("backendName", QLatin1String("VLC")); setProperty("backendComment", QLatin1String("VLC backend for Phonon")); setProperty("backendVersion", QLatin1String(PHONON_VLC_VERSION)); setProperty("backendIcon", QLatin1String("vlc")); setProperty("backendWebsite", QLatin1String("https://projects.kde.org/projects/kdesupport/phonon/phonon-vlc")); // Check if we should enable debug output int debugLevel = qgetenv("PHONON_BACKEND_DEBUG").toInt(); if (debugLevel > 3) // 3 is maximum debugLevel = 3; Debug::setMinimumDebugLevel((Debug::DebugLevel)((int) Debug::DEBUG_NONE - 1 - debugLevel)); debug() << "Constructing Phonon-VLC Version" << PHONON_VLC_VERSION; // Actual libVLC initialisation if (LibVLC::init()) { debug() << "Using VLC version" << libvlc_get_version(); if (!qApp->applicationName().isEmpty()) { QString userAgent = QString("%0/%1 (Phonon/%2; Phonon-VLC/%3)").arg( qApp->applicationName(), qApp->applicationVersion(), PHONON_VERSION_STR, PHONON_VLC_VERSION); libvlc_set_user_agent(pvlc_libvlc, qApp->applicationName().toUtf8().constData(), userAgent.toUtf8().constData()); } else { qWarning("WARNING: Setting the user agent for streaming and" " PulseAudio requires you to set QCoreApplication::applicationName()"); } PulseSupport::getInstance()->enable(true); const bool pulseActive = PulseSupport::getInstance()->isActive(); PulseSupport::getInstance()->enable(false); if (!qApp->applicationName().isEmpty()) { const QString id = QString("org.kde.phonon.%1").arg(qApp->applicationName()); const QString version = qApp->applicationVersion(); QString icon; -#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) if (!qApp->windowIcon().isNull()){ // Try to get the fromTheme() name of the QIcon. icon = qApp->windowIcon().name(); } -#endif if (icon.isEmpty()) { // If we failed to get a proper icon name, use the appname instead. icon = qApp->applicationName().toLower(); } libvlc_set_app_id(pvlc_libvlc, id.toUtf8().constData(), version.toUtf8().constData(), icon.toUtf8().constData()); } else if (pulseActive) { qWarning("WARNING: Setting PulseAudio context information requires you" " to set QCoreApplication::applicationName()," " QCoreApplication::applicationVersion() and" " QGuiApplication::windowIcon()."); } } else { #ifdef __GNUC__ #warning TODO - this error message is as useful as a knife at a gun fight #endif QMessageBox msg; msg.setIcon(QMessageBox::Critical); msg.setWindowTitle(tr("LibVLC Failed to Initialize")); msg.setText(tr("Phonon's VLC backend failed to start." "\n\n" "This usually means a problem with your VLC installation," " please report a bug with your distributor.")); msg.setDetailedText(LibVLC::errorMessage()); msg.exec(); fatal() << "Phonon::VLC::vlcInit: Failed to initialize VLC"; } #if (LIBVLC_VERSION_INT < LIBVLC_VERSION(2, 2, 2, 0)) // VLC 2.2 changed the stream creation order around internally which breaks // the Pulseaudio hijacking. Since VLC upstream doesn't feel like giving us // any more property control we now consider this feature unsupported. As // such whatever properties VLC sets will be what pulse knows about us. // Initialise PulseAudio support PulseSupport *pulse = PulseSupport::getInstance(); pulse->enable(true); connect(pulse, SIGNAL(objectDescriptionChanged(ObjectDescriptionType)), SIGNAL(objectDescriptionChanged(ObjectDescriptionType))); #endif m_deviceManager = new DeviceManager(this); m_effectManager = new EffectManager(this); } Backend::~Backend() { if (LibVLC::self) delete LibVLC::self; if (GlobalAudioChannels::self) delete GlobalAudioChannels::self; if (GlobalSubtitles::self) delete GlobalSubtitles::self; PulseSupport::shutdown(); } QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList &args) { if (!LibVLC::self || !pvlc_libvlc) return 0; switch (c) { case MediaObjectClass: return new MediaObject(parent); case AudioOutputClass: return new AudioOutput(parent); #if (LIBVLC_VERSION_INT < LIBVLC_VERSION(2, 0, 0, 0)) // Broken >= 2.0 // https://trac.videolan.org/vlc/ticket/6992 case AudioDataOutputClass: return new AudioDataOutput(parent); #endif #ifdef PHONON_EXPERIMENTAL case VideoDataOutputClass: return new VideoDataOutput(parent); #endif case VideoGraphicsObjectClass: return nullptr; // No longer supported case EffectClass: return effectManager()->createEffect(args[0].toInt(), parent); case VideoWidgetClass: return new VideoWidget(qobject_cast(parent)); // case VolumeFaderEffectClass: #ifdef __GNUC__ #warning VFE crashes and has volume bugs ... deactivated // return new VolumeFaderEffect(parent); #endif } warning() << "Backend class" << c << "is not supported by Phonon VLC :("; return 0; } QStringList Backend::availableMimeTypes() const { if (m_supportedMimeTypes.isEmpty()) const_cast(this)->m_supportedMimeTypes = mimeTypeList(); return m_supportedMimeTypes; } QList Backend::objectDescriptionIndexes(ObjectDescriptionType type) const { QList list; switch (type) { case Phonon::AudioChannelType: { list << GlobalAudioChannels::instance()->globalIndexes(); } break; case Phonon::AudioOutputDeviceType: case Phonon::AudioCaptureDeviceType: case Phonon::VideoCaptureDeviceType: { return deviceManager()->deviceIds(type); } break; case Phonon::EffectType: { QList effectList = effectManager()->effects(); for (int eff = 0; eff < effectList.size(); ++eff) { list.append(eff); } } break; case Phonon::SubtitleType: { list << GlobalSubtitles::instance()->globalIndexes(); } break; } return list; } QHash Backend::objectDescriptionProperties(ObjectDescriptionType type, int index) const { QHash ret; switch (type) { case Phonon::AudioChannelType: { const AudioChannelDescription description = GlobalAudioChannels::instance()->fromIndex(index); ret.insert("name", description.name()); ret.insert("description", description.description()); } break; case Phonon::AudioOutputDeviceType: case Phonon::AudioCaptureDeviceType: case Phonon::VideoCaptureDeviceType: { // Index should be unique, even for different categories return deviceManager()->deviceProperties(index); } break; case Phonon::EffectType: { const QList effectList = effectManager()->effects(); if (index >= 0 && index <= effectList.size()) { const EffectInfo &effect = effectList.at(index); ret.insert("name", effect.name()); ret.insert("description", effect.description()); ret.insert("author", effect.author()); } else { Q_ASSERT(1); // Since we use list position as ID, this should not happen } } break; case Phonon::SubtitleType: { const SubtitleDescription description = GlobalSubtitles::instance()->fromIndex(index); ret.insert("name", description.name()); ret.insert("description", description.description()); ret.insert("type", description.property("type")); } break; } return ret; } bool Backend::startConnectionChange(QSet objects) { //FIXME foreach(QObject * object, objects) { debug() << "Object:" << object->metaObject()->className(); } // There is nothing we can do but hope the connection changes will not take too long // so that buffers would underrun // But we should be pretty safe the way xine works by not doing anything here. return true; } bool Backend::connectNodes(QObject *source, QObject *sink) { debug() << "Backend connected" << source->metaObject()->className() << "to" << sink->metaObject()->className(); SinkNode *sinkNode = dynamic_cast(sink); if (sinkNode) { MediaObject *mediaObject = qobject_cast(source); if (mediaObject) { // Connect the SinkNode to a MediaObject sinkNode->connectToMediaObject(mediaObject); return true; } VolumeFaderEffect *effect = qobject_cast(source); if (effect) { sinkNode->connectToMediaObject(effect->mediaObject()); return true; } } warning() << "Linking" << source->metaObject()->className() << "to" << sink->metaObject()->className() << "failed"; return false; } bool Backend::disconnectNodes(QObject *source, QObject *sink) { SinkNode *sinkNode = dynamic_cast(sink); if (sinkNode) { MediaObject *const mediaObject = qobject_cast(source); if (mediaObject) { // Disconnect the SinkNode from a MediaObject sinkNode->disconnectFromMediaObject(mediaObject); return true; } VolumeFaderEffect *const effect = qobject_cast(source); if (effect) { sinkNode->disconnectFromMediaObject(effect->mediaObject()); return true; } } return false; } bool Backend::endConnectionChange(QSet objects) { foreach(QObject *object, objects) { debug() << "Object:" << object->metaObject()->className(); } return true; } DeviceManager *Backend::deviceManager() const { return m_deviceManager; } EffectManager *Backend::effectManager() const { return m_effectManager; } } // namespace VLC } // namespace Phonon diff --git a/src/backend.h b/src/backend.h index 178fd39..640b70a 100644 --- a/src/backend.h +++ b/src/backend.h @@ -1,160 +1,158 @@ /* Copyright (C) 2007-2008 Tanguy Krotoff Copyright (C) 2008 Lukas Durfina Copyright (C) 2009 Fathi Boudra Copyright (C) 2009-2011 vlc-phonon AUTHORS Copyright (C) 2011 Harald Sitter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef Phonon_VLC_BACKEND_H #define Phonon_VLC_BACKEND_H #include #include #include class LibVLC; namespace Phonon { namespace VLC { class DeviceManager; class EffectManager; /** \brief Backend class for Phonon-VLC. * * This class provides the special objects created by the backend and information about * various things that the backend supports. An object of this class is the root for * the backend plugin. * * Phonon will request the backend to create objects of various classes, like MediaObject, * AudioOutput, VideoWidget, Effect. There are also methods to handle the connections between * these objects. * * This class also provides information about the devices and effects that the backend supports. * These are audio output devices, audio capture devices, video capture devices, effects. */ class Backend : public QObject, public BackendInterface { Q_OBJECT -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) Q_PLUGIN_METADATA(IID "org.kde.phonon.vlc" FILE "phonon-vlc.json") -#endif Q_INTERFACES(Phonon::BackendInterface) public: /** * Instance. Since there is no backend instance without actual Backend object * this class behaves likes a singleton. */ static Backend *self; /** * Constructs the backend. Sets the backend properties, fetches the debug level from the * environment, initializes libVLC, constructs the device and effect managers, initializes * PulseAudio support. * * \param parent A parent object for the backend (passed to the QObject constructor) */ explicit Backend(QObject *parent = 0, const QVariantList & = QVariantList()); virtual ~Backend(); /// \return The device manager that is associated with this backend object DeviceManager *deviceManager() const; /// \return The effect manager that is associated with this backend object. EffectManager *effectManager() const; /** * Creates a backend object of the desired class and with the desired parent. Extra arguments can be provided. * * \param c The class of object that is to be created * \param parent The object that will be the parent of the new object * \param args Optional arguments for the object creation * \return The desired object or NULL if the class is not implemented. */ QObject *createObject(BackendInterface::Class, QObject *parent, const QList &args); /// \returns a list of all available mimetypes (hardcoded) QStringList availableMimeTypes() const; /** * Returns a list of indexes for the desired object types. It specifies a list of objects * of a particular category that the backend knows about. These indexes can be used with * objectDescriptionProperties() to get the properties of a particular object. * * \param type The type of objects for the list */ QList objectDescriptionIndexes(ObjectDescriptionType type) const; /** * Returns a list of properties for a particular object of the desired category. * * \param type The type of object for the index * \param index The index for the object of the desired type * \return The property list. If the object is inexistent, an empty list is returned. */ QHash objectDescriptionProperties(ObjectDescriptionType type, int index) const; /** * Called when a connection between nodes is about to be changed * * \param objects A set of objects that will be involved in the change */ bool startConnectionChange(QSet); /** * Connects two media nodes. The sink is informed that it should connect itself to the source. * * \param source The source media node for the connection * \param sink The sink media node for the connection * \return True if the connection was successful */ bool connectNodes(QObject *, QObject *); /** * Disconnects two previously connected media nodes. It disconnects the sink node from the source node. * * \param source The source node for the disconnection * \param sink The sink node for the disconnection * \return True if the disconnection was successful */ bool disconnectNodes(QObject *, QObject *); /** * Called after a connection between nodes has been changed * * \param objects Nodes involved in the disconnection */ bool endConnectionChange(QSet); Q_SIGNALS: void objectDescriptionChanged(ObjectDescriptionType); private: mutable QStringList m_supportedMimeTypes; DeviceManager *m_deviceManager; EffectManager *m_effectManager; }; } // namespace VLC } // namespace Phonon #endif // Phonon_VLC_BACKEND_H diff --git a/src/devicemanager.cpp b/src/devicemanager.cpp index b3966aa..37d6ee4 100644 --- a/src/devicemanager.cpp +++ b/src/devicemanager.cpp @@ -1,346 +1,340 @@ /* Copyright (C) 2007-2008 Tanguy Krotoff Copyright (C) 2008 Lukas Durfina Copyright (C) 2009 Fathi Boudra Copyright (C) 2009-2010 vlc-phonon AUTHORS Copyright (C) 2011 Harald Sitter This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "devicemanager.h" #include #include #include "backend.h" #include "utils/debug.h" #include "utils/libvlc.h" #include "utils/vstring.h" namespace Phonon { namespace VLC { /* * Device Info */ DeviceInfo::DeviceInfo(const QString &name, bool isAdvanced) { // Get an id static int counter = 0; m_id = counter++; // Get name and description for the device m_name = name; m_isAdvanced = isAdvanced; m_capabilities = None; // A default device should never be advanced if (name.startsWith(QLatin1String("default"), Qt::CaseInsensitive)) m_isAdvanced = false; } int DeviceInfo::id() const { return m_id; } const QString& DeviceInfo::name() const { return m_name; } const QString& DeviceInfo::description() const { return m_description; } bool DeviceInfo::isAdvanced() const { return m_isAdvanced; } void DeviceInfo::setAdvanced(bool advanced) { m_isAdvanced = advanced; } const DeviceAccessList& DeviceInfo::accessList() const { return m_accessList; } void DeviceInfo::addAccess(const DeviceAccess& access) { if (m_accessList.isEmpty()) m_description = access.first + ": " + access.second; m_accessList.append(access); } quint16 DeviceInfo::capabilities() const { return m_capabilities; } void DeviceInfo::setCapabilities(quint16 cap) { m_capabilities = cap; } /* * Device Manager */ DeviceManager::DeviceManager(Backend *parent) : QObject(parent) , m_backend(parent) { Q_ASSERT(parent); updateDeviceList(); } DeviceManager::~DeviceManager() { } QList DeviceManager::deviceIds(ObjectDescriptionType type) { DeviceInfo::Capability capability = DeviceInfo::None; switch (type) { case Phonon::AudioOutputDeviceType: capability = DeviceInfo::AudioOutput; break; case Phonon::AudioCaptureDeviceType: capability = DeviceInfo::AudioCapture; break; case Phonon::VideoCaptureDeviceType: capability = DeviceInfo::VideoCapture; break; default: ; } QList ids; foreach (const DeviceInfo &device, m_devices) { if (device.capabilities() & capability) ids.append(device.id()); } return ids; } QHash DeviceManager::deviceProperties(int id) { QHash properties; foreach (const DeviceInfo &device, m_devices) { if (device.id() == id) { properties.insert("name", device.name()); properties.insert("description", device.description()); properties.insert("isAdvanced", device.isAdvanced()); properties.insert("deviceAccessList", QVariant::fromValue(device.accessList())); properties.insert("discovererIcon", "vlc"); if (device.capabilities() & DeviceInfo::AudioOutput) { properties.insert("icon", QLatin1String("audio-card")); } if (device.capabilities() & DeviceInfo::AudioCapture) { properties.insert("hasaudio", true); properties.insert("icon", QLatin1String("audio-input-microphone")); } if (device.capabilities() & DeviceInfo::VideoCapture) { properties.insert("hasvideo", true); properties.insert("icon", QLatin1String("camera-web")); } break; } } return properties; } const DeviceInfo *DeviceManager::device(int id) const { for (int i = 0; i < m_devices.size(); i ++) { if (m_devices[i].id() == id) return &m_devices[i]; } return NULL; } static QList vlcAudioOutBackends() { QList ret; VLC_FOREACH_LIST(audio_output, aout) { QByteArray name(aout->psz_name); if (!ret.contains(name)) ret.append(name); } return ret; } void DeviceManager::updateDeviceList() { QList newDeviceList; if (!LibVLC::self || !pvlc_libvlc) return; QList audioOutBackends = vlcAudioOutBackends(); PulseSupport *pulse = PulseSupport::getInstance(); -#if (PHONON_VERSION < PHONON_VERSION_CHECK(4, 8, 51)) - if (pulse && pulse->isActive()) { -#else if (pulse && pulse->isUsable()) { -#endif if (audioOutBackends.contains("pulse")) { DeviceInfo defaultAudioOutputDevice(tr("Default"), false); defaultAudioOutputDevice.setCapabilities(DeviceInfo::AudioOutput); defaultAudioOutputDevice.addAccess(DeviceAccess("pulse", "default")); newDeviceList.append(defaultAudioOutputDevice); -#if (PHONON_VERSION >= PHONON_VERSION_CHECK(4, 8, 51)) pulse->request(true); -#endif return; } else { pulse->enable(false); } } QList knownSoundSystems; // Whitelist - Order has no particular impact. // NOTE: if listing was not intercepted by the PA code above we also need // to try injecting the pulse aout as otherwise the user would have to // use the fake PA device in ALSA to output through PA (kind of silly). knownSoundSystems << QByteArray("pulse") << QByteArray("alsa") << QByteArray("oss") << QByteArray("jack") << QByteArray("aout_directx") // Windows up to VLC 2.0 << QByteArray("directsound") // Windows from VLC 2.1 upwards << QByteArray("auhal"); // Mac foreach (const QByteArray &soundSystem, knownSoundSystems) { if (!audioOutBackends.contains(soundSystem)) { debug() << "Sound system" << soundSystem << "not supported by libvlc"; continue; } // FIXME: there is a rather ungodly amount of code duplication going // on here. #if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(2, 2, 0, 0)) bool hasDevices = false; VLC_FOREACH(audio_output_device, device, libvlc_audio_output_device_list_get(pvlc_libvlc, soundSystem), libvlc_audio_output_device_list_release) { QString idName = QString::fromUtf8(device->psz_device); QString longName = QString::fromUtf8(device->psz_description); debug() << "found device" << soundSystem << idName << longName; DeviceInfo info(longName, true); info.addAccess(DeviceAccess(soundSystem, idName)); info.setCapabilities(DeviceInfo::AudioOutput); newDeviceList.append(info); hasDevices = true; } if (!hasDevices) { debug() << "manually injecting sound system" << soundSystem; DeviceInfo info(QString::fromUtf8(soundSystem), false); info.addAccess(DeviceAccess(soundSystem, "")); info.setCapabilities(DeviceInfo::AudioOutput); newDeviceList.append(info); } #else const int deviceCount = libvlc_audio_output_device_count(pvlc_libvlc, soundSystem); for (int i = 0; i < deviceCount; i++) { VString idName(libvlc_audio_output_device_id(libvlc, soundSystem, i)); VString longName(libvlc_audio_output_device_longname(pvlc_libvlc, soundSystem, i)); debug() << "found device" << soundSystem << idName << longName; DeviceInfo info(longName, true); info.addAccess(DeviceAccess(soundSystem, idName)); info.setCapabilities(DeviceInfo::AudioOutput); newDeviceList.append(info); } // libVLC gives no devices for some sound systems, like OSS if (deviceCount == 0) { debug() << "manually injecting sound system" << soundSystem; // NOTE: Do not mark manually injected devices as advanced. // libphonon filters advanced devices from the default // selection which on systems such as OSX or Windows can // lead to an empty device list as the injected device is // the only available one. DeviceInfo info(QString::fromUtf8(soundSystem), false); info.addAccess(DeviceAccess(soundSystem, "")); info.setCapabilities(DeviceInfo::AudioOutput); newDeviceList.append(info); } #endif } /* * Compares the list with the devices available at the moment with the last list. If * a new device is seen, a signal is emitted. If a device disappeared, another signal * is emitted. */ // Search for added devices for (int i = 0; i < newDeviceList.count(); ++i) { int id = newDeviceList[i].id(); if (!listContainsDevice(m_devices, id)) { // This is a new device, add it m_devices.append(newDeviceList[i]); emit deviceAdded(id); debug() << "Added backend device" << newDeviceList[i].name(); } } // Search for removed devices for (int i = m_devices.count() - 1; i >= 0; --i) { int id = m_devices[i].id(); if (!listContainsDevice(newDeviceList, id)) { emit deviceRemoved(id); m_devices.removeAt(i); } } } bool DeviceManager::listContainsDevice(const QList &list, int id) { foreach (const DeviceInfo &d, list) { if (d.id() == id) return true; } return false; } } }