]> git.sesse.net Git - vlc/blob - bindings/phonon/vlc/backend.cpp
Phonon Backend using VLC
[vlc] / bindings / phonon / vlc / backend.cpp
1 /*****************************************************************************
2  * VLC backend for the Phonon library                                        *
3  * Copyright (C) 2007-2008 Tanguy Krotoff <tkrotoff@gmail.com>               *
4  * Copyright (C) 2008 Lukas Durfina <lukas.durfina@gmail.com>                *
5  * Copyright (C) 2009 Fathi Boudra <fabo@kde.org>                            *
6  *                                                                           *
7  * This program is free software; you can redistribute it and/or             *
8  * modify it under the terms of the GNU Lesser General Public                *
9  * License as published by the Free Software Foundation; either              *
10  * version 3 of the License, or (at your option) any later version.          *
11  *                                                                           *
12  * This program is distributed in the hope that it will be useful,           *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU         *
15  * Lesser General Public License for more details.                           *
16  *                                                                           *
17  * You should have received a copy of the GNU Lesser General Public          *
18  * License along with this package; if not, write to the Free Software       *
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA *
20  *****************************************************************************/
21
22 #include "backend.h"
23
24 #include "audiooutput.h"
25 #include "mediaobject.h"
26 #include "videowidget.h"
27 #include "devicemanager.h"
28 #include "effectmanager.h"
29 #include "effect.h"
30 #include "sinknode.h"
31 #include "vlcloader.h"
32 #include "vlcmediaobject.h"
33
34 #include <QtCore/QSet>
35 #include <QtCore/QVariant>
36 #include <QtCore/QtPlugin>
37
38 Q_EXPORT_PLUGIN2(phonon_vlc, Phonon::VLC::Backend)
39
40 namespace Phonon
41 {
42 namespace VLC {
43
44 Backend::Backend(QObject *parent, const QVariantList &)
45         : QObject(parent)
46         , m_deviceManager(0)
47         , m_effectManager(0)
48         , m_debugLevel(Warning)
49 {
50     bool wasInit = vlcInit();
51
52     setProperty("identifier",     QLatin1String("phonon_vlc"));
53     setProperty("backendName",    QLatin1String("VLC"));
54     setProperty("backendComment", QLatin1String("VLC plugin for Phonon"));
55     setProperty("backendVersion", QLatin1String("0.1"));
56     setProperty("backendWebsite", QLatin1String("http://multimedia.kde.org/"));
57
58     // Check if we should enable debug output
59     QString debugLevelString = qgetenv("PHONON_VLC_DEBUG");
60     int debugLevel = debugLevelString.toInt();
61     if (debugLevel > 3) // 3 is maximum
62         debugLevel = 3;
63     m_debugLevel = (DebugLevel)debugLevel;
64
65     if (wasInit) {
66         logMessage(QString("Using VLC version %0").arg(libvlc_get_version()));
67     } else {
68         qWarning("Phonon::VLC::vlcInit: Failed to initialize VLC");
69     }
70
71     m_deviceManager = new DeviceManager(this);
72     m_effectManager = new EffectManager(this);
73 }
74
75 Backend::~Backend()
76 {
77 //    vlcRelease();
78 }
79
80 QObject *Backend::createObject(BackendInterface::Class c, QObject *parent, const QList<QVariant> &args)
81 {
82     switch (c) {
83     case MediaObjectClass:
84         return new VLCMediaObject(parent);
85     case VolumeFaderEffectClass:
86 //        return new VolumeFaderEffect(parent);
87         logMessage("createObject() : VolumeFaderEffect not implemented");
88         break;
89     case AudioOutputClass: {
90         AudioOutput *ao = new AudioOutput(this, parent);
91         m_audioOutputs.append(ao);
92         return ao;
93     }
94     case AudioDataOutputClass:
95 //        return new AudioDataOutput(parent);
96         logMessage("createObject() : AudioDataOutput not implemented");
97         break;
98     case VisualizationClass:
99 //        return new Visualization(parent);
100         logMessage("createObject() : Visualization not implemented");
101         break;
102     case VideoDataOutputClass:
103 //        return new VideoDataOutput(parent);
104         logMessage("createObject() : VideoDataOutput not implemented");
105         break;
106     case EffectClass:
107         return new Effect(m_effectManager, args[0].toInt(), parent);
108     case VideoWidgetClass:
109         return new VideoWidget(qobject_cast<QWidget *>(parent));
110     default:
111         logMessage("createObject() : Backend object not available");
112     }
113     return 0;
114 }
115
116 bool Backend::supportsVideo() const
117 {
118     return true;
119 }
120
121 bool Backend::supportsOSD() const
122 {
123     return true;
124 }
125
126 bool Backend::supportsFourcc(quint32 fourcc) const
127 {
128     return true;
129 }
130
131 bool Backend::supportsSubtitles() const
132 {
133     return true;
134 }
135
136 QStringList Backend::availableMimeTypes() const
137 {
138     if (m_supportedMimeTypes.isEmpty()) {
139         const_cast<Backend *>(this)->m_supportedMimeTypes
140         << QLatin1String("application/ogg")
141         << QLatin1String("application/vnd.rn-realmedia")
142         << QLatin1String("application/x-annodex")
143         << QLatin1String("application/x-flash-video")
144         << QLatin1String("application/x-quicktimeplayer")
145         << QLatin1String("audio/168sv")
146         << QLatin1String("audio/8svx")
147         << QLatin1String("audio/aiff")
148         << QLatin1String("audio/basic")
149         << QLatin1String("audio/mp3")
150         << QLatin1String("audio/mpeg")
151         << QLatin1String("audio/mpeg2")
152         << QLatin1String("audio/mpeg3")
153         << QLatin1String("audio/vnd.rn-realaudio")
154         << QLatin1String("audio/wav")
155         << QLatin1String("audio/x-16sv")
156         << QLatin1String("audio/x-8svx")
157         << QLatin1String("audio/x-aiff")
158         << QLatin1String("audio/x-basic")
159         << QLatin1String("audio/x-m4a")
160         << QLatin1String("audio/x-mp3")
161         << QLatin1String("audio/x-mpeg")
162         << QLatin1String("audio/x-mpeg2")
163         << QLatin1String("audio/x-mpeg3")
164         << QLatin1String("audio/x-mpegurl")
165         << QLatin1String("audio/x-ms-wma")
166         << QLatin1String("audio/x-ogg")
167         << QLatin1String("audio/x-pn-aiff")
168         << QLatin1String("audio/x-pn-au")
169         << QLatin1String("audio/x-pn-realaudio-plugin")
170         << QLatin1String("audio/x-pn-wav")
171         << QLatin1String("audio/x-pn-windows-acm")
172         << QLatin1String("audio/x-real-audio")
173         << QLatin1String("audio/x-realaudio")
174         << QLatin1String("audio/x-speex+ogg")
175         << QLatin1String("audio/x-wav")
176         << QLatin1String("image/ilbm")
177         << QLatin1String("image/png")
178         << QLatin1String("image/x-ilbm")
179         << QLatin1String("image/x-png")
180         << QLatin1String("video/anim")
181         << QLatin1String("video/avi")
182         << QLatin1String("video/mkv")
183         << QLatin1String("video/mng")
184         << QLatin1String("video/mp4")
185         << QLatin1String("video/mpeg")
186         << QLatin1String("video/mpg")
187         << QLatin1String("video/msvideo")
188         << QLatin1String("video/quicktime")
189         << QLatin1String("video/x-anim")
190         << QLatin1String("video/x-flic")
191         << QLatin1String("video/x-mng")
192         << QLatin1String("video/x-mpeg")
193         << QLatin1String("video/x-ms-asf")
194         << QLatin1String("video/x-ms-wmv")
195         << QLatin1String("video/x-msvideo")
196         << QLatin1String("video/x-quicktime");
197     }
198     return m_supportedMimeTypes;
199 }
200
201 QList<int> Backend::objectDescriptionIndexes(ObjectDescriptionType type) const
202 {
203     QList<int> list;
204
205     switch (type) {
206     case Phonon::AudioOutputDeviceType: {
207         QList<AudioDevice> deviceList = deviceManager()->audioOutputDevices();
208         for (int dev = 0 ; dev < deviceList.size() ; ++dev)
209             list.append(deviceList[dev].id);
210         break;
211     }
212     break;
213     case Phonon::EffectType: {
214         QList<EffectInfo*> effectList = effectManager()->effects();
215         for (int eff = 0; eff < effectList.size(); ++eff)
216             list.append(eff);
217         break;
218     }
219     break;
220     default:
221         break;
222     }
223
224     return list;
225 }
226
227 QHash<QByteArray, QVariant> Backend::objectDescriptionProperties(ObjectDescriptionType type, int index) const
228 {
229     QHash<QByteArray, QVariant> ret;
230
231     switch (type) {
232     case Phonon::AudioOutputDeviceType: {
233         QList<AudioDevice> audioDevices = deviceManager()->audioOutputDevices();
234         if (index >= 0 && index < audioDevices.size()) {
235             ret.insert("name", audioDevices[index].vlcId);
236             ret.insert("description", audioDevices[index].description);
237             ret.insert("icon", QLatin1String("audio-card"));
238         }
239     }
240     break;
241     case Phonon::EffectType: {
242         QList<EffectInfo*> effectList = effectManager()->effects();
243         if (index >= 0 && index <= effectList.size()) {
244             const EffectInfo *effect = effectList[ index ];
245             ret.insert("name", effect->name());
246             ret.insert("description", effect->description());
247             ret.insert("author", effect->author());
248         } else {
249             Q_ASSERT(1); // Since we use list position as ID, this should not happen
250         }
251     }
252     break;
253     default:
254         break;
255     }
256
257     return ret;
258 }
259
260 bool Backend::startConnectionChange(QSet<QObject *> objects)
261 {
262     foreach(QObject *object, objects) {
263         logMessage(QString("Object: %0").arg(object->metaObject()->className()));
264     }
265
266     // There is nothing we can do but hope the connection changes will not take too long
267     // so that buffers would underrun
268     // But we should be pretty safe the way xine works by not doing anything here.
269     return true;
270 }
271
272 bool Backend::connectNodes(QObject *source, QObject *sink)
273 {
274     logMessage(QString("Backend connected %0 to %1")
275                .arg(source->metaObject()->className())
276                .arg(sink->metaObject()->className()));
277
278     // Example:
279     // source = Phonon::VLC_MPlayer::MediaObject
280     // sink = Phonon::VLC_MPlayer::VideoWidget
281
282     // Example:
283     // source = Phonon::VLC_MPlayer::MediaObject
284     // sink = Phonon::VLC_MPlayer::AudioOutput
285
286     // Example:
287     // source = Phonon::VLC_MPlayer::MediaObject
288     // sink = Phonon::VLC_MPlayer::Effect
289
290     // Example:
291     // source = Phonon::VLC_MPlayer::Effect
292     // sink = Phonon::VLC_MPlayer::AudioOutput
293
294     SinkNode *sinkNode = qobject_cast<SinkNode *>(sink);
295     if (sinkNode) {
296         PrivateMediaObject *mediaObject = qobject_cast<PrivateMediaObject *>(source);
297         if (mediaObject) {
298             // Connect the SinkNode to a MediaObject
299             sinkNode->connectToMediaObject(mediaObject);
300             return true;
301         } else {
302             // FIXME try to find a better way...
303 //            Effect *effect = qobject_cast<Effect *>(source);
304             return true;
305         }
306     }
307
308     logMessage(QString("Linking %0 to %1 failed")
309                .arg(source->metaObject()->className())
310                .arg(sink->metaObject()->className()),
311                Warning);
312
313     return false;
314 }
315
316 bool Backend::disconnectNodes(QObject *source, QObject *sink)
317 {
318     SinkNode *sinkNode = qobject_cast<SinkNode *>(sink);
319     if (sinkNode) {
320         PrivateMediaObject *mediaObject = qobject_cast<PrivateMediaObject *>(source);
321         if (mediaObject) {
322             // Disconnect the SinkNode from a MediaObject
323             sinkNode->disconnectFromMediaObject(mediaObject);
324             return true;
325         } else {
326             // FIXME try to find a better way...
327 //            Effect *effect = qobject_cast<Effect *>(source);
328             return true;
329         }
330     }
331
332     return false;
333 }
334
335 bool Backend::endConnectionChange(QSet<QObject *> objects)
336 {
337     foreach(QObject *object, objects) {
338         logMessage(QString("Object: %0").arg(object->metaObject()->className()));
339     }
340
341     return true;
342 }
343
344 DeviceManager* Backend::deviceManager() const
345 {
346     return m_deviceManager;
347 }
348
349 EffectManager* Backend::effectManager() const
350 {
351     return m_effectManager;
352 }
353
354 /**
355  * Return a debuglevel that is determined by the
356  * PHONON_VLC_DEBUG environment variable.
357  *
358  *  Warning - important warnings
359  *  Info    - general info
360  *  Debug   - gives extra info
361  */
362 Backend::DebugLevel Backend::debugLevel() const
363 {
364     return m_debugLevel;
365 }
366
367 /**
368  * Print a conditional debug message based on the current debug level
369  * If obj is provided, classname and objectname will be printed as well
370  *
371  * see debugLevel()
372  */
373 void Backend::logMessage(const QString &message, int priority, QObject *obj) const
374 {
375     if (debugLevel() > 0) {
376         QString output;
377         if (obj) {
378             // Strip away namespace from className
379             QString className(obj->metaObject()->className());
380             int nameLength = className.length() - className.lastIndexOf(':') - 1;
381             className = className.right(nameLength);
382             output.sprintf("%s %s (%s %p)", message.toLatin1().constData(),
383                            obj->objectName().toLatin1().constData(),
384                            className.toLatin1().constData(), obj);
385         } else {
386             output = message;
387         }
388         if (priority <= (int)debugLevel()) {
389             qDebug() << QString("PVLC(%1): %2").arg(priority).arg(output);
390         }
391     }
392 }
393
394 }
395 } // Namespace Phonon::VLC