Bug 1192390 - Part 1: Lay architectural groundwork for simulcast negotiation. r=mt r=jesup
authorByron Campen [:bwc] <docfaraday@gmail.com>
Wed, 21 Oct 2015 17:07:08 -0500
changeset 310030 3baf48e3f758452c3df3e67b80273cb9f56d3a69
parent 310029 56e5d2fe4c19e5ae606acebc40490c9167f9a211
child 310031 2e233e224c0e54b8c85081fae0ff607dbdcb6733
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmt, jesup
bugs1192390
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1192390 - Part 1: Lay architectural groundwork for simulcast negotiation. r=mt r=jesup
media/webrtc/signaling/signaling.gyp
media/webrtc/signaling/src/common/EncodingConstraints.h
media/webrtc/signaling/src/jsep/JsepCodecDescription.h
media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
media/webrtc/signaling/src/jsep/JsepTrack.cpp
media/webrtc/signaling/src/jsep/JsepTrack.h
media/webrtc/signaling/src/jsep/JsepTrackEncoding.h
media/webrtc/signaling/src/media-conduit/CodecConfig.h
media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
media/webrtc/signaling/src/media-conduit/VideoConduit.h
media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/src/sdp/SdpAttribute.cpp
media/webrtc/signaling/src/sdp/SdpAttribute.h
media/webrtc/signaling/test/jsep_session_unittest.cpp
media/webrtc/signaling/test/mediaconduit_unittests.cpp
media/webrtc/signaling/test/sdp_unittests.cpp
--- a/media/webrtc/signaling/signaling.gyp
+++ b/media/webrtc/signaling/signaling.gyp
@@ -170,16 +170,17 @@
 
          # JSEP
          './src/jsep/JsepCodecDescription.h',
          './src/jsep/JsepSession.h',
          './src/jsep/JsepSessionImpl.cpp',
          './src/jsep/JsepSessionImpl.h',
          './src/jsep/JsepTrack.cpp',
          './src/jsep/JsepTrack.h',
+         './src/jsep/JsepTrackEncoding.h',
          './src/jsep/JsepTransport.h'
       ],
 
       #
       # DEFINES
       #
 
       'defines' : [
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/common/EncodingConstraints.h
@@ -0,0 +1,46 @@
+/* -*- 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 _ENCODING_CONSTRAINTS_H_
+#define _ENCODING_CONSTRAINTS_H_
+
+#include <algorithm>
+
+namespace mozilla
+{
+class EncodingConstraints
+{
+public:
+  EncodingConstraints() :
+    maxWidth(0),
+    maxHeight(0),
+    maxFps(0),
+    maxFs(0),
+    maxBr(0),
+    maxPps(0),
+    maxMbps(0),
+    maxCpb(0),
+    maxDpb(0)
+  {}
+
+  bool operator==(const EncodingConstraints& constraints) const
+  {
+    return !memcmp(this, &constraints, sizeof(EncodingConstraints));
+  }
+
+  uint32_t maxWidth;
+  uint32_t maxHeight;
+  uint32_t maxFps;
+  uint32_t maxFs;
+  uint32_t maxBr;
+  uint32_t maxPps;
+  uint32_t maxMbps; // macroblocks per second
+  uint32_t maxCpb; // coded picture buffer size
+  uint32_t maxDpb; // decoded picture buffer size
+};
+} // namespace mozilla
+
+#endif // _ENCODING_CONSTRAINTS_H_
--- a/media/webrtc/signaling/src/jsep/JsepCodecDescription.h
+++ b/media/webrtc/signaling/src/jsep/JsepCodecDescription.h
@@ -109,16 +109,18 @@ class JsepCodecDescription {
   mozilla::SdpMediaSection::MediaType mType;
   std::string mDefaultPt;
   std::string mName;
   uint32_t mClock;
   uint32_t mChannels;
   bool mEnabled;
   bool mStronglyPreferred;
   sdp::Direction mDirection;
+  // Will hold constraints from both fmtp and rid
+  EncodingConstraints mConstraints;
 };
 
 class JsepAudioCodecDescription : public JsepCodecDescription {
  public:
   JsepAudioCodecDescription(const std::string& defaultPt,
                             const std::string& name,
                             uint32_t clock,
                             uint32_t channels,
@@ -141,23 +143,17 @@ class JsepAudioCodecDescription : public
 class JsepVideoCodecDescription : public JsepCodecDescription {
  public:
   JsepVideoCodecDescription(const std::string& defaultPt,
                             const std::string& name,
                             uint32_t clock,
                             bool enabled = true)
       : JsepCodecDescription(mozilla::SdpMediaSection::kVideo, defaultPt, name,
                              clock, 0, enabled),
-        mMaxFs(0),
-        mMaxFr(0),
-        mPacketizationMode(0),
-        mMaxMbps(0),
-        mMaxCpb(0),
-        mMaxDpb(0),
-        mMaxBr(0)
+        mPacketizationMode(0)
   {
     // Add supported rtcp-fb types
     mNackFbTypes.push_back("");
     mNackFbTypes.push_back(SdpRtcpFbAttributeList::pli);
     mCcmFbTypes.push_back(SdpRtcpFbAttributeList::fir);
   }
 
   virtual void
@@ -183,21 +179,21 @@ class JsepVideoCodecDescription : public
         if (!h264Params.level_asymmetry_allowed) {
           // First time the fmtp has been set; set just in case this is for a
           // sendonly m-line, since even though we aren't receiving the level
           // negotiation still needs to happen (sigh).
           h264Params.profile_level_id = mProfileLevelId;
         }
       } else {
         // Parameters that only apply to what we receive
-        h264Params.max_mbps = mMaxMbps;
-        h264Params.max_fs = mMaxFs;
-        h264Params.max_cpb = mMaxCpb;
-        h264Params.max_dpb = mMaxDpb;
-        h264Params.max_br = mMaxBr;
+        h264Params.max_mbps = mConstraints.maxMbps;
+        h264Params.max_fs = mConstraints.maxFs;
+        h264Params.max_cpb = mConstraints.maxCpb;
+        h264Params.max_dpb = mConstraints.maxDpb;
+        h264Params.max_br = mConstraints.maxBr;
         strncpy(h264Params.sprop_parameter_sets,
                 mSpropParameterSets.c_str(),
                 sizeof(h264Params.sprop_parameter_sets) - 1);
         h264Params.profile_level_id = mProfileLevelId;
       }
 
       // Parameters that apply to both the send and recv directions
       h264Params.packetization_mode = mPacketizationMode;
@@ -207,18 +203,18 @@ class JsepVideoCodecDescription : public
       msection.SetFmtp(
           SdpFmtpAttributeList::Fmtp(mDefaultPt, "", h264Params));
     } else if (mName == "VP8" || mName == "VP9") {
       if (mDirection == sdp::kRecv) {
         // VP8 and VP9 share the same SDP parameters thus far
         SdpFmtpAttributeList::VP8Parameters vp8Params(
             GetVP8Parameters(mDefaultPt, msection));
 
-        vp8Params.max_fs = mMaxFs;
-        vp8Params.max_fr = mMaxFr;
+        vp8Params.max_fs = mConstraints.maxFs;
+        vp8Params.max_fr = mConstraints.maxFps;
         msection.SetFmtp(
             SdpFmtpAttributeList::Fmtp(mDefaultPt, "", vp8Params));
       }
     }
   }
 
   void
   AddRtcpFbsToMSection(SdpMediaSection& msection) const
@@ -317,38 +313,38 @@ class JsepVideoCodecDescription : public
       if (!h264Params.level_asymmetry_allowed) {
         SetSaneH264Level(std::min(GetSaneH264Level(h264Params.profile_level_id),
                                   GetSaneH264Level(mProfileLevelId)),
                          &mProfileLevelId);
       }
 
       if (mDirection == sdp::kSend) {
         // Remote values of these apply only to the send codec.
-        mMaxFs = h264Params.max_fs;
-        mMaxMbps = h264Params.max_mbps;
-        mMaxCpb = h264Params.max_cpb;
-        mMaxDpb = h264Params.max_dpb;
-        mMaxBr = h264Params.max_br;
+        mConstraints.maxFs = h264Params.max_fs;
+        mConstraints.maxMbps = h264Params.max_mbps;
+        mConstraints.maxCpb = h264Params.max_cpb;
+        mConstraints.maxDpb = h264Params.max_dpb;
+        mConstraints.maxBr = h264Params.max_br;
         mSpropParameterSets = h264Params.sprop_parameter_sets;
         // Only do this if we didn't symmetrically negotiate above
         if (h264Params.level_asymmetry_allowed) {
           SetSaneH264Level(GetSaneH264Level(h264Params.profile_level_id),
                            &mProfileLevelId);
         }
       } else {
         // TODO(bug 1143709): max-recv-level support
       }
 
     } else if (mName == "VP8" || mName == "VP9") {
       if (mDirection == sdp::kSend) {
         SdpFmtpAttributeList::VP8Parameters vp8Params(
             GetVP8Parameters(mDefaultPt, remoteMsection));
 
-        mMaxFs = vp8Params.max_fs;
-        mMaxFr = vp8Params.max_fr;
+        mConstraints.maxFs = vp8Params.max_fs;
+        mConstraints.maxFps = vp8Params.max_fr;
       }
     }
 
     NegotiateRtcpFb(remoteMsection);
     return true;
   }
 
   // Maps the not-so-sane encoding of H264 level into something that is
@@ -548,26 +544,19 @@ class JsepVideoCodecDescription : public
   }
 
   JSEP_CODEC_CLONE(JsepVideoCodecDescription)
 
   std::vector<std::string> mAckFbTypes;
   std::vector<std::string> mNackFbTypes;
   std::vector<std::string> mCcmFbTypes;
 
-  uint32_t mMaxFs;
-
   // H264-specific stuff
   uint32_t mProfileLevelId;
-  uint32_t mMaxFr;
   uint32_t mPacketizationMode;
-  uint32_t mMaxMbps;
-  uint32_t mMaxCpb;
-  uint32_t mMaxDpb;
-  uint32_t mMaxBr;
   std::string mSpropParameterSets;
 };
 
 class JsepApplicationCodecDescription : public JsepCodecDescription {
  public:
   JsepApplicationCodecDescription(const std::string& defaultPt,
                                   const std::string& name,
                                   uint16_t channels,
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
@@ -2058,28 +2058,28 @@ JsepSessionImpl::SetupDefaultCodecs()
 
   // Supported video codecs.
   JsepVideoCodecDescription* vp8 = new JsepVideoCodecDescription(
       "120",
       "VP8",
       90000
       );
   // Defaults for mandatory params
-  vp8->mMaxFs = 12288;
-  vp8->mMaxFr = 60;
+  vp8->mConstraints.maxFs = 12288; // Enough for 2048x1536
+  vp8->mConstraints.maxFps = 60;
   mSupportedCodecs.values.push_back(vp8);
 
   JsepVideoCodecDescription* vp9 = new JsepVideoCodecDescription(
       "121",
       "VP9",
       90000
       );
   // Defaults for mandatory params
-  vp9->mMaxFs = 12288;
-  vp9->mMaxFr = 60;
+  vp9->mConstraints.maxFs = 12288; // Enough for 2048x1536
+  vp9->mConstraints.maxFps = 60;
   mSupportedCodecs.values.push_back(vp9);
 
   JsepVideoCodecDescription* h264_1 = new JsepVideoCodecDescription(
       "126",
       "H264",
       90000
       );
   h264_1->mPacketizationMode = 1;
--- a/media/webrtc/signaling/src/jsep/JsepTrack.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepTrack.cpp
@@ -1,27 +1,35 @@
 /* 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/jsep/JsepTrack.h"
 #include "signaling/src/jsep/JsepCodecDescription.h"
+#include "signaling/src/jsep/JsepTrackEncoding.h"
 
 #include <algorithm>
 
 namespace mozilla
 {
 void
 JsepTrack::GetNegotiatedPayloadTypes(std::vector<uint16_t>* payloadTypes)
 {
   if (!mNegotiatedDetails) {
     return;
   }
 
-  GetPayloadTypes(mNegotiatedDetails->mCodecs.values, payloadTypes);
+  for (const auto* encoding : mNegotiatedDetails->mEncodings.values) {
+    GetPayloadTypes(encoding->GetCodecs(), payloadTypes);
+  }
+
+  // Prune out dupes
+  std::sort(payloadTypes->begin(), payloadTypes->end());
+  auto newEnd = std::unique(payloadTypes->begin(), payloadTypes->end());
+  payloadTypes->erase(newEnd, payloadTypes->end());
 }
 
 /* static */
 void
 JsepTrack::GetPayloadTypes(
     const std::vector<JsepCodecDescription*>& codecs,
     std::vector<uint16_t>* payloadTypes)
 {
@@ -96,18 +104,18 @@ JsepTrack::AddToOffer(SdpMediaSection* o
 {
   AddToMsection(mPrototypeCodecs.values, offer);
 }
 
 void
 JsepTrack::AddToAnswer(const SdpMediaSection& offer,
                        SdpMediaSection* answer) const
 {
-  // We do not modify mPrototypeCodecs here, since we're only creating an answer. Once
-  // offer/answer concludes, we will update mPrototypeCodecs.
+  // We do not modify mPrototypeCodecs here, since we're only creating an
+  // answer. Once offer/answer concludes, we will update mPrototypeCodecs.
   PtrVector<JsepCodecDescription> codecs;
   codecs.values = GetCodecClones();
   NegotiateCodecs(offer, &codecs.values);
   if (codecs.values.empty()) {
     return;
   }
 
   AddToMsection(codecs.values, answer);
@@ -130,16 +138,75 @@ JsepTrack::AddToMsection(const std::vect
       msection->AddMsid(mStreamId, mTrackId);
     }
     msection->SetSending(true);
   } else {
     msection->SetReceiving(true);
   }
 }
 
+void
+JsepTrack::GetRids(const SdpMediaSection& msection,
+                   std::vector<SdpRidAttributeList::Rid>* rids) const
+{
+  // TODO(bug 1192390): Get list of rids from |answer|; first rid from each
+  // simulcast version.
+}
+
+void
+JsepTrack::UpdateRidsFromAnswer(
+    const std::vector<SdpRidAttributeList::Rid>& rids)
+{
+  // TODO(bug 1192390): For each rid, try to pair it with something in
+  // mEncodingParameters (either by matching rid, or just matching by index).
+  // Once these are paired up, update the ids in mEncodingParameters to match.
+}
+
+void
+JsepTrack::CreateEncodings(
+    const SdpMediaSection& answer,
+    const std::vector<JsepCodecDescription*>& negotiatedCodecs,
+    JsepTrackNegotiatedDetails* negotiatedDetails)
+{
+  std::vector<SdpRidAttributeList::Rid> answerRids;
+  GetRids(answer, &answerRids);
+  UpdateRidsFromAnswer(answerRids);
+  if (answerRids.empty()) {
+    // Add dummy value with an empty id to make sure we get a single unicast
+    // stream.
+    answerRids.push_back(SdpRidAttributeList::Rid());
+  }
+
+  // For each rid in the answer, make sure we have an encoding, and configure
+  // that encoding appropriately.
+  for (size_t i = 0; i < answerRids.size(); ++i) {
+    if (i >= negotiatedDetails->mEncodings.values.size()) {
+      negotiatedDetails->mEncodings.values.push_back(new JsepTrackEncoding);
+    }
+
+    JsepTrackEncoding* encoding = negotiatedDetails->mEncodings.values[i];
+
+    for (const JsepCodecDescription* codec : negotiatedCodecs) {
+      if (answerRids[i].HasFormat(codec->mDefaultPt)) {
+        encoding->AddCodec(*codec);
+      }
+    }
+
+    encoding->mRid = answerRids[i].id;
+    // If we end up supporting params for rid, we would handle that here.
+
+    // Incorporate the corresponding JS encoding constraints, if they exist
+    for (const JsConstraints& jsConstraints : mJsEncodeConstraints) {
+      if (jsConstraints.id == answerRids[i].id) {
+        encoding->mConstraints = jsConstraints.constraints;
+      }
+    }
+  }
+}
+
 std::vector<JsepCodecDescription*>
 JsepTrack::GetCodecClones() const
 {
   std::vector<JsepCodecDescription*> clones;
   for (const JsepCodecDescription* codec : mPrototypeCodecs.values) {
     clones.push_back(codec->Clone());
   }
   return clones;
@@ -204,23 +271,22 @@ JsepTrack::NegotiateCodecs(
     codecs->resize(1);
   }
 }
 
 void
 JsepTrack::Negotiate(const SdpMediaSection& answer,
                      const SdpMediaSection& remote)
 {
-  UniquePtr<JsepTrackNegotiatedDetails> negotiatedDetails =
-      MakeUnique<JsepTrackNegotiatedDetails>();
+  PtrVector<JsepCodecDescription> negotiatedCodecs;
+  negotiatedCodecs.values = GetCodecClones();
 
-  negotiatedDetails->mCodecs.values = GetCodecClones();
   std::map<std::string, std::string> formatChanges;
   NegotiateCodecs(remote,
-                  &negotiatedDetails->mCodecs.values,
+                  &negotiatedCodecs.values,
                   &answer,
                   &formatChanges);
 
   // Use |formatChanges| to update mPrototypeCodecs
   size_t insertPos = 0;
   for (size_t i = 0; i < mPrototypeCodecs.values.size(); ++i) {
     if (formatChanges.count(mPrototypeCodecs.values[i]->mDefaultPt)) {
       // Update the payload type to what was negotiated
@@ -230,16 +296,21 @@ JsepTrack::Negotiate(const SdpMediaSecti
       std::swap(mPrototypeCodecs.values[insertPos],
                 mPrototypeCodecs.values[i]);
       ++insertPos;
     }
   }
 
   EnsureNoDuplicatePayloadTypes(&mPrototypeCodecs.values);
 
+  UniquePtr<JsepTrackNegotiatedDetails> negotiatedDetails =
+      MakeUnique<JsepTrackNegotiatedDetails>();
+
+  CreateEncodings(answer, negotiatedCodecs.values, negotiatedDetails.get());
+
   if (answer.GetAttributeList().HasAttribute(SdpAttribute::kExtmapAttribute)) {
     for (auto& extmapAttr : answer.GetAttributeList().GetExtmap().mExtmaps) {
       negotiatedDetails->mExtmap[extmapAttr.extensionname] = extmapAttr;
     }
   }
 
   if ((mDirection == sdp::kRecv) &&
       remote.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) {
--- a/media/webrtc/signaling/src/jsep/JsepTrack.h
+++ b/media/webrtc/signaling/src/jsep/JsepTrack.h
@@ -11,40 +11,38 @@
 
 #include <mozilla/RefPtr.h>
 #include <mozilla/UniquePtr.h>
 #include <mozilla/Maybe.h>
 #include "nsISupportsImpl.h"
 #include "nsError.h"
 
 #include "signaling/src/jsep/JsepTransport.h"
+#include "signaling/src/jsep/JsepTrackEncoding.h"
 #include "signaling/src/sdp/Sdp.h"
 #include "signaling/src/sdp/SdpAttribute.h"
 #include "signaling/src/sdp/SdpMediaSection.h"
 #include "signaling/src/common/PtrVector.h"
 
 namespace mozilla {
 
-// Forward reference.
-class JsepCodecDescription;
-
 class JsepTrackNegotiatedDetails
 {
 public:
   size_t
-  GetCodecCount() const
+  GetEncodingCount() const
   {
-    return mCodecs.values.size();
+    return mEncodings.values.size();
   }
 
-  const JsepCodecDescription*
-  GetCodec(size_t index) const
+  const JsepTrackEncoding&
+  GetEncoding(size_t index) const
   {
-    MOZ_RELEASE_ASSERT(index < mCodecs.values.size());
-    return mCodecs.values[index];
+    MOZ_RELEASE_ASSERT(index < mEncodings.values.size());
+    return *mEncodings.values[index];
   }
 
   const SdpExtmapAttributeList::Extmap*
   GetExt(const std::string& ext_name) const
   {
     auto it = mExtmap.find(ext_name);
     if (it != mExtmap.end()) {
       return &it->second;
@@ -57,17 +55,17 @@ public:
     return mUniquePayloadTypes;
   }
 
 private:
   friend class JsepTrack;
 
   std::map<std::string, SdpExtmapAttributeList::Extmap> mExtmap;
   std::vector<uint8_t> mUniquePayloadTypes;
-  PtrVector<JsepCodecDescription> mCodecs;
+  PtrVector<JsepTrackEncoding> mEncodings;
 };
 
 class JsepTrack
 {
 public:
   JsepTrack(mozilla::SdpMediaSection::MediaType type,
             const std::string& streamid,
             const std::string& trackid,
@@ -175,26 +173,33 @@ public:
   }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(JsepTrack);
 
 protected:
   virtual ~JsepTrack() {}
 
 private:
-  virtual std::vector<JsepCodecDescription*> GetCodecClones() const;
+  std::vector<JsepCodecDescription*> GetCodecClones() const;
   static void EnsureNoDuplicatePayloadTypes(
       std::vector<JsepCodecDescription*>* codecs);
   static void GetPayloadTypes(
       const std::vector<JsepCodecDescription*>& codecs,
       std::vector<uint16_t>* pts);
   static void EnsurePayloadTypeIsUnique(std::set<uint16_t>* uniquePayloadTypes,
                                         JsepCodecDescription* codec);
-  virtual void AddToMsection(const std::vector<JsepCodecDescription*>& codecs,
-                             SdpMediaSection* msection) const;
+  void AddToMsection(const std::vector<JsepCodecDescription*>& codecs,
+                     SdpMediaSection* msection) const;
+  void GetRids(const SdpMediaSection& msection,
+               std::vector<SdpRidAttributeList::Rid>* rids) const;
+  void UpdateRidsFromAnswer(const std::vector<SdpRidAttributeList::Rid>& rids);
+  void CreateEncodings(
+      const SdpMediaSection& answer,
+      const std::vector<JsepCodecDescription*>& negotiatedCodecs,
+      JsepTrackNegotiatedDetails* details);
 
   // |answer| is set when performing the final negotiation on completion of
   // offer/answer, and is used to update the formats in |codecs|, since the
   // answer is authoritative. |formatChanges| is also set on completion of
   // offer/answer, and records how the formats in |codecs| were changed, which
   // is used by |Negotiate| to update |mPrototypeCodecs|.
   virtual void NegotiateCodecs(
       const SdpMediaSection& remote,
@@ -203,16 +208,25 @@ private:
       std::map<std::string, std::string>* formatChanges = nullptr) const;
 
   const mozilla::SdpMediaSection::MediaType mType;
   std::string mStreamId;
   std::string mTrackId;
   std::string mCNAME;
   const sdp::Direction mDirection;
   PtrVector<JsepCodecDescription> mPrototypeCodecs;
+  struct JsConstraints
+  {
+    std::string id;
+    EncodingConstraints constraints;
+  };
+  // Holds encoding params/constraints from JS. Simulcast happens when there are
+  // multiple of these. If there are none, we assume unconstrained unicast with
+  // no rid.
+  std::vector<JsConstraints> mJsEncodeConstraints;
   UniquePtr<JsepTrackNegotiatedDetails> mNegotiatedDetails;
   std::vector<uint32_t> mSsrcs;
 };
 
 // Need a better name for this.
 struct JsepTrackPair {
   size_t mLevel;
   // Is this track pair sharing a transport with another?
new file mode 100644
--- /dev/null
+++ b/media/webrtc/signaling/src/jsep/JsepTrackEncoding.h
@@ -0,0 +1,50 @@
+/* -*- 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 _JESPTRACKENCODING_H_
+#define _JESPTRACKENCODING_H_
+
+#include "signaling/src/jsep/JsepCodecDescription.h"
+#include "signaling/src/common/EncodingConstraints.h"
+#include "signaling/src/common/PtrVector.h"
+
+namespace mozilla {
+// Represents a single encoding of a media track. When simulcast is used, there
+// may be multiple. Each encoding may have some constraints (imposed by JS), and
+// may be able to use any one of multiple codecs (JsepCodecDescription) at any
+// given time.
+class JsepTrackEncoding
+{
+public:
+  const std::vector<JsepCodecDescription*>& GetCodecs() const
+  {
+    return mCodecs.values;
+  }
+
+  void AddCodec(const JsepCodecDescription& codec)
+  {
+    mCodecs.values.push_back(codec.Clone());
+  }
+
+  bool HasFormat(const std::string& format) const
+  {
+    for (const JsepCodecDescription* codec : mCodecs.values) {
+      if (codec->mDefaultPt == format) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  EncodingConstraints mConstraints;
+  std::string mRid;
+
+private:
+  PtrVector<JsepCodecDescription> mCodecs;
+};
+}
+
+#endif // _JESPTRACKENCODING_H_
--- a/media/webrtc/signaling/src/media-conduit/CodecConfig.h
+++ b/media/webrtc/signaling/src/media-conduit/CodecConfig.h
@@ -4,16 +4,18 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef CODEC_CONFIG_H_
 #define CODEC_CONFIG_H_
 
 #include <string>
 #include <vector>
 
+#include "signaling/src/common/EncodingConstraints.h"
+
 namespace mozilla {
 
 /**
  * Minimalistic Audio Codec Config Params
  */
 struct AudioCodecConfig
 {
   /*
@@ -52,21 +54,16 @@ struct AudioCodecConfig
 #define    MAX_SPROP_LEN    128
 
 // used for holding SDP negotiation results
 struct VideoCodecConfigH264
 {
     char       sprop_parameter_sets[MAX_SPROP_LEN];
     int        packetization_mode;
     int        profile_level_id;
-    int        max_mbps;
-    int        max_fs;
-    int        max_cpb;
-    int        max_dpb;
-    int        max_br;
     int        tias_bw;
 };
 
 
 // class so the std::strings can get freed more easily/reliably
 class VideoCodecConfig
 {
 public:
@@ -76,50 +73,37 @@ public:
    */
   int mType; // payload type
   std::string mName;
 
   std::vector<std::string> mAckFbTypes;
   std::vector<std::string> mNackFbTypes;
   std::vector<std::string> mCcmFbTypes;
 
-  unsigned int mMaxFrameSize;
-  unsigned int mMaxFrameRate;
-  unsigned int mMaxMBPS;    // in macroblocks-per-second
-  unsigned int mMaxBitrate;
-  // max_cpb & max_dpb would be streaming/mode-2 only
+  EncodingConstraints mEncodingConstraints;
   std::string mSpropParameterSets;
   uint8_t mProfile;
   uint8_t mConstraints;
   uint8_t mLevel;
   uint8_t mPacketizationMode;
   // TODO: add external negotiated SPS/PPS
 
   VideoCodecConfig(int type,
                    std::string name,
-                   unsigned int max_fs = 0,
-                   unsigned int max_fr = 0,
+                   const EncodingConstraints& constraints,
                    const struct VideoCodecConfigH264 *h264 = nullptr) :
     mType(type),
     mName(name),
-    mMaxFrameSize(max_fs), // may be overridden
-    mMaxFrameRate(max_fr),
-    mMaxMBPS(0),
-    mMaxBitrate(0),
+    mEncodingConstraints(constraints),
     mProfile(0x42),
     mConstraints(0xE0),
     mLevel(0x0C),
     mPacketizationMode(1)
   {
     if (h264) {
-      if (max_fs == 0 || (h264->max_fs != 0 && (unsigned int) h264->max_fs < max_fs)) {
-        mMaxFrameSize = h264->max_fs;
-      }
-      mMaxMBPS = h264->max_mbps;
-      mMaxBitrate = h264->max_br;
       mProfile = (h264->profile_level_id & 0x00FF0000) >> 16;
       mConstraints = (h264->profile_level_id & 0x0000FF00) >> 8;
       mLevel = (h264->profile_level_id & 0x000000FF);
       mPacketizationMode = h264->packetization_mode;
       mSpropParameterSets = h264->sprop_parameter_sets;
     }
   }
 
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -1019,94 +1019,106 @@ SelectBandwidth(webrtc::VideoCodec& vie_
   } else {
     // At low framerates, don't reduce bandwidth as much - cut slope to 1/2.
     // Mostly this would be ultra-low-light situations/mobile or screensharing.
     vie_codec.minBitrate = vie_codec.minBitrate * ((10-(framerate/2))/30);
     vie_codec.maxBitrate = vie_codec.maxBitrate * ((10-(framerate/2))/30);
   }
 }
 
+static void ConstrainPreservingAspectRatio(uint16_t max_width,
+                                           uint16_t max_height,
+                                           unsigned short* width,
+                                           unsigned short* height)
+{
+  if (((*width) <= max_width) && ((*height) <= max_height)) {
+    return;
+  }
+
+  if ((*width) * max_height > max_width * (*height))
+  {
+    (*height) = max_width * (*height) / (*width);
+    (*width) = max_width;
+  }
+  else
+  {
+    (*width) = max_height * (*width) / (*height);
+    (*height) = max_height;
+  }
+}
+
 // XXX we need to figure out how to feed back changes in preferred capture
 // resolution to the getUserMedia source.
 // Returns boolean if we've submitted an async change (and took ownership
 // of *frame's data)
 bool
 WebrtcVideoConduit::SelectSendResolution(unsigned short width,
                                          unsigned short height,
                                          webrtc::I420VideoFrame *frame) // may be null
 {
   mCodecMutex.AssertCurrentThreadOwns();
   // XXX This will do bandwidth-resolution adaptation as well - bug 877954
 
   mLastWidth = width;
   mLastHeight = height;
-  // Limit resolution to max-fs while keeping same aspect ratio as the
-  // incoming image.
-  if (mCurSendCodecConfig && mCurSendCodecConfig->mMaxFrameSize)
-  {
-    unsigned int cur_fs, max_width, max_height, mb_width, mb_height, mb_max;
-
-    mb_width = (width + 15) >> 4;
-    mb_height = (height + 15) >> 4;
-
-    cur_fs = mb_width * mb_height;
-
-    // Limit resolution to max_fs, but don't scale up.
-    if (cur_fs > mCurSendCodecConfig->mMaxFrameSize)
-    {
-      double scale_ratio;
-
-      scale_ratio = sqrt((double) mCurSendCodecConfig->mMaxFrameSize /
-                         (double) cur_fs);
-
-      mb_width = mb_width * scale_ratio;
-      mb_height = mb_height * scale_ratio;
-
-      // Adjust mb_width and mb_height if they were truncated to zero.
-      if (mb_width == 0) {
-        mb_width = 1;
-        mb_height = std::min(mb_height, mCurSendCodecConfig->mMaxFrameSize);
-      }
-      if (mb_height == 0) {
-        mb_height = 1;
-        mb_width = std::min(mb_width, mCurSendCodecConfig->mMaxFrameSize);
-      }
+  // Enforce constraints
+  if (mCurSendCodecConfig) {
+    uint16_t max_width = mCurSendCodecConfig->mEncodingConstraints.maxWidth;
+    uint16_t max_height = mCurSendCodecConfig->mEncodingConstraints.maxHeight;
+    if (max_width || max_height) {
+      max_width = max_width ? max_width : UINT16_MAX;
+      max_height = max_height ? max_height : UINT16_MAX;
+      ConstrainPreservingAspectRatio(max_width, max_height, &width, &height);
     }
 
-    // Limit width/height seperately to limit effect of extreme aspect ratios.
-    mb_max = (unsigned) sqrt(8 * (double) mCurSendCodecConfig->mMaxFrameSize);
-
-    max_width = 16 * std::min(mb_width, mb_max);
-    max_height = 16 * std::min(mb_height, mb_max);
-
-    if (width * max_height > max_width * height)
+    // Limit resolution to max-fs while keeping same aspect ratio as the
+    // incoming image.
+    if (mCurSendCodecConfig->mEncodingConstraints.maxFs)
     {
-      if (width > max_width)
+      uint32_t max_fs = mCurSendCodecConfig->mEncodingConstraints.maxFs;
+      unsigned int cur_fs, mb_width, mb_height, mb_max;
+
+      // Could we make this simpler by picking the larger of width and height,
+      // calculating a max for just that value based on the scale parameter,
+      // and then let ConstrainPreservingAspectRatio do the rest?
+      mb_width = (width + 15) >> 4;
+      mb_height = (height + 15) >> 4;
+
+      cur_fs = mb_width * mb_height;
+
+      // Limit resolution to max_fs, but don't scale up.
+      if (cur_fs > max_fs)
       {
-        // Due to the value is truncated to integer here and forced to even
-        // value later, adding 1 to improve accuracy.
-        height = max_width * height / width + 1;
-        width = max_width;
+        double scale_ratio;
+
+        scale_ratio = sqrt((double) max_fs / (double) cur_fs);
+
+        mb_width = mb_width * scale_ratio;
+        mb_height = mb_height * scale_ratio;
+
+        // Adjust mb_width and mb_height if they were truncated to zero.
+        if (mb_width == 0) {
+          mb_width = 1;
+          mb_height = std::min(mb_height, max_fs);
+        }
+        if (mb_height == 0) {
+          mb_height = 1;
+          mb_width = std::min(mb_width, max_fs);
+        }
       }
+
+      // Limit width/height seperately to limit effect of extreme aspect ratios.
+      mb_max = (unsigned) sqrt(8 * (double) max_fs);
+
+      max_width = 16 * std::min(mb_width, mb_max);
+      max_height = 16 * std::min(mb_height, mb_max);
+      ConstrainPreservingAspectRatio(max_width, max_height, &width, &height);
     }
-    else
-    {
-      if (height > max_height)
-      {
-        // Due to the value is truncated to integer here and forced to even
-        // value later, adding 1 to improve accuracy.
-        width = max_height * width / height + 1;
-        height = max_height;
-      }
-    }
+  }
 
-    // Favor even multiples of pixels for width and height.
-    width = std::max(width & ~1, 2);
-    height = std::max(height & ~1, 2);
-  }
 
   // Adapt to getUserMedia resolution changes
   // check if we need to reconfigure the sending resolution.
   bool changed = false;
   if (mSendingWidth != width || mSendingHeight != height)
   {
     // This will avoid us continually retrying this operation if it fails.
     // If the resolution changes, we'll try again.  In the meantime, we'll
@@ -1216,32 +1228,32 @@ WebrtcVideoConduit::ReconfigureSendCodec
 // Invoked under lock of mCodecMutex!
 unsigned int
 WebrtcVideoConduit::SelectSendFrameRate(unsigned int framerate) const
 {
   mCodecMutex.AssertCurrentThreadOwns();
   unsigned int new_framerate = framerate;
 
   // Limit frame rate based on max-mbps
-  if (mCurSendCodecConfig && mCurSendCodecConfig->mMaxMBPS)
+  if (mCurSendCodecConfig && mCurSendCodecConfig->mEncodingConstraints.maxMbps)
   {
     unsigned int cur_fs, mb_width, mb_height, max_fps;
 
     mb_width = (mSendingWidth + 15) >> 4;
     mb_height = (mSendingHeight + 15) >> 4;
 
     cur_fs = mb_width * mb_height;
-    max_fps = mCurSendCodecConfig->mMaxMBPS/cur_fs;
+    max_fps = mCurSendCodecConfig->mEncodingConstraints.maxMbps/cur_fs;
     if (max_fps < mSendingFramerate) {
       new_framerate = max_fps;
     }
 
-    if (mCurSendCodecConfig->mMaxFrameRate != 0 &&
-      mCurSendCodecConfig->mMaxFrameRate < mSendingFramerate) {
-      new_framerate = mCurSendCodecConfig->mMaxFrameRate;
+    if (mCurSendCodecConfig->mEncodingConstraints.maxFps != 0 &&
+      mCurSendCodecConfig->mEncodingConstraints.maxFps < mSendingFramerate) {
+      new_framerate = mCurSendCodecConfig->mEncodingConstraints.maxFps;
     }
   }
   return new_framerate;
 }
 
 MediaConduitErrorCode
 WebrtcVideoConduit::SetExternalSendCodec(VideoCodecConfig* config,
                                          VideoEncoder* encoder) {
@@ -1669,18 +1681,18 @@ WebrtcVideoConduit::CodecConfigToWebRTCC
     PL_strncpyz(cinst.plName, "I420", sizeof(cinst.plName));
   } else {
     cinst.codecType = webrtc::kVideoCodecUnknown;
     PL_strncpyz(cinst.plName, "Unknown", sizeof(cinst.plName));
   }
 
   // width/height will be overridden on the first frame; they must be 'sane' for
   // SetSendCodec()
-  if (codecInfo->mMaxFrameRate > 0) {
-    cinst.maxFramerate = codecInfo->mMaxFrameRate;
+  if (codecInfo->mEncodingConstraints.maxFps > 0) {
+    cinst.maxFramerate = codecInfo->mEncodingConstraints.maxFps;
   } else {
     cinst.maxFramerate = DEFAULT_VIDEO_MAX_FRAMERATE;
   }
 
   cinst.minBitrate = mMinBitrate;
   cinst.startBitrate = mStartBitrate;
   cinst.maxBitrate = mMaxBitrate;
 
@@ -1689,20 +1701,21 @@ WebrtcVideoConduit::CodecConfigToWebRTCC
 #ifdef MOZ_WEBRTC_OMX
     cinst.resolution_divisor = 16;
 #endif
     // cinst.codecSpecific.H264.profile = ?
     cinst.codecSpecific.H264.profile_byte = codecInfo->mProfile;
     cinst.codecSpecific.H264.constraints = codecInfo->mConstraints;
     cinst.codecSpecific.H264.level = codecInfo->mLevel;
     cinst.codecSpecific.H264.packetizationMode = codecInfo->mPacketizationMode;
-    if (codecInfo->mMaxBitrate > 0 && codecInfo->mMaxBitrate < cinst.maxBitrate) {
-      cinst.maxBitrate = codecInfo->mMaxBitrate;
+    if (codecInfo->mEncodingConstraints.maxBr > 0) {
+      cinst.maxBitrate = std::min(cinst.maxBitrate,
+                                  codecInfo->mEncodingConstraints.maxBr);
     }
-    if (codecInfo->mMaxMBPS > 0) {
+    if (codecInfo->mEncodingConstraints.maxMbps > 0) {
       // Not supported yet!
       CSFLogError(logTag,  "%s H.264 max_mbps not supported yet  ", __FUNCTION__);
     }
     // XXX parse the encoded SPS/PPS data
     // paranoia
     cinst.codecSpecific.H264.spsData = nullptr;
     cinst.codecSpecific.H264.spsLen = 0;
     cinst.codecSpecific.H264.ppsData = nullptr;
@@ -1725,18 +1738,17 @@ WebrtcVideoConduit::CheckCodecsForMatch(
 {
   if(!curCodecConfig)
   {
     return false;
   }
 
   if(curCodecConfig->mType  == codecInfo->mType &&
      curCodecConfig->mName.compare(codecInfo->mName) == 0 &&
-     curCodecConfig->mMaxFrameSize == codecInfo->mMaxFrameSize &&
-     curCodecConfig->mMaxFrameRate == codecInfo->mMaxFrameRate)
+     curCodecConfig->mEncodingConstraints == codecInfo->mEncodingConstraints)
   {
     return true;
   }
 
   return false;
 }
 
 /**
@@ -1800,18 +1812,18 @@ WebrtcVideoConduit::ValidateCodecConfig(
 
 void
 WebrtcVideoConduit::DumpCodecDB() const
 {
   for(std::vector<VideoCodecConfig*>::size_type i=0;i<mRecvCodecList.size();i++)
   {
     CSFLogDebug(logTag,"Payload Name: %s", mRecvCodecList[i]->mName.c_str());
     CSFLogDebug(logTag,"Payload Type: %d", mRecvCodecList[i]->mType);
-    CSFLogDebug(logTag,"Payload Max Frame Size: %d", mRecvCodecList[i]->mMaxFrameSize);
-    CSFLogDebug(logTag,"Payload Max Frame Rate: %d", mRecvCodecList[i]->mMaxFrameRate);
+    CSFLogDebug(logTag,"Payload Max Frame Size: %d", mRecvCodecList[i]->mEncodingConstraints.maxFs);
+    CSFLogDebug(logTag,"Payload Max Frame Rate: %d", mRecvCodecList[i]->mEncodingConstraints.maxFps);
   }
 }
 
 void
 WebrtcVideoConduit::VideoLatencyUpdate(uint64_t newSample)
 {
   mVideoLatencyAvg = (sRoundingPadding * newSample + sAlphaNum * mVideoLatencyAvg) / sAlphaDen;
 }
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.h
@@ -248,24 +248,24 @@ public:
   }
 
   unsigned short SendingHeight() override {
     return mSendingHeight;
   }
 
   unsigned int SendingMaxFs() override {
     if(mCurSendCodecConfig) {
-      return mCurSendCodecConfig->mMaxFrameSize;
+      return mCurSendCodecConfig->mEncodingConstraints.maxFs;
     }
     return 0;
   }
 
   unsigned int SendingMaxFr() override {
     if(mCurSendCodecConfig) {
-      return mCurSendCodecConfig->mMaxFrameRate;
+      return mCurSendCodecConfig->mEncodingConstraints.maxFps;
     }
     return 0;
   }
 
   WebrtcVideoConduit();
   virtual ~WebrtcVideoConduit();
 
   MediaConduitErrorCode InitMain();
--- a/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
+++ b/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
@@ -73,16 +73,42 @@ JsepCodecDescToCodecConfig(const JsepCod
                                   desc.mClock,
                                   desc.mPacketSize,
                                   desc.mChannels,
                                   desc.mBitrate);
 
   return NS_OK;
 }
 
+static std::vector<JsepCodecDescription*>
+GetCodecs(const JsepTrackNegotiatedDetails& aDetails)
+{
+  // We do not try to handle cases where a codec is not used on the primary
+  // encoding.
+  if (aDetails.GetEncodingCount()) {
+    return aDetails.GetEncoding(0).GetCodecs();
+  }
+  return std::vector<JsepCodecDescription*>();
+}
+
+static nsresult
+NegotiatedDetailsToAudioCodecConfigs(const JsepTrackNegotiatedDetails& aDetails,
+                                     PtrVector<AudioCodecConfig>* aConfigs)
+{
+  std::vector<JsepCodecDescription*> codecs(GetCodecs(aDetails));
+  for (const JsepCodecDescription* codec : codecs) {
+    AudioCodecConfig* config;
+    if (NS_FAILED(JsepCodecDescToCodecConfig(*codec, &config))) {
+      return NS_ERROR_INVALID_ARG;
+    }
+    aConfigs->values.push_back(config);
+  }
+  return NS_OK;
+}
+
 static nsresult
 JsepCodecDescToCodecConfig(const JsepCodecDescription& aCodec,
                            VideoCodecConfig** aConfig)
 {
   MOZ_ASSERT(aCodec.mType == SdpMediaSection::kVideo);
   if (aCodec.mType != SdpMediaSection::kVideo) {
     MOZ_ASSERT(false, "JsepCodecDescription has wrong type");
     return NS_ERROR_INVALID_ARG;
@@ -104,36 +130,53 @@ JsepCodecDescToCodecConfig(const JsepCod
     h264Config = new VideoCodecConfigH264;
     size_t spropSize = sizeof(h264Config->sprop_parameter_sets);
     strncpy(h264Config->sprop_parameter_sets,
             desc.mSpropParameterSets.c_str(),
             spropSize);
     h264Config->sprop_parameter_sets[spropSize - 1] = '\0';
     h264Config->packetization_mode = desc.mPacketizationMode;
     h264Config->profile_level_id = desc.mProfileLevelId;
-    h264Config->max_mbps = desc.mMaxMbps;
-    h264Config->max_fs = desc.mMaxFs;
-    h264Config->max_cpb = desc.mMaxCpb;
-    h264Config->max_dpb = desc.mMaxDpb;
-    h264Config->max_br = desc.mMaxBr;
     h264Config->tias_bw = 0; // TODO. Issue 165.
   }
 
   VideoCodecConfig* configRaw;
   configRaw = new VideoCodecConfig(
-      pt, desc.mName, desc.mMaxFs, desc.mMaxFr, h264Config);
+      pt, desc.mName, desc.mConstraints, h264Config);
 
   configRaw->mAckFbTypes = desc.mAckFbTypes;
   configRaw->mNackFbTypes = desc.mNackFbTypes;
   configRaw->mCcmFbTypes = desc.mCcmFbTypes;
 
   *aConfig = configRaw;
   return NS_OK;
 }
 
+static nsresult
+NegotiatedDetailsToVideoCodecConfigs(const JsepTrackNegotiatedDetails& aDetails,
+                                     PtrVector<VideoCodecConfig>* aConfigs)
+{
+  std::vector<JsepCodecDescription*> codecs(GetCodecs(aDetails));
+  for (const JsepCodecDescription* codec : codecs) {
+    VideoCodecConfig* config;
+    if (NS_FAILED(JsepCodecDescToCodecConfig(*codec, &config))) {
+      return NS_ERROR_INVALID_ARG;
+    }
+
+    for (size_t i = 0; i < aDetails.GetEncodingCount(); ++i) {
+      if (aDetails.GetEncoding(i).HasFormat(codec->mDefaultPt)) {
+        // TODO(bug 1192390): Roll constraints into simulcast entries
+      }
+    }
+    aConfigs->values.push_back(config);
+  }
+
+  return NS_OK;
+}
+
 // Accessing the PCMedia should be safe here because we shouldn't
 // have enqueued this function unless it was still active and
 // the ICE data is destroyed on the STS.
 static void
 FinalizeTransportFlow_s(RefPtr<PeerConnectionMedia> aPCMedia,
                         RefPtr<TransportFlow> aFlow, size_t aLevel,
                         bool aIsRtcp,
                         nsAutoPtr<PtrVector<TransportLayer> > aLayerList)
@@ -563,25 +606,16 @@ MediaPipelineFactory::CreateMediaPipelin
     MOZ_MTLOG(ML_ERROR, "Couldn't store receiving pipeline " <<
                         static_cast<unsigned>(rv));
     return rv;
   }
 
   return NS_OK;
 }
 
-static const JsepCodecDescription*
-GetBestCodec(const JsepTrackNegotiatedDetails& details)
-{
-  if (details.GetCodecCount()) {
-    return details.GetCodec(0);
-  }
-  return nullptr;
-}
-
 nsresult
 MediaPipelineFactory::GetOrCreateAudioConduit(
     const JsepTrackPair& aTrackPair,
     const JsepTrack& aTrack,
     RefPtr<MediaSessionConduit>* aConduitp)
 {
 
   if (!aTrack.GetNegotiatedDetails()) {
@@ -599,37 +633,32 @@ MediaPipelineFactory::GetOrCreateAudioCo
     if (!conduit) {
       MOZ_MTLOG(ML_ERROR, "Could not create audio conduit");
       return NS_ERROR_FAILURE;
     }
 
     mPCMedia->AddAudioConduit(aTrackPair.mLevel, conduit);
   }
 
-  if (!GetBestCodec(*aTrack.GetNegotiatedDetails())) {
+  PtrVector<AudioCodecConfig> configs;
+  nsresult rv = NegotiatedDetailsToAudioCodecConfigs(
+      *aTrack.GetNegotiatedDetails(), &configs);
+
+  if (NS_FAILED(rv)) {
+    MOZ_MTLOG(ML_ERROR, "Failed to convert JsepCodecDescriptions to "
+                        "AudioCodecConfigs.");
+    return rv;
+  }
+
+  if (configs.values.empty()) {
     MOZ_MTLOG(ML_ERROR, "Can't set up a conduit with 0 codecs");
     return NS_ERROR_FAILURE;
   }
 
-  size_t numCodecs = aTrack.GetNegotiatedDetails()->GetCodecCount();
   if (receiving) {
-    PtrVector<AudioCodecConfig> configs;
-
-    for (size_t i = 0; i < numCodecs; i++) {
-      const JsepCodecDescription* cdesc =
-        aTrack.GetNegotiatedDetails()->GetCodec(i);
-
-      AudioCodecConfig* configRaw;
-      nsresult rv = JsepCodecDescToCodecConfig(*cdesc, &configRaw);
-      if (NS_FAILED(rv))
-        return rv;
-
-      configs.values.push_back(configRaw);
-    }
-
     auto error = conduit->ConfigureRecvMediaCodecs(configs.values);
 
     if (error) {
       MOZ_MTLOG(ML_ERROR, "ConfigureRecvMediaCodecs failed: " << error);
       return NS_ERROR_FAILURE;
     }
 
     if (!aTrackPair.mSending) {
@@ -647,26 +676,17 @@ MediaPipelineFactory::GetOrCreateAudioCo
       if (!conduit->SetLocalSSRC(ssrcs.front())) {
         MOZ_MTLOG(ML_ERROR, "SetLocalSSRC failed");
         return NS_ERROR_FAILURE;
       }
     }
 
     conduit->SetLocalCNAME(aTrack.GetCNAME().c_str());
 
-    const JsepCodecDescription* cdesc =
-      GetBestCodec(*aTrack.GetNegotiatedDetails());
-
-    AudioCodecConfig* configRaw;
-    nsresult rv = JsepCodecDescToCodecConfig(*cdesc, &configRaw);
-    if (NS_FAILED(rv))
-      return rv;
-
-    ScopedDeletePtr<AudioCodecConfig> config(configRaw);
-    auto error = conduit->ConfigureSendMediaCodec(config.get());
+    auto error = conduit->ConfigureSendMediaCodec(configs.values[0]);
     if (error) {
       MOZ_MTLOG(ML_ERROR, "ConfigureSendMediaCodec failed: " << error);
       return NS_ERROR_FAILURE;
     }
 
     const SdpExtmapAttributeList::Extmap* audioLevelExt =
         aTrack.GetNegotiatedDetails()->GetExt(
             "urn:ietf:params:rtp-hdrext:ssrc-audio-level");
@@ -709,50 +729,54 @@ MediaPipelineFactory::GetOrCreateVideoCo
     if (!conduit) {
       MOZ_MTLOG(ML_ERROR, "Could not create video conduit");
       return NS_ERROR_FAILURE;
     }
 
     mPCMedia->AddVideoConduit(aTrackPair.mLevel, conduit);
   }
 
-  if (!GetBestCodec(*aTrack.GetNegotiatedDetails())) {
+  PtrVector<VideoCodecConfig> configs;
+  nsresult rv = NegotiatedDetailsToVideoCodecConfigs(
+      *aTrack.GetNegotiatedDetails(), &configs);
+
+  if (NS_FAILED(rv)) {
+    MOZ_MTLOG(ML_ERROR, "Failed to convert JsepCodecDescriptions to "
+                        "VideoCodecConfigs.");
+    return rv;
+  }
+
+  if (configs.values.empty()) {
     MOZ_MTLOG(ML_ERROR, "Can't set up a conduit with 0 codecs");
     return NS_ERROR_FAILURE;
   }
 
-  size_t numCodecs = aTrack.GetNegotiatedDetails()->GetCodecCount();
-
-  bool configuredH264 = false;
   if (receiving) {
-    PtrVector<VideoCodecConfig> configs;
-
-    for (size_t i = 0; i < numCodecs; i++) {
-      const JsepCodecDescription* cdesc =
-        aTrack.GetNegotiatedDetails()->GetCodec(i);
-
-      // We can only handle configuring one recv H264 codec
-      if (configuredH264 && (cdesc->mName == "H264")) {
+    // Prune out stuff we cannot actually do. We should work to eliminate the
+    // need for this.
+    bool configuredH264 = false;
+    for (size_t i = 0; i < configs.values.size();) {
+      // TODO(bug 1200768): We can only handle configuring one recv H264 codec
+      if (configuredH264 && (configs.values[i]->mName == "H264")) {
+        delete configs.values[i];
+        configs.values.erase(configs.values.begin() + i);
         continue;
       }
 
-      VideoCodecConfig* configRaw;
-      nsresult rv = JsepCodecDescToCodecConfig(*cdesc, &configRaw);
-      if (NS_FAILED(rv))
-        return rv;
-
-      UniquePtr<VideoCodecConfig> config(configRaw);
-      if (EnsureExternalCodec(*conduit, config.get(), false)) {
+      // TODO(bug 1018791): This really should be checked sooner
+      if (EnsureExternalCodec(*conduit, configs.values[i], false)) {
+        delete configs.values[i];
+        configs.values.erase(configs.values.begin() + i);
         continue;
       }
 
-      if (cdesc->mName == "H264") {
+      if (configs.values[i]->mName == "H264") {
         configuredH264 = true;
       }
-      configs.values.push_back(config.release());
+      ++i;
     }
 
     auto error = conduit->ConfigureRecvMediaCodecs(configs.values);
 
     if (error) {
       MOZ_MTLOG(ML_ERROR, "ConfigureRecvMediaCodecs failed: " << error);
       return NS_ERROR_FAILURE;
     }
@@ -772,38 +796,28 @@ MediaPipelineFactory::GetOrCreateVideoCo
       if (!conduit->SetLocalSSRC(ssrcs.front())) {
         MOZ_MTLOG(ML_ERROR, "SetLocalSSRC failed");
         return NS_ERROR_FAILURE;
       }
     }
 
     conduit->SetLocalCNAME(aTrack.GetCNAME().c_str());
 
-    const JsepCodecDescription* cdesc =
-      GetBestCodec(*aTrack.GetNegotiatedDetails());
-
-    VideoCodecConfig* configRaw;
-    nsresult rv = JsepCodecDescToCodecConfig(*cdesc, &configRaw);
-    if (NS_FAILED(rv))
-      return rv;
-
     rv = ConfigureVideoCodecMode(aTrack,*conduit);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
-    // Take possession of this pointer
-    ScopedDeletePtr<VideoCodecConfig> config(configRaw);
-
-    if (EnsureExternalCodec(*conduit, config, true)) {
+    // TODO(bug 1018791): This really should be checked sooner
+    if (EnsureExternalCodec(*conduit, configs.values[0], true)) {
       MOZ_MTLOG(ML_ERROR, "External codec not available");
       return NS_ERROR_FAILURE;
     }
 
-    auto error = conduit->ConfigureSendMediaCodec(config);
+    auto error = conduit->ConfigureSendMediaCodec(configs.values[0]);
 
     if (error) {
       MOZ_MTLOG(ML_ERROR, "ConfigureSendMediaCodec failed: " << error);
       return NS_ERROR_FAILURE;
     }
   }
 
   *aConduitp = conduit;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -1024,25 +1024,25 @@ PeerConnectionImpl::ConfigureJsepSession
             branch->GetIntPref("media.navigator.video.h264.level", &level);
             level &= 0xFF;
             // Override level
             videoCodec.mProfileLevelId &= 0xFFFF00;
             videoCodec.mProfileLevelId |= level;
 
             int32_t maxBr = 0; // Unlimited
             branch->GetIntPref("media.navigator.video.h264.max_br", &maxBr);
-            videoCodec.mMaxBr = maxBr;
+            videoCodec.mConstraints.maxBr = maxBr;
 
             int32_t maxMbps = 0; // Unlimited
 #ifdef MOZ_WEBRTC_OMX
+            // Level 1.2; but let's allow CIF@30 or QVGA@30+ by default
             maxMbps = 11880;
 #endif
-            branch->GetIntPref("media.navigator.video.h264.max_mbps",
-                               &maxMbps);
-            videoCodec.mMaxBr = maxMbps;
+            branch->GetIntPref("media.navigator.video.h264.max_mbps", &maxMbps);
+            videoCodec.mConstraints.maxMbps = maxMbps;
 
             // Might disable it, but we set up other params anyway
             videoCodec.mEnabled = h264Enabled;
 
             if (videoCodec.mPacketizationMode == 0 && !softwareH264Enabled) {
               // We're assuming packetization mode 0 is unsupported by
               // hardware.
               videoCodec.mEnabled = false;
@@ -1056,24 +1056,24 @@ PeerConnectionImpl::ConfigureJsepSession
               videoCodec.mEnabled = false;
               break;
             }
             int32_t maxFs = 0;
             branch->GetIntPref("media.navigator.video.max_fs", &maxFs);
             if (maxFs <= 0) {
               maxFs = 12288; // We must specify something other than 0
             }
-            videoCodec.mMaxFs = maxFs;
+            videoCodec.mConstraints.maxFs = maxFs;
 
             int32_t maxFr = 0;
             branch->GetIntPref("media.navigator.video.max_fr", &maxFr);
             if (maxFr <= 0) {
               maxFr = 60; // We must specify something other than 0
             }
-            videoCodec.mMaxFr = maxFr;
+            videoCodec.mConstraints.maxFps = maxFr;
 
           }
 
           // TMMBR is enabled from a pref in about:config
           bool useTmmbr = false;
           branch->GetBoolPref("media.navigator.video.use_tmmbr",
             &useTmmbr);
           if (useTmmbr) {
@@ -1143,30 +1143,28 @@ PeerConnectionImpl::GetDatachannelParame
       trackPair.mSending->GetMediaType() == SdpMediaSection::kApplication;
     bool recvDataChannel =
       trackPair.mReceiving &&
       trackPair.mReceiving->GetMediaType() == SdpMediaSection::kApplication;
     (void)recvDataChannel;
     MOZ_ASSERT(sendDataChannel == recvDataChannel);
 
     if (sendDataChannel) {
-
-      if (!trackPair.mSending->GetNegotiatedDetails()->GetCodecCount()) {
+      // This will release assert if there is no such index, and that's ok
+      const JsepTrackEncoding& encoding =
+        trackPair.mSending->GetNegotiatedDetails()->GetEncoding(0);
+
+      if (encoding.GetCodecs().empty()) {
         CSFLogError(logTag, "%s: Negotiated m=application with no codec. "
                             "This is likely to be broken.",
                             __FUNCTION__);
         return NS_ERROR_FAILURE;
       }
 
-      for (size_t i = 0;
-           i < trackPair.mSending->GetNegotiatedDetails()->GetCodecCount();
-           ++i) {
-        const JsepCodecDescription* codec =
-          trackPair.mSending->GetNegotiatedDetails()->GetCodec(i);
-
+      for (const JsepCodecDescription* codec : encoding.GetCodecs()) {
         if (codec->mType != SdpMediaSection::kApplication) {
           CSFLogError(logTag, "%s: Codec type for m=application was %u, this "
                               "is a bug.",
                               __FUNCTION__,
                               static_cast<unsigned>(codec->mType));
           MOZ_ASSERT(false, "Codec for m=application was not \"application\"");
           return NS_ERROR_FAILURE;
         }
--- a/media/webrtc/signaling/src/sdp/SdpAttribute.cpp
+++ b/media/webrtc/signaling/src/sdp/SdpAttribute.cpp
@@ -832,20 +832,20 @@ SdpRemoteCandidatesAttribute::Serialize(
   for (auto i = mCandidates.begin(); i != mCandidates.end(); i++) {
     os << (i == mCandidates.begin() ? ":" : " ") << i->id << " " << i->address
        << " " << i->port;
   }
   os << CRLF;
 }
 
 bool
-SdpRidAttributeList::Constraints::Parse(std::istream& is, std::string* error)
+SdpRidAttributeList::Rid::ParseParameters(std::istream& is, std::string* error)
 {
   if (!PeekChar(is, error)) {
-    // No constraints
+    // No parameters
     return true;
   }
 
   do {
     is >> std::ws;
     std::string key = ParseKey(is, error);
     if (key.empty()) {
       return false; // Illegal trailing cruft
@@ -853,123 +853,129 @@ SdpRidAttributeList::Constraints::Parse(
 
     // This allows pt= to appear anywhere, instead of only at the beginning, but
     // this ends up being significantly less code.
     if (key == "pt") {
       if (!ParseFormats(is, error)) {
         return false;
       }
     } else if (key == "max-width") {
-      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &maxWidth, error)) {
+      if (!GetUnsigned<uint32_t>(
+            is, 0, UINT32_MAX, &constraints.maxWidth, error)) {
         return false;
       }
     } else if (key == "max-height") {
-      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &maxHeight, error)) {
+      if (!GetUnsigned<uint32_t>(
+            is, 0, UINT32_MAX, &constraints.maxHeight, error)) {
         return false;
       }
     } else if (key == "max-fps") {
-      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &maxFps, error)) {
+      if (!GetUnsigned<uint32_t>(
+            is, 0, UINT32_MAX, &constraints.maxFps, error)) {
         return false;
       }
     } else if (key == "max-fs") {
-      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &maxFs, error)) {
+      if (!GetUnsigned<uint32_t>(
+            is, 0, UINT32_MAX, &constraints.maxFs, error)) {
         return false;
       }
     } else if (key == "max-br") {
-      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &maxBr, error)) {
+      if (!GetUnsigned<uint32_t>(
+            is, 0, UINT32_MAX, &constraints.maxBr, error)) {
         return false;
       }
     } else if (key == "max-pps") {
-      if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &maxPps, error)) {
+      if (!GetUnsigned<uint32_t>(
+            is, 0, UINT32_MAX, &constraints.maxPps, error)) {
         return false;
       }
     } else if (key == "depend") {
       if (!ParseDepend(is, error)) {
         return false;
       }
     } else {
       (void) ParseToken(is, ";", error);
     }
   } while (SkipChar(is, ';', error));
   return true;
 }
 
 bool
-SdpRidAttributeList::Constraints::ParseDepend(
+SdpRidAttributeList::Rid::ParseDepend(
     std::istream& is,
     std::string* error)
 {
   do {
     std::string id = ParseToken(is, ",;", error);
     if (id.empty()) {
       return false;
     }
     dependIds.push_back(id);
   } while(SkipChar(is, ',', error));
 
   return true;
 }
 
 bool
-SdpRidAttributeList::Constraints::ParseFormats(
+SdpRidAttributeList::Rid::ParseFormats(
     std::istream& is,
     std::string* error)
 {
   do {
     uint16_t fmt;
     if (!GetUnsigned<uint16_t>(is, 0, 127, &fmt, error)) {
       return false;
     }
     formats.push_back(fmt);
   } while (SkipChar(is, ',', error));
 
   return true;
 }
 
 void
-SdpRidAttributeList::Constraints::Serialize(std::ostream& os) const
+SdpRidAttributeList::Rid::SerializeParameters(std::ostream& os) const
 {
-  if (!IsSet()) {
+  if (!HasParameters()) {
     return;
   }
 
   os << " ";
 
   SkipFirstDelimiter semic(";");
 
   if (!formats.empty()) {
     os << semic << "pt=";
     SkipFirstDelimiter comma(",");
     for (uint16_t fmt : formats) {
       os << comma << fmt;
     }
   }
 
-  if (maxWidth) {
-    os << semic << "max-width=" << maxWidth;
+  if (constraints.maxWidth) {
+    os << semic << "max-width=" << constraints.maxWidth;
   }
 
-  if (maxHeight) {
-    os << semic << "max-height=" << maxHeight;
+  if (constraints.maxHeight) {
+    os << semic << "max-height=" << constraints.maxHeight;
   }
 
-  if (maxFps) {
-    os << semic << "max-fps=" << maxFps;
+  if (constraints.maxFps) {
+    os << semic << "max-fps=" << constraints.maxFps;
   }
 
-  if (maxFs) {
-    os << semic << "max-fs=" << maxFs;
+  if (constraints.maxFs) {
+    os << semic << "max-fs=" << constraints.maxFs;
   }
 
-  if (maxBr) {
-    os << semic << "max-br=" << maxBr;
+  if (constraints.maxBr) {
+    os << semic << "max-br=" << constraints.maxBr;
   }
 
-  if (maxPps) {
-    os << semic << "max-pps=" << maxPps;
+  if (constraints.maxPps) {
+    os << semic << "max-pps=" << constraints.maxPps;
   }
 
   if (!dependIds.empty()) {
     os << semic << "depend=";
     SkipFirstDelimiter comma(",");
     for (const std::string& id : dependIds) {
       os << comma << id;
     }
@@ -990,24 +996,40 @@ SdpRidAttributeList::Rid::Parse(std::ist
     direction = sdp::kSend;
   } else if (directionToken == "recv") {
     direction = sdp::kRecv;
   } else {
     *error = "Invalid direction, must be either send or recv";
     return false;
   }
 
-  return constraints.Parse(is, error);
+  return ParseParameters(is, error);
 }
 
 void
 SdpRidAttributeList::Rid::Serialize(std::ostream& os) const
 {
   os << id << " " << direction;
-  constraints.Serialize(os);
+  SerializeParameters(os);
+}
+
+bool
+SdpRidAttributeList::Rid::HasFormat(const std::string& format) const
+{
+  uint16_t formatAsInt;
+  if (!SdpHelper::GetPtAsInt(format, &formatAsInt)) {
+    return false;
+  }
+
+  if (formats.empty()) {
+    return true;
+  }
+
+  return (std::find(formats.begin(), formats.end(), formatAsInt) !=
+         formats.end());
 }
 
 void
 SdpRidAttributeList::Serialize(std::ostream& os) const
 {
   for (const Rid& rid : mRids) {
     os << "a=" << mType << ":";
     rid.Serialize(os);
--- a/media/webrtc/signaling/src/sdp/SdpAttribute.h
+++ b/media/webrtc/signaling/src/sdp/SdpAttribute.h
@@ -17,16 +17,17 @@
 #include <string>
 
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Maybe.h"
 
 #include "signaling/src/sdp/SdpEnum.h"
+#include "signaling/src/common/EncodingConstraints.h"
 
 namespace mozilla
 {
 
 /**
  * Base class for SDP attributes
 */
 class SdpAttribute
@@ -823,60 +824,47 @@ a=rid, draft-pthatcher-mmusic-rid-01
 */
 class SdpRidAttributeList : public SdpAttribute
 {
 public:
   explicit SdpRidAttributeList()
     : SdpAttribute(kRidAttribute)
   {}
 
-  struct Constraints
-  {
-    Constraints() :
-      maxWidth(0),
-      maxHeight(0),
-      maxFps(0),
-      maxFs(0),
-      maxBr(0),
-      maxPps(0)
-    {}
-
-    bool Parse(std::istream& is, std::string* error);
-    bool ParseDepend(std::istream& is, std::string* error);
-    bool ParseFormats(std::istream& is, std::string* error);
-    void Serialize(std::ostream& os) const;
-    bool IsSet() const
-    {
-      return !formats.empty() || maxWidth || maxHeight || maxFps || maxFs ||
-             maxBr || maxPps || !dependIds.empty();
-    }
-
-    std::vector<uint16_t> formats; // Empty implies all
-    uint32_t maxWidth;
-    uint32_t maxHeight;
-    uint32_t maxFps;
-    uint32_t maxFs;
-    uint32_t maxBr;
-    uint32_t maxPps;
-    std::vector<std::string> dependIds;
-    // We do not bother trying to store constraints we don't understand.
-  };
-
   struct Rid
   {
     Rid() :
       direction(sdp::kSend)
     {}
 
     bool Parse(std::istream& is, std::string* error);
+    bool ParseParameters(std::istream& is, std::string* error);
+    bool ParseDepend(std::istream& is, std::string* error);
+    bool ParseFormats(std::istream& is, std::string* error);
     void Serialize(std::ostream& os) const;
+    void SerializeParameters(std::ostream& os) const;
+    bool HasFormat(const std::string& format) const;
+    bool HasParameters() const
+    {
+      return !formats.empty() ||
+        constraints.maxWidth ||
+        constraints.maxHeight ||
+        constraints.maxFps ||
+        constraints.maxFs ||
+        constraints.maxBr ||
+        constraints.maxPps ||
+        !dependIds.empty();
+    }
+
 
     std::string id;
     sdp::Direction direction;
-    Constraints constraints;
+    std::vector<uint16_t> formats; // Empty implies all
+    EncodingConstraints constraints;
+    std::vector<std::string> dependIds;
   };
 
   virtual void Serialize(std::ostream& os) const override;
   bool PushEntry(const std::string& raw, std::string* error, size_t* errorPos);
 
   std::vector<Rid> mRids;
 };
 
--- a/media/webrtc/signaling/test/jsep_session_unittest.cpp
+++ b/media/webrtc/signaling/test/jsep_session_unittest.cpp
@@ -891,23 +891,25 @@ protected:
       ASSERT_EQ("19", rtpmap->pt);
       ASSERT_EQ("reserved", rtpmap->name);
     }
   }
 
   void
   DumpTrack(const JsepTrack& track)
   {
+    const JsepTrackNegotiatedDetails* details = track.GetNegotiatedDetails();
     std::cerr << "  type=" << track.GetMediaType() << std::endl;
-    std::cerr << "  codecs=" << std::endl;
-    size_t num_codecs = track.GetNegotiatedDetails()->GetCodecCount();
-    for (size_t i = 0; i < num_codecs; ++i) {
-      const JsepCodecDescription* codec =
-        track.GetNegotiatedDetails()->GetCodec(i);
-      std::cerr << "    " << codec->mName << std::endl;
+    std::cerr << "  encodings=" << std::endl;
+    for (size_t i = 0; i < details->GetEncodingCount(); ++i) {
+      const JsepTrackEncoding& encoding = details->GetEncoding(i);
+      std::cerr << "    id=" << encoding.mRid;
+      for (const JsepCodecDescription* codec : encoding.GetCodecs()) {
+        std::cerr << "      " << codec->mName << std::endl;
+      }
     }
   }
 
   void
   DumpTrackPairs(const JsepSessionImpl& session)
   {
     auto pairs = mSessionAns.GetNegotiatedTrackPairs();
     for (auto i = pairs.begin(); i != pairs.end(); ++i) {
@@ -2831,30 +2833,34 @@ TEST_F(JsepSessionTest, ValidateAnswered
 
   auto offerPairs = mSessionOff.GetNegotiatedTrackPairs();
   ASSERT_EQ(2U, offerPairs.size());
   ASSERT_TRUE(offerPairs[1].mSending);
   ASSERT_TRUE(offerPairs[1].mReceiving);
   ASSERT_TRUE(offerPairs[1].mSending->GetNegotiatedDetails());
   ASSERT_TRUE(offerPairs[1].mReceiving->GetNegotiatedDetails());
   ASSERT_EQ(1U,
-      offerPairs[1].mSending->GetNegotiatedDetails()->GetCodecCount());
+      offerPairs[1].mSending->GetNegotiatedDetails()->GetEncoding(0)
+      .GetCodecs().size());
   ASSERT_EQ(1U,
-      offerPairs[1].mReceiving->GetNegotiatedDetails()->GetCodecCount());
+      offerPairs[1].mReceiving->GetNegotiatedDetails()->GetEncoding(0)
+      .GetCodecs().size());
 
   auto answerPairs = mSessionAns.GetNegotiatedTrackPairs();
   ASSERT_EQ(2U, answerPairs.size());
   ASSERT_TRUE(answerPairs[1].mSending);
   ASSERT_TRUE(answerPairs[1].mReceiving);
   ASSERT_TRUE(answerPairs[1].mSending->GetNegotiatedDetails());
   ASSERT_TRUE(answerPairs[1].mReceiving->GetNegotiatedDetails());
   ASSERT_EQ(1U,
-      answerPairs[1].mSending->GetNegotiatedDetails()->GetCodecCount());
+      answerPairs[1].mSending->GetNegotiatedDetails()->GetEncoding(0)
+      .GetCodecs().size());
   ASSERT_EQ(1U,
-      answerPairs[1].mReceiving->GetNegotiatedDetails()->GetCodecCount());
+      answerPairs[1].mReceiving->GetNegotiatedDetails()->GetEncoding(0)
+      .GetCodecs().size());
 
 #if 0
   // H264 packetization mode 1
   ASSERT_EQ("126", fmtps[1].format);
   ASSERT_TRUE(fmtps[1].parameters);
   ASSERT_EQ(SdpRtpmapAttributeList::kH264, fmtps[1].parameters->codec_type);
 
   auto& parsed_h264_1_params =
@@ -2898,28 +2904,34 @@ static void ReplaceAll(const std::string
     Replace(toReplace, with, in);
   }
 }
 
 static void
 GetCodec(JsepSession& session,
          size_t pairIndex,
          sdp::Direction direction,
+         size_t encodingIndex,
          size_t codecIndex,
          const JsepCodecDescription** codecOut)
 {
   *codecOut = nullptr;
   ASSERT_LT(pairIndex, session.GetNegotiatedTrackPairs().size());
   JsepTrackPair pair(session.GetNegotiatedTrackPairs().front());
   RefPtr<JsepTrack> track(
       (direction == sdp::kSend) ? pair.mSending : pair.mReceiving);
   ASSERT_TRUE(track);
   ASSERT_TRUE(track->GetNegotiatedDetails());
-  ASSERT_LT(codecIndex, track->GetNegotiatedDetails()->GetCodecCount());
-  *codecOut = track->GetNegotiatedDetails()->GetCodec(codecIndex);
+  ASSERT_LT(encodingIndex, track->GetNegotiatedDetails()->GetEncodingCount());
+  ASSERT_LT(codecIndex,
+      track->GetNegotiatedDetails()->GetEncoding(encodingIndex)
+      .GetCodecs().size());
+  *codecOut =
+      track->GetNegotiatedDetails()->GetEncoding(encodingIndex)
+      .GetCodecs()[codecIndex];
 }
 
 static void
 ForceH264(JsepSession& session, uint32_t profileLevelId)
 {
   for (JsepCodecDescription* codec : session.Codecs()) {
     if (codec->mName == "H264") {
       JsepVideoCodecDescription* h264 =
@@ -2944,40 +2956,40 @@ TEST_F(JsepSessionTest, TestH264Negotiat
 
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer(CreateAnswer());
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
   SetLocalAnswer(answer, CHECK_SUCCESS);
 
   const JsepCodecDescription* offererSendCodec;
-  GetCodec(mSessionOff, 0, sdp::kSend, 0, &offererSendCodec);
+  GetCodec(mSessionOff, 0, sdp::kSend, 0, 0, &offererSendCodec);
   ASSERT_TRUE(offererSendCodec);
   ASSERT_EQ("H264", offererSendCodec->mName);
   const JsepVideoCodecDescription* offererVideoSendCodec(
       static_cast<const JsepVideoCodecDescription*>(offererSendCodec));
   ASSERT_EQ((uint32_t)0x42e00d, offererVideoSendCodec->mProfileLevelId);
 
   const JsepCodecDescription* offererRecvCodec;
-  GetCodec(mSessionOff, 0, sdp::kRecv, 0, &offererRecvCodec);
+  GetCodec(mSessionOff, 0, sdp::kRecv, 0, 0, &offererRecvCodec);
   ASSERT_EQ("H264", offererRecvCodec->mName);
   const JsepVideoCodecDescription* offererVideoRecvCodec(
       static_cast<const JsepVideoCodecDescription*>(offererRecvCodec));
   ASSERT_EQ((uint32_t)0x42e00b, offererVideoRecvCodec->mProfileLevelId);
 
   const JsepCodecDescription* answererSendCodec;
-  GetCodec(mSessionAns, 0, sdp::kSend, 0, &answererSendCodec);
+  GetCodec(mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
   ASSERT_TRUE(answererSendCodec);
   ASSERT_EQ("H264", answererSendCodec->mName);
   const JsepVideoCodecDescription* answererVideoSendCodec(
       static_cast<const JsepVideoCodecDescription*>(answererSendCodec));
   ASSERT_EQ((uint32_t)0x42e00b, answererVideoSendCodec->mProfileLevelId);
 
   const JsepCodecDescription* answererRecvCodec;
-  GetCodec(mSessionAns, 0, sdp::kRecv, 0, &answererRecvCodec);
+  GetCodec(mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec);
   ASSERT_EQ("H264", answererRecvCodec->mName);
   const JsepVideoCodecDescription* answererVideoRecvCodec(
       static_cast<const JsepVideoCodecDescription*>(answererRecvCodec));
   ASSERT_EQ((uint32_t)0x42e00d, answererVideoRecvCodec->mProfileLevelId);
 }
 
 TEST_F(JsepSessionTest, TestH264NegotiationFails)
 {
@@ -3017,17 +3029,17 @@ TEST_F(JsepSessionTest, TestH264Negotiat
 
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer(CreateAnswer());
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
   SetLocalAnswer(answer, CHECK_SUCCESS);
 
   const JsepCodecDescription* answererSendCodec;
-  GetCodec(mSessionAns, 0, sdp::kSend, 0, &answererSendCodec);
+  GetCodec(mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
   ASSERT_TRUE(answererSendCodec);
   ASSERT_EQ("H264", answererSendCodec->mName);
   const JsepVideoCodecDescription* answererVideoSendCodec(
       static_cast<const JsepVideoCodecDescription*>(answererSendCodec));
   ASSERT_EQ((uint32_t)0x420010, answererVideoSendCodec->mProfileLevelId);
 }
 
 TEST_F(JsepSessionTest, TestH264NegotiationOffererNoFmtp)
@@ -3045,25 +3057,25 @@ TEST_F(JsepSessionTest, TestH264Negotiat
 
   SetRemoteOffer(offer, CHECK_SUCCESS);
   std::string answer(CreateAnswer());
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
   SetLocalAnswer(answer, CHECK_SUCCESS);
 
   const JsepCodecDescription* answererSendCodec;
-  GetCodec(mSessionAns, 0, sdp::kSend, 0, &answererSendCodec);
+  GetCodec(mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
   ASSERT_TRUE(answererSendCodec);
   ASSERT_EQ("H264", answererSendCodec->mName);
   const JsepVideoCodecDescription* answererVideoSendCodec(
       static_cast<const JsepVideoCodecDescription*>(answererSendCodec));
   ASSERT_EQ((uint32_t)0x420010, answererVideoSendCodec->mProfileLevelId);
 
   const JsepCodecDescription* answererRecvCodec;
-  GetCodec(mSessionAns, 0, sdp::kRecv, 0, &answererRecvCodec);
+  GetCodec(mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec);
   ASSERT_EQ("H264", answererRecvCodec->mName);
   const JsepVideoCodecDescription* answererVideoRecvCodec(
       static_cast<const JsepVideoCodecDescription*>(answererRecvCodec));
   ASSERT_EQ((uint32_t)0x420010, answererVideoRecvCodec->mProfileLevelId);
 }
 
 TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByOffererWithLowLevel)
 {
@@ -3085,25 +3097,25 @@ TEST_F(JsepSessionTest, TestH264LevelAsy
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
   SetLocalAnswer(answer, CHECK_SUCCESS);
 
   // Offerer doesn't know about the shenanigans we've pulled here, so will
   // behave normally, and we test the normal behavior elsewhere.
 
   const JsepCodecDescription* answererSendCodec;
-  GetCodec(mSessionAns, 0, sdp::kSend, 0, &answererSendCodec);
+  GetCodec(mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
   ASSERT_TRUE(answererSendCodec);
   ASSERT_EQ("H264", answererSendCodec->mName);
   const JsepVideoCodecDescription* answererVideoSendCodec(
       static_cast<const JsepVideoCodecDescription*>(answererSendCodec));
   ASSERT_EQ((uint32_t)0x42e00b, answererVideoSendCodec->mProfileLevelId);
 
   const JsepCodecDescription* answererRecvCodec;
-  GetCodec(mSessionAns, 0, sdp::kRecv, 0, &answererRecvCodec);
+  GetCodec(mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec);
   ASSERT_EQ("H264", answererRecvCodec->mName);
   const JsepVideoCodecDescription* answererVideoRecvCodec(
       static_cast<const JsepVideoCodecDescription*>(answererRecvCodec));
   ASSERT_EQ((uint32_t)0x42e00b, answererVideoRecvCodec->mProfileLevelId);
 }
 
 TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByOffererWithHighLevel)
 {
@@ -3125,25 +3137,25 @@ TEST_F(JsepSessionTest, TestH264LevelAsy
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
   SetLocalAnswer(answer, CHECK_SUCCESS);
 
   // Offerer doesn't know about the shenanigans we've pulled here, so will
   // behave normally, and we test the normal behavior elsewhere.
 
   const JsepCodecDescription* answererSendCodec;
-  GetCodec(mSessionAns, 0, sdp::kSend, 0, &answererSendCodec);
+  GetCodec(mSessionAns, 0, sdp::kSend, 0, 0, &answererSendCodec);
   ASSERT_TRUE(answererSendCodec);
   ASSERT_EQ("H264", answererSendCodec->mName);
   const JsepVideoCodecDescription* answererVideoSendCodec(
       static_cast<const JsepVideoCodecDescription*>(answererSendCodec));
   ASSERT_EQ((uint32_t)0x42e00b, answererVideoSendCodec->mProfileLevelId);
 
   const JsepCodecDescription* answererRecvCodec;
-  GetCodec(mSessionAns, 0, sdp::kRecv, 0, &answererRecvCodec);
+  GetCodec(mSessionAns, 0, sdp::kRecv, 0, 0, &answererRecvCodec);
   ASSERT_EQ("H264", answererRecvCodec->mName);
   const JsepVideoCodecDescription* answererVideoRecvCodec(
       static_cast<const JsepVideoCodecDescription*>(answererRecvCodec));
   ASSERT_EQ((uint32_t)0x42e00b, answererVideoRecvCodec->mProfileLevelId);
 }
 
 TEST_F(JsepSessionTest, TestH264LevelAsymmetryDisallowedByAnswererWithLowLevel)
 {
@@ -3161,25 +3173,25 @@ TEST_F(JsepSessionTest, TestH264LevelAsy
   Replace("level-asymmetry-allowed=1",
           "level-asymmetry-allowed=0",
           &answer);
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
   SetLocalAnswer(answer, CHECK_SUCCESS);
 
   const JsepCodecDescription* offererSendCodec;
-  GetCodec(mSessionOff, 0, sdp::kSend, 0, &offererSendCodec);
+  GetCodec(mSessionOff, 0, sdp::kSend, 0, 0, &offererSendCodec);
   ASSERT_TRUE(offererSendCodec);
   ASSERT_EQ("H264", offererSendCodec->mName);
   const JsepVideoCodecDescription* offererVideoSendCodec(
       static_cast<const JsepVideoCodecDescription*>(offererSendCodec));
   ASSERT_EQ((uint32_t)0x42e00b, offererVideoSendCodec->mProfileLevelId);
 
   const JsepCodecDescription* offererRecvCodec;
-  GetCodec(mSessionOff, 0, sdp::kRecv, 0, &offererRecvCodec);
+  GetCodec(mSessionOff, 0, sdp::kRecv, 0, 0, &offererRecvCodec);
   ASSERT_EQ("H264", offererRecvCodec->mName);
   const JsepVideoCodecDescription* offererVideoRecvCodec(
       static_cast<const JsepVideoCodecDescription*>(offererRecvCodec));
   ASSERT_EQ((uint32_t)0x42e00b, offererVideoRecvCodec->mProfileLevelId);
 
   // Answerer doesn't know we've pulled these shenanigans, it should act as if
   // it did not set level-asymmetry-required, and we already check that
   // elsewhere
@@ -3201,25 +3213,25 @@ TEST_F(JsepSessionTest, TestH264LevelAsy
   Replace("level-asymmetry-allowed=1",
           "level-asymmetry-allowed=0",
           &answer);
 
   SetRemoteAnswer(answer, CHECK_SUCCESS);
   SetLocalAnswer(answer, CHECK_SUCCESS);
 
   const JsepCodecDescription* offererSendCodec;
-  GetCodec(mSessionOff, 0, sdp::kSend, 0, &offererSendCodec);
+  GetCodec(mSessionOff, 0, sdp::kSend, 0, 0, &offererSendCodec);
   ASSERT_TRUE(offererSendCodec);
   ASSERT_EQ("H264", offererSendCodec->mName);
   const JsepVideoCodecDescription* offererVideoSendCodec(
       static_cast<const JsepVideoCodecDescription*>(offererSendCodec));
   ASSERT_EQ((uint32_t)0x42e00b, offererVideoSendCodec->mProfileLevelId);
 
   const JsepCodecDescription* offererRecvCodec;
-  GetCodec(mSessionOff, 0, sdp::kRecv, 0, &offererRecvCodec);
+  GetCodec(mSessionOff, 0, sdp::kRecv, 0, 0, &offererRecvCodec);
   ASSERT_EQ("H264", offererRecvCodec->mName);
   const JsepVideoCodecDescription* offererVideoRecvCodec(
       static_cast<const JsepVideoCodecDescription*>(offererRecvCodec));
   ASSERT_EQ((uint32_t)0x42e00b, offererVideoRecvCodec->mProfileLevelId);
 
   // Answerer doesn't know we've pulled these shenanigans, it should act as if
   // it did not set level-asymmetry-required, and we already check that
   // elsewhere
@@ -3401,18 +3413,18 @@ TEST_F(JsepSessionTest, TestRtcpFbStar)
   std::string answer = CreateAnswer();
   SetLocalAnswer(answer, CHECK_SUCCESS);
   SetRemoteAnswer(answer, CHECK_SUCCESS);
 
   ASSERT_EQ(1U, mSessionAns.GetRemoteTracks().size());
   RefPtr<JsepTrack> track = mSessionAns.GetRemoteTracks()[0];
   ASSERT_TRUE(track->GetNegotiatedDetails());
   auto* details = track->GetNegotiatedDetails();
-  for (size_t i = 0; i < details->GetCodecCount(); ++i) {
-    const JsepCodecDescription* codec = details->GetCodec(i);
+  for (const JsepCodecDescription* codec :
+       details->GetEncoding(0).GetCodecs()) {
     const JsepVideoCodecDescription* videoCodec =
       static_cast<const JsepVideoCodecDescription*>(codec);
     ASSERT_EQ(1U, videoCodec->mNackFbTypes.size());
     ASSERT_EQ("", videoCodec->mNackFbTypes[0]);
   }
 }
 
 TEST_F(JsepSessionTest, TestUniquePayloadTypes)
@@ -3537,38 +3549,38 @@ TEST_F(JsepSessionTest, StronglyPreferre
 
   types.push_back(SdpMediaSection::kVideo);
   AddTracks(mSessionOff, "video");
   AddTracks(mSessionAns, "video");
 
   OfferAnswer();
 
   const JsepCodecDescription* codec;
-  GetCodec(mSessionAns, 0, sdp::kSend, 0, &codec);
+  GetCodec(mSessionAns, 0, sdp::kSend, 0, 0, &codec);
   ASSERT_TRUE(codec);
   ASSERT_EQ("H264", codec->mName);
-  GetCodec(mSessionAns, 0, sdp::kRecv, 0, &codec);
+  GetCodec(mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
   ASSERT_TRUE(codec);
   ASSERT_EQ("H264", codec->mName);
 }
 
 TEST_F(JsepSessionTest, LowDynamicPayloadType)
 {
   SetPayloadTypeNumber(mSessionOff, "opus", "12");
   types.push_back(SdpMediaSection::kAudio);
   AddTracks(mSessionOff, "audio");
   AddTracks(mSessionAns, "audio");
 
   OfferAnswer();
   const JsepCodecDescription* codec;
-  GetCodec(mSessionAns, 0, sdp::kSend, 0, &codec);
+  GetCodec(mSessionAns, 0, sdp::kSend, 0, 0, &codec);
   ASSERT_TRUE(codec);
   ASSERT_EQ("opus", codec->mName);
   ASSERT_EQ("12", codec->mDefaultPt);
-  GetCodec(mSessionAns, 0, sdp::kRecv, 0, &codec);
+  GetCodec(mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
   ASSERT_TRUE(codec);
   ASSERT_EQ("opus", codec->mName);
   ASSERT_EQ("12", codec->mDefaultPt);
 }
 
 TEST_F(JsepSessionTest, PayloadTypeClash)
 {
   // Disable this so mSessionOff doesn't have a duplicate
@@ -3576,21 +3588,21 @@ TEST_F(JsepSessionTest, PayloadTypeClash
   SetPayloadTypeNumber(mSessionOff, "opus", "0");
   SetPayloadTypeNumber(mSessionAns, "PCMU", "0");
   types.push_back(SdpMediaSection::kAudio);
   AddTracks(mSessionOff, "audio");
   AddTracks(mSessionAns, "audio");
 
   OfferAnswer();
   const JsepCodecDescription* codec;
-  GetCodec(mSessionAns, 0, sdp::kSend, 0, &codec);
+  GetCodec(mSessionAns, 0, sdp::kSend, 0, 0, &codec);
   ASSERT_TRUE(codec);
   ASSERT_EQ("opus", codec->mName);
   ASSERT_EQ("0", codec->mDefaultPt);
-  GetCodec(mSessionAns, 0, sdp::kRecv, 0, &codec);
+  GetCodec(mSessionAns, 0, sdp::kRecv, 0, 0, &codec);
   ASSERT_TRUE(codec);
   ASSERT_EQ("opus", codec->mName);
   ASSERT_EQ("0", codec->mDefaultPt);
 
   // Now, make sure that mSessionAns does not put a=rtpmap:0 PCMU in a reoffer,
   // since pt 0 is taken for opus (the answerer still supports PCMU, and will
   // reoffer it, but it should choose a new payload type for it)
   JsepOfferOptions options;
--- a/media/webrtc/signaling/test/mediaconduit_unittests.cpp
+++ b/media/webrtc/signaling/test/mediaconduit_unittests.cpp
@@ -14,16 +14,17 @@ using namespace std;
 #include "mozilla/Scoped.h"
 #include "mozilla/SyncRunnable.h"
 #include <MediaConduitInterface.h>
 #include "GmpVideoCodec.h"
 #include "nsIEventTarget.h"
 #include "FakeMediaStreamsImpl.h"
 #include "nsThreadUtils.h"
 #include "runnable_utils.h"
+#include "signaling/src/common/EncodingConstraints.h"
 
 #define GTEST_HAS_RTTI 0
 #include "gtest/gtest.h"
 #include "gtest_utils.h"
 
 nsCOMPtr<nsIThread> gMainThread;
 nsCOMPtr<nsIThread> gGtestThread;
 bool gTestsComplete = false;
@@ -632,19 +633,20 @@ class TransportConduitTest : public ::te
     // attach the transport and renderer to video-conduit
     err = mVideoSession2->AttachRenderer(mVideoRenderer);
     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
     err = mVideoSession->SetTransmitterTransport(mVideoTransport);
     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
     err = mVideoSession2->SetReceiverTransport(mVideoTransport);
     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
 
+    mozilla::EncodingConstraints constraints;
     //configure send and recv codecs on theconduit
-    mozilla::VideoCodecConfig cinst1(120, "VP8");
-    mozilla::VideoCodecConfig cinst2(124, "I420");
+    mozilla::VideoCodecConfig cinst1(120, "VP8", constraints);
+    mozilla::VideoCodecConfig cinst2(124, "I420", constraints);
 
 
     std::vector<mozilla::VideoCodecConfig* > rcvCodecList;
     rcvCodecList.push_back(&cinst1);
     rcvCodecList.push_back(&cinst2);
 
     err = mVideoSession->ConfigureSendMediaCodec(
         send_vp8 ? &cinst1 : &cinst2);
@@ -713,35 +715,36 @@ class TransportConduitTest : public ::te
 
     std::vector<mozilla::VideoCodecConfig* > rcvCodecList;
 
     //Same APIs
     cerr << "   *************************************************" << endl;
     cerr << "    1. Same Codec (VP8) Repeated Twice " << endl;
     cerr << "   *************************************************" << endl;
 
-    mozilla::VideoCodecConfig cinst1(120, "VP8");
-    mozilla::VideoCodecConfig cinst2(120, "VP8");
+    mozilla::EncodingConstraints constraints;
+    mozilla::VideoCodecConfig cinst1(120, "VP8", constraints);
+    mozilla::VideoCodecConfig cinst2(120, "VP8", constraints);
     rcvCodecList.push_back(&cinst1);
     rcvCodecList.push_back(&cinst2);
     err = videoSession->ConfigureRecvMediaCodecs(rcvCodecList);
     EXPECT_NE(err,mozilla::kMediaConduitNoError);
     rcvCodecList.pop_back();
     rcvCodecList.pop_back();
 
 
     PR_Sleep(PR_SecondsToInterval(2));
     cerr << "   *************************************************" << endl;
     cerr << "    2. Codec With Invalid Payload Names " << endl;
     cerr << "   *************************************************" << endl;
     cerr << "   Setting payload 1 with name: I4201234tttttthhhyyyy89087987y76t567r7756765rr6u6676" << endl;
     cerr << "   Setting payload 2 with name of zero length" << endl;
 
-    mozilla::VideoCodecConfig cinst3(124, "I4201234tttttthhhyyyy89087987y76t567r7756765rr6u6676");
-    mozilla::VideoCodecConfig cinst4(124, "");
+    mozilla::VideoCodecConfig cinst3(124, "I4201234tttttthhhyyyy89087987y76t567r7756765rr6u6676", constraints);
+    mozilla::VideoCodecConfig cinst4(124, "", constraints);
 
     rcvCodecList.push_back(&cinst3);
     rcvCodecList.push_back(&cinst4);
 
     err = videoSession->ConfigureRecvMediaCodecs(rcvCodecList);
     EXPECT_TRUE(err != mozilla::kMediaConduitNoError);
     rcvCodecList.pop_back();
     rcvCodecList.pop_back();
@@ -811,18 +814,20 @@ class TransportConduitTest : public ::te
 
     // Get pointer to VideoSessionConduit.
     mozilla::SyncRunnable::DispatchToThread(gMainThread,
                                             WrapRunnableNMRet(&mVideoSession,
                                                 &mozilla::VideoSessionConduit::Create));
     if( !mVideoSession )
       ASSERT_NE(mVideoSession, (void*)nullptr);
 
+    mozilla::EncodingConstraints constraints;
+    constraints.maxFs = max_fs;
     // Configure send codecs on the conduit.
-    mozilla::VideoCodecConfig cinst1(120, "VP8", max_fs);
+    mozilla::VideoCodecConfig cinst1(120, "VP8", constraints);
 
     err = mVideoSession->ConfigureSendMediaCodec(&cinst1);
     ASSERT_EQ(mozilla::kMediaConduitNoError, err);
 
     // Send one frame.
     MOZ_ASSERT(!(orig_width & 1));
     MOZ_ASSERT(!(orig_height & 1));
     int len = ((orig_width * orig_height) * 3 / 2);
@@ -947,17 +952,18 @@ class TransportConduitTest : public ::te
       }
     }
     cerr << endl;
  }
 
   void SetGmpCodecs() {
     mExternalEncoder = mozilla::GmpVideoCodec::CreateEncoder();
     mExternalDecoder = mozilla::GmpVideoCodec::CreateDecoder();
-    mozilla::VideoCodecConfig config(124, "H264");
+    mozilla::EncodingConstraints constraints;
+    mozilla::VideoCodecConfig config(124, "H264", constraints);
     mVideoSession->SetExternalSendCodec(&config, mExternalEncoder);
     mVideoSession2->SetExternalRecvCodec(&config, mExternalDecoder);
   }
 
  private:
   //Audio Conduit Test Objects
   RefPtr<mozilla::AudioSessionConduit> mAudioSession;
   RefPtr<mozilla::AudioSessionConduit> mAudioSession2;
--- a/media/webrtc/signaling/test/sdp_unittests.cpp
+++ b/media/webrtc/signaling/test/sdp_unittests.cpp
@@ -1776,21 +1776,21 @@ TEST_P(NewSdpTest, CheckRid)
         SdpAttribute::kRidAttribute));
 
   const SdpRidAttributeList& rids =
     mSdp->GetMediaSection(1).GetAttributeList().GetRid();
 
   ASSERT_EQ(2U, rids.mRids.size());
   ASSERT_EQ("foo", rids.mRids[0].id);
   ASSERT_EQ(sdp::kSend, rids.mRids[0].direction);
-  ASSERT_EQ(0U, rids.mRids[0].constraints.formats.size());
+  ASSERT_EQ(0U, rids.mRids[0].formats.size());
   ASSERT_EQ("bar", rids.mRids[1].id);
   ASSERT_EQ(sdp::kRecv, rids.mRids[1].direction);
-  ASSERT_EQ(1U, rids.mRids[1].constraints.formats.size());
-  ASSERT_EQ(96U, rids.mRids[1].constraints.formats[0]);
+  ASSERT_EQ(1U, rids.mRids[1].formats.size());
+  ASSERT_EQ(96U, rids.mRids[1].formats[0]);
   ASSERT_EQ(800U, rids.mRids[1].constraints.maxWidth);
   ASSERT_EQ(600U, rids.mRids[1].constraints.maxHeight);
 }
 
 TEST_P(NewSdpTest, CheckMediaLevelIceUfrag) {
   ParseSdp(kBasicAudioVideoOffer);
   ASSERT_TRUE(!!mSdp) << "Parse failed: " << GetParseErrors();
   ASSERT_EQ(3U, mSdp->GetMediaSectionCount()) << "Wrong number of media sections";
@@ -3675,388 +3675,16 @@ TEST(NewSdpTestNoFixture, CheckSimulcast
   ParseInvalid<SdpSimulcastAttribute>(" ", 1);
   ParseInvalid<SdpSimulcastAttribute>("vcer ", 4);
   ParseInvalid<SdpSimulcastAttribute>(" send x", 7);
   ParseInvalid<SdpSimulcastAttribute>(" recv x", 7);
   ParseInvalid<SdpSimulcastAttribute>(" send pt=8 send ", 15);
   ParseInvalid<SdpSimulcastAttribute>(" recv pt=8 recv ", 15);
 }
 
-static SdpRidAttributeList::Constraints
-ParseRidConstraints(const std::string& input)
-{
-  std::istringstream is(input);
-  std::string error;
-  SdpRidAttributeList::Constraints constraints;
-  EXPECT_TRUE(constraints.Parse(is, &error)) << error
-              << " for input \'" << input << "\'" ;
-  EXPECT_TRUE(is.eof());
-  return constraints;
-}
-
-TEST(NewSdpTestNoFixture, CheckRidConstraintsValidParse)
-{
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints(""));
-    ASSERT_EQ(0U, constraints.formats.size());
-    ASSERT_EQ(0U, constraints.maxWidth);
-    ASSERT_EQ(0U, constraints.maxHeight);
-    ASSERT_EQ(0U, constraints.maxFps);
-    ASSERT_EQ(0U, constraints.maxFs);
-    ASSERT_EQ(0U, constraints.maxBr);
-    ASSERT_EQ(0U, constraints.maxPps);
-    ASSERT_EQ(0U, constraints.dependIds.size());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints("pt=96"));
-    ASSERT_EQ(1U, constraints.formats.size());
-    ASSERT_EQ(96U, constraints.formats[0]);
-    ASSERT_EQ(0U, constraints.maxWidth);
-    ASSERT_EQ(0U, constraints.maxHeight);
-    ASSERT_EQ(0U, constraints.maxFps);
-    ASSERT_EQ(0U, constraints.maxFs);
-    ASSERT_EQ(0U, constraints.maxBr);
-    ASSERT_EQ(0U, constraints.maxPps);
-    ASSERT_EQ(0U, constraints.dependIds.size());
-  }
-
-  // This is not technically permitted by the BNF, but the parse code is simpler
-  // if we allow it. If we decide to stop allowing this, this will need to be
-  // converted to an invalid parse test-case.
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints("max-br=30000;pt=96"));
-    ASSERT_EQ(1U, constraints.formats.size());
-    ASSERT_EQ(96U, constraints.formats[0]);
-    ASSERT_EQ(0U, constraints.maxWidth);
-    ASSERT_EQ(0U, constraints.maxHeight);
-    ASSERT_EQ(0U, constraints.maxFps);
-    ASSERT_EQ(0U, constraints.maxFs);
-    ASSERT_EQ(30000U, constraints.maxBr);
-    ASSERT_EQ(0U, constraints.maxPps);
-    ASSERT_EQ(0U, constraints.dependIds.size());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints("pt=96,97,98"));
-    ASSERT_EQ(3U, constraints.formats.size());
-    ASSERT_EQ(96U, constraints.formats[0]);
-    ASSERT_EQ(97U, constraints.formats[1]);
-    ASSERT_EQ(98U, constraints.formats[2]);
-    ASSERT_EQ(0U, constraints.maxWidth);
-    ASSERT_EQ(0U, constraints.maxHeight);
-    ASSERT_EQ(0U, constraints.maxFps);
-    ASSERT_EQ(0U, constraints.maxFs);
-    ASSERT_EQ(0U, constraints.maxBr);
-    ASSERT_EQ(0U, constraints.maxPps);
-    ASSERT_EQ(0U, constraints.dependIds.size());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints("max-width=800"));
-    ASSERT_EQ(0U, constraints.formats.size());
-    ASSERT_EQ(800U, constraints.maxWidth);
-    ASSERT_EQ(0U, constraints.maxHeight);
-    ASSERT_EQ(0U, constraints.maxFps);
-    ASSERT_EQ(0U, constraints.maxFs);
-    ASSERT_EQ(0U, constraints.maxBr);
-    ASSERT_EQ(0U, constraints.maxPps);
-    ASSERT_EQ(0U, constraints.dependIds.size());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints("max-height=640"));
-    ASSERT_EQ(0U, constraints.formats.size());
-    ASSERT_EQ(0U, constraints.maxWidth);
-    ASSERT_EQ(640U, constraints.maxHeight);
-    ASSERT_EQ(0U, constraints.maxFps);
-    ASSERT_EQ(0U, constraints.maxFs);
-    ASSERT_EQ(0U, constraints.maxBr);
-    ASSERT_EQ(0U, constraints.maxPps);
-    ASSERT_EQ(0U, constraints.dependIds.size());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints("max-fps=30"));
-    ASSERT_EQ(0U, constraints.formats.size());
-    ASSERT_EQ(0U, constraints.maxWidth);
-    ASSERT_EQ(0U, constraints.maxHeight);
-    ASSERT_EQ(30U, constraints.maxFps);
-    ASSERT_EQ(0U, constraints.maxFs);
-    ASSERT_EQ(0U, constraints.maxBr);
-    ASSERT_EQ(0U, constraints.maxPps);
-    ASSERT_EQ(0U, constraints.dependIds.size());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints("max-fs=3600"));
-    ASSERT_EQ(0U, constraints.formats.size());
-    ASSERT_EQ(0U, constraints.maxWidth);
-    ASSERT_EQ(0U, constraints.maxHeight);
-    ASSERT_EQ(0U, constraints.maxFps);
-    ASSERT_EQ(3600U, constraints.maxFs);
-    ASSERT_EQ(0U, constraints.maxBr);
-    ASSERT_EQ(0U, constraints.maxPps);
-    ASSERT_EQ(0U, constraints.dependIds.size());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints("max-br=30000"));
-    ASSERT_EQ(0U, constraints.formats.size());
-    ASSERT_EQ(0U, constraints.maxWidth);
-    ASSERT_EQ(0U, constraints.maxHeight);
-    ASSERT_EQ(0U, constraints.maxFps);
-    ASSERT_EQ(0U, constraints.maxFs);
-    ASSERT_EQ(30000U, constraints.maxBr);
-    ASSERT_EQ(0U, constraints.maxPps);
-    ASSERT_EQ(0U, constraints.dependIds.size());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints("max-pps=9216000"));
-    ASSERT_EQ(0U, constraints.formats.size());
-    ASSERT_EQ(0U, constraints.maxWidth);
-    ASSERT_EQ(0U, constraints.maxHeight);
-    ASSERT_EQ(0U, constraints.maxFps);
-    ASSERT_EQ(0U, constraints.maxFs);
-    ASSERT_EQ(0U, constraints.maxBr);
-    ASSERT_EQ(9216000U, constraints.maxPps);
-    ASSERT_EQ(0U, constraints.dependIds.size());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints("depend=foo"));
-    ASSERT_EQ(0U, constraints.formats.size());
-    ASSERT_EQ(0U, constraints.maxWidth);
-    ASSERT_EQ(0U, constraints.maxHeight);
-    ASSERT_EQ(0U, constraints.maxFps);
-    ASSERT_EQ(0U, constraints.maxFs);
-    ASSERT_EQ(0U, constraints.maxBr);
-    ASSERT_EQ(0U, constraints.maxPps);
-    ASSERT_EQ(1U, constraints.dependIds.size());
-    ASSERT_EQ("foo", constraints.dependIds[0]);
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints("max-foo=20"));
-    ASSERT_EQ(0U, constraints.formats.size());
-    ASSERT_EQ(0U, constraints.maxWidth);
-    ASSERT_EQ(0U, constraints.maxHeight);
-    ASSERT_EQ(0U, constraints.maxFps);
-    ASSERT_EQ(0U, constraints.maxFs);
-    ASSERT_EQ(0U, constraints.maxBr);
-    ASSERT_EQ(0U, constraints.maxPps);
-    ASSERT_EQ(0U, constraints.dependIds.size());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints("depend=foo,bar"));
-    ASSERT_EQ(0U, constraints.formats.size());
-    ASSERT_EQ(0U, constraints.maxWidth);
-    ASSERT_EQ(0U, constraints.maxHeight);
-    ASSERT_EQ(0U, constraints.maxFps);
-    ASSERT_EQ(0U, constraints.maxFs);
-    ASSERT_EQ(0U, constraints.maxBr);
-    ASSERT_EQ(0U, constraints.maxPps);
-    ASSERT_EQ(2U, constraints.dependIds.size());
-    ASSERT_EQ("foo", constraints.dependIds[0]);
-    ASSERT_EQ("bar", constraints.dependIds[1]);
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints("max-width=800;max-height=600"));
-    ASSERT_EQ(0U, constraints.formats.size());
-    ASSERT_EQ(800U, constraints.maxWidth);
-    ASSERT_EQ(600U, constraints.maxHeight);
-    ASSERT_EQ(0U, constraints.maxFps);
-    ASSERT_EQ(0U, constraints.maxFs);
-    ASSERT_EQ(0U, constraints.maxBr);
-    ASSERT_EQ(0U, constraints.maxPps);
-    ASSERT_EQ(0U, constraints.dependIds.size());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints("pt=96,97;max-width=800;max-height=600"));
-    ASSERT_EQ(2U, constraints.formats.size());
-    ASSERT_EQ(96U, constraints.formats[0]);
-    ASSERT_EQ(97U, constraints.formats[1]);
-    ASSERT_EQ(800U, constraints.maxWidth);
-    ASSERT_EQ(600U, constraints.maxHeight);
-    ASSERT_EQ(0U, constraints.maxFps);
-    ASSERT_EQ(0U, constraints.maxFs);
-    ASSERT_EQ(0U, constraints.maxBr);
-    ASSERT_EQ(0U, constraints.maxPps);
-    ASSERT_EQ(0U, constraints.dependIds.size());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints("depend=foo,bar;max-width=800;max-height=600"));
-    ASSERT_EQ(0U, constraints.formats.size());
-    ASSERT_EQ(800U, constraints.maxWidth);
-    ASSERT_EQ(600U, constraints.maxHeight);
-    ASSERT_EQ(0U, constraints.maxFps);
-    ASSERT_EQ(0U, constraints.maxFs);
-    ASSERT_EQ(0U, constraints.maxBr);
-    ASSERT_EQ(0U, constraints.maxPps);
-    ASSERT_EQ(2U, constraints.dependIds.size());
-    ASSERT_EQ("foo", constraints.dependIds[0]);
-    ASSERT_EQ("bar", constraints.dependIds[1]);
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints(
-        ParseRidConstraints("max-foo=20;max-width=800;max-height=600"));
-    ASSERT_EQ(0U, constraints.formats.size());
-    ASSERT_EQ(800U, constraints.maxWidth);
-    ASSERT_EQ(600U, constraints.maxHeight);
-    ASSERT_EQ(0U, constraints.maxFps);
-    ASSERT_EQ(0U, constraints.maxFs);
-    ASSERT_EQ(0U, constraints.maxBr);
-    ASSERT_EQ(0U, constraints.maxPps);
-    ASSERT_EQ(0U, constraints.dependIds.size());
-  }
-}
-
-TEST(NewSdpTestNoFixture, CheckRidConstraintsInvalidParse)
-{
-  ParseInvalid<SdpRidAttributeList::Constraints>(" ", 1);
-  ParseInvalid<SdpRidAttributeList::Constraints>("pt", 2);
-  ParseInvalid<SdpRidAttributeList::Constraints>("pt=", 3);
-  ParseInvalid<SdpRidAttributeList::Constraints>("pt=x", 3);
-  ParseInvalid<SdpRidAttributeList::Constraints>("pt=-1", 3);
-  ParseInvalid<SdpRidAttributeList::Constraints>("pt=96,", 6);
-  ParseInvalid<SdpRidAttributeList::Constraints>("pt=196", 6);
-  ParseInvalid<SdpRidAttributeList::Constraints>("max-width", 9);
-  ParseInvalid<SdpRidAttributeList::Constraints>("max-width=", 10);
-  ParseInvalid<SdpRidAttributeList::Constraints>("max-width=x", 10);
-  ParseInvalid<SdpRidAttributeList::Constraints>("max-width=-1", 10);
-  ParseInvalid<SdpRidAttributeList::Constraints>("max-width=800;", 14);
-  ParseInvalid<SdpRidAttributeList::Constraints>("max-width=800; ", 15);
-  ParseInvalid<SdpRidAttributeList::Constraints>("depend=", 7);
-  ParseInvalid<SdpRidAttributeList::Constraints>("depend=,", 7);
-  ParseInvalid<SdpRidAttributeList::Constraints>("depend=1,", 9);
-}
-
-TEST(NewSdpTestNoFixture, CheckRidConstraintsSerialize)
-{
-  {
-    SdpRidAttributeList::Constraints constraints;
-    std::ostringstream os;
-    constraints.Serialize(os);
-    ASSERT_EQ("", os.str());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints;
-    constraints.formats.push_back(96);
-    std::ostringstream os;
-    constraints.Serialize(os);
-    ASSERT_EQ(" pt=96", os.str());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints;
-    constraints.formats.push_back(96);
-    constraints.formats.push_back(97);
-    std::ostringstream os;
-    constraints.Serialize(os);
-    ASSERT_EQ(" pt=96,97", os.str());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints;
-    constraints.maxWidth = 800;
-    std::ostringstream os;
-    constraints.Serialize(os);
-    ASSERT_EQ(" max-width=800", os.str());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints;
-    constraints.maxHeight = 600;
-    std::ostringstream os;
-    constraints.Serialize(os);
-    ASSERT_EQ(" max-height=600", os.str());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints;
-    constraints.maxFps = 30;
-    std::ostringstream os;
-    constraints.Serialize(os);
-    ASSERT_EQ(" max-fps=30", os.str());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints;
-    constraints.maxFs = 3600;
-    std::ostringstream os;
-    constraints.Serialize(os);
-    ASSERT_EQ(" max-fs=3600", os.str());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints;
-    constraints.maxBr = 30000;
-    std::ostringstream os;
-    constraints.Serialize(os);
-    ASSERT_EQ(" max-br=30000", os.str());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints;
-    constraints.maxPps = 9216000;
-    std::ostringstream os;
-    constraints.Serialize(os);
-    ASSERT_EQ(" max-pps=9216000", os.str());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints;
-    constraints.dependIds.push_back("foo");
-    std::ostringstream os;
-    constraints.Serialize(os);
-    ASSERT_EQ(" depend=foo", os.str());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints;
-    constraints.dependIds.push_back("foo");
-    constraints.dependIds.push_back("bar");
-    std::ostringstream os;
-    constraints.Serialize(os);
-    ASSERT_EQ(" depend=foo,bar", os.str());
-  }
-
-  {
-    SdpRidAttributeList::Constraints constraints;
-    constraints.formats.push_back(96);
-    constraints.maxBr = 30000;
-    std::ostringstream os;
-    constraints.Serialize(os);
-    ASSERT_EQ(" pt=96;max-br=30000", os.str());
-  }
-}
-
 static SdpRidAttributeList::Rid
 ParseRid(const std::string& input)
 {
   std::istringstream is(input);
   std::string error;
   SdpRidAttributeList::Rid rid;
   EXPECT_TRUE(rid.Parse(is, &error)) << error;
   EXPECT_TRUE(is.eof());
@@ -4064,97 +3692,470 @@ ParseRid(const std::string& input)
 }
 
 TEST(NewSdpTestNoFixture, CheckRidValidParse)
 {
   {
     SdpRidAttributeList::Rid rid(ParseRid("1 send"));
     ASSERT_EQ("1", rid.id);
     ASSERT_EQ(sdp::kSend, rid.direction);
-    ASSERT_EQ(0U, rid.constraints.formats.size());
+    ASSERT_EQ(0U, rid.formats.size());
     ASSERT_EQ(0U, rid.constraints.maxWidth);
     ASSERT_EQ(0U, rid.constraints.maxHeight);
     ASSERT_EQ(0U, rid.constraints.maxFps);
     ASSERT_EQ(0U, rid.constraints.maxFs);
     ASSERT_EQ(0U, rid.constraints.maxBr);
     ASSERT_EQ(0U, rid.constraints.maxPps);
-    ASSERT_EQ(0U, rid.constraints.dependIds.size());
+    ASSERT_EQ(0U, rid.dependIds.size());
   }
 
   {
     SdpRidAttributeList::Rid rid(ParseRid("1 send pt=96;max-width=800"));
     ASSERT_EQ("1", rid.id);
     ASSERT_EQ(sdp::kSend, rid.direction);
-    ASSERT_EQ(1U, rid.constraints.formats.size());
-    ASSERT_EQ(96U, rid.constraints.formats[0]);
+    ASSERT_EQ(1U, rid.formats.size());
+    ASSERT_EQ(96U, rid.formats[0]);
     ASSERT_EQ(800U, rid.constraints.maxWidth);
     ASSERT_EQ(0U, rid.constraints.maxHeight);
     ASSERT_EQ(0U, rid.constraints.maxFps);
     ASSERT_EQ(0U, rid.constraints.maxFs);
     ASSERT_EQ(0U, rid.constraints.maxBr);
     ASSERT_EQ(0U, rid.constraints.maxPps);
-    ASSERT_EQ(0U, rid.constraints.dependIds.size());
+    ASSERT_EQ(0U, rid.dependIds.size());
   }
 
   {
     SdpRidAttributeList::Rid rid(ParseRid("1 send pt=96,97,98;max-width=800"));
     ASSERT_EQ("1", rid.id);
     ASSERT_EQ(sdp::kSend, rid.direction);
-    ASSERT_EQ(3U, rid.constraints.formats.size());
-    ASSERT_EQ(96U, rid.constraints.formats[0]);
-    ASSERT_EQ(97U, rid.constraints.formats[1]);
-    ASSERT_EQ(98U, rid.constraints.formats[2]);
+    ASSERT_EQ(3U, rid.formats.size());
+    ASSERT_EQ(96U, rid.formats[0]);
+    ASSERT_EQ(97U, rid.formats[1]);
+    ASSERT_EQ(98U, rid.formats[2]);
     ASSERT_EQ(800U, rid.constraints.maxWidth);
     ASSERT_EQ(0U, rid.constraints.maxHeight);
     ASSERT_EQ(0U, rid.constraints.maxFps);
     ASSERT_EQ(0U, rid.constraints.maxFs);
     ASSERT_EQ(0U, rid.constraints.maxBr);
     ASSERT_EQ(0U, rid.constraints.maxPps);
-    ASSERT_EQ(0U, rid.constraints.dependIds.size());
+    ASSERT_EQ(0U, rid.dependIds.size());
   }
 
   {
     SdpRidAttributeList::Rid rid(
         ParseRid("0123456789az-_ recv max-width=800"));
     ASSERT_EQ("0123456789az-_", rid.id);
     ASSERT_EQ(sdp::kRecv, rid.direction);
-    ASSERT_EQ(0U, rid.constraints.formats.size());
+    ASSERT_EQ(0U, rid.formats.size());
+    ASSERT_EQ(800U, rid.constraints.maxWidth);
+    ASSERT_EQ(0U, rid.constraints.maxHeight);
+    ASSERT_EQ(0U, rid.constraints.maxFps);
+    ASSERT_EQ(0U, rid.constraints.maxFs);
+    ASSERT_EQ(0U, rid.constraints.maxBr);
+    ASSERT_EQ(0U, rid.constraints.maxPps);
+    ASSERT_EQ(0U, rid.dependIds.size());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send"));
+    ASSERT_EQ(0U, rid.formats.size());
+    ASSERT_EQ(0U, rid.constraints.maxWidth);
+    ASSERT_EQ(0U, rid.constraints.maxHeight);
+    ASSERT_EQ(0U, rid.constraints.maxFps);
+    ASSERT_EQ(0U, rid.constraints.maxFs);
+    ASSERT_EQ(0U, rid.constraints.maxBr);
+    ASSERT_EQ(0U, rid.constraints.maxPps);
+    ASSERT_EQ(0U, rid.dependIds.size());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send pt=96"));
+    ASSERT_EQ(1U, rid.formats.size());
+    ASSERT_EQ(96U, rid.formats[0]);
+    ASSERT_EQ(0U, rid.constraints.maxWidth);
+    ASSERT_EQ(0U, rid.constraints.maxHeight);
+    ASSERT_EQ(0U, rid.constraints.maxFps);
+    ASSERT_EQ(0U, rid.constraints.maxFs);
+    ASSERT_EQ(0U, rid.constraints.maxBr);
+    ASSERT_EQ(0U, rid.constraints.maxPps);
+    ASSERT_EQ(0U, rid.dependIds.size());
+  }
+
+  // This is not technically permitted by the BNF, but the parse code is simpler
+  // if we allow it. If we decide to stop allowing this, this will need to be
+  // converted to an invalid parse test-case.
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send max-br=30000;pt=96"));
+    ASSERT_EQ(1U, rid.formats.size());
+    ASSERT_EQ(96U, rid.formats[0]);
+    ASSERT_EQ(0U, rid.constraints.maxWidth);
+    ASSERT_EQ(0U, rid.constraints.maxHeight);
+    ASSERT_EQ(0U, rid.constraints.maxFps);
+    ASSERT_EQ(0U, rid.constraints.maxFs);
+    ASSERT_EQ(30000U, rid.constraints.maxBr);
+    ASSERT_EQ(0U, rid.constraints.maxPps);
+    ASSERT_EQ(0U, rid.dependIds.size());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send pt=96,97,98"));
+    ASSERT_EQ(3U, rid.formats.size());
+    ASSERT_EQ(96U, rid.formats[0]);
+    ASSERT_EQ(97U, rid.formats[1]);
+    ASSERT_EQ(98U, rid.formats[2]);
+    ASSERT_EQ(0U, rid.constraints.maxWidth);
+    ASSERT_EQ(0U, rid.constraints.maxHeight);
+    ASSERT_EQ(0U, rid.constraints.maxFps);
+    ASSERT_EQ(0U, rid.constraints.maxFs);
+    ASSERT_EQ(0U, rid.constraints.maxBr);
+    ASSERT_EQ(0U, rid.constraints.maxPps);
+    ASSERT_EQ(0U, rid.dependIds.size());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send max-width=800"));
+    ASSERT_EQ(0U, rid.formats.size());
     ASSERT_EQ(800U, rid.constraints.maxWidth);
     ASSERT_EQ(0U, rid.constraints.maxHeight);
     ASSERT_EQ(0U, rid.constraints.maxFps);
     ASSERT_EQ(0U, rid.constraints.maxFs);
     ASSERT_EQ(0U, rid.constraints.maxBr);
     ASSERT_EQ(0U, rid.constraints.maxPps);
-    ASSERT_EQ(0U, rid.constraints.dependIds.size());
+    ASSERT_EQ(0U, rid.dependIds.size());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send max-height=640"));
+    ASSERT_EQ(0U, rid.formats.size());
+    ASSERT_EQ(0U, rid.constraints.maxWidth);
+    ASSERT_EQ(640U, rid.constraints.maxHeight);
+    ASSERT_EQ(0U, rid.constraints.maxFps);
+    ASSERT_EQ(0U, rid.constraints.maxFs);
+    ASSERT_EQ(0U, rid.constraints.maxBr);
+    ASSERT_EQ(0U, rid.constraints.maxPps);
+    ASSERT_EQ(0U, rid.dependIds.size());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send max-fps=30"));
+    ASSERT_EQ(0U, rid.formats.size());
+    ASSERT_EQ(0U, rid.constraints.maxWidth);
+    ASSERT_EQ(0U, rid.constraints.maxHeight);
+    ASSERT_EQ(30U, rid.constraints.maxFps);
+    ASSERT_EQ(0U, rid.constraints.maxFs);
+    ASSERT_EQ(0U, rid.constraints.maxBr);
+    ASSERT_EQ(0U, rid.constraints.maxPps);
+    ASSERT_EQ(0U, rid.dependIds.size());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send max-fs=3600"));
+    ASSERT_EQ(0U, rid.formats.size());
+    ASSERT_EQ(0U, rid.constraints.maxWidth);
+    ASSERT_EQ(0U, rid.constraints.maxHeight);
+    ASSERT_EQ(0U, rid.constraints.maxFps);
+    ASSERT_EQ(3600U, rid.constraints.maxFs);
+    ASSERT_EQ(0U, rid.constraints.maxBr);
+    ASSERT_EQ(0U, rid.constraints.maxPps);
+    ASSERT_EQ(0U, rid.dependIds.size());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send max-br=30000"));
+    ASSERT_EQ(0U, rid.formats.size());
+    ASSERT_EQ(0U, rid.constraints.maxWidth);
+    ASSERT_EQ(0U, rid.constraints.maxHeight);
+    ASSERT_EQ(0U, rid.constraints.maxFps);
+    ASSERT_EQ(0U, rid.constraints.maxFs);
+    ASSERT_EQ(30000U, rid.constraints.maxBr);
+    ASSERT_EQ(0U, rid.constraints.maxPps);
+    ASSERT_EQ(0U, rid.dependIds.size());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send max-pps=9216000"));
+    ASSERT_EQ(0U, rid.formats.size());
+    ASSERT_EQ(0U, rid.constraints.maxWidth);
+    ASSERT_EQ(0U, rid.constraints.maxHeight);
+    ASSERT_EQ(0U, rid.constraints.maxFps);
+    ASSERT_EQ(0U, rid.constraints.maxFs);
+    ASSERT_EQ(0U, rid.constraints.maxBr);
+    ASSERT_EQ(9216000U, rid.constraints.maxPps);
+    ASSERT_EQ(0U, rid.dependIds.size());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send depend=foo"));
+    ASSERT_EQ(0U, rid.formats.size());
+    ASSERT_EQ(0U, rid.constraints.maxWidth);
+    ASSERT_EQ(0U, rid.constraints.maxHeight);
+    ASSERT_EQ(0U, rid.constraints.maxFps);
+    ASSERT_EQ(0U, rid.constraints.maxFs);
+    ASSERT_EQ(0U, rid.constraints.maxBr);
+    ASSERT_EQ(0U, rid.constraints.maxPps);
+    ASSERT_EQ(1U, rid.dependIds.size());
+    ASSERT_EQ("foo", rid.dependIds[0]);
   }
 
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send max-foo=20"));
+    ASSERT_EQ(0U, rid.formats.size());
+    ASSERT_EQ(0U, rid.constraints.maxWidth);
+    ASSERT_EQ(0U, rid.constraints.maxHeight);
+    ASSERT_EQ(0U, rid.constraints.maxFps);
+    ASSERT_EQ(0U, rid.constraints.maxFs);
+    ASSERT_EQ(0U, rid.constraints.maxBr);
+    ASSERT_EQ(0U, rid.constraints.maxPps);
+    ASSERT_EQ(0U, rid.dependIds.size());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send depend=foo,bar"));
+    ASSERT_EQ(0U, rid.formats.size());
+    ASSERT_EQ(0U, rid.constraints.maxWidth);
+    ASSERT_EQ(0U, rid.constraints.maxHeight);
+    ASSERT_EQ(0U, rid.constraints.maxFps);
+    ASSERT_EQ(0U, rid.constraints.maxFs);
+    ASSERT_EQ(0U, rid.constraints.maxBr);
+    ASSERT_EQ(0U, rid.constraints.maxPps);
+    ASSERT_EQ(2U, rid.dependIds.size());
+    ASSERT_EQ("foo", rid.dependIds[0]);
+    ASSERT_EQ("bar", rid.dependIds[1]);
+  }
+
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send max-width=800;max-height=600"));
+    ASSERT_EQ(0U, rid.formats.size());
+    ASSERT_EQ(800U, rid.constraints.maxWidth);
+    ASSERT_EQ(600U, rid.constraints.maxHeight);
+    ASSERT_EQ(0U, rid.constraints.maxFps);
+    ASSERT_EQ(0U, rid.constraints.maxFs);
+    ASSERT_EQ(0U, rid.constraints.maxBr);
+    ASSERT_EQ(0U, rid.constraints.maxPps);
+    ASSERT_EQ(0U, rid.dependIds.size());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send pt=96,97;max-width=800;max-height=600"));
+    ASSERT_EQ(2U, rid.formats.size());
+    ASSERT_EQ(96U, rid.formats[0]);
+    ASSERT_EQ(97U, rid.formats[1]);
+    ASSERT_EQ(800U, rid.constraints.maxWidth);
+    ASSERT_EQ(600U, rid.constraints.maxHeight);
+    ASSERT_EQ(0U, rid.constraints.maxFps);
+    ASSERT_EQ(0U, rid.constraints.maxFs);
+    ASSERT_EQ(0U, rid.constraints.maxBr);
+    ASSERT_EQ(0U, rid.constraints.maxPps);
+    ASSERT_EQ(0U, rid.dependIds.size());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send depend=foo,bar;max-width=800;max-height=600"));
+    ASSERT_EQ(0U, rid.formats.size());
+    ASSERT_EQ(800U, rid.constraints.maxWidth);
+    ASSERT_EQ(600U, rid.constraints.maxHeight);
+    ASSERT_EQ(0U, rid.constraints.maxFps);
+    ASSERT_EQ(0U, rid.constraints.maxFs);
+    ASSERT_EQ(0U, rid.constraints.maxBr);
+    ASSERT_EQ(0U, rid.constraints.maxPps);
+    ASSERT_EQ(2U, rid.dependIds.size());
+    ASSERT_EQ("foo", rid.dependIds[0]);
+    ASSERT_EQ("bar", rid.dependIds[1]);
+  }
+
+  {
+    SdpRidAttributeList::Rid rid(
+        ParseRid("foo send max-foo=20;max-width=800;max-height=600"));
+    ASSERT_EQ(0U, rid.formats.size());
+    ASSERT_EQ(800U, rid.constraints.maxWidth);
+    ASSERT_EQ(600U, rid.constraints.maxHeight);
+    ASSERT_EQ(0U, rid.constraints.maxFps);
+    ASSERT_EQ(0U, rid.constraints.maxFs);
+    ASSERT_EQ(0U, rid.constraints.maxBr);
+    ASSERT_EQ(0U, rid.constraints.maxPps);
+    ASSERT_EQ(0U, rid.dependIds.size());
+  }
 }
 
 TEST(NewSdpTestNoFixture, CheckRidInvalidParse)
 {
   ParseInvalid<SdpRidAttributeList::Rid>("", 0);
   ParseInvalid<SdpRidAttributeList::Rid>(" ", 0);
   ParseInvalid<SdpRidAttributeList::Rid>("foo", 3);
   ParseInvalid<SdpRidAttributeList::Rid>("foo ", 4);
   ParseInvalid<SdpRidAttributeList::Rid>("foo  ", 5);
   ParseInvalid<SdpRidAttributeList::Rid>("foo bar", 7);
   ParseInvalid<SdpRidAttributeList::Rid>("foo recv ", 9);
   ParseInvalid<SdpRidAttributeList::Rid>("foo recv pt=", 12);
+  ParseInvalid<SdpRidAttributeList::Rid>(" ", 0);
+  ParseInvalid<SdpRidAttributeList::Rid>("foo send pt", 11);
+  ParseInvalid<SdpRidAttributeList::Rid>("foo send pt=", 12);
+  ParseInvalid<SdpRidAttributeList::Rid>("foo send pt=x", 12);
+  ParseInvalid<SdpRidAttributeList::Rid>("foo send pt=-1", 12);
+  ParseInvalid<SdpRidAttributeList::Rid>("foo send pt=96,", 15);
+  ParseInvalid<SdpRidAttributeList::Rid>("foo send pt=196", 15);
+  ParseInvalid<SdpRidAttributeList::Rid>("foo send max-width", 18);
+  ParseInvalid<SdpRidAttributeList::Rid>("foo send max-width=", 19);
+  ParseInvalid<SdpRidAttributeList::Rid>("foo send max-width=x", 19);
+  ParseInvalid<SdpRidAttributeList::Rid>("foo send max-width=-1", 19);
+  ParseInvalid<SdpRidAttributeList::Rid>("foo send max-width=800;", 23);
+  ParseInvalid<SdpRidAttributeList::Rid>("foo send max-width=800; ", 24);
+  ParseInvalid<SdpRidAttributeList::Rid>("foo send depend=",16);
+  ParseInvalid<SdpRidAttributeList::Rid>("foo send depend=,", 16);
+  ParseInvalid<SdpRidAttributeList::Rid>("foo send depend=1,", 18);
 }
 
 TEST(NewSdpTestNoFixture, CheckRidSerialize)
 {
   {
     SdpRidAttributeList::Rid rid;
     rid.id = "foo";
     rid.direction = sdp::kSend;
     std::ostringstream os;
     rid.Serialize(os);
     ASSERT_EQ("foo send", os.str());
   }
+
+  {
+    SdpRidAttributeList::Rid rid;
+    rid.id = "foo";
+    rid.direction = sdp::kSend;
+    std::ostringstream os;
+    rid.Serialize(os);
+    ASSERT_EQ("foo send", os.str());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid;
+    rid.id = "foo";
+    rid.direction = sdp::kSend;
+    rid.formats.push_back(96);
+    std::ostringstream os;
+    rid.Serialize(os);
+    ASSERT_EQ("foo send pt=96", os.str());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid;
+    rid.id = "foo";
+    rid.direction = sdp::kSend;
+    rid.formats.push_back(96);
+    rid.formats.push_back(97);
+    std::ostringstream os;
+    rid.Serialize(os);
+    ASSERT_EQ("foo send pt=96,97", os.str());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid;
+    rid.id = "foo";
+    rid.direction = sdp::kSend;
+    rid.constraints.maxWidth = 800;
+    std::ostringstream os;
+    rid.Serialize(os);
+    ASSERT_EQ("foo send max-width=800", os.str());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid;
+    rid.id = "foo";
+    rid.direction = sdp::kSend;
+    rid.constraints.maxHeight = 600;
+    std::ostringstream os;
+    rid.Serialize(os);
+    ASSERT_EQ("foo send max-height=600", os.str());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid;
+    rid.id = "foo";
+    rid.direction = sdp::kSend;
+    rid.constraints.maxFps = 30;
+    std::ostringstream os;
+    rid.Serialize(os);
+    ASSERT_EQ("foo send max-fps=30", os.str());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid;
+    rid.id = "foo";
+    rid.direction = sdp::kSend;
+    rid.constraints.maxFs = 3600;
+    std::ostringstream os;
+    rid.Serialize(os);
+    ASSERT_EQ("foo send max-fs=3600", os.str());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid;
+    rid.id = "foo";
+    rid.direction = sdp::kSend;
+    rid.constraints.maxBr = 30000;
+    std::ostringstream os;
+    rid.Serialize(os);
+    ASSERT_EQ("foo send max-br=30000", os.str());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid;
+    rid.id = "foo";
+    rid.direction = sdp::kSend;
+    rid.constraints.maxPps = 9216000;
+    std::ostringstream os;
+    rid.Serialize(os);
+    ASSERT_EQ("foo send max-pps=9216000", os.str());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid;
+    rid.id = "foo";
+    rid.direction = sdp::kSend;
+    rid.dependIds.push_back("foo");
+    std::ostringstream os;
+    rid.Serialize(os);
+    ASSERT_EQ("foo send depend=foo", os.str());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid;
+    rid.id = "foo";
+    rid.direction = sdp::kSend;
+    rid.dependIds.push_back("foo");
+    rid.dependIds.push_back("bar");
+    std::ostringstream os;
+    rid.Serialize(os);
+    ASSERT_EQ("foo send depend=foo,bar", os.str());
+  }
+
+  {
+    SdpRidAttributeList::Rid rid;
+    rid.id = "foo";
+    rid.direction = sdp::kSend;
+    rid.formats.push_back(96);
+    rid.constraints.maxBr = 30000;
+    std::ostringstream os;
+    rid.Serialize(os);
+    ASSERT_EQ("foo send pt=96;max-br=30000", os.str());
+  }
 }
 
 } // End namespace test.
 
 int main(int argc, char **argv) {
   test_utils = new MtransportTestUtils();
   NSS_NoDB_Init(nullptr);
   NSS_SetDomesticPolicy();