blob: d00ca5225e0ca46819e5b4f78f6f95a87dac8b00 [file] [log] [blame]
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00001/*
kjellanderb24317b2016-02-10 07:54:43 -08002 * Copyright 2012 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003 *
kjellanderb24317b2016-02-10 07:54:43 -08004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00009 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "api/jsepsessiondescription.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
kwibergd1fe2812016-04-27 06:47:29 -070013#include <memory>
14
Karl Wiberg918f50c2018-07-05 11:40:33 +020015#include "absl/memory/memory.h"
Steve Anton88f2cb92017-12-05 12:47:32 -080016#include "p2p/base/port.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "pc/mediasession.h"
18#include "pc/webrtcsdp.h"
19#include "rtc_base/arraysize.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000020
henrike@webrtc.org28e20752013-07-10 00:45:36 +000021using cricket::SessionDescription;
22
23namespace webrtc {
Steve Anton88f2cb92017-12-05 12:47:32 -080024namespace {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000025
zhihuang38989e52017-03-21 11:04:53 -070026// RFC 5245
27// It is RECOMMENDED that default candidates be chosen based on the
28// likelihood of those candidates to work with the peer that is being
29// contacted. It is RECOMMENDED that relayed > reflexive > host.
Steve Anton88f2cb92017-12-05 12:47:32 -080030constexpr int kPreferenceUnknown = 0;
31constexpr int kPreferenceHost = 1;
32constexpr int kPreferenceReflexive = 2;
33constexpr int kPreferenceRelayed = 3;
zhihuang38989e52017-03-21 11:04:53 -070034
Steve Anton88f2cb92017-12-05 12:47:32 -080035constexpr char kDummyAddress[] = "0.0.0.0";
36constexpr int kDummyPort = 9;
zhihuang38989e52017-03-21 11:04:53 -070037
Steve Anton88f2cb92017-12-05 12:47:32 -080038int GetCandidatePreferenceFromType(const std::string& type) {
zhihuang38989e52017-03-21 11:04:53 -070039 int preference = kPreferenceUnknown;
40 if (type == cricket::LOCAL_PORT_TYPE) {
41 preference = kPreferenceHost;
42 } else if (type == cricket::STUN_PORT_TYPE) {
43 preference = kPreferenceReflexive;
44 } else if (type == cricket::RELAY_PORT_TYPE) {
45 preference = kPreferenceRelayed;
46 } else {
zhihuang15238652017-03-23 10:32:12 -070047 preference = kPreferenceUnknown;
zhihuang38989e52017-03-21 11:04:53 -070048 }
49 return preference;
50}
51
52// Update the connection address for the MediaContentDescription based on the
53// candidates.
Steve Anton88f2cb92017-12-05 12:47:32 -080054void UpdateConnectionAddress(
zhihuang38989e52017-03-21 11:04:53 -070055 const JsepCandidateCollection& candidate_collection,
Steve Antonb1c1de12017-12-21 15:14:30 -080056 cricket::MediaContentDescription* media_desc) {
zhihuang38989e52017-03-21 11:04:53 -070057 int port = kDummyPort;
58 std::string ip = kDummyAddress;
59 int current_preference = kPreferenceUnknown;
60 int current_family = AF_UNSPEC;
61 for (size_t i = 0; i < candidate_collection.count(); ++i) {
62 const IceCandidateInterface* jsep_candidate = candidate_collection.at(i);
63 if (jsep_candidate->candidate().component() !=
64 cricket::ICE_CANDIDATE_COMPONENT_RTP) {
65 continue;
66 }
67 // Default destination should be UDP only.
68 if (jsep_candidate->candidate().protocol() != cricket::UDP_PROTOCOL_NAME) {
69 continue;
70 }
71 const int preference =
72 GetCandidatePreferenceFromType(jsep_candidate->candidate().type());
73 const int family = jsep_candidate->candidate().address().ipaddr().family();
74 // See if this candidate is more preferable then the current one if it's the
75 // same family. Or if the current family is IPv4 already so we could safely
76 // ignore all IPv6 ones. WebRTC bug 4269.
77 // http://code.google.com/p/webrtc/issues/detail?id=4269
78 if ((preference <= current_preference && current_family == family) ||
79 (current_family == AF_INET && family == AF_INET6)) {
80 continue;
81 }
82 current_preference = preference;
83 current_family = family;
84 port = jsep_candidate->candidate().address().port();
85 ip = jsep_candidate->candidate().address().ipaddr().ToString();
86 }
87 rtc::SocketAddress connection_addr;
88 connection_addr.SetIP(ip);
89 connection_addr.SetPort(port);
Steve Antonb1c1de12017-12-21 15:14:30 -080090 media_desc->set_connection_address(connection_addr);
zhihuang38989e52017-03-21 11:04:53 -070091}
92
Steve Anton88f2cb92017-12-05 12:47:32 -080093} // namespace
94
henrike@webrtc.org28e20752013-07-10 00:45:36 +000095const int JsepSessionDescription::kDefaultVideoCodecId = 100;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000096const char JsepSessionDescription::kDefaultVideoCodecName[] = "VP8";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000097
Steve Anton88f2cb92017-12-05 12:47:32 -080098// TODO(steveanton): Remove this default implementation once Chromium has been
99// updated.
100SdpType SessionDescriptionInterface::GetType() const {
Danil Chapovalov66cadcc2018-06-19 16:47:43 +0200101 absl::optional<SdpType> maybe_type = SdpTypeFromString(type());
Steve Anton88f2cb92017-12-05 12:47:32 -0800102 if (maybe_type) {
103 return *maybe_type;
104 } else {
105 RTC_LOG(LS_WARNING) << "Default implementation of "
106 "SessionDescriptionInterface::GetType does not "
107 "recognize the result from type(), returning "
108 "kOffer.";
109 return SdpType::kOffer;
110 }
111}
112
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000113SessionDescriptionInterface* CreateSessionDescription(const std::string& type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000114 const std::string& sdp,
115 SdpParseError* error) {
Danil Chapovalov66cadcc2018-06-19 16:47:43 +0200116 absl::optional<SdpType> maybe_type = SdpTypeFromString(type);
Steve Anton88f2cb92017-12-05 12:47:32 -0800117 if (!maybe_type) {
118 return nullptr;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000119 }
120
Steve Anton88f2cb92017-12-05 12:47:32 -0800121 return CreateSessionDescription(*maybe_type, sdp, error).release();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000122}
123
Steve Anton88f2cb92017-12-05 12:47:32 -0800124std::unique_ptr<SessionDescriptionInterface> CreateSessionDescription(
125 SdpType type,
126 const std::string& sdp) {
127 return CreateSessionDescription(type, sdp, nullptr);
128}
129
130std::unique_ptr<SessionDescriptionInterface> CreateSessionDescription(
131 SdpType type,
132 const std::string& sdp,
133 SdpParseError* error_out) {
Karl Wiberg918f50c2018-07-05 11:40:33 +0200134 auto jsep_desc = absl::make_unique<JsepSessionDescription>(type);
Steve Anton88f2cb92017-12-05 12:47:32 -0800135 if (!SdpDeserialize(sdp, jsep_desc.get(), error_out)) {
136 return nullptr;
137 }
138 return std::move(jsep_desc);
139}
140
Steve Antond9e4a062018-07-24 18:23:33 -0700141std::unique_ptr<SessionDescriptionInterface> CreateSessionDescription(
142 SdpType type,
143 const std::string& session_id,
144 const std::string& session_version,
145 std::unique_ptr<cricket::SessionDescription> description) {
146 auto jsep_description = absl::make_unique<JsepSessionDescription>(type);
147 bool initialize_success = jsep_description->Initialize(
148 description.release(), session_id, session_version);
149 RTC_DCHECK(initialize_success);
150 return std::move(jsep_description);
151}
152
Steve Anton88f2cb92017-12-05 12:47:32 -0800153JsepSessionDescription::JsepSessionDescription(SdpType type) : type_(type) {}
154
155JsepSessionDescription::JsepSessionDescription(const std::string& type) {
Danil Chapovalov66cadcc2018-06-19 16:47:43 +0200156 absl::optional<SdpType> maybe_type = SdpTypeFromString(type);
Steve Anton88f2cb92017-12-05 12:47:32 -0800157 if (maybe_type) {
158 type_ = *maybe_type;
159 } else {
160 RTC_LOG(LS_WARNING)
161 << "JsepSessionDescription constructed with invalid type string: "
162 << type << ". Assuming it is an offer.";
163 type_ = SdpType::kOffer;
164 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000165}
166
Steve Anton6fe1fba2018-12-11 10:15:23 -0800167JsepSessionDescription::JsepSessionDescription(
168 SdpType type,
169 std::unique_ptr<cricket::SessionDescription> description,
170 absl::string_view session_id,
171 absl::string_view session_version)
172 : description_(std::move(description)),
173 session_id_(session_id),
174 session_version_(session_version),
175 type_(type) {
176 RTC_DCHECK(description_);
177 candidate_collection_.resize(number_of_mediasections());
178}
179
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000180JsepSessionDescription::~JsepSessionDescription() {}
181
182bool JsepSessionDescription::Initialize(
183 cricket::SessionDescription* description,
184 const std::string& session_id,
185 const std::string& session_version) {
186 if (!description)
187 return false;
188
189 session_id_ = session_id;
190 session_version_ = session_version;
191 description_.reset(description);
192 candidate_collection_.resize(number_of_mediasections());
193 return true;
194}
195
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000196bool JsepSessionDescription::AddCandidate(
197 const IceCandidateInterface* candidate) {
198 if (!candidate || candidate->sdp_mline_index() < 0)
199 return false;
200 size_t mediasection_index = 0;
201 if (!GetMediasectionIndex(candidate, &mediasection_index)) {
202 return false;
203 }
204 if (mediasection_index >= number_of_mediasections())
205 return false;
jbauch083b73f2015-07-16 02:46:32 -0700206 const std::string& content_name =
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000207 description_->contents()[mediasection_index].name;
208 const cricket::TransportInfo* transport_info =
209 description_->GetTransportInfoByName(content_name);
210 if (!transport_info) {
211 return false;
212 }
213
214 cricket::Candidate updated_candidate = candidate->candidate();
215 if (updated_candidate.username().empty()) {
216 updated_candidate.set_username(transport_info->description.ice_ufrag);
217 }
218 if (updated_candidate.password().empty()) {
219 updated_candidate.set_password(transport_info->description.ice_pwd);
220 }
221
kwibergd1fe2812016-04-27 06:47:29 -0700222 std::unique_ptr<JsepIceCandidate> updated_candidate_wrapper(
mallinath@webrtc.org67ee6b92014-02-03 16:57:16 +0000223 new JsepIceCandidate(candidate->sdp_mid(),
224 static_cast<int>(mediasection_index),
225 updated_candidate));
226 if (!candidate_collection_[mediasection_index].HasCandidate(
zhihuang38989e52017-03-21 11:04:53 -0700227 updated_candidate_wrapper.get())) {
mallinath@webrtc.org67ee6b92014-02-03 16:57:16 +0000228 candidate_collection_[mediasection_index].add(
229 updated_candidate_wrapper.release());
zhihuang38989e52017-03-21 11:04:53 -0700230 UpdateConnectionAddress(
231 candidate_collection_[mediasection_index],
Steve Antonb1c1de12017-12-21 15:14:30 -0800232 description_->contents()[mediasection_index].media_description());
zhihuang38989e52017-03-21 11:04:53 -0700233 }
mallinath@webrtc.org67ee6b92014-02-03 16:57:16 +0000234
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000235 return true;
236}
237
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700238size_t JsepSessionDescription::RemoveCandidates(
239 const std::vector<cricket::Candidate>& candidates) {
240 size_t num_removed = 0;
241 for (auto& candidate : candidates) {
242 int mediasection_index = GetMediasectionIndex(candidate);
243 if (mediasection_index < 0) {
244 // Not found.
245 continue;
246 }
247 num_removed += candidate_collection_[mediasection_index].remove(candidate);
zhihuang38989e52017-03-21 11:04:53 -0700248 UpdateConnectionAddress(
249 candidate_collection_[mediasection_index],
Steve Antonb1c1de12017-12-21 15:14:30 -0800250 description_->contents()[mediasection_index].media_description());
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700251 }
252 return num_removed;
253}
254
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000255size_t JsepSessionDescription::number_of_mediasections() const {
256 if (!description_)
257 return 0;
258 return description_->contents().size();
259}
260
261const IceCandidateCollection* JsepSessionDescription::candidates(
262 size_t mediasection_index) const {
263 if (mediasection_index >= candidate_collection_.size())
264 return NULL;
265 return &candidate_collection_[mediasection_index];
266}
267
268bool JsepSessionDescription::ToString(std::string* out) const {
deadbeef9d3584c2016-02-16 17:54:10 -0800269 if (!description_ || !out) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000270 return false;
deadbeef9d3584c2016-02-16 17:54:10 -0800271 }
Steve Antone831b8c2018-02-01 12:22:16 -0800272 *out = SdpSerialize(*this);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000273 return !out->empty();
274}
275
276bool JsepSessionDescription::GetMediasectionIndex(
277 const IceCandidateInterface* candidate,
278 size_t* index) {
279 if (!candidate || !index) {
280 return false;
281 }
282 *index = static_cast<size_t>(candidate->sdp_mline_index());
283 if (description_ && !candidate->sdp_mid().empty()) {
284 bool found = false;
285 // Try to match the sdp_mid with content name.
286 for (size_t i = 0; i < description_->contents().size(); ++i) {
287 if (candidate->sdp_mid() == description_->contents().at(i).name) {
288 *index = i;
289 found = true;
290 break;
291 }
292 }
293 if (!found) {
294 // If the sdp_mid is presented but we can't find a match, we consider
295 // this as an error.
296 return false;
297 }
298 }
299 return true;
300}
301
Honghai Zhang7fb69db2016-03-14 11:59:18 -0700302int JsepSessionDescription::GetMediasectionIndex(
303 const cricket::Candidate& candidate) {
304 // Find the description with a matching transport name of the candidate.
305 const std::string& transport_name = candidate.transport_name();
306 for (size_t i = 0; i < description_->contents().size(); ++i) {
307 if (transport_name == description_->contents().at(i).name) {
308 return static_cast<int>(i);
309 }
310 }
311 return -1;
312}
313
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000314} // namespace webrtc