Add SDP semantics option to RTCConfiguration

This setting allows the user of PeerConnection to choose whether
to use Plan B (current) or Unified Plan (future) semantics.
Unified Plan semantics are not yet supported.

Bug: chromium:465349
Change-Id: I77a5c376c83f335f734488e11e619582a314bffe
Reviewed-on: https://webrtc-review.googlesource.com/22766
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Peter Thatcher <pthatcher@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Zhi Huang <zhihuang@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20806}
diff --git a/api/peerconnectioninterface.h b/api/peerconnectioninterface.h
index 60f1635..c95cc7d 100644
--- a/api/peerconnectioninterface.h
+++ b/api/peerconnectioninterface.h
@@ -141,6 +141,10 @@
   virtual ~StatsObserver() {}
 };
 
+// For now, kDefault is interpreted as kPlanB.
+// TODO(bugs.webrtc.org/8530): Switch default to kUnifiedPlan.
+enum class SdpSemantics { kDefault, kPlanB, kUnifiedPlan };
+
 class PeerConnectionInterface : public rtc::RefCountInterface {
  public:
   // See http://dev.w3.org/2011/webrtc/editor/webrtc.html#state-definitions .
@@ -476,6 +480,34 @@
     // called.
     webrtc::TurnCustomizer* turn_customizer = nullptr;
 
+    // Configure the SDP semantics used by this PeerConnection. Note that the
+    // WebRTC 1.0 specification requires kUnifiedPlan semantics. The
+    // RtpTransceiver API is only available with kUnifiedPlan semantics.
+    //
+    // kPlanB will cause PeerConnection to create offers and answers with at
+    // most one audio and one video m= section with multiple RtpSenders and
+    // RtpReceivers specified as multiple a=ssrc lines within the section. This
+    // will also cause PeerConnection to reject offers/answers with multiple m=
+    // sections of the same media type.
+    //
+    // kUnifiedPlan will cause PeerConnection to create offers and answers with
+    // multiple m= sections where each m= section maps to one RtpSender and one
+    // RtpReceiver (an RtpTransceiver), either both audio or both video. Plan B
+    // style offers or answers will be rejected in calls to SetLocalDescription
+    // or SetRemoteDescription.
+    //
+    // For users who only send at most one audio and one video track, this
+    // choice does not matter and should be left as kDefault.
+    //
+    // For users who wish to send multiple audio/video streams and need to stay
+    // interoperable with legacy WebRTC implementations, specify kPlanB.
+    //
+    // For users who wish to send multiple audio/video streams and/or wish to
+    // use the new RtpTransceiver API, specify kUnifiedPlan.
+    //
+    // TODO(steveanton): Implement support for kUnifiedPlan.
+    SdpSemantics sdp_semantics = SdpSemantics::kDefault;
+
     //
     // Don't forget to update operator== if adding something.
     //
diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc
index d617c71..53c891e 100644
--- a/pc/peerconnection.cc
+++ b/pc/peerconnection.cc
@@ -675,6 +675,7 @@
     rtc::Optional<int> ice_check_min_interval;
     rtc::Optional<rtc::IntervalRange> ice_regather_interval_range;
     webrtc::TurnCustomizer* turn_customizer;
+    SdpSemantics sdp_semantics;
   };
   static_assert(sizeof(stuff_being_tested_for_equality) == sizeof(*this),
                 "Did you add something to RTCConfiguration and forget to "
@@ -710,7 +711,8 @@
          redetermine_role_on_ice_restart == o.redetermine_role_on_ice_restart &&
          ice_check_min_interval == o.ice_check_min_interval &&
          ice_regather_interval_range == o.ice_regather_interval_range &&
-         turn_customizer == o.turn_customizer;
+         turn_customizer == o.turn_customizer &&
+         sdp_semantics == o.sdp_semantics;
 }
 
 bool PeerConnectionInterface::RTCConfiguration::operator!=(
diff --git a/pc/peerconnection.h b/pc/peerconnection.h
index ae6b537..0b00588 100644
--- a/pc/peerconnection.h
+++ b/pc/peerconnection.h
@@ -458,9 +458,11 @@
   // 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; }
+  // TODO(bugs.webrtc.org/8530): Flip the default to be Unified Plan once
+  // sufficient time has passed.
+  bool IsUnifiedPlan() const {
+    return configuration_.sdp_semantics == SdpSemantics::kUnifiedPlan;
+  }
 
   // Is there an RtpSender of the given type?
   bool HasRtpSender(cricket::MediaType type) const;
diff --git a/pc/rtptransceiver.cc b/pc/rtptransceiver.cc
index 7f6ed6d..8ca29fc 100644
--- a/pc/rtptransceiver.cc
+++ b/pc/rtptransceiver.cc
@@ -20,6 +20,18 @@
              media_type == cricket::MEDIA_TYPE_VIDEO);
 }
 
+RtpTransceiver::RtpTransceiver(
+    rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,
+    rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
+        receiver)
+    : unified_plan_(true), media_type_(sender->media_type()) {
+  RTC_DCHECK(media_type_ == cricket::MEDIA_TYPE_AUDIO ||
+             media_type_ == cricket::MEDIA_TYPE_VIDEO);
+  RTC_DCHECK_EQ(sender->media_type(), receiver->media_type());
+  senders_.push_back(sender);
+  receivers_.push_back(receiver);
+}
+
 RtpTransceiver::~RtpTransceiver() {
   Stop();
 }
diff --git a/pc/rtptransceiver.h b/pc/rtptransceiver.h
index 9feaac7..7053fad 100644
--- a/pc/rtptransceiver.h
+++ b/pc/rtptransceiver.h
@@ -53,10 +53,18 @@
 class RtpTransceiver final
     : public rtc::RefCountedObject<RtpTransceiverInterface> {
  public:
-  // Construct an RtpTransceiver with no senders, receivers, or channel set.
+  // Construct a Plan B-style RtpTransceiver with no senders, receivers, or
+  // channel set.
   // |media_type| specifies the type of RtpTransceiver (and, by transitivity,
   // the type of senders, receivers, and channel). Can either by audio or video.
   explicit RtpTransceiver(cricket::MediaType media_type);
+  // Construct a Unified Plan-style RtpTransceiver with the given sender and
+  // receiver. The media type will be derived from the media types of the sender
+  // and receiver. The sender and receiver should have the same media type.
+  RtpTransceiver(
+      rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,
+      rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
+          receiver);
   ~RtpTransceiver() override;
 
   cricket::MediaType media_type() const { return media_type_; }