File indexing completed on 2024-05-19 04:53:10

0001 /*
0002  * vlcmediawidget.cpp
0003  *
0004  * Copyright (C) 2010-2012 Christoph Pfister <christophpfister@gmail.com>
0005  *
0006  * This program is free software; you can redistribute it and/or modify
0007  * it under the terms of the GNU General Public License as published by
0008  * the Free Software Foundation; either version 2 of the License, or
0009  * (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License along
0017  * with this program; if not, write to the Free Software Foundation, Inc.,
0018  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
0019  */
0020 
0021 #include "../log.h"
0022 
0023 #include <QApplication>
0024 #include <QCursor>
0025 #include <QMouseEvent>
0026 #include <QTimer>
0027 #include <QMap>
0028 #include <vlc/libvlc_version.h>
0029 
0030 #include "../configuration.h"
0031 #include "vlcmediawidget.h"
0032 
0033 const char *vlcEventName(int event)
0034 {
0035     switch (event) {
0036     case libvlc_MediaMetaChanged:
0037         return "MediaMetaChanged";
0038     case libvlc_MediaPlayerEncounteredError:
0039         return "MediaPlayerEncounteredError";
0040     case libvlc_MediaPlayerEndReached:
0041         return "MediaPlayerEndReached";
0042     case libvlc_MediaPlayerLengthChanged:
0043         return "MediaPlayerLengthChanged";
0044     case libvlc_MediaPlayerSeekableChanged:
0045         return "MediaPlayerSeekableChanged";
0046     case libvlc_MediaPlayerStopped:
0047         return "MediaPlayerStopped";
0048     case libvlc_MediaPlayerTimeChanged:
0049         return "MediaPlayerTimeChanged";
0050     case libvlc_MediaSubItemAdded:
0051         return "MediaSubItemAdded";
0052     case libvlc_MediaDurationChanged:
0053         return "MediaDurationChanged";
0054     case libvlc_MediaParsedChanged:
0055         return "MediaParsedChanged";
0056     case libvlc_MediaFreed:
0057         return "MediaFreed";
0058     case libvlc_MediaStateChanged:
0059         return "MediaStateChanged";
0060     case libvlc_MediaSubItemTreeAdded:
0061         return "MediaSubItemTreeAdded";
0062     case libvlc_MediaPlayerMediaChanged:
0063         return "MediaPlayerMediaChanged";
0064     case libvlc_MediaPlayerNothingSpecial:
0065         return "MediaPlayerNothingSpecial";
0066     case libvlc_MediaPlayerOpening:
0067         return "MediaPlayerOpening";
0068     case libvlc_MediaPlayerBuffering:
0069         return "MediaPlayerBuffering";
0070     case libvlc_MediaPlayerPlaying:
0071         return "MediaPlayerPlaying";
0072     case libvlc_MediaPlayerPaused:
0073         return "MediaPlayerPaused";
0074     case libvlc_MediaPlayerForward:
0075         return "MediaPlayerForward";
0076     case libvlc_MediaPlayerBackward:
0077         return "MediaPlayerBackward";
0078     case libvlc_MediaPlayerPositionChanged:
0079         return "MediaPlayerPositionChanged";
0080     case libvlc_MediaPlayerPausableChanged:
0081         return "MediaPlayerPausableChanged";
0082     case libvlc_MediaPlayerTitleChanged:
0083         return "MediaPlayerTitleChanged";
0084     case libvlc_MediaPlayerSnapshotTaken:
0085         return "MediaPlayerSnapshotTaken";
0086     case libvlc_MediaPlayerVout:
0087         return "MediaPlayerVout";
0088     case libvlc_MediaPlayerScrambledChanged:
0089         return "MediaPlayerScrambledChanged";
0090     case libvlc_MediaPlayerUncorked:
0091         return "MediaPlayerUncorked";
0092     case libvlc_MediaPlayerMuted:
0093         return "MediaPlayerMuted";
0094     case libvlc_MediaListItemAdded:
0095         return "MediaListItemAdded";
0096     case libvlc_MediaListWillAddItem:
0097         return "MediaListWillAddItem";
0098     case libvlc_MediaListItemDeleted:
0099         return "MediaListItemDeleted";
0100     case libvlc_MediaListWillDeleteItem:
0101         return "MediaListWillDeleteItem";
0102     case libvlc_MediaListViewItemAdded:
0103         return "MediaListViewItemAdded";
0104     case libvlc_MediaListViewWillAddItem:
0105         return "MediaListViewWillAddItem";
0106     case libvlc_MediaListViewItemDeleted:
0107         return "MediaListViewItemDeleted";
0108     case libvlc_MediaListViewWillDeleteItem:
0109         return "MediaListViewWillDeleteItem";
0110     case libvlc_MediaListPlayerPlayed:
0111         return "MediaListPlayerPlayed";
0112     case libvlc_MediaListPlayerNextItemSet:
0113         return "MediaListPlayerNextItemSet";
0114     case libvlc_MediaListPlayerStopped:
0115         return "MediaListPlayerStopped";
0116     case libvlc_VlmMediaAdded:
0117         return "VlmMediaAdded";
0118     case libvlc_VlmMediaRemoved:
0119         return "VlmMediaRemoved";
0120     case libvlc_VlmMediaChanged:
0121         return "VlmMediaChanged";
0122     case libvlc_VlmMediaInstanceStarted:
0123         return "VlmMediaInstanceStarted";
0124     case libvlc_VlmMediaInstanceStopped:
0125         return "VlmMediaInstanceStopped";
0126     case libvlc_VlmMediaInstanceStatusInit:
0127         return "VlmMediaInstanceStatusInit";
0128     case libvlc_VlmMediaInstanceStatusOpening:
0129         return "VlmMediaInstanceStatusOpening";
0130     case libvlc_VlmMediaInstanceStatusPlaying:
0131         return "VlmMediaInstanceStatusPlaying";
0132     case libvlc_VlmMediaInstanceStatusPause:
0133         return "VlmMediaInstanceStatusPause";
0134     case libvlc_VlmMediaInstanceStatusEnd:
0135         return "VlmMediaInstanceStatusEnd";
0136     case libvlc_VlmMediaInstanceStatusError:
0137         return "VlmMediaInstanceStatusError";
0138 #if LIBVLC_VERSION_MAJOR > 2
0139     case libvlc_RendererDiscovererItemAdded:
0140         return "RendererDiscovererItemAdded";
0141     case libvlc_RendererDiscovererItemDeleted:
0142         return "RendererDiscovererItemDeleted";
0143     case libvlc_MediaPlayerAudioVolume:
0144         return "MediaPlayerAudioVolume";
0145     case libvlc_MediaPlayerAudioDevice:
0146         return "MediaPlayerAudioDevice";
0147     case libvlc_MediaListEndReached:
0148         return "MediaListEndReached";
0149     case libvlc_MediaPlayerChapterChanged:
0150         return "MediaPlayerChapterChanged";
0151     case libvlc_MediaPlayerESAdded:
0152         return "MediaPlayerESAdded";
0153     case libvlc_MediaPlayerESDeleted:
0154         return "MediaPlayerESDeleted";
0155     case libvlc_MediaPlayerESSelected:
0156         return "MediaPlayerESSelected";
0157     case libvlc_MediaPlayerCorked:
0158         return "MediaPlayerCorked";
0159     case libvlc_MediaPlayerUnmuted:
0160         return "MediaPlayerUnmuted";
0161 #endif
0162 
0163     default:
0164         return "Unknown";
0165     }
0166 }
0167 
0168 VlcMediaWidget::VlcMediaWidget(QWidget *parent) : AbstractMediaWidget(parent),
0169     timer(NULL), vlcInstance(NULL), vlcMedia(NULL), vlcMediaPlayer(NULL),
0170     isPaused(false), playingDvd(false), urlIsAudioCd(false),
0171     typeOfDevice(""), trackNumber(1), numTracks(1)
0172 {
0173     libvlc_event_e events[] = {
0174         libvlc_MediaPlayerEncounteredError,
0175         libvlc_MediaPlayerEndReached,
0176         libvlc_MediaPlayerLengthChanged,
0177         libvlc_MediaPlayerSeekableChanged,
0178         libvlc_MediaPlayerStopped,
0179         libvlc_MediaPlayerTimeChanged,
0180 #if LIBVLC_VERSION_MAJOR > 2
0181         libvlc_MediaMetaChanged,
0182         libvlc_MediaPlayerESAdded,
0183         libvlc_MediaPlayerESDeleted,
0184 #endif
0185 #if 0 // all other possible events
0186         libvlc_MediaSubItemAdded,
0187         libvlc_MediaDurationChanged,
0188         libvlc_MediaParsedChanged,
0189         libvlc_MediaFreed,
0190         libvlc_MediaStateChanged,
0191         libvlc_MediaSubItemTreeAdded,
0192         libvlc_MediaPlayerMediaChanged,
0193         libvlc_MediaPlayerNothingSpecial,
0194         libvlc_MediaPlayerOpening,
0195         libvlc_MediaPlayerBuffering,
0196         libvlc_MediaPlayerPlaying,
0197         libvlc_MediaPlayerPaused,
0198         libvlc_MediaPlayerForward,
0199         libvlc_MediaPlayerBackward,
0200         libvlc_MediaPlayerPositionChanged,
0201         libvlc_MediaPlayerPausableChanged,
0202         libvlc_MediaPlayerTitleChanged,
0203         libvlc_MediaPlayerSnapshotTaken,
0204         libvlc_MediaPlayerVout,
0205         libvlc_MediaPlayerScrambledChanged,
0206         libvlc_MediaPlayerUncorked,
0207         libvlc_MediaPlayerMuted,
0208         libvlc_MediaListItemAdded,
0209         libvlc_MediaListWillAddItem,
0210         libvlc_MediaListItemDeleted,
0211         libvlc_MediaListWillDeleteItem,
0212         libvlc_MediaListViewItemAdded,
0213         libvlc_MediaListViewWillAddItem,
0214         libvlc_MediaListViewItemDeleted,
0215         libvlc_MediaListViewWillDeleteItem,
0216         libvlc_MediaListPlayerPlayed,
0217         libvlc_MediaListPlayerNextItemSet,
0218         libvlc_MediaListPlayerStopped,
0219         libvlc_VlmMediaAdded,
0220         libvlc_VlmMediaRemoved,
0221         libvlc_VlmMediaChanged,
0222         libvlc_VlmMediaInstanceStarted,
0223         libvlc_VlmMediaInstanceStopped,
0224         libvlc_VlmMediaInstanceStatusInit,
0225         libvlc_VlmMediaInstanceStatusOpening,
0226         libvlc_VlmMediaInstanceStatusPlaying,
0227         libvlc_VlmMediaInstanceStatusPause,
0228         libvlc_VlmMediaInstanceStatusEnd,
0229         libvlc_VlmMediaInstanceStatusError,
0230 #if LIBVLC_VERSION_MAJOR > 2
0231         libvlc_MediaListEndReached,
0232         libvlc_MediaPlayerAudioDevice,
0233         libvlc_MediaPlayerAudioVolume,
0234         libvlc_MediaPlayerChapterChanged,
0235         libvlc_MediaPlayerESSelected,
0236         libvlc_MediaPlayerCorked,
0237         libvlc_MediaPlayerUnmuted,
0238         libvlc_RendererDiscovererItemAdded,
0239         libvlc_RendererDiscovererItemDeleted,
0240 #endif /*LIBVLC_VERSION_MAJOR */
0241 #endif
0242     };
0243 
0244     for (uint i = 0; i < (sizeof(events) / sizeof(events[0])); ++i)
0245         eventType.append(events[i]);
0246 }
0247 
0248 bool VlcMediaWidget::init()
0249 {
0250     QString args = Configuration::instance()->getLibVlcArguments();
0251     QStringList argList;
0252     int argc = 0, size;
0253 
0254 #if QT_VERSION < 0x050e00
0255     argList = args.split(' ', QString::SkipEmptyParts);
0256 #else
0257     argList = args.split(' ', Qt::SkipEmptyParts);
0258 #endif
0259     size = argList.size();
0260 
0261     const char **argv = new const char *[size];
0262     QVector<QByteArray> str(size);
0263 
0264     for (int i = 0; i < size; i++) {
0265         str[i] = argList.at(i).toUtf8();
0266         argv[argc++] = str[i];
0267     }
0268 
0269     vlcInstance = libvlc_new(argc, argv);
0270     if (!vlcInstance) {
0271         qCWarning(logMediaWidget, "libVLC: failed to use extra args: %s", qPrintable(args));
0272         argc = 0;
0273         vlcInstance = libvlc_new(0, NULL);
0274         if (vlcInstance)
0275             qCInfo(logMediaWidget, "Using libVLC without arguments");
0276     }
0277 
0278     if (vlcInstance == NULL) {
0279         qFatal("Cannot create vlc instance %s", qPrintable(libvlc_errmsg()));
0280         delete[] argv;
0281         return false;
0282     }
0283 
0284     if (argc) {
0285         QString log = "Using libVLC with args:";
0286         for (int i = 0; i < argc; i++)
0287             log += ' ' + QLatin1String(argv[i]);
0288 
0289         qCDebug(logVlc, "%s", qPrintable(log));
0290     }
0291     delete[] argv;
0292 
0293     vlcMediaPlayer = libvlc_media_player_new(vlcInstance);
0294 
0295     if (vlcMediaPlayer == NULL) {
0296         qFatal("Cannot create vlc media player %s", qPrintable(libvlc_errmsg()));
0297         return false;
0298     }
0299 
0300     eventManager = libvlc_media_player_event_manager(vlcMediaPlayer);
0301 
0302     if (!registerEvents())
0303         return false;
0304 
0305     timer = new QTimer();
0306     connect(timer, SIGNAL(timeout()), this, SLOT(hideMouse()));
0307 
0308     libvlc_media_player_set_xwindow(vlcMediaPlayer, quint32(winId()));
0309     setAttribute(Qt::WA_NativeWindow);
0310 
0311     libvlc_audio_set_mute(vlcMediaPlayer, false);
0312 
0313     // This is broken on qt5: the kernel/qwidget.cpp tries to repaint
0314     // on a wrong place, causing this warning:
0315     //  QWidget::paintEngine: Should no longer be called
0316 //  setAttribute(Qt::WA_PaintOnScreen);
0317     return true;
0318 }
0319 
0320 VlcMediaWidget::~VlcMediaWidget()
0321 {
0322     metadata.clear();
0323     audioStreams.clear();
0324     subtitles.clear();
0325     subtitleId.clear();
0326 
0327     if (timer != NULL) {
0328         timer->stop();
0329         delete timer;
0330     }
0331 
0332     unregisterEvents();
0333 
0334     if (vlcMedia != NULL)
0335         libvlc_media_release(vlcMedia);
0336 
0337     if (vlcInstance != NULL)
0338         libvlc_release(vlcInstance);
0339 
0340     if (vlcMediaPlayer != NULL)
0341         libvlc_media_player_release(vlcMediaPlayer);
0342 }
0343 
0344 VlcMediaWidget *VlcMediaWidget::createVlcMediaWidget(QWidget *parent)
0345 {
0346     QScopedPointer<VlcMediaWidget> vlcMediaWidget(new VlcMediaWidget(parent));
0347 
0348     if (!vlcMediaWidget->init()) {
0349         return NULL;
0350     }
0351 
0352     return vlcMediaWidget.take();
0353 }
0354 
0355 QStringList VlcMediaWidget::getAudioDevices()
0356 {
0357     libvlc_audio_output_device_t *vlcAudioOutput, *i;
0358     QStringList audioDevices;
0359 
0360     // Get audio device list
0361     vlcAudioOutput = libvlc_audio_output_device_enum(vlcMediaPlayer);
0362     if (!vlcAudioOutput)
0363         return audioDevices;
0364 
0365     for (i = vlcAudioOutput; i != NULL; i = i->p_next) {
0366         QString device = QString::fromUtf8(i->psz_description);
0367         audioDevices.append(device);
0368     }
0369     libvlc_audio_output_device_list_release(vlcAudioOutput);
0370 
0371     return audioDevices;
0372 }
0373 
0374 void VlcMediaWidget::setAudioDevice(QString device)
0375 {
0376     libvlc_audio_output_device_t *vlcAudioOutput, *i;
0377     vlcAudioOutput = libvlc_audio_output_device_enum(vlcMediaPlayer);
0378 
0379     if (!vlcAudioOutput)
0380         return;
0381 
0382     for (i = vlcAudioOutput; i != NULL; i = i->p_next) {
0383         if (device.compare(QString::fromUtf8(i->psz_description)))
0384             continue;
0385         qCDebug(logVlc, "Setting audio output to: %s", qPrintable(i->psz_device));
0386 
0387         libvlc_audio_output_device_set(vlcMediaPlayer, NULL, i->psz_device);
0388     }
0389     libvlc_audio_output_device_list_release(vlcAudioOutput);
0390 }
0391 
0392 void VlcMediaWidget::setMuted(bool muted)
0393 {
0394     libvlc_audio_set_mute(vlcMediaPlayer, muted);
0395 }
0396 
0397 void VlcMediaWidget::setVolume(int volume)
0398 {
0399     // 0 <= volume <= 200
0400     if (libvlc_audio_set_volume(vlcMediaPlayer, volume) != 0) {
0401         qCWarning(logMediaWidget, "cannot set volume %i", volume);
0402     }
0403 }
0404 
0405 void VlcMediaWidget::setAspectRatio(MediaWidget::AspectRatio aspectRatio)
0406 {
0407     const char *vlcAspectRatio = "";
0408 
0409     switch (aspectRatio) {
0410     case MediaWidget::AspectRatioAuto:
0411         break;
0412     case MediaWidget::AspectRatio1_1:
0413         vlcAspectRatio = "1:1";
0414         break;
0415     case MediaWidget::AspectRatio4_3:
0416         vlcAspectRatio = "4:3";
0417         break;
0418     case MediaWidget::AspectRatio5_4:
0419         vlcAspectRatio = "5:4";
0420         break;
0421     case MediaWidget::AspectRatio16_9:
0422         vlcAspectRatio = "16:9";
0423         break;
0424     case MediaWidget::AspectRatio16_10:
0425         vlcAspectRatio = "16:10";
0426         break;
0427     case MediaWidget::AspectRatio221_100:
0428         vlcAspectRatio = "221:100";
0429         break;
0430     case MediaWidget::AspectRatio235_100:
0431         vlcAspectRatio = "235:100";
0432         break;
0433     case MediaWidget::AspectRatio239_100:
0434         vlcAspectRatio = "239:100";
0435         break;
0436     }
0437 
0438     libvlc_video_set_aspect_ratio(vlcMediaPlayer, vlcAspectRatio);
0439 }
0440 
0441 void VlcMediaWidget::resizeToVideo(float resizeFactor)
0442 {
0443     qCDebug(logMediaWidget, "video resized to %.1f", resizeFactor);
0444     libvlc_video_set_scale(vlcMediaPlayer, resizeFactor);
0445 }
0446 
0447 void VlcMediaWidget::setDeinterlacing(MediaWidget::DeinterlaceMode deinterlacing)
0448 {
0449     const char *vlcDeinterlaceMode;
0450 
0451     switch (deinterlacing) {
0452     case MediaWidget::DeinterlaceDiscard:
0453         vlcDeinterlaceMode = "discard";
0454         break;
0455     case MediaWidget::DeinterlaceBob:
0456         vlcDeinterlaceMode = "bob";
0457         break;
0458     case MediaWidget::DeinterlaceLinear:
0459         vlcDeinterlaceMode = "linear";
0460         break;
0461     case MediaWidget::DeinterlaceYadif:
0462         vlcDeinterlaceMode = "yadif";
0463         break;
0464     case MediaWidget::DeinterlaceYadif2x:
0465         vlcDeinterlaceMode = "yadif2x";
0466         break;
0467     case MediaWidget::DeinterlacePhosphor:
0468         vlcDeinterlaceMode = "phosphor";
0469         break;
0470     case MediaWidget::DeinterlaceX:
0471         vlcDeinterlaceMode = "x";
0472         break;
0473     case MediaWidget::DeinterlaceMean:
0474         vlcDeinterlaceMode = "mean";
0475         break;
0476     case MediaWidget::DeinterlaceBlend:
0477         vlcDeinterlaceMode = "blend";
0478         break;
0479     case MediaWidget::DeinterlaceIvtc:
0480         vlcDeinterlaceMode = "ivtc";
0481         break;
0482     case MediaWidget::DeinterlaceDisabled:
0483     default:
0484         vlcDeinterlaceMode = NULL;
0485     }
0486 
0487 
0488     libvlc_video_set_deinterlace(vlcMediaPlayer, vlcDeinterlaceMode);
0489 }
0490 
0491 void VlcMediaWidget::play(const MediaSource &source)
0492 {
0493     addPendingUpdates(PlaybackStatus | DvdMenu);
0494     QByteArray url = source.getUrl().toEncoded();
0495     playingDvd = false;
0496 
0497     trackNumber = 1;
0498     urlIsAudioCd = false;
0499 
0500     switch (source.getType()) {
0501     case MediaSource::Url:
0502         if (url.endsWith(".iso")) {
0503             playingDvd = true;
0504         }
0505 
0506         break;
0507     case MediaSource::AudioCd:
0508         urlIsAudioCd = true;
0509 
0510         if (url.size() >= 7) {
0511             url.replace(0, 4, "cdda");
0512         } else {
0513             url = "cdda://";
0514         }
0515 
0516         break;
0517     case MediaSource::VideoCd:
0518         if (url.size() >= 7) {
0519             url.replace(0, 4, "vcd");
0520         } else {
0521             url = "vcd://";
0522         }
0523 
0524         break;
0525     case MediaSource::Dvd:
0526         if (url.size() >= 7) {
0527             url.replace(0, 4, "dvd");
0528         } else {
0529             url = "dvd://";
0530         }
0531 
0532         playingDvd = true;
0533         break;
0534     case MediaSource::Dvb:
0535         break;
0536     }
0537 
0538     typeOfDevice = url.constData();
0539 
0540     if (vlcMedia != NULL) {
0541         libvlc_media_player_stop(vlcMediaPlayer);
0542         libvlc_media_release(vlcMedia);
0543     }
0544 
0545     vlcMedia = libvlc_media_new_location(vlcInstance, typeOfDevice);
0546     if (urlIsAudioCd)
0547         libvlc_media_add_option(vlcMedia, "cdda-track=1");
0548 
0549     if (makePlay() < 0) {
0550         stop();
0551         return;
0552     }
0553 
0554     setCursor(Qt::BlankCursor);
0555     setCursor(Qt::ArrowCursor);
0556     timer->start(1000);
0557     setMouseTracking(true);
0558 }
0559 
0560 void VlcMediaWidget::unregisterEvents()
0561 {
0562     for (int i = 0; i < eventType.size(); ++i)
0563         libvlc_event_detach(eventManager, eventType.at(i),
0564                     vlcEventHandler, this);
0565 #if LIBVLC_VERSION_MAJOR <= 2
0566     if (!vlcMedia)
0567         return;
0568     libvlc_event_manager_t *mediaEvent = libvlc_media_event_manager(vlcMedia);
0569     libvlc_event_detach(mediaEvent, libvlc_MediaMetaChanged,
0570                 vlcEventHandler, this);
0571 #endif
0572 }
0573 
0574 bool VlcMediaWidget::registerEvents()
0575 {
0576     for (int i = 0; i < eventType.size(); ++i) {
0577         if (libvlc_event_attach(eventManager, eventType.at(i), vlcEventHandler, this) != 0) {
0578             qCCritical(logMediaWidget, "Cannot attach event handler %d", eventType.at(i));
0579             return false;
0580         }
0581     }
0582     return true;
0583 }
0584 
0585 int VlcMediaWidget::makePlay()
0586 {
0587     if (vlcMedia == NULL) {
0588         libvlc_media_player_stop(vlcMediaPlayer);
0589         return -1;
0590     }
0591 
0592 #if LIBVLC_VERSION_MAJOR <= 2
0593     /*
0594      * There is a difference between libVlc 2.x and 3.x:
0595      * With version 2.x, the event needs to be registered at the
0596      * vlcMedia object, just before calling libvlc_media_player_set_media()
0597      *
0598      * On version 3.x, while this still works, you can simply register the
0599      * event directly at vlcMediaPlayer, together with all other events,
0600      * with simplifies the code.
0601      */
0602     libvlc_event_manager_t *mediaEvent = libvlc_media_event_manager(vlcMedia);
0603 
0604     if (libvlc_event_attach(mediaEvent, libvlc_MediaMetaChanged,
0605                 vlcEventHandler, this) != 0) {
0606         qCWarning(logMediaWidget, "Cannot attach event handler %d",
0607               libvlc_MediaMetaChanged);
0608     }
0609 #endif
0610 
0611     libvlc_media_player_set_media(vlcMediaPlayer, vlcMedia);
0612 
0613     /*
0614      * FIXME: This is mostly a boilerplate as, at least with vlc 3,
0615      * this function always return -1
0616          */
0617     if (urlIsAudioCd)
0618         numTracks = libvlc_audio_get_track_count(vlcMediaPlayer);
0619 
0620     return libvlc_media_player_play(vlcMediaPlayer);
0621 }
0622 
0623 void VlcMediaWidget::playDirection(int direction)
0624 {
0625     QString strBuf = "cdda-track=";
0626     libvlc_state_t state;
0627     int oldTrackNumber = trackNumber;
0628 
0629     if (direction == -1)
0630         trackNumber--;
0631     else
0632         trackNumber++;
0633 
0634     if (numTracks > 0 && trackNumber > numTracks)
0635         trackNumber = numTracks;
0636     if (trackNumber < 1)
0637         trackNumber = 1;
0638 
0639     strBuf += QString::number(trackNumber);
0640 
0641     if (vlcMedia != NULL) {
0642         libvlc_media_player_stop(vlcMediaPlayer);
0643         libvlc_media_release(vlcMedia);
0644     }
0645 
0646     vlcMedia = libvlc_media_new_location(vlcInstance, typeOfDevice);
0647     libvlc_media_add_option(vlcMedia, strBuf.toUtf8());
0648 
0649     if (makePlay() < 0)
0650         stop();
0651 
0652     do {
0653         state = libvlc_media_player_get_state (vlcMediaPlayer);
0654     } while(state != libvlc_Playing &&
0655         state != libvlc_Error &&
0656         state != libvlc_Ended );
0657 
0658     if (state != libvlc_Playing) {
0659         stop();
0660         trackNumber = oldTrackNumber;
0661     }
0662 }
0663 
0664 void VlcMediaWidget::stop()
0665 {
0666     libvlc_media_player_stop(vlcMediaPlayer);
0667 
0668     if (vlcMedia != NULL) {
0669         libvlc_media_release(vlcMedia);
0670         vlcMedia = NULL;
0671     }
0672 
0673     timer->stop();
0674     setCursor(Qt::BlankCursor);
0675     setCursor(Qt::ArrowCursor);
0676     addPendingUpdates(PlaybackStatus | Metadata |
0677               Subtitles | AudioStreams);
0678 }
0679 
0680 void VlcMediaWidget::setPaused(bool paused)
0681 {
0682     isPaused = paused;
0683     libvlc_media_player_set_pause(vlcMediaPlayer, paused);
0684     // we don't monitor playing / buffering / paused state changes
0685     addPendingUpdates(PlaybackStatus);
0686 }
0687 
0688 void VlcMediaWidget::seek(int time)
0689 {
0690     if (!seekable)
0691         return;
0692 
0693     libvlc_media_player_set_time(vlcMediaPlayer, time);
0694 }
0695 
0696 void VlcMediaWidget::setCurrentAudioStream(int _currentAudioStream)
0697 {
0698     if (_currentAudioStream < 0 ||
0699         _currentAudioStream > libvlc_audio_get_track(vlcMediaPlayer))
0700         _currentAudioStream = 0;
0701     // skip the 'deactivate' audio channel
0702     libvlc_audio_set_track(vlcMediaPlayer, _currentAudioStream + 1);
0703 
0704     currentAudioStream = _currentAudioStream;
0705 }
0706 
0707 void VlcMediaWidget::setCurrentSubtitle(int currentSubtitle)
0708 {
0709     libvlc_track_description_t *tr, *track;
0710     int requestedSubtitle = -1;
0711 
0712     QMap<int, int>::const_iterator i = subtitleId.constBegin();
0713     while (i != subtitleId.constEnd()) {
0714         qCDebug(logVlc, "Subtitle #%d, key: %d", i.value(), i.key());
0715         if (i.value() == currentSubtitle) {
0716             requestedSubtitle = i.key();
0717             break;
0718         }
0719         i++;
0720     }
0721 
0722     qCDebug(logVlc, "Try to set subtitle #%d, id %d", currentSubtitle, requestedSubtitle);
0723     libvlc_video_set_spu(vlcMediaPlayer, requestedSubtitle);
0724 
0725     /* Print what it was actually selected */
0726 
0727     tr = libvlc_video_get_spu_description(vlcMediaPlayer);
0728     track = tr;
0729     while (track != NULL) {
0730         QString subtitle = QString::fromUtf8(track->psz_name);
0731 
0732         if (subtitle.isEmpty()) {
0733             subtitle = i18n("Subtitle %1", track->i_id);
0734         }
0735 
0736         if (track->i_id == requestedSubtitle)
0737             qCDebug(logVlc, "Subtitle set to id %d: %s", track->i_id, qPrintable(subtitle));
0738         track = track->p_next;
0739     }
0740     libvlc_track_description_list_release(tr);
0741 
0742 }
0743 
0744 void VlcMediaWidget::setExternalSubtitle(const QUrl &url)
0745 {
0746     QString fname = url.toLocalFile();
0747 
0748 #if LIBVLC_VERSION_MAJOR > 2
0749     if (libvlc_media_player_add_slave(vlcMediaPlayer,
0750                       libvlc_media_slave_type_subtitle,
0751                       url.toEncoded().constData(),
0752                       true) == 0)
0753         qCWarning(logMediaWidget, "Cannot set subtitle file %s", qPrintable(fname));
0754 #else
0755     if (libvlc_video_set_subtitle_file(vlcMediaPlayer,
0756                        fname.toLocal8Bit().constData()) == 0)
0757         qCWarning(logMediaWidget, "Cannot set subtitle file %s", qPrintable(fname));
0758 #endif
0759 }
0760 
0761 void VlcMediaWidget::setCurrentTitle(int currentTitle)
0762 {
0763     libvlc_media_player_set_title(vlcMediaPlayer, currentTitle);
0764 }
0765 
0766 void VlcMediaWidget::setCurrentChapter(int currentChapter)
0767 {
0768     libvlc_media_player_set_chapter(vlcMediaPlayer, currentChapter);
0769 }
0770 
0771 void VlcMediaWidget::setCurrentAngle(int currentAngle)
0772 {
0773     Q_UNUSED(currentAngle)
0774     // FIXME
0775 }
0776 
0777 bool VlcMediaWidget::jumpToPreviousChapter()
0778 {
0779     int currentTitle = libvlc_media_player_get_title(vlcMediaPlayer);
0780     int currentChapter = libvlc_media_player_get_chapter(vlcMediaPlayer);
0781     if (urlIsAudioCd)
0782         playDirection(-1);
0783     else
0784         libvlc_media_player_previous_chapter(vlcMediaPlayer);
0785 
0786     if ((libvlc_media_player_get_title(vlcMediaPlayer) != currentTitle) ||
0787         (libvlc_media_player_get_chapter(vlcMediaPlayer) != currentChapter)) {
0788         return true;
0789     }
0790 
0791     return false;
0792 }
0793 
0794 bool VlcMediaWidget::jumpToNextChapter()
0795 {
0796     int currentTitle = libvlc_media_player_get_title(vlcMediaPlayer);
0797     int currentChapter = libvlc_media_player_get_chapter(vlcMediaPlayer);
0798     if (urlIsAudioCd)
0799         playDirection(1);
0800     else
0801         libvlc_media_player_next_chapter(vlcMediaPlayer);
0802 
0803     if ((libvlc_media_player_get_title(vlcMediaPlayer) != currentTitle) ||
0804         (libvlc_media_player_get_chapter(vlcMediaPlayer) != currentChapter)) {
0805         return true;
0806     }
0807 
0808     return false;
0809 }
0810 
0811 void VlcMediaWidget::showDvdMenu()
0812 {
0813     if (playingDvd) {
0814         libvlc_media_player_set_title(vlcMediaPlayer, 0);
0815     }
0816 }
0817 
0818 int VlcMediaWidget::updatePlaybackStatus()
0819 {
0820     MediaWidget::PlaybackStatus oldPlaybackStatus = playbackStatus;
0821 
0822     switch (libvlc_media_player_get_state(vlcMediaPlayer)) {
0823     case libvlc_NothingSpecial:
0824     case libvlc_Stopped:
0825         playbackStatus = MediaWidget::Idle;
0826         break;
0827     case libvlc_Opening:
0828     case libvlc_Buffering:
0829         playbackStatus = MediaWidget::Playing;
0830         break;
0831     case libvlc_Playing:
0832         // The first time libVLC is set to pause, it reports status as playing
0833         if (isPaused)
0834             playbackStatus = MediaWidget::Paused;
0835         else
0836             playbackStatus = MediaWidget::Playing;
0837         break;
0838     case libvlc_Paused:
0839         playbackStatus = MediaWidget::Paused;
0840         break;
0841     case libvlc_Ended:
0842         playDirection(1);
0843         break;
0844     case libvlc_Error:
0845         playbackStatus = MediaWidget::Idle;
0846         // don't keep last picture shown
0847         libvlc_media_player_stop(vlcMediaPlayer);
0848         break;
0849     }
0850 
0851     if (playbackStatus == MediaWidget::Idle) {
0852         addPendingUpdates(DvdMenu);
0853         playingDvd = false;
0854     }
0855 
0856     // Report if the status has changed
0857     return (oldPlaybackStatus != playbackStatus);
0858 }
0859 
0860 void VlcMediaWidget::updateCurrentTotalTime()
0861 {
0862     if (playbackStatus == MediaWidget::Idle)
0863         return;
0864 
0865     currentTime = int(libvlc_media_player_get_time(vlcMediaPlayer));
0866     totalTime = int(libvlc_media_player_get_length(vlcMediaPlayer));
0867 
0868     if (currentTime < 0) {
0869         currentTime = 0;
0870     }
0871 
0872     if (totalTime < 0) {
0873         totalTime = 0;
0874     }
0875 
0876     if (totalTime && currentTime > totalTime) {
0877         currentTime = totalTime;
0878     }
0879 }
0880 
0881 void VlcMediaWidget::updateSeekable()
0882 {
0883     seekable = libvlc_media_player_is_seekable(vlcMediaPlayer);
0884 }
0885 
0886 void VlcMediaWidget::updateMetadata()
0887 {
0888     char *meta;
0889 
0890     metadata.clear();
0891     if (vlcMedia != NULL) {
0892         QString s;
0893         meta = libvlc_media_get_meta(vlcMedia, libvlc_meta_Title);
0894         if (meta) {
0895             metadata.insert(MediaWidget::Title, QString::fromUtf8(meta));
0896             free(meta);
0897         }
0898         meta = libvlc_media_get_meta(vlcMedia, libvlc_meta_Artist);
0899         if (meta) {
0900             metadata.insert(MediaWidget::Artist, QString::fromUtf8(meta));
0901             free(meta);
0902         }
0903         meta = libvlc_media_get_meta(vlcMedia, libvlc_meta_Album);
0904         if (meta) {
0905             metadata.insert(MediaWidget::Album, QString::fromUtf8(meta));
0906             free(meta);
0907         }
0908         meta = libvlc_media_get_meta(vlcMedia, libvlc_meta_TrackNumber);
0909         if (meta) {
0910             metadata.insert(MediaWidget::TrackNumber, QString::fromUtf8(meta));
0911             free(meta);
0912         }
0913     }
0914 
0915     if (urlIsAudioCd)
0916         metadata.insert(MediaWidget::Title,
0917                 i18n("CD track %1", QString::number(trackNumber)));
0918 }
0919 
0920 void VlcMediaWidget::updateAudioStreams()
0921 {
0922     audioStreams.clear();
0923     libvlc_track_description_t *tr, *track;
0924 
0925     tr = libvlc_audio_get_track_description(vlcMediaPlayer);
0926     track = tr;
0927 
0928     if (track != NULL) {
0929         // skip the 'deactivate' audio channel
0930         track = track->p_next;
0931     }
0932 
0933     while (track != NULL) {
0934         QString audioStream = QString::fromUtf8(track->psz_name);
0935         int cutBegin = (audioStream.indexOf(QLatin1Char('[')) + 1);
0936 
0937         if (cutBegin > 0) {
0938             int cutEnd = audioStream.lastIndexOf(QLatin1Char(']'));
0939 
0940             if (cutEnd >= 0) {
0941                 // remove unnecessary text
0942                 audioStream = audioStream.mid(cutBegin, cutEnd - cutBegin);
0943             }
0944         }
0945 
0946         if (audioStream.isEmpty()) {
0947             audioStream = QString::number(audioStreams.size() + 1);
0948         }
0949 
0950         audioStreams.append(audioStream);
0951         track = track->p_next;
0952     }
0953     libvlc_track_description_list_release(tr);
0954 
0955     // skip the 'deactivate' audio channel
0956     currentAudioStream = (libvlc_audio_get_track(vlcMediaPlayer) - 1);
0957     if (currentAudioStream < 0)
0958         setCurrentAudioStream(0);
0959 }
0960 
0961 void VlcMediaWidget::updateSubtitles()
0962 {
0963     libvlc_track_description_t *tr, *track;
0964     int i = 0;
0965 
0966     subtitles.clear();
0967     subtitleId.clear();
0968 
0969     tr = libvlc_video_get_spu_description(vlcMediaPlayer);
0970     track = tr;
0971 
0972     if (track != NULL) {
0973         // skip the 'deactivate' subtitle
0974         track = track->p_next;
0975     }
0976 
0977     while (track != NULL) {
0978         QString subtitle = QString::fromUtf8(track->psz_name);
0979 
0980         if (subtitle.isEmpty()) {
0981             subtitle = i18n("Subtitle %1", track->i_id);
0982         }
0983 
0984         // 0 is reserved for "disabled" at mediawidget. So, we should
0985         // Start counting from 1, to match the range expected for
0986         // currentSubtitle
0987         subtitleId[track->i_id] = ++i;
0988         subtitles.append(subtitle);
0989         qCDebug(logVlc, "Got subtitle id#%d: %s", track->i_id, qPrintable(subtitle));
0990         track = track->p_next;
0991     }
0992     libvlc_track_description_list_release(tr);
0993 
0994     // skip the 'deactivate' subtitle
0995     currentSubtitle = subtitleId.value(libvlc_video_get_spu(vlcMediaPlayer), -1);
0996 }
0997 
0998 void VlcMediaWidget::updateTitles()
0999 {
1000     titleCount = libvlc_media_player_get_title_count(vlcMediaPlayer);
1001     currentTitle = libvlc_media_player_get_title(vlcMediaPlayer);
1002 }
1003 
1004 void VlcMediaWidget::updateChapters()
1005 {
1006     chapterCount = libvlc_media_player_get_chapter_count(vlcMediaPlayer);
1007     currentChapter = libvlc_media_player_get_chapter(vlcMediaPlayer);
1008 }
1009 
1010 void VlcMediaWidget::updateAngles()
1011 {
1012     // FIXME
1013 }
1014 
1015 void VlcMediaWidget::updateDvdMenu()
1016 {
1017     dvdMenu = playingDvd;
1018 }
1019 
1020 void VlcMediaWidget::updateVideoSize()
1021 {
1022     // FIXME
1023 }
1024 
1025 void VlcMediaWidget::dvdNavigate(int key)
1026 {
1027     int event;
1028 
1029     switch (key){
1030     case Qt::Key_Return:
1031         event = libvlc_navigate_activate;
1032         break;
1033     case Qt::Key_Up:
1034         event = libvlc_navigate_up;
1035         break;
1036     case Qt::Key_Down:
1037         event = libvlc_navigate_down;
1038         break;
1039     case Qt::Key_Left:
1040         event = libvlc_navigate_left;
1041         break;
1042     case Qt::Key_Right:
1043         event = libvlc_navigate_right;
1044         break;
1045     default:
1046         return;
1047     }
1048     libvlc_media_player_navigate(vlcMediaPlayer, event);
1049 }
1050 
1051 void VlcMediaWidget::mousePressEvent(QMouseEvent *event)
1052 {
1053     AbstractMediaWidget::mousePressEvent(event);
1054 }
1055 
1056 void VlcMediaWidget::hideMouse()
1057 {
1058     timer->stop();
1059 
1060     setCursor(Qt::ArrowCursor);
1061     setCursor(Qt::BlankCursor);
1062 }
1063 
1064 void VlcMediaWidget::mouseMoveEvent(QMouseEvent *event)
1065 {
1066     if (!timer->isActive()) {
1067         setCursor(Qt::BlankCursor);
1068         setCursor(Qt::ArrowCursor);
1069     }
1070     if (this->underMouse())
1071         timer->start(1000);
1072     else
1073         timer->stop();
1074 
1075     AbstractMediaWidget::mouseMoveEvent(event);
1076 }
1077 
1078 void VlcMediaWidget::vlcEvent(const libvlc_event_t *event)
1079 {
1080     PendingUpdates pendingUpdatesToBeAdded = Nothing;
1081 
1082     switch (event->type) {
1083 #if LIBVLC_VERSION_MAJOR > 2
1084     case libvlc_MediaPlayerESAdded:
1085     case libvlc_MediaPlayerESDeleted:
1086 #endif
1087     case libvlc_MediaMetaChanged:
1088         pendingUpdatesToBeAdded = Metadata | Subtitles | AudioStreams;
1089         break;
1090     case libvlc_MediaPlayerEncounteredError:
1091         pendingUpdatesToBeAdded = PlaybackStatus;
1092         break;
1093     case libvlc_MediaPlayerEndReached:
1094         pendingUpdatesToBeAdded = (PlaybackFinished | PlaybackStatus);
1095         break;
1096     case libvlc_MediaPlayerLengthChanged:
1097         pendingUpdatesToBeAdded = CurrentTotalTime;
1098         break;
1099     case libvlc_MediaPlayerSeekableChanged:
1100         pendingUpdatesToBeAdded = Seekable | Subtitles;
1101         break;
1102     case libvlc_MediaPlayerStopped:
1103         pendingUpdatesToBeAdded = PlaybackStatus;
1104         setMouseTracking(false);
1105         break;
1106     case libvlc_MediaPlayerTimeChanged:
1107         pendingUpdatesToBeAdded = CurrentTotalTime;
1108         break;
1109     }
1110 
1111     if (pendingUpdatesToBeAdded != 0) {
1112         addPendingUpdates(pendingUpdatesToBeAdded);
1113     } else {
1114         qCWarning(logMediaWidget, "Unhandled event %s (%d)",
1115               vlcEventName(event->type), event->type);
1116     }
1117 }
1118 
1119 void VlcMediaWidget::vlcEventHandler(const libvlc_event_t *event, void *instance)
1120 {
1121     reinterpret_cast<VlcMediaWidget *>(instance)->vlcEvent(event);
1122 }
1123 
1124 #include "moc_vlcmediawidget.cpp"