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/. */
5
6 #include "DOMMediaStream.h"
7
8 #include "AudioCaptureStream.h"
9 #include "AudioChannelAgent.h"
10 #include "AudioStreamTrack.h"
11 #include "Layers.h"
12 #include "MediaStreamGraph.h"
13 #include "MediaStreamGraphImpl.h"
14 #include "MediaStreamListener.h"
15 #include "VideoStreamTrack.h"
16 #include "mozilla/dom/AudioNode.h"
17 #include "mozilla/dom/AudioTrack.h"
18 #include "mozilla/dom/AudioTrackList.h"
19 #include "mozilla/dom/DocGroup.h"
20 #include "mozilla/dom/HTMLCanvasElement.h"
21 #include "mozilla/dom/LocalMediaStreamBinding.h"
22 #include "mozilla/dom/MediaStreamBinding.h"
23 #include "mozilla/dom/MediaStreamTrackEvent.h"
24 #include "mozilla/dom/Promise.h"
25 #include "mozilla/dom/VideoTrack.h"
26 #include "mozilla/dom/VideoTrackList.h"
27 #include "mozilla/media/MediaUtils.h"
28 #include "nsContentUtils.h"
29 #include "nsIScriptError.h"
30 #include "nsIUUIDGenerator.h"
31 #include "nsPIDOMWindow.h"
32 #include "nsProxyRelease.h"
33 #include "nsRFPService.h"
34 #include "nsServiceManagerUtils.h"
35
36 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
37 // GetTickCount() and conflicts with NS_DECL_NSIDOMMEDIASTREAM, containing
38 // currentTime getter.
39 #ifdef GetCurrentTime
40 #undef GetCurrentTime
41 #endif
42
43 #ifdef LOG
44 #undef LOG
45 #endif
46
47 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
48 // GetTickCount() and conflicts with MediaStream::GetCurrentTime.
49 #ifdef GetCurrentTime
50 #undef GetCurrentTime
51 #endif
52
53 using namespace mozilla;
54 using namespace mozilla::dom;
55 using namespace mozilla::layers;
56 using namespace mozilla::media;
57
58 static LazyLogModule gMediaStreamLog("MediaStream");
59 #define LOG(type, msg) MOZ_LOG(gMediaStreamLog, type, msg)
60
61 const TrackID TRACK_VIDEO_PRIMARY = 1;
62
ContainsLiveTracks(nsTArray<RefPtr<DOMMediaStream::TrackPort>> & aTracks)63 static bool ContainsLiveTracks(
64 nsTArray<RefPtr<DOMMediaStream::TrackPort>>& aTracks) {
65 for (auto& port : aTracks) {
66 if (port->GetTrack()->ReadyState() == MediaStreamTrackState::Live) {
67 return true;
68 }
69 }
70
71 return false;
72 }
73
TrackPort(MediaInputPort * aInputPort,MediaStreamTrack * aTrack,const InputPortOwnership aOwnership)74 DOMMediaStream::TrackPort::TrackPort(MediaInputPort* aInputPort,
75 MediaStreamTrack* aTrack,
76 const InputPortOwnership aOwnership)
77 : mInputPort(aInputPort), mTrack(aTrack), mOwnership(aOwnership) {
78 MOZ_ASSERT(mInputPort);
79 MOZ_ASSERT(mTrack);
80
81 MOZ_COUNT_CTOR(TrackPort);
82 }
83
~TrackPort()84 DOMMediaStream::TrackPort::~TrackPort() {
85 MOZ_COUNT_DTOR(TrackPort);
86
87 if (mOwnership == InputPortOwnership::OWNED) {
88 DestroyInputPort();
89 }
90 }
91
DestroyInputPort()92 void DOMMediaStream::TrackPort::DestroyInputPort() {
93 if (mInputPort) {
94 mInputPort->Destroy();
95 mInputPort = nullptr;
96 }
97 }
98
GetSource() const99 MediaStream* DOMMediaStream::TrackPort::GetSource() const {
100 return mInputPort ? mInputPort->GetSource() : nullptr;
101 }
102
GetSourceTrackId() const103 TrackID DOMMediaStream::TrackPort::GetSourceTrackId() const {
104 return mInputPort ? mInputPort->GetSourceTrackId() : TRACK_INVALID;
105 }
106
BlockSourceTrackId(TrackID aTrackId,BlockingMode aBlockingMode)107 already_AddRefed<Pledge<bool>> DOMMediaStream::TrackPort::BlockSourceTrackId(
108 TrackID aTrackId, BlockingMode aBlockingMode) {
109 if (mInputPort) {
110 return mInputPort->BlockSourceTrackId(aTrackId, aBlockingMode);
111 }
112 auto rejected = MakeRefPtr<Pledge<bool>>();
113 rejected->Reject(NS_ERROR_FAILURE);
114 return rejected.forget();
115 }
116
117 NS_IMPL_CYCLE_COLLECTION(DOMMediaStream::TrackPort, mTrack)
118 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMMediaStream::TrackPort, AddRef)
119 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMMediaStream::TrackPort, Release)
120
121 NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaStreamTrackSourceGetter)
122 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaStreamTrackSourceGetter)
123 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamTrackSourceGetter)
124 NS_INTERFACE_MAP_ENTRY(nsISupports)
125 NS_INTERFACE_MAP_END
126 NS_IMPL_CYCLE_COLLECTION_0(MediaStreamTrackSourceGetter)
127
128 /**
129 * Listener registered on the Owned stream to detect added and ended owned
130 * tracks for keeping the list of MediaStreamTracks in sync with the tracks
131 * added and ended directly at the source.
132 */
133 class DOMMediaStream::OwnedStreamListener : public MediaStreamListener {
134 public:
OwnedStreamListener(DOMMediaStream * aStream)135 explicit OwnedStreamListener(DOMMediaStream* aStream) : mStream(aStream) {}
136
Forget()137 void Forget() { mStream = nullptr; }
138
DoNotifyTrackCreated(MediaStreamGraph * aGraph,TrackID aTrackID,MediaSegment::Type aType,MediaStream * aInputStream,TrackID aInputTrackID)139 void DoNotifyTrackCreated(MediaStreamGraph* aGraph, TrackID aTrackID,
140 MediaSegment::Type aType, MediaStream* aInputStream,
141 TrackID aInputTrackID) {
142 MOZ_ASSERT(NS_IsMainThread());
143
144 if (!mStream) {
145 return;
146 }
147
148 MediaStreamTrack* track =
149 mStream->FindOwnedDOMTrack(aInputStream, aInputTrackID, aTrackID);
150
151 if (track) {
152 LOG(LogLevel::Debug, ("DOMMediaStream %p Track %d from owned stream %p "
153 "bound to MediaStreamTrack %p.",
154 mStream, aTrackID, aInputStream, track));
155 return;
156 }
157
158 // Track had not been created on main thread before, create it now.
159 NS_WARNING_ASSERTION(
160 !mStream->mTracks.IsEmpty(),
161 "A new track was detected on the input stream; creating a "
162 "corresponding "
163 "MediaStreamTrack. Initial tracks should be added manually to "
164 "immediately and synchronously be available to JS.");
165 RefPtr<MediaStreamTrackSource> source;
166 if (mStream->mTrackSourceGetter) {
167 source = mStream->mTrackSourceGetter->GetMediaStreamTrackSource(aTrackID);
168 }
169 if (!source) {
170 NS_ASSERTION(false,
171 "Dynamic track created without an explicit TrackSource");
172 nsPIDOMWindowInner* window = mStream->GetParentObject();
173 nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
174 nsIPrincipal* principal = doc ? doc->NodePrincipal() : nullptr;
175 source = new BasicTrackSource(principal);
176 }
177
178 RefPtr<MediaStreamTrack> newTrack =
179 mStream->CreateDOMTrack(aTrackID, aType, source);
180 aGraph->AbstractMainThread()->Dispatch(
181 NewRunnableMethod<RefPtr<MediaStreamTrack>>(
182 "DOMMediaStream::AddTrackInternal", mStream,
183 &DOMMediaStream::AddTrackInternal, newTrack));
184 }
185
DoNotifyTrackEnded(MediaStreamGraph * aGraph,MediaStream * aInputStream,TrackID aInputTrackID,TrackID aTrackID)186 void DoNotifyTrackEnded(MediaStreamGraph* aGraph, MediaStream* aInputStream,
187 TrackID aInputTrackID, TrackID aTrackID) {
188 MOZ_ASSERT(NS_IsMainThread());
189
190 if (!mStream) {
191 return;
192 }
193
194 RefPtr<MediaStreamTrack> track =
195 mStream->FindOwnedDOMTrack(aInputStream, aInputTrackID, aTrackID);
196 NS_ASSERTION(track,
197 "Owned MediaStreamTracks must be known by the DOMMediaStream");
198 if (track) {
199 LOG(LogLevel::Debug, ("DOMMediaStream %p MediaStreamTrack %p ended at "
200 "the source. Marking it ended.",
201 mStream, track.get()));
202 aGraph->AbstractMainThread()->Dispatch(
203 NewRunnableMethod("dom::MediaStreamTrack::OverrideEnded", track,
204 &MediaStreamTrack::OverrideEnded));
205 }
206 }
207
NotifyQueuedTrackChanges(MediaStreamGraph * aGraph,TrackID aID,StreamTime aTrackOffset,TrackEventCommand aTrackEvents,const MediaSegment & aQueuedMedia,MediaStream * aInputStream,TrackID aInputTrackID)208 void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
209 StreamTime aTrackOffset,
210 TrackEventCommand aTrackEvents,
211 const MediaSegment& aQueuedMedia,
212 MediaStream* aInputStream,
213 TrackID aInputTrackID) override {
214 if (aTrackEvents & TrackEventCommand::TRACK_EVENT_CREATED) {
215 aGraph->DispatchToMainThreadAfterStreamStateUpdate(
216 NewRunnableMethod<MediaStreamGraph*, TrackID, MediaSegment::Type,
217 RefPtr<MediaStream>, TrackID>(
218 "DOMMediaStream::OwnedStreamListener::DoNotifyTrackCreated", this,
219 &OwnedStreamListener::DoNotifyTrackCreated, aGraph, aID,
220 aQueuedMedia.GetType(), aInputStream, aInputTrackID));
221 } else if (aTrackEvents & TrackEventCommand::TRACK_EVENT_ENDED) {
222 aGraph->DispatchToMainThreadAfterStreamStateUpdate(
223 NewRunnableMethod<MediaStreamGraph*, RefPtr<MediaStream>, TrackID,
224 TrackID>(
225 "DOMMediaStream::OwnedStreamListener::DoNotifyTrackEnded", this,
226 &OwnedStreamListener::DoNotifyTrackEnded, aGraph, aInputStream,
227 aInputTrackID, aID));
228 }
229 }
230
231 private:
232 // These fields may only be accessed on the main thread
233 DOMMediaStream* mStream;
234 };
235
236 /**
237 * Listener registered on the Playback stream to detect when tracks end and when
238 * all new tracks this iteration have been created - for when several tracks are
239 * queued by the source and committed all at once.
240 */
241 class DOMMediaStream::PlaybackStreamListener : public MediaStreamListener {
242 public:
PlaybackStreamListener(DOMMediaStream * aStream)243 explicit PlaybackStreamListener(DOMMediaStream* aStream) : mStream(aStream) {}
244
Forget()245 void Forget() {
246 MOZ_ASSERT(NS_IsMainThread());
247 mStream = nullptr;
248 }
249
DoNotifyFinishedTrackCreation()250 void DoNotifyFinishedTrackCreation() {
251 MOZ_ASSERT(NS_IsMainThread());
252
253 if (!mStream) {
254 return;
255 }
256
257 // The owned stream listener adds its tracks after another main thread
258 // dispatch. We have to do the same to notify of created tracks to stay
259 // in sync. (Or NotifyTracksCreated is called before tracks are added).
260 MOZ_ASSERT(mStream->GetPlaybackStream());
261 mStream->GetPlaybackStream()->Graph()->AbstractMainThread()->Dispatch(
262 NewRunnableMethod("DOMMediaStream::NotifyTracksCreated", mStream,
263 &DOMMediaStream::NotifyTracksCreated));
264 }
265
DoNotifyFinished()266 void DoNotifyFinished() {
267 MOZ_ASSERT(NS_IsMainThread());
268
269 if (!mStream) {
270 return;
271 }
272
273 mStream->GetPlaybackStream()->Graph()->AbstractMainThread()->Dispatch(
274 NewRunnableMethod("DOMMediaStream::NotifyFinished", mStream,
275 &DOMMediaStream::NotifyFinished));
276 }
277
278 // The methods below are called on the MediaStreamGraph thread.
279
NotifyFinishedTrackCreation(MediaStreamGraph * aGraph)280 void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) override {
281 aGraph->DispatchToMainThreadAfterStreamStateUpdate(NewRunnableMethod(
282 "DOMMediaStream::PlaybackStreamListener::DoNotifyFinishedTrackCreation",
283 this, &PlaybackStreamListener::DoNotifyFinishedTrackCreation));
284 }
285
NotifyEvent(MediaStreamGraph * aGraph,MediaStreamGraphEvent event)286 void NotifyEvent(MediaStreamGraph* aGraph,
287 MediaStreamGraphEvent event) override {
288 if (event == MediaStreamGraphEvent::EVENT_FINISHED) {
289 aGraph->DispatchToMainThreadAfterStreamStateUpdate(NewRunnableMethod(
290 "DOMMediaStream::PlaybackStreamListener::DoNotifyFinished", this,
291 &PlaybackStreamListener::DoNotifyFinished));
292 }
293 }
294
295 private:
296 // These fields may only be accessed on the main thread
297 DOMMediaStream* mStream;
298 };
299
300 class DOMMediaStream::PlaybackTrackListener : public MediaStreamTrackConsumer {
301 public:
PlaybackTrackListener(DOMMediaStream * aStream)302 explicit PlaybackTrackListener(DOMMediaStream* aStream) : mStream(aStream) {}
303
304 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PlaybackTrackListener)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(PlaybackTrackListener)305 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(PlaybackTrackListener)
306
307 void NotifyEnded(MediaStreamTrack* aTrack) override {
308 if (!mStream) {
309 MOZ_ASSERT(false);
310 return;
311 }
312
313 if (!aTrack) {
314 MOZ_ASSERT(false);
315 return;
316 }
317
318 MOZ_ASSERT(mStream->HasTrack(*aTrack));
319 mStream->NotifyTrackRemoved(aTrack);
320 }
321
322 protected:
~PlaybackTrackListener()323 virtual ~PlaybackTrackListener() {}
324
325 RefPtr<DOMMediaStream> mStream;
326 };
327
328 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMMediaStream::PlaybackTrackListener,
329 AddRef)
330 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMMediaStream::PlaybackTrackListener,
331 Release)
332 NS_IMPL_CYCLE_COLLECTION(DOMMediaStream::PlaybackTrackListener, mStream)
333
334 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMMediaStream)
335
336 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMMediaStream,
337 DOMEventTargetHelper)
338 tmp->Destroy();
339 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwnedTracks)340 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwnedTracks)
341 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks)
342 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsumersToKeepAlive)
343 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTrackSourceGetter)
344 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlaybackTrackListener)
345 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal)
346 NS_IMPL_CYCLE_COLLECTION_UNLINK(mVideoPrincipal)
347 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
348
349 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMMediaStream,
350 DOMEventTargetHelper)
351 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
352 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwnedTracks)
353 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTracks)
354 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsumersToKeepAlive)
355 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTrackSourceGetter)
356 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlaybackTrackListener)
357 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
358 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVideoPrincipal)
359 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
360
361 NS_IMPL_ADDREF_INHERITED(DOMMediaStream, DOMEventTargetHelper)
362 NS_IMPL_RELEASE_INHERITED(DOMMediaStream, DOMEventTargetHelper)
363
364 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMMediaStream)
365 NS_INTERFACE_MAP_ENTRY(DOMMediaStream)
366 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
367
368 NS_IMPL_ADDREF_INHERITED(DOMLocalMediaStream, DOMMediaStream)
369 NS_IMPL_RELEASE_INHERITED(DOMLocalMediaStream, DOMMediaStream)
370
371 NS_INTERFACE_MAP_BEGIN(DOMLocalMediaStream)
372 NS_INTERFACE_MAP_ENTRY(DOMLocalMediaStream)
373 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
374
375 NS_IMPL_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream,
376 mStreamNode)
377
378 NS_IMPL_ADDREF_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream)
379 NS_IMPL_RELEASE_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream)
380
381 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMAudioNodeMediaStream)
382 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
383
384 DOMMediaStream::DOMMediaStream(nsPIDOMWindowInner* aWindow,
385 MediaStreamTrackSourceGetter* aTrackSourceGetter)
386 : mLogicalStreamStartTime(0),
387 mWindow(aWindow),
388 mInputStream(nullptr),
389 mOwnedStream(nullptr),
390 mPlaybackStream(nullptr),
391 mTracksPendingRemoval(0),
392 mTrackSourceGetter(aTrackSourceGetter),
393 mPlaybackTrackListener(MakeAndAddRef<PlaybackTrackListener>(this)),
394 mTracksCreated(false),
395 mNotifiedOfMediaStreamGraphShutdown(false),
396 mActive(false),
397 mSetInactiveOnFinish(false) {
398 nsresult rv;
399 nsCOMPtr<nsIUUIDGenerator> uuidgen =
400 do_GetService("@mozilla.org/uuid-generator;1", &rv);
401
402 if (NS_SUCCEEDED(rv) && uuidgen) {
403 nsID uuid;
404 memset(&uuid, 0, sizeof(uuid));
405 rv = uuidgen->GenerateUUIDInPlace(&uuid);
406 if (NS_SUCCEEDED(rv)) {
407 char buffer[NSID_LENGTH];
408 uuid.ToProvidedString(buffer);
409 mID = NS_ConvertASCIItoUTF16(buffer);
410 }
411 }
412 }
413
~DOMMediaStream()414 DOMMediaStream::~DOMMediaStream() { Destroy(); }
415
Destroy()416 void DOMMediaStream::Destroy() {
417 LOG(LogLevel::Debug, ("DOMMediaStream %p Being destroyed.", this));
418 if (mOwnedListener) {
419 mOwnedListener->Forget();
420 mOwnedListener = nullptr;
421 }
422 if (mPlaybackListener) {
423 mPlaybackListener->Forget();
424 mPlaybackListener = nullptr;
425 }
426 for (const RefPtr<TrackPort>& info : mTracks) {
427 // We must remove ourselves from each track's principal change observer list
428 // before we die. CC may have cleared info->mTrack so guard against it.
429 MediaStreamTrack* track = info->GetTrack();
430 if (track) {
431 track->RemovePrincipalChangeObserver(this);
432 if (!track->Ended()) {
433 track->RemoveConsumer(mPlaybackTrackListener);
434 }
435 }
436 }
437 if (mPlaybackPort) {
438 mPlaybackPort->Destroy();
439 mPlaybackPort = nullptr;
440 }
441 if (mOwnedPort) {
442 mOwnedPort->Destroy();
443 mOwnedPort = nullptr;
444 }
445 if (mPlaybackStream) {
446 mPlaybackStream->UnregisterUser();
447 mPlaybackStream = nullptr;
448 }
449 if (mOwnedStream) {
450 mOwnedStream->UnregisterUser();
451 mOwnedStream = nullptr;
452 }
453 if (mInputStream) {
454 mInputStream->UnregisterUser();
455 mInputStream = nullptr;
456 }
457 mRunOnTracksAvailable.Clear();
458 mTrackListeners.Clear();
459 }
460
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)461 JSObject* DOMMediaStream::WrapObject(JSContext* aCx,
462 JS::Handle<JSObject*> aGivenProto) {
463 return dom::MediaStreamBinding::Wrap(aCx, this, aGivenProto);
464 }
465
Constructor(const GlobalObject & aGlobal,ErrorResult & aRv)466 /* static */ already_AddRefed<DOMMediaStream> DOMMediaStream::Constructor(
467 const GlobalObject& aGlobal, ErrorResult& aRv) {
468 Sequence<OwningNonNull<MediaStreamTrack>> emptyTrackSeq;
469 return Constructor(aGlobal, emptyTrackSeq, aRv);
470 }
471
Constructor(const GlobalObject & aGlobal,const DOMMediaStream & aStream,ErrorResult & aRv)472 /* static */ already_AddRefed<DOMMediaStream> DOMMediaStream::Constructor(
473 const GlobalObject& aGlobal, const DOMMediaStream& aStream,
474 ErrorResult& aRv) {
475 nsTArray<RefPtr<MediaStreamTrack>> tracks;
476 aStream.GetTracks(tracks);
477
478 Sequence<OwningNonNull<MediaStreamTrack>> nonNullTrackSeq;
479 if (!nonNullTrackSeq.SetLength(tracks.Length(), fallible)) {
480 MOZ_ASSERT(false);
481 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
482 return nullptr;
483 }
484
485 for (size_t i = 0; i < tracks.Length(); ++i) {
486 nonNullTrackSeq[i] = tracks[i];
487 }
488
489 return Constructor(aGlobal, nonNullTrackSeq, aRv);
490 }
491
Constructor(const GlobalObject & aGlobal,const Sequence<OwningNonNull<MediaStreamTrack>> & aTracks,ErrorResult & aRv)492 /* static */ already_AddRefed<DOMMediaStream> DOMMediaStream::Constructor(
493 const GlobalObject& aGlobal,
494 const Sequence<OwningNonNull<MediaStreamTrack>>& aTracks,
495 ErrorResult& aRv) {
496 nsCOMPtr<nsPIDOMWindowInner> ownerWindow =
497 do_QueryInterface(aGlobal.GetAsSupports());
498 if (!ownerWindow) {
499 aRv.Throw(NS_ERROR_FAILURE);
500 return nullptr;
501 }
502
503 // Streams created from JS cannot have dynamically created tracks.
504 MediaStreamTrackSourceGetter* getter = nullptr;
505 RefPtr<DOMMediaStream> newStream = new DOMMediaStream(ownerWindow, getter);
506
507 for (MediaStreamTrack& track : aTracks) {
508 if (!newStream->GetPlaybackStream()) {
509 MOZ_RELEASE_ASSERT(track.Graph());
510 newStream->InitPlaybackStreamCommon(track.Graph());
511 }
512 newStream->AddTrack(track);
513 }
514
515 if (!newStream->GetPlaybackStream()) {
516 MOZ_ASSERT(aTracks.IsEmpty());
517 MediaStreamGraph* graph = MediaStreamGraph::GetInstance(
518 MediaStreamGraph::SYSTEM_THREAD_DRIVER, ownerWindow);
519 newStream->InitPlaybackStreamCommon(graph);
520 }
521
522 return newStream.forget();
523 }
524
CurrentTime()525 double DOMMediaStream::CurrentTime() {
526 if (!mPlaybackStream) {
527 return 0.0;
528 }
529 // The value of a MediaStream's CurrentTime will always advance forward; it
530 // will never reset (even if one rewinds a video.) Therefore we can use a
531 // single Random Seed initialized at the same time as the object.
532 return nsRFPService::ReduceTimePrecisionAsSecs(
533 mPlaybackStream->StreamTimeToSeconds(mPlaybackStream->GetCurrentTime() -
534 mLogicalStreamStartTime),
535 GetRandomTimelineSeed());
536 }
537
CountUnderlyingStreams(const GlobalObject & aGlobal,ErrorResult & aRv)538 already_AddRefed<Promise> DOMMediaStream::CountUnderlyingStreams(
539 const GlobalObject& aGlobal, ErrorResult& aRv) {
540 nsCOMPtr<nsPIDOMWindowInner> window =
541 do_QueryInterface(aGlobal.GetAsSupports());
542 if (!window) {
543 aRv.Throw(NS_ERROR_UNEXPECTED);
544 return nullptr;
545 }
546
547 nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(aGlobal.GetAsSupports());
548 if (!go) {
549 aRv.Throw(NS_ERROR_UNEXPECTED);
550 return nullptr;
551 }
552
553 RefPtr<Promise> p = Promise::Create(go, aRv);
554 if (aRv.Failed()) {
555 return nullptr;
556 }
557
558 MediaStreamGraph* graph = MediaStreamGraph::GetInstanceIfExists(window);
559 if (!graph) {
560 p->MaybeResolve(0);
561 return p.forget();
562 }
563
564 auto* graphImpl = static_cast<MediaStreamGraphImpl*>(graph);
565
566 class Counter : public ControlMessage {
567 public:
568 Counter(MediaStreamGraphImpl* aGraph, const RefPtr<Promise>& aPromise)
569 : ControlMessage(nullptr),
570 mGraph(aGraph),
571 mPromise(MakeAndAddRef<nsMainThreadPtrHolder<Promise>>(
572 "DOMMediaStream::Counter::mPromise", aPromise)) {
573 MOZ_ASSERT(NS_IsMainThread());
574 }
575
576 void Run() override {
577 nsMainThreadPtrHandle<Promise>& promise = mPromise;
578 uint32_t streams =
579 mGraph->mStreams.Length() + mGraph->mSuspendedStreams.Length();
580 mGraph->DispatchToMainThreadAfterStreamStateUpdate(
581 NewRunnableFrom([promise, streams]() mutable {
582 promise->MaybeResolve(streams);
583 return NS_OK;
584 }));
585 }
586
587 private:
588 // mGraph owns this Counter instance and decides its lifetime.
589 MediaStreamGraphImpl* mGraph;
590 nsMainThreadPtrHandle<Promise> mPromise;
591 };
592 graphImpl->AppendMessage(MakeUnique<Counter>(graphImpl, p));
593
594 return p.forget();
595 }
596
GetId(nsAString & aID) const597 void DOMMediaStream::GetId(nsAString& aID) const { aID = mID; }
598
GetAudioTracks(nsTArray<RefPtr<AudioStreamTrack>> & aTracks) const599 void DOMMediaStream::GetAudioTracks(
600 nsTArray<RefPtr<AudioStreamTrack>>& aTracks) const {
601 for (const RefPtr<TrackPort>& info : mTracks) {
602 AudioStreamTrack* t = info->GetTrack()->AsAudioStreamTrack();
603 if (t) {
604 aTracks.AppendElement(t);
605 }
606 }
607 }
608
GetVideoTracks(nsTArray<RefPtr<VideoStreamTrack>> & aTracks) const609 void DOMMediaStream::GetVideoTracks(
610 nsTArray<RefPtr<VideoStreamTrack>>& aTracks) const {
611 for (const RefPtr<TrackPort>& info : mTracks) {
612 VideoStreamTrack* t = info->GetTrack()->AsVideoStreamTrack();
613 if (t) {
614 aTracks.AppendElement(t);
615 }
616 }
617 }
618
GetTracks(nsTArray<RefPtr<MediaStreamTrack>> & aTracks) const619 void DOMMediaStream::GetTracks(
620 nsTArray<RefPtr<MediaStreamTrack>>& aTracks) const {
621 for (const RefPtr<TrackPort>& info : mTracks) {
622 aTracks.AppendElement(info->GetTrack());
623 }
624 }
625
AddTrack(MediaStreamTrack & aTrack)626 void DOMMediaStream::AddTrack(MediaStreamTrack& aTrack) {
627 MOZ_RELEASE_ASSERT(mPlaybackStream);
628
629 RefPtr<ProcessedMediaStream> dest = mPlaybackStream->AsProcessedStream();
630 MOZ_ASSERT(dest);
631 if (!dest) {
632 return;
633 }
634
635 LOG(LogLevel::Info,
636 ("DOMMediaStream %p Adding track %p (from stream %p with ID %d)", this,
637 &aTrack, aTrack.mOwningStream.get(), aTrack.mTrackID));
638
639 if (mPlaybackStream->Graph() != aTrack.Graph()) {
640 NS_ASSERTION(false,
641 "Cannot combine tracks from different MediaStreamGraphs");
642 LOG(LogLevel::Error, ("DOMMediaStream %p Own MSG %p != aTrack's MSG %p",
643 this, mPlaybackStream->Graph(), aTrack.Graph()));
644
645 nsAutoString trackId;
646 aTrack.GetId(trackId);
647 const char16_t* params[] = {trackId.get()};
648 nsCOMPtr<nsPIDOMWindowInner> pWindow = GetParentObject();
649 nsIDocument* document = pWindow ? pWindow->GetExtantDoc() : nullptr;
650 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
651 NS_LITERAL_CSTRING("Media"), document,
652 nsContentUtils::eDOM_PROPERTIES,
653 "MediaStreamAddTrackDifferentAudioChannel",
654 params, ArrayLength(params));
655 return;
656 }
657
658 if (HasTrack(aTrack)) {
659 LOG(LogLevel::Debug,
660 ("DOMMediaStream %p already contains track %p", this, &aTrack));
661 return;
662 }
663
664 // Hook up the underlying track with our underlying playback stream.
665 RefPtr<MediaInputPort> inputPort = GetPlaybackStream()->AllocateInputPort(
666 aTrack.GetOwnedStream(), aTrack.mTrackID);
667 RefPtr<TrackPort> trackPort =
668 new TrackPort(inputPort, &aTrack, TrackPort::InputPortOwnership::OWNED);
669 mTracks.AppendElement(trackPort.forget());
670 NotifyTrackAdded(&aTrack);
671
672 LOG(LogLevel::Debug, ("DOMMediaStream %p Added track %p", this, &aTrack));
673 }
674
RemoveTrack(MediaStreamTrack & aTrack)675 void DOMMediaStream::RemoveTrack(MediaStreamTrack& aTrack) {
676 LOG(LogLevel::Info,
677 ("DOMMediaStream %p Removing track %p (from stream %p with ID %d)", this,
678 &aTrack, aTrack.mOwningStream.get(), aTrack.mTrackID));
679
680 RefPtr<TrackPort> toRemove = FindPlaybackTrackPort(aTrack);
681 if (!toRemove) {
682 LOG(LogLevel::Debug,
683 ("DOMMediaStream %p does not contain track %p", this, &aTrack));
684 return;
685 }
686
687 DebugOnly<bool> removed = mTracks.RemoveElement(toRemove);
688 NS_ASSERTION(removed,
689 "If there's a track port we should be able to remove it");
690
691 // If the track comes from a TRACK_ANY input port (i.e., mOwnedPort), we need
692 // to block it in the port. Doing this for a locked track is still OK as it
693 // will first block the track, then destroy the port. Both cause the track to
694 // end.
695 // If the track has already ended, it's input port might be gone, so in those
696 // cases blocking the underlying track should be avoided.
697 if (!aTrack.Ended()) {
698 BlockPlaybackTrack(toRemove);
699 NotifyTrackRemoved(&aTrack);
700 }
701
702 LOG(LogLevel::Debug, ("DOMMediaStream %p Removed track %p", this, &aTrack));
703 }
704
705 class ClonedStreamSourceGetter : public MediaStreamTrackSourceGetter {
706 public:
707 NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ClonedStreamSourceGetter,MediaStreamTrackSourceGetter)708 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ClonedStreamSourceGetter,
709 MediaStreamTrackSourceGetter)
710
711 explicit ClonedStreamSourceGetter(DOMMediaStream* aStream)
712 : mStream(aStream) {}
713
GetMediaStreamTrackSource(TrackID aInputTrackID)714 already_AddRefed<MediaStreamTrackSource> GetMediaStreamTrackSource(
715 TrackID aInputTrackID) override {
716 MediaStreamTrack* sourceTrack =
717 mStream->FindOwnedDOMTrack(mStream->GetOwnedStream(), aInputTrackID);
718 MOZ_RELEASE_ASSERT(sourceTrack);
719
720 return do_AddRef(&sourceTrack->GetSource());
721 }
722
723 protected:
~ClonedStreamSourceGetter()724 virtual ~ClonedStreamSourceGetter() {}
725
726 RefPtr<DOMMediaStream> mStream;
727 };
728
NS_IMPL_ADDREF_INHERITED(ClonedStreamSourceGetter,MediaStreamTrackSourceGetter)729 NS_IMPL_ADDREF_INHERITED(ClonedStreamSourceGetter, MediaStreamTrackSourceGetter)
730 NS_IMPL_RELEASE_INHERITED(ClonedStreamSourceGetter,
731 MediaStreamTrackSourceGetter)
732 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ClonedStreamSourceGetter)
733 NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSourceGetter)
734 NS_IMPL_CYCLE_COLLECTION_INHERITED(ClonedStreamSourceGetter,
735 MediaStreamTrackSourceGetter, mStream)
736
737 already_AddRefed<DOMMediaStream> DOMMediaStream::Clone() {
738 return CloneInternal(TrackForwardingOption::CURRENT);
739 }
740
CloneInternal(TrackForwardingOption aForwarding)741 already_AddRefed<DOMMediaStream> DOMMediaStream::CloneInternal(
742 TrackForwardingOption aForwarding) {
743 RefPtr<DOMMediaStream> newStream =
744 new DOMMediaStream(GetParentObject(), new ClonedStreamSourceGetter(this));
745
746 LOG(LogLevel::Info,
747 ("DOMMediaStream %p created clone %p, forwarding %s tracks", this,
748 newStream.get(),
749 aForwarding == TrackForwardingOption::ALL ? "all" : "current"));
750
751 MOZ_RELEASE_ASSERT(mPlaybackStream);
752 MOZ_RELEASE_ASSERT(mPlaybackStream->Graph());
753 MediaStreamGraph* graph = mPlaybackStream->Graph();
754
755 // We initiate the owned and playback streams first, since we need to create
756 // all existing DOM tracks before we add the generic input port from
757 // mInputStream to mOwnedStream (see AllocateInputPort wrt. destination
758 // TrackID as to why).
759 newStream->InitOwnedStreamCommon(graph);
760 newStream->InitPlaybackStreamCommon(graph);
761
762 // Set up existing DOM tracks.
763 TrackID allocatedTrackID = 1;
764 for (const RefPtr<TrackPort>& info : mTracks) {
765 MediaStreamTrack& track = *info->GetTrack();
766
767 LOG(LogLevel::Debug,
768 ("DOMMediaStream %p forwarding external track %p to clone %p", this,
769 &track, newStream.get()));
770 RefPtr<MediaStreamTrack> trackClone =
771 newStream->CloneDOMTrack(track, allocatedTrackID++);
772 }
773
774 if (aForwarding == TrackForwardingOption::ALL) {
775 // Set up an input port from our input stream to the new DOM stream's owned
776 // stream, to allow for dynamically added tracks at the source to appear in
777 // the clone. The clone may treat mInputStream as its own mInputStream but
778 // ownership remains with us.
779 newStream->mInputStream = mInputStream;
780 if (mInputStream) {
781 // We have already set up track-locked input ports for all existing DOM
782 // tracks, so now we need to block those in the generic input port to
783 // avoid ending up with double instances of them.
784 nsTArray<TrackID> tracksToBlock;
785 for (const RefPtr<TrackPort>& info : mOwnedTracks) {
786 tracksToBlock.AppendElement(info->GetTrack()->mTrackID);
787 }
788
789 newStream->mInputStream->RegisterUser();
790 newStream->mOwnedPort = newStream->mOwnedStream->AllocateInputPort(
791 mInputStream, TRACK_ANY, TRACK_ANY, 0, 0, &tracksToBlock);
792 }
793 }
794
795 return newStream.forget();
796 }
797
Active() const798 bool DOMMediaStream::Active() const { return mActive; }
799
GetTrackById(const nsAString & aId) const800 MediaStreamTrack* DOMMediaStream::GetTrackById(const nsAString& aId) const {
801 for (const RefPtr<TrackPort>& info : mTracks) {
802 nsString id;
803 info->GetTrack()->GetId(id);
804 if (id == aId) {
805 return info->GetTrack();
806 }
807 }
808 return nullptr;
809 }
810
GetOwnedTrackById(const nsAString & aId)811 MediaStreamTrack* DOMMediaStream::GetOwnedTrackById(const nsAString& aId) {
812 for (const RefPtr<TrackPort>& info : mOwnedTracks) {
813 nsString id;
814 info->GetTrack()->GetId(id);
815 if (id == aId) {
816 return info->GetTrack();
817 }
818 }
819 return nullptr;
820 }
821
HasTrack(const MediaStreamTrack & aTrack) const822 bool DOMMediaStream::HasTrack(const MediaStreamTrack& aTrack) const {
823 return !!FindPlaybackTrackPort(aTrack);
824 }
825
OwnsTrack(const MediaStreamTrack & aTrack) const826 bool DOMMediaStream::OwnsTrack(const MediaStreamTrack& aTrack) const {
827 return !!FindOwnedTrackPort(aTrack);
828 }
829
IsFinished() const830 bool DOMMediaStream::IsFinished() const {
831 return !mPlaybackStream || mPlaybackStream->IsFinished();
832 }
833
GraphRate()834 TrackRate DOMMediaStream::GraphRate() {
835 if (mPlaybackStream) {
836 return mPlaybackStream->GraphRate();
837 }
838 if (mOwnedStream) {
839 return mOwnedStream->GraphRate();
840 }
841 if (mInputStream) {
842 return mInputStream->GraphRate();
843 }
844
845 MOZ_ASSERT(false, "Not hooked up to a graph");
846 return 0;
847 }
848
SetInactiveOnFinish()849 void DOMMediaStream::SetInactiveOnFinish() { mSetInactiveOnFinish = true; }
850
InitSourceStream(MediaStreamGraph * aGraph)851 void DOMMediaStream::InitSourceStream(MediaStreamGraph* aGraph) {
852 InitInputStreamCommon(aGraph->CreateSourceStream(), aGraph);
853 InitOwnedStreamCommon(aGraph);
854 InitPlaybackStreamCommon(aGraph);
855 }
856
InitTrackUnionStream(MediaStreamGraph * aGraph)857 void DOMMediaStream::InitTrackUnionStream(MediaStreamGraph* aGraph) {
858 InitInputStreamCommon(aGraph->CreateTrackUnionStream(), aGraph);
859 InitOwnedStreamCommon(aGraph);
860 InitPlaybackStreamCommon(aGraph);
861 }
862
InitAudioCaptureStream(nsIPrincipal * aPrincipal,MediaStreamGraph * aGraph)863 void DOMMediaStream::InitAudioCaptureStream(nsIPrincipal* aPrincipal,
864 MediaStreamGraph* aGraph) {
865 const TrackID AUDIO_TRACK = 1;
866
867 RefPtr<BasicTrackSource> audioCaptureSource =
868 new BasicTrackSource(aPrincipal, MediaSourceEnum::AudioCapture);
869
870 AudioCaptureStream* audioCaptureStream = static_cast<AudioCaptureStream*>(
871 aGraph->CreateAudioCaptureStream(AUDIO_TRACK));
872 InitInputStreamCommon(audioCaptureStream, aGraph);
873 InitOwnedStreamCommon(aGraph);
874 InitPlaybackStreamCommon(aGraph);
875 RefPtr<MediaStreamTrack> track =
876 CreateDOMTrack(AUDIO_TRACK, MediaSegment::AUDIO, audioCaptureSource);
877 AddTrackInternal(track);
878
879 audioCaptureStream->Start();
880 }
881
InitInputStreamCommon(MediaStream * aStream,MediaStreamGraph * aGraph)882 void DOMMediaStream::InitInputStreamCommon(MediaStream* aStream,
883 MediaStreamGraph* aGraph) {
884 MOZ_ASSERT(!mOwnedStream,
885 "Input stream must be initialized before owned stream");
886
887 mInputStream = aStream;
888 mInputStream->RegisterUser();
889 }
890
InitOwnedStreamCommon(MediaStreamGraph * aGraph)891 void DOMMediaStream::InitOwnedStreamCommon(MediaStreamGraph* aGraph) {
892 MOZ_ASSERT(!mPlaybackStream,
893 "Owned stream must be initialized before playback stream");
894
895 mOwnedStream = aGraph->CreateTrackUnionStream();
896 mOwnedStream->QueueSetAutofinish(true);
897 mOwnedStream->RegisterUser();
898 if (mInputStream) {
899 mOwnedPort = mOwnedStream->AllocateInputPort(mInputStream);
900 }
901
902 // Setup track listeners
903 mOwnedListener = new OwnedStreamListener(this);
904 mOwnedStream->AddListener(mOwnedListener);
905 }
906
InitPlaybackStreamCommon(MediaStreamGraph * aGraph)907 void DOMMediaStream::InitPlaybackStreamCommon(MediaStreamGraph* aGraph) {
908 mPlaybackStream = aGraph->CreateTrackUnionStream();
909 mPlaybackStream->QueueSetAutofinish(true);
910 mPlaybackStream->RegisterUser();
911 if (mOwnedStream) {
912 mPlaybackPort = mPlaybackStream->AllocateInputPort(mOwnedStream);
913 }
914
915 mPlaybackListener = new PlaybackStreamListener(this);
916 mPlaybackStream->AddListener(mPlaybackListener);
917
918 LOG(LogLevel::Debug, ("DOMMediaStream %p Initiated with mInputStream=%p, "
919 "mOwnedStream=%p, mPlaybackStream=%p",
920 this, mInputStream, mOwnedStream, mPlaybackStream));
921 }
922
CreateSourceStreamAsInput(nsPIDOMWindowInner * aWindow,MediaStreamGraph * aGraph,MediaStreamTrackSourceGetter * aTrackSourceGetter)923 already_AddRefed<DOMMediaStream> DOMMediaStream::CreateSourceStreamAsInput(
924 nsPIDOMWindowInner* aWindow, MediaStreamGraph* aGraph,
925 MediaStreamTrackSourceGetter* aTrackSourceGetter) {
926 RefPtr<DOMMediaStream> stream =
927 new DOMMediaStream(aWindow, aTrackSourceGetter);
928 stream->InitSourceStream(aGraph);
929 return stream.forget();
930 }
931
CreateTrackUnionStreamAsInput(nsPIDOMWindowInner * aWindow,MediaStreamGraph * aGraph,MediaStreamTrackSourceGetter * aTrackSourceGetter)932 already_AddRefed<DOMMediaStream> DOMMediaStream::CreateTrackUnionStreamAsInput(
933 nsPIDOMWindowInner* aWindow, MediaStreamGraph* aGraph,
934 MediaStreamTrackSourceGetter* aTrackSourceGetter) {
935 RefPtr<DOMMediaStream> stream =
936 new DOMMediaStream(aWindow, aTrackSourceGetter);
937 stream->InitTrackUnionStream(aGraph);
938 return stream.forget();
939 }
940
941 already_AddRefed<DOMMediaStream>
CreateAudioCaptureStreamAsInput(nsPIDOMWindowInner * aWindow,nsIPrincipal * aPrincipal,MediaStreamGraph * aGraph)942 DOMMediaStream::CreateAudioCaptureStreamAsInput(nsPIDOMWindowInner* aWindow,
943 nsIPrincipal* aPrincipal,
944 MediaStreamGraph* aGraph) {
945 // Audio capture doesn't create tracks dynamically
946 MediaStreamTrackSourceGetter* getter = nullptr;
947 RefPtr<DOMMediaStream> stream = new DOMMediaStream(aWindow, getter);
948 stream->InitAudioCaptureStream(aPrincipal, aGraph);
949 return stream.forget();
950 }
951
PrincipalChanged(MediaStreamTrack * aTrack)952 void DOMMediaStream::PrincipalChanged(MediaStreamTrack* aTrack) {
953 MOZ_ASSERT(aTrack);
954 NS_ASSERTION(HasTrack(*aTrack), "Principal changed for an unknown track");
955 LOG(LogLevel::Info,
956 ("DOMMediaStream %p Principal changed for track %p", this, aTrack));
957 RecomputePrincipal();
958 }
959
RecomputePrincipal()960 void DOMMediaStream::RecomputePrincipal() {
961 nsCOMPtr<nsIPrincipal> previousPrincipal = mPrincipal.forget();
962 nsCOMPtr<nsIPrincipal> previousVideoPrincipal = mVideoPrincipal.forget();
963
964 if (mTracksPendingRemoval > 0) {
965 LOG(LogLevel::Info, ("DOMMediaStream %p RecomputePrincipal() Cannot "
966 "recompute stream principal with tracks pending "
967 "removal.",
968 this));
969 return;
970 }
971
972 LOG(LogLevel::Debug, ("DOMMediaStream %p Recomputing principal. "
973 "Old principal was %p.",
974 this, previousPrincipal.get()));
975
976 // mPrincipal is recomputed based on all current tracks, and tracks that have
977 // not ended in our playback stream.
978 for (const RefPtr<TrackPort>& info : mTracks) {
979 if (info->GetTrack()->Ended()) {
980 continue;
981 }
982 LOG(LogLevel::Debug,
983 ("DOMMediaStream %p Taking live track %p with "
984 "principal %p into account.",
985 this, info->GetTrack(), info->GetTrack()->GetPrincipal()));
986 nsContentUtils::CombineResourcePrincipals(&mPrincipal,
987 info->GetTrack()->GetPrincipal());
988 if (info->GetTrack()->AsVideoStreamTrack()) {
989 nsContentUtils::CombineResourcePrincipals(
990 &mVideoPrincipal, info->GetTrack()->GetPrincipal());
991 }
992 }
993
994 LOG(LogLevel::Debug,
995 ("DOMMediaStream %p new principal is %p.", this, mPrincipal.get()));
996
997 if (previousPrincipal != mPrincipal ||
998 previousVideoPrincipal != mVideoPrincipal) {
999 NotifyPrincipalChanged();
1000 }
1001 }
1002
NotifyPrincipalChanged()1003 void DOMMediaStream::NotifyPrincipalChanged() {
1004 if (!mPrincipal) {
1005 // When all tracks are removed, mPrincipal will change to nullptr.
1006 LOG(LogLevel::Info,
1007 ("DOMMediaStream %p Principal changed to nothing.", this));
1008 } else {
1009 LOG(LogLevel::Info, ("DOMMediaStream %p Principal changed. Now: "
1010 "null=%d, codebase=%d, expanded=%d, system=%d",
1011 this, mPrincipal->GetIsNullPrincipal(),
1012 mPrincipal->GetIsCodebasePrincipal(),
1013 mPrincipal->GetIsExpandedPrincipal(),
1014 mPrincipal->GetIsSystemPrincipal()));
1015 }
1016
1017 for (uint32_t i = 0; i < mPrincipalChangeObservers.Length(); ++i) {
1018 mPrincipalChangeObservers[i]->PrincipalChanged(this);
1019 }
1020 }
1021
AddPrincipalChangeObserver(PrincipalChangeObserver<DOMMediaStream> * aObserver)1022 bool DOMMediaStream::AddPrincipalChangeObserver(
1023 PrincipalChangeObserver<DOMMediaStream>* aObserver) {
1024 return mPrincipalChangeObservers.AppendElement(aObserver) != nullptr;
1025 }
1026
RemovePrincipalChangeObserver(PrincipalChangeObserver<DOMMediaStream> * aObserver)1027 bool DOMMediaStream::RemovePrincipalChangeObserver(
1028 PrincipalChangeObserver<DOMMediaStream>* aObserver) {
1029 return mPrincipalChangeObservers.RemoveElement(aObserver);
1030 }
1031
AddTrackInternal(MediaStreamTrack * aTrack)1032 void DOMMediaStream::AddTrackInternal(MediaStreamTrack* aTrack) {
1033 MOZ_ASSERT(aTrack->mOwningStream == this);
1034 MOZ_ASSERT(FindOwnedDOMTrack(aTrack->GetInputStream(), aTrack->mInputTrackID,
1035 aTrack->mTrackID));
1036 MOZ_ASSERT(!FindPlaybackDOMTrack(aTrack->GetOwnedStream(), aTrack->mTrackID));
1037
1038 LOG(LogLevel::Debug,
1039 ("DOMMediaStream %p Adding owned track %p", this, aTrack));
1040
1041 mTracks.AppendElement(new TrackPort(mPlaybackPort, aTrack,
1042 TrackPort::InputPortOwnership::EXTERNAL));
1043
1044 NotifyTrackAdded(aTrack);
1045
1046 DispatchTrackEvent(NS_LITERAL_STRING("addtrack"), aTrack);
1047 }
1048
CreateDOMTrack(TrackID aTrackID,MediaSegment::Type aType,MediaStreamTrackSource * aSource,const MediaTrackConstraints & aConstraints)1049 already_AddRefed<MediaStreamTrack> DOMMediaStream::CreateDOMTrack(
1050 TrackID aTrackID, MediaSegment::Type aType, MediaStreamTrackSource* aSource,
1051 const MediaTrackConstraints& aConstraints) {
1052 MOZ_RELEASE_ASSERT(mInputStream);
1053 MOZ_RELEASE_ASSERT(mOwnedStream);
1054
1055 MOZ_ASSERT(FindOwnedDOMTrack(GetInputStream(), aTrackID) == nullptr);
1056
1057 RefPtr<MediaStreamTrack> track;
1058 switch (aType) {
1059 case MediaSegment::AUDIO:
1060 track =
1061 new AudioStreamTrack(this, aTrackID, aTrackID, aSource, aConstraints);
1062 break;
1063 case MediaSegment::VIDEO:
1064 track =
1065 new VideoStreamTrack(this, aTrackID, aTrackID, aSource, aConstraints);
1066 break;
1067 default:
1068 MOZ_CRASH("Unhandled track type");
1069 }
1070
1071 LOG(LogLevel::Debug, ("DOMMediaStream %p Created new track %p with ID %u",
1072 this, track.get(), aTrackID));
1073
1074 mOwnedTracks.AppendElement(new TrackPort(
1075 mOwnedPort, track, TrackPort::InputPortOwnership::EXTERNAL));
1076
1077 return track.forget();
1078 }
1079
CloneDOMTrack(MediaStreamTrack & aTrack,TrackID aCloneTrackID)1080 already_AddRefed<MediaStreamTrack> DOMMediaStream::CloneDOMTrack(
1081 MediaStreamTrack& aTrack, TrackID aCloneTrackID) {
1082 MOZ_RELEASE_ASSERT(mOwnedStream);
1083 MOZ_RELEASE_ASSERT(mPlaybackStream);
1084 MOZ_RELEASE_ASSERT(IsTrackIDExplicit(aCloneTrackID));
1085
1086 TrackID inputTrackID = aTrack.mInputTrackID;
1087 MediaStream* inputStream = aTrack.GetInputStream();
1088
1089 RefPtr<MediaStreamTrack> newTrack = aTrack.CloneInternal(this, aCloneTrackID);
1090
1091 newTrack->mOriginalTrack =
1092 aTrack.mOriginalTrack ? aTrack.mOriginalTrack.get() : &aTrack;
1093
1094 LOG(LogLevel::Debug,
1095 ("DOMMediaStream %p Created new track %p cloned from stream %p track %d",
1096 this, newTrack.get(), inputStream, inputTrackID));
1097
1098 RefPtr<MediaInputPort> inputPort =
1099 mOwnedStream->AllocateInputPort(inputStream, inputTrackID, aCloneTrackID);
1100
1101 mOwnedTracks.AppendElement(
1102 new TrackPort(inputPort, newTrack, TrackPort::InputPortOwnership::OWNED));
1103
1104 mTracks.AppendElement(new TrackPort(mPlaybackPort, newTrack,
1105 TrackPort::InputPortOwnership::EXTERNAL));
1106
1107 NotifyTrackAdded(newTrack);
1108
1109 newTrack->SetEnabled(aTrack.Enabled());
1110 newTrack->SetMuted(aTrack.Muted());
1111 newTrack->SetReadyState(aTrack.ReadyState());
1112
1113 if (aTrack.Ended()) {
1114 // For extra suspenders, make sure that we don't forward data by mistake
1115 // to the clone when the original has already ended.
1116 // We only block END_EXISTING to allow any pending clones to end.
1117 RefPtr<Pledge<bool, nsresult>> blockingPledge =
1118 inputPort->BlockSourceTrackId(inputTrackID, BlockingMode::END_EXISTING);
1119 Unused << blockingPledge;
1120 }
1121
1122 return newTrack.forget();
1123 }
1124
FindTrackPortAmongTracks(const MediaStreamTrack & aTrack,const nsTArray<RefPtr<DOMMediaStream::TrackPort>> & aTracks)1125 static DOMMediaStream::TrackPort* FindTrackPortAmongTracks(
1126 const MediaStreamTrack& aTrack,
1127 const nsTArray<RefPtr<DOMMediaStream::TrackPort>>& aTracks) {
1128 for (const RefPtr<DOMMediaStream::TrackPort>& info : aTracks) {
1129 if (info->GetTrack() == &aTrack) {
1130 return info;
1131 }
1132 }
1133 return nullptr;
1134 }
1135
FindOwnedDOMTrack(MediaStream * aInputStream,TrackID aInputTrackID,TrackID aTrackID) const1136 MediaStreamTrack* DOMMediaStream::FindOwnedDOMTrack(MediaStream* aInputStream,
1137 TrackID aInputTrackID,
1138 TrackID aTrackID) const {
1139 MOZ_RELEASE_ASSERT(mOwnedStream);
1140
1141 for (const RefPtr<TrackPort>& info : mOwnedTracks) {
1142 if (info->GetInputPort() &&
1143 info->GetInputPort()->GetSource() == aInputStream &&
1144 info->GetTrack()->mInputTrackID == aInputTrackID &&
1145 (aTrackID == TRACK_ANY || info->GetTrack()->mTrackID == aTrackID)) {
1146 // This track is owned externally but in our playback stream.
1147 return info->GetTrack();
1148 }
1149 }
1150 return nullptr;
1151 }
1152
FindOwnedTrackPort(const MediaStreamTrack & aTrack) const1153 DOMMediaStream::TrackPort* DOMMediaStream::FindOwnedTrackPort(
1154 const MediaStreamTrack& aTrack) const {
1155 return FindTrackPortAmongTracks(aTrack, mOwnedTracks);
1156 }
1157
FindPlaybackDOMTrack(MediaStream * aInputStream,TrackID aInputTrackID) const1158 MediaStreamTrack* DOMMediaStream::FindPlaybackDOMTrack(
1159 MediaStream* aInputStream, TrackID aInputTrackID) const {
1160 if (!mPlaybackStream) {
1161 // One would think we can assert mPlaybackStream here, but track clones have
1162 // a dummy DOMMediaStream that doesn't have a playback stream, so we can't.
1163 return nullptr;
1164 }
1165
1166 for (const RefPtr<TrackPort>& info : mTracks) {
1167 if (info->GetInputPort() == mPlaybackPort && aInputStream == mOwnedStream &&
1168 info->GetTrack()->mInputTrackID == aInputTrackID) {
1169 // This track is in our owned and playback streams.
1170 return info->GetTrack();
1171 }
1172 if (info->GetInputPort() &&
1173 info->GetInputPort()->GetSource() == aInputStream &&
1174 info->GetSourceTrackId() == aInputTrackID) {
1175 // This track is owned externally but in our playback stream.
1176 MOZ_ASSERT(IsTrackIDExplicit(aInputTrackID));
1177 return info->GetTrack();
1178 }
1179 }
1180 return nullptr;
1181 }
1182
FindPlaybackTrackPort(const MediaStreamTrack & aTrack) const1183 DOMMediaStream::TrackPort* DOMMediaStream::FindPlaybackTrackPort(
1184 const MediaStreamTrack& aTrack) const {
1185 return FindTrackPortAmongTracks(aTrack, mTracks);
1186 }
1187
OnTracksAvailable(OnTracksAvailableCallback * aRunnable)1188 void DOMMediaStream::OnTracksAvailable(OnTracksAvailableCallback* aRunnable) {
1189 if (mNotifiedOfMediaStreamGraphShutdown) {
1190 // No more tracks will ever be added, so just delete the callback now.
1191 delete aRunnable;
1192 return;
1193 }
1194 mRunOnTracksAvailable.AppendElement(aRunnable);
1195 CheckTracksAvailable();
1196 }
1197
NotifyTracksCreated()1198 void DOMMediaStream::NotifyTracksCreated() {
1199 mTracksCreated = true;
1200 CheckTracksAvailable();
1201 }
1202
NotifyFinished()1203 void DOMMediaStream::NotifyFinished() {
1204 if (!mSetInactiveOnFinish) {
1205 return;
1206 }
1207
1208 if (!mActive) {
1209 // This can happen if the stream never became active.
1210 return;
1211 }
1212
1213 MOZ_ASSERT(!ContainsLiveTracks(mTracks));
1214 mActive = false;
1215 NotifyInactive();
1216 }
1217
NotifyActive()1218 void DOMMediaStream::NotifyActive() {
1219 LOG(LogLevel::Info, ("DOMMediaStream %p NotifyActive(). ", this));
1220
1221 MOZ_ASSERT(mActive);
1222 for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
1223 mTrackListeners[i]->NotifyActive();
1224 }
1225 }
1226
NotifyInactive()1227 void DOMMediaStream::NotifyInactive() {
1228 LOG(LogLevel::Info, ("DOMMediaStream %p NotifyInactive(). ", this));
1229
1230 MOZ_ASSERT(!mActive);
1231 for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
1232 mTrackListeners[i]->NotifyInactive();
1233 }
1234 }
1235
CheckTracksAvailable()1236 void DOMMediaStream::CheckTracksAvailable() {
1237 if (!mTracksCreated) {
1238 return;
1239 }
1240 nsTArray<nsAutoPtr<OnTracksAvailableCallback>> callbacks;
1241 callbacks.SwapElements(mRunOnTracksAvailable);
1242
1243 for (uint32_t i = 0; i < callbacks.Length(); ++i) {
1244 callbacks[i]->NotifyTracksAvailable(this);
1245 }
1246 }
1247
RegisterTrackListener(TrackListener * aListener)1248 void DOMMediaStream::RegisterTrackListener(TrackListener* aListener) {
1249 MOZ_ASSERT(NS_IsMainThread());
1250
1251 if (mNotifiedOfMediaStreamGraphShutdown) {
1252 // No more tracks will ever be added, so just do nothing.
1253 return;
1254 }
1255 mTrackListeners.AppendElement(aListener);
1256 }
1257
UnregisterTrackListener(TrackListener * aListener)1258 void DOMMediaStream::UnregisterTrackListener(TrackListener* aListener) {
1259 MOZ_ASSERT(NS_IsMainThread());
1260 mTrackListeners.RemoveElement(aListener);
1261 }
1262
NotifyTrackAdded(const RefPtr<MediaStreamTrack> & aTrack)1263 void DOMMediaStream::NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack) {
1264 MOZ_ASSERT(NS_IsMainThread());
1265
1266 if (mTracksPendingRemoval > 0) {
1267 // If there are tracks pending removal we may not degrade the current
1268 // principals until those tracks have been confirmed removed from the
1269 // playback stream. Instead combine with the new track and the (potentially)
1270 // degraded principal will be calculated when it's safe.
1271 nsContentUtils::CombineResourcePrincipals(&mPrincipal,
1272 aTrack->GetPrincipal());
1273 LOG(LogLevel::Debug, ("DOMMediaStream %p saw a track get added. Combining "
1274 "its principal %p into our while waiting for pending "
1275 "tracks to be removed. New principal is %p.",
1276 this, aTrack->GetPrincipal(), mPrincipal.get()));
1277 if (aTrack->AsVideoStreamTrack()) {
1278 nsContentUtils::CombineResourcePrincipals(&mVideoPrincipal,
1279 aTrack->GetPrincipal());
1280 }
1281 } else {
1282 LOG(LogLevel::Debug, ("DOMMediaStream %p saw a track get added. "
1283 "Recomputing principal.",
1284 this));
1285 RecomputePrincipal();
1286 }
1287
1288 aTrack->AddPrincipalChangeObserver(this);
1289 aTrack->AddConsumer(mPlaybackTrackListener);
1290
1291 for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
1292 mTrackListeners[i]->NotifyTrackAdded(aTrack);
1293 }
1294
1295 if (mActive) {
1296 return;
1297 }
1298
1299 // Check if we became active.
1300 if (ContainsLiveTracks(mTracks)) {
1301 mActive = true;
1302 NotifyActive();
1303 }
1304 }
1305
NotifyTrackRemoved(const RefPtr<MediaStreamTrack> & aTrack)1306 void DOMMediaStream::NotifyTrackRemoved(
1307 const RefPtr<MediaStreamTrack>& aTrack) {
1308 MOZ_ASSERT(NS_IsMainThread());
1309
1310 aTrack->RemoveConsumer(mPlaybackTrackListener);
1311 aTrack->RemovePrincipalChangeObserver(this);
1312
1313 for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
1314 mTrackListeners[i]->NotifyTrackRemoved(aTrack);
1315 }
1316
1317 // Don't call RecomputePrincipal here as the track may still exist in the
1318 // playback stream in the MediaStreamGraph. It will instead be called when the
1319 // track has been confirmed removed by the graph. See BlockPlaybackTrack().
1320
1321 if (!mActive) {
1322 NS_ASSERTION(false, "Shouldn't remove a live track if already inactive");
1323 return;
1324 }
1325
1326 if (mSetInactiveOnFinish) {
1327 // For compatibility with mozCaptureStream we in some cases do not go
1328 // inactive until the playback stream finishes.
1329 return;
1330 }
1331
1332 // Check if we became inactive.
1333 if (!ContainsLiveTracks(mTracks)) {
1334 mActive = false;
1335 NotifyInactive();
1336 }
1337 }
1338
DispatchTrackEvent(const nsAString & aName,const RefPtr<MediaStreamTrack> & aTrack)1339 nsresult DOMMediaStream::DispatchTrackEvent(
1340 const nsAString& aName, const RefPtr<MediaStreamTrack>& aTrack) {
1341 MediaStreamTrackEventInit init;
1342 init.mTrack = aTrack;
1343
1344 RefPtr<MediaStreamTrackEvent> event =
1345 MediaStreamTrackEvent::Constructor(this, aName, init);
1346
1347 return DispatchTrustedEvent(event);
1348 }
1349
BlockPlaybackTrack(TrackPort * aTrack)1350 void DOMMediaStream::BlockPlaybackTrack(TrackPort* aTrack) {
1351 MOZ_ASSERT(aTrack);
1352 ++mTracksPendingRemoval;
1353 RefPtr<Pledge<bool>> p = aTrack->BlockSourceTrackId(
1354 aTrack->GetTrack()->mTrackID, BlockingMode::CREATION);
1355 RefPtr<DOMMediaStream> self = this;
1356 p->Then([self](const bool& aIgnore) { self->NotifyPlaybackTrackBlocked(); },
1357 [](const nsresult& aIgnore) {
1358 NS_ERROR("Could not remove track from MSG");
1359 });
1360 }
1361
NotifyPlaybackTrackBlocked()1362 void DOMMediaStream::NotifyPlaybackTrackBlocked() {
1363 MOZ_ASSERT(mTracksPendingRemoval > 0,
1364 "A track reported finished blocking more times than we asked for");
1365 if (--mTracksPendingRemoval == 0) {
1366 // The MediaStreamGraph has reported a track was blocked and we are not
1367 // waiting for any further tracks to get blocked. It is now safe to
1368 // recompute the principal based on our main thread track set state.
1369 LOG(LogLevel::Debug, ("DOMMediaStream %p saw all tracks pending removal "
1370 "finish. Recomputing principal.",
1371 this));
1372 RecomputePrincipal();
1373 }
1374 }
1375
~DOMLocalMediaStream()1376 DOMLocalMediaStream::~DOMLocalMediaStream() {
1377 if (mInputStream) {
1378 // Make sure Listeners of this stream know it's going away
1379 StopImpl();
1380 }
1381 }
1382
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)1383 JSObject* DOMLocalMediaStream::WrapObject(JSContext* aCx,
1384 JS::Handle<JSObject*> aGivenProto) {
1385 return dom::LocalMediaStreamBinding::Wrap(aCx, this, aGivenProto);
1386 }
1387
Stop()1388 void DOMLocalMediaStream::Stop() {
1389 LOG(LogLevel::Debug, ("DOMMediaStream %p Stop()", this));
1390 nsCOMPtr<nsPIDOMWindowInner> pWindow = GetParentObject();
1391 nsIDocument* document = pWindow ? pWindow->GetExtantDoc() : nullptr;
1392 nsContentUtils::ReportToConsole(
1393 nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Media"), document,
1394 nsContentUtils::eDOM_PROPERTIES, "MediaStreamStopDeprecatedWarning");
1395
1396 StopImpl();
1397 }
1398
StopImpl()1399 void DOMLocalMediaStream::StopImpl() {
1400 if (mInputStream && mInputStream->AsSourceStream()) {
1401 mInputStream->AsSourceStream()->EndAllTrackAndFinish();
1402 }
1403 }
1404
1405 already_AddRefed<DOMLocalMediaStream>
CreateSourceStreamAsInput(nsPIDOMWindowInner * aWindow,MediaStreamGraph * aGraph,MediaStreamTrackSourceGetter * aTrackSourceGetter)1406 DOMLocalMediaStream::CreateSourceStreamAsInput(
1407 nsPIDOMWindowInner* aWindow, MediaStreamGraph* aGraph,
1408 MediaStreamTrackSourceGetter* aTrackSourceGetter) {
1409 RefPtr<DOMLocalMediaStream> stream =
1410 new DOMLocalMediaStream(aWindow, aTrackSourceGetter);
1411 stream->InitSourceStream(aGraph);
1412 return stream.forget();
1413 }
1414
1415 already_AddRefed<DOMLocalMediaStream>
CreateTrackUnionStreamAsInput(nsPIDOMWindowInner * aWindow,MediaStreamGraph * aGraph,MediaStreamTrackSourceGetter * aTrackSourceGetter)1416 DOMLocalMediaStream::CreateTrackUnionStreamAsInput(
1417 nsPIDOMWindowInner* aWindow, MediaStreamGraph* aGraph,
1418 MediaStreamTrackSourceGetter* aTrackSourceGetter) {
1419 RefPtr<DOMLocalMediaStream> stream =
1420 new DOMLocalMediaStream(aWindow, aTrackSourceGetter);
1421 stream->InitTrackUnionStream(aGraph);
1422 return stream.forget();
1423 }
1424
DOMAudioNodeMediaStream(nsPIDOMWindowInner * aWindow,AudioNode * aNode)1425 DOMAudioNodeMediaStream::DOMAudioNodeMediaStream(nsPIDOMWindowInner* aWindow,
1426 AudioNode* aNode)
1427 : DOMMediaStream(aWindow, nullptr), mStreamNode(aNode) {}
1428
~DOMAudioNodeMediaStream()1429 DOMAudioNodeMediaStream::~DOMAudioNodeMediaStream() {}
1430
1431 already_AddRefed<DOMAudioNodeMediaStream>
CreateTrackUnionStreamAsInput(nsPIDOMWindowInner * aWindow,AudioNode * aNode,MediaStreamGraph * aGraph)1432 DOMAudioNodeMediaStream::CreateTrackUnionStreamAsInput(
1433 nsPIDOMWindowInner* aWindow, AudioNode* aNode, MediaStreamGraph* aGraph) {
1434 RefPtr<DOMAudioNodeMediaStream> stream =
1435 new DOMAudioNodeMediaStream(aWindow, aNode);
1436 stream->InitTrackUnionStream(aGraph);
1437 return stream.forget();
1438 }
1439