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