summaryrefslogtreecommitdiffstats
path: root/dom/media/mediacontrol/MediaControlKeyManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/mediacontrol/MediaControlKeyManager.cpp')
-rw-r--r--dom/media/mediacontrol/MediaControlKeyManager.cpp228
1 files changed, 228 insertions, 0 deletions
diff --git a/dom/media/mediacontrol/MediaControlKeyManager.cpp b/dom/media/mediacontrol/MediaControlKeyManager.cpp
new file mode 100644
index 0000000000..4cb562aa84
--- /dev/null
+++ b/dom/media/mediacontrol/MediaControlKeyManager.cpp
@@ -0,0 +1,228 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MediaControlKeyManager.h"
+
+#include "MediaControlUtils.h"
+#include "MediaControlService.h"
+#include "mozilla/AbstractThread.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/widget/MediaKeysEventSourceFactory.h"
+#include "nsContentUtils.h"
+#include "nsIObserverService.h"
+
+#undef LOG
+#define LOG(msg, ...) \
+ MOZ_LOG(gMediaControlLog, LogLevel::Debug, \
+ ("MediaControlKeyManager=%p, " msg, this, ##__VA_ARGS__))
+
+#undef LOG_INFO
+#define LOG_INFO(msg, ...) \
+ MOZ_LOG(gMediaControlLog, LogLevel::Info, \
+ ("MediaControlKeyManager=%p, " msg, this, ##__VA_ARGS__))
+
+#define MEDIA_CONTROL_PREF "media.hardwaremediakeys.enabled"
+
+namespace mozilla::dom {
+
+bool MediaControlKeyManager::IsOpened() const {
+ return mEventSource && mEventSource->IsOpened();
+}
+
+bool MediaControlKeyManager::Open() {
+ if (IsOpened()) {
+ return true;
+ }
+ const bool isEnabledMediaControl = StartMonitoringControlKeys();
+ if (isEnabledMediaControl) {
+ RefPtr<MediaControlService> service = MediaControlService::GetService();
+ MOZ_ASSERT(service);
+ service->NotifyMediaControlHasEverBeenEnabled();
+ }
+ return isEnabledMediaControl;
+}
+
+void MediaControlKeyManager::Close() {
+ // We don't call parent's `Close()` because we want to keep the listener
+ // (MediaControlKeyHandler) all the time. It would be manually removed by
+ // `MediaControlService` when shutdown.
+ StopMonitoringControlKeys();
+}
+
+MediaControlKeyManager::MediaControlKeyManager()
+ : mObserver(new Observer(this)) {
+ nsContentUtils::RegisterShutdownObserver(mObserver);
+ Preferences::AddStrongObserver(mObserver, MEDIA_CONTROL_PREF);
+}
+
+MediaControlKeyManager::~MediaControlKeyManager() { Shutdown(); }
+
+void MediaControlKeyManager::Shutdown() {
+ StopMonitoringControlKeys();
+ mEventSource = nullptr;
+ if (mObserver) {
+ nsContentUtils::UnregisterShutdownObserver(mObserver);
+ Preferences::RemoveObserver(mObserver, MEDIA_CONTROL_PREF);
+ mObserver = nullptr;
+ }
+}
+
+bool MediaControlKeyManager::StartMonitoringControlKeys() {
+ if (!StaticPrefs::media_hardwaremediakeys_enabled()) {
+ return false;
+ }
+
+ if (!mEventSource) {
+ mEventSource = widget::CreateMediaControlKeySource();
+ }
+ if (mEventSource && mEventSource->Open()) {
+ LOG_INFO("StartMonitoringControlKeys");
+ mEventSource->SetPlaybackState(mPlaybackState);
+ mEventSource->SetMediaMetadata(mMetadata);
+ mEventSource->SetSupportedMediaKeys(mSupportedKeys);
+ mEventSource->AddListener(this);
+ return true;
+ }
+ // Fail to open or create event source (eg. when cross-compiling with MinGW,
+ // we cannot use the related WinAPI)
+ return false;
+}
+
+void MediaControlKeyManager::StopMonitoringControlKeys() {
+ if (!mEventSource || !mEventSource->IsOpened()) {
+ return;
+ }
+
+ LOG_INFO("StopMonitoringControlKeys");
+ mEventSource->Close();
+ if (StaticPrefs::media_mediacontrol_testingevents_enabled()) {
+ // Close the source would reset the displayed playback state and metadata.
+ if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
+ obs->NotifyObservers(nullptr, "media-displayed-playback-changed",
+ nullptr);
+ obs->NotifyObservers(nullptr, "media-displayed-metadata-changed",
+ nullptr);
+ }
+ }
+}
+
+void MediaControlKeyManager::OnActionPerformed(
+ const MediaControlAction& aAction) {
+ for (auto listener : mListeners) {
+ listener->OnActionPerformed(aAction);
+ }
+}
+
+void MediaControlKeyManager::SetPlaybackState(
+ MediaSessionPlaybackState aState) {
+ if (mEventSource && mEventSource->IsOpened()) {
+ mEventSource->SetPlaybackState(aState);
+ }
+ mPlaybackState = aState;
+ LOG_INFO("playbackState=%s", ToMediaSessionPlaybackStateStr(mPlaybackState));
+ if (StaticPrefs::media_mediacontrol_testingevents_enabled()) {
+ if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
+ obs->NotifyObservers(nullptr, "media-displayed-playback-changed",
+ nullptr);
+ }
+ }
+}
+
+MediaSessionPlaybackState MediaControlKeyManager::GetPlaybackState() const {
+ return (mEventSource && mEventSource->IsOpened())
+ ? mEventSource->GetPlaybackState()
+ : mPlaybackState;
+}
+
+void MediaControlKeyManager::SetMediaMetadata(
+ const MediaMetadataBase& aMetadata) {
+ if (mEventSource && mEventSource->IsOpened()) {
+ mEventSource->SetMediaMetadata(aMetadata);
+ }
+ mMetadata = aMetadata;
+ LOG_INFO("title=%s, artist=%s album=%s",
+ NS_ConvertUTF16toUTF8(mMetadata.mTitle).get(),
+ NS_ConvertUTF16toUTF8(mMetadata.mArtist).get(),
+ NS_ConvertUTF16toUTF8(mMetadata.mAlbum).get());
+ if (StaticPrefs::media_mediacontrol_testingevents_enabled()) {
+ if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
+ obs->NotifyObservers(nullptr, "media-displayed-metadata-changed",
+ nullptr);
+ }
+ }
+}
+
+void MediaControlKeyManager::SetSupportedMediaKeys(
+ const MediaKeysArray& aSupportedKeys) {
+ mSupportedKeys.Clear();
+ for (const auto& key : aSupportedKeys) {
+ LOG_INFO("Supported keys=%s", ToMediaControlKeyStr(key));
+ mSupportedKeys.AppendElement(key);
+ }
+ if (mEventSource && mEventSource->IsOpened()) {
+ mEventSource->SetSupportedMediaKeys(mSupportedKeys);
+ }
+}
+
+void MediaControlKeyManager::SetEnableFullScreen(bool aIsEnabled) {
+ LOG_INFO("Set fullscreen %s", aIsEnabled ? "enabled" : "disabled");
+ if (mEventSource && mEventSource->IsOpened()) {
+ mEventSource->SetEnableFullScreen(aIsEnabled);
+ }
+}
+
+void MediaControlKeyManager::SetEnablePictureInPictureMode(bool aIsEnabled) {
+ LOG_INFO("Set Picture-In-Picture mode %s",
+ aIsEnabled ? "enabled" : "disabled");
+ if (mEventSource && mEventSource->IsOpened()) {
+ mEventSource->SetEnablePictureInPictureMode(aIsEnabled);
+ }
+}
+
+void MediaControlKeyManager::SetPositionState(const PositionState& aState) {
+ LOG_INFO("Set PositionState, duration=%f, playbackRate=%f, position=%f",
+ aState.mDuration, aState.mPlaybackRate,
+ aState.mLastReportedPlaybackPosition);
+ if (mEventSource && mEventSource->IsOpened()) {
+ mEventSource->SetPositionState(aState);
+ }
+}
+
+void MediaControlKeyManager::OnPreferenceChange() {
+ const bool isPrefEnabled = StaticPrefs::media_hardwaremediakeys_enabled();
+ // Only start monitoring control keys when the pref is on and having a main
+ // controller that means already having media which need to be controlled.
+ const bool shouldMonitorKeys =
+ isPrefEnabled && MediaControlService::GetService()->GetMainController();
+ LOG_INFO("Preference change : %s media control",
+ isPrefEnabled ? "enable" : "disable");
+ if (shouldMonitorKeys) {
+ Unused << StartMonitoringControlKeys();
+ } else {
+ StopMonitoringControlKeys();
+ }
+}
+
+NS_IMPL_ISUPPORTS(MediaControlKeyManager::Observer, nsIObserver);
+
+MediaControlKeyManager::Observer::Observer(MediaControlKeyManager* aManager)
+ : mManager(aManager) {}
+
+NS_IMETHODIMP
+MediaControlKeyManager::Observer::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData) {
+ if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+ mManager->Shutdown();
+ } else if (!strcmp(aTopic, "nsPref:changed")) {
+ mManager->OnPreferenceChange();
+ }
+ return NS_OK;
+}
+
+} // namespace mozilla::dom