File indexing completed on 2024-12-01 04:21:25

0001 /*
0002     Copyright (C) 2007-2008 Tanguy Krotoff <tkrotoff@gmail.com>
0003     Copyright (C) 2008 Lukas Durfina <lukas.durfina@gmail.com>
0004     Copyright (C) 2009 Fathi Boudra <fabo@kde.org>
0005     Copyright (C) 2009-2011 vlc-phonon AUTHORS <kde-multimedia@kde.org>
0006     Copyright (C) 2011-2018 Harald Sitter <sitter@kde.org>
0007 
0008     This library is free software; you can redistribute it and/or
0009     modify it under the terms of the GNU Lesser General Public
0010     License as published by the Free Software Foundation; either
0011     version 2.1 of the License, or (at your option) any later version.
0012 
0013     This library is distributed in the hope that it will be useful,
0014     but WITHOUT ANY WARRANTY; without even the implied warranty of
0015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0016     Lesser General Public License for more details.
0017 
0018     You should have received a copy of the GNU Lesser General Public
0019     License along with this library.  If not, see <http://www.gnu.org/licenses/>.
0020 */
0021 
0022 #include "mediacontroller.h"
0023 
0024 #include <phonon/GlobalDescriptionContainer>
0025 
0026 #include <QTimer>
0027 
0028 #include "utils/debug.h"
0029 #include "utils/libvlc.h"
0030 #include "mediaplayer.h"
0031 
0032 namespace Phonon {
0033 namespace VLC {
0034 
0035 #ifdef __GNUC__
0036 #warning titles and chapters not covered by globaldescriptioncontainer!!
0037 #endif
0038 
0039 MediaController::MediaController()
0040     : m_subtitleAutodetect(true)
0041     , m_subtitleEncoding("UTF-8")
0042     , m_subtitleFontChanged(false)
0043     , m_player(0)
0044     , m_refreshTimer(new QTimer(dynamic_cast<QObject *>(this)))
0045     , m_attemptingAutoplay(false)
0046 {
0047     GlobalSubtitles::instance()->register_(this);
0048     GlobalAudioChannels::instance()->register_(this);
0049     resetMembers();
0050 }
0051 
0052 MediaController::~MediaController()
0053 {
0054     GlobalSubtitles::instance()->unregister_(this);
0055     GlobalAudioChannels::instance()->unregister_(this);
0056 }
0057 
0058 bool MediaController::hasInterface(Interface iface) const
0059 {
0060     switch (iface) {
0061     case AddonInterface::NavigationInterface:
0062         return true;
0063         break;
0064     case AddonInterface::ChapterInterface:
0065         return true;
0066         break;
0067     case AddonInterface::AngleInterface:
0068         return false;
0069         break;
0070     case AddonInterface::TitleInterface:
0071         return true;
0072         break;
0073     case AddonInterface::SubtitleInterface:
0074         return true;
0075         break;
0076     case AddonInterface::AudioChannelInterface:
0077         return true;
0078         break;
0079     }
0080 
0081     warning() << "Interface" << iface << "is not supported by Phonon VLC :(";
0082     return false;
0083 }
0084 
0085 QVariant MediaController::interfaceCall(Interface iface, int i_command, const QList<QVariant> & arguments)
0086 {
0087     DEBUG_BLOCK;
0088     switch (iface) {
0089     case AddonInterface::ChapterInterface:
0090         switch (static_cast<AddonInterface::ChapterCommand>(i_command)) {
0091         case AddonInterface::availableChapters:
0092             return availableChapters();
0093         case AddonInterface::chapter:
0094             return currentChapter();
0095         case AddonInterface::setChapter:
0096             if (arguments.isEmpty() || !arguments.first().canConvert(QVariant::Int)) {
0097                 error() << Q_FUNC_INFO << "arguments invalid";
0098                 return false;
0099             }
0100             setCurrentChapter(arguments.first().toInt());
0101             return true;
0102         }
0103         break;
0104     case AddonInterface::TitleInterface:
0105         switch (static_cast<AddonInterface::TitleCommand>(i_command)) {
0106         case AddonInterface::availableTitles:
0107             return availableTitles();
0108         case AddonInterface::title:
0109             return currentTitle();
0110         case AddonInterface::setTitle:
0111             if (arguments.isEmpty() || !arguments.first().canConvert(QVariant::Int)) {
0112                 error() << Q_FUNC_INFO << "arguments invalid";
0113                 return false;
0114             }
0115             setCurrentTitle(arguments.first().toInt());
0116             return true;
0117         case AddonInterface::autoplayTitles:
0118             return autoplayTitles();
0119         case AddonInterface::setAutoplayTitles:
0120             if (arguments.isEmpty() || !arguments.first().canConvert(QVariant::Bool)) {
0121                 error() << Q_FUNC_INFO << " arguments invalid";
0122                 return false;
0123             }
0124             setAutoplayTitles(arguments.first().toBool());
0125             return true;
0126         }
0127         break;
0128     case AddonInterface::AngleInterface:
0129         warning() << "AddonInterface::AngleInterface not supported!";
0130         break;
0131     case AddonInterface::SubtitleInterface:
0132         switch (static_cast<AddonInterface::SubtitleCommand>(i_command)) {
0133         case AddonInterface::availableSubtitles:
0134             return QVariant::fromValue(availableSubtitles());
0135         case AddonInterface::currentSubtitle:
0136             return QVariant::fromValue(currentSubtitle());
0137         case AddonInterface::setCurrentSubtitle:
0138             if (arguments.isEmpty() || !arguments.first().canConvert<SubtitleDescription>()) {
0139                 error() << Q_FUNC_INFO << "arguments invalid";
0140                 return false;
0141             }
0142             setCurrentSubtitle(arguments.first().value<SubtitleDescription>());
0143             return true;
0144         case AddonInterface::setCurrentSubtitleFile:
0145             if (arguments.isEmpty() || !arguments.first().canConvert<QUrl>()) {
0146                 error() << Q_FUNC_INFO << " arguments invalid";
0147                 return false;
0148             }
0149             setCurrentSubtitleFile(arguments.first().value<QUrl>());
0150         case AddonInterface::subtitleAutodetect:
0151             return QVariant::fromValue(subtitleAutodetect());
0152         case AddonInterface::setSubtitleAutodetect:
0153             if (arguments.isEmpty() || !arguments.first().canConvert<bool>()) {
0154                 error() << Q_FUNC_INFO << " arguments invalid";
0155                 return false;
0156             }
0157             setSubtitleAutodetect(arguments.first().value<bool>());
0158             return true;
0159         case AddonInterface::subtitleEncoding:
0160             return subtitleEncoding();
0161         case AddonInterface::setSubtitleEncoding:
0162             if (arguments.isEmpty() || !arguments.first().canConvert<QString>()) {
0163                 error() << Q_FUNC_INFO << " arguments invalid";
0164                 return false;
0165             }
0166             setSubtitleEncoding(arguments.first().value<QString>());
0167             return true;
0168         case AddonInterface::subtitleFont:
0169             return subtitleFont();
0170         case AddonInterface::setSubtitleFont:
0171             if (arguments.isEmpty() || !arguments.first().canConvert<QFont>()) {
0172                 error() << Q_FUNC_INFO << " arguments invalid";
0173                 return false;
0174             }
0175             setSubtitleFont(arguments.first().value<QFont>());
0176             return true;
0177         }
0178         break;
0179     case AddonInterface::AudioChannelInterface:
0180         switch (static_cast<AddonInterface::AudioChannelCommand>(i_command)) {
0181         case AddonInterface::availableAudioChannels:
0182             return QVariant::fromValue(availableAudioChannels());
0183         case AddonInterface::currentAudioChannel:
0184             return QVariant::fromValue(currentAudioChannel());
0185         case AddonInterface::setCurrentAudioChannel:
0186             if (arguments.isEmpty() || !arguments.first().canConvert<AudioChannelDescription>()) {
0187                 error() << Q_FUNC_INFO << "arguments invalid";
0188                 return false;
0189             }
0190             setCurrentAudioChannel(arguments.first().value<AudioChannelDescription>());
0191             return true;
0192         }
0193         break;
0194     }
0195 
0196     error() << Q_FUNC_INFO << "unsupported AddonInterface::Interface:" << iface;
0197 
0198     return QVariant();
0199 }
0200 
0201 void MediaController::resetMediaController()
0202 {
0203     resetMembers();
0204     emit availableAudioChannelsChanged();
0205     emit availableSubtitlesChanged();
0206     emit availableTitlesChanged(0);
0207     emit availableChaptersChanged(0);
0208 }
0209 
0210 void MediaController::resetMembers()
0211 {
0212     m_currentAudioChannel = Phonon::AudioChannelDescription();
0213     GlobalAudioChannels::self->clearListFor(this);
0214 
0215     m_currentSubtitle = Phonon::SubtitleDescription();
0216     GlobalSubtitles::instance()->clearListFor(this);
0217 
0218     m_currentChapter = 0;
0219     m_availableChapters = 0;
0220 
0221     m_currentTitle = 1;
0222     m_availableTitles = 0;
0223 
0224     m_attemptingAutoplay = false;
0225 }
0226 
0227 // ----------------------------- Audio Channel ------------------------------ //
0228 void MediaController::setCurrentAudioChannel(const Phonon::AudioChannelDescription &audioChannel)
0229 {
0230     const int localIndex = GlobalAudioChannels::instance()->localIdFor(this, audioChannel.index());
0231     if (!m_player->setAudioTrack(localIndex))
0232         error() << "libVLC:" << LibVLC::errorMessage();
0233     else
0234         m_currentAudioChannel = audioChannel;
0235 }
0236 
0237 QList<Phonon::AudioChannelDescription> MediaController::availableAudioChannels() const
0238 {
0239     return GlobalAudioChannels::instance()->listFor(this);
0240 }
0241 
0242 Phonon::AudioChannelDescription MediaController::currentAudioChannel() const
0243 {
0244     return m_currentAudioChannel;
0245 }
0246 
0247 void MediaController::refreshAudioChannels()
0248 {
0249     GlobalAudioChannels::instance()->clearListFor(this);
0250 
0251     const int currentChannelId = m_player->audioTrack();
0252 
0253     int idCount = 0;
0254     VLC_FOREACH_TRACK(it, m_player->audioTrackDescription()) {
0255         idCount= it->i_id;
0256         GlobalAudioChannels::instance()->add(this, idCount, QString::fromUtf8(it->psz_name), "");
0257         if (idCount == currentChannelId) {
0258 #ifdef __GNUC__
0259 #warning GlobalDescriptionContainer does not allow reverse resolution from local to descriptor!
0260 #endif
0261             const QList<AudioChannelDescription> list = GlobalAudioChannels::instance()->listFor(this);
0262             foreach (const AudioChannelDescription &descriptor, list) {
0263                 if (descriptor.name() == QString::fromUtf8(it->psz_name)) {
0264                     m_currentAudioChannel = descriptor;
0265                 }
0266             }
0267         }
0268         ++idCount;
0269     }
0270 
0271     emit availableAudioChannelsChanged();
0272 }
0273 
0274 // -------------------------------- Subtitle -------------------------------- //
0275 void MediaController::setCurrentSubtitle(const Phonon::SubtitleDescription &subtitle)
0276 {
0277     DEBUG_BLOCK;
0278     QString type = subtitle.property("type").toString();
0279 
0280     debug() << subtitle;
0281 
0282     if (type == "file") {
0283         QString filename = subtitle.property("name").toString();
0284         if (!filename.isEmpty()) {
0285             if (!m_player->setSubtitle(filename))
0286                 error() << "libVLC:" << LibVLC::errorMessage();
0287             else
0288                 m_currentSubtitle = subtitle;
0289 
0290             // There is no subtitle event inside libvlc so let's send our own event...
0291             GlobalSubtitles::instance()->add(this, m_currentSubtitle);
0292             emit availableSubtitlesChanged();
0293         }
0294     } else {
0295         const int localIndex = GlobalSubtitles::instance()->localIdFor(this, subtitle.index());
0296         debug () << "localid" << localIndex;
0297         if (!m_player->setSubtitle(localIndex))
0298             error() << "libVLC:" << LibVLC::errorMessage();
0299         else
0300             m_currentSubtitle = subtitle;
0301     }
0302 }
0303 
0304 void MediaController::setCurrentSubtitleFile(const QUrl &url)
0305 {
0306     const QString file = url.toLocalFile();
0307     if (!m_player->setSubtitle(file))
0308         error() << "libVLC failed to set subtitle file:" << LibVLC::errorMessage();
0309     // Unfortunately the addition of SPUs does not trigger an event in the
0310     // VLC mediaplayer, yet the actual addition to the descriptor is async.
0311     // So for the time being our best shot at getting an up-to-date list of SPUs
0312     // is shooting in the dark and hoping we hit something.
0313     // Refresha after 1, 2 and 5 seconds. If we have no updated list after 5
0314     // seconds we are out of luck.
0315     // https://trac.videolan.org/vlc/ticket/9796
0316     QObject *mediaObject = dynamic_cast<QObject *>(this); // MediaObject : QObject, MediaController
0317     m_refreshTimer->singleShot(1 * 1000, mediaObject, SLOT(refreshDescriptors()));
0318     m_refreshTimer->singleShot(2 * 1000, mediaObject, SLOT(refreshDescriptors()));
0319     m_refreshTimer->singleShot(5 * 1000, mediaObject, SLOT(refreshDescriptors()));
0320 }
0321 
0322 QList<Phonon::SubtitleDescription> MediaController::availableSubtitles() const
0323 {
0324     return GlobalSubtitles::instance()->listFor(this);
0325 }
0326 
0327 Phonon::SubtitleDescription MediaController::currentSubtitle() const
0328 {
0329     return m_currentSubtitle;
0330 }
0331 
0332 void MediaController::refreshSubtitles()
0333 {
0334     DEBUG_BLOCK;
0335     GlobalSubtitles::instance()->clearListFor(this);
0336 
0337     const int currentSubtitleId = m_player->subtitle();
0338 
0339     VLC_FOREACH_TRACK(it, m_player->videoSubtitleDescription()) {
0340         debug() << "found subtitle" << it->psz_name << "[" << it->i_id << "]";
0341         GlobalSubtitles::instance()->add(this, it->i_id, QString::fromUtf8(it->psz_name), "");
0342         if (it->i_id == currentSubtitleId) {
0343 #ifdef __GNUC__
0344 #warning GlobalDescriptionContainer does not allow reverse resolution from local to descriptor!
0345 #endif
0346             const QList<SubtitleDescription> list = GlobalSubtitles::instance()->listFor(this);
0347             foreach (const SubtitleDescription &descriptor, list) {
0348                 if (descriptor.name() == QString::fromUtf8(it->psz_name)) {
0349                     m_currentSubtitle = descriptor;
0350                 }
0351             }
0352         }
0353     }
0354 
0355     emit availableSubtitlesChanged();
0356 }
0357 
0358 bool MediaController::subtitleAutodetect() const
0359 {
0360     return m_subtitleAutodetect;
0361 }
0362 
0363 void MediaController::setSubtitleAutodetect(bool enabled)
0364 {
0365     m_subtitleAutodetect = enabled;
0366 }
0367 
0368 QString MediaController::subtitleEncoding() const
0369 {
0370     return m_subtitleEncoding;
0371 }
0372 
0373 void MediaController::setSubtitleEncoding(const QString &encoding)
0374 {
0375     m_subtitleEncoding = encoding;
0376 }
0377 
0378 QFont MediaController::subtitleFont() const
0379 {
0380     return m_subtitleFont;
0381 }
0382 
0383 void MediaController::setSubtitleFont(const QFont &font)
0384 {
0385     m_subtitleFontChanged = true;
0386     m_subtitleFont = font;
0387 }
0388 
0389 // --------------------------------- Title ---------------------------------- //
0390 void MediaController::setCurrentTitle(int title)
0391 {
0392     DEBUG_BLOCK;
0393     m_currentTitle = title;
0394 
0395     switch (source().discType()) {
0396     case Cd:
0397         m_player->setCdTrack(title);
0398         return;
0399     case Dvd:
0400     case Vcd:
0401     case BluRay:
0402         m_player->setTitle(title);
0403         return;
0404     case NoDisc:
0405         warning() << "Current media source is not a CD, DVD or VCD!";
0406         return;
0407     }
0408 
0409     warning() << "MediaSource does not support setting of tile in this version of Phonon VLC!"
0410               << "Type is" << source().discType();
0411 }
0412 
0413 int MediaController::availableTitles() const
0414 {
0415     return m_availableTitles;
0416 }
0417 
0418 int MediaController::currentTitle() const
0419 {
0420     return m_currentTitle;
0421 }
0422 
0423 void MediaController::setAutoplayTitles(bool autoplay)
0424 {
0425     m_autoPlayTitles = autoplay;
0426 }
0427 
0428 bool MediaController::autoplayTitles() const
0429 {
0430     return m_autoPlayTitles;
0431 }
0432 
0433 void MediaController::refreshTitles()
0434 {
0435     m_availableTitles = 0;
0436 
0437     SharedTitleDescriptions list = m_player->titleDescription();
0438     for (unsigned int i = 0; i < list->size(); ++i) {
0439         ++m_availableTitles;
0440         emit availableTitlesChanged(m_availableTitles);
0441     }
0442 }
0443 
0444 // -------------------------------- Chapter --------------------------------- //
0445 void MediaController::setCurrentChapter(int chapter)
0446 {
0447     m_currentChapter = chapter;
0448     m_player->setChapter(chapter);
0449 }
0450 
0451 int MediaController::availableChapters() const
0452 {
0453     return m_availableChapters;
0454 }
0455 
0456 int MediaController::currentChapter() const
0457 {
0458     return m_currentChapter;
0459 }
0460 
0461 // We need to rebuild available chapters when title is changed
0462 void MediaController::refreshChapters(int title)
0463 {
0464     m_availableChapters = 0;
0465 
0466     // Get the description of available chapters for specific title
0467     SharedChapterDescriptions list = m_player->videoChapterDescription(title);
0468     for (unsigned int i = 0; i < list->size(); ++i) {
0469         ++m_availableChapters;
0470         emit availableChaptersChanged(m_availableChapters);
0471     }
0472 }
0473 
0474 // --------------------------------- Angle ---------------------------------- //
0475 //                          NOT SUPPORTED IN LIBVLC                           //
0476 
0477 } // namespace VLC
0478 } // namespace Phonon