Bug 1611332 - part2 : implement MediaSessionController which is used to update media session and store media metadata. r=chunmin

Create a class `MediaSessionController` which is used to track all alive media sessions within a tab and store their metadata which could be used to show on the virtual media control interface. That class would also be responsible to select an active media session with in a tab.

Differential Revision: https://phabricator.services.mozilla.com/D60935

--HG--
extra : moz-landing-system : lando
This commit is contained in:
alwu 2020-02-11 00:30:47 +00:00
parent da4070cfe8
commit 624272960a
4 changed files with 175 additions and 2 deletions

View File

@ -9,6 +9,7 @@
#include "ContentMediaController.h"
#include "MediaEventSource.h"
#include "mozilla/dom/MediaSessionController.h"
#include "nsDataHashtable.h"
#include "nsISupportsImpl.h"
@ -52,9 +53,9 @@ enum class PlaybackState : uint8_t {
* tabs playing media at the same time, we can use the ID to query the specific
* controller from `MediaControlService`.
*/
class MediaController final {
class MediaController final : public MediaSessionController {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaController);
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaController, override);
explicit MediaController(uint64_t aContextId);

View File

@ -0,0 +1,103 @@
/* 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 "MediaSessionController.h"
#include "mozilla/dom/BrowsingContext.h"
mozilla::LazyLogModule gMediaSession("MediaSession");
// avoid redefined macro in unified build
#undef LOG
#define LOG(msg, ...) \
MOZ_LOG(gMediaSession, LogLevel::Debug, \
("MediaSessionController=%p, " msg, this, ##__VA_ARGS__))
namespace mozilla {
namespace dom {
void MediaSessionController::NotifySessionCreated(uint64_t aSessionContextId) {
if (mMetadataMap.Contains(aSessionContextId)) {
return;
}
Maybe<MediaMetadataBase> empty;
LOG("Session %" PRId64 " has been created", aSessionContextId);
mMetadataMap.Put(aSessionContextId, empty);
UpdateActiveMediaSessionContextId();
}
void MediaSessionController::NotifySessionDestroyed(
uint64_t aSessionContextId) {
if (!mMetadataMap.Contains(aSessionContextId)) {
return;
}
LOG("Session %" PRId64 " has been destroyed", aSessionContextId);
mMetadataMap.Remove(aSessionContextId);
UpdateActiveMediaSessionContextId();
}
void MediaSessionController::UpdateMetadata(uint64_t aSessionContextId,
MediaMetadataBase& aMetadata) {
if (!mMetadataMap.Contains(aSessionContextId)) {
return;
}
LOG("Update metadata for session %" PRId64, aSessionContextId);
mMetadataMap.GetValue(aSessionContextId)->emplace(aMetadata);
}
void MediaSessionController::UpdateActiveMediaSessionContextId() {
// If there is a media session created from top level browsing context, it
// would always be chosen as an active media session. Otherwise, we would
// check if we have an active media session already. If we do have, it would
// still remain as an active session. But if we don't, we would simply choose
// arbitrary one as an active session.
uint64_t candidateId = 0;
if (mActiveMediaSessionContextId &&
mMetadataMap.Contains(*mActiveMediaSessionContextId)) {
candidateId = *mActiveMediaSessionContextId;
}
for (auto iter = mMetadataMap.ConstIter(); !iter.Done(); iter.Next()) {
if (RefPtr<BrowsingContext> bc = BrowsingContext::Get(iter.Key());
bc->IsTopContent()) {
candidateId = iter.Key();
break;
}
if (!candidateId) {
candidateId = iter.Key();
}
}
if (mActiveMediaSessionContextId &&
*mActiveMediaSessionContextId == candidateId) {
LOG("Active session %" PRId64 " keeps unchanged",
*mActiveMediaSessionContextId);
return;
}
mActiveMediaSessionContextId = Some(candidateId);
LOG("Session %" PRId64 " becomes active session",
*mActiveMediaSessionContextId);
}
MediaMetadataBase MediaSessionController::CreateDefaultMetadata() const {
// TODO : using website's title and favicon as title and arwork to fill out
// returned result in bug1611328.
return MediaMetadataBase();
}
MediaMetadataBase MediaSessionController::GetCurrentMediaMetadata() const {
// If we don't have active media session, or active media session doesn't have
// media metadata, then we should create a default metadata which is using
// website's title and favicon as artist name and album cover.
if (mActiveMediaSessionContextId) {
Maybe<MediaMetadataBase> metadata =
mMetadataMap.Get(*mActiveMediaSessionContextId);
return metadata ? *metadata : CreateDefaultMetadata();
}
return CreateDefaultMetadata();
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,67 @@
/* 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_MEDIASESSION_MEDIASESSIONCONTROLLER_H_
#define DOM_MEDIA_MEDIASESSION_MEDIASESSIONCONTROLLER_H_
#include "mozilla/dom/MediaMetadata.h"
#include "mozilla/dom/MediaSessionBinding.h"
#include "mozilla/Maybe.h"
#include "nsDataHashtable.h"
#include "nsISupportsImpl.h"
namespace mozilla {
namespace dom {
/**
* MediaSessionController is used to track all alive media sessions within a tab
* and store their metadata which could be used to show on the virtual media
* control interface. In addition, we can use it to get the current media
* metadata even if there is no media session existing.
*
* When media session is created in the content process, we would notify
* MediaSessionController in the parent process to tell it in which browsing
* context media session is created. If there are multiple media sessions
* existing in the same tab, MediaSessionController would take a resbonsibility
* to decide which media session should be an active media session. However,
* the meaning of active media session here is not equal to the definition from
* the spec [1]. We just choose the session which is the active one inside the
* tab, the global active media session among different tabs would be selected
* in other place, which is related to how we select media controller.
*
* [1] https://w3c.github.io/mediasession/#active-media-session
*/
class MediaSessionController {
public:
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
// Use these functions to ensure that we can track all existing media session
// in the same tab when media session is created or destroyed in the content
// process.
void NotifySessionCreated(uint64_t aSessionContextId);
void NotifySessionDestroyed(uint64_t aSessionContextId);
// Use this function to store the media metadata when media session updated
// its metadata in the content process.
void UpdateMetadata(uint64_t aSessionContextId, MediaMetadataBase& aMetadata);
// Return active media session's metadata if active media session exists and
// it has already set its metadata. Otherwise, return default media metadata
// which is based on website's title and favicon.
MediaMetadataBase GetCurrentMediaMetadata() const;
protected:
~MediaSessionController() = default;
MediaMetadataBase CreateDefaultMetadata() const;
void UpdateActiveMediaSessionContextId();
Maybe<uint64_t> mActiveMediaSessionContextId;
nsDataHashtable<nsUint64HashKey, Maybe<MediaMetadataBase>> mMetadataMap;
};
} // namespace dom
} // namespace mozilla
#endif // DOM_MEDIA_MEDIASESSION_MEDIASESSIONCONTROLLER_H_

View File

@ -9,11 +9,13 @@ MOCHITEST_MANIFESTS += ['test/mochitest.ini']
EXPORTS.mozilla.dom += [
'MediaMetadata.h',
'MediaSession.h',
'MediaSessionController.h',
]
UNIFIED_SOURCES += [
'MediaMetadata.cpp',
'MediaSession.cpp',
'MediaSessionController.cpp',
]
FINAL_LIBRARY = 'xul'