1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "DOMMediaStream.h"
8 #include "AudioCaptureTrack.h"
9 #include "AudioChannelAgent.h"
10 #include "AudioStreamTrack.h"
11 #include "MediaTrackGraph.h"
12 #include "MediaTrackGraphImpl.h"
13 #include "MediaTrackListener.h"
15 #include "VideoStreamTrack.h"
16 #include "mozilla/dom/AudioTrack.h"
17 #include "mozilla/dom/AudioTrackList.h"
18 #include "mozilla/dom/DocGroup.h"
19 #include "mozilla/dom/HTMLCanvasElement.h"
20 #include "mozilla/dom/MediaStreamBinding.h"
21 #include "mozilla/dom/MediaStreamTrackEvent.h"
22 #include "mozilla/dom/Promise.h"
23 #include "mozilla/dom/VideoTrack.h"
24 #include "mozilla/dom/VideoTrackList.h"
25 #include "mozilla/media/MediaUtils.h"
26 #include "nsContentUtils.h"
27 #include "nsGlobalWindowInner.h"
28 #include "nsIUUIDGenerator.h"
29 #include "nsPIDOMWindow.h"
30 #include "nsProxyRelease.h"
31 #include "nsRFPService.h"
32 #include "nsServiceManagerUtils.h"
38 using namespace mozilla
;
39 using namespace mozilla::dom
;
40 using namespace mozilla::layers
;
41 using namespace mozilla::media
;
43 static LazyLogModule
gMediaStreamLog("MediaStream");
44 #define LOG(type, msg) MOZ_LOG(gMediaStreamLog, type, msg)
46 static bool ContainsLiveTracks(
47 const nsTArray
<RefPtr
<MediaStreamTrack
>>& aTracks
) {
48 for (const auto& track
: aTracks
) {
49 if (track
->ReadyState() == MediaStreamTrackState::Live
) {
57 static bool ContainsLiveAudioTracks(
58 const nsTArray
<RefPtr
<MediaStreamTrack
>>& aTracks
) {
59 for (const auto& track
: aTracks
) {
60 if (track
->AsAudioStreamTrack() &&
61 track
->ReadyState() == MediaStreamTrackState::Live
) {
69 class DOMMediaStream::PlaybackTrackListener
: public MediaStreamTrackConsumer
{
71 NS_INLINE_DECL_REFCOUNTING(PlaybackTrackListener
)
73 explicit PlaybackTrackListener(DOMMediaStream
* aStream
) : mStream(aStream
) {}
75 void NotifyEnded(MediaStreamTrack
* aTrack
) override
{
85 MOZ_ASSERT(mStream
->HasTrack(*aTrack
));
86 mStream
->NotifyTrackRemoved(aTrack
);
90 virtual ~PlaybackTrackListener() = default;
92 WeakPtr
<DOMMediaStream
> mStream
;
95 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMMediaStream
)
97 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMMediaStream
,
100 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks
)
101 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsumersToKeepAlive
)
102 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTrackListeners
)
103 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
104 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
106 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMMediaStream
,
107 DOMEventTargetHelper
)
108 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTracks
)
109 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsumersToKeepAlive
)
110 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTrackListeners
)
111 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
113 NS_IMPL_ADDREF_INHERITED(DOMMediaStream
, DOMEventTargetHelper
)
114 NS_IMPL_RELEASE_INHERITED(DOMMediaStream
, DOMEventTargetHelper
)
116 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMMediaStream
)
117 NS_INTERFACE_MAP_ENTRY_CONCRETE(DOMMediaStream
)
118 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
120 NS_IMPL_CYCLE_COLLECTION(DOMMediaStream::TrackListener
)
121 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMMediaStream::TrackListener
)
122 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMMediaStream::TrackListener
)
123 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMMediaStream::TrackListener
)
124 NS_INTERFACE_MAP_ENTRY(nsISupports
)
127 DOMMediaStream::DOMMediaStream(nsPIDOMWindowInner
* aWindow
)
128 : DOMEventTargetHelper(aWindow
),
129 mPlaybackTrackListener(MakeAndAddRef
<PlaybackTrackListener
>(this)) {
131 nsCOMPtr
<nsIUUIDGenerator
> uuidgen
=
132 do_GetService("@mozilla.org/uuid-generator;1", &rv
);
134 if (NS_SUCCEEDED(rv
) && uuidgen
) {
136 memset(&uuid
, 0, sizeof(uuid
));
137 rv
= uuidgen
->GenerateUUIDInPlace(&uuid
);
138 if (NS_SUCCEEDED(rv
)) {
139 char buffer
[NSID_LENGTH
];
140 uuid
.ToProvidedString(buffer
);
141 mID
= NS_ConvertASCIItoUTF16(buffer
);
146 DOMMediaStream::~DOMMediaStream() { Destroy(); }
148 void DOMMediaStream::Destroy() {
149 LOG(LogLevel::Debug
, ("DOMMediaStream %p Being destroyed.", this));
150 for (const auto& track
: mTracks
) {
151 // We must remove ourselves from each track's principal change observer list
153 if (!track
->Ended()) {
154 track
->RemoveConsumer(mPlaybackTrackListener
);
157 mTrackListeners
.Clear();
160 JSObject
* DOMMediaStream::WrapObject(JSContext
* aCx
,
161 JS::Handle
<JSObject
*> aGivenProto
) {
162 return dom::MediaStream_Binding::Wrap(aCx
, this, aGivenProto
);
166 already_AddRefed
<DOMMediaStream
> DOMMediaStream::Constructor(
167 const GlobalObject
& aGlobal
, ErrorResult
& aRv
) {
168 Sequence
<OwningNonNull
<MediaStreamTrack
>> emptyTrackSeq
;
169 return Constructor(aGlobal
, emptyTrackSeq
, aRv
);
173 already_AddRefed
<DOMMediaStream
> DOMMediaStream::Constructor(
174 const GlobalObject
& aGlobal
, const DOMMediaStream
& aStream
,
176 nsTArray
<RefPtr
<MediaStreamTrack
>> tracks
;
177 aStream
.GetTracks(tracks
);
179 Sequence
<OwningNonNull
<MediaStreamTrack
>> nonNullTrackSeq
;
180 if (!nonNullTrackSeq
.SetLength(tracks
.Length(), fallible
)) {
182 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
186 for (size_t i
= 0; i
< tracks
.Length(); ++i
) {
187 nonNullTrackSeq
[i
] = tracks
[i
];
190 return Constructor(aGlobal
, nonNullTrackSeq
, aRv
);
194 already_AddRefed
<DOMMediaStream
> DOMMediaStream::Constructor(
195 const GlobalObject
& aGlobal
,
196 const Sequence
<OwningNonNull
<MediaStreamTrack
>>& aTracks
,
198 nsCOMPtr
<nsPIDOMWindowInner
> ownerWindow
=
199 do_QueryInterface(aGlobal
.GetAsSupports());
201 aRv
.Throw(NS_ERROR_FAILURE
);
205 auto newStream
= MakeRefPtr
<DOMMediaStream
>(ownerWindow
);
206 for (MediaStreamTrack
& track
: aTracks
) {
207 newStream
->AddTrack(track
);
209 return newStream
.forget();
212 already_AddRefed
<Promise
> DOMMediaStream::CountUnderlyingStreams(
213 const GlobalObject
& aGlobal
, ErrorResult
& aRv
) {
214 nsCOMPtr
<nsPIDOMWindowInner
> window
=
215 do_QueryInterface(aGlobal
.GetAsSupports());
217 aRv
.Throw(NS_ERROR_UNEXPECTED
);
221 nsCOMPtr
<nsIGlobalObject
> go
= do_QueryInterface(aGlobal
.GetAsSupports());
223 aRv
.Throw(NS_ERROR_UNEXPECTED
);
227 RefPtr
<Promise
> p
= Promise::Create(go
, aRv
);
232 MediaTrackGraph
* graph
= MediaTrackGraph::GetInstanceIfExists(
233 window
, MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE
,
234 MediaTrackGraph::DEFAULT_OUTPUT_DEVICE
);
240 auto* graphImpl
= static_cast<MediaTrackGraphImpl
*>(graph
);
242 class Counter
: public ControlMessage
{
244 Counter(MediaTrackGraphImpl
* aGraph
, const RefPtr
<Promise
>& aPromise
)
245 : ControlMessage(nullptr), mGraph(aGraph
), mPromise(aPromise
) {
246 MOZ_ASSERT(NS_IsMainThread());
249 void Run() override
{
250 TRACE("DOMMediaStream::Counter")
252 mGraph
->mTracks
.Length() + mGraph
->mSuspendedTracks
.Length();
253 mGraph
->DispatchToMainThreadStableState(NS_NewRunnableFunction(
254 "DOMMediaStream::CountUnderlyingStreams (stable state)",
255 [promise
= std::move(mPromise
), streams
]() mutable {
256 NS_DispatchToMainThread(NS_NewRunnableFunction(
257 "DOMMediaStream::CountUnderlyingStreams",
258 [promise
= std::move(promise
), streams
]() {
259 promise
->MaybeResolve(streams
);
264 // mPromise can only be AddRefed/Released on main thread.
265 // In case of shutdown, Run() does not run, so we dispatch mPromise to be
266 // released on main thread here.
267 void RunDuringShutdown() override
{
268 NS_ReleaseOnMainThread(
269 "DOMMediaStream::CountUnderlyingStreams::Counter::RunDuringShutdown",
274 // mGraph owns this Counter instance and decides its lifetime.
275 MediaTrackGraphImpl
* mGraph
;
276 RefPtr
<Promise
> mPromise
;
278 graphImpl
->AppendMessage(MakeUnique
<Counter
>(graphImpl
, p
));
283 void DOMMediaStream::GetId(nsAString
& aID
) const { aID
= mID
; }
285 void DOMMediaStream::GetAudioTracks(
286 nsTArray
<RefPtr
<AudioStreamTrack
>>& aTracks
) const {
287 for (const auto& track
: mTracks
) {
288 if (AudioStreamTrack
* t
= track
->AsAudioStreamTrack()) {
289 aTracks
.AppendElement(t
);
294 void DOMMediaStream::GetAudioTracks(
295 nsTArray
<RefPtr
<MediaStreamTrack
>>& aTracks
) const {
296 for (const auto& track
: mTracks
) {
297 if (track
->AsAudioStreamTrack()) {
298 aTracks
.AppendElement(track
);
303 void DOMMediaStream::GetVideoTracks(
304 nsTArray
<RefPtr
<VideoStreamTrack
>>& aTracks
) const {
305 for (const auto& track
: mTracks
) {
306 if (VideoStreamTrack
* t
= track
->AsVideoStreamTrack()) {
307 aTracks
.AppendElement(t
);
312 void DOMMediaStream::GetVideoTracks(
313 nsTArray
<RefPtr
<MediaStreamTrack
>>& aTracks
) const {
314 for (const auto& track
: mTracks
) {
315 if (track
->AsVideoStreamTrack()) {
316 aTracks
.AppendElement(track
);
321 void DOMMediaStream::GetTracks(
322 nsTArray
<RefPtr
<MediaStreamTrack
>>& aTracks
) const {
323 for (const auto& track
: mTracks
) {
324 aTracks
.AppendElement(track
);
328 void DOMMediaStream::AddTrack(MediaStreamTrack
& aTrack
) {
329 LOG(LogLevel::Info
, ("DOMMediaStream %p Adding track %p (from track %p)",
330 this, &aTrack
, aTrack
.GetTrack()));
332 if (HasTrack(aTrack
)) {
334 ("DOMMediaStream %p already contains track %p", this, &aTrack
));
338 mTracks
.AppendElement(&aTrack
);
340 if (!aTrack
.Ended()) {
341 NotifyTrackAdded(&aTrack
);
345 void DOMMediaStream::RemoveTrack(MediaStreamTrack
& aTrack
) {
346 if (static_cast<LogModule
*>(gMediaStreamLog
)->ShouldLog(LogLevel::Info
)) {
347 if (aTrack
.Ended()) {
349 ("DOMMediaStream %p Removing (ended) track %p", this, &aTrack
));
352 ("DOMMediaStream %p Removing track %p (from track %p)", this, &aTrack
,
357 if (!mTracks
.RemoveElement(&aTrack
)) {
359 ("DOMMediaStream %p does not contain track %p", this, &aTrack
));
363 if (!aTrack
.Ended()) {
364 NotifyTrackRemoved(&aTrack
);
368 already_AddRefed
<DOMMediaStream
> DOMMediaStream::Clone() {
369 auto newStream
= MakeRefPtr
<DOMMediaStream
>(GetOwnerWindow());
372 ("DOMMediaStream %p created clone %p", this, newStream
.get()));
374 for (const auto& track
: mTracks
) {
376 ("DOMMediaStream %p forwarding external track %p to clone %p", this,
377 track
.get(), newStream
.get()));
378 RefPtr
<MediaStreamTrack
> clone
= track
->Clone();
379 newStream
->AddTrack(*clone
);
382 return newStream
.forget();
385 bool DOMMediaStream::Active() const { return mActive
; }
386 bool DOMMediaStream::Audible() const { return mAudible
; }
388 MediaStreamTrack
* DOMMediaStream::GetTrackById(const nsAString
& aId
) const {
389 for (const auto& track
: mTracks
) {
399 bool DOMMediaStream::HasTrack(const MediaStreamTrack
& aTrack
) const {
400 return mTracks
.Contains(&aTrack
);
403 void DOMMediaStream::AddTrackInternal(MediaStreamTrack
* aTrack
) {
405 ("DOMMediaStream %p Adding owned track %p", this, aTrack
));
407 DispatchTrackEvent(u
"addtrack"_ns
, aTrack
);
410 void DOMMediaStream::RemoveTrackInternal(MediaStreamTrack
* aTrack
) {
412 ("DOMMediaStream %p Removing owned track %p", this, aTrack
));
413 if (!HasTrack(*aTrack
)) {
416 RemoveTrack(*aTrack
);
417 DispatchTrackEvent(u
"removetrack"_ns
, aTrack
);
420 already_AddRefed
<nsIPrincipal
> DOMMediaStream::GetPrincipal() {
421 nsGlobalWindowInner
* win
= GetOwnerWindow();
425 nsCOMPtr
<nsIPrincipal
> principal
= win
->GetPrincipal();
426 for (const auto& t
: mTracks
) {
430 nsContentUtils::CombineResourcePrincipals(&principal
, t
->GetPrincipal());
432 return principal
.forget();
435 void DOMMediaStream::NotifyActive() {
436 LOG(LogLevel::Info
, ("DOMMediaStream %p NotifyActive(). ", this));
439 for (int32_t i
= mTrackListeners
.Length() - 1; i
>= 0; --i
) {
440 mTrackListeners
[i
]->NotifyActive();
444 void DOMMediaStream::NotifyInactive() {
445 LOG(LogLevel::Info
, ("DOMMediaStream %p NotifyInactive(). ", this));
447 MOZ_ASSERT(!mActive
);
448 for (int32_t i
= mTrackListeners
.Length() - 1; i
>= 0; --i
) {
449 mTrackListeners
[i
]->NotifyInactive();
453 void DOMMediaStream::NotifyAudible() {
454 LOG(LogLevel::Info
, ("DOMMediaStream %p NotifyAudible(). ", this));
456 MOZ_ASSERT(mAudible
);
457 for (int32_t i
= mTrackListeners
.Length() - 1; i
>= 0; --i
) {
458 mTrackListeners
[i
]->NotifyAudible();
462 void DOMMediaStream::NotifyInaudible() {
463 LOG(LogLevel::Info
, ("DOMMediaStream %p NotifyInaudible(). ", this));
465 MOZ_ASSERT(!mAudible
);
466 for (int32_t i
= mTrackListeners
.Length() - 1; i
>= 0; --i
) {
467 mTrackListeners
[i
]->NotifyInaudible();
471 void DOMMediaStream::RegisterTrackListener(TrackListener
* aListener
) {
472 MOZ_ASSERT(NS_IsMainThread());
474 mTrackListeners
.AppendElement(aListener
);
477 void DOMMediaStream::UnregisterTrackListener(TrackListener
* aListener
) {
478 MOZ_ASSERT(NS_IsMainThread());
479 mTrackListeners
.RemoveElement(aListener
);
482 void DOMMediaStream::NotifyTrackAdded(const RefPtr
<MediaStreamTrack
>& aTrack
) {
483 MOZ_ASSERT(NS_IsMainThread());
485 aTrack
->AddConsumer(mPlaybackTrackListener
);
487 for (int32_t i
= mTrackListeners
.Length() - 1; i
>= 0; --i
) {
488 mTrackListeners
[i
]->NotifyTrackAdded(aTrack
);
492 // Check if we became active.
493 if (ContainsLiveTracks(mTracks
)) {
500 // Check if we became audible.
501 if (ContainsLiveAudioTracks(mTracks
)) {
508 void DOMMediaStream::NotifyTrackRemoved(
509 const RefPtr
<MediaStreamTrack
>& aTrack
) {
510 MOZ_ASSERT(NS_IsMainThread());
513 // aTrack may be null to allow HTMLMediaElement::MozCaptureStream streams
514 // to be played until the source media element has ended. The source media
515 // element will then call NotifyTrackRemoved(nullptr) to signal that we can
516 // go inactive, regardless of the timing of the last track ending.
518 aTrack
->RemoveConsumer(mPlaybackTrackListener
);
520 for (int32_t i
= mTrackListeners
.Length() - 1; i
>= 0; --i
) {
521 mTrackListeners
[i
]->NotifyTrackRemoved(aTrack
);
525 NS_ASSERTION(false, "Shouldn't remove a live track if already inactive");
531 // Check if we became inaudible.
532 if (!ContainsLiveAudioTracks(mTracks
)) {
538 // Check if we became inactive.
539 if (!ContainsLiveTracks(mTracks
)) {
545 nsresult
DOMMediaStream::DispatchTrackEvent(
546 const nsAString
& aName
, const RefPtr
<MediaStreamTrack
>& aTrack
) {
547 MediaStreamTrackEventInit init
;
548 init
.mTrack
= aTrack
;
550 RefPtr
<MediaStreamTrackEvent
> event
=
551 MediaStreamTrackEvent::Constructor(this, aName
, init
);
553 return DispatchTrustedEvent(event
);