Bug 1627999 - part2 : update the active media session when the owner of the audio focus changes. r=bryce

This patch will do :
- using the audio focus to decide which media session is the active media session. That is a recommend way of the spec [1].

The advantage of doing so :
- prevent to routing media control keys to incorrect media session
- prevent showing the incorrect metadata on the virtual control interface

[1] https://w3c.github.io/mediasession/#active-media-session

Differential Revision: https://phabricator.services.mozilla.com/D72496
This commit is contained in:
alwu 2020-05-15 21:50:03 +00:00
Родитель 7316002adb
Коммит 1c063b25ca
2 изменённых файлов: 65 добавлений и 41 удалений

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

@ -48,7 +48,14 @@ MediaStatusManager::MediaStatusManager(uint64_t aBrowsingContextId)
void MediaStatusManager::NotifyMediaAudibleChanged(uint64_t aBrowsingContextId,
MediaAudibleState aState) {
Maybe<uint64_t> oldAudioFocusOwnerId =
mPlaybackStatusDelegate.GetAudioFocusOwnerContextId();
mPlaybackStatusDelegate.UpdateMediaAudibleState(aBrowsingContextId, aState);
Maybe<uint64_t> newAudioFocusOwnerId =
mPlaybackStatusDelegate.GetAudioFocusOwnerContextId();
if (oldAudioFocusOwnerId != newAudioFocusOwnerId) {
HandleAudioFocusOwnerChanged(newAudioFocusOwnerId);
}
}
void MediaStatusManager::NotifySessionCreated(uint64_t aBrowsingContextId) {
@ -56,18 +63,23 @@ void MediaStatusManager::NotifySessionCreated(uint64_t aBrowsingContextId) {
return;
}
LOG("Session %" PRId64 " has been created", aBrowsingContextId);
LOG("Session %" PRIu64 " has been created", aBrowsingContextId);
mMediaSessionInfoMap.Put(aBrowsingContextId, MediaSessionInfo::EmptyInfo());
UpdateActiveMediaSessionContextId();
if (IsSessionOwningAudioFocus(aBrowsingContextId)) {
SetActiveMediaSessionContextId(aBrowsingContextId);
}
}
void MediaStatusManager::NotifySessionDestroyed(uint64_t aBrowsingContextId) {
if (!mMediaSessionInfoMap.Contains(aBrowsingContextId)) {
return;
}
LOG("Session %" PRId64 " has been destroyed", aBrowsingContextId);
LOG("Session %" PRIu64 " has been destroyed", aBrowsingContextId);
mMediaSessionInfoMap.Remove(aBrowsingContextId);
UpdateActiveMediaSessionContextId();
if (mActiveMediaSessionContextId &&
*mActiveMediaSessionContextId == aBrowsingContextId) {
ClearActiveMediaSessionContextIdIfNeeded();
}
}
void MediaStatusManager::UpdateMetadata(
@ -78,10 +90,10 @@ void MediaStatusManager::UpdateMetadata(
MediaSessionInfo* info = mMediaSessionInfoMap.GetValue(aBrowsingContextId);
if (IsMetadataEmpty(aMetadata)) {
LOG("Reset metadata for session %" PRId64, aBrowsingContextId);
LOG("Reset metadata for session %" PRIu64, aBrowsingContextId);
info->mMetadata.reset();
} else {
LOG("Update metadata for session %" PRId64 " title=%s artist=%s album=%s",
LOG("Update metadata for session %" PRIu64 " title=%s artist=%s album=%s",
aBrowsingContextId, NS_ConvertUTF16toUTF8((*aMetadata).mTitle).get(),
NS_ConvertUTF16toUTF8(aMetadata->mArtist).get(),
NS_ConvertUTF16toUTF8(aMetadata->mAlbum).get());
@ -96,48 +108,54 @@ void MediaStatusManager::UpdateMetadata(
}
}
void MediaStatusManager::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 &&
mMediaSessionInfoMap.Contains(*mActiveMediaSessionContextId)) {
candidateId = *mActiveMediaSessionContextId;
void MediaStatusManager::HandleAudioFocusOwnerChanged(
Maybe<uint64_t>& aBrowsingContextId) {
// No one is holding the audio focus.
if (!aBrowsingContextId) {
LOG("No one is owning audio focus");
return ClearActiveMediaSessionContextIdIfNeeded();
}
for (auto iter = mMediaSessionInfoMap.Iter(); !iter.Done(); iter.Next()) {
RefPtr<BrowsingContext> bc = BrowsingContext::Get(iter.Key());
// The browsing context which media session belongs to has been detroyed,
// and it wasn't be removed correctly via the IPC message. That could happen
// if the browsing context was destroyed before ContentPatent receives the
// remove message. Therefore, we should remove it and continue to iterate
// other elements.
if (!bc) {
iter.Remove();
continue;
}
if (bc->IsTopContent()) {
candidateId = iter.Key();
break;
}
if (!candidateId) {
candidateId = iter.Key();
}
// This owner of audio focus doesn't have media session, so we should deactive
// the active session because the active session must own the audio focus.
if (!mMediaSessionInfoMap.Contains(*aBrowsingContextId)) {
LOG("The owner of audio focus doesn't have media session");
return ClearActiveMediaSessionContextIdIfNeeded();
}
// This owner has media session so it should become an active session context.
SetActiveMediaSessionContextId(*aBrowsingContextId);
}
void MediaStatusManager::SetActiveMediaSessionContextId(
uint64_t aBrowsingContextId) {
if (mActiveMediaSessionContextId &&
*mActiveMediaSessionContextId == candidateId) {
LOG("Active session %" PRId64 " keeps unchanged",
*mActiveMediaSessionContextId == aBrowsingContextId) {
LOG("Active session context %" PRIu64 " keeps unchanged",
*mActiveMediaSessionContextId);
return;
}
mActiveMediaSessionContextId = Some(candidateId);
LOG("Session %" PRId64 " becomes active session",
mActiveMediaSessionContextId = Some(aBrowsingContextId);
LOG("context %" PRIu64 " becomes active session context",
*mActiveMediaSessionContextId);
mMetadataChangedEvent.Notify(GetCurrentMediaMetadata());
}
void MediaStatusManager::ClearActiveMediaSessionContextIdIfNeeded() {
if (!mActiveMediaSessionContextId) {
return;
}
LOG("Clear active session context");
mActiveMediaSessionContextId.reset();
mMetadataChangedEvent.Notify(GetCurrentMediaMetadata());
}
bool MediaStatusManager::IsSessionOwningAudioFocus(
uint64_t aBrowsingContextId) const {
Maybe<uint64_t> audioFocusContextId =
mPlaybackStatusDelegate.GetAudioFocusOwnerContextId();
return audioFocusContextId ? *audioFocusContextId == aBrowsingContextId
: false;
}
MediaMetadataBase MediaStatusManager::CreateDefaultMetadata() const {
@ -236,7 +254,7 @@ MediaSessionPlaybackState MediaStatusManager::GetCurrentDeclaredPlaybackState()
void MediaStatusManager::NotifyMediaPlaybackChanged(uint64_t aBrowsingContextId,
MediaPlaybackState aState) {
LOG("UpdateMediaPlaybackState %s for context %" PRId64,
LOG("UpdateMediaPlaybackState %s for context %" PRIu64,
ToMediaPlaybackStateStr(aState), aBrowsingContextId);
const bool oldPlaying = mPlaybackStatusDelegate.IsPlaying();
mPlaybackStatusDelegate.UpdateMediaPlaybackState(aBrowsingContextId, aState);

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

@ -132,6 +132,9 @@ class MediaStatusManager : public IMediaInfoUpdater {
virtual void HandleActualPlaybackStateChanged() = 0;
uint64_t mTopLevelBrowsingContextId;
// Within a tab, the Id of the browsing context which has already created a
// media session and owns the audio focus within a tab.
Maybe<uint64_t> mActiveMediaSessionContextId;
private:
@ -141,7 +144,10 @@ class MediaStatusManager : public IMediaInfoUpdater {
bool IsInPrivateBrowsing() const;
void FillMissingTitleAndArtworkIfNeeded(MediaMetadataBase& aMetadata) const;
void UpdateActiveMediaSessionContextId();
bool IsSessionOwningAudioFocus(uint64_t aBrowsingContextId) const;
void SetActiveMediaSessionContextId(uint64_t aBrowsingContextId);
void ClearActiveMediaSessionContextIdIfNeeded();
void HandleAudioFocusOwnerChanged(Maybe<uint64_t>& aBrowsingContextId);
// When the amount of playing media changes, we would use this function to
// update the guessed playback state.