Adding AddTrack/RemoveTrack to native PeerConnection API.

Also, now creating the RtpSender/RtpReceiver proxy objects immediately,
rather than waiting until when GetSenders/GetReceivers is called.

Review URL: https://codereview.webrtc.org/1563403002

Cr-Commit-Position: refs/heads/master@{#11259}
diff --git a/talk/app/webrtc/peerconnection.cc b/talk/app/webrtc/peerconnection.cc
index ccca18a..beae770 100644
--- a/talk/app/webrtc/peerconnection.cc
+++ b/talk/app/webrtc/peerconnection.cc
@@ -734,6 +734,80 @@
   observer_->OnRenegotiationNeeded();
 }
 
+rtc::scoped_refptr<RtpSenderInterface> PeerConnection::AddTrack(
+    MediaStreamTrackInterface* track,
+    std::vector<MediaStreamInterface*> streams) {
+  TRACE_EVENT0("webrtc", "PeerConnection::AddTrack");
+  if (IsClosed()) {
+    return nullptr;
+  }
+  if (streams.size() >= 2) {
+    LOG(LS_ERROR)
+        << "Adding a track with two streams is not currently supported.";
+    return nullptr;
+  }
+  // TODO(deadbeef): Support adding a track to two different senders.
+  if (FindSenderForTrack(track) != senders_.end()) {
+    LOG(LS_ERROR) << "Sender for track " << track->id() << " already exists.";
+    return nullptr;
+  }
+
+  // TODO(deadbeef): Support adding a track to multiple streams.
+  rtc::scoped_refptr<RtpSenderInterface> new_sender;
+  if (track->kind() == MediaStreamTrackInterface::kAudioKind) {
+    new_sender = RtpSenderProxy::Create(
+        signaling_thread(),
+        new AudioRtpSender(static_cast<AudioTrackInterface*>(track),
+                           session_.get(), stats_.get()));
+    if (!streams.empty()) {
+      new_sender->set_stream_id(streams[0]->label());
+    }
+    const TrackInfo* track_info = FindTrackInfo(
+        local_audio_tracks_, new_sender->stream_id(), track->id());
+    if (track_info) {
+      new_sender->SetSsrc(track_info->ssrc);
+    }
+  } else if (track->kind() == MediaStreamTrackInterface::kVideoKind) {
+    new_sender = RtpSenderProxy::Create(
+        signaling_thread(),
+        new VideoRtpSender(static_cast<VideoTrackInterface*>(track),
+                           session_.get()));
+    if (!streams.empty()) {
+      new_sender->set_stream_id(streams[0]->label());
+    }
+    const TrackInfo* track_info = FindTrackInfo(
+        local_video_tracks_, new_sender->stream_id(), track->id());
+    if (track_info) {
+      new_sender->SetSsrc(track_info->ssrc);
+    }
+  } else {
+    LOG(LS_ERROR) << "CreateSender called with invalid kind: " << track->kind();
+    return rtc::scoped_refptr<RtpSenderInterface>();
+  }
+
+  senders_.push_back(new_sender);
+  observer_->OnRenegotiationNeeded();
+  return new_sender;
+}
+
+bool PeerConnection::RemoveTrack(RtpSenderInterface* sender) {
+  TRACE_EVENT0("webrtc", "PeerConnection::RemoveTrack");
+  if (IsClosed()) {
+    return false;
+  }
+
+  auto it = std::find(senders_.begin(), senders_.end(), sender);
+  if (it == senders_.end()) {
+    LOG(LS_ERROR) << "Couldn't find sender " << sender->id() << " to remove.";
+    return false;
+  }
+  (*it)->Stop();
+  senders_.erase(it);
+
+  observer_->OnRenegotiationNeeded();
+  return true;
+}
+
 rtc::scoped_refptr<DtmfSenderInterface> PeerConnection::CreateDtmfSender(
     AudioTrackInterface* track) {
   TRACE_EVENT0("webrtc", "PeerConnection::CreateDtmfSender");
@@ -759,39 +833,32 @@
     const std::string& kind,
     const std::string& stream_id) {
   TRACE_EVENT0("webrtc", "PeerConnection::CreateSender");
-  RtpSenderInterface* new_sender;
+  rtc::scoped_refptr<RtpSenderInterface> new_sender;
   if (kind == MediaStreamTrackInterface::kAudioKind) {
-    new_sender = new AudioRtpSender(session_.get(), stats_.get());
+    new_sender = RtpSenderProxy::Create(
+        signaling_thread(), new AudioRtpSender(session_.get(), stats_.get()));
   } else if (kind == MediaStreamTrackInterface::kVideoKind) {
-    new_sender = new VideoRtpSender(session_.get());
+    new_sender = RtpSenderProxy::Create(signaling_thread(),
+                                        new VideoRtpSender(session_.get()));
   } else {
     LOG(LS_ERROR) << "CreateSender called with invalid kind: " << kind;
-    return rtc::scoped_refptr<RtpSenderInterface>();
+    return new_sender;
   }
   if (!stream_id.empty()) {
     new_sender->set_stream_id(stream_id);
   }
   senders_.push_back(new_sender);
-  return RtpSenderProxy::Create(signaling_thread(), new_sender);
+  return new_sender;
 }
 
 std::vector<rtc::scoped_refptr<RtpSenderInterface>> PeerConnection::GetSenders()
     const {
-  std::vector<rtc::scoped_refptr<RtpSenderInterface>> senders;
-  for (const auto& sender : senders_) {
-    senders.push_back(RtpSenderProxy::Create(signaling_thread(), sender.get()));
-  }
-  return senders;
+  return senders_;
 }
 
 std::vector<rtc::scoped_refptr<RtpReceiverInterface>>
 PeerConnection::GetReceivers() const {
-  std::vector<rtc::scoped_refptr<RtpReceiverInterface>> receivers;
-  for (const auto& receiver : receivers_) {
-    receivers.push_back(
-        RtpReceiverProxy::Create(signaling_thread(), receiver.get()));
-  }
-  return receivers;
+  return receivers_;
 }
 
 bool PeerConnection::GetStats(StatsObserver* observer,
@@ -1257,13 +1324,17 @@
 void PeerConnection::CreateAudioReceiver(MediaStreamInterface* stream,
                                          AudioTrackInterface* audio_track,
                                          uint32_t ssrc) {
-  receivers_.push_back(new AudioRtpReceiver(audio_track, ssrc, session_.get()));
+  receivers_.push_back(RtpReceiverProxy::Create(
+      signaling_thread(),
+      new AudioRtpReceiver(audio_track, ssrc, session_.get())));
 }
 
 void PeerConnection::CreateVideoReceiver(MediaStreamInterface* stream,
                                          VideoTrackInterface* video_track,
                                          uint32_t ssrc) {
-  receivers_.push_back(new VideoRtpReceiver(video_track, ssrc, session_.get()));
+  receivers_.push_back(RtpReceiverProxy::Create(
+      signaling_thread(),
+      new VideoRtpReceiver(video_track, ssrc, session_.get())));
 }
 
 // TODO(deadbeef): Keep RtpReceivers around even if track goes away in remote
@@ -1355,8 +1426,9 @@
   }
 
   // Normal case; we've never seen this track before.
-  AudioRtpSender* new_sender =
-      new AudioRtpSender(track, stream->label(), session_.get(), stats_.get());
+  rtc::scoped_refptr<RtpSenderInterface> new_sender = RtpSenderProxy::Create(
+      signaling_thread(),
+      new AudioRtpSender(track, stream->label(), session_.get(), stats_.get()));
   senders_.push_back(new_sender);
   // If the sender has already been configured in SDP, we call SetSsrc,
   // which will connect the sender to the underlying transport. This can
@@ -1396,8 +1468,9 @@
   }
 
   // Normal case; we've never seen this track before.
-  VideoRtpSender* new_sender =
-      new VideoRtpSender(track, stream->label(), session_.get());
+  rtc::scoped_refptr<RtpSenderInterface> new_sender = RtpSenderProxy::Create(
+      signaling_thread(),
+      new VideoRtpSender(track, stream->label(), session_.get()));
   senders_.push_back(new_sender);
   const TrackInfo* track_info =
       FindTrackInfo(local_video_tracks_, stream->label(), track->id());
diff --git a/talk/app/webrtc/peerconnection.h b/talk/app/webrtc/peerconnection.h
index 6e2b967..03b5853 100644
--- a/talk/app/webrtc/peerconnection.h
+++ b/talk/app/webrtc/peerconnection.h
@@ -83,6 +83,11 @@
   bool AddStream(MediaStreamInterface* local_stream) override;
   void RemoveStream(MediaStreamInterface* local_stream) override;
 
+  rtc::scoped_refptr<RtpSenderInterface> AddTrack(
+      MediaStreamTrackInterface* track,
+      std::vector<MediaStreamInterface*> streams) override;
+  bool RemoveTrack(RtpSenderInterface* sender) override;
+
   virtual WebRtcSession* session() { return session_.get(); }
 
   rtc::scoped_refptr<DtmfSenderInterface> CreateDtmfSender(
diff --git a/talk/app/webrtc/peerconnectioninterface.h b/talk/app/webrtc/peerconnectioninterface.h
index b9afbad..da99a7b 100644
--- a/talk/app/webrtc/peerconnectioninterface.h
+++ b/talk/app/webrtc/peerconnectioninterface.h
@@ -331,6 +331,23 @@
   // remote peer is notified.
   virtual void RemoveStream(MediaStreamInterface* stream) = 0;
 
+  // TODO(deadbeef): Make the following two methods pure virtual once
+  // implemented by all subclasses of PeerConnectionInterface.
+  // Add a new MediaStreamTrack to be sent on this PeerConnection.
+  // |streams| indicates which stream labels the track should be associated
+  // with.
+  virtual rtc::scoped_refptr<RtpSenderInterface> AddTrack(
+      MediaStreamTrackInterface* track,
+      std::vector<MediaStreamInterface*> streams) {
+    return nullptr;
+  }
+
+  // Remove an RtpSender from this PeerConnection.
+  // Returns true on success.
+  virtual bool RemoveTrack(RtpSenderInterface* sender) {
+    return false;
+  }
+
   // Returns pointer to the created DtmfSender on success.
   // Otherwise returns NULL.
   virtual rtc::scoped_refptr<DtmfSenderInterface> CreateDtmfSender(
diff --git a/talk/app/webrtc/peerconnectioninterface_unittest.cc b/talk/app/webrtc/peerconnectioninterface_unittest.cc
index c3789b7..b1f718b 100644
--- a/talk/app/webrtc/peerconnectioninterface_unittest.cc
+++ b/talk/app/webrtc/peerconnectioninterface_unittest.cc
@@ -1025,6 +1025,101 @@
   EXPECT_EQ(0u, pc_->local_streams()->count());
 }
 
+// Test for AddTrack and RemoveTrack methods.
+// Tests that the created offer includes tracks we added,
+// and that the RtpSenders are created correctly.
+// Also tests that RemoveTrack removes the tracks from subsequent offers.
+TEST_F(PeerConnectionInterfaceTest, AddTrackRemoveTrack) {
+  CreatePeerConnection();
+  // Create a dummy stream, so tracks share a stream label.
+  scoped_refptr<MediaStreamInterface> stream(
+      pc_factory_->CreateLocalMediaStream(kStreamLabel1));
+  std::vector<MediaStreamInterface*> stream_list;
+  stream_list.push_back(stream.get());
+  scoped_refptr<AudioTrackInterface> audio_track(
+      pc_factory_->CreateAudioTrack("audio_track", nullptr));
+  scoped_refptr<VideoTrackInterface> video_track(
+      pc_factory_->CreateVideoTrack("video_track", nullptr));
+  auto audio_sender = pc_->AddTrack(audio_track, stream_list);
+  auto video_sender = pc_->AddTrack(video_track, stream_list);
+  EXPECT_EQ(kStreamLabel1, audio_sender->stream_id());
+  EXPECT_EQ("audio_track", audio_sender->id());
+  EXPECT_EQ(audio_track, audio_sender->track());
+  EXPECT_EQ(kStreamLabel1, video_sender->stream_id());
+  EXPECT_EQ("video_track", video_sender->id());
+  EXPECT_EQ(video_track, video_sender->track());
+
+  // Now create an offer and check for the senders.
+  scoped_ptr<SessionDescriptionInterface> offer;
+  ASSERT_TRUE(DoCreateOffer(offer.accept(), nullptr));
+
+  const cricket::ContentInfo* audio_content =
+      cricket::GetFirstAudioContent(offer->description());
+  const cricket::AudioContentDescription* audio_desc =
+      static_cast<const cricket::AudioContentDescription*>(
+          audio_content->description);
+  EXPECT_TRUE(
+      ContainsTrack(audio_desc->streams(), kStreamLabel1, "audio_track"));
+
+  const cricket::ContentInfo* video_content =
+      cricket::GetFirstVideoContent(offer->description());
+  const cricket::VideoContentDescription* video_desc =
+      static_cast<const cricket::VideoContentDescription*>(
+          video_content->description);
+  EXPECT_TRUE(
+      ContainsTrack(video_desc->streams(), kStreamLabel1, "video_track"));
+
+  EXPECT_TRUE(DoSetLocalDescription(offer.release()));
+
+  // Now try removing the tracks.
+  EXPECT_TRUE(pc_->RemoveTrack(audio_sender));
+  EXPECT_TRUE(pc_->RemoveTrack(video_sender));
+
+  // Create a new offer and ensure it doesn't contain the removed senders.
+  ASSERT_TRUE(DoCreateOffer(offer.accept(), nullptr));
+
+  audio_content = cricket::GetFirstAudioContent(offer->description());
+  audio_desc = static_cast<const cricket::AudioContentDescription*>(
+      audio_content->description);
+  EXPECT_FALSE(
+      ContainsTrack(audio_desc->streams(), kStreamLabel1, "audio_track"));
+
+  video_content = cricket::GetFirstVideoContent(offer->description());
+  video_desc = static_cast<const cricket::VideoContentDescription*>(
+      video_content->description);
+  EXPECT_FALSE(
+      ContainsTrack(video_desc->streams(), kStreamLabel1, "video_track"));
+
+  EXPECT_TRUE(DoSetLocalDescription(offer.release()));
+
+  // Calling RemoveTrack on a sender no longer attached to a PeerConnection
+  // should return false.
+  EXPECT_FALSE(pc_->RemoveTrack(audio_sender));
+  EXPECT_FALSE(pc_->RemoveTrack(video_sender));
+}
+
+// Test creating senders without a stream specified,
+// expecting a random stream ID to be generated.
+TEST_F(PeerConnectionInterfaceTest, AddTrackWithoutStream) {
+  CreatePeerConnection();
+  // Create a dummy stream, so tracks share a stream label.
+  scoped_refptr<AudioTrackInterface> audio_track(
+      pc_factory_->CreateAudioTrack("audio_track", nullptr));
+  scoped_refptr<VideoTrackInterface> video_track(
+      pc_factory_->CreateVideoTrack("video_track", nullptr));
+  auto audio_sender =
+      pc_->AddTrack(audio_track, std::vector<MediaStreamInterface*>());
+  auto video_sender =
+      pc_->AddTrack(video_track, std::vector<MediaStreamInterface*>());
+  EXPECT_EQ("audio_track", audio_sender->id());
+  EXPECT_EQ(audio_track, audio_sender->track());
+  EXPECT_EQ("video_track", video_sender->id());
+  EXPECT_EQ(video_track, video_sender->track());
+  // If the ID is truly a random GUID, it should be infinitely unlikely they
+  // will be the same.
+  EXPECT_NE(video_sender->stream_id(), audio_sender->stream_id());
+}
+
 TEST_F(PeerConnectionInterfaceTest, CreateOfferReceiveAnswer) {
   InitiateCall();
   WaitAndVerifyOnAddStream(kStreamLabel1);
diff --git a/talk/app/webrtc/peerconnectionproxy.h b/talk/app/webrtc/peerconnectionproxy.h
index 3c983d7..d76a6b8 100644
--- a/talk/app/webrtc/peerconnectionproxy.h
+++ b/talk/app/webrtc/peerconnectionproxy.h
@@ -41,6 +41,11 @@
                 remote_streams)
   PROXY_METHOD1(bool, AddStream, MediaStreamInterface*)
   PROXY_METHOD1(void, RemoveStream, MediaStreamInterface*)
+  PROXY_METHOD2(rtc::scoped_refptr<RtpSenderInterface>,
+                AddTrack,
+                MediaStreamTrackInterface*,
+                std::vector<MediaStreamInterface*>)
+  PROXY_METHOD1(bool, RemoveTrack, RtpSenderInterface*)
   PROXY_METHOD1(rtc::scoped_refptr<DtmfSenderInterface>,
                 CreateDtmfSender, AudioTrackInterface*)
   PROXY_METHOD2(rtc::scoped_refptr<RtpSenderInterface>,
diff --git a/talk/app/webrtc/rtpsender.cc b/talk/app/webrtc/rtpsender.cc
index 91e484b..72a9114 100644
--- a/talk/app/webrtc/rtpsender.cc
+++ b/talk/app/webrtc/rtpsender.cc
@@ -75,6 +75,21 @@
   track_->AddSink(sink_adapter_.get());
 }
 
+AudioRtpSender::AudioRtpSender(AudioTrackInterface* track,
+                               AudioProviderInterface* provider,
+                               StatsCollector* stats)
+    : id_(track->id()),
+      stream_id_(rtc::CreateRandomUuid()),
+      provider_(provider),
+      stats_(stats),
+      track_(track),
+      cached_track_enabled_(track->enabled()),
+      sink_adapter_(new LocalAudioSinkAdapter()) {
+  RTC_DCHECK(provider != nullptr);
+  track_->RegisterObserver(this);
+  track_->AddSink(sink_adapter_.get());
+}
+
 AudioRtpSender::AudioRtpSender(AudioProviderInterface* provider,
                                StatsCollector* stats)
     : id_(rtc::CreateRandomUuid()),
@@ -211,6 +226,17 @@
   track_->RegisterObserver(this);
 }
 
+VideoRtpSender::VideoRtpSender(VideoTrackInterface* track,
+                               VideoProviderInterface* provider)
+    : id_(track->id()),
+      stream_id_(rtc::CreateRandomUuid()),
+      provider_(provider),
+      track_(track),
+      cached_track_enabled_(track->enabled()) {
+  RTC_DCHECK(provider != nullptr);
+  track_->RegisterObserver(this);
+}
+
 VideoRtpSender::VideoRtpSender(VideoProviderInterface* provider)
     : id_(rtc::CreateRandomUuid()),
       stream_id_(rtc::CreateRandomUuid()),
diff --git a/talk/app/webrtc/rtpsender.h b/talk/app/webrtc/rtpsender.h
index dd846b5..9e055c1 100644
--- a/talk/app/webrtc/rtpsender.h
+++ b/talk/app/webrtc/rtpsender.h
@@ -78,6 +78,11 @@
                  AudioProviderInterface* provider,
                  StatsCollector* stats);
 
+  // Randomly generates stream_id.
+  AudioRtpSender(AudioTrackInterface* track,
+                 AudioProviderInterface* provider,
+                 StatsCollector* stats);
+
   // Randomly generates id and stream_id.
   AudioRtpSender(AudioProviderInterface* provider, StatsCollector* stats);
 
@@ -136,6 +141,9 @@
                  const std::string& stream_id,
                  VideoProviderInterface* provider);
 
+  // Randomly generates stream_id.
+  VideoRtpSender(VideoTrackInterface* track, VideoProviderInterface* provider);
+
   // Randomly generates id and stream_id.
   explicit VideoRtpSender(VideoProviderInterface* provider);