tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MediaPlaybackStatus.cpp (7155B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "MediaPlaybackStatus.h"
      6 
      7 #include "MediaControlUtils.h"
      8 
      9 namespace mozilla::dom {
     10 
     11 #undef LOG
     12 #define LOG(msg, ...)                        \
     13  MOZ_LOG(gMediaControlLog, LogLevel::Debug, \
     14          ("MediaPlaybackStatus=%p, " msg, this, ##__VA_ARGS__))
     15 
     16 void MediaPlaybackStatus::UpdateMediaPlaybackState(uint64_t aContextId,
     17                                                   MediaPlaybackState aState) {
     18  LOG("Update playback state '%s' for context %" PRIu64,
     19      EnumValueToString(aState), aContextId);
     20  MOZ_ASSERT(NS_IsMainThread());
     21 
     22  ContextMediaInfo& info = GetNotNullContextInfo(aContextId);
     23  if (aState == MediaPlaybackState::eStarted) {
     24    info.IncreaseControlledMediaNum();
     25  } else if (aState == MediaPlaybackState::eStopped) {
     26    info.DecreaseControlledMediaNum();
     27  } else if (aState == MediaPlaybackState::ePlayed) {
     28    info.IncreasePlayingMediaNum();
     29  } else {
     30    MOZ_ASSERT(aState == MediaPlaybackState::ePaused);
     31    info.DecreasePlayingMediaNum();
     32  }
     33 
     34  // The context still has controlled media, we should keep its alive.
     35  if (info.IsAnyMediaBeingControlled()) {
     36    return;
     37  }
     38  MOZ_ASSERT(!info.IsPlaying());
     39  MOZ_ASSERT(!info.IsAudible());
     40  // DO NOT access `info` after this line.
     41  DestroyContextInfo(aContextId);
     42 }
     43 
     44 void MediaPlaybackStatus::DestroyContextInfo(uint64_t aContextId) {
     45  MOZ_ASSERT(NS_IsMainThread());
     46  LOG("Remove context %" PRIu64, aContextId);
     47  mContextInfoMap.Remove(aContextId);
     48  // If the removed context is owning the audio focus, we would find another
     49  // context to take the audio focus if it's possible.
     50  if (IsContextOwningAudioFocus(aContextId)) {
     51    ChooseNewContextToOwnAudioFocus();
     52  }
     53 }
     54 
     55 void MediaPlaybackStatus::UpdateMediaAudibleState(uint64_t aContextId,
     56                                                  MediaAudibleState aState) {
     57  LOG("Update audible state '%s' for context %" PRIu64,
     58      EnumValueToString(aState), aContextId);
     59  MOZ_ASSERT(NS_IsMainThread());
     60  ContextMediaInfo& info = GetNotNullContextInfo(aContextId);
     61  if (aState == MediaAudibleState::eAudible) {
     62    info.IncreaseAudibleMediaNum();
     63  } else {
     64    MOZ_ASSERT(aState == MediaAudibleState::eInaudible);
     65    info.DecreaseAudibleMediaNum();
     66  }
     67  if (ShouldRequestAudioFocusForInfo(info)) {
     68    SetOwningAudioFocusContextId(Some(aContextId));
     69  } else if (ShouldAbandonAudioFocusForInfo(info)) {
     70    ChooseNewContextToOwnAudioFocus();
     71  }
     72 }
     73 
     74 void MediaPlaybackStatus::UpdateGuessedPositionState(
     75    uint64_t aContextId, const nsID& aElementId,
     76    const Maybe<PositionState>& aState) {
     77  MOZ_ASSERT(NS_IsMainThread());
     78  if (aState) {
     79    LOG("Update guessed position state for context %" PRIu64
     80        " element %s (duration=%f, playbackRate=%f, position=%f)",
     81        aContextId, aElementId.ToString().get(), aState->mDuration,
     82        aState->mPlaybackRate, aState->mLastReportedPlaybackPosition);
     83  } else {
     84    LOG("Clear guessed position state for context %" PRIu64 " element %s",
     85        aContextId, aElementId.ToString().get());
     86  }
     87  ContextMediaInfo& info = GetNotNullContextInfo(aContextId);
     88  info.UpdateGuessedPositionState(aElementId, aState);
     89 }
     90 
     91 bool MediaPlaybackStatus::IsPlaying() const {
     92  MOZ_ASSERT(NS_IsMainThread());
     93  return std::any_of(mContextInfoMap.Values().cbegin(),
     94                     mContextInfoMap.Values().cend(),
     95                     [](const auto& info) { return info->IsPlaying(); });
     96 }
     97 
     98 bool MediaPlaybackStatus::IsAudible() const {
     99  MOZ_ASSERT(NS_IsMainThread());
    100  return std::any_of(mContextInfoMap.Values().cbegin(),
    101                     mContextInfoMap.Values().cend(),
    102                     [](const auto& info) { return info->IsAudible(); });
    103 }
    104 
    105 bool MediaPlaybackStatus::IsAnyMediaBeingControlled() const {
    106  MOZ_ASSERT(NS_IsMainThread());
    107  return std::any_of(
    108      mContextInfoMap.Values().cbegin(), mContextInfoMap.Values().cend(),
    109      [](const auto& info) { return info->IsAnyMediaBeingControlled(); });
    110 }
    111 
    112 Maybe<PositionState> MediaPlaybackStatus::GuessedMediaPositionState(
    113    Maybe<uint64_t> aPreferredContextId) const {
    114  auto contextId = aPreferredContextId;
    115  if (!contextId) {
    116    contextId = mOwningAudioFocusContextId;
    117  }
    118 
    119  // either the preferred or focused context
    120  if (contextId) {
    121    auto entry = mContextInfoMap.Lookup(*contextId);
    122    if (!entry) {
    123      return Nothing();
    124    }
    125    LOG("Using guessed position state from preferred/focused BC %" PRId64,
    126        *contextId);
    127    return entry.Data()->GuessedPositionState();
    128  }
    129 
    130  // look for the first position state
    131  for (const auto& context : mContextInfoMap.Values()) {
    132    auto state = context->GuessedPositionState();
    133    if (state) {
    134      LOG("Using guessed position state from BC %" PRId64, context->Id());
    135      return state;
    136    }
    137  }
    138  return Nothing();
    139 }
    140 
    141 MediaPlaybackStatus::ContextMediaInfo&
    142 MediaPlaybackStatus::GetNotNullContextInfo(uint64_t aContextId) {
    143  MOZ_ASSERT(NS_IsMainThread());
    144  return *mContextInfoMap.GetOrInsertNew(aContextId, aContextId);
    145 }
    146 
    147 Maybe<uint64_t> MediaPlaybackStatus::GetAudioFocusOwnerContextId() const {
    148  return mOwningAudioFocusContextId;
    149 }
    150 
    151 void MediaPlaybackStatus::ChooseNewContextToOwnAudioFocus() {
    152  for (const auto& info : mContextInfoMap.Values()) {
    153    if (info->IsAudible()) {
    154      SetOwningAudioFocusContextId(Some(info->Id()));
    155      return;
    156    }
    157  }
    158  // No context is audible, so no one should the own audio focus.
    159  SetOwningAudioFocusContextId(Nothing());
    160 }
    161 
    162 void MediaPlaybackStatus::SetOwningAudioFocusContextId(
    163    Maybe<uint64_t>&& aContextId) {
    164  if (mOwningAudioFocusContextId == aContextId) {
    165    return;
    166  }
    167  mOwningAudioFocusContextId = aContextId;
    168 }
    169 
    170 bool MediaPlaybackStatus::ShouldRequestAudioFocusForInfo(
    171    const ContextMediaInfo& aInfo) const {
    172  return aInfo.IsAudible() && !IsContextOwningAudioFocus(aInfo.Id());
    173 }
    174 
    175 bool MediaPlaybackStatus::ShouldAbandonAudioFocusForInfo(
    176    const ContextMediaInfo& aInfo) const {
    177  // The owner becomes inaudible and there is other context still playing, so we
    178  // should switch the audio focus to the audible context.
    179  return !aInfo.IsAudible() && IsContextOwningAudioFocus(aInfo.Id()) &&
    180         IsAudible();
    181 }
    182 
    183 bool MediaPlaybackStatus::IsContextOwningAudioFocus(uint64_t aContextId) const {
    184  return mOwningAudioFocusContextId ? *mOwningAudioFocusContextId == aContextId
    185                                    : false;
    186 }
    187 
    188 Maybe<PositionState>
    189 MediaPlaybackStatus::ContextMediaInfo::GuessedPositionState() const {
    190  if (mGuessedPositionStateMap.Count() != 1) {
    191    LOG("Count is %d", mGuessedPositionStateMap.Count());
    192    return Nothing();
    193  }
    194  return Some(mGuessedPositionStateMap.begin()->GetData());
    195 }
    196 
    197 void MediaPlaybackStatus::ContextMediaInfo::UpdateGuessedPositionState(
    198    const nsID& aElementId, const Maybe<PositionState>& aState) {
    199  if (aState) {
    200    mGuessedPositionStateMap.InsertOrUpdate(aElementId, *aState);
    201  } else {
    202    mGuessedPositionStateMap.Remove(aElementId);
    203  }
    204 }
    205 
    206 }  // namespace mozilla::dom