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,
#include "sdp/SipccSdpMediaSection.h"
#include <ostream>
#include "sdp/SdpParser.h"
extern "C" {
#include "sipcc_sdp.h"
}
#ifdef CRLF
# undef CRLF
#endif
#define CRLF "\r\n"
namespace mozilla {
SipccSdpMediaSection::SipccSdpMediaSection(
const SipccSdpMediaSection& aOrig,
const SipccSdpAttributeList* sessionLevel)
: SdpMediaSection(aOrig),
mMediaType(aOrig.mMediaType),
mPort(aOrig.mPort),
mPortCount(aOrig.mPortCount),
mProtocol(aOrig.mProtocol),
mFormats(aOrig.mFormats),
mConnection(new SdpConnection(*aOrig.mConnection)),
mBandwidths(aOrig.mBandwidths),
mAttributeList(aOrig.mAttributeList, sessionLevel) {}
unsigned int SipccSdpMediaSection::GetPort() const { return mPort; }
void SipccSdpMediaSection::SetPort(unsigned int port) { mPort = port; }
unsigned int SipccSdpMediaSection::GetPortCount() const { return mPortCount; }
SdpMediaSection::Protocol SipccSdpMediaSection::GetProtocol() const {
return mProtocol;
}
const SdpConnection& SipccSdpMediaSection::GetConnection() const {
return *mConnection;
}
SdpConnection& SipccSdpMediaSection::GetConnection() { return *mConnection; }
uint32_t SipccSdpMediaSection::GetBandwidth(const std::string& type) const {
auto found = mBandwidths.find(type);
if (found == mBandwidths.end()) {
return 0;
}
return found->second;
}
const std::vector<std::string>& SipccSdpMediaSection::GetFormats() const {
return mFormats;
}
const SdpAttributeList& SipccSdpMediaSection::GetAttributeList() const {
return mAttributeList;
}
SdpAttributeList& SipccSdpMediaSection::GetAttributeList() {
return mAttributeList;
}
SdpDirectionAttribute SipccSdpMediaSection::GetDirectionAttribute() const {
return SdpDirectionAttribute(mAttributeList.GetDirection());
}
bool SipccSdpMediaSection::Load(sdp_t* sdp, uint16_t level,
InternalResults& results) {
switch (sdp_get_media_type(sdp, level)) {
case SDP_MEDIA_AUDIO:
mMediaType = kAudio;
break;
case SDP_MEDIA_VIDEO:
mMediaType = kVideo;
break;
case SDP_MEDIA_APPLICATION:
mMediaType = kApplication;
break;
case SDP_MEDIA_TEXT:
mMediaType = kText;
break;
default:
results.AddParseError(sdp_get_media_line_number(sdp, level),
"Unsupported media section type");
return false;
}
mPort = sdp_get_media_portnum(sdp, level);
int32_t pc = sdp_get_media_portcount(sdp, level);
if (pc == SDP_INVALID_VALUE) {
// SDP_INVALID_VALUE (ie; -2) is used when there is no port count. :(
mPortCount = 0;
} else if (pc > static_cast<int32_t>(UINT16_MAX) || pc < 0) {
results.AddParseError(sdp_get_media_line_number(sdp, level),
"Invalid port count");
return false;
} else {
mPortCount = pc;
}
if (!LoadProtocol(sdp, level, results)) {
return false;
}
if (!LoadFormats(sdp, level, results)) {
return false;
}
if (!mAttributeList.Load(sdp, level, results)) {
return false;
}
if (!ValidateSimulcast(sdp, level, results)) {
return false;
}
if (!mBandwidths.Load(sdp, level, results)) {
return false;
}
return LoadConnection(sdp, level, results);
}
bool SipccSdpMediaSection::LoadProtocol(sdp_t* sdp, uint16_t level,
InternalResults& results) {
switch (sdp_get_media_transport(sdp, level)) {
case SDP_TRANSPORT_RTPAVP:
mProtocol = kRtpAvp;
break;
case SDP_TRANSPORT_RTPSAVP:
mProtocol = kRtpSavp;
break;
case SDP_TRANSPORT_RTPAVPF:
mProtocol = kRtpAvpf;
break;
case SDP_TRANSPORT_RTPSAVPF:
mProtocol = kRtpSavpf;
break;
case SDP_TRANSPORT_UDPTLSRTPSAVP:
mProtocol = kUdpTlsRtpSavp;
break;
case SDP_TRANSPORT_UDPTLSRTPSAVPF:
mProtocol = kUdpTlsRtpSavpf;
break;
case SDP_TRANSPORT_TCPDTLSRTPSAVP:
mProtocol = kTcpDtlsRtpSavp;
break;
case SDP_TRANSPORT_TCPDTLSRTPSAVPF:
mProtocol = kTcpDtlsRtpSavpf;
break;
case SDP_TRANSPORT_DTLSSCTP:
mProtocol = kDtlsSctp;
break;
case SDP_TRANSPORT_UDPDTLSSCTP:
mProtocol = kUdpDtlsSctp;
break;
case SDP_TRANSPORT_TCPDTLSSCTP:
mProtocol = kTcpDtlsSctp;
break;
default:
results.AddParseError(sdp_get_media_line_number(sdp, level),
"Unsupported media transport type");
return false;
}
return true;
}
bool SipccSdpMediaSection::LoadFormats(sdp_t* sdp, uint16_t level,
InternalResults& results) {
sdp_media_e mtype = sdp_get_media_type(sdp, level);
if (mtype == SDP_MEDIA_APPLICATION) {
sdp_transport_e ttype = sdp_get_media_transport(sdp, level);
if ((ttype == SDP_TRANSPORT_UDPDTLSSCTP) ||
(ttype == SDP_TRANSPORT_TCPDTLSSCTP)) {
if (sdp_get_media_sctp_fmt(sdp, level) ==
SDP_SCTP_MEDIA_FMT_WEBRTC_DATACHANNEL) {
mFormats.push_back("webrtc-datachannel");
}
} else {
uint32_t ptype = sdp_get_media_sctp_port(sdp, level);
std::ostringstream osPayloadType;
osPayloadType << ptype;
mFormats.push_back(osPayloadType.str());
}
} else if (mtype == SDP_MEDIA_AUDIO || mtype == SDP_MEDIA_VIDEO) {
uint16_t count = sdp_get_media_num_payload_types(sdp, level);
for (uint16_t i = 0; i < count; ++i) {
sdp_payload_ind_e indicator; // we ignore this, which is fine
uint32_t ptype =
sdp_get_media_payload_type(sdp, level, i + 1, &indicator);
if (GET_DYN_PAYLOAD_TYPE_VALUE(ptype) > UINT8_MAX) {
results.AddParseError(sdp_get_media_line_number(sdp, level),
"Format is too large");
return false;
}
std::ostringstream osPayloadType;
// sipcc stores payload types in a funny way. When sipcc and the SDP it
// parsed differ on what payload type number should be used for a given
// codec, sipcc's value goes in the lower byte, and the SDP's value in
// the upper byte. When they do not differ, only the lower byte is used.
// We want what was in the SDP, verbatim.
osPayloadType << GET_DYN_PAYLOAD_TYPE_VALUE(ptype);
mFormats.push_back(osPayloadType.str());
}
}
return true;
}
bool SipccSdpMediaSection::ValidateSimulcast(sdp_t* sdp, uint16_t level,
InternalResults& results) const {
if (!GetAttributeList().HasAttribute(SdpAttribute::kSimulcastAttribute)) {
return true;
}
const SdpSimulcastAttribute& simulcast(GetAttributeList().GetSimulcast());
if (!ValidateSimulcastVersions(sdp, level, simulcast.sendVersions, sdp::kSend,
results)) {
return false;
}
if (!ValidateSimulcastVersions(sdp, level, simulcast.recvVersions, sdp::kRecv,
results)) {
return false;
}
return true;
}
bool SipccSdpMediaSection::ValidateSimulcastVersions(
sdp_t* sdp, uint16_t level, const SdpSimulcastAttribute::Versions& versions,
sdp::Direction direction, InternalResults& results) const {
for (const SdpSimulcastAttribute::Version& version : versions) {
for (const SdpSimulcastAttribute::Encoding& encoding : version.choices) {
const SdpRidAttributeList::Rid* ridAttr = FindRid(encoding.rid);
if (!ridAttr || (ridAttr->direction != direction)) {
std::ostringstream os;
os << "No rid attribute for \'" << encoding.rid << "\'";
results.AddParseError(sdp_get_media_line_number(sdp, level), os.str());
results.AddParseError(sdp_get_media_line_number(sdp, level), os.str());
return false;
}
}
}
return true;
}
bool SipccSdpMediaSection::LoadConnection(sdp_t* sdp, uint16_t level,
InternalResults& results) {
if (!sdp_connection_valid(sdp, level)) {
level = SDP_SESSION_LEVEL;
if (!sdp_connection_valid(sdp, level)) {
results.AddParseError(sdp_get_media_line_number(sdp, level),
"Missing c= line");
return false;
}
}
sdp_nettype_e type = sdp_get_conn_nettype(sdp, level);
if (type != SDP_NT_INTERNET) {
results.AddParseError(sdp_get_media_line_number(sdp, level),
"Unsupported network type");
return false;
}
sdp::AddrType addrType;
switch (sdp_get_conn_addrtype(sdp, level)) {
case SDP_AT_IP4:
addrType = sdp::kIPv4;
break;
case SDP_AT_IP6:
addrType = sdp::kIPv6;
break;
default:
results.AddParseError(sdp_get_media_line_number(sdp, level),
"Unsupported address type");
return false;
}
std::string address = sdp_get_conn_address(sdp, level);
int16_t ttl = static_cast<uint16_t>(sdp_get_mcast_ttl(sdp, level));
if (ttl < 0) {
ttl = 0;
}
int32_t numAddr =
static_cast<uint32_t>(sdp_get_mcast_num_of_addresses(sdp, level));
if (numAddr < 0) {
numAddr = 0;
}
mConnection = MakeUnique<SdpConnection>(addrType, address, ttl, numAddr);
return true;
}
void SipccSdpMediaSection::AddCodec(const std::string& pt,
const std::string& name, uint32_t clockrate,
uint16_t channels) {
mFormats.push_back(pt);
SdpRtpmapAttributeList* rtpmap = new SdpRtpmapAttributeList();
if (mAttributeList.HasAttribute(SdpAttribute::kRtpmapAttribute)) {
const SdpRtpmapAttributeList& old = mAttributeList.GetRtpmap();
for (auto it = old.mRtpmaps.begin(); it != old.mRtpmaps.end(); ++it) {
rtpmap->mRtpmaps.push_back(*it);
}
}
SdpRtpmapAttributeList::CodecType codec = SdpRtpmapAttributeList::kOtherCodec;
if (name == "opus") {
codec = SdpRtpmapAttributeList::kOpus;
} else if (name == "G722") {
codec = SdpRtpmapAttributeList::kG722;
} else if (name == "PCMU") {
codec = SdpRtpmapAttributeList::kPCMU;
} else if (name == "PCMA") {
codec = SdpRtpmapAttributeList::kPCMA;
} else if (name == "VP8") {
codec = SdpRtpmapAttributeList::kVP8;
} else if (name == "VP9") {
codec = SdpRtpmapAttributeList::kVP9;
} else if (name == "H264") {
codec = SdpRtpmapAttributeList::kH264;
}
rtpmap->PushEntry(pt, codec, name, clockrate, channels);
mAttributeList.SetAttribute(rtpmap);
}
void SipccSdpMediaSection::ClearCodecs() {
mFormats.clear();
mAttributeList.RemoveAttribute(SdpAttribute::kRtpmapAttribute);
mAttributeList.RemoveAttribute(SdpAttribute::kFmtpAttribute);
mAttributeList.RemoveAttribute(SdpAttribute::kSctpmapAttribute);
mAttributeList.RemoveAttribute(SdpAttribute::kRtcpFbAttribute);
}
void SipccSdpMediaSection::AddDataChannel(const std::string& name,
uint16_t port, uint16_t streams,
uint32_t message_size) {
// Only one allowed, for now. This may change as the specs (and deployments)
// evolve.
mFormats.clear();
if ((mProtocol == kUdpDtlsSctp) || (mProtocol == kTcpDtlsSctp)) {
// new data channel format according to draft 21
mFormats.push_back(name);
mAttributeList.SetAttribute(
new SdpNumberAttribute(SdpAttribute::kSctpPortAttribute, port));
if (message_size) {
mAttributeList.SetAttribute(new SdpNumberAttribute(
SdpAttribute::kMaxMessageSizeAttribute, message_size));
}
} else {
// old data channels format according to draft 05
std::string port_str = std::to_string(port);
mFormats.push_back(port_str);
SdpSctpmapAttributeList* sctpmap = new SdpSctpmapAttributeList();
sctpmap->PushEntry(port_str, name, streams);
mAttributeList.SetAttribute(sctpmap);
if (message_size) {
// This is a workaround to allow detecting Firefox's w/o EOR support
mAttributeList.SetAttribute(new SdpNumberAttribute(
SdpAttribute::kMaxMessageSizeAttribute, message_size));
}
}
}
void SipccSdpMediaSection::Serialize(std::ostream& os) const {
os << "m=" << mMediaType << " " << mPort;
if (mPortCount) {
os << "/" << mPortCount;
}
os << " " << mProtocol;
for (auto i = mFormats.begin(); i != mFormats.end(); ++i) {
os << " " << (*i);
}
os << CRLF;
// We dont do i=
if (mConnection) {
os << *mConnection;
}
mBandwidths.Serialize(os);
// We dont do k= because they're evil
os << mAttributeList;
}
} // namespace mozilla