diff options
Diffstat (limited to 'media/webrtc/signaling/src/sdp/SipccSdpMediaSection.cpp')
-rw-r--r-- | media/webrtc/signaling/src/sdp/SipccSdpMediaSection.cpp | 423 |
1 files changed, 423 insertions, 0 deletions
diff --git a/media/webrtc/signaling/src/sdp/SipccSdpMediaSection.cpp b/media/webrtc/signaling/src/sdp/SipccSdpMediaSection.cpp new file mode 100644 index 000000000..33c2a9e0d --- /dev/null +++ b/media/webrtc/signaling/src/sdp/SipccSdpMediaSection.cpp @@ -0,0 +1,423 @@ +/* -*- 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 "signaling/src/sdp/SipccSdpMediaSection.h" + +#include <ostream> +#include "signaling/src/sdp/SdpErrorHolder.h" + +#ifdef CRLF +#undef CRLF +#endif +#define CRLF "\r\n" + +namespace mozilla +{ + +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, + SdpErrorHolder& errorHolder) +{ + 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: + errorHolder.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) { + errorHolder.AddParseError(sdp_get_media_line_number(sdp, level), + "Invalid port count"); + return false; + } else { + mPortCount = pc; + } + + if (!LoadProtocol(sdp, level, errorHolder)) { + return false; + } + + if (!LoadFormats(sdp, level, errorHolder)) { + return false; + } + + if (!mAttributeList.Load(sdp, level, errorHolder)) { + return false; + } + + if (!ValidateSimulcast(sdp, level, errorHolder)) { + return false; + } + + if (!mBandwidths.Load(sdp, level, errorHolder)) { + return false; + } + + return LoadConnection(sdp, level, errorHolder); +} + +bool +SipccSdpMediaSection::LoadProtocol(sdp_t* sdp, uint16_t level, + SdpErrorHolder& errorHolder) +{ + 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_TCPTLSRTPSAVP: + mProtocol = kTcpTlsRtpSavp; + break; + case SDP_TRANSPORT_TCPTLSRTPSAVPF: + mProtocol = kTcpTlsRtpSavpf; + break; + case SDP_TRANSPORT_DTLSSCTP: + mProtocol = kDtlsSctp; + break; + + default: + errorHolder.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, + SdpErrorHolder& errorHolder) +{ + sdp_media_e mtype = sdp_get_media_type(sdp, level); + + if (mtype == SDP_MEDIA_APPLICATION) { + 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) { + errorHolder.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, + SdpErrorHolder& errorHolder) const +{ + if (!GetAttributeList().HasAttribute(SdpAttribute::kSimulcastAttribute)) { + return true; + } + + const SdpSimulcastAttribute& simulcast(GetAttributeList().GetSimulcast()); + if (!ValidateSimulcastVersions( + sdp, level, simulcast.sendVersions, sdp::kSend, errorHolder)) { + return false; + } + if (!ValidateSimulcastVersions( + sdp, level, simulcast.recvVersions, sdp::kRecv, errorHolder)) { + return false; + } + return true; +} + +bool +SipccSdpMediaSection::ValidateSimulcastVersions( + sdp_t* sdp, + uint16_t level, + const SdpSimulcastAttribute::Versions& versions, + sdp::Direction direction, + SdpErrorHolder& errorHolder) const +{ + if (versions.IsSet() && !(direction & GetDirectionAttribute().mValue)) { + errorHolder.AddParseError(sdp_get_media_line_number(sdp, level), + "simulcast attribute has a direction that is " + "inconsistent with the direction of this media " + "section."); + return false; + } + + for (const SdpSimulcastAttribute::Version& version : versions) { + for (const std::string& id : version.choices) { + if (versions.type == SdpSimulcastAttribute::Versions::kRid) { + const SdpRidAttributeList::Rid* ridAttr = FindRid(id); + if (!ridAttr || (ridAttr->direction != direction)) { + std::ostringstream os; + os << "No rid attribute for \'" << id << "\'"; + errorHolder.AddParseError(sdp_get_media_line_number(sdp, level), + os.str()); + return false; + } + } else if (versions.type == SdpSimulcastAttribute::Versions::kPt) { + if (std::find(mFormats.begin(), mFormats.end(), id) + == mFormats.end()) { + std::ostringstream os; + os << "No pt for \'" << id << "\'"; + errorHolder.AddParseError(sdp_get_media_line_number(sdp, level), + os.str()); + return false; + } + } + } + } + return true; +} + +bool +SipccSdpMediaSection::LoadConnection(sdp_t* sdp, uint16_t level, + SdpErrorHolder& errorHolder) +{ + if (!sdp_connection_valid(sdp, level)) { + level = SDP_SESSION_LEVEL; + if (!sdp_connection_valid(sdp, level)) { + errorHolder.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) { + errorHolder.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: + errorHolder.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& pt, + const std::string& name, uint16_t streams) +{ + // Only one allowed, for now. This may change as the specs (and deployments) + // evolve. + mFormats.clear(); + mFormats.push_back(pt); + SdpSctpmapAttributeList* sctpmap = new SdpSctpmapAttributeList(); + sctpmap->PushEntry(pt, name, streams); + mAttributeList.SetAttribute(sctpmap); +} + +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 |