blob: c7805c16d4724523d3360ec0b2a60e44e94156b1 [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"
45#include "talk/session/media/channel.h"
46#include "talk/session/media/channelmanager.h"
47#include "talk/session/media/mediasession.h"
48
49using cricket::ContentInfo;
50using cricket::ContentInfos;
51using cricket::MediaContentDescription;
52using cricket::SessionDescription;
53using cricket::TransportInfo;
54
henrike@webrtc.org28e20752013-07-10 00:45:36 +000055namespace webrtc {
56
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000057const char MediaConstraintsInterface::kInternalConstraintPrefix[] = "internal";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000058
59// Supported MediaConstraints.
60// DTLS-SRTP pseudo-constraints.
61const char MediaConstraintsInterface::kEnableDtlsSrtp[] =
62 "DtlsSrtpKeyAgreement";
63// DataChannel pseudo constraints.
64const char MediaConstraintsInterface::kEnableRtpDataChannels[] =
65 "RtpDataChannels";
66// This constraint is for internal use only, representing the Chrome command
67// line flag. So it is prefixed with kInternalConstraintPrefix so JS values
68// will be removed.
69const char MediaConstraintsInterface::kEnableSctpDataChannels[] =
70 "internalSctpDataChannels";
71
mallinath@webrtc.org7e809c32013-09-30 18:59:08 +000072const char MediaConstraintsInterface::kInternalDisableEncryption[] =
73 "internalDisableEncryption";
74
henrike@webrtc.org28e20752013-07-10 00:45:36 +000075// Error messages
76const char kSetLocalSdpFailed[] = "SetLocalDescription failed: ";
77const char kSetRemoteSdpFailed[] = "SetRemoteDescription failed: ";
78const char kCreateChannelFailed[] = "Failed to create channels.";
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000079const char kBundleWithoutRtcpMux[] = "RTCP-MUX must be enabled when BUNDLE "
80 "is enabled.";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000081const char kInvalidCandidates[] = "Description contains invalid candidates.";
82const char kInvalidSdp[] = "Invalid session description.";
83const char kMlineMismatch[] =
84 "Offer and answer descriptions m-lines are not matching. "
85 "Rejecting answer.";
86const char kSdpWithoutCrypto[] = "Called with a SDP without crypto enabled.";
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000087const char kSdpWithoutSdesAndDtlsDisabled[] =
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +000088 "Called with an SDP without SDES crypto and DTLS disabled locally.";
89const char kSdpWithoutIceUfragPwd[] =
90 "Called with an SDP without ice-ufrag and ice-pwd.";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000091const char kSessionError[] = "Session error code: ";
92const char kUpdateStateFailed[] = "Failed to update session state: ";
93const char kPushDownOfferTDFailed[] =
94 "Failed to push down offer transport description.";
95const char kPushDownPranswerTDFailed[] =
96 "Failed to push down pranswer transport description.";
97const char kPushDownAnswerTDFailed[] =
98 "Failed to push down answer transport description.";
99
100// Compares |answer| against |offer|. Comparision is done
101// for number of m-lines in answer against offer. If matches true will be
102// returned otherwise false.
103static bool VerifyMediaDescriptions(
104 const SessionDescription* answer, const SessionDescription* offer) {
105 if (offer->contents().size() != answer->contents().size())
106 return false;
107
108 for (size_t i = 0; i < offer->contents().size(); ++i) {
109 if ((offer->contents()[i].name) != answer->contents()[i].name) {
110 return false;
111 }
112 }
113 return true;
114}
115
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000116// Checks that each non-rejected content has SDES crypto keys or a DTLS
117// fingerprint. Mismatches, such as replying with a DTLS fingerprint to SDES
118// keys, will be caught in Transport negotiation, and backstopped by Channel's
119// |secure_required| check.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000120static bool VerifyCrypto(const SessionDescription* desc,
121 bool dtls_enabled,
122 std::string* error) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000123 const ContentInfos& contents = desc->contents();
124 for (size_t index = 0; index < contents.size(); ++index) {
125 const ContentInfo* cinfo = &contents[index];
126 if (cinfo->rejected) {
127 continue;
128 }
129
130 // If the content isn't rejected, crypto must be present.
131 const MediaContentDescription* media =
132 static_cast<const MediaContentDescription*>(cinfo->description);
133 const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name);
134 if (!media || !tinfo) {
135 // Something is not right.
136 LOG(LS_ERROR) << kInvalidSdp;
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000137 *error = kInvalidSdp;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000138 return false;
139 }
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000140 if (media->cryptos().empty()) {
141 if (!tinfo->description.identity_fingerprint) {
142 // Crypto must be supplied.
143 LOG(LS_WARNING) << "Session description must have SDES or DTLS-SRTP.";
144 *error = kSdpWithoutCrypto;
145 return false;
146 }
147 if (!dtls_enabled) {
148 LOG(LS_WARNING) <<
149 "Session description must have SDES when DTLS disabled.";
150 *error = kSdpWithoutSdesAndDtlsDisabled;
151 return false;
152 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000153 }
154 }
155
156 return true;
157}
158
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000159// Checks that each non-rejected content has ice-ufrag and ice-pwd set.
160static bool VerifyIceUfragPwdPresent(const SessionDescription* desc) {
161 const ContentInfos& contents = desc->contents();
162 for (size_t index = 0; index < contents.size(); ++index) {
163 const ContentInfo* cinfo = &contents[index];
164 if (cinfo->rejected) {
165 continue;
166 }
167
168 // If the content isn't rejected, ice-ufrag and ice-pwd must be present.
169 const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name);
170 if (!tinfo) {
171 // Something is not right.
172 LOG(LS_ERROR) << kInvalidSdp;
173 return false;
174 }
175 if (tinfo->description.ice_ufrag.empty() ||
176 tinfo->description.ice_pwd.empty()) {
177 LOG(LS_ERROR) << "Session description must have ice ufrag and pwd.";
178 return false;
179 }
180 }
181 return true;
182}
183
wu@webrtc.org91053e72013-08-10 07:18:04 +0000184// Forces |sdesc->crypto_required| to the appropriate state based on the
185// current security policy, to ensure a failure occurs if there is an error
186// in crypto negotiation.
187// Called when processing the local session description.
188static void UpdateSessionDescriptionSecurePolicy(
189 cricket::SecureMediaPolicy secure_policy,
190 SessionDescription* sdesc) {
191 if (!sdesc) {
192 return;
193 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000194
wu@webrtc.org91053e72013-08-10 07:18:04 +0000195 // Updating the |crypto_required_| in MediaContentDescription to the
196 // appropriate state based on the current security policy.
197 for (cricket::ContentInfos::iterator iter = sdesc->contents().begin();
198 iter != sdesc->contents().end(); ++iter) {
199 if (cricket::IsMediaContent(&*iter)) {
200 MediaContentDescription* mdesc =
201 static_cast<MediaContentDescription*> (iter->description);
202 if (mdesc) {
203 mdesc->set_crypto_required(secure_policy == cricket::SEC_REQUIRED);
204 }
205 }
206 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000207}
208
209static bool GetAudioSsrcByTrackId(
210 const SessionDescription* session_description,
211 const std::string& track_id, uint32 *ssrc) {
212 const cricket::ContentInfo* audio_info =
213 cricket::GetFirstAudioContent(session_description);
214 if (!audio_info) {
215 LOG(LS_ERROR) << "Audio not used in this call";
216 return false;
217 }
218
219 const cricket::MediaContentDescription* audio_content =
220 static_cast<const cricket::MediaContentDescription*>(
221 audio_info->description);
222 cricket::StreamParams stream;
223 if (!cricket::GetStreamByIds(audio_content->streams(), "", track_id,
224 &stream)) {
225 return false;
226 }
227 *ssrc = stream.first_ssrc();
228 return true;
229}
230
231static bool GetTrackIdBySsrc(const SessionDescription* session_description,
232 uint32 ssrc, std::string* track_id) {
233 ASSERT(track_id != NULL);
234
235 cricket::StreamParams stream_out;
236 const cricket::ContentInfo* audio_info =
237 cricket::GetFirstAudioContent(session_description);
238 if (!audio_info) {
239 return false;
240 }
241 const cricket::MediaContentDescription* audio_content =
242 static_cast<const cricket::MediaContentDescription*>(
243 audio_info->description);
244
245 if (cricket::GetStreamBySsrc(audio_content->streams(), ssrc, &stream_out)) {
246 *track_id = stream_out.id;
247 return true;
248 }
249
250 const cricket::ContentInfo* video_info =
251 cricket::GetFirstVideoContent(session_description);
252 if (!video_info) {
253 return false;
254 }
255 const cricket::MediaContentDescription* video_content =
256 static_cast<const cricket::MediaContentDescription*>(
257 video_info->description);
258
259 if (cricket::GetStreamBySsrc(video_content->streams(), ssrc, &stream_out)) {
260 *track_id = stream_out.id;
261 return true;
262 }
263 return false;
264}
265
266static bool BadSdp(const std::string& desc, std::string* err_desc) {
267 if (err_desc) {
268 *err_desc = desc;
269 }
270 LOG(LS_ERROR) << desc;
271 return false;
272}
273
274static bool BadLocalSdp(const std::string& desc, std::string* err_desc) {
275 std::string set_local_sdp_failed = kSetLocalSdpFailed;
276 set_local_sdp_failed.append(desc);
277 return BadSdp(set_local_sdp_failed, err_desc);
278}
279
280static bool BadRemoteSdp(const std::string& desc, std::string* err_desc) {
281 std::string set_remote_sdp_failed = kSetRemoteSdpFailed;
282 set_remote_sdp_failed.append(desc);
283 return BadSdp(set_remote_sdp_failed, err_desc);
284}
285
286static bool BadSdp(cricket::ContentSource source,
287 const std::string& desc, std::string* err_desc) {
288 if (source == cricket::CS_LOCAL) {
289 return BadLocalSdp(desc, err_desc);
290 } else {
291 return BadRemoteSdp(desc, err_desc);
292 }
293}
294
295static std::string SessionErrorMsg(cricket::BaseSession::Error error) {
296 std::ostringstream desc;
297 desc << kSessionError << error;
298 return desc.str();
299}
300
301#define GET_STRING_OF_STATE(state) \
302 case cricket::BaseSession::state: \
303 result = #state; \
304 break;
305
306static std::string GetStateString(cricket::BaseSession::State state) {
307 std::string result;
308 switch (state) {
309 GET_STRING_OF_STATE(STATE_INIT)
310 GET_STRING_OF_STATE(STATE_SENTINITIATE)
311 GET_STRING_OF_STATE(STATE_RECEIVEDINITIATE)
312 GET_STRING_OF_STATE(STATE_SENTPRACCEPT)
313 GET_STRING_OF_STATE(STATE_SENTACCEPT)
314 GET_STRING_OF_STATE(STATE_RECEIVEDPRACCEPT)
315 GET_STRING_OF_STATE(STATE_RECEIVEDACCEPT)
316 GET_STRING_OF_STATE(STATE_SENTMODIFY)
317 GET_STRING_OF_STATE(STATE_RECEIVEDMODIFY)
318 GET_STRING_OF_STATE(STATE_SENTREJECT)
319 GET_STRING_OF_STATE(STATE_RECEIVEDREJECT)
320 GET_STRING_OF_STATE(STATE_SENTREDIRECT)
321 GET_STRING_OF_STATE(STATE_SENTTERMINATE)
322 GET_STRING_OF_STATE(STATE_RECEIVEDTERMINATE)
323 GET_STRING_OF_STATE(STATE_INPROGRESS)
324 GET_STRING_OF_STATE(STATE_DEINIT)
325 default:
326 ASSERT(false);
327 break;
328 }
329 return result;
330}
331
332#define GET_STRING_OF_ERROR(err) \
333 case cricket::BaseSession::err: \
334 result = #err; \
335 break;
336
337static std::string GetErrorString(cricket::BaseSession::Error err) {
338 std::string result;
339 switch (err) {
340 GET_STRING_OF_ERROR(ERROR_NONE)
341 GET_STRING_OF_ERROR(ERROR_TIME)
342 GET_STRING_OF_ERROR(ERROR_RESPONSE)
343 GET_STRING_OF_ERROR(ERROR_NETWORK)
344 GET_STRING_OF_ERROR(ERROR_CONTENT)
345 GET_STRING_OF_ERROR(ERROR_TRANSPORT)
346 default:
347 ASSERT(false);
348 break;
349 }
350 return result;
351}
352
353static bool SetSessionStateFailed(cricket::ContentSource source,
354 cricket::BaseSession::Error err,
355 std::string* err_desc) {
356 std::string set_state_err = kUpdateStateFailed;
357 set_state_err.append(GetErrorString(err));
358 return BadSdp(source, set_state_err, err_desc);
359}
360
361// Help class used to remember if a a remote peer has requested ice restart by
362// by sending a description with new ice ufrag and password.
363class IceRestartAnswerLatch {
364 public:
365 IceRestartAnswerLatch() : ice_restart_(false) { }
366
wu@webrtc.org91053e72013-08-10 07:18:04 +0000367 // Returns true if CheckForRemoteIceRestart has been called with a new session
368 // description where ice password and ufrag has changed since last time
369 // Reset() was called.
370 bool Get() const {
371 return ice_restart_;
372 }
373
374 void Reset() {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000375 if (ice_restart_) {
376 ice_restart_ = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000377 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000378 }
379
380 void CheckForRemoteIceRestart(
381 const SessionDescriptionInterface* old_desc,
382 const SessionDescriptionInterface* new_desc) {
383 if (!old_desc || new_desc->type() != SessionDescriptionInterface::kOffer) {
384 return;
385 }
386 const SessionDescription* new_sd = new_desc->description();
387 const SessionDescription* old_sd = old_desc->description();
388 const ContentInfos& contents = new_sd->contents();
389 for (size_t index = 0; index < contents.size(); ++index) {
390 const ContentInfo* cinfo = &contents[index];
391 if (cinfo->rejected) {
392 continue;
393 }
394 // If the content isn't rejected, check if ufrag and password has
395 // changed.
396 const cricket::TransportDescription* new_transport_desc =
397 new_sd->GetTransportDescriptionByName(cinfo->name);
398 const cricket::TransportDescription* old_transport_desc =
399 old_sd->GetTransportDescriptionByName(cinfo->name);
400 if (!new_transport_desc || !old_transport_desc) {
401 // No transport description exist. This is not an ice restart.
402 continue;
403 }
404 if (new_transport_desc->ice_pwd != old_transport_desc->ice_pwd &&
405 new_transport_desc->ice_ufrag != old_transport_desc->ice_ufrag) {
406 LOG(LS_INFO) << "Remote peer request ice restart.";
407 ice_restart_ = true;
408 break;
409 }
410 }
411 }
412
413 private:
414 bool ice_restart_;
415};
416
wu@webrtc.org91053e72013-08-10 07:18:04 +0000417WebRtcSession::WebRtcSession(
418 cricket::ChannelManager* channel_manager,
419 talk_base::Thread* signaling_thread,
420 talk_base::Thread* worker_thread,
421 cricket::PortAllocator* port_allocator,
422 MediaStreamSignaling* mediastream_signaling)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000423 : cricket::BaseSession(signaling_thread, worker_thread, port_allocator,
424 talk_base::ToString(talk_base::CreateRandomId64() &
425 LLONG_MAX),
426 cricket::NS_JINGLE_RTP, false),
427 // RFC 3264: The numeric value of the session id and version in the
428 // o line MUST be representable with a "64 bit signed integer".
429 // Due to this constraint session id |sid_| is max limited to LLONG_MAX.
430 channel_manager_(channel_manager),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000431 mediastream_signaling_(mediastream_signaling),
432 ice_observer_(NULL),
433 ice_connection_state_(PeerConnectionInterface::kIceConnectionNew),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000434 older_version_remote_peer_(false),
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000435 dtls_enabled_(false),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000436 data_channel_type_(cricket::DCT_NONE),
437 ice_restart_latch_(new IceRestartAnswerLatch) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000438}
439
440WebRtcSession::~WebRtcSession() {
441 if (voice_channel_.get()) {
442 SignalVoiceChannelDestroyed();
443 channel_manager_->DestroyVoiceChannel(voice_channel_.release());
444 }
445 if (video_channel_.get()) {
446 SignalVideoChannelDestroyed();
447 channel_manager_->DestroyVideoChannel(video_channel_.release());
448 }
449 if (data_channel_.get()) {
450 SignalDataChannelDestroyed();
451 channel_manager_->DestroyDataChannel(data_channel_.release());
452 }
453 for (size_t i = 0; i < saved_candidates_.size(); ++i) {
454 delete saved_candidates_[i];
455 }
456 delete identity();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000457}
458
wu@webrtc.org91053e72013-08-10 07:18:04 +0000459bool WebRtcSession::Initialize(
460 const MediaConstraintsInterface* constraints,
461 DTLSIdentityServiceInterface* dtls_identity_service) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000462 // TODO(perkj): Take |constraints| into consideration. Return false if not all
463 // mandatory constraints can be fulfilled. Note that |constraints|
464 // can be null.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000465 bool value;
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000466
467 // Enable DTLS by default if |dtls_identity_service| is valid.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000468 dtls_enabled_ = (dtls_identity_service != NULL);
469 // |constraints| can override the default |dtls_enabled_| value.
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000470 if (FindConstraint(
471 constraints,
472 MediaConstraintsInterface::kEnableDtlsSrtp,
473 &value, NULL)) {
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000474 dtls_enabled_ = value;
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000475 }
476
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000477 // Enable creation of RTP data channels if the kEnableRtpDataChannels is set.
478 // It takes precendence over the kEnableSctpDataChannels constraint.
479 if (FindConstraint(
480 constraints, MediaConstraintsInterface::kEnableRtpDataChannels,
481 &value, NULL) && value) {
482 LOG(LS_INFO) << "Allowing RTP data engine.";
483 data_channel_type_ = cricket::DCT_RTP;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000484 } else {
485 bool sctp_enabled = FindConstraint(
486 constraints,
487 MediaConstraintsInterface::kEnableSctpDataChannels,
488 &value, NULL) && value;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000489 // DTLS has to be enabled to use SCTP.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000490 if (sctp_enabled && dtls_enabled_) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000491 LOG(LS_INFO) << "Allowing SCTP data engine.";
492 data_channel_type_ = cricket::DCT_SCTP;
493 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000494 }
495 if (data_channel_type_ != cricket::DCT_NONE) {
496 mediastream_signaling_->SetDataChannelFactory(this);
497 }
498
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000499 const cricket::VideoCodec default_codec(
500 JsepSessionDescription::kDefaultVideoCodecId,
501 JsepSessionDescription::kDefaultVideoCodecName,
502 JsepSessionDescription::kMaxVideoCodecWidth,
503 JsepSessionDescription::kMaxVideoCodecHeight,
504 JsepSessionDescription::kDefaultVideoCodecFramerate,
505 JsepSessionDescription::kDefaultVideoCodecPreference);
506 channel_manager_->SetDefaultVideoEncoderConfig(
507 cricket::VideoEncoderConfig(default_codec));
wu@webrtc.org91053e72013-08-10 07:18:04 +0000508
509 webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
510 signaling_thread(),
511 channel_manager_,
512 mediastream_signaling_,
513 dtls_identity_service,
514 this,
515 id(),
516 data_channel_type_,
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000517 dtls_enabled_));
wu@webrtc.org91053e72013-08-10 07:18:04 +0000518
519 webrtc_session_desc_factory_->SignalIdentityReady.connect(
520 this, &WebRtcSession::OnIdentityReady);
mallinath@webrtc.org7e809c32013-09-30 18:59:08 +0000521
522 // Disable encryption if kDisableEncryption is set.
523 if (FindConstraint(
524 constraints,
525 MediaConstraintsInterface::kInternalDisableEncryption,
526 &value, NULL) && value) {
527 webrtc_session_desc_factory_->set_secure(cricket::SEC_DISABLED);
528 }
529
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000530 return true;
531}
532
533void WebRtcSession::Terminate() {
534 SetState(STATE_RECEIVEDTERMINATE);
535 RemoveUnusedChannelsAndTransports(NULL);
536 ASSERT(voice_channel_.get() == NULL);
537 ASSERT(video_channel_.get() == NULL);
538 ASSERT(data_channel_.get() == NULL);
539}
540
541bool WebRtcSession::StartCandidatesAllocation() {
542 // SpeculativelyConnectTransportChannels, will call ConnectChannels method
543 // from TransportProxy to start gathering ice candidates.
544 SpeculativelyConnectAllTransportChannels();
545 if (!saved_candidates_.empty()) {
546 // If there are saved candidates which arrived before local description is
547 // set, copy those to remote description.
548 CopySavedCandidates(remote_desc_.get());
549 }
550 // Push remote candidates present in remote description to transport channels.
551 UseCandidatesInSessionDescription(remote_desc_.get());
552 return true;
553}
554
555void WebRtcSession::set_secure_policy(
556 cricket::SecureMediaPolicy secure_policy) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000557 webrtc_session_desc_factory_->set_secure(secure_policy);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000558}
559
wu@webrtc.org91053e72013-08-10 07:18:04 +0000560cricket::SecureMediaPolicy WebRtcSession::secure_policy() const {
561 return webrtc_session_desc_factory_->secure();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000562}
563
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000564bool WebRtcSession::GetSslRole(talk_base::SSLRole* role) {
565 if (local_description() == NULL || remote_description() == NULL) {
566 LOG(LS_INFO) << "Local and Remote descriptions must be applied to get "
567 << "SSL Role of the session.";
568 return false;
569 }
570
571 // TODO(mallinath) - Return role of each transport, as role may differ from
572 // one another.
573 // In current implementaion we just return the role of first transport in the
574 // transport map.
575 for (cricket::TransportMap::const_iterator iter = transport_proxies().begin();
576 iter != transport_proxies().end(); ++iter) {
577 if (iter->second->impl()) {
578 return iter->second->impl()->GetSslRole(role);
579 }
580 }
581 return false;
582}
583
wu@webrtc.org91053e72013-08-10 07:18:04 +0000584void WebRtcSession::CreateOffer(CreateSessionDescriptionObserver* observer,
585 const MediaConstraintsInterface* constraints) {
586 webrtc_session_desc_factory_->CreateOffer(observer, constraints);
587}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000588
wu@webrtc.org91053e72013-08-10 07:18:04 +0000589void WebRtcSession::CreateAnswer(CreateSessionDescriptionObserver* observer,
590 const MediaConstraintsInterface* constraints) {
591 webrtc_session_desc_factory_->CreateAnswer(observer, constraints);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000592}
593
594bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
595 std::string* err_desc) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000596 // Takes the ownership of |desc| regardless of the result.
597 talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
598
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000599 // Validate SDP.
600 if (!ValidateSessionDescription(desc, cricket::CS_LOCAL, err_desc)) {
601 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000602 }
603
604 // Update the initiator flag if this session is the initiator.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000605 Action action = GetAction(desc->type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000606 if (state() == STATE_INIT && action == kOffer) {
607 set_initiator(true);
608 }
609
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000610 cricket::SecureMediaPolicy secure_policy =
611 webrtc_session_desc_factory_->secure();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000612 // Update the MediaContentDescription crypto settings as per the policy set.
wu@webrtc.org91053e72013-08-10 07:18:04 +0000613 UpdateSessionDescriptionSecurePolicy(secure_policy, desc->description());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000614
615 set_local_description(desc->description()->Copy());
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000616 local_desc_.reset(desc_temp.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000617
618 // Transport and Media channels will be created only when offer is set.
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000619 if (action == kOffer && !CreateChannels(local_desc_->description())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000620 // TODO(mallinath) - Handle CreateChannel failure, as new local description
621 // is applied. Restore back to old description.
622 return BadLocalSdp(kCreateChannelFailed, err_desc);
623 }
624
625 // Remove channel and transport proxies, if MediaContentDescription is
626 // rejected.
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000627 RemoveUnusedChannelsAndTransports(local_desc_->description());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000628
629 if (!UpdateSessionState(action, cricket::CS_LOCAL,
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000630 local_desc_->description(), err_desc)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000631 return false;
632 }
633 // Kick starting the ice candidates allocation.
634 StartCandidatesAllocation();
635
636 // Update state and SSRC of local MediaStreams and DataChannels based on the
637 // local session description.
638 mediastream_signaling_->OnLocalDescriptionChanged(local_desc_.get());
639
640 if (error() != cricket::BaseSession::ERROR_NONE) {
641 return BadLocalSdp(SessionErrorMsg(error()), err_desc);
642 }
643 return true;
644}
645
646bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
647 std::string* err_desc) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000648 // Takes the ownership of |desc| regardless of the result.
649 talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
650
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000651 // Validate SDP.
652 if (!ValidateSessionDescription(desc, cricket::CS_REMOTE, err_desc)) {
653 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000654 }
655
656 // Transport and Media channels will be created only when offer is set.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000657 Action action = GetAction(desc->type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000658 if (action == kOffer && !CreateChannels(desc->description())) {
659 // TODO(mallinath) - Handle CreateChannel failure, as new local description
660 // is applied. Restore back to old description.
661 return BadRemoteSdp(kCreateChannelFailed, err_desc);
662 }
663
664 // Remove channel and transport proxies, if MediaContentDescription is
665 // rejected.
666 RemoveUnusedChannelsAndTransports(desc->description());
667
668 // NOTE: Candidates allocation will be initiated only when SetLocalDescription
669 // is called.
670 set_remote_description(desc->description()->Copy());
671 if (!UpdateSessionState(action, cricket::CS_REMOTE,
672 desc->description(), err_desc)) {
673 return false;
674 }
675
676 // Update remote MediaStreams.
677 mediastream_signaling_->OnRemoteDescriptionChanged(desc);
678 if (local_description() && !UseCandidatesInSessionDescription(desc)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000679 return BadRemoteSdp(kInvalidCandidates, err_desc);
680 }
681
682 // Copy all saved candidates.
683 CopySavedCandidates(desc);
684 // We retain all received candidates.
wu@webrtc.org91053e72013-08-10 07:18:04 +0000685 WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
686 remote_desc_.get(), desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000687 // Check if this new SessionDescription contains new ice ufrag and password
688 // that indicates the remote peer requests ice restart.
689 ice_restart_latch_->CheckForRemoteIceRestart(remote_desc_.get(),
690 desc);
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000691 remote_desc_.reset(desc_temp.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000692 if (error() != cricket::BaseSession::ERROR_NONE) {
693 return BadRemoteSdp(SessionErrorMsg(error()), err_desc);
694 }
695 return true;
696}
697
698bool WebRtcSession::UpdateSessionState(
699 Action action, cricket::ContentSource source,
700 const cricket::SessionDescription* desc,
701 std::string* err_desc) {
702 // If there's already a pending error then no state transition should happen.
703 // But all call-sites should be verifying this before calling us!
704 ASSERT(error() == cricket::BaseSession::ERROR_NONE);
705 if (action == kOffer) {
706 if (!PushdownTransportDescription(source, cricket::CA_OFFER)) {
707 return BadSdp(source, kPushDownOfferTDFailed, err_desc);
708 }
709 SetState(source == cricket::CS_LOCAL ?
710 STATE_SENTINITIATE : STATE_RECEIVEDINITIATE);
711 if (error() != cricket::BaseSession::ERROR_NONE) {
712 return SetSessionStateFailed(source, error(), err_desc);
713 }
714 } else if (action == kPrAnswer) {
715 if (!PushdownTransportDescription(source, cricket::CA_PRANSWER)) {
716 return BadSdp(source, kPushDownPranswerTDFailed, err_desc);
717 }
718 EnableChannels();
719 SetState(source == cricket::CS_LOCAL ?
720 STATE_SENTPRACCEPT : STATE_RECEIVEDPRACCEPT);
721 if (error() != cricket::BaseSession::ERROR_NONE) {
722 return SetSessionStateFailed(source, error(), err_desc);
723 }
724 } else if (action == kAnswer) {
725 if (!PushdownTransportDescription(source, cricket::CA_ANSWER)) {
726 return BadSdp(source, kPushDownAnswerTDFailed, err_desc);
727 }
728 MaybeEnableMuxingSupport();
729 EnableChannels();
730 SetState(source == cricket::CS_LOCAL ?
731 STATE_SENTACCEPT : STATE_RECEIVEDACCEPT);
732 if (error() != cricket::BaseSession::ERROR_NONE) {
733 return SetSessionStateFailed(source, error(), err_desc);
734 }
735 }
736 return true;
737}
738
739WebRtcSession::Action WebRtcSession::GetAction(const std::string& type) {
740 if (type == SessionDescriptionInterface::kOffer) {
741 return WebRtcSession::kOffer;
742 } else if (type == SessionDescriptionInterface::kPrAnswer) {
743 return WebRtcSession::kPrAnswer;
744 } else if (type == SessionDescriptionInterface::kAnswer) {
745 return WebRtcSession::kAnswer;
746 }
747 ASSERT(false && "unknown action type");
748 return WebRtcSession::kOffer;
749}
750
751bool WebRtcSession::ProcessIceMessage(const IceCandidateInterface* candidate) {
752 if (state() == STATE_INIT) {
753 LOG(LS_ERROR) << "ProcessIceMessage: ICE candidates can't be added "
754 << "without any offer (local or remote) "
755 << "session description.";
756 return false;
757 }
758
759 if (!candidate) {
760 LOG(LS_ERROR) << "ProcessIceMessage: Candidate is NULL";
761 return false;
762 }
763
764 if (!local_description() || !remote_description()) {
765 LOG(LS_INFO) << "ProcessIceMessage: Remote description not set, "
766 << "save the candidate for later use.";
767 saved_candidates_.push_back(
768 new JsepIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(),
769 candidate->candidate()));
770 return true;
771 }
772
773 // Add this candidate to the remote session description.
774 if (!remote_desc_->AddCandidate(candidate)) {
775 LOG(LS_ERROR) << "ProcessIceMessage: Candidate cannot be used";
776 return false;
777 }
778
779 return UseCandidatesInSessionDescription(remote_desc_.get());
780}
781
782bool WebRtcSession::GetTrackIdBySsrc(uint32 ssrc, std::string* id) {
783 if (GetLocalTrackId(ssrc, id)) {
784 if (GetRemoteTrackId(ssrc, id)) {
785 LOG(LS_WARNING) << "SSRC " << ssrc
786 << " exists in both local and remote descriptions";
787 return true; // We return the remote track id.
788 }
789 return true;
790 } else {
791 return GetRemoteTrackId(ssrc, id);
792 }
793}
794
795bool WebRtcSession::GetLocalTrackId(uint32 ssrc, std::string* track_id) {
796 if (!BaseSession::local_description())
797 return false;
798 return webrtc::GetTrackIdBySsrc(
799 BaseSession::local_description(), ssrc, track_id);
800}
801
802bool WebRtcSession::GetRemoteTrackId(uint32 ssrc, std::string* track_id) {
803 if (!BaseSession::remote_description())
804 return false;
805 return webrtc::GetTrackIdBySsrc(
806 BaseSession::remote_description(), ssrc, track_id);
807}
808
809std::string WebRtcSession::BadStateErrMsg(
810 const std::string& type, State state) {
811 std::ostringstream desc;
812 desc << "Called with type in wrong state, "
813 << "type: " << type << " state: " << GetStateString(state);
814 return desc.str();
815}
816
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000817void WebRtcSession::SetAudioPlayout(uint32 ssrc, bool enable,
818 cricket::AudioRenderer* renderer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000819 ASSERT(signaling_thread()->IsCurrent());
820 if (!voice_channel_) {
821 LOG(LS_ERROR) << "SetAudioPlayout: No audio channel exists.";
822 return;
823 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000824 if (!voice_channel_->SetRemoteRenderer(ssrc, renderer)) {
825 // SetRenderer() can fail if the ssrc does not match any playout channel.
826 LOG(LS_ERROR) << "SetAudioPlayout: ssrc is incorrect: " << ssrc;
827 return;
828 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000829 if (!voice_channel_->SetOutputScaling(ssrc, enable ? 1 : 0, enable ? 1 : 0)) {
830 // Allow that SetOutputScaling fail if |enable| is false but assert
831 // otherwise. This in the normal case when the underlying media channel has
832 // already been deleted.
833 ASSERT(enable == false);
834 }
835}
836
837void WebRtcSession::SetAudioSend(uint32 ssrc, bool enable,
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000838 const cricket::AudioOptions& options,
839 cricket::AudioRenderer* renderer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000840 ASSERT(signaling_thread()->IsCurrent());
841 if (!voice_channel_) {
842 LOG(LS_ERROR) << "SetAudioSend: No audio channel exists.";
843 return;
844 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000845 if (!voice_channel_->SetLocalRenderer(ssrc, renderer)) {
846 // SetRenderer() can fail if the ssrc does not match any send channel.
847 LOG(LS_ERROR) << "SetAudioSend: ssrc is incorrect: " << ssrc;
848 return;
849 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000850 if (!voice_channel_->MuteStream(ssrc, !enable)) {
851 // Allow that MuteStream fail if |enable| is false but assert otherwise.
852 // This in the normal case when the underlying media channel has already
853 // been deleted.
854 ASSERT(enable == false);
855 return;
856 }
857 if (enable)
858 voice_channel_->SetChannelOptions(options);
859}
860
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000861bool WebRtcSession::SetCaptureDevice(uint32 ssrc,
862 cricket::VideoCapturer* camera) {
863 ASSERT(signaling_thread()->IsCurrent());
864
865 if (!video_channel_.get()) {
866 // |video_channel_| doesnt't exist. Probably because the remote end doesnt't
867 // support video.
868 LOG(LS_WARNING) << "Video not used in this call.";
869 return false;
870 }
871 if (!video_channel_->SetCapturer(ssrc, camera)) {
872 // Allow that SetCapturer fail if |camera| is NULL but assert otherwise.
873 // This in the normal case when the underlying media channel has already
874 // been deleted.
875 ASSERT(camera == NULL);
876 return false;
877 }
878 return true;
879}
880
881void WebRtcSession::SetVideoPlayout(uint32 ssrc,
882 bool enable,
883 cricket::VideoRenderer* renderer) {
884 ASSERT(signaling_thread()->IsCurrent());
885 if (!video_channel_) {
886 LOG(LS_WARNING) << "SetVideoPlayout: No video channel exists.";
887 return;
888 }
889 if (!video_channel_->SetRenderer(ssrc, enable ? renderer : NULL)) {
890 // Allow that SetRenderer fail if |renderer| is NULL but assert otherwise.
891 // This in the normal case when the underlying media channel has already
892 // been deleted.
893 ASSERT(renderer == NULL);
894 }
895}
896
897void WebRtcSession::SetVideoSend(uint32 ssrc, bool enable,
898 const cricket::VideoOptions* options) {
899 ASSERT(signaling_thread()->IsCurrent());
900 if (!video_channel_) {
901 LOG(LS_WARNING) << "SetVideoSend: No video channel exists.";
902 return;
903 }
904 if (!video_channel_->MuteStream(ssrc, !enable)) {
905 // Allow that MuteStream fail if |enable| is false but assert otherwise.
906 // This in the normal case when the underlying media channel has already
907 // been deleted.
908 ASSERT(enable == false);
909 return;
910 }
911 if (enable && options)
912 video_channel_->SetChannelOptions(*options);
913}
914
915bool WebRtcSession::CanInsertDtmf(const std::string& track_id) {
916 ASSERT(signaling_thread()->IsCurrent());
917 if (!voice_channel_) {
918 LOG(LS_ERROR) << "CanInsertDtmf: No audio channel exists.";
919 return false;
920 }
921 uint32 send_ssrc = 0;
922 // The Dtmf is negotiated per channel not ssrc, so we only check if the ssrc
923 // exists.
924 if (!GetAudioSsrcByTrackId(BaseSession::local_description(), track_id,
925 &send_ssrc)) {
926 LOG(LS_ERROR) << "CanInsertDtmf: Track does not exist: " << track_id;
927 return false;
928 }
929 return voice_channel_->CanInsertDtmf();
930}
931
932bool WebRtcSession::InsertDtmf(const std::string& track_id,
933 int code, int duration) {
934 ASSERT(signaling_thread()->IsCurrent());
935 if (!voice_channel_) {
936 LOG(LS_ERROR) << "InsertDtmf: No audio channel exists.";
937 return false;
938 }
939 uint32 send_ssrc = 0;
940 if (!VERIFY(GetAudioSsrcByTrackId(BaseSession::local_description(),
941 track_id, &send_ssrc))) {
942 LOG(LS_ERROR) << "InsertDtmf: Track does not exist: " << track_id;
943 return false;
944 }
945 if (!voice_channel_->InsertDtmf(send_ssrc, code, duration,
946 cricket::DF_SEND)) {
947 LOG(LS_ERROR) << "Failed to insert DTMF to channel.";
948 return false;
949 }
950 return true;
951}
952
953sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() {
954 return &SignalVoiceChannelDestroyed;
955}
956
wu@webrtc.org78187522013-10-07 23:32:02 +0000957bool WebRtcSession::SendData(const cricket::SendDataParams& params,
958 const talk_base::Buffer& payload,
959 cricket::SendDataResult* result) {
960 if (!data_channel_.get()) {
961 LOG(LS_ERROR) << "SendData called when data_channel_ is NULL.";
962 return false;
963 }
964 return data_channel_->SendData(params, payload, result);
965}
966
967bool WebRtcSession::ConnectDataChannel(DataChannel* webrtc_data_channel) {
968 if (!data_channel_.get()) {
969 LOG(LS_ERROR) << "ConnectDataChannel called when data_channel_ is NULL.";
970 return false;
971 }
972
973 data_channel_->SignalReadyToSendData.connect(webrtc_data_channel,
974 &DataChannel::OnChannelReady);
975 data_channel_->SignalDataReceived.connect(webrtc_data_channel,
976 &DataChannel::OnDataReceived);
977 cricket::StreamParams params =
978 cricket::StreamParams::CreateLegacy(webrtc_data_channel->id());
979 data_channel_->AddRecvStream(params);
980 data_channel_->AddSendStream(params);
981 return true;
982}
983
984void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) {
985 data_channel_->RemoveSendStream(webrtc_data_channel->id());
986 data_channel_->RemoveRecvStream(webrtc_data_channel->id());
987 data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
988 data_channel_->SignalDataReceived.disconnect(webrtc_data_channel);
989}
990
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000991talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
wu@webrtc.org78187522013-10-07 23:32:02 +0000992 const std::string& label,
993 const DataChannelInit* config) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000994 if (state() == STATE_RECEIVEDTERMINATE) {
995 return NULL;
996 }
997 if (data_channel_type_ == cricket::DCT_NONE) {
998 LOG(LS_ERROR) << "CreateDataChannel: Data is not supported in this call.";
999 return NULL;
1000 }
1001 DataChannelInit new_config = config ? (*config) : DataChannelInit();
1002
1003 if (data_channel_type_ == cricket::DCT_SCTP) {
1004 if (new_config.id < 0) {
1005 if (!mediastream_signaling_->AllocateSctpId(&new_config.id)) {
1006 LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel.";
1007 return NULL;
1008 }
1009 } else if (!mediastream_signaling_->IsSctpIdAvailable(new_config.id)) {
1010 LOG(LS_ERROR) << "Failed to create a SCTP data channel "
1011 << "because the id is already in use or out of range.";
1012 return NULL;
1013 }
1014 }
wu@webrtc.org91053e72013-08-10 07:18:04 +00001015
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001016 talk_base::scoped_refptr<DataChannel> channel(
wu@webrtc.org78187522013-10-07 23:32:02 +00001017 DataChannel::Create(this, data_channel_type_, label, &new_config));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001018 if (channel == NULL)
1019 return NULL;
1020 if (!mediastream_signaling_->AddDataChannel(channel))
1021 return NULL;
wu@webrtc.org91053e72013-08-10 07:18:04 +00001022 if (data_channel_type_ == cricket::DCT_SCTP) {
1023 if (config == NULL) {
1024 LOG(LS_WARNING) << "Could not send data channel OPEN message"
1025 << " because of NULL config.";
1026 return NULL;
1027 }
1028 if (data_channel_.get()) {
1029 channel->SetReceiveSsrc(new_config.id);
1030 channel->SetSendSsrc(new_config.id);
wu@webrtc.org91053e72013-08-10 07:18:04 +00001031 }
1032 if (!config->negotiated) {
1033 talk_base::Buffer *payload = new talk_base::Buffer;
1034 if (!mediastream_signaling_->WriteDataChannelOpenMessage(
1035 label, *config, payload)) {
1036 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 }
1371 data_channel_->SignalDataReceived.connect(
1372 this, &WebRtcSession::OnDataReceived);
1373 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.org91053e72013-08-10 07:18:04 +00001389// Look for OPEN messages and set up data channels in response.
1390void WebRtcSession::OnDataReceived(
1391 cricket::DataChannel* channel,
1392 const cricket::ReceiveDataParams& params,
1393 const talk_base::Buffer& payload) {
1394 if (params.type != cricket::DMT_CONTROL) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001395 return;
1396 }
1397
wu@webrtc.org91053e72013-08-10 07:18:04 +00001398 std::string label;
1399 DataChannelInit config;
1400 if (!mediastream_signaling_->ParseDataChannelOpenMessage(
1401 payload, &label, &config)) {
1402 LOG(LS_WARNING) << "Failed to parse data channel OPEN message.";
1403 return;
1404 }
1405
1406 config.negotiated = true; // This is the negotiation.
1407
1408 if (!mediastream_signaling_->AddDataChannelFromOpenMessage(
1409 label, config)) {
1410 LOG(LS_WARNING) << "Failed to create data channel from OPEN message.";
1411 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001412 }
1413}
1414
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001415// Returns false if bundle is enabled and rtcp_mux is disabled.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001416bool WebRtcSession::ValidateBundleSettings(const SessionDescription* desc) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001417 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1418 if (!bundle_enabled)
1419 return true;
1420
1421 const cricket::ContentGroup* bundle_group =
1422 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
1423 ASSERT(bundle_group != NULL);
1424
1425 const cricket::ContentInfos& contents = desc->contents();
1426 for (cricket::ContentInfos::const_iterator citer = contents.begin();
1427 citer != contents.end(); ++citer) {
1428 const cricket::ContentInfo* content = (&*citer);
1429 ASSERT(content != NULL);
1430 if (bundle_group->HasContentName(content->name) &&
1431 !content->rejected && content->type == cricket::NS_JINGLE_RTP) {
1432 if (!HasRtcpMuxEnabled(content))
1433 return false;
1434 }
1435 }
1436 // RTCP-MUX is enabled in all the contents.
1437 return true;
1438}
1439
1440bool WebRtcSession::HasRtcpMuxEnabled(
1441 const cricket::ContentInfo* content) {
1442 const cricket::MediaContentDescription* description =
1443 static_cast<cricket::MediaContentDescription*>(content->description);
1444 return description->rtcp_mux();
1445}
1446
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001447bool WebRtcSession::ValidateSessionDescription(
1448 const SessionDescriptionInterface* sdesc,
1449 cricket::ContentSource source, std::string* error_desc) {
1450
1451 if (error() != cricket::BaseSession::ERROR_NONE) {
1452 return BadSdp(source, SessionErrorMsg(error()), error_desc);
1453 }
1454
1455 if (!sdesc || !sdesc->description()) {
1456 return BadSdp(source, kInvalidSdp, error_desc);
1457 }
1458
1459 std::string type = sdesc->type();
1460 Action action = GetAction(sdesc->type());
1461 if (source == cricket::CS_LOCAL) {
1462 if (!ExpectSetLocalDescription(action))
1463 return BadSdp(source, BadStateErrMsg(type, state()), error_desc);
1464 } else {
1465 if (!ExpectSetRemoteDescription(action))
1466 return BadSdp(source, BadStateErrMsg(type, state()), error_desc);
1467 }
1468
1469 // Verify crypto settings.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +00001470 std::string crypto_error;
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001471 if (webrtc_session_desc_factory_->secure() == cricket::SEC_REQUIRED &&
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +00001472 !VerifyCrypto(sdesc->description(), dtls_enabled_, &crypto_error)) {
1473 return BadSdp(source, crypto_error, error_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001474 }
1475
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001476 // Verify ice-ufrag and ice-pwd.
1477 if (!VerifyIceUfragPwdPresent(sdesc->description())) {
1478 return BadSdp(source, kSdpWithoutIceUfragPwd, error_desc);
1479 }
1480
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001481 if (!ValidateBundleSettings(sdesc->description())) {
1482 return BadSdp(source, kBundleWithoutRtcpMux, error_desc);
1483 }
1484
1485 // Verify m-lines in Answer when compared against Offer.
1486 if (action == kAnswer) {
1487 const cricket::SessionDescription* offer_desc =
1488 (source == cricket::CS_LOCAL) ? remote_description()->description() :
1489 local_description()->description();
1490 if (!VerifyMediaDescriptions(sdesc->description(), offer_desc)) {
1491 return BadSdp(source, kMlineMismatch, error_desc);
1492 }
1493 }
1494
1495 return true;
1496}
1497
1498bool WebRtcSession::ExpectSetLocalDescription(Action action) {
1499 return ((action == kOffer && state() == STATE_INIT) ||
1500 // update local offer
1501 (action == kOffer && state() == STATE_SENTINITIATE) ||
1502 // update the current ongoing session.
1503 (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
1504 (action == kOffer && state() == STATE_SENTACCEPT) ||
1505 (action == kOffer && state() == STATE_INPROGRESS) ||
1506 // accept remote offer
1507 (action == kAnswer && state() == STATE_RECEIVEDINITIATE) ||
1508 (action == kAnswer && state() == STATE_SENTPRACCEPT) ||
1509 (action == kPrAnswer && state() == STATE_RECEIVEDINITIATE) ||
1510 (action == kPrAnswer && state() == STATE_SENTPRACCEPT));
1511}
1512
1513bool WebRtcSession::ExpectSetRemoteDescription(Action action) {
1514 return ((action == kOffer && state() == STATE_INIT) ||
1515 // update remote offer
1516 (action == kOffer && state() == STATE_RECEIVEDINITIATE) ||
1517 // update the current ongoing session
1518 (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
1519 (action == kOffer && state() == STATE_SENTACCEPT) ||
1520 (action == kOffer && state() == STATE_INPROGRESS) ||
1521 // accept local offer
1522 (action == kAnswer && state() == STATE_SENTINITIATE) ||
1523 (action == kAnswer && state() == STATE_RECEIVEDPRACCEPT) ||
1524 (action == kPrAnswer && state() == STATE_SENTINITIATE) ||
1525 (action == kPrAnswer && state() == STATE_RECEIVEDPRACCEPT));
1526}
1527
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001528} // namespace webrtc