summaryrefslogtreecommitdiffstats
path: root/dom/media/mediacontrol/MediaPlaybackStatus.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/media/mediacontrol/MediaPlaybackStatus.cpp142
1 files changed, 142 insertions, 0 deletions
diff --git a/dom/media/mediacontrol/MediaPlaybackStatus.cpp b/dom/media/mediacontrol/MediaPlaybackStatus.cpp
new file mode 100644
index 0000000000..80dedf8599
--- /dev/null
+++ b/dom/media/mediacontrol/MediaPlaybackStatus.cpp
@@ -0,0 +1,142 @@
+/* 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 "MediaPlaybackStatus.h"
+
+#include "MediaControlUtils.h"
+
+namespace mozilla::dom {
+
+#undef LOG
+#define LOG(msg, ...) \
+ MOZ_LOG(gMediaControlLog, LogLevel::Debug, \
+ ("MediaPlaybackStatus=%p, " msg, this, ##__VA_ARGS__))
+
+void MediaPlaybackStatus::UpdateMediaPlaybackState(uint64_t aContextId,
+ MediaPlaybackState aState) {
+ LOG("Update playback state '%s' for context %" PRIu64,
+ ToMediaPlaybackStateStr(aState), aContextId);
+ MOZ_ASSERT(NS_IsMainThread());
+
+ ContextMediaInfo& info = GetNotNullContextInfo(aContextId);
+ if (aState == MediaPlaybackState::eStarted) {
+ info.IncreaseControlledMediaNum();
+ } else if (aState == MediaPlaybackState::eStopped) {
+ info.DecreaseControlledMediaNum();
+ } else if (aState == MediaPlaybackState::ePlayed) {
+ info.IncreasePlayingMediaNum();
+ } else {
+ MOZ_ASSERT(aState == MediaPlaybackState::ePaused);
+ info.DecreasePlayingMediaNum();
+ }
+
+ // The context still has controlled media, we should keep its alive.
+ if (info.IsAnyMediaBeingControlled()) {
+ return;
+ }
+ MOZ_ASSERT(!info.IsPlaying());
+ MOZ_ASSERT(!info.IsAudible());
+ // DO NOT access `info` after this line.
+ DestroyContextInfo(aContextId);
+}
+
+void MediaPlaybackStatus::DestroyContextInfo(uint64_t aContextId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ LOG("Remove context %" PRIu64, aContextId);
+ mContextInfoMap.Remove(aContextId);
+ // If the removed context is owning the audio focus, we would find another
+ // context to take the audio focus if it's possible.
+ if (IsContextOwningAudioFocus(aContextId)) {
+ ChooseNewContextToOwnAudioFocus();
+ }
+}
+
+void MediaPlaybackStatus::UpdateMediaAudibleState(uint64_t aContextId,
+ MediaAudibleState aState) {
+ LOG("Update audible state '%s' for context %" PRIu64,
+ ToMediaAudibleStateStr(aState), aContextId);
+ MOZ_ASSERT(NS_IsMainThread());
+ ContextMediaInfo& info = GetNotNullContextInfo(aContextId);
+ if (aState == MediaAudibleState::eAudible) {
+ info.IncreaseAudibleMediaNum();
+ } else {
+ MOZ_ASSERT(aState == MediaAudibleState::eInaudible);
+ info.DecreaseAudibleMediaNum();
+ }
+ if (ShouldRequestAudioFocusForInfo(info)) {
+ SetOwningAudioFocusContextId(Some(aContextId));
+ } else if (ShouldAbandonAudioFocusForInfo(info)) {
+ ChooseNewContextToOwnAudioFocus();
+ }
+}
+
+bool MediaPlaybackStatus::IsPlaying() const {
+ MOZ_ASSERT(NS_IsMainThread());
+ return std::any_of(mContextInfoMap.Values().cbegin(),
+ mContextInfoMap.Values().cend(),
+ [](const auto& info) { return info->IsPlaying(); });
+}
+
+bool MediaPlaybackStatus::IsAudible() const {
+ MOZ_ASSERT(NS_IsMainThread());
+ return std::any_of(mContextInfoMap.Values().cbegin(),
+ mContextInfoMap.Values().cend(),
+ [](const auto& info) { return info->IsAudible(); });
+}
+
+bool MediaPlaybackStatus::IsAnyMediaBeingControlled() const {
+ MOZ_ASSERT(NS_IsMainThread());
+ return std::any_of(
+ mContextInfoMap.Values().cbegin(), mContextInfoMap.Values().cend(),
+ [](const auto& info) { return info->IsAnyMediaBeingControlled(); });
+}
+
+MediaPlaybackStatus::ContextMediaInfo&
+MediaPlaybackStatus::GetNotNullContextInfo(uint64_t aContextId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ return *mContextInfoMap.GetOrInsertNew(aContextId, aContextId);
+}
+
+Maybe<uint64_t> MediaPlaybackStatus::GetAudioFocusOwnerContextId() const {
+ return mOwningAudioFocusContextId;
+}
+
+void MediaPlaybackStatus::ChooseNewContextToOwnAudioFocus() {
+ for (const auto& info : mContextInfoMap.Values()) {
+ if (info->IsAudible()) {
+ SetOwningAudioFocusContextId(Some(info->Id()));
+ return;
+ }
+ }
+ // No context is audible, so no one should the own audio focus.
+ SetOwningAudioFocusContextId(Nothing());
+}
+
+void MediaPlaybackStatus::SetOwningAudioFocusContextId(
+ Maybe<uint64_t>&& aContextId) {
+ if (mOwningAudioFocusContextId == aContextId) {
+ return;
+ }
+ mOwningAudioFocusContextId = aContextId;
+}
+
+bool MediaPlaybackStatus::ShouldRequestAudioFocusForInfo(
+ const ContextMediaInfo& aInfo) const {
+ return aInfo.IsAudible() && !IsContextOwningAudioFocus(aInfo.Id());
+}
+
+bool MediaPlaybackStatus::ShouldAbandonAudioFocusForInfo(
+ const ContextMediaInfo& aInfo) const {
+ // The owner becomes inaudible and there is other context still playing, so we
+ // should switch the audio focus to the audible context.
+ return !aInfo.IsAudible() && IsContextOwningAudioFocus(aInfo.Id()) &&
+ IsAudible();
+}
+
+bool MediaPlaybackStatus::IsContextOwningAudioFocus(uint64_t aContextId) const {
+ return mOwningAudioFocusContextId ? *mOwningAudioFocusContextId == aContextId
+ : false;
+}
+
+} // namespace mozilla::dom