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> *
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. *
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. *
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 *****************************************************************************/
22 #include "vlcmediaobject.h"
24 #include "videowidget.h"
26 #include "vlcloader.h"
28 #include <QtCore/QTimer>
29 #include <QtCore/QtDebug>
35 // VLC returns a strange position
36 // We have to multiply by VLC_POSITION_RESOLUTION
37 static const int vlcPositionResolution = 1000;
39 VLCMediaObject::VLCMediaObject(QObject * parent)
40 : MediaObject(parent), VLCMediaController()
42 // Create an empty Media Player object
43 p_vlc_media_player = libvlc_media_player_new(vlc_instance, vlc_exception);
45 p_vlc_media_player_event_manager = 0;
49 p_vlc_media_event_manager = 0;
52 p_vlc_media_discoverer = 0;
53 p_vlc_media_discoverer_event_manager = 0;
60 VLCMediaObject::~VLCMediaObject()
63 libvlc_media_player_release(p_vlc_media_player);
66 void VLCMediaObject::unloadMedia()
68 // if( p_vlc_media_player ) {
69 // libvlc_media_player_release(p_vlc_media_player);
70 // p_vlc_media_player = 0;
74 libvlc_media_release(p_vlc_media);
79 void VLCMediaObject::loadMediaInternal(const QString & filename)
81 qDebug() << __FUNCTION__ << filename;
83 // Create a media with the given MRL
84 p_vlc_media = libvlc_media_new(vlc_instance, filename.toAscii(), vlc_exception);
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);
91 // No need to keep the media now
92 // libvlc_media_release(p_vlc_media);
94 // connectToAllVLCEvents() at the end since it needs p_vlc_media_player
95 connectToAllVLCEvents();
97 b_play_request_reached = false;
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()));
103 // Get meta data (artist, title, etc...)
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();
114 void VLCMediaObject::loadMediaInternal()
116 if (b_play_request_reached) {
117 // The media is playing, no need to load it
121 emit stateChanged(Phonon::StoppedState);
124 void VLCMediaObject::setVLCWidgetId()
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);
134 vlcExceptionRaised();
137 void VLCMediaObject::playInternal()
139 b_play_request_reached = true;
141 // Clear subtitles/chapters/etc...
142 clearMediaController();
144 vlc_current_media_player = p_vlc_media_player;
149 libvlc_media_player_play(p_vlc_media_player, vlc_exception);
150 vlcExceptionRaised();
153 void VLCMediaObject::pause()
155 libvlc_media_player_pause(p_vlc_media_player, vlc_exception);
156 vlcExceptionRaised();
159 void VLCMediaObject::stop()
161 libvlc_media_player_stop(p_vlc_media_player, vlc_exception);
162 vlcExceptionRaised();
166 void VLCMediaObject::seekInternal(qint64 milliseconds)
168 qDebug() << __FUNCTION__ << milliseconds;
169 libvlc_media_player_set_time(p_vlc_media_player, milliseconds, vlc_exception);
170 vlcExceptionRaised();
173 QString VLCMediaObject::errorString() const
175 return libvlc_exception_get_message(vlc_exception);
178 bool VLCMediaObject::hasVideo() const
183 bool VLCMediaObject::isSeekable() const
188 void VLCMediaObject::connectToAllVLCEvents()
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,
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();
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,
221 libvlc_MediaStateChanged,
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();
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
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);
242 void VLCMediaObject::libvlc_callback(const libvlc_event_t *p_event, void *p_user_data)
244 static int i_first_time_media_player_time_changed = 0;
245 static bool b_media_player_title_changed = false;
247 VLCMediaObject *p_vlc_mediaObject = (VLCMediaObject *) p_user_data;
249 // qDebug() << (int)pp_vlc_mediaObject << "event:" << libvlc_event_type_name(event->type);
251 // Media player events
252 if (p_event->type == libvlc_MediaPlayerTimeChanged) {
254 i_first_time_media_player_time_changed++;
256 // FIXME It is ugly. It should be solved by some events in libvlc
257 if (i_first_time_media_player_time_changed == 15) {
259 p_vlc_mediaObject->updateMetaData();
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);
270 // Get current video width
271 int i_width = libvlc_video_get_width(p_vlc_mediaObject->p_vlc_media_player, vlc_exception);
272 vlcExceptionRaised();
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);
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);
288 // Give info about audio tracks
289 p_vlc_mediaObject->refreshAudioChannels();
290 // Give info about subtitle tracks
291 p_vlc_mediaObject->refreshSubtitles();
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();
305 p_vlc_mediaObject->titleAdded(p_info->i_id, p_info->psz_name);
306 p_info = p_info->p_next;
308 libvlc_track_description_release(p_info);
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));
316 p_vlc_mediaObject->refreshChapters(0);
318 if (b_media_player_title_changed)
319 b_media_player_title_changed = false;
322 // Bugfix with Qt mediaplayer example
323 // Now we are in playing state
324 emit p_vlc_mediaObject->stateChanged(Phonon::PlayingState);
327 emit p_vlc_mediaObject->tickInternal(p_vlc_mediaObject->currentTime());
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);
337 if (p_event->type == libvlc_MediaPlayerPaused) {
338 emit p_vlc_mediaObject->stateChanged(Phonon::PausedState);
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();
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);
354 if (p_event->type == libvlc_MediaPlayerTitleChanged) {
355 i_first_time_media_player_time_changed = 0;
356 b_media_player_title_changed = true;
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;
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);
373 if (p_event->type == libvlc_MediaMetaChanged) {
377 void VLCMediaObject::updateMetaData()
379 QMultiMap<QString, QString> metaDataMap;
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)));
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();
418 emit metaDataChanged(metaDataMap);
421 qint64 VLCMediaObject::totalTime() const
426 qint64 VLCMediaObject::currentTimeInternal() const
428 libvlc_time_t time = libvlc_media_player_get_time(p_vlc_media_player, vlc_exception);
429 vlcExceptionRaised();
435 } // Namespace Phonon::VLC