зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1633010 - part2 : create `MediaPlaybackStatus` to handle the tasks of determining playback related status. r=chunmin
This patch will do : - implement a new class `MediaPlaybackStatus` to handle the detail of modifying different media status counter The advantage of doing so : - encapsulate the low level details to the delegate and simplify the code in `MediaController` - help us maintain separated media status for each different context within a tab - using `MediaPlaybackStatus` can fix the problem of not `IsAudible()` being able to represent the actual audible state of media controller Differential Revision: https://phabricator.services.mozilla.com/D73486
This commit is contained in:
Родитель
32695cee12
Коммит
36c6a1d4e9
|
@ -114,6 +114,18 @@ inline const char* ToMediaPlaybackStateStr(MediaPlaybackState aState) {
|
|||
}
|
||||
}
|
||||
|
||||
inline const char* ToMediaAudibleStateStr(MediaAudibleState aState) {
|
||||
switch (aState) {
|
||||
case MediaAudibleState::eInaudible:
|
||||
return "inaudible";
|
||||
case MediaAudibleState::eAudible:
|
||||
return "audible";
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid audible state.");
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
BrowsingContext* GetAliveTopBrowsingContext(BrowsingContext* aBC);
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -88,7 +88,7 @@ void MediaController::UpdateMediaControlKeysEventToContentMediaIfNeeded(
|
|||
MediaControlKeysEvent aEvent) {
|
||||
// There is no controlled media existing or controller has been shutdown, we
|
||||
// have no need to update media action to the content process.
|
||||
if (!ControlledMediaNum() || mShutdown) {
|
||||
if (!IsAnyMediaBeingControlled() || mShutdown) {
|
||||
return;
|
||||
}
|
||||
// If we have an active media session, then we should directly notify the
|
||||
|
@ -114,8 +114,6 @@ void MediaController::Shutdown() {
|
|||
// the corresponding controller. Therefore, we should manually remove the
|
||||
// controller from the service.
|
||||
Deactivate();
|
||||
mControlledMediaNum = 0;
|
||||
mPlayingControlledMediaNum = 0;
|
||||
mShutdown = true;
|
||||
}
|
||||
|
||||
|
@ -124,14 +122,17 @@ void MediaController::NotifyMediaPlaybackChanged(uint64_t aBrowsingContextId,
|
|||
if (mShutdown) {
|
||||
return;
|
||||
}
|
||||
if (aState == MediaPlaybackState::eStarted) {
|
||||
IncreaseControlledMediaNum();
|
||||
} else if (aState == MediaPlaybackState::eStopped) {
|
||||
DecreaseControlledMediaNum();
|
||||
} else if (aState == MediaPlaybackState::ePlayed) {
|
||||
IncreasePlayingControlledMediaNum();
|
||||
} else if (aState == MediaPlaybackState::ePaused) {
|
||||
DecreasePlayingControlledMediaNum();
|
||||
mMediaStatus.UpdateMediaPlaybackState(aBrowsingContextId, aState);
|
||||
|
||||
// Update controller's status according to the media status.
|
||||
if (ShouldActivateController()) {
|
||||
Activate();
|
||||
} else if (ShouldDeactivateController()) {
|
||||
Deactivate();
|
||||
} else if (mMediaStatus.IsPlaying()) {
|
||||
SetGuessedPlayState(MediaSessionPlaybackState::Playing);
|
||||
} else if (!mMediaStatus.IsPlaying()) {
|
||||
SetGuessedPlayState(MediaSessionPlaybackState::Paused);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,63 +141,36 @@ void MediaController::NotifyMediaAudibleChanged(uint64_t aBrowsingContextId,
|
|||
if (mShutdown) {
|
||||
return;
|
||||
}
|
||||
mAudibleState = aState;
|
||||
|
||||
bool oldAudible = IsAudible();
|
||||
mMediaStatus.UpdateMediaAudibleState(aBrowsingContextId, aState);
|
||||
if (IsAudible() == oldAudible) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Request the audio focus amongs different controllers that could cause
|
||||
// pausing other audible controllers if we enable the audio focus management.
|
||||
RefPtr<MediaControlService> service = MediaControlService::GetService();
|
||||
MOZ_ASSERT(service);
|
||||
if (mAudibleState == MediaAudibleState::eAudible) {
|
||||
if (IsAudible()) {
|
||||
service->GetAudioFocusManager().RequestAudioFocus(this);
|
||||
} else {
|
||||
service->GetAudioFocusManager().RevokeAudioFocus(this);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaController::IncreaseControlledMediaNum() {
|
||||
bool MediaController::ShouldActivateController() const {
|
||||
MOZ_ASSERT(!mShutdown);
|
||||
MOZ_DIAGNOSTIC_ASSERT(mControlledMediaNum >= 0);
|
||||
mControlledMediaNum++;
|
||||
LOG("Increase controlled media num to %" PRId64, mControlledMediaNum);
|
||||
if (mControlledMediaNum == 1) {
|
||||
Activate();
|
||||
}
|
||||
return IsAnyMediaBeingControlled() && !mIsRegisteredToService;
|
||||
}
|
||||
|
||||
void MediaController::DecreaseControlledMediaNum() {
|
||||
bool MediaController::ShouldDeactivateController() const {
|
||||
MOZ_ASSERT(!mShutdown);
|
||||
MOZ_DIAGNOSTIC_ASSERT(mControlledMediaNum >= 1);
|
||||
mControlledMediaNum--;
|
||||
LOG("Decrease controlled media num to %" PRId64, mControlledMediaNum);
|
||||
if (mControlledMediaNum == 0) {
|
||||
Deactivate();
|
||||
}
|
||||
return !IsAnyMediaBeingControlled() && mIsRegisteredToService;
|
||||
}
|
||||
|
||||
void MediaController::IncreasePlayingControlledMediaNum() {
|
||||
MOZ_ASSERT(!mShutdown);
|
||||
MOZ_ASSERT(mPlayingControlledMediaNum >= 0);
|
||||
mPlayingControlledMediaNum++;
|
||||
LOG("Increase playing controlled media num to %" PRId64,
|
||||
mPlayingControlledMediaNum);
|
||||
MOZ_ASSERT(mPlayingControlledMediaNum <= mControlledMediaNum,
|
||||
"The number of playing media should not exceed the number of "
|
||||
"controlled media!");
|
||||
if (mPlayingControlledMediaNum == 1) {
|
||||
SetGuessedPlayState(MediaSessionPlaybackState::Playing);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaController::DecreasePlayingControlledMediaNum() {
|
||||
MOZ_ASSERT(!mShutdown);
|
||||
mPlayingControlledMediaNum--;
|
||||
LOG("Decrease playing controlled media num to %" PRId64,
|
||||
mPlayingControlledMediaNum);
|
||||
MOZ_ASSERT(mPlayingControlledMediaNum >= 0);
|
||||
if (mPlayingControlledMediaNum == 0) {
|
||||
SetGuessedPlayState(MediaSessionPlaybackState::Paused);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : Use watchable to moniter mControlledMediaNum
|
||||
void MediaController::Activate() {
|
||||
LOG("Activate");
|
||||
MOZ_ASSERT(!mShutdown);
|
||||
RefPtr<MediaControlService> service = MediaControlService::GetService();
|
||||
if (service && !mIsRegisteredToService) {
|
||||
|
@ -206,6 +180,7 @@ void MediaController::Activate() {
|
|||
}
|
||||
|
||||
void MediaController::Deactivate() {
|
||||
LOG("Deactivate");
|
||||
MOZ_ASSERT(!mShutdown);
|
||||
RefPtr<MediaControlService> service = MediaControlService::GetService();
|
||||
if (service) {
|
||||
|
@ -274,11 +249,11 @@ MediaSessionPlaybackState MediaController::GetState() const {
|
|||
|
||||
bool MediaController::IsAudible() const {
|
||||
return mGuessedPlaybackState == MediaSessionPlaybackState::Playing &&
|
||||
mAudibleState == MediaAudibleState::eAudible;
|
||||
mMediaStatus.IsAudible();
|
||||
}
|
||||
|
||||
uint64_t MediaController::ControlledMediaNum() const {
|
||||
return mControlledMediaNum;
|
||||
bool MediaController::IsAnyMediaBeingControlled() const {
|
||||
return mMediaStatus.IsAnyMediaBeingControlled();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "ContentMediaController.h"
|
||||
#include "MediaEventSource.h"
|
||||
#include "MediaPlaybackStatus.h"
|
||||
#include "mozilla/dom/MediaSessionController.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
@ -75,7 +76,7 @@ class MediaController final
|
|||
void Shutdown();
|
||||
|
||||
bool IsAudible() const;
|
||||
uint64_t ControlledMediaNum() const;
|
||||
bool IsAnyMediaBeingControlled() const;
|
||||
MediaSessionPlaybackState GetState() const;
|
||||
|
||||
void SetDeclaredPlaybackState(uint64_t aSessionContextId,
|
||||
|
@ -93,14 +94,13 @@ class MediaController final
|
|||
|
||||
void UpdateMediaControlKeysEventToContentMediaIfNeeded(
|
||||
MediaControlKeysEvent aEvent);
|
||||
void IncreaseControlledMediaNum();
|
||||
void DecreaseControlledMediaNum();
|
||||
void IncreasePlayingControlledMediaNum();
|
||||
void DecreasePlayingControlledMediaNum();
|
||||
|
||||
void Activate();
|
||||
void Deactivate();
|
||||
|
||||
bool ShouldActivateController() const;
|
||||
bool ShouldDeactivateController() const;
|
||||
|
||||
void SetGuessedPlayState(MediaSessionPlaybackState aState);
|
||||
|
||||
// Whenever the declared playback state (from media session controller) or the
|
||||
|
@ -108,12 +108,10 @@ class MediaController final
|
|||
// to know if we need to update the virtual control interface.
|
||||
void UpdateActualPlaybackState();
|
||||
|
||||
MediaAudibleState mAudibleState = MediaAudibleState::eInaudible;
|
||||
bool mIsRegisteredToService = false;
|
||||
int64_t mControlledMediaNum = 0;
|
||||
int64_t mPlayingControlledMediaNum = 0;
|
||||
bool mShutdown = false;
|
||||
bool mIsInPictureInPictureMode = false;
|
||||
MediaPlaybackStatus mMediaStatus;
|
||||
|
||||
// This state can match to the `guessed playback state` in the spec [1], it
|
||||
// indicates if we have any media element playing within the tab which this
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/* 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 {
|
||||
namespace 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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
bool MediaPlaybackStatus::IsPlaying() const {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
for (auto iter = mContextInfoMap.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
if (iter.Data()->IsPlaying()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MediaPlaybackStatus::IsAudible() const {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
for (auto iter = mContextInfoMap.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
if (iter.Data()->IsAudible()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MediaPlaybackStatus::IsAnyMediaBeingControlled() const {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
for (auto iter = mContextInfoMap.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
if (iter.Data()->IsAnyMediaBeingControlled()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MediaPlaybackStatus::ContextMediaInfo&
|
||||
MediaPlaybackStatus::GetNotNullContextInfo(uint64_t aContextId) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mContextInfoMap.Contains(aContextId)) {
|
||||
mContextInfoMap.Put(aContextId, MakeUnique<ContextMediaInfo>(aContextId));
|
||||
}
|
||||
return *(mContextInfoMap.GetValue(aContextId)->get());
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,100 @@
|
|||
/* 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/. */
|
||||
|
||||
#ifndef DOM_MEDIA_MEDIACONTROL_MEDIAPLAYBACKSTATUS_H_
|
||||
#define DOM_MEDIA_MEDIACONTROL_MEDIAPLAYBACKSTATUS_H_
|
||||
|
||||
#include "ContentMediaController.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/**
|
||||
* MediaPlaybackStatus is an internal module for the media controller, it
|
||||
* represents a tab's media related status, such like "does the tab contain any
|
||||
* controlled media? is the tab playing? is the tab audible?".
|
||||
*
|
||||
* The reason we need this class is that we would like to encapsulate the
|
||||
* details of determining the tab's media status. A tab can contains multiple
|
||||
* browsing contexts, and each browsing context can have different media status.
|
||||
* The final media status would be decided by checking all those context status.
|
||||
*
|
||||
* Use `UpdateMediaXXXState()` to update controlled media status, and use
|
||||
* `IsXXX()` methods to acquire the playback status of the tab.
|
||||
*/
|
||||
class MediaPlaybackStatus final {
|
||||
public:
|
||||
void UpdateMediaPlaybackState(uint64_t aContextId, MediaPlaybackState aState);
|
||||
void UpdateMediaAudibleState(uint64_t aContextId, MediaAudibleState aState);
|
||||
|
||||
bool IsPlaying() const;
|
||||
bool IsAudible() const;
|
||||
bool IsAnyMediaBeingControlled() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* This internal class stores detailed media status of controlled media for
|
||||
* a browsing context.
|
||||
*/
|
||||
class ContextMediaInfo final {
|
||||
public:
|
||||
explicit ContextMediaInfo(uint64_t aContextId) : mContextId(aContextId) {}
|
||||
~ContextMediaInfo() = default;
|
||||
|
||||
void IncreaseControlledMediaNum() {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mControlledMediaNum < UINT_MAX);
|
||||
mControlledMediaNum++;
|
||||
}
|
||||
void DecreaseControlledMediaNum() {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mControlledMediaNum > 0);
|
||||
mControlledMediaNum--;
|
||||
}
|
||||
void IncreasePlayingMediaNum() {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mPlayingMediaNum < mControlledMediaNum);
|
||||
mPlayingMediaNum++;
|
||||
}
|
||||
void DecreasePlayingMediaNum() {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mPlayingMediaNum > 0);
|
||||
mPlayingMediaNum--;
|
||||
}
|
||||
void IncreaseAudibleMediaNum() {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mAudibleMediaNum < mPlayingMediaNum);
|
||||
mAudibleMediaNum++;
|
||||
}
|
||||
void DecreaseAudibleMediaNum() {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mAudibleMediaNum > 0);
|
||||
mAudibleMediaNum--;
|
||||
}
|
||||
bool IsPlaying() const { return mPlayingMediaNum > 0; }
|
||||
bool IsAudible() const { return mAudibleMediaNum > 0; }
|
||||
bool IsAnyMediaBeingControlled() const { return mControlledMediaNum > 0; }
|
||||
uint64_t Id() const { return mContextId; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* The possible value for those three numbers should follow this rule,
|
||||
* mControlledMediaNum >= mPlayingMediaNum >= mAudibleMediaNum
|
||||
*/
|
||||
uint32_t mControlledMediaNum = 0;
|
||||
uint32_t mAudibleMediaNum = 0;
|
||||
uint32_t mPlayingMediaNum = 0;
|
||||
uint64_t mContextId = 0;
|
||||
};
|
||||
|
||||
ContextMediaInfo& GetNotNullContextInfo(uint64_t aContextId);
|
||||
void DestroyContextInfo(uint64_t aContextId);
|
||||
|
||||
// This contains all the media status of browsing contexts within a tab.
|
||||
nsDataHashtable<nsUint64HashKey, UniquePtr<ContextMediaInfo>> mContextInfoMap;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // DOM_MEDIA_MEDIACONTROL_MEDIAPLAYBACKSTATUS_H_
|
|
@ -13,6 +13,7 @@ EXPORTS.mozilla.dom += [
|
|||
'MediaController.h',
|
||||
'MediaControlService.h',
|
||||
'MediaControlUtils.h',
|
||||
'MediaPlaybackStatus.h',
|
||||
]
|
||||
|
||||
EXPORTS.ipc += [
|
||||
|
@ -29,6 +30,7 @@ UNIFIED_SOURCES += [
|
|||
'MediaController.cpp',
|
||||
'MediaControlService.cpp',
|
||||
'MediaControlUtils.cpp',
|
||||
'MediaPlaybackStatus.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
|
|
@ -15,32 +15,32 @@ using namespace mozilla::dom;
|
|||
TEST(MediaController, DefaultValueCheck)
|
||||
{
|
||||
RefPtr<MediaController> controller = new MediaController(CONTROLLER_ID);
|
||||
ASSERT_TRUE(controller->ControlledMediaNum() == 0);
|
||||
ASSERT_TRUE(!controller->IsAnyMediaBeingControlled());
|
||||
ASSERT_TRUE(controller->Id() == CONTROLLER_ID);
|
||||
ASSERT_TRUE(controller->GetState() == MediaSessionPlaybackState::None);
|
||||
ASSERT_TRUE(!controller->IsAudible());
|
||||
}
|
||||
|
||||
TEST(MediaController, NotifyMediaPlaybackChanged)
|
||||
TEST(MediaController, IsAnyMediaBeingControlled)
|
||||
{
|
||||
RefPtr<MediaController> controller = new MediaController(CONTROLLER_ID);
|
||||
ASSERT_TRUE(controller->ControlledMediaNum() == 0);
|
||||
ASSERT_TRUE(!controller->IsAnyMediaBeingControlled());
|
||||
|
||||
controller->NotifyMediaPlaybackChanged(FAKE_CONTEXT_ID,
|
||||
MediaPlaybackState::eStarted);
|
||||
ASSERT_TRUE(controller->ControlledMediaNum() == 1);
|
||||
ASSERT_TRUE(controller->IsAnyMediaBeingControlled());
|
||||
|
||||
controller->NotifyMediaPlaybackChanged(FAKE_CONTEXT_ID,
|
||||
MediaPlaybackState::eStarted);
|
||||
ASSERT_TRUE(controller->ControlledMediaNum() == 2);
|
||||
ASSERT_TRUE(controller->IsAnyMediaBeingControlled());
|
||||
|
||||
controller->NotifyMediaPlaybackChanged(FAKE_CONTEXT_ID,
|
||||
MediaPlaybackState::eStopped);
|
||||
ASSERT_TRUE(controller->ControlledMediaNum() == 1);
|
||||
ASSERT_TRUE(controller->IsAnyMediaBeingControlled());
|
||||
|
||||
controller->NotifyMediaPlaybackChanged(FAKE_CONTEXT_ID,
|
||||
MediaPlaybackState::eStopped);
|
||||
ASSERT_TRUE(controller->ControlledMediaNum() == 0);
|
||||
ASSERT_TRUE(!controller->IsAnyMediaBeingControlled());
|
||||
}
|
||||
|
||||
TEST(MediaController, ActiveAndDeactiveController)
|
||||
|
|
Загрузка…
Ссылка в новой задаче