401 lines
12 KiB
C++
401 lines
12 KiB
C++
/* -*- 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/. */
|
|
|
|
#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
|