]> git.sesse.net Git - vlc/blob - bindings/phonon/vlc/vlcmediaobject.cpp
Phonon Backend using VLC
[vlc] / bindings / phonon / vlc / vlcmediaobject.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 "vlcmediaobject.h"
23
24 #include "videowidget.h"
25
26 #include "vlcloader.h"
27
28 #include <QtCore/QTimer>
29 #include <QtCore/QtDebug>
30
31 namespace Phonon
32 {
33 namespace VLC {
34
35 // VLC returns a strange position
36 // We have to multiply by VLC_POSITION_RESOLUTION
37 static const int vlcPositionResolution = 1000;
38
39 VLCMediaObject::VLCMediaObject(QObject * parent)
40         : MediaObject(parent), VLCMediaController()
41 {
42     // Create an empty Media Player object
43     p_vlc_media_player = libvlc_media_player_new(vlc_instance, vlc_exception);
44     vlcExceptionRaised();
45     p_vlc_media_player_event_manager = 0;
46
47     // Media
48     p_vlc_media = 0;
49     p_vlc_media_event_manager = 0;
50
51     // Media Discoverer
52     p_vlc_media_discoverer = 0;
53     p_vlc_media_discoverer_event_manager = 0;
54
55     i_total_time = 0;
56     b_has_video = false;
57     b_seekable = false;
58 }
59
60 VLCMediaObject::~VLCMediaObject()
61 {
62 //    unloadMedia();
63     libvlc_media_player_release(p_vlc_media_player);
64 }
65
66 void VLCMediaObject::unloadMedia()
67 {
68 //    if( p_vlc_media_player ) {
69 //        libvlc_media_player_release(p_vlc_media_player);
70 //        p_vlc_media_player = 0;
71 //    }
72
73     if (p_vlc_media) {
74         libvlc_media_release(p_vlc_media);
75         p_vlc_media = 0;
76     }
77 }
78
79 void VLCMediaObject::loadMediaInternal(const QString & filename)
80 {
81     qDebug() << __FUNCTION__ << filename;
82
83     // Create a media with the given MRL
84     p_vlc_media = libvlc_media_new(vlc_instance, filename.toAscii(), vlc_exception);
85     vlcExceptionRaised();
86
87     // Set the media that will be used by the media player
88     libvlc_media_player_set_media(p_vlc_media_player, p_vlc_media, vlc_exception);
89     vlcExceptionRaised();
90
91     // No need to keep the media now
92 //    libvlc_media_release(p_vlc_media);
93
94     // connectToAllVLCEvents() at the end since it needs p_vlc_media_player
95     connectToAllVLCEvents();
96
97     b_play_request_reached = false;
98
99     // Optimization: wait to see if play() is run just after loadMedia()
100     //               100 milliseconds should be fine
101     QTimer::singleShot(100, this, SLOT(loadMediaInternal()));
102
103     // Get meta data (artist, title, etc...)
104     updateMetaData();
105
106     // Update available audio channels/subtitles/angles/chapters/etc...
107     // i.e everything from MediaController
108     // There is no audio channel/subtitle/angle/chapter events inside libvlc
109     // so let's send our own events...
110     // This will reset the GUI
111     clearMediaController();
112 }
113
114 void VLCMediaObject::loadMediaInternal()
115 {
116     if (b_play_request_reached) {
117         // The media is playing, no need to load it
118         return;
119     }
120
121     emit stateChanged(Phonon::StoppedState);
122 }
123
124 void VLCMediaObject::setVLCWidgetId()
125 {
126     // Get our media player to use our window
127 #if defined(Q_OS_UNIX)
128     libvlc_media_player_set_xwindow(p_vlc_media_player, i_video_widget_id, vlc_exception);
129 #elif defined(Q_OS_WIN)
130     libvlc_media_player_set_hwnd(p_vlc_media_player, i_video_widget_id, vlc_exception);
131 #elif defined(Q_OS_MAC)
132     libvlc_media_player_set_agl(p_vlc_media_player, i_video_widget_id, vlc_exception);
133 #endif
134     vlcExceptionRaised();
135 }
136
137 void VLCMediaObject::playInternal()
138 {
139     b_play_request_reached = true;
140
141     // Clear subtitles/chapters/etc...
142     clearMediaController();
143
144     vlc_current_media_player = p_vlc_media_player;
145
146     setVLCWidgetId();
147
148     // Play
149     libvlc_media_player_play(p_vlc_media_player, vlc_exception);
150     vlcExceptionRaised();
151 }
152
153 void VLCMediaObject::pause()
154 {
155     libvlc_media_player_pause(p_vlc_media_player, vlc_exception);
156     vlcExceptionRaised();
157 }
158
159 void VLCMediaObject::stop()
160 {
161     libvlc_media_player_stop(p_vlc_media_player, vlc_exception);
162     vlcExceptionRaised();
163 //    unloadMedia();
164 }
165
166 void VLCMediaObject::seekInternal(qint64 milliseconds)
167 {
168     qDebug() << __FUNCTION__ << milliseconds;
169     libvlc_media_player_set_time(p_vlc_media_player, milliseconds, vlc_exception);
170     vlcExceptionRaised();
171 }
172
173 QString VLCMediaObject::errorString() const
174 {
175     return libvlc_exception_get_message(vlc_exception);
176 }
177
178 bool VLCMediaObject::hasVideo() const
179 {
180     return b_has_video;
181 }
182
183 bool VLCMediaObject::isSeekable() const
184 {
185     return b_seekable;
186 }
187
188 void VLCMediaObject::connectToAllVLCEvents()
189 {
190     // Get the event manager from which the media player send event
191     p_vlc_media_player_event_manager = libvlc_media_player_event_manager(p_vlc_media_player, vlc_exception);
192     libvlc_event_type_t eventsMediaPlayer[] = {
193         libvlc_MediaPlayerPlaying,
194         libvlc_MediaPlayerPaused,
195         libvlc_MediaPlayerEndReached,
196         libvlc_MediaPlayerStopped,
197         libvlc_MediaPlayerEncounteredError,
198         libvlc_MediaPlayerTimeChanged,
199         libvlc_MediaPlayerTitleChanged,
200         libvlc_MediaPlayerPositionChanged,
201         libvlc_MediaPlayerSeekableChanged,
202         libvlc_MediaPlayerPausableChanged,
203     };
204     int i_nbEvents = sizeof(eventsMediaPlayer) / sizeof(*eventsMediaPlayer);
205     for (int i = 0; i < i_nbEvents; i++) {
206         libvlc_event_attach(p_vlc_media_player_event_manager, eventsMediaPlayer[i],
207                             libvlc_callback, this, vlc_exception);
208         vlcExceptionRaised();
209     }
210
211
212     // Get event manager from media descriptor object
213     p_vlc_media_event_manager = libvlc_media_event_manager(p_vlc_media, vlc_exception);
214     libvlc_event_type_t eventsMedia[] = {
215         libvlc_MediaMetaChanged,
216         libvlc_MediaSubItemAdded,
217         libvlc_MediaDurationChanged,
218         // FIXME libvlc does not know this event
219 //    libvlc_MediaPreparsedChanged,
220         libvlc_MediaFreed,
221         libvlc_MediaStateChanged,
222     };
223     i_nbEvents = sizeof(eventsMedia) / sizeof(*eventsMedia);
224     for (int i = 0; i < i_nbEvents; i++) {
225         libvlc_event_attach(p_vlc_media_event_manager, eventsMedia[i], libvlc_callback, this, vlc_exception);
226         vlcExceptionRaised();
227     }
228
229     // Get event manager from media service discoverer object
230     // FIXME why libvlc_media_discoverer_event_manager() does not take a libvlc_exception_t ?
231 //    p_vlc_media_discoverer_event_manager = libvlc_media_discoverer_event_manager(p_vlc_media_discoverer);
232 //    libvlc_event_type_t eventsMediaDiscoverer[] = {
233 //        libvlc_MediaDiscovererStarted,
234 //        libvlc_MediaDiscovererEnded
235 //    };
236 //    nbEvents = sizeof(eventsMediaDiscoverer) / sizeof(*eventsMediaDiscoverer);
237 //    for (int i = 0; i < nbEvents; i++) {
238 //        libvlc_event_attach(p_vlc_media_discoverer_event_manager, eventsMediaDiscoverer[i], libvlc_callback, this, vlc_exception);
239 //    }
240 }
241
242 void VLCMediaObject::libvlc_callback(const libvlc_event_t *p_event, void *p_user_data)
243 {
244     static int i_first_time_media_player_time_changed = 0;
245     static bool b_media_player_title_changed = false;
246
247     VLCMediaObject *p_vlc_mediaObject = (VLCMediaObject *) p_user_data;
248
249 //    qDebug() << (int)pp_vlc_mediaObject << "event:" << libvlc_event_type_name(event->type);
250
251     // Media player events
252     if (p_event->type == libvlc_MediaPlayerTimeChanged) {
253
254         i_first_time_media_player_time_changed++;
255
256         // FIXME It is ugly. It should be solved by some events in libvlc
257         if (i_first_time_media_player_time_changed == 15) {
258             // Update metadata
259             p_vlc_mediaObject->updateMetaData();
260
261             // Is this media player seekable
262             bool b_seekable = libvlc_media_player_is_seekable(p_vlc_mediaObject->p_vlc_media_player, vlc_exception);
263             vlcExceptionRaised();
264             if (b_seekable != p_vlc_mediaObject->b_seekable) {
265                 qDebug() << "libvlc_callback(): isSeekable:" << b_seekable;
266                 p_vlc_mediaObject->b_seekable = b_seekable;
267                 emit p_vlc_mediaObject->seekableChanged(p_vlc_mediaObject->b_seekable);
268             }
269
270             // Get current video width
271             int i_width = libvlc_video_get_width(p_vlc_mediaObject->p_vlc_media_player, vlc_exception);
272             vlcExceptionRaised();
273
274             // Get current video height
275             int i_height = libvlc_video_get_height(p_vlc_mediaObject->p_vlc_media_player, vlc_exception);
276             vlcExceptionRaised();
277             emit p_vlc_mediaObject->videoWidgetSizeChanged(i_width, i_height);
278
279             // Does this media player have a video output
280             bool b_has_video = libvlc_media_player_has_vout(p_vlc_mediaObject->p_vlc_media_player, vlc_exception);
281             vlcExceptionRaised();
282             if (b_has_video != p_vlc_mediaObject->b_has_video) {
283                 p_vlc_mediaObject->b_has_video = b_has_video;
284                 emit p_vlc_mediaObject->hasVideoChanged(p_vlc_mediaObject->b_has_video);
285             }
286
287             if (b_has_video) {
288                 // Give info about audio tracks
289                 p_vlc_mediaObject->refreshAudioChannels();
290                 // Give info about subtitle tracks
291                 p_vlc_mediaObject->refreshSubtitles();
292
293                 // Get movie chapter count
294                 // It is not a title/chapter media if there is no chapter
295                 if (libvlc_media_player_get_chapter_count(
296                             p_vlc_mediaObject->p_vlc_media_player, vlc_exception) > 0) {
297                     vlcExceptionRaised();
298                     // Give info about title
299                     // only first time, no when title changed
300                     if (!b_media_player_title_changed) {
301                         libvlc_track_description_t *p_info = libvlc_video_get_title_description(
302                                                                  p_vlc_mediaObject->p_vlc_media_player, vlc_exception);
303                         vlcExceptionRaised();
304                         while (p_info) {
305                             p_vlc_mediaObject->titleAdded(p_info->i_id, p_info->psz_name);
306                             p_info = p_info->p_next;
307                         }
308                         libvlc_track_description_release(p_info);
309                     }
310
311                     // Give info about chapters for actual title 0
312                     if (b_media_player_title_changed)
313                         p_vlc_mediaObject->refreshChapters(libvlc_media_player_get_title(
314                                                                p_vlc_mediaObject->p_vlc_media_player, vlc_exception));
315                     else
316                         p_vlc_mediaObject->refreshChapters(0);
317                 }
318                 if (b_media_player_title_changed)
319                     b_media_player_title_changed = false;
320             }
321
322             // Bugfix with Qt mediaplayer example
323             // Now we are in playing state
324             emit p_vlc_mediaObject->stateChanged(Phonon::PlayingState);
325         }
326
327         emit p_vlc_mediaObject->tickInternal(p_vlc_mediaObject->currentTime());
328     }
329
330     if (p_event->type == libvlc_MediaPlayerPlaying) {
331         if (p_vlc_mediaObject->state() != Phonon::LoadingState) {
332             // Bugfix with Qt mediaplayer example
333             emit p_vlc_mediaObject->stateChanged(Phonon::PlayingState);
334         }
335     }
336
337     if (p_event->type == libvlc_MediaPlayerPaused) {
338         emit p_vlc_mediaObject->stateChanged(Phonon::PausedState);
339     }
340
341     if (p_event->type == libvlc_MediaPlayerEndReached) {
342         i_first_time_media_player_time_changed = 0;
343         p_vlc_mediaObject->clearMediaController();
344         emit p_vlc_mediaObject->stateChanged(Phonon::StoppedState);
345         emit p_vlc_mediaObject->finished();
346     }
347
348     if (p_event->type == libvlc_MediaPlayerStopped) {
349         i_first_time_media_player_time_changed = 0;
350         p_vlc_mediaObject->clearMediaController();
351         emit p_vlc_mediaObject->stateChanged(Phonon::StoppedState);
352     }
353
354     if (p_event->type == libvlc_MediaPlayerTitleChanged) {
355         i_first_time_media_player_time_changed = 0;
356         b_media_player_title_changed = true;
357     }
358
359     // Media events
360
361     if (p_event->type == libvlc_MediaDurationChanged) {
362         // Get duration of media descriptor object item
363         libvlc_time_t totalTime = libvlc_media_get_duration(p_vlc_mediaObject->p_vlc_media, vlc_exception);
364         vlcExceptionRaised();
365         totalTime = totalTime / vlcPositionResolution;
366
367         if (totalTime != p_vlc_mediaObject->i_total_time) {
368             p_vlc_mediaObject->i_total_time = totalTime;
369             emit p_vlc_mediaObject->totalTimeChanged(p_vlc_mediaObject->i_total_time);
370         }
371     }
372
373     if (p_event->type == libvlc_MediaMetaChanged) {
374     }
375 }
376
377 void VLCMediaObject::updateMetaData()
378 {
379     QMultiMap<QString, QString> metaDataMap;
380
381     metaDataMap.insert(QLatin1String("ARTIST"),
382                        QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_Artist, vlc_exception)));
383     vlcExceptionRaised();
384     metaDataMap.insert(QLatin1String("ALBUM"),
385                        QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_Album, vlc_exception)));
386     vlcExceptionRaised();
387     metaDataMap.insert(QLatin1String("TITLE"),
388                        QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_Title, vlc_exception)));
389     vlcExceptionRaised();
390     metaDataMap.insert(QLatin1String("DATE"),
391                        QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_Date, vlc_exception)));
392     vlcExceptionRaised();
393     metaDataMap.insert(QLatin1String("GENRE"),
394                        QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_Genre, vlc_exception)));
395     vlcExceptionRaised();
396     metaDataMap.insert(QLatin1String("TRACKNUMBER"),
397                        QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_TrackNumber, vlc_exception)));
398     vlcExceptionRaised();
399     metaDataMap.insert(QLatin1String("DESCRIPTION"),
400                        QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_Description, vlc_exception)));
401     vlcExceptionRaised();
402     metaDataMap.insert(QLatin1String("COPYRIGHT"),
403                        QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_TrackNumber, vlc_exception)));
404     vlcExceptionRaised();
405     metaDataMap.insert(QLatin1String("URL"),
406                        QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_URL, vlc_exception)));
407     vlcExceptionRaised();
408     metaDataMap.insert(QLatin1String("ENCODEDBY"),
409                        QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_EncodedBy, vlc_exception)));
410
411     qDebug() << "updateMetaData(): artist:"
412     << libvlc_media_get_meta(p_vlc_media, libvlc_meta_Artist, vlc_exception);
413     vlcExceptionRaised();
414     qDebug() << "updateMetaData(): title:"
415     << libvlc_media_get_meta(p_vlc_media, libvlc_meta_Title, vlc_exception);
416     vlcExceptionRaised();
417
418     emit metaDataChanged(metaDataMap);
419 }
420
421 qint64 VLCMediaObject::totalTime() const
422 {
423     return i_total_time;
424 }
425
426 qint64 VLCMediaObject::currentTimeInternal() const
427 {
428     libvlc_time_t time = libvlc_media_player_get_time(p_vlc_media_player, vlc_exception);
429     vlcExceptionRaised();
430
431     return time;
432 }
433
434 }
435 } // Namespace Phonon::VLC