Coverage Report

Created: 2018-09-25 14:53

/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