Reland "Reland "Version 2 "Refactoring DataContentDescription class"""

This reverts commit 46afbf9481fbcc939c998c898ca1031ce41cc6b1.

Reason for revert: Tightened protocol name handling.

Original change's description:
> Revert "Reland "Version 2 "Refactoring DataContentDescription class"""
>
> This reverts commit 37f2b43274a0d718de53a4cfcf02226356edcf6e.
>
> Reason for revert: fuzzer failures
>
> Original change's description:
> > Reland "Version 2 "Refactoring DataContentDescription class""
> >
> > This is a reland of 14b2758726879d21671a21291dfed8fb4fd5c21c
> >
> > Original change's description:
> > > Version 2 "Refactoring DataContentDescription class"
> > >
> > > (substantial changes since version 1)
> > >
> > > This CL splits the cricket::DataContentDescription class into
> > > two classes: cricket::RtpDataContentDescription (used for RTP data)
> > > and cricket::SctpDataContentDescription (used for SCTP only).
> > >
> > > SctpDataContentDescription no longer inherits from
> > > MediaContentDescriptionImpl, and no longer contains "codecs".
> > >
> > > Due to usage of internal interfaces by consumers, shimming the old
> > > DataContentDescription API is needed.
> > >
> > > A new cricket::DataContentDescription class is defined, which is
> > > a shim over RtpDataContentDescription and SctpDataContentDescription.
> > > It exposes as little functionality as possible, but supports the
> > > concerned consumer's usage
> > >
> > > Design document:
> > > https://docs.google.com/document/d/1H5LfQxJA2ikMWTQ8FZ3_GAmaXM7knfVQWiSz6ph8VQ0/edit#
> > >
> > > Version 1 reviewed-on: https://webrtc-review.googlesource.com/c/src/+/132700
> > >

Bug: webrtc:10358
Change-Id: Ia9fb8f4679e082e3d18fbbb6b03fc13a08e06110
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/136581
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27933}
diff --git a/media/base/codec.cc b/media/base/codec.cc
index d0ca29b..4380514 100644
--- a/media/base/codec.cc
+++ b/media/base/codec.cc
@@ -334,22 +334,22 @@
   return true;
 }
 
-DataCodec::DataCodec(int id, const std::string& name)
+RtpDataCodec::RtpDataCodec(int id, const std::string& name)
     : Codec(id, name, kDataCodecClockrate) {}
 
-DataCodec::DataCodec() : Codec() {
+RtpDataCodec::RtpDataCodec() : Codec() {
   clockrate = kDataCodecClockrate;
 }
 
-DataCodec::DataCodec(const DataCodec& c) = default;
-DataCodec::DataCodec(DataCodec&& c) = default;
-DataCodec& DataCodec::operator=(const DataCodec& c) = default;
-DataCodec& DataCodec::operator=(DataCodec&& c) = default;
+RtpDataCodec::RtpDataCodec(const RtpDataCodec& c) = default;
+RtpDataCodec::RtpDataCodec(RtpDataCodec&& c) = default;
+RtpDataCodec& RtpDataCodec::operator=(const RtpDataCodec& c) = default;
+RtpDataCodec& RtpDataCodec::operator=(RtpDataCodec&& c) = default;
 
-std::string DataCodec::ToString() const {
+std::string RtpDataCodec::ToString() const {
   char buf[256];
   rtc::SimpleStringBuilder sb(buf);
-  sb << "DataCodec[" << id << ":" << name << "]";
+  sb << "RtpDataCodec[" << id << ":" << name << "]";
   return sb.str();
 }
 
diff --git a/media/base/codec.h b/media/base/codec.h
index 091adb6..bbb147d 100644
--- a/media/base/codec.h
+++ b/media/base/codec.h
@@ -192,19 +192,23 @@
   void SetDefaultParameters();
 };
 
-struct DataCodec : public Codec {
-  DataCodec(int id, const std::string& name);
-  DataCodec();
-  DataCodec(const DataCodec& c);
-  DataCodec(DataCodec&& c);
-  ~DataCodec() override = default;
+struct RtpDataCodec : public Codec {
+  RtpDataCodec(int id, const std::string& name);
+  RtpDataCodec();
+  RtpDataCodec(const RtpDataCodec& c);
+  RtpDataCodec(RtpDataCodec&& c);
+  ~RtpDataCodec() override = default;
 
-  DataCodec& operator=(const DataCodec& c);
-  DataCodec& operator=(DataCodec&& c);
+  RtpDataCodec& operator=(const RtpDataCodec& c);
+  RtpDataCodec& operator=(RtpDataCodec&& c);
 
   std::string ToString() const;
 };
 
+// For backwards compatibility
+// TODO(bugs.webrtc.org/10597): Remove when no longer needed.
+typedef RtpDataCodec DataCodec;
+
 // Get the codec setting associated with |payload_type|. If there
 // is no codec associated with that payload type it returns nullptr.
 template <class Codec>
diff --git a/media/base/rtp_data_engine.h b/media/base/rtp_data_engine.h
index a4647ae..b8bfca2 100644
--- a/media/base/rtp_data_engine.h
+++ b/media/base/rtp_data_engine.h
@@ -16,6 +16,7 @@
 #include <string>
 #include <vector>
 
+#include "media/base/codec.h"
 #include "media/base/media_channel.h"
 #include "media/base/media_constants.h"
 #include "media/base/media_engine.h"
@@ -26,8 +27,6 @@
 
 namespace cricket {
 
-struct DataCodec;
-
 class RtpDataEngine : public DataEngineInterface {
  public:
   RtpDataEngine();
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index 56078c1..1013f7c 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -72,6 +72,7 @@
   ]
 
   deps = [
+    ":media_protocol_names",
     "../api:array_view",
     "../api:audio_options_api",
     "../api:call_api",
@@ -122,6 +123,13 @@
   ]
 }
 
+rtc_source_set("media_protocol_names") {
+  sources = [
+    "media_protocol_names.cc",
+    "media_protocol_names.h",
+  ]
+}
+
 rtc_static_library("peerconnection") {
   visibility = [ "*" ]
   cflags = []
diff --git a/pc/channel.cc b/pc/channel.cc
index 647663e..82de7de 100644
--- a/pc/channel.cc
+++ b/pc/channel.cc
@@ -1143,7 +1143,7 @@
 }
 
 bool RtpDataChannel::CheckDataChannelTypeFromContent(
-    const DataContentDescription* content,
+    const RtpDataContentDescription* content,
     std::string* error_desc) {
   bool is_sctp = ((content->protocol() == kMediaProtocolSctp) ||
                   (content->protocol() == kMediaProtocolDtlsSctp));
@@ -1169,7 +1169,7 @@
     return false;
   }
 
-  const DataContentDescription* data = content->as_data();
+  const RtpDataContentDescription* data = content->as_rtp_data();
 
   if (!CheckDataChannelTypeFromContent(data, error_desc)) {
     return false;
@@ -1223,7 +1223,12 @@
     return false;
   }
 
-  const DataContentDescription* data = content->as_data();
+  const RtpDataContentDescription* data = content->as_rtp_data();
+
+  if (!data) {
+    RTC_LOG(LS_INFO) << "Accepting and ignoring non-RTP content description";
+    return true;
+  }
 
   // If the remote data doesn't have codecs, it must be empty, so ignore it.
   if (!data->has_codecs()) {
diff --git a/pc/channel.h b/pc/channel.h
index 1a4cc72..9747ec2 100644
--- a/pc/channel.h
+++ b/pc/channel.h
@@ -518,7 +518,7 @@
 
   // overrides from BaseChannel
   // Checks that data channel type is RTP.
-  bool CheckDataChannelTypeFromContent(const DataContentDescription* content,
+  bool CheckDataChannelTypeFromContent(const RtpDataContentDescription* content,
                                        std::string* error_desc);
   bool SetLocalContent_w(const MediaContentDescription* content,
                          webrtc::SdpType type,
diff --git a/pc/channel_unittest.cc b/pc/channel_unittest.cc
index 9c5f82b..e31ab53 100644
--- a/pc/channel_unittest.cc
+++ b/pc/channel_unittest.cc
@@ -94,8 +94,8 @@
 
 class DataTraits : public Traits<cricket::RtpDataChannel,
                                  cricket::FakeDataMediaChannel,
-                                 cricket::DataContentDescription,
-                                 cricket::DataCodec,
+                                 cricket::RtpDataContentDescription,
+                                 cricket::RtpDataCodec,
                                  cricket::DataMediaInfo,
                                  cricket::DataOptions> {};
 
@@ -2308,15 +2308,15 @@
     int flags,
     const cricket::AudioCodec& audio_codec,
     const cricket::VideoCodec& video_codec,
-    cricket::DataContentDescription* data) {
+    cricket::RtpDataContentDescription* data) {
   data->AddCodec(kGoogleDataCodec);
   data->set_rtcp_mux((flags & RTCP_MUX) != 0);
 }
 
 template <>
 void ChannelTest<DataTraits>::CopyContent(
-    const cricket::DataContentDescription& source,
-    cricket::DataContentDescription* data) {
+    const cricket::RtpDataContentDescription& source,
+    cricket::RtpDataContentDescription* data) {
   *data = source;
 }
 
@@ -2330,7 +2330,7 @@
 void ChannelTest<DataTraits>::AddLegacyStreamInContent(
     uint32_t ssrc,
     int flags,
-    cricket::DataContentDescription* data) {
+    cricket::RtpDataContentDescription* data) {
   data->AddLegacyStream(ssrc);
 }
 
diff --git a/pc/jsep_transport_controller_unittest.cc b/pc/jsep_transport_controller_unittest.cc
index e81b667..c0927b9d 100644
--- a/pc/jsep_transport_controller_unittest.cc
+++ b/pc/jsep_transport_controller_unittest.cc
@@ -175,8 +175,9 @@
                       cricket::IceMode ice_mode,
                       cricket::ConnectionRole conn_role,
                       rtc::scoped_refptr<rtc::RTCCertificate> cert) {
-    std::unique_ptr<cricket::DataContentDescription> data(
-        new cricket::DataContentDescription());
+    RTC_CHECK(protocol_type == cricket::MediaProtocolType::kSctp);
+    std::unique_ptr<cricket::SctpDataContentDescription> data(
+        new cricket::SctpDataContentDescription());
     data->set_rtcp_mux(true);
     description->AddContent(mid, protocol_type,
                             /*rejected=*/false, data.release());
diff --git a/pc/media_protocol_names.cc b/pc/media_protocol_names.cc
new file mode 100644
index 0000000..3def3f0
--- /dev/null
+++ b/pc/media_protocol_names.cc
@@ -0,0 +1,55 @@
+/*
+ *  Copyright 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "pc/media_protocol_names.h"
+
+namespace cricket {
+
+// There are multiple variants of the RTP protocol stack, including
+// UDP/TLS/RTP/SAVPF (WebRTC default), RTP/AVP, RTP/AVPF, RTP/SAVPF,
+// TCP/DTLS/RTP/SAVPF and so on. We accept anything that has RTP/
+// embedded in it somewhere as being an RTP protocol.
+const char kMediaProtocolRtpPrefix[] = "RTP/";
+
+const char kMediaProtocolSctp[] = "SCTP";
+const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
+const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
+const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
+
+bool IsDtlsSctp(const std::string& protocol) {
+  return protocol == kMediaProtocolDtlsSctp ||
+         protocol == kMediaProtocolUdpDtlsSctp ||
+         protocol == kMediaProtocolTcpDtlsSctp;
+}
+
+bool IsPlainSctp(const std::string& protocol) {
+  return protocol == kMediaProtocolSctp;
+}
+
+bool IsRtpProtocol(const std::string& protocol) {
+  if (protocol.empty()) {
+    return true;
+  }
+  size_t pos = protocol.find(cricket::kMediaProtocolRtpPrefix);
+  if (pos == std::string::npos) {
+    return false;
+  }
+  // RTP must be at the beginning of a string or not preceded by alpha
+  if (pos == 0 || !isalpha(protocol[pos - 1])) {
+    return true;
+  }
+  return false;
+}
+
+bool IsSctpProtocol(const std::string& protocol) {
+  return IsPlainSctp(protocol) || IsDtlsSctp(protocol);
+}
+
+}  // namespace cricket
diff --git a/pc/media_protocol_names.h b/pc/media_protocol_names.h
new file mode 100644
index 0000000..88f1c46
--- /dev/null
+++ b/pc/media_protocol_names.h
@@ -0,0 +1,35 @@
+/*
+ *  Copyright 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef PC_MEDIA_PROTOCOL_NAMES_H_
+#define PC_MEDIA_PROTOCOL_NAMES_H_
+
+#include <string>
+
+namespace cricket {
+
+// Names or name prefixes of protocols as defined by SDP specifications.
+extern const char kMediaProtocolRtpPrefix[];
+extern const char kMediaProtocolSctp[];
+extern const char kMediaProtocolDtlsSctp[];
+extern const char kMediaProtocolUdpDtlsSctp[];
+extern const char kMediaProtocolTcpDtlsSctp[];
+
+bool IsDtlsSctp(const std::string& protocol);
+bool IsPlainSctp(const std::string& protocol);
+
+// Returns true if the given media section protocol indicates use of RTP.
+bool IsRtpProtocol(const std::string& protocol);
+// Returns true if the given media section protocol indicates use of SCTP.
+bool IsSctpProtocol(const std::string& protocol);
+
+}  // namespace cricket
+
+#endif  // PC_MEDIA_PROTOCOL_NAMES_H_
diff --git a/pc/media_session.cc b/pc/media_session.cc
index 0eace22..9c03a1e 100644
--- a/pc/media_session.cc
+++ b/pc/media_session.cc
@@ -27,6 +27,7 @@
 #include "media/base/media_constants.h"
 #include "p2p/base/p2p_constants.h"
 #include "pc/channel_manager.h"
+#include "pc/media_protocol_names.h"
 #include "pc/rtp_media_utils.h"
 #include "pc/srtp_filter.h"
 #include "rtc_base/checks.h"
@@ -68,13 +69,6 @@
 // but we tolerate "RTP/SAVPF" in offers we receive, for compatibility.
 const char kMediaProtocolSavpf[] = "RTP/SAVPF";
 
-const char kMediaProtocolRtpPrefix[] = "RTP/";
-
-const char kMediaProtocolSctp[] = "SCTP";
-const char kMediaProtocolDtlsSctp[] = "DTLS/SCTP";
-const char kMediaProtocolUdpDtlsSctp[] = "UDP/DTLS/SCTP";
-const char kMediaProtocolTcpDtlsSctp[] = "TCP/DTLS/SCTP";
-
 // Note that the below functions support some protocol strings purely for
 // legacy compatibility, as required by JSEP in Section 5.1.2, Profile Names
 // and Interoperability.
@@ -91,20 +85,6 @@
          protocol == "RTP/SAVP" || protocol == "RTP/AVP";
 }
 
-static bool IsDtlsSctp(const std::string& protocol) {
-  return protocol == kMediaProtocolDtlsSctp ||
-         protocol == kMediaProtocolUdpDtlsSctp ||
-         protocol == kMediaProtocolTcpDtlsSctp;
-}
-
-static bool IsPlainSctp(const std::string& protocol) {
-  return protocol == kMediaProtocolSctp;
-}
-
-static bool IsSctp(const std::string& protocol) {
-  return IsPlainSctp(protocol) || IsDtlsSctp(protocol);
-}
-
 static RtpTransceiverDirection NegotiateRtpTransceiverDirection(
     RtpTransceiverDirection offer,
     RtpTransceiverDirection wants) {
@@ -489,7 +469,7 @@
     StreamParamsVec* current_streams,
     MediaContentDescriptionImpl<C>* content_description) {
   // SCTP streams are not negotiated using SDP/ContentDescriptions.
-  if (IsSctp(content_description->protocol())) {
+  if (IsSctpProtocol(content_description->protocol())) {
     return true;
   }
 
@@ -608,11 +588,6 @@
       target_cryptos->end());
 }
 
-bool IsRtpProtocol(const std::string& protocol) {
-  return protocol.empty() ||
-         (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
-}
-
 static bool IsRtpContent(SessionDescription* sdesc,
                          const std::string& content_name) {
   bool is_rtp = false;
@@ -741,32 +716,22 @@
 // crypto (in current_cryptos) and it is enabled (in secure_policy), crypto is
 // created (according to crypto_suites). The created content is added to the
 // offer.
-template <class C>
-static bool CreateMediaContentOffer(
+static bool CreateContentOffer(
     const MediaDescriptionOptions& media_description_options,
     const MediaSessionOptions& session_options,
-    const std::vector<C>& codecs,
     const SecurePolicy& secure_policy,
     const CryptoParamsVec* current_cryptos,
     const std::vector<std::string>& crypto_suites,
     const RtpHeaderExtensions& rtp_extensions,
     UniqueRandomIdGenerator* ssrc_generator,
     StreamParamsVec* current_streams,
-    MediaContentDescriptionImpl<C>* offer) {
-  offer->AddCodecs(codecs);
-
+    MediaContentDescription* offer) {
   offer->set_rtcp_mux(session_options.rtcp_mux_enabled);
   if (offer->type() == cricket::MEDIA_TYPE_VIDEO) {
     offer->set_rtcp_reduced_size(true);
   }
   offer->set_rtp_header_extensions(rtp_extensions);
 
-  if (!AddStreamParams(media_description_options.sender_options,
-                       session_options.rtcp_cname, ssrc_generator,
-                       current_streams, offer)) {
-    return false;
-  }
-
   AddSimulcastToMediaDescription(media_description_options, offer);
 
   if (secure_policy != SEC_DISABLED) {
@@ -785,6 +750,30 @@
   }
   return true;
 }
+template <class C>
+static bool CreateMediaContentOffer(
+    const MediaDescriptionOptions& media_description_options,
+    const MediaSessionOptions& session_options,
+    const std::vector<C>& codecs,
+    const SecurePolicy& secure_policy,
+    const CryptoParamsVec* current_cryptos,
+    const std::vector<std::string>& crypto_suites,
+    const RtpHeaderExtensions& rtp_extensions,
+    UniqueRandomIdGenerator* ssrc_generator,
+    StreamParamsVec* current_streams,
+    MediaContentDescriptionImpl<C>* offer) {
+  offer->AddCodecs(codecs);
+  if (!AddStreamParams(media_description_options.sender_options,
+                       session_options.rtcp_cname, ssrc_generator,
+                       current_streams, offer)) {
+    return false;
+  }
+
+  return CreateContentOffer(media_description_options, session_options,
+                            secure_policy, current_cryptos, crypto_suites,
+                            rtp_extensions, ssrc_generator, current_streams,
+                            offer);
+}
 
 template <class C>
 static bool ReferencedCodecsMatch(const std::vector<C>& codecs1,
@@ -1186,6 +1175,28 @@
                       audio_codecs->end());
 }
 
+template <class C>
+static bool SetCodecsInAnswer(
+    const MediaContentDescriptionImpl<C>* offer,
+    const std::vector<C>& local_codecs,
+    const MediaDescriptionOptions& media_description_options,
+    const MediaSessionOptions& session_options,
+    UniqueRandomIdGenerator* ssrc_generator,
+    StreamParamsVec* current_streams,
+    MediaContentDescriptionImpl<C>* answer) {
+  std::vector<C> negotiated_codecs;
+  NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs,
+                  media_description_options.codec_preferences.empty());
+  answer->AddCodecs(negotiated_codecs);
+  answer->set_protocol(offer->protocol());
+  if (!AddStreamParams(media_description_options.sender_options,
+                       session_options.rtcp_cname, ssrc_generator,
+                       current_streams, answer)) {
+    return false;  // Something went seriously wrong.
+  }
+  return true;
+}
+
 // Create a media content to be answered for the given |sender_options|
 // according to the given session_options.rtcp_mux, session_options.streams,
 // codecs, crypto, and current_streams.  If we don't currently have crypto (in
@@ -1193,12 +1204,10 @@
 // (according to crypto_suites). The codecs, rtcp_mux, and crypto are all
 // negotiated with the offer. If the negotiation fails, this method returns
 // false.  The created content is added to the offer.
-template <class C>
 static bool CreateMediaContentAnswer(
-    const MediaContentDescriptionImpl<C>* offer,
+    const MediaContentDescription* offer,
     const MediaDescriptionOptions& media_description_options,
     const MediaSessionOptions& session_options,
-    const std::vector<C>& local_codecs,
     const SecurePolicy& sdes_policy,
     const CryptoParamsVec* current_cryptos,
     const RtpHeaderExtensions& local_rtp_extenstions,
@@ -1206,13 +1215,7 @@
     bool enable_encrypted_rtp_header_extensions,
     StreamParamsVec* current_streams,
     bool bundle_enabled,
-    MediaContentDescriptionImpl<C>* answer) {
-  std::vector<C> negotiated_codecs;
-  NegotiateCodecs(local_codecs, offer->codecs(), &negotiated_codecs,
-                  media_description_options.codec_preferences.empty());
-  answer->AddCodecs(negotiated_codecs);
-  answer->set_protocol(offer->protocol());
-
+    MediaContentDescription* answer) {
   answer->set_extmap_allow_mixed_enum(offer->extmap_allow_mixed_enum());
   RtpHeaderExtensions negotiated_rtp_extensions;
   NegotiateRtpHeaderExtensions(
@@ -1240,12 +1243,6 @@
     return false;
   }
 
-  if (!AddStreamParams(media_description_options.sender_options,
-                       session_options.rtcp_cname, ssrc_generator,
-                       current_streams, answer)) {
-    return false;  // Something went seriously wrong.
-  }
-
   AddSimulcastToMediaDescription(media_description_options, answer);
 
   answer->set_direction(NegotiateRtpTransceiverDirection(
@@ -1397,7 +1394,7 @@
   channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
   channel_manager->GetSupportedVideoCodecs(&video_codecs_);
   channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
-  channel_manager->GetSupportedDataCodecs(&data_codecs_);
+  channel_manager->GetSupportedDataCodecs(&rtp_data_codecs_);
   ComputeAudioCodecsIntersectionAndUnion();
 }
 
@@ -1484,15 +1481,15 @@
 
   AudioCodecs offer_audio_codecs;
   VideoCodecs offer_video_codecs;
-  DataCodecs offer_data_codecs;
+  RtpDataCodecs offer_rtp_data_codecs;
   GetCodecsForOffer(current_active_contents, &offer_audio_codecs,
-                    &offer_video_codecs, &offer_data_codecs);
+                    &offer_video_codecs, &offer_rtp_data_codecs);
 
   if (!session_options.vad_enabled) {
     // If application doesn't want CN codecs in offer.
     StripCNCodecs(&offer_audio_codecs);
   }
-  FilterDataCodecs(&offer_data_codecs,
+  FilterDataCodecs(&offer_rtp_data_codecs,
                    session_options.data_channel_type == DCT_SCTP);
 
   RtpHeaderExtensions audio_rtp_extensions;
@@ -1536,7 +1533,7 @@
       case MEDIA_TYPE_DATA:
         if (!AddDataContentForOffer(media_description_options, session_options,
                                     current_content, current_description,
-                                    offer_data_codecs, &current_streams,
+                                    offer_rtp_data_codecs, &current_streams,
                                     offer.get(), &ice_credentials)) {
           return nullptr;
         }
@@ -1634,15 +1631,15 @@
   // sections.
   AudioCodecs answer_audio_codecs;
   VideoCodecs answer_video_codecs;
-  DataCodecs answer_data_codecs;
+  RtpDataCodecs answer_rtp_data_codecs;
   GetCodecsForAnswer(current_active_contents, *offer, &answer_audio_codecs,
-                     &answer_video_codecs, &answer_data_codecs);
+                     &answer_video_codecs, &answer_rtp_data_codecs);
 
   if (!session_options.vad_enabled) {
     // If application doesn't want CN codecs in answer.
     StripCNCodecs(&answer_audio_codecs);
   }
-  FilterDataCodecs(&answer_data_codecs,
+  FilterDataCodecs(&answer_rtp_data_codecs,
                    session_options.data_channel_type == DCT_SCTP);
 
   auto answer = absl::make_unique<SessionDescription>();
@@ -1695,8 +1692,8 @@
         if (!AddDataContentForAnswer(
                 media_description_options, session_options, offer_content,
                 offer, current_content, current_description,
-                bundle_transport.get(), answer_data_codecs, &current_streams,
-                answer.get(), &ice_credentials)) {
+                bundle_transport.get(), answer_rtp_data_codecs,
+                &current_streams, answer.get(), &ice_credentials)) {
           return nullptr;
         }
         break;
@@ -1816,7 +1813,7 @@
     const std::vector<const ContentInfo*>& current_active_contents,
     AudioCodecs* audio_codecs,
     VideoCodecs* video_codecs,
-    DataCodecs* data_codecs,
+    RtpDataCodecs* rtp_data_codecs,
     UsedPayloadTypes* used_pltypes) {
   for (const ContentInfo* content : current_active_contents) {
     if (IsMediaContentOfType(content, MEDIA_TYPE_AUDIO)) {
@@ -1828,9 +1825,13 @@
           content->media_description()->as_video();
       MergeCodecs<VideoCodec>(video->codecs(), video_codecs, used_pltypes);
     } else if (IsMediaContentOfType(content, MEDIA_TYPE_DATA)) {
-      const DataContentDescription* data =
-          content->media_description()->as_data();
-      MergeCodecs<DataCodec>(data->codecs(), data_codecs, used_pltypes);
+      const RtpDataContentDescription* data =
+          content->media_description()->as_rtp_data();
+      if (data) {
+        // Only relevant for RTP datachannels
+        MergeCodecs<RtpDataCodec>(data->codecs(), rtp_data_codecs,
+                                  used_pltypes);
+      }
     }
   }
 }
@@ -1845,18 +1846,18 @@
     const std::vector<const ContentInfo*>& current_active_contents,
     AudioCodecs* audio_codecs,
     VideoCodecs* video_codecs,
-    DataCodecs* data_codecs) const {
+    RtpDataCodecs* rtp_data_codecs) const {
   // First - get all codecs from the current description if the media type
   // is used. Add them to |used_pltypes| so the payload type is not reused if a
   // new media type is added.
   UsedPayloadTypes used_pltypes;
   MergeCodecsFromDescription(current_active_contents, audio_codecs,
-                             video_codecs, data_codecs, &used_pltypes);
+                             video_codecs, rtp_data_codecs, &used_pltypes);
 
   // Add our codecs that are not in the current description.
   MergeCodecs<AudioCodec>(all_audio_codecs_, audio_codecs, &used_pltypes);
   MergeCodecs<VideoCodec>(video_codecs_, video_codecs, &used_pltypes);
-  MergeCodecs<DataCodec>(data_codecs_, data_codecs, &used_pltypes);
+  MergeCodecs<DataCodec>(rtp_data_codecs_, rtp_data_codecs, &used_pltypes);
 }
 
 // Getting codecs for an answer involves these steps:
@@ -1871,18 +1872,18 @@
     const SessionDescription& remote_offer,
     AudioCodecs* audio_codecs,
     VideoCodecs* video_codecs,
-    DataCodecs* data_codecs) const {
+    RtpDataCodecs* rtp_data_codecs) const {
   // First - get all codecs from the current description if the media type
   // is used. Add them to |used_pltypes| so the payload type is not reused if a
   // new media type is added.
   UsedPayloadTypes used_pltypes;
   MergeCodecsFromDescription(current_active_contents, audio_codecs,
-                             video_codecs, data_codecs, &used_pltypes);
+                             video_codecs, rtp_data_codecs, &used_pltypes);
 
   // Second - filter out codecs that we don't support at all and should ignore.
   AudioCodecs filtered_offered_audio_codecs;
   VideoCodecs filtered_offered_video_codecs;
-  DataCodecs filtered_offered_data_codecs;
+  RtpDataCodecs filtered_offered_rtp_data_codecs;
   for (const ContentInfo& content : remote_offer.contents()) {
     if (IsMediaContentOfType(&content, MEDIA_TYPE_AUDIO)) {
       const AudioContentDescription* audio =
@@ -1909,15 +1910,19 @@
         }
       }
     } else if (IsMediaContentOfType(&content, MEDIA_TYPE_DATA)) {
-      const DataContentDescription* data =
-          content.media_description()->as_data();
-      for (const DataCodec& offered_data_codec : data->codecs()) {
-        if (!FindMatchingCodec<DataCodec>(data->codecs(),
-                                          filtered_offered_data_codecs,
-                                          offered_data_codec, nullptr) &&
-            FindMatchingCodec<DataCodec>(data->codecs(), data_codecs_,
-                                         offered_data_codec, nullptr)) {
-          filtered_offered_data_codecs.push_back(offered_data_codec);
+      const RtpDataContentDescription* data =
+          content.media_description()->as_rtp_data();
+      if (data) {
+        // RTP data. This part is inactive for SCTP data.
+        for (const RtpDataCodec& offered_rtp_data_codec : data->codecs()) {
+          if (!FindMatchingCodec<RtpDataCodec>(
+                  data->codecs(), filtered_offered_rtp_data_codecs,
+                  offered_rtp_data_codec, nullptr) &&
+              FindMatchingCodec<RtpDataCodec>(data->codecs(), rtp_data_codecs_,
+                                              offered_rtp_data_codec,
+                                              nullptr)) {
+            filtered_offered_rtp_data_codecs.push_back(offered_rtp_data_codec);
+          }
         }
       }
     }
@@ -1929,7 +1934,7 @@
                           &used_pltypes);
   MergeCodecs<VideoCodec>(filtered_offered_video_codecs, video_codecs,
                           &used_pltypes);
-  MergeCodecs<DataCodec>(filtered_offered_data_codecs, data_codecs,
+  MergeCodecs<DataCodec>(filtered_offered_rtp_data_codecs, rtp_data_codecs,
                          &used_pltypes);
 }
 
@@ -2206,18 +2211,101 @@
   return true;
 }
 
+bool MediaSessionDescriptionFactory::AddSctpDataContentForOffer(
+    const MediaDescriptionOptions& media_description_options,
+    const MediaSessionOptions& session_options,
+    const ContentInfo* current_content,
+    const SessionDescription* current_description,
+    StreamParamsVec* current_streams,
+    SessionDescription* desc,
+    IceCredentialsIterator* ice_credentials) const {
+  std::unique_ptr<SctpDataContentDescription> data(
+      new SctpDataContentDescription());
+
+  bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
+
+  cricket::SecurePolicy sdes_policy =
+      IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
+                                                         : secure();
+  std::vector<std::string> crypto_suites;
+  // SDES doesn't make sense for SCTP, so we disable it, and we only
+  // get SDES crypto suites for RTP-based data channels.
+  sdes_policy = cricket::SEC_DISABLED;
+  // Unlike SetMediaProtocol below, we need to set the protocol
+  // before we call CreateMediaContentOffer.  Otherwise,
+  // CreateMediaContentOffer won't know this is SCTP and will
+  // generate SSRCs rather than SIDs.
+  // TODO(deadbeef): Offer kMediaProtocolUdpDtlsSctp (or TcpDtlsSctp), once
+  // it's safe to do so. Older versions of webrtc would reject these
+  // protocols; see https://bugs.chromium.org/p/webrtc/issues/detail?id=7706.
+  data->set_protocol(secure_transport ? kMediaProtocolDtlsSctp
+                                      : kMediaProtocolSctp);
+
+  if (!CreateContentOffer(media_description_options, session_options,
+                          sdes_policy, GetCryptos(current_content),
+                          crypto_suites, RtpHeaderExtensions(), ssrc_generator_,
+                          current_streams, data.get())) {
+    return false;
+  }
+
+  desc->AddContent(media_description_options.mid, MediaProtocolType::kSctp,
+                   data.release());
+  if (!AddTransportOffer(media_description_options.mid,
+                         media_description_options.transport_options,
+                         current_description, desc, ice_credentials)) {
+    return false;
+  }
+  return true;
+}
+
+bool MediaSessionDescriptionFactory::AddRtpDataContentForOffer(
+    const MediaDescriptionOptions& media_description_options,
+    const MediaSessionOptions& session_options,
+    const ContentInfo* current_content,
+    const SessionDescription* current_description,
+    const RtpDataCodecs& rtp_data_codecs,
+    StreamParamsVec* current_streams,
+    SessionDescription* desc,
+    IceCredentialsIterator* ice_credentials) const {
+  std::unique_ptr<RtpDataContentDescription> data(
+      new RtpDataContentDescription());
+  bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
+
+  cricket::SecurePolicy sdes_policy =
+      IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
+                                                         : secure();
+  std::vector<std::string> crypto_suites;
+  GetSupportedDataSdesCryptoSuiteNames(session_options.crypto_options,
+                                       &crypto_suites);
+  if (!CreateMediaContentOffer(media_description_options, session_options,
+                               rtp_data_codecs, sdes_policy,
+                               GetCryptos(current_content), crypto_suites,
+                               RtpHeaderExtensions(), ssrc_generator_,
+                               current_streams, data.get())) {
+    return false;
+  }
+
+  data->set_bandwidth(kDataMaxBandwidth);
+  SetMediaProtocol(secure_transport, data.get());
+  desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
+                   media_description_options.stopped, data.release());
+  if (!AddTransportOffer(media_description_options.mid,
+                         media_description_options.transport_options,
+                         current_description, desc, ice_credentials)) {
+    return false;
+  }
+  return true;
+}
+
 bool MediaSessionDescriptionFactory::AddDataContentForOffer(
     const MediaDescriptionOptions& media_description_options,
     const MediaSessionOptions& session_options,
     const ContentInfo* current_content,
     const SessionDescription* current_description,
-    const DataCodecs& data_codecs,
+    const RtpDataCodecs& rtp_data_codecs,
     StreamParamsVec* current_streams,
     SessionDescription* desc,
     IceCredentialsIterator* ice_credentials) const {
-  bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
-
-  std::unique_ptr<DataContentDescription> data(new DataContentDescription());
   bool is_sctp = (session_options.data_channel_type == DCT_SCTP);
   // If the DataChannel type is not specified, use the DataChannel type in
   // the current description.
@@ -2226,52 +2314,16 @@
     is_sctp = (current_content->media_description()->protocol() ==
                kMediaProtocolSctp);
   }
-
-  cricket::SecurePolicy sdes_policy =
-      IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
-                                                         : secure();
-  std::vector<std::string> crypto_suites;
   if (is_sctp) {
-    // SDES doesn't make sense for SCTP, so we disable it, and we only
-    // get SDES crypto suites for RTP-based data channels.
-    sdes_policy = cricket::SEC_DISABLED;
-    // Unlike SetMediaProtocol below, we need to set the protocol
-    // before we call CreateMediaContentOffer.  Otherwise,
-    // CreateMediaContentOffer won't know this is SCTP and will
-    // generate SSRCs rather than SIDs.
-    // TODO(deadbeef): Offer kMediaProtocolUdpDtlsSctp (or TcpDtlsSctp), once
-    // it's safe to do so. Older versions of webrtc would reject these
-    // protocols; see https://bugs.chromium.org/p/webrtc/issues/detail?id=7706.
-    data->set_protocol(secure_transport ? kMediaProtocolDtlsSctp
-                                        : kMediaProtocolSctp);
+    return AddSctpDataContentForOffer(
+        media_description_options, session_options, current_content,
+        current_description, current_streams, desc, ice_credentials);
   } else {
-    GetSupportedDataSdesCryptoSuiteNames(session_options.crypto_options,
-                                         &crypto_suites);
+    return AddRtpDataContentForOffer(media_description_options, session_options,
+                                     current_content, current_description,
+                                     rtp_data_codecs, current_streams, desc,
+                                     ice_credentials);
   }
-
-  // Even SCTP uses a "codec".
-  if (!CreateMediaContentOffer(
-          media_description_options, session_options, data_codecs, sdes_policy,
-          GetCryptos(current_content), crypto_suites, RtpHeaderExtensions(),
-          ssrc_generator_, current_streams, data.get())) {
-    return false;
-  }
-
-  if (is_sctp) {
-    desc->AddContent(media_description_options.mid, MediaProtocolType::kSctp,
-                     data.release());
-  } else {
-    data->set_bandwidth(kDataMaxBandwidth);
-    SetMediaProtocol(secure_transport, data.get());
-    desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
-                     media_description_options.stopped, data.release());
-  }
-  if (!AddTransportOffer(media_description_options.mid,
-                         media_description_options.transport_options,
-                         current_description, desc, ice_credentials)) {
-    return false;
-  }
-  return true;
 }
 
 // |audio_codecs| = set of all possible codecs that can be used, with correct
@@ -2359,9 +2411,15 @@
   // Do not require or create SDES cryptos if DTLS is used.
   cricket::SecurePolicy sdes_policy =
       audio_transport->secure() ? cricket::SEC_DISABLED : secure();
+  if (!SetCodecsInAnswer(offer_audio_description, filtered_codecs,
+                         media_description_options, session_options,
+                         ssrc_generator_, current_streams,
+                         audio_answer.get())) {
+    return false;
+  }
   if (!CreateMediaContentAnswer(
           offer_audio_description, media_description_options, session_options,
-          filtered_codecs, sdes_policy, GetCryptos(current_content),
+          sdes_policy, GetCryptos(current_content),
           audio_rtp_header_extensions(), ssrc_generator_,
           enable_encrypted_rtp_header_extensions_, current_streams,
           bundle_enabled, audio_answer.get())) {
@@ -2454,9 +2512,15 @@
   // Do not require or create SDES cryptos if DTLS is used.
   cricket::SecurePolicy sdes_policy =
       video_transport->secure() ? cricket::SEC_DISABLED : secure();
+  if (!SetCodecsInAnswer(offer_video_description, filtered_codecs,
+                         media_description_options, session_options,
+                         ssrc_generator_, current_streams,
+                         video_answer.get())) {
+    return false;
+  }
   if (!CreateMediaContentAnswer(
           offer_video_description, media_description_options, session_options,
-          filtered_codecs, sdes_policy, GetCryptos(current_content),
+          sdes_policy, GetCryptos(current_content),
           video_rtp_header_extensions(), ssrc_generator_,
           enable_encrypted_rtp_header_extensions_, current_streams,
           bundle_enabled, video_answer.get())) {
@@ -2492,7 +2556,7 @@
     const ContentInfo* current_content,
     const SessionDescription* current_description,
     const TransportInfo* bundle_transport,
-    const DataCodecs& data_codecs,
+    const RtpDataCodecs& rtp_data_codecs,
     StreamParamsVec* current_streams,
     SessionDescription* answer,
     IceCredentialsIterator* ice_credentials) const {
@@ -2504,28 +2568,51 @@
     return false;
   }
 
-  std::unique_ptr<DataContentDescription> data_answer(
-      new DataContentDescription());
   // Do not require or create SDES cryptos if DTLS is used.
   cricket::SecurePolicy sdes_policy =
       data_transport->secure() ? cricket::SEC_DISABLED : secure();
   bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
                         session_options.bundle_enabled;
   RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_DATA));
-  const DataContentDescription* offer_data_description =
-      offer_content->media_description()->as_data();
-  if (!CreateMediaContentAnswer(
-          offer_data_description, media_description_options, session_options,
-          data_codecs, sdes_policy, GetCryptos(current_content),
-          RtpHeaderExtensions(), ssrc_generator_,
-          enable_encrypted_rtp_header_extensions_, current_streams,
-          bundle_enabled, data_answer.get())) {
-    return false;  // Fails the session setup.
-  }
+  std::unique_ptr<MediaContentDescription> data_answer;
+  if (offer_content->media_description()->as_sctp()) {
+    // SCTP data content
+    data_answer = absl::make_unique<SctpDataContentDescription>();
+    const SctpDataContentDescription* offer_data_description =
+        offer_content->media_description()->as_sctp();
+    // Respond with the offerer's proto, whatever it is.
+    data_answer->as_sctp()->set_protocol(offer_data_description->protocol());
+    if (!CreateMediaContentAnswer(
+            offer_data_description, media_description_options, session_options,
+            sdes_policy, GetCryptos(current_content), RtpHeaderExtensions(),
+            ssrc_generator_, enable_encrypted_rtp_header_extensions_,
+            current_streams, bundle_enabled, data_answer.get())) {
+      return false;  // Fails the session setup.
+    }
+    // Respond with sctpmap if the offer uses sctpmap.
+    bool offer_uses_sctpmap = offer_data_description->use_sctpmap();
+    data_answer->as_sctp()->set_use_sctpmap(offer_uses_sctpmap);
+  } else {
+    // RTP offer
+    data_answer = absl::make_unique<RtpDataContentDescription>();
 
-  // Respond with sctpmap if the offer uses sctpmap.
-  bool offer_uses_sctpmap = offer_data_description->use_sctpmap();
-  data_answer->set_use_sctpmap(offer_uses_sctpmap);
+    const RtpDataContentDescription* offer_data_description =
+        offer_content->media_description()->as_rtp_data();
+    RTC_CHECK(offer_data_description);
+    if (!SetCodecsInAnswer(offer_data_description, rtp_data_codecs,
+                           media_description_options, session_options,
+                           ssrc_generator_, current_streams,
+                           data_answer->as_rtp_data())) {
+      return false;
+    }
+    if (!CreateMediaContentAnswer(
+            offer_data_description, media_description_options, session_options,
+            sdes_policy, GetCryptos(current_content), RtpHeaderExtensions(),
+            ssrc_generator_, enable_encrypted_rtp_header_extensions_,
+            current_streams, bundle_enabled, data_answer.get())) {
+      return false;  // Fails the session setup.
+    }
+  }
 
   bool secure = bundle_transport ? bundle_transport->description.secure()
                                  : data_transport->secure();
@@ -2649,20 +2736,35 @@
 
 const AudioContentDescription* GetFirstAudioContentDescription(
     const SessionDescription* sdesc) {
-  return static_cast<const AudioContentDescription*>(
-      GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
+  auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO);
+  return desc ? desc->as_audio() : nullptr;
 }
 
 const VideoContentDescription* GetFirstVideoContentDescription(
     const SessionDescription* sdesc) {
-  return static_cast<const VideoContentDescription*>(
-      GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
+  auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO);
+  return desc ? desc->as_video() : nullptr;
 }
 
+const RtpDataContentDescription* GetFirstRtpDataContentDescription(
+    const SessionDescription* sdesc) {
+  auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA);
+  return desc ? desc->as_rtp_data() : nullptr;
+}
+
+const SctpDataContentDescription* GetFirstSctpDataContentDescription(
+    const SessionDescription* sdesc) {
+  auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA);
+  return desc ? desc->as_sctp() : nullptr;
+}
+
+// Returns a shim representing either an SctpDataContentDescription
+// or an RtpDataContentDescription, as appropriate.
+// TODO(bugs.webrtc.org/10597): Remove together with shim.
 const DataContentDescription* GetFirstDataContentDescription(
     const SessionDescription* sdesc) {
-  return static_cast<const DataContentDescription*>(
-      GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
+  auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA);
+  return desc ? desc->as_data() : nullptr;
 }
 
 //
@@ -2721,20 +2823,33 @@
 
 AudioContentDescription* GetFirstAudioContentDescription(
     SessionDescription* sdesc) {
-  return static_cast<AudioContentDescription*>(
-      GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO));
+  auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_AUDIO);
+  return desc ? desc->as_audio() : nullptr;
 }
 
 VideoContentDescription* GetFirstVideoContentDescription(
     SessionDescription* sdesc) {
-  return static_cast<VideoContentDescription*>(
-      GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO));
+  auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_VIDEO);
+  return desc ? desc->as_video() : nullptr;
 }
 
+RtpDataContentDescription* GetFirstRtpDataContentDescription(
+    SessionDescription* sdesc) {
+  auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA);
+  return desc ? desc->as_rtp_data() : nullptr;
+}
+
+SctpDataContentDescription* GetFirstSctpDataContentDescription(
+    SessionDescription* sdesc) {
+  auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA);
+  return desc ? desc->as_sctp() : nullptr;
+}
+
+// Returns shim
 DataContentDescription* GetFirstDataContentDescription(
     SessionDescription* sdesc) {
-  return static_cast<DataContentDescription*>(
-      GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA));
+  auto desc = GetFirstMediaContentDescription(sdesc, MEDIA_TYPE_DATA);
+  return desc ? desc->as_data() : nullptr;
 }
 
 }  // namespace cricket
diff --git a/pc/media_session.h b/pc/media_session.h
index a369756..dc889b2 100644
--- a/pc/media_session.h
+++ b/pc/media_session.h
@@ -24,6 +24,7 @@
 #include "p2p/base/ice_credentials_iterator.h"
 #include "p2p/base/transport_description_factory.h"
 #include "pc/jsep_transport.h"
+#include "pc/media_protocol_names.h"
 #include "pc/session_description.h"
 #include "rtc_base/unique_id_generator.h"
 
@@ -154,8 +155,10 @@
     video_rtp_extensions_ = extensions;
   }
   RtpHeaderExtensions video_rtp_header_extensions() const;
-  const DataCodecs& data_codecs() const { return data_codecs_; }
-  void set_data_codecs(const DataCodecs& codecs) { data_codecs_ = codecs; }
+  const RtpDataCodecs& rtp_data_codecs() const { return rtp_data_codecs_; }
+  void set_rtp_data_codecs(const RtpDataCodecs& codecs) {
+    rtp_data_codecs_ = codecs;
+  }
   SecurePolicy secure() const { return secure_; }
   void set_secure(SecurePolicy s) { secure_ = s; }
 
@@ -185,13 +188,13 @@
       const std::vector<const ContentInfo*>& current_active_contents,
       AudioCodecs* audio_codecs,
       VideoCodecs* video_codecs,
-      DataCodecs* data_codecs) const;
+      RtpDataCodecs* rtp_data_codecs) const;
   void GetCodecsForAnswer(
       const std::vector<const ContentInfo*>& current_active_contents,
       const SessionDescription& remote_offer,
       AudioCodecs* audio_codecs,
       VideoCodecs* video_codecs,
-      DataCodecs* data_codecs) const;
+      RtpDataCodecs* rtp_data_codecs) const;
   void GetRtpHdrExtsToOffer(
       const std::vector<const ContentInfo*>& current_active_contents,
       RtpHeaderExtensions* audio_extensions,
@@ -240,12 +243,32 @@
       SessionDescription* desc,
       IceCredentialsIterator* ice_credentials) const;
 
+  bool AddSctpDataContentForOffer(
+      const MediaDescriptionOptions& media_description_options,
+      const MediaSessionOptions& session_options,
+      const ContentInfo* current_content,
+      const SessionDescription* current_description,
+      StreamParamsVec* current_streams,
+      SessionDescription* desc,
+      IceCredentialsIterator* ice_credentials) const;
+  bool AddRtpDataContentForOffer(
+      const MediaDescriptionOptions& media_description_options,
+      const MediaSessionOptions& session_options,
+      const ContentInfo* current_content,
+      const SessionDescription* current_description,
+      const RtpDataCodecs& rtp_data_codecs,
+      StreamParamsVec* current_streams,
+      SessionDescription* desc,
+      IceCredentialsIterator* ice_credentials) const;
+  // This function calls either AddRtpDataContentForOffer or
+  // AddSctpDataContentForOffer depending on protocol.
+  // The codecs argument is ignored for SCTP.
   bool AddDataContentForOffer(
       const MediaDescriptionOptions& media_description_options,
       const MediaSessionOptions& session_options,
       const ContentInfo* current_content,
       const SessionDescription* current_description,
-      const DataCodecs& data_codecs,
+      const RtpDataCodecs& rtp_data_codecs,
       StreamParamsVec* current_streams,
       SessionDescription* desc,
       IceCredentialsIterator* ice_credentials) const;
@@ -284,7 +307,7 @@
       const ContentInfo* current_content,
       const SessionDescription* current_description,
       const TransportInfo* bundle_transport,
-      const DataCodecs& data_codecs,
+      const RtpDataCodecs& rtp_data_codecs,
       StreamParamsVec* current_streams,
       SessionDescription* answer,
       IceCredentialsIterator* ice_credentials) const;
@@ -301,7 +324,7 @@
   RtpHeaderExtensions audio_rtp_extensions_;
   VideoCodecs video_codecs_;
   RtpHeaderExtensions video_rtp_extensions_;
-  DataCodecs data_codecs_;
+  RtpDataCodecs rtp_data_codecs_;
   // This object is not owned by the channel so it must outlive it.
   rtc::UniqueRandomIdGenerator* const ssrc_generator_;
   bool enable_encrypted_rtp_header_extensions_ = false;
@@ -330,6 +353,11 @@
     const SessionDescription* sdesc);
 const VideoContentDescription* GetFirstVideoContentDescription(
     const SessionDescription* sdesc);
+const RtpDataContentDescription* GetFirstRtpDataContentDescription(
+    const SessionDescription* sdesc);
+const SctpDataContentDescription* GetFirstSctpDataContentDescription(
+    const SessionDescription* sdesc);
+// Returns shim. Deprecated - ask for the right protocol instead.
 const DataContentDescription* GetFirstDataContentDescription(
     const SessionDescription* sdesc);
 // Non-const versions of the above functions.
@@ -347,6 +375,10 @@
     SessionDescription* sdesc);
 VideoContentDescription* GetFirstVideoContentDescription(
     SessionDescription* sdesc);
+RtpDataContentDescription* GetFirstRtpDataContentDescription(
+    SessionDescription* sdesc);
+SctpDataContentDescription* GetFirstSctpDataContentDescription(
+    SessionDescription* sdesc);
 DataContentDescription* GetFirstDataContentDescription(
     SessionDescription* sdesc);
 
@@ -370,9 +402,6 @@
     const webrtc::CryptoOptions& crypto_options,
     std::vector<std::string>* crypto_suite_names);
 
-// Returns true if the given media section protocol indicates use of RTP.
-bool IsRtpProtocol(const std::string& protocol);
-
 }  // namespace cricket
 
 #endif  // PC_MEDIA_SESSION_H_
diff --git a/pc/media_session_unittest.cc b/pc/media_session_unittest.cc
index 1136607..b69ded3 100644
--- a/pc/media_session_unittest.cc
+++ b/pc/media_session_unittest.cc
@@ -42,12 +42,10 @@
 using cricket::AudioContentDescription;
 using cricket::ContentInfo;
 using cricket::CryptoParamsVec;
-using cricket::DataCodec;
-using cricket::DataContentDescription;
 using cricket::GetFirstAudioContent;
 using cricket::GetFirstAudioContentDescription;
 using cricket::GetFirstDataContent;
-using cricket::GetFirstDataContentDescription;
+using cricket::GetFirstRtpDataContentDescription;
 using cricket::GetFirstVideoContent;
 using cricket::GetFirstVideoContentDescription;
 using cricket::kAutoBandwidth;
@@ -62,6 +60,9 @@
 using cricket::MediaType;
 using cricket::RidDescription;
 using cricket::RidDirection;
+using cricket::RtpDataCodec;
+using cricket::RtpDataContentDescription;
+using cricket::SctpDataContentDescription;
 using cricket::SEC_DISABLED;
 using cricket::SEC_ENABLED;
 using cricket::SEC_REQUIRED;
@@ -126,14 +127,14 @@
 
 static const VideoCodec kVideoCodecsAnswer[] = {VideoCodec(97, "H264")};
 
-static const DataCodec kDataCodecs1[] = {DataCodec(98, "binary-data"),
-                                         DataCodec(99, "utf8-text")};
+static const RtpDataCodec kDataCodecs1[] = {RtpDataCodec(98, "binary-data"),
+                                            RtpDataCodec(99, "utf8-text")};
 
-static const DataCodec kDataCodecs2[] = {DataCodec(126, "binary-data"),
-                                         DataCodec(127, "utf8-text")};
+static const RtpDataCodec kDataCodecs2[] = {RtpDataCodec(126, "binary-data"),
+                                            RtpDataCodec(127, "utf8-text")};
 
-static const DataCodec kDataCodecsAnswer[] = {DataCodec(98, "binary-data"),
-                                              DataCodec(99, "utf8-text")};
+static const RtpDataCodec kDataCodecsAnswer[] = {
+    RtpDataCodec(98, "binary-data"), RtpDataCodec(99, "utf8-text")};
 
 static const RtpExtension kAudioRtpExtension1[] = {
     RtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level", 8),
@@ -412,11 +413,11 @@
     f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1),
                          MAKE_VECTOR(kAudioCodecs1));
     f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1));
-    f1_.set_data_codecs(MAKE_VECTOR(kDataCodecs1));
+    f1_.set_rtp_data_codecs(MAKE_VECTOR(kDataCodecs1));
     f2_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs2),
                          MAKE_VECTOR(kAudioCodecs2));
     f2_.set_video_codecs(MAKE_VECTOR(kVideoCodecs2));
-    f2_.set_data_codecs(MAKE_VECTOR(kDataCodecs2));
+    f2_.set_rtp_data_codecs(MAKE_VECTOR(kDataCodecs2));
     tdf1_.set_certificate(rtc::RTCCertificate::Create(
         std::unique_ptr<rtc::SSLIdentity>(new rtc::FakeSSLIdentity("id1"))));
     tdf2_.set_certificate(rtc::RTCCertificate::Create(
@@ -801,7 +802,7 @@
 TEST_F(MediaSessionDescriptionFactoryTest, TestBundleOfferWithSameCodecPlType) {
   const VideoCodec& offered_video_codec = f2_.video_codecs()[0];
   const AudioCodec& offered_audio_codec = f2_.audio_sendrecv_codecs()[0];
-  const DataCodec& offered_data_codec = f2_.data_codecs()[0];
+  const RtpDataCodec& offered_data_codec = f2_.rtp_data_codecs()[0];
   ASSERT_EQ(offered_video_codec.id, offered_audio_codec.id);
   ASSERT_EQ(offered_video_codec.id, offered_data_codec.id);
 
@@ -814,8 +815,8 @@
       GetFirstVideoContentDescription(offer.get());
   const AudioContentDescription* acd =
       GetFirstAudioContentDescription(offer.get());
-  const DataContentDescription* dcd =
-      GetFirstDataContentDescription(offer.get());
+  const RtpDataContentDescription* dcd =
+      GetFirstRtpDataContentDescription(offer.get());
   ASSERT_TRUE(NULL != vcd);
   ASSERT_TRUE(NULL != acd);
   ASSERT_TRUE(NULL != dcd);
@@ -858,8 +859,8 @@
       GetFirstAudioContentDescription(updated_offer.get());
   const VideoContentDescription* vcd =
       GetFirstVideoContentDescription(updated_offer.get());
-  const DataContentDescription* dcd =
-      GetFirstDataContentDescription(updated_offer.get());
+  const RtpDataContentDescription* dcd =
+      GetFirstRtpDataContentDescription(updated_offer.get());
   EXPECT_TRUE(NULL != vcd);
   EXPECT_TRUE(NULL != acd);
   EXPECT_TRUE(NULL != dcd);
@@ -887,7 +888,7 @@
   EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
   EXPECT_EQ(MediaProtocolType::kRtp, dc->type);
   const AudioContentDescription* acd = ac->media_description()->as_audio();
-  const DataContentDescription* dcd = dc->media_description()->as_data();
+  const RtpDataContentDescription* dcd = dc->media_description()->as_rtp_data();
   EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
   EXPECT_EQ(f1_.audio_sendrecv_codecs(), acd->codecs());
   EXPECT_EQ(0U, acd->first_ssrc());             // no sender is attched.
@@ -896,7 +897,7 @@
   ASSERT_CRYPTO(acd, 1U, kDefaultSrtpCryptoSuite);
   EXPECT_EQ(cricket::kMediaProtocolSavpf, acd->protocol());
   EXPECT_EQ(MEDIA_TYPE_DATA, dcd->type());
-  EXPECT_EQ(f1_.data_codecs(), dcd->codecs());
+  EXPECT_EQ(f1_.rtp_data_codecs(), dcd->codecs());
   EXPECT_EQ(0U, dcd->first_ssrc());  // no sender is attached.
   EXPECT_EQ(cricket::kDataMaxBandwidth,
             dcd->bandwidth());   // default bandwidth (auto)
@@ -1280,7 +1281,7 @@
   EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
   EXPECT_EQ(MediaProtocolType::kRtp, dc->type);
   const AudioContentDescription* acd = ac->media_description()->as_audio();
-  const DataContentDescription* dcd = dc->media_description()->as_data();
+  const RtpDataContentDescription* dcd = dc->media_description()->as_rtp_data();
   EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
   EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
   EXPECT_EQ(kAutoBandwidth, acd->bandwidth());  // negotiated auto bw
@@ -1312,7 +1313,7 @@
   EXPECT_EQ(MediaProtocolType::kRtp, ac->type);
   EXPECT_EQ(MediaProtocolType::kRtp, dc->type);
   const AudioContentDescription* acd = ac->media_description()->as_audio();
-  const DataContentDescription* dcd = dc->media_description()->as_data();
+  const RtpDataContentDescription* dcd = dc->media_description()->as_rtp_data();
   EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
   EXPECT_THAT(acd->codecs(), ElementsAreArray(kAudioCodecsAnswer));
   EXPECT_EQ(kAutoBandwidth, acd->bandwidth());  // negotiated auto bw
@@ -1336,15 +1337,16 @@
   ASSERT_TRUE(offer.get() != NULL);
   ContentInfo* dc_offer = offer->GetContentByName("data");
   ASSERT_TRUE(dc_offer != NULL);
-  DataContentDescription* dcd_offer = dc_offer->media_description()->as_data();
+  SctpDataContentDescription* dcd_offer =
+      dc_offer->media_description()->as_sctp();
   EXPECT_TRUE(dcd_offer->use_sctpmap());
 
   std::unique_ptr<SessionDescription> answer =
       f2_.CreateAnswer(offer.get(), opts, NULL);
   const ContentInfo* dc_answer = answer->GetContentByName("data");
   ASSERT_TRUE(dc_answer != NULL);
-  const DataContentDescription* dcd_answer =
-      dc_answer->media_description()->as_data();
+  const SctpDataContentDescription* dcd_answer =
+      dc_answer->media_description()->as_sctp();
   EXPECT_TRUE(dcd_answer->use_sctpmap());
 }
 
@@ -1356,15 +1358,16 @@
   ASSERT_TRUE(offer.get() != NULL);
   ContentInfo* dc_offer = offer->GetContentByName("data");
   ASSERT_TRUE(dc_offer != NULL);
-  DataContentDescription* dcd_offer = dc_offer->media_description()->as_data();
+  SctpDataContentDescription* dcd_offer =
+      dc_offer->media_description()->as_sctp();
   dcd_offer->set_use_sctpmap(false);
 
   std::unique_ptr<SessionDescription> answer =
       f2_.CreateAnswer(offer.get(), opts, NULL);
   const ContentInfo* dc_answer = answer->GetContentByName("data");
   ASSERT_TRUE(dc_answer != NULL);
-  const DataContentDescription* dcd_answer =
-      dc_answer->media_description()->as_data();
+  const SctpDataContentDescription* dcd_answer =
+      dc_answer->media_description()->as_sctp();
   EXPECT_FALSE(dcd_answer->use_sctpmap());
 }
 
@@ -1385,7 +1388,9 @@
   ASSERT_TRUE(offer.get() != nullptr);
   ContentInfo* dc_offer = offer->GetContentByName("data");
   ASSERT_TRUE(dc_offer != nullptr);
-  DataContentDescription* dcd_offer = dc_offer->media_description()->as_data();
+  SctpDataContentDescription* dcd_offer =
+      dc_offer->media_description()->as_sctp();
+  ASSERT_TRUE(dcd_offer);
 
   std::vector<std::string> protos = {"DTLS/SCTP", "UDP/DTLS/SCTP",
                                      "TCP/DTLS/SCTP"};
@@ -1395,8 +1400,8 @@
         f2_.CreateAnswer(offer.get(), opts, nullptr);
     const ContentInfo* dc_answer = answer->GetContentByName("data");
     ASSERT_TRUE(dc_answer != nullptr);
-    const DataContentDescription* dcd_answer =
-        dc_answer->media_description()->as_data();
+    const SctpDataContentDescription* dcd_answer =
+        dc_answer->media_description()->as_sctp();
     EXPECT_FALSE(dc_answer->rejected);
     EXPECT_EQ(proto, dcd_answer->protocol());
   }
@@ -1478,9 +1483,11 @@
   std::unique_ptr<SessionDescription> offer = f1_.CreateOffer(opts, NULL);
   ContentInfo* dc_offer = offer->GetContentByName("data");
   ASSERT_TRUE(dc_offer != NULL);
-  DataContentDescription* dcd_offer = dc_offer->media_description()->as_data();
+  RtpDataContentDescription* dcd_offer =
+      dc_offer->media_description()->as_rtp_data();
   ASSERT_TRUE(dcd_offer != NULL);
-  std::string protocol = "a weird unknown protocol";
+  // Offer must be acceptable as an RTP protocol in order to be set.
+  std::string protocol = "RTP/a weird unknown protocol";
   dcd_offer->set_protocol(protocol);
 
   std::unique_ptr<SessionDescription> answer =
@@ -1489,8 +1496,8 @@
   const ContentInfo* dc_answer = answer->GetContentByName("data");
   ASSERT_TRUE(dc_answer != NULL);
   EXPECT_TRUE(dc_answer->rejected);
-  const DataContentDescription* dcd_answer =
-      dc_answer->media_description()->as_data();
+  const RtpDataContentDescription* dcd_answer =
+      dc_answer->media_description()->as_rtp_data();
   ASSERT_TRUE(dcd_answer != NULL);
   EXPECT_EQ(protocol, dcd_answer->protocol());
 }
@@ -1688,7 +1695,7 @@
   ASSERT_TRUE(vc != NULL);
   const AudioContentDescription* acd = ac->media_description()->as_audio();
   const VideoContentDescription* vcd = vc->media_description()->as_video();
-  const DataContentDescription* dcd = dc->media_description()->as_data();
+  const RtpDataContentDescription* dcd = dc->media_description()->as_rtp_data();
 
   EXPECT_FALSE(acd->has_ssrcs());  // No StreamParams.
   EXPECT_FALSE(vcd->has_ssrcs());  // No StreamParams.
@@ -1716,16 +1723,16 @@
   answer = f2_.CreateAnswer(offer.get(), answer_opts, NULL);
   ASSERT_TRUE(NULL != GetFirstAudioContentDescription(offer.get()));
   ASSERT_TRUE(NULL != GetFirstVideoContentDescription(offer.get()));
-  ASSERT_TRUE(NULL != GetFirstDataContentDescription(offer.get()));
+  ASSERT_TRUE(NULL != GetFirstRtpDataContentDescription(offer.get()));
   ASSERT_TRUE(NULL != GetFirstAudioContentDescription(answer.get()));
   ASSERT_TRUE(NULL != GetFirstVideoContentDescription(answer.get()));
-  ASSERT_TRUE(NULL != GetFirstDataContentDescription(answer.get()));
+  ASSERT_TRUE(NULL != GetFirstRtpDataContentDescription(answer.get()));
   EXPECT_TRUE(GetFirstAudioContentDescription(offer.get())->rtcp_mux());
   EXPECT_TRUE(GetFirstVideoContentDescription(offer.get())->rtcp_mux());
-  EXPECT_TRUE(GetFirstDataContentDescription(offer.get())->rtcp_mux());
+  EXPECT_TRUE(GetFirstRtpDataContentDescription(offer.get())->rtcp_mux());
   EXPECT_TRUE(GetFirstAudioContentDescription(answer.get())->rtcp_mux());
   EXPECT_TRUE(GetFirstVideoContentDescription(answer.get())->rtcp_mux());
-  EXPECT_TRUE(GetFirstDataContentDescription(answer.get())->rtcp_mux());
+  EXPECT_TRUE(GetFirstRtpDataContentDescription(answer.get())->rtcp_mux());
 
   offer_opts.rtcp_mux_enabled = true;
   answer_opts.rtcp_mux_enabled = false;
@@ -1733,16 +1740,16 @@
   answer = f2_.CreateAnswer(offer.get(), answer_opts, NULL);
   ASSERT_TRUE(NULL != GetFirstAudioContentDescription(offer.get()));
   ASSERT_TRUE(NULL != GetFirstVideoContentDescription(offer.get()));
-  ASSERT_TRUE(NULL != GetFirstDataContentDescription(offer.get()));
+  ASSERT_TRUE(NULL != GetFirstRtpDataContentDescription(offer.get()));
   ASSERT_TRUE(NULL != GetFirstAudioContentDescription(answer.get()));
   ASSERT_TRUE(NULL != GetFirstVideoContentDescription(answer.get()));
-  ASSERT_TRUE(NULL != GetFirstDataContentDescription(answer.get()));
+  ASSERT_TRUE(NULL != GetFirstRtpDataContentDescription(answer.get()));
   EXPECT_TRUE(GetFirstAudioContentDescription(offer.get())->rtcp_mux());
   EXPECT_TRUE(GetFirstVideoContentDescription(offer.get())->rtcp_mux());
-  EXPECT_TRUE(GetFirstDataContentDescription(offer.get())->rtcp_mux());
+  EXPECT_TRUE(GetFirstRtpDataContentDescription(offer.get())->rtcp_mux());
   EXPECT_FALSE(GetFirstAudioContentDescription(answer.get())->rtcp_mux());
   EXPECT_FALSE(GetFirstVideoContentDescription(answer.get())->rtcp_mux());
-  EXPECT_FALSE(GetFirstDataContentDescription(answer.get())->rtcp_mux());
+  EXPECT_FALSE(GetFirstRtpDataContentDescription(answer.get())->rtcp_mux());
 
   offer_opts.rtcp_mux_enabled = false;
   answer_opts.rtcp_mux_enabled = true;
@@ -1750,16 +1757,16 @@
   answer = f2_.CreateAnswer(offer.get(), answer_opts, NULL);
   ASSERT_TRUE(NULL != GetFirstAudioContentDescription(offer.get()));
   ASSERT_TRUE(NULL != GetFirstVideoContentDescription(offer.get()));
-  ASSERT_TRUE(NULL != GetFirstDataContentDescription(offer.get()));
+  ASSERT_TRUE(NULL != GetFirstRtpDataContentDescription(offer.get()));
   ASSERT_TRUE(NULL != GetFirstAudioContentDescription(answer.get()));
   ASSERT_TRUE(NULL != GetFirstVideoContentDescription(answer.get()));
-  ASSERT_TRUE(NULL != GetFirstDataContentDescription(answer.get()));
+  ASSERT_TRUE(NULL != GetFirstRtpDataContentDescription(answer.get()));
   EXPECT_FALSE(GetFirstAudioContentDescription(offer.get())->rtcp_mux());
   EXPECT_FALSE(GetFirstVideoContentDescription(offer.get())->rtcp_mux());
-  EXPECT_FALSE(GetFirstDataContentDescription(offer.get())->rtcp_mux());
+  EXPECT_FALSE(GetFirstRtpDataContentDescription(offer.get())->rtcp_mux());
   EXPECT_FALSE(GetFirstAudioContentDescription(answer.get())->rtcp_mux());
   EXPECT_FALSE(GetFirstVideoContentDescription(answer.get())->rtcp_mux());
-  EXPECT_FALSE(GetFirstDataContentDescription(answer.get())->rtcp_mux());
+  EXPECT_FALSE(GetFirstRtpDataContentDescription(answer.get())->rtcp_mux());
 
   offer_opts.rtcp_mux_enabled = false;
   answer_opts.rtcp_mux_enabled = false;
@@ -1767,16 +1774,16 @@
   answer = f2_.CreateAnswer(offer.get(), answer_opts, NULL);
   ASSERT_TRUE(NULL != GetFirstAudioContentDescription(offer.get()));
   ASSERT_TRUE(NULL != GetFirstVideoContentDescription(offer.get()));
-  ASSERT_TRUE(NULL != GetFirstDataContentDescription(offer.get()));
+  ASSERT_TRUE(NULL != GetFirstRtpDataContentDescription(offer.get()));
   ASSERT_TRUE(NULL != GetFirstAudioContentDescription(answer.get()));
   ASSERT_TRUE(NULL != GetFirstVideoContentDescription(answer.get()));
-  ASSERT_TRUE(NULL != GetFirstDataContentDescription(answer.get()));
+  ASSERT_TRUE(NULL != GetFirstRtpDataContentDescription(answer.get()));
   EXPECT_FALSE(GetFirstAudioContentDescription(offer.get())->rtcp_mux());
   EXPECT_FALSE(GetFirstVideoContentDescription(offer.get())->rtcp_mux());
-  EXPECT_FALSE(GetFirstDataContentDescription(offer.get())->rtcp_mux());
+  EXPECT_FALSE(GetFirstRtpDataContentDescription(offer.get())->rtcp_mux());
   EXPECT_FALSE(GetFirstAudioContentDescription(answer.get())->rtcp_mux());
   EXPECT_FALSE(GetFirstVideoContentDescription(answer.get())->rtcp_mux());
-  EXPECT_FALSE(GetFirstDataContentDescription(answer.get())->rtcp_mux());
+  EXPECT_FALSE(GetFirstRtpDataContentDescription(answer.get())->rtcp_mux());
 }
 
 // Create an audio-only answer to a video offer.
@@ -1948,7 +1955,7 @@
   ASSERT_TRUE(dc != NULL);
   const AudioContentDescription* acd = ac->media_description()->as_audio();
   const VideoContentDescription* vcd = vc->media_description()->as_video();
-  const DataContentDescription* dcd = dc->media_description()->as_data();
+  const RtpDataContentDescription* dcd = dc->media_description()->as_rtp_data();
   EXPECT_EQ(MEDIA_TYPE_AUDIO, acd->type());
   EXPECT_EQ(f1_.audio_sendrecv_codecs(), acd->codecs());
 
@@ -1978,7 +1985,7 @@
   EXPECT_TRUE(vcd->rtcp_mux());                 // rtcp-mux defaults on
 
   EXPECT_EQ(MEDIA_TYPE_DATA, dcd->type());
-  EXPECT_EQ(f1_.data_codecs(), dcd->codecs());
+  EXPECT_EQ(f1_.rtp_data_codecs(), dcd->codecs());
   ASSERT_CRYPTO(dcd, 1U, kDefaultSrtpCryptoSuite);
 
   const StreamParamsVec& data_streams = dcd->streams();
@@ -2020,8 +2027,8 @@
       ac->media_description()->as_audio();
   const VideoContentDescription* updated_vcd =
       vc->media_description()->as_video();
-  const DataContentDescription* updated_dcd =
-      dc->media_description()->as_data();
+  const RtpDataContentDescription* updated_dcd =
+      dc->media_description()->as_rtp_data();
 
   EXPECT_EQ(acd->type(), updated_acd->type());
   EXPECT_EQ(acd->codecs(), updated_acd->codecs());
@@ -2307,7 +2314,7 @@
   ASSERT_TRUE(dc != NULL);
   const AudioContentDescription* acd = ac->media_description()->as_audio();
   const VideoContentDescription* vcd = vc->media_description()->as_video();
-  const DataContentDescription* dcd = dc->media_description()->as_data();
+  const RtpDataContentDescription* dcd = dc->media_description()->as_rtp_data();
   ASSERT_CRYPTO(acd, 1U, kDefaultSrtpCryptoSuite);
   ASSERT_CRYPTO(vcd, 1U, kDefaultSrtpCryptoSuite);
   ASSERT_CRYPTO(dcd, 1U, kDefaultSrtpCryptoSuite);
@@ -2375,8 +2382,8 @@
       ac->media_description()->as_audio();
   const VideoContentDescription* updated_vcd =
       vc->media_description()->as_video();
-  const DataContentDescription* updated_dcd =
-      dc->media_description()->as_data();
+  const RtpDataContentDescription* updated_dcd =
+      dc->media_description()->as_rtp_data();
 
   ASSERT_CRYPTO(updated_acd, 1U, kDefaultSrtpCryptoSuite);
   EXPECT_TRUE(CompareCryptoParams(acd->cryptos(), updated_acd->cryptos()));
@@ -3536,8 +3543,8 @@
   const VideoContentDescription* video_offer =
       GetFirstVideoContentDescription(offer.get());
   ASSERT_TRUE(video_offer->cryptos().empty());
-  const DataContentDescription* data_offer =
-      GetFirstDataContentDescription(offer.get());
+  const RtpDataContentDescription* data_offer =
+      GetFirstRtpDataContentDescription(offer.get());
   ASSERT_TRUE(data_offer->cryptos().empty());
 
   const cricket::TransportDescription* audio_offer_trans_desc =
@@ -4068,11 +4075,11 @@
     f1_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs1),
                          MAKE_VECTOR(kAudioCodecs1));
     f1_.set_video_codecs(MAKE_VECTOR(kVideoCodecs1));
-    f1_.set_data_codecs(MAKE_VECTOR(kDataCodecs1));
+    f1_.set_rtp_data_codecs(MAKE_VECTOR(kDataCodecs1));
     f2_.set_audio_codecs(MAKE_VECTOR(kAudioCodecs2),
                          MAKE_VECTOR(kAudioCodecs2));
     f2_.set_video_codecs(MAKE_VECTOR(kVideoCodecs2));
-    f2_.set_data_codecs(MAKE_VECTOR(kDataCodecs2));
+    f2_.set_rtp_data_codecs(MAKE_VECTOR(kDataCodecs2));
     f1_.set_secure(SEC_ENABLED);
     f2_.set_secure(SEC_ENABLED);
     tdf1_.set_certificate(rtc::RTCCertificate::Create(
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc
index b4c5fe2..781b3a5 100644
--- a/pc/peer_connection.cc
+++ b/pc/peer_connection.cc
@@ -559,24 +559,13 @@
 // Get the SCTP port out of a SessionDescription.
 // Return -1 if not found.
 int GetSctpPort(const SessionDescription* session_description) {
-  const cricket::DataContentDescription* data_desc =
-      GetFirstDataContentDescription(session_description);
+  const cricket::SctpDataContentDescription* data_desc =
+      GetFirstSctpDataContentDescription(session_description);
   RTC_DCHECK(data_desc);
   if (!data_desc) {
     return -1;
   }
-  std::string value;
-  cricket::DataCodec match_pattern(cricket::kGoogleSctpDataCodecPlType,
-                                   cricket::kGoogleSctpDataCodecName);
-  for (const cricket::DataCodec& codec : data_desc->codecs()) {
-    if (!codec.Matches(match_pattern)) {
-      continue;
-    }
-    if (codec.GetParam(cricket::kCodecParamPort, &value)) {
-      return rtc::FromString<int>(value);
-    }
-  }
-  return -1;
+  return data_desc->port();
 }
 
 // Returns true if |new_desc| requests an ICE restart (i.e., new ufrag/pwd).
@@ -2423,11 +2412,11 @@
   const cricket::ContentInfo* data_content =
       GetFirstDataContent(local_description()->description());
   if (data_content) {
-    const cricket::DataContentDescription* data_desc =
-        data_content->media_description()->as_data();
-    if (absl::StartsWith(data_desc->protocol(),
-                         cricket::kMediaProtocolRtpPrefix)) {
-      UpdateLocalRtpDataChannels(data_desc->streams());
+    const cricket::RtpDataContentDescription* rtp_data_desc =
+        data_content->media_description()->as_rtp_data();
+    // rtp_data_desc will be null if this is an SCTP description.
+    if (rtp_data_desc) {
+      UpdateLocalRtpDataChannels(rtp_data_desc->streams());
     }
   }
 
@@ -2833,8 +2822,8 @@
       GetFirstAudioContentDescription(remote_description()->description());
   const cricket::VideoContentDescription* video_desc =
       GetFirstVideoContentDescription(remote_description()->description());
-  const cricket::DataContentDescription* data_desc =
-      GetFirstDataContentDescription(remote_description()->description());
+  const cricket::RtpDataContentDescription* rtp_data_desc =
+      GetFirstRtpDataContentDescription(remote_description()->description());
 
   // Check if the descriptions include streams, just in case the peer supports
   // MSID, but doesn't indicate so with "a=msid-semantic".
@@ -2887,12 +2876,10 @@
       }
     }
 
-    // Update the DataChannels with the information from the remote peer.
-    if (data_desc) {
-      if (absl::StartsWith(data_desc->protocol(),
-                           cricket::kMediaProtocolRtpPrefix)) {
-        UpdateRemoteRtpDataChannels(GetActiveStreams(data_desc));
-      }
+    // If this is an RTP data transport, update the DataChannels with the
+    // information from the remote peer.
+    if (rtp_data_desc) {
+      UpdateRemoteRtpDataChannels(GetActiveStreams(rtp_data_desc));
     }
 
     // Iterate new_streams and notify the observer about new MediaStreams.
diff --git a/pc/peer_connection_data_channel_unittest.cc b/pc/peer_connection_data_channel_unittest.cc
index ad3817e..4080dd9 100644
--- a/pc/peer_connection_data_channel_unittest.cc
+++ b/pc/peer_connection_data_channel_unittest.cc
@@ -193,14 +193,11 @@
   // Changes the SCTP data channel port on the given session description.
   void ChangeSctpPortOnDescription(cricket::SessionDescription* desc,
                                    int port) {
-    cricket::DataCodec sctp_codec(cricket::kGoogleSctpDataCodecPlType,
-                                  cricket::kGoogleSctpDataCodecName);
-    sctp_codec.SetParam(cricket::kCodecParamPort, port);
-
     auto* data_content = cricket::GetFirstDataContent(desc);
     RTC_DCHECK(data_content);
-    auto* data_desc = data_content->media_description()->as_data();
-    data_desc->set_codecs({sctp_codec});
+    auto* data_desc = data_content->media_description()->as_sctp();
+    RTC_DCHECK(data_desc);
+    data_desc->set_port(port);
   }
 
   std::unique_ptr<rtc::VirtualSocketServer> vss_;
diff --git a/pc/peer_connection_integrationtest.cc b/pc/peer_connection_integrationtest.cc
index d514152..3a39971 100644
--- a/pc/peer_connection_integrationtest.cc
+++ b/pc/peer_connection_integrationtest.cc
@@ -3450,8 +3450,8 @@
 }
 
 static void MakeSpecCompliantSctpOffer(cricket::SessionDescription* desc) {
-  cricket::DataContentDescription* dcd_offer =
-      GetFirstDataContentDescription(desc);
+  cricket::SctpDataContentDescription* dcd_offer =
+      GetFirstSctpDataContentDescription(desc);
   ASSERT_TRUE(dcd_offer);
   dcd_offer->set_use_sctpmap(false);
   dcd_offer->set_protocol("UDP/DTLS/SCTP");
diff --git a/pc/session_description.cc b/pc/session_description.cc
index d4ccb50..cf3d147 100644
--- a/pc/session_description.cc
+++ b/pc/session_description.cc
@@ -15,6 +15,7 @@
 
 #include "absl/algorithm/container.h"
 #include "absl/memory/memory.h"
+#include "pc/media_protocol_names.h"
 #include "rtc_base/checks.h"
 
 namespace cricket {
@@ -183,6 +184,24 @@
 }
 
 void SessionDescription::AddContent(ContentInfo* content) {
+  // Unwrap the as_data shim layer before using.
+  auto* description = content->media_description();
+  bool should_delete = false;
+  if (description->as_rtp_data()) {
+    if (description->as_rtp_data() != description) {
+      content->set_media_description(
+          description->as_data()->Unshim(&should_delete));
+    }
+  }
+  if (description->as_sctp()) {
+    if (description->as_sctp() != description) {
+      content->set_media_description(
+          description->as_data()->Unshim(&should_delete));
+    }
+  }
+  if (should_delete) {
+    delete description;
+  }
   if (extmap_allow_mixed()) {
     // Mixed support on session level overrides setting on media level.
     content->description->set_extmap_allow_mixed_enum(
@@ -272,4 +291,406 @@
   return NULL;
 }
 
+// DataContentDescription shim creation
+DataContentDescription* RtpDataContentDescription::as_data() {
+  if (!shim_) {
+    shim_.reset(new DataContentDescription(this));
+  }
+  return shim_.get();
+}
+
+const DataContentDescription* RtpDataContentDescription::as_data() const {
+  return const_cast<RtpDataContentDescription*>(this)->as_data();
+}
+
+DataContentDescription* SctpDataContentDescription::as_data() {
+  if (!shim_) {
+    shim_.reset(new DataContentDescription(this));
+  }
+  return shim_.get();
+}
+
+const DataContentDescription* SctpDataContentDescription::as_data() const {
+  return const_cast<SctpDataContentDescription*>(this)->as_data();
+}
+
+DataContentDescription::DataContentDescription() {
+  // In this case, we will initialize |owned_description_| as soon as
+  // we are told what protocol to use via set_protocol or another function
+  // calling CreateShimTarget.
+}
+
+DataContentDescription::DataContentDescription(
+    SctpDataContentDescription* wrapped)
+    : real_description_(wrapped) {
+  // SctpDataContentDescription doesn't contain codecs, but code
+  // using DataContentDescription expects to see one.
+  Super::AddCodec(
+      cricket::DataCodec(kGoogleSctpDataCodecPlType, kGoogleSctpDataCodecName));
+}
+
+DataContentDescription::DataContentDescription(
+    RtpDataContentDescription* wrapped)
+    : real_description_(wrapped) {}
+
+DataContentDescription::DataContentDescription(
+    const DataContentDescription* o) {
+  if (o->real_description_) {
+    owned_description_ = absl::WrapUnique(o->real_description_->Copy());
+    real_description_ = owned_description_.get();
+  } else {
+    // Copy all information collected so far, including codecs.
+    Super::operator=(*o);
+  }
+}
+
+void DataContentDescription::CreateShimTarget(bool is_sctp) {
+  RTC_LOG(LS_INFO) << "Creating shim target, is_sctp is " << is_sctp;
+  RTC_CHECK(!owned_description_.get());
+  if (is_sctp) {
+    owned_description_ = absl::make_unique<SctpDataContentDescription>();
+    // Copy all information collected so far, except codecs.
+    owned_description_->MediaContentDescription::operator=(*this);
+  } else {
+    owned_description_ = absl::make_unique<RtpDataContentDescription>();
+    // Copy all information collected so far, including codecs.
+    owned_description_->as_rtp_data()
+        ->MediaContentDescriptionImpl<RtpDataCodec>::operator=(*this);
+  }
+  real_description_ = owned_description_.get();
+}
+
+MediaContentDescription* DataContentDescription::Unshim(bool* should_delete) {
+  // If protocol isn't decided at this point, we have a problem.
+  RTC_CHECK(real_description_);
+  if (owned_description_) {
+    // Pass ownership to caller, and remove myself.
+    // Since caller can't know if I was owner or owned, tell them.
+    MediaContentDescription* to_return = owned_description_.release();
+    *should_delete = true;
+    return to_return;
+  }
+  // Real object is owner, and presumably referenced from elsewhere.
+  *should_delete = false;
+  return real_description_;
+}
+
+void DataContentDescription::set_protocol(const std::string& protocol) {
+  if (!real_description_) {
+    CreateShimTarget(IsSctpProtocol(protocol));
+  }
+  real_description_->set_protocol(protocol);
+}
+
+bool DataContentDescription::IsSctp() const {
+  return (real_description_ && real_description_->as_sctp());
+}
+
+void DataContentDescription::EnsureIsRtp() {
+  RTC_CHECK(real_description_);
+  RTC_CHECK(real_description_->as_rtp_data());
+}
+
+RtpDataContentDescription* DataContentDescription::as_rtp_data() {
+  if (real_description_) {
+    return real_description_->as_rtp_data();
+  }
+  return nullptr;
+}
+
+SctpDataContentDescription* DataContentDescription::as_sctp() {
+  if (real_description_) {
+    return real_description_->as_sctp();
+  }
+  return nullptr;
+}
+
+// Override all methods defined in MediaContentDescription.
+bool DataContentDescription::has_codecs() const {
+  if (!real_description_) {
+    return Super::has_codecs();
+  }
+  return real_description_->has_codecs();
+}
+std::string DataContentDescription::protocol() const {
+  if (!real_description_) {
+    return Super::protocol();
+  }
+  return real_description_->protocol();
+}
+
+webrtc::RtpTransceiverDirection DataContentDescription::direction() const {
+  if (!real_description_) {
+    return Super::direction();
+  }
+  return real_description_->direction();
+}
+void DataContentDescription::set_direction(
+    webrtc::RtpTransceiverDirection direction) {
+  if (!real_description_) {
+    return Super::set_direction(direction);
+  }
+  return real_description_->set_direction(direction);
+}
+bool DataContentDescription::rtcp_mux() const {
+  if (!real_description_) {
+    return Super::rtcp_mux();
+  }
+  return real_description_->rtcp_mux();
+}
+void DataContentDescription::set_rtcp_mux(bool mux) {
+  if (!real_description_) {
+    Super::set_rtcp_mux(mux);
+    return;
+  }
+  real_description_->set_rtcp_mux(mux);
+}
+bool DataContentDescription::rtcp_reduced_size() const {
+  if (!real_description_) {
+    return Super::rtcp_reduced_size();
+  }
+  return real_description_->rtcp_reduced_size();
+}
+void DataContentDescription::set_rtcp_reduced_size(bool reduced_size) {
+  if (!real_description_) {
+    return Super::set_rtcp_reduced_size(reduced_size);
+  }
+
+  return real_description_->set_rtcp_reduced_size(reduced_size);
+}
+int DataContentDescription::bandwidth() const {
+  if (!real_description_) {
+    return Super::bandwidth();
+  }
+
+  return real_description_->bandwidth();
+}
+void DataContentDescription::set_bandwidth(int bandwidth) {
+  if (!real_description_) {
+    return Super::set_bandwidth(bandwidth);
+  }
+
+  return real_description_->set_bandwidth(bandwidth);
+}
+const std::vector<CryptoParams>& DataContentDescription::cryptos() const {
+  if (!real_description_) {
+    return Super::cryptos();
+  }
+
+  return real_description_->cryptos();
+}
+void DataContentDescription::AddCrypto(const CryptoParams& params) {
+  if (!real_description_) {
+    return Super::AddCrypto(params);
+  }
+
+  return real_description_->AddCrypto(params);
+}
+void DataContentDescription::set_cryptos(
+    const std::vector<CryptoParams>& cryptos) {
+  if (!real_description_) {
+    return Super::set_cryptos(cryptos);
+  }
+
+  return real_description_->set_cryptos(cryptos);
+}
+const RtpHeaderExtensions& DataContentDescription::rtp_header_extensions()
+    const {
+  if (!real_description_) {
+    return Super::rtp_header_extensions();
+  }
+
+  return real_description_->rtp_header_extensions();
+}
+void DataContentDescription::set_rtp_header_extensions(
+    const RtpHeaderExtensions& extensions) {
+  if (!real_description_) {
+    return Super::set_rtp_header_extensions(extensions);
+  }
+
+  return real_description_->set_rtp_header_extensions(extensions);
+}
+void DataContentDescription::AddRtpHeaderExtension(
+    const webrtc::RtpExtension& ext) {
+  if (!real_description_) {
+    return Super::AddRtpHeaderExtension(ext);
+  }
+  return real_description_->AddRtpHeaderExtension(ext);
+}
+void DataContentDescription::AddRtpHeaderExtension(
+    const cricket::RtpHeaderExtension& ext) {
+  if (!real_description_) {
+    return Super::AddRtpHeaderExtension(ext);
+  }
+  return real_description_->AddRtpHeaderExtension(ext);
+}
+void DataContentDescription::ClearRtpHeaderExtensions() {
+  if (!real_description_) {
+    return Super::ClearRtpHeaderExtensions();
+  }
+  return real_description_->ClearRtpHeaderExtensions();
+}
+bool DataContentDescription::rtp_header_extensions_set() const {
+  if (!real_description_) {
+    return Super::rtp_header_extensions_set();
+  }
+  return real_description_->rtp_header_extensions_set();
+}
+const StreamParamsVec& DataContentDescription::streams() const {
+  if (!real_description_) {
+    return Super::streams();
+  }
+  return real_description_->streams();
+}
+StreamParamsVec& DataContentDescription::mutable_streams() {
+  if (!real_description_) {
+    return Super::mutable_streams();
+  }
+  return real_description_->mutable_streams();
+}
+void DataContentDescription::AddStream(const StreamParams& stream) {
+  if (!real_description_) {
+    return Super::AddStream(stream);
+  }
+  return real_description_->AddStream(stream);
+}
+void DataContentDescription::SetCnameIfEmpty(const std::string& cname) {
+  if (!real_description_) {
+    return Super::SetCnameIfEmpty(cname);
+  }
+  return real_description_->SetCnameIfEmpty(cname);
+}
+uint32_t DataContentDescription::first_ssrc() const {
+  if (!real_description_) {
+    return Super::first_ssrc();
+  }
+  return real_description_->first_ssrc();
+}
+bool DataContentDescription::has_ssrcs() const {
+  if (!real_description_) {
+    return Super::has_ssrcs();
+  }
+  return real_description_->has_ssrcs();
+}
+void DataContentDescription::set_conference_mode(bool enable) {
+  if (!real_description_) {
+    return Super::set_conference_mode(enable);
+  }
+  return real_description_->set_conference_mode(enable);
+}
+bool DataContentDescription::conference_mode() const {
+  if (!real_description_) {
+    return Super::conference_mode();
+  }
+  return real_description_->conference_mode();
+}
+void DataContentDescription::set_connection_address(
+    const rtc::SocketAddress& address) {
+  if (!real_description_) {
+    return Super::set_connection_address(address);
+  }
+  return real_description_->set_connection_address(address);
+}
+const rtc::SocketAddress& DataContentDescription::connection_address() const {
+  if (!real_description_) {
+    return Super::connection_address();
+  }
+  return real_description_->connection_address();
+}
+void DataContentDescription::set_extmap_allow_mixed_enum(
+    ExtmapAllowMixed mixed) {
+  if (!real_description_) {
+    return Super::set_extmap_allow_mixed_enum(mixed);
+  }
+  return real_description_->set_extmap_allow_mixed_enum(mixed);
+}
+MediaContentDescription::ExtmapAllowMixed
+DataContentDescription::extmap_allow_mixed_enum() const {
+  if (!real_description_) {
+    return Super::extmap_allow_mixed_enum();
+  }
+  return real_description_->extmap_allow_mixed_enum();
+}
+bool DataContentDescription::HasSimulcast() const {
+  if (!real_description_) {
+    return Super::HasSimulcast();
+  }
+  return real_description_->HasSimulcast();
+}
+SimulcastDescription& DataContentDescription::simulcast_description() {
+  if (!real_description_) {
+    return Super::simulcast_description();
+  }
+  return real_description_->simulcast_description();
+}
+const SimulcastDescription& DataContentDescription::simulcast_description()
+    const {
+  if (!real_description_) {
+    return Super::simulcast_description();
+  }
+  return real_description_->simulcast_description();
+}
+void DataContentDescription::set_simulcast_description(
+    const SimulcastDescription& simulcast) {
+  if (!real_description_) {
+    return Super::set_simulcast_description(simulcast);
+  }
+  return real_description_->set_simulcast_description(simulcast);
+}
+
+// Methods defined in MediaContentDescriptionImpl.
+// For SCTP, we implement codec handling.
+// For RTP, we pass the codecs.
+// In the cases where type hasn't been decided yet, we return dummies.
+
+const std::vector<DataCodec>& DataContentDescription::codecs() const {
+  if (IsSctp() || !real_description_) {
+    return Super::codecs();
+  }
+  return real_description_->as_rtp_data()->codecs();
+}
+
+void DataContentDescription::set_codecs(const std::vector<DataCodec>& codecs) {
+  if (IsSctp() || !real_description_) {
+    Super::set_codecs(codecs);
+  } else {
+    EnsureIsRtp();
+    real_description_->as_rtp_data()->set_codecs(codecs);
+  }
+}
+
+bool DataContentDescription::HasCodec(int id) {
+  if (IsSctp() || !real_description_) {
+    return Super::HasCodec(id);
+  }
+  return real_description_->as_rtp_data()->HasCodec(id);
+}
+
+void DataContentDescription::AddCodec(const DataCodec& codec) {
+  if (IsSctp() || !real_description_) {
+    Super::AddCodec(codec);
+  } else {
+    EnsureIsRtp();
+    real_description_->as_rtp_data()->AddCodec(codec);
+  }
+}
+
+void DataContentDescription::AddOrReplaceCodec(const DataCodec& codec) {
+  if (IsSctp() || real_description_) {
+    Super::AddOrReplaceCodec(codec);
+  } else {
+    EnsureIsRtp();
+    real_description_->as_rtp_data()->AddOrReplaceCodec(codec);
+  }
+}
+
+void DataContentDescription::AddCodecs(const std::vector<DataCodec>& codecs) {
+  if (IsSctp() || !real_description_) {
+    Super::AddCodecs(codecs);
+  } else {
+    EnsureIsRtp();
+    real_description_->as_rtp_data()->AddCodecs(codecs);
+  }
+}
+
 }  // namespace cricket
diff --git a/pc/session_description.h b/pc/session_description.h
index 7e62510..27b781f 100644
--- a/pc/session_description.h
+++ b/pc/session_description.h
@@ -18,6 +18,7 @@
 #include <string>
 #include <vector>
 
+#include "absl/memory/memory.h"
 #include "api/crypto_params.h"
 #include "api/media_types.h"
 #include "api/rtp_parameters.h"
@@ -26,6 +27,7 @@
 #include "media/base/stream_params.h"
 #include "p2p/base/transport_description.h"
 #include "p2p/base/transport_info.h"
+#include "pc/media_protocol_names.h"
 #include "pc/simulcast_description.h"
 #include "rtc_base/deprecation.h"
 #include "rtc_base/socket_address.h"
@@ -34,7 +36,7 @@
 
 typedef std::vector<AudioCodec> AudioCodecs;
 typedef std::vector<VideoCodec> VideoCodecs;
-typedef std::vector<DataCodec> DataCodecs;
+typedef std::vector<RtpDataCodec> RtpDataCodecs;
 typedef std::vector<CryptoParams> CryptoParamsVec;
 typedef std::vector<webrtc::RtpExtension> RtpHeaderExtensions;
 
@@ -45,19 +47,15 @@
 
 extern const char kMediaProtocolDtlsSavpf[];
 
-extern const char kMediaProtocolRtpPrefix[];
-
-extern const char kMediaProtocolSctp[];
-extern const char kMediaProtocolDtlsSctp[];
-extern const char kMediaProtocolUdpDtlsSctp[];
-extern const char kMediaProtocolTcpDtlsSctp[];
 
 // Options to control how session descriptions are generated.
 const int kAutoBandwidth = -1;
 
 class AudioContentDescription;
-class DataContentDescription;
 class VideoContentDescription;
+class DataContentDescription;
+class RtpDataContentDescription;
+class SctpDataContentDescription;
 
 // Describes a session description media section. There are subclasses for each
 // media type (audio, video, data) that will have additional information.
@@ -78,61 +76,77 @@
   virtual VideoContentDescription* as_video() { return nullptr; }
   virtual const VideoContentDescription* as_video() const { return nullptr; }
 
-  // Try to cast this media description to a DataContentDescription. Returns
-  // nullptr if the cast fails.
+  // Backwards compatible shim: Return a shim object that allows
+  // callers to ignore the distinction between RtpDataContentDescription
+  // and SctpDataContentDescription objects.
   virtual DataContentDescription* as_data() { return nullptr; }
   virtual const DataContentDescription* as_data() const { return nullptr; }
 
+  virtual RtpDataContentDescription* as_rtp_data() { return nullptr; }
+  virtual const RtpDataContentDescription* as_rtp_data() const {
+    return nullptr;
+  }
+
+  virtual SctpDataContentDescription* as_sctp() { return nullptr; }
+  virtual const SctpDataContentDescription* as_sctp() const { return nullptr; }
+
   virtual bool has_codecs() const = 0;
 
   virtual MediaContentDescription* Copy() const = 0;
 
   // |protocol| is the expected media transport protocol, such as RTP/AVPF,
   // RTP/SAVPF or SCTP/DTLS.
-  std::string protocol() const { return protocol_; }
-  void set_protocol(const std::string& protocol) { protocol_ = protocol; }
+  virtual std::string protocol() const { return protocol_; }
+  virtual void set_protocol(const std::string& protocol) {
+    protocol_ = protocol;
+  }
 
-  webrtc::RtpTransceiverDirection direction() const { return direction_; }
-  void set_direction(webrtc::RtpTransceiverDirection direction) {
+  virtual webrtc::RtpTransceiverDirection direction() const {
+    return direction_;
+  }
+  virtual void set_direction(webrtc::RtpTransceiverDirection direction) {
     direction_ = direction;
   }
 
-  bool rtcp_mux() const { return rtcp_mux_; }
-  void set_rtcp_mux(bool mux) { rtcp_mux_ = mux; }
+  virtual bool rtcp_mux() const { return rtcp_mux_; }
+  virtual void set_rtcp_mux(bool mux) { rtcp_mux_ = mux; }
 
-  bool rtcp_reduced_size() const { return rtcp_reduced_size_; }
-  void set_rtcp_reduced_size(bool reduced_size) {
+  virtual bool rtcp_reduced_size() const { return rtcp_reduced_size_; }
+  virtual void set_rtcp_reduced_size(bool reduced_size) {
     rtcp_reduced_size_ = reduced_size;
   }
 
-  int bandwidth() const { return bandwidth_; }
-  void set_bandwidth(int bandwidth) { bandwidth_ = bandwidth; }
+  virtual int bandwidth() const { return bandwidth_; }
+  virtual void set_bandwidth(int bandwidth) { bandwidth_ = bandwidth; }
 
-  const std::vector<CryptoParams>& cryptos() const { return cryptos_; }
-  void AddCrypto(const CryptoParams& params) { cryptos_.push_back(params); }
-  void set_cryptos(const std::vector<CryptoParams>& cryptos) {
+  virtual const std::vector<CryptoParams>& cryptos() const { return cryptos_; }
+  virtual void AddCrypto(const CryptoParams& params) {
+    cryptos_.push_back(params);
+  }
+  virtual void set_cryptos(const std::vector<CryptoParams>& cryptos) {
     cryptos_ = cryptos;
   }
 
-  const RtpHeaderExtensions& rtp_header_extensions() const {
+  virtual const RtpHeaderExtensions& rtp_header_extensions() const {
     return rtp_header_extensions_;
   }
-  void set_rtp_header_extensions(const RtpHeaderExtensions& extensions) {
+  virtual void set_rtp_header_extensions(
+      const RtpHeaderExtensions& extensions) {
     rtp_header_extensions_ = extensions;
     rtp_header_extensions_set_ = true;
   }
-  void AddRtpHeaderExtension(const webrtc::RtpExtension& ext) {
+  virtual void AddRtpHeaderExtension(const webrtc::RtpExtension& ext) {
     rtp_header_extensions_.push_back(ext);
     rtp_header_extensions_set_ = true;
   }
-  void AddRtpHeaderExtension(const cricket::RtpHeaderExtension& ext) {
+  virtual void AddRtpHeaderExtension(const cricket::RtpHeaderExtension& ext) {
     webrtc::RtpExtension webrtc_extension;
     webrtc_extension.uri = ext.uri;
     webrtc_extension.id = ext.id;
     rtp_header_extensions_.push_back(webrtc_extension);
     rtp_header_extensions_set_ = true;
   }
-  void ClearRtpHeaderExtensions() {
+  virtual void ClearRtpHeaderExtensions() {
     rtp_header_extensions_.clear();
     rtp_header_extensions_set_ = true;
   }
@@ -141,62 +155,65 @@
   // signal them. For now we assume an empty list means no signaling, but
   // provide the ClearRtpHeaderExtensions method to allow "no support" to be
   // clearly indicated (i.e. when derived from other information).
-  bool rtp_header_extensions_set() const { return rtp_header_extensions_set_; }
-  const StreamParamsVec& streams() const { return send_streams_; }
+  virtual bool rtp_header_extensions_set() const {
+    return rtp_header_extensions_set_;
+  }
+  virtual const StreamParamsVec& streams() const { return send_streams_; }
   // TODO(pthatcher): Remove this by giving mediamessage.cc access
   // to MediaContentDescription
-  StreamParamsVec& mutable_streams() { return send_streams_; }
-  void AddStream(const StreamParams& stream) {
+  virtual StreamParamsVec& mutable_streams() { return send_streams_; }
+  virtual void AddStream(const StreamParams& stream) {
     send_streams_.push_back(stream);
   }
   // Legacy streams have an ssrc, but nothing else.
   void AddLegacyStream(uint32_t ssrc) {
-    send_streams_.push_back(StreamParams::CreateLegacy(ssrc));
+    AddStream(StreamParams::CreateLegacy(ssrc));
   }
   void AddLegacyStream(uint32_t ssrc, uint32_t fid_ssrc) {
     StreamParams sp = StreamParams::CreateLegacy(ssrc);
     sp.AddFidSsrc(ssrc, fid_ssrc);
-    send_streams_.push_back(sp);
+    AddStream(sp);
   }
 
   // Sets the CNAME of all StreamParams if it have not been set.
-  void SetCnameIfEmpty(const std::string& cname) {
+  virtual void SetCnameIfEmpty(const std::string& cname) {
     for (cricket::StreamParamsVec::iterator it = send_streams_.begin();
          it != send_streams_.end(); ++it) {
       if (it->cname.empty())
         it->cname = cname;
     }
   }
-  uint32_t first_ssrc() const {
+  virtual uint32_t first_ssrc() const {
     if (send_streams_.empty()) {
       return 0;
     }
     return send_streams_[0].first_ssrc();
   }
-  bool has_ssrcs() const {
+  virtual bool has_ssrcs() const {
     if (send_streams_.empty()) {
       return false;
     }
     return send_streams_[0].has_ssrcs();
   }
 
-  void set_conference_mode(bool enable) { conference_mode_ = enable; }
-  bool conference_mode() const { return conference_mode_; }
+  virtual void set_conference_mode(bool enable) { conference_mode_ = enable; }
+  virtual bool conference_mode() const { return conference_mode_; }
 
   // https://tools.ietf.org/html/rfc4566#section-5.7
   // May be present at the media or session level of SDP. If present at both
   // levels, the media-level attribute overwrites the session-level one.
-  void set_connection_address(const rtc::SocketAddress& address) {
+  virtual void set_connection_address(const rtc::SocketAddress& address) {
     connection_address_ = address;
   }
-  const rtc::SocketAddress& connection_address() const {
+  virtual const rtc::SocketAddress& connection_address() const {
     return connection_address_;
   }
 
   // Determines if it's allowed to mix one- and two-byte rtp header extensions
   // within the same rtp stream.
   enum ExtmapAllowMixed { kNo, kSession, kMedia };
-  void set_extmap_allow_mixed_enum(ExtmapAllowMixed new_extmap_allow_mixed) {
+  virtual void set_extmap_allow_mixed_enum(
+      ExtmapAllowMixed new_extmap_allow_mixed) {
     if (new_extmap_allow_mixed == kMedia &&
         extmap_allow_mixed_enum_ == kSession) {
       // Do not downgrade from session level to media level.
@@ -204,10 +221,12 @@
     }
     extmap_allow_mixed_enum_ = new_extmap_allow_mixed;
   }
-  ExtmapAllowMixed extmap_allow_mixed_enum() const {
+  virtual ExtmapAllowMixed extmap_allow_mixed_enum() const {
     return extmap_allow_mixed_enum_;
   }
-  bool extmap_allow_mixed() const { return extmap_allow_mixed_enum_ != kNo; }
+  virtual bool extmap_allow_mixed() const {
+    return extmap_allow_mixed_enum_ != kNo;
+  }
 
   // Simulcast functionality.
   virtual bool HasSimulcast() const { return !simulcast_.empty(); }
@@ -248,13 +267,18 @@
 template <class C>
 class MediaContentDescriptionImpl : public MediaContentDescription {
  public:
+  void set_protocol(const std::string& protocol) override {
+    RTC_DCHECK(IsRtpProtocol(protocol));
+    protocol_ = protocol;
+  }
+
   typedef C CodecType;
 
   // Codecs should be in preference order (most preferred codec first).
-  const std::vector<C>& codecs() const { return codecs_; }
-  void set_codecs(const std::vector<C>& codecs) { codecs_ = codecs; }
-  virtual bool has_codecs() const { return !codecs_.empty(); }
-  bool HasCodec(int id) {
+  virtual const std::vector<C>& codecs() const { return codecs_; }
+  virtual void set_codecs(const std::vector<C>& codecs) { codecs_ = codecs; }
+  bool has_codecs() const override { return !codecs_.empty(); }
+  virtual bool HasCodec(int id) {
     bool found = false;
     for (typename std::vector<C>::iterator iter = codecs_.begin();
          iter != codecs_.end(); ++iter) {
@@ -265,8 +289,8 @@
     }
     return found;
   }
-  void AddCodec(const C& codec) { codecs_.push_back(codec); }
-  void AddOrReplaceCodec(const C& codec) {
+  virtual void AddCodec(const C& codec) { codecs_.push_back(codec); }
+  virtual void AddOrReplaceCodec(const C& codec) {
     for (typename std::vector<C>::iterator iter = codecs_.begin();
          iter != codecs_.end(); ++iter) {
       if (iter->id == codec.id) {
@@ -276,7 +300,7 @@
     }
     AddCodec(codec);
   }
-  void AddCodecs(const std::vector<C>& codecs) {
+  virtual void AddCodecs(const std::vector<C>& codecs) {
     typename std::vector<C>::const_iterator codec;
     for (codec = codecs.begin(); codec != codecs.end(); ++codec) {
       AddCodec(*codec);
@@ -309,22 +333,173 @@
   virtual const VideoContentDescription* as_video() const { return this; }
 };
 
+// The DataContentDescription is a shim over the RtpDataContentDescription
+// and SctpDataContentDescription classes that is used for external callers
+// into this internal API.
+// It is a templated derivation of MediaContentDescriptionImpl because
+// that's what the external caller expects it to be.
+// TODO(bugs.webrtc.org/10597): Declare this class obsolete and remove it
+// once external callers have been updated.
 class DataContentDescription : public MediaContentDescriptionImpl<DataCodec> {
  public:
-  DataContentDescription() {}
+  DataContentDescription();
+  MediaType type() const override { return MEDIA_TYPE_DATA; }
+  DataContentDescription* as_data() override { return this; }
+  const DataContentDescription* as_data() const override { return this; }
 
-  virtual DataContentDescription* Copy() const {
-    return new DataContentDescription(*this);
+  // Override all methods defined in MediaContentDescription.
+  bool has_codecs() const override;
+  DataContentDescription* Copy() const override {
+    return new DataContentDescription(this);
   }
-  virtual MediaType type() const { return MEDIA_TYPE_DATA; }
-  virtual DataContentDescription* as_data() { return this; }
-  virtual const DataContentDescription* as_data() const { return this; }
+  std::string protocol() const override;
+  void set_protocol(const std::string& protocol) override;
+  webrtc::RtpTransceiverDirection direction() const override;
+  void set_direction(webrtc::RtpTransceiverDirection direction) override;
+  bool rtcp_mux() const override;
+  void set_rtcp_mux(bool mux) override;
+  bool rtcp_reduced_size() const override;
+  void set_rtcp_reduced_size(bool) override;
+  int bandwidth() const override;
+  void set_bandwidth(int bandwidth) override;
+  const std::vector<CryptoParams>& cryptos() const override;
+  void AddCrypto(const CryptoParams& params) override;
+  void set_cryptos(const std::vector<CryptoParams>& cryptos) override;
+  const RtpHeaderExtensions& rtp_header_extensions() const override;
+  void set_rtp_header_extensions(
+      const RtpHeaderExtensions& extensions) override;
+  void AddRtpHeaderExtension(const webrtc::RtpExtension& ext) override;
+  void AddRtpHeaderExtension(const cricket::RtpHeaderExtension& ext) override;
+  void ClearRtpHeaderExtensions() override;
+  bool rtp_header_extensions_set() const override;
+  const StreamParamsVec& streams() const override;
+  StreamParamsVec& mutable_streams() override;
+  void AddStream(const StreamParams& stream) override;
+  void SetCnameIfEmpty(const std::string& cname) override;
+  uint32_t first_ssrc() const override;
+  bool has_ssrcs() const override;
+  void set_conference_mode(bool enable) override;
+  bool conference_mode() const override;
+  void set_connection_address(const rtc::SocketAddress& address) override;
+  const rtc::SocketAddress& connection_address() const override;
+  void set_extmap_allow_mixed_enum(ExtmapAllowMixed) override;
+  ExtmapAllowMixed extmap_allow_mixed_enum() const override;
+  bool HasSimulcast() const override;
+  SimulcastDescription& simulcast_description() override;
+  const SimulcastDescription& simulcast_description() const override;
+  void set_simulcast_description(
+      const SimulcastDescription& simulcast) override;
+
+  // Override all methods defined in MediaContentDescriptionImpl.
+  const std::vector<CodecType>& codecs() const override;
+  void set_codecs(const std::vector<CodecType>& codecs) override;
+  bool HasCodec(int id) override;
+  void AddCodec(const CodecType& codec) override;
+  void AddOrReplaceCodec(const CodecType& codec) override;
+  void AddCodecs(const std::vector<CodecType>& codec) override;
+
+ private:
+  typedef MediaContentDescriptionImpl<DataCodec> Super;
+  // Friend classes are allowed to create proxies for themselves.
+  friend class RtpDataContentDescription;  // for constructors
+  friend class SctpDataContentDescription;
+  friend class SessionDescription;  // for Unshim()
+  // Copy constructor. A copy results in an object that owns its
+  // real description, which is a copy of the original description
+  // (whether that was owned or not).
+  explicit DataContentDescription(const DataContentDescription* o);
+
+  explicit DataContentDescription(RtpDataContentDescription*);
+  explicit DataContentDescription(SctpDataContentDescription*);
+
+  // Exposed for internal use - new clients should not use this class.
+  RtpDataContentDescription* as_rtp_data() override;
+  SctpDataContentDescription* as_sctp() override;
+
+  // Create a shimmed object, owned by the shim.
+  void CreateShimTarget(bool is_sctp);
+
+  // Return the shimmed object, passing ownership if owned, and set
+  // |should_delete| to true if it was the owner. If |should_delete|
+  // is true on return, the caller should immediately delete the
+  // DataContentDescription object.
+  MediaContentDescription* Unshim(bool* should_delete);
+
+  // Returns whether SCTP is in use. False when it's not decided.
+  bool IsSctp() const;
+  // Check function for use when caller obviously assumes RTP.
+  void EnsureIsRtp();
+
+  MediaContentDescription* real_description_ = nullptr;
+  std::unique_ptr<MediaContentDescription> owned_description_;
+};
+
+class RtpDataContentDescription
+    : public MediaContentDescriptionImpl<RtpDataCodec> {
+ public:
+  RtpDataContentDescription() {}
+  RtpDataContentDescription(const RtpDataContentDescription& o)
+      : MediaContentDescriptionImpl<RtpDataCodec>(o), shim_(nullptr) {}
+  RtpDataContentDescription& operator=(const RtpDataContentDescription& o) {
+    this->MediaContentDescriptionImpl<RtpDataCodec>::operator=(o);
+    // Do not copy the shim.
+    return *this;
+  }
+
+  RtpDataContentDescription* Copy() const override {
+    return new RtpDataContentDescription(*this);
+  }
+  MediaType type() const override { return MEDIA_TYPE_DATA; }
+  RtpDataContentDescription* as_rtp_data() override { return this; }
+  const RtpDataContentDescription* as_rtp_data() const override { return this; }
+  // Shim support
+  DataContentDescription* as_data() override;
+  const DataContentDescription* as_data() const override;
+
+ private:
+  std::unique_ptr<DataContentDescription> shim_;
+};
+
+class SctpDataContentDescription : public MediaContentDescription {
+ public:
+  SctpDataContentDescription() {}
+  SctpDataContentDescription(const SctpDataContentDescription& o)
+      : MediaContentDescription(o),
+        use_sctpmap_(o.use_sctpmap_),
+        port_(o.port_),
+        max_message_size_(o.max_message_size_),
+        shim_(nullptr) {}
+  SctpDataContentDescription* Copy() const override {
+    return new SctpDataContentDescription(*this);
+  }
+  MediaType type() const override { return MEDIA_TYPE_DATA; }
+  SctpDataContentDescription* as_sctp() override { return this; }
+  const SctpDataContentDescription* as_sctp() const override { return this; }
+  // Shim support
+  DataContentDescription* as_data() override;
+  const DataContentDescription* as_data() const override;
+
+  bool has_codecs() const override { return false; }
+  void set_protocol(const std::string& protocol) override {
+    RTC_DCHECK(IsSctpProtocol(protocol));
+    protocol_ = protocol;
+  }
 
   bool use_sctpmap() const { return use_sctpmap_; }
   void set_use_sctpmap(bool enable) { use_sctpmap_ = enable; }
+  int port() const { return port_; }
+  void set_port(int port) { port_ = port; }
+  int max_message_size() const { return max_message_size_; }
+  void set_max_message_size(int max_message_size) {
+    max_message_size_ = max_message_size;
+  }
 
  private:
-  bool use_sctpmap_ = true;
+  bool use_sctpmap_ = true;  // Note: "true" is no longer conformant.
+  // Defaults should be constants imported from SCTP. Quick hack.
+  int port_ = 5000;
+  int max_message_size_ = 256 * 1024;
+  std::unique_ptr<DataContentDescription> shim_;
 };
 
 // Protocol used for encoding media. This is the "top level" protocol that may
diff --git a/pc/session_description_unittest.cc b/pc/session_description_unittest.cc
index 3b05dca..9797ed5 100644
--- a/pc/session_description_unittest.cc
+++ b/pc/session_description_unittest.cc
@@ -9,6 +9,7 @@
  */
 #include "pc/session_description.h"
 
+#include "absl/memory/memory.h"
 #include "test/gtest.h"
 
 namespace cricket {
@@ -121,11 +122,69 @@
             video_desc->extmap_allow_mixed_enum());
 
   // Session level setting overrides media level when new content is added.
-  MediaContentDescription* data_desc = new DataContentDescription;
+  MediaContentDescription* data_desc = new RtpDataContentDescription;
   data_desc->set_extmap_allow_mixed_enum(MediaContentDescription::kMedia);
   session_desc.AddContent("data", MediaProtocolType::kRtp, data_desc);
   EXPECT_EQ(MediaContentDescription::kSession,
             data_desc->extmap_allow_mixed_enum());
 }
 
+TEST(SessionDescriptionTest, DataContentDescriptionCanAddStream) {
+  auto description = absl::make_unique<DataContentDescription>();
+  // Adding a stream without setting protocol first should work.
+  description->AddLegacyStream(1234);
+  EXPECT_EQ(1UL, description->streams().size());
+}
+
+TEST(SessionDescriptionTest, DataContentDescriptionCopyWorks) {
+  auto description = absl::make_unique<RtpDataContentDescription>();
+  auto shim_description = description->as_data();
+  auto shim_copy = shim_description->Copy();
+  delete shim_copy;
+}
+
+TEST(SessionDescriptionTest, DataContentDescriptionCodecsCallableOnNull) {
+  auto shim_description = absl::make_unique<DataContentDescription>();
+  auto codec_list = shim_description->codecs();
+  EXPECT_EQ(0UL, codec_list.size());
+}
+
+TEST(SessionDescriptionTest, DataContentDescriptionSctpConferenceMode) {
+  auto description = absl::make_unique<SctpDataContentDescription>();
+  auto shim_description = description->as_data();
+  EXPECT_FALSE(shim_description->conference_mode());
+  shim_description->set_conference_mode(true);
+  EXPECT_TRUE(shim_description->conference_mode());
+}
+
+TEST(SessionDescriptionTest, DataContentDesriptionInSessionIsUnwrapped) {
+  auto description = absl::make_unique<DataContentDescription>();
+  // Create a DTLS object behind the shim.
+  description->set_protocol(kMediaProtocolUdpDtlsSctp);
+  SessionDescription session;
+  session.AddContent("name", MediaProtocolType::kSctp, description.release());
+  ContentInfo* content = &(session.contents()[0]);
+  ASSERT_TRUE(content);
+  ASSERT_TRUE(content->media_description()->type() == MEDIA_TYPE_DATA);
+  ASSERT_TRUE(content->media_description()->as_sctp());
+}
+
+TEST(SessionDescriptionTest,
+     DataContentDescriptionInfoSurvivesInstantiationAsSctp) {
+  auto description = absl::make_unique<DataContentDescription>();
+  description->set_rtcp_mux(true);
+  description->set_protocol(kMediaProtocolUdpDtlsSctp);
+  EXPECT_TRUE(description->rtcp_mux());
+}
+
+TEST(SessionDescriptionTest,
+     DataContentDescriptionStreamInfoSurvivesInstantiationAsRtp) {
+  auto description = absl::make_unique<DataContentDescription>();
+  StreamParams stream;
+  description->AddLegacyStream(1234);
+  EXPECT_EQ(1UL, description->streams().size());
+  description->set_protocol(kMediaProtocolDtlsSavpf);
+  EXPECT_EQ(1UL, description->streams().size());
+}
+
 }  // namespace cricket
diff --git a/pc/webrtc_sdp.cc b/pc/webrtc_sdp.cc
index 97015fe..c6968b1 100644
--- a/pc/webrtc_sdp.cc
+++ b/pc/webrtc_sdp.cc
@@ -76,7 +76,9 @@
 using cricket::MediaProtocolType;
 using cricket::MediaType;
 using cricket::RidDescription;
+using cricket::RtpDataContentDescription;
 using cricket::RtpHeaderExtensions;
+using cricket::SctpDataContentDescription;
 using cricket::SimulcastDescription;
 using cricket::SimulcastLayer;
 using cricket::SimulcastLayerList;
@@ -285,8 +287,6 @@
                            std::string* message);
 static void BuildIceOptions(const std::vector<std::string>& transport_options,
                             std::string* message);
-static bool IsRtp(const std::string& protocol);
-static bool IsDtlsSctp(const std::string& protocol);
 static bool ParseSessionDescription(const std::string& message,
                                     size_t* pos,
                                     std::string* session_id,
@@ -1337,8 +1337,6 @@
   const MediaContentDescription* media_desc = content_info->media_description();
   RTC_DCHECK(media_desc);
 
-  int sctp_port = cricket::kSctpDefaultPort;
-
   // RFC 4566
   // m=<media> <port> <proto> <fmt>
   // fmt is a list of payload type numbers that MAY be used in the session.
@@ -1366,25 +1364,20 @@
       fmt.append(rtc::ToString(codec.id));
     }
   } else if (media_type == cricket::MEDIA_TYPE_DATA) {
-    const DataContentDescription* data_desc = media_desc->as_data();
-    if (IsDtlsSctp(media_desc->protocol())) {
+    const cricket::SctpDataContentDescription* sctp_data_desc =
+        media_desc->as_sctp();
+    if (sctp_data_desc) {
       fmt.append(" ");
 
-      if (data_desc->use_sctpmap()) {
-        for (const cricket::DataCodec& codec : data_desc->codecs()) {
-          if (absl::EqualsIgnoreCase(codec.name,
-                                     cricket::kGoogleSctpDataCodecName) &&
-              codec.GetParam(cricket::kCodecParamPort, &sctp_port)) {
-            break;
-          }
-        }
-
-        fmt.append(rtc::ToString(sctp_port));
+      if (sctp_data_desc->use_sctpmap()) {
+        fmt.append(rtc::ToString(sctp_data_desc->port()));
       } else {
         fmt.append(kDefaultSctpmapProtocol);
       }
     } else {
-      for (const cricket::DataCodec& codec : data_desc->codecs()) {
+      const RtpDataContentDescription* rtp_data_desc =
+          media_desc->as_rtp_data();
+      for (const cricket::RtpDataCodec& codec : rtp_data_desc->codecs()) {
         fmt.append(" ");
         fmt.append(rtc::ToString(codec.id));
       }
@@ -1455,7 +1448,7 @@
   }
 
   // Add the a=rtcp line.
-  if (IsRtp(media_desc->protocol())) {
+  if (cricket::IsRtpProtocol(media_desc->protocol())) {
     std::string rtcp_line = GetRtcpLine(candidates);
     if (!rtcp_line.empty()) {
       AddLine(rtcp_line, message);
@@ -1522,11 +1515,12 @@
   os << kSdpDelimiterColon << content_info->name;
   AddLine(os.str(), message);
 
-  if (IsDtlsSctp(media_desc->protocol())) {
-    const DataContentDescription* data_desc = media_desc->as_data();
+  if (cricket::IsDtlsSctp(media_desc->protocol())) {
+    const cricket::SctpDataContentDescription* data_desc =
+        media_desc->as_sctp();
     bool use_sctpmap = data_desc->use_sctpmap();
-    BuildSctpContentAttributes(message, sctp_port, use_sctpmap);
-  } else if (IsRtp(media_desc->protocol())) {
+    BuildSctpContentAttributes(message, data_desc->port(), use_sctpmap);
+  } else if (cricket::IsRtpProtocol(media_desc->protocol())) {
     BuildRtpContentAttributes(media_desc, media_type, msid_signaling, message);
   }
 }
@@ -1834,43 +1828,6 @@
   }
 }
 
-cricket::DataCodec FindOrMakeSctpDataCodec(DataContentDescription* media_desc) {
-  for (const auto& codec : media_desc->codecs()) {
-    if (absl::EqualsIgnoreCase(codec.name, cricket::kGoogleSctpDataCodecName)) {
-      return codec;
-    }
-  }
-  cricket::DataCodec codec_port(cricket::kGoogleSctpDataCodecPlType,
-                                cricket::kGoogleSctpDataCodecName);
-  return codec_port;
-}
-
-bool AddOrModifySctpDataCodecPort(DataContentDescription* media_desc,
-                                  int sctp_port) {
-  // Add the SCTP Port number as a pseudo-codec "port" parameter
-  auto codec = FindOrMakeSctpDataCodec(media_desc);
-  int dummy;
-  if (codec.GetParam(cricket::kCodecParamPort, &dummy)) {
-    return false;
-  }
-  codec.SetParam(cricket::kCodecParamPort, sctp_port);
-  media_desc->AddOrReplaceCodec(codec);
-  return true;
-}
-
-bool AddOrModifySctpDataMaxMessageSize(DataContentDescription* media_desc,
-                                       int max_message_size) {
-  // Add the SCTP Max Message Size as a pseudo-parameter to the codec
-  auto codec = FindOrMakeSctpDataCodec(media_desc);
-  int dummy;
-  if (codec.GetParam(cricket::kCodecParamMaxMessageSize, &dummy)) {
-    return false;
-  }
-  codec.SetParam(cricket::kCodecParamMaxMessageSize, max_message_size);
-  media_desc->AddOrReplaceCodec(codec);
-  return true;
-}
-
 bool GetMinValue(const std::vector<int>& values, int* value) {
   if (values.empty()) {
     return false;
@@ -1960,14 +1917,17 @@
       AddAttributeLine(kCodecParamPTime, ptime, message);
     }
   } else if (media_type == cricket::MEDIA_TYPE_DATA) {
-    for (const cricket::DataCodec& codec : media_desc->as_data()->codecs()) {
-      // RFC 4566
-      // a=rtpmap:<payload type> <encoding name>/<clock rate>
-      // [/<encodingparameters>]
-      InitAttrLine(kAttributeRtpmap, &os);
-      os << kSdpDelimiterColon << codec.id << " " << codec.name << "/"
-         << codec.clockrate;
-      AddLine(os.str(), message);
+    if (media_desc->as_rtp_data()) {
+      for (const cricket::RtpDataCodec& codec :
+           media_desc->as_rtp_data()->codecs()) {
+        // RFC 4566
+        // a=rtpmap:<payload type> <encoding name>/<clock rate>
+        // [/<encodingparameters>]
+        InitAttrLine(kAttributeRtpmap, &os);
+        os << kSdpDelimiterColon << codec.id << " " << codec.name << "/"
+           << codec.clockrate;
+        AddLine(os.str(), message);
+      }
     }
   }
 }
@@ -2053,16 +2013,6 @@
   }
 }
 
-bool IsRtp(const std::string& protocol) {
-  return protocol.empty() ||
-         (protocol.find(cricket::kMediaProtocolRtpPrefix) != std::string::npos);
-}
-
-bool IsDtlsSctp(const std::string& protocol) {
-  // This intentionally excludes "SCTP" and "SCTP/DTLS".
-  return protocol.find(cricket::kMediaProtocolDtlsSctp) != std::string::npos;
-}
-
 bool ParseConnectionData(const std::string& line,
                          rtc::SocketAddress* addr,
                          SdpParseError* error) {
@@ -2710,7 +2660,7 @@
 
     // <fmt>
     std::vector<int> payload_types;
-    if (IsRtp(protocol)) {
+    if (cricket::IsRtpProtocol(protocol)) {
       for (size_t j = 3; j < fields.size(); ++j) {
         // TODO(wu): Remove when below bug is fixed.
         // https://bugzilla.mozilla.org/show_bug.cgi?id=996329
@@ -2748,24 +2698,37 @@
           payload_types, pos, &content_name, &bundle_only,
           &section_msid_signaling, &transport, candidates, error);
     } else if (HasAttribute(line, kMediaTypeData)) {
-      std::unique_ptr<DataContentDescription> data_desc =
-          ParseContentDescription<DataContentDescription>(
-              message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
-              payload_types, pos, &content_name, &bundle_only,
-              &section_msid_signaling, &transport, candidates, error);
-
-      if (data_desc && IsDtlsSctp(protocol)) {
+      if (cricket::IsDtlsSctp(protocol)) {
+        // The draft-03 format is:
+        // m=application <port> DTLS/SCTP <sctp-port>...
+        // use_sctpmap should be false.
+        // The draft-26 format is:
+        // m=application <port> UDP/DTLS/SCTP webrtc-datachannel
+        // use_sctpmap should be false.
+        auto data_desc = absl::make_unique<SctpDataContentDescription>();
         int p;
         if (rtc::FromString(fields[3], &p)) {
-          if (!AddOrModifySctpDataCodecPort(data_desc.get(), p)) {
-            return false;
-          }
+          data_desc->set_port(p);
         } else if (fields[3] == kDefaultSctpmapProtocol) {
           data_desc->set_use_sctpmap(false);
         }
+        if (!ParseContent(message, cricket::MEDIA_TYPE_DATA, mline_index,
+                          protocol, payload_types, pos, &content_name,
+                          &bundle_only, &section_msid_signaling,
+                          data_desc.get(), &transport, candidates, error)) {
+          return false;
+        }
+        data_desc->set_protocol(protocol);
+        content = std::move(data_desc);
+      } else {
+        // RTP
+        std::unique_ptr<RtpDataContentDescription> data_desc =
+            ParseContentDescription<RtpDataContentDescription>(
+                message, cricket::MEDIA_TYPE_DATA, mline_index, protocol,
+                payload_types, pos, &content_name, &bundle_only,
+                &section_msid_signaling, &transport, candidates, error);
+        content = std::move(data_desc);
       }
-
-      content = std::move(data_desc);
     } else {
       RTC_LOG(LS_WARNING) << "Unsupported media type: " << line;
       continue;
@@ -2796,7 +2759,8 @@
       content_rejected = port_rejected;
     }
 
-    if (IsRtp(protocol)) {
+    if (cricket::IsRtpProtocol(protocol) && !content->as_sctp()) {
+      content->set_protocol(protocol);
       // Set the extmap.
       if (!session_extmaps.empty() &&
           !content->rtp_header_extensions().empty()) {
@@ -2808,8 +2772,12 @@
       for (size_t i = 0; i < session_extmaps.size(); ++i) {
         content->AddRtpHeaderExtension(session_extmaps[i]);
       }
+    } else if (content->as_sctp()) {
+      // Do nothing, it's OK
+    } else {
+      RTC_LOG(LS_WARNING) << "Parse failed with unknown protocol " << protocol;
+      return false;
     }
-    content->set_protocol(protocol);
 
     // Use the session level connection address if the media level addresses are
     // not specified.
@@ -2821,8 +2789,8 @@
     content->set_connection_address(address);
 
     desc->AddContent(content_name,
-                     IsDtlsSctp(protocol) ? MediaProtocolType::kSctp
-                                          : MediaProtocolType::kRtp,
+                     cricket::IsDtlsSctp(protocol) ? MediaProtocolType::kSctp
+                                                   : MediaProtocolType::kRtp,
                      content_rejected, bundle_only, content.release());
     // Create TransportInfo with the media level "ice-pwd" and "ice-ufrag".
     desc->AddTransportInfo(TransportInfo(content_name, transport));
@@ -3051,7 +3019,8 @@
           // data channels. Don't allow SDP to set the bandwidth, because
           // that would give JS the opportunity to "break the Internet".
           // See: https://code.google.com/p/chromium/issues/detail?id=280726
-          if (media_type == cricket::MEDIA_TYPE_DATA && IsRtp(protocol) &&
+          if (media_type == cricket::MEDIA_TYPE_DATA &&
+              cricket::IsRtpProtocol(protocol) &&
               b > cricket::kDataMaxBandwidth / 1000) {
             rtc::StringBuilder description;
             description << "RTP-based data channels may not send more than "
@@ -3132,20 +3101,23 @@
       if (!ParseDtlsSetup(line, &(transport->connection_role), error)) {
         return false;
       }
-    } else if (IsDtlsSctp(protocol) && HasAttribute(line, kAttributeSctpPort)) {
+    } else if (cricket::IsDtlsSctp(protocol) &&
+               HasAttribute(line, kAttributeSctpPort)) {
       if (media_type != cricket::MEDIA_TYPE_DATA) {
         return ParseFailed(
             line, "sctp-port attribute found in non-data media description.",
             error);
       }
+      if (media_desc->as_sctp()->use_sctpmap()) {
+        return ParseFailed(
+            line, "sctp-port attribute can't be used with sctpmap.", error);
+      }
       int sctp_port;
       if (!ParseSctpPort(line, &sctp_port, error)) {
         return false;
       }
-      if (!AddOrModifySctpDataCodecPort(media_desc->as_data(), sctp_port)) {
-        return false;
-      }
-    } else if (IsDtlsSctp(protocol) &&
+      media_desc->as_sctp()->set_port(sctp_port);
+    } else if (cricket::IsDtlsSctp(protocol) &&
                HasAttribute(line, kAttributeMaxMessageSize)) {
       if (media_type != cricket::MEDIA_TYPE_DATA) {
         return ParseFailed(
@@ -3157,11 +3129,8 @@
       if (!ParseSctpMaxMessageSize(line, &max_message_size, error)) {
         return false;
       }
-      if (!AddOrModifySctpDataMaxMessageSize(media_desc->as_data(),
-                                             max_message_size)) {
-        return false;
-      }
-    } else if (IsRtp(protocol)) {
+      media_desc->as_sctp()->set_max_message_size(max_message_size);
+    } else if (cricket::IsRtpProtocol(protocol)) {
       //
       // RTP specific attrubtes
       //
@@ -3621,8 +3590,10 @@
     UpdateCodec(payload_type, encoding_name, clock_rate, 0, channels,
                 audio_desc);
   } else if (media_type == cricket::MEDIA_TYPE_DATA) {
-    DataContentDescription* data_desc = media_desc->as_data();
-    data_desc->AddCodec(cricket::DataCodec(payload_type, encoding_name));
+    RtpDataContentDescription* data_desc = media_desc->as_rtp_data();
+    if (data_desc) {
+      data_desc->AddCodec(cricket::RtpDataCodec(payload_type, encoding_name));
+    }
   }
   return true;
 }
diff --git a/pc/webrtc_sdp_unittest.cc b/pc/webrtc_sdp_unittest.cc
index 7294cfc..9048ee5 100644
--- a/pc/webrtc_sdp_unittest.cc
+++ b/pc/webrtc_sdp_unittest.cc
@@ -56,7 +56,6 @@
 using cricket::ContentInfo;
 using cricket::CryptoParams;
 using cricket::DataCodec;
-using cricket::DataContentDescription;
 using cricket::ICE_CANDIDATE_COMPONENT_RTCP;
 using cricket::ICE_CANDIDATE_COMPONENT_RTP;
 using cricket::kFecSsrcGroupSemantics;
@@ -65,6 +64,8 @@
 using cricket::RELAY_PORT_TYPE;
 using cricket::RidDescription;
 using cricket::RidDirection;
+using cricket::RtpDataContentDescription;
+using cricket::SctpDataContentDescription;
 using cricket::SessionDescription;
 using cricket::SimulcastDescription;
 using cricket::SimulcastLayer;
@@ -275,6 +276,7 @@
     "a=ssrc:10 mslabel:data_channel\r\n"
     "a=ssrc:10 label:data_channeld0\r\n";
 
+// draft-ietf-mmusic-sctp-sdp-03
 static const char kSdpSctpDataChannelString[] =
     "m=application 9 DTLS/SCTP 5000\r\n"
     "c=IN IP4 0.0.0.0\r\n"
@@ -1443,10 +1445,17 @@
               simulcast2.receive_layers().size());
   }
 
-  void CompareDataContentDescription(const DataContentDescription* dcd1,
-                                     const DataContentDescription* dcd2) {
+  void CompareRtpDataContentDescription(const RtpDataContentDescription* dcd1,
+                                        const RtpDataContentDescription* dcd2) {
+    CompareMediaContentDescription<RtpDataContentDescription>(dcd1, dcd2);
+  }
+
+  void CompareSctpDataContentDescription(
+      const SctpDataContentDescription* dcd1,
+      const SctpDataContentDescription* dcd2) {
     EXPECT_EQ(dcd1->use_sctpmap(), dcd2->use_sctpmap());
-    CompareMediaContentDescription<DataContentDescription>(dcd1, dcd2);
+    EXPECT_EQ(dcd1->port(), dcd2->port());
+    EXPECT_EQ(dcd1->max_message_size(), dcd2->max_message_size());
   }
 
   void CompareSessionDescription(const SessionDescription& desc1,
@@ -1484,10 +1493,21 @@
       }
 
       ASSERT_EQ(IsDataContent(&c1), IsDataContent(&c2));
-      if (IsDataContent(&c1)) {
-        const DataContentDescription* dcd1 = c1.media_description()->as_data();
-        const DataContentDescription* dcd2 = c2.media_description()->as_data();
-        CompareDataContentDescription(dcd1, dcd2);
+      if (c1.media_description()->as_sctp()) {
+        ASSERT_TRUE(c2.media_description()->as_sctp());
+        const SctpDataContentDescription* scd1 =
+            c1.media_description()->as_sctp();
+        const SctpDataContentDescription* scd2 =
+            c2.media_description()->as_sctp();
+        CompareSctpDataContentDescription(scd1, scd2);
+      } else {
+        if (IsDataContent(&c1)) {
+          const RtpDataContentDescription* dcd1 =
+              c1.media_description()->as_rtp_data();
+          const RtpDataContentDescription* dcd2 =
+              c2.media_description()->as_rtp_data();
+          CompareRtpDataContentDescription(dcd1, dcd2);
+        }
       }
 
       CompareSimulcastDescription(
@@ -1760,14 +1780,12 @@
   }
 
   void AddSctpDataChannel(bool use_sctpmap) {
-    std::unique_ptr<DataContentDescription> data(new DataContentDescription());
-    data_desc_ = data.get();
-    data_desc_->set_use_sctpmap(use_sctpmap);
-    data_desc_->set_protocol(cricket::kMediaProtocolDtlsSctp);
-    DataCodec codec(cricket::kGoogleSctpDataCodecPlType,
-                    cricket::kGoogleSctpDataCodecName);
-    codec.SetParam(cricket::kCodecParamPort, kDefaultSctpPort);
-    data_desc_->AddCodec(codec);
+    std::unique_ptr<SctpDataContentDescription> data(
+        new SctpDataContentDescription());
+    sctp_desc_ = data.get();
+    sctp_desc_->set_use_sctpmap(use_sctpmap);
+    sctp_desc_->set_protocol(cricket::kMediaProtocolDtlsSctp);
+    sctp_desc_->set_port(kDefaultSctpPort);
     desc_.AddContent(kDataContentName, MediaProtocolType::kSctp,
                      data.release());
     desc_.AddTransportInfo(TransportInfo(
@@ -1775,7 +1793,8 @@
   }
 
   void AddRtpDataChannel() {
-    std::unique_ptr<DataContentDescription> data(new DataContentDescription());
+    std::unique_ptr<RtpDataContentDescription> data(
+        new RtpDataContentDescription());
     data_desc_ = data.get();
 
     data_desc_->AddCodec(DataCodec(101, "google-data"));
@@ -2043,7 +2062,8 @@
   SessionDescription desc_;
   AudioContentDescription* audio_desc_;
   VideoContentDescription* video_desc_;
-  DataContentDescription* data_desc_;
+  RtpDataContentDescription* data_desc_;
+  SctpDataContentDescription* sctp_desc_;
   Candidates candidates_;
   std::unique_ptr<IceCandidateInterface> jcandidate_;
   JsepSessionDescription jdesc_;
@@ -2215,21 +2235,26 @@
   EXPECT_EQ(message, expected_sdp);
 }
 
+void MutateJsepSctpPort(JsepSessionDescription* jdesc,
+                        const SessionDescription& desc,
+                        int port) {
+  // Take our pre-built session description and change the SCTP port.
+  std::unique_ptr<cricket::SessionDescription> mutant = desc.Clone();
+  SctpDataContentDescription* dcdesc =
+      mutant->GetContentDescriptionByName(kDataContentName)->as_sctp();
+  dcdesc->set_port(port);
+  ASSERT_TRUE(
+      jdesc->Initialize(std::move(mutant), kSessionId, kSessionVersion));
+}
+
 TEST_F(WebRtcSdpTest, SerializeWithSctpDataChannelAndNewPort) {
   bool use_sctpmap = true;
   AddSctpDataChannel(use_sctpmap);
   JsepSessionDescription jsep_desc(kDummyType);
   MakeDescriptionWithoutCandidates(&jsep_desc);
-  DataContentDescription* dcdesc =
-      jsep_desc.description()
-          ->GetContentDescriptionByName(kDataContentName)
-          ->as_data();
 
   const int kNewPort = 1234;
-  cricket::DataCodec codec(cricket::kGoogleSctpDataCodecPlType,
-                           cricket::kGoogleSctpDataCodecName);
-  codec.SetParam(cricket::kCodecParamPort, kNewPort);
-  dcdesc->AddOrReplaceCodec(codec);
+  MutateJsepSctpPort(&jsep_desc, desc_, kNewPort);
 
   std::string message = webrtc::SdpSerialize(jsep_desc);
 
@@ -2868,14 +2893,12 @@
 // Helper function to set the max-message-size parameter in the
 // SCTP data codec.
 void MutateJsepSctpMaxMessageSize(const SessionDescription& desc,
-                                  const std::string& new_value,
+                                  int new_value,
                                   JsepSessionDescription* jdesc) {
   std::unique_ptr<cricket::SessionDescription> mutant = desc.Clone();
-  DataContentDescription* dcdesc =
-      mutant->GetContentDescriptionByName(kDataContentName)->as_data();
-  std::vector<cricket::DataCodec> codecs(dcdesc->codecs());
-  codecs[0].SetParam(cricket::kCodecParamMaxMessageSize, new_value);
-  dcdesc->set_codecs(codecs);
+  SctpDataContentDescription* dcdesc =
+      mutant->GetContentDescriptionByName(kDataContentName)->as_sctp();
+  dcdesc->set_max_message_size(new_value);
   jdesc->Initialize(std::move(mutant), kSessionId, kSessionVersion);
 }
 
@@ -2887,7 +2910,7 @@
 
   sdp_with_data.append(kSdpSctpDataChannelStringWithSctpColonPort);
   sdp_with_data.append("a=max-message-size:12345\r\n");
-  MutateJsepSctpMaxMessageSize(desc_, "12345", &jdesc);
+  MutateJsepSctpMaxMessageSize(desc_, 12345, &jdesc);
   JsepSessionDescription jdesc_output(kDummyType);
 
   // Verify with DTLS/SCTP.
@@ -2925,6 +2948,44 @@
   EXPECT_FALSE(SdpDeserialize(sdp_with_data, &jdesc_output));
 }
 
+// Test behavior if a=rtpmap occurs in an SCTP section.
+TEST_F(WebRtcSdpTest, DeserializeSdpWithRtpmapAttribute) {
+  std::string sdp_with_data = kSdpString;
+  // Append m= attributes
+  sdp_with_data.append(kSdpSctpDataChannelString);
+  // Append a=rtpmap attribute
+  sdp_with_data.append("a=rtpmap:111 opus/48000/2\r\n");
+  JsepSessionDescription jdesc_output(kDummyType);
+  // Correct behavior is to ignore the extra attribute.
+  EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output));
+}
+
+TEST_F(WebRtcSdpTest, DeserializeSdpWithStrangeApplicationProtocolNames) {
+  static const char* bad_strings[] = {"DTLS/SCTPRTP/", "obviously-bogus",
+                                      "UDP/TL/RTSP/SAVPF", "UDP/TL/RTSP/S"};
+  for (auto proto : bad_strings) {
+    std::string sdp_with_data = kSdpString;
+    sdp_with_data.append("m=application 9 ");
+    sdp_with_data.append(proto);
+    sdp_with_data.append(" 47\r\n");
+    JsepSessionDescription jdesc_output(kDummyType);
+    EXPECT_FALSE(SdpDeserialize(sdp_with_data, &jdesc_output))
+        << "Parsing should have failed on " << proto;
+  }
+  // The following strings are strange, but acceptable as RTP.
+  static const char* weird_strings[] = {"DTLS/SCTP/RTP/FOO",
+                                        "obviously-bogus/RTP/"};
+  for (auto proto : weird_strings) {
+    std::string sdp_with_data = kSdpString;
+    sdp_with_data.append("m=application 9 ");
+    sdp_with_data.append(proto);
+    sdp_with_data.append(" 47\r\n");
+    JsepSessionDescription jdesc_output(kDummyType);
+    EXPECT_TRUE(SdpDeserialize(sdp_with_data, &jdesc_output))
+        << "Parsing should have succeeded on " << proto;
+  }
+}
+
 // For crbug/344475.
 TEST_F(WebRtcSdpTest, DeserializeSdpWithCorruptedSctpDataChannels) {
   std::string sdp_with_data = kSdpString;
@@ -2937,29 +2998,13 @@
   // No crash is a pass.
 }
 
-void MutateJsepSctpPort(JsepSessionDescription* jdesc,
-                        const SessionDescription& desc) {
-  // take our pre-built session description and change the SCTP port.
-  std::unique_ptr<cricket::SessionDescription> mutant = desc.Clone();
-  DataContentDescription* dcdesc =
-      mutant->GetContentDescriptionByName(kDataContentName)->as_data();
-  std::vector<cricket::DataCodec> codecs(dcdesc->codecs());
-  EXPECT_EQ(1U, codecs.size());
-  EXPECT_EQ(cricket::kGoogleSctpDataCodecPlType, codecs[0].id);
-  codecs[0].SetParam(cricket::kCodecParamPort, kUnusualSctpPort);
-  dcdesc->set_codecs(codecs);
-
-  ASSERT_TRUE(
-      jdesc->Initialize(std::move(mutant), kSessionId, kSessionVersion));
-}
-
 TEST_F(WebRtcSdpTest, DeserializeSdpWithSctpDataChannelAndUnusualPort) {
   bool use_sctpmap = true;
   AddSctpDataChannel(use_sctpmap);
 
   // First setup the expected JsepSessionDescription.
   JsepSessionDescription jdesc(kDummyType);
-  MutateJsepSctpPort(&jdesc, desc_);
+  MutateJsepSctpPort(&jdesc, desc_, kUnusualSctpPort);
 
   // Then get the deserialized JsepSessionDescription.
   std::string sdp_with_data = kSdpString;
@@ -2979,7 +3024,7 @@
   AddSctpDataChannel(use_sctpmap);
 
   JsepSessionDescription jdesc(kDummyType);
-  MutateJsepSctpPort(&jdesc, desc_);
+  MutateJsepSctpPort(&jdesc, desc_, kUnusualSctpPort);
 
   // We need to test the deserialized JsepSessionDescription from
   // kSdpSctpDataChannelStringWithSctpPort for
@@ -3015,7 +3060,7 @@
   bool use_sctpmap = true;
   AddSctpDataChannel(use_sctpmap);
   JsepSessionDescription jdesc(kDummyType);
-  DataContentDescription* dcd = GetFirstDataContentDescription(&desc_);
+  SctpDataContentDescription* dcd = GetFirstSctpDataContentDescription(&desc_);
   dcd->set_bandwidth(100 * 1000);
   ASSERT_TRUE(jdesc.Initialize(desc_.Clone(), kSessionId, kSessionVersion));