Eine aufbereitete Darstellung der Quelle

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

Benutzer

Quellcode-Bibliothek MediaSession.cpp   Sprache: unbekannt

 
Haftungsausschluß.cpp Download desUnknown {[0] [0] [0]}diese Dinge liegen außhalb unserer Verantwortung

/* 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 "mozilla/dom/MediaSession.h"

#include "mozilla/EnumeratedArrayCycleCollection.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/ContentMediaController.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/MediaControlUtils.h"
#include "mozilla/dom/WindowContext.h"
#include "nsPIDOMWindowInlines.h"

// avoid redefined macro in unified build
#undef LOG
#define LOG(msg, ...)                                                     \
  MOZ_LOG_FMT(gMediaControlLog, LogLevel::Debug, "MediaSession={}, " msg, \
              fmt::ptr(this), ##__VA_ARGS__)

namespace mozilla::dom {

double PositionState::CurrentPlaybackPosition(TimeStamp aNow) const {
  // https://w3c.github.io/mediasession/#current-playback-position

  // Set time elapsed to the system time in seconds minus the last position
  // updated time.
  auto timeElapsed = aNow - mPositionUpdatedTime;
  // Mutliply time elapsed with actual playback rate.
  timeElapsed = timeElapsed.MultDouble(mPlaybackRate);
  // Set position to time elapsed added to last reported playback position.
  auto position = timeElapsed.ToSeconds() + mLastReportedPlaybackPosition;

  // If position is less than zero, return zero.
  if (position < 0.0) {
    return 0.0;
  }
  // If position is greater than duration, return duration.
  if (position > mDuration) {
    return mDuration;
  }
  // Return position.
  return position;
}

// We don't use NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE because we need to
// unregister MediaSession from document's activity listeners.
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(MediaSession)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaSession)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaMetadata)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActionHandlers)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaSession)
  tmp->Shutdown();
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaMetadata)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mActionHandlers)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaSession)
NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaSession)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaSession)
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  NS_INTERFACE_MAP_ENTRY(nsISupports)
  NS_INTERFACE_MAP_ENTRY(nsIDocumentActivity)
NS_INTERFACE_MAP_END

MediaSession::MediaSession(nsPIDOMWindowInner* aParent)
    : mParent(aParent), mDoc(mParent->GetExtantDoc()) {
  MOZ_ASSERT(mParent);
  MOZ_ASSERT(mDoc);
  mDoc->RegisterActivityObserver(this);
  if (mDoc->IsCurrentActiveDocument()) {
    SetMediaSessionDocStatus(SessionDocStatus::eActive);
  }
}

MediaSession::~MediaSession() { DisconnectRequestAndListener(); }

void MediaSession::DisconnectRequestAndListener() {
  mLoadingArtworkRequest.DisconnectIfExists();
  mMetadataChangeListener.DisconnectIfExists();
}

void MediaSession::Shutdown() {
  if (mDoc) {
    mDoc->UnregisterActivityObserver(this);
  }
  if (mParent) {
    SetMediaSessionDocStatus(SessionDocStatus::eInactive);
  }
  DisconnectRequestAndListener();
}

void MediaSession::NotifyOwnerDocumentActivityChanged() {
  const bool isDocActive = mDoc->IsCurrentActiveDocument();
  LOG("Document activity changed, isActive={}", isDocActive);
  if (isDocActive) {
    SetMediaSessionDocStatus(SessionDocStatus::eActive);
  } else {
    SetMediaSessionDocStatus(SessionDocStatus::eInactive);
  }
}

void MediaSession::SetMediaSessionDocStatus(SessionDocStatus aState) {
  if (mSessionDocState == aState) {
    return;
  }
  mSessionDocState = aState;
  NotifyMediaSessionDocStatus(mSessionDocState);
}

nsPIDOMWindowInner* MediaSession::GetParentObject() const { return mParent; }

JSObject* MediaSession::WrapObject(JSContext* aCx,
                                   JS::Handle<JSObject*> aGivenProto) {
  return MediaSession_Binding::Wrap(aCx, this, aGivenProto);
}

MediaMetadata* MediaSession::GetMetadata() const { return mMediaMetadata; }

void MediaSession::SetMetadata(MediaMetadata* aMetadata) {
  mMetadataChangeListener.DisconnectIfExists();
  mMediaMetadata = aMetadata;
  // Do not register a raw-pointer listener while inactive. eInactive covers
  // both bfcache (temporary, session may resume) and Navigator::Invalidate()
  // (permanent teardown). We still update mMediaMetadata above so that
  // NotifyMediaSessionAttributes() can resync it when the session becomes
  // active again after bfcache restore.
  if (mMediaMetadata && mSessionDocState != SessionDocStatus::eInactive) {
    mMetadataChangeListener = mMediaMetadata->MetadataChangeEvent().Connect(
        AbstractThread::MainThread(), this,
        &MediaSession::NotifyMetadataUpdated);
  }
  NotifyMetadataUpdated();
}

void MediaSession::SetPlaybackState(
    const MediaSessionPlaybackState& aPlaybackState) {
  if (mDeclaredPlaybackState == aPlaybackState) {
    return;
  }
  mDeclaredPlaybackState = aPlaybackState;
  NotifyPlaybackStateUpdated();
}

MediaSessionPlaybackState MediaSession::PlaybackState() const {
  return mDeclaredPlaybackState;
}

void MediaSession::SetActionHandler(MediaSessionAction aAction,
                                    MediaSessionActionHandler* aHandler) {
  MOZ_ASSERT(size_t(aAction) < std::size(mActionHandlers));
  // If the media session changes its supported action, then we would propagate
  // this information to the chrome process in order to run the media session
  // actions update algorithm.
  // https://w3c.github.io/mediasession/#supported-media-session-actions
  RefPtr<MediaSessionActionHandler>& handler = mActionHandlers[aAction];
  if (!handler && aHandler) {
    NotifyEnableSupportedAction(aAction);
  } else if (handler && !aHandler) {
    NotifyDisableSupportedAction(aAction);
  }
  mActionHandlers[aAction] = aHandler;
}

MediaSessionActionHandler* MediaSession::GetActionHandler(
    MediaSessionAction aAction) const {
  MOZ_ASSERT(size_t(aAction) < std::size(mActionHandlers));
  return mActionHandlers[aAction];
}

void MediaSession::SetPositionState(const MediaPositionState& aState,
                                    ErrorResult& aRv) {
  // https://w3c.github.io/mediasession/#dom-mediasession-setpositionstate
  // If the state is an empty dictionary then clear the position state.
  if (!aState.IsAnyMemberPresent()) {
    mPositionState.reset();
    NotifyPositionStateChanged();
    return;
  }

  // If the duration is not present, throw a TypeError.
  if (!aState.mDuration.WasPassed()) {
    return aRv.ThrowTypeError("Duration is not present");
  }

  // If the duration is negative, throw a TypeError.
  if (aState.mDuration.WasPassed() && aState.mDuration.Value() < 0.0) {
    return aRv.ThrowTypeError(nsPrintfCString(
        "Invalid duration %f, it can't be negative", aState.mDuration.Value()));
  }

  // If the position is negative or greater than duration, throw a TypeError.
  if (aState.mPosition.WasPassed() &&
      (aState.mPosition.Value() < 0.0 ||
       aState.mPosition.Value() > aState.mDuration.Value())) {
    return aRv.ThrowTypeError(nsPrintfCString(
        "Invalid position %f, it can't be negative or greater than duration",
        aState.mPosition.Value()));
  }

  // If the playbackRate is zero, throw a TypeError.
  if (aState.mPlaybackRate.WasPassed() && aState.mPlaybackRate.Value() == 0.0) {
    return aRv.ThrowTypeError("The playbackRate is zero");
  }

  // If the position is not present, set it to zero.
  double position = aState.mPosition.WasPassed() ? aState.mPosition.Value() : 0;

  // If the playbackRate is not present, set it to 1.0.
  double playbackRate =
      aState.mPlaybackRate.WasPassed() ? aState.mPlaybackRate.Value() : 1.0;

  // Update the position state and last position updated time.
  MOZ_ASSERT(aState.mDuration.WasPassed());
  mPositionState = Some(PositionState(aState.mDuration.Value(), playbackRate,
                                      position, TimeStamp::Now()));
  NotifyPositionStateChanged();
}

void MediaSession::NotifyHandler(const MediaSessionActionDetails& aDetails) {
  DispatchNotifyHandler(aDetails);
}

void MediaSession::DispatchNotifyHandler(
    const MediaSessionActionDetails& aDetails) {
  class Runnable final : public mozilla::Runnable {
   public:
    Runnable(const MediaSession* aSession,
             const MediaSessionActionDetails& aDetails)
        : mozilla::Runnable("MediaSession::DispatchNotifyHandler"),
          mSession(aSession),
          mDetails(aDetails) {}

    MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
      if (RefPtr<MediaSessionActionHandler> handler =
              mSession->GetActionHandler(mDetails.mAction)) {
        handler->Call(mDetails);
      }
      return NS_OK;
    }

   private:
    RefPtr<const MediaSession> mSession;
    MediaSessionActionDetails mDetails;
  };

  RefPtr<nsIRunnable> runnable = new Runnable(this, aDetails);
  NS_DispatchToMainThread(runnable);
}

bool MediaSession::IsSupportedAction(MediaSessionAction aAction) const {
  MOZ_ASSERT(size_t(aAction) < std::size(mActionHandlers));
  return mActionHandlers[aAction] != nullptr;
}

bool MediaSession::IsActive() const {
  RefPtr<BrowsingContext> currentBC = GetParentObject()->GetBrowsingContext();
  MOZ_ASSERT(currentBC);
  RefPtr<WindowContext> wc = currentBC->GetTopWindowContext();
  if (!wc) {
    return false;
  }
  Maybe<uint64_t> activeSessionContextId = wc->GetActiveMediaSessionContextId();
  if (!activeSessionContextId) {
    return false;
  }
  LOG("session context Id={}, active session context Id={}", currentBC->Id(),
      *activeSessionContextId);
  return *activeSessionContextId == currentBC->Id();
}

void MediaSession::NotifyMediaSessionDocStatus(SessionDocStatus aState) {
  RefPtr<BrowsingContext> currentBC = GetParentObject()->GetBrowsingContext();
  MOZ_ASSERT(currentBC, "Update session status after context destroyed!");

  RefPtr<IMediaInfoUpdater> updater = ContentMediaAgent::Get(currentBC);
  if (!updater) {
    return;
  }
  if (aState == SessionDocStatus::eActive) {
    updater->NotifySessionCreated(currentBC->Id());
    // If media session set its attributes before its document becomes active,
    // then we would notify those attributes which hasn't been notified as well
    // because attributes update would only happen if its document is already
    // active.
    NotifyMediaSessionAttributes();
  } else {
    updater->NotifySessionDestroyed(currentBC->Id());
  }
}

void MediaSession::NotifyMediaSessionAttributes() {
  MOZ_ASSERT(mSessionDocState == SessionDocStatus::eActive);
  if (mDeclaredPlaybackState != MediaSessionPlaybackState::None) {
    NotifyPlaybackStateUpdated();
  }
  if (mMediaMetadata) {
    NotifyMetadataUpdated();
  }
  for (size_t idx = 0; idx < std::size(mActionHandlers); idx++) {
    MediaSessionAction action = static_cast<MediaSessionAction>(idx);
    if (mActionHandlers[action]) {
      NotifyEnableSupportedAction(action);
    }
  }
  if (mPositionState) {
    NotifyPositionStateChanged();
  }
}

void MediaSession::NotifyPlaybackStateUpdated() {
  if (mSessionDocState != SessionDocStatus::eActive) {
    return;
  }
  RefPtr<BrowsingContext> currentBC = GetParentObject()->GetBrowsingContext();
  MOZ_ASSERT(currentBC,
             "Update session playback state after context destroyed!");
  if (RefPtr<IMediaInfoUpdater> updater = ContentMediaAgent::Get(currentBC)) {
    updater->SetDeclaredPlaybackState(currentBC->Id(), mDeclaredPlaybackState);
  }
}

void MediaSession::NotifyMetadataUpdated() {
  if (mSessionDocState != SessionDocStatus::eActive) {
    return;
  }
  RefPtr<BrowsingContext> currentBC = GetParentObject()->GetBrowsingContext();
  MOZ_ASSERT(currentBC, "Update session metadata after context destroyed!");

  mLoadingArtworkRequest.DisconnectIfExists();

  RefPtr<IMediaInfoUpdater> updater = ContentMediaAgent::Get(currentBC);
  if (!updater) {
    return;
  }

  Maybe<MediaMetadataBase> metadata;
  if (mMediaMetadata) {
    metadata.emplace(*mMediaMetadata->AsMetadataBaseWithoutArtworkSurface());
  }

  updater->UpdateMetadata(currentBC->Id(), metadata);

  // If we don't have any metadata, just return. Otherwise we might have
  // artwork that we have to load asynchronously and update the metadata
  // together with the artwork's image surface again.
  if (metadata.isNothing() || metadata->mArtwork.IsEmpty()) {
    return;
  }

  if (!mDoc) {
    return;
  }

  LOG("Starting load of the MediaMetadata artwork.");
  mMediaMetadata->LoadMetadataArtwork(mDoc)
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [self = RefPtr{this}, currentBC](MediaMetadataBase&& aMetadata) {
            if (RefPtr<IMediaInfoUpdater> updater =
                    ContentMediaAgent::Get(currentBC)) {
              updater->UpdateMetadata(currentBC->Id(), Some(aMetadata));
            }

            self->mLoadingArtworkRequest.Complete();
          },
          [](nsresult rv) {
            MOZ_ASSERT_UNREACHABLE("LoadMetadataArtwork should always resolve");
          })
      ->Track(mLoadingArtworkRequest);
}

void MediaSession::NotifyEnableSupportedAction(MediaSessionAction aAction) {
  if (mSessionDocState != SessionDocStatus::eActive) {
    return;
  }
  RefPtr<BrowsingContext> currentBC = GetParentObject()->GetBrowsingContext();
  MOZ_ASSERT(currentBC, "Update action after context destroyed!");
  if (RefPtr<IMediaInfoUpdater> updater = ContentMediaAgent::Get(currentBC)) {
    updater->EnableAction(currentBC->Id(), aAction);
  }
}

void MediaSession::NotifyDisableSupportedAction(MediaSessionAction aAction) {
  if (mSessionDocState != SessionDocStatus::eActive) {
    return;
  }
  RefPtr<BrowsingContext> currentBC = GetParentObject()->GetBrowsingContext();
  MOZ_ASSERT(currentBC, "Update action after context destroyed!");
  if (RefPtr<IMediaInfoUpdater> updater = ContentMediaAgent::Get(currentBC)) {
    updater->DisableAction(currentBC->Id(), aAction);
  }
}

void MediaSession::NotifyPositionStateChanged() {
  if (mSessionDocState != SessionDocStatus::eActive) {
    return;
  }
  RefPtr<BrowsingContext> currentBC = GetParentObject()->GetBrowsingContext();
  MOZ_ASSERT(currentBC, "Update action after context destroyed!");
  if (RefPtr<IMediaInfoUpdater> updater = ContentMediaAgent::Get(currentBC)) {
    updater->UpdatePositionState(currentBC->Id(), mPositionState);
  }
}

}  // namespace mozilla::dom

[0.46Quellennavigators]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002