From 61d275e7dbbae256c6ae7519d65da8272255c576 Mon Sep 17 00:00:00 2001 From: "Byron Campen [:bwc]" Date: Thu, 8 Oct 2015 16:55:39 -0500 Subject: [PATCH] Bug 1212907 - a=rid support. r=mt --HG-- extra : rebase_source : a9547d76a83de75304db263a5beb67295a362238 --- .../webrtc/signaling/src/sdp/SdpAttribute.cpp | 311 +++++++++-- media/webrtc/signaling/src/sdp/SdpAttribute.h | 109 ++++ .../signaling/src/sdp/SdpAttributeList.h | 3 +- media/webrtc/signaling/src/sdp/SdpEnum.h | 11 + .../src/sdp/SipccSdpAttributeList.cpp | 57 +- .../signaling/src/sdp/SipccSdpAttributeList.h | 6 +- media/webrtc/signaling/src/sdp/sipcc/ccsdp.h | 1 + .../signaling/src/sdp/sipcc/sdp_attr_access.c | 3 +- .../webrtc/signaling/src/sdp/sipcc/sdp_main.c | 2 + media/webrtc/signaling/test/sdp_unittests.cpp | 504 ++++++++++++++++++ 10 files changed, 942 insertions(+), 65 deletions(-) diff --git a/media/webrtc/signaling/src/sdp/SdpAttribute.cpp b/media/webrtc/signaling/src/sdp/SdpAttribute.cpp index 30690298863d..57c60124c91b 100644 --- a/media/webrtc/signaling/src/sdp/SdpAttribute.cpp +++ b/media/webrtc/signaling/src/sdp/SdpAttribute.cpp @@ -147,6 +147,34 @@ void SdpIdentityAttribute::Serialize(std::ostream& os) const } #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; + } + + private: + std::string mDelim; + bool mFirst; +}; + +static std::ostream& operator<<(std::ostream& os, SkipFirstDelimiter& delim) +{ + return delim.print(os); +} + void SdpImageattrAttributeList::XYRange::Serialize(std::ostream& os) const { @@ -160,13 +188,9 @@ SdpImageattrAttributeList::XYRange::Serialize(std::ostream& os) const os << discreteValues.front(); } else { os << "["; - bool first = true; + SkipFirstDelimiter comma(","); for (auto value : discreteValues) { - if (!first) { - os << ","; - } - first = false; - os << value; + os << comma << value; } os << "]"; } @@ -442,13 +466,9 @@ SdpImageattrAttributeList::SRange::Serialize(std::ostream& os) const os << discreteValues.front(); } else { os << "["; - bool first = true; + SkipFirstDelimiter comma(","); for (auto value : discreteValues) { - if (!first) { - os << ","; - } - first = false; - os << value; + os << comma << value; } os << "]"; } @@ -461,19 +481,29 @@ SdpImageattrAttributeList::PRange::Serialize(std::ostream& os) const os << "[" << min << "-" << max << "]"; } -static std::string ParseKey(std::istream& is, std::string* error) +static std::string ParseToken(std::istream& is, + const std::string& delims, + std::string* error) { is >> std::ws; - std::string key; - while (is && PeekChar(is, error) != '=') { - key.push_back(std::tolower(is.get())); + std::string token; + while (is) { + unsigned char c = PeekChar(is, error); + if (!c || (delims.find(c) != std::string::npos)) { + break; + } + token.push_back(std::tolower(is.get())); } + return token; +} +static std::string ParseKey(std::istream& is, std::string* error) +{ + std::string token = ParseToken(is, "=", error); if (!SkipChar(is, '=', error)) { return ""; } - - return key; + return token; } static bool SkipBraces(std::istream& is, std::string* error) @@ -630,31 +660,11 @@ SdpImageattrAttributeList::Set::Serialize(std::ostream& os) const os << "]"; } -static std::string -GetLowercaseToken(std::istream& is, std::string* error) -{ - is >> std::ws; - std::string token; - while (true) { - switch (PeekChar(is, error)) { - case '\0': - case ' ': - case '\t': - return token; - default: - token.push_back(std::tolower(is.get())); - } - } - - MOZ_ASSERT_UNREACHABLE("Unexpected break in loop"); - return ""; -} - bool SdpImageattrAttributeList::Imageattr::ParseSets(std::istream& is, std::string* error) { - std::string type = GetLowercaseToken(is, error); + std::string type = ParseToken(is, " \t", error); bool* isAll = nullptr; std::vector* sets = nullptr; @@ -827,6 +837,201 @@ SdpRemoteCandidatesAttribute::Serialize(std::ostream& os) const os << CRLF; } +bool +SdpRidAttributeList::Constraints::Parse(std::istream& is, std::string* error) +{ + if (!PeekChar(is, error)) { + // No constraints + return true; + } + + do { + std::string key = ParseKey(is, error); + if (key.empty()) { + return false; // 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)) { + return false; + } + } else if (key == "max-width") { + if (!GetUnsigned(is, 0, UINT32_MAX, &maxWidth, error)) { + return false; + } + } else if (key == "max-height") { + if (!GetUnsigned(is, 0, UINT32_MAX, &maxHeight, error)) { + return false; + } + } else if (key == "max-fps") { + if (!GetUnsigned(is, 0, UINT32_MAX, &maxFps, error)) { + return false; + } + } else if (key == "max-fs") { + if (!GetUnsigned(is, 0, UINT32_MAX, &maxFs, error)) { + return false; + } + } else if (key == "max-br") { + if (!GetUnsigned(is, 0, UINT32_MAX, &maxBr, error)) { + return false; + } + } else if (key == "max-pps") { + if (!GetUnsigned(is, 0, UINT32_MAX, &maxPps, error)) { + return false; + } + } else if (key == "depend") { + if (!ParseDepend(is, error)) { + return false; + } + } else { + (void) ParseToken(is, ";", error); + } + } while (SkipChar(is, ';', error)); + return true; +} + +bool +SdpRidAttributeList::Constraints::ParseDepend( + std::istream& is, + std::string* error) +{ + do { + std::string id = ParseToken(is, ",;", error); + if (id.empty()) { + return false; + } + dependIds.push_back(id); + } while(SkipChar(is, ',', error)); + + return true; +} + +bool +SdpRidAttributeList::Constraints::ParseFormats( + std::istream& is, + std::string* error) +{ + do { + uint16_t fmt; + if (!GetUnsigned(is, 0, 127, &fmt, error)) { + return false; + } + formats.push_back(fmt); + } while (SkipChar(is, ',', error)); + + return true; +} + +void +SdpRidAttributeList::Constraints::Serialize(std::ostream& os) const +{ + if (!IsSet()) { + return; + } + + os << " "; + + SkipFirstDelimiter semic(";"); + + if (!formats.empty()) { + os << semic << "pt="; + SkipFirstDelimiter comma(","); + for (uint16_t fmt : formats) { + os << comma << fmt; + } + } + + if (maxWidth) { + os << semic << "max-width=" << maxWidth; + } + + if (maxHeight) { + os << semic << "max-height=" << maxHeight; + } + + if (maxFps) { + os << semic << "max-fps=" << maxFps; + } + + if (maxFs) { + os << semic << "max-fs=" << maxFs; + } + + if (maxBr) { + os << semic << "max-br=" << maxBr; + } + + if (maxPps) { + os << semic << "max-pps=" << maxPps; + } + + if (!dependIds.empty()) { + os << semic << "depend="; + SkipFirstDelimiter comma(","); + for (const std::string& id : dependIds) { + os << comma << id; + } + } +} + +bool +SdpRidAttributeList::Rid::Parse(std::istream& is, std::string* error) +{ + id = ParseToken(is, " ", error); + if (id.empty()) { + return false; + } + + std::string directionToken = ParseToken(is, " ", error); + if (directionToken == "send") { + direction = sdp::kSend; + } else if (directionToken == "recv") { + direction = sdp::kRecv; + } else { + *error = "Invalid direction, must be either send or recv"; + return false; + } + + return constraints.Parse(is, error); +} + +void +SdpRidAttributeList::Rid::Serialize(std::ostream& os) const +{ + os << id << " " << direction; + constraints.Serialize(os); +} + +void +SdpRidAttributeList::Serialize(std::ostream& os) const +{ + for (const Rid& rid : mRids) { + os << "a=" << mType << ":"; + rid.Serialize(os); + os << CRLF; + } +} + +bool +SdpRidAttributeList::PushEntry(const std::string& raw, + std::string* error, + size_t* errorPos) +{ + std::istringstream is(raw); + + Rid rid; + if (!rid.Parse(is, error)) { + is.clear(); + *errorPos = is.tellg(); + return false; + } + + mRids.push_back(rid); + return true; +} + void SdpRtcpAttribute::Serialize(std::ostream& os) const { @@ -913,15 +1118,9 @@ SdpSetupAttribute::Serialize(std::ostream& os) const void SdpSimulcastAttribute::Version::Serialize(std::ostream& os) const { - bool first = true; + SkipFirstDelimiter comma(","); for (uint16_t format : choices) { - if (first) { - first = false; - } else { - os << ","; - } - - os << format; + os << comma << format; } } @@ -965,18 +1164,12 @@ SdpSimulcastAttribute::Version::AddChoice(const std::string& pt) void SdpSimulcastAttribute::Versions::Serialize(std::ostream& os) const { - bool first = true; + SkipFirstDelimiter semic(";"); for (const Version& version : *this) { if (!version.IsSet()) { continue; } - - if (first) { - first = false; - } else { - os << ";"; - } - + os << semic; version.Serialize(os); } } @@ -1030,7 +1223,7 @@ SdpSimulcastAttribute::Parse(std::istream& is, std::string* error) bool gotSendrecv = false; while (true) { - std::string token = GetLowercaseToken(is, error); + std::string token = ParseToken(is, " \t", error); if (token.empty()) { break; } @@ -1216,6 +1409,8 @@ SdpAttribute::IsAllowedAtMediaLevel(AttributeType type) return true; case kRemoteCandidatesAttribute: return true; + case kRidAttribute: + return true; case kRtcpAttribute: return true; case kRtcpFbAttribute: @@ -1298,6 +1493,8 @@ SdpAttribute::IsAllowedAtSessionLevel(AttributeType type) return true; case kRemoteCandidatesAttribute: return false; + case kRidAttribute: + return false; case kRtcpAttribute: return false; case kRtcpFbAttribute: @@ -1378,6 +1575,8 @@ SdpAttribute::GetAttributeTypeString(AttributeType type) return "recvonly"; case kRemoteCandidatesAttribute: return "remote-candidates"; + case kRidAttribute: + return "rid"; case kRtcpAttribute: return "rtcp"; case kRtcpFbAttribute: diff --git a/media/webrtc/signaling/src/sdp/SdpAttribute.h b/media/webrtc/signaling/src/sdp/SdpAttribute.h index 7d7eea141602..8eec95552456 100644 --- a/media/webrtc/signaling/src/sdp/SdpAttribute.h +++ b/media/webrtc/signaling/src/sdp/SdpAttribute.h @@ -59,6 +59,7 @@ public: kPtimeAttribute, kRecvonlyAttribute, kRemoteCandidatesAttribute, + kRidAttribute, kRtcpAttribute, kRtcpFbAttribute, kRtcpMuxAttribute, @@ -771,6 +772,114 @@ public: std::vector mCandidates; }; +/* +a=rid, draft-pthatcher-mmusic-rid-01 + + rid-syntax = "a=rid:" rid-identifier SP rid-dir + [ rid-pt-param-list / rid-param-list ] + + rid-identifier = 1*(alpha-numeric / "-" / "_") + + rid-dir = "send" / "recv" + + rid-pt-param-list = SP rid-fmt-list *(";" rid-param) + + rid-param-list = SP rid-param *(";" rid-param) + + rid-fmt-list = "pt=" fmt *( "," fmt ) + ; fmt defined in {{RFC4566}} + + rid-param = rid-width-param + / rid-height-param + / rid-fps-param + / rid-fs-param + / rid-br-param + / rid-pps-param + / rid-depend-param + / rid-param-other + + rid-width-param = "max-width" [ "=" int-param-val ] + + rid-height-param = "max-height" [ "=" int-param-val ] + + rid-fps-param = "max-fps" [ "=" int-param-val ] + + rid-fs-param = "max-fs" [ "=" int-param-val ] + + rid-br-param = "max-br" [ "=" int-param-val ] + + rid-pps-param = "max-pps" [ "=" int-param-val ] + + rid-depend-param = "depend=" rid-list + + rid-param-other = 1*(alpha-numeric / "-") [ "=" param-val ] + + rid-list = rid-identifier *( "," rid-identifier ) + + int-param-val = 1*DIGIT + + param-val = *( %x20-58 / %x60-7E ) + ; Any printable character except semicolon +*/ +class SdpRidAttributeList : public SdpAttribute +{ +public: + explicit SdpRidAttributeList() + : SdpAttribute(kRidAttribute) + {} + + struct Constraints + { + Constraints() : + maxWidth(0), + maxHeight(0), + maxFps(0), + maxFs(0), + maxBr(0), + maxPps(0) + {} + + bool Parse(std::istream& is, std::string* error); + bool ParseDepend(std::istream& is, std::string* error); + bool ParseFormats(std::istream& is, std::string* error); + void Serialize(std::ostream& os) const; + bool IsSet() const + { + return !formats.empty() || maxWidth || maxHeight || maxFps || maxFs || + maxBr || maxPps || !dependIds.empty(); + } + + std::vector formats; // Empty implies all + uint32_t maxWidth; + uint32_t maxHeight; + uint32_t maxFps; + uint32_t maxFs; + uint32_t maxBr; + uint32_t maxPps; + std::vector dependIds; + // We do not bother trying to store constraints we don't understand. + }; + + struct Rid + { + Rid() : + direction(sdp::kSend) + {} + + bool Parse(std::istream& is, std::string* error); + void Serialize(std::ostream& os) const; + + std::string id; + sdp::Direction direction; + Constraints constraints; + }; + + virtual void Serialize(std::ostream& os) const override; + bool PushEntry(const std::string& raw, std::string* error, size_t* errorPos); + + std::vector mRids; +}; + /////////////////////////////////////////////////////////////////////////// // a=rtcp, RFC3605 //------------------------------------------------------------------------- diff --git a/media/webrtc/signaling/src/sdp/SdpAttributeList.h b/media/webrtc/signaling/src/sdp/SdpAttributeList.h index 88e677c609c2..7dc2b8effd25 100644 --- a/media/webrtc/signaling/src/sdp/SdpAttributeList.h +++ b/media/webrtc/signaling/src/sdp/SdpAttributeList.h @@ -58,6 +58,8 @@ public: virtual const SdpImageattrAttributeList& GetImageattr() const = 0; virtual const SdpSimulcastAttribute& GetSimulcast() const = 0; virtual const SdpMsidAttributeList& GetMsid() const = 0; + virtual const SdpMsidSemanticAttributeList& GetMsidSemantic() const = 0; + virtual const SdpRidAttributeList& GetRid() const = 0; virtual const SdpRtcpFbAttributeList& GetRtcpFb() const = 0; virtual const SdpRtpmapAttributeList& GetRtpmap() const = 0; virtual const SdpSctpmapAttributeList& GetSctpmap() const = 0; @@ -72,7 +74,6 @@ public: virtual const std::string& GetLabel() const = 0; virtual unsigned int GetMaxptime() const = 0; virtual const std::string& GetMid() const = 0; - virtual const SdpMsidSemanticAttributeList& GetMsidSemantic() const = 0; virtual unsigned int GetPtime() const = 0; // This is "special", because it's multiple things diff --git a/media/webrtc/signaling/src/sdp/SdpEnum.h b/media/webrtc/signaling/src/sdp/SdpEnum.h index 2c7419b1ffae..b4a0d16b3822 100644 --- a/media/webrtc/signaling/src/sdp/SdpEnum.h +++ b/media/webrtc/signaling/src/sdp/SdpEnum.h @@ -52,6 +52,17 @@ enum Direction { kRecv = 2 }; +inline std::ostream& operator<<(std::ostream& os, sdp::Direction d) +{ + switch (d) { + case sdp::kSend: + return os << "send"; + case sdp::kRecv: + return os << "recv"; + } + MOZ_CRASH("Unknown Direction"); +} + } // namespace sdp } // namespace mozilla diff --git a/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.cpp b/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.cpp index 72c8b6fcae2a..50fb345d4588 100644 --- a/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.cpp +++ b/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.cpp @@ -513,8 +513,8 @@ SipccSdpAttributeList::LoadImageattr(sdp_t* sdp, std::string error; size_t errorPos; if (!imageattrs->PushEntry(imageattrRaw, &error, &errorPos)) { - std::ostringstream fullError(error + " at column "); - fullError << errorPos; + std::ostringstream fullError; + fullError << error << " at column " << errorPos; errorHolder.AddParseError( sdp_attr_line_number(sdp, SDP_ATTR_IMAGEATTR, level, 0, i), fullError.str()); @@ -548,9 +548,8 @@ SipccSdpAttributeList::LoadSimulcast(sdp_t* sdp, std::istringstream is(simulcastRaw); std::string error; if (!simulcast->Parse(is, &error)) { - is.clear(); - std::ostringstream fullError(error + " at column "); - fullError << is.tellg(); + std::ostringstream fullError; + fullError << error << " at column " << is.tellg(); errorHolder.AddParseError( sdp_attr_line_number(sdp, SDP_ATTR_SIMULCAST, level, 0, 1), fullError.str()); @@ -783,6 +782,41 @@ SipccSdpAttributeList::LoadMsids(sdp_t* sdp, uint16_t level, } } +bool +SipccSdpAttributeList::LoadRid(sdp_t* sdp, + uint16_t level, + SdpErrorHolder& errorHolder) +{ + UniquePtr rids(new SdpRidAttributeList); + + for (uint16_t i = 1; i < UINT16_MAX; ++i) { + const char* ridRaw = sdp_attr_get_simple_string(sdp, + SDP_ATTR_RID, + level, + 0, + i); + if (!ridRaw) { + break; + } + + std::string error; + size_t errorPos; + if (!rids->PushEntry(ridRaw, &error, &errorPos)) { + std::ostringstream fullError; + fullError << error << " at column " << errorPos; + errorHolder.AddParseError( + sdp_attr_line_number(sdp, SDP_ATTR_RID, level, 0, i), + fullError.str()); + return false; + } + } + + if (!rids->mRids.empty()) { + SetAttribute(rids.release()); + } + return true; +} + void SipccSdpAttributeList::LoadExtmap(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder) @@ -1000,6 +1034,9 @@ SipccSdpAttributeList::Load(sdp_t* sdp, uint16_t level, if (!LoadSimulcast(sdp, level, errorHolder)) { return false; } + if (!LoadRid(sdp, level, errorHolder)) { + return false; + } } LoadIceAttributes(sdp, level); @@ -1224,6 +1261,16 @@ SipccSdpAttributeList::GetMsidSemantic() const return *static_cast(attr); } +const SdpRidAttributeList& +SipccSdpAttributeList::GetRid() const +{ + if (!HasAttribute(SdpAttribute::kRidAttribute)) { + MOZ_CRASH(); + } + const SdpAttribute* attr = GetAttribute(SdpAttribute::kRidAttribute); + return *static_cast(attr); +} + uint32_t SipccSdpAttributeList::GetPtime() const { diff --git a/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.h b/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.h index 268d18c3196f..0c092f68c2a7 100644 --- a/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.h +++ b/media/webrtc/signaling/src/sdp/SipccSdpAttributeList.h @@ -58,6 +58,9 @@ public: virtual const SdpImageattrAttributeList& GetImageattr() const override; const SdpSimulcastAttribute& GetSimulcast() const override; virtual const SdpMsidAttributeList& GetMsid() const override; + virtual const SdpMsidSemanticAttributeList& GetMsidSemantic() + const override; + const SdpRidAttributeList& GetRid() const override; virtual const SdpRtcpFbAttributeList& GetRtcpFb() const override; virtual const SdpRtpmapAttributeList& GetRtpmap() const override; virtual const SdpSctpmapAttributeList& GetSctpmap() const override; @@ -70,8 +73,6 @@ public: virtual const std::string& GetLabel() const override; virtual unsigned int GetMaxptime() const override; virtual const std::string& GetMid() const override; - virtual const SdpMsidSemanticAttributeList& GetMsidSemantic() - const override; virtual unsigned int GetPtime() const override; virtual SdpDirectionAttribute::Direction GetDirection() const override; @@ -114,6 +115,7 @@ private: SdpErrorHolder& errorHolder); void LoadFmtp(sdp_t* sdp, uint16_t level); void LoadMsids(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder); + bool LoadRid(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder); void LoadExtmap(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder); void LoadRtcpFb(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder); void LoadRtcp(sdp_t* sdp, uint16_t level, SdpErrorHolder& errorHolder); diff --git a/media/webrtc/signaling/src/sdp/sipcc/ccsdp.h b/media/webrtc/signaling/src/sdp/sipcc/ccsdp.h index 9c3b762757e3..c47b1d7bd7ab 100644 --- a/media/webrtc/signaling/src/sdp/sipcc/ccsdp.h +++ b/media/webrtc/signaling/src/sdp/sipcc/ccsdp.h @@ -186,6 +186,7 @@ typedef enum { SDP_ATTR_SSRC, SDP_ATTR_IMAGEATTR, SDP_ATTR_SIMULCAST, + SDP_ATTR_RID, SDP_MAX_ATTR_TYPES, SDP_ATTR_INVALID } sdp_attr_e; diff --git a/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c b/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c index 5cf0a680a4fb..a95de2ad5e02 100644 --- a/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c +++ b/media/webrtc/signaling/src/sdp/sipcc/sdp_attr_access.c @@ -684,7 +684,8 @@ static boolean sdp_attr_is_simple_string(sdp_attr_e attr_type) { (attr_type != SDP_ATTR_IDENTITY) && (attr_type != SDP_ATTR_ICE_OPTIONS) && (attr_type != SDP_ATTR_IMAGEATTR) && - (attr_type != SDP_ATTR_SIMULCAST)) { + (attr_type != SDP_ATTR_SIMULCAST) && + (attr_type != SDP_ATTR_RID)) { return FALSE; } return TRUE; diff --git a/media/webrtc/signaling/src/sdp/sipcc/sdp_main.c b/media/webrtc/signaling/src/sdp/sipcc/sdp_main.c index c84ad4bf3e5d..741669893987 100644 --- a/media/webrtc/signaling/src/sdp/sipcc/sdp_main.c +++ b/media/webrtc/signaling/src/sdp/sipcc/sdp_main.c @@ -198,6 +198,8 @@ const sdp_attrarray_t sdp_attr[SDP_MAX_ATTR_TYPES] = sdp_parse_attr_complete_line, sdp_build_attr_simple_string}, {"simulcast", sizeof("simulcast"), sdp_parse_attr_complete_line, sdp_build_attr_simple_string}, + {"rid", sizeof("rid"), + sdp_parse_attr_complete_line, sdp_build_attr_simple_string}, }; /* Note: These *must* be in the same order as the enum types. */ diff --git a/media/webrtc/signaling/test/sdp_unittests.cpp b/media/webrtc/signaling/test/sdp_unittests.cpp index 52e82be84f7d..55eb937752bf 100644 --- a/media/webrtc/signaling/test/sdp_unittests.cpp +++ b/media/webrtc/signaling/test/sdp_unittests.cpp @@ -1196,6 +1196,8 @@ const std::string kBasicAudioVideoOffer = "a=imageattr:120 send * recv *" CRLF "a=imageattr:121 send [x=640,y=480] recv [x=640,y=480]" CRLF "a=simulcast:sendrecv 120;121" CRLF +"a=rid:foo send" CRLF +"a=rid:bar recv pt=96;max-width=800;max-height=600" CRLF "m=audio 9 RTP/SAVPF 0" CRLF "a=mid:third" CRLF "a=rtpmap:0 PCMU/8000" CRLF @@ -1758,6 +1760,36 @@ TEST_P(NewSdpTest, CheckMsid) { ASSERT_EQ("", msids3.mMsids[0].appdata); } +TEST_P(NewSdpTest, CheckRid) +{ + ParseSdp(kBasicAudioVideoOffer); + ASSERT_TRUE(!!mSdp); + ASSERT_EQ(3U, mSdp->GetMediaSectionCount()) << "Wrong number of media sections"; + + ASSERT_FALSE(mSdp->GetAttributeList().HasAttribute( + SdpAttribute::kRidAttribute)); + ASSERT_FALSE(mSdp->GetMediaSection(0).GetAttributeList().HasAttribute( + SdpAttribute::kRidAttribute)); + ASSERT_TRUE(mSdp->GetMediaSection(1).GetAttributeList().HasAttribute( + SdpAttribute::kRidAttribute)); + ASSERT_FALSE(mSdp->GetMediaSection(2).GetAttributeList().HasAttribute( + SdpAttribute::kRidAttribute)); + + const SdpRidAttributeList& rids = + mSdp->GetMediaSection(1).GetAttributeList().GetRid(); + + ASSERT_EQ(2U, rids.mRids.size()); + ASSERT_EQ("foo", rids.mRids[0].id); + ASSERT_EQ(sdp::kSend, rids.mRids[0].direction); + ASSERT_EQ(0U, rids.mRids[0].constraints.formats.size()); + ASSERT_EQ("bar", rids.mRids[1].id); + ASSERT_EQ(sdp::kRecv, rids.mRids[1].direction); + ASSERT_EQ(1U, rids.mRids[1].constraints.formats.size()); + ASSERT_EQ(96U, rids.mRids[1].constraints.formats[0]); + ASSERT_EQ(800U, rids.mRids[1].constraints.maxWidth); + ASSERT_EQ(600U, rids.mRids[1].constraints.maxHeight); +} + TEST_P(NewSdpTest, CheckMediaLevelIceUfrag) { ParseSdp(kBasicAudioVideoOffer); ASSERT_TRUE(!!mSdp) << "Parse failed: " << GetParseErrors(); @@ -3641,6 +3673,478 @@ TEST(NewSdpTestNoFixture, CheckSimulcastInvalidParse) ParseInvalid(" sendrecv 8 sendrecv ", 20); } +static SdpRidAttributeList::Constraints +ParseRidConstraints(const std::string& input) +{ + std::istringstream is(input); + std::string error; + SdpRidAttributeList::Constraints constraints; + EXPECT_TRUE(constraints.Parse(is, &error)) << error + << " for input \'" << input << "\'" ; + EXPECT_TRUE(is.eof()); + return constraints; +} + +TEST(NewSdpTestNoFixture, CheckRidConstraintsValidParse) +{ + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("")); + ASSERT_EQ(0U, constraints.formats.size()); + ASSERT_EQ(0U, constraints.maxWidth); + ASSERT_EQ(0U, constraints.maxHeight); + ASSERT_EQ(0U, constraints.maxFps); + ASSERT_EQ(0U, constraints.maxFs); + ASSERT_EQ(0U, constraints.maxBr); + ASSERT_EQ(0U, constraints.maxPps); + ASSERT_EQ(0U, constraints.dependIds.size()); + } + + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("pt=96")); + ASSERT_EQ(1U, constraints.formats.size()); + ASSERT_EQ(96U, constraints.formats[0]); + ASSERT_EQ(0U, constraints.maxWidth); + ASSERT_EQ(0U, constraints.maxHeight); + ASSERT_EQ(0U, constraints.maxFps); + ASSERT_EQ(0U, constraints.maxFs); + ASSERT_EQ(0U, constraints.maxBr); + ASSERT_EQ(0U, constraints.maxPps); + ASSERT_EQ(0U, constraints.dependIds.size()); + } + + // This is not technically permitted by the BNF, but the parse code is simpler + // if we allow it. If we decide to stop allowing this, this will need to be + // converted to an invalid parse test-case. + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("max-br=30000;pt=96")); + ASSERT_EQ(1U, constraints.formats.size()); + ASSERT_EQ(96U, constraints.formats[0]); + ASSERT_EQ(0U, constraints.maxWidth); + ASSERT_EQ(0U, constraints.maxHeight); + ASSERT_EQ(0U, constraints.maxFps); + ASSERT_EQ(0U, constraints.maxFs); + ASSERT_EQ(30000U, constraints.maxBr); + ASSERT_EQ(0U, constraints.maxPps); + ASSERT_EQ(0U, constraints.dependIds.size()); + } + + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("pt=96,97,98")); + ASSERT_EQ(3U, constraints.formats.size()); + ASSERT_EQ(96U, constraints.formats[0]); + ASSERT_EQ(97U, constraints.formats[1]); + ASSERT_EQ(98U, constraints.formats[2]); + ASSERT_EQ(0U, constraints.maxWidth); + ASSERT_EQ(0U, constraints.maxHeight); + ASSERT_EQ(0U, constraints.maxFps); + ASSERT_EQ(0U, constraints.maxFs); + ASSERT_EQ(0U, constraints.maxBr); + ASSERT_EQ(0U, constraints.maxPps); + ASSERT_EQ(0U, constraints.dependIds.size()); + } + + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("max-width=800")); + ASSERT_EQ(0U, constraints.formats.size()); + ASSERT_EQ(800U, constraints.maxWidth); + ASSERT_EQ(0U, constraints.maxHeight); + ASSERT_EQ(0U, constraints.maxFps); + ASSERT_EQ(0U, constraints.maxFs); + ASSERT_EQ(0U, constraints.maxBr); + ASSERT_EQ(0U, constraints.maxPps); + ASSERT_EQ(0U, constraints.dependIds.size()); + } + + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("max-height=640")); + ASSERT_EQ(0U, constraints.formats.size()); + ASSERT_EQ(0U, constraints.maxWidth); + ASSERT_EQ(640U, constraints.maxHeight); + ASSERT_EQ(0U, constraints.maxFps); + ASSERT_EQ(0U, constraints.maxFs); + ASSERT_EQ(0U, constraints.maxBr); + ASSERT_EQ(0U, constraints.maxPps); + ASSERT_EQ(0U, constraints.dependIds.size()); + } + + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("max-fps=30")); + ASSERT_EQ(0U, constraints.formats.size()); + ASSERT_EQ(0U, constraints.maxWidth); + ASSERT_EQ(0U, constraints.maxHeight); + ASSERT_EQ(30U, constraints.maxFps); + ASSERT_EQ(0U, constraints.maxFs); + ASSERT_EQ(0U, constraints.maxBr); + ASSERT_EQ(0U, constraints.maxPps); + ASSERT_EQ(0U, constraints.dependIds.size()); + } + + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("max-fs=3600")); + ASSERT_EQ(0U, constraints.formats.size()); + ASSERT_EQ(0U, constraints.maxWidth); + ASSERT_EQ(0U, constraints.maxHeight); + ASSERT_EQ(0U, constraints.maxFps); + ASSERT_EQ(3600U, constraints.maxFs); + ASSERT_EQ(0U, constraints.maxBr); + ASSERT_EQ(0U, constraints.maxPps); + ASSERT_EQ(0U, constraints.dependIds.size()); + } + + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("max-br=30000")); + ASSERT_EQ(0U, constraints.formats.size()); + ASSERT_EQ(0U, constraints.maxWidth); + ASSERT_EQ(0U, constraints.maxHeight); + ASSERT_EQ(0U, constraints.maxFps); + ASSERT_EQ(0U, constraints.maxFs); + ASSERT_EQ(30000U, constraints.maxBr); + ASSERT_EQ(0U, constraints.maxPps); + ASSERT_EQ(0U, constraints.dependIds.size()); + } + + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("max-pps=9216000")); + ASSERT_EQ(0U, constraints.formats.size()); + ASSERT_EQ(0U, constraints.maxWidth); + ASSERT_EQ(0U, constraints.maxHeight); + ASSERT_EQ(0U, constraints.maxFps); + ASSERT_EQ(0U, constraints.maxFs); + ASSERT_EQ(0U, constraints.maxBr); + ASSERT_EQ(9216000U, constraints.maxPps); + ASSERT_EQ(0U, constraints.dependIds.size()); + } + + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("depend=foo")); + ASSERT_EQ(0U, constraints.formats.size()); + ASSERT_EQ(0U, constraints.maxWidth); + ASSERT_EQ(0U, constraints.maxHeight); + ASSERT_EQ(0U, constraints.maxFps); + ASSERT_EQ(0U, constraints.maxFs); + ASSERT_EQ(0U, constraints.maxBr); + ASSERT_EQ(0U, constraints.maxPps); + ASSERT_EQ(1U, constraints.dependIds.size()); + ASSERT_EQ("foo", constraints.dependIds[0]); + } + + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("max-foo=20")); + ASSERT_EQ(0U, constraints.formats.size()); + ASSERT_EQ(0U, constraints.maxWidth); + ASSERT_EQ(0U, constraints.maxHeight); + ASSERT_EQ(0U, constraints.maxFps); + ASSERT_EQ(0U, constraints.maxFs); + ASSERT_EQ(0U, constraints.maxBr); + ASSERT_EQ(0U, constraints.maxPps); + ASSERT_EQ(0U, constraints.dependIds.size()); + } + + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("depend=foo,bar")); + ASSERT_EQ(0U, constraints.formats.size()); + ASSERT_EQ(0U, constraints.maxWidth); + ASSERT_EQ(0U, constraints.maxHeight); + ASSERT_EQ(0U, constraints.maxFps); + ASSERT_EQ(0U, constraints.maxFs); + ASSERT_EQ(0U, constraints.maxBr); + ASSERT_EQ(0U, constraints.maxPps); + ASSERT_EQ(2U, constraints.dependIds.size()); + ASSERT_EQ("foo", constraints.dependIds[0]); + ASSERT_EQ("bar", constraints.dependIds[1]); + } + + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("max-width=800;max-height=600")); + ASSERT_EQ(0U, constraints.formats.size()); + ASSERT_EQ(800U, constraints.maxWidth); + ASSERT_EQ(600U, constraints.maxHeight); + ASSERT_EQ(0U, constraints.maxFps); + ASSERT_EQ(0U, constraints.maxFs); + ASSERT_EQ(0U, constraints.maxBr); + ASSERT_EQ(0U, constraints.maxPps); + ASSERT_EQ(0U, constraints.dependIds.size()); + } + + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("pt=96,97;max-width=800;max-height=600")); + ASSERT_EQ(2U, constraints.formats.size()); + ASSERT_EQ(96U, constraints.formats[0]); + ASSERT_EQ(97U, constraints.formats[1]); + ASSERT_EQ(800U, constraints.maxWidth); + ASSERT_EQ(600U, constraints.maxHeight); + ASSERT_EQ(0U, constraints.maxFps); + ASSERT_EQ(0U, constraints.maxFs); + ASSERT_EQ(0U, constraints.maxBr); + ASSERT_EQ(0U, constraints.maxPps); + ASSERT_EQ(0U, constraints.dependIds.size()); + } + + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("depend=foo,bar;max-width=800;max-height=600")); + ASSERT_EQ(0U, constraints.formats.size()); + ASSERT_EQ(800U, constraints.maxWidth); + ASSERT_EQ(600U, constraints.maxHeight); + ASSERT_EQ(0U, constraints.maxFps); + ASSERT_EQ(0U, constraints.maxFs); + ASSERT_EQ(0U, constraints.maxBr); + ASSERT_EQ(0U, constraints.maxPps); + ASSERT_EQ(2U, constraints.dependIds.size()); + ASSERT_EQ("foo", constraints.dependIds[0]); + ASSERT_EQ("bar", constraints.dependIds[1]); + } + + { + SdpRidAttributeList::Constraints constraints( + ParseRidConstraints("max-foo=20;max-width=800;max-height=600")); + ASSERT_EQ(0U, constraints.formats.size()); + ASSERT_EQ(800U, constraints.maxWidth); + ASSERT_EQ(600U, constraints.maxHeight); + ASSERT_EQ(0U, constraints.maxFps); + ASSERT_EQ(0U, constraints.maxFs); + ASSERT_EQ(0U, constraints.maxBr); + ASSERT_EQ(0U, constraints.maxPps); + ASSERT_EQ(0U, constraints.dependIds.size()); + } +} + +TEST(NewSdpTestNoFixture, CheckRidConstraintsInvalidParse) +{ + ParseInvalid(" ", 1); + ParseInvalid("pt", 2); + ParseInvalid("pt=", 3); + ParseInvalid("pt=x", 3); + ParseInvalid("pt=-1", 3); + ParseInvalid("pt=96,", 6); + ParseInvalid("pt=196", 6); + ParseInvalid("max-width", 9); + ParseInvalid("max-width=", 10); + ParseInvalid("max-width=x", 10); + ParseInvalid("max-width=-1", 10); + ParseInvalid("max-width=800;", 14); + ParseInvalid("max-width=800; ", 15); + ParseInvalid("depend=", 7); + ParseInvalid("depend=,", 7); + ParseInvalid("depend=1,", 9); +} + +TEST(NewSdpTestNoFixture, CheckRidConstraintsSerialize) +{ + { + SdpRidAttributeList::Constraints constraints; + std::ostringstream os; + constraints.Serialize(os); + ASSERT_EQ("", os.str()); + } + + { + SdpRidAttributeList::Constraints constraints; + constraints.formats.push_back(96); + std::ostringstream os; + constraints.Serialize(os); + ASSERT_EQ(" pt=96", os.str()); + } + + { + SdpRidAttributeList::Constraints constraints; + constraints.formats.push_back(96); + constraints.formats.push_back(97); + std::ostringstream os; + constraints.Serialize(os); + ASSERT_EQ(" pt=96,97", os.str()); + } + + { + SdpRidAttributeList::Constraints constraints; + constraints.maxWidth = 800; + std::ostringstream os; + constraints.Serialize(os); + ASSERT_EQ(" max-width=800", os.str()); + } + + { + SdpRidAttributeList::Constraints constraints; + constraints.maxHeight = 600; + std::ostringstream os; + constraints.Serialize(os); + ASSERT_EQ(" max-height=600", os.str()); + } + + { + SdpRidAttributeList::Constraints constraints; + constraints.maxFps = 30; + std::ostringstream os; + constraints.Serialize(os); + ASSERT_EQ(" max-fps=30", os.str()); + } + + { + SdpRidAttributeList::Constraints constraints; + constraints.maxFs = 3600; + std::ostringstream os; + constraints.Serialize(os); + ASSERT_EQ(" max-fs=3600", os.str()); + } + + { + SdpRidAttributeList::Constraints constraints; + constraints.maxBr = 30000; + std::ostringstream os; + constraints.Serialize(os); + ASSERT_EQ(" max-br=30000", os.str()); + } + + { + SdpRidAttributeList::Constraints constraints; + constraints.maxPps = 9216000; + std::ostringstream os; + constraints.Serialize(os); + ASSERT_EQ(" max-pps=9216000", os.str()); + } + + { + SdpRidAttributeList::Constraints constraints; + constraints.dependIds.push_back("foo"); + std::ostringstream os; + constraints.Serialize(os); + ASSERT_EQ(" depend=foo", os.str()); + } + + { + SdpRidAttributeList::Constraints constraints; + constraints.dependIds.push_back("foo"); + constraints.dependIds.push_back("bar"); + std::ostringstream os; + constraints.Serialize(os); + ASSERT_EQ(" depend=foo,bar", os.str()); + } + + { + SdpRidAttributeList::Constraints constraints; + constraints.formats.push_back(96); + constraints.maxBr = 30000; + std::ostringstream os; + constraints.Serialize(os); + ASSERT_EQ(" pt=96;max-br=30000", os.str()); + } +} + +static SdpRidAttributeList::Rid +ParseRid(const std::string& input) +{ + std::istringstream is(input); + std::string error; + SdpRidAttributeList::Rid rid; + EXPECT_TRUE(rid.Parse(is, &error)) << error; + EXPECT_TRUE(is.eof()); + return rid; +} + +TEST(NewSdpTestNoFixture, CheckRidValidParse) +{ + { + SdpRidAttributeList::Rid rid(ParseRid("1 send")); + ASSERT_EQ("1", rid.id); + ASSERT_EQ(sdp::kSend, rid.direction); + ASSERT_EQ(0U, rid.constraints.formats.size()); + ASSERT_EQ(0U, rid.constraints.maxWidth); + ASSERT_EQ(0U, rid.constraints.maxHeight); + ASSERT_EQ(0U, rid.constraints.maxFps); + ASSERT_EQ(0U, rid.constraints.maxFs); + ASSERT_EQ(0U, rid.constraints.maxBr); + ASSERT_EQ(0U, rid.constraints.maxPps); + ASSERT_EQ(0U, rid.constraints.dependIds.size()); + } + + { + SdpRidAttributeList::Rid rid(ParseRid("1 send pt=96;max-width=800")); + ASSERT_EQ("1", rid.id); + ASSERT_EQ(sdp::kSend, rid.direction); + ASSERT_EQ(1U, rid.constraints.formats.size()); + ASSERT_EQ(96U, rid.constraints.formats[0]); + ASSERT_EQ(800U, rid.constraints.maxWidth); + ASSERT_EQ(0U, rid.constraints.maxHeight); + ASSERT_EQ(0U, rid.constraints.maxFps); + ASSERT_EQ(0U, rid.constraints.maxFs); + ASSERT_EQ(0U, rid.constraints.maxBr); + ASSERT_EQ(0U, rid.constraints.maxPps); + ASSERT_EQ(0U, rid.constraints.dependIds.size()); + } + + { + SdpRidAttributeList::Rid rid(ParseRid("1 send pt=96,97,98;max-width=800")); + ASSERT_EQ("1", rid.id); + ASSERT_EQ(sdp::kSend, rid.direction); + ASSERT_EQ(3U, rid.constraints.formats.size()); + ASSERT_EQ(96U, rid.constraints.formats[0]); + ASSERT_EQ(97U, rid.constraints.formats[1]); + ASSERT_EQ(98U, rid.constraints.formats[2]); + ASSERT_EQ(800U, rid.constraints.maxWidth); + ASSERT_EQ(0U, rid.constraints.maxHeight); + ASSERT_EQ(0U, rid.constraints.maxFps); + ASSERT_EQ(0U, rid.constraints.maxFs); + ASSERT_EQ(0U, rid.constraints.maxBr); + ASSERT_EQ(0U, rid.constraints.maxPps); + ASSERT_EQ(0U, rid.constraints.dependIds.size()); + } + + { + SdpRidAttributeList::Rid rid( + ParseRid("0123456789az-_ recv max-width=800")); + ASSERT_EQ("0123456789az-_", rid.id); + ASSERT_EQ(sdp::kRecv, rid.direction); + ASSERT_EQ(0U, rid.constraints.formats.size()); + ASSERT_EQ(800U, rid.constraints.maxWidth); + ASSERT_EQ(0U, rid.constraints.maxHeight); + ASSERT_EQ(0U, rid.constraints.maxFps); + ASSERT_EQ(0U, rid.constraints.maxFs); + ASSERT_EQ(0U, rid.constraints.maxBr); + ASSERT_EQ(0U, rid.constraints.maxPps); + ASSERT_EQ(0U, rid.constraints.dependIds.size()); + } + +} + +TEST(NewSdpTestNoFixture, CheckRidInvalidParse) +{ + ParseInvalid("", 0); + ParseInvalid(" ", 1); + ParseInvalid("foo", 3); + ParseInvalid("foo ", 4); + ParseInvalid("foo ", 5); + ParseInvalid("foo bar", 7); + ParseInvalid("foo recv ", 9); + ParseInvalid("foo recv pt=", 12); +} + +TEST(NewSdpTestNoFixture, CheckRidSerialize) +{ + { + SdpRidAttributeList::Rid rid; + rid.id = "foo"; + rid.direction = sdp::kSend; + std::ostringstream os; + rid.Serialize(os); + ASSERT_EQ("foo send", os.str()); + } +} + } // End namespace test. int main(int argc, char **argv) {