Source code
Revision control
Copy as Markdown
Other Tools
/* -*- 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,
#ifndef _SDPATTRIBUTE_H_
#define _SDPATTRIBUTE_H_
#include <algorithm>
#include <cctype>
#include <vector>
#include <ostream>
#include <sstream>
#include <cstring>
#include <iomanip>
#include <string>
#include "mozilla/UniquePtr.h"
#include "mozilla/Attributes.h"
#include "mozilla/Assertions.h"
#include "mozilla/Maybe.h"
#include "nsString.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() = default;
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) {}
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;
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 = <Defined in RFC 3986>
//
// byte-string = <Defined in RFC 4566>
//
// SP = <Defined in RFC 5234>
//
// DIGIT = <Defined in RFC 5234>
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<Extmap> 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<uint8_t> fingerprint;
};
// For use by application programmers. Enforces that it's a known and
// reasonable algorithm.
void PushEntry(std::string algorithm_str,
const std::vector<uint8_t>& 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<uint8_t>& 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<Fingerprint> mFingerprints;
static std::string FormatFingerprint(const std::vector<uint8_t>& fp);
static std::vector<uint8_t> ParseFingerprint(const std::string& str);
};
inline nsLiteralCString ToString(SdpFingerprintAttributeList::HashAlgorithm a) {
static constexpr nsLiteralCString Values[] = {
"sha-1"_ns, "sha-224"_ns, "sha-256"_ns, "sha-384"_ns,
"sha-512"_ns, "md5"_ns, "md2"_ns,
};
if (a < std::size(Values)) return Values[a];
MOZ_ASSERT(false);
return "?"_ns;
}
inline std::ostream& operator<<(std::ostream& os,
SdpFingerprintAttributeList::HashAlgorithm a) {
return os << ToString(a);
}
///////////////////////////////////////////////////////////////////////////
// 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<std::string> tags;
};
void PushEntry(Semantics semantics, const std::vector<std::string>& 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<Group> 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<std::string> &extensions =
std::vector<std::string>()) :
SdpAttribute(kIdentityAttribute),
mAssertion(assertion),
mExtensions(extensions) {}
virtual void Serialize(std::ostream& os) const override;
std::string mAssertion;
std::vector<std::string> 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;
bool Parse(std::istream& is, std::string* error);
bool ParseAfterBracket(std::istream& is, std::string* error);
bool ParseAfterMin(std::istream& is, std::string* error);
bool ParseDiscreteValues(std::istream& is, std::string* error);
std::vector<uint32_t> 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;
bool Parse(std::istream& is, std::string* error);
bool ParseAfterBracket(std::istream& is, std::string* error);
bool ParseAfterMin(std::istream& is, std::string* error);
bool ParseDiscreteValues(std::istream& is, std::string* error);
bool IsSet() const { return !discreteValues.empty() || (min && max); }
std::vector<float> 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;
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;
bool Parse(std::istream& is, std::string* error);
XYRange xRange;
XYRange yRange;
SRange sRange;
PRange pRange;
float qValue;
};
class Imageattr {
public:
Imageattr() : sendAll(false), recvAll(false) {}
void Serialize(std::ostream& os) const;
bool Parse(std::istream& is, std::string* error);
bool ParseSets(std::istream& is, std::string* error);
// If not set, this means all payload types
Maybe<uint16_t> pt;
bool sendAll;
std::vector<Set> sendSets;
bool recvAll;
std::vector<Set> recvSets;
};
SdpAttribute* Clone() const override {
return new SdpImageattrAttributeList(*this);
}
virtual void Serialize(std::ostream& os) const override;
bool PushEntry(const std::string& raw, std::string* error, size_t* errorPos);
std::vector<Imageattr> 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<Msid> 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<std::string> msids;
};
void PushEntry(const std::string& semantic,
const std::vector<std::string>& 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<MsidSemantic> 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<Candidate>& candidates)
: SdpAttribute(kRemoteCandidatesAttribute), mCandidates(candidates) {}
SdpAttribute* Clone() const override {
return new SdpRemoteCandidatesAttribute(*this);
}
virtual void Serialize(std::ostream& os) const override;
std::vector<Candidate> 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) {}
bool Parse(std::istream& is, std::string* error);
bool ParseParameters(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;
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<uint16_t> formats; // Empty implies all
EncodingConstraints constraints;
std::vector<std::string> 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;
bool PushEntry(const std::string& raw, std::string* error, size_t* errorPos);
void PushEntry(const std::string& id, sdp::Direction dir,
const std::vector<uint16_t>& formats,
const EncodingConstraints& constraints,
const std::vector<std::string>& dependIds);
std::vector<Rid> 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;
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<Feedback> 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:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
class SdpRtpmapAttributeList : public SdpAttribute {
public:
SdpRtpmapAttributeList() : SdpAttribute(kRtpmapAttribute) {}
// Minimal set to get going
enum CodecType {
kOpus,
kG722,
kPCMU,
kPCMA,
kVP8,
kVP9,
kiLBC,
kiSAC,
kH264,
kAV1,
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<Rtpmap> 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::kAV1:
os << "AV1";
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:<format> <format specific parameters>
//
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() = default;
virtual Parameters* Clone() const = 0;
virtual bool ShouldSerialize() const { return true; }
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<const RedParameters&>(other).encodings;
}
std::vector<uint8_t> encodings;
};
struct Av1Parameters : public Parameters {
Maybe<uint8_t> profile;
static constexpr uint8_t kDefaultProfile = 0;
Maybe<uint8_t> levelIdx;
static constexpr uint8_t kDefaultLevelIdx = 5;
Maybe<uint8_t> tier;
static constexpr uint8_t kDefaultTier = 0;
Av1Parameters() : Parameters(SdpRtpmapAttributeList::kAV1) {}
Av1Parameters(const Av1Parameters&) = default;
virtual ~Av1Parameters() = default;
virtual Parameters* Clone() const override {
return new Av1Parameters(*this);
}
// Returns the profile parameter if set, or the spec mandated default of 0.
auto profileValue() const -> uint8_t {
return profile.valueOr(kDefaultProfile);
}
// Returns the level-idx parameter if set, or the spec mandated default of
// 5.
auto levelIdxValue() const -> uint8_t {
return levelIdx.valueOr(kDefaultLevelIdx);
}
// Returns the tier parameter if set, or the spec mandated default of 0.
auto tierValue() const -> uint8_t { return tier.valueOr(kDefaultTier); }
virtual bool ShouldSerialize() const override {
return profile.isSome() || levelIdx.isSome() || tier.isSome();
};
virtual void Serialize(std::ostream& os) const override {
bool first = true;
profile.apply([&](const auto& profileV) {
os << "profile=" << static_cast<int>(profileV);
first = false;
});
levelIdx.apply([&](const auto& levelIdxV) {
os << (first ? "" : ";") << "level-idx=" << static_cast<int>(levelIdxV);
first = false;
});
tier.apply([&](const auto& tierV) {
os << (first ? "" : ";") << "tier=" << static_cast<int>(tierV);
});
}
virtual bool CompareEq(const Parameters& aOther) const override {
return aOther.codec_type == codec_type &&
static_cast<const Av1Parameters&>(aOther).profile == profile &&
static_cast<const Av1Parameters&>(aOther).levelIdx == levelIdx &&
static_cast<const Av1Parameters&>(aOther).tier == tier;
}
};
class RtxParameters : public Parameters {
public:
uint8_t apt = 255; // Valid payload types are 0 - 127, use 255 to represent
// unset value.
Maybe<uint32_t> rtx_time;
RtxParameters() : Parameters(SdpRtpmapAttributeList::kRtx) {}
virtual ~RtxParameters() = default;
virtual Parameters* Clone() const override {
return new RtxParameters(*this);
}
virtual void Serialize(std::ostream& os) const override {
if (apt <= 127) {
os << "apt=" << static_cast<uint32_t>(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<const RtxParameters&>(aOther);
return other.apt == apt && other.rtx_time == rtx_time;
}
};
class H264Parameters : public Parameters {
public:
// Baseline no constraints level 1
static const uint32_t kDefaultProfileLevelId = 0x42000A;
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<const H264Parameters&>(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<const VP8Parameters&>(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<const OpusParameters&>(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<const TelephoneEventParameters&>(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> 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<Fmtp> 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<Sctpmap> 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<Encoding> choices;
};
class Versions : public std::vector<Version> {
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<Ssrc> 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<uint32_t> ssrcs;
};
SdpSsrcGroupAttributeList() : SdpAttribute(kSsrcGroupAttribute) {}
void PushEntry(Semantics semantics, const std::vector<uint32_t>& 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<SsrcGroup> 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<std::string> 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<std::string> 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