1 /*
2  * vlcmediawidget.cpp
3  *
4  * Copyright (C) 2010-2012 Christoph Pfister <christophpfister@gmail.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 #include "../log.h"
22 
23 #include <QApplication>
24 #include <QCursor>
25 #include <QMouseEvent>
26 #include <QTimer>
27 #include <QMap>
28 #include <vlc/libvlc_version.h>
29 
30 #include "../configuration.h"
31 #include "vlcmediawidget.h"
32 
vlcEventName(int event)33 const char *vlcEventName(int event)
34 {
35 	switch (event) {
36 	case libvlc_MediaMetaChanged:
37 		return "MediaMetaChanged";
38 	case libvlc_MediaPlayerEncounteredError:
39 		return "MediaPlayerEncounteredError";
40 	case libvlc_MediaPlayerEndReached:
41 		return "MediaPlayerEndReached";
42 	case libvlc_MediaPlayerLengthChanged:
43 		return "MediaPlayerLengthChanged";
44 	case libvlc_MediaPlayerSeekableChanged:
45 		return "MediaPlayerSeekableChanged";
46 	case libvlc_MediaPlayerStopped:
47 		return "MediaPlayerStopped";
48 	case libvlc_MediaPlayerTimeChanged:
49 		return "MediaPlayerTimeChanged";
50 	case libvlc_MediaSubItemAdded:
51 		return "MediaSubItemAdded";
52 	case libvlc_MediaDurationChanged:
53 		return "MediaDurationChanged";
54 	case libvlc_MediaParsedChanged:
55 		return "MediaParsedChanged";
56 	case libvlc_MediaFreed:
57 		return "MediaFreed";
58 	case libvlc_MediaStateChanged:
59 		return "MediaStateChanged";
60 	case libvlc_MediaSubItemTreeAdded:
61 		return "MediaSubItemTreeAdded";
62 	case libvlc_MediaPlayerMediaChanged:
63 		return "MediaPlayerMediaChanged";
64 	case libvlc_MediaPlayerNothingSpecial:
65 		return "MediaPlayerNothingSpecial";
66 	case libvlc_MediaPlayerOpening:
67 		return "MediaPlayerOpening";
68 	case libvlc_MediaPlayerBuffering:
69 		return "MediaPlayerBuffering";
70 	case libvlc_MediaPlayerPlaying:
71 		return "MediaPlayerPlaying";
72 	case libvlc_MediaPlayerPaused:
73 		return "MediaPlayerPaused";
74 	case libvlc_MediaPlayerForward:
75 		return "MediaPlayerForward";
76 	case libvlc_MediaPlayerBackward:
77 		return "MediaPlayerBackward";
78 	case libvlc_MediaPlayerPositionChanged:
79 		return "MediaPlayerPositionChanged";
80 	case libvlc_MediaPlayerPausableChanged:
81 		return "MediaPlayerPausableChanged";
82 	case libvlc_MediaPlayerTitleChanged:
83 		return "MediaPlayerTitleChanged";
84 	case libvlc_MediaPlayerSnapshotTaken:
85 		return "MediaPlayerSnapshotTaken";
86 	case libvlc_MediaPlayerVout:
87 		return "MediaPlayerVout";
88 	case libvlc_MediaPlayerScrambledChanged:
89 		return "MediaPlayerScrambledChanged";
90 	case libvlc_MediaPlayerUncorked:
91 		return "MediaPlayerUncorked";
92 	case libvlc_MediaPlayerMuted:
93 		return "MediaPlayerMuted";
94 	case libvlc_MediaListItemAdded:
95 		return "MediaListItemAdded";
96 	case libvlc_MediaListWillAddItem:
97 		return "MediaListWillAddItem";
98 	case libvlc_MediaListItemDeleted:
99 		return "MediaListItemDeleted";
100 	case libvlc_MediaListWillDeleteItem:
101 		return "MediaListWillDeleteItem";
102 	case libvlc_MediaListViewItemAdded:
103 		return "MediaListViewItemAdded";
104 	case libvlc_MediaListViewWillAddItem:
105 		return "MediaListViewWillAddItem";
106 	case libvlc_MediaListViewItemDeleted:
107 		return "MediaListViewItemDeleted";
108 	case libvlc_MediaListViewWillDeleteItem:
109 		return "MediaListViewWillDeleteItem";
110 	case libvlc_MediaListPlayerPlayed:
111 		return "MediaListPlayerPlayed";
112 	case libvlc_MediaListPlayerNextItemSet:
113 		return "MediaListPlayerNextItemSet";
114 	case libvlc_MediaListPlayerStopped:
115 		return "MediaListPlayerStopped";
116 	case libvlc_VlmMediaAdded:
117 		return "VlmMediaAdded";
118 	case libvlc_VlmMediaRemoved:
119 		return "VlmMediaRemoved";
120 	case libvlc_VlmMediaChanged:
121 		return "VlmMediaChanged";
122 	case libvlc_VlmMediaInstanceStarted:
123 		return "VlmMediaInstanceStarted";
124 	case libvlc_VlmMediaInstanceStopped:
125 		return "VlmMediaInstanceStopped";
126 	case libvlc_VlmMediaInstanceStatusInit:
127 		return "VlmMediaInstanceStatusInit";
128 	case libvlc_VlmMediaInstanceStatusOpening:
129 		return "VlmMediaInstanceStatusOpening";
130 	case libvlc_VlmMediaInstanceStatusPlaying:
131 		return "VlmMediaInstanceStatusPlaying";
132 	case libvlc_VlmMediaInstanceStatusPause:
133 		return "VlmMediaInstanceStatusPause";
134 	case libvlc_VlmMediaInstanceStatusEnd:
135 		return "VlmMediaInstanceStatusEnd";
136 	case libvlc_VlmMediaInstanceStatusError:
137 		return "VlmMediaInstanceStatusError";
138 #if LIBVLC_VERSION_MAJOR > 2
139 	case libvlc_RendererDiscovererItemAdded:
140 		return "RendererDiscovererItemAdded";
141 	case libvlc_RendererDiscovererItemDeleted:
142 		return "RendererDiscovererItemDeleted";
143 	case libvlc_MediaPlayerAudioVolume:
144 		return "MediaPlayerAudioVolume";
145 	case libvlc_MediaPlayerAudioDevice:
146 		return "MediaPlayerAudioDevice";
147 	case libvlc_MediaListEndReached:
148 		return "MediaListEndReached";
149 	case libvlc_MediaPlayerChapterChanged:
150 		return "MediaPlayerChapterChanged";
151 	case libvlc_MediaPlayerESAdded:
152 		return "MediaPlayerESAdded";
153 	case libvlc_MediaPlayerESDeleted:
154 		return "MediaPlayerESDeleted";
155 	case libvlc_MediaPlayerESSelected:
156 		return "MediaPlayerESSelected";
157 	case libvlc_MediaPlayerCorked:
158 		return "MediaPlayerCorked";
159 	case libvlc_MediaPlayerUnmuted:
160 		return "MediaPlayerUnmuted";
161 #endif
162 
163 	default:
164 		return "Unknown";
165 	}
166 }
167 
VlcMediaWidget(QWidget * parent)168 VlcMediaWidget::VlcMediaWidget(QWidget *parent) : AbstractMediaWidget(parent),
169     timer(NULL), vlcInstance(NULL), vlcMedia(NULL), vlcMediaPlayer(NULL),
170     isPaused(false), playingDvd(false), urlIsAudioCd(false),
171     typeOfDevice(""), trackNumber(1), numTracks(1)
172 {
173 	libvlc_event_e events[] = {
174 		libvlc_MediaPlayerEncounteredError,
175 		libvlc_MediaPlayerEndReached,
176 		libvlc_MediaPlayerLengthChanged,
177 		libvlc_MediaPlayerSeekableChanged,
178 		libvlc_MediaPlayerStopped,
179 		libvlc_MediaPlayerTimeChanged,
180 #if LIBVLC_VERSION_MAJOR > 2
181 		libvlc_MediaMetaChanged,
182 		libvlc_MediaPlayerESAdded,
183 		libvlc_MediaPlayerESDeleted,
184 #endif
185 #if 0 // all other possible events
186 		libvlc_MediaSubItemAdded,
187 		libvlc_MediaDurationChanged,
188 		libvlc_MediaParsedChanged,
189 		libvlc_MediaFreed,
190 		libvlc_MediaStateChanged,
191 		libvlc_MediaSubItemTreeAdded,
192 		libvlc_MediaPlayerMediaChanged,
193 		libvlc_MediaPlayerNothingSpecial,
194 		libvlc_MediaPlayerOpening,
195 		libvlc_MediaPlayerBuffering,
196 		libvlc_MediaPlayerPlaying,
197 		libvlc_MediaPlayerPaused,
198 		libvlc_MediaPlayerForward,
199 		libvlc_MediaPlayerBackward,
200 		libvlc_MediaPlayerPositionChanged,
201 		libvlc_MediaPlayerPausableChanged,
202 		libvlc_MediaPlayerTitleChanged,
203 		libvlc_MediaPlayerSnapshotTaken,
204 		libvlc_MediaPlayerVout,
205 		libvlc_MediaPlayerScrambledChanged,
206 		libvlc_MediaPlayerUncorked,
207 		libvlc_MediaPlayerMuted,
208 		libvlc_MediaListItemAdded,
209 		libvlc_MediaListWillAddItem,
210 		libvlc_MediaListItemDeleted,
211 		libvlc_MediaListWillDeleteItem,
212 		libvlc_MediaListViewItemAdded,
213 		libvlc_MediaListViewWillAddItem,
214 		libvlc_MediaListViewItemDeleted,
215 		libvlc_MediaListViewWillDeleteItem,
216 		libvlc_MediaListPlayerPlayed,
217 		libvlc_MediaListPlayerNextItemSet,
218 		libvlc_MediaListPlayerStopped,
219 		libvlc_VlmMediaAdded,
220 		libvlc_VlmMediaRemoved,
221 		libvlc_VlmMediaChanged,
222 		libvlc_VlmMediaInstanceStarted,
223 		libvlc_VlmMediaInstanceStopped,
224 		libvlc_VlmMediaInstanceStatusInit,
225 		libvlc_VlmMediaInstanceStatusOpening,
226 		libvlc_VlmMediaInstanceStatusPlaying,
227 		libvlc_VlmMediaInstanceStatusPause,
228 		libvlc_VlmMediaInstanceStatusEnd,
229 		libvlc_VlmMediaInstanceStatusError,
230 #if LIBVLC_VERSION_MAJOR > 2
231 		libvlc_MediaListEndReached,
232 		libvlc_MediaPlayerAudioDevice,
233 		libvlc_MediaPlayerAudioVolume,
234 		libvlc_MediaPlayerChapterChanged,
235 		libvlc_MediaPlayerESSelected,
236 		libvlc_MediaPlayerCorked,
237 		libvlc_MediaPlayerUnmuted,
238 		libvlc_RendererDiscovererItemAdded,
239 		libvlc_RendererDiscovererItemDeleted,
240 #endif /*LIBVLC_VERSION_MAJOR */
241 #endif
242 	};
243 
244 	for (uint i = 0; i < (sizeof(events) / sizeof(events[0])); ++i)
245 		eventType.append(events[i]);
246 }
247 
init()248 bool VlcMediaWidget::init()
249 {
250 	QString args = Configuration::instance()->getLibVlcArguments();
251 	QStringList argList;
252 	int argc = 0, size;
253 
254 	argList = args.split(' ', QString::SkipEmptyParts);
255 	size = argList.size();
256 
257 	const char **argv = new const char *[size];
258 	QVector<QByteArray> str(size);
259 
260 	for (int i = 0; i < size; i++) {
261 		str[i] = argList.at(i).toUtf8();
262 		argv[argc++] = str[i];
263 	}
264 
265 	vlcInstance = libvlc_new(argc, argv);
266 	if (!vlcInstance) {
267 		qCWarning(logMediaWidget, "libVLC: failed to use extra args: %s", qPrintable(args));
268 		argc = 0;
269 		vlcInstance = libvlc_new(0, NULL);
270 		if (vlcInstance)
271 			qCInfo(logMediaWidget, "Using libVLC without arguments");
272 	}
273 
274 	if (vlcInstance == NULL) {
275 		qFatal("Cannot create vlc instance %s", qPrintable(libvlc_errmsg()));
276 		delete[] argv;
277 		return false;
278 	}
279 
280 	if (argc) {
281 		QString log = "Using libVLC with args:";
282 		for (int i = 0; i < argc; i++)
283 			log += ' ' + QLatin1String(argv[i]);
284 
285 		qCDebug(logVlc, "%s", qPrintable(log));
286 	}
287 	delete[] argv;
288 
289 	vlcMediaPlayer = libvlc_media_player_new(vlcInstance);
290 
291 	if (vlcMediaPlayer == NULL) {
292 		qFatal("Cannot create vlc media player %s", qPrintable(libvlc_errmsg()));
293 		return false;
294 	}
295 
296 	eventManager = libvlc_media_player_event_manager(vlcMediaPlayer);
297 
298 	if (!registerEvents())
299 		return false;
300 
301 	timer = new QTimer();
302 	connect(timer, SIGNAL(timeout()), this, SLOT(hideMouse()));
303 
304 	libvlc_media_player_set_xwindow(vlcMediaPlayer, quint32(winId()));
305 	setAttribute(Qt::WA_NativeWindow);
306 
307 	libvlc_audio_set_mute(vlcMediaPlayer, false);
308 
309 	// This is broken on qt5: the kernel/qwidget.cpp tries to repaint
310 	// on a wrong place, causing this warning:
311 	//	QWidget::paintEngine: Should no longer be called
312 //	setAttribute(Qt::WA_PaintOnScreen);
313 	return true;
314 }
315 
~VlcMediaWidget()316 VlcMediaWidget::~VlcMediaWidget()
317 {
318 	metadata.clear();
319 	audioStreams.clear();
320 	subtitles.clear();
321 	subtitleId.clear();
322 
323 	if (timer != NULL) {
324 		timer->stop();
325 		delete timer;
326 	}
327 
328 	unregisterEvents();
329 
330 	if (vlcMedia != NULL)
331 		libvlc_media_release(vlcMedia);
332 
333 	if (vlcInstance != NULL)
334 		libvlc_release(vlcInstance);
335 
336 	if (vlcMediaPlayer != NULL)
337 		libvlc_media_player_release(vlcMediaPlayer);
338 }
339 
createVlcMediaWidget(QWidget * parent)340 VlcMediaWidget *VlcMediaWidget::createVlcMediaWidget(QWidget *parent)
341 {
342 	QScopedPointer<VlcMediaWidget> vlcMediaWidget(new VlcMediaWidget(parent));
343 
344 	if (!vlcMediaWidget->init()) {
345 		return NULL;
346 	}
347 
348 	return vlcMediaWidget.take();
349 }
350 
getAudioDevices()351 QStringList VlcMediaWidget::getAudioDevices()
352 {
353 	libvlc_audio_output_device_t *vlcAudioOutput, *i;
354 	QStringList audioDevices;
355 
356 	// Get audio device list
357 	vlcAudioOutput = libvlc_audio_output_device_enum(vlcMediaPlayer);
358 	if (!vlcAudioOutput)
359 		return audioDevices;
360 
361 	for (i = vlcAudioOutput; i != NULL; i = i->p_next) {
362 		QString device = QString::fromUtf8(i->psz_description);
363 		audioDevices.append(device);
364 	}
365 	libvlc_audio_output_device_list_release(vlcAudioOutput);
366 
367 	return audioDevices;
368 }
369 
setAudioDevice(QString device)370 void VlcMediaWidget::setAudioDevice(QString device)
371 {
372 	libvlc_audio_output_device_t *vlcAudioOutput, *i;
373 	vlcAudioOutput = libvlc_audio_output_device_enum(vlcMediaPlayer);
374 
375 	if (!vlcAudioOutput)
376 		return;
377 
378 	for (i = vlcAudioOutput; i != NULL; i = i->p_next) {
379 		if (device.compare(QString::fromUtf8(i->psz_description)))
380 			continue;
381 		qCDebug(logVlc, "Setting audio output to: %s", qPrintable(i->psz_device));
382 
383 		libvlc_audio_output_device_set(vlcMediaPlayer, NULL, i->psz_device);
384 	}
385 	libvlc_audio_output_device_list_release(vlcAudioOutput);
386 }
387 
setMuted(bool muted)388 void VlcMediaWidget::setMuted(bool muted)
389 {
390 	libvlc_audio_set_mute(vlcMediaPlayer, muted);
391 }
392 
setVolume(int volume)393 void VlcMediaWidget::setVolume(int volume)
394 {
395 	// 0 <= volume <= 200
396 	if (libvlc_audio_set_volume(vlcMediaPlayer, volume) != 0) {
397 		qCWarning(logMediaWidget, "cannot set volume %i", volume);
398 	}
399 }
400 
setAspectRatio(MediaWidget::AspectRatio aspectRatio)401 void VlcMediaWidget::setAspectRatio(MediaWidget::AspectRatio aspectRatio)
402 {
403 	const char *vlcAspectRatio = "";
404 
405 	switch (aspectRatio) {
406 	case MediaWidget::AspectRatioAuto:
407 		break;
408 	case MediaWidget::AspectRatio1_1:
409 		vlcAspectRatio = "1:1";
410 		break;
411 	case MediaWidget::AspectRatio4_3:
412 		vlcAspectRatio = "4:3";
413 		break;
414 	case MediaWidget::AspectRatio5_4:
415 		vlcAspectRatio = "5:4";
416 		break;
417 	case MediaWidget::AspectRatio16_9:
418 		vlcAspectRatio = "16:9";
419 		break;
420 	case MediaWidget::AspectRatio16_10:
421 		vlcAspectRatio = "16:10";
422 		break;
423 	case MediaWidget::AspectRatio221_100:
424 		vlcAspectRatio = "221:100";
425 		break;
426 	case MediaWidget::AspectRatio235_100:
427 		vlcAspectRatio = "235:100";
428 		break;
429 	case MediaWidget::AspectRatio239_100:
430 		vlcAspectRatio = "239:100";
431 		break;
432 	}
433 
434 	libvlc_video_set_aspect_ratio(vlcMediaPlayer, vlcAspectRatio);
435 }
436 
resizeToVideo(float resizeFactor)437 void VlcMediaWidget::resizeToVideo(float resizeFactor)
438 {
439 	qCDebug(logMediaWidget, "video resized to %.1f", resizeFactor);
440 	libvlc_video_set_scale(vlcMediaPlayer, resizeFactor);
441 }
442 
setDeinterlacing(MediaWidget::DeinterlaceMode deinterlacing)443 void VlcMediaWidget::setDeinterlacing(MediaWidget::DeinterlaceMode deinterlacing)
444 {
445 	const char *vlcDeinterlaceMode;
446 
447 	switch (deinterlacing) {
448 	case MediaWidget::DeinterlaceDiscard:
449 		vlcDeinterlaceMode = "discard";
450 		break;
451 	case MediaWidget::DeinterlaceBob:
452 		vlcDeinterlaceMode = "bob";
453 		break;
454 	case MediaWidget::DeinterlaceLinear:
455 		vlcDeinterlaceMode = "linear";
456 		break;
457 	case MediaWidget::DeinterlaceYadif:
458 		vlcDeinterlaceMode = "yadif";
459 		break;
460 	case MediaWidget::DeinterlaceYadif2x:
461 		vlcDeinterlaceMode = "yadif2x";
462 		break;
463 	case MediaWidget::DeinterlacePhosphor:
464 		vlcDeinterlaceMode = "phosphor";
465 		break;
466 	case MediaWidget::DeinterlaceX:
467 		vlcDeinterlaceMode = "x";
468 		break;
469 	case MediaWidget::DeinterlaceMean:
470 		vlcDeinterlaceMode = "mean";
471 		break;
472 	case MediaWidget::DeinterlaceBlend:
473 		vlcDeinterlaceMode = "blend";
474 		break;
475 	case MediaWidget::DeinterlaceIvtc:
476 		vlcDeinterlaceMode = "ivtc";
477 		break;
478 	case MediaWidget::DeinterlaceDisabled:
479 	default:
480 		vlcDeinterlaceMode = NULL;
481 	}
482 
483 
484 	libvlc_video_set_deinterlace(vlcMediaPlayer, vlcDeinterlaceMode);
485 }
486 
play(const MediaSource & source)487 void VlcMediaWidget::play(const MediaSource &source)
488 {
489 	addPendingUpdates(PlaybackStatus | DvdMenu);
490 	QByteArray url = source.getUrl().toEncoded();
491 	playingDvd = false;
492 
493 	trackNumber = 1;
494 	urlIsAudioCd = false;
495 
496 	switch (source.getType()) {
497 	case MediaSource::Url:
498 		if (url.endsWith(".iso")) {
499 			playingDvd = true;
500 		}
501 
502 		break;
503 	case MediaSource::AudioCd:
504 		urlIsAudioCd = true;
505 
506 		if (url.size() >= 7) {
507 			url.replace(0, 4, "cdda");
508 		} else {
509 			url = "cdda://";
510 		}
511 
512 		break;
513 	case MediaSource::VideoCd:
514 		if (url.size() >= 7) {
515 			url.replace(0, 4, "vcd");
516 		} else {
517 			url = "vcd://";
518 		}
519 
520 		break;
521 	case MediaSource::Dvd:
522 		if (url.size() >= 7) {
523 			url.replace(0, 4, "dvd");
524 		} else {
525 			url = "dvd://";
526 		}
527 
528 		playingDvd = true;
529 		break;
530 	case MediaSource::Dvb:
531 		break;
532 	}
533 
534 	typeOfDevice = url.constData();
535 
536 	if (vlcMedia != NULL) {
537 		libvlc_media_player_stop(vlcMediaPlayer);
538 		libvlc_media_release(vlcMedia);
539 	}
540 
541 	vlcMedia = libvlc_media_new_location(vlcInstance, typeOfDevice);
542 	if (urlIsAudioCd)
543 		libvlc_media_add_option(vlcMedia, "cdda-track=1");
544 
545 	if (makePlay() < 0) {
546 		stop();
547 		return;
548 	}
549 
550 	setCursor(Qt::BlankCursor);
551 	setCursor(Qt::ArrowCursor);
552 	timer->start(1000);
553 	setMouseTracking(true);
554 }
555 
unregisterEvents()556 void VlcMediaWidget::unregisterEvents()
557 {
558 	for (int i = 0; i < eventType.size(); ++i)
559 		libvlc_event_detach(eventManager, eventType.at(i),
560 				    vlcEventHandler, this);
561 #if LIBVLC_VERSION_MAJOR <= 2
562 	if (!vlcMedia)
563 		return;
564 	libvlc_event_manager_t *mediaEvent = libvlc_media_event_manager(vlcMedia);
565 	libvlc_event_detach(mediaEvent, libvlc_MediaMetaChanged,
566 			    vlcEventHandler, this);
567 #endif
568 }
569 
registerEvents()570 bool VlcMediaWidget::registerEvents()
571 {
572 	for (int i = 0; i < eventType.size(); ++i) {
573 		if (libvlc_event_attach(eventManager, eventType.at(i), vlcEventHandler, this) != 0) {
574 			qCCritical(logMediaWidget, "Cannot attach event handler %d", eventType.at(i));
575 			return false;
576 		}
577 	}
578 	return true;
579 }
580 
makePlay()581 int VlcMediaWidget::makePlay()
582 {
583 	if (vlcMedia == NULL) {
584 		libvlc_media_player_stop(vlcMediaPlayer);
585 		return -1;
586 	}
587 
588 #if LIBVLC_VERSION_MAJOR <= 2
589 	/*
590 	 * There is a difference between libVlc 2.x and 3.x:
591 	 * With version 2.x, the event needs to be registered at the
592 	 * vlcMedia object, just before calling libvlc_media_player_set_media()
593 	 *
594 	 * On version 3.x, while this still works, you can simply register the
595 	 * event directly at vlcMediaPlayer, together with all other events,
596 	 * with simplifies the code.
597 	 */
598 	libvlc_event_manager_t *mediaEvent = libvlc_media_event_manager(vlcMedia);
599 
600 	if (libvlc_event_attach(mediaEvent, libvlc_MediaMetaChanged,
601 				vlcEventHandler, this) != 0) {
602 		qCWarning(logMediaWidget, "Cannot attach event handler %d",
603 			  libvlc_MediaMetaChanged);
604 	}
605 #endif
606 
607 	libvlc_media_player_set_media(vlcMediaPlayer, vlcMedia);
608 
609 	/*
610 	 * FIXME: This is mostly a boilerplate as, at least with vlc 3,
611 	 * this function always return -1
612          */
613 	if (urlIsAudioCd)
614 		numTracks = libvlc_audio_get_track_count(vlcMediaPlayer);
615 
616 	return libvlc_media_player_play(vlcMediaPlayer);
617 }
618 
playDirection(int direction)619 void VlcMediaWidget::playDirection(int direction)
620 {
621 	QString strBuf = "cdda-track=";
622 	libvlc_state_t state;
623 	int oldTrackNumber = trackNumber;
624 
625 	if (direction == -1)
626 		trackNumber--;
627 	else
628 		trackNumber++;
629 
630 	if (numTracks > 0 && trackNumber > numTracks)
631 		trackNumber = numTracks;
632 	if (trackNumber < 1)
633 		trackNumber = 1;
634 
635 	strBuf += QString::number(trackNumber);
636 
637 	if (vlcMedia != NULL) {
638 		libvlc_media_player_stop(vlcMediaPlayer);
639 		libvlc_media_release(vlcMedia);
640 	}
641 
642 	vlcMedia = libvlc_media_new_location(vlcInstance, typeOfDevice);
643 	libvlc_media_add_option(vlcMedia, strBuf.toUtf8());
644 
645 	if (makePlay() < 0)
646 		stop();
647 
648 	do {
649 		state = libvlc_media_player_get_state (vlcMediaPlayer);
650 	} while(state != libvlc_Playing &&
651 		state != libvlc_Error &&
652 		state != libvlc_Ended );
653 
654 	if (state != libvlc_Playing) {
655 		stop();
656 		trackNumber = oldTrackNumber;
657 	}
658 }
659 
stop()660 void VlcMediaWidget::stop()
661 {
662 	libvlc_media_player_stop(vlcMediaPlayer);
663 
664 	if (vlcMedia != NULL) {
665 		libvlc_media_release(vlcMedia);
666 		vlcMedia = NULL;
667 	}
668 
669 	timer->stop();
670 	setCursor(Qt::BlankCursor);
671 	setCursor(Qt::ArrowCursor);
672 	addPendingUpdates(PlaybackStatus | Metadata |
673 			  Subtitles | AudioStreams);
674 }
675 
setPaused(bool paused)676 void VlcMediaWidget::setPaused(bool paused)
677 {
678 	isPaused = paused;
679 	libvlc_media_player_set_pause(vlcMediaPlayer, paused);
680 	// we don't monitor playing / buffering / paused state changes
681 	addPendingUpdates(PlaybackStatus);
682 }
683 
seek(int time)684 void VlcMediaWidget::seek(int time)
685 {
686 	if (!seekable)
687 		return;
688 
689 	libvlc_media_player_set_time(vlcMediaPlayer, time);
690 }
691 
setCurrentAudioStream(int _currentAudioStream)692 void VlcMediaWidget::setCurrentAudioStream(int _currentAudioStream)
693 {
694 	if (_currentAudioStream < 0 ||
695 	    _currentAudioStream > libvlc_audio_get_track(vlcMediaPlayer))
696 		_currentAudioStream = 0;
697 	// skip the 'deactivate' audio channel
698 	libvlc_audio_set_track(vlcMediaPlayer, _currentAudioStream + 1);
699 
700 	currentAudioStream = _currentAudioStream;
701 }
702 
setCurrentSubtitle(int currentSubtitle)703 void VlcMediaWidget::setCurrentSubtitle(int currentSubtitle)
704 {
705 	libvlc_track_description_t *tr, *track;
706 	int requestedSubtitle = -1;
707 
708 	QMap<int, int>::const_iterator i = subtitleId.constBegin();
709 	while (i != subtitleId.constEnd()) {
710 		qCDebug(logVlc, "Subtitle #%d, key: %d", i.value(), i.key());
711 		if (i.value() == currentSubtitle) {
712 			requestedSubtitle = i.key();
713 			break;
714 		}
715 		i++;
716 	}
717 
718 	qCDebug(logVlc, "Try to set subtitle #%d, id %d", currentSubtitle, requestedSubtitle);
719 	libvlc_video_set_spu(vlcMediaPlayer, requestedSubtitle);
720 
721 	/* Print what it was actually selected */
722 
723 	tr = libvlc_video_get_spu_description(vlcMediaPlayer);
724 	track = tr;
725 	while (track != NULL) {
726 		QString subtitle = QString::fromUtf8(track->psz_name);
727 
728 		if (subtitle.isEmpty()) {
729 			subtitle = i18n("Subtitle %1", track->i_id);
730 		}
731 
732 		if (track->i_id == requestedSubtitle)
733 			qCDebug(logVlc, "Subtitle set to id %d: %s", track->i_id, qPrintable(subtitle));
734 		track = track->p_next;
735 	}
736 	libvlc_track_description_list_release(tr);
737 
738 }
739 
setExternalSubtitle(const QUrl & url)740 void VlcMediaWidget::setExternalSubtitle(const QUrl &url)
741 {
742 	QString fname = url.toLocalFile();
743 
744 #if LIBVLC_VERSION_MAJOR > 2
745 	if (libvlc_media_player_add_slave(vlcMediaPlayer,
746 					  libvlc_media_slave_type_subtitle,
747 					  url.toEncoded().constData(),
748 					  true) == 0)
749 		qCWarning(logMediaWidget, "Cannot set subtitle file %s", qPrintable(fname));
750 #else
751 	if (libvlc_video_set_subtitle_file(vlcMediaPlayer,
752 					   fname.toLocal8Bit().constData()) == 0)
753 		qCWarning(logMediaWidget, "Cannot set subtitle file %s", qPrintable(fname));
754 #endif
755 }
756 
setCurrentTitle(int currentTitle)757 void VlcMediaWidget::setCurrentTitle(int currentTitle)
758 {
759 	libvlc_media_player_set_title(vlcMediaPlayer, currentTitle);
760 }
761 
setCurrentChapter(int currentChapter)762 void VlcMediaWidget::setCurrentChapter(int currentChapter)
763 {
764 	libvlc_media_player_set_chapter(vlcMediaPlayer, currentChapter);
765 }
766 
setCurrentAngle(int currentAngle)767 void VlcMediaWidget::setCurrentAngle(int currentAngle)
768 {
769 	Q_UNUSED(currentAngle)
770 	// FIXME
771 }
772 
jumpToPreviousChapter()773 bool VlcMediaWidget::jumpToPreviousChapter()
774 {
775 	int currentTitle = libvlc_media_player_get_title(vlcMediaPlayer);
776 	int currentChapter = libvlc_media_player_get_chapter(vlcMediaPlayer);
777 	if (urlIsAudioCd)
778 		playDirection(-1);
779 	else
780 		libvlc_media_player_previous_chapter(vlcMediaPlayer);
781 
782 	if ((libvlc_media_player_get_title(vlcMediaPlayer) != currentTitle) ||
783 	    (libvlc_media_player_get_chapter(vlcMediaPlayer) != currentChapter)) {
784 		return true;
785 	}
786 
787 	return false;
788 }
789 
jumpToNextChapter()790 bool VlcMediaWidget::jumpToNextChapter()
791 {
792 	int currentTitle = libvlc_media_player_get_title(vlcMediaPlayer);
793 	int currentChapter = libvlc_media_player_get_chapter(vlcMediaPlayer);
794 	if (urlIsAudioCd)
795 		playDirection(1);
796 	else
797 		libvlc_media_player_next_chapter(vlcMediaPlayer);
798 
799 	if ((libvlc_media_player_get_title(vlcMediaPlayer) != currentTitle) ||
800 	    (libvlc_media_player_get_chapter(vlcMediaPlayer) != currentChapter)) {
801 		return true;
802 	}
803 
804 	return false;
805 }
806 
showDvdMenu()807 void VlcMediaWidget::showDvdMenu()
808 {
809 	if (playingDvd) {
810 		libvlc_media_player_set_title(vlcMediaPlayer, 0);
811 	}
812 }
813 
updatePlaybackStatus()814 int VlcMediaWidget::updatePlaybackStatus()
815 {
816 	MediaWidget::PlaybackStatus oldPlaybackStatus = playbackStatus;
817 
818 	switch (libvlc_media_player_get_state(vlcMediaPlayer)) {
819 	case libvlc_NothingSpecial:
820 	case libvlc_Stopped:
821 		playbackStatus = MediaWidget::Idle;
822 		break;
823 	case libvlc_Opening:
824 	case libvlc_Buffering:
825 		playbackStatus = MediaWidget::Playing;
826 		break;
827 	case libvlc_Playing:
828 		// The first time libVLC is set to pause, it reports status as playing
829 		if (isPaused)
830 			playbackStatus = MediaWidget::Paused;
831 		else
832 			playbackStatus = MediaWidget::Playing;
833 		break;
834 	case libvlc_Paused:
835 		playbackStatus = MediaWidget::Paused;
836 		break;
837 	case libvlc_Ended:
838 		playDirection(1);
839 		break;
840 	case libvlc_Error:
841 		playbackStatus = MediaWidget::Idle;
842 		// don't keep last picture shown
843 		libvlc_media_player_stop(vlcMediaPlayer);
844 		break;
845 	}
846 
847 	if (playbackStatus == MediaWidget::Idle) {
848 		addPendingUpdates(DvdMenu);
849 		playingDvd = false;
850 	}
851 
852 	// Report if the status has changed
853 	return (oldPlaybackStatus != playbackStatus);
854 }
855 
updateCurrentTotalTime()856 void VlcMediaWidget::updateCurrentTotalTime()
857 {
858 	if (playbackStatus == MediaWidget::Idle)
859 		return;
860 
861 	currentTime = int(libvlc_media_player_get_time(vlcMediaPlayer));
862 	totalTime = int(libvlc_media_player_get_length(vlcMediaPlayer));
863 
864 	if (currentTime < 0) {
865 		currentTime = 0;
866 	}
867 
868 	if (totalTime < 0) {
869 		totalTime = 0;
870 	}
871 
872 	if (totalTime && currentTime > totalTime) {
873 		currentTime = totalTime;
874 	}
875 }
876 
updateSeekable()877 void VlcMediaWidget::updateSeekable()
878 {
879 	seekable = libvlc_media_player_is_seekable(vlcMediaPlayer);
880 }
881 
updateMetadata()882 void VlcMediaWidget::updateMetadata()
883 {
884 	char *meta;
885 
886 	metadata.clear();
887 	if (vlcMedia != NULL) {
888 		QString s;
889 		meta = libvlc_media_get_meta(vlcMedia, libvlc_meta_Title);
890 		if (meta) {
891 			metadata.insert(MediaWidget::Title, QString::fromUtf8(meta));
892 			free(meta);
893 		}
894 		meta = libvlc_media_get_meta(vlcMedia, libvlc_meta_Artist);
895 		if (meta) {
896 			metadata.insert(MediaWidget::Artist, QString::fromUtf8(meta));
897 			free(meta);
898 		}
899 		meta = libvlc_media_get_meta(vlcMedia, libvlc_meta_Album);
900 		if (meta) {
901 			metadata.insert(MediaWidget::Album, QString::fromUtf8(meta));
902 			free(meta);
903 		}
904 		meta = libvlc_media_get_meta(vlcMedia, libvlc_meta_TrackNumber);
905 		if (meta) {
906 			metadata.insert(MediaWidget::TrackNumber, QString::fromUtf8(meta));
907 			free(meta);
908 		}
909 	}
910 
911 	if (urlIsAudioCd)
912 		metadata.insert(MediaWidget::Title,
913 				i18n("CD track ") + QString::number(trackNumber));
914 }
915 
updateAudioStreams()916 void VlcMediaWidget::updateAudioStreams()
917 {
918 	audioStreams.clear();
919 	libvlc_track_description_t *tr, *track;
920 
921 	tr = libvlc_audio_get_track_description(vlcMediaPlayer);
922 	track = tr;
923 
924 	if (track != NULL) {
925 		// skip the 'deactivate' audio channel
926 		track = track->p_next;
927 	}
928 
929 	while (track != NULL) {
930 		QString audioStream = QString::fromUtf8(track->psz_name);
931 		int cutBegin = (audioStream.indexOf(QLatin1Char('[')) + 1);
932 
933 		if (cutBegin > 0) {
934 			int cutEnd = audioStream.lastIndexOf(QLatin1Char(']'));
935 
936 			if (cutEnd >= 0) {
937 				// remove unnecessary text
938 				audioStream = audioStream.mid(cutBegin, cutEnd - cutBegin);
939 			}
940 		}
941 
942 		if (audioStream.isEmpty()) {
943 			audioStream = QString::number(audioStreams.size() + 1);
944 		}
945 
946 		audioStreams.append(audioStream);
947 		track = track->p_next;
948 	}
949 	libvlc_track_description_list_release(tr);
950 
951 	// skip the 'deactivate' audio channel
952 	currentAudioStream = (libvlc_audio_get_track(vlcMediaPlayer) - 1);
953 	if (currentAudioStream < 0)
954 		setCurrentAudioStream(0);
955 }
956 
updateSubtitles()957 void VlcMediaWidget::updateSubtitles()
958 {
959 	libvlc_track_description_t *tr, *track;
960 	int i = 0;
961 
962 	subtitles.clear();
963 	subtitleId.clear();
964 
965 	tr = libvlc_video_get_spu_description(vlcMediaPlayer);
966 	track = tr;
967 
968 	if (track != NULL) {
969 		// skip the 'deactivate' subtitle
970 		track = track->p_next;
971 	}
972 
973 	while (track != NULL) {
974 		QString subtitle = QString::fromUtf8(track->psz_name);
975 
976 		if (subtitle.isEmpty()) {
977 			subtitle = i18n("Subtitle %1", track->i_id);
978 		}
979 
980 		// 0 is reserved for "disabled" at mediawidget. So, we should
981 		// Start counting from 1, to match the range expected for
982 		// currentSubtitle
983 		subtitleId[track->i_id] = ++i;
984 		subtitles.append(subtitle);
985 		qCDebug(logVlc, "Got subtitle id#%d: %s", track->i_id, qPrintable(subtitle));
986 		track = track->p_next;
987 	}
988 	libvlc_track_description_list_release(tr);
989 
990 	// skip the 'deactivate' subtitle
991 	currentSubtitle = subtitleId.value(libvlc_video_get_spu(vlcMediaPlayer), -1);
992 }
993 
updateTitles()994 void VlcMediaWidget::updateTitles()
995 {
996 	titleCount = libvlc_media_player_get_title_count(vlcMediaPlayer);
997 	currentTitle = libvlc_media_player_get_title(vlcMediaPlayer);
998 }
999 
updateChapters()1000 void VlcMediaWidget::updateChapters()
1001 {
1002 	chapterCount = libvlc_media_player_get_chapter_count(vlcMediaPlayer);
1003 	currentChapter = libvlc_media_player_get_chapter(vlcMediaPlayer);
1004 }
1005 
updateAngles()1006 void VlcMediaWidget::updateAngles()
1007 {
1008 	// FIXME
1009 }
1010 
updateDvdMenu()1011 void VlcMediaWidget::updateDvdMenu()
1012 {
1013 	dvdMenu = playingDvd;
1014 }
1015 
updateVideoSize()1016 void VlcMediaWidget::updateVideoSize()
1017 {
1018 	// FIXME
1019 }
1020 
dvdNavigate(int key)1021 void VlcMediaWidget::dvdNavigate(int key)
1022 {
1023 	int event;
1024 
1025 	switch (key){
1026 	case Qt::Key_Return:
1027 		event = libvlc_navigate_activate;
1028 		break;
1029 	case Qt::Key_Up:
1030 		event = libvlc_navigate_up;
1031 		break;
1032 	case Qt::Key_Down:
1033 		event = libvlc_navigate_down;
1034 		break;
1035 	case Qt::Key_Left:
1036 		event = libvlc_navigate_left;
1037 		break;
1038 	case Qt::Key_Right:
1039 		event = libvlc_navigate_right;
1040 		break;
1041 	default:
1042 		return;
1043 	}
1044 	libvlc_media_player_navigate(vlcMediaPlayer, event);
1045 }
1046 
mousePressEvent(QMouseEvent * event)1047 void VlcMediaWidget::mousePressEvent(QMouseEvent *event)
1048 {
1049 	AbstractMediaWidget::mousePressEvent(event);
1050 }
1051 
hideMouse()1052 void VlcMediaWidget::hideMouse()
1053 {
1054 	timer->stop();
1055 
1056 	setCursor(Qt::ArrowCursor);
1057 	setCursor(Qt::BlankCursor);
1058 }
1059 
mouseMoveEvent(QMouseEvent * event)1060 void VlcMediaWidget::mouseMoveEvent(QMouseEvent *event)
1061 {
1062 	if (!timer->isActive()) {
1063 		setCursor(Qt::BlankCursor);
1064 		setCursor(Qt::ArrowCursor);
1065 	}
1066 	if (this->underMouse())
1067 		timer->start(1000);
1068 	else
1069 		timer->stop();
1070 
1071 	AbstractMediaWidget::mouseMoveEvent(event);
1072 }
1073 
vlcEvent(const libvlc_event_t * event)1074 void VlcMediaWidget::vlcEvent(const libvlc_event_t *event)
1075 {
1076 	PendingUpdates pendingUpdatesToBeAdded = 0;
1077 
1078 	switch (event->type) {
1079 #if LIBVLC_VERSION_MAJOR > 2
1080 	case libvlc_MediaPlayerESAdded:
1081 	case libvlc_MediaPlayerESDeleted:
1082 #endif
1083 	case libvlc_MediaMetaChanged:
1084 		pendingUpdatesToBeAdded = Metadata | Subtitles | AudioStreams;
1085 		break;
1086 	case libvlc_MediaPlayerEncounteredError:
1087 		pendingUpdatesToBeAdded = PlaybackStatus;
1088 		break;
1089 	case libvlc_MediaPlayerEndReached:
1090 		pendingUpdatesToBeAdded = (PlaybackFinished | PlaybackStatus);
1091 		break;
1092 	case libvlc_MediaPlayerLengthChanged:
1093 		pendingUpdatesToBeAdded = CurrentTotalTime;
1094 		break;
1095 	case libvlc_MediaPlayerSeekableChanged:
1096 		pendingUpdatesToBeAdded = Seekable | Subtitles;
1097 		break;
1098 	case libvlc_MediaPlayerStopped:
1099 		pendingUpdatesToBeAdded = PlaybackStatus;
1100 		setMouseTracking(false);
1101 		break;
1102 	case libvlc_MediaPlayerTimeChanged:
1103 		pendingUpdatesToBeAdded = CurrentTotalTime;
1104 		break;
1105 	}
1106 
1107 	if (pendingUpdatesToBeAdded != 0) {
1108 		addPendingUpdates(pendingUpdatesToBeAdded);
1109 	} else {
1110 		qCWarning(logMediaWidget, "Unhandled event %s (%d)",
1111 			  vlcEventName(event->type), event->type);
1112 	}
1113 }
1114 
vlcEventHandler(const libvlc_event_t * event,void * instance)1115 void VlcMediaWidget::vlcEventHandler(const libvlc_event_t *event, void *instance)
1116 {
1117 	reinterpret_cast<VlcMediaWidget *>(instance)->vlcEvent(event);
1118 }
1119