summaryrefslogtreecommitdiffstats
path: root/dom/media/mediacontrol/AudioFocusManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/media/mediacontrol/AudioFocusManager.cpp134
1 files changed, 134 insertions, 0 deletions
diff --git a/dom/media/mediacontrol/AudioFocusManager.cpp b/dom/media/mediacontrol/AudioFocusManager.cpp
new file mode 100644
index 0000000000..24ac7374d4
--- /dev/null
+++ b/dom/media/mediacontrol/AudioFocusManager.cpp
@@ -0,0 +1,134 @@
+/* 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 "AudioFocusManager.h"
+
+#include "MediaController.h"
+#include "MediaControlUtils.h"
+#include "MediaControlService.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/Logging.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/Telemetry.h"
+#include "nsThreadUtils.h"
+
+#undef LOG
+#define LOG(msg, ...) \
+ MOZ_LOG(gMediaControlLog, LogLevel::Debug, \
+ ("AudioFocusManager=%p, " msg, this, ##__VA_ARGS__))
+
+namespace mozilla::dom {
+
+void AudioFocusManager::RequestAudioFocus(IMediaController* aController) {
+ MOZ_ASSERT(aController);
+ if (mOwningFocusControllers.Contains(aController)) {
+ return;
+ }
+ const bool hasManagedAudioFocus = ClearFocusControllersIfNeeded();
+ LOG("Controller %" PRId64 " grants audio focus", aController->Id());
+ mOwningFocusControllers.AppendElement(aController);
+ if (hasManagedAudioFocus) {
+ AccumulateCategorical(
+ mozilla::Telemetry::LABELS_TABS_AUDIO_COMPETITION::ManagedFocusByGecko);
+ } else if (GetAudioFocusNums() == 1) {
+ // Only one audible tab is playing within gecko.
+ AccumulateCategorical(
+ mozilla::Telemetry::LABELS_TABS_AUDIO_COMPETITION::None);
+ } else {
+ // Multiple audible tabs are playing at the same time within gecko.
+ CreateTimerForUpdatingTelemetry();
+ }
+}
+
+void AudioFocusManager::RevokeAudioFocus(IMediaController* aController) {
+ MOZ_ASSERT(aController);
+ if (!mOwningFocusControllers.Contains(aController)) {
+ return;
+ }
+ LOG("Controller %" PRId64 " loses audio focus", aController->Id());
+ mOwningFocusControllers.RemoveElement(aController);
+}
+
+bool AudioFocusManager::ClearFocusControllersIfNeeded() {
+ // Enable audio focus management will start the audio competition which is
+ // only allowing one controller playing at a time.
+ if (!StaticPrefs::media_audioFocus_management()) {
+ return false;
+ }
+
+ bool hasStoppedAnyController = false;
+ for (auto& controller : mOwningFocusControllers) {
+ LOG("Controller %" PRId64 " loses audio focus in audio competitition",
+ controller->Id());
+ hasStoppedAnyController = true;
+ controller->Stop();
+ }
+ mOwningFocusControllers.Clear();
+ return hasStoppedAnyController;
+}
+
+uint32_t AudioFocusManager::GetAudioFocusNums() const {
+ return mOwningFocusControllers.Length();
+}
+
+void AudioFocusManager::CreateTimerForUpdatingTelemetry() {
+ MOZ_ASSERT(NS_IsMainThread());
+ // Already create the timer.
+ if (mTelemetryTimer) {
+ return;
+ }
+
+ const uint32_t focusNum = GetAudioFocusNums();
+ MOZ_ASSERT(focusNum > 1);
+
+ RefPtr<MediaControlService> service = MediaControlService::GetService();
+ MOZ_ASSERT(service);
+ const uint32_t activeControllerNum = service->GetActiveControllersNum();
+
+ // It takes time if users want to manually manage the audio competition by
+ // pausing one of playing tabs. So we will check the status after a short
+ // while to see if users handle the audio competition, or simply ignore it.
+ nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
+ "AudioFocusManager::RequestAudioFocus",
+ [focusNum, activeControllerNum]() {
+ if (RefPtr<MediaControlService> service =
+ MediaControlService::GetService()) {
+ service->GetAudioFocusManager().UpdateTelemetryDataFromTimer(
+ focusNum, activeControllerNum);
+ }
+ });
+ mTelemetryTimer =
+ SimpleTimer::Create(task, 4000, GetMainThreadSerialEventTarget());
+}
+
+void AudioFocusManager::UpdateTelemetryDataFromTimer(
+ uint32_t aPrevFocusNum, uint64_t aPrevActiveControllerNum) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mTelemetryTimer);
+ // Users pause or mute tabs which decreases the amount of audible playing
+ // tabs, which should not affect the total controller amount.
+ if (GetAudioFocusNums() < aPrevFocusNum) {
+ // If active controller amount is not equal, that means controllers got
+ // deactivated by other reasons, such as reaching to the end, which are not
+ // the situation we would like to accumulate for telemetry.
+ if (MediaControlService::GetService()->GetActiveControllersNum() ==
+ aPrevActiveControllerNum) {
+ AccumulateCategorical(mozilla::Telemetry::LABELS_TABS_AUDIO_COMPETITION::
+ ManagedFocusByUser);
+ }
+ } else {
+ AccumulateCategorical(
+ mozilla::Telemetry::LABELS_TABS_AUDIO_COMPETITION::Ignored);
+ }
+ mTelemetryTimer = nullptr;
+}
+
+AudioFocusManager::~AudioFocusManager() {
+ if (mTelemetryTimer) {
+ mTelemetryTimer->Cancel();
+ mTelemetryTimer = nullptr;
+ }
+}
+
+} // namespace mozilla::dom