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