|
@@ -215,7 +215,8 @@ void Description::addIceOption(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 {
|
|
@@ -458,11 +459,14 @@ variant<Description::Media *, Description::Application *> Description::media(uns
|
|
|
auto result = dynamic_cast<Application *>(entry.get());
|
|
|
if (!result)
|
|
|
throw std::logic_error("Bad type of application in description");
|
|
|
+
|
|
|
return result;
|
|
|
+
|
|
|
} else {
|
|
|
auto result = dynamic_cast<Media *>(entry.get());
|
|
|
if (!result)
|
|
|
throw std::logic_error("Bad type of media in description");
|
|
|
+
|
|
|
return result;
|
|
|
}
|
|
|
}
|
|
@@ -477,11 +481,14 @@ Description::media(unsigned int index) const {
|
|
|
auto result = dynamic_cast<Application *>(entry.get());
|
|
|
if (!result)
|
|
|
throw std::logic_error("Bad type of application in description");
|
|
|
+
|
|
|
return result;
|
|
|
+
|
|
|
} else {
|
|
|
auto result = dynamic_cast<Media *>(entry.get());
|
|
|
if (!result)
|
|
|
throw std::logic_error("Bad type of media in description");
|
|
|
+
|
|
|
return result;
|
|
|
}
|
|
|
}
|
|
@@ -500,6 +507,40 @@ Description::Entry::Entry(const string &mline, string mid, Direction 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"); }
|
|
|
|
|
|
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=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;
|
|
|
|
|
|
sdp << "a=extmap:" << map.id;
|
|
@@ -561,6 +602,7 @@ string Description::Entry::generateSdpLines(string_view eol) const {
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
+ // TODO
|
|
|
for (const auto &attr : mAttributes) {
|
|
|
if (attr.find("extmap") == string::npos && attr.find("rtcp-rsize") == string::npos)
|
|
|
sdp << "a=" << attr << eol;
|
|
@@ -574,16 +616,16 @@ void Description::Entry::parseSdpLine(string_view line) {
|
|
|
string_view attr = line.substr(2);
|
|
|
auto [key, value] = parse_pair(attr);
|
|
|
|
|
|
- if (key == "mid")
|
|
|
+ if (key == "mid") {
|
|
|
mMid = value;
|
|
|
- else if (key == "extmap") {
|
|
|
+ } else if (key == "extmap") {
|
|
|
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);
|
|
|
- }
|
|
|
+
|
|
|
} else if (attr == "sendonly")
|
|
|
mDirection = Direction::SendOnly;
|
|
|
else if (attr == "recvonly")
|
|
@@ -608,29 +650,24 @@ Description::Entry::removeAttribute(std::vector<string>::iterator 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() {
|
|
|
- return mExtMap.begin();
|
|
|
+ return mExtMaps.begin();
|
|
|
}
|
|
|
|
|
|
std::map<int, Description::Entry::ExtMap>::iterator Description::Entry::endExtMaps() {
|
|
|
- return mExtMap.end();
|
|
|
+ return mExtMaps.end();
|
|
|
}
|
|
|
|
|
|
std::map<int, Description::Entry::ExtMap>::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); }
|
|
|
|
|
|
-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) {
|
|
@@ -780,7 +817,7 @@ Description::Media::Media(const string &mline, string mid, Direction dir)
|
|
|
string Description::Media::description() const {
|
|
|
std::ostringstream desc;
|
|
|
desc << Entry::description();
|
|
|
- for (auto it = mRtpMap.begin(); it != mRtpMap.end(); ++it)
|
|
|
+ for (auto it = mRtpMaps.begin(); it != mRtpMaps.end(); ++it)
|
|
|
desc << ' ' << it->first;
|
|
|
|
|
|
return desc.str();
|
|
@@ -803,7 +840,7 @@ Description::Media Description::Media::reciprocate() const {
|
|
|
}
|
|
|
|
|
|
// 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;
|
|
|
switch (map.direction) {
|
|
|
case Direction::RecvOnly:
|
|
@@ -832,117 +869,80 @@ Description::Media Description::Media::reciprocate() const {
|
|
|
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 {
|
|
@@ -953,21 +953,22 @@ string Description::Media::generateSdpLines(string_view eol) const {
|
|
|
sdp << Entry::generateSdpLines(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;
|
|
|
|
|
|
// 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())
|
|
|
sdp << '/' << map.encParams;
|
|
|
+
|
|
|
sdp << eol;
|
|
|
|
|
|
- for (const auto &val : map.rtcpFbs) {
|
|
|
+ for (const auto &val : map.rtcpFbs)
|
|
|
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)
|
|
|
- sdp << "a=fmtp:" << map.pt << ' ' << val << eol;
|
|
|
+ sdp << "a=fmtp:" << map.payloadType << ' ' << val << eol;
|
|
|
}
|
|
|
|
|
|
return sdp.str();
|
|
@@ -979,44 +980,50 @@ void Description::Media::parseSdpLine(string_view line) {
|
|
|
auto [key, value] = parse_pair(attr);
|
|
|
|
|
|
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") {
|
|
|
size_t p = value.find(' ');
|
|
|
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));
|
|
|
+
|
|
|
} else if (key == "fmtp") {
|
|
|
size_t p = value.find(' ');
|
|
|
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));
|
|
|
+
|
|
|
} else if (key == "rtcp-mux") {
|
|
|
// always added
|
|
|
+
|
|
|
} else if (key == "ssrc") {
|
|
|
auto ssrc = to_integer<uint32_t>(value);
|
|
|
- if (!hasSSRC(ssrc)) {
|
|
|
+ if (!hasSSRC(ssrc))
|
|
|
mSsrcs.emplace_back(ssrc);
|
|
|
- }
|
|
|
+
|
|
|
auto cnamePos = value.find("cname:");
|
|
|
if (cnamePos != string::npos) {
|
|
|
auto cname = value.substr(cnamePos + 6);
|
|
|
mCNameMap.emplace(ssrc, cname);
|
|
|
}
|
|
|
mAttributes.emplace_back(attr);
|
|
|
+
|
|
|
} else {
|
|
|
Entry::parseSdpLine(line);
|
|
|
}
|
|
|
+
|
|
|
} else if (match_prefix(line, "b=AS")) {
|
|
|
mBas = to_integer<int>(line.substr(line.find(':') + 1));
|
|
|
} 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)
|
|
|
- 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('/');
|
|
|
if (spl == string::npos)
|
|
|
- throw std::invalid_argument("Invalid m-line");
|
|
|
+ throw std::invalid_argument("Invalid format description");
|
|
|
|
|
|
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)
|
|
|
: 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) {
|
|
|
addAudioCodec(payloadType, "OPUS", profile);
|
|
|
}
|
|
@@ -1108,6 +1133,45 @@ void Description::Audio::addOpusCodec(int payloadType, optional<string> profile)
|
|
|
Description::Video::Video(string mid, Direction 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) {
|
|
|
using TypeMap_t = std::unordered_map<string, Type>;
|
|
|
static const TypeMap_t TypeMap = {{"unspec", Type::Unspec},
|