When a track is added/removed directly to MediaStream notify observer->OnRenegotionNeeded

There is an inconsistency in behavior of PeerConnection.
When I remove track from PeerConnection observer->OnRenegotiationNeeded is called, however if I remove track from MediaStream then there is no notification to renegotiate.
This patch adds missing OnRenegotiationNeeded calls.

BUG=webrtc:7966

Review-Url: https://codereview.webrtc.org/2977493002
Cr-Original-Commit-Position: refs/heads/master@{#19125}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: ec390b5dfbadb9972e4fafa36226d1724c6c2204
diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc
index 1a5fd6c..f006b23 100644
--- a/pc/peerconnection.cc
+++ b/pc/peerconnection.cc
@@ -574,10 +574,10 @@
   stream_observers_.push_back(std::unique_ptr<MediaStreamObserver>(observer));
 
   for (const auto& track : local_stream->GetAudioTracks()) {
-    OnAudioTrackAdded(track.get(), local_stream);
+    AddAudioTrack(track.get(), local_stream);
   }
   for (const auto& track : local_stream->GetVideoTracks()) {
-    OnVideoTrackAdded(track.get(), local_stream);
+    AddVideoTrack(track.get(), local_stream);
   }
 
   stats_->AddStream(local_stream);
@@ -587,13 +587,14 @@
 
 void PeerConnection::RemoveStream(MediaStreamInterface* local_stream) {
   TRACE_EVENT0("webrtc", "PeerConnection::RemoveStream");
-  for (const auto& track : local_stream->GetAudioTracks()) {
-    OnAudioTrackRemoved(track.get(), local_stream);
+  if (!IsClosed()) {
+    for (const auto& track : local_stream->GetAudioTracks()) {
+      RemoveAudioTrack(track.get(), local_stream);
+    }
+    for (const auto& track : local_stream->GetVideoTracks()) {
+      RemoveVideoTrack(track.get(), local_stream);
+    }
   }
-  for (const auto& track : local_stream->GetVideoTracks()) {
-    OnVideoTrackRemoved(track.get(), local_stream);
-  }
-
   local_streams_->RemoveStream(local_stream);
   stream_observers_.erase(
       std::remove_if(
@@ -1496,6 +1497,89 @@
   }
 }
 
+void PeerConnection::AddAudioTrack(AudioTrackInterface* track,
+                                   MediaStreamInterface* stream) {
+  RTC_DCHECK(!IsClosed());
+  auto sender = FindSenderForTrack(track);
+  if (sender != senders_.end()) {
+    // We already have a sender for this track, so just change the stream_id
+    // so that it's correct in the next call to CreateOffer.
+    (*sender)->internal()->set_stream_id(stream->label());
+    return;
+  }
+
+  // Normal case; we've never seen this track before.
+  rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> new_sender =
+      RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
+          signaling_thread(),
+          new AudioRtpSender(track, stream->label(), session_->voice_channel(),
+                             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
+  // occur if a local session description that contains the ID of the sender
+  // is set before AddStream is called. It can also occur if the local
+  // session description is not changed and RemoveStream is called, and
+  // later AddStream is called again with the same stream.
+  const TrackInfo* track_info =
+      FindTrackInfo(local_audio_tracks_, stream->label(), track->id());
+  if (track_info) {
+    new_sender->internal()->SetSsrc(track_info->ssrc);
+  }
+}
+
+// TODO(deadbeef): Don't destroy RtpSenders here; they should be kept around
+// indefinitely, when we have unified plan SDP.
+void PeerConnection::RemoveAudioTrack(AudioTrackInterface* track,
+                                      MediaStreamInterface* stream) {
+  RTC_DCHECK(!IsClosed());
+  auto sender = FindSenderForTrack(track);
+  if (sender == senders_.end()) {
+    LOG(LS_WARNING) << "RtpSender for track with id " << track->id()
+                    << " doesn't exist.";
+    return;
+  }
+  (*sender)->internal()->Stop();
+  senders_.erase(sender);
+}
+
+void PeerConnection::AddVideoTrack(VideoTrackInterface* track,
+                                   MediaStreamInterface* stream) {
+  RTC_DCHECK(!IsClosed());
+  auto sender = FindSenderForTrack(track);
+  if (sender != senders_.end()) {
+    // We already have a sender for this track, so just change the stream_id
+    // so that it's correct in the next call to CreateOffer.
+    (*sender)->internal()->set_stream_id(stream->label());
+    return;
+  }
+
+  // Normal case; we've never seen this track before.
+  rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> new_sender =
+      RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
+          signaling_thread(), new VideoRtpSender(track, stream->label(),
+                                                 session_->video_channel()));
+  senders_.push_back(new_sender);
+  const TrackInfo* track_info =
+      FindTrackInfo(local_video_tracks_, stream->label(), track->id());
+  if (track_info) {
+    new_sender->internal()->SetSsrc(track_info->ssrc);
+  }
+}
+
+void PeerConnection::RemoveVideoTrack(VideoTrackInterface* track,
+                                      MediaStreamInterface* stream) {
+  RTC_DCHECK(!IsClosed());
+  auto sender = FindSenderForTrack(track);
+  if (sender == senders_.end()) {
+    LOG(LS_WARNING) << "RtpSender for track with id " << track->id()
+                    << " doesn't exist.";
+    return;
+  }
+  (*sender)->internal()->Stop();
+  senders_.erase(sender);
+}
+
 void PeerConnection::OnIceConnectionStateChange(
     PeerConnectionInterface::IceConnectionState new_state) {
   RTC_DCHECK(signaling_thread()->IsCurrent());
@@ -1563,49 +1647,17 @@
   if (IsClosed()) {
     return;
   }
-  auto sender = FindSenderForTrack(track);
-  if (sender != senders_.end()) {
-    // We already have a sender for this track, so just change the stream_id
-    // so that it's correct in the next call to CreateOffer.
-    (*sender)->internal()->set_stream_id(stream->label());
-    return;
-  }
-
-  // Normal case; we've never seen this track before.
-  rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> new_sender =
-      RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
-          signaling_thread(),
-          new AudioRtpSender(track, stream->label(), session_->voice_channel(),
-                             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
-  // occur if a local session description that contains the ID of the sender
-  // is set before AddStream is called. It can also occur if the local
-  // session description is not changed and RemoveStream is called, and
-  // later AddStream is called again with the same stream.
-  const TrackInfo* track_info =
-      FindTrackInfo(local_audio_tracks_, stream->label(), track->id());
-  if (track_info) {
-    new_sender->internal()->SetSsrc(track_info->ssrc);
-  }
+  AddAudioTrack(track, stream);
+  observer_->OnRenegotiationNeeded();
 }
 
-// TODO(deadbeef): Don't destroy RtpSenders here; they should be kept around
-// indefinitely, when we have unified plan SDP.
 void PeerConnection::OnAudioTrackRemoved(AudioTrackInterface* track,
                                          MediaStreamInterface* stream) {
   if (IsClosed()) {
     return;
   }
-  auto sender = FindSenderForTrack(track);
-  if (sender == senders_.end()) {
-    LOG(LS_WARNING) << "RtpSender for track with id " << track->id()
-                    << " doesn't exist.";
-    return;
-  }
-  (*sender)->internal()->Stop();
-  senders_.erase(sender);
+  RemoveAudioTrack(track, stream);
+  observer_->OnRenegotiationNeeded();
 }
 
 void PeerConnection::OnVideoTrackAdded(VideoTrackInterface* track,
@@ -1613,25 +1665,8 @@
   if (IsClosed()) {
     return;
   }
-  auto sender = FindSenderForTrack(track);
-  if (sender != senders_.end()) {
-    // We already have a sender for this track, so just change the stream_id
-    // so that it's correct in the next call to CreateOffer.
-    (*sender)->internal()->set_stream_id(stream->label());
-    return;
-  }
-
-  // Normal case; we've never seen this track before.
-  rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> new_sender =
-      RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
-          signaling_thread(), new VideoRtpSender(track, stream->label(),
-                                                 session_->video_channel()));
-  senders_.push_back(new_sender);
-  const TrackInfo* track_info =
-      FindTrackInfo(local_video_tracks_, stream->label(), track->id());
-  if (track_info) {
-    new_sender->internal()->SetSsrc(track_info->ssrc);
-  }
+  AddVideoTrack(track, stream);
+  observer_->OnRenegotiationNeeded();
 }
 
 void PeerConnection::OnVideoTrackRemoved(VideoTrackInterface* track,
@@ -1639,14 +1674,8 @@
   if (IsClosed()) {
     return;
   }
-  auto sender = FindSenderForTrack(track);
-  if (sender == senders_.end()) {
-    LOG(LS_WARNING) << "RtpSender for track with id " << track->id()
-                    << " doesn't exist.";
-    return;
-  }
-  (*sender)->internal()->Stop();
-  senders_.erase(sender);
+  RemoveVideoTrack(track, stream);
+  observer_->OnRenegotiationNeeded();
 }
 
 void PeerConnection::PostSetSessionDescriptionFailure(
diff --git a/pc/peerconnection.h b/pc/peerconnection.h
index 6fec348..f8b6a54 100644
--- a/pc/peerconnection.h
+++ b/pc/peerconnection.h
@@ -193,11 +193,15 @@
                            const std::string& track_id,
                            uint32_t ssrc);
   void DestroyReceiver(const std::string& track_id);
-  void DestroyAudioSender(MediaStreamInterface* stream,
-                          AudioTrackInterface* audio_track,
-                          uint32_t ssrc);
-  void DestroyVideoSender(MediaStreamInterface* stream,
-                          VideoTrackInterface* video_track);
+
+  // May be called either by AddStream/RemoveStream, or when a track is
+  // added/removed from a stream previously added via AddStream.
+  void AddAudioTrack(AudioTrackInterface* track, MediaStreamInterface* stream);
+  void RemoveAudioTrack(AudioTrackInterface* track,
+                        MediaStreamInterface* stream);
+  void AddVideoTrack(VideoTrackInterface* track, MediaStreamInterface* stream);
+  void RemoveVideoTrack(VideoTrackInterface* track,
+                        MediaStreamInterface* stream);
 
   // Implements IceObserver
   void OnIceConnectionStateChange(IceConnectionState new_state) override;
diff --git a/pc/peerconnectioninterface_unittest.cc b/pc/peerconnectioninterface_unittest.cc
index ce5180b..1a26a43 100644
--- a/pc/peerconnectioninterface_unittest.cc
+++ b/pc/peerconnectioninterface_unittest.cc
@@ -3736,3 +3736,37 @@
       PeerConnectionInterface::RTCConfigurationType::kAggressive);
   EXPECT_NE(a, h);
 }
+
+// This test ensures OnRenegotiationNeeded is called when we add track with
+// MediaStream -> AddTrack in the same way it is called when we add track with
+// PeerConnection -> AddTrack.
+// The test can be removed once addStream is rewritten in terms of addTrack
+// https://bugs.chromium.org/p/webrtc/issues/detail?id=7815
+TEST_F(PeerConnectionInterfaceTest, MediaStreamAddTrackRemoveTrackRenegotiate) {
+  CreatePeerConnectionWithoutDtls();
+  rtc::scoped_refptr<MediaStreamInterface> stream(
+      pc_factory_->CreateLocalMediaStream(kStreamLabel1));
+  pc_->AddStream(stream);
+  rtc::scoped_refptr<AudioTrackInterface> audio_track(
+      pc_factory_->CreateAudioTrack("audio_track", nullptr));
+  rtc::scoped_refptr<VideoTrackInterface> video_track(
+      pc_factory_->CreateVideoTrack(
+          "video_track", pc_factory_->CreateVideoSource(
+              std::unique_ptr<cricket::VideoCapturer>(
+                  new cricket::FakeVideoCapturer()))));
+  stream->AddTrack(audio_track);
+  EXPECT_TRUE_WAIT(observer_.renegotiation_needed_, kTimeout);
+  observer_.renegotiation_needed_ = false;
+
+  stream->AddTrack(video_track);
+  EXPECT_TRUE_WAIT(observer_.renegotiation_needed_, kTimeout);
+  observer_.renegotiation_needed_ = false;
+
+  stream->RemoveTrack(audio_track);
+  EXPECT_TRUE_WAIT(observer_.renegotiation_needed_, kTimeout);
+  observer_.renegotiation_needed_ = false;
+
+  stream->RemoveTrack(video_track);
+  EXPECT_TRUE_WAIT(observer_.renegotiation_needed_, kTimeout);
+  observer_.renegotiation_needed_ = false;
+}