File indexing completed on 2024-12-01 04:21:26
0001 /* 0002 Copyright (C) 2011-2018 Harald Sitter <sitter@kde.org> 0003 0004 This library is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Lesser General Public 0006 License as published by the Free Software Foundation; either 0007 version 2.1 of the License, or (at your option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Lesser General Public License for more details. 0013 0014 You should have received a copy of the GNU Lesser General Public 0015 License along with this library. If not, see <http://www.gnu.org/licenses/>. 0016 */ 0017 0018 #include "mediaplayer.h" 0019 0020 #include <QtCore/QDebug> 0021 #include <QtCore/QDir> 0022 #include <QtCore/QMetaType> 0023 #include <QtCore/QString> 0024 #include <QtCore/QTemporaryFile> 0025 #include <QtGui/QImage> 0026 0027 #include <vlc/libvlc_version.h> 0028 0029 #include "utils/libvlc.h" 0030 #include "media.h" 0031 0032 // Callbacks come from a VLC thread. In some cases Qt fails to detect this and 0033 // tries to invoke directly (i.e. from same thread). This can lead to thread 0034 // pollution throughout Phonon, which is very much not desired. 0035 #define P_EMIT_HAS_VIDEO(hasVideo) \ 0036 QMetaObject::invokeMethod(\ 0037 that, "hasVideoChanged", \ 0038 Qt::QueuedConnection, \ 0039 Q_ARG(bool, hasVideo)) 0040 0041 #define P_EMIT_STATE(__state) \ 0042 QMetaObject::invokeMethod(\ 0043 that, "stateChanged", \ 0044 Qt::QueuedConnection, \ 0045 Q_ARG(MediaPlayer::State, __state)) 0046 0047 namespace Phonon { 0048 namespace VLC { 0049 0050 MediaPlayer::MediaPlayer(QObject *parent) 0051 : QObject(parent) 0052 , m_media(0) 0053 , m_player(libvlc_media_player_new(pvlc_libvlc)) 0054 , m_doingPausedPlay(false) 0055 , m_volume(75) 0056 , m_fadeAmount(1.0f) 0057 { 0058 Q_ASSERT(m_player); 0059 0060 qRegisterMetaType<MediaPlayer::State>("MediaPlayer::State"); 0061 0062 libvlc_event_manager_t *manager = libvlc_media_player_event_manager(m_player); 0063 libvlc_event_type_t events[] = { 0064 libvlc_MediaPlayerMediaChanged, 0065 libvlc_MediaPlayerNothingSpecial, 0066 libvlc_MediaPlayerOpening, 0067 libvlc_MediaPlayerBuffering, 0068 libvlc_MediaPlayerPlaying, 0069 libvlc_MediaPlayerPaused, 0070 libvlc_MediaPlayerStopped, 0071 libvlc_MediaPlayerForward, 0072 libvlc_MediaPlayerBackward, 0073 libvlc_MediaPlayerEndReached, 0074 libvlc_MediaPlayerEncounteredError, 0075 libvlc_MediaPlayerTimeChanged, 0076 libvlc_MediaPlayerPositionChanged, 0077 libvlc_MediaPlayerSeekableChanged, 0078 libvlc_MediaPlayerPausableChanged, 0079 libvlc_MediaPlayerTitleChanged, 0080 libvlc_MediaPlayerSnapshotTaken, 0081 libvlc_MediaPlayerLengthChanged, 0082 libvlc_MediaPlayerVout, 0083 libvlc_MediaPlayerCorked, 0084 libvlc_MediaPlayerUncorked, 0085 libvlc_MediaPlayerMuted, 0086 libvlc_MediaPlayerUnmuted, 0087 libvlc_MediaPlayerAudioVolume 0088 }; 0089 const int eventCount = sizeof(events) / sizeof(*events); 0090 for (int i = 0; i < eventCount; ++i) { 0091 libvlc_event_attach(manager, events[i], event_cb, this); 0092 } 0093 0094 // Deactivate video title overlay (i.e. name of the video displaying 0095 // at start. Since 2.1 that is handled via the API which in general is more 0096 // reliable than setting it via libvlc_new (or so I have been told....) 0097 libvlc_media_player_set_video_title_display(m_player, libvlc_position_disable, 0); 0098 } 0099 0100 MediaPlayer::~MediaPlayer() 0101 { 0102 libvlc_media_player_release(m_player); 0103 } 0104 0105 void MediaPlayer::setMedia(Media *media) 0106 { 0107 m_media = media; 0108 libvlc_media_player_set_media(m_player, *m_media); 0109 } 0110 0111 bool MediaPlayer::play() 0112 { 0113 m_doingPausedPlay = false; 0114 return libvlc_media_player_play(m_player) == 0; 0115 } 0116 0117 void MediaPlayer::pause() 0118 { 0119 m_doingPausedPlay = false; 0120 libvlc_media_player_set_pause(m_player, 1); 0121 } 0122 0123 void MediaPlayer::pausedPlay() 0124 { 0125 m_doingPausedPlay = true; 0126 libvlc_media_player_play(m_player); 0127 } 0128 0129 void MediaPlayer::resume() 0130 { 0131 m_doingPausedPlay = false; 0132 libvlc_media_player_set_pause(m_player, 0); 0133 } 0134 0135 void MediaPlayer::togglePause() 0136 { 0137 libvlc_media_player_pause(m_player); 0138 } 0139 0140 void MediaPlayer::stop() 0141 { 0142 m_doingPausedPlay = false; 0143 #ifdef __GNUC__ 0144 #warning changed to stop_async does this have impliciations 0145 #endif 0146 #if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(4, 0, 0, 0)) 0147 libvlc_media_player_stop_async(m_player); 0148 #else 0149 libvlc_media_player_stop(m_player); 0150 #endif 0151 } 0152 0153 qint64 MediaPlayer::length() const 0154 { 0155 return libvlc_media_player_get_length(m_player); 0156 } 0157 0158 qint64 MediaPlayer::time() const 0159 { 0160 return libvlc_media_player_get_time(m_player); 0161 } 0162 0163 void MediaPlayer::setTime(qint64 newTime) 0164 { 0165 #if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(4, 0, 0, 0)) 0166 libvlc_media_player_set_time(m_player, newTime, false /* not fast, but precise */); 0167 #else 0168 libvlc_media_player_set_time(m_player, newTime); 0169 #endif 0170 } 0171 0172 bool MediaPlayer::isSeekable() const 0173 { 0174 return libvlc_media_player_is_seekable(m_player); 0175 } 0176 0177 bool MediaPlayer::hasVideoOutput() const 0178 { 0179 return libvlc_media_player_has_vout(m_player) > 0; 0180 } 0181 0182 bool MediaPlayer::setSubtitle(int subtitle) 0183 { 0184 return libvlc_video_set_spu(m_player, subtitle) == 0; 0185 } 0186 0187 bool MediaPlayer::setSubtitle(const QString &file) 0188 { 0189 return libvlc_media_player_add_slave(m_player, 0190 libvlc_media_slave_type_subtitle, 0191 file.toUtf8().data(), 0192 true) == 0; 0193 } 0194 0195 void MediaPlayer::setTitle(int title) 0196 { 0197 libvlc_media_player_set_title(m_player, title); 0198 } 0199 0200 void MediaPlayer::setChapter(int chapter) 0201 { 0202 libvlc_media_player_set_chapter(m_player, chapter); 0203 } 0204 0205 QImage MediaPlayer::snapshot() const 0206 { 0207 QTemporaryFile tempFile(QDir::tempPath() % QDir::separator() % QStringLiteral("phonon-vlc-snapshot")); 0208 tempFile.open(); 0209 0210 // This function is sync. 0211 if (libvlc_video_take_snapshot(m_player, 0, tempFile.fileName().toLocal8Bit().data(), 0, 0) != 0) 0212 return QImage(); 0213 0214 return QImage(tempFile.fileName()); 0215 } 0216 0217 bool MediaPlayer::setAudioTrack(int track) 0218 { 0219 return libvlc_audio_set_track(m_player, track) == 0; 0220 } 0221 0222 void MediaPlayer::event_cb(const libvlc_event_t *event, void *opaque) 0223 { 0224 MediaPlayer *that = reinterpret_cast<MediaPlayer *>(opaque); 0225 Q_ASSERT(that); 0226 0227 // Do not forget to register for the events you want to handle here! 0228 switch (event->type) { 0229 case libvlc_MediaPlayerTimeChanged: 0230 QMetaObject::invokeMethod( 0231 that, "timeChanged", 0232 Qt::QueuedConnection, 0233 Q_ARG(qint64, event->u.media_player_time_changed.new_time)); 0234 break; 0235 case libvlc_MediaPlayerSeekableChanged: 0236 QMetaObject::invokeMethod( 0237 that, "seekableChanged", 0238 Qt::QueuedConnection, 0239 Q_ARG(bool, event->u.media_player_seekable_changed.new_seekable)); 0240 break; 0241 case libvlc_MediaPlayerLengthChanged: 0242 QMetaObject::invokeMethod( 0243 that, "lengthChanged", 0244 Qt::QueuedConnection, 0245 Q_ARG(qint64, event->u.media_player_length_changed.new_length)); 0246 break; 0247 case libvlc_MediaPlayerNothingSpecial: 0248 P_EMIT_STATE(NoState); 0249 break; 0250 case libvlc_MediaPlayerOpening: 0251 P_EMIT_STATE(OpeningState); 0252 break; 0253 case libvlc_MediaPlayerBuffering: 0254 QMetaObject::invokeMethod( 0255 that, "bufferChanged", 0256 Qt::QueuedConnection, 0257 Q_ARG(int, event->u.media_player_buffering.new_cache)); 0258 break; 0259 case libvlc_MediaPlayerPlaying: 0260 // Intercept state change and apply pausing once playing. 0261 if (that->m_doingPausedPlay) { 0262 that->m_doingPausedPlay = false; 0263 // VLC internally will call stop if a player can not be paused, this 0264 // can lead to deadlocks as stop is partially blocking, to avoid this 0265 // we explicitly do a queued stop whenever a player can not be paused. 0266 // In those situations pausedplay capability can not be provided, so 0267 // applications wanting to do pausedplay will need to handle it anyway 0268 // as faking a paused state when there is none would be a very code 0269 // intense workaround asking for weird abstraction leakage. 0270 // See kde bug 337604. 0271 if (libvlc_media_player_can_pause(that->m_player)) { 0272 that->pause(); 0273 } else { 0274 QMetaObject::invokeMethod(that, "stop", Qt::QueuedConnection); 0275 } 0276 } else 0277 P_EMIT_STATE(PlayingState); 0278 break; 0279 case libvlc_MediaPlayerPaused: 0280 P_EMIT_STATE(PausedState); 0281 break; 0282 case libvlc_MediaPlayerStopped: 0283 P_EMIT_STATE(StoppedState); 0284 break; 0285 case libvlc_MediaPlayerEndReached: 0286 P_EMIT_STATE(EndedState); 0287 break; 0288 case libvlc_MediaPlayerEncounteredError: 0289 P_EMIT_STATE(ErrorState); 0290 break; 0291 case libvlc_MediaPlayerVout: 0292 if (event->u.media_player_vout.new_count > 0) { 0293 P_EMIT_HAS_VIDEO(true); 0294 } else { 0295 P_EMIT_HAS_VIDEO(false); 0296 } 0297 break; 0298 case libvlc_MediaPlayerMediaChanged: 0299 break; 0300 case libvlc_MediaPlayerCorked: 0301 that->pause(); 0302 break; 0303 case libvlc_MediaPlayerUncorked: 0304 that->play(); 0305 break; 0306 case libvlc_MediaPlayerMuted: 0307 QMetaObject::invokeMethod( 0308 that, "mutedChanged", 0309 Qt::QueuedConnection, 0310 Q_ARG(bool, true)); 0311 break; 0312 case libvlc_MediaPlayerUnmuted: 0313 QMetaObject::invokeMethod( 0314 that, "mutedChanged", 0315 Qt::QueuedConnection, 0316 Q_ARG(bool, false)); 0317 break; 0318 case libvlc_MediaPlayerAudioVolume: 0319 QMetaObject::invokeMethod( 0320 that, "volumeChanged", 0321 Qt::QueuedConnection, 0322 Q_ARG(float, event->u.media_player_audio_volume.volume)); 0323 break; 0324 case libvlc_MediaPlayerForward: 0325 case libvlc_MediaPlayerBackward: 0326 case libvlc_MediaPlayerPositionChanged: 0327 case libvlc_MediaPlayerPausableChanged: 0328 case libvlc_MediaPlayerTitleChanged: 0329 case libvlc_MediaPlayerSnapshotTaken: // Snapshot call is sync, so this is useless. 0330 default: 0331 break; 0332 QString msg = QString("Unknown event: ") + QString::number(event->type); 0333 Q_ASSERT_X(false, "event_cb", qPrintable(msg)); 0334 break; 0335 } 0336 } 0337 0338 QDebug operator<<(QDebug dbg, const MediaPlayer::State &s) 0339 { 0340 QString name; 0341 switch (s) { 0342 case MediaPlayer::NoState: 0343 name = QLatin1String("MediaPlayer::NoState"); 0344 break; 0345 case MediaPlayer::OpeningState: 0346 name = QLatin1String("MediaPlayer::OpeningState"); 0347 break; 0348 case MediaPlayer::BufferingState: 0349 name = QLatin1String("MediaPlayer::BufferingState"); 0350 break; 0351 case MediaPlayer::PlayingState: 0352 name = QLatin1String("MediaPlayer::PlayingState"); 0353 break; 0354 case MediaPlayer::PausedState: 0355 name = QLatin1String("MediaPlayer::PausedState"); 0356 break; 0357 case MediaPlayer::StoppedState: 0358 name = QLatin1String("MediaPlayer::StoppedState"); 0359 break; 0360 case MediaPlayer::EndedState: 0361 name = QLatin1String("MediaPlayer::EndedState"); 0362 break; 0363 case MediaPlayer::ErrorState: 0364 name = QLatin1String("MediaPlayer::ErrorState"); 0365 break; 0366 } 0367 dbg.nospace() << "State(" << qPrintable(name) << ")"; 0368 return dbg.space(); 0369 } 0370 0371 void MediaPlayer::setAudioFade(qreal fade) 0372 { 0373 m_fadeAmount = fade; 0374 setVolumeInternal(); 0375 } 0376 0377 void MediaPlayer::setAudioVolume(int volume) 0378 { 0379 m_volume = volume; 0380 setVolumeInternal(); 0381 } 0382 0383 bool MediaPlayer::mute() const 0384 { 0385 return libvlc_audio_get_mute(m_player); 0386 } 0387 0388 void MediaPlayer::setMute(bool mute) 0389 { 0390 libvlc_audio_set_mute(m_player, mute); 0391 } 0392 0393 void MediaPlayer::setVolumeInternal() 0394 { 0395 libvlc_audio_set_volume(m_player, m_volume * m_fadeAmount); 0396 } 0397 0398 void MediaPlayer::setCdTrack(int track) 0399 { 0400 if (!m_media) 0401 return; 0402 #ifdef __GNUC__ 0403 #warning changed to stop_async does this have impliciations 0404 #endif 0405 #if (LIBVLC_VERSION_INT >= LIBVLC_VERSION(4, 0, 0, 0)) 0406 libvlc_media_player_stop_async(m_player); 0407 #else 0408 libvlc_media_player_stop(m_player); 0409 #endif 0410 m_media->setCdTrack(track); 0411 libvlc_media_player_set_media(m_player, *m_media); 0412 libvlc_media_player_play(m_player); 0413 } 0414 0415 void MediaPlayer::setEqualizer(libvlc_equalizer_t *equalizer) 0416 { 0417 libvlc_media_player_set_equalizer(m_player, equalizer); 0418 } 0419 0420 } // namespace VLC 0421 } // namespace Phonon