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