Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  MediaPlaybackStatus.cpp

  Sprache: C
 

/* 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 "AudioSessionRecord.h"
#include "MediaControlUtils.h"

namespace mozilla::dom {

#undef LOG
#define LOG(msg, ...)                            \
  MOZ_LOG_FMT(gMediaControlLog, LogLevel::Debug, \
              "MediaPlaybackStatus={}, " msg, fmt::ptr(this), ##__VA_ARGS__)

void MediaPlaybackStatus::UpdateMediaPlaybackState(uint64_t aContextId,
                                                   MediaPlaybackState aState) {
  LOG("Update playback state '{}' for context {}", EnumValueToString(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();
  }

  LOG("UpdateMediaPlaybackState for context {} (controlled {} audible {})",
      aContextId, info.ControlledMediaNum(), info.AudibleSourceCount());
  // DO NOT access `info` after this line.
  MaybeDestroyContextInfo(aContextId, info);
}

void MediaPlaybackStatus::DestroyContextInfo(uint64_t aContextId) {
  MOZ_ASSERT(NS_IsMainThread());
  LOG("Remove context {}", aContextId);
  mContextInfoMap.Remove(aContextId);
  // If the removed context was the active audible controllable context,
  // re-derive it from the remaining contexts (or clear it if none qualify).
  if (IsActiveAudibleControllableContext(aContextId)) {
    ChooseNewActiveAudibleControllableContext();
  }
}

void MediaPlaybackStatus::MaybeDestroyContextInfo(
    uint64_t aContextId, const ContextMediaInfo& aInfo) {
  // A context entry is kept while it still has any of: controlled media,
  // playing media, or an audible source. Once all three are empty the
  // entry has no remaining purpose and is destroyed.
  if (aInfo.IsAnyMediaBeingControlled() || aInfo.IsPlaying() ||
      aInfo.IsAudible()) {
    return;
  }
  DestroyContextInfo(aContextId);
}

bool MediaPlaybackStatus::UpdateMediaAudibleState(
    uint64_t aContextId, MediaAudibleState aState, ControlType aControlType,
    AudioSessionType aSessionType) {
  LOG("Update audible state '{}' for context {}", EnumValueToString(aState),
      aContextId);
  MOZ_ASSERT(NS_IsMainThread());
  ContextMediaInfo& info = GetNotNullContextInfo(aContextId);
  const Maybe<uint64_t> oldActiveContextId =
      mActiveAudibleControllableContextId;

  if (aState == MediaAudibleState::eAudible) {
    info.AddAudibleSource(aControlType, aSessionType);
  } else {
    MOZ_ASSERT(aState == MediaAudibleState::eInaudible);
    info.RemoveAudibleSource(aControlType, aSessionType);
  }

  if (ShouldClaimActiveAudibleControllableContextForInfo(info, aControlType)) {
    SetActiveAudibleControllableContextId(Some(aContextId));
  } else if (ShouldHandOffActiveAudibleControllableContextForInfo(
                 info, aControlType)) {
    ChooseNewActiveAudibleControllableContext();
  }

  MaybeDestroyContextInfo(aContextId, info);
  return oldActiveContextId != mActiveAudibleControllableContextId;
}

void MediaPlaybackStatus::UpdateGuessedPositionState(
    uint64_t aContextId, const nsID& aElementId,
    const Maybe<PositionState>& aState) {
  MOZ_ASSERT(NS_IsMainThread());
  if (aState) {
    LOG("Update guessed position state for context {} element {} (duration={}, "
        "playbackRate={}, position={})",
        aContextId, aElementId.ToString().get(), aState->mDuration,
        aState->mPlaybackRate, aState->mLastReportedPlaybackPosition);
  } else {
    LOG("Clear guessed position state for context {} element {}", aContextId,
        aElementId.ToString().get());
  }
  ContextMediaInfo& info = GetNotNullContextInfo(aContextId);
  info.UpdateGuessedPositionState(aElementId, aState);
}

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::IsBcAudible(uint64_t aBcId) const {
  MOZ_ASSERT(NS_IsMainThread());
  auto entry = mContextInfoMap.Lookup(aBcId);
  return entry && entry.Data()->IsAudible();
}

const nsTArray<AudibleSource>* MediaPlaybackStatus::GetAudibleSourcesForTesting(
    uint64_t aBcId) const {
  auto entry = mContextInfoMap.Lookup(aBcId);
  return entry ? &entry.Data()->AudibleSourcesForTesting() : nullptr;
}

bool MediaPlaybackStatus::IsAnyMediaBeingControlled() const {
  MOZ_ASSERT(NS_IsMainThread());
  return std::any_of(
      mContextInfoMap.Values().cbegin(), mContextInfoMap.Values().cend(),
      [](const auto& info) { return info->IsAnyMediaBeingControlled(); });
}

Maybe<PositionState> MediaPlaybackStatus::GuessedMediaPositionState(
    Maybe<uint64_t> aPreferredContextId) const {
  auto contextId = aPreferredContextId;
  if (!contextId) {
    contextId = mActiveAudibleControllableContextId;
  }

  // either the preferred or focused context
  if (contextId) {
    auto entry = mContextInfoMap.Lookup(*contextId);
    if (!entry) {
      return Nothing();
    }
    LOG("Using guessed position state from preferred/focused BC {}",
        *contextId);
    return entry.Data()->GuessedPositionState();
  }

  // look for the first position state
  for (const auto& context : mContextInfoMap.Values()) {
    auto state = context->GuessedPositionState();
    if (state) {
      LOG("Using guessed position state from BC {}", context->Id());
      return state;
    }
  }
  return Nothing();
}

MediaPlaybackStatus::ContextMediaInfo&
MediaPlaybackStatus::GetNotNullContextInfo(uint64_t aContextId) {
  MOZ_ASSERT(NS_IsMainThread());
  return *mContextInfoMap.GetOrInsertNew(aContextId, aContextId);
}

Maybe<uint64_t> MediaPlaybackStatus::GetActiveAudibleControllableContextId()
    const {
  return mActiveAudibleControllableContextId;
}

void MediaPlaybackStatus::ChooseNewActiveAudibleControllableContext() {
  // Walk every context, picking one with at least one CONTROLLABLE audible
  // source. Uncontrollable-only BCs do not qualify. If no candidate is
  // found, clear the active audible controllable context.
  for (const auto& info : mContextInfoMap.Values()) {
    if (info->HasAudibleSourceOfControlType(ControlType::eControllable)) {
      SetActiveAudibleControllableContextId(Some(info->Id()));
      return;
    }
  }
  SetActiveAudibleControllableContextId(Nothing());
}

void MediaPlaybackStatus::SetActiveAudibleControllableContextId(
    Maybe<uint64_t>&& aContextId) {
  if (mActiveAudibleControllableContextId == aContextId) {
    return;
  }
  mActiveAudibleControllableContextId = std::move(aContextId);
}

bool MediaPlaybackStatus::ShouldClaimActiveAudibleControllableContextForInfo(
    const ContextMediaInfo& aInfo, ControlType aControlType) const {
  // Only a controllable update can become the active audible controllable
  // context; uncontrollable sources contribute to per-tab audibility but
  // never qualify.
  return aControlType == ControlType::eControllable &&
         aInfo.HasAudibleSourceOfControlType(ControlType::eControllable) &&
         !IsActiveAudibleControllableContext(aInfo.Id());
}

bool MediaPlaybackStatus::ShouldHandOffActiveAudibleControllableContextForInfo(
    const ContextMediaInfo& aInfo, ControlType aControlType) const {
  // The current active audible controllable context has lost its last
  // controllable audible source, but another context still has one — hand
  // off to that context. If there is no other candidate the current value
  // stays sticky so a subsequent default-action handler still has a target.
  // Only triggered by controllable transitions; uncontrollable updates
  // never hand off.
  return aControlType == ControlType::eControllable &&
         !aInfo.HasAudibleSourceOfControlType(ControlType::eControllable) &&
         IsActiveAudibleControllableContext(aInfo.Id()) &&
         HasAnyControllableAudibleSource();
}

bool MediaPlaybackStatus::HasAnyControllableAudibleSource() const {
  return std::any_of(
      mContextInfoMap.Values().cbegin(), mContextInfoMap.Values().cend(),
      [](const auto& info) {
        return info->HasAudibleSourceOfControlType(ControlType::eControllable);
      });
}

bool MediaPlaybackStatus::IsActiveAudibleControllableContext(
    uint64_t aContextId) const {
  return mActiveAudibleControllableContextId
             ? *mActiveAudibleControllableContextId == aContextId
             : false;
}

Maybe<PositionState>
MediaPlaybackStatus::ContextMediaInfo::GuessedPositionState() const {
  if (mGuessedPositionStateMap.Count() != 1) {
    LOG("Count is {}", mGuessedPositionStateMap.Count());
    return Nothing();
  }
  return Some(mGuessedPositionStateMap.begin()->GetData());
}

void MediaPlaybackStatus::ContextMediaInfo::UpdateGuessedPositionState(
    const nsID& aElementId, const Maybe<PositionState>& aState) {
  if (aState) {
    mGuessedPositionStateMap.InsertOrUpdate(aElementId, *aState);
  } else {
    mGuessedPositionStateMap.Remove(aElementId);
  }
}

void MediaPlaybackStatus::ContextMediaInfo::AddAudibleSource(
    ControlType aControlType, AudioSessionType aSessionType) {
  mAudibleSources.AppendElement(AudibleSource{aControlType, aSessionType});
}

void MediaPlaybackStatus::ContextMediaInfo::RemoveAudibleSource(
    ControlType aControlType, AudioSessionType aSessionType) {
  for (size_t i = 0; i < mAudibleSources.Length(); ++i) {
    const AudibleSource& src = mAudibleSources[i];
    if (src.mControlType == aControlType && src.mSessionType == aSessionType) {
      mAudibleSources.RemoveElementAt(i);
      return;
    }
  }
}

bool MediaPlaybackStatus::ContextMediaInfo::HasAudibleSourceOfControlType(
    ControlType aControlType) const {
  for (const AudibleSource& src : mAudibleSources) {
    if (src.mControlType == aControlType) {
      return true;
    }
  }
  return false;
}

AudioSessionType
MediaPlaybackStatus::ContextMediaInfo::PriorityTypeFromAudibleSources() const {
  // Spec: https://w3c.github.io/audio-session/#compute-the-audio-session-type
  // Pick the highest-priority type that has an audible source. The
  // AudioSessionType enum is ordered so that the integer value is the
  // priority rank.
  Maybe<AudioSessionType> winner;
  for (const AudibleSource& src : mAudibleSources) {
    if (!winner || AudioSessionTypePriorityRank(src.mSessionType) >
                       AudioSessionTypePriorityRank(*winner)) {
      winner = Some(src.mSessionType);
    }
  }
  const AudioSessionType result = winner.valueOr(DefaultAudioSessionType());
  LOG("PriorityTypeFromAudibleSources for context {} -> {}", mContextId,
      GetEnumString(result).get());
  return result;
}

AudioSessionType MediaPlaybackStatus::EffectiveTypeForBc(uint64_t aBcId) const {
  MOZ_ASSERT(NS_IsMainThread());
  auto entry = mContextInfoMap.Lookup(aBcId);
  if (!entry) {
    LOG("[warning] EffectiveTypeForBc no entry for context {}", aBcId);
    return DefaultAudioSessionType();
  }
  return entry.Data()->PriorityTypeFromAudibleSources();
}

}  // namespace mozilla::dom

Messung V0.5 in Prozent
C=91 H=97 G=93

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002