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