blob: 83f89128284ff2dc6b5f1016bad0ba1fba05279d [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
henrike@webrtc.org28e20752013-07-10 00:45:36 +000072// Error messages
73const char kSetLocalSdpFailed[] = "SetLocalDescription failed: ";
74const char kSetRemoteSdpFailed[] = "SetRemoteDescription failed: ";
75const char kCreateChannelFailed[] = "Failed to create channels.";
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000076const char kBundleWithoutRtcpMux[] = "RTCP-MUX must be enabled when BUNDLE "
77 "is enabled.";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000078const char kInvalidCandidates[] = "Description contains invalid candidates.";
79const char kInvalidSdp[] = "Invalid session description.";
80const char kMlineMismatch[] =
81 "Offer and answer descriptions m-lines are not matching. "
82 "Rejecting answer.";
83const char kSdpWithoutCrypto[] = "Called with a SDP without crypto enabled.";
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000084const char kSdpWithoutSdesAndDtlsDisabled[] =
85 "Called with a SDP without SDES crypto and DTLS disabled locally.";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000086const char kSessionError[] = "Session error code: ";
87const char kUpdateStateFailed[] = "Failed to update session state: ";
88const char kPushDownOfferTDFailed[] =
89 "Failed to push down offer transport description.";
90const char kPushDownPranswerTDFailed[] =
91 "Failed to push down pranswer transport description.";
92const char kPushDownAnswerTDFailed[] =
93 "Failed to push down answer transport description.";
94
95// Compares |answer| against |offer|. Comparision is done
96// for number of m-lines in answer against offer. If matches true will be
97// returned otherwise false.
98static bool VerifyMediaDescriptions(
99 const SessionDescription* answer, const SessionDescription* offer) {
100 if (offer->contents().size() != answer->contents().size())
101 return false;
102
103 for (size_t i = 0; i < offer->contents().size(); ++i) {
104 if ((offer->contents()[i].name) != answer->contents()[i].name) {
105 return false;
106 }
107 }
108 return true;
109}
110
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000111// Checks that each non-rejected content has SDES crypto keys or a DTLS
112// fingerprint. Mismatches, such as replying with a DTLS fingerprint to SDES
113// keys, will be caught in Transport negotiation, and backstopped by Channel's
114// |secure_required| check.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000115static bool VerifyCrypto(const SessionDescription* desc,
116 bool dtls_enabled,
117 std::string* error) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000118 const ContentInfos& contents = desc->contents();
119 for (size_t index = 0; index < contents.size(); ++index) {
120 const ContentInfo* cinfo = &contents[index];
121 if (cinfo->rejected) {
122 continue;
123 }
124
125 // If the content isn't rejected, crypto must be present.
126 const MediaContentDescription* media =
127 static_cast<const MediaContentDescription*>(cinfo->description);
128 const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name);
129 if (!media || !tinfo) {
130 // Something is not right.
131 LOG(LS_ERROR) << kInvalidSdp;
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000132 *error = kInvalidSdp;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000133 return false;
134 }
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000135 if (media->cryptos().empty()) {
136 if (!tinfo->description.identity_fingerprint) {
137 // Crypto must be supplied.
138 LOG(LS_WARNING) << "Session description must have SDES or DTLS-SRTP.";
139 *error = kSdpWithoutCrypto;
140 return false;
141 }
142 if (!dtls_enabled) {
143 LOG(LS_WARNING) <<
144 "Session description must have SDES when DTLS disabled.";
145 *error = kSdpWithoutSdesAndDtlsDisabled;
146 return false;
147 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000148 }
149 }
150
151 return true;
152}
153
wu@webrtc.org91053e72013-08-10 07:18:04 +0000154// Forces |sdesc->crypto_required| to the appropriate state based on the
155// current security policy, to ensure a failure occurs if there is an error
156// in crypto negotiation.
157// Called when processing the local session description.
158static void UpdateSessionDescriptionSecurePolicy(
159 cricket::SecureMediaPolicy secure_policy,
160 SessionDescription* sdesc) {
161 if (!sdesc) {
162 return;
163 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000164
wu@webrtc.org91053e72013-08-10 07:18:04 +0000165 // Updating the |crypto_required_| in MediaContentDescription to the
166 // appropriate state based on the current security policy.
167 for (cricket::ContentInfos::iterator iter = sdesc->contents().begin();
168 iter != sdesc->contents().end(); ++iter) {
169 if (cricket::IsMediaContent(&*iter)) {
170 MediaContentDescription* mdesc =
171 static_cast<MediaContentDescription*> (iter->description);
172 if (mdesc) {
173 mdesc->set_crypto_required(secure_policy == cricket::SEC_REQUIRED);
174 }
175 }
176 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000177}
178
179static bool GetAudioSsrcByTrackId(
180 const SessionDescription* session_description,
181 const std::string& track_id, uint32 *ssrc) {
182 const cricket::ContentInfo* audio_info =
183 cricket::GetFirstAudioContent(session_description);
184 if (!audio_info) {
185 LOG(LS_ERROR) << "Audio not used in this call";
186 return false;
187 }
188
189 const cricket::MediaContentDescription* audio_content =
190 static_cast<const cricket::MediaContentDescription*>(
191 audio_info->description);
192 cricket::StreamParams stream;
193 if (!cricket::GetStreamByIds(audio_content->streams(), "", track_id,
194 &stream)) {
195 return false;
196 }
197 *ssrc = stream.first_ssrc();
198 return true;
199}
200
201static bool GetTrackIdBySsrc(const SessionDescription* session_description,
202 uint32 ssrc, std::string* track_id) {
203 ASSERT(track_id != NULL);
204
205 cricket::StreamParams stream_out;
206 const cricket::ContentInfo* audio_info =
207 cricket::GetFirstAudioContent(session_description);
208 if (!audio_info) {
209 return false;
210 }
211 const cricket::MediaContentDescription* audio_content =
212 static_cast<const cricket::MediaContentDescription*>(
213 audio_info->description);
214
215 if (cricket::GetStreamBySsrc(audio_content->streams(), ssrc, &stream_out)) {
216 *track_id = stream_out.id;
217 return true;
218 }
219
220 const cricket::ContentInfo* video_info =
221 cricket::GetFirstVideoContent(session_description);
222 if (!video_info) {
223 return false;
224 }
225 const cricket::MediaContentDescription* video_content =
226 static_cast<const cricket::MediaContentDescription*>(
227 video_info->description);
228
229 if (cricket::GetStreamBySsrc(video_content->streams(), ssrc, &stream_out)) {
230 *track_id = stream_out.id;
231 return true;
232 }
233 return false;
234}
235
236static bool BadSdp(const std::string& desc, std::string* err_desc) {
237 if (err_desc) {
238 *err_desc = desc;
239 }
240 LOG(LS_ERROR) << desc;
241 return false;
242}
243
244static bool BadLocalSdp(const std::string& desc, std::string* err_desc) {
245 std::string set_local_sdp_failed = kSetLocalSdpFailed;
246 set_local_sdp_failed.append(desc);
247 return BadSdp(set_local_sdp_failed, err_desc);
248}
249
250static bool BadRemoteSdp(const std::string& desc, std::string* err_desc) {
251 std::string set_remote_sdp_failed = kSetRemoteSdpFailed;
252 set_remote_sdp_failed.append(desc);
253 return BadSdp(set_remote_sdp_failed, err_desc);
254}
255
256static bool BadSdp(cricket::ContentSource source,
257 const std::string& desc, std::string* err_desc) {
258 if (source == cricket::CS_LOCAL) {
259 return BadLocalSdp(desc, err_desc);
260 } else {
261 return BadRemoteSdp(desc, err_desc);
262 }
263}
264
265static std::string SessionErrorMsg(cricket::BaseSession::Error error) {
266 std::ostringstream desc;
267 desc << kSessionError << error;
268 return desc.str();
269}
270
271#define GET_STRING_OF_STATE(state) \
272 case cricket::BaseSession::state: \
273 result = #state; \
274 break;
275
276static std::string GetStateString(cricket::BaseSession::State state) {
277 std::string result;
278 switch (state) {
279 GET_STRING_OF_STATE(STATE_INIT)
280 GET_STRING_OF_STATE(STATE_SENTINITIATE)
281 GET_STRING_OF_STATE(STATE_RECEIVEDINITIATE)
282 GET_STRING_OF_STATE(STATE_SENTPRACCEPT)
283 GET_STRING_OF_STATE(STATE_SENTACCEPT)
284 GET_STRING_OF_STATE(STATE_RECEIVEDPRACCEPT)
285 GET_STRING_OF_STATE(STATE_RECEIVEDACCEPT)
286 GET_STRING_OF_STATE(STATE_SENTMODIFY)
287 GET_STRING_OF_STATE(STATE_RECEIVEDMODIFY)
288 GET_STRING_OF_STATE(STATE_SENTREJECT)
289 GET_STRING_OF_STATE(STATE_RECEIVEDREJECT)
290 GET_STRING_OF_STATE(STATE_SENTREDIRECT)
291 GET_STRING_OF_STATE(STATE_SENTTERMINATE)
292 GET_STRING_OF_STATE(STATE_RECEIVEDTERMINATE)
293 GET_STRING_OF_STATE(STATE_INPROGRESS)
294 GET_STRING_OF_STATE(STATE_DEINIT)
295 default:
296 ASSERT(false);
297 break;
298 }
299 return result;
300}
301
302#define GET_STRING_OF_ERROR(err) \
303 case cricket::BaseSession::err: \
304 result = #err; \
305 break;
306
307static std::string GetErrorString(cricket::BaseSession::Error err) {
308 std::string result;
309 switch (err) {
310 GET_STRING_OF_ERROR(ERROR_NONE)
311 GET_STRING_OF_ERROR(ERROR_TIME)
312 GET_STRING_OF_ERROR(ERROR_RESPONSE)
313 GET_STRING_OF_ERROR(ERROR_NETWORK)
314 GET_STRING_OF_ERROR(ERROR_CONTENT)
315 GET_STRING_OF_ERROR(ERROR_TRANSPORT)
316 default:
317 ASSERT(false);
318 break;
319 }
320 return result;
321}
322
323static bool SetSessionStateFailed(cricket::ContentSource source,
324 cricket::BaseSession::Error err,
325 std::string* err_desc) {
326 std::string set_state_err = kUpdateStateFailed;
327 set_state_err.append(GetErrorString(err));
328 return BadSdp(source, set_state_err, err_desc);
329}
330
331// Help class used to remember if a a remote peer has requested ice restart by
332// by sending a description with new ice ufrag and password.
333class IceRestartAnswerLatch {
334 public:
335 IceRestartAnswerLatch() : ice_restart_(false) { }
336
wu@webrtc.org91053e72013-08-10 07:18:04 +0000337 // Returns true if CheckForRemoteIceRestart has been called with a new session
338 // description where ice password and ufrag has changed since last time
339 // Reset() was called.
340 bool Get() const {
341 return ice_restart_;
342 }
343
344 void Reset() {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000345 if (ice_restart_) {
346 ice_restart_ = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000347 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000348 }
349
350 void CheckForRemoteIceRestart(
351 const SessionDescriptionInterface* old_desc,
352 const SessionDescriptionInterface* new_desc) {
353 if (!old_desc || new_desc->type() != SessionDescriptionInterface::kOffer) {
354 return;
355 }
356 const SessionDescription* new_sd = new_desc->description();
357 const SessionDescription* old_sd = old_desc->description();
358 const ContentInfos& contents = new_sd->contents();
359 for (size_t index = 0; index < contents.size(); ++index) {
360 const ContentInfo* cinfo = &contents[index];
361 if (cinfo->rejected) {
362 continue;
363 }
364 // If the content isn't rejected, check if ufrag and password has
365 // changed.
366 const cricket::TransportDescription* new_transport_desc =
367 new_sd->GetTransportDescriptionByName(cinfo->name);
368 const cricket::TransportDescription* old_transport_desc =
369 old_sd->GetTransportDescriptionByName(cinfo->name);
370 if (!new_transport_desc || !old_transport_desc) {
371 // No transport description exist. This is not an ice restart.
372 continue;
373 }
374 if (new_transport_desc->ice_pwd != old_transport_desc->ice_pwd &&
375 new_transport_desc->ice_ufrag != old_transport_desc->ice_ufrag) {
376 LOG(LS_INFO) << "Remote peer request ice restart.";
377 ice_restart_ = true;
378 break;
379 }
380 }
381 }
382
383 private:
384 bool ice_restart_;
385};
386
wu@webrtc.org91053e72013-08-10 07:18:04 +0000387WebRtcSession::WebRtcSession(
388 cricket::ChannelManager* channel_manager,
389 talk_base::Thread* signaling_thread,
390 talk_base::Thread* worker_thread,
391 cricket::PortAllocator* port_allocator,
392 MediaStreamSignaling* mediastream_signaling)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000393 : cricket::BaseSession(signaling_thread, worker_thread, port_allocator,
394 talk_base::ToString(talk_base::CreateRandomId64() &
395 LLONG_MAX),
396 cricket::NS_JINGLE_RTP, false),
397 // RFC 3264: The numeric value of the session id and version in the
398 // o line MUST be representable with a "64 bit signed integer".
399 // Due to this constraint session id |sid_| is max limited to LLONG_MAX.
400 channel_manager_(channel_manager),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000401 mediastream_signaling_(mediastream_signaling),
402 ice_observer_(NULL),
403 ice_connection_state_(PeerConnectionInterface::kIceConnectionNew),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000404 older_version_remote_peer_(false),
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000405 dtls_enabled_(false),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000406 data_channel_type_(cricket::DCT_NONE),
407 ice_restart_latch_(new IceRestartAnswerLatch) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000408}
409
410WebRtcSession::~WebRtcSession() {
411 if (voice_channel_.get()) {
412 SignalVoiceChannelDestroyed();
413 channel_manager_->DestroyVoiceChannel(voice_channel_.release());
414 }
415 if (video_channel_.get()) {
416 SignalVideoChannelDestroyed();
417 channel_manager_->DestroyVideoChannel(video_channel_.release());
418 }
419 if (data_channel_.get()) {
420 SignalDataChannelDestroyed();
421 channel_manager_->DestroyDataChannel(data_channel_.release());
422 }
423 for (size_t i = 0; i < saved_candidates_.size(); ++i) {
424 delete saved_candidates_[i];
425 }
426 delete identity();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000427}
428
wu@webrtc.org91053e72013-08-10 07:18:04 +0000429bool WebRtcSession::Initialize(
430 const MediaConstraintsInterface* constraints,
431 DTLSIdentityServiceInterface* dtls_identity_service) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000432 // TODO(perkj): Take |constraints| into consideration. Return false if not all
433 // mandatory constraints can be fulfilled. Note that |constraints|
434 // can be null.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000435 bool value;
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000436
437 // Enable DTLS by default if |dtls_identity_service| is valid.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000438 dtls_enabled_ = (dtls_identity_service != NULL);
439 // |constraints| can override the default |dtls_enabled_| value.
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000440 if (FindConstraint(
441 constraints,
442 MediaConstraintsInterface::kEnableDtlsSrtp,
443 &value, NULL)) {
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000444 dtls_enabled_ = value;
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000445 }
446
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000447 // Enable creation of RTP data channels if the kEnableRtpDataChannels is set.
448 // It takes precendence over the kEnableSctpDataChannels constraint.
449 if (FindConstraint(
450 constraints, MediaConstraintsInterface::kEnableRtpDataChannels,
451 &value, NULL) && value) {
452 LOG(LS_INFO) << "Allowing RTP data engine.";
453 data_channel_type_ = cricket::DCT_RTP;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000454 } else {
455 bool sctp_enabled = FindConstraint(
456 constraints,
457 MediaConstraintsInterface::kEnableSctpDataChannels,
458 &value, NULL) && value;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000459 // DTLS has to be enabled to use SCTP.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000460 if (sctp_enabled && dtls_enabled_) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000461 LOG(LS_INFO) << "Allowing SCTP data engine.";
462 data_channel_type_ = cricket::DCT_SCTP;
463 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000464 }
465 if (data_channel_type_ != cricket::DCT_NONE) {
466 mediastream_signaling_->SetDataChannelFactory(this);
467 }
468
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000469 const cricket::VideoCodec default_codec(
470 JsepSessionDescription::kDefaultVideoCodecId,
471 JsepSessionDescription::kDefaultVideoCodecName,
472 JsepSessionDescription::kMaxVideoCodecWidth,
473 JsepSessionDescription::kMaxVideoCodecHeight,
474 JsepSessionDescription::kDefaultVideoCodecFramerate,
475 JsepSessionDescription::kDefaultVideoCodecPreference);
476 channel_manager_->SetDefaultVideoEncoderConfig(
477 cricket::VideoEncoderConfig(default_codec));
wu@webrtc.org91053e72013-08-10 07:18:04 +0000478
479 webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
480 signaling_thread(),
481 channel_manager_,
482 mediastream_signaling_,
483 dtls_identity_service,
484 this,
485 id(),
486 data_channel_type_,
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000487 dtls_enabled_));
wu@webrtc.org91053e72013-08-10 07:18:04 +0000488
489 webrtc_session_desc_factory_->SignalIdentityReady.connect(
490 this, &WebRtcSession::OnIdentityReady);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000491 return true;
492}
493
494void WebRtcSession::Terminate() {
495 SetState(STATE_RECEIVEDTERMINATE);
496 RemoveUnusedChannelsAndTransports(NULL);
497 ASSERT(voice_channel_.get() == NULL);
498 ASSERT(video_channel_.get() == NULL);
499 ASSERT(data_channel_.get() == NULL);
500}
501
502bool WebRtcSession::StartCandidatesAllocation() {
503 // SpeculativelyConnectTransportChannels, will call ConnectChannels method
504 // from TransportProxy to start gathering ice candidates.
505 SpeculativelyConnectAllTransportChannels();
506 if (!saved_candidates_.empty()) {
507 // If there are saved candidates which arrived before local description is
508 // set, copy those to remote description.
509 CopySavedCandidates(remote_desc_.get());
510 }
511 // Push remote candidates present in remote description to transport channels.
512 UseCandidatesInSessionDescription(remote_desc_.get());
513 return true;
514}
515
516void WebRtcSession::set_secure_policy(
517 cricket::SecureMediaPolicy secure_policy) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000518 webrtc_session_desc_factory_->set_secure(secure_policy);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000519}
520
wu@webrtc.org91053e72013-08-10 07:18:04 +0000521cricket::SecureMediaPolicy WebRtcSession::secure_policy() const {
522 return webrtc_session_desc_factory_->secure();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000523}
524
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000525bool WebRtcSession::GetSslRole(talk_base::SSLRole* role) {
526 if (local_description() == NULL || remote_description() == NULL) {
527 LOG(LS_INFO) << "Local and Remote descriptions must be applied to get "
528 << "SSL Role of the session.";
529 return false;
530 }
531
532 // TODO(mallinath) - Return role of each transport, as role may differ from
533 // one another.
534 // In current implementaion we just return the role of first transport in the
535 // transport map.
536 for (cricket::TransportMap::const_iterator iter = transport_proxies().begin();
537 iter != transport_proxies().end(); ++iter) {
538 if (iter->second->impl()) {
539 return iter->second->impl()->GetSslRole(role);
540 }
541 }
542 return false;
543}
544
wu@webrtc.org91053e72013-08-10 07:18:04 +0000545void WebRtcSession::CreateOffer(CreateSessionDescriptionObserver* observer,
546 const MediaConstraintsInterface* constraints) {
547 webrtc_session_desc_factory_->CreateOffer(observer, constraints);
548}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000549
wu@webrtc.org91053e72013-08-10 07:18:04 +0000550void WebRtcSession::CreateAnswer(CreateSessionDescriptionObserver* observer,
551 const MediaConstraintsInterface* constraints) {
552 webrtc_session_desc_factory_->CreateAnswer(observer, constraints);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000553}
554
555bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
556 std::string* err_desc) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000557 // Takes the ownership of |desc| regardless of the result.
558 talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
559
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000560 // Validate SDP.
561 if (!ValidateSessionDescription(desc, cricket::CS_LOCAL, err_desc)) {
562 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000563 }
564
565 // Update the initiator flag if this session is the initiator.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000566 Action action = GetAction(desc->type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000567 if (state() == STATE_INIT && action == kOffer) {
568 set_initiator(true);
569 }
570
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000571 cricket::SecureMediaPolicy secure_policy =
572 webrtc_session_desc_factory_->secure();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000573 // Update the MediaContentDescription crypto settings as per the policy set.
wu@webrtc.org91053e72013-08-10 07:18:04 +0000574 UpdateSessionDescriptionSecurePolicy(secure_policy, desc->description());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000575
576 set_local_description(desc->description()->Copy());
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000577 local_desc_.reset(desc_temp.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000578
579 // Transport and Media channels will be created only when offer is set.
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000580 if (action == kOffer && !CreateChannels(local_desc_->description())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000581 // TODO(mallinath) - Handle CreateChannel failure, as new local description
582 // is applied. Restore back to old description.
583 return BadLocalSdp(kCreateChannelFailed, err_desc);
584 }
585
586 // Remove channel and transport proxies, if MediaContentDescription is
587 // rejected.
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000588 RemoveUnusedChannelsAndTransports(local_desc_->description());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000589
590 if (!UpdateSessionState(action, cricket::CS_LOCAL,
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000591 local_desc_->description(), err_desc)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000592 return false;
593 }
594 // Kick starting the ice candidates allocation.
595 StartCandidatesAllocation();
596
597 // Update state and SSRC of local MediaStreams and DataChannels based on the
598 // local session description.
599 mediastream_signaling_->OnLocalDescriptionChanged(local_desc_.get());
600
601 if (error() != cricket::BaseSession::ERROR_NONE) {
602 return BadLocalSdp(SessionErrorMsg(error()), err_desc);
603 }
604 return true;
605}
606
607bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
608 std::string* err_desc) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000609 // Takes the ownership of |desc| regardless of the result.
610 talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
611
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000612 // Validate SDP.
613 if (!ValidateSessionDescription(desc, cricket::CS_REMOTE, err_desc)) {
614 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000615 }
616
617 // Transport and Media channels will be created only when offer is set.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000618 Action action = GetAction(desc->type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000619 if (action == kOffer && !CreateChannels(desc->description())) {
620 // TODO(mallinath) - Handle CreateChannel failure, as new local description
621 // is applied. Restore back to old description.
622 return BadRemoteSdp(kCreateChannelFailed, err_desc);
623 }
624
625 // Remove channel and transport proxies, if MediaContentDescription is
626 // rejected.
627 RemoveUnusedChannelsAndTransports(desc->description());
628
629 // NOTE: Candidates allocation will be initiated only when SetLocalDescription
630 // is called.
631 set_remote_description(desc->description()->Copy());
632 if (!UpdateSessionState(action, cricket::CS_REMOTE,
633 desc->description(), err_desc)) {
634 return false;
635 }
636
637 // Update remote MediaStreams.
638 mediastream_signaling_->OnRemoteDescriptionChanged(desc);
639 if (local_description() && !UseCandidatesInSessionDescription(desc)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000640 return BadRemoteSdp(kInvalidCandidates, err_desc);
641 }
642
643 // Copy all saved candidates.
644 CopySavedCandidates(desc);
645 // We retain all received candidates.
wu@webrtc.org91053e72013-08-10 07:18:04 +0000646 WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
647 remote_desc_.get(), desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000648 // Check if this new SessionDescription contains new ice ufrag and password
649 // that indicates the remote peer requests ice restart.
650 ice_restart_latch_->CheckForRemoteIceRestart(remote_desc_.get(),
651 desc);
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000652 remote_desc_.reset(desc_temp.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000653 if (error() != cricket::BaseSession::ERROR_NONE) {
654 return BadRemoteSdp(SessionErrorMsg(error()), err_desc);
655 }
656 return true;
657}
658
659bool WebRtcSession::UpdateSessionState(
660 Action action, cricket::ContentSource source,
661 const cricket::SessionDescription* desc,
662 std::string* err_desc) {
663 // If there's already a pending error then no state transition should happen.
664 // But all call-sites should be verifying this before calling us!
665 ASSERT(error() == cricket::BaseSession::ERROR_NONE);
666 if (action == kOffer) {
667 if (!PushdownTransportDescription(source, cricket::CA_OFFER)) {
668 return BadSdp(source, kPushDownOfferTDFailed, err_desc);
669 }
670 SetState(source == cricket::CS_LOCAL ?
671 STATE_SENTINITIATE : STATE_RECEIVEDINITIATE);
672 if (error() != cricket::BaseSession::ERROR_NONE) {
673 return SetSessionStateFailed(source, error(), err_desc);
674 }
675 } else if (action == kPrAnswer) {
676 if (!PushdownTransportDescription(source, cricket::CA_PRANSWER)) {
677 return BadSdp(source, kPushDownPranswerTDFailed, err_desc);
678 }
679 EnableChannels();
680 SetState(source == cricket::CS_LOCAL ?
681 STATE_SENTPRACCEPT : STATE_RECEIVEDPRACCEPT);
682 if (error() != cricket::BaseSession::ERROR_NONE) {
683 return SetSessionStateFailed(source, error(), err_desc);
684 }
685 } else if (action == kAnswer) {
686 if (!PushdownTransportDescription(source, cricket::CA_ANSWER)) {
687 return BadSdp(source, kPushDownAnswerTDFailed, err_desc);
688 }
689 MaybeEnableMuxingSupport();
690 EnableChannels();
691 SetState(source == cricket::CS_LOCAL ?
692 STATE_SENTACCEPT : STATE_RECEIVEDACCEPT);
693 if (error() != cricket::BaseSession::ERROR_NONE) {
694 return SetSessionStateFailed(source, error(), err_desc);
695 }
696 }
697 return true;
698}
699
700WebRtcSession::Action WebRtcSession::GetAction(const std::string& type) {
701 if (type == SessionDescriptionInterface::kOffer) {
702 return WebRtcSession::kOffer;
703 } else if (type == SessionDescriptionInterface::kPrAnswer) {
704 return WebRtcSession::kPrAnswer;
705 } else if (type == SessionDescriptionInterface::kAnswer) {
706 return WebRtcSession::kAnswer;
707 }
708 ASSERT(false && "unknown action type");
709 return WebRtcSession::kOffer;
710}
711
712bool WebRtcSession::ProcessIceMessage(const IceCandidateInterface* candidate) {
713 if (state() == STATE_INIT) {
714 LOG(LS_ERROR) << "ProcessIceMessage: ICE candidates can't be added "
715 << "without any offer (local or remote) "
716 << "session description.";
717 return false;
718 }
719
720 if (!candidate) {
721 LOG(LS_ERROR) << "ProcessIceMessage: Candidate is NULL";
722 return false;
723 }
724
725 if (!local_description() || !remote_description()) {
726 LOG(LS_INFO) << "ProcessIceMessage: Remote description not set, "
727 << "save the candidate for later use.";
728 saved_candidates_.push_back(
729 new JsepIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(),
730 candidate->candidate()));
731 return true;
732 }
733
734 // Add this candidate to the remote session description.
735 if (!remote_desc_->AddCandidate(candidate)) {
736 LOG(LS_ERROR) << "ProcessIceMessage: Candidate cannot be used";
737 return false;
738 }
739
740 return UseCandidatesInSessionDescription(remote_desc_.get());
741}
742
743bool WebRtcSession::GetTrackIdBySsrc(uint32 ssrc, std::string* id) {
744 if (GetLocalTrackId(ssrc, id)) {
745 if (GetRemoteTrackId(ssrc, id)) {
746 LOG(LS_WARNING) << "SSRC " << ssrc
747 << " exists in both local and remote descriptions";
748 return true; // We return the remote track id.
749 }
750 return true;
751 } else {
752 return GetRemoteTrackId(ssrc, id);
753 }
754}
755
756bool WebRtcSession::GetLocalTrackId(uint32 ssrc, std::string* track_id) {
757 if (!BaseSession::local_description())
758 return false;
759 return webrtc::GetTrackIdBySsrc(
760 BaseSession::local_description(), ssrc, track_id);
761}
762
763bool WebRtcSession::GetRemoteTrackId(uint32 ssrc, std::string* track_id) {
764 if (!BaseSession::remote_description())
765 return false;
766 return webrtc::GetTrackIdBySsrc(
767 BaseSession::remote_description(), ssrc, track_id);
768}
769
770std::string WebRtcSession::BadStateErrMsg(
771 const std::string& type, State state) {
772 std::ostringstream desc;
773 desc << "Called with type in wrong state, "
774 << "type: " << type << " state: " << GetStateString(state);
775 return desc.str();
776}
777
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000778void WebRtcSession::SetAudioPlayout(uint32 ssrc, bool enable,
779 cricket::AudioRenderer* renderer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000780 ASSERT(signaling_thread()->IsCurrent());
781 if (!voice_channel_) {
782 LOG(LS_ERROR) << "SetAudioPlayout: No audio channel exists.";
783 return;
784 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000785 if (!voice_channel_->SetRemoteRenderer(ssrc, renderer)) {
786 // SetRenderer() can fail if the ssrc does not match any playout channel.
787 LOG(LS_ERROR) << "SetAudioPlayout: ssrc is incorrect: " << ssrc;
788 return;
789 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000790 if (!voice_channel_->SetOutputScaling(ssrc, enable ? 1 : 0, enable ? 1 : 0)) {
791 // Allow that SetOutputScaling fail if |enable| is false but assert
792 // otherwise. This in the normal case when the underlying media channel has
793 // already been deleted.
794 ASSERT(enable == false);
795 }
796}
797
798void WebRtcSession::SetAudioSend(uint32 ssrc, bool enable,
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000799 const cricket::AudioOptions& options,
800 cricket::AudioRenderer* renderer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000801 ASSERT(signaling_thread()->IsCurrent());
802 if (!voice_channel_) {
803 LOG(LS_ERROR) << "SetAudioSend: No audio channel exists.";
804 return;
805 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000806 if (!voice_channel_->SetLocalRenderer(ssrc, renderer)) {
807 // SetRenderer() can fail if the ssrc does not match any send channel.
808 LOG(LS_ERROR) << "SetAudioSend: ssrc is incorrect: " << ssrc;
809 return;
810 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000811 if (!voice_channel_->MuteStream(ssrc, !enable)) {
812 // Allow that MuteStream fail if |enable| is false but assert otherwise.
813 // This in the normal case when the underlying media channel has already
814 // been deleted.
815 ASSERT(enable == false);
816 return;
817 }
818 if (enable)
819 voice_channel_->SetChannelOptions(options);
820}
821
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000822bool WebRtcSession::SetCaptureDevice(uint32 ssrc,
823 cricket::VideoCapturer* camera) {
824 ASSERT(signaling_thread()->IsCurrent());
825
826 if (!video_channel_.get()) {
827 // |video_channel_| doesnt't exist. Probably because the remote end doesnt't
828 // support video.
829 LOG(LS_WARNING) << "Video not used in this call.";
830 return false;
831 }
832 if (!video_channel_->SetCapturer(ssrc, camera)) {
833 // Allow that SetCapturer fail if |camera| is NULL but assert otherwise.
834 // This in the normal case when the underlying media channel has already
835 // been deleted.
836 ASSERT(camera == NULL);
837 return false;
838 }
839 return true;
840}
841
842void WebRtcSession::SetVideoPlayout(uint32 ssrc,
843 bool enable,
844 cricket::VideoRenderer* renderer) {
845 ASSERT(signaling_thread()->IsCurrent());
846 if (!video_channel_) {
847 LOG(LS_WARNING) << "SetVideoPlayout: No video channel exists.";
848 return;
849 }
850 if (!video_channel_->SetRenderer(ssrc, enable ? renderer : NULL)) {
851 // Allow that SetRenderer fail if |renderer| is NULL but assert otherwise.
852 // This in the normal case when the underlying media channel has already
853 // been deleted.
854 ASSERT(renderer == NULL);
855 }
856}
857
858void WebRtcSession::SetVideoSend(uint32 ssrc, bool enable,
859 const cricket::VideoOptions* options) {
860 ASSERT(signaling_thread()->IsCurrent());
861 if (!video_channel_) {
862 LOG(LS_WARNING) << "SetVideoSend: No video channel exists.";
863 return;
864 }
865 if (!video_channel_->MuteStream(ssrc, !enable)) {
866 // Allow that MuteStream fail if |enable| is false but assert otherwise.
867 // This in the normal case when the underlying media channel has already
868 // been deleted.
869 ASSERT(enable == false);
870 return;
871 }
872 if (enable && options)
873 video_channel_->SetChannelOptions(*options);
874}
875
876bool WebRtcSession::CanInsertDtmf(const std::string& track_id) {
877 ASSERT(signaling_thread()->IsCurrent());
878 if (!voice_channel_) {
879 LOG(LS_ERROR) << "CanInsertDtmf: No audio channel exists.";
880 return false;
881 }
882 uint32 send_ssrc = 0;
883 // The Dtmf is negotiated per channel not ssrc, so we only check if the ssrc
884 // exists.
885 if (!GetAudioSsrcByTrackId(BaseSession::local_description(), track_id,
886 &send_ssrc)) {
887 LOG(LS_ERROR) << "CanInsertDtmf: Track does not exist: " << track_id;
888 return false;
889 }
890 return voice_channel_->CanInsertDtmf();
891}
892
893bool WebRtcSession::InsertDtmf(const std::string& track_id,
894 int code, int duration) {
895 ASSERT(signaling_thread()->IsCurrent());
896 if (!voice_channel_) {
897 LOG(LS_ERROR) << "InsertDtmf: No audio channel exists.";
898 return false;
899 }
900 uint32 send_ssrc = 0;
901 if (!VERIFY(GetAudioSsrcByTrackId(BaseSession::local_description(),
902 track_id, &send_ssrc))) {
903 LOG(LS_ERROR) << "InsertDtmf: Track does not exist: " << track_id;
904 return false;
905 }
906 if (!voice_channel_->InsertDtmf(send_ssrc, code, duration,
907 cricket::DF_SEND)) {
908 LOG(LS_ERROR) << "Failed to insert DTMF to channel.";
909 return false;
910 }
911 return true;
912}
913
914sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() {
915 return &SignalVoiceChannelDestroyed;
916}
917
918talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
919 const std::string& label,
920 const DataChannelInit* config) {
921 if (state() == STATE_RECEIVEDTERMINATE) {
922 return NULL;
923 }
924 if (data_channel_type_ == cricket::DCT_NONE) {
925 LOG(LS_ERROR) << "CreateDataChannel: Data is not supported in this call.";
926 return NULL;
927 }
928 DataChannelInit new_config = config ? (*config) : DataChannelInit();
929
930 if (data_channel_type_ == cricket::DCT_SCTP) {
931 if (new_config.id < 0) {
932 if (!mediastream_signaling_->AllocateSctpId(&new_config.id)) {
933 LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel.";
934 return NULL;
935 }
936 } else if (!mediastream_signaling_->IsSctpIdAvailable(new_config.id)) {
937 LOG(LS_ERROR) << "Failed to create a SCTP data channel "
938 << "because the id is already in use or out of range.";
939 return NULL;
940 }
941 }
wu@webrtc.org91053e72013-08-10 07:18:04 +0000942
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000943 talk_base::scoped_refptr<DataChannel> channel(
944 DataChannel::Create(this, label, &new_config));
945 if (channel == NULL)
946 return NULL;
947 if (!mediastream_signaling_->AddDataChannel(channel))
948 return NULL;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000949 if (data_channel_type_ == cricket::DCT_SCTP) {
950 if (config == NULL) {
951 LOG(LS_WARNING) << "Could not send data channel OPEN message"
952 << " because of NULL config.";
953 return NULL;
954 }
955 if (data_channel_.get()) {
956 channel->SetReceiveSsrc(new_config.id);
957 channel->SetSendSsrc(new_config.id);
wu@webrtc.org91053e72013-08-10 07:18:04 +0000958 }
959 if (!config->negotiated) {
960 talk_base::Buffer *payload = new talk_base::Buffer;
961 if (!mediastream_signaling_->WriteDataChannelOpenMessage(
962 label, *config, payload)) {
963 LOG(LS_WARNING) << "Could not write data channel OPEN message";
964 }
965 // SendControl may queue the message until the data channel's set up,
966 // or congestion clears.
967 channel->SendControl(payload);
968 }
969 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000970 return channel;
971}
972
973cricket::DataChannelType WebRtcSession::data_channel_type() const {
974 return data_channel_type_;
975}
976
wu@webrtc.org91053e72013-08-10 07:18:04 +0000977bool WebRtcSession::IceRestartPending() const {
978 return ice_restart_latch_->Get();
979}
980
981void WebRtcSession::ResetIceRestartLatch() {
982 ice_restart_latch_->Reset();
983}
984
985void WebRtcSession::OnIdentityReady(talk_base::SSLIdentity* identity) {
986 SetIdentity(identity);
987}
988
989bool WebRtcSession::waiting_for_identity() const {
990 return webrtc_session_desc_factory_->waiting_for_identity();
991}
992
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000993void WebRtcSession::SetIceConnectionState(
994 PeerConnectionInterface::IceConnectionState state) {
995 if (ice_connection_state_ == state) {
996 return;
997 }
998
999 // ASSERT that the requested transition is allowed. Note that
1000 // WebRtcSession does not implement "kIceConnectionClosed" (that is handled
1001 // within PeerConnection). This switch statement should compile away when
1002 // ASSERTs are disabled.
1003 switch (ice_connection_state_) {
1004 case PeerConnectionInterface::kIceConnectionNew:
1005 ASSERT(state == PeerConnectionInterface::kIceConnectionChecking);
1006 break;
1007 case PeerConnectionInterface::kIceConnectionChecking:
1008 ASSERT(state == PeerConnectionInterface::kIceConnectionFailed ||
1009 state == PeerConnectionInterface::kIceConnectionConnected);
1010 break;
1011 case PeerConnectionInterface::kIceConnectionConnected:
1012 ASSERT(state == PeerConnectionInterface::kIceConnectionDisconnected ||
1013 state == PeerConnectionInterface::kIceConnectionChecking ||
1014 state == PeerConnectionInterface::kIceConnectionCompleted);
1015 break;
1016 case PeerConnectionInterface::kIceConnectionCompleted:
1017 ASSERT(state == PeerConnectionInterface::kIceConnectionConnected ||
1018 state == PeerConnectionInterface::kIceConnectionDisconnected);
1019 break;
1020 case PeerConnectionInterface::kIceConnectionFailed:
1021 ASSERT(state == PeerConnectionInterface::kIceConnectionNew);
1022 break;
1023 case PeerConnectionInterface::kIceConnectionDisconnected:
1024 ASSERT(state == PeerConnectionInterface::kIceConnectionChecking ||
1025 state == PeerConnectionInterface::kIceConnectionConnected ||
1026 state == PeerConnectionInterface::kIceConnectionCompleted ||
1027 state == PeerConnectionInterface::kIceConnectionFailed);
1028 break;
1029 case PeerConnectionInterface::kIceConnectionClosed:
1030 ASSERT(false);
1031 break;
1032 default:
1033 ASSERT(false);
1034 break;
1035 }
1036
1037 ice_connection_state_ = state;
1038 if (ice_observer_) {
1039 ice_observer_->OnIceConnectionChange(ice_connection_state_);
1040 }
1041}
1042
1043void WebRtcSession::OnTransportRequestSignaling(
1044 cricket::Transport* transport) {
1045 ASSERT(signaling_thread()->IsCurrent());
1046 transport->OnSignalingReady();
1047 if (ice_observer_) {
1048 ice_observer_->OnIceGatheringChange(
1049 PeerConnectionInterface::kIceGatheringGathering);
1050 }
1051}
1052
1053void WebRtcSession::OnTransportConnecting(cricket::Transport* transport) {
1054 ASSERT(signaling_thread()->IsCurrent());
1055 // start monitoring for the write state of the transport.
1056 OnTransportWritable(transport);
1057}
1058
1059void WebRtcSession::OnTransportWritable(cricket::Transport* transport) {
1060 ASSERT(signaling_thread()->IsCurrent());
1061 // TODO(bemasc): Expose more API from Transport to detect when
1062 // candidate selection starts or stops, due to success or failure.
1063 if (transport->all_channels_writable()) {
1064 if (ice_connection_state_ ==
1065 PeerConnectionInterface::kIceConnectionChecking ||
1066 ice_connection_state_ ==
1067 PeerConnectionInterface::kIceConnectionDisconnected) {
1068 SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected);
1069 }
1070 } else if (transport->HasChannels()) {
1071 // If the current state is Connected or Completed, then there were writable
1072 // channels but now there are not, so the next state must be Disconnected.
1073 if (ice_connection_state_ ==
1074 PeerConnectionInterface::kIceConnectionConnected ||
1075 ice_connection_state_ ==
1076 PeerConnectionInterface::kIceConnectionCompleted) {
1077 SetIceConnectionState(
1078 PeerConnectionInterface::kIceConnectionDisconnected);
1079 }
1080 }
1081}
1082
1083void WebRtcSession::OnTransportProxyCandidatesReady(
1084 cricket::TransportProxy* proxy, const cricket::Candidates& candidates) {
1085 ASSERT(signaling_thread()->IsCurrent());
1086 ProcessNewLocalCandidate(proxy->content_name(), candidates);
1087}
1088
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001089void WebRtcSession::OnCandidatesAllocationDone() {
1090 ASSERT(signaling_thread()->IsCurrent());
1091 if (ice_observer_) {
1092 ice_observer_->OnIceGatheringChange(
1093 PeerConnectionInterface::kIceGatheringComplete);
1094 ice_observer_->OnIceComplete();
1095 }
1096}
1097
1098// Enabling voice and video channel.
1099void WebRtcSession::EnableChannels() {
1100 if (voice_channel_ && !voice_channel_->enabled())
1101 voice_channel_->Enable(true);
1102
1103 if (video_channel_ && !video_channel_->enabled())
1104 video_channel_->Enable(true);
1105
1106 if (data_channel_.get() && !data_channel_->enabled())
1107 data_channel_->Enable(true);
1108}
1109
1110void WebRtcSession::ProcessNewLocalCandidate(
1111 const std::string& content_name,
1112 const cricket::Candidates& candidates) {
1113 int sdp_mline_index;
1114 if (!GetLocalCandidateMediaIndex(content_name, &sdp_mline_index)) {
1115 LOG(LS_ERROR) << "ProcessNewLocalCandidate: content name "
1116 << content_name << " not found";
1117 return;
1118 }
1119
1120 for (cricket::Candidates::const_iterator citer = candidates.begin();
1121 citer != candidates.end(); ++citer) {
1122 // Use content_name as the candidate media id.
1123 JsepIceCandidate candidate(content_name, sdp_mline_index, *citer);
1124 if (ice_observer_) {
1125 ice_observer_->OnIceCandidate(&candidate);
1126 }
1127 if (local_desc_) {
1128 local_desc_->AddCandidate(&candidate);
1129 }
1130 }
1131}
1132
1133// Returns the media index for a local ice candidate given the content name.
1134bool WebRtcSession::GetLocalCandidateMediaIndex(const std::string& content_name,
1135 int* sdp_mline_index) {
1136 if (!BaseSession::local_description() || !sdp_mline_index)
1137 return false;
1138
1139 bool content_found = false;
1140 const ContentInfos& contents = BaseSession::local_description()->contents();
1141 for (size_t index = 0; index < contents.size(); ++index) {
1142 if (contents[index].name == content_name) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001143 *sdp_mline_index = static_cast<int>(index);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001144 content_found = true;
1145 break;
1146 }
1147 }
1148 return content_found;
1149}
1150
1151bool WebRtcSession::UseCandidatesInSessionDescription(
1152 const SessionDescriptionInterface* remote_desc) {
1153 if (!remote_desc)
1154 return true;
1155 bool ret = true;
1156 for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) {
1157 const IceCandidateCollection* candidates = remote_desc->candidates(m);
1158 for (size_t n = 0; n < candidates->count(); ++n) {
1159 ret = UseCandidate(candidates->at(n));
1160 if (!ret)
1161 break;
1162 }
1163 }
1164 return ret;
1165}
1166
1167bool WebRtcSession::UseCandidate(
1168 const IceCandidateInterface* candidate) {
1169
1170 size_t mediacontent_index = static_cast<size_t>(candidate->sdp_mline_index());
1171 size_t remote_content_size =
1172 BaseSession::remote_description()->contents().size();
1173 if (mediacontent_index >= remote_content_size) {
1174 LOG(LS_ERROR)
1175 << "UseRemoteCandidateInSession: Invalid candidate media index.";
1176 return false;
1177 }
1178
1179 cricket::ContentInfo content =
1180 BaseSession::remote_description()->contents()[mediacontent_index];
1181 std::vector<cricket::Candidate> candidates;
1182 candidates.push_back(candidate->candidate());
1183 // Invoking BaseSession method to handle remote candidates.
1184 std::string error;
1185 if (OnRemoteCandidates(content.name, candidates, &error)) {
1186 // Candidates successfully submitted for checking.
1187 if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew ||
1188 ice_connection_state_ ==
1189 PeerConnectionInterface::kIceConnectionDisconnected) {
1190 // If state is New, then the session has just gotten its first remote ICE
1191 // candidates, so go to Checking.
1192 // If state is Disconnected, the session is re-using old candidates or
1193 // receiving additional ones, so go to Checking.
1194 // If state is Connected, stay Connected.
1195 // TODO(bemasc): If state is Connected, and the new candidates are for a
1196 // newly added transport, then the state actually _should_ move to
1197 // checking. Add a way to distinguish that case.
1198 SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking);
1199 }
1200 // TODO(bemasc): If state is Completed, go back to Connected.
1201 } else {
1202 LOG(LS_WARNING) << error;
1203 }
1204 return true;
1205}
1206
1207void WebRtcSession::RemoveUnusedChannelsAndTransports(
1208 const SessionDescription* desc) {
1209 const cricket::ContentInfo* voice_info =
1210 cricket::GetFirstAudioContent(desc);
1211 if ((!voice_info || voice_info->rejected) && voice_channel_) {
1212 mediastream_signaling_->OnAudioChannelClose();
1213 SignalVoiceChannelDestroyed();
1214 const std::string content_name = voice_channel_->content_name();
1215 channel_manager_->DestroyVoiceChannel(voice_channel_.release());
1216 DestroyTransportProxy(content_name);
1217 }
1218
1219 const cricket::ContentInfo* video_info =
1220 cricket::GetFirstVideoContent(desc);
1221 if ((!video_info || video_info->rejected) && video_channel_) {
1222 mediastream_signaling_->OnVideoChannelClose();
1223 SignalVideoChannelDestroyed();
1224 const std::string content_name = video_channel_->content_name();
1225 channel_manager_->DestroyVideoChannel(video_channel_.release());
1226 DestroyTransportProxy(content_name);
1227 }
1228
1229 const cricket::ContentInfo* data_info =
1230 cricket::GetFirstDataContent(desc);
1231 if ((!data_info || data_info->rejected) && data_channel_) {
1232 mediastream_signaling_->OnDataChannelClose();
1233 SignalDataChannelDestroyed();
1234 const std::string content_name = data_channel_->content_name();
1235 channel_manager_->DestroyDataChannel(data_channel_.release());
1236 DestroyTransportProxy(content_name);
1237 }
1238}
1239
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001240// TODO(mallinath) - Add a correct error code if the channels are not creatued
1241// due to BUNDLE is enabled but rtcp-mux is disabled.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001242bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
1243 // Disabling the BUNDLE flag in PortAllocator if offer disabled it.
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001244 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1245 if (state() == STATE_INIT && !bundle_enabled) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001246 port_allocator()->set_flags(port_allocator()->flags() &
1247 ~cricket::PORTALLOCATOR_ENABLE_BUNDLE);
1248 }
1249
1250 // Creating the media channels and transport proxies.
1251 const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(desc);
1252 if (voice && !voice->rejected && !voice_channel_) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001253 if (!CreateVoiceChannel(voice)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001254 LOG(LS_ERROR) << "Failed to create voice channel.";
1255 return false;
1256 }
1257 }
1258
1259 const cricket::ContentInfo* video = cricket::GetFirstVideoContent(desc);
1260 if (video && !video->rejected && !video_channel_) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001261 if (!CreateVideoChannel(video)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001262 LOG(LS_ERROR) << "Failed to create video channel.";
1263 return false;
1264 }
1265 }
1266
1267 const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc);
1268 if (data_channel_type_ != cricket::DCT_NONE &&
1269 data && !data->rejected && !data_channel_.get()) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001270 if (!CreateDataChannel(data)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001271 LOG(LS_ERROR) << "Failed to create data channel.";
1272 return false;
1273 }
1274 }
1275
1276 return true;
1277}
1278
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001279bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001280 voice_channel_.reset(channel_manager_->CreateVoiceChannel(
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001281 this, content->name, true));
1282 return (voice_channel_ != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001283}
1284
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001285bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001286 video_channel_.reset(channel_manager_->CreateVideoChannel(
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001287 this, content->name, true, voice_channel_.get()));
1288 return (video_channel_ != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001289}
1290
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001291bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001292 bool rtcp = (data_channel_type_ == cricket::DCT_RTP);
1293 data_channel_.reset(channel_manager_->CreateDataChannel(
wu@webrtc.org91053e72013-08-10 07:18:04 +00001294 this, content->name, rtcp, data_channel_type_));
1295 if (!data_channel_.get()) {
1296 return false;
1297 }
1298 data_channel_->SignalDataReceived.connect(
1299 this, &WebRtcSession::OnDataReceived);
1300 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001301}
1302
1303void WebRtcSession::CopySavedCandidates(
1304 SessionDescriptionInterface* dest_desc) {
1305 if (!dest_desc) {
1306 ASSERT(false);
1307 return;
1308 }
1309 for (size_t i = 0; i < saved_candidates_.size(); ++i) {
1310 dest_desc->AddCandidate(saved_candidates_[i]);
1311 delete saved_candidates_[i];
1312 }
1313 saved_candidates_.clear();
1314}
1315
wu@webrtc.org91053e72013-08-10 07:18:04 +00001316// Look for OPEN messages and set up data channels in response.
1317void WebRtcSession::OnDataReceived(
1318 cricket::DataChannel* channel,
1319 const cricket::ReceiveDataParams& params,
1320 const talk_base::Buffer& payload) {
1321 if (params.type != cricket::DMT_CONTROL) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001322 return;
1323 }
1324
wu@webrtc.org91053e72013-08-10 07:18:04 +00001325 std::string label;
1326 DataChannelInit config;
1327 if (!mediastream_signaling_->ParseDataChannelOpenMessage(
1328 payload, &label, &config)) {
1329 LOG(LS_WARNING) << "Failed to parse data channel OPEN message.";
1330 return;
1331 }
1332
1333 config.negotiated = true; // This is the negotiation.
1334
1335 if (!mediastream_signaling_->AddDataChannelFromOpenMessage(
1336 label, config)) {
1337 LOG(LS_WARNING) << "Failed to create data channel from OPEN message.";
1338 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001339 }
1340}
1341
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001342// Returns false if bundle is enabled and rtcp_mux is disabled.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001343bool WebRtcSession::ValidateBundleSettings(const SessionDescription* desc) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001344 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1345 if (!bundle_enabled)
1346 return true;
1347
1348 const cricket::ContentGroup* bundle_group =
1349 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
1350 ASSERT(bundle_group != NULL);
1351
1352 const cricket::ContentInfos& contents = desc->contents();
1353 for (cricket::ContentInfos::const_iterator citer = contents.begin();
1354 citer != contents.end(); ++citer) {
1355 const cricket::ContentInfo* content = (&*citer);
1356 ASSERT(content != NULL);
1357 if (bundle_group->HasContentName(content->name) &&
1358 !content->rejected && content->type == cricket::NS_JINGLE_RTP) {
1359 if (!HasRtcpMuxEnabled(content))
1360 return false;
1361 }
1362 }
1363 // RTCP-MUX is enabled in all the contents.
1364 return true;
1365}
1366
1367bool WebRtcSession::HasRtcpMuxEnabled(
1368 const cricket::ContentInfo* content) {
1369 const cricket::MediaContentDescription* description =
1370 static_cast<cricket::MediaContentDescription*>(content->description);
1371 return description->rtcp_mux();
1372}
1373
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001374bool WebRtcSession::ValidateSessionDescription(
1375 const SessionDescriptionInterface* sdesc,
1376 cricket::ContentSource source, std::string* error_desc) {
1377
1378 if (error() != cricket::BaseSession::ERROR_NONE) {
1379 return BadSdp(source, SessionErrorMsg(error()), error_desc);
1380 }
1381
1382 if (!sdesc || !sdesc->description()) {
1383 return BadSdp(source, kInvalidSdp, error_desc);
1384 }
1385
1386 std::string type = sdesc->type();
1387 Action action = GetAction(sdesc->type());
1388 if (source == cricket::CS_LOCAL) {
1389 if (!ExpectSetLocalDescription(action))
1390 return BadSdp(source, BadStateErrMsg(type, state()), error_desc);
1391 } else {
1392 if (!ExpectSetRemoteDescription(action))
1393 return BadSdp(source, BadStateErrMsg(type, state()), error_desc);
1394 }
1395
1396 // Verify crypto settings.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +00001397 std::string crypto_error;
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001398 if (webrtc_session_desc_factory_->secure() == cricket::SEC_REQUIRED &&
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +00001399 !VerifyCrypto(sdesc->description(), dtls_enabled_, &crypto_error)) {
1400 return BadSdp(source, crypto_error, error_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001401 }
1402
1403 if (!ValidateBundleSettings(sdesc->description())) {
1404 return BadSdp(source, kBundleWithoutRtcpMux, error_desc);
1405 }
1406
1407 // Verify m-lines in Answer when compared against Offer.
1408 if (action == kAnswer) {
1409 const cricket::SessionDescription* offer_desc =
1410 (source == cricket::CS_LOCAL) ? remote_description()->description() :
1411 local_description()->description();
1412 if (!VerifyMediaDescriptions(sdesc->description(), offer_desc)) {
1413 return BadSdp(source, kMlineMismatch, error_desc);
1414 }
1415 }
1416
1417 return true;
1418}
1419
1420bool WebRtcSession::ExpectSetLocalDescription(Action action) {
1421 return ((action == kOffer && state() == STATE_INIT) ||
1422 // update local offer
1423 (action == kOffer && state() == STATE_SENTINITIATE) ||
1424 // update the current ongoing session.
1425 (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
1426 (action == kOffer && state() == STATE_SENTACCEPT) ||
1427 (action == kOffer && state() == STATE_INPROGRESS) ||
1428 // accept remote offer
1429 (action == kAnswer && state() == STATE_RECEIVEDINITIATE) ||
1430 (action == kAnswer && state() == STATE_SENTPRACCEPT) ||
1431 (action == kPrAnswer && state() == STATE_RECEIVEDINITIATE) ||
1432 (action == kPrAnswer && state() == STATE_SENTPRACCEPT));
1433}
1434
1435bool WebRtcSession::ExpectSetRemoteDescription(Action action) {
1436 return ((action == kOffer && state() == STATE_INIT) ||
1437 // update remote offer
1438 (action == kOffer && state() == STATE_RECEIVEDINITIATE) ||
1439 // update the current ongoing session
1440 (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
1441 (action == kOffer && state() == STATE_SENTACCEPT) ||
1442 (action == kOffer && state() == STATE_INPROGRESS) ||
1443 // accept local offer
1444 (action == kAnswer && state() == STATE_SENTINITIATE) ||
1445 (action == kAnswer && state() == STATE_RECEIVEDPRACCEPT) ||
1446 (action == kPrAnswer && state() == STATE_SENTINITIATE) ||
1447 (action == kPrAnswer && state() == STATE_RECEIVEDPRACCEPT));
1448}
1449
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001450} // namespace webrtc