/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
void SdpExtmapAttributeList::Serialize(std::ostream& os) const { for (auto i = mExtmaps.begin(); i != mExtmaps.end(); ++i) {
os << "a=" << mType << ":" << i->entry; if (i->direction_specified) {
os << "/" << i->direction;
}
os << " " << i->extensionname; if (i->extensionattributes.length()) {
os << " " << i->extensionattributes;
}
os << CRLF;
}
}
void SdpFingerprintAttributeList::Serialize(std::ostream& os) const { for (auto i = mFingerprints.begin(); i != mFingerprints.end(); ++i) {
os << "a=" << mType << ":" << i->hashFunc << " "
<< FormatFingerprint(i->fingerprint) << CRLF;
}
}
// Format the fingerprint in RFC 4572 Section 5 attribute format
std::string SdpFingerprintAttributeList::FormatFingerprint( const std::vector<uint8_t>& fp) { if (fp.empty()) {
MOZ_ASSERT(false, "Cannot format an empty fingerprint."); return"";
}
std::ostringstream os; for (auto i = fp.begin(); i != fp.end(); ++i) {
os << ":" << std::hex << std::uppercase << std::setw(2) << std::setfill('0')
<< static_cast<uint32_t>(*i);
} return os.str().substr(1);
}
void SdpFmtpAttributeList::Serialize(std::ostream& os) const { for (auto i = mFmtps.begin(); i != mFmtps.end(); ++i) { if (i->parameters && i->parameters->ShouldSerialize()) {
os << "a=" << mType << ":" << i->format << " ";
i->parameters->Serialize(os);
os << CRLF;
}
}
}
void SdpGroupAttributeList::Serialize(std::ostream& os) const { for (auto i = mGroups.begin(); i != mGroups.end(); ++i) {
os << "a=" << mType << ":" << i->semantics; for (auto j = i->tags.begin(); j != i->tags.end(); ++j) {
os << " " << (*j);
}
os << CRLF;
}
}
// We're just using an SdpStringAttribute for this right now #if 0 void SdpIdentityAttribute::Serialize(std::ostream& os) const
{
os << "a=" << mType << ":" << mAssertion; for (auto i = mExtensions.begin(); i != mExtensions.end(); i++) {
os << (i == mExtensions.begin() ? " " : ";") << (*i);
}
os << CRLF;
} #endif
// Class to help with omitting a leading delimiter for the first item in a list class SkipFirstDelimiter { public: explicit SkipFirstDelimiter(const std::string& delim)
: mDelim(delim), mFirst(true) {}
std::ostream& print(std::ostream& os) { if (!mFirst) {
os << mDelim;
}
mFirst = false; return os;
}
void SdpImageattrAttributeList::XYRange::Serialize(std::ostream& os) const { if (discreteValues.empty()) {
os << "[" << min << ":"; if (step != 1) {
os << step << ":";
}
os << max << "]";
} elseif (discreteValues.size() == 1) {
os << discreteValues.front();
} else {
os << "[";
SkipFirstDelimiter comma(","); for (auto value : discreteValues) {
os << comma << value;
}
os << "]";
}
}
template <typename T> bool GetUnsigned(std::istream& is, T min, T max, T* value, std::string* error) { if (PeekChar(is, error) == '-') {
*error = "Value is less than 0"; returnfalse;
}
is >> std::noskipws >> *value;
if (is.fail()) {
*error = "Malformed"; returnfalse;
}
if (*value < min) {
*error = "Value too small"; returnfalse;
}
if (*value > max) {
*error = "Value too large"; returnfalse;
}
bool SdpImageattrAttributeList::XYRange::ParseDiscreteValues(
std::istream& is, std::string* error) { do {
uint32_t value; if (!GetXYValue(is, &value, error)) { returnfalse;
}
discreteValues.push_back(value);
} while (SkipChar(is, ',', error));
return SkipChar(is, ']', error);
}
bool SdpImageattrAttributeList::XYRange::ParseAfterMin(std::istream& is,
std::string* error) { // We have already parsed "[320:", and now expect another uint
uint32_t value; if (!GetXYValue(is, &value, error)) { returnfalse;
}
if (SkipChar(is, ':', error)) { // Range with step eg [320:16:640]
step = value; // Now |value| should be the max if (!GetXYValue(is, &value, error)) { returnfalse;
}
}
max = value; if (min >= max) {
*error = "Min is not smaller than max"; returnfalse;
}
return SkipChar(is, ']', error);
}
bool SdpImageattrAttributeList::XYRange::ParseAfterBracket(std::istream& is,
std::string* error) { // Either a range, or a list of discrete values // [320:640], [320:16:640], or [320,640]
uint32_t value; if (!GetXYValue(is, &value, error)) { returnfalse;
}
if (SkipChar(is, ':', error)) { // Range - [640:480] or [640:16:480]
min = value; return ParseAfterMin(is, error);
}
if (SkipChar(is, ',', error)) {
discreteValues.push_back(value); return ParseDiscreteValues(is, error);
}
*error = "Expected \':\' or \',\'"; returnfalse;
}
bool SdpImageattrAttributeList::XYRange::Parse(std::istream& is,
std::string* error) { if (SkipChar(is, '[', error)) { return ParseAfterBracket(is, error);
}
// Single discrete value
uint32_t value; if (!GetXYValue(is, &value, error)) { returnfalse;
}
discreteValues.push_back(value);
bool SdpImageattrAttributeList::SRange::ParseDiscreteValues(
std::istream& is, std::string* error) { do { float value; if (!GetSPValue(is, &value, error)) { returnfalse;
}
discreteValues.push_back(value);
} while (SkipChar(is, ',', error));
return SkipChar(is, ']', error);
}
bool SdpImageattrAttributeList::SRange::ParseAfterMin(std::istream& is,
std::string* error) { if (!GetSPValue(is, &max, error)) { returnfalse;
}
if (min >= max) {
*error = "Min is not smaller than max"; returnfalse;
}
return SkipChar(is, ']', error);
}
bool SdpImageattrAttributeList::SRange::ParseAfterBracket(std::istream& is,
std::string* error) { // Either a range, or a list of discrete values float value; if (!GetSPValue(is, &value, error)) { returnfalse;
}
if (SkipChar(is, '-', error)) {
min = value; return ParseAfterMin(is, error);
}
if (SkipChar(is, ',', error)) {
discreteValues.push_back(value); return ParseDiscreteValues(is, error);
}
*error = "Expected either \'-\' or \',\'"; returnfalse;
}
bool SdpImageattrAttributeList::SRange::Parse(std::istream& is,
std::string* error) { if (SkipChar(is, '[', error)) { return ParseAfterBracket(is, error);
}
// Single discrete value float value; if (!GetSPValue(is, &value, error)) { returnfalse;
}
discreteValues.push_back(value); returntrue;
}
bool SdpImageattrAttributeList::PRange::Parse(std::istream& is,
std::string* error) { if (!SkipChar(is, '[', error)) { returnfalse;
}
if (!GetSPValue(is, &min, error)) { returnfalse;
}
if (!SkipChar(is, '-', error)) { returnfalse;
}
if (!GetSPValue(is, &max, error)) { returnfalse;
}
if (min >= max) {
*error = "min must be smaller than max"; returnfalse;
}
if (!SkipChar(is, ']', error)) { returnfalse;
} returntrue;
}
void SdpImageattrAttributeList::SRange::Serialize(std::ostream& os) const {
os << std::setprecision(4) << std::fixed; if (discreteValues.empty()) {
os << "[" << min << "-" << max << "]";
} elseif (discreteValues.size() == 1) {
os << discreteValues.front();
} else {
os << "[";
SkipFirstDelimiter comma(","); for (auto value : discreteValues) {
os << comma << value;
}
os << "]";
}
}
void SdpImageattrAttributeList::PRange::Serialize(std::ostream& os) const {
os << std::setprecision(4) << std::fixed;
os << "[" << min << "-" << max << "]";
}
if (!is) {
*error = "Expected closing brace"; returnfalse;
}
returntrue;
}
// Assumptions: // 1. If the value contains '[' or ']', they are balanced. // 2. The value contains no ',' outside of brackets. staticbool SkipValue(std::istream& is, std::string* error) { while (is) { switch (PeekChar(is, error)) { case',': case']': returntrue; case'[': if (!SkipBraces(is, error)) { returnfalse;
} break; default:
is.get();
}
}
*error = "No closing \']\' on set"; returnfalse;
}
bool SdpImageattrAttributeList::Set::Parse(std::istream& is,
std::string* error) { if (!SkipChar(is, '[', error)) { returnfalse;
}
void SdpMsidAttributeList::Serialize(std::ostream& os) const { for (auto i = mMsids.begin(); i != mMsids.end(); ++i) {
os << "a=" << mType << ":" << i->identifier; if (i->appdata.length()) {
os << " " << i->appdata;
}
os << CRLF;
}
}
void SdpMsidSemanticAttributeList::Serialize(std::ostream& os) const { for (auto i = mMsidSemantics.begin(); i != mMsidSemantics.end(); ++i) {
os << "a=" << mType << ":" << i->semantic; for (auto j = i->msids.begin(); j != i->msids.end(); ++j) {
os << " " << *j;
}
os << CRLF;
}
}
void SdpRemoteCandidatesAttribute::Serialize(std::ostream& os) const { if (mCandidates.empty()) { return;
}
os << "a=" << mType; for (auto i = mCandidates.begin(); i != mCandidates.end(); i++) {
os << (i == mCandidates.begin() ? ":" : " ") << i->id << " " << i->address
<< " " << i->port;
}
os << CRLF;
}
// Remove this function. See Bug 1469702 bool SdpRidAttributeList::Rid::ParseParameters(std::istream& is,
std::string* error) { if (!PeekChar(is, error)) { // No parameters returntrue;
}
do {
is >> std::ws;
std::string key = ParseKey(is, error); if (key.empty()) { returnfalse; // Illegal trailing cruft
}
// This allows pt= to appear anywhere, instead of only at the beginning, but // this ends up being significantly less code. if (key == "pt") { if (!ParseFormats(is, error)) { returnfalse;
}
} elseif (key == "max-width") { if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxWidth,
error)) { returnfalse;
}
} elseif (key == "max-height") { if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxHeight,
error)) { returnfalse;
}
} elseif (key == "max-fps") {
uint32_t maxFps; if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &maxFps, error)) { returnfalse;
}
constraints.maxFps = Some(maxFps);
} elseif (key == "max-fs") { if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxFs,
error)) { returnfalse;
}
} elseif (key == "max-br") { if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxBr,
error)) { returnfalse;
}
} elseif (key == "max-pps") { if (!GetUnsigned<uint32_t>(is, 0, UINT32_MAX, &constraints.maxPps,
error)) { returnfalse;
}
} elseif (key == "depend") { if (!ParseDepend(is, error)) { returnfalse;
}
} else {
(void)ParseToken(is, ";", error);
}
} while (SkipChar(is, ';', error)); returntrue;
}
// Remove this function. See Bug 1469702 bool SdpRidAttributeList::Rid::ParseDepend(std::istream& is,
std::string* error) { do {
std::string id = ParseToken(is, ",;", error); if (id.empty()) { returnfalse;
}
dependIds.push_back(id);
} while (SkipChar(is, ',', error));
returntrue;
}
// Remove this function. See Bug 1469702 bool SdpRidAttributeList::Rid::ParseFormats(std::istream& is,
std::string* error) { do {
uint16_t fmt; if (!GetUnsigned<uint16_t>(is, 0, 127, &fmt, error)) { returnfalse;
}
formats.push_back(fmt);
} while (SkipChar(is, ',', error));
returntrue;
}
void SdpRidAttributeList::Rid::SerializeParameters(std::ostream& os) const { if (!HasParameters()) { return;
}
os << " ";
SkipFirstDelimiter semic(";");
if (!formats.empty()) {
os << semic << "pt=";
SkipFirstDelimiter comma(","); for (uint16_t fmt : formats) {
os << comma << fmt;
}
}
if (constraints.maxWidth) {
os << semic << "max-width=" << constraints.maxWidth;
}
if (constraints.maxHeight) {
os << semic << "max-height=" << constraints.maxHeight;
}
if (constraints.maxFps) {
os << semic << "max-fps=" << constraints.maxFps;
}
if (constraints.maxFs) {
os << semic << "max-fs=" << constraints.maxFs;
}
if (constraints.maxBr) {
os << semic << "max-br=" << constraints.maxBr;
}
if (constraints.maxPps) {
os << semic << "max-pps=" << constraints.maxPps;
}
if (!dependIds.empty()) {
os << semic << "depend=";
SkipFirstDelimiter comma(","); for (const std::string& id : dependIds) {
os << comma << id;
}
}
}
// Remove this function. See Bug 1469702 bool SdpRidAttributeList::Rid::Parse(std::istream& is, std::string* error) {
id = ParseToken(is, " ", error); if (!CheckRidValidity(id, error)) { returnfalse;
}
is >> std::ws;
std::string directionToken = ParseToken(is, " ", error); if (directionToken == "send") {
direction = sdp::kSend;
} elseif (directionToken == "recv") {
direction = sdp::kRecv;
} else {
*error = "Invalid direction, must be either send or recv"; returnfalse;
}
return ParseParameters(is, error);
}
static std::bitset<256> GetAllowedRidCharacters() { // From RFC 8851: // rid-id = 1*(alpha-numeric / "-" / "_")
std::bitset<256> result; for (unsignedchar c = 'a'; c <= 'z'; ++c) {
result.set(c);
} for (unsignedchar c = 'A'; c <= 'Z'; ++c) {
result.set(c);
} for (unsignedchar c = '0'; c <= '9'; ++c) {
result.set(c);
} // NOTE: RFC 8851 says these are allowed, but RFC 8852 says they are not // https://www.rfc-editor.org/errata/eid7132 // result.set('-'); // result.set('_'); return result;
}
/* static */ bool SdpRidAttributeList::CheckRidValidity(const std::string& aRid,
std::string* aError) { if (aRid.empty()) {
*aError = "Rid must be non-empty (according to RFC 8851)"; returnfalse;
}
// We need to check against a maximum length, but that is nowhere // specified in webrtc-pc right now. if (aRid.size() > 255) {
*aError = "Rid can be at most 255 characters long (according to RFC 8852)"; returnfalse;
}
// TODO: Right now, if the rid is longer than kMaxRidLength, we don't treat it // as a parse error, since the grammar does not have this restriction. // Instead, our JSEP code ignores rids that exceed this limit. However, there // is a possibility that the IETF grammar (in RFC 8852) will change the limit // from 255 to 16, in which case we will need to revise this code.
staticconst std::bitset<256> allowed = GetAllowedRidCharacters(); for (unsignedchar c : aRid) { if (!allowed[c]) {
*aError = "Rid can contain only alphanumeric characters (according to RFC " "8852)"; returnfalse;
}
}
returntrue;
}
// This can be overridden if necessary
size_t SdpRidAttributeList::kMaxRidLength = 255;
void SdpRidAttributeList::Rid::Serialize(std::ostream& os) const {
os << id << " " << direction;
SerializeParameters(os);
}
void SdpRtcpFbAttributeList::Serialize(std::ostream& os) const { for (auto i = mFeedbacks.begin(); i != mFeedbacks.end(); ++i) {
os << "a=" << mType << ":" << i->pt << " " << i->type; if (i->parameter.length()) {
os << " " << i->parameter; if (i->extra.length()) {
os << " " << i->extra;
}
}
os << CRLF;
}
}
staticbool ShouldSerializeChannels(SdpRtpmapAttributeList::CodecType type) { switch (type) { case SdpRtpmapAttributeList::kOpus: case SdpRtpmapAttributeList::kG722: returntrue; case SdpRtpmapAttributeList::kPCMU: case SdpRtpmapAttributeList::kPCMA: case SdpRtpmapAttributeList::kVP8: case SdpRtpmapAttributeList::kVP9: case SdpRtpmapAttributeList::kiLBC: case SdpRtpmapAttributeList::kiSAC: case SdpRtpmapAttributeList::kH264: case SdpRtpmapAttributeList::kAV1: case SdpRtpmapAttributeList::kRed: case SdpRtpmapAttributeList::kUlpfec: case SdpRtpmapAttributeList::kTelephoneEvent: case SdpRtpmapAttributeList::kRtx: returnfalse; case SdpRtpmapAttributeList::kOtherCodec: returntrue;
}
MOZ_CRASH();
}
void SdpRtpmapAttributeList::Serialize(std::ostream& os) const { for (auto i = mRtpmaps.begin(); i != mRtpmaps.end(); ++i) {
os << "a=" << mType << ":" << i->pt << " " << i->name << "/" << i->clock; if (i->channels && ShouldSerializeChannels(i->codec)) {
os << "/" << i->channels;
}
os << CRLF;
}
}
void SdpSctpmapAttributeList::Serialize(std::ostream& os) const { for (auto i = mSctpmaps.begin(); i != mSctpmaps.end(); ++i) {
os << "a=" << mType << ":" << i->pt << " " << i->name << " " << i->streams
<< CRLF;
}
}
void SdpSimulcastAttribute::Version::Serialize(std::ostream& os) const {
SkipFirstDelimiter comma(","); for (const Encoding& choice : choices) {
os << comma; if (choice.paused) {
os << '~';
}
os << choice.rid;
}
}
bool SdpSimulcastAttribute::Version::Parse(std::istream& is,
std::string* error) { do { bool paused = SkipChar(is, '~', error);
std::string value = ParseToken(is, ",; ", error); if (value.empty()) {
*error = "Missing rid"; returnfalse;
} if (!SdpRidAttributeList::CheckRidValidity(value, error)) { returnfalse;
}
choices.push_back(Encoding(value, paused));
} while (SkipChar(is, ',', error));
returntrue;
}
void SdpSimulcastAttribute::Versions::Serialize(std::ostream& os) const {
SkipFirstDelimiter semic(";"); for (const Version& version : *this) { if (!version.IsSet()) { continue;
}
os << semic;
version.Serialize(os);
}
}
bool SdpSimulcastAttribute::Versions::Parse(std::istream& is,
std::string* error) { do {
Version version; if (!version.Parse(is, error)) { returnfalse;
}
push_back(version);
} while (SkipChar(is, ';', error));
bool SdpAttribute::IsAllowedAtMediaLevel(AttributeType type) { switch (type) { case kBundleOnlyAttribute: returntrue; case kCandidateAttribute: returntrue; case kConnectionAttribute: returntrue; case kDirectionAttribute: returntrue; case kDtlsMessageAttribute: returnfalse; case kEndOfCandidatesAttribute: returntrue; case kExtmapAttribute: returntrue; case kFingerprintAttribute: returntrue; case kFmtpAttribute: returntrue; case kGroupAttribute: returnfalse; case kIceLiteAttribute: returnfalse; case kIceMismatchAttribute: returntrue; // RFC 5245 says this is session-level only, but // draft-ietf-mmusic-ice-sip-sdp-03 updates this to allow at the media // level. case kIceOptionsAttribute: returntrue; case kIcePwdAttribute: returntrue; case kIceUfragAttribute: returntrue; case kIdentityAttribute: returnfalse; case kImageattrAttribute: returntrue; case kLabelAttribute: returntrue; case kMaxptimeAttribute: returntrue; case kMidAttribute: returntrue; case kMsidAttribute: returntrue; case kMsidSemanticAttribute: returnfalse; case kPtimeAttribute: returntrue; case kRemoteCandidatesAttribute: returntrue; case kRidAttribute: returntrue; case kRtcpAttribute: returntrue; case kRtcpFbAttribute: returntrue; case kRtcpMuxAttribute: returntrue; case kRtcpRsizeAttribute: returntrue; case kRtpmapAttribute: returntrue; case kSctpmapAttribute: returntrue; case kSetupAttribute: returntrue; case kSimulcastAttribute: returntrue; case kSsrcAttribute: returntrue; case kSsrcGroupAttribute: returntrue; case kSctpPortAttribute: returntrue; case kMaxMessageSizeAttribute: returntrue;
}
MOZ_CRASH("Unknown attribute type");
}
bool SdpAttribute::IsAllowedAtSessionLevel(AttributeType type) { switch (type) { case kBundleOnlyAttribute: returnfalse; case kCandidateAttribute: returnfalse; case kConnectionAttribute: returntrue; case kDirectionAttribute: returntrue; case kDtlsMessageAttribute: returntrue; case kEndOfCandidatesAttribute: returntrue; case kExtmapAttribute: returntrue; case kFingerprintAttribute: returntrue; case kFmtpAttribute: returnfalse; case kGroupAttribute: returntrue; case kIceLiteAttribute: returntrue; case kIceMismatchAttribute: returnfalse; case kIceOptionsAttribute: returntrue; case kIcePwdAttribute: returntrue; case kIceUfragAttribute: returntrue; case kIdentityAttribute: returntrue; case kImageattrAttribute: returnfalse; case kLabelAttribute: returnfalse; case kMaxptimeAttribute: returnfalse; case kMidAttribute: returnfalse; case kMsidSemanticAttribute: returntrue; case kMsidAttribute: returnfalse; case kPtimeAttribute: returnfalse; case kRemoteCandidatesAttribute: returnfalse; case kRidAttribute: returnfalse; case kRtcpAttribute: returnfalse; case kRtcpFbAttribute: returnfalse; case kRtcpMuxAttribute: returnfalse; case kRtcpRsizeAttribute: returnfalse; case kRtpmapAttribute: returnfalse; case kSctpmapAttribute: returnfalse; case kSetupAttribute: returntrue; case kSimulcastAttribute: returnfalse; case kSsrcAttribute: returnfalse; case kSsrcGroupAttribute: returnfalse; case kSctpPortAttribute: returnfalse; case kMaxMessageSizeAttribute: returnfalse;
}
MOZ_CRASH("Unknown attribute type");
}
const std::string SdpAttribute::GetAttributeTypeString(AttributeType type) { switch (type) { case kBundleOnlyAttribute: return"bundle-only"; case kCandidateAttribute: return"candidate"; case kConnectionAttribute: return"connection"; case kDtlsMessageAttribute: return"dtls-message"; case kEndOfCandidatesAttribute: return"end-of-candidates"; case kExtmapAttribute: return"extmap"; case kFingerprintAttribute: return"fingerprint"; case kFmtpAttribute: return"fmtp"; case kGroupAttribute: return"group"; case kIceLiteAttribute: return"ice-lite"; case kIceMismatchAttribute: return"ice-mismatch"; case kIceOptionsAttribute: return"ice-options"; case kIcePwdAttribute: return"ice-pwd"; case kIceUfragAttribute: return"ice-ufrag"; case kIdentityAttribute: return"identity"; case kImageattrAttribute: return"imageattr"; case kLabelAttribute: return"label"; case kMaxptimeAttribute: return"maxptime"; case kMidAttribute: return"mid"; case kMsidAttribute: return"msid"; case kMsidSemanticAttribute: return"msid-semantic"; case kPtimeAttribute: return"ptime"; case kRemoteCandidatesAttribute: return"remote-candidates"; case kRidAttribute: return"rid"; case kRtcpAttribute: return"rtcp"; case kRtcpFbAttribute: return"rtcp-fb"; case kRtcpMuxAttribute: return"rtcp-mux"; case kRtcpRsizeAttribute: return"rtcp-rsize"; case kRtpmapAttribute: return"rtpmap"; case kSctpmapAttribute: return"sctpmap"; case kSetupAttribute: return"setup"; case kSimulcastAttribute: return"simulcast"; case kSsrcAttribute: return"ssrc"; case kSsrcGroupAttribute: return"ssrc-group"; case kSctpPortAttribute: return"sctp-port"; case kMaxMessageSizeAttribute: return"max-message-size"; case kDirectionAttribute:
MOZ_CRASH("kDirectionAttribute not valid here");
}
MOZ_CRASH("Unknown attribute type");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.