Browse Source

Enhanced Description API

Paul-Louis Ageneau 3 years ago
parent
commit
6a00efd1ad
3 changed files with 309 additions and 227 deletions
  1. 63 42
      include/rtc/description.hpp
  2. 5 8
      src/capi.cpp
  3. 241 177
      src/description.cpp

+ 63 - 42
include/rtc/description.hpp

@@ -89,40 +89,49 @@ public:
 		Direction direction() const { return mDirection; }
 		Direction direction() const { return mDirection; }
 		void setDirection(Direction dir);
 		void setDirection(Direction dir);
 
 
-		operator string() const;
-		string generateSdp(string_view eol, string_view addr, string_view port) const;
-
-		virtual void parseSdpLine(string_view line);
-
-		std::vector<string>::iterator beginAttributes();
-		std::vector<string>::iterator endAttributes();
-		std::vector<string>::iterator removeAttribute(std::vector<string>::iterator iterator);
+		std::vector<string> attributes() const;
+		void addAttribute(string attr);
+		void removeAttribute(const string &attr);
 
 
 		struct RTC_CPP_EXPORT ExtMap {
 		struct RTC_CPP_EXPORT ExtMap {
+			static int parseId(string_view description);
+
 			ExtMap(string_view description);
 			ExtMap(string_view description);
-			ExtMap() {}
+
+			void setDescription(string_view description);
 
 
 			int id;
 			int id;
 			string uri;
 			string uri;
 			string attributes;
 			string attributes;
 			Direction direction = Direction::Unknown;
 			Direction direction = Direction::Unknown;
-
-			static int parseId(string_view view);
-			void setDescription(string_view view);
 		};
 		};
 
 
-		void addExtMap(const ExtMap &map);
+		std::vector<int> extIds();
+		ExtMap *extMap(int id);
+		void addExtMap(ExtMap map);
+		void removeExtMap(int id);
+
+		operator string() const;
+		string generateSdp(string_view eol, string_view addr, string_view port) const;
+
+		virtual void parseSdpLine(string_view line);
 
 
-		std::map<int, ExtMap>::iterator beginExtMaps();
-		std::map<int, ExtMap>::iterator endExtMaps();
-		std::map<int, ExtMap>::iterator removeExtMap(std::map<int, ExtMap>::iterator iterator);
+		// For backward compatibility, do not use
+		[[deprecated]] std::vector<string>::iterator beginAttributes();
+		[[deprecated]] std::vector<string>::iterator endAttributes();
+		[[deprecated]] std::vector<string>::iterator
+		removeAttribute(std::vector<string>::iterator iterator);
+		[[deprecated]] std::map<int, ExtMap>::iterator beginExtMaps();
+		[[deprecated]] std::map<int, ExtMap>::iterator endExtMaps();
+		[[deprecated]] std::map<int, ExtMap>::iterator
+		removeExtMap(std::map<int, ExtMap>::iterator iterator);
 
 
 	protected:
 	protected:
 		Entry(const string &mline, string mid, Direction dir = Direction::Unknown);
 		Entry(const string &mline, string mid, Direction dir = Direction::Unknown);
 		virtual string generateSdpLines(string_view eol) const;
 		virtual string generateSdpLines(string_view eol) const;
 
 
 		std::vector<string> mAttributes;
 		std::vector<string> mAttributes;
-		std::map<int, ExtMap> mExtMap;
+		std::map<int, ExtMap> mExtMaps;
 
 
 	private:
 	private:
 		string mType;
 		string mType;
@@ -165,8 +174,6 @@ public:
 		string description() const override;
 		string description() const override;
 		Media reciprocate() const;
 		Media reciprocate() const;
 
 
-		void removeFormat(const string &fmt);
-
 		void addSSRC(uint32_t ssrc, optional<string> name, optional<string> msid = nullopt,
 		void addSSRC(uint32_t ssrc, optional<string> name, optional<string> msid = nullopt,
 		             optional<string> trackID = nullopt);
 		             optional<string> trackID = nullopt);
 		void removeSSRC(uint32_t oldSSRC);
 		void removeSSRC(uint32_t oldSSRC);
@@ -176,25 +183,23 @@ public:
 		std::vector<uint32_t> getSSRCs();
 		std::vector<uint32_t> getSSRCs();
 		std::optional<std::string> getCNameForSsrc(uint32_t ssrc);
 		std::optional<std::string> getCNameForSsrc(uint32_t ssrc);
 
 
+		int bitrate() const;
 		void setBitrate(int bitrate);
 		void setBitrate(int bitrate);
-		int getBitrate() const;
 
 
-		bool hasPayloadType(int payloadType) const;
-
-		void addRTXCodec(unsigned int payloadType, unsigned int originalPayloadType,
-		                 unsigned int clockRate);
+		struct RTC_CPP_EXPORT RtpMap {
+			static int parsePayloadType(string_view description);
 
 
-		virtual void parseSdpLine(string_view line) override;
+			explicit RtpMap(int payloadType);
+			RtpMap(string_view description);
 
 
-		struct RTC_CPP_EXPORT RTPMap {
-			RTPMap(string_view mline);
-			RTPMap() {}
+			void setDescription(string_view description);
 
 
-			void removeFB(const string &string);
-			void addFB(const string &string);
-			void addAttribute(string attr) { fmtps.emplace_back(std::move(attr)); }
+			void addFeedback(string fb);
+			void removeFeedback(const string &str);
+			void addParameter(string p);
+			void removeParameter(const string &str);
 
 
-			int pt;
+			int payloadType;
 			string format;
 			string format;
 			int clockRate;
 			int clockRate;
 			string encParams;
 			string encParams;
@@ -202,25 +207,41 @@ public:
 			std::vector<string> rtcpFbs;
 			std::vector<string> rtcpFbs;
 			std::vector<string> fmtps;
 			std::vector<string> fmtps;
 
 
-			static int parsePT(string_view view);
-			void setMLine(string_view view);
+			// For backward compatibility, do not use
+			[[deprecated]] void addFB(string fb) { addFeedback(std::move(fb)); }
+			[[deprecated]] void removeFB(const string &str) { removeFeedback(str); }
+			[[deprecated]] void addAttribute(string attr) { addParameter(std::move(attr)); }
 		};
 		};
 
 
-		void addRTPMap(const RTPMap &map);
+		bool hasPayloadType(int payloadType) const;
+		std::vector<int> payloadTypes() const;
+		RtpMap *rtpMap(int payloadType);
+		void addRtpMap(RtpMap map);
+		void removeRtpMap(int payloadType);
+		void removeFormat(const string &format);
+
+		void addRtxCodec(int payloadType, int origPayloadType, unsigned int clockRate);
 
 
-		std::map<int, RTPMap>::iterator beginMaps();
-		std::map<int, RTPMap>::iterator endMaps();
-		std::map<int, RTPMap>::iterator removeMap(std::map<int, RTPMap>::iterator iterator);
+		virtual void parseSdpLine(string_view line) override;
+
+		// For backward compatibility, do not use
+		using RTPMap = RtpMap;
+		[[deprecated]] int getBitrate() const { return bitrate(); }
+		[[deprecated]] inline void addRTPMap(RtpMap map) { addRtpMap(std::move(map)); }
+		[[deprecated]] inline void addRTXCodec(int pt, int origpt, unsigned int clk) {
+			addRtxCodec(pt, origpt, clk);
+		}
+		[[deprecated]] std::map<int, RtpMap>::iterator beginMaps();
+		[[deprecated]] std::map<int, RtpMap>::iterator endMaps();
+		[[deprecated]] std::map<int, RtpMap>::iterator
+		removeMap(std::map<int, RtpMap>::iterator iterator);
 
 
 	private:
 	private:
 		virtual string generateSdpLines(string_view eol) const override;
 		virtual string generateSdpLines(string_view eol) const override;
 
 
 		int mBas = -1;
 		int mBas = -1;
 
 
-		Media::RTPMap &getFormat(int fmt);
-		Media::RTPMap &getFormat(const string &fmt);
-
-		std::map<int, RTPMap> mRtpMap;
+		std::map<int, RtpMap> mRtpMaps;
 		std::vector<uint32_t> mSsrcs;
 		std::vector<uint32_t> mSsrcs;
 		std::map<uint32_t, string> mCNameMap;
 		std::map<uint32_t, string> mCNameMap;
 	};
 	};

+ 5 - 8
src/capi.cpp

@@ -1173,14 +1173,11 @@ int rtcGetTrackPayloadTypesForCodec(int tr, const char *ccodec, int *buffer, int
 		auto track = getTrack(tr);
 		auto track = getTrack(tr);
 		auto codec = lowercased(string(ccodec));
 		auto codec = lowercased(string(ccodec));
 		auto description = track->description();
 		auto description = track->description();
-		std::vector<int> payloadTypes{};
-		payloadTypes.reserve(std::max(size, 0));
-		for (auto it = description.beginMaps(); it != description.endMaps(); it++) {
-			auto element = *it;
-			if (lowercased(element.second.format) == codec) {
-				payloadTypes.push_back(element.first);
-			}
-		}
+		std::vector<int> payloadTypes;
+		for(int pt : description.payloadTypes())
+			if (lowercased(description.rtpMap(pt)->format) == codec)
+				payloadTypes.push_back(pt);
+
 		return copyAndReturn(payloadTypes, buffer, size);
 		return copyAndReturn(payloadTypes, buffer, size);
 	});
 	});
 }
 }

+ 241 - 177
src/description.cpp

@@ -215,7 +215,8 @@ void Description::addIceOption(string option) {
 }
 }
 
 
 void Description::removeIceOption(const string &option) {
 void Description::removeIceOption(const string &option) {
-	mIceOptions.erase(std::remove(mIceOptions.begin(), mIceOptions.end(), option), mIceOptions.end());
+	mIceOptions.erase(std::remove(mIceOptions.begin(), mIceOptions.end(), option),
+	                  mIceOptions.end());
 }
 }
 
 
 bool Description::hasCandidate(const Candidate &candidate) const {
 bool Description::hasCandidate(const Candidate &candidate) const {
@@ -458,11 +459,14 @@ variant<Description::Media *, Description::Application *> Description::media(uns
 		auto result = dynamic_cast<Application *>(entry.get());
 		auto result = dynamic_cast<Application *>(entry.get());
 		if (!result)
 		if (!result)
 			throw std::logic_error("Bad type of application in description");
 			throw std::logic_error("Bad type of application in description");
+
 		return result;
 		return result;
+
 	} else {
 	} else {
 		auto result = dynamic_cast<Media *>(entry.get());
 		auto result = dynamic_cast<Media *>(entry.get());
 		if (!result)
 		if (!result)
 			throw std::logic_error("Bad type of media in description");
 			throw std::logic_error("Bad type of media in description");
+
 		return result;
 		return result;
 	}
 	}
 }
 }
@@ -477,11 +481,14 @@ Description::media(unsigned int index) const {
 		auto result = dynamic_cast<Application *>(entry.get());
 		auto result = dynamic_cast<Application *>(entry.get());
 		if (!result)
 		if (!result)
 			throw std::logic_error("Bad type of application in description");
 			throw std::logic_error("Bad type of application in description");
+
 		return result;
 		return result;
+
 	} else {
 	} else {
 		auto result = dynamic_cast<Media *>(entry.get());
 		auto result = dynamic_cast<Media *>(entry.get());
 		if (!result)
 		if (!result)
 			throw std::logic_error("Bad type of media in description");
 			throw std::logic_error("Bad type of media in description");
+
 		return result;
 		return result;
 	}
 	}
 }
 }
@@ -500,6 +507,40 @@ Description::Entry::Entry(const string &mline, string mid, Direction dir)
 
 
 void Description::Entry::setDirection(Direction dir) { mDirection = dir; }
 void Description::Entry::setDirection(Direction dir) { mDirection = dir; }
 
 
+std::vector<string> Description::Entry::attributes() const { return mAttributes; }
+
+void Description::Entry::addAttribute(string attr) {
+	if (std::find(mAttributes.begin(), mAttributes.end(), attr) == mAttributes.end())
+		mAttributes.emplace_back(std::move(attr));
+}
+
+void Description::Entry::removeAttribute(const string &attr) {
+	mAttributes.erase(std::remove(mAttributes.begin(), mAttributes.end(), attr), mAttributes.end());
+}
+
+std::vector<int> Description::Entry::extIds() {
+	std::vector<int> result;
+	for (auto it = mExtMaps.begin(); it != mExtMaps.end(); ++it)
+		result.push_back(it->first);
+
+	return result;
+}
+
+Description::Entry::ExtMap *Description::Entry::extMap(int id) {
+	auto it = mExtMaps.find(id);
+	if (it == mExtMaps.end())
+		throw std::invalid_argument("extmap not found");
+
+	return &it->second;
+}
+
+void Description::Entry::addExtMap(ExtMap map) {
+	auto id = map.id;
+	mExtMaps.emplace(id, std::move(map));
+}
+
+void Description::Entry::removeExtMap(int id) { mExtMaps.erase(id); }
+
 Description::Entry::operator string() const { return generateSdp("\r\n", "IP4 0.0.0.0", "9"); }
 Description::Entry::operator string() const { return generateSdp("\r\n", "IP4 0.0.0.0", "9"); }
 
 
 string Description::Entry::generateSdp(string_view eol, string_view addr, string_view port) const {
 string Description::Entry::generateSdp(string_view eol, string_view addr, string_view port) const {
@@ -516,7 +557,7 @@ string Description::Entry::generateSdpLines(string_view eol) const {
 	sdp << "a=bundle-only" << eol;
 	sdp << "a=bundle-only" << eol;
 	sdp << "a=mid:" << mMid << eol;
 	sdp << "a=mid:" << mMid << eol;
 
 
-	for (auto it = mExtMap.begin(); it != mExtMap.end(); ++it) {
+	for (auto it = mExtMaps.begin(); it != mExtMaps.end(); ++it) {
 		auto &map = it->second;
 		auto &map = it->second;
 
 
 		sdp << "a=extmap:" << map.id;
 		sdp << "a=extmap:" << map.id;
@@ -561,6 +602,7 @@ string Description::Entry::generateSdpLines(string_view eol) const {
 		break;
 		break;
 	}
 	}
 
 
+	// TODO
 	for (const auto &attr : mAttributes) {
 	for (const auto &attr : mAttributes) {
 		if (attr.find("extmap") == string::npos && attr.find("rtcp-rsize") == string::npos)
 		if (attr.find("extmap") == string::npos && attr.find("rtcp-rsize") == string::npos)
 			sdp << "a=" << attr << eol;
 			sdp << "a=" << attr << eol;
@@ -574,16 +616,16 @@ void Description::Entry::parseSdpLine(string_view line) {
 		string_view attr = line.substr(2);
 		string_view attr = line.substr(2);
 		auto [key, value] = parse_pair(attr);
 		auto [key, value] = parse_pair(attr);
 
 
-		if (key == "mid")
+		if (key == "mid") {
 			mMid = value;
 			mMid = value;
-		else if (key == "extmap") {
+		} else if (key == "extmap") {
 			auto id = Description::Media::ExtMap::parseId(value);
 			auto id = Description::Media::ExtMap::parseId(value);
-			auto it = mExtMap.find(id);
-			if (it == mExtMap.end()) {
-				it = mExtMap.insert(std::make_pair(id, Description::Media::ExtMap(value))).first;
-			} else {
+			auto it = mExtMaps.find(id);
+			if (it == mExtMaps.end())
+				it = mExtMaps.insert(std::make_pair(id, Description::Media::ExtMap(value))).first;
+			else
 				it->second.setDescription(value);
 				it->second.setDescription(value);
-			}
+
 		} else if (attr == "sendonly")
 		} else if (attr == "sendonly")
 			mDirection = Direction::SendOnly;
 			mDirection = Direction::SendOnly;
 		else if (attr == "recvonly")
 		else if (attr == "recvonly")
@@ -608,29 +650,24 @@ Description::Entry::removeAttribute(std::vector<string>::iterator it) {
 	return mAttributes.erase(it);
 	return mAttributes.erase(it);
 }
 }
 
 
-void Description::Entry::addExtMap(const Description::Entry::ExtMap &map) {
-	mExtMap.emplace(map.id, map);
-}
-
 std::map<int, Description::Entry::ExtMap>::iterator Description::Entry::beginExtMaps() {
 std::map<int, Description::Entry::ExtMap>::iterator Description::Entry::beginExtMaps() {
-	return mExtMap.begin();
+	return mExtMaps.begin();
 }
 }
 
 
 std::map<int, Description::Entry::ExtMap>::iterator Description::Entry::endExtMaps() {
 std::map<int, Description::Entry::ExtMap>::iterator Description::Entry::endExtMaps() {
-	return mExtMap.end();
+	return mExtMaps.end();
 }
 }
 
 
 std::map<int, Description::Entry::ExtMap>::iterator
 std::map<int, Description::Entry::ExtMap>::iterator
 Description::Entry::removeExtMap(std::map<int, Description::Entry::ExtMap>::iterator iterator) {
 Description::Entry::removeExtMap(std::map<int, Description::Entry::ExtMap>::iterator iterator) {
-	return mExtMap.erase(iterator);
+	return mExtMaps.erase(iterator);
 }
 }
 
 
 Description::Entry::ExtMap::ExtMap(string_view description) { setDescription(description); }
 Description::Entry::ExtMap::ExtMap(string_view description) { setDescription(description); }
 
 
-int Description::Entry::ExtMap::parseId(string_view view) {
-	size_t p = view.find(' ');
-
-	return to_integer<int>(view.substr(0, p));
+int Description::Entry::ExtMap::parseId(string_view description) {
+	size_t p = description.find(' ');
+	return to_integer<int>(description.substr(0, p));
 }
 }
 
 
 void Description::Entry::ExtMap::setDescription(string_view description) {
 void Description::Entry::ExtMap::setDescription(string_view description) {
@@ -780,7 +817,7 @@ Description::Media::Media(const string &mline, string mid, Direction dir)
 string Description::Media::description() const {
 string Description::Media::description() const {
 	std::ostringstream desc;
 	std::ostringstream desc;
 	desc << Entry::description();
 	desc << Entry::description();
-	for (auto it = mRtpMap.begin(); it != mRtpMap.end(); ++it)
+	for (auto it = mRtpMaps.begin(); it != mRtpMaps.end(); ++it)
 		desc << ' ' << it->first;
 		desc << ' ' << it->first;
 
 
 	return desc.str();
 	return desc.str();
@@ -803,7 +840,7 @@ Description::Media Description::Media::reciprocate() const {
 	}
 	}
 
 
 	// Invert directions of extmap
 	// Invert directions of extmap
-	for (auto it = reciprocated.mExtMap.begin(); it != reciprocated.mExtMap.end(); ++it) {
+	for (auto it = reciprocated.mExtMaps.begin(); it != reciprocated.mExtMaps.end(); ++it) {
 		auto &map = it->second;
 		auto &map = it->second;
 		switch (map.direction) {
 		switch (map.direction) {
 		case Direction::RecvOnly:
 		case Direction::RecvOnly:
@@ -832,117 +869,80 @@ Description::Media Description::Media::reciprocate() const {
 	return reciprocated;
 	return reciprocated;
 }
 }
 
 
-Description::Media::RTPMap &Description::Media::getFormat(int fmt) {
-	auto it = mRtpMap.find(fmt);
-	if (it != mRtpMap.end())
-		return it->second;
-
-	throw std::invalid_argument("m-line index is out of bounds");
-}
-
-Description::Media::RTPMap &Description::Media::getFormat(const string &fmt) {
-	for (auto it = mRtpMap.begin(); it != mRtpMap.end(); ++it)
-		if (it->second.format == fmt)
-			return it->second;
+std::vector<uint32_t> Description::Media::getSSRCs() { return mSsrcs; }
 
 
-	throw std::invalid_argument("format was not found");
+optional<string> Description::Media::getCNameForSsrc(uint32_t ssrc) {
+	auto it = mCNameMap.find(ssrc);
+	if (it != mCNameMap.end()) {
+		return it->second;
+	}
+	return nullopt;
 }
 }
 
 
-void Description::Media::removeFormat(const string &fmt) {
-	auto it = mRtpMap.begin();
-	std::vector<int> remed;
+int Description::Media::bitrate() const { return mBas; }
 
 
-	// Remove the actual formats
-	while (it != mRtpMap.end()) {
-		if (it->second.format == fmt) {
-			remed.emplace_back(it->first);
-			it = mRtpMap.erase(it);
-		} else {
-			it++;
-		}
-	}
+void Description::Media::setBitrate(int bitrate) { mBas = bitrate; }
 
 
-	// Remove any other rtpmaps that depend on the formats we just removed
-	it = mRtpMap.begin();
-	while (it != mRtpMap.end()) {
-		auto it2 = it->second.fmtps.begin();
-		bool rem = false;
-		while (it2 != it->second.fmtps.end()) {
-			if (it2->find("apt=") == 0) {
-				for (auto remid : remed) {
-					if (it2->find(std::to_string(remid)) != string::npos) {
-						std::cout << *it2 << ' ' << remid << std::endl;
-						it = mRtpMap.erase(it);
-						rem = true;
-						break;
-					}
-				}
-				break;
-			}
-			it2++;
-		}
-		if (!rem)
-			it++;
-	}
+bool Description::Media::hasPayloadType(int payloadType) const {
+	return mRtpMaps.find(payloadType) != mRtpMaps.end();
 }
 }
 
 
-void Description::Video::addVideoCodec(int payloadType, string codec, optional<string> profile) {
-	RTPMap map(std::to_string(payloadType) + ' ' + codec + "/90000");
-	map.addFB("nack");
-	map.addFB("nack pli");
-	//    map.addFB("ccm fir");
-	map.addFB("goog-remb");
-	if (profile)
-		map.fmtps.emplace_back(*profile);
-	addRTPMap(map);
+std::vector<int> Description::Media::payloadTypes() const {
+	std::vector<int> result;
+	result.reserve(mRtpMaps.size());
+	for (auto it = mRtpMaps.begin(); it != mRtpMaps.end(); ++it)
+		result.push_back(it->first);
 
 
-	/* TODO
-	 *  TIL that Firefox does not properly support the negotiation of RTX! It works, but doesn't
-	 * negotiate the SSRC so we have no idea what SSRC is RTX going to be. Three solutions: One) we
-	 * don't negotitate it and (maybe) break RTX support with Edge. Two) we do negotiate it and
-	 * rebuild the original packet before we send it distribute it to each track. Three) we complain
-	 * to mozilla. This one probably won't do much.
-	 */
-	// RTX Packets
-	// RTPMap rtx(std::to_string(payloadType+1) + " rtx/90000");
-	// // TODO rtx-time is how long can a request be stashed for before needing to resend it.
-	// Needs to be parameterized rtx.addAttribute("apt=" + std::to_string(payloadType) +
-	// ";rtx-time=3000"); addRTPMap(rtx);
+	return result;
 }
 }
 
 
-void Description::Audio::addAudioCodec(int payloadType, string codec, optional<string> profile) {
-	// TODO This 48000/2 should be parameterized
-	RTPMap map(std::to_string(payloadType) + ' ' + codec + "/48000/2");
-	if (profile)
-		map.fmtps.emplace_back(*profile);
-	addRTPMap(map);
-}
+Description::Media::RtpMap *Description::Media::rtpMap(int payloadType) {
+	auto it = mRtpMaps.find(payloadType);
+	if (it == mRtpMaps.end())
+		throw std::invalid_argument("rtpmap not found");
 
 
-void Description::Media::addRTXCodec(unsigned int payloadType, unsigned int originalPayloadType,
-                                     unsigned int clockRate) {
-	RTPMap map(std::to_string(payloadType) + " RTX/" + std::to_string(clockRate));
-	map.fmtps.emplace_back("apt=" + std::to_string(originalPayloadType));
-	addRTPMap(map);
+	return &it->second;
 }
 }
 
 
-void Description::Video::addH264Codec(int pt, optional<string> profile) {
-	addVideoCodec(pt, "H264", profile);
+void Description::Media::addRtpMap(RtpMap map) {
+	auto payloadType = map.payloadType;
+	mRtpMaps.emplace(payloadType, std::move(map));
 }
 }
 
 
-void Description::Video::addVP8Codec(int payloadType) {
-	addVideoCodec(payloadType, "VP8", nullopt);
-}
+void Description::Media::removeRtpMap(int payloadType) {
+	// Remove the actual format
+	mRtpMaps.erase(payloadType);
 
 
-void Description::Video::addVP9Codec(int payloadType) {
-	addVideoCodec(payloadType, "VP9", nullopt);
+	// Remove any other rtpmaps that depend on the format we just removed
+	auto it = mRtpMaps.begin();
+	while (it != mRtpMaps.end()) {
+		const auto &fmtps = it->second.fmtps;
+		if (std::find(fmtps.begin(), fmtps.end(), "apt=" + std::to_string(payloadType)) !=
+		    fmtps.end())
+			it = mRtpMaps.erase(it);
+		else
+			++it;
+	}
 }
 }
 
 
-void Description::Media::setBitrate(int bitrate) { mBas = bitrate; }
+void Description::Media::removeFormat(const string &format) {
+	std::vector<int> payloadTypes;
+	auto it = mRtpMaps.begin();
+	while (it != mRtpMaps.end()) {
+		if (it->second.format == format)
+			payloadTypes.push_back(it->first);
+		else
+			++it;
+	}
 
 
-int Description::Media::getBitrate() const { return mBas; }
+	for (int pt : payloadTypes)
+		removeRtpMap(pt);
+}
 
 
-bool Description::Media::hasPayloadType(int payloadType) const {
-	return mRtpMap.find(payloadType) != mRtpMap.end();
+void Description::Media::addRtxCodec(int payloadType, int origPayloadType, unsigned int clockRate) {
+	RtpMap rtp(std::to_string(payloadType) + " RTX/" + std::to_string(clockRate));
+	rtp.fmtps.emplace_back("apt=" + std::to_string(origPayloadType));
+	addRtpMap(rtp);
 }
 }
 
 
 string Description::Media::generateSdpLines(string_view eol) const {
 string Description::Media::generateSdpLines(string_view eol) const {
@@ -953,21 +953,22 @@ string Description::Media::generateSdpLines(string_view eol) const {
 	sdp << Entry::generateSdpLines(eol);
 	sdp << Entry::generateSdpLines(eol);
 	sdp << "a=rtcp-mux" << eol;
 	sdp << "a=rtcp-mux" << eol;
 
 
-	for (auto it = mRtpMap.begin(); it != mRtpMap.end(); ++it) {
+	for (auto it = mRtpMaps.begin(); it != mRtpMaps.end(); ++it) {
 		auto &map = it->second;
 		auto &map = it->second;
 
 
 		// Create the a=rtpmap
 		// Create the a=rtpmap
-		sdp << "a=rtpmap:" << map.pt << ' ' << map.format << '/' << map.clockRate;
+		sdp << "a=rtpmap:" << map.payloadType << ' ' << map.format << '/' << map.clockRate;
 		if (!map.encParams.empty())
 		if (!map.encParams.empty())
 			sdp << '/' << map.encParams;
 			sdp << '/' << map.encParams;
+
 		sdp << eol;
 		sdp << eol;
 
 
-		for (const auto &val : map.rtcpFbs) {
+		for (const auto &val : map.rtcpFbs)
 			if (val != "transport-cc")
 			if (val != "transport-cc")
-				sdp << "a=rtcp-fb:" << map.pt << ' ' << val << eol;
-		}
+				sdp << "a=rtcp-fb:" << map.payloadType << ' ' << val << eol;
+
 		for (const auto &val : map.fmtps)
 		for (const auto &val : map.fmtps)
-			sdp << "a=fmtp:" << map.pt << ' ' << val << eol;
+			sdp << "a=fmtp:" << map.payloadType << ' ' << val << eol;
 	}
 	}
 
 
 	return sdp.str();
 	return sdp.str();
@@ -979,44 +980,50 @@ void Description::Media::parseSdpLine(string_view line) {
 		auto [key, value] = parse_pair(attr);
 		auto [key, value] = parse_pair(attr);
 
 
 		if (key == "rtpmap") {
 		if (key == "rtpmap") {
-			auto pt = Description::Media::RTPMap::parsePT(value);
-			auto it = mRtpMap.find(pt);
-			if (it == mRtpMap.end()) {
-				it = mRtpMap.insert(std::make_pair(pt, Description::Media::RTPMap(value))).first;
-			} else {
-				it->second.setMLine(value);
-			}
+			auto pt = Description::Media::RtpMap::parsePayloadType(value);
+			auto it = mRtpMaps.find(pt);
+			if (it == mRtpMaps.end())
+				it = mRtpMaps.insert(std::make_pair(pt, Description::Media::RtpMap(value))).first;
+			else
+				it->second.setDescription(value);
+
 		} else if (key == "rtcp-fb") {
 		} else if (key == "rtcp-fb") {
 			size_t p = value.find(' ');
 			size_t p = value.find(' ');
 			int pt = to_integer<int>(value.substr(0, p));
 			int pt = to_integer<int>(value.substr(0, p));
-			auto it = mRtpMap.find(pt);
-			if (it == mRtpMap.end()) {
-				it = mRtpMap.insert(std::make_pair(pt, Description::Media::RTPMap())).first;
-			}
+			auto it = mRtpMaps.find(pt);
+			if (it == mRtpMaps.end())
+				it = mRtpMaps.insert(std::make_pair(pt, Description::Media::RtpMap(pt))).first;
+
 			it->second.rtcpFbs.emplace_back(value.substr(p + 1));
 			it->second.rtcpFbs.emplace_back(value.substr(p + 1));
+
 		} else if (key == "fmtp") {
 		} else if (key == "fmtp") {
 			size_t p = value.find(' ');
 			size_t p = value.find(' ');
 			int pt = to_integer<int>(value.substr(0, p));
 			int pt = to_integer<int>(value.substr(0, p));
-			auto it = mRtpMap.find(pt);
-			if (it == mRtpMap.end())
-				it = mRtpMap.insert(std::make_pair(pt, Description::Media::RTPMap())).first;
+			auto it = mRtpMaps.find(pt);
+			if (it == mRtpMaps.end())
+				it = mRtpMaps.insert(std::make_pair(pt, Description::Media::RtpMap(pt))).first;
+
 			it->second.fmtps.emplace_back(value.substr(p + 1));
 			it->second.fmtps.emplace_back(value.substr(p + 1));
+
 		} else if (key == "rtcp-mux") {
 		} else if (key == "rtcp-mux") {
 			// always added
 			// always added
+
 		} else if (key == "ssrc") {
 		} else if (key == "ssrc") {
 			auto ssrc = to_integer<uint32_t>(value);
 			auto ssrc = to_integer<uint32_t>(value);
-			if (!hasSSRC(ssrc)) {
+			if (!hasSSRC(ssrc))
 				mSsrcs.emplace_back(ssrc);
 				mSsrcs.emplace_back(ssrc);
-			}
+
 			auto cnamePos = value.find("cname:");
 			auto cnamePos = value.find("cname:");
 			if (cnamePos != string::npos) {
 			if (cnamePos != string::npos) {
 				auto cname = value.substr(cnamePos + 6);
 				auto cname = value.substr(cnamePos + 6);
 				mCNameMap.emplace(ssrc, cname);
 				mCNameMap.emplace(ssrc, cname);
 			}
 			}
 			mAttributes.emplace_back(attr);
 			mAttributes.emplace_back(attr);
+
 		} else {
 		} else {
 			Entry::parseSdpLine(line);
 			Entry::parseSdpLine(line);
 		}
 		}
+
 	} else if (match_prefix(line, "b=AS")) {
 	} else if (match_prefix(line, "b=AS")) {
 		mBas = to_integer<int>(line.substr(line.find(':') + 1));
 		mBas = to_integer<int>(line.substr(line.find(':') + 1));
 	} else {
 	} else {
@@ -1024,64 +1031,42 @@ void Description::Media::parseSdpLine(string_view line) {
 	}
 	}
 }
 }
 
 
-void Description::Media::addRTPMap(const Description::Media::RTPMap &map) {
-	mRtpMap.emplace(map.pt, map);
-}
-
-std::vector<uint32_t> Description::Media::getSSRCs() { return mSsrcs; }
-
-optional<string> Description::Media::getCNameForSsrc(uint32_t ssrc) {
-	auto it = mCNameMap.find(ssrc);
-	if (it != mCNameMap.end()) {
-		return it->second;
-	}
-	return nullopt;
+std::map<int, Description::Media::RtpMap>::iterator Description::Media::beginMaps() {
+	return mRtpMaps.begin();
 }
 }
 
 
-std::map<int, Description::Media::RTPMap>::iterator Description::Media::beginMaps() {
-	return mRtpMap.begin();
+std::map<int, Description::Media::RtpMap>::iterator Description::Media::endMaps() {
+	return mRtpMaps.end();
 }
 }
 
 
-std::map<int, Description::Media::RTPMap>::iterator Description::Media::endMaps() {
-	return mRtpMap.end();
+std::map<int, Description::Media::RtpMap>::iterator
+Description::Media::removeMap(std::map<int, Description::Media::RtpMap>::iterator iterator) {
+	return mRtpMaps.erase(iterator);
 }
 }
 
 
-std::map<int, Description::Media::RTPMap>::iterator
-Description::Media::removeMap(std::map<int, Description::Media::RTPMap>::iterator iterator) {
-	return mRtpMap.erase(iterator);
+Description::Media::RtpMap::RtpMap(int payloadType) {
+	this->payloadType = payloadType;
+	this->clockRate = 0;
 }
 }
 
 
-Description::Media::RTPMap::RTPMap(string_view mline) { setMLine(mline); }
-
-void Description::Media::RTPMap::removeFB(const string &str) {
-	auto it = rtcpFbs.begin();
-	while (it != rtcpFbs.end()) {
-		if (it->find(str) != string::npos) {
-			it = rtcpFbs.erase(it);
-		} else
-			it++;
-	}
+int Description::Media::RtpMap::parsePayloadType(string_view mline) {
+	size_t p = mline.find(' ');
+	return to_integer<int>(mline.substr(0, p));
 }
 }
 
 
-void Description::Media::RTPMap::addFB(const string &str) { rtcpFbs.emplace_back(str); }
+Description::Media::RtpMap::RtpMap(string_view description) { setDescription(description); }
 
 
-int Description::Media::RTPMap::parsePT(string_view view) {
-	size_t p = view.find(' ');
-
-	return to_integer<int>(view.substr(0, p));
-}
-
-void Description::Media::RTPMap::setMLine(string_view mline) {
-	size_t p = mline.find(' ');
+void Description::Media::RtpMap::setDescription(string_view description) {
+	size_t p = description.find(' ');
 	if (p == string::npos)
 	if (p == string::npos)
-		throw std::invalid_argument("Invalid m-line");
+		throw std::invalid_argument("Invalid format description");
 
 
-	this->pt = to_integer<int>(mline.substr(0, p));
+	this->payloadType = to_integer<int>(description.substr(0, p));
 
 
-	string_view line = mline.substr(p + 1);
+	string_view line = description.substr(p + 1);
 	size_t spl = line.find('/');
 	size_t spl = line.find('/');
 	if (spl == string::npos)
 	if (spl == string::npos)
-		throw std::invalid_argument("Invalid m-line");
+		throw std::invalid_argument("Invalid format description");
 
 
 	this->format = line.substr(0, spl);
 	this->format = line.substr(0, spl);
 
 
@@ -1098,9 +1083,49 @@ void Description::Media::RTPMap::setMLine(string_view mline) {
 	}
 	}
 }
 }
 
 
+void Description::Media::RtpMap::addFeedback(string fb) {
+	if (std::find(rtcpFbs.begin(), rtcpFbs.end(), fb) == rtcpFbs.end())
+		fmtps.emplace_back(std::move(fb));
+}
+
+void Description::Media::RtpMap::removeFeedback(const string &str) {
+	auto it = rtcpFbs.begin();
+	while (it != rtcpFbs.end()) {
+		if (it->find(str) != string::npos)
+			it = rtcpFbs.erase(it);
+		else
+			it++;
+	}
+}
+
+void Description::Media::RtpMap::addParameter(string p) {
+	if (std::find(fmtps.begin(), fmtps.end(), p) == fmtps.end())
+		fmtps.emplace_back(std::move(p));
+}
+
+void Description::Media::RtpMap::removeParameter(const string &str) {
+	auto it = fmtps.begin();
+	while (it != fmtps.end()) {
+		if (it->find(str) != string::npos)
+			it = fmtps.erase(it);
+		else
+			it++;
+	}
+}
+
 Description::Audio::Audio(string mid, Direction dir)
 Description::Audio::Audio(string mid, Direction dir)
     : Media("audio 9 UDP/TLS/RTP/SAVPF", std::move(mid), dir) {}
     : Media("audio 9 UDP/TLS/RTP/SAVPF", std::move(mid), dir) {}
 
 
+void Description::Audio::addAudioCodec(int payloadType, string codec, optional<string> profile) {
+	// TODO This 48000/2 should be parameterized
+	RtpMap map(std::to_string(payloadType) + ' ' + codec + "/48000/2");
+
+	if (profile)
+		map.fmtps.emplace_back(*profile);
+
+	addRtpMap(map);
+}
+
 void Description::Audio::addOpusCodec(int payloadType, optional<string> profile) {
 void Description::Audio::addOpusCodec(int payloadType, optional<string> profile) {
 	addAudioCodec(payloadType, "OPUS", profile);
 	addAudioCodec(payloadType, "OPUS", profile);
 }
 }
@@ -1108,6 +1133,45 @@ void Description::Audio::addOpusCodec(int payloadType, optional<string> profile)
 Description::Video::Video(string mid, Direction dir)
 Description::Video::Video(string mid, Direction dir)
     : Media("video 9 UDP/TLS/RTP/SAVPF", std::move(mid), dir) {}
     : Media("video 9 UDP/TLS/RTP/SAVPF", std::move(mid), dir) {}
 
 
+void Description::Video::addVideoCodec(int payloadType, string codec, optional<string> profile) {
+	RtpMap map(std::to_string(payloadType) + ' ' + codec + "/90000");
+
+	map.addFeedback("nack");
+	map.addFeedback("nack pli");
+	// map.addFB("ccm fir");
+	map.addFeedback("goog-remb");
+
+	if (profile)
+		map.fmtps.emplace_back(*profile);
+
+	addRtpMap(map);
+
+	/* TODO
+	 *  TIL that Firefox does not properly support the negotiation of RTX! It works, but doesn't
+	 * negotiate the SSRC so we have no idea what SSRC is RTX going to be. Three solutions: One) we
+	 * don't negotitate it and (maybe) break RTX support with Edge. Two) we do negotiate it and
+	 * rebuild the original packet before we send it distribute it to each track. Three) we complain
+	 * to mozilla. This one probably won't do much.
+	 */
+	// RTX Packets
+	// Format rtx(std::to_string(payloadType+1) + " rtx/90000");
+	// // TODO rtx-time is how long can a request be stashed for before needing to resend it.
+	// Needs to be parameterized rtx.addAttribute("apt=" + std::to_string(payloadType) +
+	// ";rtx-time=3000"); addFormat(rtx);
+}
+
+void Description::Video::addH264Codec(int pt, optional<string> profile) {
+	addVideoCodec(pt, "H264", profile);
+}
+
+void Description::Video::addVP8Codec(int payloadType) {
+	addVideoCodec(payloadType, "VP8", nullopt);
+}
+
+void Description::Video::addVP9Codec(int payloadType) {
+	addVideoCodec(payloadType, "VP9", nullopt);
+}
+
 Description::Type Description::stringToType(const string &typeString) {
 Description::Type Description::stringToType(const string &typeString) {
 	using TypeMap_t = std::unordered_map<string, Type>;
 	using TypeMap_t = std::unordered_map<string, Type>;
 	static const TypeMap_t TypeMap = {{"unspec", Type::Unspec},
 	static const TypeMap_t TypeMap = {{"unspec", Type::Unspec},