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


Quelle  MediaSession.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/BrowsingContext.h"
#include "mozilla/dom/ContentMediaController.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/MediaSession.h"
#include "mozilla/dom/MediaControlUtils.h"
#include "mozilla/dom/WindowContext.h"
#include "mozilla/EnumeratedArrayCycleCollection.h"

// avoid redefined macro in unified build
#undef LOG
#define LOG(msg, ...)                        \
  MOZ_LOG(gMediaControlLog, LogLevel::Debug, \
          ("MediaSession=%p, " msg, 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);
  }
}

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

void MediaSession::NotifyOwnerDocumentActivityChanged() {
  const bool isDocActive = mDoc->IsCurrentActiveDocument();
  LOG("Document activity changed, isActive=%d", 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) {
  mMediaMetadata = aMetadata;
  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=%" PRIu64 ", active session context Id=%" PRIu64,
      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!");

  Maybe<MediaMetadataBase> metadata;
  if (GetMetadata()) {
    metadata.emplace(*(GetMetadata()->AsMetadataBase()));
  }
  if (RefPtr<IMediaInfoUpdater> updater = ContentMediaAgent::Get(currentBC)) {
    updater->UpdateMetadata(currentBC->Id(), metadata);
  }
}

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

Messung V0.5
C=94 H=97 G=95

¤ Dauer der Verarbeitung: 0.26 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

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

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge