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 VLCMediaObject::VLCMediaObject(QObject * parent)
36 : MediaObject(parent), VLCMediaController()
38 // Create an empty Media Player object
39 p_vlc_media_player = libvlc_media_player_new(vlc_instance, vlc_exception);
41 p_vlc_media_player_event_manager = 0;
45 p_vlc_media_event_manager = 0;
48 p_vlc_media_discoverer = 0;
49 p_vlc_media_discoverer_event_manager = 0;
56 VLCMediaObject::~VLCMediaObject()
60 libvlc_media_player_stop(p_vlc_media_player); // ensure that we are stopped
61 libvlc_media_player_release(p_vlc_media_player);
64 void VLCMediaObject::unloadMedia()
66 // if( p_vlc_media_player ) {
67 // libvlc_media_player_release(p_vlc_media_player);
68 // p_vlc_media_player = 0;
72 libvlc_media_release(p_vlc_media);
77 void VLCMediaObject::loadMediaInternal(const QString & filename)
79 qDebug() << __FUNCTION__ << filename;
81 // Create a media with the given MRL
82 p_vlc_media = libvlc_media_new(vlc_instance, filename.toAscii(), vlc_exception);
85 // Set the media that will be used by the media player
86 libvlc_media_player_set_media(p_vlc_media_player, p_vlc_media);
88 // No need to keep the media now
89 // libvlc_media_release(p_vlc_media);
91 // connectToAllVLCEvents() at the end since it needs p_vlc_media_player
92 connectToAllVLCEvents();
94 b_play_request_reached = false;
96 // Get meta data (artist, title, etc...)
99 // Update available audio channels/subtitles/angles/chapters/etc...
100 // i.e everything from MediaController
101 // There is no audio channel/subtitle/angle/chapter events inside libvlc
102 // so let's send our own events...
103 // This will reset the GUI
104 clearMediaController();
106 // We need to do this, otherwise we never get any events with the real length
107 libvlc_media_get_duration(p_vlc_media, vlc_exception);
109 if (b_play_request_reached) {
110 // The media is playing, no need to load it
114 emit stateChanged(Phonon::StoppedState);
117 void VLCMediaObject::setVLCWidgetId()
119 // Get our media player to use our window
120 #if defined(Q_OS_UNIX)
121 libvlc_media_player_set_xwindow(p_vlc_media_player, i_video_widget_id);
122 #elif defined(Q_OS_WIN)
123 libvlc_media_player_set_hwnd(p_vlc_media_player, i_video_widget_id);
124 #elif defined(Q_OS_MAC)
125 libvlc_media_player_set_agl(p_vlc_media_player, i_video_widget_id);
129 void VLCMediaObject::playInternal()
131 b_play_request_reached = true;
133 // Clear subtitles/chapters/etc...
134 clearMediaController();
136 vlc_current_media_player = p_vlc_media_player;
141 libvlc_media_player_play(p_vlc_media_player, vlc_exception);
142 vlcExceptionRaised();
145 void VLCMediaObject::pause()
147 libvlc_media_player_pause(p_vlc_media_player, vlc_exception);
148 vlcExceptionRaised();
151 void VLCMediaObject::stop()
153 libvlc_media_player_stop(p_vlc_media_player);
157 void VLCMediaObject::seekInternal(qint64 milliseconds)
159 qDebug() << __FUNCTION__ << milliseconds;
160 libvlc_media_player_set_time(p_vlc_media_player, milliseconds, vlc_exception);
161 vlcExceptionRaised();
164 QString VLCMediaObject::errorString() const
166 return libvlc_errmsg();
169 bool VLCMediaObject::hasVideo() const
174 bool VLCMediaObject::isSeekable() const
179 void VLCMediaObject::connectToAllVLCEvents()
181 // Get the event manager from which the media player send event
182 p_vlc_media_player_event_manager = libvlc_media_player_event_manager(p_vlc_media_player);
183 libvlc_event_type_t eventsMediaPlayer[] = {
184 libvlc_MediaPlayerPlaying,
185 libvlc_MediaPlayerPaused,
186 libvlc_MediaPlayerEndReached,
187 libvlc_MediaPlayerStopped,
188 libvlc_MediaPlayerEncounteredError,
189 libvlc_MediaPlayerTimeChanged,
190 libvlc_MediaPlayerTitleChanged,
191 libvlc_MediaPlayerPositionChanged,
192 //libvlc_MediaPlayerSeekableChanged, //FIXME: doesn't work anymore? it asserts
193 libvlc_MediaPlayerPausableChanged,
195 int i_nbEvents = sizeof(eventsMediaPlayer) / sizeof(*eventsMediaPlayer);
196 for (int i = 0; i < i_nbEvents; i++) {
197 libvlc_event_attach(p_vlc_media_player_event_manager, eventsMediaPlayer[i],
198 libvlc_callback, this, vlc_exception);
199 vlcExceptionRaised();
203 // Get event manager from media descriptor object
204 p_vlc_media_event_manager = libvlc_media_event_manager(p_vlc_media);
205 libvlc_event_type_t eventsMedia[] = {
206 libvlc_MediaMetaChanged,
207 libvlc_MediaSubItemAdded,
208 libvlc_MediaDurationChanged,
209 // FIXME libvlc does not know this event
210 // libvlc_MediaPreparsedChanged,
212 libvlc_MediaStateChanged,
214 i_nbEvents = sizeof(eventsMedia) / sizeof(*eventsMedia);
215 for (int i = 0; i < i_nbEvents; i++) {
216 libvlc_event_attach(p_vlc_media_event_manager, eventsMedia[i], libvlc_callback, this, vlc_exception);
217 vlcExceptionRaised();
220 // Get event manager from media service discoverer object
221 // FIXME why libvlc_media_discoverer_event_manager() does not take a libvlc_exception_t ?
222 // p_vlc_media_discoverer_event_manager = libvlc_media_discoverer_event_manager(p_vlc_media_discoverer);
223 // libvlc_event_type_t eventsMediaDiscoverer[] = {
224 // libvlc_MediaDiscovererStarted,
225 // libvlc_MediaDiscovererEnded
227 // nbEvents = sizeof(eventsMediaDiscoverer) / sizeof(*eventsMediaDiscoverer);
228 // for (int i = 0; i < nbEvents; i++) {
229 // libvlc_event_attach(p_vlc_media_discoverer_event_manager, eventsMediaDiscoverer[i], libvlc_callback, this, vlc_exception);
233 void VLCMediaObject::libvlc_callback(const libvlc_event_t *p_event, void *p_user_data)
235 static int i_first_time_media_player_time_changed = 0;
236 static bool b_media_player_title_changed = false;
238 VLCMediaObject *p_vlc_mediaObject = (VLCMediaObject *) p_user_data;
240 // qDebug() << (int)pp_vlc_mediaObject << "event:" << libvlc_event_type_name(event->type);
242 // Media player events
243 if (p_event->type == libvlc_MediaPlayerTimeChanged) {
245 i_first_time_media_player_time_changed++;
247 // FIXME It is ugly. It should be solved by some events in libvlc
248 if (i_first_time_media_player_time_changed == 15) {
250 p_vlc_mediaObject->updateMetaData();
252 // Is this media player seekable
253 bool b_seekable = libvlc_media_player_is_seekable(p_vlc_mediaObject->p_vlc_media_player, vlc_exception);
254 vlcExceptionRaised();
255 if (b_seekable != p_vlc_mediaObject->b_seekable) {
256 qDebug() << "libvlc_callback(): isSeekable:" << b_seekable;
257 p_vlc_mediaObject->b_seekable = b_seekable;
258 emit p_vlc_mediaObject->seekableChanged(p_vlc_mediaObject->b_seekable);
261 // Get current video width
262 int i_width = libvlc_video_get_width(p_vlc_mediaObject->p_vlc_media_player, vlc_exception);
263 vlcExceptionRaised();
265 // Get current video height
266 int i_height = libvlc_video_get_height(p_vlc_mediaObject->p_vlc_media_player, vlc_exception);
267 vlcExceptionRaised();
268 emit p_vlc_mediaObject->videoWidgetSizeChanged(i_width, i_height);
270 // Does this media player have a video output
271 bool b_has_video = libvlc_media_player_has_vout(p_vlc_mediaObject->p_vlc_media_player, vlc_exception);
272 vlcExceptionRaised();
273 if (b_has_video != p_vlc_mediaObject->b_has_video) {
274 p_vlc_mediaObject->b_has_video = b_has_video;
275 emit p_vlc_mediaObject->hasVideoChanged(p_vlc_mediaObject->b_has_video);
279 // Give info about audio tracks
280 p_vlc_mediaObject->refreshAudioChannels();
281 // Give info about subtitle tracks
282 p_vlc_mediaObject->refreshSubtitles();
284 // Get movie chapter count
285 // It is not a title/chapter media if there is no chapter
286 if (libvlc_media_player_get_chapter_count(
287 p_vlc_mediaObject->p_vlc_media_player, vlc_exception) > 0) {
288 vlcExceptionRaised();
289 // Give info about title
290 // only first time, no when title changed
291 if (!b_media_player_title_changed) {
292 libvlc_track_description_t *p_info = libvlc_video_get_title_description(
293 p_vlc_mediaObject->p_vlc_media_player, vlc_exception);
294 vlcExceptionRaised();
296 p_vlc_mediaObject->titleAdded(p_info->i_id, p_info->psz_name);
297 p_info = p_info->p_next;
299 libvlc_track_description_release(p_info);
302 // Give info about chapters for actual title 0
303 if (b_media_player_title_changed)
304 p_vlc_mediaObject->refreshChapters(libvlc_media_player_get_title(
305 p_vlc_mediaObject->p_vlc_media_player, vlc_exception));
307 p_vlc_mediaObject->refreshChapters(0);
309 if (b_media_player_title_changed)
310 b_media_player_title_changed = false;
313 // Bugfix with Qt mediaplayer example
314 // Now we are in playing state
315 emit p_vlc_mediaObject->stateChanged(Phonon::PlayingState);
318 emit p_vlc_mediaObject->tickInternal(p_vlc_mediaObject->currentTime());
321 if (p_event->type == libvlc_MediaPlayerPlaying) {
322 if (p_vlc_mediaObject->state() != Phonon::LoadingState) {
323 // Bugfix with Qt mediaplayer example
324 emit p_vlc_mediaObject->stateChanged(Phonon::PlayingState);
328 if (p_event->type == libvlc_MediaPlayerPaused) {
329 emit p_vlc_mediaObject->stateChanged(Phonon::PausedState);
332 if (p_event->type == libvlc_MediaPlayerEndReached) {
333 i_first_time_media_player_time_changed = 0;
334 p_vlc_mediaObject->clearMediaController();
335 emit p_vlc_mediaObject->stateChanged(Phonon::StoppedState);
336 emit p_vlc_mediaObject->finished();
339 if (p_event->type == libvlc_MediaPlayerStopped) {
340 i_first_time_media_player_time_changed = 0;
341 p_vlc_mediaObject->clearMediaController();
342 emit p_vlc_mediaObject->stateChanged(Phonon::StoppedState);
345 if (p_event->type == libvlc_MediaPlayerTitleChanged) {
346 i_first_time_media_player_time_changed = 0;
347 b_media_player_title_changed = true;
352 if (p_event->type == libvlc_MediaDurationChanged) {
353 // Get duration of media descriptor object item
354 libvlc_time_t totalTime = libvlc_media_get_duration(p_vlc_mediaObject->p_vlc_media, vlc_exception);
355 vlcExceptionRaised();
357 if (totalTime != p_vlc_mediaObject->i_total_time) {
358 p_vlc_mediaObject->i_total_time = totalTime;
359 emit p_vlc_mediaObject->totalTimeChanged(p_vlc_mediaObject->i_total_time);
363 if (p_event->type == libvlc_MediaMetaChanged) {
367 void VLCMediaObject::updateMetaData()
369 QMultiMap<QString, QString> metaDataMap;
371 metaDataMap.insert(QLatin1String("ARTIST"),
372 QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_Artist)));
373 vlcExceptionRaised();
374 metaDataMap.insert(QLatin1String("ALBUM"),
375 QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_Album)));
376 vlcExceptionRaised();
377 metaDataMap.insert(QLatin1String("TITLE"),
378 QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_Title)));
379 vlcExceptionRaised();
380 metaDataMap.insert(QLatin1String("DATE"),
381 QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_Date)));
382 vlcExceptionRaised();
383 metaDataMap.insert(QLatin1String("GENRE"),
384 QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_Genre)));
385 vlcExceptionRaised();
386 metaDataMap.insert(QLatin1String("TRACKNUMBER"),
387 QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_TrackNumber)));
388 vlcExceptionRaised();
389 metaDataMap.insert(QLatin1String("DESCRIPTION"),
390 QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_Description)));
391 vlcExceptionRaised();
392 metaDataMap.insert(QLatin1String("COPYRIGHT"),
393 QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_TrackNumber)));
394 vlcExceptionRaised();
395 metaDataMap.insert(QLatin1String("URL"),
396 QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_URL)));
397 vlcExceptionRaised();
398 metaDataMap.insert(QLatin1String("ENCODEDBY"),
399 QString::fromUtf8(libvlc_media_get_meta(p_vlc_media, libvlc_meta_EncodedBy)));
401 qDebug() << "updateMetaData(): artist:"
402 << libvlc_media_get_meta(p_vlc_media, libvlc_meta_Artist);
403 vlcExceptionRaised();
404 qDebug() << "updateMetaData(): title:"
405 << libvlc_media_get_meta(p_vlc_media, libvlc_meta_Title);
406 vlcExceptionRaised();
408 emit metaDataChanged(metaDataMap);
411 qint64 VLCMediaObject::totalTime() const
416 qint64 VLCMediaObject::currentTimeInternal() const
418 libvlc_time_t time = libvlc_media_player_get_time(p_vlc_media_player, vlc_exception);
419 vlcExceptionRaised();
425 } // Namespace Phonon::VLC