blob: 3dce0d38995bd2a909453efe44d6bbab5847a692 [file] [log] [blame]
wu@webrtc.org91053e72013-08-10 07:18:04 +00001/*
2 * libjingle
3 * Copyright 2013, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/app/webrtc/webrtcsessiondescriptionfactory.h"
29
30#include "talk/app/webrtc/jsep.h"
31#include "talk/app/webrtc/jsepsessiondescription.h"
32#include "talk/app/webrtc/mediaconstraintsinterface.h"
33#include "talk/app/webrtc/mediastreamsignaling.h"
34#include "talk/app/webrtc/webrtcsession.h"
35
wu@webrtc.org364f2042013-11-20 21:49:41 +000036using cricket::MediaSessionOptions;
37
wu@webrtc.org91053e72013-08-10 07:18:04 +000038namespace webrtc {
wu@webrtc.org91053e72013-08-10 07:18:04 +000039namespace {
wu@webrtc.org91053e72013-08-10 07:18:04 +000040static const char kFailedDueToIdentityFailed[] =
41 " failed because DTLS identity request failed";
42
43// Arbitrary constant used as common name for the identity.
44// Chosen to make the certificates more readable.
45static const char kWebRTCIdentityName[] = "WebRTC";
46
47static const uint64 kInitSessionVersion = 2;
48
wu@webrtc.org364f2042013-11-20 21:49:41 +000049static bool CompareStream(const MediaSessionOptions::Stream& stream1,
50 const MediaSessionOptions::Stream& stream2) {
51 return stream1.id < stream2.id;
wu@webrtc.org91053e72013-08-10 07:18:04 +000052}
53
wu@webrtc.org364f2042013-11-20 21:49:41 +000054static bool SameId(const MediaSessionOptions::Stream& stream1,
55 const MediaSessionOptions::Stream& stream2) {
56 return stream1.id == stream2.id;
wu@webrtc.org91053e72013-08-10 07:18:04 +000057}
58
59// Checks if each Stream within the |streams| has unique id.
wu@webrtc.org364f2042013-11-20 21:49:41 +000060static bool ValidStreams(const MediaSessionOptions::Streams& streams) {
61 MediaSessionOptions::Streams sorted_streams = streams;
wu@webrtc.org91053e72013-08-10 07:18:04 +000062 std::sort(sorted_streams.begin(), sorted_streams.end(), CompareStream);
wu@webrtc.org364f2042013-11-20 21:49:41 +000063 MediaSessionOptions::Streams::iterator it =
wu@webrtc.org91053e72013-08-10 07:18:04 +000064 std::adjacent_find(sorted_streams.begin(), sorted_streams.end(),
65 SameId);
wu@webrtc.org364f2042013-11-20 21:49:41 +000066 return it == sorted_streams.end();
wu@webrtc.org91053e72013-08-10 07:18:04 +000067}
68
69enum {
70 MSG_CREATE_SESSIONDESCRIPTION_SUCCESS,
71 MSG_CREATE_SESSIONDESCRIPTION_FAILED,
72 MSG_GENERATE_IDENTITY,
73};
74
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000075struct CreateSessionDescriptionMsg : public rtc::MessageData {
wu@webrtc.org91053e72013-08-10 07:18:04 +000076 explicit CreateSessionDescriptionMsg(
77 webrtc::CreateSessionDescriptionObserver* observer)
78 : observer(observer) {
79 }
80
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000081 rtc::scoped_refptr<webrtc::CreateSessionDescriptionObserver> observer;
wu@webrtc.org91053e72013-08-10 07:18:04 +000082 std::string error;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000083 rtc::scoped_ptr<webrtc::SessionDescriptionInterface> description;
wu@webrtc.org91053e72013-08-10 07:18:04 +000084};
wu@webrtc.org91053e72013-08-10 07:18:04 +000085} // namespace
86
87// static
88void WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
89 const SessionDescriptionInterface* source_desc,
90 SessionDescriptionInterface* dest_desc) {
91 if (!source_desc)
92 return;
93 for (size_t m = 0; m < source_desc->number_of_mediasections() &&
94 m < dest_desc->number_of_mediasections(); ++m) {
95 const IceCandidateCollection* source_candidates =
96 source_desc->candidates(m);
97 const IceCandidateCollection* dest_candidates = dest_desc->candidates(m);
98 for (size_t n = 0; n < source_candidates->count(); ++n) {
99 const IceCandidateInterface* new_candidate = source_candidates->at(n);
100 if (!dest_candidates->HasCandidate(new_candidate))
101 dest_desc->AddCandidate(source_candidates->at(n));
102 }
103 }
104}
105
106WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000107 rtc::Thread* signaling_thread,
wu@webrtc.org91053e72013-08-10 07:18:04 +0000108 cricket::ChannelManager* channel_manager,
109 MediaStreamSignaling* mediastream_signaling,
110 DTLSIdentityServiceInterface* dtls_identity_service,
111 WebRtcSession* session,
112 const std::string& session_id,
113 cricket::DataChannelType dct,
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000114 bool dtls_enabled)
wu@webrtc.org91053e72013-08-10 07:18:04 +0000115 : signaling_thread_(signaling_thread),
116 mediastream_signaling_(mediastream_signaling),
117 session_desc_factory_(channel_manager, &transport_desc_factory_),
118 // RFC 4566 suggested a Network Time Protocol (NTP) format timestamp
119 // as the session id and session version. To simplify, it should be fine
120 // to just use a random number as session id and start version from
121 // |kInitSessionVersion|.
122 session_version_(kInitSessionVersion),
123 identity_service_(dtls_identity_service),
124 session_(session),
125 session_id_(session_id),
126 data_channel_type_(dct),
127 identity_request_state_(IDENTITY_NOT_NEEDED) {
128 transport_desc_factory_.set_protocol(cricket::ICEPROTO_HYBRID);
129 session_desc_factory_.set_add_legacy_streams(false);
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000130 // SRTP-SDES is disabled if DTLS is on.
131 SetSdesPolicy(dtls_enabled ? cricket::SEC_DISABLED : cricket::SEC_REQUIRED);
wu@webrtc.org91053e72013-08-10 07:18:04 +0000132
wu@webrtc.org364f2042013-11-20 21:49:41 +0000133 if (!dtls_enabled) {
134 return;
135 }
wu@webrtc.org91053e72013-08-10 07:18:04 +0000136
wu@webrtc.org364f2042013-11-20 21:49:41 +0000137 if (identity_service_.get()) {
138 identity_request_observer_ =
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000139 new rtc::RefCountedObject<WebRtcIdentityRequestObserver>();
wu@webrtc.org91053e72013-08-10 07:18:04 +0000140
wu@webrtc.org364f2042013-11-20 21:49:41 +0000141 identity_request_observer_->SignalRequestFailed.connect(
142 this, &WebRtcSessionDescriptionFactory::OnIdentityRequestFailed);
143 identity_request_observer_->SignalIdentityReady.connect(
144 this, &WebRtcSessionDescriptionFactory::OnIdentityReady);
145
146 if (identity_service_->RequestIdentity(kWebRTCIdentityName,
147 kWebRTCIdentityName,
148 identity_request_observer_)) {
149 LOG(LS_VERBOSE) << "DTLS-SRTP enabled; sent DTLS identity request.";
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000150 identity_request_state_ = IDENTITY_WAITING;
wu@webrtc.org364f2042013-11-20 21:49:41 +0000151 } else {
152 LOG(LS_ERROR) << "Failed to send DTLS identity request.";
153 identity_request_state_ = IDENTITY_FAILED;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000154 }
wu@webrtc.org364f2042013-11-20 21:49:41 +0000155 } else {
156 identity_request_state_ = IDENTITY_WAITING;
157 // Do not generate the identity in the constructor since the caller has
158 // not got a chance to connect to SignalIdentityReady.
159 signaling_thread_->Post(this, MSG_GENERATE_IDENTITY, NULL);
wu@webrtc.org91053e72013-08-10 07:18:04 +0000160 }
161}
162
163WebRtcSessionDescriptionFactory::~WebRtcSessionDescriptionFactory() {
164 transport_desc_factory_.set_identity(NULL);
165}
166
167void WebRtcSessionDescriptionFactory::CreateOffer(
168 CreateSessionDescriptionObserver* observer,
169 const MediaConstraintsInterface* constraints) {
170 cricket::MediaSessionOptions options;
171 std::string error = "CreateOffer";
172 if (identity_request_state_ == IDENTITY_FAILED) {
173 error += kFailedDueToIdentityFailed;
174 LOG(LS_ERROR) << error;
175 PostCreateSessionDescriptionFailed(observer, error);
176 return;
177 }
178
179 if (!mediastream_signaling_->GetOptionsForOffer(constraints, &options)) {
180 error += " called with invalid constraints.";
181 LOG(LS_ERROR) << error;
182 PostCreateSessionDescriptionFailed(observer, error);
183 return;
184 }
185
186 if (!ValidStreams(options.streams)) {
187 error += " called with invalid media streams.";
188 LOG(LS_ERROR) << error;
189 PostCreateSessionDescriptionFailed(observer, error);
190 return;
191 }
192
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000193 if (data_channel_type_ == cricket::DCT_SCTP &&
194 mediastream_signaling_->HasDataChannels()) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000195 options.data_channel_type = cricket::DCT_SCTP;
196 }
197
198 CreateSessionDescriptionRequest request(
199 CreateSessionDescriptionRequest::kOffer, observer, options);
200 if (identity_request_state_ == IDENTITY_WAITING) {
201 create_session_description_requests_.push(request);
202 } else {
203 ASSERT(identity_request_state_ == IDENTITY_SUCCEEDED ||
204 identity_request_state_ == IDENTITY_NOT_NEEDED);
205 InternalCreateOffer(request);
206 }
207}
208
209void WebRtcSessionDescriptionFactory::CreateAnswer(
210 CreateSessionDescriptionObserver* observer,
211 const MediaConstraintsInterface* constraints) {
212 std::string error = "CreateAnswer";
213 if (identity_request_state_ == IDENTITY_FAILED) {
214 error += kFailedDueToIdentityFailed;
215 LOG(LS_ERROR) << error;
216 PostCreateSessionDescriptionFailed(observer, error);
217 return;
218 }
219 if (!session_->remote_description()) {
220 error += " can't be called before SetRemoteDescription.";
221 LOG(LS_ERROR) << error;
222 PostCreateSessionDescriptionFailed(observer, error);
223 return;
224 }
225 if (session_->remote_description()->type() !=
226 JsepSessionDescription::kOffer) {
227 error += " failed because remote_description is not an offer.";
228 LOG(LS_ERROR) << error;
229 PostCreateSessionDescriptionFailed(observer, error);
230 return;
231 }
232
233 cricket::MediaSessionOptions options;
234 if (!mediastream_signaling_->GetOptionsForAnswer(constraints, &options)) {
235 error += " called with invalid constraints.";
236 LOG(LS_ERROR) << error;
237 PostCreateSessionDescriptionFailed(observer, error);
238 return;
239 }
240 if (!ValidStreams(options.streams)) {
241 error += " called with invalid media streams.";
242 LOG(LS_ERROR) << error;
243 PostCreateSessionDescriptionFailed(observer, error);
244 return;
245 }
mallinath@webrtc.org1112c302013-09-23 20:34:45 +0000246 // RTP data channel is handled in MediaSessionOptions::AddStream. SCTP streams
247 // are not signaled in the SDP so does not go through that path and must be
248 // handled here.
249 if (data_channel_type_ == cricket::DCT_SCTP) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000250 options.data_channel_type = cricket::DCT_SCTP;
251 }
252
253 CreateSessionDescriptionRequest request(
254 CreateSessionDescriptionRequest::kAnswer, observer, options);
255 if (identity_request_state_ == IDENTITY_WAITING) {
256 create_session_description_requests_.push(request);
257 } else {
258 ASSERT(identity_request_state_ == IDENTITY_SUCCEEDED ||
259 identity_request_state_ == IDENTITY_NOT_NEEDED);
260 InternalCreateAnswer(request);
261 }
262}
263
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000264void WebRtcSessionDescriptionFactory::SetSdesPolicy(
265 cricket::SecurePolicy secure_policy) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000266 session_desc_factory_.set_secure(secure_policy);
267}
268
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000269cricket::SecurePolicy WebRtcSessionDescriptionFactory::SdesPolicy() const {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000270 return session_desc_factory_.secure();
271}
272
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000273void WebRtcSessionDescriptionFactory::OnMessage(rtc::Message* msg) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000274 switch (msg->message_id) {
275 case MSG_CREATE_SESSIONDESCRIPTION_SUCCESS: {
276 CreateSessionDescriptionMsg* param =
277 static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
278 param->observer->OnSuccess(param->description.release());
279 delete param;
280 break;
281 }
282 case MSG_CREATE_SESSIONDESCRIPTION_FAILED: {
283 CreateSessionDescriptionMsg* param =
284 static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
285 param->observer->OnFailure(param->error);
286 delete param;
287 break;
288 }
289 case MSG_GENERATE_IDENTITY: {
290 LOG(LS_INFO) << "Generating identity.";
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000291 SetIdentity(rtc::SSLIdentity::Generate(kWebRTCIdentityName));
wu@webrtc.org91053e72013-08-10 07:18:04 +0000292 break;
293 }
294 default:
295 ASSERT(false);
296 break;
297 }
298}
299
300void WebRtcSessionDescriptionFactory::InternalCreateOffer(
301 CreateSessionDescriptionRequest request) {
302 cricket::SessionDescription* desc(
303 session_desc_factory_.CreateOffer(
304 request.options,
305 static_cast<cricket::BaseSession*>(session_)->local_description()));
306 // RFC 3264
307 // When issuing an offer that modifies the session,
308 // the "o=" line of the new SDP MUST be identical to that in the
309 // previous SDP, except that the version in the origin field MUST
310 // increment by one from the previous SDP.
311
312 // Just increase the version number by one each time when a new offer
313 // is created regardless if it's identical to the previous one or not.
314 // The |session_version_| is a uint64, the wrap around should not happen.
315 ASSERT(session_version_ + 1 > session_version_);
316 JsepSessionDescription* offer(new JsepSessionDescription(
317 JsepSessionDescription::kOffer));
318 if (!offer->Initialize(desc, session_id_,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000319 rtc::ToString(session_version_++))) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000320 delete offer;
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000321 PostCreateSessionDescriptionFailed(request.observer,
322 "Failed to initialize the offer.");
wu@webrtc.org91053e72013-08-10 07:18:04 +0000323 return;
324 }
325 if (session_->local_description() &&
326 !request.options.transport_options.ice_restart) {
327 // Include all local ice candidates in the SessionDescription unless
328 // the an ice restart has been requested.
329 CopyCandidatesFromSessionDescription(session_->local_description(), offer);
330 }
331 PostCreateSessionDescriptionSucceeded(request.observer, offer);
332}
333
334void WebRtcSessionDescriptionFactory::InternalCreateAnswer(
335 CreateSessionDescriptionRequest request) {
336 // According to http://tools.ietf.org/html/rfc5245#section-9.2.1.1
337 // an answer should also contain new ice ufrag and password if an offer has
338 // been received with new ufrag and password.
339 request.options.transport_options.ice_restart = session_->IceRestartPending();
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000340 // We should pass current ssl role to the transport description factory, if
341 // there is already an existing ongoing session.
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000342 rtc::SSLRole ssl_role;
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000343 if (session_->GetSslRole(&ssl_role)) {
344 request.options.transport_options.prefer_passive_role =
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000345 (rtc::SSL_SERVER == ssl_role);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000346 }
wu@webrtc.org91053e72013-08-10 07:18:04 +0000347
348 cricket::SessionDescription* desc(session_desc_factory_.CreateAnswer(
349 static_cast<cricket::BaseSession*>(session_)->remote_description(),
350 request.options,
351 static_cast<cricket::BaseSession*>(session_)->local_description()));
352 // RFC 3264
353 // If the answer is different from the offer in any way (different IP
354 // addresses, ports, etc.), the origin line MUST be different in the answer.
355 // In that case, the version number in the "o=" line of the answer is
356 // unrelated to the version number in the o line of the offer.
357 // Get a new version number by increasing the |session_version_answer_|.
358 // The |session_version_| is a uint64, the wrap around should not happen.
359 ASSERT(session_version_ + 1 > session_version_);
360 JsepSessionDescription* answer(new JsepSessionDescription(
361 JsepSessionDescription::kAnswer));
362 if (!answer->Initialize(desc, session_id_,
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000363 rtc::ToString(session_version_++))) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000364 delete answer;
365 PostCreateSessionDescriptionFailed(request.observer,
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000366 "Failed to initialize the answer.");
wu@webrtc.org91053e72013-08-10 07:18:04 +0000367 return;
368 }
369 if (session_->local_description() &&
370 !request.options.transport_options.ice_restart) {
371 // Include all local ice candidates in the SessionDescription unless
372 // the remote peer has requested an ice restart.
373 CopyCandidatesFromSessionDescription(session_->local_description(), answer);
374 }
375 session_->ResetIceRestartLatch();
376 PostCreateSessionDescriptionSucceeded(request.observer, answer);
377}
378
379void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionFailed(
380 CreateSessionDescriptionObserver* observer, const std::string& error) {
381 CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
382 msg->error = error;
383 signaling_thread_->Post(this, MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg);
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000384 LOG(LS_ERROR) << "Create SDP failed: " << error;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000385}
386
387void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionSucceeded(
388 CreateSessionDescriptionObserver* observer,
389 SessionDescriptionInterface* description) {
390 CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
391 msg->description.reset(description);
392 signaling_thread_->Post(this, MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, msg);
393}
394
395void WebRtcSessionDescriptionFactory::OnIdentityRequestFailed(int error) {
396 ASSERT(signaling_thread_->IsCurrent());
397
398 LOG(LS_ERROR) << "Async identity request failed: error = " << error;
399 identity_request_state_ = IDENTITY_FAILED;
400
401 std::string msg = kFailedDueToIdentityFailed;
402 while (!create_session_description_requests_.empty()) {
403 const CreateSessionDescriptionRequest& request =
404 create_session_description_requests_.front();
405 PostCreateSessionDescriptionFailed(
406 request.observer,
407 ((request.type == CreateSessionDescriptionRequest::kOffer) ?
408 "CreateOffer" : "CreateAnswer") + msg);
409 create_session_description_requests_.pop();
410 }
411}
412
413void WebRtcSessionDescriptionFactory::OnIdentityReady(
414 const std::string& der_cert,
415 const std::string& der_private_key) {
416 ASSERT(signaling_thread_->IsCurrent());
417 LOG(LS_VERBOSE) << "Identity is successfully generated.";
418
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000419 std::string pem_cert = rtc::SSLIdentity::DerToPem(
420 rtc::kPemTypeCertificate,
wu@webrtc.org91053e72013-08-10 07:18:04 +0000421 reinterpret_cast<const unsigned char*>(der_cert.data()),
422 der_cert.length());
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000423 std::string pem_key = rtc::SSLIdentity::DerToPem(
424 rtc::kPemTypeRsaPrivateKey,
wu@webrtc.org91053e72013-08-10 07:18:04 +0000425 reinterpret_cast<const unsigned char*>(der_private_key.data()),
426 der_private_key.length());
427
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000428 rtc::SSLIdentity* identity =
429 rtc::SSLIdentity::FromPEMStrings(pem_key, pem_cert);
wu@webrtc.org91053e72013-08-10 07:18:04 +0000430 SetIdentity(identity);
431}
432
433void WebRtcSessionDescriptionFactory::SetIdentity(
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000434 rtc::SSLIdentity* identity) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000435 identity_request_state_ = IDENTITY_SUCCEEDED;
436 SignalIdentityReady(identity);
437
438 transport_desc_factory_.set_identity(identity);
wu@webrtc.org91053e72013-08-10 07:18:04 +0000439 transport_desc_factory_.set_secure(cricket::SEC_ENABLED);
440
441 while (!create_session_description_requests_.empty()) {
442 if (create_session_description_requests_.front().type ==
443 CreateSessionDescriptionRequest::kOffer) {
444 InternalCreateOffer(create_session_description_requests_.front());
445 } else {
446 InternalCreateAnswer(create_session_description_requests_.front());
447 }
448 create_session_description_requests_.pop();
449 }
450}
wu@webrtc.org91053e72013-08-10 07:18:04 +0000451} // namespace webrtc