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