Use RtpTransceivers in PeerConnection

Moves ownership of the RtpSenders/RtpReceivers/BaseChannels to
RtpTransceiver objects. For now, there can only be one
RtpTransceiver for audio and one for video. Future work to
implement Unified Plan will relax this restriction.

Bug: webrtc:7600
Change-Id: I9dfe324de61e2b363948858da72624396e27fc1a
Reviewed-on: https://webrtc-review.googlesource.com/21461
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Peter Thatcher <pthatcher@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20802}
diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc
index e8857a3..d617c71 100644
--- a/pc/peerconnection.cc
+++ b/pc/peerconnection.cc
@@ -91,8 +91,8 @@
 namespace {
 
 static const char kDefaultStreamLabel[] = "default";
-static const char kDefaultAudioTrackLabel[] = "defaulta0";
-static const char kDefaultVideoTrackLabel[] = "defaultv0";
+static const char kDefaultAudioSenderId[] = "defaulta0";
+static const char kDefaultVideoSenderId[] = "defaultv0";
 
 // The length of RTCP CNAMEs.
 static const int kRtcpCnameLength = 16;
@@ -227,34 +227,6 @@
   return cricket::CF_NONE;
 }
 
-// Helper method to set a voice/video channel on all applicable senders
-// and receivers when one is created/destroyed by PeerConnection.
-//
-// Used by On(Voice|Video)Channel(Created|Destroyed)
-template <class SENDER,
-          class RECEIVER,
-          class CHANNEL,
-          class SENDERS,
-          class RECEIVERS>
-void SetChannelOnSendersAndReceivers(CHANNEL* channel,
-                                     const SENDERS& senders,
-                                     const RECEIVERS& receivers,
-                                     cricket::MediaType media_type) {
-  for (auto& sender : senders) {
-    if (sender->media_type() == media_type) {
-      static_cast<SENDER*>(sender->internal())->SetChannel(channel);
-    }
-  }
-  for (auto& receiver : receivers) {
-    if (receiver->media_type() == media_type) {
-      if (!channel) {
-        receiver->internal()->Stop();
-      }
-      static_cast<RECEIVER*>(receiver->internal())->SetChannel(channel);
-    }
-  }
-}
-
 // Helper to set an error and return from a method.
 bool SafeSetError(webrtc::RTCErrorType type, webrtc::RTCError* error) {
   if (error) {
@@ -827,15 +799,14 @@
 
 PeerConnection::~PeerConnection() {
   TRACE_EVENT0("webrtc", "PeerConnection::~PeerConnection");
-  RTC_DCHECK(signaling_thread()->IsCurrent());
-  // Need to detach RTP senders/receivers from PeerConnection,
-  // since it's about to be destroyed.
-  for (const auto& sender : senders_) {
-    sender->internal()->Stop();
+  RTC_DCHECK_RUN_ON(signaling_thread());
+
+  // Need to detach RTP transceivers from PeerConnection, since it's about to be
+  // destroyed.
+  for (auto transceiver : transceivers_) {
+    transceiver->Stop();
   }
-  for (const auto& receiver : receivers_) {
-    receiver->internal()->Stop();
-  }
+
   // Destroy stats_ because it depends on session_.
   stats_.reset(nullptr);
   if (stats_collector_) {
@@ -845,11 +816,19 @@
 
   // Destroy video channels first since they may have a pointer to a voice
   // channel.
-  for (auto* channel : video_channels_) {
-    DestroyVideoChannel(channel);
+  for (auto transceiver : transceivers_) {
+    if (transceiver->internal()->media_type() == cricket::MEDIA_TYPE_VIDEO &&
+        transceiver->internal()->channel()) {
+      DestroyVideoChannel(static_cast<cricket::VideoChannel*>(
+          transceiver->internal()->channel()));
+    }
   }
-  for (auto* channel : voice_channels_) {
-    DestroyVoiceChannel(channel);
+  for (auto transceiver : transceivers_) {
+    if (transceiver->internal()->media_type() == cricket::MEDIA_TYPE_AUDIO &&
+        transceiver->internal()->channel()) {
+      DestroyVoiceChannel(static_cast<cricket::VoiceChannel*>(
+          transceiver->internal()->channel()));
+    }
   }
   if (rtp_data_channel_) {
     DestroyDataChannel();
@@ -1019,6 +998,16 @@
   webrtc_session_desc_factory_->set_enable_encrypted_rtp_header_extensions(
       options.crypto_options.enable_encrypted_rtp_header_extensions);
 
+  // Add default audio/video transceivers for Plan B SDP.
+  if (!IsUnifiedPlan()) {
+    transceivers_.push_back(
+        RtpTransceiverProxyWithInternal<RtpTransceiver>::Create(
+            signaling_thread(), new RtpTransceiver(cricket::MEDIA_TYPE_AUDIO)));
+    transceivers_.push_back(
+        RtpTransceiverProxyWithInternal<RtpTransceiver>::Create(
+            signaling_thread(), new RtpTransceiver(cricket::MEDIA_TYPE_VIDEO)));
+  }
+
   return true;
 }
 
@@ -1114,8 +1103,7 @@
         << "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()) {
+  if (FindSenderForTrack(track)) {
     RTC_LOG(LS_ERROR) << "Sender for track " << track->id()
                       << " already exists.";
     return nullptr;
@@ -1128,26 +1116,30 @@
         signaling_thread(),
         new AudioRtpSender(static_cast<AudioTrackInterface*>(track),
                            voice_channel(), stats_.get()));
+    GetAudioTransceiver()->internal()->AddSender(new_sender);
     if (!streams.empty()) {
       new_sender->internal()->set_stream_id(streams[0]->label());
     }
-    const TrackInfo* track_info = FindTrackInfo(
-        local_audio_tracks_, new_sender->internal()->stream_id(), track->id());
-    if (track_info) {
-      new_sender->internal()->SetSsrc(track_info->ssrc);
+    const RtpSenderInfo* sender_info =
+        FindSenderInfo(local_audio_sender_infos_,
+                       new_sender->internal()->stream_id(), track->id());
+    if (sender_info) {
+      new_sender->internal()->SetSsrc(sender_info->first_ssrc);
     }
   } else if (track->kind() == MediaStreamTrackInterface::kVideoKind) {
     new_sender = RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
         signaling_thread(),
         new VideoRtpSender(static_cast<VideoTrackInterface*>(track),
                            video_channel()));
+    GetVideoTransceiver()->internal()->AddSender(new_sender);
     if (!streams.empty()) {
       new_sender->internal()->set_stream_id(streams[0]->label());
     }
-    const TrackInfo* track_info = FindTrackInfo(
-        local_video_tracks_, new_sender->internal()->stream_id(), track->id());
-    if (track_info) {
-      new_sender->internal()->SetSsrc(track_info->ssrc);
+    const RtpSenderInfo* sender_info =
+        FindSenderInfo(local_video_sender_infos_,
+                       new_sender->internal()->stream_id(), track->id());
+    if (sender_info) {
+      new_sender->internal()->SetSsrc(sender_info->first_ssrc);
     }
   } else {
     RTC_LOG(LS_ERROR) << "CreateSender called with invalid kind: "
@@ -1155,7 +1147,6 @@
     return rtc::scoped_refptr<RtpSenderInterface>();
   }
 
-  senders_.push_back(new_sender);
   observer_->OnRenegotiationNeeded();
   return new_sender;
 }
@@ -1166,14 +1157,18 @@
     return false;
   }
 
-  auto it = std::find(senders_.begin(), senders_.end(), sender);
-  if (it == senders_.end()) {
+  bool removed;
+  if (sender->media_type() == cricket::MEDIA_TYPE_AUDIO) {
+    removed = GetAudioTransceiver()->internal()->RemoveSender(sender);
+  } else {
+    RTC_DCHECK_EQ(cricket::MEDIA_TYPE_VIDEO, sender->media_type());
+    removed = GetVideoTransceiver()->internal()->RemoveSender(sender);
+  }
+  if (!removed) {
     RTC_LOG(LS_ERROR) << "Couldn't find sender " << sender->id()
                       << " to remove.";
     return false;
   }
-  (*it)->internal()->Stop();
-  senders_.erase(it);
 
   observer_->OnRenegotiationNeeded();
   return true;
@@ -1189,13 +1184,13 @@
     RTC_LOG(LS_ERROR) << "CreateDtmfSender - track is NULL.";
     return nullptr;
   }
-  auto it = FindSenderForTrack(track);
-  if (it == senders_.end()) {
+  auto track_sender = FindSenderForTrack(track);
+  if (!track_sender) {
     RTC_LOG(LS_ERROR) << "CreateDtmfSender called with a non-added track.";
     return nullptr;
   }
 
-  return (*it)->GetDtmfSender();
+  return track_sender->GetDtmfSender();
 }
 
 rtc::scoped_refptr<RtpSenderInterface> PeerConnection::CreateSender(
@@ -1205,42 +1200,72 @@
   if (IsClosed()) {
     return nullptr;
   }
+
+  // TODO(steveanton): Move construction of the RtpSenders to RtpTransceiver.
   rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> new_sender;
   if (kind == MediaStreamTrackInterface::kAudioKind) {
     new_sender = RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
         signaling_thread(), new AudioRtpSender(voice_channel(), stats_.get()));
+    GetAudioTransceiver()->internal()->AddSender(new_sender);
   } else if (kind == MediaStreamTrackInterface::kVideoKind) {
     new_sender = RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
         signaling_thread(), new VideoRtpSender(video_channel()));
+    GetVideoTransceiver()->internal()->AddSender(new_sender);
   } else {
     RTC_LOG(LS_ERROR) << "CreateSender called with invalid kind: " << kind;
-    return new_sender;
+    return nullptr;
   }
+
   if (!stream_id.empty()) {
     new_sender->internal()->set_stream_id(stream_id);
   }
-  senders_.push_back(new_sender);
+
   return new_sender;
 }
 
 std::vector<rtc::scoped_refptr<RtpSenderInterface>> PeerConnection::GetSenders()
     const {
   std::vector<rtc::scoped_refptr<RtpSenderInterface>> ret;
-  for (const auto& sender : senders_) {
-    ret.push_back(sender.get());
+  for (auto sender : GetSendersInternal()) {
+    ret.push_back(sender);
   }
   return ret;
 }
 
+std::vector<rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>>
+PeerConnection::GetSendersInternal() const {
+  std::vector<rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>>
+      all_senders;
+  for (auto transceiver : transceivers_) {
+    auto senders = transceiver->internal()->senders();
+    all_senders.insert(all_senders.end(), senders.begin(), senders.end());
+  }
+  return all_senders;
+}
+
 std::vector<rtc::scoped_refptr<RtpReceiverInterface>>
 PeerConnection::GetReceivers() const {
   std::vector<rtc::scoped_refptr<RtpReceiverInterface>> ret;
-  for (const auto& receiver : receivers_) {
-    ret.push_back(receiver.get());
+  for (const auto& receiver : GetReceiversInternal()) {
+    ret.push_back(receiver);
   }
   return ret;
 }
 
+std::vector<
+    rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>>
+PeerConnection::GetReceiversInternal() const {
+  std::vector<
+      rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>>
+      all_receivers;
+  for (auto transceiver : transceivers_) {
+    auto receivers = transceiver->internal()->receivers();
+    all_receivers.insert(all_receivers.end(), receivers.begin(),
+                         receivers.end());
+  }
+  return all_receivers;
+}
+
 bool PeerConnection::GetStats(StatsObserver* observer,
                               MediaStreamTrackInterface* track,
                               StatsOutputLevel level) {
@@ -1455,12 +1480,12 @@
       GetFirstAudioContent(local_description()->description());
   if (audio_content) {
     if (audio_content->rejected) {
-      RemoveTracks(cricket::MEDIA_TYPE_AUDIO);
+      RemoveSenders(cricket::MEDIA_TYPE_AUDIO);
     } else {
       const cricket::AudioContentDescription* audio_desc =
           static_cast<const cricket::AudioContentDescription*>(
               audio_content->description);
-      UpdateLocalTracks(audio_desc->streams(), audio_desc->type());
+      UpdateLocalSenders(audio_desc->streams(), audio_desc->type());
     }
   }
 
@@ -1468,12 +1493,12 @@
       GetFirstVideoContent(local_description()->description());
   if (video_content) {
     if (video_content->rejected) {
-      RemoveTracks(cricket::MEDIA_TYPE_VIDEO);
+      RemoveSenders(cricket::MEDIA_TYPE_VIDEO);
     } else {
       const cricket::VideoContentDescription* video_desc =
           static_cast<const cricket::VideoContentDescription*>(
               video_content->description);
-      UpdateLocalTracks(video_desc->streams(), video_desc->type());
+      UpdateLocalSenders(video_desc->streams(), video_desc->type());
     }
   }
 
@@ -1583,19 +1608,19 @@
   // rejected media section, there is some cleanup logic that expects the voice/
   // video channel to still be set. But in this method the voice/video channel
   // would have been destroyed by the SetRemoteDescription caller above so the
-  // cleanup that relies on them fails to run. The RemoveTracks calls should be
+  // cleanup that relies on them fails to run. The RemoveSenders calls should be
   // moved to right before the DestroyChannel calls to fix this.
 
   // Find all audio rtp streams and create corresponding remote AudioTracks
   // and MediaStreams.
   if (audio_content) {
     if (audio_content->rejected) {
-      RemoveTracks(cricket::MEDIA_TYPE_AUDIO);
+      RemoveSenders(cricket::MEDIA_TYPE_AUDIO);
     } else {
       bool default_audio_track_needed =
           !remote_peer_supports_msid_ &&
           MediaContentDirectionHasSend(audio_desc->direction());
-      UpdateRemoteStreamsList(GetActiveStreams(audio_desc),
+      UpdateRemoteSendersList(GetActiveStreams(audio_desc),
                               default_audio_track_needed, audio_desc->type(),
                               new_streams);
     }
@@ -1605,12 +1630,12 @@
   // and MediaStreams.
   if (video_content) {
     if (video_content->rejected) {
-      RemoveTracks(cricket::MEDIA_TYPE_VIDEO);
+      RemoveSenders(cricket::MEDIA_TYPE_VIDEO);
     } else {
       bool default_video_track_needed =
           !remote_peer_supports_msid_ &&
           MediaContentDirectionHasSend(video_desc->direction());
-      UpdateRemoteStreamsList(GetActiveStreams(video_desc),
+      UpdateRemoteSendersList(GetActiveStreams(video_desc),
                               default_video_track_needed, video_desc->type(),
                               new_streams);
     }
@@ -1987,8 +2012,8 @@
 
   ChangeSignalingState(PeerConnectionInterface::kClosed);
   RemoveUnusedChannels(nullptr);
-  RTC_DCHECK(voice_channels_.empty());
-  RTC_DCHECK(video_channels_.empty());
+  RTC_DCHECK(!GetAudioTransceiver()->internal()->channel());
+  RTC_DCHECK(!GetVideoTransceiver()->internal()->channel());
   RTC_DCHECK(!rtp_data_channel_);
   RTC_DCHECK(!sctp_transport_);
 
@@ -2045,31 +2070,33 @@
   }
 }
 
-void PeerConnection::CreateAudioReceiver(MediaStreamInterface* stream,
-                                         const std::string& track_id,
-                                         uint32_t ssrc) {
+void PeerConnection::CreateAudioReceiver(
+    MediaStreamInterface* stream,
+    const RtpSenderInfo& remote_sender_info) {
   rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
       receiver = RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
           signaling_thread(),
-          new AudioRtpReceiver(track_id, ssrc, voice_channel()));
+          new AudioRtpReceiver(remote_sender_info.sender_id,
+                               remote_sender_info.first_ssrc, voice_channel()));
   stream->AddTrack(
       static_cast<AudioTrackInterface*>(receiver->internal()->track().get()));
-  receivers_.push_back(receiver);
+  GetAudioTransceiver()->internal()->AddReceiver(receiver);
   std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams;
   streams.push_back(rtc::scoped_refptr<MediaStreamInterface>(stream));
   observer_->OnAddTrack(receiver, streams);
 }
 
-void PeerConnection::CreateVideoReceiver(MediaStreamInterface* stream,
-                                         const std::string& track_id,
-                                         uint32_t ssrc) {
+void PeerConnection::CreateVideoReceiver(
+    MediaStreamInterface* stream,
+    const RtpSenderInfo& remote_sender_info) {
   rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
       receiver = RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
-          signaling_thread(), new VideoRtpReceiver(track_id, worker_thread(),
-                                                   ssrc, video_channel()));
+          signaling_thread(),
+          new VideoRtpReceiver(remote_sender_info.sender_id, worker_thread(),
+                               remote_sender_info.first_ssrc, video_channel()));
   stream->AddTrack(
       static_cast<VideoTrackInterface*>(receiver->internal()->track().get()));
-  receivers_.push_back(receiver);
+  GetVideoTransceiver()->internal()->AddReceiver(receiver);
   std::vector<rtc::scoped_refptr<MediaStreamInterface>> streams;
   streams.push_back(rtc::scoped_refptr<MediaStreamInterface>(stream));
   observer_->OnAddTrack(receiver, streams);
@@ -2078,16 +2105,18 @@
 // TODO(deadbeef): Keep RtpReceivers around even if track goes away in remote
 // description.
 rtc::scoped_refptr<RtpReceiverInterface> PeerConnection::RemoveAndStopReceiver(
-    const std::string& track_id) {
-  auto it = FindReceiverForTrack(track_id);
-  if (it == receivers_.end()) {
-    RTC_LOG(LS_WARNING) << "RtpReceiver for track with id " << track_id
-                        << " doesn't exist.";
+    const RtpSenderInfo& remote_sender_info) {
+  auto receiver = FindReceiverById(remote_sender_info.sender_id);
+  if (!receiver) {
+    RTC_LOG(LS_WARNING) << "RtpReceiver for track with id "
+                        << remote_sender_info.sender_id << " doesn't exist.";
     return nullptr;
   }
-  (*it)->internal()->Stop();
-  rtc::scoped_refptr<RtpReceiverInterface> receiver = *it;
-  receivers_.erase(it);
+  if (receiver->media_type() == cricket::MEDIA_TYPE_AUDIO) {
+    GetAudioTransceiver()->internal()->RemoveReceiver(receiver);
+  } else {
+    GetVideoTransceiver()->internal()->RemoveReceiver(receiver);
+  }
   return receiver;
 }
 
@@ -2095,10 +2124,10 @@
                                    MediaStreamInterface* stream) {
   RTC_DCHECK(!IsClosed());
   auto sender = FindSenderForTrack(track);
-  if (sender != senders_.end()) {
+  if (sender) {
     // 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());
+    sender->internal()->set_stream_id(stream->label());
     return;
   }
 
@@ -2108,17 +2137,17 @@
           signaling_thread(),
           new AudioRtpSender(track, {stream->label()}, voice_channel(),
                              stats_.get()));
-  senders_.push_back(new_sender);
+  GetAudioTransceiver()->internal()->AddSender(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);
+  const RtpSenderInfo* sender_info =
+      FindSenderInfo(local_audio_sender_infos_, stream->label(), track->id());
+  if (sender_info) {
+    new_sender->internal()->SetSsrc(sender_info->first_ssrc);
   }
 }
 
@@ -2128,23 +2157,22 @@
                                       MediaStreamInterface* stream) {
   RTC_DCHECK(!IsClosed());
   auto sender = FindSenderForTrack(track);
-  if (sender == senders_.end()) {
+  if (!sender) {
     RTC_LOG(LS_WARNING) << "RtpSender for track with id " << track->id()
                         << " doesn't exist.";
     return;
   }
-  (*sender)->internal()->Stop();
-  senders_.erase(sender);
+  GetAudioTransceiver()->internal()->RemoveSender(sender);
 }
 
 void PeerConnection::AddVideoTrack(VideoTrackInterface* track,
                                    MediaStreamInterface* stream) {
   RTC_DCHECK(!IsClosed());
   auto sender = FindSenderForTrack(track);
-  if (sender != senders_.end()) {
+  if (sender) {
     // 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());
+    sender->internal()->set_stream_id(stream->label());
     return;
   }
 
@@ -2153,11 +2181,11 @@
       RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
           signaling_thread(),
           new VideoRtpSender(track, {stream->label()}, 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);
+  GetVideoTransceiver()->internal()->AddSender(new_sender);
+  const RtpSenderInfo* sender_info =
+      FindSenderInfo(local_video_sender_infos_, stream->label(), track->id());
+  if (sender_info) {
+    new_sender->internal()->SetSsrc(sender_info->first_ssrc);
   }
 }
 
@@ -2165,13 +2193,12 @@
                                       MediaStreamInterface* stream) {
   RTC_DCHECK(!IsClosed());
   auto sender = FindSenderForTrack(track);
-  if (sender == senders_.end()) {
+  if (!sender) {
     RTC_LOG(LS_WARNING) << "RtpSender for track with id " << track->id()
                         << " doesn't exist.";
     return;
   }
-  (*sender)->internal()->Stop();
-  senders_.erase(sender);
+  GetVideoTransceiver()->internal()->RemoveSender(sender);
 }
 
 void PeerConnection::SetIceConnectionState(IceConnectionState new_state) {
@@ -2386,7 +2413,7 @@
         configuration_.enable_ice_renomination;
   }
 
-  AddRtpSenderOptions(senders_, audio_media_description_options,
+  AddRtpSenderOptions(GetSendersInternal(), audio_media_description_options,
                       video_media_description_options);
   AddRtpDataChannelOptions(rtp_data_channels_, data_media_description_options);
 
@@ -2458,7 +2485,7 @@
         configuration_.enable_ice_renomination;
   }
 
-  AddRtpSenderOptions(senders_, audio_media_description_options,
+  AddRtpSenderOptions(GetSendersInternal(), audio_media_description_options,
                       video_media_description_options);
   AddRtpDataChannelOptions(rtp_data_channels_, data_media_description_options);
 
@@ -2536,43 +2563,45 @@
   }
 }
 
-void PeerConnection::RemoveTracks(cricket::MediaType media_type) {
-  UpdateLocalTracks(std::vector<cricket::StreamParams>(), media_type);
-  UpdateRemoteStreamsList(std::vector<cricket::StreamParams>(), false,
+void PeerConnection::RemoveSenders(cricket::MediaType media_type) {
+  UpdateLocalSenders(std::vector<cricket::StreamParams>(), media_type);
+  UpdateRemoteSendersList(std::vector<cricket::StreamParams>(), false,
                           media_type, nullptr);
 }
 
-void PeerConnection::UpdateRemoteStreamsList(
+void PeerConnection::UpdateRemoteSendersList(
     const cricket::StreamParamsVec& streams,
-    bool default_track_needed,
+    bool default_sender_needed,
     cricket::MediaType media_type,
     StreamCollection* new_streams) {
-  TrackInfos* current_tracks = GetRemoteTracks(media_type);
+  std::vector<RtpSenderInfo>* current_senders =
+      GetRemoteSenderInfos(media_type);
 
-  // Find removed tracks. I.e., tracks where the track id or ssrc don't match
+  // Find removed senders. I.e., senders where the sender id or ssrc don't match
   // the new StreamParam.
-  auto track_it = current_tracks->begin();
-  while (track_it != current_tracks->end()) {
-    const TrackInfo& info = *track_it;
+  for (auto sender_it = current_senders->begin();
+       sender_it != current_senders->end();
+       /* incremented manually */) {
+    const RtpSenderInfo& info = *sender_it;
     const cricket::StreamParams* params =
-        cricket::GetStreamBySsrc(streams, info.ssrc);
-    bool track_exists = params && params->id == info.track_id;
+        cricket::GetStreamBySsrc(streams, info.first_ssrc);
+    bool sender_exists = params && params->id == info.sender_id;
     // If this is a default track, and we still need it, don't remove it.
-    if ((info.stream_label == kDefaultStreamLabel && default_track_needed) ||
-        track_exists) {
-      ++track_it;
+    if ((info.stream_label == kDefaultStreamLabel && default_sender_needed) ||
+        sender_exists) {
+      ++sender_it;
     } else {
-      OnRemoteTrackRemoved(info.stream_label, info.track_id, media_type);
-      track_it = current_tracks->erase(track_it);
+      OnRemoteSenderRemoved(info, media_type);
+      sender_it = current_senders->erase(sender_it);
     }
   }
 
-  // Find new and active tracks.
+  // Find new and active senders.
   for (const cricket::StreamParams& params : streams) {
     // The sync_label is the MediaStream label and the |stream.id| is the
-    // track id.
+    // sender id.
     const std::string& stream_label = params.sync_label;
-    const std::string& track_id = params.id;
+    const std::string& sender_id = params.id;
     uint32_t ssrc = params.first_ssrc();
 
     rtc::scoped_refptr<MediaStreamInterface> stream =
@@ -2585,16 +2614,16 @@
       new_streams->AddStream(stream);
     }
 
-    const TrackInfo* track_info =
-        FindTrackInfo(*current_tracks, stream_label, track_id);
-    if (!track_info) {
-      current_tracks->push_back(TrackInfo(stream_label, track_id, ssrc));
-      OnRemoteTrackSeen(stream_label, track_id, ssrc, media_type);
+    const RtpSenderInfo* sender_info =
+        FindSenderInfo(*current_senders, stream_label, sender_id);
+    if (!sender_info) {
+      current_senders->push_back(RtpSenderInfo(stream_label, sender_id, ssrc));
+      OnRemoteSenderAdded(current_senders->back(), media_type);
     }
   }
 
-  // Add default track if necessary.
-  if (default_track_needed) {
+  // Add default sender if necessary.
+  if (default_sender_needed) {
     rtc::scoped_refptr<MediaStreamInterface> default_stream =
         remote_streams_->find(kDefaultStreamLabel);
     if (!default_stream) {
@@ -2604,55 +2633,54 @@
       remote_streams_->AddStream(default_stream);
       new_streams->AddStream(default_stream);
     }
-    std::string default_track_id = (media_type == cricket::MEDIA_TYPE_AUDIO)
-                                       ? kDefaultAudioTrackLabel
-                                       : kDefaultVideoTrackLabel;
-    const TrackInfo* default_track_info =
-        FindTrackInfo(*current_tracks, kDefaultStreamLabel, default_track_id);
-    if (!default_track_info) {
-      current_tracks->push_back(
-          TrackInfo(kDefaultStreamLabel, default_track_id, 0));
-      OnRemoteTrackSeen(kDefaultStreamLabel, default_track_id, 0, media_type);
+    std::string default_sender_id = (media_type == cricket::MEDIA_TYPE_AUDIO)
+                                        ? kDefaultAudioSenderId
+                                        : kDefaultVideoSenderId;
+    const RtpSenderInfo* default_sender_info = FindSenderInfo(
+        *current_senders, kDefaultStreamLabel, default_sender_id);
+    if (!default_sender_info) {
+      current_senders->push_back(
+          RtpSenderInfo(kDefaultStreamLabel, default_sender_id, 0));
+      OnRemoteSenderAdded(current_senders->back(), media_type);
     }
   }
 }
 
-void PeerConnection::OnRemoteTrackSeen(const std::string& stream_label,
-                                       const std::string& track_id,
-                                       uint32_t ssrc,
-                                       cricket::MediaType media_type) {
-  MediaStreamInterface* stream = remote_streams_->find(stream_label);
+void PeerConnection::OnRemoteSenderAdded(const RtpSenderInfo& sender_info,
+                                         cricket::MediaType media_type) {
+  MediaStreamInterface* stream =
+      remote_streams_->find(sender_info.stream_label);
 
   if (media_type == cricket::MEDIA_TYPE_AUDIO) {
-    CreateAudioReceiver(stream, track_id, ssrc);
+    CreateAudioReceiver(stream, sender_info);
   } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
-    CreateVideoReceiver(stream, track_id, ssrc);
+    CreateVideoReceiver(stream, sender_info);
   } else {
     RTC_NOTREACHED() << "Invalid media type";
   }
 }
 
-void PeerConnection::OnRemoteTrackRemoved(const std::string& stream_label,
-                                          const std::string& track_id,
-                                          cricket::MediaType media_type) {
-  MediaStreamInterface* stream = remote_streams_->find(stream_label);
+void PeerConnection::OnRemoteSenderRemoved(const RtpSenderInfo& sender_info,
+                                           cricket::MediaType media_type) {
+  MediaStreamInterface* stream =
+      remote_streams_->find(sender_info.stream_label);
 
   rtc::scoped_refptr<RtpReceiverInterface> receiver;
   if (media_type == cricket::MEDIA_TYPE_AUDIO) {
     // When the MediaEngine audio channel is destroyed, the RemoteAudioSource
     // will be notified which will end the AudioRtpReceiver::track().
-    receiver = RemoveAndStopReceiver(track_id);
+    receiver = RemoveAndStopReceiver(sender_info);
     rtc::scoped_refptr<AudioTrackInterface> audio_track =
-        stream->FindAudioTrack(track_id);
+        stream->FindAudioTrack(sender_info.sender_id);
     if (audio_track) {
       stream->RemoveTrack(audio_track);
     }
   } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
     // Stopping or destroying a VideoRtpReceiver will end the
     // VideoRtpReceiver::track().
-    receiver = RemoveAndStopReceiver(track_id);
+    receiver = RemoveAndStopReceiver(sender_info);
     rtc::scoped_refptr<VideoTrackInterface> video_track =
-        stream->FindVideoTrack(track_id);
+        stream->FindVideoTrack(sender_info.sender_id);
     if (video_track) {
       // There's no guarantee the track is still available, e.g. the track may
       // have been removed from the stream by an application.
@@ -2681,51 +2709,50 @@
   }
 }
 
-void PeerConnection::UpdateLocalTracks(
+void PeerConnection::UpdateLocalSenders(
     const std::vector<cricket::StreamParams>& streams,
     cricket::MediaType media_type) {
-  TrackInfos* current_tracks = GetLocalTracks(media_type);
+  std::vector<RtpSenderInfo>* current_senders = GetLocalSenderInfos(media_type);
 
   // Find removed tracks. I.e., tracks where the track id, stream label or ssrc
   // don't match the new StreamParam.
-  TrackInfos::iterator track_it = current_tracks->begin();
-  while (track_it != current_tracks->end()) {
-    const TrackInfo& info = *track_it;
+  for (auto sender_it = current_senders->begin();
+       sender_it != current_senders->end();
+       /* incremented manually */) {
+    const RtpSenderInfo& info = *sender_it;
     const cricket::StreamParams* params =
-        cricket::GetStreamBySsrc(streams, info.ssrc);
-    if (!params || params->id != info.track_id ||
+        cricket::GetStreamBySsrc(streams, info.first_ssrc);
+    if (!params || params->id != info.sender_id ||
         params->sync_label != info.stream_label) {
-      OnLocalTrackRemoved(info.stream_label, info.track_id, info.ssrc,
-                          media_type);
-      track_it = current_tracks->erase(track_it);
+      OnLocalSenderRemoved(info, media_type);
+      sender_it = current_senders->erase(sender_it);
     } else {
-      ++track_it;
+      ++sender_it;
     }
   }
 
-  // Find new and active tracks.
+  // Find new and active senders.
   for (const cricket::StreamParams& params : streams) {
     // The sync_label is the MediaStream label and the |stream.id| is the
-    // track id.
+    // sender id.
     const std::string& stream_label = params.sync_label;
-    const std::string& track_id = params.id;
+    const std::string& sender_id = params.id;
     uint32_t ssrc = params.first_ssrc();
-    const TrackInfo* track_info =
-        FindTrackInfo(*current_tracks, stream_label, track_id);
-    if (!track_info) {
-      current_tracks->push_back(TrackInfo(stream_label, track_id, ssrc));
-      OnLocalTrackSeen(stream_label, track_id, params.first_ssrc(), media_type);
+    const RtpSenderInfo* sender_info =
+        FindSenderInfo(*current_senders, stream_label, sender_id);
+    if (!sender_info) {
+      current_senders->push_back(RtpSenderInfo(stream_label, sender_id, ssrc));
+      OnLocalSenderAdded(current_senders->back(), media_type);
     }
   }
 }
 
-void PeerConnection::OnLocalTrackSeen(const std::string& stream_label,
-                                      const std::string& track_id,
-                                      uint32_t ssrc,
-                                      cricket::MediaType media_type) {
-  RtpSenderInternal* sender = FindSenderById(track_id);
+void PeerConnection::OnLocalSenderAdded(const RtpSenderInfo& sender_info,
+                                        cricket::MediaType media_type) {
+  auto sender = FindSenderById(sender_info.sender_id);
   if (!sender) {
-    RTC_LOG(LS_WARNING) << "An unknown RtpSender with id " << track_id
+    RTC_LOG(LS_WARNING) << "An unknown RtpSender with id "
+                        << sender_info.sender_id
                         << " has been configured in the local description.";
     return;
   }
@@ -2736,15 +2763,13 @@
     return;
   }
 
-  sender->set_stream_id(stream_label);
-  sender->SetSsrc(ssrc);
+  sender->internal()->set_stream_id(sender_info.stream_label);
+  sender->internal()->SetSsrc(sender_info.first_ssrc);
 }
 
-void PeerConnection::OnLocalTrackRemoved(const std::string& stream_label,
-                                         const std::string& track_id,
-                                         uint32_t ssrc,
-                                         cricket::MediaType media_type) {
-  RtpSenderInternal* sender = FindSenderById(track_id);
+void PeerConnection::OnLocalSenderRemoved(const RtpSenderInfo& sender_info,
+                                          cricket::MediaType media_type) {
+  auto sender = FindSenderById(sender_info.sender_id);
   if (!sender) {
     // This is the normal case. I.e., RemoveStream has been called and the
     // SessionDescriptions has been renegotiated.
@@ -2760,7 +2785,7 @@
     return;
   }
 
-  sender->SetSsrc(0);
+  sender->internal()->SetSsrc(0);
 }
 
 void PeerConnection::UpdateLocalRtpDataChannels(
@@ -2975,71 +3000,110 @@
   observer_->OnDataChannel(std::move(proxy_channel));
 }
 
+rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
+PeerConnection::GetAudioTransceiver() const {
+  // This method only works with Plan B SDP, where there is a single
+  // audio/video transceiver.
+  RTC_DCHECK(!IsUnifiedPlan());
+  for (auto transceiver : transceivers_) {
+    if (transceiver->internal()->media_type() == cricket::MEDIA_TYPE_AUDIO) {
+      return transceiver;
+    }
+  }
+  RTC_NOTREACHED();
+  return nullptr;
+}
+
+rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
+PeerConnection::GetVideoTransceiver() const {
+  // This method only works with Plan B SDP, where there is a single
+  // audio/video transceiver.
+  RTC_DCHECK(!IsUnifiedPlan());
+  for (auto transceiver : transceivers_) {
+    if (transceiver->internal()->media_type() == cricket::MEDIA_TYPE_VIDEO) {
+      return transceiver;
+    }
+  }
+  RTC_NOTREACHED();
+  return nullptr;
+}
+
+// TODO(bugs.webrtc.org/7600): Remove this when multiple transceivers with
+// individual transceiver directions are supported.
 bool PeerConnection::HasRtpSender(cricket::MediaType type) const {
-  return std::find_if(
-             senders_.begin(), senders_.end(),
-             [type](const rtc::scoped_refptr<
-                    RtpSenderProxyWithInternal<RtpSenderInternal>>& sender) {
-               return sender->media_type() == type;
-             }) != senders_.end();
+  switch (type) {
+    case cricket::MEDIA_TYPE_AUDIO:
+      return !GetAudioTransceiver()->internal()->senders().empty();
+    case cricket::MEDIA_TYPE_VIDEO:
+      return !GetVideoTransceiver()->internal()->senders().empty();
+    case cricket::MEDIA_TYPE_DATA:
+      return false;
+  }
+  RTC_NOTREACHED();
+  return false;
 }
 
-RtpSenderInternal* PeerConnection::FindSenderById(const std::string& id) {
-  auto it = std::find_if(
-      senders_.begin(), senders_.end(),
-      [id](const rtc::scoped_refptr<
-           RtpSenderProxyWithInternal<RtpSenderInternal>>& sender) {
-        return sender->id() == id;
-      });
-  return it != senders_.end() ? (*it)->internal() : nullptr;
+rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>
+PeerConnection::FindSenderForTrack(MediaStreamTrackInterface* track) const {
+  for (auto transceiver : transceivers_) {
+    for (auto sender : transceiver->internal()->senders()) {
+      if (sender->track() == track) {
+        return sender;
+      }
+    }
+  }
+  return nullptr;
 }
 
-std::vector<
-    rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>>::iterator
-PeerConnection::FindSenderForTrack(MediaStreamTrackInterface* track) {
-  return std::find_if(
-      senders_.begin(), senders_.end(),
-      [track](const rtc::scoped_refptr<
-              RtpSenderProxyWithInternal<RtpSenderInternal>>& sender) {
-        return sender->track() == track;
-      });
+rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>
+PeerConnection::FindSenderById(const std::string& sender_id) const {
+  for (auto transceiver : transceivers_) {
+    for (auto sender : transceiver->internal()->senders()) {
+      if (sender->id() == sender_id) {
+        return sender;
+      }
+    }
+  }
+  return nullptr;
 }
 
-std::vector<rtc::scoped_refptr<
-    RtpReceiverProxyWithInternal<RtpReceiverInternal>>>::iterator
-PeerConnection::FindReceiverForTrack(const std::string& track_id) {
-  return std::find_if(
-      receivers_.begin(), receivers_.end(),
-      [track_id](const rtc::scoped_refptr<
-                 RtpReceiverProxyWithInternal<RtpReceiverInternal>>& receiver) {
-        return receiver->id() == track_id;
-      });
+rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
+PeerConnection::FindReceiverById(const std::string& receiver_id) const {
+  for (auto transceiver : transceivers_) {
+    for (auto receiver : transceiver->internal()->receivers()) {
+      if (receiver->id() == receiver_id) {
+        return receiver;
+      }
+    }
+  }
+  return nullptr;
 }
 
-PeerConnection::TrackInfos* PeerConnection::GetRemoteTracks(
+std::vector<PeerConnection::RtpSenderInfo>*
+PeerConnection::GetRemoteSenderInfos(cricket::MediaType media_type) {
+  RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO ||
+             media_type == cricket::MEDIA_TYPE_VIDEO);
+  return (media_type == cricket::MEDIA_TYPE_AUDIO)
+             ? &remote_audio_sender_infos_
+             : &remote_video_sender_infos_;
+}
+
+std::vector<PeerConnection::RtpSenderInfo>* PeerConnection::GetLocalSenderInfos(
     cricket::MediaType media_type) {
   RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO ||
              media_type == cricket::MEDIA_TYPE_VIDEO);
-  return (media_type == cricket::MEDIA_TYPE_AUDIO) ? &remote_audio_tracks_
-                                                   : &remote_video_tracks_;
+  return (media_type == cricket::MEDIA_TYPE_AUDIO) ? &local_audio_sender_infos_
+                                                   : &local_video_sender_infos_;
 }
 
-PeerConnection::TrackInfos* PeerConnection::GetLocalTracks(
-    cricket::MediaType media_type) {
-  RTC_DCHECK(media_type == cricket::MEDIA_TYPE_AUDIO ||
-             media_type == cricket::MEDIA_TYPE_VIDEO);
-  return (media_type == cricket::MEDIA_TYPE_AUDIO) ? &local_audio_tracks_
-                                                   : &local_video_tracks_;
-}
-
-const PeerConnection::TrackInfo* PeerConnection::FindTrackInfo(
-    const PeerConnection::TrackInfos& infos,
+const PeerConnection::RtpSenderInfo* PeerConnection::FindSenderInfo(
+    const std::vector<PeerConnection::RtpSenderInfo>& infos,
     const std::string& stream_label,
-    const std::string track_id) const {
-  for (const TrackInfo& track_info : infos) {
-    if (track_info.stream_label == stream_label &&
-        track_info.track_id == track_id) {
-      return &track_info;
+    const std::string sender_id) const {
+  for (const RtpSenderInfo& sender_info : infos) {
+    if (sender_info.stream_label == stream_label &&
+        sender_info.sender_id == sender_id) {
+      return &sender_info;
     }
   }
   return nullptr;
@@ -3357,10 +3421,12 @@
 // vectors. At that point, this will become a simple getter.
 std::vector<cricket::BaseChannel*> PeerConnection::Channels() const {
   std::vector<cricket::BaseChannel*> channels;
-  channels.insert(channels.end(), voice_channels_.begin(),
-                  voice_channels_.end());
-  channels.insert(channels.end(), video_channels_.begin(),
-                  video_channels_.end());
+  if (voice_channel()) {
+    channels.push_back(voice_channel());
+  }
+  if (video_channel()) {
+    channels.push_back(video_channel());
+  }
   if (rtp_data_channel_) {
     channels.push_back(rtp_data_channel_);
   }
@@ -3942,20 +4008,17 @@
 
 // Enabling voice and video (and RTP data) channels.
 void PeerConnection::EnableChannels() {
-  for (cricket::VoiceChannel* voice_channel : voice_channels_) {
-    if (!voice_channel->enabled()) {
-      voice_channel->Enable(true);
-    }
+  if (voice_channel() && !voice_channel()->enabled()) {
+    voice_channel()->Enable(true);
   }
 
-  for (cricket::VideoChannel* video_channel : video_channels_) {
-    if (!video_channel->enabled()) {
-      video_channel->Enable(true);
-    }
+  if (video_channel() && !video_channel()->enabled()) {
+    video_channel()->Enable(true);
   }
 
-  if (rtp_data_channel_ && !rtp_data_channel_->enabled())
+  if (rtp_data_channel_ && !rtp_data_channel_->enabled()) {
     rtp_data_channel_->Enable(true);
+  }
 }
 
 // Returns the media index for a local ice candidate given the content name.
@@ -4053,12 +4116,12 @@
   // voice channel.
   const cricket::ContentInfo* video_info = cricket::GetFirstVideoContent(desc);
   if ((!video_info || video_info->rejected) && video_channel()) {
-    RemoveAndDestroyVideoChannel(video_channel());
+    DestroyVideoChannel(video_channel());
   }
 
   const cricket::ContentInfo* voice_info = cricket::GetFirstAudioContent(desc);
   if ((!voice_info || voice_info->rejected) && voice_channel()) {
-    RemoveAndDestroyVoiceChannel(voice_channel());
+    DestroyVoiceChannel(voice_channel());
   }
 
   const cricket::ContentInfo* data_info = cricket::GetFirstDataContent(desc);
@@ -4139,11 +4202,12 @@
   return true;
 }
 
+// TODO(steveanton): Perhaps this should be managed by the RtpTransceiver.
 bool PeerConnection::CreateVoiceChannel(const cricket::ContentInfo* content,
                                         const std::string* bundle_transport) {
   // TODO(steveanton): Check to see if it's safe to create multiple voice
   // channels.
-  RTC_DCHECK(voice_channels_.empty());
+  RTC_DCHECK(!voice_channel());
 
   std::string transport_name =
       bundle_transport ? *bundle_transport : content->name;
@@ -4171,26 +4235,24 @@
     }
     return false;
   }
-
-  voice_channels_.push_back(voice_channel);
-
   voice_channel->SignalRtcpMuxFullyActive.connect(
       this, &PeerConnection::DestroyRtcpTransport_n);
   voice_channel->SignalDtlsSrtpSetupFailure.connect(
       this, &PeerConnection::OnDtlsSrtpSetupFailure);
-
-  SetChannelOnSendersAndReceivers<AudioRtpSender, AudioRtpReceiver>(
-      voice_channel, senders_, receivers_, cricket::MEDIA_TYPE_AUDIO);
   voice_channel->SignalSentPacket.connect(this,
                                           &PeerConnection::OnSentPacket_w);
+
+  GetAudioTransceiver()->internal()->SetChannel(voice_channel);
+
   return true;
 }
 
+// TODO(steveanton): Perhaps this should be managed by the RtpTransceiver.
 bool PeerConnection::CreateVideoChannel(const cricket::ContentInfo* content,
                                         const std::string* bundle_transport) {
   // TODO(steveanton): Check to see if it's safe to create multiple video
   // channels.
-  RTC_DCHECK(video_channels_.empty());
+  RTC_DCHECK(!video_channel());
 
   std::string transport_name =
       bundle_transport ? *bundle_transport : content->name;
@@ -4219,18 +4281,15 @@
     }
     return false;
   }
-
-  video_channels_.push_back(video_channel);
-
   video_channel->SignalRtcpMuxFullyActive.connect(
       this, &PeerConnection::DestroyRtcpTransport_n);
   video_channel->SignalDtlsSrtpSetupFailure.connect(
       this, &PeerConnection::OnDtlsSrtpSetupFailure);
-
-  SetChannelOnSendersAndReceivers<VideoRtpSender, VideoRtpReceiver>(
-      video_channel, senders_, receivers_, cricket::MEDIA_TYPE_VIDEO);
   video_channel->SignalSentPacket.connect(this,
                                           &PeerConnection::OnSentPacket_w);
+
+  GetVideoTransceiver()->internal()->SetChannel(video_channel);
+
   return true;
 }
 
@@ -4769,23 +4828,12 @@
       transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP);
 }
 
-void PeerConnection::RemoveAndDestroyVideoChannel(
-    cricket::VideoChannel* video_channel) {
-  auto it =
-      std::find(video_channels_.begin(), video_channels_.end(), video_channel);
-  RTC_DCHECK(it != video_channels_.end());
-  if (it == video_channels_.end()) {
-    return;
-  }
-  video_channels_.erase(it);
-  DestroyVideoChannel(video_channel);
-}
-
+// TODO(steveanton): Perhaps this should be managed by the RtpTransceiver.
 void PeerConnection::DestroyVideoChannel(cricket::VideoChannel* video_channel) {
-  SetChannelOnSendersAndReceivers<VideoRtpSender, VideoRtpReceiver,
-                                  cricket::VideoChannel>(
-      nullptr, senders_, receivers_, cricket::MEDIA_TYPE_VIDEO);
+  RTC_DCHECK(video_channel);
   RTC_DCHECK(video_channel->rtp_dtls_transport());
+  RTC_DCHECK_EQ(GetVideoTransceiver()->internal()->channel(), video_channel);
+  GetVideoTransceiver()->internal()->SetChannel(nullptr);
   const std::string transport_name =
       video_channel->rtp_dtls_transport()->transport_name();
   const bool need_to_delete_rtcp =
@@ -4801,23 +4849,12 @@
   }
 }
 
-void PeerConnection::RemoveAndDestroyVoiceChannel(
-    cricket::VoiceChannel* voice_channel) {
-  auto it =
-      std::find(voice_channels_.begin(), voice_channels_.end(), voice_channel);
-  RTC_DCHECK(it != voice_channels_.end());
-  if (it == voice_channels_.end()) {
-    return;
-  }
-  voice_channels_.erase(it);
-  DestroyVoiceChannel(voice_channel);
-}
-
+// TODO(steveanton): Perhaps this should be managed by the RtpTransceiver.
 void PeerConnection::DestroyVoiceChannel(cricket::VoiceChannel* voice_channel) {
-  SetChannelOnSendersAndReceivers<AudioRtpSender, AudioRtpReceiver,
-                                  cricket::VoiceChannel>(
-      nullptr, senders_, receivers_, cricket::MEDIA_TYPE_AUDIO);
+  RTC_DCHECK(voice_channel);
   RTC_DCHECK(voice_channel->rtp_dtls_transport());
+  RTC_DCHECK_EQ(GetAudioTransceiver()->internal()->channel(), voice_channel);
+  GetAudioTransceiver()->internal()->SetChannel(nullptr);
   const std::string transport_name =
       voice_channel->rtp_dtls_transport()->transport_name();
   const bool need_to_delete_rtcp =
diff --git a/pc/peerconnection.h b/pc/peerconnection.h
index e0bf2b8..ae6b537 100644
--- a/pc/peerconnection.h
+++ b/pc/peerconnection.h
@@ -22,8 +22,7 @@
 #include "pc/iceserverparsing.h"
 #include "pc/peerconnectionfactory.h"
 #include "pc/rtcstatscollector.h"
-#include "pc/rtpreceiver.h"
-#include "pc/rtpsender.h"
+#include "pc/rtptransceiver.h"
 #include "pc/statscollector.h"
 #include "pc/streamcollection.h"
 #include "pc/webrtcsessiondescriptionfactory.h"
@@ -223,19 +222,13 @@
 
   // Exposed for stats collecting.
   // TODO(steveanton): Switch callers to use the plural form and remove these.
-  virtual cricket::VoiceChannel* voice_channel() {
-    if (voice_channels_.empty()) {
-      return nullptr;
-    } else {
-      return voice_channels_[0];
-    }
+  virtual cricket::VoiceChannel* voice_channel() const {
+    return static_cast<cricket::VoiceChannel*>(
+        GetAudioTransceiver()->internal()->channel());
   }
-  virtual cricket::VideoChannel* video_channel() {
-    if (video_channels_.empty()) {
-      return nullptr;
-    } else {
-      return video_channels_[0];
-    }
+  virtual cricket::VideoChannel* video_channel() const {
+    return static_cast<cricket::VideoChannel*>(
+        GetVideoTransceiver()->internal()->channel());
   }
 
   // Only valid when using deprecated RTP data channels.
@@ -277,34 +270,45 @@
   ~PeerConnection() override;
 
  private:
-  struct TrackInfo {
-    TrackInfo() : ssrc(0) {}
-    TrackInfo(const std::string& stream_label,
-              const std::string track_id,
-              uint32_t ssrc)
-        : stream_label(stream_label), track_id(track_id), ssrc(ssrc) {}
-    bool operator==(const TrackInfo& other) {
+  struct RtpSenderInfo {
+    RtpSenderInfo() : first_ssrc(0) {}
+    RtpSenderInfo(const std::string& stream_label,
+                  const std::string sender_id,
+                  uint32_t ssrc)
+        : stream_label(stream_label), sender_id(sender_id), first_ssrc(ssrc) {}
+    bool operator==(const RtpSenderInfo& other) {
       return this->stream_label == other.stream_label &&
-             this->track_id == other.track_id && this->ssrc == other.ssrc;
+             this->sender_id == other.sender_id &&
+             this->first_ssrc == other.first_ssrc;
     }
     std::string stream_label;
-    std::string track_id;
-    uint32_t ssrc;
+    std::string sender_id;
+    // An RtpSender can have many SSRCs. The first one is used as a sort of ID
+    // for communicating with the lower layers.
+    uint32_t first_ssrc;
   };
-  typedef std::vector<TrackInfo> TrackInfos;
 
   // Implements MessageHandler.
   void OnMessage(rtc::Message* msg) override;
 
+  std::vector<rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>>
+  GetSendersInternal() const;
+  std::vector<
+      rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>>
+  GetReceiversInternal() const;
+
+  rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
+  GetAudioTransceiver() const;
+  rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>
+  GetVideoTransceiver() const;
+
   void CreateAudioReceiver(MediaStreamInterface* stream,
-                           const std::string& track_id,
-                           uint32_t ssrc);
+                           const RtpSenderInfo& remote_sender_info);
 
   void CreateVideoReceiver(MediaStreamInterface* stream,
-                           const std::string& track_id,
-                           uint32_t ssrc);
+                           const RtpSenderInfo& remote_sender_info);
   rtc::scoped_refptr<RtpReceiverInterface> RemoveAndStopReceiver(
-      const std::string& track_id);
+      const RtpSenderInfo& remote_sender_info);
 
   // May be called either by AddStream/RemoveStream, or when a track is
   // added/removed from a stream previously added via AddStream.
@@ -369,9 +373,9 @@
       rtc::Optional<size_t>* data_index,
       cricket::MediaSessionOptions* session_options);
 
-  // Remove all local and remote tracks of type |media_type|.
+  // Remove all local and remote senders of type |media_type|.
   // Called when a media type is rejected (m-line set to port 0).
-  void RemoveTracks(cricket::MediaType media_type);
+  void RemoveSenders(cricket::MediaType media_type);
 
   // Makes sure a MediaStreamTrack is created for each StreamParam in |streams|,
   // and existing MediaStreamTracks are removed if there is no corresponding
@@ -379,26 +383,23 @@
   // is created if it doesn't exist; if false, it's removed if it exists.
   // |media_type| is the type of the |streams| and can be either audio or video.
   // If a new MediaStream is created it is added to |new_streams|.
-  void UpdateRemoteStreamsList(
+  void UpdateRemoteSendersList(
       const std::vector<cricket::StreamParams>& streams,
       bool default_track_needed,
       cricket::MediaType media_type,
       StreamCollection* new_streams);
 
-  // Triggered when a remote track has been seen for the first time in a remote
+  // Triggered when a remote sender has been seen for the first time in a remote
   // session description. It creates a remote MediaStreamTrackInterface
   // implementation and triggers CreateAudioReceiver or CreateVideoReceiver.
-  void OnRemoteTrackSeen(const std::string& stream_label,
-                         const std::string& track_id,
-                         uint32_t ssrc,
-                         cricket::MediaType media_type);
+  void OnRemoteSenderAdded(const RtpSenderInfo& sender_info,
+                           cricket::MediaType media_type);
 
-  // Triggered when a remote track has been removed from a remote session
-  // description. It removes the remote track with id |track_id| from a remote
+  // Triggered when a remote sender has been removed from a remote session
+  // description. It removes the remote sender with id |sender_id| from a remote
   // MediaStream and triggers DestroyAudioReceiver or DestroyVideoReceiver.
-  void OnRemoteTrackRemoved(const std::string& stream_label,
-                            const std::string& track_id,
-                            cricket::MediaType media_type);
+  void OnRemoteSenderRemoved(const RtpSenderInfo& sender_info,
+                             cricket::MediaType media_type);
 
   // Finds remote MediaStreams without any tracks and removes them from
   // |remote_streams_| and notifies the observer that the MediaStreams no longer
@@ -407,30 +408,26 @@
 
   // Loops through the vector of |streams| and finds added and removed
   // StreamParams since last time this method was called.
-  // For each new or removed StreamParam, OnLocalTrackSeen or
-  // OnLocalTrackRemoved is invoked.
-  void UpdateLocalTracks(const std::vector<cricket::StreamParams>& streams,
-                         cricket::MediaType media_type);
+  // For each new or removed StreamParam, OnLocalSenderSeen or
+  // OnLocalSenderRemoved is invoked.
+  void UpdateLocalSenders(const std::vector<cricket::StreamParams>& streams,
+                          cricket::MediaType media_type);
 
-  // Triggered when a local track has been seen for the first time in a local
+  // Triggered when a local sender has been seen for the first time in a local
   // session description.
   // This method triggers CreateAudioSender or CreateVideoSender if the rtp
   // streams in the local SessionDescription can be mapped to a MediaStreamTrack
   // in a MediaStream in |local_streams_|
-  void OnLocalTrackSeen(const std::string& stream_label,
-                        const std::string& track_id,
-                        uint32_t ssrc,
-                        cricket::MediaType media_type);
+  void OnLocalSenderAdded(const RtpSenderInfo& sender_info,
+                          cricket::MediaType media_type);
 
-  // Triggered when a local track has been removed from a local session
+  // Triggered when a local sender has been removed from a local session
   // description.
   // This method triggers DestroyAudioSender or DestroyVideoSender if a stream
   // has been removed from the local SessionDescription and the stream can be
   // mapped to a MediaStreamTrack in a MediaStream in |local_streams_|.
-  void OnLocalTrackRemoved(const std::string& stream_label,
-                           const std::string& track_id,
-                           uint32_t ssrc,
-                           cricket::MediaType media_type);
+  void OnLocalSenderRemoved(const RtpSenderInfo& sender_info,
+                            cricket::MediaType media_type);
 
   void UpdateLocalRtpDataChannels(const cricket::StreamParamsVec& streams);
   void UpdateRemoteRtpDataChannels(const cricket::StreamParamsVec& streams);
@@ -457,21 +454,36 @@
   void OnDataChannelOpenMessage(const std::string& label,
                                 const InternalDataChannelInit& config);
 
+  // Returns true if the PeerConnection is configured to use Unified Plan
+  // semantics for creating offers/answers and setting local/remote
+  // descriptions. If this is true the RtpTransceiver API will also be available
+  // to the user. If this is false, Plan B semantics are assumed.
+  // TODO(steveanton): Flip the default to be Unified Plan once sufficient time
+  // has passed.
+  bool IsUnifiedPlan() const { return false; }
+
+  // Is there an RtpSender of the given type?
   bool HasRtpSender(cricket::MediaType type) const;
-  RtpSenderInternal* FindSenderById(const std::string& id);
 
-  std::vector<rtc::scoped_refptr<
-      RtpSenderProxyWithInternal<RtpSenderInternal>>>::iterator
-  FindSenderForTrack(MediaStreamTrackInterface* track);
-  std::vector<rtc::scoped_refptr<
-      RtpReceiverProxyWithInternal<RtpReceiverInternal>>>::iterator
-  FindReceiverForTrack(const std::string& track_id);
+  // Return the RtpSender with the given track attached.
+  rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>
+  FindSenderForTrack(MediaStreamTrackInterface* track) const;
 
-  TrackInfos* GetRemoteTracks(cricket::MediaType media_type);
-  TrackInfos* GetLocalTracks(cricket::MediaType media_type);
-  const TrackInfo* FindTrackInfo(const TrackInfos& infos,
-                                 const std::string& stream_label,
-                                 const std::string track_id) const;
+  // Return the RtpSender with the given id, or null if none exists.
+  rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>
+  FindSenderById(const std::string& sender_id) const;
+
+  // Return the RtpReceiver with the given id, or null if none exists.
+  rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
+  FindReceiverById(const std::string& receiver_id) const;
+
+  std::vector<RtpSenderInfo>* GetRemoteSenderInfos(
+      cricket::MediaType media_type);
+  std::vector<RtpSenderInfo>* GetLocalSenderInfos(
+      cricket::MediaType media_type);
+  const RtpSenderInfo* FindSenderInfo(const std::vector<RtpSenderInfo>& infos,
+                                      const std::string& stream_label,
+                                      const std::string sender_id) const;
 
   // Returns the specified SCTP DataChannel in sctp_data_channels_,
   // or nullptr if not found.
@@ -518,13 +530,6 @@
   Error error() const { return error_; }
   const std::string& error_desc() const { return error_desc_; }
 
-  virtual std::vector<cricket::VoiceChannel*> voice_channels() const {
-    return voice_channels_;
-  }
-  virtual std::vector<cricket::VideoChannel*> video_channels() const {
-    return video_channels_;
-  }
-
   cricket::BaseChannel* GetChannel(const std::string& content_name);
 
   // Get current SSL role used by SCTP's underlying transport.
@@ -760,11 +765,11 @@
 
   std::vector<std::unique_ptr<MediaStreamObserver>> stream_observers_;
 
-  // These lists store track info seen in local/remote descriptions.
-  TrackInfos remote_audio_tracks_;
-  TrackInfos remote_video_tracks_;
-  TrackInfos local_audio_tracks_;
-  TrackInfos local_video_tracks_;
+  // These lists store sender info seen in local/remote descriptions.
+  std::vector<RtpSenderInfo> remote_audio_sender_infos_;
+  std::vector<RtpSenderInfo> remote_video_sender_infos_;
+  std::vector<RtpSenderInfo> local_audio_sender_infos_;
+  std::vector<RtpSenderInfo> local_video_sender_infos_;
 
   SctpSidAllocator sid_allocator_;
   // label -> DataChannel
@@ -778,11 +783,9 @@
   std::unique_ptr<StatsCollector> stats_;  // A pointer is passed to senders_
   rtc::scoped_refptr<RTCStatsCollector> stats_collector_;
 
-  std::vector<rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>>>
-      senders_;
   std::vector<
-      rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>>
-      receivers_;
+      rtc::scoped_refptr<RtpTransceiverProxyWithInternal<RtpTransceiver>>>
+      transceivers_;
 
   Error error_ = ERROR_NONE;
   std::string error_desc_;
@@ -792,13 +795,6 @@
 
   std::unique_ptr<cricket::TransportController> transport_controller_;
   std::unique_ptr<cricket::SctpTransportInternalFactory> sctp_factory_;
-  // TODO(steveanton): voice_channels_ and video_channels_ used to be a single
-  // VoiceChannel/VideoChannel respectively but are being changed to support
-  // multiple m= lines in unified plan. But until more work is done, these can
-  // only have 0 or 1 channel each.
-  // These channels are owned by ChannelManager.
-  std::vector<cricket::VoiceChannel*> voice_channels_;
-  std::vector<cricket::VideoChannel*> video_channels_;
   // |rtp_data_channel_| is used if in RTP data channel mode, |sctp_transport_|
   // when using SCTP.
   cricket::RtpDataChannel* rtp_data_channel_ = nullptr;
diff --git a/pc/test/mock_peerconnection.h b/pc/test/mock_peerconnection.h
index 444ee2e..cf11593 100644
--- a/pc/test/mock_peerconnection.h
+++ b/pc/test/mock_peerconnection.h
@@ -61,8 +61,8 @@
                      std::vector<rtc::scoped_refptr<RtpReceiverInterface>>());
   MOCK_CONST_METHOD0(sctp_data_channels,
                      const std::vector<rtc::scoped_refptr<DataChannel>>&());
-  MOCK_METHOD0(voice_channel, cricket::VoiceChannel*());
-  MOCK_METHOD0(video_channel, cricket::VideoChannel*());
+  MOCK_CONST_METHOD0(voice_channel, cricket::VoiceChannel*());
+  MOCK_CONST_METHOD0(video_channel, cricket::VideoChannel*());
   // Libjingle uses "local" for a outgoing track, and "remote" for a incoming
   // track.
   MOCK_METHOD2(GetLocalTrackIdBySsrc, bool(uint32_t, std::string*));