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 "mozilla/DOMEventTargetHelper.h"
10 #include "nsError.h"
11 #include "nsID.h"
12 #include "nsIPrincipal.h"
13 #include "StreamTracks.h"
14 #include "MediaTrackConstraints.h"
15 #include "mozilla/CORSMode.h"
16 #include "PrincipalChangeObserver.h"
17 #include "mozilla/dom/MediaStreamTrackBinding.h"
18 #include "mozilla/dom/MediaTrackSettingsBinding.h"
19 #include "mozilla/media/MediaUtils.h"
20 
21 namespace mozilla {
22 
23 class DOMMediaStream;
24 class MediaEnginePhotoCallback;
25 class MediaInputPort;
26 class MediaStream;
27 class MediaStreamGraph;
28 class MediaStreamGraphImpl;
29 class MediaStreamTrackListener;
30 class DirectMediaStreamTrackListener;
31 class PeerConnectionImpl;
32 class PeerConnectionMedia;
33 class PeerIdentity;
34 class ProcessedMediaStream;
35 class RemoteSourceStreamInfo;
36 class SourceStreamInfo;
37 
38 namespace dom {
39 
40 class AudioStreamTrack;
41 class VideoStreamTrack;
42 class MediaStreamError;
43 
44 /**
45  * Common interface through which a MediaStreamTrack can communicate with its
46  * producer on the main thread.
47  *
48  * Kept alive by a strong ref in all MediaStreamTracks (original and clones)
49  * sharing this source.
50  */
51 class MediaStreamTrackSource : public nsISupports
52 {
53   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(MediaStreamTrackSource)54   NS_DECL_CYCLE_COLLECTION_CLASS(MediaStreamTrackSource)
55 
56 public:
57   class Sink
58   {
59   public:
60     virtual void PrincipalChanged() = 0;
61   };
62 
MediaStreamTrackSource(nsIPrincipal * aPrincipal,const nsString & aLabel)63   MediaStreamTrackSource(nsIPrincipal* aPrincipal,
64                          const nsString& aLabel)
65     : mPrincipal(aPrincipal),
66       mLabel(aLabel),
67       mStopped(false)
68   {
69     MOZ_COUNT_CTOR(MediaStreamTrackSource);
70   }
71 
72   /**
73    * Use to clean up any resources that have to be cleaned before the
74    * destructor is called. It is often too late in the destructor because
75    * of garbage collection having removed the members already.
76    */
Destroy()77   virtual void Destroy() {}
78 
79   /**
80    * Gets the source's MediaSourceEnum for usage by PeerConnections.
81    */
82   virtual MediaSourceEnum GetMediaSource() const = 0;
83 
84   /**
85    * Get this TrackSource's principal.
86    */
GetPrincipal()87   nsIPrincipal* GetPrincipal() const { return mPrincipal; }
88 
89   /**
90    * Get the source's current CORSMode. If not applicable CORS_NONE is returned.
91    * The sink will be notified of changes to our CORSMode through
92    * PrincipalChanged().
93    */
GetCORSMode()94   virtual CORSMode GetCORSMode() const { return CORS_NONE; }
95 
96   /**
97    * This is used in WebRTC. A peerIdentity constrained MediaStreamTrack cannot
98    * be sent across the network to anything other than a peer with the provided
99    * identity. If this is set, then GetPrincipal() should return an instance of
100    * nsNullPrincipal.
101    *
102    * A track's PeerIdentity is immutable and will not change during the track's
103    * lifetime.
104    */
GetPeerIdentity()105   virtual const PeerIdentity* GetPeerIdentity() const { return nullptr; }
106 
107   /**
108    * MediaStreamTrack::GetLabel (see spec) calls through to here.
109    */
GetLabel(nsAString & aLabel)110   void GetLabel(nsAString& aLabel) { aLabel.Assign(mLabel); }
111 
112   /**
113    * Forwards a photo request to backends that support it. Other backends return
114    * NS_ERROR_NOT_IMPLEMENTED to indicate that a MediaStreamGraph-based fallback
115    * should be used.
116    */
TakePhoto(MediaEnginePhotoCallback *)117   virtual nsresult TakePhoto(MediaEnginePhotoCallback*) const { return NS_ERROR_NOT_IMPLEMENTED; }
118 
119   typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
120 
121   /**
122    * We provide a fallback solution to ApplyConstraints() here.
123    * Sources that support ApplyConstraints() will have to override it.
124    */
125   virtual already_AddRefed<PledgeVoid>
126   ApplyConstraints(nsPIDOMWindowInner* aWindow,
127                    const dom::MediaTrackConstraints& aConstraints);
128 
129   /**
130    * Same for GetSettings (no-op).
131    */
132   virtual void
GetSettings(dom::MediaTrackSettings & aResult)133   GetSettings(dom::MediaTrackSettings& aResult) {};
134 
135   /**
136    * Called by the source interface when all registered sinks have unregistered.
137    */
138   virtual void Stop() = 0;
139 
140   /**
141    * Called by each MediaStreamTrack clone on initialization.
142    */
RegisterSink(Sink * aSink)143   void RegisterSink(Sink* aSink)
144   {
145     MOZ_ASSERT(NS_IsMainThread());
146     if (mStopped) {
147       return;
148     }
149     mSinks.AppendElement(aSink);
150   }
151 
152   /**
153    * Called by each MediaStreamTrack clone on Stop() if supported by the
154    * source (us) or destruction.
155    */
UnregisterSink(Sink * aSink)156   void UnregisterSink(Sink* aSink)
157   {
158     MOZ_ASSERT(NS_IsMainThread());
159     if (mSinks.RemoveElement(aSink) && mSinks.IsEmpty()) {
160       MOZ_ASSERT(!mStopped);
161       Stop();
162       mStopped = true;
163     }
164   }
165 
166 protected:
~MediaStreamTrackSource()167   virtual ~MediaStreamTrackSource()
168   {
169     MOZ_COUNT_DTOR(MediaStreamTrackSource);
170   }
171 
172   /**
173    * Called by a sub class when the principal has changed.
174    * Notifies all sinks.
175    */
PrincipalChanged()176   void PrincipalChanged()
177   {
178     for (Sink* sink : mSinks) {
179       sink->PrincipalChanged();
180     }
181   }
182 
183   // Principal identifying who may access the contents of this source.
184   nsCOMPtr<nsIPrincipal> mPrincipal;
185 
186   // Currently registered sinks.
187   nsTArray<Sink*> mSinks;
188 
189   // The label of the track we are the source of per the MediaStreamTrack spec.
190   const nsString mLabel;
191 
192   // True if all MediaStreamTrack users have unregistered from this source and
193   // Stop() has been called.
194   bool mStopped;
195 };
196 
197 /**
198  * Basic implementation of MediaStreamTrackSource that doesn't forward Stop().
199  */
200 class BasicTrackSource : public MediaStreamTrackSource
201 {
202 public:
203   explicit BasicTrackSource(nsIPrincipal* aPrincipal,
204                             const MediaSourceEnum aMediaSource =
205                             MediaSourceEnum::Other)
MediaStreamTrackSource(aPrincipal,nsString ())206     : MediaStreamTrackSource(aPrincipal, nsString())
207     , mMediaSource(aMediaSource)
208   {}
209 
GetMediaSource()210   MediaSourceEnum GetMediaSource() const override { return mMediaSource; }
211 
Stop()212   void Stop() override {}
213 
214 protected:
~BasicTrackSource()215   ~BasicTrackSource() {}
216 
217   const MediaSourceEnum mMediaSource;
218 };
219 
220 /**
221  * Base class that consumers of a MediaStreamTrack can use to get notifications
222  * about state changes in the track.
223  */
224 class MediaStreamTrackConsumer : public nsISupports
225 {
226 public:
227   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(MediaStreamTrackConsumer)228   NS_DECL_CYCLE_COLLECTION_CLASS(MediaStreamTrackConsumer)
229 
230   /**
231    * Called when the track's readyState transitions to "ended".
232    * Unlike the "ended" event exposed to script this is called for any reason,
233    * including MediaStreamTrack::Stop().
234    */
235   virtual void NotifyEnded(MediaStreamTrack* aTrack) {};
236 
237 protected:
~MediaStreamTrackConsumer()238   virtual ~MediaStreamTrackConsumer() {}
239 };
240 
241 /**
242  * Class representing a track in a DOMMediaStream.
243  */
244 class MediaStreamTrack : public DOMEventTargetHelper,
245                          public MediaStreamTrackSource::Sink
246 {
247   // DOMMediaStream owns MediaStreamTrack instances, and requires access to
248   // some internal state, e.g., GetInputStream(), GetOwnedStream().
249   friend class mozilla::DOMMediaStream;
250 
251   // PeerConnection and friends need to know our owning DOMStream and track id.
252   friend class mozilla::PeerConnectionImpl;
253   friend class mozilla::PeerConnectionMedia;
254   friend class mozilla::SourceStreamInfo;
255   friend class mozilla::RemoteSourceStreamInfo;
256 
257   class PrincipalHandleListener;
258 
259 public:
260   /**
261    * aTrackID is the MediaStreamGraph track ID for the track in the
262    * MediaStream owned by aStream.
263    */
264   MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID,
265       TrackID aInputTrackID,
266       MediaStreamTrackSource* aSource,
267       const MediaTrackConstraints& aConstraints = MediaTrackConstraints());
268 
269   NS_DECL_ISUPPORTS_INHERITED
270   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamTrack,
271                                            DOMEventTargetHelper)
272 
273   nsPIDOMWindowInner* GetParentObject() const;
274   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override = 0;
275 
AsAudioStreamTrack()276   virtual AudioStreamTrack* AsAudioStreamTrack() { return nullptr; }
AsVideoStreamTrack()277   virtual VideoStreamTrack* AsVideoStreamTrack() { return nullptr; }
278 
AsAudioStreamTrack()279   virtual const AudioStreamTrack* AsAudioStreamTrack() const { return nullptr; }
AsVideoStreamTrack()280   virtual const VideoStreamTrack* AsVideoStreamTrack() const { return nullptr; }
281 
282   // WebIDL
283   virtual void GetKind(nsAString& aKind) = 0;
284   void GetId(nsAString& aID) const;
GetLabel(nsAString & aLabel)285   void GetLabel(nsAString& aLabel) { GetSource().GetLabel(aLabel); }
Enabled()286   bool Enabled() { return mEnabled; }
287   void SetEnabled(bool aEnabled);
288   void Stop();
289   void GetConstraints(dom::MediaTrackConstraints& aResult);
290   void GetSettings(dom::MediaTrackSettings& aResult);
291 
292   already_AddRefed<Promise>
293   ApplyConstraints(const dom::MediaTrackConstraints& aConstraints, ErrorResult &aRv);
294   already_AddRefed<MediaStreamTrack> Clone();
ReadyState()295   MediaStreamTrackState ReadyState() { return mReadyState; }
296 
IMPL_EVENT_HANDLER(ended)297   IMPL_EVENT_HANDLER(ended)
298 
299   /**
300    * Convenience (and legacy) method for when ready state is "ended".
301    */
302   bool Ended() const { return mReadyState == MediaStreamTrackState::Ended; }
303 
304   /**
305    * Forces the ready state to a particular value, for instance when we're
306    * cloning an already ended track.
307    */
308   void SetReadyState(MediaStreamTrackState aState);
309 
310   /**
311    * Notified by the MediaStreamGraph, through our owning MediaStream on the
312    * main thread.
313    *
314    * Note that this sets the track to ended and raises the "ended" event
315    * synchronously.
316    */
317   void OverrideEnded();
318 
319   /**
320    * Get this track's principal.
321    */
GetPrincipal()322   nsIPrincipal* GetPrincipal() const { return mPrincipal; }
323 
324   /**
325    * Called by the PrincipalHandleListener when this track's PrincipalHandle changes on
326    * the MediaStreamGraph thread. When the PrincipalHandle matches the pending
327    * principal we know that the principal change has propagated to consumers.
328    */
329   void NotifyPrincipalHandleChanged(const PrincipalHandle& aPrincipalHandle);
330 
331   /**
332    * Called when this track's readyState transitions to "ended".
333    * Notifies all MediaStreamTrackConsumers that this track ended.
334    */
335   void NotifyEnded();
336 
337   /**
338    * Get this track's CORS mode.
339    */
GetCORSMode()340   CORSMode GetCORSMode() const { return GetSource().GetCORSMode(); }
341 
342   /**
343    * Get this track's PeerIdentity.
344    */
GetPeerIdentity()345   const PeerIdentity* GetPeerIdentity() const { return GetSource().GetPeerIdentity(); }
346 
347   MediaStreamGraph* Graph();
348   MediaStreamGraphImpl* GraphImpl();
349 
GetSource()350   MediaStreamTrackSource& GetSource() const
351   {
352     MOZ_RELEASE_ASSERT(mSource, "The track source is only removed on destruction");
353     return *mSource;
354   }
355 
356   // Webrtc allows the remote side to name tracks whatever it wants, and we
357   // need to surface this to content.
AssignId(const nsAString & aID)358   void AssignId(const nsAString& aID) { mID = aID; }
359 
360   // Implementation of MediaStreamTrackSource::Sink
361   void PrincipalChanged() override;
362 
363   /**
364    * Add a PrincipalChangeObserver to this track.
365    *
366    * Returns true if it was successfully added.
367    *
368    * Ownership of the PrincipalChangeObserver remains with the caller, and it's
369    * the caller's responsibility to remove the observer before it dies.
370    */
371   bool AddPrincipalChangeObserver(PrincipalChangeObserver<MediaStreamTrack>* aObserver);
372 
373   /**
374    * Remove an added PrincipalChangeObserver from this track.
375    *
376    * Returns true if it was successfully removed.
377    */
378   bool RemovePrincipalChangeObserver(PrincipalChangeObserver<MediaStreamTrack>* aObserver);
379 
380   /**
381    * Add a MediaStreamTrackConsumer to this track.
382    *
383    * Adding the same consumer multiple times is prohibited.
384    */
385   void AddConsumer(MediaStreamTrackConsumer* aConsumer);
386 
387   /**
388    * Remove an added MediaStreamTrackConsumer from this track.
389    */
390   void RemoveConsumer(MediaStreamTrackConsumer* aConsumer);
391 
392   /**
393    * Adds a MediaStreamTrackListener to the MediaStreamGraph representation of
394    * this track.
395    */
396   void AddListener(MediaStreamTrackListener* aListener);
397 
398   /**
399    * Removes a MediaStreamTrackListener from the MediaStreamGraph representation
400    * of this track.
401    */
402   void RemoveListener(MediaStreamTrackListener* aListener);
403 
404   /**
405    * Attempts to add a direct track listener to this track.
406    * Callers must listen to the NotifyInstalled event to know if installing
407    * the listener succeeded (tracks originating from SourceMediaStreams) or
408    * failed (e.g., WebAudio originated tracks).
409    */
410   void AddDirectListener(DirectMediaStreamTrackListener *aListener);
411   void RemoveDirectListener(DirectMediaStreamTrackListener  *aListener);
412 
413   /**
414    * Sets up a MediaInputPort from the underlying track that this
415    * MediaStreamTrack represents, to aStream, and returns it.
416    */
417   already_AddRefed<MediaInputPort> ForwardTrackContentsTo(ProcessedMediaStream* aStream,
418                                                           TrackID aDestinationTrackID = TRACK_ANY);
419 
420   /**
421    * Returns true if this track is connected to aPort and forwarded to aPort's
422    * output stream.
423    */
424   bool IsForwardedThrough(MediaInputPort* aPort);
425 
426   void SetMediaStreamSizeListener(DirectMediaStreamTrackListener* aListener);
427 
428 protected:
429   virtual ~MediaStreamTrack();
430 
431   void Destroy();
432 
433   // Returns the original DOMMediaStream's underlying input stream.
434   MediaStream* GetInputStream();
435 
436   // Returns the owning DOMMediaStream's underlying owned stream.
437   ProcessedMediaStream* GetOwnedStream();
438 
439   // Returns the original DOMMediaStream. If this track is a clone,
440   // the original track's owning DOMMediaStream is returned.
441   DOMMediaStream* GetInputDOMStream();
442 
443   /**
444    * Sets the principal and notifies PrincipalChangeObservers if it changes.
445    */
446   void SetPrincipal(nsIPrincipal* aPrincipal);
447 
448   /**
449    * Creates a new MediaStreamTrack with the same type, input track ID and
450    * source as this MediaStreamTrack.
451    * aTrackID is the TrackID the new track will have in its owned stream.
452    */
453   virtual already_AddRefed<MediaStreamTrack> CloneInternal(DOMMediaStream* aOwningStream,
454                                                            TrackID aTrackID) = 0;
455 
456   nsTArray<PrincipalChangeObserver<MediaStreamTrack>*> mPrincipalChangeObservers;
457 
458   nsTArray<RefPtr<MediaStreamTrackConsumer>> mConsumers;
459 
460   RefPtr<DOMMediaStream> mOwningStream;
461   TrackID mTrackID;
462   TrackID mInputTrackID;
463   RefPtr<MediaStreamTrackSource> mSource;
464   RefPtr<MediaStreamTrack> mOriginalTrack;
465   nsCOMPtr<nsIPrincipal> mPrincipal;
466   nsCOMPtr<nsIPrincipal> mPendingPrincipal;
467   RefPtr<PrincipalHandleListener> mPrincipalHandleListener;
468   // Keep tracking MediaStreamTrackListener and DirectMediaStreamTrackListener,
469   // so we can remove them in |Destory|.
470   nsTArray<RefPtr<MediaStreamTrackListener>> mTrackListeners;
471   nsTArray<RefPtr<DirectMediaStreamTrackListener>> mDirectTrackListeners;
472   nsString mID;
473   MediaStreamTrackState mReadyState;
474   bool mEnabled;
475   dom::MediaTrackConstraints mConstraints;
476 };
477 
478 } // namespace dom
479 } // namespace mozilla
480 
481 #endif /* MEDIASTREAMTRACK_H_ */
482