Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/MediaStreamTrack.h
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
#ifndef MEDIASTREAMTRACK_H_
7
#define MEDIASTREAMTRACK_H_
8
9
#include "MediaTrackConstraints.h"
10
#include "PrincipalChangeObserver.h"
11
#include "StreamTracks.h"
12
#include "mozilla/CORSMode.h"
13
#include "mozilla/DOMEventTargetHelper.h"
14
#include "mozilla/dom/MediaStreamTrackBinding.h"
15
#include "mozilla/dom/MediaTrackSettingsBinding.h"
16
#include "mozilla/media/MediaUtils.h"
17
#include "mozilla/WeakPtr.h"
18
#include "nsError.h"
19
#include "nsID.h"
20
#include "nsIPrincipal.h"
21
22
namespace mozilla {
23
24
class DOMMediaStream;
25
class MediaEnginePhotoCallback;
26
class MediaInputPort;
27
class MediaStream;
28
class MediaStreamGraph;
29
class MediaStreamGraphImpl;
30
class MediaStreamTrackListener;
31
class DirectMediaStreamTrackListener;
32
class PeerConnectionImpl;
33
class PeerConnectionMedia;
34
class PeerIdentity;
35
class ProcessedMediaStream;
36
class RemoteSourceStreamInfo;
37
class SourceStreamInfo;
38
39
namespace dom {
40
41
class AudioStreamTrack;
42
class VideoStreamTrack;
43
class MediaStreamError;
44
enum class CallerType : uint32_t;
45
46
/**
47
 * Common interface through which a MediaStreamTrack can communicate with its
48
 * producer on the main thread.
49
 *
50
 * Kept alive by a strong ref in all MediaStreamTracks (original and clones)
51
 * sharing this source.
52
 */
53
class MediaStreamTrackSource : public nsISupports
54
{
55
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
56
  NS_DECL_CYCLE_COLLECTION_CLASS(MediaStreamTrackSource)
57
58
public:
59
  class Sink : public SupportsWeakPtr<Sink>
60
  {
61
  public:
62
    MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MediaStreamTrackSource::Sink)
63
64
    /**
65
     * Must be constant throughout the Sink's lifetime.
66
     *
67
     * Return true to keep the MediaStreamTrackSource where this sink is
68
     * registered alive.
69
     * Return false to allow the source to stop.
70
     *
71
     * Typically MediaStreamTrack::Sink returns true and other Sinks
72
     * (like HTMLMediaElement::StreamCaptureTrackSource) return false.
73
     */
74
    virtual bool KeepsSourceAlive() const = 0;
75
76
    /**
77
     * Return true to ensure that the MediaStreamTrackSource where this Sink is
78
     * registered is kept turned on and active.
79
     * Return false to allow the source to pause, and any underlying devices to
80
     * temporarily stop.
81
     *
82
     * When the underlying enabled state of the sink changes,
83
     * call MediaStreamTrackSource::SinkEnabledStateChanged().
84
     *
85
     * Typically MediaStreamTrack returns the track's enabled state and other
86
     * Sinks (like HTMLMediaElement::StreamCaptureTrackSource) return false so
87
     * control over device state remains with tracks and their enabled state.
88
     */
89
    virtual bool Enabled() const = 0;
90
91
    virtual void PrincipalChanged() = 0;
92
    virtual void MutedChanged(bool aNewState) = 0;
93
  };
94
95
  MediaStreamTrackSource(nsIPrincipal* aPrincipal,
96
                         const nsString& aLabel)
97
    : mPrincipal(aPrincipal),
98
      mLabel(aLabel),
99
      mStopped(false)
100
0
  {
101
0
  }
102
103
  /**
104
   * Use to clean up any resources that have to be cleaned before the
105
   * destructor is called. It is often too late in the destructor because
106
   * of garbage collection having removed the members already.
107
   */
108
0
  virtual void Destroy() {}
109
110
  /**
111
   * Gets the source's MediaSourceEnum for usage by PeerConnections.
112
   */
113
  virtual MediaSourceEnum GetMediaSource() const = 0;
114
115
  /**
116
   * Get this TrackSource's principal.
117
   */
118
0
  nsIPrincipal* GetPrincipal() const { return mPrincipal; }
119
120
  /**
121
   * Get the source's current CORSMode. If not applicable CORS_NONE is returned.
122
   * The sink will be notified of changes to our CORSMode through
123
   * PrincipalChanged().
124
   */
125
0
  virtual CORSMode GetCORSMode() const { return CORS_NONE; }
126
127
  /**
128
   * This is used in WebRTC. A peerIdentity constrained MediaStreamTrack cannot
129
   * be sent across the network to anything other than a peer with the provided
130
   * identity. If this is set, then GetPrincipal() should return an instance of
131
   * NullPrincipal.
132
   *
133
   * A track's PeerIdentity is immutable and will not change during the track's
134
   * lifetime.
135
   */
136
0
  virtual const PeerIdentity* GetPeerIdentity() const { return nullptr; }
137
138
  /**
139
   * MediaStreamTrack::GetLabel (see spec) calls through to here.
140
   */
141
0
  void GetLabel(nsAString& aLabel) { aLabel.Assign(mLabel); }
142
143
  /**
144
   * Forwards a photo request to backends that support it. Other backends return
145
   * NS_ERROR_NOT_IMPLEMENTED to indicate that a MediaStreamGraph-based fallback
146
   * should be used.
147
   */
148
0
  virtual nsresult TakePhoto(MediaEnginePhotoCallback*) const { return NS_ERROR_NOT_IMPLEMENTED; }
149
150
  typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
151
152
  /**
153
   * We provide a fallback solution to ApplyConstraints() here.
154
   * Sources that support ApplyConstraints() will have to override it.
155
   */
156
  virtual already_AddRefed<PledgeVoid>
157
  ApplyConstraints(nsPIDOMWindowInner* aWindow,
158
                   const dom::MediaTrackConstraints& aConstraints,
159
                   CallerType aCallerType);
160
161
  /**
162
   * Same for GetSettings (no-op).
163
   */
164
  virtual void
165
0
  GetSettings(dom::MediaTrackSettings& aResult) {};
166
167
  /**
168
   * Called by the source interface when all registered sinks with
169
   * KeepsSourceAlive() == true have unregistered.
170
   */
171
  virtual void Stop() = 0;
172
173
  /**
174
   * Called by the source interface when all registered sinks with
175
   * KeepsSourceAlive() == true become disabled.
176
   */
177
  virtual void Disable() = 0;
178
179
  /**
180
   * Called by the source interface when at least one registered sink with
181
   * KeepsSourceAlive() == true become enabled.
182
   */
183
  virtual void Enable() = 0;
184
185
  /**
186
   * Called when a Sink's Enabled() state changed. Will iterate through all
187
   * sinks and notify the source of the aggregated enabled state.
188
   *
189
   * Note that a Sink with KeepsSourceAlive() == false counts as disabled.
190
   */
191
  void SinkEnabledStateChanged()
192
0
  {
193
0
    if (IsEnabled()) {
194
0
      Enable();
195
0
    } else {
196
0
      Disable();
197
0
    }
198
0
  }
199
200
  /**
201
   * Called by each MediaStreamTrack clone on initialization.
202
   */
203
  void RegisterSink(Sink* aSink)
204
0
  {
205
0
    MOZ_ASSERT(NS_IsMainThread());
206
0
    if (mStopped) {
207
0
      return;
208
0
    }
209
0
    mSinks.AppendElement(aSink);
210
0
    while(mSinks.RemoveElement(nullptr)) {
211
0
      MOZ_ASSERT_UNREACHABLE("Sink was not explicitly removed");
212
0
    }
213
0
  }
214
215
  /**
216
   * Called by each MediaStreamTrack clone on Stop() if supported by the
217
   * source (us) or destruction.
218
   */
219
  void UnregisterSink(Sink* aSink)
220
0
  {
221
0
    MOZ_ASSERT(NS_IsMainThread());
222
0
    while(mSinks.RemoveElement(nullptr)) {
223
0
      MOZ_ASSERT_UNREACHABLE("Sink was not explicitly removed");
224
0
    }
225
0
    if (mSinks.RemoveElement(aSink) && !IsActive()) {
226
0
      MOZ_ASSERT(!aSink->KeepsSourceAlive() || !mStopped,
227
0
                 "When the last sink keeping the source alive is removed, "
228
0
                 "we should still be live");
229
0
      Stop();
230
0
      mStopped = true;
231
0
    }
232
0
    if (!mStopped) {
233
0
      SinkEnabledStateChanged();
234
0
    }
235
0
  }
236
237
protected:
238
  virtual ~MediaStreamTrackSource()
239
0
  {
240
0
  }
241
242
  bool IsActive()
243
0
  {
244
0
    for (const WeakPtr<Sink>& sink : mSinks) {
245
0
      if (sink && sink->KeepsSourceAlive()) {
246
0
        return true;
247
0
      }
248
0
    }
249
0
    return false;
250
0
  }
251
252
  bool IsEnabled()
253
0
  {
254
0
    for (const WeakPtr<Sink>& sink : mSinks) {
255
0
      if (sink && sink->KeepsSourceAlive() && sink->Enabled()) {
256
0
        return true;
257
0
      }
258
0
    }
259
0
    return false;
260
0
  }
261
262
  /**
263
   * Called by a sub class when the principal has changed.
264
   * Notifies all sinks.
265
   */
266
  void PrincipalChanged()
267
0
  {
268
0
    MOZ_ASSERT(NS_IsMainThread());
269
0
    nsTArray<WeakPtr<Sink>> sinks(mSinks);
270
0
    for (auto& sink : sinks) {
271
0
      if (!sink) {
272
0
        MOZ_ASSERT_UNREACHABLE("Sink was not explicitly removed");
273
0
        mSinks.RemoveElement(sink);
274
0
        continue;
275
0
      }
276
0
      sink->PrincipalChanged();
277
0
    }
278
0
  }
279
280
  /**
281
   * Called by a sub class when the source's muted state has changed. Note that
282
   * the source is responsible for making the content black/silent during mute.
283
   * Notifies all sinks.
284
   */
285
  void MutedChanged(bool aNewState)
286
0
  {
287
0
    MOZ_ASSERT(NS_IsMainThread());
288
0
    nsTArray<WeakPtr<Sink>> sinks(mSinks);
289
0
    for (auto& sink : sinks) {
290
0
      if (!sink) {
291
0
        MOZ_ASSERT_UNREACHABLE("Sink was not explicitly removed");
292
0
        mSinks.RemoveElement(sink);
293
0
        continue;
294
0
      }
295
0
      sink->MutedChanged(aNewState);
296
0
    }
297
0
  }
298
299
  // Principal identifying who may access the contents of this source.
300
  nsCOMPtr<nsIPrincipal> mPrincipal;
301
302
  // Currently registered sinks.
303
  nsTArray<WeakPtr<Sink>> mSinks;
304
305
  // The label of the track we are the source of per the MediaStreamTrack spec.
306
  const nsString mLabel;
307
308
  // True if all MediaStreamTrack users have unregistered from this source and
309
  // Stop() has been called.
310
  bool mStopped;
311
};
312
313
/**
314
 * Basic implementation of MediaStreamTrackSource that doesn't forward Stop().
315
 */
316
class BasicTrackSource : public MediaStreamTrackSource
317
{
318
public:
319
  explicit BasicTrackSource(nsIPrincipal* aPrincipal,
320
                            const MediaSourceEnum aMediaSource =
321
                            MediaSourceEnum::Other)
322
    : MediaStreamTrackSource(aPrincipal, nsString())
323
    , mMediaSource(aMediaSource)
324
  {}
325
326
  MediaSourceEnum GetMediaSource() const override { return mMediaSource; }
327
328
  void Stop() override {}
329
  void Disable() override {}
330
  void Enable() override {}
331
332
protected:
333
  ~BasicTrackSource() {}
334
335
  const MediaSourceEnum mMediaSource;
336
};
337
338
/**
339
 * Base class that consumers of a MediaStreamTrack can use to get notifications
340
 * about state changes in the track.
341
 */
342
class MediaStreamTrackConsumer
343
  : public SupportsWeakPtr<MediaStreamTrackConsumer>
344
{
345
public:
346
  MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MediaStreamTrackConsumer)
347
348
  /**
349
   * Called when the track's readyState transitions to "ended".
350
   * Unlike the "ended" event exposed to script this is called for any reason,
351
   * including MediaStreamTrack::Stop().
352
   */
353
  virtual void NotifyEnded(MediaStreamTrack* aTrack) {};
354
};
355
356
/**
357
 * Class representing a track in a DOMMediaStream.
358
 */
359
class MediaStreamTrack : public DOMEventTargetHelper,
360
                         public MediaStreamTrackSource::Sink
361
{
362
  // DOMMediaStream owns MediaStreamTrack instances, and requires access to
363
  // some internal state, e.g., GetInputStream(), GetOwnedStream().
364
  friend class mozilla::DOMMediaStream;
365
366
  // PeerConnection and friends need to know our owning DOMStream and track id.
367
  friend class mozilla::PeerConnectionImpl;
368
  friend class mozilla::PeerConnectionMedia;
369
  friend class mozilla::SourceStreamInfo;
370
  friend class mozilla::RemoteSourceStreamInfo;
371
372
  class PrincipalHandleListener;
373
374
public:
375
  /**
376
   * aTrackID is the MediaStreamGraph track ID for the track in the
377
   * MediaStream owned by aStream.
378
   */
379
  MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
380
      TrackID aInputTrackID,
381
      MediaStreamTrackSource* aSource,
382
      const MediaTrackConstraints& aConstraints = MediaTrackConstraints());
383
384
  NS_DECL_ISUPPORTS_INHERITED
385
  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamTrack,
386
                                           DOMEventTargetHelper)
387
388
  nsPIDOMWindowInner* GetParentObject() const;
389
  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override = 0;
390
391
0
  virtual AudioStreamTrack* AsAudioStreamTrack() { return nullptr; }
392
0
  virtual VideoStreamTrack* AsVideoStreamTrack() { return nullptr; }
393
394
0
  virtual const AudioStreamTrack* AsAudioStreamTrack() const { return nullptr; }
395
0
  virtual const VideoStreamTrack* AsVideoStreamTrack() const { return nullptr; }
396
397
  // WebIDL
398
  virtual void GetKind(nsAString& aKind) = 0;
399
  void GetId(nsAString& aID) const;
400
0
  virtual void GetLabel(nsAString& aLabel, CallerType /* aCallerType */) { GetSource().GetLabel(aLabel); }
401
0
  bool Enabled() const override { return mEnabled; }
402
  void SetEnabled(bool aEnabled);
403
0
  bool Muted() { return mMuted; }
404
  void Stop();
405
  void GetConstraints(dom::MediaTrackConstraints& aResult);
406
  void GetSettings(dom::MediaTrackSettings& aResult, CallerType aCallerType);
407
408
  already_AddRefed<Promise>
409
  ApplyConstraints(const dom::MediaTrackConstraints& aConstraints,
410
                   CallerType aCallerType, ErrorResult &aRv);
411
  already_AddRefed<MediaStreamTrack> Clone();
412
  MediaStreamTrackState ReadyState() { return mReadyState; }
413
414
  IMPL_EVENT_HANDLER(mute)
415
  IMPL_EVENT_HANDLER(unmute)
416
  IMPL_EVENT_HANDLER(ended)
417
418
  /**
419
   * Convenience (and legacy) method for when ready state is "ended".
420
   */
421
0
  bool Ended() const { return mReadyState == MediaStreamTrackState::Ended; }
422
423
  /**
424
   * Forces the ready state to a particular value, for instance when we're
425
   * cloning an already ended track.
426
   */
427
  void SetReadyState(MediaStreamTrackState aState);
428
429
  /**
430
   * Notified by the MediaStreamGraph, through our owning MediaStream on the
431
   * main thread.
432
   *
433
   * Note that this sets the track to ended and raises the "ended" event
434
   * synchronously.
435
   */
436
  void OverrideEnded();
437
438
  /**
439
   * Get this track's principal.
440
   */
441
0
  nsIPrincipal* GetPrincipal() const { return mPrincipal; }
442
443
  /**
444
   * Called by the PrincipalHandleListener when this track's PrincipalHandle changes on
445
   * the MediaStreamGraph thread. When the PrincipalHandle matches the pending
446
   * principal we know that the principal change has propagated to consumers.
447
   */
448
  void NotifyPrincipalHandleChanged(const PrincipalHandle& aPrincipalHandle);
449
450
  /**
451
   * Called when this track's readyState transitions to "ended".
452
   * Notifies all MediaStreamTrackConsumers that this track ended.
453
   */
454
  void NotifyEnded();
455
456
  /**
457
   * Get this track's CORS mode.
458
   */
459
0
  CORSMode GetCORSMode() const { return GetSource().GetCORSMode(); }
460
461
  /**
462
   * Get this track's PeerIdentity.
463
   */
464
0
  const PeerIdentity* GetPeerIdentity() const { return GetSource().GetPeerIdentity(); }
465
466
  MediaStreamGraph* Graph();
467
  MediaStreamGraphImpl* GraphImpl();
468
469
  MediaStreamTrackSource& GetSource() const
470
0
  {
471
0
    MOZ_RELEASE_ASSERT(mSource, "The track source is only removed on destruction");
472
0
    return *mSource;
473
0
  }
474
475
  // Webrtc allows the remote side to name tracks whatever it wants, and we
476
  // need to surface this to content.
477
  void AssignId(const nsAString& aID) { mID = aID; }
478
479
  // Implementation of MediaStreamTrackSource::Sink
480
481
  /**
482
   * Keep the track source alive. This track and any clones are controlling the
483
   * lifetime of the source by being registered as its sinks.
484
   */
485
  bool KeepsSourceAlive() const override
486
0
  {
487
0
    return true;
488
0
  }
489
490
  void PrincipalChanged() override;
491
492
  /**
493
   * 4.3.1 Life-cycle and Media flow - Media flow
494
   * To set a track's muted state to newState, the User Agent MUST run the
495
   * following steps:
496
   *  1. Let track be the MediaStreamTrack in question.
497
   *  2. Set track's muted attribute to newState.
498
   *  3. If newState is true let eventName be mute, otherwise unmute.
499
   *  4. Fire a simple event named eventName on track.
500
   */
501
  void MutedChanged(bool aNewState) override;
502
503
  /**
504
   * Add a PrincipalChangeObserver to this track.
505
   *
506
   * Returns true if it was successfully added.
507
   *
508
   * Ownership of the PrincipalChangeObserver remains with the caller, and it's
509
   * the caller's responsibility to remove the observer before it dies.
510
   */
511
  bool AddPrincipalChangeObserver(PrincipalChangeObserver<MediaStreamTrack>* aObserver);
512
513
  /**
514
   * Remove an added PrincipalChangeObserver from this track.
515
   *
516
   * Returns true if it was successfully removed.
517
   */
518
  bool RemovePrincipalChangeObserver(PrincipalChangeObserver<MediaStreamTrack>* aObserver);
519
520
  /**
521
   * Add a MediaStreamTrackConsumer to this track.
522
   *
523
   * Adding the same consumer multiple times is prohibited.
524
   */
525
  void AddConsumer(MediaStreamTrackConsumer* aConsumer);
526
527
  /**
528
   * Remove an added MediaStreamTrackConsumer from this track.
529
   */
530
  void RemoveConsumer(MediaStreamTrackConsumer* aConsumer);
531
532
  /**
533
   * Adds a MediaStreamTrackListener to the MediaStreamGraph representation of
534
   * this track.
535
   */
536
  virtual void AddListener(MediaStreamTrackListener* aListener);
537
538
  /**
539
   * Removes a MediaStreamTrackListener from the MediaStreamGraph representation
540
   * of this track.
541
   */
542
  void RemoveListener(MediaStreamTrackListener* aListener);
543
544
  /**
545
   * Attempts to add a direct track listener to this track.
546
   * Callers must listen to the NotifyInstalled event to know if installing
547
   * the listener succeeded (tracks originating from SourceMediaStreams) or
548
   * failed (e.g., WebAudio originated tracks).
549
   */
550
  virtual void AddDirectListener(DirectMediaStreamTrackListener *aListener);
551
  void RemoveDirectListener(DirectMediaStreamTrackListener  *aListener);
552
553
  /**
554
   * Sets up a MediaInputPort from the underlying track that this
555
   * MediaStreamTrack represents, to aStream, and returns it.
556
   */
557
  already_AddRefed<MediaInputPort> ForwardTrackContentsTo(ProcessedMediaStream* aStream,
558
                                                          TrackID aDestinationTrackID = TRACK_ANY);
559
560
  /**
561
   * Returns true if this track is connected to aPort and forwarded to aPort's
562
   * output stream.
563
   */
564
  bool IsForwardedThrough(MediaInputPort* aPort);
565
566
  void SetMediaStreamSizeListener(DirectMediaStreamTrackListener* aListener);
567
568
  // Returns the original DOMMediaStream's underlying input stream.
569
  MediaStream* GetInputStream();
570
571
  TrackID GetInputTrackId() const
572
0
  {
573
0
    return mInputTrackID;
574
0
  }
575
576
protected:
577
  virtual ~MediaStreamTrack();
578
579
  /**
580
   * Sets this track's muted state without raising any events.
581
   */
582
  void SetMuted(bool aMuted) { mMuted = aMuted; }
583
584
  void Destroy();
585
586
  // Returns the owning DOMMediaStream's underlying owned stream.
587
  ProcessedMediaStream* GetOwnedStream();
588
589
  // Returns the original DOMMediaStream. If this track is a clone,
590
  // the original track's owning DOMMediaStream is returned.
591
  DOMMediaStream* GetInputDOMStream();
592
593
  /**
594
   * Sets the principal and notifies PrincipalChangeObservers if it changes.
595
   */
596
  void SetPrincipal(nsIPrincipal* aPrincipal);
597
598
  /**
599
   * Creates a new MediaStreamTrack with the same type, input track ID and
600
   * source as this MediaStreamTrack.
601
   * aTrackID is the TrackID the new track will have in its owned stream.
602
   */
603
  virtual already_AddRefed<MediaStreamTrack> CloneInternal(DOMMediaStream* aOwningStream,
604
                                                           TrackID aTrackID) = 0;
605
606
  nsTArray<PrincipalChangeObserver<MediaStreamTrack>*> mPrincipalChangeObservers;
607
608
  nsTArray<WeakPtr<MediaStreamTrackConsumer>> mConsumers;
609
610
  RefPtr<DOMMediaStream> mOwningStream;
611
  TrackID mTrackID;
612
  TrackID mInputTrackID;
613
  RefPtr<MediaStreamTrackSource> mSource;
614
  RefPtr<MediaStreamTrack> mOriginalTrack;
615
  nsCOMPtr<nsIPrincipal> mPrincipal;
616
  nsCOMPtr<nsIPrincipal> mPendingPrincipal;
617
  RefPtr<PrincipalHandleListener> mPrincipalHandleListener;
618
  // Keep tracking MediaStreamTrackListener and DirectMediaStreamTrackListener,
619
  // so we can remove them in |Destory|.
620
  nsTArray<RefPtr<MediaStreamTrackListener>> mTrackListeners;
621
  nsTArray<RefPtr<DirectMediaStreamTrackListener>> mDirectTrackListeners;
622
  nsString mID;
623
  MediaStreamTrackState mReadyState;
624
  bool mEnabled;
625
  bool mMuted;
626
  dom::MediaTrackConstraints mConstraints;
627
};
628
629
} // namespace dom
630
} // namespace mozilla
631
632
#endif /* MEDIASTREAMTRACK_H_ */