|
@@ -149,6 +149,8 @@ Description::Description(const string &sdp, Type type, Role role)
|
|
mEnded = true;
|
|
mEnded = true;
|
|
} else if (current) {
|
|
} else if (current) {
|
|
current->parseSdpLine(std::move(line));
|
|
current->parseSdpLine(std::move(line));
|
|
|
|
+ } else {
|
|
|
|
+ mAttributes.emplace_back(attr);
|
|
}
|
|
}
|
|
|
|
|
|
} else if (current) {
|
|
} else if (current) {
|
|
@@ -215,25 +217,42 @@ 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 {
|
|
|
|
- for (const Candidate &other : mCandidates)
|
|
|
|
- if (candidate == other)
|
|
|
|
- return true;
|
|
|
|
|
|
+std::vector<string> Description::Entry::attributes() const { return mAttributes; }
|
|
|
|
|
|
- return false;
|
|
|
|
|
|
+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_if(mAttributes.begin(), mAttributes.end(),
|
|
|
|
+ [&](const auto &a) { return a == attr || parse_pair(a).first == attr; }),
|
|
|
|
+ mAttributes.end());
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+std::vector<Candidate> Description::candidates() const { return mCandidates; }
|
|
|
|
+
|
|
|
|
+std::vector<Candidate> Description::extractCandidates() {
|
|
|
|
+ std::vector<Candidate> result;
|
|
|
|
+ std::swap(mCandidates, result);
|
|
|
|
+ mEnded = false;
|
|
|
|
+ return result;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+bool Description::hasCandidate(const Candidate &candidate) const {
|
|
|
|
+ return std::find(mCandidates.begin(), mCandidates.end(), candidate) != mCandidates.end();
|
|
}
|
|
}
|
|
|
|
|
|
void Description::addCandidate(Candidate candidate) {
|
|
void Description::addCandidate(Candidate candidate) {
|
|
candidate.hintMid(bundleMid());
|
|
candidate.hintMid(bundleMid());
|
|
|
|
|
|
- for (const Candidate &other : mCandidates)
|
|
|
|
- if (candidate == other)
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
- mCandidates.emplace_back(std::move(candidate));
|
|
|
|
|
|
+ if (!hasCandidate(candidate))
|
|
|
|
+ mCandidates.emplace_back(std::move(candidate));
|
|
}
|
|
}
|
|
|
|
|
|
void Description::addCandidates(std::vector<Candidate> candidates) {
|
|
void Description::addCandidates(std::vector<Candidate> candidates) {
|
|
@@ -243,13 +262,6 @@ void Description::addCandidates(std::vector<Candidate> candidates) {
|
|
|
|
|
|
void Description::endCandidates() { mEnded = true; }
|
|
void Description::endCandidates() { mEnded = true; }
|
|
|
|
|
|
-std::vector<Candidate> Description::extractCandidates() {
|
|
|
|
- std::vector<Candidate> result;
|
|
|
|
- std::swap(mCandidates, result);
|
|
|
|
- mEnded = false;
|
|
|
|
- return result;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
Description::operator string() const { return generateSdp("\r\n"); }
|
|
Description::operator string() const { return generateSdp("\r\n"); }
|
|
|
|
|
|
string Description::generateSdp(string_view eol) const {
|
|
string Description::generateSdp(string_view eol) const {
|
|
@@ -290,6 +302,9 @@ string Description::generateSdp(string_view eol) const {
|
|
if (mFingerprint)
|
|
if (mFingerprint)
|
|
sdp << "a=fingerprint:sha-256 " << *mFingerprint << eol;
|
|
sdp << "a=fingerprint:sha-256 " << *mFingerprint << eol;
|
|
|
|
|
|
|
|
+ for (const auto &attr : mAttributes)
|
|
|
|
+ sdp << "a=" << attr << eol;
|
|
|
|
+
|
|
auto cand = defaultCandidate();
|
|
auto cand = defaultCandidate();
|
|
const string addr = cand && cand->isResolved()
|
|
const string addr = cand && cand->isResolved()
|
|
? (string(cand->family() == Candidate::Family::Ipv6 ? "IP6" : "IP4") +
|
|
? (string(cand->family() == Candidate::Family::Ipv6 ? "IP6" : "IP4") +
|
|
@@ -350,6 +365,9 @@ string Description::generateApplicationSdp(string_view eol) const {
|
|
if (mFingerprint)
|
|
if (mFingerprint)
|
|
sdp << "a=fingerprint:sha-256 " << *mFingerprint << eol;
|
|
sdp << "a=fingerprint:sha-256 " << *mFingerprint << eol;
|
|
|
|
|
|
|
|
+ for (const auto &attr : mAttributes)
|
|
|
|
+ sdp << "a=" << attr << eol;
|
|
|
|
+
|
|
// Candidates
|
|
// Candidates
|
|
for (const auto &candidate : mCandidates)
|
|
for (const auto &candidate : mCandidates)
|
|
sdp << string(candidate) << eol;
|
|
sdp << string(candidate) << eol;
|
|
@@ -458,11 +476,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 +498,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 +524,43 @@ 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::attributes() const { return mAttributes; }
|
|
|
|
+
|
|
|
|
+void Description::addAttribute(string attr) {
|
|
|
|
+ if (std::find(mAttributes.begin(), mAttributes.end(), attr) == mAttributes.end())
|
|
|
|
+ mAttributes.emplace_back(std::move(attr));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void Description::removeAttribute(const string &attr) {
|
|
|
|
+ mAttributes.erase(
|
|
|
|
+ std::remove_if(mAttributes.begin(), mAttributes.end(),
|
|
|
|
+ [&](const auto &a) { return a == attr || parse_pair(a).first == 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 +577,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,10 +622,8 @@ string Description::Entry::generateSdpLines(string_view eol) const {
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
- for (const auto &attr : mAttributes) {
|
|
|
|
- if (attr.find("extmap") == string::npos && attr.find("rtcp-rsize") == string::npos)
|
|
|
|
- sdp << "a=" << attr << eol;
|
|
|
|
- }
|
|
|
|
|
|
+ for (const auto &attr : mAttributes)
|
|
|
|
+ sdp << "a=" << attr << eol;
|
|
|
|
|
|
return sdp.str();
|
|
return sdp.str();
|
|
}
|
|
}
|
|
@@ -574,16 +633,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")
|
|
@@ -595,7 +654,7 @@ void Description::Entry::parseSdpLine(string_view line) {
|
|
else if (key == "bundle-only") {
|
|
else if (key == "bundle-only") {
|
|
// always added
|
|
// always added
|
|
} else
|
|
} else
|
|
- mAttributes.emplace_back(line.substr(2));
|
|
|
|
|
|
+ mAttributes.emplace_back(attr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -608,29 +667,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) {
|
|
@@ -670,7 +724,7 @@ void Description::Entry::ExtMap::setDescription(string_view description) {
|
|
}
|
|
}
|
|
|
|
|
|
void Description::Media::addSSRC(uint32_t ssrc, optional<string> name, optional<string> msid,
|
|
void Description::Media::addSSRC(uint32_t ssrc, optional<string> name, optional<string> msid,
|
|
- optional<string> trackID) {
|
|
|
|
|
|
+ optional<string> trackId) {
|
|
if (name) {
|
|
if (name) {
|
|
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + *name);
|
|
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " cname:" + *name);
|
|
mCNameMap.emplace(ssrc, *name);
|
|
mCNameMap.emplace(ssrc, *name);
|
|
@@ -680,32 +734,23 @@ void Description::Media::addSSRC(uint32_t ssrc, optional<string> name, optional<
|
|
|
|
|
|
if (msid)
|
|
if (msid)
|
|
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " msid:" + *msid + " " +
|
|
mAttributes.emplace_back("ssrc:" + std::to_string(ssrc) + " msid:" + *msid + " " +
|
|
- trackID.value_or(*msid));
|
|
|
|
|
|
+ trackId.value_or(*msid));
|
|
|
|
|
|
mSsrcs.emplace_back(ssrc);
|
|
mSsrcs.emplace_back(ssrc);
|
|
}
|
|
}
|
|
|
|
|
|
-void Description::Media::removeSSRC(uint32_t oldSSRC) {
|
|
|
|
- auto it = mAttributes.begin();
|
|
|
|
- while (it != mAttributes.end()) {
|
|
|
|
- if (match_prefix(*it, "ssrc:" + std::to_string(oldSSRC)))
|
|
|
|
- it = mAttributes.erase(it);
|
|
|
|
- else
|
|
|
|
- ++it;
|
|
|
|
- }
|
|
|
|
|
|
+void Description::Media::removeSSRC(uint32_t ssrc) {
|
|
|
|
+ string prefix = "ssrc:" + std::to_string(ssrc);
|
|
|
|
+ mAttributes.erase(std::remove_if(mAttributes.begin(), mAttributes.end(),
|
|
|
|
+ [&](const auto &a) { return match_prefix(a, prefix); }),
|
|
|
|
+ mAttributes.end());
|
|
|
|
|
|
- auto jt = mSsrcs.begin();
|
|
|
|
- while (jt != mSsrcs.end()) {
|
|
|
|
- if (*jt == oldSSRC)
|
|
|
|
- jt = mSsrcs.erase(jt);
|
|
|
|
- else
|
|
|
|
- ++jt;
|
|
|
|
- }
|
|
|
|
|
|
+ mSsrcs.erase(std::remove(mSsrcs.begin(), mSsrcs.end(), ssrc), mSsrcs.end());
|
|
}
|
|
}
|
|
|
|
|
|
-void Description::Media::replaceSSRC(uint32_t oldSSRC, uint32_t ssrc, optional<string> name,
|
|
|
|
|
|
+void Description::Media::replaceSSRC(uint32_t old, uint32_t ssrc, optional<string> name,
|
|
optional<string> msid, optional<string> trackID) {
|
|
optional<string> msid, optional<string> trackID) {
|
|
- removeSSRC(oldSSRC);
|
|
|
|
|
|
+ removeSSRC(old);
|
|
addSSRC(ssrc, std::move(name), std::move(msid), std::move(trackID));
|
|
addSSRC(ssrc, std::move(name), std::move(msid), std::move(trackID));
|
|
}
|
|
}
|
|
|
|
|
|
@@ -780,7 +825,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 +848,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:
|
|
@@ -829,120 +874,86 @@ Description::Media Description::Media::reciprocate() const {
|
|
reciprocated.mSsrcs.clear();
|
|
reciprocated.mSsrcs.clear();
|
|
reciprocated.mCNameMap.clear();
|
|
reciprocated.mCNameMap.clear();
|
|
|
|
|
|
- return reciprocated;
|
|
|
|
-}
|
|
|
|
|
|
+ // Remove rtcp-rsize attribute as Reduced-Size RTCP is not supported (see RFC 5506)
|
|
|
|
+ reciprocated.removeAttribute("rtcp-rsize");
|
|
|
|
|
|
-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");
|
|
|
|
|
|
+ return reciprocated;
|
|
}
|
|
}
|
|
|
|
|
|
-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 +964,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 +991,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 +1042,42 @@ void Description::Media::parseSdpLine(string_view line) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-void Description::Media::addRTPMap(const Description::Media::RTPMap &map) {
|
|
|
|
- mRtpMap.emplace(map.pt, map);
|
|
|
|
|
|
+std::map<int, Description::Media::RtpMap>::iterator Description::Media::beginMaps() {
|
|
|
|
+ return mRtpMaps.begin();
|
|
}
|
|
}
|
|
|
|
|
|
-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::endMaps() {
|
|
|
|
+ return mRtpMaps.end();
|
|
}
|
|
}
|
|
|
|
|
|
-std::map<int, Description::Media::RTPMap>::iterator Description::Media::beginMaps() {
|
|
|
|
- return mRtpMap.begin();
|
|
|
|
|
|
+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::endMaps() {
|
|
|
|
- return mRtpMap.end();
|
|
|
|
|
|
+Description::Media::RtpMap::RtpMap(int payloadType) {
|
|
|
|
+ this->payloadType = payloadType;
|
|
|
|
+ this->clockRate = 0;
|
|
}
|
|
}
|
|
|
|
|
|
-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(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 +1094,47 @@ 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) {
|
|
|
|
+ fmtps.erase(std::remove_if(fmtps.begin(), fmtps.end(),
|
|
|
|
+ [&](const auto &p) { return p.find(str) != string::npos; }),
|
|
|
|
+ fmtps.end());
|
|
|
|
+}
|
|
|
|
+
|
|
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) {
|
|
|
|
+ if (codec.find('/') == string::npos)
|
|
|
|
+ codec += "/48000/2";
|
|
|
|
+
|
|
|
|
+ RtpMap map(std::to_string(payloadType) + ' ' + codec);
|
|
|
|
+
|
|
|
|
+ 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 +1142,48 @@ 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) {
|
|
|
|
+ if (codec.find('/') == string::npos)
|
|
|
|
+ codec += "/90000";
|
|
|
|
+
|
|
|
|
+ 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},
|