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

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

Issue 2514883002: Create //webrtc/api:libjingle_peerconnection_api + refactorings. (Closed)
Patch Set: Added three headers for backwards-compatibility, specifically for building chromium. 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 cricket::SessionDescription* desc(session_desc_factory_.CreateOffer(
342 request.options, session_->local_description()
343 ? session_->local_description()->description()
344 : nullptr));
345 // RFC 3264
346 // When issuing an offer that modifies the session,
347 // the "o=" line of the new SDP MUST be identical to that in the
348 // previous SDP, except that the version in the origin field MUST
349 // increment by one from the previous SDP.
350
351 // Just increase the version number by one each time when a new offer
352 // is created regardless if it's identical to the previous one or not.
353 // The |session_version_| is a uint64_t, the wrap around should not happen.
354 ASSERT(session_version_ + 1 > session_version_);
355 JsepSessionDescription* offer(new JsepSessionDescription(
356 JsepSessionDescription::kOffer));
357 if (!offer->Initialize(desc, session_id_,
358 rtc::ToString(session_version_++))) {
359 delete offer;
360 PostCreateSessionDescriptionFailed(request.observer,
361 "Failed to initialize the offer.");
362 return;
363 }
364 if (session_->local_description()) {
365 for (const cricket::ContentInfo& content :
366 session_->local_description()->description()->contents()) {
367 // Include all local ICE candidates in the SessionDescription unless
368 // the remote peer has requested an ICE restart.
369 if (!request.options.transport_options[content.name].ice_restart) {
370 CopyCandidatesFromSessionDescription(session_->local_description(),
371 content.name, offer);
372 }
373 }
374 }
375 PostCreateSessionDescriptionSucceeded(request.observer, offer);
376 }
377
378 void WebRtcSessionDescriptionFactory::InternalCreateAnswer(
379 CreateSessionDescriptionRequest request) {
380 if (session_->remote_description()) {
381 for (const cricket::ContentInfo& content :
382 session_->remote_description()->description()->contents()) {
383 // According to http://tools.ietf.org/html/rfc5245#section-9.2.1.1
384 // an answer should also contain new ICE ufrag and password if an offer
385 // has been received with new ufrag and password.
386 request.options.transport_options[content.name].ice_restart =
387 session_->IceRestartPending(content.name);
388 // We should pass the current SSL role to the transport description
389 // factory, if there is already an existing ongoing session.
390 rtc::SSLRole ssl_role;
391 if (session_->GetSslRole(session_->GetChannel(content.name), &ssl_role)) {
392 request.options.transport_options[content.name].prefer_passive_role =
393 (rtc::SSL_SERVER == ssl_role);
394 }
395 }
396 }
397
398 cricket::SessionDescription* desc(session_desc_factory_.CreateAnswer(
399 session_->remote_description()
400 ? session_->remote_description()->description()
401 : nullptr,
402 request.options, session_->local_description()
403 ? session_->local_description()->description()
404 : nullptr));
405 // RFC 3264
406 // If the answer is different from the offer in any way (different IP
407 // addresses, ports, etc.), the origin line MUST be different in the answer.
408 // In that case, the version number in the "o=" line of the answer is
409 // unrelated to the version number in the o line of the offer.
410 // Get a new version number by increasing the |session_version_answer_|.
411 // The |session_version_| is a uint64_t, the wrap around should not happen.
412 ASSERT(session_version_ + 1 > session_version_);
413 JsepSessionDescription* answer(new JsepSessionDescription(
414 JsepSessionDescription::kAnswer));
415 if (!answer->Initialize(desc, session_id_,
416 rtc::ToString(session_version_++))) {
417 delete answer;
418 PostCreateSessionDescriptionFailed(request.observer,
419 "Failed to initialize the answer.");
420 return;
421 }
422 if (session_->local_description()) {
423 for (const cricket::ContentInfo& content :
424 session_->local_description()->description()->contents()) {
425 // Include all local ICE candidates in the SessionDescription unless
426 // the remote peer has requested an ICE restart.
427 if (!request.options.transport_options[content.name].ice_restart) {
428 CopyCandidatesFromSessionDescription(session_->local_description(),
429 content.name, answer);
430 }
431 }
432 }
433 PostCreateSessionDescriptionSucceeded(request.observer, answer);
434 }
435
436 void WebRtcSessionDescriptionFactory::FailPendingRequests(
437 const std::string& reason) {
438 ASSERT(signaling_thread_->IsCurrent());
439 while (!create_session_description_requests_.empty()) {
440 const CreateSessionDescriptionRequest& request =
441 create_session_description_requests_.front();
442 PostCreateSessionDescriptionFailed(request.observer,
443 ((request.type == CreateSessionDescriptionRequest::kOffer) ?
444 "CreateOffer" : "CreateAnswer") + reason);
445 create_session_description_requests_.pop();
446 }
447 }
448
449 void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionFailed(
450 CreateSessionDescriptionObserver* observer, const std::string& error) {
451 CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
452 msg->error = error;
453 signaling_thread_->Post(RTC_FROM_HERE, this,
454 MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg);
455 LOG(LS_ERROR) << "Create SDP failed: " << error;
456 }
457
458 void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionSucceeded(
459 CreateSessionDescriptionObserver* observer,
460 SessionDescriptionInterface* description) {
461 CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
462 msg->description.reset(description);
463 signaling_thread_->Post(RTC_FROM_HERE, this,
464 MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, msg);
465 }
466
467 void WebRtcSessionDescriptionFactory::OnCertificateRequestFailed() {
468 ASSERT(signaling_thread_->IsCurrent());
469
470 LOG(LS_ERROR) << "Asynchronous certificate generation request failed.";
471 certificate_request_state_ = CERTIFICATE_FAILED;
472
473 FailPendingRequests(kFailedDueToIdentityFailed);
474 }
475
476 void WebRtcSessionDescriptionFactory::SetCertificate(
477 const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
478 RTC_DCHECK(certificate);
479 LOG(LS_VERBOSE) << "Setting new certificate.";
480
481 certificate_request_state_ = CERTIFICATE_SUCCEEDED;
482 SignalCertificateReady(certificate);
483
484 transport_desc_factory_.set_certificate(certificate);
485 transport_desc_factory_.set_secure(cricket::SEC_ENABLED);
486
487 while (!create_session_description_requests_.empty()) {
488 if (create_session_description_requests_.front().type ==
489 CreateSessionDescriptionRequest::kOffer) {
490 InternalCreateOffer(create_session_description_requests_.front());
491 } else {
492 InternalCreateAnswer(create_session_description_requests_.front());
493 }
494 create_session_description_requests_.pop();
495 }
496 }
497 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698