Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(250)

Side by Side Diff: webrtc/api/webrtcsessiondescriptionfactory.cc

Issue 2514883002: Create //webrtc/api:libjingle_peerconnection_api + refactorings. (Closed)
Patch Set: Rebase Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright 2013 The WebRTC project authors. All Rights Reserved.
3 *
4 * 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.
9 */
10
11 #include "webrtc/api/webrtcsessiondescriptionfactory.h"
12
13 #include <utility>
14
15 #include "webrtc/api/jsep.h"
16 #include "webrtc/api/jsepsessiondescription.h"
17 #include "webrtc/api/mediaconstraintsinterface.h"
18 #include "webrtc/api/webrtcsession.h"
19 #include "webrtc/base/sslidentity.h"
20
21 using cricket::MediaSessionOptions;
22
23 namespace webrtc {
24 namespace {
25 static const char kFailedDueToIdentityFailed[] =
26 " failed because DTLS identity request failed";
27 static const char kFailedDueToSessionShutdown[] =
28 " failed because the session was shut down";
29
30 static const uint64_t kInitSessionVersion = 2;
31
32 static bool CompareStream(const MediaSessionOptions::Stream& stream1,
33 const MediaSessionOptions::Stream& stream2) {
34 return stream1.id < stream2.id;
35 }
36
37 static bool SameId(const MediaSessionOptions::Stream& stream1,
38 const MediaSessionOptions::Stream& stream2) {
39 return stream1.id == stream2.id;
40 }
41
42 // Checks if each Stream within the |streams| has unique id.
43 static bool ValidStreams(const MediaSessionOptions::Streams& streams) {
44 MediaSessionOptions::Streams sorted_streams = streams;
45 std::sort(sorted_streams.begin(), sorted_streams.end(), CompareStream);
46 MediaSessionOptions::Streams::iterator it =
47 std::adjacent_find(sorted_streams.begin(), sorted_streams.end(),
48 SameId);
49 return it == sorted_streams.end();
50 }
51
52 enum {
53 MSG_CREATE_SESSIONDESCRIPTION_SUCCESS,
54 MSG_CREATE_SESSIONDESCRIPTION_FAILED,
55 MSG_USE_CONSTRUCTOR_CERTIFICATE
56 };
57
58 struct CreateSessionDescriptionMsg : public rtc::MessageData {
59 explicit CreateSessionDescriptionMsg(
60 webrtc::CreateSessionDescriptionObserver* observer)
61 : observer(observer) {
62 }
63
64 rtc::scoped_refptr<webrtc::CreateSessionDescriptionObserver> observer;
65 std::string error;
66 std::unique_ptr<webrtc::SessionDescriptionInterface> description;
67 };
68 } // namespace
69
70 void WebRtcCertificateGeneratorCallback::OnFailure() {
71 SignalRequestFailed();
72 }
73
74 void WebRtcCertificateGeneratorCallback::OnSuccess(
75 const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
76 SignalCertificateReady(certificate);
77 }
78
79 // static
80 void WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
81 const SessionDescriptionInterface* source_desc,
82 const std::string& content_name,
83 SessionDescriptionInterface* dest_desc) {
84 if (!source_desc) {
85 return;
86 }
87 const cricket::ContentInfos& contents =
88 source_desc->description()->contents();
89 const cricket::ContentInfo* cinfo =
90 source_desc->description()->GetContentByName(content_name);
91 if (!cinfo) {
92 return;
93 }
94 size_t mediasection_index = static_cast<int>(cinfo - &contents[0]);
95 const IceCandidateCollection* source_candidates =
96 source_desc->candidates(mediasection_index);
97 const IceCandidateCollection* dest_candidates =
98 dest_desc->candidates(mediasection_index);
99 if (!source_candidates || !dest_candidates) {
100 return;
101 }
102 for (size_t n = 0; n < source_candidates->count(); ++n) {
103 const IceCandidateInterface* new_candidate = source_candidates->at(n);
104 if (!dest_candidates->HasCandidate(new_candidate)) {
105 dest_desc->AddCandidate(source_candidates->at(n));
106 }
107 }
108 }
109
110 // Private constructor called by other constructors.
111 WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
112 rtc::Thread* signaling_thread,
113 cricket::ChannelManager* channel_manager,
114 WebRtcSession* session,
115 const std::string& session_id,
116 std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator,
117 const rtc::scoped_refptr<rtc::RTCCertificate>& certificate)
118 : signaling_thread_(signaling_thread),
119 session_desc_factory_(channel_manager, &transport_desc_factory_),
120 // RFC 4566 suggested a Network Time Protocol (NTP) format timestamp
121 // as the session id and session version. To simplify, it should be fine
122 // to just use a random number as session id and start version from
123 // |kInitSessionVersion|.
124 session_version_(kInitSessionVersion),
125 cert_generator_(std::move(cert_generator)),
126 session_(session),
127 session_id_(session_id),
128 certificate_request_state_(CERTIFICATE_NOT_NEEDED) {
129 RTC_DCHECK(signaling_thread_);
130 session_desc_factory_.set_add_legacy_streams(false);
131 bool dtls_enabled = cert_generator_ || certificate;
132 // SRTP-SDES is disabled if DTLS is on.
133 SetSdesPolicy(dtls_enabled ? cricket::SEC_DISABLED : cricket::SEC_REQUIRED);
134 if (!dtls_enabled) {
135 LOG(LS_VERBOSE) << "DTLS-SRTP disabled.";
136 return;
137 }
138
139 if (certificate) {
140 // Use |certificate|.
141 certificate_request_state_ = CERTIFICATE_WAITING;
142
143 LOG(LS_VERBOSE) << "DTLS-SRTP enabled; has certificate parameter.";
144 // We already have a certificate but we wait to do |SetIdentity|; if we do
145 // it in the constructor then the caller has not had a chance to connect to
146 // |SignalCertificateReady|.
147 signaling_thread_->Post(
148 RTC_FROM_HERE, this, MSG_USE_CONSTRUCTOR_CERTIFICATE,
149 new rtc::ScopedRefMessageData<rtc::RTCCertificate>(certificate));
150 } else {
151 // Generate certificate.
152 certificate_request_state_ = CERTIFICATE_WAITING;
153
154 rtc::scoped_refptr<WebRtcCertificateGeneratorCallback> callback(
155 new rtc::RefCountedObject<WebRtcCertificateGeneratorCallback>());
156 callback->SignalRequestFailed.connect(
157 this, &WebRtcSessionDescriptionFactory::OnCertificateRequestFailed);
158 callback->SignalCertificateReady.connect(
159 this, &WebRtcSessionDescriptionFactory::SetCertificate);
160
161 rtc::KeyParams key_params = rtc::KeyParams();
162 LOG(LS_VERBOSE) << "DTLS-SRTP enabled; sending DTLS identity request (key "
163 << "type: " << key_params.type() << ").";
164
165 // Request certificate. This happens asynchronously, so that the caller gets
166 // a chance to connect to |SignalCertificateReady|.
167 cert_generator_->GenerateCertificateAsync(
168 key_params, rtc::Optional<uint64_t>(), callback);
169 }
170 }
171
172 WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
173 rtc::Thread* signaling_thread,
174 cricket::ChannelManager* channel_manager,
175 WebRtcSession* session,
176 const std::string& session_id,
177 std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator)
178 : WebRtcSessionDescriptionFactory(
179 signaling_thread,
180 channel_manager,
181 session,
182 session_id,
183 std::move(cert_generator),
184 nullptr) {
185 }
186
187 WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
188 rtc::Thread* signaling_thread,
189 cricket::ChannelManager* channel_manager,
190 WebRtcSession* session,
191 const std::string& session_id,
192 const rtc::scoped_refptr<rtc::RTCCertificate>& certificate)
193 : WebRtcSessionDescriptionFactory(signaling_thread,
194 channel_manager,
195 session,
196 session_id,
197 nullptr,
198 certificate) {
199 RTC_DCHECK(certificate);
200 }
201
202 WebRtcSessionDescriptionFactory::~WebRtcSessionDescriptionFactory() {
203 ASSERT(signaling_thread_->IsCurrent());
204
205 // Fail any requests that were asked for before identity generation completed.
206 FailPendingRequests(kFailedDueToSessionShutdown);
207
208 // Process all pending notifications in the message queue. If we don't do
209 // this, requests will linger and not know they succeeded or failed.
210 rtc::MessageList list;
211 signaling_thread_->Clear(this, rtc::MQID_ANY, &list);
212 for (auto& msg : list) {
213 if (msg.message_id != MSG_USE_CONSTRUCTOR_CERTIFICATE) {
214 OnMessage(&msg);
215 } else {
216 // Skip MSG_USE_CONSTRUCTOR_CERTIFICATE because we don't want to trigger
217 // SetIdentity-related callbacks in the destructor. This can be a problem
218 // when WebRtcSession listens to the callback but it was the WebRtcSession
219 // destructor that caused WebRtcSessionDescriptionFactory's destruction.
220 // The callback is then ignored, leaking memory allocated by OnMessage for
221 // MSG_USE_CONSTRUCTOR_CERTIFICATE.
222 delete msg.pdata;
223 }
224 }
225 }
226
227 void WebRtcSessionDescriptionFactory::CreateOffer(
228 CreateSessionDescriptionObserver* observer,
229 const PeerConnectionInterface::RTCOfferAnswerOptions& options,
230 const cricket::MediaSessionOptions& session_options) {
231 std::string error = "CreateOffer";
232 if (certificate_request_state_ == CERTIFICATE_FAILED) {
233 error += kFailedDueToIdentityFailed;
234 LOG(LS_ERROR) << error;
235 PostCreateSessionDescriptionFailed(observer, error);
236 return;
237 }
238
239 if (!ValidStreams(session_options.streams)) {
240 error += " called with invalid media streams.";
241 LOG(LS_ERROR) << error;
242 PostCreateSessionDescriptionFailed(observer, error);
243 return;
244 }
245
246 CreateSessionDescriptionRequest request(
247 CreateSessionDescriptionRequest::kOffer, observer, session_options);
248 if (certificate_request_state_ == CERTIFICATE_WAITING) {
249 create_session_description_requests_.push(request);
250 } else {
251 ASSERT(certificate_request_state_ == CERTIFICATE_SUCCEEDED ||
252 certificate_request_state_ == CERTIFICATE_NOT_NEEDED);
253 InternalCreateOffer(request);
254 }
255 }
256
257 void WebRtcSessionDescriptionFactory::CreateAnswer(
258 CreateSessionDescriptionObserver* observer,
259 const cricket::MediaSessionOptions& session_options) {
260 std::string error = "CreateAnswer";
261 if (certificate_request_state_ == CERTIFICATE_FAILED) {
262 error += kFailedDueToIdentityFailed;
263 LOG(LS_ERROR) << error;
264 PostCreateSessionDescriptionFailed(observer, error);
265 return;
266 }
267 if (!session_->remote_description()) {
268 error += " can't be called before SetRemoteDescription.";
269 LOG(LS_ERROR) << error;
270 PostCreateSessionDescriptionFailed(observer, error);
271 return;
272 }
273 if (session_->remote_description()->type() !=
274 JsepSessionDescription::kOffer) {
275 error += " failed because remote_description is not an offer.";
276 LOG(LS_ERROR) << error;
277 PostCreateSessionDescriptionFailed(observer, error);
278 return;
279 }
280
281 if (!ValidStreams(session_options.streams)) {
282 error += " called with invalid media streams.";
283 LOG(LS_ERROR) << error;
284 PostCreateSessionDescriptionFailed(observer, error);
285 return;
286 }
287
288 CreateSessionDescriptionRequest request(
289 CreateSessionDescriptionRequest::kAnswer, observer, session_options);
290 if (certificate_request_state_ == CERTIFICATE_WAITING) {
291 create_session_description_requests_.push(request);
292 } else {
293 ASSERT(certificate_request_state_ == CERTIFICATE_SUCCEEDED ||
294 certificate_request_state_ == CERTIFICATE_NOT_NEEDED);
295 InternalCreateAnswer(request);
296 }
297 }
298
299 void WebRtcSessionDescriptionFactory::SetSdesPolicy(
300 cricket::SecurePolicy secure_policy) {
301 session_desc_factory_.set_secure(secure_policy);
302 }
303
304 cricket::SecurePolicy WebRtcSessionDescriptionFactory::SdesPolicy() const {
305 return session_desc_factory_.secure();
306 }
307
308 void WebRtcSessionDescriptionFactory::OnMessage(rtc::Message* msg) {
309 switch (msg->message_id) {
310 case MSG_CREATE_SESSIONDESCRIPTION_SUCCESS: {
311 CreateSessionDescriptionMsg* param =
312 static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
313 param->observer->OnSuccess(param->description.release());
314 delete param;
315 break;
316 }
317 case MSG_CREATE_SESSIONDESCRIPTION_FAILED: {
318 CreateSessionDescriptionMsg* param =
319 static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
320 param->observer->OnFailure(param->error);
321 delete param;
322 break;
323 }
324 case MSG_USE_CONSTRUCTOR_CERTIFICATE: {
325 rtc::ScopedRefMessageData<rtc::RTCCertificate>* param =
326 static_cast<rtc::ScopedRefMessageData<rtc::RTCCertificate>*>(
327 msg->pdata);
328 LOG(LS_INFO) << "Using certificate supplied to the constructor.";
329 SetCertificate(param->data());
330 delete param;
331 break;
332 }
333 default:
334 ASSERT(false);
335 break;
336 }
337 }
338
339 void WebRtcSessionDescriptionFactory::InternalCreateOffer(
340 CreateSessionDescriptionRequest request) {
341 if (session_->local_description()) {
342 for (const cricket::TransportInfo& transport :
343 session_->local_description()->description()->transport_infos()) {
344 // If the needs-ice-restart flag is set as described by JSEP, we should
345 // generate an offer with a new ufrag/password to trigger an ICE restart.
346 if (session_->NeedsIceRestart(transport.content_name)) {
347 request.options.transport_options[transport.content_name].ice_restart =
348 true;
349 }
350 }
351 }
352
353 cricket::SessionDescription* desc(session_desc_factory_.CreateOffer(
354 request.options, session_->local_description()
355 ? session_->local_description()->description()
356 : nullptr));
357 // RFC 3264
358 // When issuing an offer that modifies the session,
359 // the "o=" line of the new SDP MUST be identical to that in the
360 // previous SDP, except that the version in the origin field MUST
361 // increment by one from the previous SDP.
362
363 // Just increase the version number by one each time when a new offer
364 // is created regardless if it's identical to the previous one or not.
365 // The |session_version_| is a uint64_t, the wrap around should not happen.
366 ASSERT(session_version_ + 1 > session_version_);
367 JsepSessionDescription* offer(new JsepSessionDescription(
368 JsepSessionDescription::kOffer));
369 if (!offer->Initialize(desc, session_id_,
370 rtc::ToString(session_version_++))) {
371 delete offer;
372 PostCreateSessionDescriptionFailed(request.observer,
373 "Failed to initialize the offer.");
374 return;
375 }
376 if (session_->local_description()) {
377 for (const cricket::ContentInfo& content :
378 session_->local_description()->description()->contents()) {
379 // Include all local ICE candidates in the SessionDescription unless
380 // the remote peer has requested an ICE restart.
381 if (!request.options.transport_options[content.name].ice_restart) {
382 CopyCandidatesFromSessionDescription(session_->local_description(),
383 content.name, offer);
384 }
385 }
386 }
387 PostCreateSessionDescriptionSucceeded(request.observer, offer);
388 }
389
390 void WebRtcSessionDescriptionFactory::InternalCreateAnswer(
391 CreateSessionDescriptionRequest request) {
392 if (session_->remote_description()) {
393 for (const cricket::ContentInfo& content :
394 session_->remote_description()->description()->contents()) {
395 // According to http://tools.ietf.org/html/rfc5245#section-9.2.1.1
396 // an answer should also contain new ICE ufrag and password if an offer
397 // has been received with new ufrag and password.
398 request.options.transport_options[content.name].ice_restart =
399 session_->IceRestartPending(content.name);
400 // We should pass the current SSL role to the transport description
401 // factory, if there is already an existing ongoing session.
402 rtc::SSLRole ssl_role;
403 if (session_->GetSslRole(session_->GetChannel(content.name), &ssl_role)) {
404 request.options.transport_options[content.name].prefer_passive_role =
405 (rtc::SSL_SERVER == ssl_role);
406 }
407 }
408 }
409
410 cricket::SessionDescription* desc(session_desc_factory_.CreateAnswer(
411 session_->remote_description()
412 ? session_->remote_description()->description()
413 : nullptr,
414 request.options, session_->local_description()
415 ? session_->local_description()->description()
416 : nullptr));
417 // RFC 3264
418 // If the answer is different from the offer in any way (different IP
419 // addresses, ports, etc.), the origin line MUST be different in the answer.
420 // In that case, the version number in the "o=" line of the answer is
421 // unrelated to the version number in the o line of the offer.
422 // Get a new version number by increasing the |session_version_answer_|.
423 // The |session_version_| is a uint64_t, the wrap around should not happen.
424 ASSERT(session_version_ + 1 > session_version_);
425 JsepSessionDescription* answer(new JsepSessionDescription(
426 JsepSessionDescription::kAnswer));
427 if (!answer->Initialize(desc, session_id_,
428 rtc::ToString(session_version_++))) {
429 delete answer;
430 PostCreateSessionDescriptionFailed(request.observer,
431 "Failed to initialize the answer.");
432 return;
433 }
434 if (session_->local_description()) {
435 for (const cricket::ContentInfo& content :
436 session_->local_description()->description()->contents()) {
437 // Include all local ICE candidates in the SessionDescription unless
438 // the remote peer has requested an ICE restart.
439 if (!request.options.transport_options[content.name].ice_restart) {
440 CopyCandidatesFromSessionDescription(session_->local_description(),
441 content.name, answer);
442 }
443 }
444 }
445 PostCreateSessionDescriptionSucceeded(request.observer, answer);
446 }
447
448 void WebRtcSessionDescriptionFactory::FailPendingRequests(
449 const std::string& reason) {
450 ASSERT(signaling_thread_->IsCurrent());
451 while (!create_session_description_requests_.empty()) {
452 const CreateSessionDescriptionRequest& request =
453 create_session_description_requests_.front();
454 PostCreateSessionDescriptionFailed(request.observer,
455 ((request.type == CreateSessionDescriptionRequest::kOffer) ?
456 "CreateOffer" : "CreateAnswer") + reason);
457 create_session_description_requests_.pop();
458 }
459 }
460
461 void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionFailed(
462 CreateSessionDescriptionObserver* observer, const std::string& error) {
463 CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
464 msg->error = error;
465 signaling_thread_->Post(RTC_FROM_HERE, this,
466 MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg);
467 LOG(LS_ERROR) << "Create SDP failed: " << error;
468 }
469
470 void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionSucceeded(
471 CreateSessionDescriptionObserver* observer,
472 SessionDescriptionInterface* description) {
473 CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
474 msg->description.reset(description);
475 signaling_thread_->Post(RTC_FROM_HERE, this,
476 MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, msg);
477 }
478
479 void WebRtcSessionDescriptionFactory::OnCertificateRequestFailed() {
480 ASSERT(signaling_thread_->IsCurrent());
481
482 LOG(LS_ERROR) << "Asynchronous certificate generation request failed.";
483 certificate_request_state_ = CERTIFICATE_FAILED;
484
485 FailPendingRequests(kFailedDueToIdentityFailed);
486 }
487
488 void WebRtcSessionDescriptionFactory::SetCertificate(
489 const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
490 RTC_DCHECK(certificate);
491 LOG(LS_VERBOSE) << "Setting new certificate.";
492
493 certificate_request_state_ = CERTIFICATE_SUCCEEDED;
494 SignalCertificateReady(certificate);
495
496 transport_desc_factory_.set_certificate(certificate);
497 transport_desc_factory_.set_secure(cricket::SEC_ENABLED);
498
499 while (!create_session_description_requests_.empty()) {
500 if (create_session_description_requests_.front().type ==
501 CreateSessionDescriptionRequest::kOffer) {
502 InternalCreateOffer(create_session_description_requests_.front());
503 } else {
504 InternalCreateAnswer(create_session_description_requests_.front());
505 }
506 create_session_description_requests_.pop();
507 }
508 }
509 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698