Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/media/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 15 kB image not shown  

Quelle  DOMMediaStream.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* 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 "DOMMediaStream.h"

#include "AudioCaptureTrack.h"
#include "AudioChannelAgent.h"
#include "AudioStreamTrack.h"
#include "MediaTrackGraph.h"
#include "MediaTrackGraphImpl.h"
#include "MediaTrackListener.h"
#include "Tracing.h"
#include "VideoStreamTrack.h"
#include "mozilla/dom/AudioTrack.h"
#include "mozilla/dom/AudioTrackList.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/dom/MediaStreamBinding.h"
#include "mozilla/dom/MediaStreamTrackEvent.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/VideoTrack.h"
#include "mozilla/dom/VideoTrackList.h"
#include "mozilla/media/MediaUtils.h"
#include "nsContentUtils.h"
#include "nsGlobalWindowInner.h"
#include "nsIUUIDGenerator.h"
#include "nsPIDOMWindow.h"
#include "nsProxyRelease.h"
#include "nsRFPService.h"
#include "nsServiceManagerUtils.h"

#ifdef LOG
#  undef LOG
#endif

using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::layers;
using namespace mozilla::media;

static LazyLogModule gMediaStreamLog("MediaStream");
#define LOG(type, msg) MOZ_LOG(gMediaStreamLog, type, msg)

static bool ContainsLiveTracks(
    const nsTArray<RefPtr<MediaStreamTrack>>& aTracks) {
  for (const auto& track : aTracks) {
    if (track->ReadyState() == MediaStreamTrackState::Live) {
      return true;
    }
  }

  return false;
}

static bool ContainsLiveAudioTracks(
    const nsTArray<RefPtr<MediaStreamTrack>>& aTracks) {
  for (const auto& track : aTracks) {
    if (track->AsAudioStreamTrack() &&
        track->ReadyState() == MediaStreamTrackState::Live) {
      return true;
    }
  }

  return false;
}

class DOMMediaStream::PlaybackTrackListener : public MediaStreamTrackConsumer {
 public:
  NS_INLINE_DECL_REFCOUNTING(PlaybackTrackListener)

  explicit PlaybackTrackListener(DOMMediaStream* aStream) : mStream(aStream) {}

  void NotifyEnded(MediaStreamTrack* aTrack) override {
    if (!mStream) {
      return;
    }

    if (!aTrack) {
      MOZ_ASSERT(false);
      return;
    }

    MOZ_ASSERT(mStream->HasTrack(*aTrack));
    mStream->NotifyTrackRemoved(aTrack);
  }

 protected:
  virtual ~PlaybackTrackListener() = default;

  WeakPtr<DOMMediaStream> mStream;
};

NS_IMPL_CYCLE_COLLECTION_CLASS(DOMMediaStream)

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMMediaStream,
                                                DOMEventTargetHelper)
  tmp->Destroy();
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsumersToKeepAlive)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTrackListeners)
  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMMediaStream,
                                                  DOMEventTargetHelper)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTracks)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsumersToKeepAlive)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTrackListeners)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_ADDREF_INHERITED(DOMMediaStream, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(DOMMediaStream, DOMEventTargetHelper)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMMediaStream)
  NS_INTERFACE_MAP_ENTRY_CONCRETE(DOMMediaStream)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)

NS_IMPL_CYCLE_COLLECTION(DOMMediaStream::TrackListener)
NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMMediaStream::TrackListener)
NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMMediaStream::TrackListener)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMMediaStream::TrackListener)
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

DOMMediaStream::DOMMediaStream(nsPIDOMWindowInner* aWindow)
    : DOMEventTargetHelper(aWindow),
      mPlaybackTrackListener(MakeAndAddRef<PlaybackTrackListener>(this)) {
  nsresult rv;
  nsCOMPtr<nsIUUIDGenerator> uuidgen =
      do_GetService("@mozilla.org/uuid-generator;1", &rv);

  if (NS_SUCCEEDED(rv) && uuidgen) {
    nsID uuid;
    memset(&uuid, 0, sizeof(uuid));
    rv = uuidgen->GenerateUUIDInPlace(&uuid);
    if (NS_SUCCEEDED(rv)) {
      char buffer[NSID_LENGTH];
      uuid.ToProvidedString(buffer);
      mID = NS_ConvertASCIItoUTF16(buffer);
    }
  }
}

DOMMediaStream::~DOMMediaStream() { Destroy(); }

void DOMMediaStream::Destroy() {
  LOG(LogLevel::Debug, ("DOMMediaStream %p Being destroyed."this));
  for (const auto& track : mTracks) {
    // We must remove ourselves from each track's principal change observer list
    // before we die.
    if (!track->Ended()) {
      track->RemoveConsumer(mPlaybackTrackListener);
    }
  }
  mTrackListeners.Clear();
}

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

/* static */
already_AddRefed<DOMMediaStream> DOMMediaStream::Constructor(
    const GlobalObject& aGlobal, ErrorResult& aRv) {
  Sequence<OwningNonNull<MediaStreamTrack>> emptyTrackSeq;
  return Constructor(aGlobal, emptyTrackSeq, aRv);
}

/* static */
already_AddRefed<DOMMediaStream> DOMMediaStream::Constructor(
    const GlobalObject& aGlobal, const DOMMediaStream& aStream,
    ErrorResult& aRv) {
  nsTArray<RefPtr<MediaStreamTrack>> tracks;
  aStream.GetTracks(tracks);

  Sequence<OwningNonNull<MediaStreamTrack>> nonNullTrackSeq;
  if (!nonNullTrackSeq.SetLength(tracks.Length(), fallible)) {
    MOZ_ASSERT(false);
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    return nullptr;
  }

  for (size_t i = 0; i < tracks.Length(); ++i) {
    nonNullTrackSeq[i] = tracks[i];
  }

  return Constructor(aGlobal, nonNullTrackSeq, aRv);
}

/* static */
already_AddRefed<DOMMediaStream> DOMMediaStream::Constructor(
    const GlobalObject& aGlobal,
    const Sequence<OwningNonNull<MediaStreamTrack>>& aTracks,
    ErrorResult& aRv) {
  nsCOMPtr<nsPIDOMWindowInner> ownerWindow =
      do_QueryInterface(aGlobal.GetAsSupports());
  if (!ownerWindow) {
    aRv.Throw(NS_ERROR_FAILURE);
    return nullptr;
  }

  auto newStream = MakeRefPtr<DOMMediaStream>(ownerWindow);
  for (MediaStreamTrack& track : aTracks) {
    newStream->AddTrack(track);
  }
  return newStream.forget();
}

already_AddRefed<Promise> DOMMediaStream::CountUnderlyingStreams(
    const GlobalObject& aGlobal, ErrorResult& aRv) {
  nsCOMPtr<nsPIDOMWindowInner> window =
      do_QueryInterface(aGlobal.GetAsSupports());
  if (!window) {
    aRv.Throw(NS_ERROR_UNEXPECTED);
    return nullptr;
  }

  nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(aGlobal.GetAsSupports());
  if (!go) {
    aRv.Throw(NS_ERROR_UNEXPECTED);
    return nullptr;
  }

  RefPtr<Promise> p = Promise::Create(go, aRv);
  if (aRv.Failed()) {
    return nullptr;
  }

  MediaTrackGraph* graph = MediaTrackGraph::GetInstanceIfExists(
      window, MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE,
      MediaTrackGraph::DEFAULT_OUTPUT_DEVICE);
  if (!graph) {
    p->MaybeResolve(0);
    return p.forget();
  }

  auto* graphImpl = static_cast<MediaTrackGraphImpl*>(graph);

  class Counter : public ControlMessage {
   public:
    Counter(MediaTrackGraphImpl* aGraph, const RefPtr<Promise>& aPromise)
        : ControlMessage(nullptr), mGraph(aGraph), mPromise(aPromise) {
      MOZ_ASSERT(NS_IsMainThread());
    }

    void Run() override {
      TRACE("DOMMediaStream::Counter")
      uint32_t streams =
          mGraph->mTracks.Length() + mGraph->mSuspendedTracks.Length();
      mGraph->DispatchToMainThreadStableState(NS_NewRunnableFunction(
          "DOMMediaStream::CountUnderlyingStreams (stable state)",
          [promise = std::move(mPromise), streams]() mutable {
            NS_DispatchToMainThread(NS_NewRunnableFunction(
                "DOMMediaStream::CountUnderlyingStreams",
                [promise = std::move(promise), streams]() {
                  promise->MaybeResolve(streams);
                }));
          }));
    }

    // mPromise can only be AddRefed/Released on main thread.
    // In case of shutdown, Run() does not run, so we dispatch mPromise to be
    // released on main thread here.
    void RunDuringShutdown() override {
      NS_ReleaseOnMainThread(
          "DOMMediaStream::CountUnderlyingStreams::Counter::RunDuringShutdown",
          mPromise.forget());
    }

   private:
    // mGraph owns this Counter instance and decides its lifetime.
    MediaTrackGraphImpl* mGraph;
    RefPtr<Promise> mPromise;
  };
  graphImpl->AppendMessage(MakeUnique<Counter>(graphImpl, p));

  return p.forget();
}

void DOMMediaStream::GetId(nsAString& aID) const { aID = mID; }

void DOMMediaStream::GetAudioTracks(
    nsTArray<RefPtr<AudioStreamTrack>>& aTracks) const {
  for (const auto& track : mTracks) {
    if (AudioStreamTrack* t = track->AsAudioStreamTrack()) {
      aTracks.AppendElement(t);
    }
  }
}

void DOMMediaStream::GetAudioTracks(
    nsTArray<RefPtr<MediaStreamTrack>>& aTracks) const {
  for (const auto& track : mTracks) {
    if (track->AsAudioStreamTrack()) {
      aTracks.AppendElement(track);
    }
  }
}

void DOMMediaStream::GetVideoTracks(
    nsTArray<RefPtr<VideoStreamTrack>>& aTracks) const {
  for (const auto& track : mTracks) {
    if (VideoStreamTrack* t = track->AsVideoStreamTrack()) {
      aTracks.AppendElement(t);
    }
  }
}

void DOMMediaStream::GetVideoTracks(
    nsTArray<RefPtr<MediaStreamTrack>>& aTracks) const {
  for (const auto& track : mTracks) {
    if (track->AsVideoStreamTrack()) {
      aTracks.AppendElement(track);
    }
  }
}

void DOMMediaStream::GetTracks(
    nsTArray<RefPtr<MediaStreamTrack>>& aTracks) const {
  for (const auto& track : mTracks) {
    aTracks.AppendElement(track);
  }
}

void DOMMediaStream::AddTrack(MediaStreamTrack& aTrack) {
  LOG(LogLevel::Info, ("DOMMediaStream %p Adding track %p (from track %p)",
                       this, &aTrack, aTrack.GetTrack()));

  if (HasTrack(aTrack)) {
    LOG(LogLevel::Debug,
        ("DOMMediaStream %p already contains track %p"this, &aTrack));
    return;
  }

  mTracks.AppendElement(&aTrack);

  if (!aTrack.Ended()) {
    NotifyTrackAdded(&aTrack);
  }
}

void DOMMediaStream::RemoveTrack(MediaStreamTrack& aTrack) {
  if (static_cast<LogModule*>(gMediaStreamLog)->ShouldLog(LogLevel::Info)) {
    if (aTrack.Ended()) {
      LOG(LogLevel::Info,
          ("DOMMediaStream %p Removing (ended) track %p"this, &aTrack));
    } else {
      LOG(LogLevel::Info,
          ("DOMMediaStream %p Removing track %p (from track %p)"this, &aTrack,
           aTrack.GetTrack()));
    }
  }

  if (!mTracks.RemoveElement(&aTrack)) {
    LOG(LogLevel::Debug,
        ("DOMMediaStream %p does not contain track %p"this, &aTrack));
    return;
  }

  if (!aTrack.Ended()) {
    NotifyTrackRemoved(&aTrack);
  }
}

already_AddRefed<DOMMediaStream> DOMMediaStream::Clone() {
  auto newStream = MakeRefPtr<DOMMediaStream>(GetOwnerWindow());

  LOG(LogLevel::Info,
      ("DOMMediaStream %p created clone %p"this, newStream.get()));

  for (const auto& track : mTracks) {
    LOG(LogLevel::Debug,
        ("DOMMediaStream %p forwarding external track %p to clone %p"this,
         track.get(), newStream.get()));
    RefPtr<MediaStreamTrack> clone = track->Clone();
    newStream->AddTrack(*clone);
  }

  return newStream.forget();
}

bool DOMMediaStream::Active() const { return mActive; }
bool DOMMediaStream::Audible() const { return mAudible; }

MediaStreamTrack* DOMMediaStream::GetTrackById(const nsAString& aId) const {
  for (const auto& track : mTracks) {
    nsString id;
    track->GetId(id);
    if (id == aId) {
      return track;
    }
  }
  return nullptr;
}

bool DOMMediaStream::HasTrack(const MediaStreamTrack& aTrack) const {
  return mTracks.Contains(&aTrack);
}

void DOMMediaStream::AddTrackInternal(MediaStreamTrack* aTrack) {
  LOG(LogLevel::Debug,
      ("DOMMediaStream %p Adding owned track %p"this, aTrack));
  AddTrack(*aTrack);
  DispatchTrackEvent(u"addtrack"_ns, aTrack);
}

void DOMMediaStream::RemoveTrackInternal(MediaStreamTrack* aTrack) {
  LOG(LogLevel::Debug,
      ("DOMMediaStream %p Removing owned track %p"this, aTrack));
  if (!HasTrack(*aTrack)) {
    return;
  }
  RemoveTrack(*aTrack);
  DispatchTrackEvent(u"removetrack"_ns, aTrack);
}

already_AddRefed<nsIPrincipal> DOMMediaStream::GetPrincipal() {
  nsGlobalWindowInner* win = GetOwnerWindow();
  if (!win) {
    return nullptr;
  }
  nsCOMPtr<nsIPrincipal> principal = win->GetPrincipal();
  for (const auto& t : mTracks) {
    if (t->Ended()) {
      continue;
    }
    nsContentUtils::CombineResourcePrincipals(&principal, t->GetPrincipal());
  }
  return principal.forget();
}

void DOMMediaStream::NotifyActive() {
  LOG(LogLevel::Info, ("DOMMediaStream %p NotifyActive(). "this));

  MOZ_ASSERT(mActive);
  for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
    mTrackListeners[i]->NotifyActive();
  }
}

void DOMMediaStream::NotifyInactive() {
  LOG(LogLevel::Info, ("DOMMediaStream %p NotifyInactive(). "this));

  MOZ_ASSERT(!mActive);
  for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
    mTrackListeners[i]->NotifyInactive();
  }
}

void DOMMediaStream::NotifyAudible() {
  LOG(LogLevel::Info, ("DOMMediaStream %p NotifyAudible(). "this));

  MOZ_ASSERT(mAudible);
  for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
    mTrackListeners[i]->NotifyAudible();
  }
}

void DOMMediaStream::NotifyInaudible() {
  LOG(LogLevel::Info, ("DOMMediaStream %p NotifyInaudible(). "this));

  MOZ_ASSERT(!mAudible);
  for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
    mTrackListeners[i]->NotifyInaudible();
  }
}

void DOMMediaStream::RegisterTrackListener(TrackListener* aListener) {
  MOZ_ASSERT(NS_IsMainThread());

  mTrackListeners.AppendElement(aListener);
}

void DOMMediaStream::UnregisterTrackListener(TrackListener* aListener) {
  MOZ_ASSERT(NS_IsMainThread());
  mTrackListeners.RemoveElement(aListener);
}

void DOMMediaStream::NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack) {
  MOZ_ASSERT(NS_IsMainThread());

  aTrack->AddConsumer(mPlaybackTrackListener);

  for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
    mTrackListeners[i]->NotifyTrackAdded(aTrack);
  }

  if (!mActive) {
    // Check if we became active.
    if (ContainsLiveTracks(mTracks)) {
      mActive = true;
      NotifyActive();
    }
  }

  if (!mAudible) {
    // Check if we became audible.
    if (ContainsLiveAudioTracks(mTracks)) {
      mAudible = true;
      NotifyAudible();
    }
  }
}

void DOMMediaStream::NotifyTrackRemoved(
    const RefPtr<MediaStreamTrack>& aTrack) {
  MOZ_ASSERT(NS_IsMainThread());

  if (aTrack) {
    // aTrack may be null to allow HTMLMediaElement::MozCaptureStream streams
    // to be played until the source media element has ended. The source media
    // element will then call NotifyTrackRemoved(nullptr) to signal that we can
    // go inactive, regardless of the timing of the last track ending.

    aTrack->RemoveConsumer(mPlaybackTrackListener);

    for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
      mTrackListeners[i]->NotifyTrackRemoved(aTrack);
    }

    if (!mActive) {
      NS_ASSERTION(false"Shouldn't remove a live track if already inactive");
      return;
    }
  }

  if (mAudible) {
    // Check if we became inaudible.
    if (!ContainsLiveAudioTracks(mTracks)) {
      mAudible = false;
      NotifyInaudible();
    }
  }

  // Check if we became inactive.
  if (!ContainsLiveTracks(mTracks)) {
    mActive = false;
    NotifyInactive();
  }
}

nsresult DOMMediaStream::DispatchTrackEvent(
    const nsAString& aName, const RefPtr<MediaStreamTrack>& aTrack) {
  MediaStreamTrackEventInit init;
  init.mTrack = aTrack;

  RefPtr<MediaStreamTrackEvent> event =
      MediaStreamTrackEvent::Constructor(this, aName, init);

  return DispatchTrustedEvent(event);
}

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

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© 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.