SipccSdpMediaSection.cpp (12812B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "sdp/SipccSdpMediaSection.h" 8 9 #include <ostream> 10 11 #include "sdp/SdpParser.h" 12 13 extern "C" { 14 #include "sipcc_sdp.h" 15 } 16 17 #ifdef CRLF 18 # undef CRLF 19 #endif 20 #define CRLF "\r\n" 21 22 namespace mozilla { 23 24 SipccSdpMediaSection::SipccSdpMediaSection( 25 const SipccSdpMediaSection& aOrig, 26 const SipccSdpAttributeList* sessionLevel) 27 : SdpMediaSection(aOrig), 28 mMediaType(aOrig.mMediaType), 29 mPort(aOrig.mPort), 30 mPortCount(aOrig.mPortCount), 31 mProtocol(aOrig.mProtocol), 32 mFormats(aOrig.mFormats), 33 mConnection(new SdpConnection(*aOrig.mConnection)), 34 mBandwidths(aOrig.mBandwidths), 35 mAttributeList(aOrig.mAttributeList, sessionLevel) {} 36 37 unsigned int SipccSdpMediaSection::GetPort() const { return mPort; } 38 39 void SipccSdpMediaSection::SetPort(unsigned int port) { mPort = port; } 40 41 unsigned int SipccSdpMediaSection::GetPortCount() const { return mPortCount; } 42 43 SdpMediaSection::Protocol SipccSdpMediaSection::GetProtocol() const { 44 return mProtocol; 45 } 46 47 const SdpConnection& SipccSdpMediaSection::GetConnection() const { 48 return *mConnection; 49 } 50 51 SdpConnection& SipccSdpMediaSection::GetConnection() { return *mConnection; } 52 53 uint32_t SipccSdpMediaSection::GetBandwidth(const std::string& type) const { 54 auto found = mBandwidths.find(type); 55 if (found == mBandwidths.end()) { 56 return 0; 57 } 58 return found->second; 59 } 60 61 const std::vector<std::string>& SipccSdpMediaSection::GetFormats() const { 62 return mFormats; 63 } 64 65 const SdpAttributeList& SipccSdpMediaSection::GetAttributeList() const { 66 return mAttributeList; 67 } 68 69 SdpAttributeList& SipccSdpMediaSection::GetAttributeList() { 70 return mAttributeList; 71 } 72 73 SdpDirectionAttribute SipccSdpMediaSection::GetDirectionAttribute() const { 74 return SdpDirectionAttribute(mAttributeList.GetDirection()); 75 } 76 77 bool SipccSdpMediaSection::Load(sdp_t* sdp, uint16_t level, 78 InternalResults& results) { 79 switch (sdp_get_media_type(sdp, level)) { 80 case SDP_MEDIA_AUDIO: 81 mMediaType = kAudio; 82 break; 83 case SDP_MEDIA_VIDEO: 84 mMediaType = kVideo; 85 break; 86 case SDP_MEDIA_APPLICATION: 87 mMediaType = kApplication; 88 break; 89 case SDP_MEDIA_TEXT: 90 mMediaType = kText; 91 break; 92 93 default: 94 results.AddParseError(sdp_get_media_line_number(sdp, level), 95 "Unsupported media section type"); 96 return false; 97 } 98 99 mPort = sdp_get_media_portnum(sdp, level); 100 int32_t pc = sdp_get_media_portcount(sdp, level); 101 if (pc == SDP_INVALID_VALUE) { 102 // SDP_INVALID_VALUE (ie; -2) is used when there is no port count. :( 103 mPortCount = 0; 104 } else if (pc > static_cast<int32_t>(UINT16_MAX) || pc < 0) { 105 results.AddParseError(sdp_get_media_line_number(sdp, level), 106 "Invalid port count"); 107 return false; 108 } else { 109 mPortCount = pc; 110 } 111 112 if (!LoadProtocol(sdp, level, results)) { 113 return false; 114 } 115 116 if (!LoadFormats(sdp, level, results)) { 117 return false; 118 } 119 120 if (!mAttributeList.Load(sdp, level, results)) { 121 return false; 122 } 123 124 if (!ValidateSimulcast(sdp, level, results)) { 125 return false; 126 } 127 128 if (!mBandwidths.Load(sdp, level, results)) { 129 return false; 130 } 131 132 return LoadConnection(sdp, level, results); 133 } 134 135 bool SipccSdpMediaSection::LoadProtocol(sdp_t* sdp, uint16_t level, 136 InternalResults& results) { 137 switch (sdp_get_media_transport(sdp, level)) { 138 case SDP_TRANSPORT_RTPAVP: 139 mProtocol = kRtpAvp; 140 break; 141 case SDP_TRANSPORT_RTPSAVP: 142 mProtocol = kRtpSavp; 143 break; 144 case SDP_TRANSPORT_RTPAVPF: 145 mProtocol = kRtpAvpf; 146 break; 147 case SDP_TRANSPORT_RTPSAVPF: 148 mProtocol = kRtpSavpf; 149 break; 150 case SDP_TRANSPORT_UDPTLSRTPSAVP: 151 mProtocol = kUdpTlsRtpSavp; 152 break; 153 case SDP_TRANSPORT_UDPTLSRTPSAVPF: 154 mProtocol = kUdpTlsRtpSavpf; 155 break; 156 case SDP_TRANSPORT_TCPDTLSRTPSAVP: 157 mProtocol = kTcpDtlsRtpSavp; 158 break; 159 case SDP_TRANSPORT_TCPDTLSRTPSAVPF: 160 mProtocol = kTcpDtlsRtpSavpf; 161 break; 162 case SDP_TRANSPORT_DTLSSCTP: 163 mProtocol = kDtlsSctp; 164 break; 165 case SDP_TRANSPORT_UDPDTLSSCTP: 166 mProtocol = kUdpDtlsSctp; 167 break; 168 case SDP_TRANSPORT_TCPDTLSSCTP: 169 mProtocol = kTcpDtlsSctp; 170 break; 171 172 default: 173 results.AddParseError(sdp_get_media_line_number(sdp, level), 174 "Unsupported media transport type"); 175 return false; 176 } 177 return true; 178 } 179 180 bool SipccSdpMediaSection::LoadFormats(sdp_t* sdp, uint16_t level, 181 InternalResults& results) { 182 sdp_media_e mtype = sdp_get_media_type(sdp, level); 183 184 if (mtype == SDP_MEDIA_APPLICATION) { 185 sdp_transport_e ttype = sdp_get_media_transport(sdp, level); 186 if ((ttype == SDP_TRANSPORT_UDPDTLSSCTP) || 187 (ttype == SDP_TRANSPORT_TCPDTLSSCTP)) { 188 if (sdp_get_media_sctp_fmt(sdp, level) == 189 SDP_SCTP_MEDIA_FMT_WEBRTC_DATACHANNEL) { 190 mFormats.push_back("webrtc-datachannel"); 191 } 192 } else { 193 uint32_t ptype = sdp_get_media_sctp_port(sdp, level); 194 std::ostringstream osPayloadType; 195 osPayloadType << ptype; 196 mFormats.push_back(osPayloadType.str()); 197 } 198 } else if (mtype == SDP_MEDIA_AUDIO || mtype == SDP_MEDIA_VIDEO) { 199 uint16_t count = sdp_get_media_num_payload_types(sdp, level); 200 for (uint16_t i = 0; i < count; ++i) { 201 sdp_payload_ind_e indicator; // we ignore this, which is fine 202 uint32_t ptype = 203 sdp_get_media_payload_type(sdp, level, i + 1, &indicator); 204 205 if (GET_DYN_PAYLOAD_TYPE_VALUE(ptype) > UINT8_MAX) { 206 results.AddParseError(sdp_get_media_line_number(sdp, level), 207 "Format is too large"); 208 return false; 209 } 210 211 std::ostringstream osPayloadType; 212 // sipcc stores payload types in a funny way. When sipcc and the SDP it 213 // parsed differ on what payload type number should be used for a given 214 // codec, sipcc's value goes in the lower byte, and the SDP's value in 215 // the upper byte. When they do not differ, only the lower byte is used. 216 // We want what was in the SDP, verbatim. 217 osPayloadType << GET_DYN_PAYLOAD_TYPE_VALUE(ptype); 218 mFormats.push_back(osPayloadType.str()); 219 } 220 } 221 222 return true; 223 } 224 225 bool SipccSdpMediaSection::ValidateSimulcast(sdp_t* sdp, uint16_t level, 226 InternalResults& results) const { 227 if (!GetAttributeList().HasAttribute(SdpAttribute::kSimulcastAttribute)) { 228 return true; 229 } 230 231 const SdpSimulcastAttribute& simulcast(GetAttributeList().GetSimulcast()); 232 if (!ValidateSimulcastVersions(sdp, level, simulcast.sendVersions, sdp::kSend, 233 results)) { 234 return false; 235 } 236 if (!ValidateSimulcastVersions(sdp, level, simulcast.recvVersions, sdp::kRecv, 237 results)) { 238 return false; 239 } 240 return true; 241 } 242 243 bool SipccSdpMediaSection::ValidateSimulcastVersions( 244 sdp_t* sdp, uint16_t level, const SdpSimulcastAttribute::Versions& versions, 245 sdp::Direction direction, InternalResults& results) const { 246 for (const SdpSimulcastAttribute::Version& version : versions) { 247 for (const SdpSimulcastAttribute::Encoding& encoding : version.choices) { 248 const SdpRidAttributeList::Rid* ridAttr = FindRid(encoding.rid); 249 if (!ridAttr || (ridAttr->direction != direction)) { 250 std::ostringstream os; 251 os << "No rid attribute for \'" << encoding.rid << "\'"; 252 results.AddParseError(sdp_get_media_line_number(sdp, level), os.str()); 253 results.AddParseError(sdp_get_media_line_number(sdp, level), os.str()); 254 return false; 255 } 256 } 257 } 258 return true; 259 } 260 261 bool SipccSdpMediaSection::LoadConnection(sdp_t* sdp, uint16_t level, 262 InternalResults& results) { 263 if (!sdp_connection_valid(sdp, level)) { 264 level = SDP_SESSION_LEVEL; 265 if (!sdp_connection_valid(sdp, level)) { 266 results.AddParseError(sdp_get_media_line_number(sdp, level), 267 "Missing c= line"); 268 return false; 269 } 270 } 271 272 sdp_nettype_e type = sdp_get_conn_nettype(sdp, level); 273 if (type != SDP_NT_INTERNET) { 274 results.AddParseError(sdp_get_media_line_number(sdp, level), 275 "Unsupported network type"); 276 return false; 277 } 278 279 sdp::AddrType addrType; 280 switch (sdp_get_conn_addrtype(sdp, level)) { 281 case SDP_AT_IP4: 282 addrType = sdp::kIPv4; 283 break; 284 case SDP_AT_IP6: 285 addrType = sdp::kIPv6; 286 break; 287 default: 288 results.AddParseError(sdp_get_media_line_number(sdp, level), 289 "Unsupported address type"); 290 return false; 291 } 292 293 std::string address = sdp_get_conn_address(sdp, level); 294 int16_t ttl = static_cast<uint16_t>(sdp_get_mcast_ttl(sdp, level)); 295 if (ttl < 0) { 296 ttl = 0; 297 } 298 int32_t numAddr = 299 static_cast<uint32_t>(sdp_get_mcast_num_of_addresses(sdp, level)); 300 if (numAddr < 0) { 301 numAddr = 0; 302 } 303 mConnection = MakeUnique<SdpConnection>(addrType, address, ttl, numAddr); 304 return true; 305 } 306 307 void SipccSdpMediaSection::AddCodec(const std::string& pt, 308 const std::string& name, uint32_t clockrate, 309 uint16_t channels) { 310 mFormats.push_back(pt); 311 312 SdpRtpmapAttributeList* rtpmap = new SdpRtpmapAttributeList(); 313 if (mAttributeList.HasAttribute(SdpAttribute::kRtpmapAttribute)) { 314 const SdpRtpmapAttributeList& old = mAttributeList.GetRtpmap(); 315 for (auto it = old.mRtpmaps.begin(); it != old.mRtpmaps.end(); ++it) { 316 rtpmap->mRtpmaps.push_back(*it); 317 } 318 } 319 SdpRtpmapAttributeList::CodecType codec = SdpRtpmapAttributeList::kOtherCodec; 320 if (name == "opus") { 321 codec = SdpRtpmapAttributeList::kOpus; 322 } else if (name == "G722") { 323 codec = SdpRtpmapAttributeList::kG722; 324 } else if (name == "PCMU") { 325 codec = SdpRtpmapAttributeList::kPCMU; 326 } else if (name == "PCMA") { 327 codec = SdpRtpmapAttributeList::kPCMA; 328 } else if (name == "VP8") { 329 codec = SdpRtpmapAttributeList::kVP8; 330 } else if (name == "VP9") { 331 codec = SdpRtpmapAttributeList::kVP9; 332 } else if (name == "H264") { 333 codec = SdpRtpmapAttributeList::kH264; 334 } 335 336 rtpmap->PushEntry(pt, codec, name, clockrate, channels); 337 mAttributeList.SetAttribute(rtpmap); 338 } 339 340 void SipccSdpMediaSection::ClearCodecs() { 341 mFormats.clear(); 342 mAttributeList.RemoveAttribute(SdpAttribute::kRtpmapAttribute); 343 mAttributeList.RemoveAttribute(SdpAttribute::kFmtpAttribute); 344 mAttributeList.RemoveAttribute(SdpAttribute::kSctpmapAttribute); 345 mAttributeList.RemoveAttribute(SdpAttribute::kRtcpFbAttribute); 346 } 347 348 void SipccSdpMediaSection::AddDataChannel(const std::string& name, 349 uint16_t port, uint16_t streams, 350 uint32_t message_size) { 351 // Only one allowed, for now. This may change as the specs (and deployments) 352 // evolve. 353 mFormats.clear(); 354 if ((mProtocol == kUdpDtlsSctp) || (mProtocol == kTcpDtlsSctp)) { 355 // new data channel format according to draft 21 356 mFormats.push_back(name); 357 mAttributeList.SetAttribute( 358 new SdpNumberAttribute(SdpAttribute::kSctpPortAttribute, port)); 359 if (message_size) { 360 mAttributeList.SetAttribute(new SdpNumberAttribute( 361 SdpAttribute::kMaxMessageSizeAttribute, message_size)); 362 } 363 } else { 364 // old data channels format according to draft 05 365 std::string port_str = std::to_string(port); 366 mFormats.push_back(port_str); 367 SdpSctpmapAttributeList* sctpmap = new SdpSctpmapAttributeList(); 368 sctpmap->PushEntry(port_str, name, streams); 369 mAttributeList.SetAttribute(sctpmap); 370 if (message_size) { 371 // This is a workaround to allow detecting Firefox's w/o EOR support 372 mAttributeList.SetAttribute(new SdpNumberAttribute( 373 SdpAttribute::kMaxMessageSizeAttribute, message_size)); 374 } 375 } 376 } 377 378 void SipccSdpMediaSection::Serialize(std::ostream& os) const { 379 os << "m=" << mMediaType << " " << mPort; 380 if (mPortCount) { 381 os << "/" << mPortCount; 382 } 383 os << " " << mProtocol; 384 for (auto i = mFormats.begin(); i != mFormats.end(); ++i) { 385 os << " " << (*i); 386 } 387 os << CRLF; 388 389 // We dont do i= 390 391 if (mConnection) { 392 os << *mConnection; 393 } 394 395 mBandwidths.Serialize(os); 396 397 // We dont do k= because they're evil 398 399 os << mAttributeList; 400 } 401 402 } // namespace mozilla