Bug 1379265 - Add C++ bindings for rsdparsa; r=bwc draft
authorPaul Ellenbogen <pe5@cs.princeton.edu>
Fri, 30 Jun 2017 12:54:12 -0700
changeset 756280 a58577b52f5db3ef7afde0b7384da1d76ceb7b24
parent 756279 797c024526a3ea11bb9a1e29c673b53d68c14a25
child 756281 16cdbb8eb1bc91304a7495cee25c60f60a486364
push id99458
push userbmo:dminor@mozilla.com
push dateFri, 16 Feb 2018 19:57:29 +0000
reviewersbwc
bugs1379265
milestone60.0a1
Bug 1379265 - Add C++ bindings for rsdparsa; r=bwc MozReview-Commit-ID: FdhpTT5wzwI
media/webrtc/signaling/src/sdp/RsdparsaSdp.cpp
media/webrtc/signaling/src/sdp/RsdparsaSdp.h
media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.cpp
media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.h
media/webrtc/signaling/src/sdp/RsdparsaSdpGlue.cpp
media/webrtc/signaling/src/sdp/RsdparsaSdpGlue.h
media/webrtc/signaling/src/sdp/RsdparsaSdpInc.h
media/webrtc/signaling/src/sdp/RsdparsaSdpMediaSection.cpp
media/webrtc/signaling/src/sdp/RsdparsaSdpMediaSection.h
media/webrtc/signaling/src/sdp/RsdparsaSdpParser.cpp
media/webrtc/signaling/src/sdp/RsdparsaSdpParser.h
media/webrtc/signaling/src/sdp/moz.build
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdp.cpp
@@ -0,0 +1,120 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "signaling/src/sdp/RsdparsaSdp.h"
+
+#include <cstdlib>
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Assertions.h"
+#include "nsError.h"
+
+
+#include "signaling/src/sdp/SdpErrorHolder.h"
+#include "signaling/src/sdp/RsdparsaSdpInc.h"
+#include "signaling/src/sdp/RsdparsaSdpMediaSection.h"
+
+#ifdef CRLF
+#undef CRLF
+#endif
+#define CRLF "\r\n"
+
+namespace mozilla
+{
+
+RsdparsaSdp::RsdparsaSdp(RsdparsaSessionHandle session, const SdpOrigin& origin)
+  : mSession(Move(session))
+  , mOrigin(origin)
+{
+  RsdparsaSessionHandle attributeSession(sdp_new_reference(mSession.get()));
+  mAttributeList.reset(new RsdparsaSdpAttributeList(Move(attributeSession)));
+
+  size_t section_count = sdp_media_section_count(mSession.get());
+  for (size_t level = 0; level < section_count; level++) {
+    RustMediaSection* mediaSection = sdp_get_media_section(mSession.get(),
+                                                           level);
+    if (!mediaSection) {
+      MOZ_ASSERT(false, "sdp_get_media_section failed because level was out of"
+                        " bounds, but we did a bounds check!");
+      break;
+    }
+    RsdparsaSessionHandle newSession(sdp_new_reference(mSession.get()));
+    RsdparsaSdpMediaSection* sdpMediaSection;
+    sdpMediaSection = new RsdparsaSdpMediaSection(level,
+                                                  Move(newSession),
+                                                  mediaSection,
+                                                  mAttributeList.get());
+    mMediaSections.values.push_back(sdpMediaSection);
+  }
+}
+
+const SdpOrigin&
+RsdparsaSdp::GetOrigin() const
+{
+  return mOrigin;
+}
+
+uint32_t
+RsdparsaSdp::GetBandwidth(const std::string& type) const
+{
+  return get_sdp_bandwidth(mSession.get(), type.c_str());
+}
+
+const SdpMediaSection&
+RsdparsaSdp::GetMediaSection(size_t level) const
+{
+  if (level > mMediaSections.values.size()) {
+    MOZ_CRASH();
+  }
+  return *mMediaSections.values[level];
+}
+
+SdpMediaSection&
+RsdparsaSdp::GetMediaSection(size_t level)
+{
+  if (level > mMediaSections.values.size()) {
+    MOZ_CRASH();
+  }
+  return *mMediaSections.values[level];
+}
+
+SdpMediaSection&
+RsdparsaSdp::AddMediaSection(SdpMediaSection::MediaType mediaType,
+                             SdpDirectionAttribute::Direction dir,
+                             uint16_t port,
+                             SdpMediaSection::Protocol protocol,
+                             sdp::AddrType addrType, const std::string& addr)
+{
+  //TODO: See Bug 1436080
+  MOZ_CRASH("Method not implemented");
+}
+
+void
+RsdparsaSdp::Serialize(std::ostream& os) const
+{
+  os << "v=0" << CRLF << mOrigin << "s=-" << CRLF;
+
+  // We don't support creating i=, u=, e=, p=
+  // We don't generate c= at the session level (only in media)
+
+  BandwidthVec* bwVec = sdp_get_session_bandwidth_vec(mSession.get());
+  char *bwString = sdp_serialize_bandwidth(bwVec);
+  if (bwString) {
+    os << bwString;
+    sdp_free_string(bwString);
+  }
+
+  os << "t=0 0" << CRLF;
+
+  // We don't support r= or z=
+
+  // attributes
+  os << *mAttributeList;
+
+  // media sections
+  for (const SdpMediaSection* msection : mMediaSections.values) {
+    os << *msection;
+  }
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdp.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _RSDPARSA_SDP_H_
+#define _RSDPARSA_SDP_H_
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Attributes.h"
+
+#include "signaling/src/common/PtrVector.h"
+
+#include "signaling/src/sdp/Sdp.h"
+
+#include "signaling/src/sdp/RsdparsaSdpMediaSection.h"
+#include "signaling/src/sdp/RsdparsaSdpAttributeList.h"
+#include "signaling/src/sdp/RsdparsaSdpInc.h"
+#include "signaling/src/sdp/RsdparsaSdpGlue.h"
+
+
+namespace mozilla
+{
+
+class RsdparsaSdpParser;
+class SdpErrorHolder;
+
+class RsdparsaSdp final : public Sdp
+{
+  friend class RsdparsaSdpParser;
+
+public:
+  explicit RsdparsaSdp(RsdparsaSessionHandle session, const SdpOrigin& origin);
+
+  const SdpOrigin& GetOrigin() const override;
+
+  // Note: connection information is always retrieved from media sections
+  uint32_t GetBandwidth(const std::string& type) const override;
+
+  size_t
+  GetMediaSectionCount() const override
+  {
+    return sdp_media_section_count(mSession.get());
+  }
+
+  const SdpAttributeList&
+  GetAttributeList() const override
+  {
+    return *mAttributeList;
+  }
+
+  SdpAttributeList&
+  GetAttributeList() override
+  {
+    return *mAttributeList;
+  }
+
+  const SdpMediaSection& GetMediaSection(size_t level) const
+      override;
+
+  SdpMediaSection& GetMediaSection(size_t level) override;
+
+  SdpMediaSection& AddMediaSection(
+      SdpMediaSection::MediaType media, SdpDirectionAttribute::Direction dir,
+      uint16_t port, SdpMediaSection::Protocol proto, sdp::AddrType addrType,
+      const std::string& addr) override;
+
+  void Serialize(std::ostream&) const override;
+
+private:
+  RsdparsaSdp() : mOrigin("", 0, 0, sdp::kIPv4, "") {}
+
+  RsdparsaSessionHandle mSession;
+  SdpOrigin mOrigin;
+  UniquePtr<RsdparsaSdpAttributeList> mAttributeList;
+  PtrVector<RsdparsaSdpMediaSection> mMediaSections;
+};
+
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.cpp
@@ -0,0 +1,1021 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "signaling/src/sdp/RsdparsaSdpAttributeList.h"
+#include "signaling/src/sdp/RsdparsaSdpInc.h"
+#include "signaling/src/sdp/RsdparsaSdpGlue.h"
+
+#include <ostream>
+#include "mozilla/Assertions.h"
+
+namespace mozilla
+{
+
+const std::string RsdparsaSdpAttributeList::kEmptyString = "";
+
+RsdparsaSdpAttributeList::~RsdparsaSdpAttributeList()
+{
+  for (size_t i = 0; i < kNumAttributeTypes; ++i) {
+    delete mAttributes[i];
+  }
+}
+
+bool
+RsdparsaSdpAttributeList::HasAttribute(AttributeType type,
+                                       bool sessionFallback) const
+{
+  return !!GetAttribute(type, sessionFallback);
+}
+
+const SdpAttribute*
+RsdparsaSdpAttributeList::GetAttribute(AttributeType type,
+                                       bool sessionFallback) const
+{
+  const SdpAttribute* value = mAttributes[static_cast<size_t>(type)];
+  // Only do fallback when the attribute can appear at both the media and
+  // session level
+  if (!value && !AtSessionLevel() && sessionFallback &&
+      SdpAttribute::IsAllowedAtSessionLevel(type) &&
+      SdpAttribute::IsAllowedAtMediaLevel(type)) {
+    return mSessionAttributes->GetAttribute(type, false);
+  }
+  return value;
+}
+
+void
+RsdparsaSdpAttributeList::RemoveAttribute(AttributeType type)
+{
+  delete mAttributes[static_cast<size_t>(type)];
+  mAttributes[static_cast<size_t>(type)] = nullptr;
+}
+
+void
+RsdparsaSdpAttributeList::Clear()
+{
+  for (size_t i = 0; i < kNumAttributeTypes; ++i) {
+    RemoveAttribute(static_cast<AttributeType>(i));
+  }
+}
+
+uint32_t
+RsdparsaSdpAttributeList::Count() const
+{
+  uint32_t count = 0;
+  for (size_t i = 0; i < kNumAttributeTypes; ++i) {
+    if (mAttributes[i]) {
+      count++;
+    }
+  }
+  return count;
+}
+
+void
+RsdparsaSdpAttributeList::SetAttribute(SdpAttribute* attr)
+{
+  if (!IsAllowedHere(attr->GetType())) {
+    MOZ_ASSERT(false, "This type of attribute is not allowed here");
+    delete attr;
+    return;
+  }
+  RemoveAttribute(attr->GetType());
+  mAttributes[attr->GetType()] = attr;
+}
+
+const std::vector<std::string>&
+RsdparsaSdpAttributeList::GetCandidate() const
+{
+  if (!HasAttribute(SdpAttribute::kCandidateAttribute)) {
+    MOZ_CRASH();
+  }
+
+  return static_cast<const SdpMultiStringAttribute*>(
+             GetAttribute(SdpAttribute::kCandidateAttribute))->mValues;
+}
+
+const SdpConnectionAttribute&
+RsdparsaSdpAttributeList::GetConnection() const
+{
+  if (!HasAttribute(SdpAttribute::kConnectionAttribute)) {
+    MOZ_CRASH();
+  }
+
+  return *static_cast<const SdpConnectionAttribute*>(
+             GetAttribute(SdpAttribute::kConnectionAttribute));
+}
+
+SdpDirectionAttribute::Direction
+RsdparsaSdpAttributeList::GetDirection() const
+{
+  if (!HasAttribute(SdpAttribute::kDirectionAttribute)) {
+    MOZ_CRASH();
+  }
+
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kDirectionAttribute);
+  return static_cast<const SdpDirectionAttribute*>(attr)->mValue;
+}
+
+const SdpDtlsMessageAttribute&
+RsdparsaSdpAttributeList::GetDtlsMessage() const
+{
+  if (!HasAttribute(SdpAttribute::kDtlsMessageAttribute)) {
+    MOZ_CRASH();
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kDtlsMessageAttribute);
+  return *static_cast<const SdpDtlsMessageAttribute*>(attr);
+}
+
+const SdpExtmapAttributeList&
+RsdparsaSdpAttributeList::GetExtmap() const
+{
+  if (!HasAttribute(SdpAttribute::kExtmapAttribute)) {
+    MOZ_CRASH();
+  }
+
+  return *static_cast<const SdpExtmapAttributeList*>(
+             GetAttribute(SdpAttribute::kExtmapAttribute));
+}
+
+const SdpFingerprintAttributeList&
+RsdparsaSdpAttributeList::GetFingerprint() const
+{
+  if (!HasAttribute(SdpAttribute::kFingerprintAttribute)) {
+    MOZ_CRASH();
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kFingerprintAttribute);
+  return *static_cast<const SdpFingerprintAttributeList*>(attr);
+}
+
+const SdpFmtpAttributeList&
+RsdparsaSdpAttributeList::GetFmtp() const
+{
+  if (!HasAttribute(SdpAttribute::kFmtpAttribute)) {
+    MOZ_CRASH();
+  }
+
+  return *static_cast<const SdpFmtpAttributeList*>(
+             GetAttribute(SdpAttribute::kFmtpAttribute));
+}
+
+const SdpGroupAttributeList&
+RsdparsaSdpAttributeList::GetGroup() const
+{
+  if (!HasAttribute(SdpAttribute::kGroupAttribute)) {
+    MOZ_CRASH();
+  }
+
+  return *static_cast<const SdpGroupAttributeList*>(
+             GetAttribute(SdpAttribute::kGroupAttribute));
+}
+
+const SdpOptionsAttribute&
+RsdparsaSdpAttributeList::GetIceOptions() const
+{
+  if (!HasAttribute(SdpAttribute::kIceOptionsAttribute)) {
+    MOZ_CRASH();
+  }
+
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kIceOptionsAttribute);
+  return *static_cast<const SdpOptionsAttribute*>(attr);
+}
+
+const std::string&
+RsdparsaSdpAttributeList::GetIcePwd() const
+{
+  if (!HasAttribute(SdpAttribute::kIcePwdAttribute)) {
+    return kEmptyString;
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kIcePwdAttribute);
+  return static_cast<const SdpStringAttribute*>(attr)->mValue;
+}
+
+const std::string&
+RsdparsaSdpAttributeList::GetIceUfrag() const
+{
+  if (!HasAttribute(SdpAttribute::kIceUfragAttribute)) {
+    return kEmptyString;
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kIceUfragAttribute);
+  return static_cast<const SdpStringAttribute*>(attr)->mValue;
+}
+
+const std::string&
+RsdparsaSdpAttributeList::GetIdentity() const
+{
+  if (!HasAttribute(SdpAttribute::kIdentityAttribute)) {
+    return kEmptyString;
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kIdentityAttribute);
+  return static_cast<const SdpStringAttribute*>(attr)->mValue;
+}
+
+const SdpImageattrAttributeList&
+RsdparsaSdpAttributeList::GetImageattr() const
+{
+  if (!HasAttribute(SdpAttribute::kImageattrAttribute)) {
+    MOZ_CRASH();
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kImageattrAttribute);
+  return *static_cast<const SdpImageattrAttributeList*>(attr);
+}
+
+const SdpSimulcastAttribute&
+RsdparsaSdpAttributeList::GetSimulcast() const
+{
+  if (!HasAttribute(SdpAttribute::kSimulcastAttribute)) {
+    MOZ_CRASH();
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kSimulcastAttribute);
+  return *static_cast<const SdpSimulcastAttribute*>(attr);
+}
+
+const std::string&
+RsdparsaSdpAttributeList::GetLabel() const
+{
+  if (!HasAttribute(SdpAttribute::kLabelAttribute)) {
+    return kEmptyString;
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kLabelAttribute);
+  return static_cast<const SdpStringAttribute*>(attr)->mValue;
+}
+
+uint32_t
+RsdparsaSdpAttributeList::GetMaxptime() const
+{
+  if (!HasAttribute(SdpAttribute::kMaxptimeAttribute)) {
+    MOZ_CRASH();
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kMaxptimeAttribute);
+  return static_cast<const SdpNumberAttribute*>(attr)->mValue;
+}
+
+const std::string&
+RsdparsaSdpAttributeList::GetMid() const
+{
+  if (!HasAttribute(SdpAttribute::kMidAttribute)) {
+    return kEmptyString;
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kMidAttribute);
+  return static_cast<const SdpStringAttribute*>(attr)->mValue;
+}
+
+const SdpMsidAttributeList&
+RsdparsaSdpAttributeList::GetMsid() const
+{
+  if (!HasAttribute(SdpAttribute::kMsidAttribute)) {
+    MOZ_CRASH();
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kMsidAttribute);
+  return *static_cast<const SdpMsidAttributeList*>(attr);
+}
+
+const SdpMsidSemanticAttributeList&
+RsdparsaSdpAttributeList::GetMsidSemantic() const
+{
+  if (!HasAttribute(SdpAttribute::kMsidSemanticAttribute)) {
+    MOZ_CRASH();
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kMsidSemanticAttribute);
+  return *static_cast<const SdpMsidSemanticAttributeList*>(attr);
+}
+
+const SdpRidAttributeList&
+RsdparsaSdpAttributeList::GetRid() const
+{
+  if (!HasAttribute(SdpAttribute::kRidAttribute)) {
+    MOZ_CRASH();
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kRidAttribute);
+  return *static_cast<const SdpRidAttributeList*>(attr);
+}
+
+uint32_t
+RsdparsaSdpAttributeList::GetPtime() const
+{
+  if (!HasAttribute(SdpAttribute::kPtimeAttribute)) {
+    MOZ_CRASH();
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kPtimeAttribute);
+  return static_cast<const SdpNumberAttribute*>(attr)->mValue;
+}
+
+const SdpRtcpAttribute&
+RsdparsaSdpAttributeList::GetRtcp() const
+{
+  if (!HasAttribute(SdpAttribute::kRtcpAttribute)) {
+    MOZ_CRASH();
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kRtcpAttribute);
+  return *static_cast<const SdpRtcpAttribute*>(attr);
+}
+
+const SdpRtcpFbAttributeList&
+RsdparsaSdpAttributeList::GetRtcpFb() const
+{
+  if (!HasAttribute(SdpAttribute::kRtcpFbAttribute)) {
+    MOZ_CRASH();
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kRtcpFbAttribute);
+  return *static_cast<const SdpRtcpFbAttributeList*>(attr);
+}
+
+const SdpRemoteCandidatesAttribute&
+RsdparsaSdpAttributeList::GetRemoteCandidates() const
+{
+  MOZ_CRASH("Not yet implemented");
+}
+
+const SdpRtpmapAttributeList&
+RsdparsaSdpAttributeList::GetRtpmap() const
+{
+  if (!HasAttribute(SdpAttribute::kRtpmapAttribute)) {
+    MOZ_CRASH();
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kRtpmapAttribute);
+  return *static_cast<const SdpRtpmapAttributeList*>(attr);
+}
+
+const SdpSctpmapAttributeList&
+RsdparsaSdpAttributeList::GetSctpmap() const
+{
+  if (!HasAttribute(SdpAttribute::kSctpmapAttribute)) {
+    MOZ_CRASH();
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kSctpmapAttribute);
+  return *static_cast<const SdpSctpmapAttributeList*>(attr);
+}
+
+uint32_t
+RsdparsaSdpAttributeList::GetSctpPort() const
+{
+  if (!HasAttribute(SdpAttribute::kSctpPortAttribute)) {
+    MOZ_CRASH();
+  }
+
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kSctpPortAttribute);
+  return static_cast<const SdpNumberAttribute*>(attr)->mValue;
+}
+
+uint32_t
+RsdparsaSdpAttributeList::GetMaxMessageSize() const
+{
+  if (!HasAttribute(SdpAttribute::kMaxMessageSizeAttribute)) {
+    MOZ_CRASH();
+  }
+
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kMaxMessageSizeAttribute);
+  return static_cast<const SdpNumberAttribute*>(attr)->mValue;
+}
+
+const SdpSetupAttribute&
+RsdparsaSdpAttributeList::GetSetup() const
+{
+  if (!HasAttribute(SdpAttribute::kSetupAttribute)) {
+    MOZ_CRASH();
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kSetupAttribute);
+  return *static_cast<const SdpSetupAttribute*>(attr);
+}
+
+const SdpSsrcAttributeList&
+RsdparsaSdpAttributeList::GetSsrc() const
+{
+  if (!HasAttribute(SdpAttribute::kSsrcAttribute)) {
+    MOZ_CRASH();
+  }
+  const SdpAttribute* attr = GetAttribute(SdpAttribute::kSsrcAttribute);
+  return *static_cast<const SdpSsrcAttributeList*>(attr);
+}
+
+const SdpSsrcGroupAttributeList&
+RsdparsaSdpAttributeList::GetSsrcGroup() const
+{
+  // TODO: See Bug 1437166.
+  MOZ_CRASH("Not yet implemented");
+}
+
+void
+RsdparsaSdpAttributeList::LoadAttribute(RustAttributeList *attributeList,
+                                        AttributeType type)
+{
+  if(!mAttributes[type]) {
+    switch(type) {
+      case SdpAttribute::kIceUfragAttribute:
+        LoadIceUfrag(attributeList);
+        return;
+      case SdpAttribute::kIcePwdAttribute:
+        LoadIcePwd(attributeList);
+        return;
+      case SdpAttribute::kIceOptionsAttribute:
+        LoadIceOptions(attributeList);
+        return;
+      case SdpAttribute::kFingerprintAttribute:
+        LoadFingerprint(attributeList);
+        return;
+      case SdpAttribute::kIdentityAttribute:
+        LoadIdentity(attributeList);
+        return;
+      case SdpAttribute::kSetupAttribute:
+        LoadSetup(attributeList);
+        return;
+      case SdpAttribute::kSsrcAttribute:
+        LoadSsrc(attributeList);
+        return;
+      case SdpAttribute::kRtpmapAttribute:
+        LoadRtpmap(attributeList);
+        return;
+      case SdpAttribute::kFmtpAttribute:
+        LoadFmtp(attributeList);
+        return;
+      case SdpAttribute::kPtimeAttribute:
+        LoadPtime(attributeList);
+        return;
+      case SdpAttribute::kIceLiteAttribute:
+      case SdpAttribute::kRtcpMuxAttribute:
+      case SdpAttribute::kBundleOnlyAttribute:
+      case SdpAttribute::kEndOfCandidatesAttribute:
+        LoadFlags(attributeList);
+        return;
+      case SdpAttribute::kMidAttribute:
+        LoadMid(attributeList);
+        return;
+      case SdpAttribute::kMsidAttribute:
+        LoadMsid(attributeList);
+        return;
+      case SdpAttribute::kMsidSemanticAttribute:
+        LoadMsidSemantics(attributeList);
+        return;
+      case SdpAttribute::kGroupAttribute:
+        LoadGroup(attributeList);
+        return;
+      case SdpAttribute::kRtcpAttribute:
+        LoadRtcp(attributeList);
+        return;
+      case SdpAttribute::kImageattrAttribute:
+        LoadImageattr(attributeList);
+        return;
+      case SdpAttribute::kSctpmapAttribute:
+        LoadSctpmaps(attributeList);
+        return;
+      case SdpAttribute::kDirectionAttribute:
+        LoadDirection(attributeList);
+        return;
+      case SdpAttribute::kRemoteCandidatesAttribute:
+        LoadRemoteCandidates(attributeList);
+        return;
+      case SdpAttribute::kRidAttribute:
+        LoadRids(attributeList);
+        return;
+      case SdpAttribute::kExtmapAttribute:
+        LoadExtmap(attributeList);
+        return;
+      case SdpAttribute::kDtlsMessageAttribute:
+      case SdpAttribute::kLabelAttribute:
+      case SdpAttribute::kMaxptimeAttribute:
+      case SdpAttribute::kSsrcGroupAttribute:
+      case SdpAttribute::kMaxMessageSizeAttribute:
+      case SdpAttribute::kRtcpFbAttribute:
+      case SdpAttribute::kRtcpRsizeAttribute:
+      case SdpAttribute::kSctpPortAttribute:
+      case SdpAttribute::kCandidateAttribute:
+      case SdpAttribute::kSimulcastAttribute:
+      case SdpAttribute::kConnectionAttribute:
+      case SdpAttribute::kIceMismatchAttribute:
+        // TODO: Not implemented, or not applicable.
+        // Sort this out in Bug 1437165.
+        return;
+    }
+  }
+}
+
+void
+RsdparsaSdpAttributeList::LoadAll(RustAttributeList *attributeList)
+{
+  for (int i = SdpAttribute::kFirstAttribute; i <= SdpAttribute::kLastAttribute; i++) {
+    LoadAttribute(attributeList, static_cast<SdpAttribute::AttributeType>(i));
+  }
+}
+
+void
+RsdparsaSdpAttributeList::LoadIceUfrag(RustAttributeList* attributeList)
+{
+  StringView ufragStr;
+  nsresult nr = sdp_get_iceufrag(attributeList, &ufragStr);
+  if (NS_SUCCEEDED(nr)) {
+    std::string iceufrag = convertStringView(ufragStr);
+    SetAttribute(new SdpStringAttribute(SdpAttribute::kIceUfragAttribute,
+                                        iceufrag));
+  }
+}
+
+void
+RsdparsaSdpAttributeList::LoadIcePwd(RustAttributeList* attributeList)
+{
+  StringView pwdStr;
+  nsresult nr = sdp_get_icepwd(attributeList, &pwdStr);
+  if (NS_SUCCEEDED(nr)) {
+    std::string icePwd = convertStringView(pwdStr);
+    SetAttribute(new SdpStringAttribute(SdpAttribute::kIcePwdAttribute,
+                                        icePwd));
+  }
+}
+
+void
+RsdparsaSdpAttributeList::LoadIdentity(RustAttributeList* attributeList)
+{
+  StringView identityStr;
+  nsresult nr = sdp_get_identity(attributeList, &identityStr);
+  if (NS_SUCCEEDED(nr)) {
+    std::string identity = convertStringView(identityStr);
+    SetAttribute(new SdpStringAttribute(SdpAttribute::kIdentityAttribute,
+                                        identity));
+  }
+}
+
+void
+RsdparsaSdpAttributeList::LoadIceOptions(RustAttributeList* attributeList)
+{
+  StringVec* options;
+  nsresult nr = sdp_get_iceoptions(attributeList, &options);
+  if (NS_SUCCEEDED(nr)) {
+    std::vector<std::string> optionsVec;
+    auto optionsAttr = MakeUnique<SdpOptionsAttribute>(
+                                  SdpAttribute::kIceOptionsAttribute);
+    for (size_t i = 0; i < string_vec_len(options); i++) {
+      StringView optionStr;
+      string_vec_get_view(options, i, &optionStr);
+      optionsAttr->PushEntry(convertStringView(optionStr));
+    }
+    SetAttribute(optionsAttr.release());
+  }
+}
+
+void
+RsdparsaSdpAttributeList::LoadFingerprint(RustAttributeList* attributeList)
+{
+  size_t nFp = sdp_get_fingerprint_count(attributeList);
+  if (nFp == 0) {
+    return;
+  }
+  auto rustFingerprints = MakeUnique<RustSdpAttributeFingerprint[]>(nFp);
+  sdp_get_fingerprints(attributeList, nFp, rustFingerprints.get());
+  auto fingerprints = MakeUnique<SdpFingerprintAttributeList>();
+  for(size_t i = 0; i < nFp; i++) {
+    RustSdpAttributeFingerprint& fingerprint = rustFingerprints[i];
+    std::string algorithm = convertStringView(fingerprint.hashAlgorithm);
+    std::string fingerprintToken = convertStringView(fingerprint.fingerprint);
+    std::vector<uint8_t> fingerprintBytes =
+      SdpFingerprintAttributeList::ParseFingerprint(fingerprintToken);
+    if (fingerprintBytes.size() == 0) {
+      // TODO: Should we load fingerprint earlier to detect if it is malformed
+      // and throw a proper error?
+      // TODO: We should improve our error checking. See Bug 1437169.
+      continue;
+    }
+    fingerprints->PushEntry(algorithm, fingerprintBytes);
+  }
+  SetAttribute(fingerprints.release());
+}
+
+void
+RsdparsaSdpAttributeList::LoadSetup(RustAttributeList* attributeList)
+{
+  RustSdpSetup rustSetup;
+  nsresult nr = sdp_get_setup(attributeList, &rustSetup);
+  if (NS_SUCCEEDED(nr)) {
+    SdpSetupAttribute::Role setupEnum;
+    switch(rustSetup) {
+      case kRustActive:
+        setupEnum = SdpSetupAttribute::kActive;
+        break;
+      case kRustActpass:
+        setupEnum = SdpSetupAttribute::kActpass;
+        break;
+      case kRustHoldconn:
+        setupEnum = SdpSetupAttribute::kHoldconn;
+        break;
+      case kRustPassive:
+        setupEnum = SdpSetupAttribute::kPassive;
+        break;
+    }
+    SetAttribute(new SdpSetupAttribute(setupEnum));
+  }
+}
+
+void
+RsdparsaSdpAttributeList::LoadSsrc(RustAttributeList* attributeList)
+{
+  size_t numSsrc = sdp_get_ssrc_count(attributeList);
+  if (numSsrc == 0) {
+    return;
+  }
+  auto rustSsrcs = MakeUnique<RustSdpAttributeSsrc[]>(numSsrc);
+  sdp_get_ssrcs(attributeList, numSsrc, rustSsrcs.get());
+  auto ssrcs = MakeUnique<SdpSsrcAttributeList>();
+  for(size_t i = 0; i < numSsrc; i++) {
+    RustSdpAttributeSsrc& ssrc = rustSsrcs[i];
+    std::string attribute = convertStringView(ssrc.attribute);
+    std::string value = convertStringView(ssrc.value);
+    if (value.length() == 0) {
+      ssrcs->PushEntry(ssrc.id, attribute);
+    } else {
+      ssrcs->PushEntry(ssrc.id, attribute + ":" + value);
+    }
+  }
+  SetAttribute(ssrcs.release());
+}
+
+SdpRtpmapAttributeList::CodecType
+strToCodecType(const std::string &name)
+{
+  auto codec = SdpRtpmapAttributeList::kOtherCodec;
+  if (!strcasecmp(name.c_str(), "opus")) {
+    codec = SdpRtpmapAttributeList::kOpus;
+  } else if (!strcasecmp(name.c_str(), "G722")) {
+    codec = SdpRtpmapAttributeList::kG722;
+  } else if (!strcasecmp(name.c_str(), "PCMU")) {
+    codec = SdpRtpmapAttributeList::kPCMU;
+  } else if (!strcasecmp(name.c_str(), "PCMA")) {
+    codec = SdpRtpmapAttributeList::kPCMA;
+  } else if (!strcasecmp(name.c_str(), "VP8")) {
+    codec = SdpRtpmapAttributeList::kVP8;
+  } else if (!strcasecmp(name.c_str(), "VP9")) {
+    codec = SdpRtpmapAttributeList::kVP9;
+  } else if (!strcasecmp(name.c_str(), "iLBC")) {
+    codec = SdpRtpmapAttributeList::kiLBC;
+  } else if(!strcasecmp(name.c_str(), "iSAC")) {
+    codec = SdpRtpmapAttributeList::kiSAC;
+  } else if (!strcasecmp(name.c_str(), "H264")) {
+    codec = SdpRtpmapAttributeList::kH264;
+  } else if (!strcasecmp(name.c_str(), "red")) {
+    codec = SdpRtpmapAttributeList::kRed;
+  } else if (!strcasecmp(name.c_str(), "ulpfec")) {
+    codec = SdpRtpmapAttributeList::kUlpfec;
+  } else if (!strcasecmp(name.c_str(), "telephone-event")) {
+    codec = SdpRtpmapAttributeList::kTelephoneEvent;
+  }
+  return codec;
+}
+
+void
+RsdparsaSdpAttributeList::LoadRtpmap(RustAttributeList* attributeList)
+{
+  size_t numRtpmap = sdp_get_rtpmap_count(attributeList);
+  if (numRtpmap == 0) {
+    return;
+  }
+  auto rustRtpmaps = MakeUnique<RustSdpAttributeRtpmap[]>(numRtpmap);
+  sdp_get_rtpmaps(attributeList, numRtpmap, rustRtpmaps.get());
+  auto rtpmapList = MakeUnique<SdpRtpmapAttributeList>();
+  for(size_t i = 0; i < numRtpmap; i++) {
+    RustSdpAttributeRtpmap& rtpmap = rustRtpmaps[i];
+    std::string payloadType = std::to_string(rtpmap.payloadType);
+    std::string name = convertStringView(rtpmap.codecName);
+    auto codec = strToCodecType(name);
+    uint32_t channels = rtpmap.channels;
+    if (mIsVideo) {
+      // channels is expected to be 0 for video in higher level code,
+      // channels don't make sense, so the value is arbitrary. 1 is
+      // the arbitrary value for that code.
+      // TODO: handle this in Rust parser, see Bug 1436403
+      channels = 0;
+    }
+    rtpmapList->PushEntry(payloadType, codec, name,
+                          rtpmap.frequency, channels);
+  }
+  SetAttribute(rtpmapList.release());
+}
+
+void
+RsdparsaSdpAttributeList::LoadFmtp(RustAttributeList* attributeList)
+{
+  // TODO: Not implemented, see Bug 1432918
+#if 0
+  size_t numFmtp = sdp_get_fmtp_count(attributeList);
+    if (numFmtp == 0) {
+    return;
+  }
+  auto rustFmtps = MakeUnique<RustSdpAttributeFmtp[]>(numFmtp);
+  size_t numValidFmtp = sdp_get_fmtp(attributeList, numFmtp,
+                                        rustFmtps.get());
+  for(size_t i = 0; i < numValidFmtp; i++) {
+    RustSdpAttributeFmtp& fmtp = rustFmtps[i];
+  }
+#endif
+}
+
+void
+RsdparsaSdpAttributeList::LoadPtime(RustAttributeList* attributeList)
+{
+  int64_t ptime = sdp_get_ptime(attributeList);
+  if (ptime >= 0) {
+    SetAttribute(new SdpNumberAttribute(SdpAttribute::kPtimeAttribute,
+                                        static_cast<uint32_t>(ptime)));
+  }
+}
+
+void
+RsdparsaSdpAttributeList::LoadFlags(RustAttributeList* attributeList)
+{
+  RustSdpAttributeFlags flags = sdp_get_attribute_flags(attributeList);
+  if (flags.iceLite) {
+    SetAttribute(new SdpFlagAttribute(SdpAttribute::kIceLiteAttribute));
+  }
+  if (flags.rtcpMux) {
+    SetAttribute(new SdpFlagAttribute(SdpAttribute::kRtcpMuxAttribute));
+  }
+  if (flags.bundleOnly) {
+    SetAttribute(new SdpFlagAttribute(SdpAttribute::kBundleOnlyAttribute));
+  }
+  if (flags.endOfCandidates) {
+    SetAttribute(new SdpFlagAttribute(SdpAttribute::kEndOfCandidatesAttribute));
+  }
+}
+
+void
+RsdparsaSdpAttributeList::LoadMid(RustAttributeList* attributeList)
+{
+  StringView rustMid;
+  if (NS_SUCCEEDED(sdp_get_mid(attributeList, &rustMid))) {
+    std::string mid = convertStringView(rustMid);
+    SetAttribute(new SdpStringAttribute(SdpAttribute::kMidAttribute, mid));
+  }
+}
+
+void
+RsdparsaSdpAttributeList::LoadMsid(RustAttributeList* attributeList)
+{
+  size_t numMsid = sdp_get_msid_count(attributeList);
+  if (numMsid == 0) {
+    return;
+  }
+  auto rustMsid = MakeUnique<RustSdpAttributeMsid[]>(numMsid);
+  sdp_get_msids(attributeList, numMsid, rustMsid.get());
+  auto msids = MakeUnique<SdpMsidAttributeList>();
+  for(size_t i = 0; i < numMsid; i++) {
+    RustSdpAttributeMsid& msid = rustMsid[i];
+    std::string id = convertStringView(msid.id);
+    std::string appdata = convertStringView(msid.appdata);
+    msids->PushEntry(id, appdata);
+  }
+  SetAttribute(msids.release());
+}
+
+void
+RsdparsaSdpAttributeList::LoadMsidSemantics(RustAttributeList* attributeList)
+{
+  size_t numMsidSemantic = sdp_get_msid_semantic_count(attributeList);
+  if (numMsidSemantic == 0) {
+    return;
+  }
+  auto rustMsidSemantics = MakeUnique<RustSdpAttributeMsidSemantic[]>(numMsidSemantic);
+  sdp_get_msid_semantics(attributeList, numMsidSemantic,
+                         rustMsidSemantics.get());
+  auto msidSemantics = MakeUnique<SdpMsidSemanticAttributeList>();
+  for(size_t i = 0; i < numMsidSemantic; i++) {
+    RustSdpAttributeMsidSemantic& rustMsidSemantic = rustMsidSemantics[i];
+    std::string semantic = convertStringView(rustMsidSemantic.semantic);
+    std::vector<std::string> msids = convertStringVec(rustMsidSemantic.msids);
+    msidSemantics->PushEntry(semantic, msids);
+  }
+  SetAttribute(msidSemantics.release());
+}
+
+void
+RsdparsaSdpAttributeList::LoadGroup(RustAttributeList* attributeList)
+{
+  size_t numGroup = sdp_get_group_count(attributeList);
+  if (numGroup == 0) {
+    return;
+  }
+  auto rustGroups = MakeUnique<RustSdpAttributeGroup[]>(numGroup);
+  nsresult nr = sdp_get_groups(attributeList, numGroup, rustGroups.get());
+  if (NS_FAILED(nr)) {
+    return;
+  }
+  auto groups = MakeUnique<SdpGroupAttributeList>();
+  for(size_t i = 0; i < numGroup; i++) {
+    RustSdpAttributeGroup& group = rustGroups[i];
+    SdpGroupAttributeList::Semantics semantic;
+    switch(group.semantic) {
+      case RustSdpAttributeGroupSemantic ::kRustLipSynchronization:
+        semantic = SdpGroupAttributeList::kLs;
+        break;
+      case RustSdpAttributeGroupSemantic ::kRustFlowIdentification:
+        semantic = SdpGroupAttributeList::kFid;
+        break;
+      case RustSdpAttributeGroupSemantic ::kRustSingleReservationFlow:
+        semantic = SdpGroupAttributeList::kSrf;
+        break;
+      case RustSdpAttributeGroupSemantic ::kRustAlternateNetworkAddressType:
+        semantic = SdpGroupAttributeList::kAnat;
+        break;
+      case RustSdpAttributeGroupSemantic ::kRustForwardErrorCorrection:
+        semantic = SdpGroupAttributeList::kFec;
+        break;
+      case RustSdpAttributeGroupSemantic ::kRustDecodingDependency:
+        semantic = SdpGroupAttributeList::kDdp;
+        break;
+      case RustSdpAttributeGroupSemantic ::kRustBundle:
+        semantic = SdpGroupAttributeList::kBundle;
+        break;
+    }
+    std::vector<std::string> tags = convertStringVec(group.tags);
+    groups->PushEntry(semantic, tags);
+  }
+  SetAttribute(groups.release());
+}
+
+void
+RsdparsaSdpAttributeList::LoadRtcp(RustAttributeList* attributeList)
+{
+  RustSdpAttributeRtcp rtcp;
+  if (NS_SUCCEEDED(sdp_get_rtcp(attributeList, &rtcp))) {
+    sdp::AddrType addrType = convertAddressType(rtcp.unicastAddr.addrType);
+    if (sdp::kAddrTypeNone == addrType) {
+      SetAttribute(new SdpRtcpAttribute(rtcp.port));
+    } else {
+      std::string addr(rtcp.unicastAddr.unicastAddr);
+      SetAttribute(new SdpRtcpAttribute(rtcp.port, sdp::kInternet,
+                                        addrType, addr));
+    }
+  }
+}
+
+void
+RsdparsaSdpAttributeList::LoadImageattr(RustAttributeList* attributeList)
+{
+  size_t numImageattrs = sdp_get_imageattr_count(attributeList);
+  if (numImageattrs == 0) {
+    return;
+  }
+  auto rustImageattrs = MakeUnique<StringView[]>(numImageattrs);
+  sdp_get_imageattrs(attributeList, numImageattrs, rustImageattrs.get());
+  auto imageattrList = MakeUnique<SdpImageattrAttributeList>();
+  for(size_t i = 0; i < numImageattrs; i++) {
+    StringView& imageAttr = rustImageattrs[i];
+    std::string image = convertStringView(imageAttr);
+    std::string error;
+    size_t errorPos;
+    if (!imageattrList->PushEntry(image, &error, &errorPos)) {
+      // TODO: handle error, see Bug 1438237
+    }
+  }
+  SetAttribute(imageattrList.release());
+}
+
+void
+RsdparsaSdpAttributeList::LoadSctpmaps(RustAttributeList* attributeList)
+{
+  size_t numSctpmaps = sdp_get_sctpmap_count(attributeList);
+  if (numSctpmaps == 0) {
+    return;
+  }
+  auto rustSctpmaps = MakeUnique<RustSdpAttributeSctpmap[]>(numSctpmaps);
+  sdp_get_sctpmaps(attributeList, numSctpmaps, rustSctpmaps.get());
+  auto sctpmapList = MakeUnique<SdpSctpmapAttributeList>();
+  for(size_t i = 0; i < numSctpmaps; i++) {
+    RustSdpAttributeSctpmap& sctpmap = rustSctpmaps[i];
+    sctpmapList->PushEntry(std::to_string(sctpmap.port),
+                           "webrtc-datachannel",
+                           sctpmap.channels);
+  }
+  SetAttribute(sctpmapList.release());
+}
+
+void
+RsdparsaSdpAttributeList::LoadDirection(RustAttributeList* attributeList)
+{
+  SdpDirectionAttribute::Direction dir;
+  RustDirection rustDir = sdp_get_direction(attributeList);
+  switch(rustDir) {
+    case kRustRecvonly:
+      dir = SdpDirectionAttribute::kRecvonly;
+      break;
+    case kRustSendonly:
+      dir = SdpDirectionAttribute::kSendonly;
+      break;
+    case kRustSendrecv:
+      dir = SdpDirectionAttribute::kSendrecv;
+      break;
+    case kRustInactive:
+      dir = SdpDirectionAttribute::kInactive;
+      break;
+  }
+  SetAttribute(new SdpDirectionAttribute(dir));
+}
+
+void
+RsdparsaSdpAttributeList::LoadRemoteCandidates(RustAttributeList* attributeList)
+{
+  size_t nC = sdp_get_remote_candidate_count(attributeList);
+  if (nC == 0) {
+    return;
+  }
+  auto rustCandidates = MakeUnique<RustSdpAttributeRemoteCandidate[]>(nC);
+  sdp_get_remote_candidates(attributeList, nC,
+                            rustCandidates.get());
+  std::vector<SdpRemoteCandidatesAttribute::Candidate> candidates;
+  for(size_t i = 0; i < nC; i++) {
+    RustSdpAttributeRemoteCandidate& rustCandidate = rustCandidates[i];
+    SdpRemoteCandidatesAttribute::Candidate candidate;
+    candidate.port = rustCandidate.port;
+    candidate.id = std::to_string(rustCandidate.component);
+    candidate.address = std::string(rustCandidate.address.unicastAddr);
+    candidates.push_back(candidate);
+  }
+  SdpRemoteCandidatesAttribute* candidatesList;
+  candidatesList = new SdpRemoteCandidatesAttribute(candidates);
+  SetAttribute(candidatesList);
+}
+
+void
+RsdparsaSdpAttributeList::LoadRids(RustAttributeList* attributeList)
+{
+  size_t numRids = sdp_get_rid_count(attributeList);
+  if (numRids == 0) {
+    return;
+  }
+  auto rustRids = MakeUnique<StringView[]>(numRids);
+  sdp_get_rids(attributeList, numRids,
+               rustRids.get());
+  auto rids = MakeUnique<SdpRidAttributeList>();
+  for(size_t i = 0; i < numRids; i++) {
+    std::string rid = convertStringView(rustRids[i]);
+    std::string error;
+    size_t errorPos;
+    if (!rids->PushEntry(rid, &error, &errorPos)) {
+      // TODO: handle error, see Bug 1438237
+    }
+  }
+  SetAttribute(rids.release());
+}
+
+void
+RsdparsaSdpAttributeList::LoadExtmap(RustAttributeList* attributeList)
+{
+  size_t numExtmap = sdp_get_extmap_count(attributeList);
+  if (numExtmap == 0) {
+    return;
+  }
+  auto rustExtmaps = MakeUnique<RustSdpAttributeExtmap[]>(numExtmap);
+  sdp_get_extmaps(attributeList, numExtmap,
+                  rustExtmaps.get());
+  auto extmaps = MakeUnique<SdpExtmapAttributeList>();
+  for(size_t i = 0; i < numExtmap; i++) {
+    RustSdpAttributeExtmap& rustExtmap = rustExtmaps[i];
+    std::string name = convertStringView(rustExtmap.url);
+    SdpDirectionAttribute::Direction direction;
+    bool directionSpecified = true;
+    switch(rustExtmap.direction) {
+      case kRustRecvonly:
+        direction = SdpDirectionAttribute::kRecvonly;
+        break;
+      case kRustSendonly:
+        direction = SdpDirectionAttribute::kSendonly;
+        break;
+      case kRustSendrecv:
+        direction = SdpDirectionAttribute::kSendrecv;
+        break;
+      case kRustInactive:
+        // TODO: Fix this, see Bug 1438544
+        direction = SdpDirectionAttribute::kSendrecv;
+        directionSpecified = false;
+        break;
+    }
+    std::string extensionAttributes;
+    extensionAttributes = convertStringView(rustExtmap.extensionAttributes);
+    extmaps->PushEntry((uint16_t) rustExtmap.id, direction,
+                       directionSpecified, name, extensionAttributes);
+  }
+  SetAttribute(extmaps.release());
+}
+
+
+bool
+RsdparsaSdpAttributeList::IsAllowedHere(SdpAttribute::AttributeType type)
+{
+  if (AtSessionLevel() && !SdpAttribute::IsAllowedAtSessionLevel(type)) {
+    return false;
+  }
+
+  if (!AtSessionLevel() && !SdpAttribute::IsAllowedAtMediaLevel(type)) {
+    return false;
+  }
+
+  return true;
+}
+
+void
+RsdparsaSdpAttributeList::Serialize(std::ostream& os) const
+{
+  for (size_t i = 0; i < kNumAttributeTypes; ++i) {
+    if (mAttributes[i]) {
+      os << *mAttributes[i];
+    }
+  }
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpAttributeList.h
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _RSDPARSA_SDP_ATTRIBUTE_LIST_H_
+#define _RSDPARSA_SDP_ATTRIBUTE_LIST_H_
+
+#include "signaling/src/sdp/RsdparsaSdpGlue.h"
+#include "signaling/src/sdp/RsdparsaSdpInc.h"
+#include "signaling/src/sdp/SdpAttributeList.h"
+
+namespace mozilla
+{
+
+class RsdparsaSdp;
+class RsdparsaSdpMediaSection;
+class SdpErrorHolder;
+
+class RsdparsaSdpAttributeList : public SdpAttributeList
+{
+  friend class RsdparsaSdpMediaSection;
+  friend class RsdparsaSdp;
+
+public:
+  // Make sure we don't hide the default arg thunks
+  using SdpAttributeList::HasAttribute;
+  using SdpAttributeList::GetAttribute;
+
+  bool HasAttribute(AttributeType type,
+                    bool sessionFallback) const override;
+  const SdpAttribute* GetAttribute(AttributeType type,
+                                   bool sessionFallback) const override;
+  void SetAttribute(SdpAttribute* attr) override;
+  void RemoveAttribute(AttributeType type) override;
+  void Clear() override;
+  uint32_t Count() const override;
+
+  const SdpConnectionAttribute& GetConnection() const override;
+  const SdpFingerprintAttributeList& GetFingerprint() const override;
+  const SdpGroupAttributeList& GetGroup() const override;
+  const SdpOptionsAttribute& GetIceOptions() const override;
+  const SdpRtcpAttribute& GetRtcp() const override;
+  const SdpRemoteCandidatesAttribute& GetRemoteCandidates() const override;
+  const SdpSetupAttribute& GetSetup() const override;
+  const SdpSsrcAttributeList& GetSsrc() const override;
+  const SdpSsrcGroupAttributeList& GetSsrcGroup() const override;
+  const SdpDtlsMessageAttribute& GetDtlsMessage() const override;
+
+  // These attributes can appear multiple times, so the returned
+  // classes actually represent a collection of values.
+  const std::vector<std::string>& GetCandidate() const override;
+  const SdpExtmapAttributeList& GetExtmap() const override;
+  const SdpFmtpAttributeList& GetFmtp() const override;
+  const SdpImageattrAttributeList& GetImageattr() const override;
+  const SdpSimulcastAttribute& GetSimulcast() const override;
+  const SdpMsidAttributeList& GetMsid() const override;
+  const SdpMsidSemanticAttributeList& GetMsidSemantic() const override;
+  const SdpRidAttributeList& GetRid() const override;
+  const SdpRtcpFbAttributeList& GetRtcpFb() const override;
+  const SdpRtpmapAttributeList& GetRtpmap() const override;
+  const SdpSctpmapAttributeList& GetSctpmap() const override;
+
+  // These attributes are effectively simple types, so we'll make life
+  // easy by just returning their value.
+  uint32_t GetSctpPort() const override;
+  uint32_t GetMaxMessageSize() const override;
+  const std::string& GetIcePwd() const override;
+  const std::string& GetIceUfrag() const override;
+  const std::string& GetIdentity() const override;
+  const std::string& GetLabel() const override;
+  unsigned int GetMaxptime() const override;
+  const std::string& GetMid() const override;
+  unsigned int GetPtime() const override;
+
+  SdpDirectionAttribute::Direction GetDirection() const override;
+
+  void Serialize(std::ostream&) const override;
+
+  virtual ~RsdparsaSdpAttributeList();
+
+private:
+  explicit RsdparsaSdpAttributeList(RsdparsaSessionHandle session)
+    : mSession(Move(session))
+    , mSessionAttributes(nullptr)
+    , mIsVideo(false)
+    , mAttributes()
+  {
+    RustAttributeList* attributes = get_sdp_session_attributes(mSession.get());
+    LoadAll(attributes);
+  }
+
+  RsdparsaSdpAttributeList(RsdparsaSessionHandle session,
+                           const RustMediaSection* const msection,
+                           const RsdparsaSdpAttributeList* sessionAttributes)
+    : mSession(Move(session))
+    , mSessionAttributes(sessionAttributes)
+    , mAttributes()
+  {
+    mIsVideo = sdp_rust_get_media_type(msection) == kRustVideo;
+    RustAttributeList* attributes = sdp_get_media_attribute_list(msection);
+    LoadAll(attributes);
+  }
+
+  static const std::string kEmptyString;
+  static const size_t kNumAttributeTypes = SdpAttribute::kLastAttribute + 1;
+
+  const RsdparsaSessionHandle mSession;
+  const RsdparsaSdpAttributeList* mSessionAttributes;
+  bool mIsVideo;
+
+  bool
+  AtSessionLevel() const
+  {
+    return !mSessionAttributes;
+  }
+
+  bool IsAllowedHere(SdpAttribute::AttributeType type);
+  void LoadAll(RustAttributeList* attributeList);
+  void LoadAttribute(RustAttributeList* attributeList, AttributeType type);
+  void LoadIceUfrag(RustAttributeList* attributeList);
+  void LoadIcePwd(RustAttributeList* attributeList);
+  void LoadIdentity(RustAttributeList* attributeList);
+  void LoadIceOptions(RustAttributeList* attributeList);
+  void LoadFingerprint(RustAttributeList* attributeList);
+  void LoadSetup(RustAttributeList* attributeList);
+  void LoadSsrc(RustAttributeList* attributeList);
+  void LoadRtpmap(RustAttributeList* attributeList);
+  void LoadFmtp(RustAttributeList* attributeList);
+  void LoadPtime(RustAttributeList* attributeList);
+  void LoadFlags(RustAttributeList* attributeList);
+  void LoadMid(RustAttributeList* attributeList);
+  void LoadMsid(RustAttributeList* attributeList);
+  void LoadMsidSemantics(RustAttributeList* attributeList);
+  void LoadGroup(RustAttributeList* attributeList);
+  void LoadRtcp(RustAttributeList* attributeList);
+  void LoadImageattr(RustAttributeList* attributeList);
+  void LoadSctpmaps(RustAttributeList* attributeList);
+  void LoadDirection(RustAttributeList* attributeList);
+  void LoadRemoteCandidates(RustAttributeList* attributeList);
+  void LoadRids(RustAttributeList* attributeList);
+  void LoadExtmap(RustAttributeList* attributeList);
+
+  void WarnAboutMisplacedAttribute(SdpAttribute::AttributeType type,
+                                   uint32_t lineNumber,
+                                   SdpErrorHolder& errorHolder);
+
+
+  SdpAttribute* mAttributes[kNumAttributeTypes];
+
+  RsdparsaSdpAttributeList(const RsdparsaSdpAttributeList& orig) = delete;
+  RsdparsaSdpAttributeList& operator=(const RsdparsaSdpAttributeList& rhs) = delete;
+};
+
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpGlue.cpp
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include <string>
+
+#include "signaling/src/sdp/RsdparsaSdpInc.h"
+#include "signaling/src/sdp/RsdparsaSdpGlue.h"
+namespace mozilla
+{
+
+std::string convertStringView(StringView str)
+{
+  if (nullptr == str.buf) {
+    return std::string();
+  } else {
+    return std::string(str.buf, str.len);
+  }
+}
+
+std::vector<std::string> convertStringVec(StringVec* vec)
+{
+  std::vector<std::string> ret;
+  size_t len = string_vec_len(vec);
+  for (size_t i = 0; i < len; i++) {
+    StringView view;
+    string_vec_get_view(vec, i, &view);
+    ret.push_back(convertStringView(view));
+  }
+  return ret;
+}
+
+sdp::AddrType convertAddressType(RustSdpAddrType addrType)
+{
+  switch(addrType) {
+    case kRustAddrNone:
+      return sdp::kAddrTypeNone;
+    case kRustAddrIp4:
+      return sdp::kIPv4;
+    case kRustAddrIp6:
+      return sdp::kIPv6;
+  }
+
+  MOZ_CRASH("unknown address type");
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpGlue.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _RUSTSDPGLUE_H_
+#define _RUSTSDPGLUE_H_
+
+#include <string>
+#include <vector>
+
+#include "signaling/src/sdp/Sdp.h"
+#include "signaling/src/sdp/RsdparsaSdpInc.h"
+
+namespace mozilla
+{
+
+struct FreeRustSdpSession {
+  void operator()(RustSdpSession* aSess) { sdp_free_session(aSess); }
+};
+
+typedef UniquePtr<RustSdpSession, FreeRustSdpSession> RsdparsaSessionHandle;
+
+std::string convertStringView(StringView str);
+std::vector<std::string> convertStringVec(StringVec* vec);
+sdp::AddrType convertAddressType(RustSdpAddrType addr);
+
+}
+
+
+#endif
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpInc.h
@@ -0,0 +1,292 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _RUSTSDPINC_H_
+#define _RUSTSDPINC_H_
+
+#include "nsError.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdbool.h>
+
+  typedef struct BandwidthVec BandwidthVec;
+  typedef struct RustSdpSession RustSdpSession;
+  typedef struct RustSdpError RustSdpError;
+  typedef struct RustMediaSection RustMediaSection;
+  typedef struct RustAttributeList RustAttributeList;
+  typedef struct StringVec StringVec;
+  typedef struct U32Vec U32Vec;
+  typedef struct RustHeapString RustHeapString;
+
+
+  enum RustSdpAddrType {
+    kRustAddrNone,
+    kRustAddrIp4,
+    kRustAddrIp6
+  };
+
+  typedef struct RustIpAddr {
+    RustSdpAddrType addrType;
+    char unicastAddr[50];
+  } RustIpAddr;
+
+  typedef struct StringView {
+    char* buf;
+    size_t len;
+  } StringView;
+
+  typedef struct RustSdpConnection {
+    RustIpAddr addr;
+    uint8_t ttl;
+    uint64_t amount;
+  } RustSdpConnection;
+
+  typedef struct RustSdpOrigin {
+    StringView username;
+    uint64_t sessionId;
+    uint64_t sessionVersion;
+    RustIpAddr addr;
+  } RustSdpOrigin;
+
+  enum RustSdpMediaValue {
+    kRustAudio,
+    kRustVideo,
+    kRustApplication
+  };
+
+  enum RustSdpProtocolValue {
+    kRustRtpSavpf,
+    kRustUdpTlsRtpSavpf,
+    kRustTcpTlsRtpSavpf,
+    kRustDtlsSctp,
+    kRustUdpDtlsSctp,
+    kRustTcpDtlsSctp,
+  };
+
+  enum RustSdpFormatType {
+    kRustIntegers,
+    kRustStrings
+  };
+
+  size_t string_vec_len(const StringVec* vec);
+  nsresult string_vec_get_view(const StringVec* vec, size_t index,
+                               StringView* str);
+
+  size_t u32_vec_len(const U32Vec* vec);
+  nsresult u32_vec_get(const U32Vec* vec, size_t index, uint32_t* ret);
+
+  void sdp_free_string(char* string);
+
+  nsresult parse_sdp(const char* sdp, uint32_t length, bool fail_on_warning,
+		     RustSdpSession** ret, RustSdpError** err);
+  RustSdpSession* sdp_new_reference(RustSdpSession* aSess);
+  void sdp_free_session(RustSdpSession* ret);
+  size_t sdp_get_error_line_num(const RustSdpError* err);
+  StringView sdp_get_error_message(const RustSdpError* err);
+  void sdp_free_error(RustSdpError* err);
+
+  RustSdpOrigin sdp_get_origin(const RustSdpSession* aSess);
+
+  uint32_t get_sdp_bandwidth(const RustSdpSession* aSess,
+                             const char* aBandwidthType);
+  BandwidthVec* sdp_get_session_bandwidth_vec(const RustSdpSession* aSess);
+  BandwidthVec* sdp_get_media_bandwidth_vec(const RustMediaSection* aMediaSec);
+  char* sdp_serialize_bandwidth(const BandwidthVec* bandwidths);
+  bool sdp_session_has_connection(const RustSdpSession* aSess);
+  nsresult sdp_get_session_connection(const RustSdpSession* aSess,
+                                      RustSdpConnection* ret);
+  RustAttributeList* get_sdp_session_attributes(const RustSdpSession* aSess);
+
+  size_t sdp_media_section_count(const RustSdpSession* aSess);
+  RustMediaSection* sdp_get_media_section(const RustSdpSession* aSess,
+                                          size_t aLevel);
+  RustSdpMediaValue sdp_rust_get_media_type(const RustMediaSection* aMediaSec);
+  RustSdpProtocolValue
+  sdp_get_media_protocol(const RustMediaSection* aMediaSec);
+  RustSdpFormatType sdp_get_format_type(const RustMediaSection* aMediaSec);
+  StringVec* sdp_get_format_string_vec(const RustMediaSection* aMediaSec);
+  U32Vec* sdp_get_format_u32_vec(const RustMediaSection* aMediaSec);
+  uint32_t sdp_get_media_port(const RustMediaSection* aMediaSec);
+  uint32_t sdp_get_media_port_count(const RustMediaSection* aMediaSec);
+  uint32_t sdp_get_media_bandwidth(const RustMediaSection* aMediaSec,
+				                           const char* aBandwidthType);
+  bool sdp_media_has_connection(const RustMediaSection* aMediaSec);
+  nsresult sdp_get_media_connection(const RustMediaSection* aMediaSec,
+                                    RustSdpConnection* ret);
+
+  RustAttributeList*
+  sdp_get_media_attribute_list(const RustMediaSection* aMediaSec);
+
+  nsresult sdp_get_iceufrag(const RustAttributeList* aList, StringView* ret);
+  nsresult sdp_get_icepwd(const RustAttributeList* aList, StringView* ret);
+  nsresult sdp_get_identity(const RustAttributeList* aList, StringView* ret);
+  nsresult sdp_get_iceoptions(const RustAttributeList* aList, StringVec** ret);
+
+  typedef struct RustSdpAttributeFingerprint {
+    StringView hashAlgorithm;
+    StringView fingerprint;
+  } RustSdpAttributeFingerprint;
+
+  size_t sdp_get_fingerprint_count(const RustAttributeList* aList);
+  void sdp_get_fingerprints(const RustAttributeList* aList, size_t listSize,
+                            RustSdpAttributeFingerprint* ret);
+
+  enum RustSdpSetup {
+    kRustActive,
+    kRustActpass,
+    kRustHoldconn,
+    kRustPassive
+  };
+
+  nsresult sdp_get_setup(const RustAttributeList* aList, RustSdpSetup* ret);
+
+  typedef struct RustSdpAttributeSsrc {
+    uint32_t id;
+    StringView attribute;
+    StringView value;
+  } RustSdpAttributeSsrc;
+
+  size_t sdp_get_ssrc_count(const RustAttributeList* aList);
+  void sdp_get_ssrcs(const RustAttributeList* aList,
+                     size_t listSize, RustSdpAttributeSsrc* ret);
+
+  typedef struct RustSdpAttributeRtpmap {
+    uint8_t payloadType;
+    StringView codecName;
+    uint32_t frequency;
+    uint32_t channels;
+  } RustSdpAttributeRtpmap;
+
+  size_t sdp_get_rtpmap_count(const RustAttributeList* aList);
+  void sdp_get_rtpmaps(const RustAttributeList* aList, size_t listSize,
+                       RustSdpAttributeRtpmap* ret);
+
+  typedef struct RustSdpAttributeFmtp {
+    uint8_t payloadType;
+    StringView codecName;
+    StringVec* tokens;
+  } RustSdpAttributeFmtp;
+
+  size_t sdp_get_fmtp_count(const RustAttributeList* aList);
+  size_t sdp_get_fmtp(const RustAttributeList* aList, size_t listSize,
+                      RustSdpAttributeFmtp* ret);
+
+  int64_t sdp_get_ptime(const RustAttributeList* aList);
+
+  typedef struct RustSdpAttributeFlags {
+    bool iceLite;
+    bool rtcpMux;
+    bool bundleOnly;
+    bool endOfCandidates;
+  } RustSdpAttributeFlags;
+
+  RustSdpAttributeFlags sdp_get_attribute_flags(const RustAttributeList* aList);
+
+  nsresult sdp_get_mid(const RustAttributeList* aList, StringView* ret);
+
+  typedef struct RustSdpAttributeMsid {
+    StringView id;
+    StringView appdata;
+  } RustSdpAttributeMsid;
+
+  size_t sdp_get_msid_count(const RustAttributeList* aList);
+  void sdp_get_msids(const RustAttributeList* aList, size_t listSize,
+                     RustSdpAttributeMsid* ret);
+
+  typedef struct RustSdpAttributeMsidSemantic {
+    StringView semantic;
+    StringVec* msids;
+  } RustSdpAttributeMsidSemantic;
+
+  size_t sdp_get_msid_semantic_count(RustAttributeList* aList);
+  void sdp_get_msid_semantics(const RustAttributeList* aList, size_t listSize,
+                              RustSdpAttributeMsidSemantic* ret);
+
+  enum RustSdpAttributeGroupSemantic {
+    kRustLipSynchronization,
+    kRustFlowIdentification,
+    kRustSingleReservationFlow,
+    kRustAlternateNetworkAddressType,
+    kRustForwardErrorCorrection,
+    kRustDecodingDependency,
+    kRustBundle,
+  };
+
+  typedef struct RustSdpAttributeGroup {
+    RustSdpAttributeGroupSemantic semantic;
+    StringVec* tags;
+  } RustSdpAttributeGroup;
+
+  size_t sdp_get_group_count(const RustAttributeList* aList);
+  nsresult sdp_get_groups(const RustAttributeList* aList, size_t listSize,
+                          RustSdpAttributeGroup* ret);
+
+  typedef struct RustSdpAttributeRtcp {
+    uint16_t port;
+    RustIpAddr unicastAddr;
+  } RustSdpAttributeRtcp;
+
+  nsresult sdp_get_rtcp(const RustAttributeList* aList,
+                        RustSdpAttributeRtcp* ret);
+
+  size_t sdp_get_imageattr_count(const RustAttributeList* aList);
+  void sdp_get_imageattrs(const RustAttributeList* aList, size_t listSize,
+                          StringView* ret);
+
+  typedef struct RustSdpAttributeSctpmap {
+    uint16_t port;
+    uint32_t channels;
+  } RustSdpAttributeSctpmap;
+
+  size_t sdp_get_sctpmap_count(const RustAttributeList* aList);
+  void sdp_get_sctpmaps(const RustAttributeList* aList, size_t listSize,
+                        RustSdpAttributeSctpmap* ret);
+
+  enum RustDirection {
+    kRustRecvonly,
+    kRustSendonly,
+    kRustSendrecv,
+    kRustInactive
+  };
+
+  RustDirection sdp_get_direction(const RustAttributeList* aList);
+
+  typedef struct RustSdpAttributeRemoteCandidate {
+    uint32_t component;
+    RustIpAddr address;
+    uint32_t port;
+  } RustSdpAttributeRemoteCandidate;
+
+  size_t sdp_get_remote_candidate_count(const RustAttributeList* aList);
+  void sdp_get_remote_candidates(const RustAttributeList* aList,
+                                 size_t listSize,
+                                 RustSdpAttributeRemoteCandidate* ret);
+
+  size_t sdp_get_rid_count(const RustAttributeList* aList);
+  void sdp_get_rids(const RustAttributeList* aList, size_t listSize,
+                    StringView* ret);
+
+  // TODO: Add field indicating whether direction was specified
+  // See Bug 1438536.
+  typedef struct RustSdpAttributeExtmap {
+    uint16_t id;
+    RustDirection direction;
+    StringView url;
+    StringView extensionAttributes;
+  } RustSdpAttributeExtmap;
+
+  size_t sdp_get_extmap_count(const RustAttributeList* aList);
+  void sdp_get_extmaps(const RustAttributeList* aList, size_t listSize,
+                       RustSdpAttributeExtmap* ret);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpMediaSection.cpp
@@ -0,0 +1,235 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Bug 1433534 tracks cleaning up the TODOs in the file
+
+#include "signaling/src/sdp/SdpMediaSection.h"
+#include "signaling/src/sdp/RsdparsaSdpMediaSection.h"
+
+#include "signaling/src/sdp/RsdparsaSdpGlue.h"
+#include "signaling/src/sdp/RsdparsaSdpInc.h"
+
+#include <ostream>
+#include "signaling/src/sdp/SdpErrorHolder.h"
+
+#ifdef CRLF
+#undef CRLF
+#endif
+#define CRLF "\r\n"
+
+namespace mozilla
+{
+
+RsdparsaSdpMediaSection::RsdparsaSdpMediaSection(size_t level,
+      RsdparsaSessionHandle session, const RustMediaSection* const section,
+      const RsdparsaSdpAttributeList* sessionLevel)
+  : SdpMediaSection(level), mSession(Move(session)),
+    mSection(section)
+{
+  switch(sdp_rust_get_media_type(section)) {
+    case kRustAudio:
+      mMediaType = kAudio;
+      break;
+    case kRustVideo:
+      mMediaType = kVideo;
+      break;
+    case kRustApplication:
+      mMediaType = kApplication;
+      break;
+  }
+
+  RsdparsaSessionHandle attributeSession(sdp_new_reference(mSession.get()));
+  mAttributeList.reset(new RsdparsaSdpAttributeList(Move(attributeSession),
+                                                    section,
+                                                    sessionLevel));
+
+  LoadFormats();
+  LoadConnection();
+}
+
+unsigned int
+RsdparsaSdpMediaSection::GetPort() const
+{
+  return sdp_get_media_port(mSection);
+}
+
+void
+RsdparsaSdpMediaSection::SetPort(unsigned int port)
+{
+  // TODO, see Bug 1433093
+}
+
+unsigned int
+RsdparsaSdpMediaSection::GetPortCount() const
+{
+  return sdp_get_media_port_count(mSection);
+}
+
+SdpMediaSection::Protocol
+RsdparsaSdpMediaSection::GetProtocol() const
+{
+  switch(sdp_get_media_protocol(mSection)) {
+    case kRustRtpSavpf:
+      return kRtpSavpf;
+    case kRustUdpTlsRtpSavpf:
+      return kUdpTlsRtpSavpf;
+    case kRustTcpTlsRtpSavpf:
+      return kTcpTlsRtpSavpf;
+    case kRustDtlsSctp:
+      return kDtlsSctp;
+    case kRustUdpDtlsSctp:
+      return kUdpDtlsSctp;
+    case kRustTcpDtlsSctp:
+      return kTcpDtlsSctp;
+  }
+
+  MOZ_CRASH("invalid media protocol");
+}
+
+const SdpConnection&
+RsdparsaSdpMediaSection::GetConnection() const
+{
+  MOZ_ASSERT(mConnection);
+  return *mConnection;
+}
+
+SdpConnection&
+RsdparsaSdpMediaSection::GetConnection()
+{
+  MOZ_ASSERT(mConnection);
+  return *mConnection;
+}
+
+uint32_t
+RsdparsaSdpMediaSection::GetBandwidth(const std::string& type) const
+{
+  return sdp_get_media_bandwidth(mSection, type.c_str());
+}
+
+const std::vector<std::string>&
+RsdparsaSdpMediaSection::GetFormats() const
+{
+  return mFormats;
+}
+
+const SdpAttributeList&
+RsdparsaSdpMediaSection::GetAttributeList() const
+{
+  return *mAttributeList;
+}
+
+SdpAttributeList&
+RsdparsaSdpMediaSection::GetAttributeList()
+{
+  return *mAttributeList;
+}
+
+SdpDirectionAttribute
+RsdparsaSdpMediaSection::GetDirectionAttribute() const
+{
+  return SdpDirectionAttribute(mAttributeList->GetDirection());
+}
+
+void
+RsdparsaSdpMediaSection::AddCodec(const std::string& pt,
+                                  const std::string& name,
+                                  uint32_t clockrate, uint16_t channels)
+{
+  //TODO: see Bug 1438289
+}
+
+void
+RsdparsaSdpMediaSection::ClearCodecs()
+{
+
+  //TODO: see Bug 1438289
+}
+
+void
+RsdparsaSdpMediaSection::AddDataChannel(const std::string& name, uint16_t port,
+                                        uint16_t streams, uint32_t message_size)
+{
+  //TODO: See 1438290
+}
+
+void
+RsdparsaSdpMediaSection::Serialize(std::ostream& os) const
+{
+  os << "m=" << mMediaType << " " << GetPort();
+  if (GetPortCount()) {
+    os << "/" << GetPortCount();
+  }
+  os << " " << GetProtocol();
+  for (auto i = mFormats.begin(); i != mFormats.end(); ++i) {
+    os << " " << (*i);
+  }
+  os << CRLF;
+
+  // We dont do i=
+
+  if (mConnection) {
+    os << *mConnection;
+  }
+
+  BandwidthVec* bwVec = sdp_get_media_bandwidth_vec(mSection);
+  char* bwString = sdp_serialize_bandwidth(bwVec);
+  if (bwString) {
+    os << bwString;
+    sdp_free_string(bwString);
+  }
+
+  // We dont do k= because they're evil
+
+  os << *mAttributeList;
+}
+
+void
+RsdparsaSdpMediaSection::LoadFormats()
+{
+  RustSdpFormatType formatType = sdp_get_format_type(mSection);
+  if (formatType == kRustIntegers) {
+    U32Vec* vec = sdp_get_format_u32_vec(mSection);
+    size_t len = u32_vec_len(vec);
+    for (size_t i = 0; i < len; i++) {
+      uint32_t val;
+      u32_vec_get(vec, i, &val);
+      mFormats.push_back(std::to_string(val));
+    }
+  } else {
+    StringVec* vec = sdp_get_format_string_vec(mSection);
+    mFormats = convertStringVec(vec);
+  }
+}
+
+UniquePtr<SdpConnection> convertRustConnection(RustSdpConnection conn)
+{
+  std::string addr(conn.addr.unicastAddr);
+  sdp::AddrType type = convertAddressType(conn.addr.addrType);
+  return MakeUnique<SdpConnection>(type, addr, conn.ttl, conn.amount);
+}
+
+void
+RsdparsaSdpMediaSection::LoadConnection()
+{
+  RustSdpConnection conn;
+  nsresult nr;
+  if (sdp_media_has_connection(mSection)) {
+    nr = sdp_get_media_connection(mSection, &conn);
+    if (NS_SUCCEEDED(nr)) {
+      mConnection = convertRustConnection(conn);
+    }
+  } else if (sdp_session_has_connection(mSession.get())){
+    // TODO: rsdparsa needs to ensure there is a connection at the session level
+    // if it is missing at a media level. See Bug 1438539.
+    nr = sdp_get_session_connection(mSession.get(), &conn);
+    if (NS_SUCCEEDED(nr)) {
+      mConnection = convertRustConnection(conn);
+    }
+  }
+}
+
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpMediaSection.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _RUSTSDPMEDIASECTION_H_
+#define _RUSTSDPMEDIASECTION_H_
+
+#include "mozilla/Attributes.h"
+#include "mozilla/UniquePtr.h"
+#include "signaling/src/sdp/RsdparsaSdpInc.h"
+#include "signaling/src/sdp/RsdparsaSdpGlue.h"
+#include "signaling/src/sdp/SdpMediaSection.h"
+#include "signaling/src/sdp/RsdparsaSdpAttributeList.h"
+
+#include <map>
+
+namespace mozilla
+{
+
+class RsdparsaSdp;
+class SdpErrorHolder;
+
+class RsdparsaSdpMediaSection final : public SdpMediaSection
+{
+  friend class RsdparsaSdp;
+
+public:
+  ~RsdparsaSdpMediaSection() {}
+
+  MediaType
+  GetMediaType() const override
+  {
+    return mMediaType;
+  }
+
+  unsigned int GetPort() const override;
+  void SetPort(unsigned int port) override;
+  unsigned int GetPortCount() const override;
+  Protocol GetProtocol() const override;
+  const SdpConnection& GetConnection() const override;
+  SdpConnection& GetConnection() override;
+  uint32_t GetBandwidth(const std::string& type) const override;
+  const std::vector<std::string>& GetFormats() const override;
+
+  const SdpAttributeList& GetAttributeList() const override;
+  SdpAttributeList& GetAttributeList() override;
+  SdpDirectionAttribute GetDirectionAttribute() const override;
+
+  void AddCodec(const std::string& pt, const std::string& name,
+                uint32_t clockrate, uint16_t channels) override;
+  void ClearCodecs() override;
+
+  void AddDataChannel(const std::string& name, uint16_t port,
+                      uint16_t streams, uint32_t message_size) override;
+
+  void Serialize(std::ostream&) const override;
+
+private:
+  RsdparsaSdpMediaSection(size_t level,
+                          RsdparsaSessionHandle session,
+                          const RustMediaSection* const section,
+                          const RsdparsaSdpAttributeList* sessionLevel);
+
+  void LoadFormats();
+  void LoadConnection();
+
+  RsdparsaSessionHandle mSession;
+  const RustMediaSection* mSection;
+
+  MediaType mMediaType;
+  std::vector<std::string> mFormats;
+
+  UniquePtr<SdpConnection> mConnection;
+
+  UniquePtr<RsdparsaSdpAttributeList> mAttributeList;
+};
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpParser.cpp
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsError.h"
+
+#include "mozilla/UniquePtr.h"
+
+#include "signaling/src/sdp/Sdp.h"
+#include "signaling/src/sdp/SdpEnum.h"
+#include "signaling/src/sdp/RsdparsaSdp.h"
+#include "signaling/src/sdp/RsdparsaSdpParser.h"
+#include "signaling/src/sdp/RsdparsaSdpInc.h"
+#include "signaling/src/sdp/RsdparsaSdpGlue.h"
+
+namespace mozilla
+{
+
+UniquePtr<Sdp>
+RsdparsaSdpParser::Parse(const std::string &sdpText)
+{
+  ClearParseErrors();
+  const char* rawString = sdpText.c_str();
+  RustSdpSession* result;
+  RustSdpError* err;
+  nsresult rv = parse_sdp(rawString, sdpText.length() + 1, false,
+                          &result, &err);
+  if (rv != NS_OK) {
+    // TODO: err should eventually never be null if rv != NS_OK
+    // see Bug 1433529
+    if (err != nullptr) {
+      size_t line = sdp_get_error_line_num(err);
+      std::string errMsg = convertStringView(sdp_get_error_message(err));
+      sdp_free_error(err);
+      AddParseError(line, errMsg);
+    } else {
+      AddParseError(0, "Unhandled Rsdparsa parsing error");
+    }
+    return nullptr;
+  }
+  RsdparsaSessionHandle uniqueResult;
+  uniqueResult.reset(result);
+  RustSdpOrigin rustOrigin = sdp_get_origin(uniqueResult.get());
+  sdp::AddrType addrType = convertAddressType(rustOrigin.addr.addrType);
+  SdpOrigin origin(convertStringView(rustOrigin.username),
+                   rustOrigin.sessionId, rustOrigin.sessionVersion,
+                   addrType, std::string(rustOrigin.addr.unicastAddr));
+  return MakeUnique<RsdparsaSdp>(Move(uniqueResult), origin);
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/sdp/RsdparsaSdpParser.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _RUSTSDPPARSER_H_
+#define _RUSTSDPPARSER_H_
+
+#include <string>
+
+#include "mozilla/UniquePtr.h"
+
+#include "signaling/src/sdp/Sdp.h"
+#include "signaling/src/sdp/SdpErrorHolder.h"
+
+namespace mozilla
+{
+
+class RsdparsaSdpParser final : public SdpErrorHolder
+{
+public:
+  RsdparsaSdpParser() {}
+  virtual ~RsdparsaSdpParser() {}
+
+  /**
+   * This parses the provided text into an SDP object.
+   * This returns a nullptr-valued pointer if things go poorly.
+   */
+  UniquePtr<Sdp> Parse(const std::string& sdpText);
+};
+
+} // namespace mozilla
+
+#endif
--- a/media/webrtc/signaling/src/sdp/moz.build
+++ b/media/webrtc/signaling/src/sdp/moz.build
@@ -32,18 +32,25 @@ UNIFIED_SOURCES += [
     'SdpHelper.cpp',
     'SdpMediaSection.cpp',
     'SipccSdp.cpp',
     'SipccSdpAttributeList.cpp',
     'SipccSdpMediaSection.cpp',
     'SipccSdpParser.cpp',
 ]
 
-# Multiple definitions of "logTag" mean we can't use unified build here.
 SOURCES += [
+    # Building these as part of the unified build leads to multiply defined
+    # symbols on windows.
+    'RsdparsaSdp.cpp',
+    'RsdparsaSdpAttributeList.cpp',
+    'RsdparsaSdpGlue.cpp',
+    'RsdparsaSdpMediaSection.cpp',
+    'RsdparsaSdpParser.cpp',
+    # Multiple definitions of "logTag" mean we can't use unified build here.
     'sipcc/cpr_string.c',
     'sipcc/sdp_access.c',
     'sipcc/sdp_attr.c',
     'sipcc/sdp_attr_access.c',
     'sipcc/sdp_base64.c',
     'sipcc/sdp_config.c',
     'sipcc/sdp_main.c',
     'sipcc/sdp_token.c',