/* -*- 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/. */ #ifndef _SDPATTRIBUTE_H_ #define _SDPATTRIBUTE_H_ #include #include #include #include #include #include #include #include #include "mozilla/UniquePtr.h" #include "mozilla/Attributes.h" #include "mozilla/Assertions.h" #include "mozilla/Maybe.h" #include "sdp/SdpEnum.h" #include "common/EncodingConstraints.h" namespace mozilla { /** * Base class for SDP attributes */ class SdpAttribute { public: enum AttributeType { kFirstAttribute = 0, kBundleOnlyAttribute = 0, kCandidateAttribute, kConnectionAttribute, kDirectionAttribute, kDtlsMessageAttribute, kEndOfCandidatesAttribute, kExtmapAttribute, kFingerprintAttribute, kFmtpAttribute, kGroupAttribute, kIceLiteAttribute, kIceMismatchAttribute, kIceOptionsAttribute, kIcePwdAttribute, kIceUfragAttribute, kIdentityAttribute, kImageattrAttribute, kLabelAttribute, kMaxptimeAttribute, kMidAttribute, kMsidAttribute, kMsidSemanticAttribute, kPtimeAttribute, kRemoteCandidatesAttribute, kRidAttribute, kRtcpAttribute, kRtcpFbAttribute, kRtcpMuxAttribute, kRtcpRsizeAttribute, kRtpmapAttribute, kSctpmapAttribute, kSetupAttribute, kSimulcastAttribute, kSsrcAttribute, kSsrcGroupAttribute, kSctpPortAttribute, kMaxMessageSizeAttribute, kLastAttribute = kMaxMessageSizeAttribute }; explicit SdpAttribute(AttributeType type) : mType(type) {} virtual ~SdpAttribute() {} virtual SdpAttribute* Clone() const = 0; AttributeType GetType() const { return mType; } virtual void Serialize(std::ostream&) const = 0; static bool IsAllowedAtSessionLevel(AttributeType type); static bool IsAllowedAtMediaLevel(AttributeType type); static const std::string GetAttributeTypeString(AttributeType type); protected: AttributeType mType; }; inline std::ostream& operator<<(std::ostream& os, const SdpAttribute& attr) { attr.Serialize(os); return os; } inline std::ostream& operator<<(std::ostream& os, const SdpAttribute::AttributeType type) { os << SdpAttribute::GetAttributeTypeString(type); return os; } /////////////////////////////////////////////////////////////////////////// // a=candidate, RFC5245 //------------------------------------------------------------------------- // // candidate-attribute = "candidate" ":" foundation SP component-id SP // transport SP // priority SP // connection-address SP ;from RFC 4566 // port ;port from RFC 4566 // SP cand-type // [SP rel-addr] // [SP rel-port] // *(SP extension-att-name SP // extension-att-value) // foundation = 1*32ice-char // component-id = 1*5DIGIT // transport = "UDP" / transport-extension // transport-extension = token ; from RFC 3261 // priority = 1*10DIGIT // cand-type = "typ" SP candidate-types // candidate-types = "host" / "srflx" / "prflx" / "relay" / token // rel-addr = "raddr" SP connection-address // rel-port = "rport" SP port // extension-att-name = byte-string ;from RFC 4566 // extension-att-value = byte-string // ice-char = ALPHA / DIGIT / "+" / "/" // We use a SdpMultiStringAttribute for candidates /////////////////////////////////////////////////////////////////////////// // a=connection, RFC4145 //------------------------------------------------------------------------- // connection-attr = "a=connection:" conn-value // conn-value = "new" / "existing" class SdpConnectionAttribute : public SdpAttribute { public: enum ConnValue { kNew, kExisting }; explicit SdpConnectionAttribute(SdpConnectionAttribute::ConnValue value) : SdpAttribute(kConnectionAttribute), mValue(value) {} SdpAttribute* Clone() const override { return new SdpConnectionAttribute(*this); } virtual void Serialize(std::ostream& os) const override; ConnValue mValue; }; inline std::ostream& operator<<(std::ostream& os, SdpConnectionAttribute::ConnValue c) { switch (c) { case SdpConnectionAttribute::kNew: os << "new"; break; case SdpConnectionAttribute::kExisting: os << "existing"; break; default: MOZ_ASSERT(false); os << "?"; } return os; } /////////////////////////////////////////////////////////////////////////// // a=sendrecv / a=sendonly / a=recvonly / a=inactive, RFC 4566 //------------------------------------------------------------------------- class SdpDirectionAttribute : public SdpAttribute { public: enum Direction { kInactive = 0, kSendonly = sdp::kSend, kRecvonly = sdp::kRecv, kSendrecv = sdp::kSend | sdp::kRecv }; explicit SdpDirectionAttribute(Direction value) : SdpAttribute(kDirectionAttribute), mValue(value) {} SdpAttribute* Clone() const override { return new SdpDirectionAttribute(*this); } virtual void Serialize(std::ostream& os) const override; Direction mValue; }; inline std::ostream& operator<<(std::ostream& os, SdpDirectionAttribute::Direction d) { switch (d) { case SdpDirectionAttribute::kSendonly: os << "sendonly"; break; case SdpDirectionAttribute::kRecvonly: os << "recvonly"; break; case SdpDirectionAttribute::kSendrecv: os << "sendrecv"; break; case SdpDirectionAttribute::kInactive: os << "inactive"; break; default: MOZ_ASSERT(false); os << "?"; } return os; } inline SdpDirectionAttribute::Direction reverse( SdpDirectionAttribute::Direction d) { switch (d) { case SdpDirectionAttribute::Direction::kInactive: return SdpDirectionAttribute::Direction::kInactive; case SdpDirectionAttribute::Direction::kSendonly: return SdpDirectionAttribute::Direction::kRecvonly; case SdpDirectionAttribute::Direction::kRecvonly: return SdpDirectionAttribute::Direction::kSendonly; case SdpDirectionAttribute::Direction::kSendrecv: return SdpDirectionAttribute::Direction::kSendrecv; } MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Invalid direction!"); MOZ_RELEASE_ASSERT(false); } inline SdpDirectionAttribute::Direction operator|( SdpDirectionAttribute::Direction d1, SdpDirectionAttribute::Direction d2) { return (SdpDirectionAttribute::Direction)((unsigned)d1 | (unsigned)d2); } inline SdpDirectionAttribute::Direction operator&( SdpDirectionAttribute::Direction d1, SdpDirectionAttribute::Direction d2) { return (SdpDirectionAttribute::Direction)((unsigned)d1 & (unsigned)d2); } inline SdpDirectionAttribute::Direction operator|=( SdpDirectionAttribute::Direction& d1, SdpDirectionAttribute::Direction d2) { d1 = d1 | d2; return d1; } inline SdpDirectionAttribute::Direction operator&=( SdpDirectionAttribute::Direction& d1, SdpDirectionAttribute::Direction d2) { d1 = d1 & d2; return d1; } /////////////////////////////////////////////////////////////////////////// // a=dtls-message, draft-rescorla-dtls-in-sdp //------------------------------------------------------------------------- // attribute =/ dtls-message-attribute // // dtls-message-attribute = "dtls-message" ":" role SP value // // role = "client" / "server" // // value = 1*(ALPHA / DIGIT / "+" / "/" / "=" ) // ; base64 encoded message class SdpDtlsMessageAttribute : public SdpAttribute { public: enum Role { kClient, kServer }; explicit SdpDtlsMessageAttribute(Role role, const std::string& value) : SdpAttribute(kDtlsMessageAttribute), mRole(role), mValue(value) {} // TODO: remove this, Bug 1469702 explicit SdpDtlsMessageAttribute(const std::string& unparsed) : SdpAttribute(kDtlsMessageAttribute), mRole(kClient) { std::istringstream is(unparsed); std::string error; // We're not really worried about errors here if we don't parse; // this attribute is a pure optimization. Parse(is, &error); } SdpAttribute* Clone() const override { return new SdpDtlsMessageAttribute(*this); } virtual void Serialize(std::ostream& os) const override; // TODO: remove this, Bug 1469702 bool Parse(std::istream& is, std::string* error); Role mRole; std::string mValue; }; inline std::ostream& operator<<(std::ostream& os, SdpDtlsMessageAttribute::Role r) { switch (r) { case SdpDtlsMessageAttribute::kClient: os << "client"; break; case SdpDtlsMessageAttribute::kServer: os << "server"; break; default: MOZ_ASSERT(false); os << "?"; } return os; } /////////////////////////////////////////////////////////////////////////// // a=extmap, RFC5285 //------------------------------------------------------------------------- // RFC5285 // extmap = mapentry SP extensionname [SP extensionattributes] // // extensionname = URI // // direction = "sendonly" / "recvonly" / "sendrecv" / "inactive" // // mapentry = "extmap:" 1*5DIGIT ["/" direction] // // extensionattributes = byte-string // // URI = // // byte-string = // // SP = // // DIGIT = class SdpExtmapAttributeList : public SdpAttribute { public: SdpExtmapAttributeList() : SdpAttribute(kExtmapAttribute) {} struct Extmap { uint16_t entry; SdpDirectionAttribute::Direction direction; bool direction_specified; std::string extensionname; std::string extensionattributes; }; void PushEntry(uint16_t entry, SdpDirectionAttribute::Direction direction, bool direction_specified, const std::string& extensionname, const std::string& extensionattributes = "") { Extmap value = {entry, direction, direction_specified, extensionname, extensionattributes}; mExtmaps.push_back(value); } SdpAttribute* Clone() const override { return new SdpExtmapAttributeList(*this); } virtual void Serialize(std::ostream& os) const override; std::vector mExtmaps; }; /////////////////////////////////////////////////////////////////////////// // a=fingerprint, RFC4572 //------------------------------------------------------------------------- // fingerprint-attribute = "fingerprint" ":" hash-func SP fingerprint // // hash-func = "sha-1" / "sha-224" / "sha-256" / // "sha-384" / "sha-512" / // "md5" / "md2" / token // ; Additional hash functions can only come // ; from updates to RFC 3279 // // fingerprint = 2UHEX *(":" 2UHEX) // ; Each byte in upper-case hex, separated // ; by colons. // // UHEX = DIGIT / %x41-46 ; A-F uppercase class SdpFingerprintAttributeList : public SdpAttribute { public: SdpFingerprintAttributeList() : SdpAttribute(kFingerprintAttribute) {} enum HashAlgorithm { kSha1, kSha224, kSha256, kSha384, kSha512, kMd5, kMd2, kUnknownAlgorithm }; struct Fingerprint { HashAlgorithm hashFunc; std::vector fingerprint; }; // For use by application programmers. Enforces that it's a known and // reasonable algorithm. void PushEntry(std::string algorithm_str, const std::vector& fingerprint, bool enforcePlausible = true) { std::transform(algorithm_str.begin(), algorithm_str.end(), algorithm_str.begin(), ::tolower); SdpFingerprintAttributeList::HashAlgorithm algorithm = SdpFingerprintAttributeList::kUnknownAlgorithm; if (algorithm_str == "sha-1") { algorithm = SdpFingerprintAttributeList::kSha1; } else if (algorithm_str == "sha-224") { algorithm = SdpFingerprintAttributeList::kSha224; } else if (algorithm_str == "sha-256") { algorithm = SdpFingerprintAttributeList::kSha256; } else if (algorithm_str == "sha-384") { algorithm = SdpFingerprintAttributeList::kSha384; } else if (algorithm_str == "sha-512") { algorithm = SdpFingerprintAttributeList::kSha512; } else if (algorithm_str == "md5") { algorithm = SdpFingerprintAttributeList::kMd5; } else if (algorithm_str == "md2") { algorithm = SdpFingerprintAttributeList::kMd2; } if ((algorithm == SdpFingerprintAttributeList::kUnknownAlgorithm) || fingerprint.empty()) { if (enforcePlausible) { MOZ_ASSERT(false, "Unknown fingerprint algorithm"); } else { return; } } PushEntry(algorithm, fingerprint); } void PushEntry(HashAlgorithm hashFunc, const std::vector& fingerprint) { Fingerprint value = {hashFunc, fingerprint}; mFingerprints.push_back(value); } SdpAttribute* Clone() const override { return new SdpFingerprintAttributeList(*this); } virtual void Serialize(std::ostream& os) const override; std::vector mFingerprints; static std::string FormatFingerprint(const std::vector& fp); static std::vector ParseFingerprint(const std::string& str); }; inline std::ostream& operator<<(std::ostream& os, SdpFingerprintAttributeList::HashAlgorithm a) { switch (a) { case SdpFingerprintAttributeList::kSha1: os << "sha-1"; break; case SdpFingerprintAttributeList::kSha224: os << "sha-224"; break; case SdpFingerprintAttributeList::kSha256: os << "sha-256"; break; case SdpFingerprintAttributeList::kSha384: os << "sha-384"; break; case SdpFingerprintAttributeList::kSha512: os << "sha-512"; break; case SdpFingerprintAttributeList::kMd5: os << "md5"; break; case SdpFingerprintAttributeList::kMd2: os << "md2"; break; default: MOZ_ASSERT(false); os << "?"; } return os; } /////////////////////////////////////////////////////////////////////////// // a=group, RFC5888 //------------------------------------------------------------------------- // group-attribute = "a=group:" semantics // *(SP identification-tag) // semantics = "LS" / "FID" / semantics-extension // semantics-extension = token // identification-tag = token class SdpGroupAttributeList : public SdpAttribute { public: SdpGroupAttributeList() : SdpAttribute(kGroupAttribute) {} enum Semantics { kLs, // RFC5888 kFid, // RFC5888 kSrf, // RFC3524 kAnat, // RFC4091 kFec, // RFC5956 kFecFr, // RFC5956 kCs, // draft-mehta-rmt-flute-sdp-05 kDdp, // RFC5583 kDup, // RFC7104 kBundle // draft-ietf-mmusic-bundle }; struct Group { Semantics semantics; std::vector tags; }; void PushEntry(Semantics semantics, const std::vector& tags) { Group value = {semantics, tags}; mGroups.push_back(value); } void RemoveMid(const std::string& mid) { for (auto i = mGroups.begin(); i != mGroups.end();) { auto tag = std::find(i->tags.begin(), i->tags.end(), mid); if (tag != i->tags.end()) { i->tags.erase(tag); } if (i->tags.empty()) { i = mGroups.erase(i); } else { ++i; } } } SdpAttribute* Clone() const override { return new SdpGroupAttributeList(*this); } virtual void Serialize(std::ostream& os) const override; std::vector mGroups; }; inline std::ostream& operator<<(std::ostream& os, SdpGroupAttributeList::Semantics s) { switch (s) { case SdpGroupAttributeList::kLs: os << "LS"; break; case SdpGroupAttributeList::kFid: os << "FID"; break; case SdpGroupAttributeList::kSrf: os << "SRF"; break; case SdpGroupAttributeList::kAnat: os << "ANAT"; break; case SdpGroupAttributeList::kFec: os << "FEC"; break; case SdpGroupAttributeList::kFecFr: os << "FEC-FR"; break; case SdpGroupAttributeList::kCs: os << "CS"; break; case SdpGroupAttributeList::kDdp: os << "DDP"; break; case SdpGroupAttributeList::kDup: os << "DUP"; break; case SdpGroupAttributeList::kBundle: os << "BUNDLE"; break; default: MOZ_ASSERT(false); os << "?"; } return os; } /////////////////////////////////////////////////////////////////////////// // a=identity, draft-ietf-rtcweb-security-arch //------------------------------------------------------------------------- // identity-attribute = "identity:" identity-assertion // [ SP identity-extension // *(";" [ SP ] identity-extension) ] // identity-assertion = base64 // base64 = 1*(ALPHA / DIGIT / "+" / "/" / "=" ) // identity-extension = extension-att-name [ "=" extension-att-value ] // extension-att-name = token // extension-att-value = 1*(%x01-09 / %x0b-0c / %x0e-3a / %x3c-ff) // ; byte-string from [RFC4566] omitting ";" // We're just using an SdpStringAttribute for this right now #if 0 class SdpIdentityAttribute : public SdpAttribute { public: explicit SdpIdentityAttribute(const std::string &assertion, const std::vector &extensions = std::vector()) : SdpAttribute(kIdentityAttribute), mAssertion(assertion), mExtensions(extensions) {} virtual void Serialize(std::ostream& os) const override; std::string mAssertion; std::vector mExtensions; } #endif /////////////////////////////////////////////////////////////////////////// // a=imageattr, RFC6236 //------------------------------------------------------------------------- // image-attr = "imageattr:" PT 1*2( 1*WSP ( "send" / "recv" ) // 1*WSP attr-list ) // PT = 1*DIGIT / "*" // attr-list = ( set *(1*WSP set) ) / "*" // ; WSP and DIGIT defined in [RFC5234] // // set= "[" "x=" xyrange "," "y=" xyrange *( "," key-value ) "]" // ; x is the horizontal image size range (pixel count) // ; y is the vertical image size range (pixel count) // // key-value = ( "sar=" srange ) // / ( "par=" prange ) // / ( "q=" qvalue ) // ; Key-value MAY be extended with other keyword // ; parameters. // ; At most, one instance each of sar, par, or q // ; is allowed in a set. // ; // ; sar (sample aspect ratio) is the sample aspect ratio // ; associated with the set (optional, MAY be ignored) // ; par (picture aspect ratio) is the allowed // ; ratio between the display's x and y physical // ; size (optional) // ; q (optional, range [0.0..1.0], default value 0.5) // ; is the preference for the given set, // ; a higher value means a higher preference // // onetonine = "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9" // ; Digit between 1 and 9 // xyvalue = onetonine *5DIGIT // ; Digit between 1 and 9 that is // ; followed by 0 to 5 other digits // step = xyvalue // xyrange = ( "[" xyvalue ":" [ step ":" ] xyvalue "]" ) // ; Range between a lower and an upper value // ; with an optional step, default step = 1 // ; The rightmost occurrence of xyvalue MUST have a // ; higher value than the leftmost occurrence. // / ( "[" xyvalue 1*( "," xyvalue ) "]" ) // ; Discrete values separated by ',' // / ( xyvalue ) // ; A single value // spvalue = ( "0" "." onetonine *3DIGIT ) // ; Values between 0.1000 and 0.9999 // / ( onetonine "." 1*4DIGIT ) // ; Values between 1.0000 and 9.9999 // srange = ( "[" spvalue 1*( "," spvalue ) "]" ) // ; Discrete values separated by ','. // ; Each occurrence of spvalue MUST be // ; greater than the previous occurrence. // / ( "[" spvalue "-" spvalue "]" ) // ; Range between a lower and an upper level (inclusive) // ; The second occurrence of spvalue MUST have a higher // ; value than the first // / ( spvalue ) // ; A single value // // prange = ( "[" spvalue "-" spvalue "]" ) // ; Range between a lower and an upper level (inclusive) // ; The second occurrence of spvalue MUST have a higher // ; value than the first // // qvalue = ( "0" "." 1*2DIGIT ) // / ( "1" "." 1*2("0") ) // ; Values between 0.00 and 1.00 // // XXX TBD -- We don't use this yet, and it's a project unto itself. // class SdpImageattrAttributeList : public SdpAttribute { public: SdpImageattrAttributeList() : SdpAttribute(kImageattrAttribute) {} class XYRange { public: XYRange() : min(0), max(0), step(1) {} void Serialize(std::ostream& os) const; // TODO: Remove this Bug 1469702 bool Parse(std::istream& is, std::string* error); // TODO: Remove this Bug 1469702 bool ParseAfterBracket(std::istream& is, std::string* error); // TODO: Remove this Bug 1469702 bool ParseAfterMin(std::istream& is, std::string* error); // TODO: Remove this Bug 1469702 bool ParseDiscreteValues(std::istream& is, std::string* error); std::vector discreteValues; // min/max are used iff discreteValues is empty uint32_t min; uint32_t max; uint32_t step; }; class SRange { public: SRange() : min(0), max(0) {} void Serialize(std::ostream& os) const; // TODO: Remove this Bug 1469702 bool Parse(std::istream& is, std::string* error); // TODO: Remove this Bug 1469702 bool ParseAfterBracket(std::istream& is, std::string* error); // TODO: Remove this Bug 1469702 bool ParseAfterMin(std::istream& is, std::string* error); // TODO: Remove this Bug 1469702 bool ParseDiscreteValues(std::istream& is, std::string* error); bool IsSet() const { return !discreteValues.empty() || (min && max); } std::vector discreteValues; // min/max are used iff discreteValues is empty float min; float max; }; class PRange { public: PRange() : min(0), max(0) {} void Serialize(std::ostream& os) const; // TODO: Remove this Bug 1469702 bool Parse(std::istream& is, std::string* error); bool IsSet() const { return min && max; } float min; float max; }; class Set { public: Set() : qValue(-1) {} void Serialize(std::ostream& os) const; // TODO: Remove this Bug 1469702 bool Parse(std::istream& is, std::string* error); XYRange xRange; XYRange yRange; SRange sRange; PRange pRange; float qValue; }; class Imageattr { public: Imageattr() : pt(), sendAll(false), recvAll(false) {} void Serialize(std::ostream& os) const; // TODO: Remove this Bug 1469702 bool Parse(std::istream& is, std::string* error); // TODO: Remove this Bug 1469702 bool ParseSets(std::istream& is, std::string* error); // If not set, this means all payload types Maybe pt; bool sendAll; std::vector sendSets; bool recvAll; std::vector recvSets; }; SdpAttribute* Clone() const override { return new SdpImageattrAttributeList(*this); } virtual void Serialize(std::ostream& os) const override; // TODO: Remove this Bug 1469702 bool PushEntry(const std::string& raw, std::string* error, size_t* errorPos); std::vector mImageattrs; }; /////////////////////////////////////////////////////////////////////////// // a=msid, draft-ietf-mmusic-msid //------------------------------------------------------------------------- // msid-attr = "msid:" identifier [ SP appdata ] // identifier = 1*64token-char ; see RFC 4566 // appdata = 1*64token-char ; see RFC 4566 class SdpMsidAttributeList : public SdpAttribute { public: SdpMsidAttributeList() : SdpAttribute(kMsidAttribute) {} struct Msid { std::string identifier; std::string appdata; }; void PushEntry(const std::string& identifier, const std::string& appdata = "") { Msid value = {identifier, appdata}; mMsids.push_back(value); } SdpAttribute* Clone() const override { return new SdpMsidAttributeList(*this); } virtual void Serialize(std::ostream& os) const override; std::vector mMsids; }; /////////////////////////////////////////////////////////////////////////// // a=msid-semantic, draft-ietf-mmusic-msid //------------------------------------------------------------------------- // msid-semantic-attr = "msid-semantic:" msid-semantic msid-list // msid-semantic = token ; see RFC 4566 // msid-list = *(" " msid-id) / " *" class SdpMsidSemanticAttributeList : public SdpAttribute { public: SdpMsidSemanticAttributeList() : SdpAttribute(kMsidSemanticAttribute) {} struct MsidSemantic { // TODO: Once we have some more of these, we might want to make an enum std::string semantic; std::vector msids; }; void PushEntry(const std::string& semantic, const std::vector& msids) { MsidSemantic value = {semantic, msids}; mMsidSemantics.push_back(value); } SdpAttribute* Clone() const override { return new SdpMsidSemanticAttributeList(*this); } virtual void Serialize(std::ostream& os) const override; std::vector mMsidSemantics; }; /////////////////////////////////////////////////////////////////////////// // a=remote-candiate, RFC5245 //------------------------------------------------------------------------- // remote-candidate-att = "remote-candidates" ":" remote-candidate // 0*(SP remote-candidate) // remote-candidate = component-ID SP connection-address SP port class SdpRemoteCandidatesAttribute : public SdpAttribute { public: struct Candidate { std::string id; std::string address; uint16_t port; }; explicit SdpRemoteCandidatesAttribute( const std::vector& candidates) : SdpAttribute(kRemoteCandidatesAttribute), mCandidates(candidates) {} SdpAttribute* Clone() const override { return new SdpRemoteCandidatesAttribute(*this); } virtual void Serialize(std::ostream& os) const override; 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 Rid { Rid() : direction(sdp::kSend) {} // Remove this function. See Bug 1469702 bool Parse(std::istream& is, std::string* error); // Remove this function. See Bug 1469702 bool ParseParameters(std::istream& is, std::string* error); // Remove this function. See Bug 1469702 bool ParseDepend(std::istream& is, std::string* error); // Remove this function. See Bug 1469702 bool ParseFormats(std::istream& is, std::string* error); void Serialize(std::ostream& os) const; void SerializeParameters(std::ostream& os) const; bool HasFormat(const std::string& format) const; bool HasParameters() const { return !formats.empty() || constraints.maxWidth || constraints.maxHeight || constraints.maxFps || constraints.maxFs || constraints.maxBr || constraints.maxPps || !dependIds.empty(); } std::string id; sdp::Direction direction; std::vector formats; // Empty implies all EncodingConstraints constraints; std::vector dependIds; }; SdpAttribute* Clone() const override { return new SdpRidAttributeList(*this); } static bool CheckRidValidity(const std::string& aRid, std::string* aError); static size_t kMaxRidLength; virtual void Serialize(std::ostream& os) const override; // Remove this function. See Bug 1469702 bool PushEntry(const std::string& raw, std::string* error, size_t* errorPos); void PushEntry(const std::string& id, sdp::Direction dir, const std::vector& formats, const EncodingConstraints& constraints, const std::vector& dependIds); std::vector mRids; }; /////////////////////////////////////////////////////////////////////////// // a=rtcp, RFC3605 //------------------------------------------------------------------------- // rtcp-attribute = "a=rtcp:" port [nettype space addrtype space // connection-address] CRLF class SdpRtcpAttribute : public SdpAttribute { public: explicit SdpRtcpAttribute(uint16_t port) : SdpAttribute(kRtcpAttribute), mPort(port), mNetType(sdp::kNetTypeNone), mAddrType(sdp::kAddrTypeNone) {} SdpRtcpAttribute(uint16_t port, sdp::NetType netType, sdp::AddrType addrType, const std::string& address) : SdpAttribute(kRtcpAttribute), mPort(port), mNetType(netType), mAddrType(addrType), mAddress(address) { MOZ_ASSERT(netType != sdp::kNetTypeNone); MOZ_ASSERT(addrType != sdp::kAddrTypeNone); MOZ_ASSERT(!address.empty()); } SdpAttribute* Clone() const override { return new SdpRtcpAttribute(*this); } virtual void Serialize(std::ostream& os) const override; uint16_t mPort; sdp::NetType mNetType; sdp::AddrType mAddrType; std::string mAddress; }; /////////////////////////////////////////////////////////////////////////// // a=rtcp-fb, RFC4585 //------------------------------------------------------------------------- // rtcp-fb-syntax = "a=rtcp-fb:" rtcp-fb-pt SP rtcp-fb-val CRLF // // rtcp-fb-pt = "*" ; wildcard: applies to all formats // / fmt ; as defined in SDP spec // // rtcp-fb-val = "ack" rtcp-fb-ack-param // / "nack" rtcp-fb-nack-param // / "trr-int" SP 1*DIGIT // / rtcp-fb-id rtcp-fb-param // // rtcp-fb-id = 1*(alpha-numeric / "-" / "_") // // rtcp-fb-param = SP "app" [SP byte-string] // / SP token [SP byte-string] // / ; empty // // rtcp-fb-ack-param = SP "rpsi" // / SP "app" [SP byte-string] // / SP token [SP byte-string] // / ; empty // // rtcp-fb-nack-param = SP "pli" // / SP "sli" // / SP "rpsi" // / SP "app" [SP byte-string] // / SP token [SP byte-string] // / ; empty // class SdpRtcpFbAttributeList : public SdpAttribute { public: SdpRtcpFbAttributeList() : SdpAttribute(kRtcpFbAttribute) {} enum Type { kAck, kApp, kCcm, kNack, kTrrInt, kRemb, kTransportCC }; static const char* pli; static const char* sli; static const char* rpsi; static const char* app; static const char* fir; static const char* tmmbr; static const char* tstr; static const char* vbcm; struct Feedback { std::string pt; Type type; std::string parameter; std::string extra; // TODO(bug 1744307): Use =default here once it is supported bool operator==(const Feedback& aOther) const { return pt == aOther.pt && type == aOther.type && parameter == aOther.parameter && extra == aOther.extra; } }; void PushEntry(const std::string& pt, Type type, const std::string& parameter = "", const std::string& extra = "") { Feedback value = {pt, type, parameter, extra}; mFeedbacks.push_back(value); } SdpAttribute* Clone() const override { return new SdpRtcpFbAttributeList(*this); } virtual void Serialize(std::ostream& os) const override; std::vector mFeedbacks; }; inline std::ostream& operator<<(std::ostream& os, SdpRtcpFbAttributeList::Type type) { switch (type) { case SdpRtcpFbAttributeList::kAck: os << "ack"; break; case SdpRtcpFbAttributeList::kApp: os << "app"; break; case SdpRtcpFbAttributeList::kCcm: os << "ccm"; break; case SdpRtcpFbAttributeList::kNack: os << "nack"; break; case SdpRtcpFbAttributeList::kTrrInt: os << "trr-int"; break; case SdpRtcpFbAttributeList::kRemb: os << "goog-remb"; break; case SdpRtcpFbAttributeList::kTransportCC: os << "transport-cc"; break; default: MOZ_ASSERT(false); os << "?"; } return os; } /////////////////////////////////////////////////////////////////////////// // a=rtpmap, RFC4566 //------------------------------------------------------------------------- // a=rtpmap: / [/] class SdpRtpmapAttributeList : public SdpAttribute { public: SdpRtpmapAttributeList() : SdpAttribute(kRtpmapAttribute) {} // Minimal set to get going enum CodecType { kOpus, kG722, kPCMU, kPCMA, kVP8, kVP9, kiLBC, kiSAC, kH264, kRed, kUlpfec, kTelephoneEvent, kRtx, kOtherCodec }; struct Rtpmap { std::string pt; CodecType codec; std::string name; uint32_t clock; // Technically, this could mean something else in the future. // In practice, that's probably not going to happen. uint32_t channels; }; void PushEntry(const std::string& pt, CodecType codec, const std::string& name, uint32_t clock, uint32_t channels = 0) { Rtpmap value = {pt, codec, name, clock, channels}; mRtpmaps.push_back(value); } SdpAttribute* Clone() const override { return new SdpRtpmapAttributeList(*this); } virtual void Serialize(std::ostream& os) const override; bool HasEntry(const std::string& pt) const { for (auto it = mRtpmaps.begin(); it != mRtpmaps.end(); ++it) { if (it->pt == pt) { return true; } } return false; } const Rtpmap& GetEntry(const std::string& pt) const { for (auto it = mRtpmaps.begin(); it != mRtpmaps.end(); ++it) { if (it->pt == pt) { return *it; } } MOZ_CRASH(); } std::vector mRtpmaps; }; inline std::ostream& operator<<(std::ostream& os, SdpRtpmapAttributeList::CodecType c) { switch (c) { case SdpRtpmapAttributeList::kOpus: os << "opus"; break; case SdpRtpmapAttributeList::kG722: os << "G722"; break; case SdpRtpmapAttributeList::kPCMU: os << "PCMU"; break; case SdpRtpmapAttributeList::kPCMA: os << "PCMA"; break; case SdpRtpmapAttributeList::kVP8: os << "VP8"; break; case SdpRtpmapAttributeList::kVP9: os << "VP9"; break; case SdpRtpmapAttributeList::kiLBC: os << "iLBC"; break; case SdpRtpmapAttributeList::kiSAC: os << "iSAC"; break; case SdpRtpmapAttributeList::kH264: os << "H264"; break; case SdpRtpmapAttributeList::kRed: os << "red"; break; case SdpRtpmapAttributeList::kUlpfec: os << "ulpfec"; break; case SdpRtpmapAttributeList::kTelephoneEvent: os << "telephone-event"; break; case SdpRtpmapAttributeList::kRtx: os << "rtx"; break; default: MOZ_ASSERT(false); os << "?"; } return os; } /////////////////////////////////////////////////////////////////////////// // a=fmtp, RFC4566, RFC5576 //------------------------------------------------------------------------- // a=fmtp: // class SdpFmtpAttributeList : public SdpAttribute { public: SdpFmtpAttributeList() : SdpAttribute(kFmtpAttribute) {} // Base class for format parameters class Parameters { public: explicit Parameters(SdpRtpmapAttributeList::CodecType aCodec) : codec_type(aCodec) {} virtual ~Parameters() {} virtual Parameters* Clone() const = 0; virtual void Serialize(std::ostream& os) const = 0; virtual bool CompareEq(const Parameters& other) const = 0; bool operator==(const Parameters& other) const { return codec_type == other.codec_type && CompareEq(other); } SdpRtpmapAttributeList::CodecType codec_type; }; class RedParameters : public Parameters { public: RedParameters() : Parameters(SdpRtpmapAttributeList::kRed) {} virtual Parameters* Clone() const override { return new RedParameters(*this); } virtual void Serialize(std::ostream& os) const override { for (size_t i = 0; i < encodings.size(); ++i) { os << (i != 0 ? "/" : "") << std::to_string(encodings[i]); } } virtual bool CompareEq(const Parameters& other) const override { return encodings == static_cast(other).encodings; } std::vector encodings; }; class RtxParameters : public Parameters { public: uint8_t apt = 255; // Valid payload types are 0 - 127, use 255 to represent // unset value. Maybe rtx_time; RtxParameters() : Parameters(SdpRtpmapAttributeList::kRtx) {} virtual ~RtxParameters() {} virtual Parameters* Clone() const override { return new RtxParameters(*this); } virtual void Serialize(std::ostream& os) const override { if (apt <= 127) { os << "apt=" << static_cast(apt); rtx_time.apply([&](const auto& time) { os << ";rtx-time=" << time; }); } } virtual bool CompareEq(const Parameters& aOther) const override { if (aOther.codec_type != codec_type) { return false; } auto other = static_cast(aOther); return other.apt == apt && other.rtx_time == rtx_time; } }; class H264Parameters : public Parameters { public: static const uint32_t kDefaultProfileLevelId = 0x420010; H264Parameters() : Parameters(SdpRtpmapAttributeList::kH264), packetization_mode(0), level_asymmetry_allowed(false), profile_level_id(kDefaultProfileLevelId), max_mbps(0), max_fs(0), max_cpb(0), max_dpb(0), max_br(0) { memset(sprop_parameter_sets, 0, sizeof(sprop_parameter_sets)); } virtual Parameters* Clone() const override { return new H264Parameters(*this); } virtual void Serialize(std::ostream& os) const override { // Note: don't move this, since having an unconditional param up top // lets us avoid a whole bunch of conditional streaming of ';' below os << "profile-level-id=" << std::hex << std::setfill('0') << std::setw(6) << profile_level_id << std::dec << std::setfill(' '); os << ";level-asymmetry-allowed=" << (level_asymmetry_allowed ? 1 : 0); if (strlen(sprop_parameter_sets)) { os << ";sprop-parameter-sets=" << sprop_parameter_sets; } if (packetization_mode != 0) { os << ";packetization-mode=" << packetization_mode; } if (max_mbps != 0) { os << ";max-mbps=" << max_mbps; } if (max_fs != 0) { os << ";max-fs=" << max_fs; } if (max_cpb != 0) { os << ";max-cpb=" << max_cpb; } if (max_dpb != 0) { os << ";max-dpb=" << max_dpb; } if (max_br != 0) { os << ";max-br=" << max_br; } } virtual bool CompareEq(const Parameters& other) const override { const auto& otherH264 = static_cast(other); // sprop is not comapred here as it does not get parsed in the rsdparsa return packetization_mode == otherH264.packetization_mode && level_asymmetry_allowed == otherH264.level_asymmetry_allowed && profile_level_id == otherH264.profile_level_id && max_mbps == otherH264.max_mbps && max_fs == otherH264.max_fs && max_cpb == otherH264.max_cpb && max_dpb == otherH264.max_dpb && max_br == otherH264.max_br; } static const size_t max_sprop_len = 128; char sprop_parameter_sets[max_sprop_len]; unsigned int packetization_mode; bool level_asymmetry_allowed; unsigned int profile_level_id; unsigned int max_mbps; unsigned int max_fs; unsigned int max_cpb; unsigned int max_dpb; unsigned int max_br; }; // Also used for VP9 since they share parameters class VP8Parameters : public Parameters { public: explicit VP8Parameters(SdpRtpmapAttributeList::CodecType type) : Parameters(type), max_fs(0), max_fr(0) {} virtual Parameters* Clone() const override { return new VP8Parameters(*this); } virtual void Serialize(std::ostream& os) const override { // draft-ietf-payload-vp8-11 says these are mandatory, upper layer // needs to ensure they're set properly. os << "max-fs=" << max_fs; os << ";max-fr=" << max_fr; } virtual bool CompareEq(const Parameters& other) const override { const auto& otherVP8 = static_cast(other); return max_fs == otherVP8.max_fs && max_fr == otherVP8.max_fr; } unsigned int max_fs; unsigned int max_fr; }; class OpusParameters : public Parameters { public: enum { kDefaultMaxPlaybackRate = 48000, kDefaultStereo = 0, kDefaultUseInBandFec = 0, kDefaultMaxAverageBitrate = 0, kDefaultUseDTX = 0, kDefaultFrameSize = 0, kDefaultMinFrameSize = 0, kDefaultMaxFrameSize = 0, kDefaultUseCbr = 0 }; OpusParameters() : Parameters(SdpRtpmapAttributeList::kOpus), maxplaybackrate(kDefaultMaxPlaybackRate), stereo(kDefaultStereo), useInBandFec(kDefaultUseInBandFec), maxAverageBitrate(kDefaultMaxAverageBitrate), useDTX(kDefaultUseDTX), frameSizeMs(kDefaultFrameSize), minFrameSizeMs(kDefaultMinFrameSize), maxFrameSizeMs(kDefaultMaxFrameSize), useCbr(kDefaultUseCbr) {} Parameters* Clone() const override { return new OpusParameters(*this); } void Serialize(std::ostream& os) const override { os << "maxplaybackrate=" << maxplaybackrate << ";stereo=" << stereo << ";useinbandfec=" << useInBandFec; if (useDTX) { os << ";usedtx=1"; } if (maxAverageBitrate) { os << ";maxaveragebitrate=" << maxAverageBitrate; } if (frameSizeMs) { os << ";ptime=" << frameSizeMs; } if (minFrameSizeMs) { os << ";minptime=" << minFrameSizeMs; } if (maxFrameSizeMs) { os << ";maxptime=" << maxFrameSizeMs; } if (useCbr) { os << ";cbr=1"; } } virtual bool CompareEq(const Parameters& other) const override { const auto& otherOpus = static_cast(other); bool maxplaybackrateIsEq = (maxplaybackrate == otherOpus.maxplaybackrate); // This is due to a bug in sipcc that causes maxplaybackrate to // always be 0 if it appears in the fmtp if (((maxplaybackrate == 0) && (otherOpus.maxplaybackrate != 0)) || ((maxplaybackrate != 0) && (otherOpus.maxplaybackrate == 0))) { maxplaybackrateIsEq = true; } return maxplaybackrateIsEq && stereo == otherOpus.stereo && useInBandFec == otherOpus.useInBandFec && maxAverageBitrate == otherOpus.maxAverageBitrate && useDTX == otherOpus.useDTX && frameSizeMs == otherOpus.frameSizeMs && minFrameSizeMs == otherOpus.minFrameSizeMs && maxFrameSizeMs == otherOpus.maxFrameSizeMs && useCbr == otherOpus.useCbr; } unsigned int maxplaybackrate; unsigned int stereo; unsigned int useInBandFec; uint32_t maxAverageBitrate; bool useDTX; uint32_t frameSizeMs; uint32_t minFrameSizeMs; uint32_t maxFrameSizeMs; bool useCbr; }; class TelephoneEventParameters : public Parameters { public: TelephoneEventParameters() : Parameters(SdpRtpmapAttributeList::kTelephoneEvent), dtmfTones("0-15") {} virtual Parameters* Clone() const override { return new TelephoneEventParameters(*this); } void Serialize(std::ostream& os) const override { os << dtmfTones; } virtual bool CompareEq(const Parameters& other) const override { return dtmfTones == static_cast(other).dtmfTones; } std::string dtmfTones; }; class Fmtp { public: Fmtp(const std::string& aFormat, const Parameters& aParameters) : format(aFormat), parameters(aParameters.Clone()) {} // TODO: Rip all of this out when we have move semantics in the stl. Fmtp(const Fmtp& orig) { *this = orig; } Fmtp& operator=(const Fmtp& rhs) { if (this != &rhs) { format = rhs.format; parameters.reset(rhs.parameters ? rhs.parameters->Clone() : nullptr); } return *this; } bool operator==(const Fmtp& other) const { return format == other.format && *parameters == *other.parameters; } // The contract around these is as follows: // * |parameters| is only set if we recognized the media type and had // a subclass of Parameters to represent that type of parameters // * |parameters| is a best-effort representation; it might be missing // stuff // * Parameters::codec_type tells you the concrete class, eg // kH264 -> H264Parameters std::string format; UniquePtr parameters; }; bool operator==(const SdpFmtpAttributeList& other) const; SdpAttribute* Clone() const override { return new SdpFmtpAttributeList(*this); } virtual void Serialize(std::ostream& os) const override; void PushEntry(const std::string& format, const Parameters& parameters) { mFmtps.push_back(Fmtp(format, parameters)); } std::vector mFmtps; }; /////////////////////////////////////////////////////////////////////////// // a=sctpmap, draft-ietf-mmusic-sctp-sdp-05 //------------------------------------------------------------------------- // sctpmap-attr = "a=sctpmap:" sctpmap-number media-subtypes // [streams] // sctpmap-number = 1*DIGIT // protocol = labelstring // labelstring = text // text = byte-string // streams = 1*DIGIT // // We're going to pretend that there are spaces where they make sense. class SdpSctpmapAttributeList : public SdpAttribute { public: SdpSctpmapAttributeList() : SdpAttribute(kSctpmapAttribute) {} struct Sctpmap { std::string pt; std::string name; uint32_t streams; }; void PushEntry(const std::string& pt, const std::string& name, uint32_t streams = 0) { Sctpmap value = {pt, name, streams}; mSctpmaps.push_back(value); } SdpAttribute* Clone() const override { return new SdpSctpmapAttributeList(*this); } virtual void Serialize(std::ostream& os) const override; bool HasEntry(const std::string& pt) const { for (auto it = mSctpmaps.begin(); it != mSctpmaps.end(); ++it) { if (it->pt == pt) { return true; } } return false; } const Sctpmap& GetFirstEntry() const { return mSctpmaps[0]; } std::vector mSctpmaps; }; /////////////////////////////////////////////////////////////////////////// // a=setup, RFC4145 //------------------------------------------------------------------------- // setup-attr = "a=setup:" role // role = "active" / "passive" / "actpass" / "holdconn" class SdpSetupAttribute : public SdpAttribute { public: enum Role { kActive, kPassive, kActpass, kHoldconn }; explicit SdpSetupAttribute(Role role) : SdpAttribute(kSetupAttribute), mRole(role) {} SdpAttribute* Clone() const override { return new SdpSetupAttribute(*this); } virtual void Serialize(std::ostream& os) const override; Role mRole; }; inline std::ostream& operator<<(std::ostream& os, SdpSetupAttribute::Role r) { switch (r) { case SdpSetupAttribute::kActive: os << "active"; break; case SdpSetupAttribute::kPassive: os << "passive"; break; case SdpSetupAttribute::kActpass: os << "actpass"; break; case SdpSetupAttribute::kHoldconn: os << "holdconn"; break; default: MOZ_ASSERT(false); os << "?"; } return os; } // Old draft-04 // sc-attr = "a=simulcast:" 1*2( WSP sc-str-list ) [WSP sc-pause-list] // sc-str-list = sc-dir WSP sc-id-type "=" sc-alt-list *( ";" sc-alt-list ) // sc-pause-list = "paused=" sc-alt-list // sc-dir = "send" / "recv" // sc-id-type = "pt" / "rid" / token // sc-alt-list = sc-id *( "," sc-id ) // sc-id = fmt / rid-identifier / token // ; WSP defined in [RFC5234] // ; fmt, token defined in [RFC4566] // ; rid-identifier defined in [I-D.pthatcher-mmusic-rid] // // New draft 14, need to parse this for now, will eventually emit it // sc-value = ( sc-send [SP sc-recv] ) / ( sc-recv [SP sc-send] ) // sc-send = %s"send" SP sc-str-list // sc-recv = %s"recv" SP sc-str-list // sc-str-list = sc-alt-list *( ";" sc-alt-list ) // sc-alt-list = sc-id *( "," sc-id ) // sc-id-paused = "~" // sc-id = [sc-id-paused] rid-id // ; SP defined in [RFC5234] // ; rid-id defined in [I-D.ietf-mmusic-rid] class SdpSimulcastAttribute : public SdpAttribute { public: SdpSimulcastAttribute() : SdpAttribute(kSimulcastAttribute) {} SdpAttribute* Clone() const override { return new SdpSimulcastAttribute(*this); } void Serialize(std::ostream& os) const override; bool Parse(std::istream& is, std::string* error); class Encoding { public: Encoding(const std::string& aRid, bool aPaused) : rid(aRid), paused(aPaused) {} std::string rid; bool paused = false; }; class Version { public: void Serialize(std::ostream& os) const; bool IsSet() const { return !choices.empty(); } bool Parse(std::istream& is, std::string* error); std::vector choices; }; class Versions : public std::vector { public: void Serialize(std::ostream& os) const; bool IsSet() const { if (empty()) { return false; } for (const Version& version : *this) { if (version.IsSet()) { return true; } } return false; } bool Parse(std::istream& is, std::string* error); }; Versions sendVersions; Versions recvVersions; }; /////////////////////////////////////////////////////////////////////////// // a=ssrc, RFC5576 //------------------------------------------------------------------------- // ssrc-attr = "ssrc:" ssrc-id SP attribute // ; The base definition of "attribute" is in RFC 4566. // ; (It is the content of "a=" lines.) // // ssrc-id = integer ; 0 .. 2**32 - 1 //------------------------------------------------------------------------- // TODO -- In the future, it might be nice if we ran a parse on the // attribute section of this so that we could interpret it semantically. // For WebRTC, the key use case for a=ssrc is assocaiting SSRCs with // media sections, and we're not really going to care about the attribute // itself. So we're just going to store it as a string for the time being. // Issue 187. class SdpSsrcAttributeList : public SdpAttribute { public: SdpSsrcAttributeList() : SdpAttribute(kSsrcAttribute) {} struct Ssrc { uint32_t ssrc; std::string attribute; }; void PushEntry(uint32_t ssrc, const std::string& attribute) { Ssrc value = {ssrc, attribute}; mSsrcs.push_back(value); } SdpAttribute* Clone() const override { return new SdpSsrcAttributeList(*this); } virtual void Serialize(std::ostream& os) const override; std::vector mSsrcs; }; /////////////////////////////////////////////////////////////////////////// // a=ssrc-group, RFC5576 //------------------------------------------------------------------------- // ssrc-group-attr = "ssrc-group:" semantics *(SP ssrc-id) // // semantics = "FEC" / "FID" / token // // ssrc-id = integer ; 0 .. 2**32 - 1 class SdpSsrcGroupAttributeList : public SdpAttribute { public: enum Semantics { kFec, // RFC5576 kFid, // RFC5576 kFecFr, // RFC5956 kDup, // RFC7104 kSim // non-standard, used by hangouts }; struct SsrcGroup { Semantics semantics; std::vector ssrcs; }; SdpSsrcGroupAttributeList() : SdpAttribute(kSsrcGroupAttribute) {} void PushEntry(Semantics semantics, const std::vector& ssrcs) { SsrcGroup value = {semantics, ssrcs}; mSsrcGroups.push_back(value); } SdpAttribute* Clone() const override { return new SdpSsrcGroupAttributeList(*this); } virtual void Serialize(std::ostream& os) const override; std::vector mSsrcGroups; }; inline std::ostream& operator<<(std::ostream& os, SdpSsrcGroupAttributeList::Semantics s) { switch (s) { case SdpSsrcGroupAttributeList::kFec: os << "FEC"; break; case SdpSsrcGroupAttributeList::kFid: os << "FID"; break; case SdpSsrcGroupAttributeList::kFecFr: os << "FEC-FR"; break; case SdpSsrcGroupAttributeList::kDup: os << "DUP"; break; case SdpSsrcGroupAttributeList::kSim: os << "SIM"; break; default: MOZ_ASSERT(false); os << "?"; } return os; } /////////////////////////////////////////////////////////////////////////// class SdpMultiStringAttribute : public SdpAttribute { public: explicit SdpMultiStringAttribute(AttributeType type) : SdpAttribute(type) {} void PushEntry(const std::string& entry) { mValues.push_back(entry); } SdpAttribute* Clone() const override { return new SdpMultiStringAttribute(*this); } virtual void Serialize(std::ostream& os) const override; std::vector mValues; }; // otherwise identical to SdpMultiStringAttribute, this is used for // ice-options and other places where the value is serialized onto // a single line with space separating tokens class SdpOptionsAttribute : public SdpAttribute { public: explicit SdpOptionsAttribute(AttributeType type) : SdpAttribute(type) {} void PushEntry(const std::string& entry) { mValues.push_back(entry); } void Load(const std::string& value); SdpAttribute* Clone() const override { return new SdpOptionsAttribute(*this); } virtual void Serialize(std::ostream& os) const override; std::vector mValues; }; // Used for attributes that take no value (eg; a=ice-lite) class SdpFlagAttribute : public SdpAttribute { public: explicit SdpFlagAttribute(AttributeType type) : SdpAttribute(type) {} SdpAttribute* Clone() const override { return new SdpFlagAttribute(*this); } virtual void Serialize(std::ostream& os) const override; }; // Used for any other kind of single-valued attribute not otherwise specialized class SdpStringAttribute : public SdpAttribute { public: explicit SdpStringAttribute(AttributeType type, const std::string& value) : SdpAttribute(type), mValue(value) {} SdpAttribute* Clone() const override { return new SdpStringAttribute(*this); } virtual void Serialize(std::ostream& os) const override; std::string mValue; }; // Used for any purely (non-negative) numeric attribute class SdpNumberAttribute : public SdpAttribute { public: explicit SdpNumberAttribute(AttributeType type, uint32_t value = 0) : SdpAttribute(type), mValue(value) {} SdpAttribute* Clone() const override { return new SdpNumberAttribute(*this); } virtual void Serialize(std::ostream& os) const override; uint32_t mValue; }; } // namespace mozilla #endif