Bug 1683788 - add telemetry probe for the audio competition. r=bryce

If multiple audible tabs play at the same time, we call it as `Audio Competition`.

When the audio competition happens, there are several possibilities

(1) ignore that and keep multiple tabs playing at the same time
(2) user manually handles that by pausing or muting audible playing tabs, remaining only an audible playing tab
(3) user enables audio focus management, so gecko would pause them and only allow one audible tab playing at a time

By collecting these situations, we can know the details of how user would react for the audio compeition.

In addition, we also collect the situation where no audio competition happens, in order to know whether the audio competition is common to users.

Differential Revision: https://phabricator.services.mozilla.com/D100292
This commit is contained in:
alwu 2020-12-24 02:42:56 +00:00
Родитель 2578d1b239
Коммит df9c134aa6
3 изменённых файлов: 103 добавлений и 5 удалений

Просмотреть файл

@ -10,6 +10,8 @@
#include "mozilla/dom/CanonicalBrowsingContext.h" #include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/Logging.h" #include "mozilla/Logging.h"
#include "mozilla/StaticPrefs_media.h" #include "mozilla/StaticPrefs_media.h"
#include "mozilla/Telemetry.h"
#include "nsThreadUtils.h"
#undef LOG #undef LOG
#define LOG(msg, ...) \ #define LOG(msg, ...) \
@ -23,9 +25,20 @@ void AudioFocusManager::RequestAudioFocus(IMediaController* aController) {
if (mOwningFocusControllers.Contains(aController)) { if (mOwningFocusControllers.Contains(aController)) {
return; return;
} }
ClearFocusControllersIfNeeded(); const bool hasManagedAudioFocus = ClearFocusControllersIfNeeded();
LOG("Controller %" PRId64 " grants audio focus", aController->Id()); LOG("Controller %" PRId64 " grants audio focus", aController->Id());
mOwningFocusControllers.AppendElement(aController); 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) { void AudioFocusManager::RevokeAudioFocus(IMediaController* aController) {
@ -37,23 +50,85 @@ void AudioFocusManager::RevokeAudioFocus(IMediaController* aController) {
mOwningFocusControllers.RemoveElement(aController); mOwningFocusControllers.RemoveElement(aController);
} }
void AudioFocusManager::ClearFocusControllersIfNeeded() { bool AudioFocusManager::ClearFocusControllersIfNeeded() {
// Enable audio focus management will start the audio competition which is // Enable audio focus management will start the audio competition which is
// only allowing one controller playing at a time. // only allowing one controller playing at a time.
if (!StaticPrefs::media_audioFocus_management()) { if (!StaticPrefs::media_audioFocus_management()) {
return; return false;
} }
bool hasStoppedAnyController = false;
for (auto& controller : mOwningFocusControllers) { for (auto& controller : mOwningFocusControllers) {
LOG("Controller %" PRId64 " loses audio focus in audio competitition", LOG("Controller %" PRId64 " loses audio focus in audio competitition",
controller->Id()); controller->Id());
hasStoppedAnyController = true;
controller->Stop(); controller->Stop();
} }
mOwningFocusControllers.Clear(); mOwningFocusControllers.Clear();
return hasStoppedAnyController;
} }
uint32_t AudioFocusManager::GetAudioFocusNums() const { uint32_t AudioFocusManager::GetAudioFocusNums() const {
return mOwningFocusControllers.Length(); 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 } // namespace mozilla::dom

Просмотреть файл

@ -7,6 +7,7 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "nsTArray.h" #include "nsTArray.h"
#include "VideoUtils.h"
namespace mozilla { namespace mozilla {
namespace dom { namespace dom {
@ -29,15 +30,24 @@ class AudioFocusManager {
void RevokeAudioFocus(IMediaController* aController); void RevokeAudioFocus(IMediaController* aController);
explicit AudioFocusManager() = default; explicit AudioFocusManager() = default;
~AudioFocusManager() = default; ~AudioFocusManager();
uint32_t GetAudioFocusNums() const; uint32_t GetAudioFocusNums() const;
private: private:
friend class MediaControlService; friend class MediaControlService;
void ClearFocusControllersIfNeeded(); // Return true if we manage audio focus by clearing other controllers owning
// audio focus before assigning audio focus to the new controller.
bool ClearFocusControllersIfNeeded();
void CreateTimerForUpdatingTelemetry();
// This would check if user has managed audio focus by themselves and update
// the result to telemetry. This method should only be called from timer.
void UpdateTelemetryDataFromTimer(uint32_t aPrevFocusNum,
uint64_t aPrevActiveControllerNum);
nsTArray<RefPtr<IMediaController>> mOwningFocusControllers; nsTArray<RefPtr<IMediaController>> mOwningFocusControllers;
RefPtr<SimpleTimer> mTelemetryTimer;
}; };
} // namespace dom } // namespace dom

Просмотреть файл

@ -15499,6 +15499,19 @@
"description": "The number of times `play/pause/stop` actions are being handled on either default handler or media session action handler.", "description": "The number of times `play/pause/stop` actions are being handled on either default handler or media session action handler.",
"releaseChannelCollection": "opt-out" "releaseChannelCollection": "opt-out"
}, },
"TABS_AUDIO_COMPETITION": {
"record_in_processes": ["main"],
"products": ["firefox"],
"alert_emails": [
"alwu@mozilla.com"
],
"bug_numbers": [1683788],
"expires_in_version": "91",
"kind": "categorical",
"labels": ["None", "Ignored", "ManagedFocusByUser", "ManagedFocusByGecko"],
"description": "This probe would be reported whenever (1) media starts without encountering audio competition (2) encountering audio competition but users ignore it (keep all media continually playing) (3) encountering audio competition and users manually handle it (4) encountering audio competition and Gecko handled it. The value of each state would be the total happening count for specific situation during the whole session.",
"releaseChannelCollection": "opt-out"
},
"QM_QUOTA_INFO_LOAD_TIME_V0": { "QM_QUOTA_INFO_LOAD_TIME_V0": {
"record_in_processes": ["main"], "record_in_processes": ["main"],
"products": ["firefox", "fennec"], "products": ["firefox", "fennec"],