MediaControlKeyManager.cpp (7916B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include "MediaControlKeyManager.h" 6 7 #include "MediaControlService.h" 8 #include "MediaControlUtils.h" 9 #include "mozilla/AbstractThread.h" 10 #include "mozilla/Logging.h" 11 #include "mozilla/Preferences.h" 12 #include "mozilla/Services.h" 13 #include "mozilla/StaticPrefs_media.h" 14 #include "mozilla/widget/MediaKeysEventSourceFactory.h" 15 #include "nsContentUtils.h" 16 #include "nsIObserverService.h" 17 18 #undef LOG 19 #define LOG(msg, ...) \ 20 MOZ_LOG(gMediaControlLog, LogLevel::Debug, \ 21 ("MediaControlKeyManager=%p, " msg, this, ##__VA_ARGS__)) 22 23 #undef LOG_INFO 24 #define LOG_INFO(msg, ...) \ 25 MOZ_LOG(gMediaControlLog, LogLevel::Info, \ 26 ("MediaControlKeyManager=%p, " msg, this, ##__VA_ARGS__)) 27 28 #define MEDIA_CONTROL_PREF "media.hardwaremediakeys.enabled" 29 30 namespace mozilla::dom { 31 32 bool MediaControlKeyManager::IsOpened() const { 33 return mEventSource && mEventSource->IsOpened(); 34 } 35 36 bool MediaControlKeyManager::Open() { 37 if (IsOpened()) { 38 return true; 39 } 40 return StartMonitoringControlKeys(); 41 } 42 43 void MediaControlKeyManager::Close() { 44 // We don't call parent's `Close()` because we want to keep the listener 45 // (MediaControlKeyHandler) all the time. It would be manually removed by 46 // `MediaControlService` when shutdown. 47 StopMonitoringControlKeys(); 48 } 49 50 MediaControlKeyManager::MediaControlKeyManager() 51 : mObserver(new Observer(this)) { 52 nsContentUtils::RegisterShutdownObserver(mObserver); 53 Preferences::AddStrongObserver(mObserver, MEDIA_CONTROL_PREF); 54 } 55 56 MediaControlKeyManager::~MediaControlKeyManager() { Shutdown(); } 57 58 void MediaControlKeyManager::Shutdown() { 59 StopMonitoringControlKeys(); 60 mEventSource = nullptr; 61 if (mObserver) { 62 nsContentUtils::UnregisterShutdownObserver(mObserver); 63 Preferences::RemoveObserver(mObserver, MEDIA_CONTROL_PREF); 64 mObserver = nullptr; 65 } 66 } 67 68 bool MediaControlKeyManager::StartMonitoringControlKeys() { 69 if (!StaticPrefs::media_hardwaremediakeys_enabled()) { 70 return false; 71 } 72 73 if (!mEventSource) { 74 mEventSource = widget::CreateMediaControlKeySource(); 75 } 76 if (mEventSource && mEventSource->Open()) { 77 LOG_INFO("StartMonitoringControlKeys"); 78 mEventSource->SetPlaybackState(mPlaybackState); 79 mEventSource->SetMediaMetadata(mMetadata); 80 mEventSource->SetSupportedMediaKeys(mSupportedKeys); 81 mEventSource->AddListener(this); 82 return true; 83 } 84 // Fail to open or create event source (eg. when cross-compiling with MinGW, 85 // we cannot use the related WinAPI) 86 return false; 87 } 88 89 void MediaControlKeyManager::StopMonitoringControlKeys() { 90 if (!mEventSource || !mEventSource->IsOpened()) { 91 return; 92 } 93 94 LOG_INFO("StopMonitoringControlKeys"); 95 mEventSource->Close(); 96 if (StaticPrefs::media_mediacontrol_testingevents_enabled()) { 97 // Close the source would reset the displayed playback state and metadata. 98 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) { 99 obs->NotifyObservers(nullptr, "media-displayed-playback-changed", 100 nullptr); 101 obs->NotifyObservers(nullptr, "media-displayed-metadata-changed", 102 nullptr); 103 obs->NotifyObservers(nullptr, "media-position-state-changed", nullptr); 104 } 105 } 106 } 107 108 void MediaControlKeyManager::OnActionPerformed( 109 const MediaControlAction& aAction) { 110 for (auto listener : mListeners) { 111 listener->OnActionPerformed(aAction); 112 } 113 } 114 115 void MediaControlKeyManager::SetPlaybackState( 116 MediaSessionPlaybackState aState) { 117 if (mEventSource && mEventSource->IsOpened()) { 118 mEventSource->SetPlaybackState(aState); 119 } 120 mPlaybackState = aState; 121 LOG_INFO("playbackState=%s", ToMediaSessionPlaybackStateStr(mPlaybackState)); 122 if (StaticPrefs::media_mediacontrol_testingevents_enabled()) { 123 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) { 124 obs->NotifyObservers(nullptr, "media-displayed-playback-changed", 125 nullptr); 126 } 127 } 128 } 129 130 MediaSessionPlaybackState MediaControlKeyManager::GetPlaybackState() const { 131 return (mEventSource && mEventSource->IsOpened()) 132 ? mEventSource->GetPlaybackState() 133 : mPlaybackState; 134 } 135 136 void MediaControlKeyManager::SetMediaMetadata( 137 const MediaMetadataBase& aMetadata) { 138 if (mEventSource && mEventSource->IsOpened()) { 139 mEventSource->SetMediaMetadata(aMetadata); 140 } 141 mMetadata = aMetadata; 142 LOG_INFO("title=%s, artist=%s album=%s", 143 NS_ConvertUTF16toUTF8(mMetadata.mTitle).get(), 144 NS_ConvertUTF16toUTF8(mMetadata.mArtist).get(), 145 NS_ConvertUTF16toUTF8(mMetadata.mAlbum).get()); 146 if (StaticPrefs::media_mediacontrol_testingevents_enabled()) { 147 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) { 148 obs->NotifyObservers(nullptr, "media-displayed-metadata-changed", 149 nullptr); 150 } 151 } 152 } 153 154 void MediaControlKeyManager::SetSupportedMediaKeys( 155 const MediaKeysArray& aSupportedKeys) { 156 mSupportedKeys.Clear(); 157 for (const auto& key : aSupportedKeys) { 158 LOG_INFO("Supported keys=%s", GetEnumString(key).get()); 159 mSupportedKeys.AppendElement(key); 160 } 161 if (mEventSource && mEventSource->IsOpened()) { 162 mEventSource->SetSupportedMediaKeys(mSupportedKeys); 163 } 164 } 165 166 void MediaControlKeyManager::SetEnableFullScreen(bool aIsEnabled) { 167 LOG_INFO("Set fullscreen %s", aIsEnabled ? "enabled" : "disabled"); 168 if (mEventSource && mEventSource->IsOpened()) { 169 mEventSource->SetEnableFullScreen(aIsEnabled); 170 } 171 } 172 173 void MediaControlKeyManager::SetEnablePictureInPictureMode(bool aIsEnabled) { 174 LOG_INFO("Set Picture-In-Picture mode %s", 175 aIsEnabled ? "enabled" : "disabled"); 176 if (mEventSource && mEventSource->IsOpened()) { 177 mEventSource->SetEnablePictureInPictureMode(aIsEnabled); 178 } 179 } 180 181 void MediaControlKeyManager::SetPositionState( 182 const Maybe<PositionState>& aState) { 183 if (aState) { 184 LOG_INFO("Set PositionState, duration=%f, playbackRate=%f, position=%f", 185 aState->mDuration, aState->mPlaybackRate, 186 aState->mLastReportedPlaybackPosition); 187 } else { 188 LOG_INFO("Set PositionState, Nothing"); 189 } 190 191 if (mEventSource && mEventSource->IsOpened()) { 192 mEventSource->SetPositionState(aState); 193 } 194 195 if (StaticPrefs::media_mediacontrol_testingevents_enabled()) { 196 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) { 197 obs->NotifyObservers(nullptr, "media-position-state-changed", nullptr); 198 } 199 } 200 } 201 202 void MediaControlKeyManager::OnPreferenceChange() { 203 const bool isPrefEnabled = StaticPrefs::media_hardwaremediakeys_enabled(); 204 // Only start monitoring control keys when the pref is on and having a main 205 // controller that means already having media which need to be controlled. 206 const bool shouldMonitorKeys = 207 isPrefEnabled && MediaControlService::GetService()->GetMainController(); 208 LOG_INFO("Preference change : %s media control", 209 isPrefEnabled ? "enable" : "disable"); 210 if (shouldMonitorKeys) { 211 (void)StartMonitoringControlKeys(); 212 } else { 213 StopMonitoringControlKeys(); 214 } 215 } 216 217 NS_IMPL_ISUPPORTS(MediaControlKeyManager::Observer, nsIObserver); 218 219 MediaControlKeyManager::Observer::Observer(MediaControlKeyManager* aManager) 220 : mManager(aManager) {} 221 222 NS_IMETHODIMP 223 MediaControlKeyManager::Observer::Observe(nsISupports* aSubject, 224 const char* aTopic, 225 const char16_t* aData) { 226 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { 227 mManager->Shutdown(); 228 } else if (!strcmp(aTopic, "nsPref:changed")) { 229 mManager->OnPreferenceChange(); 230 } 231 return NS_OK; 232 } 233 234 } // namespace mozilla::dom