blob: b056757df4388321d20a342189f7d2e696031651 [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;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000425 // Enable creation of RTP data channels if the kEnableRtpDataChannels is set.
426 // It takes precendence over the kEnableSctpDataChannels constraint.
427 if (FindConstraint(
428 constraints, MediaConstraintsInterface::kEnableRtpDataChannels,
429 &value, NULL) && value) {
430 LOG(LS_INFO) << "Allowing RTP data engine.";
431 data_channel_type_ = cricket::DCT_RTP;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000432 } else {
433 bool sctp_enabled = FindConstraint(
434 constraints,
435 MediaConstraintsInterface::kEnableSctpDataChannels,
436 &value, NULL) && value;
437 bool dtls_enabled = FindConstraint(
438 constraints,
439 MediaConstraintsInterface::kEnableDtlsSrtp,
440 &value, NULL) && value;
441
442 // DTLS has to be enabled to use SCTP.
443 if (sctp_enabled && dtls_enabled) {
444 LOG(LS_INFO) << "Allowing SCTP data engine.";
445 data_channel_type_ = cricket::DCT_SCTP;
446 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000447 }
448 if (data_channel_type_ != cricket::DCT_NONE) {
449 mediastream_signaling_->SetDataChannelFactory(this);
450 }
451
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000452 const cricket::VideoCodec default_codec(
453 JsepSessionDescription::kDefaultVideoCodecId,
454 JsepSessionDescription::kDefaultVideoCodecName,
455 JsepSessionDescription::kMaxVideoCodecWidth,
456 JsepSessionDescription::kMaxVideoCodecHeight,
457 JsepSessionDescription::kDefaultVideoCodecFramerate,
458 JsepSessionDescription::kDefaultVideoCodecPreference);
459 channel_manager_->SetDefaultVideoEncoderConfig(
460 cricket::VideoEncoderConfig(default_codec));
wu@webrtc.org91053e72013-08-10 07:18:04 +0000461
462 webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
463 signaling_thread(),
464 channel_manager_,
465 mediastream_signaling_,
466 dtls_identity_service,
467 this,
468 id(),
469 data_channel_type_,
470 constraints));
471
472 webrtc_session_desc_factory_->SignalIdentityReady.connect(
473 this, &WebRtcSession::OnIdentityReady);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000474 return true;
475}
476
477void WebRtcSession::Terminate() {
478 SetState(STATE_RECEIVEDTERMINATE);
479 RemoveUnusedChannelsAndTransports(NULL);
480 ASSERT(voice_channel_.get() == NULL);
481 ASSERT(video_channel_.get() == NULL);
482 ASSERT(data_channel_.get() == NULL);
483}
484
485bool WebRtcSession::StartCandidatesAllocation() {
486 // SpeculativelyConnectTransportChannels, will call ConnectChannels method
487 // from TransportProxy to start gathering ice candidates.
488 SpeculativelyConnectAllTransportChannels();
489 if (!saved_candidates_.empty()) {
490 // If there are saved candidates which arrived before local description is
491 // set, copy those to remote description.
492 CopySavedCandidates(remote_desc_.get());
493 }
494 // Push remote candidates present in remote description to transport channels.
495 UseCandidatesInSessionDescription(remote_desc_.get());
496 return true;
497}
498
499void WebRtcSession::set_secure_policy(
500 cricket::SecureMediaPolicy secure_policy) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000501 webrtc_session_desc_factory_->set_secure(secure_policy);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000502}
503
wu@webrtc.org91053e72013-08-10 07:18:04 +0000504cricket::SecureMediaPolicy WebRtcSession::secure_policy() const {
505 return webrtc_session_desc_factory_->secure();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000506}
507
wu@webrtc.org91053e72013-08-10 07:18:04 +0000508void WebRtcSession::CreateOffer(CreateSessionDescriptionObserver* observer,
509 const MediaConstraintsInterface* constraints) {
510 webrtc_session_desc_factory_->CreateOffer(observer, constraints);
511}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000512
wu@webrtc.org91053e72013-08-10 07:18:04 +0000513void WebRtcSession::CreateAnswer(CreateSessionDescriptionObserver* observer,
514 const MediaConstraintsInterface* constraints) {
515 webrtc_session_desc_factory_->CreateAnswer(observer, constraints);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000516}
517
518bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
519 std::string* err_desc) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000520 cricket::SecureMediaPolicy secure_policy =
521 webrtc_session_desc_factory_->secure();
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000522 // Takes the ownership of |desc| regardless of the result.
523 talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
524
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000525 if (error() != cricket::BaseSession::ERROR_NONE) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000526 return BadLocalSdp(SessionErrorMsg(error()), err_desc);
527 }
528
529 if (!desc || !desc->description()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000530 return BadLocalSdp(kInvalidSdp, err_desc);
531 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000532
533 if (!VerifyBundleSettings(desc->description())) {
534 return BadLocalSdp(kBundleWithoutRtcpMux, err_desc);
535 }
536
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000537 Action action = GetAction(desc->type());
538 if (!ExpectSetLocalDescription(action)) {
539 std::string type = desc->type();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000540 return BadLocalSdp(BadStateErrMsg(type, state()), err_desc);
541 }
wu@webrtc.org91053e72013-08-10 07:18:04 +0000542 if (secure_policy == cricket::SEC_REQUIRED &&
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000543 !VerifyCrypto(desc->description())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000544 return BadLocalSdp(kSdpWithoutCrypto, err_desc);
545 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000546 if (action == kAnswer && !VerifyMediaDescriptions(
547 desc->description(), remote_description()->description())) {
548 return BadLocalSdp(kMlineMismatch, err_desc);
549 }
550
551 // Update the initiator flag if this session is the initiator.
552 if (state() == STATE_INIT && action == kOffer) {
553 set_initiator(true);
554 }
555
556 // Update the MediaContentDescription crypto settings as per the policy set.
wu@webrtc.org91053e72013-08-10 07:18:04 +0000557 UpdateSessionDescriptionSecurePolicy(secure_policy, desc->description());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000558
559 set_local_description(desc->description()->Copy());
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000560 local_desc_.reset(desc_temp.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000561
562 // Transport and Media channels will be created only when offer is set.
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000563 if (action == kOffer && !CreateChannels(local_desc_->description())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000564 // TODO(mallinath) - Handle CreateChannel failure, as new local description
565 // is applied. Restore back to old description.
566 return BadLocalSdp(kCreateChannelFailed, err_desc);
567 }
568
569 // Remove channel and transport proxies, if MediaContentDescription is
570 // rejected.
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000571 RemoveUnusedChannelsAndTransports(local_desc_->description());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000572
573 if (!UpdateSessionState(action, cricket::CS_LOCAL,
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000574 local_desc_->description(), err_desc)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000575 return false;
576 }
577 // Kick starting the ice candidates allocation.
578 StartCandidatesAllocation();
579
580 // Update state and SSRC of local MediaStreams and DataChannels based on the
581 // local session description.
582 mediastream_signaling_->OnLocalDescriptionChanged(local_desc_.get());
583
584 if (error() != cricket::BaseSession::ERROR_NONE) {
585 return BadLocalSdp(SessionErrorMsg(error()), err_desc);
586 }
587 return true;
588}
589
590bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
591 std::string* err_desc) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000592 cricket::SecureMediaPolicy secure_policy =
593 webrtc_session_desc_factory_->secure();
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000594 // Takes the ownership of |desc| regardless of the result.
595 talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
596
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000597 if (error() != cricket::BaseSession::ERROR_NONE) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000598 return BadRemoteSdp(SessionErrorMsg(error()), err_desc);
599 }
600
601 if (!desc || !desc->description()) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000602 return BadRemoteSdp(kInvalidSdp, err_desc);
603 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000604
605 if (!VerifyBundleSettings(desc->description())) {
606 return BadRemoteSdp(kBundleWithoutRtcpMux, err_desc);
607 }
608
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000609 Action action = GetAction(desc->type());
610 if (!ExpectSetRemoteDescription(action)) {
611 std::string type = desc->type();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000612 return BadRemoteSdp(BadStateErrMsg(type, state()), err_desc);
613 }
614
615 if (action == kAnswer && !VerifyMediaDescriptions(
616 desc->description(), local_description()->description())) {
617 return BadRemoteSdp(kMlineMismatch, err_desc);
618 }
619
wu@webrtc.org91053e72013-08-10 07:18:04 +0000620 if (secure_policy == cricket::SEC_REQUIRED &&
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000621 !VerifyCrypto(desc->description())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000622 return BadRemoteSdp(kSdpWithoutCrypto, err_desc);
623 }
624
625 // Transport and Media channels will be created only when offer is set.
626 if (action == kOffer && !CreateChannels(desc->description())) {
627 // TODO(mallinath) - Handle CreateChannel failure, as new local description
628 // is applied. Restore back to old description.
629 return BadRemoteSdp(kCreateChannelFailed, err_desc);
630 }
631
632 // Remove channel and transport proxies, if MediaContentDescription is
633 // rejected.
634 RemoveUnusedChannelsAndTransports(desc->description());
635
636 // NOTE: Candidates allocation will be initiated only when SetLocalDescription
637 // is called.
638 set_remote_description(desc->description()->Copy());
639 if (!UpdateSessionState(action, cricket::CS_REMOTE,
640 desc->description(), err_desc)) {
641 return false;
642 }
643
644 // Update remote MediaStreams.
645 mediastream_signaling_->OnRemoteDescriptionChanged(desc);
646 if (local_description() && !UseCandidatesInSessionDescription(desc)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000647 return BadRemoteSdp(kInvalidCandidates, err_desc);
648 }
649
650 // Copy all saved candidates.
651 CopySavedCandidates(desc);
652 // We retain all received candidates.
wu@webrtc.org91053e72013-08-10 07:18:04 +0000653 WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
654 remote_desc_.get(), desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000655 // Check if this new SessionDescription contains new ice ufrag and password
656 // that indicates the remote peer requests ice restart.
657 ice_restart_latch_->CheckForRemoteIceRestart(remote_desc_.get(),
658 desc);
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000659 remote_desc_.reset(desc_temp.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000660 if (error() != cricket::BaseSession::ERROR_NONE) {
661 return BadRemoteSdp(SessionErrorMsg(error()), err_desc);
662 }
663 return true;
664}
665
666bool WebRtcSession::UpdateSessionState(
667 Action action, cricket::ContentSource source,
668 const cricket::SessionDescription* desc,
669 std::string* err_desc) {
670 // If there's already a pending error then no state transition should happen.
671 // But all call-sites should be verifying this before calling us!
672 ASSERT(error() == cricket::BaseSession::ERROR_NONE);
673 if (action == kOffer) {
674 if (!PushdownTransportDescription(source, cricket::CA_OFFER)) {
675 return BadSdp(source, kPushDownOfferTDFailed, err_desc);
676 }
677 SetState(source == cricket::CS_LOCAL ?
678 STATE_SENTINITIATE : STATE_RECEIVEDINITIATE);
679 if (error() != cricket::BaseSession::ERROR_NONE) {
680 return SetSessionStateFailed(source, error(), err_desc);
681 }
682 } else if (action == kPrAnswer) {
683 if (!PushdownTransportDescription(source, cricket::CA_PRANSWER)) {
684 return BadSdp(source, kPushDownPranswerTDFailed, err_desc);
685 }
686 EnableChannels();
687 SetState(source == cricket::CS_LOCAL ?
688 STATE_SENTPRACCEPT : STATE_RECEIVEDPRACCEPT);
689 if (error() != cricket::BaseSession::ERROR_NONE) {
690 return SetSessionStateFailed(source, error(), err_desc);
691 }
692 } else if (action == kAnswer) {
693 if (!PushdownTransportDescription(source, cricket::CA_ANSWER)) {
694 return BadSdp(source, kPushDownAnswerTDFailed, err_desc);
695 }
696 MaybeEnableMuxingSupport();
697 EnableChannels();
698 SetState(source == cricket::CS_LOCAL ?
699 STATE_SENTACCEPT : STATE_RECEIVEDACCEPT);
700 if (error() != cricket::BaseSession::ERROR_NONE) {
701 return SetSessionStateFailed(source, error(), err_desc);
702 }
703 }
704 return true;
705}
706
707WebRtcSession::Action WebRtcSession::GetAction(const std::string& type) {
708 if (type == SessionDescriptionInterface::kOffer) {
709 return WebRtcSession::kOffer;
710 } else if (type == SessionDescriptionInterface::kPrAnswer) {
711 return WebRtcSession::kPrAnswer;
712 } else if (type == SessionDescriptionInterface::kAnswer) {
713 return WebRtcSession::kAnswer;
714 }
715 ASSERT(false && "unknown action type");
716 return WebRtcSession::kOffer;
717}
718
719bool WebRtcSession::ProcessIceMessage(const IceCandidateInterface* candidate) {
720 if (state() == STATE_INIT) {
721 LOG(LS_ERROR) << "ProcessIceMessage: ICE candidates can't be added "
722 << "without any offer (local or remote) "
723 << "session description.";
724 return false;
725 }
726
727 if (!candidate) {
728 LOG(LS_ERROR) << "ProcessIceMessage: Candidate is NULL";
729 return false;
730 }
731
732 if (!local_description() || !remote_description()) {
733 LOG(LS_INFO) << "ProcessIceMessage: Remote description not set, "
734 << "save the candidate for later use.";
735 saved_candidates_.push_back(
736 new JsepIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(),
737 candidate->candidate()));
738 return true;
739 }
740
741 // Add this candidate to the remote session description.
742 if (!remote_desc_->AddCandidate(candidate)) {
743 LOG(LS_ERROR) << "ProcessIceMessage: Candidate cannot be used";
744 return false;
745 }
746
747 return UseCandidatesInSessionDescription(remote_desc_.get());
748}
749
750bool WebRtcSession::GetTrackIdBySsrc(uint32 ssrc, std::string* id) {
751 if (GetLocalTrackId(ssrc, id)) {
752 if (GetRemoteTrackId(ssrc, id)) {
753 LOG(LS_WARNING) << "SSRC " << ssrc
754 << " exists in both local and remote descriptions";
755 return true; // We return the remote track id.
756 }
757 return true;
758 } else {
759 return GetRemoteTrackId(ssrc, id);
760 }
761}
762
763bool WebRtcSession::GetLocalTrackId(uint32 ssrc, std::string* track_id) {
764 if (!BaseSession::local_description())
765 return false;
766 return webrtc::GetTrackIdBySsrc(
767 BaseSession::local_description(), ssrc, track_id);
768}
769
770bool WebRtcSession::GetRemoteTrackId(uint32 ssrc, std::string* track_id) {
771 if (!BaseSession::remote_description())
772 return false;
773 return webrtc::GetTrackIdBySsrc(
774 BaseSession::remote_description(), ssrc, track_id);
775}
776
777std::string WebRtcSession::BadStateErrMsg(
778 const std::string& type, State state) {
779 std::ostringstream desc;
780 desc << "Called with type in wrong state, "
781 << "type: " << type << " state: " << GetStateString(state);
782 return desc.str();
783}
784
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000785void WebRtcSession::SetAudioPlayout(uint32 ssrc, bool enable,
786 cricket::AudioRenderer* renderer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000787 ASSERT(signaling_thread()->IsCurrent());
788 if (!voice_channel_) {
789 LOG(LS_ERROR) << "SetAudioPlayout: No audio channel exists.";
790 return;
791 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000792 if (!voice_channel_->SetRemoteRenderer(ssrc, renderer)) {
793 // SetRenderer() can fail if the ssrc does not match any playout channel.
794 LOG(LS_ERROR) << "SetAudioPlayout: ssrc is incorrect: " << ssrc;
795 return;
796 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000797 if (!voice_channel_->SetOutputScaling(ssrc, enable ? 1 : 0, enable ? 1 : 0)) {
798 // Allow that SetOutputScaling fail if |enable| is false but assert
799 // otherwise. This in the normal case when the underlying media channel has
800 // already been deleted.
801 ASSERT(enable == false);
802 }
803}
804
805void WebRtcSession::SetAudioSend(uint32 ssrc, bool enable,
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000806 const cricket::AudioOptions& options,
807 cricket::AudioRenderer* renderer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000808 ASSERT(signaling_thread()->IsCurrent());
809 if (!voice_channel_) {
810 LOG(LS_ERROR) << "SetAudioSend: No audio channel exists.";
811 return;
812 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000813 if (!voice_channel_->SetLocalRenderer(ssrc, renderer)) {
814 // SetRenderer() can fail if the ssrc does not match any send channel.
815 LOG(LS_ERROR) << "SetAudioSend: ssrc is incorrect: " << ssrc;
816 return;
817 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000818 if (!voice_channel_->MuteStream(ssrc, !enable)) {
819 // Allow that MuteStream fail if |enable| is false but assert otherwise.
820 // This in the normal case when the underlying media channel has already
821 // been deleted.
822 ASSERT(enable == false);
823 return;
824 }
825 if (enable)
826 voice_channel_->SetChannelOptions(options);
827}
828
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000829bool WebRtcSession::SetCaptureDevice(uint32 ssrc,
830 cricket::VideoCapturer* camera) {
831 ASSERT(signaling_thread()->IsCurrent());
832
833 if (!video_channel_.get()) {
834 // |video_channel_| doesnt't exist. Probably because the remote end doesnt't
835 // support video.
836 LOG(LS_WARNING) << "Video not used in this call.";
837 return false;
838 }
839 if (!video_channel_->SetCapturer(ssrc, camera)) {
840 // Allow that SetCapturer fail if |camera| is NULL but assert otherwise.
841 // This in the normal case when the underlying media channel has already
842 // been deleted.
843 ASSERT(camera == NULL);
844 return false;
845 }
846 return true;
847}
848
849void WebRtcSession::SetVideoPlayout(uint32 ssrc,
850 bool enable,
851 cricket::VideoRenderer* renderer) {
852 ASSERT(signaling_thread()->IsCurrent());
853 if (!video_channel_) {
854 LOG(LS_WARNING) << "SetVideoPlayout: No video channel exists.";
855 return;
856 }
857 if (!video_channel_->SetRenderer(ssrc, enable ? renderer : NULL)) {
858 // Allow that SetRenderer fail if |renderer| is NULL but assert otherwise.
859 // This in the normal case when the underlying media channel has already
860 // been deleted.
861 ASSERT(renderer == NULL);
862 }
863}
864
865void WebRtcSession::SetVideoSend(uint32 ssrc, bool enable,
866 const cricket::VideoOptions* options) {
867 ASSERT(signaling_thread()->IsCurrent());
868 if (!video_channel_) {
869 LOG(LS_WARNING) << "SetVideoSend: No video channel exists.";
870 return;
871 }
872 if (!video_channel_->MuteStream(ssrc, !enable)) {
873 // Allow that MuteStream fail if |enable| is false but assert otherwise.
874 // This in the normal case when the underlying media channel has already
875 // been deleted.
876 ASSERT(enable == false);
877 return;
878 }
879 if (enable && options)
880 video_channel_->SetChannelOptions(*options);
881}
882
883bool WebRtcSession::CanInsertDtmf(const std::string& track_id) {
884 ASSERT(signaling_thread()->IsCurrent());
885 if (!voice_channel_) {
886 LOG(LS_ERROR) << "CanInsertDtmf: No audio channel exists.";
887 return false;
888 }
889 uint32 send_ssrc = 0;
890 // The Dtmf is negotiated per channel not ssrc, so we only check if the ssrc
891 // exists.
892 if (!GetAudioSsrcByTrackId(BaseSession::local_description(), track_id,
893 &send_ssrc)) {
894 LOG(LS_ERROR) << "CanInsertDtmf: Track does not exist: " << track_id;
895 return false;
896 }
897 return voice_channel_->CanInsertDtmf();
898}
899
900bool WebRtcSession::InsertDtmf(const std::string& track_id,
901 int code, int duration) {
902 ASSERT(signaling_thread()->IsCurrent());
903 if (!voice_channel_) {
904 LOG(LS_ERROR) << "InsertDtmf: No audio channel exists.";
905 return false;
906 }
907 uint32 send_ssrc = 0;
908 if (!VERIFY(GetAudioSsrcByTrackId(BaseSession::local_description(),
909 track_id, &send_ssrc))) {
910 LOG(LS_ERROR) << "InsertDtmf: Track does not exist: " << track_id;
911 return false;
912 }
913 if (!voice_channel_->InsertDtmf(send_ssrc, code, duration,
914 cricket::DF_SEND)) {
915 LOG(LS_ERROR) << "Failed to insert DTMF to channel.";
916 return false;
917 }
918 return true;
919}
920
921sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() {
922 return &SignalVoiceChannelDestroyed;
923}
924
925talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
926 const std::string& label,
927 const DataChannelInit* config) {
928 if (state() == STATE_RECEIVEDTERMINATE) {
929 return NULL;
930 }
931 if (data_channel_type_ == cricket::DCT_NONE) {
932 LOG(LS_ERROR) << "CreateDataChannel: Data is not supported in this call.";
933 return NULL;
934 }
935 DataChannelInit new_config = config ? (*config) : DataChannelInit();
936
937 if (data_channel_type_ == cricket::DCT_SCTP) {
938 if (new_config.id < 0) {
939 if (!mediastream_signaling_->AllocateSctpId(&new_config.id)) {
940 LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel.";
941 return NULL;
942 }
943 } else if (!mediastream_signaling_->IsSctpIdAvailable(new_config.id)) {
944 LOG(LS_ERROR) << "Failed to create a SCTP data channel "
945 << "because the id is already in use or out of range.";
946 return NULL;
947 }
948 }
wu@webrtc.org91053e72013-08-10 07:18:04 +0000949
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000950 talk_base::scoped_refptr<DataChannel> channel(
951 DataChannel::Create(this, label, &new_config));
952 if (channel == NULL)
953 return NULL;
954 if (!mediastream_signaling_->AddDataChannel(channel))
955 return NULL;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000956 if (data_channel_type_ == cricket::DCT_SCTP) {
957 if (config == NULL) {
958 LOG(LS_WARNING) << "Could not send data channel OPEN message"
959 << " because of NULL config.";
960 return NULL;
961 }
962 if (data_channel_.get()) {
963 channel->SetReceiveSsrc(new_config.id);
964 channel->SetSendSsrc(new_config.id);
965 channel->ConnectToDataSession();
966 }
967 if (!config->negotiated) {
968 talk_base::Buffer *payload = new talk_base::Buffer;
969 if (!mediastream_signaling_->WriteDataChannelOpenMessage(
970 label, *config, payload)) {
971 LOG(LS_WARNING) << "Could not write data channel OPEN message";
972 }
973 // SendControl may queue the message until the data channel's set up,
974 // or congestion clears.
975 channel->SendControl(payload);
976 }
977 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000978 return channel;
979}
980
981cricket::DataChannelType WebRtcSession::data_channel_type() const {
982 return data_channel_type_;
983}
984
wu@webrtc.org91053e72013-08-10 07:18:04 +0000985bool WebRtcSession::IceRestartPending() const {
986 return ice_restart_latch_->Get();
987}
988
989void WebRtcSession::ResetIceRestartLatch() {
990 ice_restart_latch_->Reset();
991}
992
993void WebRtcSession::OnIdentityReady(talk_base::SSLIdentity* identity) {
994 SetIdentity(identity);
995}
996
997bool WebRtcSession::waiting_for_identity() const {
998 return webrtc_session_desc_factory_->waiting_for_identity();
999}
1000
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001001void WebRtcSession::SetIceConnectionState(
1002 PeerConnectionInterface::IceConnectionState state) {
1003 if (ice_connection_state_ == state) {
1004 return;
1005 }
1006
1007 // ASSERT that the requested transition is allowed. Note that
1008 // WebRtcSession does not implement "kIceConnectionClosed" (that is handled
1009 // within PeerConnection). This switch statement should compile away when
1010 // ASSERTs are disabled.
1011 switch (ice_connection_state_) {
1012 case PeerConnectionInterface::kIceConnectionNew:
1013 ASSERT(state == PeerConnectionInterface::kIceConnectionChecking);
1014 break;
1015 case PeerConnectionInterface::kIceConnectionChecking:
1016 ASSERT(state == PeerConnectionInterface::kIceConnectionFailed ||
1017 state == PeerConnectionInterface::kIceConnectionConnected);
1018 break;
1019 case PeerConnectionInterface::kIceConnectionConnected:
1020 ASSERT(state == PeerConnectionInterface::kIceConnectionDisconnected ||
1021 state == PeerConnectionInterface::kIceConnectionChecking ||
1022 state == PeerConnectionInterface::kIceConnectionCompleted);
1023 break;
1024 case PeerConnectionInterface::kIceConnectionCompleted:
1025 ASSERT(state == PeerConnectionInterface::kIceConnectionConnected ||
1026 state == PeerConnectionInterface::kIceConnectionDisconnected);
1027 break;
1028 case PeerConnectionInterface::kIceConnectionFailed:
1029 ASSERT(state == PeerConnectionInterface::kIceConnectionNew);
1030 break;
1031 case PeerConnectionInterface::kIceConnectionDisconnected:
1032 ASSERT(state == PeerConnectionInterface::kIceConnectionChecking ||
1033 state == PeerConnectionInterface::kIceConnectionConnected ||
1034 state == PeerConnectionInterface::kIceConnectionCompleted ||
1035 state == PeerConnectionInterface::kIceConnectionFailed);
1036 break;
1037 case PeerConnectionInterface::kIceConnectionClosed:
1038 ASSERT(false);
1039 break;
1040 default:
1041 ASSERT(false);
1042 break;
1043 }
1044
1045 ice_connection_state_ = state;
1046 if (ice_observer_) {
1047 ice_observer_->OnIceConnectionChange(ice_connection_state_);
1048 }
1049}
1050
1051void WebRtcSession::OnTransportRequestSignaling(
1052 cricket::Transport* transport) {
1053 ASSERT(signaling_thread()->IsCurrent());
1054 transport->OnSignalingReady();
1055 if (ice_observer_) {
1056 ice_observer_->OnIceGatheringChange(
1057 PeerConnectionInterface::kIceGatheringGathering);
1058 }
1059}
1060
1061void WebRtcSession::OnTransportConnecting(cricket::Transport* transport) {
1062 ASSERT(signaling_thread()->IsCurrent());
1063 // start monitoring for the write state of the transport.
1064 OnTransportWritable(transport);
1065}
1066
1067void WebRtcSession::OnTransportWritable(cricket::Transport* transport) {
1068 ASSERT(signaling_thread()->IsCurrent());
1069 // TODO(bemasc): Expose more API from Transport to detect when
1070 // candidate selection starts or stops, due to success or failure.
1071 if (transport->all_channels_writable()) {
1072 if (ice_connection_state_ ==
1073 PeerConnectionInterface::kIceConnectionChecking ||
1074 ice_connection_state_ ==
1075 PeerConnectionInterface::kIceConnectionDisconnected) {
1076 SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected);
1077 }
1078 } else if (transport->HasChannels()) {
1079 // If the current state is Connected or Completed, then there were writable
1080 // channels but now there are not, so the next state must be Disconnected.
1081 if (ice_connection_state_ ==
1082 PeerConnectionInterface::kIceConnectionConnected ||
1083 ice_connection_state_ ==
1084 PeerConnectionInterface::kIceConnectionCompleted) {
1085 SetIceConnectionState(
1086 PeerConnectionInterface::kIceConnectionDisconnected);
1087 }
1088 }
1089}
1090
1091void WebRtcSession::OnTransportProxyCandidatesReady(
1092 cricket::TransportProxy* proxy, const cricket::Candidates& candidates) {
1093 ASSERT(signaling_thread()->IsCurrent());
1094 ProcessNewLocalCandidate(proxy->content_name(), candidates);
1095}
1096
1097bool WebRtcSession::ExpectSetLocalDescription(Action action) {
1098 return ((action == kOffer && state() == STATE_INIT) ||
1099 // update local offer
1100 (action == kOffer && state() == STATE_SENTINITIATE) ||
1101 // update the current ongoing session.
1102 (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
1103 (action == kOffer && state() == STATE_SENTACCEPT) ||
1104 (action == kOffer && state() == STATE_INPROGRESS) ||
1105 // accept remote offer
1106 (action == kAnswer && state() == STATE_RECEIVEDINITIATE) ||
1107 (action == kAnswer && state() == STATE_SENTPRACCEPT) ||
1108 (action == kPrAnswer && state() == STATE_RECEIVEDINITIATE) ||
1109 (action == kPrAnswer && state() == STATE_SENTPRACCEPT));
1110}
1111
1112bool WebRtcSession::ExpectSetRemoteDescription(Action action) {
1113 return ((action == kOffer && state() == STATE_INIT) ||
1114 // update remote offer
1115 (action == kOffer && state() == STATE_RECEIVEDINITIATE) ||
1116 // update the current ongoing session
1117 (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
1118 (action == kOffer && state() == STATE_SENTACCEPT) ||
1119 (action == kOffer && state() == STATE_INPROGRESS) ||
1120 // accept local offer
1121 (action == kAnswer && state() == STATE_SENTINITIATE) ||
1122 (action == kAnswer && state() == STATE_RECEIVEDPRACCEPT) ||
1123 (action == kPrAnswer && state() == STATE_SENTINITIATE) ||
1124 (action == kPrAnswer && state() == STATE_RECEIVEDPRACCEPT));
1125}
1126
1127void WebRtcSession::OnCandidatesAllocationDone() {
1128 ASSERT(signaling_thread()->IsCurrent());
1129 if (ice_observer_) {
1130 ice_observer_->OnIceGatheringChange(
1131 PeerConnectionInterface::kIceGatheringComplete);
1132 ice_observer_->OnIceComplete();
1133 }
1134}
1135
1136// Enabling voice and video channel.
1137void WebRtcSession::EnableChannels() {
1138 if (voice_channel_ && !voice_channel_->enabled())
1139 voice_channel_->Enable(true);
1140
1141 if (video_channel_ && !video_channel_->enabled())
1142 video_channel_->Enable(true);
1143
1144 if (data_channel_.get() && !data_channel_->enabled())
1145 data_channel_->Enable(true);
1146}
1147
1148void WebRtcSession::ProcessNewLocalCandidate(
1149 const std::string& content_name,
1150 const cricket::Candidates& candidates) {
1151 int sdp_mline_index;
1152 if (!GetLocalCandidateMediaIndex(content_name, &sdp_mline_index)) {
1153 LOG(LS_ERROR) << "ProcessNewLocalCandidate: content name "
1154 << content_name << " not found";
1155 return;
1156 }
1157
1158 for (cricket::Candidates::const_iterator citer = candidates.begin();
1159 citer != candidates.end(); ++citer) {
1160 // Use content_name as the candidate media id.
1161 JsepIceCandidate candidate(content_name, sdp_mline_index, *citer);
1162 if (ice_observer_) {
1163 ice_observer_->OnIceCandidate(&candidate);
1164 }
1165 if (local_desc_) {
1166 local_desc_->AddCandidate(&candidate);
1167 }
1168 }
1169}
1170
1171// Returns the media index for a local ice candidate given the content name.
1172bool WebRtcSession::GetLocalCandidateMediaIndex(const std::string& content_name,
1173 int* sdp_mline_index) {
1174 if (!BaseSession::local_description() || !sdp_mline_index)
1175 return false;
1176
1177 bool content_found = false;
1178 const ContentInfos& contents = BaseSession::local_description()->contents();
1179 for (size_t index = 0; index < contents.size(); ++index) {
1180 if (contents[index].name == content_name) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001181 *sdp_mline_index = static_cast<int>(index);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001182 content_found = true;
1183 break;
1184 }
1185 }
1186 return content_found;
1187}
1188
1189bool WebRtcSession::UseCandidatesInSessionDescription(
1190 const SessionDescriptionInterface* remote_desc) {
1191 if (!remote_desc)
1192 return true;
1193 bool ret = true;
1194 for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) {
1195 const IceCandidateCollection* candidates = remote_desc->candidates(m);
1196 for (size_t n = 0; n < candidates->count(); ++n) {
1197 ret = UseCandidate(candidates->at(n));
1198 if (!ret)
1199 break;
1200 }
1201 }
1202 return ret;
1203}
1204
1205bool WebRtcSession::UseCandidate(
1206 const IceCandidateInterface* candidate) {
1207
1208 size_t mediacontent_index = static_cast<size_t>(candidate->sdp_mline_index());
1209 size_t remote_content_size =
1210 BaseSession::remote_description()->contents().size();
1211 if (mediacontent_index >= remote_content_size) {
1212 LOG(LS_ERROR)
1213 << "UseRemoteCandidateInSession: Invalid candidate media index.";
1214 return false;
1215 }
1216
1217 cricket::ContentInfo content =
1218 BaseSession::remote_description()->contents()[mediacontent_index];
1219 std::vector<cricket::Candidate> candidates;
1220 candidates.push_back(candidate->candidate());
1221 // Invoking BaseSession method to handle remote candidates.
1222 std::string error;
1223 if (OnRemoteCandidates(content.name, candidates, &error)) {
1224 // Candidates successfully submitted for checking.
1225 if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew ||
1226 ice_connection_state_ ==
1227 PeerConnectionInterface::kIceConnectionDisconnected) {
1228 // If state is New, then the session has just gotten its first remote ICE
1229 // candidates, so go to Checking.
1230 // If state is Disconnected, the session is re-using old candidates or
1231 // receiving additional ones, so go to Checking.
1232 // If state is Connected, stay Connected.
1233 // TODO(bemasc): If state is Connected, and the new candidates are for a
1234 // newly added transport, then the state actually _should_ move to
1235 // checking. Add a way to distinguish that case.
1236 SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking);
1237 }
1238 // TODO(bemasc): If state is Completed, go back to Connected.
1239 } else {
1240 LOG(LS_WARNING) << error;
1241 }
1242 return true;
1243}
1244
1245void WebRtcSession::RemoveUnusedChannelsAndTransports(
1246 const SessionDescription* desc) {
1247 const cricket::ContentInfo* voice_info =
1248 cricket::GetFirstAudioContent(desc);
1249 if ((!voice_info || voice_info->rejected) && voice_channel_) {
1250 mediastream_signaling_->OnAudioChannelClose();
1251 SignalVoiceChannelDestroyed();
1252 const std::string content_name = voice_channel_->content_name();
1253 channel_manager_->DestroyVoiceChannel(voice_channel_.release());
1254 DestroyTransportProxy(content_name);
1255 }
1256
1257 const cricket::ContentInfo* video_info =
1258 cricket::GetFirstVideoContent(desc);
1259 if ((!video_info || video_info->rejected) && video_channel_) {
1260 mediastream_signaling_->OnVideoChannelClose();
1261 SignalVideoChannelDestroyed();
1262 const std::string content_name = video_channel_->content_name();
1263 channel_manager_->DestroyVideoChannel(video_channel_.release());
1264 DestroyTransportProxy(content_name);
1265 }
1266
1267 const cricket::ContentInfo* data_info =
1268 cricket::GetFirstDataContent(desc);
1269 if ((!data_info || data_info->rejected) && data_channel_) {
1270 mediastream_signaling_->OnDataChannelClose();
1271 SignalDataChannelDestroyed();
1272 const std::string content_name = data_channel_->content_name();
1273 channel_manager_->DestroyDataChannel(data_channel_.release());
1274 DestroyTransportProxy(content_name);
1275 }
1276}
1277
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001278// TODO(mallinath) - Add a correct error code if the channels are not creatued
1279// due to BUNDLE is enabled but rtcp-mux is disabled.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001280bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
1281 // Disabling the BUNDLE flag in PortAllocator if offer disabled it.
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001282 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1283 if (state() == STATE_INIT && !bundle_enabled) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001284 port_allocator()->set_flags(port_allocator()->flags() &
1285 ~cricket::PORTALLOCATOR_ENABLE_BUNDLE);
1286 }
1287
1288 // Creating the media channels and transport proxies.
1289 const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(desc);
1290 if (voice && !voice->rejected && !voice_channel_) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001291 if (!CreateVoiceChannel(voice)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001292 LOG(LS_ERROR) << "Failed to create voice channel.";
1293 return false;
1294 }
1295 }
1296
1297 const cricket::ContentInfo* video = cricket::GetFirstVideoContent(desc);
1298 if (video && !video->rejected && !video_channel_) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001299 if (!CreateVideoChannel(video)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001300 LOG(LS_ERROR) << "Failed to create video channel.";
1301 return false;
1302 }
1303 }
1304
1305 const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc);
1306 if (data_channel_type_ != cricket::DCT_NONE &&
1307 data && !data->rejected && !data_channel_.get()) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001308 if (!CreateDataChannel(data)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001309 LOG(LS_ERROR) << "Failed to create data channel.";
1310 return false;
1311 }
1312 }
1313
1314 return true;
1315}
1316
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001317bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001318 voice_channel_.reset(channel_manager_->CreateVoiceChannel(
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001319 this, content->name, true));
1320 return (voice_channel_ != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001321}
1322
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001323bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001324 video_channel_.reset(channel_manager_->CreateVideoChannel(
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001325 this, content->name, true, voice_channel_.get()));
1326 return (video_channel_ != NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001327}
1328
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001329bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001330 bool rtcp = (data_channel_type_ == cricket::DCT_RTP);
1331 data_channel_.reset(channel_manager_->CreateDataChannel(
wu@webrtc.org91053e72013-08-10 07:18:04 +00001332 this, content->name, rtcp, data_channel_type_));
1333 if (!data_channel_.get()) {
1334 return false;
1335 }
1336 data_channel_->SignalDataReceived.connect(
1337 this, &WebRtcSession::OnDataReceived);
1338 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001339}
1340
1341void WebRtcSession::CopySavedCandidates(
1342 SessionDescriptionInterface* dest_desc) {
1343 if (!dest_desc) {
1344 ASSERT(false);
1345 return;
1346 }
1347 for (size_t i = 0; i < saved_candidates_.size(); ++i) {
1348 dest_desc->AddCandidate(saved_candidates_[i]);
1349 delete saved_candidates_[i];
1350 }
1351 saved_candidates_.clear();
1352}
1353
wu@webrtc.org91053e72013-08-10 07:18:04 +00001354// Look for OPEN messages and set up data channels in response.
1355void WebRtcSession::OnDataReceived(
1356 cricket::DataChannel* channel,
1357 const cricket::ReceiveDataParams& params,
1358 const talk_base::Buffer& payload) {
1359 if (params.type != cricket::DMT_CONTROL) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001360 return;
1361 }
1362
wu@webrtc.org91053e72013-08-10 07:18:04 +00001363 std::string label;
1364 DataChannelInit config;
1365 if (!mediastream_signaling_->ParseDataChannelOpenMessage(
1366 payload, &label, &config)) {
1367 LOG(LS_WARNING) << "Failed to parse data channel OPEN message.";
1368 return;
1369 }
1370
1371 config.negotiated = true; // This is the negotiation.
1372
1373 if (!mediastream_signaling_->AddDataChannelFromOpenMessage(
1374 label, config)) {
1375 LOG(LS_WARNING) << "Failed to create data channel from OPEN message.";
1376 return;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001377 }
1378}
1379
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001380// Returns false if bundle is enabled and rtcp_mux is disabled.
1381bool WebRtcSession::VerifyBundleSettings(const SessionDescription* desc) {
1382 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1383 if (!bundle_enabled)
1384 return true;
1385
1386 const cricket::ContentGroup* bundle_group =
1387 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
1388 ASSERT(bundle_group != NULL);
1389
1390 const cricket::ContentInfos& contents = desc->contents();
1391 for (cricket::ContentInfos::const_iterator citer = contents.begin();
1392 citer != contents.end(); ++citer) {
1393 const cricket::ContentInfo* content = (&*citer);
1394 ASSERT(content != NULL);
1395 if (bundle_group->HasContentName(content->name) &&
1396 !content->rejected && content->type == cricket::NS_JINGLE_RTP) {
1397 if (!HasRtcpMuxEnabled(content))
1398 return false;
1399 }
1400 }
1401 // RTCP-MUX is enabled in all the contents.
1402 return true;
1403}
1404
1405bool WebRtcSession::HasRtcpMuxEnabled(
1406 const cricket::ContentInfo* content) {
1407 const cricket::MediaContentDescription* description =
1408 static_cast<cricket::MediaContentDescription*>(content->description);
1409 return description->rtcp_mux();
1410}
1411
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001412} // namespace webrtc