blob: f987993196e636b99760f83209823ce73d041354 [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
pbos@webrtc.org371243d2014-03-07 15:22:04 +000030#include <limits.h>
31
henrike@webrtc.org28e20752013-07-10 00:45:36 +000032#include <algorithm>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000033#include <vector>
34
35#include "talk/app/webrtc/jsepicecandidate.h"
36#include "talk/app/webrtc/jsepsessiondescription.h"
37#include "talk/app/webrtc/mediaconstraintsinterface.h"
38#include "talk/app/webrtc/mediastreamsignaling.h"
39#include "talk/app/webrtc/peerconnectioninterface.h"
wu@webrtc.org91053e72013-08-10 07:18:04 +000040#include "talk/app/webrtc/webrtcsessiondescriptionfactory.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000041#include "talk/base/helpers.h"
42#include "talk/base/logging.h"
43#include "talk/base/stringencode.h"
44#include "talk/media/base/constants.h"
45#include "talk/media/base/videocapturer.h"
46#include "talk/session/media/channel.h"
47#include "talk/session/media/channelmanager.h"
48#include "talk/session/media/mediasession.h"
49
50using cricket::ContentInfo;
51using cricket::ContentInfos;
52using cricket::MediaContentDescription;
53using cricket::SessionDescription;
54using cricket::TransportInfo;
55
henrike@webrtc.org28e20752013-07-10 00:45:36 +000056namespace webrtc {
57
henrike@webrtc.org28e20752013-07-10 00:45:36 +000058// Error messages
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000059const char kBundleWithoutRtcpMux[] = "RTCP-MUX must be enabled when BUNDLE "
60 "is enabled.";
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +000061const char kCreateChannelFailed[] = "Failed to create channels.";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000062const char kInvalidCandidates[] = "Description contains invalid candidates.";
63const char kInvalidSdp[] = "Invalid session description.";
64const char kMlineMismatch[] =
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +000065 "Offer and answer descriptions m-lines are not matching. Rejecting answer.";
66const char kPushDownTDFailed[] =
67 "Failed to push down transport description:";
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +000068const char kSdpWithoutDtlsFingerprint[] =
69 "Called with SDP without DTLS fingerprint.";
70const char kSdpWithoutSdesCrypto[] =
71 "Called with SDP without SDES crypto.";
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +000072const char kSdpWithoutIceUfragPwd[] =
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +000073 "Called with SDP without ice-ufrag and ice-pwd.";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000074const char kSessionError[] = "Session error code: ";
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +000075const char kSessionErrorDesc[] = "Session error description: ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000076
77// Compares |answer| against |offer|. Comparision is done
78// for number of m-lines in answer against offer. If matches true will be
79// returned otherwise false.
80static bool VerifyMediaDescriptions(
81 const SessionDescription* answer, const SessionDescription* offer) {
82 if (offer->contents().size() != answer->contents().size())
83 return false;
84
85 for (size_t i = 0; i < offer->contents().size(); ++i) {
86 if ((offer->contents()[i].name) != answer->contents()[i].name) {
87 return false;
88 }
89 }
90 return true;
91}
92
henrike@webrtc.org28e20752013-07-10 00:45:36 +000093// Checks that each non-rejected content has SDES crypto keys or a DTLS
94// fingerprint. Mismatches, such as replying with a DTLS fingerprint to SDES
95// keys, will be caught in Transport negotiation, and backstopped by Channel's
96// |secure_required| check.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000097static bool VerifyCrypto(const SessionDescription* desc,
98 bool dtls_enabled,
99 std::string* error) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000100 const ContentInfos& contents = desc->contents();
101 for (size_t index = 0; index < contents.size(); ++index) {
102 const ContentInfo* cinfo = &contents[index];
103 if (cinfo->rejected) {
104 continue;
105 }
106
107 // If the content isn't rejected, crypto must be present.
108 const MediaContentDescription* media =
109 static_cast<const MediaContentDescription*>(cinfo->description);
110 const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name);
111 if (!media || !tinfo) {
112 // Something is not right.
113 LOG(LS_ERROR) << kInvalidSdp;
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000114 *error = kInvalidSdp;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000115 return false;
116 }
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000117 if (dtls_enabled) {
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000118 if (!tinfo->description.identity_fingerprint) {
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000119 LOG(LS_WARNING) <<
120 "Session description must have DTLS fingerprint if DTLS enabled.";
121 *error = kSdpWithoutDtlsFingerprint;
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000122 return false;
123 }
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000124 } else {
125 if (media->cryptos().empty()) {
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000126 LOG(LS_WARNING) <<
127 "Session description must have SDES when DTLS disabled.";
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000128 *error = kSdpWithoutSdesCrypto;
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000129 return false;
130 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000131 }
132 }
133
134 return true;
135}
136
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000137// Checks that each non-rejected content has ice-ufrag and ice-pwd set.
138static bool VerifyIceUfragPwdPresent(const SessionDescription* desc) {
139 const ContentInfos& contents = desc->contents();
140 for (size_t index = 0; index < contents.size(); ++index) {
141 const ContentInfo* cinfo = &contents[index];
142 if (cinfo->rejected) {
143 continue;
144 }
145
146 // If the content isn't rejected, ice-ufrag and ice-pwd must be present.
147 const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name);
148 if (!tinfo) {
149 // Something is not right.
150 LOG(LS_ERROR) << kInvalidSdp;
151 return false;
152 }
153 if (tinfo->description.ice_ufrag.empty() ||
154 tinfo->description.ice_pwd.empty()) {
155 LOG(LS_ERROR) << "Session description must have ice ufrag and pwd.";
156 return false;
157 }
158 }
159 return true;
160}
161
wu@webrtc.org91053e72013-08-10 07:18:04 +0000162// Forces |sdesc->crypto_required| to the appropriate state based on the
163// current security policy, to ensure a failure occurs if there is an error
164// in crypto negotiation.
165// Called when processing the local session description.
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000166static void UpdateSessionDescriptionSecurePolicy(cricket::CryptoType type,
167 SessionDescription* sdesc) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000168 if (!sdesc) {
169 return;
170 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000171
wu@webrtc.org91053e72013-08-10 07:18:04 +0000172 // Updating the |crypto_required_| in MediaContentDescription to the
173 // appropriate state based on the current security policy.
174 for (cricket::ContentInfos::iterator iter = sdesc->contents().begin();
175 iter != sdesc->contents().end(); ++iter) {
176 if (cricket::IsMediaContent(&*iter)) {
177 MediaContentDescription* mdesc =
178 static_cast<MediaContentDescription*> (iter->description);
179 if (mdesc) {
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000180 mdesc->set_crypto_required(type);
wu@webrtc.org91053e72013-08-10 07:18:04 +0000181 }
182 }
183 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000184}
185
186static bool GetAudioSsrcByTrackId(
187 const SessionDescription* session_description,
188 const std::string& track_id, uint32 *ssrc) {
189 const cricket::ContentInfo* audio_info =
190 cricket::GetFirstAudioContent(session_description);
191 if (!audio_info) {
192 LOG(LS_ERROR) << "Audio not used in this call";
193 return false;
194 }
195
196 const cricket::MediaContentDescription* audio_content =
197 static_cast<const cricket::MediaContentDescription*>(
198 audio_info->description);
199 cricket::StreamParams stream;
200 if (!cricket::GetStreamByIds(audio_content->streams(), "", track_id,
201 &stream)) {
202 return false;
203 }
204 *ssrc = stream.first_ssrc();
205 return true;
206}
207
208static bool GetTrackIdBySsrc(const SessionDescription* session_description,
209 uint32 ssrc, std::string* track_id) {
210 ASSERT(track_id != NULL);
211
212 cricket::StreamParams stream_out;
213 const cricket::ContentInfo* audio_info =
214 cricket::GetFirstAudioContent(session_description);
215 if (!audio_info) {
216 return false;
217 }
218 const cricket::MediaContentDescription* audio_content =
219 static_cast<const cricket::MediaContentDescription*>(
220 audio_info->description);
221
222 if (cricket::GetStreamBySsrc(audio_content->streams(), ssrc, &stream_out)) {
223 *track_id = stream_out.id;
224 return true;
225 }
226
227 const cricket::ContentInfo* video_info =
228 cricket::GetFirstVideoContent(session_description);
229 if (!video_info) {
230 return false;
231 }
232 const cricket::MediaContentDescription* video_content =
233 static_cast<const cricket::MediaContentDescription*>(
234 video_info->description);
235
236 if (cricket::GetStreamBySsrc(video_content->streams(), ssrc, &stream_out)) {
237 *track_id = stream_out.id;
238 return true;
239 }
240 return false;
241}
242
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000243static bool BadSdp(const std::string& source,
244 const std::string& type,
245 const std::string& reason,
246 std::string* err_desc) {
247 std::ostringstream desc;
248 desc << "Failed to set " << source << " " << type << " sdp: " << reason;
249
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000250 if (err_desc) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000251 *err_desc = desc.str();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000252 }
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000253 LOG(LS_ERROR) << desc.str();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000254 return false;
255}
256
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000257static bool BadSdp(cricket::ContentSource source,
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000258 const std::string& type,
259 const std::string& reason,
260 std::string* err_desc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000261 if (source == cricket::CS_LOCAL) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000262 return BadSdp("local", type, reason, err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000263 } else {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000264 return BadSdp("remote", type, reason, err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000265 }
266}
267
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000268static bool BadLocalSdp(const std::string& type,
269 const std::string& reason,
270 std::string* err_desc) {
271 return BadSdp(cricket::CS_LOCAL, type, reason, err_desc);
272}
273
274static bool BadRemoteSdp(const std::string& type,
275 const std::string& reason,
276 std::string* err_desc) {
277 return BadSdp(cricket::CS_REMOTE, type, reason, err_desc);
278}
279
280static bool BadOfferSdp(cricket::ContentSource source,
281 const std::string& reason,
282 std::string* err_desc) {
283 return BadSdp(source, SessionDescriptionInterface::kOffer, reason, err_desc);
284}
285
286static bool BadPranswerSdp(cricket::ContentSource source,
287 const std::string& reason,
288 std::string* err_desc) {
289 return BadSdp(source, SessionDescriptionInterface::kPrAnswer,
290 reason, err_desc);
291}
292
293static bool BadAnswerSdp(cricket::ContentSource source,
294 const std::string& reason,
295 std::string* err_desc) {
296 return BadSdp(source, SessionDescriptionInterface::kAnswer, reason, err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000297}
298
299#define GET_STRING_OF_STATE(state) \
300 case cricket::BaseSession::state: \
301 result = #state; \
302 break;
303
304static std::string GetStateString(cricket::BaseSession::State state) {
305 std::string result;
306 switch (state) {
307 GET_STRING_OF_STATE(STATE_INIT)
308 GET_STRING_OF_STATE(STATE_SENTINITIATE)
309 GET_STRING_OF_STATE(STATE_RECEIVEDINITIATE)
310 GET_STRING_OF_STATE(STATE_SENTPRACCEPT)
311 GET_STRING_OF_STATE(STATE_SENTACCEPT)
312 GET_STRING_OF_STATE(STATE_RECEIVEDPRACCEPT)
313 GET_STRING_OF_STATE(STATE_RECEIVEDACCEPT)
314 GET_STRING_OF_STATE(STATE_SENTMODIFY)
315 GET_STRING_OF_STATE(STATE_RECEIVEDMODIFY)
316 GET_STRING_OF_STATE(STATE_SENTREJECT)
317 GET_STRING_OF_STATE(STATE_RECEIVEDREJECT)
318 GET_STRING_OF_STATE(STATE_SENTREDIRECT)
319 GET_STRING_OF_STATE(STATE_SENTTERMINATE)
320 GET_STRING_OF_STATE(STATE_RECEIVEDTERMINATE)
321 GET_STRING_OF_STATE(STATE_INPROGRESS)
322 GET_STRING_OF_STATE(STATE_DEINIT)
323 default:
324 ASSERT(false);
325 break;
326 }
327 return result;
328}
329
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000330#define GET_STRING_OF_ERROR_CODE(err) \
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000331 case cricket::BaseSession::err: \
332 result = #err; \
333 break;
334
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000335static std::string GetErrorCodeString(cricket::BaseSession::Error err) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000336 std::string result;
337 switch (err) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000338 GET_STRING_OF_ERROR_CODE(ERROR_NONE)
339 GET_STRING_OF_ERROR_CODE(ERROR_TIME)
340 GET_STRING_OF_ERROR_CODE(ERROR_RESPONSE)
341 GET_STRING_OF_ERROR_CODE(ERROR_NETWORK)
342 GET_STRING_OF_ERROR_CODE(ERROR_CONTENT)
343 GET_STRING_OF_ERROR_CODE(ERROR_TRANSPORT)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000344 default:
345 ASSERT(false);
346 break;
347 }
348 return result;
349}
350
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000351static std::string MakeErrorString(const std::string& error,
352 const std::string& desc) {
353 std::ostringstream ret;
354 ret << error << " " << desc;
355 return ret.str();
356}
357
358static std::string MakeTdErrorString(const std::string& desc) {
359 return MakeErrorString(kPushDownTDFailed, desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000360}
361
362// Help class used to remember if a a remote peer has requested ice restart by
363// by sending a description with new ice ufrag and password.
364class IceRestartAnswerLatch {
365 public:
366 IceRestartAnswerLatch() : ice_restart_(false) { }
367
wu@webrtc.org91053e72013-08-10 07:18:04 +0000368 // Returns true if CheckForRemoteIceRestart has been called with a new session
369 // description where ice password and ufrag has changed since last time
370 // Reset() was called.
371 bool Get() const {
372 return ice_restart_;
373 }
374
375 void Reset() {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000376 if (ice_restart_) {
377 ice_restart_ = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000378 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000379 }
380
381 void CheckForRemoteIceRestart(
382 const SessionDescriptionInterface* old_desc,
383 const SessionDescriptionInterface* new_desc) {
384 if (!old_desc || new_desc->type() != SessionDescriptionInterface::kOffer) {
385 return;
386 }
387 const SessionDescription* new_sd = new_desc->description();
388 const SessionDescription* old_sd = old_desc->description();
389 const ContentInfos& contents = new_sd->contents();
390 for (size_t index = 0; index < contents.size(); ++index) {
391 const ContentInfo* cinfo = &contents[index];
392 if (cinfo->rejected) {
393 continue;
394 }
395 // If the content isn't rejected, check if ufrag and password has
396 // changed.
397 const cricket::TransportDescription* new_transport_desc =
398 new_sd->GetTransportDescriptionByName(cinfo->name);
399 const cricket::TransportDescription* old_transport_desc =
400 old_sd->GetTransportDescriptionByName(cinfo->name);
401 if (!new_transport_desc || !old_transport_desc) {
402 // No transport description exist. This is not an ice restart.
403 continue;
404 }
405 if (new_transport_desc->ice_pwd != old_transport_desc->ice_pwd &&
406 new_transport_desc->ice_ufrag != old_transport_desc->ice_ufrag) {
407 LOG(LS_INFO) << "Remote peer request ice restart.";
408 ice_restart_ = true;
409 break;
410 }
411 }
412 }
413
414 private:
415 bool ice_restart_;
416};
417
wu@webrtc.org91053e72013-08-10 07:18:04 +0000418WebRtcSession::WebRtcSession(
419 cricket::ChannelManager* channel_manager,
420 talk_base::Thread* signaling_thread,
421 talk_base::Thread* worker_thread,
422 cricket::PortAllocator* port_allocator,
423 MediaStreamSignaling* mediastream_signaling)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000424 : cricket::BaseSession(signaling_thread, worker_thread, port_allocator,
425 talk_base::ToString(talk_base::CreateRandomId64() &
426 LLONG_MAX),
427 cricket::NS_JINGLE_RTP, false),
428 // RFC 3264: The numeric value of the session id and version in the
429 // o line MUST be representable with a "64 bit signed integer".
430 // Due to this constraint session id |sid_| is max limited to LLONG_MAX.
431 channel_manager_(channel_manager),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000432 mediastream_signaling_(mediastream_signaling),
433 ice_observer_(NULL),
434 ice_connection_state_(PeerConnectionInterface::kIceConnectionNew),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000435 older_version_remote_peer_(false),
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000436 dtls_enabled_(false),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000437 data_channel_type_(cricket::DCT_NONE),
438 ice_restart_latch_(new IceRestartAnswerLatch) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000439}
440
441WebRtcSession::~WebRtcSession() {
442 if (voice_channel_.get()) {
443 SignalVoiceChannelDestroyed();
444 channel_manager_->DestroyVoiceChannel(voice_channel_.release());
445 }
446 if (video_channel_.get()) {
447 SignalVideoChannelDestroyed();
448 channel_manager_->DestroyVideoChannel(video_channel_.release());
449 }
450 if (data_channel_.get()) {
451 SignalDataChannelDestroyed();
452 channel_manager_->DestroyDataChannel(data_channel_.release());
453 }
454 for (size_t i = 0; i < saved_candidates_.size(); ++i) {
455 delete saved_candidates_[i];
456 }
457 delete identity();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000458}
459
wu@webrtc.org91053e72013-08-10 07:18:04 +0000460bool WebRtcSession::Initialize(
wu@webrtc.org97077a32013-10-25 21:18:33 +0000461 const PeerConnectionFactoryInterface::Options& options,
wu@webrtc.org91053e72013-08-10 07:18:04 +0000462 const MediaConstraintsInterface* constraints,
463 DTLSIdentityServiceInterface* dtls_identity_service) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000464 // TODO(perkj): Take |constraints| into consideration. Return false if not all
465 // mandatory constraints can be fulfilled. Note that |constraints|
466 // can be null.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000467 bool value;
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000468
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000469 if (options.disable_encryption) {
470 dtls_enabled_ = false;
471 } else {
472 // Enable DTLS by default if |dtls_identity_service| is valid.
473 dtls_enabled_ = (dtls_identity_service != NULL);
474 // |constraints| can override the default |dtls_enabled_| value.
475 if (FindConstraint(
476 constraints,
477 MediaConstraintsInterface::kEnableDtlsSrtp,
478 &value, NULL)) {
479 dtls_enabled_ = value;
480 }
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000481 }
482
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000483 // Enable creation of RTP data channels if the kEnableRtpDataChannels is set.
wu@webrtc.org97077a32013-10-25 21:18:33 +0000484 // It takes precendence over the disable_sctp_data_channels
485 // PeerConnectionFactoryInterface::Options.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000486 if (FindConstraint(
487 constraints, MediaConstraintsInterface::kEnableRtpDataChannels,
488 &value, NULL) && value) {
489 LOG(LS_INFO) << "Allowing RTP data engine.";
490 data_channel_type_ = cricket::DCT_RTP;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000491 } else {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000492 // DTLS has to be enabled to use SCTP.
wu@webrtc.org97077a32013-10-25 21:18:33 +0000493 if (!options.disable_sctp_data_channels && dtls_enabled_) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000494 LOG(LS_INFO) << "Allowing SCTP data engine.";
495 data_channel_type_ = cricket::DCT_SCTP;
496 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000497 }
498 if (data_channel_type_ != cricket::DCT_NONE) {
499 mediastream_signaling_->SetDataChannelFactory(this);
500 }
501
wu@webrtc.orgde305012013-10-31 15:40:38 +0000502 // Find DSCP constraint.
503 if (FindConstraint(
504 constraints,
505 MediaConstraintsInterface::kEnableDscp,
506 &value, NULL)) {
henrike@webrtc.org6e3dbc22014-03-25 17:09:47 +0000507 audio_options_.dscp.Set(value);
508 video_options_.dscp.Set(value);
509 }
510
511 // Find Suspend Below Min Bitrate constraint.
512 if (FindConstraint(
513 constraints,
514 MediaConstraintsInterface::kEnableVideoSuspendBelowMinBitrate,
515 &value,
516 NULL)) {
517 video_options_.suspend_below_min_bitrate.Set(value);
wu@webrtc.orgde305012013-10-31 15:40:38 +0000518 }
519
henrike@webrtc.orgdce3feb2014-03-26 01:17:30 +0000520 if (FindConstraint(
521 constraints,
522 MediaConstraintsInterface::kSkipEncodingUnusedStreams,
523 &value,
524 NULL)) {
525 video_options_.skip_encoding_unused_streams.Set(value);
526 }
527
528 std::string string_value;
529 if (constraints &&
530 constraints->GetOptional().FindFirst(
531 MediaConstraintsInterface::kScreencastMinBitrate,
532 &string_value)) {
533 int bitrate;
534 if (talk_base::FromString(string_value, &bitrate)) {
535 video_options_.screencast_min_bitrate.Set(bitrate);
536 }
537 }
538
539 // Find improved wifi bwe constraint.
540 if (FindConstraint(
541 constraints,
542 MediaConstraintsInterface::kImprovedWifiBwe,
543 &value,
544 NULL)) {
545 video_options_.use_improved_wifi_bandwidth_estimator.Set(value);
546 }
547
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000548 const cricket::VideoCodec default_codec(
549 JsepSessionDescription::kDefaultVideoCodecId,
550 JsepSessionDescription::kDefaultVideoCodecName,
551 JsepSessionDescription::kMaxVideoCodecWidth,
552 JsepSessionDescription::kMaxVideoCodecHeight,
553 JsepSessionDescription::kDefaultVideoCodecFramerate,
554 JsepSessionDescription::kDefaultVideoCodecPreference);
555 channel_manager_->SetDefaultVideoEncoderConfig(
556 cricket::VideoEncoderConfig(default_codec));
wu@webrtc.org91053e72013-08-10 07:18:04 +0000557
558 webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
559 signaling_thread(),
560 channel_manager_,
561 mediastream_signaling_,
562 dtls_identity_service,
563 this,
564 id(),
565 data_channel_type_,
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000566 dtls_enabled_));
wu@webrtc.org91053e72013-08-10 07:18:04 +0000567
568 webrtc_session_desc_factory_->SignalIdentityReady.connect(
569 this, &WebRtcSession::OnIdentityReady);
mallinath@webrtc.org7e809c32013-09-30 18:59:08 +0000570
wu@webrtc.org97077a32013-10-25 21:18:33 +0000571 if (options.disable_encryption) {
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000572 webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED);
mallinath@webrtc.org7e809c32013-09-30 18:59:08 +0000573 }
574
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000575 return true;
576}
577
578void WebRtcSession::Terminate() {
579 SetState(STATE_RECEIVEDTERMINATE);
580 RemoveUnusedChannelsAndTransports(NULL);
581 ASSERT(voice_channel_.get() == NULL);
582 ASSERT(video_channel_.get() == NULL);
583 ASSERT(data_channel_.get() == NULL);
584}
585
586bool WebRtcSession::StartCandidatesAllocation() {
587 // SpeculativelyConnectTransportChannels, will call ConnectChannels method
588 // from TransportProxy to start gathering ice candidates.
589 SpeculativelyConnectAllTransportChannels();
590 if (!saved_candidates_.empty()) {
591 // If there are saved candidates which arrived before local description is
592 // set, copy those to remote description.
593 CopySavedCandidates(remote_desc_.get());
594 }
595 // Push remote candidates present in remote description to transport channels.
596 UseCandidatesInSessionDescription(remote_desc_.get());
597 return true;
598}
599
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000600void WebRtcSession::SetSdesPolicy(cricket::SecurePolicy secure_policy) {
601 webrtc_session_desc_factory_->SetSdesPolicy(secure_policy);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000602}
603
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000604cricket::SecurePolicy WebRtcSession::SdesPolicy() const {
605 return webrtc_session_desc_factory_->SdesPolicy();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000606}
607
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000608bool WebRtcSession::GetSslRole(talk_base::SSLRole* role) {
609 if (local_description() == NULL || remote_description() == NULL) {
610 LOG(LS_INFO) << "Local and Remote descriptions must be applied to get "
611 << "SSL Role of the session.";
612 return false;
613 }
614
615 // TODO(mallinath) - Return role of each transport, as role may differ from
616 // one another.
617 // In current implementaion we just return the role of first transport in the
618 // transport map.
619 for (cricket::TransportMap::const_iterator iter = transport_proxies().begin();
620 iter != transport_proxies().end(); ++iter) {
621 if (iter->second->impl()) {
622 return iter->second->impl()->GetSslRole(role);
623 }
624 }
625 return false;
626}
627
wu@webrtc.org91053e72013-08-10 07:18:04 +0000628void WebRtcSession::CreateOffer(CreateSessionDescriptionObserver* observer,
629 const MediaConstraintsInterface* constraints) {
630 webrtc_session_desc_factory_->CreateOffer(observer, constraints);
631}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000632
wu@webrtc.org91053e72013-08-10 07:18:04 +0000633void WebRtcSession::CreateAnswer(CreateSessionDescriptionObserver* observer,
634 const MediaConstraintsInterface* constraints) {
635 webrtc_session_desc_factory_->CreateAnswer(observer, constraints);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000636}
637
638bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
639 std::string* err_desc) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000640 // Takes the ownership of |desc| regardless of the result.
641 talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
642
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000643 // Validate SDP.
644 if (!ValidateSessionDescription(desc, cricket::CS_LOCAL, err_desc)) {
645 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000646 }
647
648 // Update the initiator flag if this session is the initiator.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000649 Action action = GetAction(desc->type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000650 if (state() == STATE_INIT && action == kOffer) {
651 set_initiator(true);
652 }
653
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000654 cricket::SecurePolicy sdes_policy =
655 webrtc_session_desc_factory_->SdesPolicy();
656 cricket::CryptoType crypto_required = dtls_enabled_ ?
657 cricket::CT_DTLS : (sdes_policy == cricket::SEC_REQUIRED ?
658 cricket::CT_SDES : cricket::CT_NONE);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000659 // Update the MediaContentDescription crypto settings as per the policy set.
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000660 UpdateSessionDescriptionSecurePolicy(crypto_required, desc->description());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000661
662 set_local_description(desc->description()->Copy());
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000663 local_desc_.reset(desc_temp.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000664
665 // Transport and Media channels will be created only when offer is set.
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000666 if (action == kOffer && !CreateChannels(local_desc_->description())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000667 // TODO(mallinath) - Handle CreateChannel failure, as new local description
668 // is applied. Restore back to old description.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000669 return BadLocalSdp(desc->type(), kCreateChannelFailed, err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000670 }
671
672 // Remove channel and transport proxies, if MediaContentDescription is
673 // rejected.
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000674 RemoveUnusedChannelsAndTransports(local_desc_->description());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000675
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000676 if (!UpdateSessionState(action, cricket::CS_LOCAL, err_desc)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000677 return false;
678 }
679 // Kick starting the ice candidates allocation.
680 StartCandidatesAllocation();
681
682 // Update state and SSRC of local MediaStreams and DataChannels based on the
683 // local session description.
684 mediastream_signaling_->OnLocalDescriptionChanged(local_desc_.get());
685
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000686 talk_base::SSLRole role;
687 if (data_channel_type_ == cricket::DCT_SCTP && GetSslRole(&role)) {
688 mediastream_signaling_->OnDtlsRoleReadyForSctp(role);
689 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000690 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000691 return BadLocalSdp(desc->type(), GetSessionErrorMsg(), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000692 }
693 return true;
694}
695
696bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
697 std::string* err_desc) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000698 // Takes the ownership of |desc| regardless of the result.
699 talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
700
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000701 // Validate SDP.
702 if (!ValidateSessionDescription(desc, cricket::CS_REMOTE, err_desc)) {
703 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000704 }
705
706 // Transport and Media channels will be created only when offer is set.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000707 Action action = GetAction(desc->type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000708 if (action == kOffer && !CreateChannels(desc->description())) {
709 // TODO(mallinath) - Handle CreateChannel failure, as new local description
710 // is applied. Restore back to old description.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000711 return BadRemoteSdp(desc->type(), kCreateChannelFailed, err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000712 }
713
714 // Remove channel and transport proxies, if MediaContentDescription is
715 // rejected.
716 RemoveUnusedChannelsAndTransports(desc->description());
717
718 // NOTE: Candidates allocation will be initiated only when SetLocalDescription
719 // is called.
720 set_remote_description(desc->description()->Copy());
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000721 if (!UpdateSessionState(action, cricket::CS_REMOTE, err_desc)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000722 return false;
723 }
724
725 // Update remote MediaStreams.
726 mediastream_signaling_->OnRemoteDescriptionChanged(desc);
727 if (local_description() && !UseCandidatesInSessionDescription(desc)) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000728 return BadRemoteSdp(desc->type(), kInvalidCandidates, err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000729 }
730
731 // Copy all saved candidates.
732 CopySavedCandidates(desc);
733 // We retain all received candidates.
wu@webrtc.org91053e72013-08-10 07:18:04 +0000734 WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
735 remote_desc_.get(), desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000736 // Check if this new SessionDescription contains new ice ufrag and password
737 // that indicates the remote peer requests ice restart.
738 ice_restart_latch_->CheckForRemoteIceRestart(remote_desc_.get(),
739 desc);
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000740 remote_desc_.reset(desc_temp.release());
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000741
742 talk_base::SSLRole role;
743 if (data_channel_type_ == cricket::DCT_SCTP && GetSslRole(&role)) {
744 mediastream_signaling_->OnDtlsRoleReadyForSctp(role);
745 }
746
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000747 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000748 return BadRemoteSdp(desc->type(), GetSessionErrorMsg(), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000749 }
750 return true;
751}
752
753bool WebRtcSession::UpdateSessionState(
754 Action action, cricket::ContentSource source,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000755 std::string* err_desc) {
756 // If there's already a pending error then no state transition should happen.
757 // But all call-sites should be verifying this before calling us!
758 ASSERT(error() == cricket::BaseSession::ERROR_NONE);
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000759 std::string td_err;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000760 if (action == kOffer) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000761 if (!PushdownTransportDescription(source, cricket::CA_OFFER, &td_err)) {
762 return BadOfferSdp(source, MakeTdErrorString(td_err), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000763 }
764 SetState(source == cricket::CS_LOCAL ?
765 STATE_SENTINITIATE : STATE_RECEIVEDINITIATE);
766 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000767 return BadOfferSdp(source, GetSessionErrorMsg(), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000768 }
769 } else if (action == kPrAnswer) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000770 if (!PushdownTransportDescription(source, cricket::CA_PRANSWER, &td_err)) {
771 return BadPranswerSdp(source, MakeTdErrorString(td_err), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000772 }
773 EnableChannels();
774 SetState(source == cricket::CS_LOCAL ?
775 STATE_SENTPRACCEPT : STATE_RECEIVEDPRACCEPT);
776 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000777 return BadPranswerSdp(source, GetSessionErrorMsg(), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000778 }
779 } else if (action == kAnswer) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000780 if (!PushdownTransportDescription(source, cricket::CA_ANSWER, &td_err)) {
781 return BadAnswerSdp(source, MakeTdErrorString(td_err), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000782 }
783 MaybeEnableMuxingSupport();
784 EnableChannels();
785 SetState(source == cricket::CS_LOCAL ?
786 STATE_SENTACCEPT : STATE_RECEIVEDACCEPT);
787 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000788 return BadAnswerSdp(source, GetSessionErrorMsg(), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000789 }
790 }
791 return true;
792}
793
794WebRtcSession::Action WebRtcSession::GetAction(const std::string& type) {
795 if (type == SessionDescriptionInterface::kOffer) {
796 return WebRtcSession::kOffer;
797 } else if (type == SessionDescriptionInterface::kPrAnswer) {
798 return WebRtcSession::kPrAnswer;
799 } else if (type == SessionDescriptionInterface::kAnswer) {
800 return WebRtcSession::kAnswer;
801 }
802 ASSERT(false && "unknown action type");
803 return WebRtcSession::kOffer;
804}
805
806bool WebRtcSession::ProcessIceMessage(const IceCandidateInterface* candidate) {
807 if (state() == STATE_INIT) {
808 LOG(LS_ERROR) << "ProcessIceMessage: ICE candidates can't be added "
809 << "without any offer (local or remote) "
810 << "session description.";
811 return false;
812 }
813
814 if (!candidate) {
815 LOG(LS_ERROR) << "ProcessIceMessage: Candidate is NULL";
816 return false;
817 }
818
819 if (!local_description() || !remote_description()) {
820 LOG(LS_INFO) << "ProcessIceMessage: Remote description not set, "
821 << "save the candidate for later use.";
822 saved_candidates_.push_back(
823 new JsepIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(),
824 candidate->candidate()));
825 return true;
826 }
827
828 // Add this candidate to the remote session description.
829 if (!remote_desc_->AddCandidate(candidate)) {
830 LOG(LS_ERROR) << "ProcessIceMessage: Candidate cannot be used";
831 return false;
832 }
833
mallinath@webrtc.org67ee6b92014-02-03 16:57:16 +0000834 return UseCandidate(candidate);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000835}
836
837bool WebRtcSession::GetTrackIdBySsrc(uint32 ssrc, std::string* id) {
838 if (GetLocalTrackId(ssrc, id)) {
839 if (GetRemoteTrackId(ssrc, id)) {
840 LOG(LS_WARNING) << "SSRC " << ssrc
841 << " exists in both local and remote descriptions";
842 return true; // We return the remote track id.
843 }
844 return true;
845 } else {
846 return GetRemoteTrackId(ssrc, id);
847 }
848}
849
850bool WebRtcSession::GetLocalTrackId(uint32 ssrc, std::string* track_id) {
851 if (!BaseSession::local_description())
852 return false;
853 return webrtc::GetTrackIdBySsrc(
854 BaseSession::local_description(), ssrc, track_id);
855}
856
857bool WebRtcSession::GetRemoteTrackId(uint32 ssrc, std::string* track_id) {
858 if (!BaseSession::remote_description())
859 return false;
860 return webrtc::GetTrackIdBySsrc(
861 BaseSession::remote_description(), ssrc, track_id);
862}
863
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000864std::string WebRtcSession::BadStateErrMsg(State state) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000865 std::ostringstream desc;
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000866 desc << "Called in wrong state: " << GetStateString(state);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000867 return desc.str();
868}
869
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000870void WebRtcSession::SetAudioPlayout(uint32 ssrc, bool enable,
871 cricket::AudioRenderer* renderer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000872 ASSERT(signaling_thread()->IsCurrent());
873 if (!voice_channel_) {
874 LOG(LS_ERROR) << "SetAudioPlayout: No audio channel exists.";
875 return;
876 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000877 if (!voice_channel_->SetRemoteRenderer(ssrc, renderer)) {
878 // SetRenderer() can fail if the ssrc does not match any playout channel.
879 LOG(LS_ERROR) << "SetAudioPlayout: ssrc is incorrect: " << ssrc;
880 return;
881 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000882 if (!voice_channel_->SetOutputScaling(ssrc, enable ? 1 : 0, enable ? 1 : 0)) {
883 // Allow that SetOutputScaling fail if |enable| is false but assert
884 // otherwise. This in the normal case when the underlying media channel has
885 // already been deleted.
886 ASSERT(enable == false);
887 }
888}
889
890void WebRtcSession::SetAudioSend(uint32 ssrc, bool enable,
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000891 const cricket::AudioOptions& options,
892 cricket::AudioRenderer* renderer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000893 ASSERT(signaling_thread()->IsCurrent());
894 if (!voice_channel_) {
895 LOG(LS_ERROR) << "SetAudioSend: No audio channel exists.";
896 return;
897 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000898 if (!voice_channel_->SetLocalRenderer(ssrc, renderer)) {
899 // SetRenderer() can fail if the ssrc does not match any send channel.
900 LOG(LS_ERROR) << "SetAudioSend: ssrc is incorrect: " << ssrc;
901 return;
902 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000903 if (!voice_channel_->MuteStream(ssrc, !enable)) {
904 // Allow that MuteStream fail if |enable| is false but assert otherwise.
905 // This in the normal case when the underlying media channel has already
906 // been deleted.
907 ASSERT(enable == false);
908 return;
909 }
910 if (enable)
911 voice_channel_->SetChannelOptions(options);
912}
913
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000914void WebRtcSession::SetAudioPlayoutVolume(uint32 ssrc, double volume) {
915 ASSERT(signaling_thread()->IsCurrent());
916 ASSERT(volume >= 0 && volume <= 10);
917 if (!voice_channel_) {
918 LOG(LS_ERROR) << "SetAudioPlayoutVolume: No audio channel exists.";
919 return;
920 }
921
922 if (!voice_channel_->SetOutputScaling(ssrc, volume, volume))
923 ASSERT(false);
924}
925
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000926bool WebRtcSession::SetCaptureDevice(uint32 ssrc,
927 cricket::VideoCapturer* camera) {
928 ASSERT(signaling_thread()->IsCurrent());
929
930 if (!video_channel_.get()) {
931 // |video_channel_| doesnt't exist. Probably because the remote end doesnt't
932 // support video.
933 LOG(LS_WARNING) << "Video not used in this call.";
934 return false;
935 }
936 if (!video_channel_->SetCapturer(ssrc, camera)) {
937 // Allow that SetCapturer fail if |camera| is NULL but assert otherwise.
938 // This in the normal case when the underlying media channel has already
939 // been deleted.
940 ASSERT(camera == NULL);
941 return false;
942 }
943 return true;
944}
945
946void WebRtcSession::SetVideoPlayout(uint32 ssrc,
947 bool enable,
948 cricket::VideoRenderer* renderer) {
949 ASSERT(signaling_thread()->IsCurrent());
950 if (!video_channel_) {
951 LOG(LS_WARNING) << "SetVideoPlayout: No video channel exists.";
952 return;
953 }
954 if (!video_channel_->SetRenderer(ssrc, enable ? renderer : NULL)) {
955 // Allow that SetRenderer fail if |renderer| is NULL but assert otherwise.
956 // This in the normal case when the underlying media channel has already
957 // been deleted.
958 ASSERT(renderer == NULL);
959 }
960}
961
962void WebRtcSession::SetVideoSend(uint32 ssrc, bool enable,
963 const cricket::VideoOptions* options) {
964 ASSERT(signaling_thread()->IsCurrent());
965 if (!video_channel_) {
966 LOG(LS_WARNING) << "SetVideoSend: No video channel exists.";
967 return;
968 }
969 if (!video_channel_->MuteStream(ssrc, !enable)) {
970 // Allow that MuteStream fail if |enable| is false but assert otherwise.
971 // This in the normal case when the underlying media channel has already
972 // been deleted.
973 ASSERT(enable == false);
974 return;
975 }
976 if (enable && options)
977 video_channel_->SetChannelOptions(*options);
978}
979
980bool WebRtcSession::CanInsertDtmf(const std::string& track_id) {
981 ASSERT(signaling_thread()->IsCurrent());
982 if (!voice_channel_) {
983 LOG(LS_ERROR) << "CanInsertDtmf: No audio channel exists.";
984 return false;
985 }
986 uint32 send_ssrc = 0;
987 // The Dtmf is negotiated per channel not ssrc, so we only check if the ssrc
988 // exists.
989 if (!GetAudioSsrcByTrackId(BaseSession::local_description(), track_id,
990 &send_ssrc)) {
991 LOG(LS_ERROR) << "CanInsertDtmf: Track does not exist: " << track_id;
992 return false;
993 }
994 return voice_channel_->CanInsertDtmf();
995}
996
997bool WebRtcSession::InsertDtmf(const std::string& track_id,
998 int code, int duration) {
999 ASSERT(signaling_thread()->IsCurrent());
1000 if (!voice_channel_) {
1001 LOG(LS_ERROR) << "InsertDtmf: No audio channel exists.";
1002 return false;
1003 }
1004 uint32 send_ssrc = 0;
1005 if (!VERIFY(GetAudioSsrcByTrackId(BaseSession::local_description(),
1006 track_id, &send_ssrc))) {
1007 LOG(LS_ERROR) << "InsertDtmf: Track does not exist: " << track_id;
1008 return false;
1009 }
1010 if (!voice_channel_->InsertDtmf(send_ssrc, code, duration,
1011 cricket::DF_SEND)) {
1012 LOG(LS_ERROR) << "Failed to insert DTMF to channel.";
1013 return false;
1014 }
1015 return true;
1016}
1017
1018sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() {
1019 return &SignalVoiceChannelDestroyed;
1020}
1021
wu@webrtc.org78187522013-10-07 23:32:02 +00001022bool WebRtcSession::SendData(const cricket::SendDataParams& params,
1023 const talk_base::Buffer& payload,
1024 cricket::SendDataResult* result) {
1025 if (!data_channel_.get()) {
1026 LOG(LS_ERROR) << "SendData called when data_channel_ is NULL.";
1027 return false;
1028 }
1029 return data_channel_->SendData(params, payload, result);
1030}
1031
1032bool WebRtcSession::ConnectDataChannel(DataChannel* webrtc_data_channel) {
1033 if (!data_channel_.get()) {
1034 LOG(LS_ERROR) << "ConnectDataChannel called when data_channel_ is NULL.";
1035 return false;
1036 }
wu@webrtc.org78187522013-10-07 23:32:02 +00001037 data_channel_->SignalReadyToSendData.connect(webrtc_data_channel,
1038 &DataChannel::OnChannelReady);
1039 data_channel_->SignalDataReceived.connect(webrtc_data_channel,
1040 &DataChannel::OnDataReceived);
wu@webrtc.org78187522013-10-07 23:32:02 +00001041 return true;
1042}
1043
1044void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001045 if (!data_channel_.get()) {
1046 LOG(LS_ERROR) << "DisconnectDataChannel called when data_channel_ is NULL.";
1047 return;
1048 }
wu@webrtc.org78187522013-10-07 23:32:02 +00001049 data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
1050 data_channel_->SignalDataReceived.disconnect(webrtc_data_channel);
1051}
1052
sergeyu@chromium.orga23f0ca2013-11-13 22:48:52 +00001053void WebRtcSession::AddSctpDataStream(uint32 sid) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001054 if (!data_channel_.get()) {
1055 LOG(LS_ERROR) << "AddDataChannelStreams called when data_channel_ is NULL.";
1056 return;
1057 }
sergeyu@chromium.orga23f0ca2013-11-13 22:48:52 +00001058 data_channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(sid));
1059 data_channel_->AddSendStream(cricket::StreamParams::CreateLegacy(sid));
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001060}
1061
sergeyu@chromium.orga23f0ca2013-11-13 22:48:52 +00001062void WebRtcSession::RemoveSctpDataStream(uint32 sid) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001063 if (!data_channel_.get()) {
1064 LOG(LS_ERROR) << "RemoveDataChannelStreams called when data_channel_ is "
1065 << "NULL.";
1066 return;
1067 }
sergeyu@chromium.orga23f0ca2013-11-13 22:48:52 +00001068 data_channel_->RemoveRecvStream(sid);
1069 data_channel_->RemoveSendStream(sid);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001070}
1071
wu@webrtc.org07a6fbe2013-11-04 18:41:34 +00001072bool WebRtcSession::ReadyToSendData() const {
1073 return data_channel_.get() && data_channel_->ready_to_send_data();
1074}
1075
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001076talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
wu@webrtc.org78187522013-10-07 23:32:02 +00001077 const std::string& label,
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001078 const InternalDataChannelInit* config) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001079 if (state() == STATE_RECEIVEDTERMINATE) {
1080 return NULL;
1081 }
1082 if (data_channel_type_ == cricket::DCT_NONE) {
1083 LOG(LS_ERROR) << "CreateDataChannel: Data is not supported in this call.";
1084 return NULL;
1085 }
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001086 InternalDataChannelInit new_config =
1087 config ? (*config) : InternalDataChannelInit();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001088 if (data_channel_type_ == cricket::DCT_SCTP) {
1089 if (new_config.id < 0) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001090 talk_base::SSLRole role;
1091 if (GetSslRole(&role) &&
1092 !mediastream_signaling_->AllocateSctpSid(role, &new_config.id)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001093 LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel.";
1094 return NULL;
1095 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001096 } else if (!mediastream_signaling_->IsSctpSidAvailable(new_config.id)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001097 LOG(LS_ERROR) << "Failed to create a SCTP data channel "
1098 << "because the id is already in use or out of range.";
1099 return NULL;
1100 }
1101 }
wu@webrtc.org91053e72013-08-10 07:18:04 +00001102
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001103 talk_base::scoped_refptr<DataChannel> channel(DataChannel::Create(
1104 this, data_channel_type_, label, new_config));
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001105 if (channel && !mediastream_signaling_->AddDataChannel(channel))
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001106 return NULL;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001107
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001108 return channel;
1109}
1110
1111cricket::DataChannelType WebRtcSession::data_channel_type() const {
1112 return data_channel_type_;
1113}
1114
wu@webrtc.org91053e72013-08-10 07:18:04 +00001115bool WebRtcSession::IceRestartPending() const {
1116 return ice_restart_latch_->Get();
1117}
1118
1119void WebRtcSession::ResetIceRestartLatch() {
1120 ice_restart_latch_->Reset();
1121}
1122
1123void WebRtcSession::OnIdentityReady(talk_base::SSLIdentity* identity) {
1124 SetIdentity(identity);
1125}
1126
1127bool WebRtcSession::waiting_for_identity() const {
1128 return webrtc_session_desc_factory_->waiting_for_identity();
1129}
1130
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001131void WebRtcSession::SetIceConnectionState(
1132 PeerConnectionInterface::IceConnectionState state) {
1133 if (ice_connection_state_ == state) {
1134 return;
1135 }
1136
1137 // ASSERT that the requested transition is allowed. Note that
1138 // WebRtcSession does not implement "kIceConnectionClosed" (that is handled
1139 // within PeerConnection). This switch statement should compile away when
1140 // ASSERTs are disabled.
1141 switch (ice_connection_state_) {
1142 case PeerConnectionInterface::kIceConnectionNew:
1143 ASSERT(state == PeerConnectionInterface::kIceConnectionChecking);
1144 break;
1145 case PeerConnectionInterface::kIceConnectionChecking:
1146 ASSERT(state == PeerConnectionInterface::kIceConnectionFailed ||
1147 state == PeerConnectionInterface::kIceConnectionConnected);
1148 break;
1149 case PeerConnectionInterface::kIceConnectionConnected:
1150 ASSERT(state == PeerConnectionInterface::kIceConnectionDisconnected ||
1151 state == PeerConnectionInterface::kIceConnectionChecking ||
1152 state == PeerConnectionInterface::kIceConnectionCompleted);
1153 break;
1154 case PeerConnectionInterface::kIceConnectionCompleted:
1155 ASSERT(state == PeerConnectionInterface::kIceConnectionConnected ||
1156 state == PeerConnectionInterface::kIceConnectionDisconnected);
1157 break;
1158 case PeerConnectionInterface::kIceConnectionFailed:
1159 ASSERT(state == PeerConnectionInterface::kIceConnectionNew);
1160 break;
1161 case PeerConnectionInterface::kIceConnectionDisconnected:
1162 ASSERT(state == PeerConnectionInterface::kIceConnectionChecking ||
1163 state == PeerConnectionInterface::kIceConnectionConnected ||
1164 state == PeerConnectionInterface::kIceConnectionCompleted ||
1165 state == PeerConnectionInterface::kIceConnectionFailed);
1166 break;
1167 case PeerConnectionInterface::kIceConnectionClosed:
1168 ASSERT(false);
1169 break;
1170 default:
1171 ASSERT(false);
1172 break;
1173 }
1174
1175 ice_connection_state_ = state;
1176 if (ice_observer_) {
1177 ice_observer_->OnIceConnectionChange(ice_connection_state_);
1178 }
1179}
1180
1181void WebRtcSession::OnTransportRequestSignaling(
1182 cricket::Transport* transport) {
1183 ASSERT(signaling_thread()->IsCurrent());
1184 transport->OnSignalingReady();
1185 if (ice_observer_) {
1186 ice_observer_->OnIceGatheringChange(
1187 PeerConnectionInterface::kIceGatheringGathering);
1188 }
1189}
1190
1191void WebRtcSession::OnTransportConnecting(cricket::Transport* transport) {
1192 ASSERT(signaling_thread()->IsCurrent());
1193 // start monitoring for the write state of the transport.
1194 OnTransportWritable(transport);
1195}
1196
1197void WebRtcSession::OnTransportWritable(cricket::Transport* transport) {
1198 ASSERT(signaling_thread()->IsCurrent());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001199 if (transport->all_channels_writable()) {
henrike@webrtc.org05376342014-03-10 15:53:12 +00001200 SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001201 } else if (transport->HasChannels()) {
1202 // If the current state is Connected or Completed, then there were writable
1203 // channels but now there are not, so the next state must be Disconnected.
1204 if (ice_connection_state_ ==
1205 PeerConnectionInterface::kIceConnectionConnected ||
1206 ice_connection_state_ ==
1207 PeerConnectionInterface::kIceConnectionCompleted) {
1208 SetIceConnectionState(
1209 PeerConnectionInterface::kIceConnectionDisconnected);
1210 }
1211 }
1212}
1213
mallinath@webrtc.org385857d2014-02-14 00:56:12 +00001214void WebRtcSession::OnTransportCompleted(cricket::Transport* transport) {
1215 ASSERT(signaling_thread()->IsCurrent());
1216 SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted);
1217}
1218
1219void WebRtcSession::OnTransportFailed(cricket::Transport* transport) {
1220 ASSERT(signaling_thread()->IsCurrent());
1221 SetIceConnectionState(PeerConnectionInterface::kIceConnectionFailed);
1222}
1223
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001224void WebRtcSession::OnTransportProxyCandidatesReady(
1225 cricket::TransportProxy* proxy, const cricket::Candidates& candidates) {
1226 ASSERT(signaling_thread()->IsCurrent());
1227 ProcessNewLocalCandidate(proxy->content_name(), candidates);
1228}
1229
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001230void WebRtcSession::OnCandidatesAllocationDone() {
1231 ASSERT(signaling_thread()->IsCurrent());
1232 if (ice_observer_) {
1233 ice_observer_->OnIceGatheringChange(
1234 PeerConnectionInterface::kIceGatheringComplete);
1235 ice_observer_->OnIceComplete();
1236 }
1237}
1238
1239// Enabling voice and video channel.
1240void WebRtcSession::EnableChannels() {
1241 if (voice_channel_ && !voice_channel_->enabled())
1242 voice_channel_->Enable(true);
1243
1244 if (video_channel_ && !video_channel_->enabled())
1245 video_channel_->Enable(true);
1246
1247 if (data_channel_.get() && !data_channel_->enabled())
1248 data_channel_->Enable(true);
1249}
1250
1251void WebRtcSession::ProcessNewLocalCandidate(
1252 const std::string& content_name,
1253 const cricket::Candidates& candidates) {
1254 int sdp_mline_index;
1255 if (!GetLocalCandidateMediaIndex(content_name, &sdp_mline_index)) {
1256 LOG(LS_ERROR) << "ProcessNewLocalCandidate: content name "
1257 << content_name << " not found";
1258 return;
1259 }
1260
1261 for (cricket::Candidates::const_iterator citer = candidates.begin();
1262 citer != candidates.end(); ++citer) {
1263 // Use content_name as the candidate media id.
1264 JsepIceCandidate candidate(content_name, sdp_mline_index, *citer);
1265 if (ice_observer_) {
1266 ice_observer_->OnIceCandidate(&candidate);
1267 }
1268 if (local_desc_) {
1269 local_desc_->AddCandidate(&candidate);
1270 }
1271 }
1272}
1273
1274// Returns the media index for a local ice candidate given the content name.
1275bool WebRtcSession::GetLocalCandidateMediaIndex(const std::string& content_name,
1276 int* sdp_mline_index) {
1277 if (!BaseSession::local_description() || !sdp_mline_index)
1278 return false;
1279
1280 bool content_found = false;
1281 const ContentInfos& contents = BaseSession::local_description()->contents();
1282 for (size_t index = 0; index < contents.size(); ++index) {
1283 if (contents[index].name == content_name) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001284 *sdp_mline_index = static_cast<int>(index);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001285 content_found = true;
1286 break;
1287 }
1288 }
1289 return content_found;
1290}
1291
1292bool WebRtcSession::UseCandidatesInSessionDescription(
1293 const SessionDescriptionInterface* remote_desc) {
1294 if (!remote_desc)
1295 return true;
1296 bool ret = true;
1297 for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) {
1298 const IceCandidateCollection* candidates = remote_desc->candidates(m);
1299 for (size_t n = 0; n < candidates->count(); ++n) {
1300 ret = UseCandidate(candidates->at(n));
1301 if (!ret)
1302 break;
1303 }
1304 }
1305 return ret;
1306}
1307
1308bool WebRtcSession::UseCandidate(
1309 const IceCandidateInterface* candidate) {
1310
1311 size_t mediacontent_index = static_cast<size_t>(candidate->sdp_mline_index());
1312 size_t remote_content_size =
1313 BaseSession::remote_description()->contents().size();
1314 if (mediacontent_index >= remote_content_size) {
1315 LOG(LS_ERROR)
1316 << "UseRemoteCandidateInSession: Invalid candidate media index.";
1317 return false;
1318 }
1319
1320 cricket::ContentInfo content =
1321 BaseSession::remote_description()->contents()[mediacontent_index];
1322 std::vector<cricket::Candidate> candidates;
1323 candidates.push_back(candidate->candidate());
1324 // Invoking BaseSession method to handle remote candidates.
1325 std::string error;
1326 if (OnRemoteCandidates(content.name, candidates, &error)) {
1327 // Candidates successfully submitted for checking.
1328 if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew ||
1329 ice_connection_state_ ==
1330 PeerConnectionInterface::kIceConnectionDisconnected) {
1331 // If state is New, then the session has just gotten its first remote ICE
1332 // candidates, so go to Checking.
1333 // If state is Disconnected, the session is re-using old candidates or
1334 // receiving additional ones, so go to Checking.
1335 // If state is Connected, stay Connected.
1336 // TODO(bemasc): If state is Connected, and the new candidates are for a
1337 // newly added transport, then the state actually _should_ move to
1338 // checking. Add a way to distinguish that case.
1339 SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking);
1340 }
1341 // TODO(bemasc): If state is Completed, go back to Connected.
1342 } else {
1343 LOG(LS_WARNING) << error;
1344 }
1345 return true;
1346}
1347
1348void WebRtcSession::RemoveUnusedChannelsAndTransports(
1349 const SessionDescription* desc) {
1350 const cricket::ContentInfo* voice_info =
1351 cricket::GetFirstAudioContent(desc);
1352 if ((!voice_info || voice_info->rejected) && voice_channel_) {
1353 mediastream_signaling_->OnAudioChannelClose();
1354 SignalVoiceChannelDestroyed();
1355 const std::string content_name = voice_channel_->content_name();
1356 channel_manager_->DestroyVoiceChannel(voice_channel_.release());
1357 DestroyTransportProxy(content_name);
1358 }
1359
1360 const cricket::ContentInfo* video_info =
1361 cricket::GetFirstVideoContent(desc);
1362 if ((!video_info || video_info->rejected) && video_channel_) {
1363 mediastream_signaling_->OnVideoChannelClose();
1364 SignalVideoChannelDestroyed();
1365 const std::string content_name = video_channel_->content_name();
1366 channel_manager_->DestroyVideoChannel(video_channel_.release());
1367 DestroyTransportProxy(content_name);
1368 }
1369
1370 const cricket::ContentInfo* data_info =
1371 cricket::GetFirstDataContent(desc);
1372 if ((!data_info || data_info->rejected) && data_channel_) {
1373 mediastream_signaling_->OnDataChannelClose();
1374 SignalDataChannelDestroyed();
1375 const std::string content_name = data_channel_->content_name();
1376 channel_manager_->DestroyDataChannel(data_channel_.release());
1377 DestroyTransportProxy(content_name);
1378 }
1379}
1380
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001381// TODO(mallinath) - Add a correct error code if the channels are not creatued
1382// due to BUNDLE is enabled but rtcp-mux is disabled.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001383bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
1384 // Disabling the BUNDLE flag in PortAllocator if offer disabled it.
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001385 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1386 if (state() == STATE_INIT && !bundle_enabled) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001387 port_allocator()->set_flags(port_allocator()->flags() &
1388 ~cricket::PORTALLOCATOR_ENABLE_BUNDLE);
1389 }
1390
1391 // Creating the media channels and transport proxies.
1392 const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(desc);
1393 if (voice && !voice->rejected && !voice_channel_) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001394 if (!CreateVoiceChannel(voice)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001395 LOG(LS_ERROR) << "Failed to create voice channel.";
1396 return false;
1397 }
1398 }
1399
1400 const cricket::ContentInfo* video = cricket::GetFirstVideoContent(desc);
1401 if (video && !video->rejected && !video_channel_) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001402 if (!CreateVideoChannel(video)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001403 LOG(LS_ERROR) << "Failed to create video channel.";
1404 return false;
1405 }
1406 }
1407
1408 const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc);
1409 if (data_channel_type_ != cricket::DCT_NONE &&
1410 data && !data->rejected && !data_channel_.get()) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001411 if (!CreateDataChannel(data)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001412 LOG(LS_ERROR) << "Failed to create data channel.";
1413 return false;
1414 }
1415 }
1416
1417 return true;
1418}
1419
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001420bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001421 voice_channel_.reset(channel_manager_->CreateVoiceChannel(
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001422 this, content->name, true));
wu@webrtc.orgde305012013-10-31 15:40:38 +00001423 if (!voice_channel_.get())
1424 return false;
1425
henrike@webrtc.org6e3dbc22014-03-25 17:09:47 +00001426 voice_channel_->SetChannelOptions(audio_options_);
wu@webrtc.orgde305012013-10-31 15:40:38 +00001427 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001428}
1429
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001430bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001431 video_channel_.reset(channel_manager_->CreateVideoChannel(
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001432 this, content->name, true, voice_channel_.get()));
wu@webrtc.orgde305012013-10-31 15:40:38 +00001433 if (!video_channel_.get())
1434 return false;
1435
henrike@webrtc.org6e3dbc22014-03-25 17:09:47 +00001436 video_channel_->SetChannelOptions(video_options_);
wu@webrtc.orgde305012013-10-31 15:40:38 +00001437 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001438}
1439
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001440bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001441 bool sctp = (data_channel_type_ == cricket::DCT_SCTP);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001442 data_channel_.reset(channel_manager_->CreateDataChannel(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001443 this, content->name, !sctp, data_channel_type_));
wu@webrtc.org91053e72013-08-10 07:18:04 +00001444 if (!data_channel_.get()) {
1445 return false;
1446 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001447 if (sctp) {
1448 mediastream_signaling_->OnDataTransportCreatedForSctp();
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001449 data_channel_->SignalDataReceived.connect(
1450 this, &WebRtcSession::OnDataChannelMessageReceived);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001451 }
wu@webrtc.org91053e72013-08-10 07:18:04 +00001452 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001453}
1454
1455void WebRtcSession::CopySavedCandidates(
1456 SessionDescriptionInterface* dest_desc) {
1457 if (!dest_desc) {
1458 ASSERT(false);
1459 return;
1460 }
1461 for (size_t i = 0; i < saved_candidates_.size(); ++i) {
1462 dest_desc->AddCandidate(saved_candidates_[i]);
1463 delete saved_candidates_[i];
1464 }
1465 saved_candidates_.clear();
1466}
1467
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001468void WebRtcSession::OnDataChannelMessageReceived(
1469 cricket::DataChannel* channel,
1470 const cricket::ReceiveDataParams& params,
1471 const talk_base::Buffer& payload) {
wu@webrtc.org1d1ffc92013-10-16 18:12:02 +00001472 ASSERT(data_channel_type_ == cricket::DCT_SCTP);
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001473 if (params.type == cricket::DMT_CONTROL &&
1474 mediastream_signaling_->IsSctpSidAvailable(params.ssrc)) {
1475 // Received CONTROL on unused sid, process as an OPEN message.
1476 mediastream_signaling_->AddDataChannelFromOpenMessage(params, payload);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001477 }
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001478 // otherwise ignore the message.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001479}
1480
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001481// Returns false if bundle is enabled and rtcp_mux is disabled.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001482bool WebRtcSession::ValidateBundleSettings(const SessionDescription* desc) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001483 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1484 if (!bundle_enabled)
1485 return true;
1486
1487 const cricket::ContentGroup* bundle_group =
1488 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
1489 ASSERT(bundle_group != NULL);
1490
1491 const cricket::ContentInfos& contents = desc->contents();
1492 for (cricket::ContentInfos::const_iterator citer = contents.begin();
1493 citer != contents.end(); ++citer) {
1494 const cricket::ContentInfo* content = (&*citer);
1495 ASSERT(content != NULL);
1496 if (bundle_group->HasContentName(content->name) &&
1497 !content->rejected && content->type == cricket::NS_JINGLE_RTP) {
1498 if (!HasRtcpMuxEnabled(content))
1499 return false;
1500 }
1501 }
1502 // RTCP-MUX is enabled in all the contents.
1503 return true;
1504}
1505
1506bool WebRtcSession::HasRtcpMuxEnabled(
1507 const cricket::ContentInfo* content) {
1508 const cricket::MediaContentDescription* description =
1509 static_cast<cricket::MediaContentDescription*>(content->description);
1510 return description->rtcp_mux();
1511}
1512
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001513bool WebRtcSession::ValidateSessionDescription(
1514 const SessionDescriptionInterface* sdesc,
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001515 cricket::ContentSource source, std::string* err_desc) {
1516 std::string type;
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001517 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001518 return BadSdp(source, type, GetSessionErrorMsg(), err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001519 }
1520
1521 if (!sdesc || !sdesc->description()) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001522 return BadSdp(source, type, kInvalidSdp, err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001523 }
1524
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001525 type = sdesc->type();
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001526 Action action = GetAction(sdesc->type());
1527 if (source == cricket::CS_LOCAL) {
1528 if (!ExpectSetLocalDescription(action))
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001529 return BadLocalSdp(type, BadStateErrMsg(state()), err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001530 } else {
1531 if (!ExpectSetRemoteDescription(action))
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001532 return BadRemoteSdp(type, BadStateErrMsg(state()), err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001533 }
1534
1535 // Verify crypto settings.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +00001536 std::string crypto_error;
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001537 if ((webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED ||
1538 dtls_enabled_) &&
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +00001539 !VerifyCrypto(sdesc->description(), dtls_enabled_, &crypto_error)) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001540 return BadSdp(source, type, crypto_error, err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001541 }
1542
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001543 // Verify ice-ufrag and ice-pwd.
1544 if (!VerifyIceUfragPwdPresent(sdesc->description())) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001545 return BadSdp(source, type, kSdpWithoutIceUfragPwd, err_desc);
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001546 }
1547
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001548 if (!ValidateBundleSettings(sdesc->description())) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001549 return BadSdp(source, type, kBundleWithoutRtcpMux, err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001550 }
1551
1552 // Verify m-lines in Answer when compared against Offer.
1553 if (action == kAnswer) {
1554 const cricket::SessionDescription* offer_desc =
1555 (source == cricket::CS_LOCAL) ? remote_description()->description() :
1556 local_description()->description();
1557 if (!VerifyMediaDescriptions(sdesc->description(), offer_desc)) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001558 return BadAnswerSdp(source, kMlineMismatch, err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001559 }
1560 }
1561
1562 return true;
1563}
1564
1565bool WebRtcSession::ExpectSetLocalDescription(Action action) {
1566 return ((action == kOffer && state() == STATE_INIT) ||
1567 // update local offer
1568 (action == kOffer && state() == STATE_SENTINITIATE) ||
1569 // update the current ongoing session.
1570 (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
1571 (action == kOffer && state() == STATE_SENTACCEPT) ||
1572 (action == kOffer && state() == STATE_INPROGRESS) ||
1573 // accept remote offer
1574 (action == kAnswer && state() == STATE_RECEIVEDINITIATE) ||
1575 (action == kAnswer && state() == STATE_SENTPRACCEPT) ||
1576 (action == kPrAnswer && state() == STATE_RECEIVEDINITIATE) ||
1577 (action == kPrAnswer && state() == STATE_SENTPRACCEPT));
1578}
1579
1580bool WebRtcSession::ExpectSetRemoteDescription(Action action) {
1581 return ((action == kOffer && state() == STATE_INIT) ||
1582 // update remote offer
1583 (action == kOffer && state() == STATE_RECEIVEDINITIATE) ||
1584 // update the current ongoing session
1585 (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
1586 (action == kOffer && state() == STATE_SENTACCEPT) ||
1587 (action == kOffer && state() == STATE_INPROGRESS) ||
1588 // accept local offer
1589 (action == kAnswer && state() == STATE_SENTINITIATE) ||
1590 (action == kAnswer && state() == STATE_RECEIVEDPRACCEPT) ||
1591 (action == kPrAnswer && state() == STATE_SENTINITIATE) ||
1592 (action == kPrAnswer && state() == STATE_RECEIVEDPRACCEPT));
1593}
1594
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001595std::string WebRtcSession::GetSessionErrorMsg() {
1596 std::ostringstream desc;
1597 desc << kSessionError << GetErrorCodeString(error()) << ". ";
1598 desc << kSessionErrorDesc << error_desc() << ".";
1599 return desc.str();
1600}
1601
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001602} // namespace webrtc