blob: 74d1488e7b8923a6bd62d120ed2e41d1c068a9a3 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
3 * Copyright 2012, 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/webrtcsession.h"
29
30#include <algorithm>
31#include <climits>
32#include <vector>
33
34#include "talk/app/webrtc/jsepicecandidate.h"
35#include "talk/app/webrtc/jsepsessiondescription.h"
36#include "talk/app/webrtc/mediaconstraintsinterface.h"
37#include "talk/app/webrtc/mediastreamsignaling.h"
38#include "talk/app/webrtc/peerconnectioninterface.h"
wu@webrtc.org91053e72013-08-10 07:18:04 +000039#include "talk/app/webrtc/webrtcsessiondescriptionfactory.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000040#include "talk/base/helpers.h"
41#include "talk/base/logging.h"
42#include "talk/base/stringencode.h"
43#include "talk/media/base/constants.h"
44#include "talk/media/base/videocapturer.h"
wu@webrtc.org1d1ffc92013-10-16 18:12:02 +000045#include "talk/media/sctp/sctputils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000046#include "talk/session/media/channel.h"
47#include "talk/session/media/channelmanager.h"
48#include "talk/session/media/mediasession.h"
49
50using cricket::ContentInfo;
51using cricket::ContentInfos;
52using cricket::MediaContentDescription;
53using cricket::SessionDescription;
54using cricket::TransportInfo;
55
henrike@webrtc.org28e20752013-07-10 00:45:36 +000056namespace webrtc {
57
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000058const char MediaConstraintsInterface::kInternalConstraintPrefix[] = "internal";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000059
60// Supported MediaConstraints.
61// DTLS-SRTP pseudo-constraints.
62const char MediaConstraintsInterface::kEnableDtlsSrtp[] =
63 "DtlsSrtpKeyAgreement";
64// DataChannel pseudo constraints.
65const char MediaConstraintsInterface::kEnableRtpDataChannels[] =
66 "RtpDataChannels";
67// This constraint is for internal use only, representing the Chrome command
68// line flag. So it is prefixed with kInternalConstraintPrefix so JS values
69// will be removed.
70const char MediaConstraintsInterface::kEnableSctpDataChannels[] =
71 "internalSctpDataChannels";
72
mallinath@webrtc.org7e809c32013-09-30 18:59:08 +000073const char MediaConstraintsInterface::kInternalDisableEncryption[] =
74 "internalDisableEncryption";
75
henrike@webrtc.org28e20752013-07-10 00:45:36 +000076// Error messages
77const char kSetLocalSdpFailed[] = "SetLocalDescription failed: ";
78const char kSetRemoteSdpFailed[] = "SetRemoteDescription failed: ";
79const char kCreateChannelFailed[] = "Failed to create channels.";
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000080const char kBundleWithoutRtcpMux[] = "RTCP-MUX must be enabled when BUNDLE "
81 "is enabled.";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000082const char kInvalidCandidates[] = "Description contains invalid candidates.";
83const char kInvalidSdp[] = "Invalid session description.";
84const char kMlineMismatch[] =
85 "Offer and answer descriptions m-lines are not matching. "
86 "Rejecting answer.";
87const char kSdpWithoutCrypto[] = "Called with a SDP without crypto enabled.";
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000088const char kSdpWithoutSdesAndDtlsDisabled[] =
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +000089 "Called with an SDP without SDES crypto and DTLS disabled locally.";
90const char kSdpWithoutIceUfragPwd[] =
91 "Called with an SDP without ice-ufrag and ice-pwd.";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000092const char kSessionError[] = "Session error code: ";
93const char kUpdateStateFailed[] = "Failed to update session state: ";
94const char kPushDownOfferTDFailed[] =
95 "Failed to push down offer transport description.";
96const char kPushDownPranswerTDFailed[] =
97 "Failed to push down pranswer transport description.";
98const char kPushDownAnswerTDFailed[] =
99 "Failed to push down answer transport description.";
100
101// Compares |answer| against |offer|. Comparision is done
102// for number of m-lines in answer against offer. If matches true will be
103// returned otherwise false.
104static bool VerifyMediaDescriptions(
105 const SessionDescription* answer, const SessionDescription* offer) {
106 if (offer->contents().size() != answer->contents().size())
107 return false;
108
109 for (size_t i = 0; i < offer->contents().size(); ++i) {
110 if ((offer->contents()[i].name) != answer->contents()[i].name) {
111 return false;
112 }
113 }
114 return true;
115}
116
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000117// Checks that each non-rejected content has SDES crypto keys or a DTLS
118// fingerprint. Mismatches, such as replying with a DTLS fingerprint to SDES
119// keys, will be caught in Transport negotiation, and backstopped by Channel's
120// |secure_required| check.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000121static bool VerifyCrypto(const SessionDescription* desc,
122 bool dtls_enabled,
123 std::string* error) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000124 const ContentInfos& contents = desc->contents();
125 for (size_t index = 0; index < contents.size(); ++index) {
126 const ContentInfo* cinfo = &contents[index];
127 if (cinfo->rejected) {
128 continue;
129 }
130
131 // If the content isn't rejected, crypto must be present.
132 const MediaContentDescription* media =
133 static_cast<const MediaContentDescription*>(cinfo->description);
134 const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name);
135 if (!media || !tinfo) {
136 // Something is not right.
137 LOG(LS_ERROR) << kInvalidSdp;
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000138 *error = kInvalidSdp;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000139 return false;
140 }
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000141 if (media->cryptos().empty()) {
142 if (!tinfo->description.identity_fingerprint) {
143 // Crypto must be supplied.
144 LOG(LS_WARNING) << "Session description must have SDES or DTLS-SRTP.";
145 *error = kSdpWithoutCrypto;
146 return false;
147 }
148 if (!dtls_enabled) {
149 LOG(LS_WARNING) <<
150 "Session description must have SDES when DTLS disabled.";
151 *error = kSdpWithoutSdesAndDtlsDisabled;
152 return false;
153 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000154 }
155 }
156
157 return true;
158}
159
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000160// Checks that each non-rejected content has ice-ufrag and ice-pwd set.
161static bool VerifyIceUfragPwdPresent(const SessionDescription* desc) {
162 const ContentInfos& contents = desc->contents();
163 for (size_t index = 0; index < contents.size(); ++index) {
164 const ContentInfo* cinfo = &contents[index];
165 if (cinfo->rejected) {
166 continue;
167 }
168
169 // If the content isn't rejected, ice-ufrag and ice-pwd must be present.
170 const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name);
171 if (!tinfo) {
172 // Something is not right.
173 LOG(LS_ERROR) << kInvalidSdp;
174 return false;
175 }
176 if (tinfo->description.ice_ufrag.empty() ||
177 tinfo->description.ice_pwd.empty()) {
178 LOG(LS_ERROR) << "Session description must have ice ufrag and pwd.";
179 return false;
180 }
181 }
182 return true;
183}
184
wu@webrtc.org91053e72013-08-10 07:18:04 +0000185// Forces |sdesc->crypto_required| to the appropriate state based on the
186// current security policy, to ensure a failure occurs if there is an error
187// in crypto negotiation.
188// Called when processing the local session description.
189static void UpdateSessionDescriptionSecurePolicy(
190 cricket::SecureMediaPolicy secure_policy,
191 SessionDescription* sdesc) {
192 if (!sdesc) {
193 return;
194 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000195
wu@webrtc.org91053e72013-08-10 07:18:04 +0000196 // Updating the |crypto_required_| in MediaContentDescription to the
197 // appropriate state based on the current security policy.
198 for (cricket::ContentInfos::iterator iter = sdesc->contents().begin();
199 iter != sdesc->contents().end(); ++iter) {
200 if (cricket::IsMediaContent(&*iter)) {
201 MediaContentDescription* mdesc =
202 static_cast<MediaContentDescription*> (iter->description);
203 if (mdesc) {
204 mdesc->set_crypto_required(secure_policy == cricket::SEC_REQUIRED);
205 }
206 }
207 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000208}
209
210static bool GetAudioSsrcByTrackId(
211 const SessionDescription* session_description,
212 const std::string& track_id, uint32 *ssrc) {
213 const cricket::ContentInfo* audio_info =
214 cricket::GetFirstAudioContent(session_description);
215 if (!audio_info) {
216 LOG(LS_ERROR) << "Audio not used in this call";
217 return false;
218 }
219
220 const cricket::MediaContentDescription* audio_content =
221 static_cast<const cricket::MediaContentDescription*>(
222 audio_info->description);
223 cricket::StreamParams stream;
224 if (!cricket::GetStreamByIds(audio_content->streams(), "", track_id,
225 &stream)) {
226 return false;
227 }
228 *ssrc = stream.first_ssrc();
229 return true;
230}
231
232static bool GetTrackIdBySsrc(const SessionDescription* session_description,
233 uint32 ssrc, std::string* track_id) {
234 ASSERT(track_id != NULL);
235
236 cricket::StreamParams stream_out;
237 const cricket::ContentInfo* audio_info =
238 cricket::GetFirstAudioContent(session_description);
239 if (!audio_info) {
240 return false;
241 }
242 const cricket::MediaContentDescription* audio_content =
243 static_cast<const cricket::MediaContentDescription*>(
244 audio_info->description);
245
246 if (cricket::GetStreamBySsrc(audio_content->streams(), ssrc, &stream_out)) {
247 *track_id = stream_out.id;
248 return true;
249 }
250
251 const cricket::ContentInfo* video_info =
252 cricket::GetFirstVideoContent(session_description);
253 if (!video_info) {
254 return false;
255 }
256 const cricket::MediaContentDescription* video_content =
257 static_cast<const cricket::MediaContentDescription*>(
258 video_info->description);
259
260 if (cricket::GetStreamBySsrc(video_content->streams(), ssrc, &stream_out)) {
261 *track_id = stream_out.id;
262 return true;
263 }
264 return false;
265}
266
267static bool BadSdp(const std::string& desc, std::string* err_desc) {
268 if (err_desc) {
269 *err_desc = desc;
270 }
271 LOG(LS_ERROR) << desc;
272 return false;
273}
274
275static bool BadLocalSdp(const std::string& desc, std::string* err_desc) {
276 std::string set_local_sdp_failed = kSetLocalSdpFailed;
277 set_local_sdp_failed.append(desc);
278 return BadSdp(set_local_sdp_failed, err_desc);
279}
280
281static bool BadRemoteSdp(const std::string& desc, std::string* err_desc) {
282 std::string set_remote_sdp_failed = kSetRemoteSdpFailed;
283 set_remote_sdp_failed.append(desc);
284 return BadSdp(set_remote_sdp_failed, err_desc);
285}
286
287static bool BadSdp(cricket::ContentSource source,
288 const std::string& desc, std::string* err_desc) {
289 if (source == cricket::CS_LOCAL) {
290 return BadLocalSdp(desc, err_desc);
291 } else {
292 return BadRemoteSdp(desc, err_desc);
293 }
294}
295
296static std::string SessionErrorMsg(cricket::BaseSession::Error error) {
297 std::ostringstream desc;
298 desc << kSessionError << error;
299 return desc.str();
300}
301
302#define GET_STRING_OF_STATE(state) \
303 case cricket::BaseSession::state: \
304 result = #state; \
305 break;
306
307static std::string GetStateString(cricket::BaseSession::State state) {
308 std::string result;
309 switch (state) {
310 GET_STRING_OF_STATE(STATE_INIT)
311 GET_STRING_OF_STATE(STATE_SENTINITIATE)
312 GET_STRING_OF_STATE(STATE_RECEIVEDINITIATE)
313 GET_STRING_OF_STATE(STATE_SENTPRACCEPT)
314 GET_STRING_OF_STATE(STATE_SENTACCEPT)
315 GET_STRING_OF_STATE(STATE_RECEIVEDPRACCEPT)
316 GET_STRING_OF_STATE(STATE_RECEIVEDACCEPT)
317 GET_STRING_OF_STATE(STATE_SENTMODIFY)
318 GET_STRING_OF_STATE(STATE_RECEIVEDMODIFY)
319 GET_STRING_OF_STATE(STATE_SENTREJECT)
320 GET_STRING_OF_STATE(STATE_RECEIVEDREJECT)
321 GET_STRING_OF_STATE(STATE_SENTREDIRECT)
322 GET_STRING_OF_STATE(STATE_SENTTERMINATE)
323 GET_STRING_OF_STATE(STATE_RECEIVEDTERMINATE)
324 GET_STRING_OF_STATE(STATE_INPROGRESS)
325 GET_STRING_OF_STATE(STATE_DEINIT)
326 default:
327 ASSERT(false);
328 break;
329 }
330 return result;
331}
332
333#define GET_STRING_OF_ERROR(err) \
334 case cricket::BaseSession::err: \
335 result = #err; \
336 break;
337
338static std::string GetErrorString(cricket::BaseSession::Error err) {
339 std::string result;
340 switch (err) {
341 GET_STRING_OF_ERROR(ERROR_NONE)
342 GET_STRING_OF_ERROR(ERROR_TIME)
343 GET_STRING_OF_ERROR(ERROR_RESPONSE)
344 GET_STRING_OF_ERROR(ERROR_NETWORK)
345 GET_STRING_OF_ERROR(ERROR_CONTENT)
346 GET_STRING_OF_ERROR(ERROR_TRANSPORT)
347 default:
348 ASSERT(false);
349 break;
350 }
351 return result;
352}
353
354static bool SetSessionStateFailed(cricket::ContentSource source,
355 cricket::BaseSession::Error err,
356 std::string* err_desc) {
357 std::string set_state_err = kUpdateStateFailed;
358 set_state_err.append(GetErrorString(err));
359 return BadSdp(source, set_state_err, err_desc);
360}
361
362// Help class used to remember if a a remote peer has requested ice restart by
363// by sending a description with new ice ufrag and password.
364class IceRestartAnswerLatch {
365 public:
366 IceRestartAnswerLatch() : ice_restart_(false) { }
367
wu@webrtc.org91053e72013-08-10 07:18:04 +0000368 // Returns true if CheckForRemoteIceRestart has been called with a new session
369 // description where ice password and ufrag has changed since last time
370 // Reset() was called.
371 bool Get() const {
372 return ice_restart_;
373 }
374
375 void Reset() {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000376 if (ice_restart_) {
377 ice_restart_ = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000378 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000379 }
380
381 void CheckForRemoteIceRestart(
382 const SessionDescriptionInterface* old_desc,
383 const SessionDescriptionInterface* new_desc) {
384 if (!old_desc || new_desc->type() != SessionDescriptionInterface::kOffer) {
385 return;
386 }
387 const SessionDescription* new_sd = new_desc->description();
388 const SessionDescription* old_sd = old_desc->description();
389 const ContentInfos& contents = new_sd->contents();
390 for (size_t index = 0; index < contents.size(); ++index) {
391 const ContentInfo* cinfo = &contents[index];
392 if (cinfo->rejected) {
393 continue;
394 }
395 // If the content isn't rejected, check if ufrag and password has
396 // changed.
397 const cricket::TransportDescription* new_transport_desc =
398 new_sd->GetTransportDescriptionByName(cinfo->name);
399 const cricket::TransportDescription* old_transport_desc =
400 old_sd->GetTransportDescriptionByName(cinfo->name);
401 if (!new_transport_desc || !old_transport_desc) {
402 // No transport description exist. This is not an ice restart.
403 continue;
404 }
405 if (new_transport_desc->ice_pwd != old_transport_desc->ice_pwd &&
406 new_transport_desc->ice_ufrag != old_transport_desc->ice_ufrag) {
407 LOG(LS_INFO) << "Remote peer request ice restart.";
408 ice_restart_ = true;
409 break;
410 }
411 }
412 }
413
414 private:
415 bool ice_restart_;
416};
417
wu@webrtc.org91053e72013-08-10 07:18:04 +0000418WebRtcSession::WebRtcSession(
419 cricket::ChannelManager* channel_manager,
420 talk_base::Thread* signaling_thread,
421 talk_base::Thread* worker_thread,
422 cricket::PortAllocator* port_allocator,
423 MediaStreamSignaling* mediastream_signaling)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000424 : cricket::BaseSession(signaling_thread, worker_thread, port_allocator,
425 talk_base::ToString(talk_base::CreateRandomId64() &
426 LLONG_MAX),
427 cricket::NS_JINGLE_RTP, false),
428 // RFC 3264: The numeric value of the session id and version in the
429 // o line MUST be representable with a "64 bit signed integer".
430 // Due to this constraint session id |sid_| is max limited to LLONG_MAX.
431 channel_manager_(channel_manager),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000432 mediastream_signaling_(mediastream_signaling),
433 ice_observer_(NULL),
434 ice_connection_state_(PeerConnectionInterface::kIceConnectionNew),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000435 older_version_remote_peer_(false),
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000436 dtls_enabled_(false),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000437 data_channel_type_(cricket::DCT_NONE),
438 ice_restart_latch_(new IceRestartAnswerLatch) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000439}
440
441WebRtcSession::~WebRtcSession() {
442 if (voice_channel_.get()) {
443 SignalVoiceChannelDestroyed();
444 channel_manager_->DestroyVoiceChannel(voice_channel_.release());
445 }
446 if (video_channel_.get()) {
447 SignalVideoChannelDestroyed();
448 channel_manager_->DestroyVideoChannel(video_channel_.release());
449 }
450 if (data_channel_.get()) {
451 SignalDataChannelDestroyed();
452 channel_manager_->DestroyDataChannel(data_channel_.release());
453 }
454 for (size_t i = 0; i < saved_candidates_.size(); ++i) {
455 delete saved_candidates_[i];
456 }
457 delete identity();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000458}
459
wu@webrtc.org91053e72013-08-10 07:18:04 +0000460bool WebRtcSession::Initialize(
461 const MediaConstraintsInterface* constraints,
462 DTLSIdentityServiceInterface* dtls_identity_service) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000463 // TODO(perkj): Take |constraints| into consideration. Return false if not all
464 // mandatory constraints can be fulfilled. Note that |constraints|
465 // can be null.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000466 bool value;
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000467
468 // Enable DTLS by default if |dtls_identity_service| is valid.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000469 dtls_enabled_ = (dtls_identity_service != NULL);
470 // |constraints| can override the default |dtls_enabled_| value.
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000471 if (FindConstraint(
472 constraints,
473 MediaConstraintsInterface::kEnableDtlsSrtp,
474 &value, NULL)) {
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000475 dtls_enabled_ = value;
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000476 }
477
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000478 // Enable creation of RTP data channels if the kEnableRtpDataChannels is set.
479 // It takes precendence over the kEnableSctpDataChannels constraint.
480 if (FindConstraint(
481 constraints, MediaConstraintsInterface::kEnableRtpDataChannels,
482 &value, NULL) && value) {
483 LOG(LS_INFO) << "Allowing RTP data engine.";
484 data_channel_type_ = cricket::DCT_RTP;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000485 } else {
486 bool sctp_enabled = FindConstraint(
487 constraints,
488 MediaConstraintsInterface::kEnableSctpDataChannels,
489 &value, NULL) && value;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000490 // DTLS has to be enabled to use SCTP.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000491 if (sctp_enabled && dtls_enabled_) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000492 LOG(LS_INFO) << "Allowing SCTP data engine.";
493 data_channel_type_ = cricket::DCT_SCTP;
494 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000495 }
496 if (data_channel_type_ != cricket::DCT_NONE) {
497 mediastream_signaling_->SetDataChannelFactory(this);
498 }
499
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000500 const cricket::VideoCodec default_codec(
501 JsepSessionDescription::kDefaultVideoCodecId,
502 JsepSessionDescription::kDefaultVideoCodecName,
503 JsepSessionDescription::kMaxVideoCodecWidth,
504 JsepSessionDescription::kMaxVideoCodecHeight,
505 JsepSessionDescription::kDefaultVideoCodecFramerate,
506 JsepSessionDescription::kDefaultVideoCodecPreference);
507 channel_manager_->SetDefaultVideoEncoderConfig(
508 cricket::VideoEncoderConfig(default_codec));
wu@webrtc.org91053e72013-08-10 07:18:04 +0000509
510 webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
511 signaling_thread(),
512 channel_manager_,
513 mediastream_signaling_,
514 dtls_identity_service,
515 this,
516 id(),
517 data_channel_type_,
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000518 dtls_enabled_));
wu@webrtc.org91053e72013-08-10 07:18:04 +0000519
520 webrtc_session_desc_factory_->SignalIdentityReady.connect(
521 this, &WebRtcSession::OnIdentityReady);
mallinath@webrtc.org7e809c32013-09-30 18:59:08 +0000522
523 // Disable encryption if kDisableEncryption is set.
524 if (FindConstraint(
525 constraints,
526 MediaConstraintsInterface::kInternalDisableEncryption,
527 &value, NULL) && value) {
528 webrtc_session_desc_factory_->set_secure(cricket::SEC_DISABLED);
529 }
530
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000531 return true;
532}
533
534void WebRtcSession::Terminate() {
535 SetState(STATE_RECEIVEDTERMINATE);
536 RemoveUnusedChannelsAndTransports(NULL);
537 ASSERT(voice_channel_.get() == NULL);
538 ASSERT(video_channel_.get() == NULL);
539 ASSERT(data_channel_.get() == NULL);
540}
541
542bool WebRtcSession::StartCandidatesAllocation() {
543 // SpeculativelyConnectTransportChannels, will call ConnectChannels method
544 // from TransportProxy to start gathering ice candidates.
545 SpeculativelyConnectAllTransportChannels();
546 if (!saved_candidates_.empty()) {
547 // If there are saved candidates which arrived before local description is
548 // set, copy those to remote description.
549 CopySavedCandidates(remote_desc_.get());
550 }
551 // Push remote candidates present in remote description to transport channels.
552 UseCandidatesInSessionDescription(remote_desc_.get());
553 return true;
554}
555
556void WebRtcSession::set_secure_policy(
557 cricket::SecureMediaPolicy secure_policy) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000558 webrtc_session_desc_factory_->set_secure(secure_policy);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000559}
560
wu@webrtc.org91053e72013-08-10 07:18:04 +0000561cricket::SecureMediaPolicy WebRtcSession::secure_policy() const {
562 return webrtc_session_desc_factory_->secure();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000563}
564
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000565bool WebRtcSession::GetSslRole(talk_base::SSLRole* role) {
566 if (local_description() == NULL || remote_description() == NULL) {
567 LOG(LS_INFO) << "Local and Remote descriptions must be applied to get "
568 << "SSL Role of the session.";
569 return false;
570 }
571
572 // TODO(mallinath) - Return role of each transport, as role may differ from
573 // one another.
574 // In current implementaion we just return the role of first transport in the
575 // transport map.
576 for (cricket::TransportMap::const_iterator iter = transport_proxies().begin();
577 iter != transport_proxies().end(); ++iter) {
578 if (iter->second->impl()) {
579 return iter->second->impl()->GetSslRole(role);
580 }
581 }
582 return false;
583}
584
wu@webrtc.org91053e72013-08-10 07:18:04 +0000585void WebRtcSession::CreateOffer(CreateSessionDescriptionObserver* observer,
586 const MediaConstraintsInterface* constraints) {
587 webrtc_session_desc_factory_->CreateOffer(observer, constraints);
588}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000589
wu@webrtc.org91053e72013-08-10 07:18:04 +0000590void WebRtcSession::CreateAnswer(CreateSessionDescriptionObserver* observer,
591 const MediaConstraintsInterface* constraints) {
592 webrtc_session_desc_factory_->CreateAnswer(observer, constraints);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000593}
594
595bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
596 std::string* err_desc) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000597 // Takes the ownership of |desc| regardless of the result.
598 talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
599
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000600 // Validate SDP.
601 if (!ValidateSessionDescription(desc, cricket::CS_LOCAL, err_desc)) {
602 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000603 }
604
605 // Update the initiator flag if this session is the initiator.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000606 Action action = GetAction(desc->type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000607 if (state() == STATE_INIT && action == kOffer) {
608 set_initiator(true);
609 }
610
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000611 cricket::SecureMediaPolicy secure_policy =
612 webrtc_session_desc_factory_->secure();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000613 // Update the MediaContentDescription crypto settings as per the policy set.
wu@webrtc.org91053e72013-08-10 07:18:04 +0000614 UpdateSessionDescriptionSecurePolicy(secure_policy, desc->description());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000615
616 set_local_description(desc->description()->Copy());
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000617 local_desc_.reset(desc_temp.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000618
619 // Transport and Media channels will be created only when offer is set.
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000620 if (action == kOffer && !CreateChannels(local_desc_->description())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000621 // TODO(mallinath) - Handle CreateChannel failure, as new local description
622 // is applied. Restore back to old description.
623 return BadLocalSdp(kCreateChannelFailed, err_desc);
624 }
625
626 // Remove channel and transport proxies, if MediaContentDescription is
627 // rejected.
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000628 RemoveUnusedChannelsAndTransports(local_desc_->description());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000629
630 if (!UpdateSessionState(action, cricket::CS_LOCAL,
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000631 local_desc_->description(), err_desc)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000632 return false;
633 }
634 // Kick starting the ice candidates allocation.
635 StartCandidatesAllocation();
636
637 // Update state and SSRC of local MediaStreams and DataChannels based on the
638 // local session description.
639 mediastream_signaling_->OnLocalDescriptionChanged(local_desc_.get());
640
641 if (error() != cricket::BaseSession::ERROR_NONE) {
642 return BadLocalSdp(SessionErrorMsg(error()), err_desc);
643 }
644 return true;
645}
646
647bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
648 std::string* err_desc) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000649 // Takes the ownership of |desc| regardless of the result.
650 talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
651
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000652 // Validate SDP.
653 if (!ValidateSessionDescription(desc, cricket::CS_REMOTE, err_desc)) {
654 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000655 }
656
657 // Transport and Media channels will be created only when offer is set.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000658 Action action = GetAction(desc->type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000659 if (action == kOffer && !CreateChannels(desc->description())) {
660 // TODO(mallinath) - Handle CreateChannel failure, as new local description
661 // is applied. Restore back to old description.
662 return BadRemoteSdp(kCreateChannelFailed, err_desc);
663 }
664
665 // Remove channel and transport proxies, if MediaContentDescription is
666 // rejected.
667 RemoveUnusedChannelsAndTransports(desc->description());
668
669 // NOTE: Candidates allocation will be initiated only when SetLocalDescription
670 // is called.
671 set_remote_description(desc->description()->Copy());
672 if (!UpdateSessionState(action, cricket::CS_REMOTE,
673 desc->description(), err_desc)) {
674 return false;
675 }
676
677 // Update remote MediaStreams.
678 mediastream_signaling_->OnRemoteDescriptionChanged(desc);
679 if (local_description() && !UseCandidatesInSessionDescription(desc)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000680 return BadRemoteSdp(kInvalidCandidates, err_desc);
681 }
682
683 // Copy all saved candidates.
684 CopySavedCandidates(desc);
685 // We retain all received candidates.
wu@webrtc.org91053e72013-08-10 07:18:04 +0000686 WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
687 remote_desc_.get(), desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000688 // Check if this new SessionDescription contains new ice ufrag and password
689 // that indicates the remote peer requests ice restart.
690 ice_restart_latch_->CheckForRemoteIceRestart(remote_desc_.get(),
691 desc);
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000692 remote_desc_.reset(desc_temp.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000693 if (error() != cricket::BaseSession::ERROR_NONE) {
694 return BadRemoteSdp(SessionErrorMsg(error()), err_desc);
695 }
696 return true;
697}
698
699bool WebRtcSession::UpdateSessionState(
700 Action action, cricket::ContentSource source,
701 const cricket::SessionDescription* desc,
702 std::string* err_desc) {
703 // If there's already a pending error then no state transition should happen.
704 // But all call-sites should be verifying this before calling us!
705 ASSERT(error() == cricket::BaseSession::ERROR_NONE);
706 if (action == kOffer) {
707 if (!PushdownTransportDescription(source, cricket::CA_OFFER)) {
708 return BadSdp(source, kPushDownOfferTDFailed, err_desc);
709 }
710 SetState(source == cricket::CS_LOCAL ?
711 STATE_SENTINITIATE : STATE_RECEIVEDINITIATE);
712 if (error() != cricket::BaseSession::ERROR_NONE) {
713 return SetSessionStateFailed(source, error(), err_desc);
714 }
715 } else if (action == kPrAnswer) {
716 if (!PushdownTransportDescription(source, cricket::CA_PRANSWER)) {
717 return BadSdp(source, kPushDownPranswerTDFailed, err_desc);
718 }
719 EnableChannels();
720 SetState(source == cricket::CS_LOCAL ?
721 STATE_SENTPRACCEPT : STATE_RECEIVEDPRACCEPT);
722 if (error() != cricket::BaseSession::ERROR_NONE) {
723 return SetSessionStateFailed(source, error(), err_desc);
724 }
725 } else if (action == kAnswer) {
726 if (!PushdownTransportDescription(source, cricket::CA_ANSWER)) {
727 return BadSdp(source, kPushDownAnswerTDFailed, err_desc);
728 }
729 MaybeEnableMuxingSupport();
730 EnableChannels();
731 SetState(source == cricket::CS_LOCAL ?
732 STATE_SENTACCEPT : STATE_RECEIVEDACCEPT);
733 if (error() != cricket::BaseSession::ERROR_NONE) {
734 return SetSessionStateFailed(source, error(), err_desc);
735 }
736 }
737 return true;
738}
739
740WebRtcSession::Action WebRtcSession::GetAction(const std::string& type) {
741 if (type == SessionDescriptionInterface::kOffer) {
742 return WebRtcSession::kOffer;
743 } else if (type == SessionDescriptionInterface::kPrAnswer) {
744 return WebRtcSession::kPrAnswer;
745 } else if (type == SessionDescriptionInterface::kAnswer) {
746 return WebRtcSession::kAnswer;
747 }
748 ASSERT(false && "unknown action type");
749 return WebRtcSession::kOffer;
750}
751
752bool WebRtcSession::ProcessIceMessage(const IceCandidateInterface* candidate) {
753 if (state() == STATE_INIT) {
754 LOG(LS_ERROR) << "ProcessIceMessage: ICE candidates can't be added "
755 << "without any offer (local or remote) "
756 << "session description.";
757 return false;
758 }
759
760 if (!candidate) {
761 LOG(LS_ERROR) << "ProcessIceMessage: Candidate is NULL";
762 return false;
763 }
764
765 if (!local_description() || !remote_description()) {
766 LOG(LS_INFO) << "ProcessIceMessage: Remote description not set, "
767 << "save the candidate for later use.";
768 saved_candidates_.push_back(
769 new JsepIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(),
770 candidate->candidate()));
771 return true;
772 }
773
774 // Add this candidate to the remote session description.
775 if (!remote_desc_->AddCandidate(candidate)) {
776 LOG(LS_ERROR) << "ProcessIceMessage: Candidate cannot be used";
777 return false;
778 }
779
780 return UseCandidatesInSessionDescription(remote_desc_.get());
781}
782
783bool WebRtcSession::GetTrackIdBySsrc(uint32 ssrc, std::string* id) {
784 if (GetLocalTrackId(ssrc, id)) {
785 if (GetRemoteTrackId(ssrc, id)) {
786 LOG(LS_WARNING) << "SSRC " << ssrc
787 << " exists in both local and remote descriptions";
788 return true; // We return the remote track id.
789 }
790 return true;
791 } else {
792 return GetRemoteTrackId(ssrc, id);
793 }
794}
795
796bool WebRtcSession::GetLocalTrackId(uint32 ssrc, std::string* track_id) {
797 if (!BaseSession::local_description())
798 return false;
799 return webrtc::GetTrackIdBySsrc(
800 BaseSession::local_description(), ssrc, track_id);
801}
802
803bool WebRtcSession::GetRemoteTrackId(uint32 ssrc, std::string* track_id) {
804 if (!BaseSession::remote_description())
805 return false;
806 return webrtc::GetTrackIdBySsrc(
807 BaseSession::remote_description(), ssrc, track_id);
808}
809
810std::string WebRtcSession::BadStateErrMsg(
811 const std::string& type, State state) {
812 std::ostringstream desc;
813 desc << "Called with type in wrong state, "
814 << "type: " << type << " state: " << GetStateString(state);
815 return desc.str();
816}
817
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000818void WebRtcSession::SetAudioPlayout(uint32 ssrc, bool enable,
819 cricket::AudioRenderer* renderer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000820 ASSERT(signaling_thread()->IsCurrent());
821 if (!voice_channel_) {
822 LOG(LS_ERROR) << "SetAudioPlayout: No audio channel exists.";
823 return;
824 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000825 if (!voice_channel_->SetRemoteRenderer(ssrc, renderer)) {
826 // SetRenderer() can fail if the ssrc does not match any playout channel.
827 LOG(LS_ERROR) << "SetAudioPlayout: ssrc is incorrect: " << ssrc;
828 return;
829 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000830 if (!voice_channel_->SetOutputScaling(ssrc, enable ? 1 : 0, enable ? 1 : 0)) {
831 // Allow that SetOutputScaling fail if |enable| is false but assert
832 // otherwise. This in the normal case when the underlying media channel has
833 // already been deleted.
834 ASSERT(enable == false);
835 }
836}
837
838void WebRtcSession::SetAudioSend(uint32 ssrc, bool enable,
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000839 const cricket::AudioOptions& options,
840 cricket::AudioRenderer* renderer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000841 ASSERT(signaling_thread()->IsCurrent());
842 if (!voice_channel_) {
843 LOG(LS_ERROR) << "SetAudioSend: No audio channel exists.";
844 return;
845 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000846 if (!voice_channel_->SetLocalRenderer(ssrc, renderer)) {
847 // SetRenderer() can fail if the ssrc does not match any send channel.
848 LOG(LS_ERROR) << "SetAudioSend: ssrc is incorrect: " << ssrc;
849 return;
850 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000851 if (!voice_channel_->MuteStream(ssrc, !enable)) {
852 // Allow that MuteStream fail if |enable| is false but assert otherwise.
853 // This in the normal case when the underlying media channel has already
854 // been deleted.
855 ASSERT(enable == false);
856 return;
857 }
858 if (enable)
859 voice_channel_->SetChannelOptions(options);
860}
861
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000862bool WebRtcSession::SetCaptureDevice(uint32 ssrc,
863 cricket::VideoCapturer* camera) {
864 ASSERT(signaling_thread()->IsCurrent());
865
866 if (!video_channel_.get()) {
867 // |video_channel_| doesnt't exist. Probably because the remote end doesnt't
868 // support video.
869 LOG(LS_WARNING) << "Video not used in this call.";
870 return false;
871 }
872 if (!video_channel_->SetCapturer(ssrc, camera)) {
873 // Allow that SetCapturer fail if |camera| is NULL but assert otherwise.
874 // This in the normal case when the underlying media channel has already
875 // been deleted.
876 ASSERT(camera == NULL);
877 return false;
878 }
879 return true;
880}
881
882void WebRtcSession::SetVideoPlayout(uint32 ssrc,
883 bool enable,
884 cricket::VideoRenderer* renderer) {
885 ASSERT(signaling_thread()->IsCurrent());
886 if (!video_channel_) {
887 LOG(LS_WARNING) << "SetVideoPlayout: No video channel exists.";
888 return;
889 }
890 if (!video_channel_->SetRenderer(ssrc, enable ? renderer : NULL)) {
891 // Allow that SetRenderer fail if |renderer| is NULL but assert otherwise.
892 // This in the normal case when the underlying media channel has already
893 // been deleted.
894 ASSERT(renderer == NULL);
895 }
896}
897
898void WebRtcSession::SetVideoSend(uint32 ssrc, bool enable,
899 const cricket::VideoOptions* options) {
900 ASSERT(signaling_thread()->IsCurrent());
901 if (!video_channel_) {
902 LOG(LS_WARNING) << "SetVideoSend: No video channel exists.";
903 return;
904 }
905 if (!video_channel_->MuteStream(ssrc, !enable)) {
906 // Allow that MuteStream fail if |enable| is false but assert otherwise.
907 // This in the normal case when the underlying media channel has already
908 // been deleted.
909 ASSERT(enable == false);
910 return;
911 }
912 if (enable && options)
913 video_channel_->SetChannelOptions(*options);
914}
915
916bool WebRtcSession::CanInsertDtmf(const std::string& track_id) {
917 ASSERT(signaling_thread()->IsCurrent());
918 if (!voice_channel_) {
919 LOG(LS_ERROR) << "CanInsertDtmf: No audio channel exists.";
920 return false;
921 }
922 uint32 send_ssrc = 0;
923 // The Dtmf is negotiated per channel not ssrc, so we only check if the ssrc
924 // exists.
925 if (!GetAudioSsrcByTrackId(BaseSession::local_description(), track_id,
926 &send_ssrc)) {
927 LOG(LS_ERROR) << "CanInsertDtmf: Track does not exist: " << track_id;
928 return false;
929 }
930 return voice_channel_->CanInsertDtmf();
931}
932
933bool WebRtcSession::InsertDtmf(const std::string& track_id,
934 int code, int duration) {
935 ASSERT(signaling_thread()->IsCurrent());
936 if (!voice_channel_) {
937 LOG(LS_ERROR) << "InsertDtmf: No audio channel exists.";
938 return false;
939 }
940 uint32 send_ssrc = 0;
941 if (!VERIFY(GetAudioSsrcByTrackId(BaseSession::local_description(),
942 track_id, &send_ssrc))) {
943 LOG(LS_ERROR) << "InsertDtmf: Track does not exist: " << track_id;
944 return false;
945 }
946 if (!voice_channel_->InsertDtmf(send_ssrc, code, duration,
947 cricket::DF_SEND)) {
948 LOG(LS_ERROR) << "Failed to insert DTMF to channel.";
949 return false;
950 }
951 return true;
952}
953
954sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() {
955 return &SignalVoiceChannelDestroyed;
956}
957
wu@webrtc.org78187522013-10-07 23:32:02 +0000958bool WebRtcSession::SendData(const cricket::SendDataParams& params,
959 const talk_base::Buffer& payload,
960 cricket::SendDataResult* result) {
961 if (!data_channel_.get()) {
962 LOG(LS_ERROR) << "SendData called when data_channel_ is NULL.";
963 return false;
964 }
965 return data_channel_->SendData(params, payload, result);
966}
967
968bool WebRtcSession::ConnectDataChannel(DataChannel* webrtc_data_channel) {
969 if (!data_channel_.get()) {
970 LOG(LS_ERROR) << "ConnectDataChannel called when data_channel_ is NULL.";
971 return false;
972 }
973
974 data_channel_->SignalReadyToSendData.connect(webrtc_data_channel,
975 &DataChannel::OnChannelReady);
976 data_channel_->SignalDataReceived.connect(webrtc_data_channel,
977 &DataChannel::OnDataReceived);
978 cricket::StreamParams params =
979 cricket::StreamParams::CreateLegacy(webrtc_data_channel->id());
980 data_channel_->AddRecvStream(params);
981 data_channel_->AddSendStream(params);
982 return true;
983}
984
985void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) {
986 data_channel_->RemoveSendStream(webrtc_data_channel->id());
987 data_channel_->RemoveRecvStream(webrtc_data_channel->id());
988 data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
989 data_channel_->SignalDataReceived.disconnect(webrtc_data_channel);
990}
991
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000992talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
wu@webrtc.org78187522013-10-07 23:32:02 +0000993 const std::string& label,
994 const DataChannelInit* config) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000995 if (state() == STATE_RECEIVEDTERMINATE) {
996 return NULL;
997 }
998 if (data_channel_type_ == cricket::DCT_NONE) {
999 LOG(LS_ERROR) << "CreateDataChannel: Data is not supported in this call.";
1000 return NULL;
1001 }
1002 DataChannelInit new_config = config ? (*config) : DataChannelInit();
1003
1004 if (data_channel_type_ == cricket::DCT_SCTP) {
1005 if (new_config.id < 0) {
1006 if (!mediastream_signaling_->AllocateSctpId(&new_config.id)) {
1007 LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel.";
1008 return NULL;
1009 }
1010 } else if (!mediastream_signaling_->IsSctpIdAvailable(new_config.id)) {
1011 LOG(LS_ERROR) << "Failed to create a SCTP data channel "
1012 << "because the id is already in use or out of range.";
1013 return NULL;
1014 }
1015 }
wu@webrtc.org91053e72013-08-10 07:18:04 +00001016
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001017 talk_base::scoped_refptr<DataChannel> channel(
wu@webrtc.org78187522013-10-07 23:32:02 +00001018 DataChannel::Create(this, data_channel_type_, label, &new_config));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001019 if (channel == NULL)
1020 return NULL;
1021 if (!mediastream_signaling_->AddDataChannel(channel))
1022 return NULL;
wu@webrtc.org91053e72013-08-10 07:18:04 +00001023 if (data_channel_type_ == cricket::DCT_SCTP) {
1024 if (config == NULL) {
1025 LOG(LS_WARNING) << "Could not send data channel OPEN message"
1026 << " because of NULL config.";
1027 return NULL;
1028 }
1029 if (data_channel_.get()) {
1030 channel->SetReceiveSsrc(new_config.id);
1031 channel->SetSendSsrc(new_config.id);
wu@webrtc.org91053e72013-08-10 07:18:04 +00001032 }
1033 if (!config->negotiated) {
1034 talk_base::Buffer *payload = new talk_base::Buffer;
wu@webrtc.org1d1ffc92013-10-16 18:12:02 +00001035 if (!cricket::WriteDataChannelOpenMessage(label, *config, payload)) {
wu@webrtc.org91053e72013-08-10 07:18:04 +00001036 LOG(LS_WARNING) << "Could not write data channel OPEN message";
1037 }
1038 // SendControl may queue the message until the data channel's set up,
1039 // or congestion clears.
1040 channel->SendControl(payload);
1041 }
1042 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001043 return channel;
1044}
1045
1046cricket::DataChannelType WebRtcSession::data_channel_type() const {
1047 return data_channel_type_;
1048}
1049
wu@webrtc.org91053e72013-08-10 07:18:04 +00001050bool WebRtcSession::IceRestartPending() const {
1051 return ice_restart_latch_->Get();
1052}
1053
1054void WebRtcSession::ResetIceRestartLatch() {
1055 ice_restart_latch_->Reset();
1056}
1057
1058void WebRtcSession::OnIdentityReady(talk_base::SSLIdentity* identity) {
1059 SetIdentity(identity);
1060}
1061
1062bool WebRtcSession::waiting_for_identity() const {
1063 return webrtc_session_desc_factory_->waiting_for_identity();
1064}
1065
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001066void WebRtcSession::SetIceConnectionState(
1067 PeerConnectionInterface::IceConnectionState state) {
1068 if (ice_connection_state_ == state) {
1069 return;
1070 }
1071
1072 // ASSERT that the requested transition is allowed. Note that
1073 // WebRtcSession does not implement "kIceConnectionClosed" (that is handled
1074 // within PeerConnection). This switch statement should compile away when
1075 // ASSERTs are disabled.
1076 switch (ice_connection_state_) {
1077 case PeerConnectionInterface::kIceConnectionNew:
1078 ASSERT(state == PeerConnectionInterface::kIceConnectionChecking);
1079 break;
1080 case PeerConnectionInterface::kIceConnectionChecking:
1081 ASSERT(state == PeerConnectionInterface::kIceConnectionFailed ||
1082 state == PeerConnectionInterface::kIceConnectionConnected);
1083 break;
1084 case PeerConnectionInterface::kIceConnectionConnected:
1085 ASSERT(state == PeerConnectionInterface::kIceConnectionDisconnected ||
1086 state == PeerConnectionInterface::kIceConnectionChecking ||
1087 state == PeerConnectionInterface::kIceConnectionCompleted);
1088 break;
1089 case PeerConnectionInterface::kIceConnectionCompleted:
1090 ASSERT(state == PeerConnectionInterface::kIceConnectionConnected ||
1091 state == PeerConnectionInterface::kIceConnectionDisconnected);
1092 break;
1093 case PeerConnectionInterface::kIceConnectionFailed:
1094 ASSERT(state == PeerConnectionInterface::kIceConnectionNew);
1095 break;
1096 case PeerConnectionInterface::kIceConnectionDisconnected:
1097 ASSERT(state == PeerConnectionInterface::kIceConnectionChecking ||
1098 state == PeerConnectionInterface::kIceConnectionConnected ||
1099 state == PeerConnectionInterface::kIceConnectionCompleted ||
1100 state == PeerConnectionInterface::kIceConnectionFailed);
1101 break;
1102 case PeerConnectionInterface::kIceConnectionClosed:
1103 ASSERT(false);
1104 break;
1105 default:
1106 ASSERT(false);
1107 break;
1108 }
1109
1110 ice_connection_state_ = state;
1111 if (ice_observer_) {
1112 ice_observer_->OnIceConnectionChange(ice_connection_state_);
1113 }
1114}
1115
1116void WebRtcSession::OnTransportRequestSignaling(
1117 cricket::Transport* transport) {
1118 ASSERT(signaling_thread()->IsCurrent());
1119 transport->OnSignalingReady();
1120 if (ice_observer_) {
1121 ice_observer_->OnIceGatheringChange(
1122 PeerConnectionInterface::kIceGatheringGathering);
1123 }
1124}
1125
1126void WebRtcSession::OnTransportConnecting(cricket::Transport* transport) {
1127 ASSERT(signaling_thread()->IsCurrent());
1128 // start monitoring for the write state of the transport.
1129 OnTransportWritable(transport);
1130}
1131
1132void WebRtcSession::OnTransportWritable(cricket::Transport* transport) {
1133 ASSERT(signaling_thread()->IsCurrent());
1134 // TODO(bemasc): Expose more API from Transport to detect when
1135 // candidate selection starts or stops, due to success or failure.
1136 if (transport->all_channels_writable()) {
1137 if (ice_connection_state_ ==
1138 PeerConnectionInterface::kIceConnectionChecking ||
1139 ice_connection_state_ ==
1140 PeerConnectionInterface::kIceConnectionDisconnected) {
1141 SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected);
1142 }
1143 } else if (transport->HasChannels()) {
1144 // If the current state is Connected or Completed, then there were writable
1145 // channels but now there are not, so the next state must be Disconnected.
1146 if (ice_connection_state_ ==
1147 PeerConnectionInterface::kIceConnectionConnected ||
1148 ice_connection_state_ ==
1149 PeerConnectionInterface::kIceConnectionCompleted) {
1150 SetIceConnectionState(
1151 PeerConnectionInterface::kIceConnectionDisconnected);
1152 }
1153 }
1154}
1155
1156void WebRtcSession::OnTransportProxyCandidatesReady(
1157 cricket::TransportProxy* proxy, const cricket::Candidates& candidates) {
1158 ASSERT(signaling_thread()->IsCurrent());
1159 ProcessNewLocalCandidate(proxy->content_name(), candidates);
1160}
1161
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001162void WebRtcSession::OnCandidatesAllocationDone() {
1163 ASSERT(signaling_thread()->IsCurrent());
1164 if (ice_observer_) {
1165 ice_observer_->OnIceGatheringChange(
1166 PeerConnectionInterface::kIceGatheringComplete);
1167 ice_observer_->OnIceComplete();
1168 }
1169}
1170
1171// Enabling voice and video channel.
1172void WebRtcSession::EnableChannels() {
1173 if (voice_channel_ && !voice_channel_->enabled())
1174 voice_channel_->Enable(true);
1175
1176 if (video_channel_ && !video_channel_->enabled())
1177 video_channel_->Enable(true);
1178
1179 if (data_channel_.get() && !data_channel_->enabled())
1180 data_channel_->Enable(true);
1181}
1182
1183void WebRtcSession::ProcessNewLocalCandidate(
1184 const std::string& content_name,
1185 const cricket::Candidates& candidates) {
1186 int sdp_mline_index;
1187 if (!GetLocalCandidateMediaIndex(content_name, &sdp_mline_index)) {
1188 LOG(LS_ERROR) << "ProcessNewLocalCandidate: content name "
1189 << content_name << " not found";
1190 return;
1191 }
1192
1193 for (cricket::Candidates::const_iterator citer = candidates.begin();
1194 citer != candidates.end(); ++citer) {
1195 // Use content_name as the candidate media id.
1196 JsepIceCandidate candidate(content_name, sdp_mline_index, *citer);
1197 if (ice_observer_) {
1198 ice_observer_->OnIceCandidate(&candidate);
1199 }
1200 if (local_desc_) {
1201 local_desc_->AddCandidate(&candidate);
1202 }
1203 }
1204}
1205
1206// Returns the media index for a local ice candidate given the content name.
1207bool WebRtcSession::GetLocalCandidateMediaIndex(const std::string& content_name,
1208 int* sdp_mline_index) {
1209 if (!BaseSession::local_description() || !sdp_mline_index)
1210 return false;
1211
1212 bool content_found = false;
1213 const ContentInfos& contents = BaseSession::local_description()->contents();
1214 for (size_t index = 0; index < contents.size(); ++index) {
1215 if (contents[index].name == content_name) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001216 *sdp_mline_index = static_cast<int>(index);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001217 content_found = true;
1218 break;
1219 }
1220 }
1221 return content_found;
1222}
1223
1224bool WebRtcSession::UseCandidatesInSessionDescription(
1225 const SessionDescriptionInterface* remote_desc) {
1226 if (!remote_desc)
1227 return true;
1228 bool ret = true;
1229 for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) {
1230 const IceCandidateCollection* candidates = remote_desc->candidates(m);
1231 for (size_t n = 0; n < candidates->count(); ++n) {
1232 ret = UseCandidate(candidates->at(n));
1233 if (!ret)
1234 break;
1235 }
1236 }
1237 return ret;
1238}
1239
1240bool WebRtcSession::UseCandidate(
1241 const IceCandidateInterface* candidate) {
1242
1243 size_t mediacontent_index = static_cast<size_t>(candidate->sdp_mline_index());
1244 size_t remote_content_size =
1245 BaseSession::remote_description()->contents().size();
1246 if (mediacontent_index >= remote_content_size) {
1247 LOG(LS_ERROR)
1248 << "UseRemoteCandidateInSession: Invalid candidate media index.";
1249 return false;
1250 }
1251
1252 cricket::ContentInfo content =
1253 BaseSession::remote_description()->contents()[mediacontent_index];
1254 std::vector<cricket::Candidate> candidates;
1255 candidates.push_back(candidate->candidate());
1256 // Invoking BaseSession method to handle remote candidates.
1257 std::string error;
1258 if (OnRemoteCandidates(content.name, candidates, &error)) {
1259 // Candidates successfully submitted for checking.
1260 if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew ||
1261 ice_connection_state_ ==
1262 PeerConnectionInterface::kIceConnectionDisconnected) {
1263 // If state is New, then the session has just gotten its first remote ICE
1264 // candidates, so go to Checking.
1265 // If state is Disconnected, the session is re-using old candidates or
1266 // receiving additional ones, so go to Checking.
1267 // If state is Connected, stay Connected.
1268 // TODO(bemasc): If state is Connected, and the new candidates are for a
1269 // newly added transport, then the state actually _should_ move to
1270 // checking. Add a way to distinguish that case.
1271 SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking);
1272 }
1273 // TODO(bemasc): If state is Completed, go back to Connected.
1274 } else {
1275 LOG(LS_WARNING) << error;
1276 }
1277 return true;
1278}
1279
1280void WebRtcSession::RemoveUnusedChannelsAndTransports(
1281 const SessionDescription* desc) {
1282 const cricket::ContentInfo* voice_info =
1283 cricket::GetFirstAudioContent(desc);
1284 if ((!voice_info || voice_info->rejected) && voice_channel_) {
1285 mediastream_signaling_->OnAudioChannelClose();
1286 SignalVoiceChannelDestroyed();
1287 const std::string content_name = voice_channel_->content_name();
1288 channel_manager_->DestroyVoiceChannel(voice_channel_.release());
1289 DestroyTransportProxy(content_name);
1290 }
1291
1292 const cricket::ContentInfo* video_info =
1293 cricket::GetFirstVideoContent(desc);
1294 if ((!video_info || video_info->rejected) && video_channel_) {
1295 mediastream_signaling_->OnVideoChannelClose();
1296 SignalVideoChannelDestroyed();
1297 const std::string content_name = video_channel_->content_name();
1298 channel_manager_->DestroyVideoChannel(video_channel_.release());
1299 DestroyTransportProxy(content_name);
1300 }
1301
1302 const cricket::ContentInfo* data_info =
1303 cricket::GetFirstDataContent(desc);
1304 if ((!data_info || data_info->rejected) && data_channel_) {
1305 mediastream_signaling_->OnDataChannelClose();
1306 SignalDataChannelDestroyed();
1307 const std::string content_name = data_channel_->content_name();
1308 channel_manager_->DestroyDataChannel(data_channel_.release());
1309 DestroyTransportProxy(content_name);
1310 }
1311}
1312
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001313// TODO(mallinath) - Add a correct error code if the channels are not creatued
1314// due to BUNDLE is enabled but rtcp-mux is disabled.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001315bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
1316 // Disabling the BUNDLE flag in PortAllocator if offer disabled it.
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001317 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1318 if (state() == STATE_INIT && !bundle_enabled) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001319 port_allocator()->set_flags(port_allocator()->flags() &
1320 ~cricket::PORTALLOCATOR_ENABLE_BUNDLE);
1321 }
1322
1323 // Creating the media channels and transport proxies.
1324 const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(desc);
1325 if (voice && !voice->rejected && !voice_channel_) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001326 if (!CreateVoiceChannel(voice)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001327 LOG(LS_ERROR) << "Failed to create voice channel.";
1328 return false;
1329 }
1330 }
1331
1332 const cricket::ContentInfo* video = cricket::GetFirstVideoContent(desc);
1333 if (video && !video->rejected && !video_channel_) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001334 if (!CreateVideoChannel(video)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001335 LOG(LS_ERROR) << "Failed to create video channel.";
1336 return false;
1337 }
1338 }
1339
1340 const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc);
1341 if (data_channel_type_ != cricket::DCT_NONE &&
1342 data && !data->rejected && !data_channel_.get()) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001343 if (!CreateDataChannel(data)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001344 LOG(LS_ERROR) << "Failed to create data channel.";
1345 return false;
1346 }
1347 }
1348
1349 return true;
1350}
1351
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001352bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001353 voice_channel_.reset(channel_manager_->CreateVoiceChannel(
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001354 this, content->name, true));
1355 return (voice_channel_ != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001356}
1357
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001358bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001359 video_channel_.reset(channel_manager_->CreateVideoChannel(
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001360 this, content->name, true, voice_channel_.get()));
1361 return (video_channel_ != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001362}
1363
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001364bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001365 bool rtcp = (data_channel_type_ == cricket::DCT_RTP);
1366 data_channel_.reset(channel_manager_->CreateDataChannel(
wu@webrtc.org91053e72013-08-10 07:18:04 +00001367 this, content->name, rtcp, data_channel_type_));
1368 if (!data_channel_.get()) {
1369 return false;
1370 }
wu@webrtc.org1d1ffc92013-10-16 18:12:02 +00001371 data_channel_->SignalNewStreamReceived.connect(
1372 this, &WebRtcSession::OnNewDataChannelReceived);
wu@webrtc.org91053e72013-08-10 07:18:04 +00001373 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001374}
1375
1376void WebRtcSession::CopySavedCandidates(
1377 SessionDescriptionInterface* dest_desc) {
1378 if (!dest_desc) {
1379 ASSERT(false);
1380 return;
1381 }
1382 for (size_t i = 0; i < saved_candidates_.size(); ++i) {
1383 dest_desc->AddCandidate(saved_candidates_[i]);
1384 delete saved_candidates_[i];
1385 }
1386 saved_candidates_.clear();
1387}
1388
wu@webrtc.org1d1ffc92013-10-16 18:12:02 +00001389void WebRtcSession::OnNewDataChannelReceived(
1390 const std::string& label, const DataChannelInit& init) {
1391 ASSERT(data_channel_type_ == cricket::DCT_SCTP);
wu@webrtc.org91053e72013-08-10 07:18:04 +00001392 if (!mediastream_signaling_->AddDataChannelFromOpenMessage(
wu@webrtc.org1d1ffc92013-10-16 18:12:02 +00001393 label, init)) {
wu@webrtc.org91053e72013-08-10 07:18:04 +00001394 LOG(LS_WARNING) << "Failed to create data channel from OPEN message.";
1395 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001396 }
1397}
1398
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001399// Returns false if bundle is enabled and rtcp_mux is disabled.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001400bool WebRtcSession::ValidateBundleSettings(const SessionDescription* desc) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001401 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1402 if (!bundle_enabled)
1403 return true;
1404
1405 const cricket::ContentGroup* bundle_group =
1406 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
1407 ASSERT(bundle_group != NULL);
1408
1409 const cricket::ContentInfos& contents = desc->contents();
1410 for (cricket::ContentInfos::const_iterator citer = contents.begin();
1411 citer != contents.end(); ++citer) {
1412 const cricket::ContentInfo* content = (&*citer);
1413 ASSERT(content != NULL);
1414 if (bundle_group->HasContentName(content->name) &&
1415 !content->rejected && content->type == cricket::NS_JINGLE_RTP) {
1416 if (!HasRtcpMuxEnabled(content))
1417 return false;
1418 }
1419 }
1420 // RTCP-MUX is enabled in all the contents.
1421 return true;
1422}
1423
1424bool WebRtcSession::HasRtcpMuxEnabled(
1425 const cricket::ContentInfo* content) {
1426 const cricket::MediaContentDescription* description =
1427 static_cast<cricket::MediaContentDescription*>(content->description);
1428 return description->rtcp_mux();
1429}
1430
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001431bool WebRtcSession::ValidateSessionDescription(
1432 const SessionDescriptionInterface* sdesc,
1433 cricket::ContentSource source, std::string* error_desc) {
1434
1435 if (error() != cricket::BaseSession::ERROR_NONE) {
1436 return BadSdp(source, SessionErrorMsg(error()), error_desc);
1437 }
1438
1439 if (!sdesc || !sdesc->description()) {
1440 return BadSdp(source, kInvalidSdp, error_desc);
1441 }
1442
1443 std::string type = sdesc->type();
1444 Action action = GetAction(sdesc->type());
1445 if (source == cricket::CS_LOCAL) {
1446 if (!ExpectSetLocalDescription(action))
1447 return BadSdp(source, BadStateErrMsg(type, state()), error_desc);
1448 } else {
1449 if (!ExpectSetRemoteDescription(action))
1450 return BadSdp(source, BadStateErrMsg(type, state()), error_desc);
1451 }
1452
1453 // Verify crypto settings.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +00001454 std::string crypto_error;
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001455 if (webrtc_session_desc_factory_->secure() == cricket::SEC_REQUIRED &&
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +00001456 !VerifyCrypto(sdesc->description(), dtls_enabled_, &crypto_error)) {
1457 return BadSdp(source, crypto_error, error_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001458 }
1459
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001460 // Verify ice-ufrag and ice-pwd.
1461 if (!VerifyIceUfragPwdPresent(sdesc->description())) {
1462 return BadSdp(source, kSdpWithoutIceUfragPwd, error_desc);
1463 }
1464
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001465 if (!ValidateBundleSettings(sdesc->description())) {
1466 return BadSdp(source, kBundleWithoutRtcpMux, error_desc);
1467 }
1468
1469 // Verify m-lines in Answer when compared against Offer.
1470 if (action == kAnswer) {
1471 const cricket::SessionDescription* offer_desc =
1472 (source == cricket::CS_LOCAL) ? remote_description()->description() :
1473 local_description()->description();
1474 if (!VerifyMediaDescriptions(sdesc->description(), offer_desc)) {
1475 return BadSdp(source, kMlineMismatch, error_desc);
1476 }
1477 }
1478
1479 return true;
1480}
1481
1482bool WebRtcSession::ExpectSetLocalDescription(Action action) {
1483 return ((action == kOffer && state() == STATE_INIT) ||
1484 // update local offer
1485 (action == kOffer && state() == STATE_SENTINITIATE) ||
1486 // update the current ongoing session.
1487 (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
1488 (action == kOffer && state() == STATE_SENTACCEPT) ||
1489 (action == kOffer && state() == STATE_INPROGRESS) ||
1490 // accept remote offer
1491 (action == kAnswer && state() == STATE_RECEIVEDINITIATE) ||
1492 (action == kAnswer && state() == STATE_SENTPRACCEPT) ||
1493 (action == kPrAnswer && state() == STATE_RECEIVEDINITIATE) ||
1494 (action == kPrAnswer && state() == STATE_SENTPRACCEPT));
1495}
1496
1497bool WebRtcSession::ExpectSetRemoteDescription(Action action) {
1498 return ((action == kOffer && state() == STATE_INIT) ||
1499 // update remote offer
1500 (action == kOffer && state() == STATE_RECEIVEDINITIATE) ||
1501 // update the current ongoing session
1502 (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
1503 (action == kOffer && state() == STATE_SENTACCEPT) ||
1504 (action == kOffer && state() == STATE_INPROGRESS) ||
1505 // accept local offer
1506 (action == kAnswer && state() == STATE_SENTINITIATE) ||
1507 (action == kAnswer && state() == STATE_RECEIVEDPRACCEPT) ||
1508 (action == kPrAnswer && state() == STATE_SENTINITIATE) ||
1509 (action == kPrAnswer && state() == STATE_RECEIVEDPRACCEPT));
1510}
1511
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001512} // namespace webrtc