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