mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	This prevents HTMLMediaElements from being prematurely cycle-collected, and also makes things a bit safer. Differential Revision: https://phabricator.services.mozilla.com/D205734
		
			
				
	
	
		
			554 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			554 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			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>(GetOwner());
 | 
						|
 | 
						|
  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() {
 | 
						|
  if (!GetOwner()) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
  nsCOMPtr<nsIPrincipal> principal =
 | 
						|
      nsGlobalWindowInner::Cast(GetOwner())->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);
 | 
						|
}
 |