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