summaryrefslogtreecommitdiff
path: root/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
blob: 8908e51546fda9c53ac4efa9bb5640ad4c4ec90e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef _PEER_CONNECTION_MEDIA_H_
#define _PEER_CONNECTION_MEDIA_H_

#include <string>
#include <vector>
#include <map>

#include "nspr.h"
#include "prlock.h"

#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "nsComponentManagerUtils.h"
#include "nsIProtocolProxyCallback.h"

#include "signaling/src/jsep/JsepSession.h"
#include "AudioSegment.h"

#if !defined(MOZILLA_EXTERNAL_LINKAGE)
#include "Layers.h"
#include "VideoUtils.h"
#include "ImageLayers.h"
#include "VideoSegment.h"
#include "MediaStreamTrack.h"
#endif

class nsIPrincipal;

namespace mozilla {
class DataChannel;
class PeerIdentity;
class MediaPipelineFactory;
namespace dom {
struct RTCInboundRTPStreamStats;
struct RTCOutboundRTPStreamStats;
}
}

#include "nricectxhandler.h"
#include "nriceresolver.h"
#include "nricemediastream.h"
#include "MediaPipeline.h"

namespace mozilla {

class PeerConnectionImpl;
class PeerConnectionMedia;
class PCUuidGenerator;

class SourceStreamInfo {
public:
  SourceStreamInfo(DOMMediaStream* aMediaStream,
                   PeerConnectionMedia *aParent,
                   const std::string& aId)
      : mMediaStream(aMediaStream),
        mParent(aParent),
        mId(aId) {
    MOZ_ASSERT(mMediaStream);
  }

  SourceStreamInfo(already_AddRefed<DOMMediaStream>& aMediaStream,
                   PeerConnectionMedia *aParent,
                   const std::string& aId)
      : mMediaStream(aMediaStream),
        mParent(aParent),
        mId(aId) {
    MOZ_ASSERT(mMediaStream);
  }

  virtual ~SourceStreamInfo() {}

  DOMMediaStream* GetMediaStream() const {
    return mMediaStream;
  }

  nsresult StorePipeline(const std::string& trackId,
                         const RefPtr<MediaPipeline>& aPipeline);

  virtual void AddTrack(const std::string& trackId,
                        const RefPtr<dom::MediaStreamTrack>& aTrack)
  {
    mTracks.insert(std::make_pair(trackId, aTrack));
  }
  virtual void RemoveTrack(const std::string& trackId);
  bool HasTrack(const std::string& trackId) const
  {
    return !!mTracks.count(trackId);
  }
  size_t GetTrackCount() const { return mTracks.size(); }

  // This method exists for stats and the unittests.
  // It allows visibility into the pipelines and flows.
  const std::map<std::string, RefPtr<MediaPipeline>>&
  GetPipelines() const { return mPipelines; }
  RefPtr<MediaPipeline> GetPipelineByTrackId_m(const std::string& trackId);
  // This is needed so PeerConnectionImpl can unregister itself as
  // PrincipalChangeObserver from each track.
  const std::map<std::string, RefPtr<dom::MediaStreamTrack>>&
  GetMediaStreamTracks() const { return mTracks; }
  dom::MediaStreamTrack* GetTrackById(const std::string& trackId) const
  {
    auto it = mTracks.find(trackId);
    if (it == mTracks.end()) {
      return nullptr;
    }

    return it->second;
  }
  const std::string& GetId() const { return mId; }

  void DetachTransport_s();
  virtual void DetachMedia_m();
  bool AnyCodecHasPluginID(uint64_t aPluginID);
protected:
  void EndTrack(MediaStream* stream, dom::MediaStreamTrack* track);
  RefPtr<DOMMediaStream> mMediaStream;
  PeerConnectionMedia *mParent;
  const std::string mId;
  // These get set up before we generate our local description, the pipelines
  // and conduits are set up once offer/answer completes.
  std::map<std::string, RefPtr<dom::MediaStreamTrack>> mTracks;
  std::map<std::string, RefPtr<MediaPipeline>> mPipelines;
};

// TODO(ekr@rtfm.com): Refactor {Local,Remote}SourceStreamInfo
// bug 837539.
class LocalSourceStreamInfo : public SourceStreamInfo {
  ~LocalSourceStreamInfo() {
    mMediaStream = nullptr;
  }
public:
  LocalSourceStreamInfo(DOMMediaStream *aMediaStream,
                        PeerConnectionMedia *aParent,
                        const std::string& aId)
     : SourceStreamInfo(aMediaStream, aParent, aId) {}

  nsresult TakePipelineFrom(RefPtr<LocalSourceStreamInfo>& info,
                            const std::string& oldTrackId,
                            dom::MediaStreamTrack& aNewTrack,
                            const std::string& newTrackId);

#if !defined(MOZILLA_EXTERNAL_LINKAGE)
  void UpdateSinkIdentity_m(dom::MediaStreamTrack* aTrack,
                            nsIPrincipal* aPrincipal,
                            const PeerIdentity* aSinkIdentity);
#endif

  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LocalSourceStreamInfo)

private:
  already_AddRefed<MediaPipeline> ForgetPipelineByTrackId_m(
      const std::string& trackId);
};

#if !defined(MOZILLA_EXTERNAL_LINKAGE)
class RemoteTrackSource : public dom::MediaStreamTrackSource
{
public:
  explicit RemoteTrackSource(nsIPrincipal* aPrincipal, const nsString& aLabel)
    : dom::MediaStreamTrackSource(aPrincipal, aLabel) {}

  dom::MediaSourceEnum GetMediaSource() const override
  {
    return dom::MediaSourceEnum::Other;
  }

  already_AddRefed<PledgeVoid>
  ApplyConstraints(nsPIDOMWindowInner* aWindow,
                   const dom::MediaTrackConstraints& aConstraints) override;

  void Stop() override
  {
    // XXX (Bug 1314270): Implement rejection logic if necessary when we have
    //                    clarity in the spec.
  }

  void SetPrincipal(nsIPrincipal* aPrincipal)
  {
    mPrincipal = aPrincipal;
    PrincipalChanged();
  }

protected:
  virtual ~RemoteTrackSource() {}
};
#endif

class RemoteSourceStreamInfo : public SourceStreamInfo {
  ~RemoteSourceStreamInfo() {}
 public:
  RemoteSourceStreamInfo(already_AddRefed<DOMMediaStream> aMediaStream,
                         PeerConnectionMedia *aParent,
                         const std::string& aId)
    : SourceStreamInfo(aMediaStream, aParent, aId),
      mReceiving(false)
  {
  }

  void DetachMedia_m() override;
  void RemoveTrack(const std::string& trackId) override;
  void SyncPipeline(RefPtr<MediaPipelineReceive> aPipeline);

#if !defined(MOZILLA_EXTERNAL_LINKAGE)
  void UpdatePrincipal_m(nsIPrincipal* aPrincipal);
#endif

  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RemoteSourceStreamInfo)

  void AddTrack(const std::string& trackId,
                const RefPtr<dom::MediaStreamTrack>& aTrack) override
  {
    SourceStreamInfo::AddTrack(trackId, aTrack);
  }

  TrackID GetNumericTrackId(const std::string& trackId) const
  {
    dom::MediaStreamTrack* track = GetTrackById(trackId);
    if (!track) {
      return TRACK_INVALID;
    }
    return track->mTrackID;
  }

  void StartReceiving();

 private:
  // True iff SetPullEnabled(true) has been called on the DOMMediaStream. This
  // happens when offer/answer concludes.
  bool mReceiving;
};

class PeerConnectionMedia : public sigslot::has_slots<> {
  ~PeerConnectionMedia()
  {
    MOZ_RELEASE_ASSERT(!mMainThread);
  }

 public:
  explicit PeerConnectionMedia(PeerConnectionImpl *parent);

  enum IceRestartState { ICE_RESTART_NONE,
                         ICE_RESTART_PROVISIONAL,
                         ICE_RESTART_COMMITTED
  };

  PeerConnectionImpl* GetPC() { return mParent; }
  nsresult Init(const std::vector<NrIceStunServer>& stun_servers,
                const std::vector<NrIceTurnServer>& turn_servers,
                NrIceCtx::Policy policy);
  // WARNING: This destroys the object!
  void SelfDestruct();

  RefPtr<NrIceCtxHandler> ice_ctx_hdlr() const { return mIceCtxHdlr; }
  RefPtr<NrIceCtx> ice_ctx() const { return mIceCtxHdlr->ctx(); }

  RefPtr<NrIceMediaStream> ice_media_stream(size_t i) const {
    return mIceCtxHdlr->ctx()->GetStream(i);
  }

  size_t num_ice_media_streams() const {
    return mIceCtxHdlr->ctx()->GetStreamCount();
  }

  // Ensure ICE transports exist that we might need when offer/answer concludes
  void EnsureTransports(const JsepSession& aSession);

  // Activate or remove ICE transports at the conclusion of offer/answer,
  // or when rollback occurs.
  void ActivateOrRemoveTransports(const JsepSession& aSession,
                                  const bool forceIceTcp);

  // Start ICE checks.
  void StartIceChecks(const JsepSession& session);

  bool IsIceRestarting() const;
  IceRestartState GetIceRestartState() const;

  // Begin ICE restart
  void BeginIceRestart(const std::string& ufrag,
                       const std::string& pwd);
  // Commit ICE Restart - offer/answer complete, no rollback possible
  void CommitIceRestart();
  // Finalize ICE restart
  void FinalizeIceRestart();
  // Abort ICE restart
  void RollbackIceRestart();

  // Process a trickle ICE candidate.
  void AddIceCandidate(const std::string& candidate, const std::string& mid,
                       uint32_t aMLine);

  // Handle complete media pipelines.
  nsresult UpdateMediaPipelines(const JsepSession& session);

  // Add a track (main thread only)
  nsresult AddTrack(DOMMediaStream& aMediaStream,
                    const std::string& streamId,
                    dom::MediaStreamTrack& aTrack,
                    const std::string& trackId);

  nsresult RemoveLocalTrack(const std::string& streamId,
                            const std::string& trackId);
  nsresult RemoveRemoteTrack(const std::string& streamId,
                            const std::string& trackId);

  // Get a specific local stream
  uint32_t LocalStreamsLength()
  {
    return mLocalSourceStreams.Length();
  }
  LocalSourceStreamInfo* GetLocalStreamByIndex(int index);
  LocalSourceStreamInfo* GetLocalStreamById(const std::string& id);
  LocalSourceStreamInfo* GetLocalStreamByTrackId(const std::string& id);

  // Get a specific remote stream
  uint32_t RemoteStreamsLength()
  {
    return mRemoteSourceStreams.Length();
  }

  RemoteSourceStreamInfo* GetRemoteStreamByIndex(size_t index);
  RemoteSourceStreamInfo* GetRemoteStreamById(const std::string& id);
  RemoteSourceStreamInfo* GetRemoteStreamByTrackId(const std::string& id);

  // Add a remote stream.
  nsresult AddRemoteStream(RefPtr<RemoteSourceStreamInfo> aInfo);

  nsresult ReplaceTrack(const std::string& aOldStreamId,
                        const std::string& aOldTrackId,
                        dom::MediaStreamTrack& aNewTrack,
                        const std::string& aNewStreamId,
                        const std::string& aNewTrackId);

#if !defined(MOZILLA_EXTERNAL_LINKAGE)
  // In cases where the peer isn't yet identified, we disable the pipeline (not
  // the stream, that would potentially affect others), so that it sends
  // black/silence.  Once the peer is identified, re-enable those streams.
  // aTrack will be set if this update came from a principal change on aTrack.
  void UpdateSinkIdentity_m(dom::MediaStreamTrack* aTrack,
                            nsIPrincipal* aPrincipal,
                            const PeerIdentity* aSinkIdentity);
  // this determines if any track is peerIdentity constrained
  bool AnyLocalTrackHasPeerIdentity() const;
  // When we finally learn who is on the other end, we need to change the ownership
  // on streams
  void UpdateRemoteStreamPrincipals_m(nsIPrincipal* aPrincipal);
#endif

  bool AnyCodecHasPluginID(uint64_t aPluginID);

  const nsCOMPtr<nsIThread>& GetMainThread() const { return mMainThread; }
  const nsCOMPtr<nsIEventTarget>& GetSTSThread() const { return mSTSThread; }

  static size_t GetTransportFlowIndex(int aStreamIndex, bool aRtcp)
  {
    return aStreamIndex * 2 + (aRtcp ? 1 : 0);
  }

  // Get a transport flow either RTP/RTCP for a particular stream
  // A stream can be of audio/video/datachannel/budled(?) types
  RefPtr<TransportFlow> GetTransportFlow(int aStreamIndex, bool aIsRtcp) {
    int index_inner = GetTransportFlowIndex(aStreamIndex, aIsRtcp);

    if (mTransportFlows.find(index_inner) == mTransportFlows.end())
      return nullptr;

    return mTransportFlows[index_inner];
  }

  // Add a transport flow
  void AddTransportFlow(int aIndex, bool aRtcp,
                        const RefPtr<TransportFlow> &aFlow);
  void RemoveTransportFlow(int aIndex, bool aRtcp);
  void ConnectDtlsListener_s(const RefPtr<TransportFlow>& aFlow);
  void DtlsConnected_s(TransportLayer* aFlow,
                       TransportLayer::State state);
  static void DtlsConnected_m(const std::string& aParentHandle,
                              bool aPrivacyRequested);

  RefPtr<AudioSessionConduit> GetAudioConduit(size_t level) {
    auto it = mConduits.find(level);
    if (it == mConduits.end()) {
      return nullptr;
    }

    if (it->second.first) {
      MOZ_ASSERT(false, "In GetAudioConduit, we found a video conduit!");
      return nullptr;
    }

    return RefPtr<AudioSessionConduit>(
        static_cast<AudioSessionConduit*>(it->second.second.get()));
  }

  RefPtr<VideoSessionConduit> GetVideoConduit(size_t level) {
    auto it = mConduits.find(level);
    if (it == mConduits.end()) {
      return nullptr;
    }

    if (!it->second.first) {
      MOZ_ASSERT(false, "In GetVideoConduit, we found an audio conduit!");
      return nullptr;
    }

    return RefPtr<VideoSessionConduit>(
        static_cast<VideoSessionConduit*>(it->second.second.get()));
  }

  // Add a conduit
  void AddAudioConduit(size_t level, const RefPtr<AudioSessionConduit> &aConduit) {
    mConduits[level] = std::make_pair(false, aConduit);
  }

  void AddVideoConduit(size_t level, const RefPtr<VideoSessionConduit> &aConduit) {
    mConduits[level] = std::make_pair(true, aConduit);
  }

  // ICE state signals
  sigslot::signal2<NrIceCtx*, NrIceCtx::GatheringState>
      SignalIceGatheringStateChange;
  sigslot::signal2<NrIceCtx*, NrIceCtx::ConnectionState>
      SignalIceConnectionStateChange;
  // This passes a candidate:... attribute  and level
  sigslot::signal2<const std::string&, uint16_t> SignalCandidate;
  // This passes address, port, level of the default candidate.
  sigslot::signal5<const std::string&, uint16_t,
                   const std::string&, uint16_t, uint16_t>
      SignalUpdateDefaultCandidate;
  sigslot::signal1<uint16_t>
      SignalEndOfLocalCandidates;

 private:
  nsresult InitProxy();
  class ProtocolProxyQueryHandler : public nsIProtocolProxyCallback {
   public:
    explicit ProtocolProxyQueryHandler(PeerConnectionMedia *pcm) :
      pcm_(pcm) {}

    NS_IMETHOD OnProxyAvailable(nsICancelable *request,
                                nsIChannel *aChannel,
                                nsIProxyInfo *proxyinfo,
                                nsresult result) override;
    NS_DECL_ISUPPORTS

   private:
    void SetProxyOnPcm(nsIProxyInfo& proxyinfo);
    RefPtr<PeerConnectionMedia> pcm_;
    virtual ~ProtocolProxyQueryHandler() {}
  };

  // Shutdown media transport. Must be called on STS thread.
  void ShutdownMediaTransport_s();

  // Final destruction of the media stream. Must be called on the main
  // thread.
  void SelfDestruct_m();

  // Manage ICE transports.
  void EnsureTransport_s(size_t aLevel, size_t aComponentCount);
  void ActivateOrRemoveTransport_s(
      size_t aMLine,
      size_t aComponentCount,
      const std::string& aUfrag,
      const std::string& aPassword,
      const std::vector<std::string>& aCandidateList);
  void RemoveTransportsAtOrAfter_s(size_t aMLine);

  void GatherIfReady();
  void FlushIceCtxOperationQueueIfReady();
  void PerformOrEnqueueIceCtxOperation(nsIRunnable* runnable);
  void EnsureIceGathering_s(bool aDefaultRouteOnly, bool aProxyOnly);
  void StartIceChecks_s(bool aIsControlling,
                        bool aIsIceLite,
                        const std::vector<std::string>& aIceOptionsList);

  void BeginIceRestart_s(RefPtr<NrIceCtx> new_ctx);
  void FinalizeIceRestart_s();
  void RollbackIceRestart_s();
  bool GetPrefDefaultAddressOnly() const;
  bool GetPrefProxyOnly() const;

  void ConnectSignals(NrIceCtx *aCtx, NrIceCtx *aOldCtx=nullptr);

  // Process a trickle ICE candidate.
  void AddIceCandidate_s(const std::string& aCandidate, const std::string& aMid,
                         uint32_t aMLine);


  // ICE events
  void IceGatheringStateChange_s(NrIceCtx* ctx,
                               NrIceCtx::GatheringState state);
  void IceConnectionStateChange_s(NrIceCtx* ctx,
                                NrIceCtx::ConnectionState state);
  void IceStreamReady_s(NrIceMediaStream *aStream);
  void OnCandidateFound_s(NrIceMediaStream *aStream,
                          const std::string& aCandidate);
  void EndOfLocalCandidates(const std::string& aDefaultAddr,
                            uint16_t aDefaultPort,
                            const std::string& aDefaultRtcpAddr,
                            uint16_t aDefaultRtcpPort,
                            uint16_t aMLine);
  void GetDefaultCandidates(const NrIceMediaStream& aStream,
                            NrIceCandidate* aCandidate,
                            NrIceCandidate* aRtcpCandidate);

  void IceGatheringStateChange_m(NrIceCtx* ctx,
                                 NrIceCtx::GatheringState state);
  void IceConnectionStateChange_m(NrIceCtx* ctx,
                                  NrIceCtx::ConnectionState state);
  void OnCandidateFound_m(const std::string& aCandidateLine,
                          const std::string& aDefaultAddr,
                          uint16_t aDefaultPort,
                          const std::string& aDefaultRtcpAddr,
                          uint16_t aDefaultRtcpPort,
                          uint16_t aMLine);
  void EndOfLocalCandidates_m(const std::string& aDefaultAddr,
                              uint16_t aDefaultPort,
                              const std::string& aDefaultRtcpAddr,
                              uint16_t aDefaultRtcpPort,
                              uint16_t aMLine);
  bool IsIceCtxReady() const {
    return mProxyResolveCompleted;
  }

  // The parent PC
  PeerConnectionImpl *mParent;
  // and a loose handle on it for event driven stuff
  std::string mParentHandle;
  std::string mParentName;

  // A list of streams returned from GetUserMedia
  // This is only accessed on the main thread (with one special exception)
  nsTArray<RefPtr<LocalSourceStreamInfo> > mLocalSourceStreams;

  // A list of streams provided by the other side
  // This is only accessed on the main thread (with one special exception)
  nsTArray<RefPtr<RemoteSourceStreamInfo> > mRemoteSourceStreams;

  std::map<size_t, std::pair<bool, RefPtr<MediaSessionConduit>>> mConduits;

  // ICE objects
  RefPtr<NrIceCtxHandler> mIceCtxHdlr;

  // DNS
  RefPtr<NrIceResolver> mDNSResolver;

  // Transport flows: even is RTP, odd is RTCP
  std::map<int, RefPtr<TransportFlow> > mTransportFlows;

  // UUID Generator
  UniquePtr<PCUuidGenerator> mUuidGen;

  // The main thread.
  nsCOMPtr<nsIThread> mMainThread;

  // The STS thread.
  nsCOMPtr<nsIEventTarget> mSTSThread;

  // Used whenever we need to dispatch a runnable to STS to tweak something
  // on our ICE ctx, but are not ready to do so at the moment (eg; we are
  // waiting to get a callback with our http proxy config before we start
  // gathering or start checking)
  std::vector<nsCOMPtr<nsIRunnable>> mQueuedIceCtxOperations;

  // Used to cancel any ongoing proxy request.
  nsCOMPtr<nsICancelable> mProxyRequest;

  // Used to track the state of the request.
  bool mProxyResolveCompleted;

  // Used to store the result of the request.
  UniquePtr<NrIceProxyServer> mProxyServer;

  // Used to track the state of ice restart
  IceRestartState mIceRestartState;

  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PeerConnectionMedia)
};

} // namespace mozilla

#endif