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-2011 vlc-phonon AUTHORS <kde-multimedia@kde.org> 0006 Copyright (C) 2011-2013 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 "backend.h" 0023 0024 #include <QApplication> 0025 #include <QIcon> 0026 #include <QMessageBox> 0027 #include <QtPlugin> 0028 #include <QVariant> 0029 0030 #include <phonon/GlobalDescriptionContainer> 0031 #include <phonon/pulsesupport.h> 0032 0033 #include <vlc/libvlc_version.h> 0034 0035 #include "audio/audiooutput.h" 0036 #include "audio/volumefadereffect.h" 0037 #include "config.h" 0038 #include "devicemanager.h" 0039 #include "effect.h" 0040 #include "effectmanager.h" 0041 #include "mediaobject.h" 0042 #include "sinknode.h" 0043 #include "utils/debug.h" 0044 #include "utils/libvlc.h" 0045 #include "utils/mime.h" 0046 #ifdef PHONON_EXPERIMENTAL 0047 #include "video/videodataoutput.h" 0048 #endif 0049 #include "video/videowidget.h" 0050 0051 namespace Phonon 0052 { 0053 namespace VLC 0054 { 0055 0056 Backend *Backend::self; 0057 0058 Backend::Backend(QObject *parent, const QVariantList &) 0059 : QObject(parent) 0060 , m_deviceManager(0) 0061 , m_effectManager(0) 0062 { 0063 self = this; 0064 0065 // Backend information properties 0066 setProperty("identifier", QLatin1String("phonon_vlc")); 0067 setProperty("backendName", QLatin1String("VLC")); 0068 setProperty("backendComment", QLatin1String("VLC backend for Phonon")); 0069 setProperty("backendVersion", QLatin1String(PHONON_VLC_VERSION)); 0070 setProperty("backendIcon", QLatin1String("vlc")); 0071 setProperty("backendWebsite", QLatin1String("https://commits.kde.org/phonon-vlc")); 0072 0073 // Check if we should enable debug output 0074 int debugLevel = qgetenv("PHONON_BACKEND_DEBUG").toInt(); 0075 if (debugLevel > 3) // 3 is maximum 0076 debugLevel = 3; 0077 Debug::setMinimumDebugLevel((Debug::DebugLevel)((int) Debug::DEBUG_NONE - 1 - debugLevel)); 0078 0079 debug() << "Constructing Phonon-VLC Version" << PHONON_VLC_VERSION; 0080 0081 // Actual libVLC initialisation 0082 if (LibVLC::init()) { 0083 debug() << "Using VLC version" << libvlc_get_version(); 0084 if (!qApp->applicationName().isEmpty()) { 0085 QString userAgent = 0086 QString("%0/%1 (Phonon/%2; Phonon-VLC/%3)").arg( 0087 qApp->applicationName(), 0088 qApp->applicationVersion(), 0089 PHONON_VERSION_STR, 0090 PHONON_VLC_VERSION); 0091 libvlc_set_user_agent(pvlc_libvlc, 0092 qApp->applicationName().toUtf8().constData(), 0093 userAgent.toUtf8().constData()); 0094 } else { 0095 qWarning("WARNING: Setting the user agent for streaming and" 0096 " PulseAudio requires you to set QCoreApplication::applicationName()"); 0097 } 0098 0099 PulseSupport::getInstance()->enable(true); 0100 const bool pulseActive = PulseSupport::getInstance()->isActive(); 0101 PulseSupport::getInstance()->enable(false); 0102 if (!qApp->applicationName().isEmpty()) { 0103 const QString id = QString("org.kde.phonon.%1").arg(qApp->applicationName()); 0104 const QString version = qApp->applicationVersion(); 0105 QString icon; 0106 if (!qApp->windowIcon().isNull()){ 0107 // Try to get the fromTheme() name of the QIcon. 0108 icon = qApp->windowIcon().name(); 0109 } 0110 if (icon.isEmpty()) { 0111 // If we failed to get a proper icon name, use the appname instead. 0112 icon = qApp->applicationName().toLower(); 0113 } 0114 libvlc_set_app_id(pvlc_libvlc, 0115 id.toUtf8().constData(), 0116 version.toUtf8().constData(), 0117 icon.toUtf8().constData()); 0118 } else if (pulseActive) { 0119 qWarning("WARNING: Setting PulseAudio context information requires you" 0120 " to set QCoreApplication::applicationName()," 0121 " QCoreApplication::applicationVersion() and" 0122 " QGuiApplication::windowIcon()."); 0123 } 0124 } else { 0125 #ifdef __GNUC__ 0126 #warning TODO - this error message is as useful as a knife at a gun fight 0127 #endif 0128 QMessageBox msg; 0129 msg.setIcon(QMessageBox::Critical); 0130 msg.setWindowTitle(tr("LibVLC Failed to Initialize")); 0131 msg.setText(tr("Phonon's VLC backend failed to start." 0132 "\n\n" 0133 "This usually means a problem with your VLC installation," 0134 " please report a bug with your distributor.")); 0135 msg.setDetailedText(LibVLC::errorMessage()); 0136 msg.exec(); 0137 fatal() << "Phonon::VLC::vlcInit: Failed to initialize VLC"; 0138 } 0139 0140 // Since VLC 2.2 PulseSupport is disabled since the "overlay" it implements clashes substantially with libvlc 0141 // internals. Instead VLC has full control. 0142 0143 m_deviceManager = new DeviceManager(this); 0144 m_effectManager = new EffectManager(this); 0145 } 0146 0147 Backend::~Backend() 0148 { 0149 if (LibVLC::self) 0150 delete LibVLC::self; 0151 if (GlobalAudioChannels::self) 0152 delete GlobalAudioChannels::self; 0153 if (GlobalSubtitles::self) 0154 delete GlobalSubtitles::self; 0155 PulseSupport::shutdown(); 0156 } 0157 0158 QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args) 0159 { 0160 if (!LibVLC::self || !pvlc_libvlc) 0161 return 0; 0162 0163 switch (c) { 0164 case MediaObjectClass: 0165 return new MediaObject(parent); 0166 case AudioOutputClass: 0167 return new AudioOutput(parent); 0168 case AudioDataOutputClass: 0169 // With VLC we can't have actual output and at the same time stream 0170 // the data to memory. We therefore can't support ADO. 0171 // https://trac.videolan.org/vlc/ticket/6992 0172 return nullptr; 0173 #ifdef PHONON_EXPERIMENTAL 0174 case VideoDataOutputClass: 0175 return new VideoDataOutput(parent); 0176 #endif 0177 case VideoGraphicsObjectClass: 0178 return nullptr; // No longer supported 0179 case EffectClass: 0180 return effectManager()->createEffect(args[0].toInt(), parent); 0181 case VideoWidgetClass: 0182 return new VideoWidget(qobject_cast<QWidget *>(parent)); 0183 // case VolumeFaderEffectClass: 0184 #ifdef __GNUC__ 0185 #warning VFE crashes and has volume bugs ... deactivated 0186 // return new VolumeFaderEffect(parent); 0187 #endif 0188 } 0189 0190 warning() << "Backend class" << c << "is not supported by Phonon VLC :("; 0191 return 0; 0192 } 0193 0194 QStringList Backend::availableMimeTypes() const 0195 { 0196 if (m_supportedMimeTypes.isEmpty()) 0197 const_cast<Backend *>(this)->m_supportedMimeTypes = mimeTypeList(); 0198 return m_supportedMimeTypes; 0199 } 0200 0201 QList<int> Backend::objectDescriptionIndexes(ObjectDescriptionType type) const 0202 { 0203 QList<int> list; 0204 0205 switch (type) { 0206 case Phonon::AudioChannelType: { 0207 list << GlobalAudioChannels::instance()->globalIndexes(); 0208 } 0209 break; 0210 case Phonon::AudioOutputDeviceType: 0211 case Phonon::AudioCaptureDeviceType: 0212 case Phonon::VideoCaptureDeviceType: { 0213 return deviceManager()->deviceIds(type); 0214 } 0215 break; 0216 case Phonon::EffectType: { 0217 QList<EffectInfo> effectList = effectManager()->effects(); 0218 for (int eff = 0; eff < effectList.size(); ++eff) { 0219 list.append(eff); 0220 } 0221 } 0222 break; 0223 case Phonon::SubtitleType: { 0224 list << GlobalSubtitles::instance()->globalIndexes(); 0225 } 0226 break; 0227 } 0228 0229 return list; 0230 } 0231 0232 QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(ObjectDescriptionType type, int index) const 0233 { 0234 QHash<QByteArray, QVariant> ret; 0235 0236 switch (type) { 0237 case Phonon::AudioChannelType: { 0238 const AudioChannelDescription description = GlobalAudioChannels::instance()->fromIndex(index); 0239 ret.insert("name", description.name()); 0240 ret.insert("description", description.description()); 0241 } 0242 break; 0243 case Phonon::AudioOutputDeviceType: 0244 case Phonon::AudioCaptureDeviceType: 0245 case Phonon::VideoCaptureDeviceType: { 0246 // Index should be unique, even for different categories 0247 return deviceManager()->deviceProperties(index); 0248 } 0249 break; 0250 case Phonon::EffectType: { 0251 const QList<EffectInfo> effectList = effectManager()->effects(); 0252 if (index >= 0 && index <= effectList.size()) { 0253 const EffectInfo &effect = effectList.at(index); 0254 ret.insert("name", effect.name()); 0255 ret.insert("description", effect.description()); 0256 ret.insert("author", effect.author()); 0257 } else { 0258 Q_ASSERT(1); // Since we use list position as ID, this should not happen 0259 } 0260 } 0261 break; 0262 case Phonon::SubtitleType: { 0263 const SubtitleDescription description = GlobalSubtitles::instance()->fromIndex(index); 0264 ret.insert("name", description.name()); 0265 ret.insert("description", description.description()); 0266 ret.insert("type", description.property("type")); 0267 } 0268 break; 0269 } 0270 0271 return ret; 0272 } 0273 0274 bool Backend::startConnectionChange(QSet<QObject *> objects) 0275 { 0276 //FIXME 0277 foreach(QObject * object, objects) { 0278 debug() << "Object:" << object->metaObject()->className(); 0279 } 0280 0281 // There is nothing we can do but hope the connection changes will not take too long 0282 // so that buffers would underrun 0283 // But we should be pretty safe the way xine works by not doing anything here. 0284 return true; 0285 } 0286 0287 bool Backend::connectNodes(QObject *source, QObject *sink) 0288 { 0289 debug() << "Backend connected" << source->metaObject()->className() << "to" << sink->metaObject()->className(); 0290 0291 SinkNode *sinkNode = dynamic_cast<SinkNode *>(sink); 0292 if (sinkNode) { 0293 MediaObject *mediaObject = qobject_cast<MediaObject *>(source); 0294 if (mediaObject) { 0295 // Connect the SinkNode to a MediaObject 0296 sinkNode->connectToMediaObject(mediaObject); 0297 return true; 0298 } 0299 0300 VolumeFaderEffect *effect = qobject_cast<VolumeFaderEffect *>(source); 0301 if (effect) { 0302 sinkNode->connectToMediaObject(effect->mediaObject()); 0303 return true; 0304 } 0305 } 0306 0307 warning() << "Linking" << source->metaObject()->className() << "to" << sink->metaObject()->className() << "failed"; 0308 0309 return false; 0310 } 0311 0312 bool Backend::disconnectNodes(QObject *source, QObject *sink) 0313 { 0314 SinkNode *sinkNode = dynamic_cast<SinkNode *>(sink); 0315 if (sinkNode) { 0316 MediaObject *const mediaObject = qobject_cast<MediaObject *>(source); 0317 if (mediaObject) { 0318 // Disconnect the SinkNode from a MediaObject 0319 sinkNode->disconnectFromMediaObject(mediaObject); 0320 return true; 0321 } 0322 0323 VolumeFaderEffect *const effect = qobject_cast<VolumeFaderEffect *>(source); 0324 if (effect) { 0325 sinkNode->disconnectFromMediaObject(effect->mediaObject()); 0326 return true; 0327 } 0328 } 0329 0330 return false; 0331 } 0332 0333 bool Backend::endConnectionChange(QSet<QObject *> objects) 0334 { 0335 foreach(QObject *object, objects) { 0336 debug() << "Object:" << object->metaObject()->className(); 0337 } 0338 return true; 0339 } 0340 0341 DeviceManager *Backend::deviceManager() const 0342 { 0343 return m_deviceManager; 0344 } 0345 0346 EffectManager *Backend::effectManager() const 0347 { 0348 return m_effectManager; 0349 } 0350 0351 } // namespace VLC 0352 } // namespace Phonon