Parse the connection data in SDP (c= line).
Extract the remote addresses from SDP c= line on both session level and media level. The media level address will overwrite the session level one if exists. WebRTC is not using c= and this is used for new SDP parsing API. BUG=webrtc:7311 Review-Url: https://codereview.webrtc.org/2742903002 Cr-Commit-Position: refs/heads/master@{#17326}
This commit is contained in:
parent
d19bcb7116
commit
38989e593c
@ -88,7 +88,7 @@ const int64_t kForgetPacketAfter = 30000; // 30 seconds
|
||||
|
||||
namespace cricket {
|
||||
|
||||
// TODO(ronghuawu): Use "host", "srflx", "prflx" and "relay". But this requires
|
||||
// TODO(ronghuawu): Use "local", "srflx", "prflx" and "relay". But this requires
|
||||
// the signaling part be updated correspondingly as well.
|
||||
const char LOCAL_PORT_TYPE[] = "local";
|
||||
const char STUN_PORT_TYPE[] = "stun";
|
||||
|
||||
@ -14,8 +14,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/p2p/base/transportinfo.h"
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/p2p/base/transportinfo.h"
|
||||
|
||||
namespace cricket {
|
||||
|
||||
|
||||
@ -38,6 +38,74 @@ static bool IsTypeSupported(const std::string& type) {
|
||||
return type_supported;
|
||||
}
|
||||
|
||||
// RFC 5245
|
||||
// It is RECOMMENDED that default candidates be chosen based on the
|
||||
// likelihood of those candidates to work with the peer that is being
|
||||
// contacted. It is RECOMMENDED that relayed > reflexive > host.
|
||||
static const int kPreferenceUnknown = 0;
|
||||
static const int kPreferenceHost = 1;
|
||||
static const int kPreferenceReflexive = 2;
|
||||
static const int kPreferenceRelayed = 3;
|
||||
|
||||
static const char kDummyAddress[] = "0.0.0.0";
|
||||
static const int kDummyPort = 9;
|
||||
|
||||
static int GetCandidatePreferenceFromType(const std::string& type) {
|
||||
int preference = kPreferenceUnknown;
|
||||
if (type == cricket::LOCAL_PORT_TYPE) {
|
||||
preference = kPreferenceHost;
|
||||
} else if (type == cricket::STUN_PORT_TYPE) {
|
||||
preference = kPreferenceReflexive;
|
||||
} else if (type == cricket::RELAY_PORT_TYPE) {
|
||||
preference = kPreferenceRelayed;
|
||||
} else {
|
||||
RTC_NOTREACHED();
|
||||
}
|
||||
return preference;
|
||||
}
|
||||
|
||||
// Update the connection address for the MediaContentDescription based on the
|
||||
// candidates.
|
||||
static void UpdateConnectionAddress(
|
||||
const JsepCandidateCollection& candidate_collection,
|
||||
cricket::ContentDescription* content_description) {
|
||||
int port = kDummyPort;
|
||||
std::string ip = kDummyAddress;
|
||||
int current_preference = kPreferenceUnknown;
|
||||
int current_family = AF_UNSPEC;
|
||||
for (size_t i = 0; i < candidate_collection.count(); ++i) {
|
||||
const IceCandidateInterface* jsep_candidate = candidate_collection.at(i);
|
||||
if (jsep_candidate->candidate().component() !=
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP) {
|
||||
continue;
|
||||
}
|
||||
// Default destination should be UDP only.
|
||||
if (jsep_candidate->candidate().protocol() != cricket::UDP_PROTOCOL_NAME) {
|
||||
continue;
|
||||
}
|
||||
const int preference =
|
||||
GetCandidatePreferenceFromType(jsep_candidate->candidate().type());
|
||||
const int family = jsep_candidate->candidate().address().ipaddr().family();
|
||||
// See if this candidate is more preferable then the current one if it's the
|
||||
// same family. Or if the current family is IPv4 already so we could safely
|
||||
// ignore all IPv6 ones. WebRTC bug 4269.
|
||||
// http://code.google.com/p/webrtc/issues/detail?id=4269
|
||||
if ((preference <= current_preference && current_family == family) ||
|
||||
(current_family == AF_INET && family == AF_INET6)) {
|
||||
continue;
|
||||
}
|
||||
current_preference = preference;
|
||||
current_family = family;
|
||||
port = jsep_candidate->candidate().address().port();
|
||||
ip = jsep_candidate->candidate().address().ipaddr().ToString();
|
||||
}
|
||||
rtc::SocketAddress connection_addr;
|
||||
connection_addr.SetIP(ip);
|
||||
connection_addr.SetPort(port);
|
||||
static_cast<cricket::MediaContentDescription*>(content_description)
|
||||
->set_connection_address(connection_addr);
|
||||
}
|
||||
|
||||
const char SessionDescriptionInterface::kOffer[] = "offer";
|
||||
const char SessionDescriptionInterface::kPrAnswer[] = "pranswer";
|
||||
const char SessionDescriptionInterface::kAnswer[] = "answer";
|
||||
@ -116,9 +184,13 @@ bool JsepSessionDescription::AddCandidate(
|
||||
static_cast<int>(mediasection_index),
|
||||
updated_candidate));
|
||||
if (!candidate_collection_[mediasection_index].HasCandidate(
|
||||
updated_candidate_wrapper.get()))
|
||||
updated_candidate_wrapper.get())) {
|
||||
candidate_collection_[mediasection_index].add(
|
||||
updated_candidate_wrapper.release());
|
||||
UpdateConnectionAddress(
|
||||
candidate_collection_[mediasection_index],
|
||||
description_->contents()[mediasection_index].description);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -133,6 +205,9 @@ size_t JsepSessionDescription::RemoveCandidates(
|
||||
continue;
|
||||
}
|
||||
num_removed += candidate_collection_[mediasection_index].remove(candidate);
|
||||
UpdateConnectionAddress(
|
||||
candidate_collection_[mediasection_index],
|
||||
description_->contents()[mediasection_index].description);
|
||||
}
|
||||
return num_removed;
|
||||
}
|
||||
|
||||
@ -34,6 +34,9 @@ static const char kCandidateUfragVoice[] = "ufrag_voice";
|
||||
static const char kCandidatePwdVoice[] = "pwd_voice";
|
||||
static const char kCandidateUfragVideo[] = "ufrag_video";
|
||||
static const char kCandidatePwdVideo[] = "pwd_video";
|
||||
static const char kCandidateFoundation[] = "a0+B/1";
|
||||
static const uint32_t kCandidatePriority = 2130706432U; // pref = 1.0
|
||||
static const uint32_t kCandidateGeneration = 2;
|
||||
|
||||
// This creates a session description with both audio and video media contents.
|
||||
// In SDP this is described by two m lines, one audio and one video.
|
||||
@ -228,3 +231,177 @@ TEST_F(JsepSessionDescriptionTest, SerializeDeserializeWithCandidates) {
|
||||
|
||||
EXPECT_EQ(sdp_with_candidate, parsed_sdp_with_candidate);
|
||||
}
|
||||
|
||||
// TODO(zhihuang): Modify these tests. These are used to verify that after
|
||||
// adding the candidates, the connection_address field is set correctly. Modify
|
||||
// those so that the "connection address" is tested directly.
|
||||
// Tests serialization of SDP with only IPv6 candidates and verifies that IPv6
|
||||
// is used as default address in c line according to preference.
|
||||
TEST_F(JsepSessionDescriptionTest, SerializeSessionDescriptionWithIPv6Only) {
|
||||
// Stun has a high preference than local host.
|
||||
cricket::Candidate candidate1(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
|
||||
cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
cricket::Candidate candidate2(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("::2", 1235), kCandidatePriority, "", "",
|
||||
cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
|
||||
JsepIceCandidate jice1("audio", 0, candidate1);
|
||||
JsepIceCandidate jice2("audio", 0, candidate2);
|
||||
JsepIceCandidate jice3("video", 0, candidate1);
|
||||
JsepIceCandidate jice4("video", 0, candidate2);
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice1));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice2));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice3));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice4));
|
||||
std::string message = Serialize(jsep_desc_.get());
|
||||
|
||||
// Should have a c line like this one.
|
||||
EXPECT_NE(message.find("c=IN IP6 ::1"), std::string::npos);
|
||||
// Shouldn't have a IP4 c line.
|
||||
EXPECT_EQ(message.find("c=IN IP4"), std::string::npos);
|
||||
}
|
||||
|
||||
// Tests serialization of SDP with both IPv4 and IPv6 candidates and
|
||||
// verifies that IPv4 is used as default address in c line even if the
|
||||
// preference of IPv4 is lower.
|
||||
TEST_F(JsepSessionDescriptionTest,
|
||||
SerializeSessionDescriptionWithBothIPFamilies) {
|
||||
cricket::Candidate candidate_v4(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("192.168.1.5", 1234), kCandidatePriority, "", "",
|
||||
cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
cricket::Candidate candidate_v6(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
|
||||
cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
|
||||
JsepIceCandidate jice_v4("audio", 0, candidate_v4);
|
||||
JsepIceCandidate jice_v6("audio", 0, candidate_v6);
|
||||
JsepIceCandidate jice_v4_video("video", 0, candidate_v4);
|
||||
JsepIceCandidate jice_v6_video("video", 0, candidate_v6);
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v4));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v6));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v4_video));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice_v6_video));
|
||||
std::string message = Serialize(jsep_desc_.get());
|
||||
|
||||
// Should have a c line like this one.
|
||||
EXPECT_NE(message.find("c=IN IP4 192.168.1.5"), std::string::npos);
|
||||
// Shouldn't have a IP6 c line.
|
||||
EXPECT_EQ(message.find("c=IN IP6"), std::string::npos);
|
||||
}
|
||||
|
||||
// Tests serialization of SDP with both UDP and TCP candidates and
|
||||
// verifies that UDP is used as default address in c line even if the
|
||||
// preference of UDP is lower.
|
||||
TEST_F(JsepSessionDescriptionTest,
|
||||
SerializeSessionDescriptionWithBothProtocols) {
|
||||
// Stun has a high preference than local host.
|
||||
cricket::Candidate candidate1(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
|
||||
rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
|
||||
cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
cricket::Candidate candidate2(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("fe80::1234:5678:abcd:ef12", 1235), kCandidatePriority,
|
||||
"", "", cricket::LOCAL_PORT_TYPE, kCandidateGeneration,
|
||||
kCandidateFoundation);
|
||||
|
||||
JsepIceCandidate jice1("audio", 0, candidate1);
|
||||
JsepIceCandidate jice2("audio", 0, candidate2);
|
||||
JsepIceCandidate jice3("video", 0, candidate1);
|
||||
JsepIceCandidate jice4("video", 0, candidate2);
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice1));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice2));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice3));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice4));
|
||||
std::string message = Serialize(jsep_desc_.get());
|
||||
|
||||
// Should have a c line like this one.
|
||||
EXPECT_NE(message.find("c=IN IP6 fe80::1234:5678:abcd:ef12"),
|
||||
std::string::npos);
|
||||
// Shouldn't have a IP4 c line.
|
||||
EXPECT_EQ(message.find("c=IN IP4"), std::string::npos);
|
||||
}
|
||||
|
||||
// Tests serialization of SDP with only TCP candidates and verifies that
|
||||
// null IPv4 is used as default address in c line.
|
||||
TEST_F(JsepSessionDescriptionTest, SerializeSessionDescriptionWithTCPOnly) {
|
||||
// Stun has a high preference than local host.
|
||||
cricket::Candidate candidate1(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
|
||||
rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
|
||||
cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
cricket::Candidate candidate2(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
|
||||
rtc::SocketAddress("::2", 1235), kCandidatePriority, "", "",
|
||||
cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
|
||||
JsepIceCandidate jice1("audio", 0, candidate1);
|
||||
JsepIceCandidate jice2("audio", 0, candidate2);
|
||||
JsepIceCandidate jice3("video", 0, candidate1);
|
||||
JsepIceCandidate jice4("video", 0, candidate2);
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice1));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice2));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice3));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice4));
|
||||
|
||||
std::string message = Serialize(jsep_desc_.get());
|
||||
EXPECT_EQ(message.find("c=IN IP6 ::3"), std::string::npos);
|
||||
// Should have a c line like this one when no any default exists.
|
||||
EXPECT_NE(message.find("c=IN IP4 0.0.0.0"), std::string::npos);
|
||||
}
|
||||
|
||||
// Tests that the connection address will be correctly set when the Candidate is
|
||||
// removed.
|
||||
TEST_F(JsepSessionDescriptionTest, RemoveCandidateAndSetConnectionAddress) {
|
||||
cricket::Candidate candidate1(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
|
||||
cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
candidate1.set_transport_name("audio");
|
||||
|
||||
cricket::Candidate candidate2(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
|
||||
rtc::SocketAddress("::2", 1235), kCandidatePriority, "", "",
|
||||
cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
candidate2.set_transport_name("audio");
|
||||
|
||||
cricket::Candidate candidate3(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("192.168.1.1", 1236), kCandidatePriority, "", "",
|
||||
cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation);
|
||||
candidate3.set_transport_name("audio");
|
||||
|
||||
JsepIceCandidate jice1("audio", 0, candidate1);
|
||||
JsepIceCandidate jice2("audio", 0, candidate2);
|
||||
JsepIceCandidate jice3("audio", 0, candidate3);
|
||||
|
||||
size_t audio_index = 0;
|
||||
auto media_desc = static_cast<cricket::MediaContentDescription*>(
|
||||
jsep_desc_->description()->contents()[audio_index].description);
|
||||
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice1));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice2));
|
||||
ASSERT_TRUE(jsep_desc_->AddCandidate(&jice3));
|
||||
|
||||
std::vector<cricket::Candidate> candidates;
|
||||
EXPECT_EQ("192.168.1.1:1236", media_desc->connection_address().ToString());
|
||||
|
||||
candidates.push_back(candidate3);
|
||||
ASSERT_TRUE(jsep_desc_->RemoveCandidates(candidates));
|
||||
EXPECT_EQ("[::1]:1234", media_desc->connection_address().ToString());
|
||||
|
||||
candidates.clear();
|
||||
candidates.push_back(candidate2);
|
||||
ASSERT_TRUE(jsep_desc_->RemoveCandidates(candidates));
|
||||
EXPECT_EQ("[::1]:1234", media_desc->connection_address().ToString());
|
||||
|
||||
candidates.clear();
|
||||
candidates.push_back(candidate1);
|
||||
ASSERT_TRUE(jsep_desc_->RemoveCandidates(candidates));
|
||||
EXPECT_EQ("0.0.0.0:9", media_desc->connection_address().ToString());
|
||||
}
|
||||
|
||||
@ -309,6 +309,16 @@ class MediaContentDescription : public ContentDescription {
|
||||
}
|
||||
int buffered_mode_latency() const { return buffered_mode_latency_; }
|
||||
|
||||
// https://tools.ietf.org/html/rfc4566#section-5.7
|
||||
// May be present at the media or session level of SDP. If present at both
|
||||
// levels, the media-level attribute overwrites the session-level one.
|
||||
void set_connection_address(const rtc::SocketAddress& address) {
|
||||
connection_address_ = address;
|
||||
}
|
||||
const rtc::SocketAddress& connection_address() const {
|
||||
return connection_address_;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool rtcp_mux_ = false;
|
||||
bool rtcp_reduced_size_ = false;
|
||||
@ -324,6 +334,7 @@ class MediaContentDescription : public ContentDescription {
|
||||
bool partial_ = false;
|
||||
int buffered_mode_latency_ = kBufferedModeDisabled;
|
||||
MediaContentDirection direction_ = MD_SENDRECV;
|
||||
rtc::SocketAddress connection_address_;
|
||||
};
|
||||
|
||||
template <class C>
|
||||
|
||||
@ -249,11 +249,13 @@ static void BuildIceOptions(const std::vector<std::string>& transport_options,
|
||||
std::string* message);
|
||||
static bool IsRtp(const std::string& protocol);
|
||||
static bool IsDtlsSctp(const std::string& protocol);
|
||||
static bool ParseSessionDescription(const std::string& message, size_t* pos,
|
||||
static bool ParseSessionDescription(const std::string& message,
|
||||
size_t* pos,
|
||||
std::string* session_id,
|
||||
std::string* session_version,
|
||||
TransportDescription* session_td,
|
||||
RtpHeaderExtensions* session_extmaps,
|
||||
rtc::SocketAddress* connection_addr,
|
||||
cricket::SessionDescription* desc,
|
||||
SdpParseError* error);
|
||||
static bool ParseGroupAttribute(const std::string& line,
|
||||
@ -263,7 +265,9 @@ static bool ParseMediaDescription(
|
||||
const std::string& message,
|
||||
const TransportDescription& session_td,
|
||||
const RtpHeaderExtensions& session_extmaps,
|
||||
size_t* pos, cricket::SessionDescription* desc,
|
||||
size_t* pos,
|
||||
const rtc::SocketAddress& session_connection_addr,
|
||||
cricket::SessionDescription* desc,
|
||||
std::vector<JsepIceCandidate*>* candidates,
|
||||
SdpParseError* error);
|
||||
static bool ParseContent(const std::string& message,
|
||||
@ -713,47 +717,6 @@ static void GetDefaultDestination(
|
||||
}
|
||||
}
|
||||
|
||||
// Update |mline|'s default destination and append a c line after it.
|
||||
static void UpdateMediaDefaultDestination(
|
||||
const std::vector<Candidate>& candidates,
|
||||
const std::string& mline,
|
||||
std::string* message) {
|
||||
std::string new_lines;
|
||||
AddLine(mline, &new_lines);
|
||||
// RFC 4566
|
||||
// m=<media> <port> <proto> <fmt> ...
|
||||
std::vector<std::string> fields;
|
||||
rtc::split(mline, kSdpDelimiterSpace, &fields);
|
||||
if (fields.size() < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::ostringstream os;
|
||||
std::string rtp_port, rtp_ip, addr_type;
|
||||
GetDefaultDestination(candidates, ICE_CANDIDATE_COMPONENT_RTP,
|
||||
&rtp_port, &rtp_ip, &addr_type);
|
||||
// Found default RTP candidate.
|
||||
// RFC 5245
|
||||
// The default candidates are added to the SDP as the default
|
||||
// destination for media. For streams based on RTP, this is done by
|
||||
// placing the IP address and port of the RTP candidate into the c and m
|
||||
// lines, respectively.
|
||||
// Update the port in the m line.
|
||||
// If this is a m-line with port equal to 0, we don't change it.
|
||||
if (fields[1] != kMediaPortRejected) {
|
||||
new_lines.replace(fields[0].size() + 1,
|
||||
fields[1].size(),
|
||||
rtp_port);
|
||||
}
|
||||
// Add the c line.
|
||||
// RFC 4566
|
||||
// c=<nettype> <addrtype> <connection-address>
|
||||
InitLine(kLineTypeConnection, kConnectionNettype, &os);
|
||||
os << " " << addr_type << " " << rtp_ip;
|
||||
AddLine(os.str(), &new_lines);
|
||||
message->append(new_lines);
|
||||
}
|
||||
|
||||
// Gets "a=rtcp" line if found default RTCP candidate from |candidates|.
|
||||
static std::string GetRtcpLine(const std::vector<Candidate>& candidates) {
|
||||
std::string rtcp_line, rtcp_port, rtcp_ip, addr_type;
|
||||
@ -902,6 +865,7 @@ bool SdpDeserialize(const std::string& message,
|
||||
std::string session_version;
|
||||
TransportDescription session_td("", "");
|
||||
RtpHeaderExtensions session_extmaps;
|
||||
rtc::SocketAddress session_connection_addr;
|
||||
cricket::SessionDescription* desc = new cricket::SessionDescription();
|
||||
std::vector<JsepIceCandidate*> candidates;
|
||||
size_t current_pos = 0;
|
||||
@ -909,14 +873,15 @@ bool SdpDeserialize(const std::string& message,
|
||||
// Session Description
|
||||
if (!ParseSessionDescription(message, ¤t_pos, &session_id,
|
||||
&session_version, &session_td, &session_extmaps,
|
||||
desc, error)) {
|
||||
&session_connection_addr, desc, error)) {
|
||||
delete desc;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Media Description
|
||||
if (!ParseMediaDescription(message, session_td, session_extmaps, ¤t_pos,
|
||||
desc, &candidates, error)) {
|
||||
session_connection_addr, desc, &candidates,
|
||||
error)) {
|
||||
delete desc;
|
||||
for (std::vector<JsepIceCandidate*>::const_iterator
|
||||
it = candidates.begin(); it != candidates.end(); ++it) {
|
||||
@ -1315,9 +1280,12 @@ void BuildMediaDescription(const ContentInfo* content_info,
|
||||
//
|
||||
// However, the BUNDLE draft adds a new meaning to port zero, when used along
|
||||
// with a=bundle-only.
|
||||
const std::string& port =
|
||||
(content_info->rejected || content_info->bundle_only) ? kMediaPortRejected
|
||||
: kDummyPort;
|
||||
std::string port = kDummyPort;
|
||||
if (content_info->rejected || content_info->bundle_only) {
|
||||
port = kMediaPortRejected;
|
||||
} else if (!media_desc->connection_address().IsNil()) {
|
||||
port = rtc::ToString(media_desc->connection_address().port());
|
||||
}
|
||||
|
||||
rtc::SSLFingerprint* fp = (transport_info) ?
|
||||
transport_info->description.identity_fingerprint.get() : NULL;
|
||||
@ -1325,8 +1293,19 @@ void BuildMediaDescription(const ContentInfo* content_info,
|
||||
// Add the m and c lines.
|
||||
InitLine(kLineTypeMedia, type, &os);
|
||||
os << " " << port << " " << media_desc->protocol() << fmt;
|
||||
std::string mline = os.str();
|
||||
UpdateMediaDefaultDestination(candidates, mline, message);
|
||||
AddLine(os.str(), message);
|
||||
|
||||
InitLine(kLineTypeConnection, kConnectionNettype, &os);
|
||||
if (media_desc->connection_address().IsNil()) {
|
||||
os << " " << kConnectionIpv4Addrtype << " " << kDummyAddress;
|
||||
} else if (media_desc->connection_address().family() == AF_INET) {
|
||||
os << " " << kConnectionIpv4Addrtype << " "
|
||||
<< media_desc->connection_address().ipaddr().ToString();
|
||||
} else {
|
||||
os << " " << kConnectionIpv6Addrtype << " "
|
||||
<< media_desc->connection_address().ipaddr().ToString();
|
||||
}
|
||||
AddLine(os.str(), message);
|
||||
|
||||
// RFC 4566
|
||||
// b=AS:<bandwidth>
|
||||
@ -1900,11 +1879,62 @@ bool IsDtlsSctp(const std::string& protocol) {
|
||||
return protocol.find(cricket::kMediaProtocolDtlsSctp) != std::string::npos;
|
||||
}
|
||||
|
||||
bool ParseSessionDescription(const std::string& message, size_t* pos,
|
||||
bool ParseConnectionData(const std::string& line,
|
||||
rtc::SocketAddress* addr,
|
||||
SdpParseError* error) {
|
||||
// Parse the line from left to right.
|
||||
std::string token;
|
||||
std::string rightpart;
|
||||
// RFC 4566
|
||||
// c=<nettype> <addrtype> <connection-address>
|
||||
// Skip the "c="
|
||||
if (!rtc::tokenize_first(line, kSdpDelimiterEqual, &token, &rightpart)) {
|
||||
return ParseFailed(line, "Failed to parse the network type.", error);
|
||||
}
|
||||
|
||||
// Extract and verify the <nettype>
|
||||
if (!rtc::tokenize_first(rightpart, kSdpDelimiterSpace, &token, &rightpart) ||
|
||||
token != kConnectionNettype) {
|
||||
return ParseFailed(line,
|
||||
"Failed to parse the connection data. The network type "
|
||||
"is not currently supported.",
|
||||
error);
|
||||
}
|
||||
|
||||
// Extract the "<addrtype>" and "<connection-address>".
|
||||
if (!rtc::tokenize_first(rightpart, kSdpDelimiterSpace, &token, &rightpart)) {
|
||||
return ParseFailed(line, "Failed to parse the address type.", error);
|
||||
}
|
||||
|
||||
// The rightpart part should be the IP address without the slash which is used
|
||||
// for multicast.
|
||||
if (rightpart.find('/') != std::string::npos) {
|
||||
return ParseFailed(line,
|
||||
"Failed to parse the connection data. Multicast is not "
|
||||
"currently supported.",
|
||||
error);
|
||||
}
|
||||
addr->SetIP(rightpart);
|
||||
|
||||
// Verify that the addrtype matches the type of the parsed address.
|
||||
if ((addr->family() == AF_INET && token != "IP4") ||
|
||||
(addr->family() == AF_INET6 && token != "IP6")) {
|
||||
addr->Clear();
|
||||
return ParseFailed(
|
||||
line,
|
||||
"Failed to parse the connection data. The address type is mismatching.",
|
||||
error);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParseSessionDescription(const std::string& message,
|
||||
size_t* pos,
|
||||
std::string* session_id,
|
||||
std::string* session_version,
|
||||
TransportDescription* session_td,
|
||||
RtpHeaderExtensions* session_extmaps,
|
||||
rtc::SocketAddress* connection_addr,
|
||||
cricket::SessionDescription* desc,
|
||||
SdpParseError* error) {
|
||||
std::string line;
|
||||
@ -1962,7 +1992,11 @@ bool ParseSessionDescription(const std::string& message, size_t* pos,
|
||||
// RFC 4566
|
||||
// c=* (connection information -- not required if included in
|
||||
// all media)
|
||||
GetLineWithType(message, pos, &line, kLineTypeConnection);
|
||||
if (GetLineWithType(message, pos, &line, kLineTypeConnection)) {
|
||||
if (!ParseConnectionData(line, connection_addr, error)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// RFC 4566
|
||||
// b=* (zero or more bandwidth information lines)
|
||||
@ -2266,7 +2300,7 @@ static C* ParseContentDescription(const std::string& message,
|
||||
pos, content_name, bundle_only, media_desc, transport,
|
||||
candidates, error)) {
|
||||
delete media_desc;
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
// Sort the codecs according to the m-line fmt list.
|
||||
std::unordered_map<int, int> payload_type_preferences;
|
||||
@ -2291,6 +2325,7 @@ bool ParseMediaDescription(const std::string& message,
|
||||
const TransportDescription& session_td,
|
||||
const RtpHeaderExtensions& session_extmaps,
|
||||
size_t* pos,
|
||||
const rtc::SocketAddress& session_connection_addr,
|
||||
cricket::SessionDescription* desc,
|
||||
std::vector<JsepIceCandidate*>* candidates,
|
||||
SdpParseError* error) {
|
||||
@ -2320,6 +2355,10 @@ bool ParseMediaDescription(const std::string& message,
|
||||
port_rejected = true;
|
||||
}
|
||||
|
||||
int port = 0;
|
||||
if (!rtc::FromString<int>(fields[1], &port) || !IsValidPort(port)) {
|
||||
return ParseFailed(line, "The port number is invalid", error);
|
||||
}
|
||||
std::string protocol = fields[2];
|
||||
|
||||
// <fmt>
|
||||
@ -2420,6 +2459,16 @@ bool ParseMediaDescription(const std::string& message,
|
||||
}
|
||||
}
|
||||
content->set_protocol(protocol);
|
||||
|
||||
// Use the session level connection address if the media level addresses are
|
||||
// not specified.
|
||||
rtc::SocketAddress address;
|
||||
address = content->connection_address().IsNil()
|
||||
? session_connection_addr
|
||||
: content->connection_address();
|
||||
address.SetPort(port);
|
||||
content->set_connection_address(address);
|
||||
|
||||
desc->AddContent(content_name,
|
||||
IsDtlsSctp(protocol) ? cricket::NS_JINGLE_DRAFT_SCTP
|
||||
: cricket::NS_JINGLE_RTP,
|
||||
@ -2668,6 +2717,16 @@ bool ParseContent(const std::string& message,
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse the media level connection data.
|
||||
if (IsLineType(line, kLineTypeConnection)) {
|
||||
rtc::SocketAddress addr;
|
||||
if (!ParseConnectionData(line, &addr, error)) {
|
||||
return false;
|
||||
}
|
||||
media_desc->set_connection_address(addr);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsLineType(line, kLineTypeAttributes)) {
|
||||
// TODO: Handle other lines if needed.
|
||||
LOG(LS_INFO) << "Ignored line: " << line;
|
||||
|
||||
@ -875,6 +875,8 @@ class WebRtcSdpTest : public testing::Test {
|
||||
audio_stream.sync_label = kStreamLabel1;
|
||||
audio_stream.ssrcs.push_back(kAudioTrack1Ssrc);
|
||||
audio_desc_->AddStream(audio_stream);
|
||||
rtc::SocketAddress audio_addr("74.125.127.126", 2345);
|
||||
audio_desc_->set_connection_address(audio_addr);
|
||||
desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_desc_);
|
||||
|
||||
// VideoContentDescription
|
||||
@ -888,6 +890,8 @@ class WebRtcSdpTest : public testing::Test {
|
||||
cricket::SsrcGroup ssrc_group(kFecSsrcGroupSemantics, video_stream.ssrcs);
|
||||
video_stream.ssrc_groups.push_back(ssrc_group);
|
||||
video_desc_->AddStream(video_stream);
|
||||
rtc::SocketAddress video_addr("74.125.224.39", 3457);
|
||||
video_desc_->set_connection_address(video_addr);
|
||||
desc_.AddContent(kVideoContentName, NS_JINGLE_RTP, video_desc_);
|
||||
|
||||
// TransportInfo
|
||||
@ -1090,7 +1094,6 @@ class WebRtcSdpTest : public testing::Test {
|
||||
desc_.AddContent(kAudioContentName2, NS_JINGLE_RTP, audio_desc_2);
|
||||
EXPECT_TRUE(desc_.AddTransportInfo(TransportInfo(
|
||||
kAudioContentName2, TransportDescription(kUfragVoice2, kPwdVoice2))));
|
||||
|
||||
// Video track 2, in stream 2.
|
||||
VideoContentDescription* video_desc_2 = CreateVideoContentDescription();
|
||||
StreamParams video_track_2;
|
||||
@ -1470,6 +1473,7 @@ class WebRtcSdpTest : public testing::Test {
|
||||
audio_desc_->Copy());
|
||||
video_desc_ = static_cast<VideoContentDescription*>(
|
||||
video_desc_->Copy());
|
||||
|
||||
desc_.RemoveContentByName(kAudioContentName);
|
||||
desc_.RemoveContentByName(kVideoContentName);
|
||||
desc_.AddContent(kAudioContentName, NS_JINGLE_RTP, audio_rejected,
|
||||
@ -1485,10 +1489,7 @@ class WebRtcSdpTest : public testing::Test {
|
||||
ReplaceRejected(audio_rejected, video_rejected, &new_sdp);
|
||||
|
||||
JsepSessionDescription jdesc_no_candidates(kDummyString);
|
||||
if (!jdesc_no_candidates.Initialize(desc_.Copy(), kSessionId,
|
||||
kSessionVersion)) {
|
||||
return false;
|
||||
}
|
||||
MakeDescriptionWithoutCandidates(&jdesc_no_candidates);
|
||||
std::string message = webrtc::SdpSerialize(jdesc_no_candidates, false);
|
||||
EXPECT_EQ(new_sdp, message);
|
||||
return true;
|
||||
@ -1766,6 +1767,18 @@ class WebRtcSdpTest : public testing::Test {
|
||||
EXPECT_TRUE(CompareSessionDescription(jdesc, jdesc_output_des));
|
||||
}
|
||||
|
||||
// Calling 'Initialize' with a copy of the inner SessionDescription will
|
||||
// create a copy of the JsepSessionDescription without candidates. The
|
||||
// 'connection address' field, previously set from the candidates, must also
|
||||
// be reset.
|
||||
void MakeDescriptionWithoutCandidates(JsepSessionDescription* jdesc) {
|
||||
rtc::SocketAddress audio_addr("0.0.0.0", 9);
|
||||
rtc::SocketAddress video_addr("0.0.0.0", 9);
|
||||
audio_desc_->set_connection_address(audio_addr);
|
||||
video_desc_->set_connection_address(video_addr);
|
||||
ASSERT_TRUE(jdesc->Initialize(desc_.Copy(), kSessionId, kSessionVersion));
|
||||
}
|
||||
|
||||
protected:
|
||||
SessionDescription desc_;
|
||||
AudioContentDescription* audio_desc_;
|
||||
@ -1801,134 +1814,12 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionEmpty) {
|
||||
EXPECT_EQ("", webrtc::SdpSerialize(jdesc_empty, false));
|
||||
}
|
||||
|
||||
// This tests serialization of SDP with only IPv6 candidates and verifies that
|
||||
// IPv6 is used as default address in c line according to preference.
|
||||
TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithIPv6Only) {
|
||||
// Only test 1 m line.
|
||||
desc_.RemoveContentByName("video_content_name");
|
||||
// Stun has a high preference than local host.
|
||||
cricket::Candidate candidate1(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
|
||||
cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation1);
|
||||
cricket::Candidate candidate2(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("::2", 1235), kCandidatePriority, "", "",
|
||||
cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation1);
|
||||
JsepSessionDescription jdesc(kDummyString);
|
||||
ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
|
||||
|
||||
// Only add the candidates to audio m line.
|
||||
JsepIceCandidate jice1("audio_content_name", 0, candidate1);
|
||||
JsepIceCandidate jice2("audio_content_name", 0, candidate2);
|
||||
ASSERT_TRUE(jdesc.AddCandidate(&jice1));
|
||||
ASSERT_TRUE(jdesc.AddCandidate(&jice2));
|
||||
std::string message = webrtc::SdpSerialize(jdesc, false);
|
||||
|
||||
// Audio line should have a c line like this one.
|
||||
EXPECT_NE(message.find("c=IN IP6 ::1"), std::string::npos);
|
||||
// Shouldn't have a IP4 c line.
|
||||
EXPECT_EQ(message.find("c=IN IP4"), std::string::npos);
|
||||
}
|
||||
|
||||
// This tests serialization of SDP with both IPv4 and IPv6 candidates and
|
||||
// verifies that IPv4 is used as default address in c line even if the
|
||||
// preference of IPv4 is lower.
|
||||
TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBothIPFamilies) {
|
||||
// Only test 1 m line.
|
||||
desc_.RemoveContentByName("video_content_name");
|
||||
cricket::Candidate candidate_v4(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("192.168.1.5", 1234), kCandidatePriority, "", "",
|
||||
cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation1);
|
||||
cricket::Candidate candidate_v6(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
|
||||
cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation1);
|
||||
JsepSessionDescription jdesc(kDummyString);
|
||||
ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
|
||||
|
||||
// Only add the candidates to audio m line.
|
||||
JsepIceCandidate jice_v4("audio_content_name", 0, candidate_v4);
|
||||
JsepIceCandidate jice_v6("audio_content_name", 0, candidate_v6);
|
||||
ASSERT_TRUE(jdesc.AddCandidate(&jice_v4));
|
||||
ASSERT_TRUE(jdesc.AddCandidate(&jice_v6));
|
||||
std::string message = webrtc::SdpSerialize(jdesc, false);
|
||||
|
||||
// Audio line should have a c line like this one.
|
||||
EXPECT_NE(message.find("c=IN IP4 192.168.1.5"), std::string::npos);
|
||||
// Shouldn't have a IP6 c line.
|
||||
EXPECT_EQ(message.find("c=IN IP6"), std::string::npos);
|
||||
}
|
||||
|
||||
// This tests serialization of SDP with both UDP and TCP candidates and
|
||||
// verifies that UDP is used as default address in c line even if the
|
||||
// preference of UDP is lower.
|
||||
TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithBothProtocols) {
|
||||
// Only test 1 m line.
|
||||
desc_.RemoveContentByName("video_content_name");
|
||||
// Stun has a high preference than local host.
|
||||
cricket::Candidate candidate1(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
|
||||
rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
|
||||
cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation1);
|
||||
cricket::Candidate candidate2(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "udp",
|
||||
rtc::SocketAddress("fe80::1234:5678:abcd:ef12", 1235), kCandidatePriority,
|
||||
"", "", cricket::LOCAL_PORT_TYPE, kCandidateGeneration,
|
||||
kCandidateFoundation1);
|
||||
JsepSessionDescription jdesc(kDummyString);
|
||||
ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
|
||||
|
||||
// Only add the candidates to audio m line.
|
||||
JsepIceCandidate jice1("audio_content_name", 0, candidate1);
|
||||
JsepIceCandidate jice2("audio_content_name", 0, candidate2);
|
||||
ASSERT_TRUE(jdesc.AddCandidate(&jice1));
|
||||
ASSERT_TRUE(jdesc.AddCandidate(&jice2));
|
||||
std::string message = webrtc::SdpSerialize(jdesc, false);
|
||||
|
||||
// Audio line should have a c line like this one.
|
||||
EXPECT_NE(message.find("c=IN IP6 fe80::1234:5678:abcd:ef12"),
|
||||
std::string::npos);
|
||||
// Shouldn't have a IP4 c line.
|
||||
EXPECT_EQ(message.find("c=IN IP4"), std::string::npos);
|
||||
}
|
||||
|
||||
// This tests serialization of SDP with only TCP candidates and verifies that
|
||||
// null IPv4 is used as default address in c line.
|
||||
TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithTCPOnly) {
|
||||
// Only test 1 m line.
|
||||
desc_.RemoveContentByName("video_content_name");
|
||||
// Stun has a high preference than local host.
|
||||
cricket::Candidate candidate1(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
|
||||
rtc::SocketAddress("::1", 1234), kCandidatePriority, "", "",
|
||||
cricket::STUN_PORT_TYPE, kCandidateGeneration, kCandidateFoundation1);
|
||||
cricket::Candidate candidate2(
|
||||
cricket::ICE_CANDIDATE_COMPONENT_RTP, "tcp",
|
||||
rtc::SocketAddress("::2", 1235), kCandidatePriority, "", "",
|
||||
cricket::LOCAL_PORT_TYPE, kCandidateGeneration, kCandidateFoundation1);
|
||||
JsepSessionDescription jdesc(kDummyString);
|
||||
ASSERT_TRUE(jdesc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
|
||||
|
||||
// Only add the candidates to audio m line.
|
||||
JsepIceCandidate jice1("audio_content_name", 0, candidate1);
|
||||
JsepIceCandidate jice2("audio_content_name", 0, candidate2);
|
||||
ASSERT_TRUE(jdesc.AddCandidate(&jice1));
|
||||
ASSERT_TRUE(jdesc.AddCandidate(&jice2));
|
||||
std::string message = webrtc::SdpSerialize(jdesc, false);
|
||||
|
||||
// Audio line should have a c line like this one when no any default exists.
|
||||
EXPECT_NE(message.find("c=IN IP4 0.0.0.0"), std::string::npos);
|
||||
}
|
||||
|
||||
// This tests serialization of SDP with a=crypto and a=fingerprint, as would be
|
||||
// the case in a DTLS offer.
|
||||
TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprint) {
|
||||
AddFingerprint();
|
||||
JsepSessionDescription jdesc_with_fingerprint(kDummyString);
|
||||
ASSERT_TRUE(jdesc_with_fingerprint.Initialize(desc_.Copy(),
|
||||
kSessionId, kSessionVersion));
|
||||
MakeDescriptionWithoutCandidates(&jdesc_with_fingerprint);
|
||||
std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint, false);
|
||||
|
||||
std::string sdp_with_fingerprint = kSdpString;
|
||||
@ -1946,8 +1837,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprintNoCryptos) {
|
||||
AddFingerprint();
|
||||
RemoveCryptos();
|
||||
JsepSessionDescription jdesc_with_fingerprint(kDummyString);
|
||||
ASSERT_TRUE(jdesc_with_fingerprint.Initialize(desc_.Copy(),
|
||||
kSessionId, kSessionVersion));
|
||||
MakeDescriptionWithoutCandidates(&jdesc_with_fingerprint);
|
||||
std::string message = webrtc::SdpSerialize(jdesc_with_fingerprint, false);
|
||||
|
||||
std::string sdp_with_fingerprint = kSdpString;
|
||||
@ -1964,8 +1854,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithFingerprintNoCryptos) {
|
||||
TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithoutCandidates) {
|
||||
// JsepSessionDescription with desc but without candidates.
|
||||
JsepSessionDescription jdesc_no_candidates(kDummyString);
|
||||
ASSERT_TRUE(jdesc_no_candidates.Initialize(desc_.Copy(), kSessionId,
|
||||
kSessionVersion));
|
||||
MakeDescriptionWithoutCandidates(&jdesc_no_candidates);
|
||||
std::string message = webrtc::SdpSerialize(jdesc_no_candidates, false);
|
||||
EXPECT_EQ(std::string(kSdpString), message);
|
||||
}
|
||||
@ -2058,7 +1947,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithRtpDataChannel) {
|
||||
AddRtpDataChannel();
|
||||
JsepSessionDescription jsep_desc(kDummyString);
|
||||
|
||||
ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
|
||||
MakeDescriptionWithoutCandidates(&jsep_desc);
|
||||
std::string message = webrtc::SdpSerialize(jsep_desc, false);
|
||||
|
||||
std::string expected_sdp = kSdpString;
|
||||
@ -2071,7 +1960,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithSctpDataChannel) {
|
||||
AddSctpDataChannel(use_sctpmap);
|
||||
JsepSessionDescription jsep_desc(kDummyString);
|
||||
|
||||
ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
|
||||
MakeDescriptionWithoutCandidates(&jsep_desc);
|
||||
std::string message = webrtc::SdpSerialize(jsep_desc, false);
|
||||
|
||||
std::string expected_sdp = kSdpString;
|
||||
@ -2083,8 +1972,7 @@ TEST_F(WebRtcSdpTest, SerializeWithSctpDataChannelAndNewPort) {
|
||||
bool use_sctpmap = true;
|
||||
AddSctpDataChannel(use_sctpmap);
|
||||
JsepSessionDescription jsep_desc(kDummyString);
|
||||
|
||||
ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
|
||||
MakeDescriptionWithoutCandidates(&jsep_desc);
|
||||
DataContentDescription* dcdesc = static_cast<DataContentDescription*>(
|
||||
jsep_desc.description()->GetContentDescriptionByName(kDataContentName));
|
||||
|
||||
@ -2112,11 +2000,10 @@ TEST_F(WebRtcSdpTest, SerializeWithSctpDataChannelAndNewPort) {
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithDataChannelAndBandwidth) {
|
||||
JsepSessionDescription jsep_desc(kDummyString);
|
||||
AddRtpDataChannel();
|
||||
data_desc_->set_bandwidth(100*1000);
|
||||
JsepSessionDescription jsep_desc(kDummyString);
|
||||
|
||||
ASSERT_TRUE(jsep_desc.Initialize(desc_.Copy(), kSessionId, kSessionVersion));
|
||||
MakeDescriptionWithoutCandidates(&jsep_desc);
|
||||
std::string message = webrtc::SdpSerialize(jsep_desc, false);
|
||||
|
||||
std::string expected_sdp = kSdpString;
|
||||
@ -2131,8 +2018,7 @@ TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithDataChannelAndBandwidth) {
|
||||
TEST_F(WebRtcSdpTest, SerializeSessionDescriptionWithExtmap) {
|
||||
AddExtmap();
|
||||
JsepSessionDescription desc_with_extmap("dummy");
|
||||
ASSERT_TRUE(desc_with_extmap.Initialize(desc_.Copy(),
|
||||
kSessionId, kSessionVersion));
|
||||
MakeDescriptionWithoutCandidates(&desc_with_extmap);
|
||||
std::string message = webrtc::SdpSerialize(desc_with_extmap, false);
|
||||
|
||||
std::string sdp_with_extmap = kSdpString;
|
||||
@ -3504,3 +3390,121 @@ TEST_F(WebRtcSdpTest, DeserializeMsidAttributeWithMissingStreamId) {
|
||||
JsepSessionDescription jdesc_output(kDummyString);
|
||||
EXPECT_FALSE(SdpDeserialize(kSdpWithMissingStreamId, &jdesc_output));
|
||||
}
|
||||
|
||||
// Tests that if both session-level address and media-level address exist, use
|
||||
// the media-level address.
|
||||
TEST_F(WebRtcSdpTest, ParseConnectionData) {
|
||||
JsepSessionDescription jsep_desc(kDummyString);
|
||||
|
||||
// Sesssion-level address.
|
||||
std::string sdp = kSdpFullString;
|
||||
InjectAfter("s=-\r\n", "c=IN IP4 192.168.0.3\r\n", &sdp);
|
||||
EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
|
||||
|
||||
const auto& content1 = jsep_desc.description()->contents()[0];
|
||||
EXPECT_EQ("74.125.127.126:2345",
|
||||
static_cast<cricket::MediaContentDescription*>(content1.description)
|
||||
->connection_address()
|
||||
.ToString());
|
||||
const auto& content2 = jsep_desc.description()->contents()[1];
|
||||
EXPECT_EQ("74.125.224.39:3457",
|
||||
static_cast<cricket::MediaContentDescription*>(content2.description)
|
||||
->connection_address()
|
||||
.ToString());
|
||||
}
|
||||
|
||||
// Tests that the session-level connection address will be used if the media
|
||||
// level-addresses are not specified.
|
||||
TEST_F(WebRtcSdpTest, ParseConnectionDataSessionLevelOnly) {
|
||||
JsepSessionDescription jsep_desc(kDummyString);
|
||||
|
||||
// Sesssion-level address.
|
||||
std::string sdp = kSdpString;
|
||||
InjectAfter("s=-\r\n", "c=IN IP4 192.168.0.3\r\n", &sdp);
|
||||
// Remove the media level addresses.
|
||||
Replace("c=IN IP4 0.0.0.0\r\n", "", &sdp);
|
||||
Replace("c=IN IP4 0.0.0.0\r\n", "", &sdp);
|
||||
EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
|
||||
|
||||
const auto& content1 = jsep_desc.description()->contents()[0];
|
||||
EXPECT_EQ("192.168.0.3:9",
|
||||
static_cast<cricket::MediaContentDescription*>(content1.description)
|
||||
->connection_address()
|
||||
.ToString());
|
||||
const auto& content2 = jsep_desc.description()->contents()[1];
|
||||
EXPECT_EQ("192.168.0.3:9",
|
||||
static_cast<cricket::MediaContentDescription*>(content2.description)
|
||||
->connection_address()
|
||||
.ToString());
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSdpTest, ParseConnectionDataIPv6) {
|
||||
JsepSessionDescription jsep_desc(kDummyString);
|
||||
|
||||
std::string sdp = kSdpString;
|
||||
EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
|
||||
Replace("m=audio 9 RTP/SAVPF 111 103 104\r\nc=IN IP4 0.0.0.0\r\n",
|
||||
"m=audio 9 RTP/SAVPF 111 103 104\r\nc=IN IP6 "
|
||||
"2001:0db8:85a3:0000:0000:8a2e:0370:7335\r\n",
|
||||
&sdp);
|
||||
Replace("m=video 9 RTP/SAVPF 120\r\nc=IN IP4 0.0.0.0\r\n",
|
||||
"m=video 9 RTP/SAVPF 120\r\nc=IN IP6 "
|
||||
"2001:0db8:85a3:0000:0000:8a2e:0370:7336\r\n",
|
||||
&sdp);
|
||||
EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
|
||||
const auto& content1 = jsep_desc.description()->contents()[0];
|
||||
EXPECT_EQ("[2001:db8:85a3::8a2e:370:7335]:9",
|
||||
static_cast<cricket::MediaContentDescription*>(content1.description)
|
||||
->connection_address()
|
||||
.ToString());
|
||||
const auto& content2 = jsep_desc.description()->contents()[1];
|
||||
EXPECT_EQ("[2001:db8:85a3::8a2e:370:7336]:9",
|
||||
static_cast<cricket::MediaContentDescription*>(content2.description)
|
||||
->connection_address()
|
||||
.ToString());
|
||||
}
|
||||
|
||||
// Test that the invalid or unsupprted connection data cannot be parsed.
|
||||
TEST_F(WebRtcSdpTest, ParseConnectionDataFailure) {
|
||||
JsepSessionDescription jsep_desc(kDummyString);
|
||||
std::string sdp = kSdpString;
|
||||
EXPECT_TRUE(SdpDeserialize(sdp, &jsep_desc));
|
||||
|
||||
// Unsupported multicast IPv4 address.
|
||||
sdp = kSdpFullString;
|
||||
Replace("c=IN IP4 74.125.224.39\r\n", "c=IN IP4 74.125.224.39/127\r\n", &sdp);
|
||||
EXPECT_FALSE(SdpDeserialize(sdp, &jsep_desc));
|
||||
|
||||
// Unsupported multicast IPv6 address.
|
||||
sdp = kSdpFullString;
|
||||
Replace("c=IN IP4 74.125.224.39\r\n", "c=IN IP6 ::1/3\r\n", &sdp);
|
||||
EXPECT_FALSE(SdpDeserialize(sdp, &jsep_desc));
|
||||
|
||||
// Mismatched address type.
|
||||
sdp = kSdpFullString;
|
||||
Replace("c=IN IP4 74.125.224.39\r\n", "c=IN IP6 74.125.224.39\r\n", &sdp);
|
||||
EXPECT_FALSE(SdpDeserialize(sdp, &jsep_desc));
|
||||
|
||||
sdp = kSdpFullString;
|
||||
Replace("c=IN IP4 74.125.224.39\r\n",
|
||||
"c=IN IP4 2001:0db8:85a3:0000:0000:8a2e:0370:7334\r\n", &sdp);
|
||||
EXPECT_FALSE(SdpDeserialize(sdp, &jsep_desc));
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSdpTest, SerializeAndDeserializeWithConnectionAddress) {
|
||||
JsepSessionDescription expected_jsep(kDummyString);
|
||||
MakeDescriptionWithoutCandidates(&expected_jsep);
|
||||
// Serialization.
|
||||
std::string message = webrtc::SdpSerialize(expected_jsep, false);
|
||||
// Deserialization.
|
||||
JsepSessionDescription jdesc(kDummyString);
|
||||
EXPECT_TRUE(SdpDeserialize(message, &jdesc));
|
||||
auto audio_desc = static_cast<cricket::MediaContentDescription*>(
|
||||
jdesc.description()->GetContentByName(kAudioContentName)->description);
|
||||
auto video_desc = static_cast<cricket::MediaContentDescription*>(
|
||||
jdesc.description()->GetContentByName(kVideoContentName)->description);
|
||||
EXPECT_EQ(audio_desc_->connection_address().ToString(),
|
||||
audio_desc->connection_address().ToString());
|
||||
EXPECT_EQ(video_desc_->connection_address().ToString(),
|
||||
video_desc->connection_address().ToString());
|
||||
}
|
||||
|
||||
@ -2168,7 +2168,7 @@ TEST_F(WebRtcSessionTest, TestAddAndRemoveRemoteCandidates) {
|
||||
SendAudioVideoStream1();
|
||||
|
||||
cricket::Candidate candidate(1, "udp", rtc::SocketAddress("1.1.1.1", 5000), 0,
|
||||
"", "", "host", 0, "");
|
||||
"", "", "local", 0, "");
|
||||
candidate.set_transport_name("audio");
|
||||
JsepIceCandidate ice_candidate1(kMediaContentName0, 0, candidate);
|
||||
|
||||
@ -3077,6 +3077,7 @@ TEST_F(WebRtcSessionTest, TestIgnoreCandidatesForUnusedTransportWhenBundling) {
|
||||
candidate0.set_address(rtc::SocketAddress("1.1.1.1", 5000));
|
||||
candidate0.set_component(1);
|
||||
candidate0.set_protocol("udp");
|
||||
candidate0.set_type("local");
|
||||
JsepIceCandidate ice_candidate0(kMediaContentName0, kMediaContentIndex0,
|
||||
candidate0);
|
||||
EXPECT_TRUE(session_->ProcessIceMessage(&ice_candidate0));
|
||||
@ -3086,6 +3087,7 @@ TEST_F(WebRtcSessionTest, TestIgnoreCandidatesForUnusedTransportWhenBundling) {
|
||||
candidate1.set_address(rtc::SocketAddress("1.1.1.1", 6000));
|
||||
candidate1.set_component(1);
|
||||
candidate1.set_protocol("udp");
|
||||
candidate1.set_type("local");
|
||||
JsepIceCandidate ice_candidate1(kMediaContentName1, kMediaContentIndex1,
|
||||
candidate1);
|
||||
EXPECT_TRUE(session_->ProcessIceMessage(&ice_candidate1));
|
||||
@ -3095,6 +3097,7 @@ TEST_F(WebRtcSessionTest, TestIgnoreCandidatesForUnusedTransportWhenBundling) {
|
||||
candidate2.set_address(rtc::SocketAddress("1.1.1.1", 5001));
|
||||
candidate2.set_component(1);
|
||||
candidate2.set_protocol("udp");
|
||||
candidate2.set_type("local");
|
||||
JsepIceCandidate ice_candidate2(kMediaContentName0, kMediaContentIndex0,
|
||||
candidate2);
|
||||
EXPECT_TRUE(session_->ProcessIceMessage(&ice_candidate2));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user