blob: b72291c5e42aebe5d9110f7d6c7ba570010f45e8 [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 +000057// Error messages
henrike@webrtc.org1e09a712013-07-26 19:17:59 +000058const char kBundleWithoutRtcpMux[] = "RTCP-MUX must be enabled when BUNDLE "
59 "is enabled.";
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +000060const char kCreateChannelFailed[] = "Failed to create channels.";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000061const char kInvalidCandidates[] = "Description contains invalid candidates.";
62const char kInvalidSdp[] = "Invalid session description.";
63const char kMlineMismatch[] =
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +000064 "Offer and answer descriptions m-lines are not matching. Rejecting answer.";
65const char kPushDownTDFailed[] =
66 "Failed to push down transport description:";
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +000067const char kSdpWithoutDtlsFingerprint[] =
68 "Called with SDP without DTLS fingerprint.";
69const char kSdpWithoutSdesCrypto[] =
70 "Called with SDP without SDES crypto.";
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +000071const char kSdpWithoutIceUfragPwd[] =
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +000072 "Called with SDP without ice-ufrag and ice-pwd.";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000073const char kSessionError[] = "Session error code: ";
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +000074const char kSessionErrorDesc[] = "Session error description: ";
henrike@webrtc.org28e20752013-07-10 00:45:36 +000075
76// Compares |answer| against |offer|. Comparision is done
77// for number of m-lines in answer against offer. If matches true will be
78// returned otherwise false.
79static bool VerifyMediaDescriptions(
80 const SessionDescription* answer, const SessionDescription* offer) {
81 if (offer->contents().size() != answer->contents().size())
82 return false;
83
84 for (size_t i = 0; i < offer->contents().size(); ++i) {
85 if ((offer->contents()[i].name) != answer->contents()[i].name) {
86 return false;
87 }
88 }
89 return true;
90}
91
henrike@webrtc.org28e20752013-07-10 00:45:36 +000092// Checks that each non-rejected content has SDES crypto keys or a DTLS
93// fingerprint. Mismatches, such as replying with a DTLS fingerprint to SDES
94// keys, will be caught in Transport negotiation, and backstopped by Channel's
95// |secure_required| check.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000096static bool VerifyCrypto(const SessionDescription* desc,
97 bool dtls_enabled,
98 std::string* error) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000099 const ContentInfos& contents = desc->contents();
100 for (size_t index = 0; index < contents.size(); ++index) {
101 const ContentInfo* cinfo = &contents[index];
102 if (cinfo->rejected) {
103 continue;
104 }
105
106 // If the content isn't rejected, crypto must be present.
107 const MediaContentDescription* media =
108 static_cast<const MediaContentDescription*>(cinfo->description);
109 const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name);
110 if (!media || !tinfo) {
111 // Something is not right.
112 LOG(LS_ERROR) << kInvalidSdp;
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000113 *error = kInvalidSdp;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000114 return false;
115 }
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000116 if (dtls_enabled) {
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000117 if (!tinfo->description.identity_fingerprint) {
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000118 LOG(LS_WARNING) <<
119 "Session description must have DTLS fingerprint if DTLS enabled.";
120 *error = kSdpWithoutDtlsFingerprint;
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000121 return false;
122 }
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000123 } else {
124 if (media->cryptos().empty()) {
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000125 LOG(LS_WARNING) <<
126 "Session description must have SDES when DTLS disabled.";
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000127 *error = kSdpWithoutSdesCrypto;
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000128 return false;
129 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000130 }
131 }
132
133 return true;
134}
135
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +0000136// Checks that each non-rejected content has ice-ufrag and ice-pwd set.
137static bool VerifyIceUfragPwdPresent(const SessionDescription* desc) {
138 const ContentInfos& contents = desc->contents();
139 for (size_t index = 0; index < contents.size(); ++index) {
140 const ContentInfo* cinfo = &contents[index];
141 if (cinfo->rejected) {
142 continue;
143 }
144
145 // If the content isn't rejected, ice-ufrag and ice-pwd must be present.
146 const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name);
147 if (!tinfo) {
148 // Something is not right.
149 LOG(LS_ERROR) << kInvalidSdp;
150 return false;
151 }
152 if (tinfo->description.ice_ufrag.empty() ||
153 tinfo->description.ice_pwd.empty()) {
154 LOG(LS_ERROR) << "Session description must have ice ufrag and pwd.";
155 return false;
156 }
157 }
158 return true;
159}
160
wu@webrtc.org91053e72013-08-10 07:18:04 +0000161// Forces |sdesc->crypto_required| to the appropriate state based on the
162// current security policy, to ensure a failure occurs if there is an error
163// in crypto negotiation.
164// Called when processing the local session description.
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000165static void UpdateSessionDescriptionSecurePolicy(cricket::CryptoType type,
166 SessionDescription* sdesc) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000167 if (!sdesc) {
168 return;
169 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000170
wu@webrtc.org91053e72013-08-10 07:18:04 +0000171 // Updating the |crypto_required_| in MediaContentDescription to the
172 // appropriate state based on the current security policy.
173 for (cricket::ContentInfos::iterator iter = sdesc->contents().begin();
174 iter != sdesc->contents().end(); ++iter) {
175 if (cricket::IsMediaContent(&*iter)) {
176 MediaContentDescription* mdesc =
177 static_cast<MediaContentDescription*> (iter->description);
178 if (mdesc) {
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000179 mdesc->set_crypto_required(type);
wu@webrtc.org91053e72013-08-10 07:18:04 +0000180 }
181 }
182 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000183}
184
185static bool GetAudioSsrcByTrackId(
186 const SessionDescription* session_description,
187 const std::string& track_id, uint32 *ssrc) {
188 const cricket::ContentInfo* audio_info =
189 cricket::GetFirstAudioContent(session_description);
190 if (!audio_info) {
191 LOG(LS_ERROR) << "Audio not used in this call";
192 return false;
193 }
194
195 const cricket::MediaContentDescription* audio_content =
196 static_cast<const cricket::MediaContentDescription*>(
197 audio_info->description);
198 cricket::StreamParams stream;
199 if (!cricket::GetStreamByIds(audio_content->streams(), "", track_id,
200 &stream)) {
201 return false;
202 }
203 *ssrc = stream.first_ssrc();
204 return true;
205}
206
207static bool GetTrackIdBySsrc(const SessionDescription* session_description,
208 uint32 ssrc, std::string* track_id) {
209 ASSERT(track_id != NULL);
210
211 cricket::StreamParams stream_out;
212 const cricket::ContentInfo* audio_info =
213 cricket::GetFirstAudioContent(session_description);
214 if (!audio_info) {
215 return false;
216 }
217 const cricket::MediaContentDescription* audio_content =
218 static_cast<const cricket::MediaContentDescription*>(
219 audio_info->description);
220
221 if (cricket::GetStreamBySsrc(audio_content->streams(), ssrc, &stream_out)) {
222 *track_id = stream_out.id;
223 return true;
224 }
225
226 const cricket::ContentInfo* video_info =
227 cricket::GetFirstVideoContent(session_description);
228 if (!video_info) {
229 return false;
230 }
231 const cricket::MediaContentDescription* video_content =
232 static_cast<const cricket::MediaContentDescription*>(
233 video_info->description);
234
235 if (cricket::GetStreamBySsrc(video_content->streams(), ssrc, &stream_out)) {
236 *track_id = stream_out.id;
237 return true;
238 }
239 return false;
240}
241
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000242static bool BadSdp(const std::string& source,
243 const std::string& type,
244 const std::string& reason,
245 std::string* err_desc) {
246 std::ostringstream desc;
247 desc << "Failed to set " << source << " " << type << " sdp: " << reason;
248
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000249 if (err_desc) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000250 *err_desc = desc.str();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000251 }
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000252 LOG(LS_ERROR) << desc.str();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000253 return false;
254}
255
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000256static bool BadSdp(cricket::ContentSource source,
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000257 const std::string& type,
258 const std::string& reason,
259 std::string* err_desc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000260 if (source == cricket::CS_LOCAL) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000261 return BadSdp("local", type, reason, err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000262 } else {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000263 return BadSdp("remote", type, reason, err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000264 }
265}
266
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000267static bool BadLocalSdp(const std::string& type,
268 const std::string& reason,
269 std::string* err_desc) {
270 return BadSdp(cricket::CS_LOCAL, type, reason, err_desc);
271}
272
273static bool BadRemoteSdp(const std::string& type,
274 const std::string& reason,
275 std::string* err_desc) {
276 return BadSdp(cricket::CS_REMOTE, type, reason, err_desc);
277}
278
279static bool BadOfferSdp(cricket::ContentSource source,
280 const std::string& reason,
281 std::string* err_desc) {
282 return BadSdp(source, SessionDescriptionInterface::kOffer, reason, err_desc);
283}
284
285static bool BadPranswerSdp(cricket::ContentSource source,
286 const std::string& reason,
287 std::string* err_desc) {
288 return BadSdp(source, SessionDescriptionInterface::kPrAnswer,
289 reason, err_desc);
290}
291
292static bool BadAnswerSdp(cricket::ContentSource source,
293 const std::string& reason,
294 std::string* err_desc) {
295 return BadSdp(source, SessionDescriptionInterface::kAnswer, reason, err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000296}
297
298#define GET_STRING_OF_STATE(state) \
299 case cricket::BaseSession::state: \
300 result = #state; \
301 break;
302
303static std::string GetStateString(cricket::BaseSession::State state) {
304 std::string result;
305 switch (state) {
306 GET_STRING_OF_STATE(STATE_INIT)
307 GET_STRING_OF_STATE(STATE_SENTINITIATE)
308 GET_STRING_OF_STATE(STATE_RECEIVEDINITIATE)
309 GET_STRING_OF_STATE(STATE_SENTPRACCEPT)
310 GET_STRING_OF_STATE(STATE_SENTACCEPT)
311 GET_STRING_OF_STATE(STATE_RECEIVEDPRACCEPT)
312 GET_STRING_OF_STATE(STATE_RECEIVEDACCEPT)
313 GET_STRING_OF_STATE(STATE_SENTMODIFY)
314 GET_STRING_OF_STATE(STATE_RECEIVEDMODIFY)
315 GET_STRING_OF_STATE(STATE_SENTREJECT)
316 GET_STRING_OF_STATE(STATE_RECEIVEDREJECT)
317 GET_STRING_OF_STATE(STATE_SENTREDIRECT)
318 GET_STRING_OF_STATE(STATE_SENTTERMINATE)
319 GET_STRING_OF_STATE(STATE_RECEIVEDTERMINATE)
320 GET_STRING_OF_STATE(STATE_INPROGRESS)
321 GET_STRING_OF_STATE(STATE_DEINIT)
322 default:
323 ASSERT(false);
324 break;
325 }
326 return result;
327}
328
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000329#define GET_STRING_OF_ERROR_CODE(err) \
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000330 case cricket::BaseSession::err: \
331 result = #err; \
332 break;
333
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000334static std::string GetErrorCodeString(cricket::BaseSession::Error err) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000335 std::string result;
336 switch (err) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000337 GET_STRING_OF_ERROR_CODE(ERROR_NONE)
338 GET_STRING_OF_ERROR_CODE(ERROR_TIME)
339 GET_STRING_OF_ERROR_CODE(ERROR_RESPONSE)
340 GET_STRING_OF_ERROR_CODE(ERROR_NETWORK)
341 GET_STRING_OF_ERROR_CODE(ERROR_CONTENT)
342 GET_STRING_OF_ERROR_CODE(ERROR_TRANSPORT)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000343 default:
344 ASSERT(false);
345 break;
346 }
347 return result;
348}
349
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000350static std::string MakeErrorString(const std::string& error,
351 const std::string& desc) {
352 std::ostringstream ret;
353 ret << error << " " << desc;
354 return ret.str();
355}
356
357static std::string MakeTdErrorString(const std::string& desc) {
358 return MakeErrorString(kPushDownTDFailed, desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000359}
360
361// Help class used to remember if a a remote peer has requested ice restart by
362// by sending a description with new ice ufrag and password.
363class IceRestartAnswerLatch {
364 public:
365 IceRestartAnswerLatch() : ice_restart_(false) { }
366
wu@webrtc.org91053e72013-08-10 07:18:04 +0000367 // Returns true if CheckForRemoteIceRestart has been called with a new session
368 // description where ice password and ufrag has changed since last time
369 // Reset() was called.
370 bool Get() const {
371 return ice_restart_;
372 }
373
374 void Reset() {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000375 if (ice_restart_) {
376 ice_restart_ = false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000377 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000378 }
379
380 void CheckForRemoteIceRestart(
381 const SessionDescriptionInterface* old_desc,
382 const SessionDescriptionInterface* new_desc) {
383 if (!old_desc || new_desc->type() != SessionDescriptionInterface::kOffer) {
384 return;
385 }
386 const SessionDescription* new_sd = new_desc->description();
387 const SessionDescription* old_sd = old_desc->description();
388 const ContentInfos& contents = new_sd->contents();
389 for (size_t index = 0; index < contents.size(); ++index) {
390 const ContentInfo* cinfo = &contents[index];
391 if (cinfo->rejected) {
392 continue;
393 }
394 // If the content isn't rejected, check if ufrag and password has
395 // changed.
396 const cricket::TransportDescription* new_transport_desc =
397 new_sd->GetTransportDescriptionByName(cinfo->name);
398 const cricket::TransportDescription* old_transport_desc =
399 old_sd->GetTransportDescriptionByName(cinfo->name);
400 if (!new_transport_desc || !old_transport_desc) {
401 // No transport description exist. This is not an ice restart.
402 continue;
403 }
404 if (new_transport_desc->ice_pwd != old_transport_desc->ice_pwd &&
405 new_transport_desc->ice_ufrag != old_transport_desc->ice_ufrag) {
406 LOG(LS_INFO) << "Remote peer request ice restart.";
407 ice_restart_ = true;
408 break;
409 }
410 }
411 }
412
413 private:
414 bool ice_restart_;
415};
416
wu@webrtc.org91053e72013-08-10 07:18:04 +0000417WebRtcSession::WebRtcSession(
418 cricket::ChannelManager* channel_manager,
419 talk_base::Thread* signaling_thread,
420 talk_base::Thread* worker_thread,
421 cricket::PortAllocator* port_allocator,
422 MediaStreamSignaling* mediastream_signaling)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000423 : cricket::BaseSession(signaling_thread, worker_thread, port_allocator,
424 talk_base::ToString(talk_base::CreateRandomId64() &
425 LLONG_MAX),
426 cricket::NS_JINGLE_RTP, false),
427 // RFC 3264: The numeric value of the session id and version in the
428 // o line MUST be representable with a "64 bit signed integer".
429 // Due to this constraint session id |sid_| is max limited to LLONG_MAX.
430 channel_manager_(channel_manager),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000431 mediastream_signaling_(mediastream_signaling),
432 ice_observer_(NULL),
433 ice_connection_state_(PeerConnectionInterface::kIceConnectionNew),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000434 older_version_remote_peer_(false),
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000435 dtls_enabled_(false),
wu@webrtc.orgde305012013-10-31 15:40:38 +0000436 dscp_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)) {
507 dscp_enabled_ = value;
508 }
509
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000510 const cricket::VideoCodec default_codec(
511 JsepSessionDescription::kDefaultVideoCodecId,
512 JsepSessionDescription::kDefaultVideoCodecName,
513 JsepSessionDescription::kMaxVideoCodecWidth,
514 JsepSessionDescription::kMaxVideoCodecHeight,
515 JsepSessionDescription::kDefaultVideoCodecFramerate,
516 JsepSessionDescription::kDefaultVideoCodecPreference);
517 channel_manager_->SetDefaultVideoEncoderConfig(
518 cricket::VideoEncoderConfig(default_codec));
wu@webrtc.org91053e72013-08-10 07:18:04 +0000519
520 webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
521 signaling_thread(),
522 channel_manager_,
523 mediastream_signaling_,
524 dtls_identity_service,
525 this,
526 id(),
527 data_channel_type_,
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000528 dtls_enabled_));
wu@webrtc.org91053e72013-08-10 07:18:04 +0000529
530 webrtc_session_desc_factory_->SignalIdentityReady.connect(
531 this, &WebRtcSession::OnIdentityReady);
mallinath@webrtc.org7e809c32013-09-30 18:59:08 +0000532
wu@webrtc.org97077a32013-10-25 21:18:33 +0000533 if (options.disable_encryption) {
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000534 webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED);
mallinath@webrtc.org7e809c32013-09-30 18:59:08 +0000535 }
536
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000537 return true;
538}
539
540void WebRtcSession::Terminate() {
541 SetState(STATE_RECEIVEDTERMINATE);
542 RemoveUnusedChannelsAndTransports(NULL);
543 ASSERT(voice_channel_.get() == NULL);
544 ASSERT(video_channel_.get() == NULL);
545 ASSERT(data_channel_.get() == NULL);
546}
547
548bool WebRtcSession::StartCandidatesAllocation() {
549 // SpeculativelyConnectTransportChannels, will call ConnectChannels method
550 // from TransportProxy to start gathering ice candidates.
551 SpeculativelyConnectAllTransportChannels();
552 if (!saved_candidates_.empty()) {
553 // If there are saved candidates which arrived before local description is
554 // set, copy those to remote description.
555 CopySavedCandidates(remote_desc_.get());
556 }
557 // Push remote candidates present in remote description to transport channels.
558 UseCandidatesInSessionDescription(remote_desc_.get());
559 return true;
560}
561
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000562void WebRtcSession::SetSdesPolicy(cricket::SecurePolicy secure_policy) {
563 webrtc_session_desc_factory_->SetSdesPolicy(secure_policy);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000564}
565
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000566cricket::SecurePolicy WebRtcSession::SdesPolicy() const {
567 return webrtc_session_desc_factory_->SdesPolicy();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000568}
569
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000570bool WebRtcSession::GetSslRole(talk_base::SSLRole* role) {
571 if (local_description() == NULL || remote_description() == NULL) {
572 LOG(LS_INFO) << "Local and Remote descriptions must be applied to get "
573 << "SSL Role of the session.";
574 return false;
575 }
576
577 // TODO(mallinath) - Return role of each transport, as role may differ from
578 // one another.
579 // In current implementaion we just return the role of first transport in the
580 // transport map.
581 for (cricket::TransportMap::const_iterator iter = transport_proxies().begin();
582 iter != transport_proxies().end(); ++iter) {
583 if (iter->second->impl()) {
584 return iter->second->impl()->GetSslRole(role);
585 }
586 }
587 return false;
588}
589
wu@webrtc.org91053e72013-08-10 07:18:04 +0000590void WebRtcSession::CreateOffer(CreateSessionDescriptionObserver* observer,
591 const MediaConstraintsInterface* constraints) {
592 webrtc_session_desc_factory_->CreateOffer(observer, constraints);
593}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000594
wu@webrtc.org91053e72013-08-10 07:18:04 +0000595void WebRtcSession::CreateAnswer(CreateSessionDescriptionObserver* observer,
596 const MediaConstraintsInterface* constraints) {
597 webrtc_session_desc_factory_->CreateAnswer(observer, constraints);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000598}
599
600bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
601 std::string* err_desc) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000602 // Takes the ownership of |desc| regardless of the result.
603 talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
604
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000605 // Validate SDP.
606 if (!ValidateSessionDescription(desc, cricket::CS_LOCAL, err_desc)) {
607 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000608 }
609
610 // Update the initiator flag if this session is the initiator.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000611 Action action = GetAction(desc->type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000612 if (state() == STATE_INIT && action == kOffer) {
613 set_initiator(true);
614 }
615
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000616 cricket::SecurePolicy sdes_policy =
617 webrtc_session_desc_factory_->SdesPolicy();
618 cricket::CryptoType crypto_required = dtls_enabled_ ?
619 cricket::CT_DTLS : (sdes_policy == cricket::SEC_REQUIRED ?
620 cricket::CT_SDES : cricket::CT_NONE);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000621 // Update the MediaContentDescription crypto settings as per the policy set.
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000622 UpdateSessionDescriptionSecurePolicy(crypto_required, desc->description());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000623
624 set_local_description(desc->description()->Copy());
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000625 local_desc_.reset(desc_temp.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000626
627 // Transport and Media channels will be created only when offer is set.
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000628 if (action == kOffer && !CreateChannels(local_desc_->description())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000629 // TODO(mallinath) - Handle CreateChannel failure, as new local description
630 // is applied. Restore back to old description.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000631 return BadLocalSdp(desc->type(), kCreateChannelFailed, err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000632 }
633
634 // Remove channel and transport proxies, if MediaContentDescription is
635 // rejected.
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000636 RemoveUnusedChannelsAndTransports(local_desc_->description());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000637
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000638 if (!UpdateSessionState(action, cricket::CS_LOCAL, err_desc)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000639 return false;
640 }
641 // Kick starting the ice candidates allocation.
642 StartCandidatesAllocation();
643
644 // Update state and SSRC of local MediaStreams and DataChannels based on the
645 // local session description.
646 mediastream_signaling_->OnLocalDescriptionChanged(local_desc_.get());
647
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000648 talk_base::SSLRole role;
649 if (data_channel_type_ == cricket::DCT_SCTP && GetSslRole(&role)) {
650 mediastream_signaling_->OnDtlsRoleReadyForSctp(role);
651 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000652 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000653 return BadLocalSdp(desc->type(), GetSessionErrorMsg(), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000654 }
655 return true;
656}
657
658bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
659 std::string* err_desc) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000660 // Takes the ownership of |desc| regardless of the result.
661 talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
662
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000663 // Validate SDP.
664 if (!ValidateSessionDescription(desc, cricket::CS_REMOTE, err_desc)) {
665 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000666 }
667
668 // Transport and Media channels will be created only when offer is set.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000669 Action action = GetAction(desc->type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000670 if (action == kOffer && !CreateChannels(desc->description())) {
671 // TODO(mallinath) - Handle CreateChannel failure, as new local description
672 // is applied. Restore back to old description.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000673 return BadRemoteSdp(desc->type(), kCreateChannelFailed, err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000674 }
675
676 // Remove channel and transport proxies, if MediaContentDescription is
677 // rejected.
678 RemoveUnusedChannelsAndTransports(desc->description());
679
680 // NOTE: Candidates allocation will be initiated only when SetLocalDescription
681 // is called.
682 set_remote_description(desc->description()->Copy());
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000683 if (!UpdateSessionState(action, cricket::CS_REMOTE, err_desc)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000684 return false;
685 }
686
687 // Update remote MediaStreams.
688 mediastream_signaling_->OnRemoteDescriptionChanged(desc);
689 if (local_description() && !UseCandidatesInSessionDescription(desc)) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000690 return BadRemoteSdp(desc->type(), kInvalidCandidates, err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000691 }
692
693 // Copy all saved candidates.
694 CopySavedCandidates(desc);
695 // We retain all received candidates.
wu@webrtc.org91053e72013-08-10 07:18:04 +0000696 WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
697 remote_desc_.get(), desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000698 // Check if this new SessionDescription contains new ice ufrag and password
699 // that indicates the remote peer requests ice restart.
700 ice_restart_latch_->CheckForRemoteIceRestart(remote_desc_.get(),
701 desc);
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000702 remote_desc_.reset(desc_temp.release());
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000703
704 talk_base::SSLRole role;
705 if (data_channel_type_ == cricket::DCT_SCTP && GetSslRole(&role)) {
706 mediastream_signaling_->OnDtlsRoleReadyForSctp(role);
707 }
708
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000709 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000710 return BadRemoteSdp(desc->type(), GetSessionErrorMsg(), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000711 }
712 return true;
713}
714
715bool WebRtcSession::UpdateSessionState(
716 Action action, cricket::ContentSource source,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000717 std::string* err_desc) {
718 // If there's already a pending error then no state transition should happen.
719 // But all call-sites should be verifying this before calling us!
720 ASSERT(error() == cricket::BaseSession::ERROR_NONE);
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000721 std::string td_err;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000722 if (action == kOffer) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000723 if (!PushdownTransportDescription(source, cricket::CA_OFFER, &td_err)) {
724 return BadOfferSdp(source, MakeTdErrorString(td_err), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000725 }
726 SetState(source == cricket::CS_LOCAL ?
727 STATE_SENTINITIATE : STATE_RECEIVEDINITIATE);
728 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000729 return BadOfferSdp(source, GetSessionErrorMsg(), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000730 }
731 } else if (action == kPrAnswer) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000732 if (!PushdownTransportDescription(source, cricket::CA_PRANSWER, &td_err)) {
733 return BadPranswerSdp(source, MakeTdErrorString(td_err), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000734 }
735 EnableChannels();
736 SetState(source == cricket::CS_LOCAL ?
737 STATE_SENTPRACCEPT : STATE_RECEIVEDPRACCEPT);
738 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000739 return BadPranswerSdp(source, GetSessionErrorMsg(), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000740 }
741 } else if (action == kAnswer) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000742 if (!PushdownTransportDescription(source, cricket::CA_ANSWER, &td_err)) {
743 return BadAnswerSdp(source, MakeTdErrorString(td_err), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000744 }
745 MaybeEnableMuxingSupport();
746 EnableChannels();
747 SetState(source == cricket::CS_LOCAL ?
748 STATE_SENTACCEPT : STATE_RECEIVEDACCEPT);
749 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000750 return BadAnswerSdp(source, GetSessionErrorMsg(), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000751 }
752 }
753 return true;
754}
755
756WebRtcSession::Action WebRtcSession::GetAction(const std::string& type) {
757 if (type == SessionDescriptionInterface::kOffer) {
758 return WebRtcSession::kOffer;
759 } else if (type == SessionDescriptionInterface::kPrAnswer) {
760 return WebRtcSession::kPrAnswer;
761 } else if (type == SessionDescriptionInterface::kAnswer) {
762 return WebRtcSession::kAnswer;
763 }
764 ASSERT(false && "unknown action type");
765 return WebRtcSession::kOffer;
766}
767
768bool WebRtcSession::ProcessIceMessage(const IceCandidateInterface* candidate) {
769 if (state() == STATE_INIT) {
770 LOG(LS_ERROR) << "ProcessIceMessage: ICE candidates can't be added "
771 << "without any offer (local or remote) "
772 << "session description.";
773 return false;
774 }
775
776 if (!candidate) {
777 LOG(LS_ERROR) << "ProcessIceMessage: Candidate is NULL";
778 return false;
779 }
780
781 if (!local_description() || !remote_description()) {
782 LOG(LS_INFO) << "ProcessIceMessage: Remote description not set, "
783 << "save the candidate for later use.";
784 saved_candidates_.push_back(
785 new JsepIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(),
786 candidate->candidate()));
787 return true;
788 }
789
790 // Add this candidate to the remote session description.
791 if (!remote_desc_->AddCandidate(candidate)) {
792 LOG(LS_ERROR) << "ProcessIceMessage: Candidate cannot be used";
793 return false;
794 }
795
mallinath@webrtc.org67ee6b92014-02-03 16:57:16 +0000796 return UseCandidate(candidate);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000797}
798
799bool WebRtcSession::GetTrackIdBySsrc(uint32 ssrc, std::string* id) {
800 if (GetLocalTrackId(ssrc, id)) {
801 if (GetRemoteTrackId(ssrc, id)) {
802 LOG(LS_WARNING) << "SSRC " << ssrc
803 << " exists in both local and remote descriptions";
804 return true; // We return the remote track id.
805 }
806 return true;
807 } else {
808 return GetRemoteTrackId(ssrc, id);
809 }
810}
811
812bool WebRtcSession::GetLocalTrackId(uint32 ssrc, std::string* track_id) {
813 if (!BaseSession::local_description())
814 return false;
815 return webrtc::GetTrackIdBySsrc(
816 BaseSession::local_description(), ssrc, track_id);
817}
818
819bool WebRtcSession::GetRemoteTrackId(uint32 ssrc, std::string* track_id) {
820 if (!BaseSession::remote_description())
821 return false;
822 return webrtc::GetTrackIdBySsrc(
823 BaseSession::remote_description(), ssrc, track_id);
824}
825
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000826std::string WebRtcSession::BadStateErrMsg(State state) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000827 std::ostringstream desc;
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000828 desc << "Called in wrong state: " << GetStateString(state);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000829 return desc.str();
830}
831
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000832void WebRtcSession::SetAudioPlayout(uint32 ssrc, bool enable,
833 cricket::AudioRenderer* renderer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000834 ASSERT(signaling_thread()->IsCurrent());
835 if (!voice_channel_) {
836 LOG(LS_ERROR) << "SetAudioPlayout: No audio channel exists.";
837 return;
838 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000839 if (!voice_channel_->SetRemoteRenderer(ssrc, renderer)) {
840 // SetRenderer() can fail if the ssrc does not match any playout channel.
841 LOG(LS_ERROR) << "SetAudioPlayout: ssrc is incorrect: " << ssrc;
842 return;
843 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000844 if (!voice_channel_->SetOutputScaling(ssrc, enable ? 1 : 0, enable ? 1 : 0)) {
845 // Allow that SetOutputScaling fail if |enable| is false but assert
846 // otherwise. This in the normal case when the underlying media channel has
847 // already been deleted.
848 ASSERT(enable == false);
849 }
850}
851
852void WebRtcSession::SetAudioSend(uint32 ssrc, bool enable,
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000853 const cricket::AudioOptions& options,
854 cricket::AudioRenderer* renderer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000855 ASSERT(signaling_thread()->IsCurrent());
856 if (!voice_channel_) {
857 LOG(LS_ERROR) << "SetAudioSend: No audio channel exists.";
858 return;
859 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000860 if (!voice_channel_->SetLocalRenderer(ssrc, renderer)) {
861 // SetRenderer() can fail if the ssrc does not match any send channel.
862 LOG(LS_ERROR) << "SetAudioSend: ssrc is incorrect: " << ssrc;
863 return;
864 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000865 if (!voice_channel_->MuteStream(ssrc, !enable)) {
866 // Allow that MuteStream fail if |enable| is false but assert otherwise.
867 // This in the normal case when the underlying media channel has already
868 // been deleted.
869 ASSERT(enable == false);
870 return;
871 }
872 if (enable)
873 voice_channel_->SetChannelOptions(options);
874}
875
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000876void WebRtcSession::SetAudioPlayoutVolume(uint32 ssrc, double volume) {
877 ASSERT(signaling_thread()->IsCurrent());
878 ASSERT(volume >= 0 && volume <= 10);
879 if (!voice_channel_) {
880 LOG(LS_ERROR) << "SetAudioPlayoutVolume: No audio channel exists.";
881 return;
882 }
883
884 if (!voice_channel_->SetOutputScaling(ssrc, volume, volume))
885 ASSERT(false);
886}
887
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000888bool WebRtcSession::SetCaptureDevice(uint32 ssrc,
889 cricket::VideoCapturer* camera) {
890 ASSERT(signaling_thread()->IsCurrent());
891
892 if (!video_channel_.get()) {
893 // |video_channel_| doesnt't exist. Probably because the remote end doesnt't
894 // support video.
895 LOG(LS_WARNING) << "Video not used in this call.";
896 return false;
897 }
898 if (!video_channel_->SetCapturer(ssrc, camera)) {
899 // Allow that SetCapturer fail if |camera| is NULL but assert otherwise.
900 // This in the normal case when the underlying media channel has already
901 // been deleted.
902 ASSERT(camera == NULL);
903 return false;
904 }
905 return true;
906}
907
908void WebRtcSession::SetVideoPlayout(uint32 ssrc,
909 bool enable,
910 cricket::VideoRenderer* renderer) {
911 ASSERT(signaling_thread()->IsCurrent());
912 if (!video_channel_) {
913 LOG(LS_WARNING) << "SetVideoPlayout: No video channel exists.";
914 return;
915 }
916 if (!video_channel_->SetRenderer(ssrc, enable ? renderer : NULL)) {
917 // Allow that SetRenderer fail if |renderer| is NULL but assert otherwise.
918 // This in the normal case when the underlying media channel has already
919 // been deleted.
920 ASSERT(renderer == NULL);
921 }
922}
923
924void WebRtcSession::SetVideoSend(uint32 ssrc, bool enable,
925 const cricket::VideoOptions* options) {
926 ASSERT(signaling_thread()->IsCurrent());
927 if (!video_channel_) {
928 LOG(LS_WARNING) << "SetVideoSend: No video channel exists.";
929 return;
930 }
931 if (!video_channel_->MuteStream(ssrc, !enable)) {
932 // Allow that MuteStream fail if |enable| is false but assert otherwise.
933 // This in the normal case when the underlying media channel has already
934 // been deleted.
935 ASSERT(enable == false);
936 return;
937 }
938 if (enable && options)
939 video_channel_->SetChannelOptions(*options);
940}
941
942bool WebRtcSession::CanInsertDtmf(const std::string& track_id) {
943 ASSERT(signaling_thread()->IsCurrent());
944 if (!voice_channel_) {
945 LOG(LS_ERROR) << "CanInsertDtmf: No audio channel exists.";
946 return false;
947 }
948 uint32 send_ssrc = 0;
949 // The Dtmf is negotiated per channel not ssrc, so we only check if the ssrc
950 // exists.
951 if (!GetAudioSsrcByTrackId(BaseSession::local_description(), track_id,
952 &send_ssrc)) {
953 LOG(LS_ERROR) << "CanInsertDtmf: Track does not exist: " << track_id;
954 return false;
955 }
956 return voice_channel_->CanInsertDtmf();
957}
958
959bool WebRtcSession::InsertDtmf(const std::string& track_id,
960 int code, int duration) {
961 ASSERT(signaling_thread()->IsCurrent());
962 if (!voice_channel_) {
963 LOG(LS_ERROR) << "InsertDtmf: No audio channel exists.";
964 return false;
965 }
966 uint32 send_ssrc = 0;
967 if (!VERIFY(GetAudioSsrcByTrackId(BaseSession::local_description(),
968 track_id, &send_ssrc))) {
969 LOG(LS_ERROR) << "InsertDtmf: Track does not exist: " << track_id;
970 return false;
971 }
972 if (!voice_channel_->InsertDtmf(send_ssrc, code, duration,
973 cricket::DF_SEND)) {
974 LOG(LS_ERROR) << "Failed to insert DTMF to channel.";
975 return false;
976 }
977 return true;
978}
979
980sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() {
981 return &SignalVoiceChannelDestroyed;
982}
983
wu@webrtc.org78187522013-10-07 23:32:02 +0000984bool WebRtcSession::SendData(const cricket::SendDataParams& params,
985 const talk_base::Buffer& payload,
986 cricket::SendDataResult* result) {
987 if (!data_channel_.get()) {
988 LOG(LS_ERROR) << "SendData called when data_channel_ is NULL.";
989 return false;
990 }
991 return data_channel_->SendData(params, payload, result);
992}
993
994bool WebRtcSession::ConnectDataChannel(DataChannel* webrtc_data_channel) {
995 if (!data_channel_.get()) {
996 LOG(LS_ERROR) << "ConnectDataChannel called when data_channel_ is NULL.";
997 return false;
998 }
wu@webrtc.org78187522013-10-07 23:32:02 +0000999 data_channel_->SignalReadyToSendData.connect(webrtc_data_channel,
1000 &DataChannel::OnChannelReady);
1001 data_channel_->SignalDataReceived.connect(webrtc_data_channel,
1002 &DataChannel::OnDataReceived);
wu@webrtc.org78187522013-10-07 23:32:02 +00001003 return true;
1004}
1005
1006void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001007 if (!data_channel_.get()) {
1008 LOG(LS_ERROR) << "DisconnectDataChannel called when data_channel_ is NULL.";
1009 return;
1010 }
wu@webrtc.org78187522013-10-07 23:32:02 +00001011 data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
1012 data_channel_->SignalDataReceived.disconnect(webrtc_data_channel);
1013}
1014
sergeyu@chromium.orga23f0ca2013-11-13 22:48:52 +00001015void WebRtcSession::AddSctpDataStream(uint32 sid) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001016 if (!data_channel_.get()) {
1017 LOG(LS_ERROR) << "AddDataChannelStreams called when data_channel_ is NULL.";
1018 return;
1019 }
sergeyu@chromium.orga23f0ca2013-11-13 22:48:52 +00001020 data_channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(sid));
1021 data_channel_->AddSendStream(cricket::StreamParams::CreateLegacy(sid));
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001022}
1023
sergeyu@chromium.orga23f0ca2013-11-13 22:48:52 +00001024void WebRtcSession::RemoveSctpDataStream(uint32 sid) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001025 if (!data_channel_.get()) {
1026 LOG(LS_ERROR) << "RemoveDataChannelStreams called when data_channel_ is "
1027 << "NULL.";
1028 return;
1029 }
sergeyu@chromium.orga23f0ca2013-11-13 22:48:52 +00001030 data_channel_->RemoveRecvStream(sid);
1031 data_channel_->RemoveSendStream(sid);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001032}
1033
wu@webrtc.org07a6fbe2013-11-04 18:41:34 +00001034bool WebRtcSession::ReadyToSendData() const {
1035 return data_channel_.get() && data_channel_->ready_to_send_data();
1036}
1037
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001038talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
wu@webrtc.org78187522013-10-07 23:32:02 +00001039 const std::string& label,
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001040 const InternalDataChannelInit* config) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001041 if (state() == STATE_RECEIVEDTERMINATE) {
1042 return NULL;
1043 }
1044 if (data_channel_type_ == cricket::DCT_NONE) {
1045 LOG(LS_ERROR) << "CreateDataChannel: Data is not supported in this call.";
1046 return NULL;
1047 }
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001048 InternalDataChannelInit new_config =
1049 config ? (*config) : InternalDataChannelInit();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001050 if (data_channel_type_ == cricket::DCT_SCTP) {
1051 if (new_config.id < 0) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001052 talk_base::SSLRole role;
1053 if (GetSslRole(&role) &&
1054 !mediastream_signaling_->AllocateSctpSid(role, &new_config.id)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001055 LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel.";
1056 return NULL;
1057 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001058 } else if (!mediastream_signaling_->IsSctpSidAvailable(new_config.id)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001059 LOG(LS_ERROR) << "Failed to create a SCTP data channel "
1060 << "because the id is already in use or out of range.";
1061 return NULL;
1062 }
1063 }
wu@webrtc.org91053e72013-08-10 07:18:04 +00001064
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001065 talk_base::scoped_refptr<DataChannel> channel(DataChannel::Create(
1066 this, data_channel_type_, label, new_config));
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001067 if (channel && !mediastream_signaling_->AddDataChannel(channel))
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001068 return NULL;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001069
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001070 return channel;
1071}
1072
1073cricket::DataChannelType WebRtcSession::data_channel_type() const {
1074 return data_channel_type_;
1075}
1076
wu@webrtc.org91053e72013-08-10 07:18:04 +00001077bool WebRtcSession::IceRestartPending() const {
1078 return ice_restart_latch_->Get();
1079}
1080
1081void WebRtcSession::ResetIceRestartLatch() {
1082 ice_restart_latch_->Reset();
1083}
1084
1085void WebRtcSession::OnIdentityReady(talk_base::SSLIdentity* identity) {
1086 SetIdentity(identity);
1087}
1088
1089bool WebRtcSession::waiting_for_identity() const {
1090 return webrtc_session_desc_factory_->waiting_for_identity();
1091}
1092
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001093void WebRtcSession::SetIceConnectionState(
1094 PeerConnectionInterface::IceConnectionState state) {
1095 if (ice_connection_state_ == state) {
1096 return;
1097 }
1098
1099 // ASSERT that the requested transition is allowed. Note that
1100 // WebRtcSession does not implement "kIceConnectionClosed" (that is handled
1101 // within PeerConnection). This switch statement should compile away when
1102 // ASSERTs are disabled.
1103 switch (ice_connection_state_) {
1104 case PeerConnectionInterface::kIceConnectionNew:
1105 ASSERT(state == PeerConnectionInterface::kIceConnectionChecking);
1106 break;
1107 case PeerConnectionInterface::kIceConnectionChecking:
1108 ASSERT(state == PeerConnectionInterface::kIceConnectionFailed ||
1109 state == PeerConnectionInterface::kIceConnectionConnected);
1110 break;
1111 case PeerConnectionInterface::kIceConnectionConnected:
1112 ASSERT(state == PeerConnectionInterface::kIceConnectionDisconnected ||
1113 state == PeerConnectionInterface::kIceConnectionChecking ||
1114 state == PeerConnectionInterface::kIceConnectionCompleted);
1115 break;
1116 case PeerConnectionInterface::kIceConnectionCompleted:
1117 ASSERT(state == PeerConnectionInterface::kIceConnectionConnected ||
1118 state == PeerConnectionInterface::kIceConnectionDisconnected);
1119 break;
1120 case PeerConnectionInterface::kIceConnectionFailed:
1121 ASSERT(state == PeerConnectionInterface::kIceConnectionNew);
1122 break;
1123 case PeerConnectionInterface::kIceConnectionDisconnected:
1124 ASSERT(state == PeerConnectionInterface::kIceConnectionChecking ||
1125 state == PeerConnectionInterface::kIceConnectionConnected ||
1126 state == PeerConnectionInterface::kIceConnectionCompleted ||
1127 state == PeerConnectionInterface::kIceConnectionFailed);
1128 break;
1129 case PeerConnectionInterface::kIceConnectionClosed:
1130 ASSERT(false);
1131 break;
1132 default:
1133 ASSERT(false);
1134 break;
1135 }
1136
1137 ice_connection_state_ = state;
1138 if (ice_observer_) {
1139 ice_observer_->OnIceConnectionChange(ice_connection_state_);
1140 }
1141}
1142
1143void WebRtcSession::OnTransportRequestSignaling(
1144 cricket::Transport* transport) {
1145 ASSERT(signaling_thread()->IsCurrent());
1146 transport->OnSignalingReady();
1147 if (ice_observer_) {
1148 ice_observer_->OnIceGatheringChange(
1149 PeerConnectionInterface::kIceGatheringGathering);
1150 }
1151}
1152
1153void WebRtcSession::OnTransportConnecting(cricket::Transport* transport) {
1154 ASSERT(signaling_thread()->IsCurrent());
1155 // start monitoring for the write state of the transport.
1156 OnTransportWritable(transport);
1157}
1158
1159void WebRtcSession::OnTransportWritable(cricket::Transport* transport) {
1160 ASSERT(signaling_thread()->IsCurrent());
1161 // TODO(bemasc): Expose more API from Transport to detect when
1162 // candidate selection starts or stops, due to success or failure.
1163 if (transport->all_channels_writable()) {
mallinath@webrtc.org385857d2014-02-14 00:56:12 +00001164 // By the time |SignalTransportWritable| arrives, the excess channels may
1165 // already have been pruned, so that the Transport is Completed. The
1166 // specification requires that transitions from Checking to Completed pass
1167 // through Connected. This check enforces that requirement.
1168 // (Direct transitions from Connected and Disconnected to Completed are
1169 // allowed.)
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001170 if (ice_connection_state_ ==
mallinath@webrtc.org385857d2014-02-14 00:56:12 +00001171 PeerConnectionInterface::kIceConnectionChecking) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001172 SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected);
1173 }
mallinath@webrtc.org385857d2014-02-14 00:56:12 +00001174
1175 SetIceConnectionState(transport->completed() ?
1176 PeerConnectionInterface::kIceConnectionCompleted :
1177 PeerConnectionInterface::kIceConnectionConnected);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001178 } else if (transport->HasChannels()) {
1179 // If the current state is Connected or Completed, then there were writable
1180 // channels but now there are not, so the next state must be Disconnected.
1181 if (ice_connection_state_ ==
1182 PeerConnectionInterface::kIceConnectionConnected ||
1183 ice_connection_state_ ==
1184 PeerConnectionInterface::kIceConnectionCompleted) {
1185 SetIceConnectionState(
1186 PeerConnectionInterface::kIceConnectionDisconnected);
1187 }
1188 }
1189}
1190
mallinath@webrtc.org385857d2014-02-14 00:56:12 +00001191void WebRtcSession::OnTransportCompleted(cricket::Transport* transport) {
1192 ASSERT(signaling_thread()->IsCurrent());
1193 SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted);
1194}
1195
1196void WebRtcSession::OnTransportFailed(cricket::Transport* transport) {
1197 ASSERT(signaling_thread()->IsCurrent());
1198 SetIceConnectionState(PeerConnectionInterface::kIceConnectionFailed);
1199}
1200
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001201void WebRtcSession::OnTransportProxyCandidatesReady(
1202 cricket::TransportProxy* proxy, const cricket::Candidates& candidates) {
1203 ASSERT(signaling_thread()->IsCurrent());
1204 ProcessNewLocalCandidate(proxy->content_name(), candidates);
1205}
1206
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001207void WebRtcSession::OnCandidatesAllocationDone() {
1208 ASSERT(signaling_thread()->IsCurrent());
1209 if (ice_observer_) {
1210 ice_observer_->OnIceGatheringChange(
1211 PeerConnectionInterface::kIceGatheringComplete);
1212 ice_observer_->OnIceComplete();
1213 }
1214}
1215
1216// Enabling voice and video channel.
1217void WebRtcSession::EnableChannels() {
1218 if (voice_channel_ && !voice_channel_->enabled())
1219 voice_channel_->Enable(true);
1220
1221 if (video_channel_ && !video_channel_->enabled())
1222 video_channel_->Enable(true);
1223
1224 if (data_channel_.get() && !data_channel_->enabled())
1225 data_channel_->Enable(true);
1226}
1227
1228void WebRtcSession::ProcessNewLocalCandidate(
1229 const std::string& content_name,
1230 const cricket::Candidates& candidates) {
1231 int sdp_mline_index;
1232 if (!GetLocalCandidateMediaIndex(content_name, &sdp_mline_index)) {
1233 LOG(LS_ERROR) << "ProcessNewLocalCandidate: content name "
1234 << content_name << " not found";
1235 return;
1236 }
1237
1238 for (cricket::Candidates::const_iterator citer = candidates.begin();
1239 citer != candidates.end(); ++citer) {
1240 // Use content_name as the candidate media id.
1241 JsepIceCandidate candidate(content_name, sdp_mline_index, *citer);
1242 if (ice_observer_) {
1243 ice_observer_->OnIceCandidate(&candidate);
1244 }
1245 if (local_desc_) {
1246 local_desc_->AddCandidate(&candidate);
1247 }
1248 }
1249}
1250
1251// Returns the media index for a local ice candidate given the content name.
1252bool WebRtcSession::GetLocalCandidateMediaIndex(const std::string& content_name,
1253 int* sdp_mline_index) {
1254 if (!BaseSession::local_description() || !sdp_mline_index)
1255 return false;
1256
1257 bool content_found = false;
1258 const ContentInfos& contents = BaseSession::local_description()->contents();
1259 for (size_t index = 0; index < contents.size(); ++index) {
1260 if (contents[index].name == content_name) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001261 *sdp_mline_index = static_cast<int>(index);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001262 content_found = true;
1263 break;
1264 }
1265 }
1266 return content_found;
1267}
1268
1269bool WebRtcSession::UseCandidatesInSessionDescription(
1270 const SessionDescriptionInterface* remote_desc) {
1271 if (!remote_desc)
1272 return true;
1273 bool ret = true;
1274 for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) {
1275 const IceCandidateCollection* candidates = remote_desc->candidates(m);
1276 for (size_t n = 0; n < candidates->count(); ++n) {
1277 ret = UseCandidate(candidates->at(n));
1278 if (!ret)
1279 break;
1280 }
1281 }
1282 return ret;
1283}
1284
1285bool WebRtcSession::UseCandidate(
1286 const IceCandidateInterface* candidate) {
1287
1288 size_t mediacontent_index = static_cast<size_t>(candidate->sdp_mline_index());
1289 size_t remote_content_size =
1290 BaseSession::remote_description()->contents().size();
1291 if (mediacontent_index >= remote_content_size) {
1292 LOG(LS_ERROR)
1293 << "UseRemoteCandidateInSession: Invalid candidate media index.";
1294 return false;
1295 }
1296
1297 cricket::ContentInfo content =
1298 BaseSession::remote_description()->contents()[mediacontent_index];
1299 std::vector<cricket::Candidate> candidates;
1300 candidates.push_back(candidate->candidate());
1301 // Invoking BaseSession method to handle remote candidates.
1302 std::string error;
1303 if (OnRemoteCandidates(content.name, candidates, &error)) {
1304 // Candidates successfully submitted for checking.
1305 if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew ||
1306 ice_connection_state_ ==
1307 PeerConnectionInterface::kIceConnectionDisconnected) {
1308 // If state is New, then the session has just gotten its first remote ICE
1309 // candidates, so go to Checking.
1310 // If state is Disconnected, the session is re-using old candidates or
1311 // receiving additional ones, so go to Checking.
1312 // If state is Connected, stay Connected.
1313 // TODO(bemasc): If state is Connected, and the new candidates are for a
1314 // newly added transport, then the state actually _should_ move to
1315 // checking. Add a way to distinguish that case.
1316 SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking);
1317 }
1318 // TODO(bemasc): If state is Completed, go back to Connected.
1319 } else {
1320 LOG(LS_WARNING) << error;
1321 }
1322 return true;
1323}
1324
1325void WebRtcSession::RemoveUnusedChannelsAndTransports(
1326 const SessionDescription* desc) {
1327 const cricket::ContentInfo* voice_info =
1328 cricket::GetFirstAudioContent(desc);
1329 if ((!voice_info || voice_info->rejected) && voice_channel_) {
1330 mediastream_signaling_->OnAudioChannelClose();
1331 SignalVoiceChannelDestroyed();
1332 const std::string content_name = voice_channel_->content_name();
1333 channel_manager_->DestroyVoiceChannel(voice_channel_.release());
1334 DestroyTransportProxy(content_name);
1335 }
1336
1337 const cricket::ContentInfo* video_info =
1338 cricket::GetFirstVideoContent(desc);
1339 if ((!video_info || video_info->rejected) && video_channel_) {
1340 mediastream_signaling_->OnVideoChannelClose();
1341 SignalVideoChannelDestroyed();
1342 const std::string content_name = video_channel_->content_name();
1343 channel_manager_->DestroyVideoChannel(video_channel_.release());
1344 DestroyTransportProxy(content_name);
1345 }
1346
1347 const cricket::ContentInfo* data_info =
1348 cricket::GetFirstDataContent(desc);
1349 if ((!data_info || data_info->rejected) && data_channel_) {
1350 mediastream_signaling_->OnDataChannelClose();
1351 SignalDataChannelDestroyed();
1352 const std::string content_name = data_channel_->content_name();
1353 channel_manager_->DestroyDataChannel(data_channel_.release());
1354 DestroyTransportProxy(content_name);
1355 }
1356}
1357
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001358// TODO(mallinath) - Add a correct error code if the channels are not creatued
1359// due to BUNDLE is enabled but rtcp-mux is disabled.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001360bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
1361 // Disabling the BUNDLE flag in PortAllocator if offer disabled it.
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001362 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1363 if (state() == STATE_INIT && !bundle_enabled) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001364 port_allocator()->set_flags(port_allocator()->flags() &
1365 ~cricket::PORTALLOCATOR_ENABLE_BUNDLE);
1366 }
1367
1368 // Creating the media channels and transport proxies.
1369 const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(desc);
1370 if (voice && !voice->rejected && !voice_channel_) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001371 if (!CreateVoiceChannel(voice)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001372 LOG(LS_ERROR) << "Failed to create voice channel.";
1373 return false;
1374 }
1375 }
1376
1377 const cricket::ContentInfo* video = cricket::GetFirstVideoContent(desc);
1378 if (video && !video->rejected && !video_channel_) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001379 if (!CreateVideoChannel(video)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001380 LOG(LS_ERROR) << "Failed to create video channel.";
1381 return false;
1382 }
1383 }
1384
1385 const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc);
1386 if (data_channel_type_ != cricket::DCT_NONE &&
1387 data && !data->rejected && !data_channel_.get()) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001388 if (!CreateDataChannel(data)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001389 LOG(LS_ERROR) << "Failed to create data channel.";
1390 return false;
1391 }
1392 }
1393
1394 return true;
1395}
1396
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001397bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001398 voice_channel_.reset(channel_manager_->CreateVoiceChannel(
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001399 this, content->name, true));
wu@webrtc.orgde305012013-10-31 15:40:38 +00001400 if (!voice_channel_.get())
1401 return false;
1402
1403 if (dscp_enabled_) {
1404 cricket::AudioOptions options;
1405 options.dscp.Set(true);
1406 voice_channel_->SetChannelOptions(options);
1407 }
1408 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001409}
1410
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001411bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001412 video_channel_.reset(channel_manager_->CreateVideoChannel(
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001413 this, content->name, true, voice_channel_.get()));
wu@webrtc.orgde305012013-10-31 15:40:38 +00001414 if (!video_channel_.get())
1415 return false;
1416
1417 if (dscp_enabled_) {
1418 cricket::VideoOptions options;
1419 options.dscp.Set(true);
1420 video_channel_->SetChannelOptions(options);
1421 }
1422 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001423}
1424
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001425bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001426 bool sctp = (data_channel_type_ == cricket::DCT_SCTP);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001427 data_channel_.reset(channel_manager_->CreateDataChannel(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001428 this, content->name, !sctp, data_channel_type_));
wu@webrtc.org91053e72013-08-10 07:18:04 +00001429 if (!data_channel_.get()) {
1430 return false;
1431 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001432 if (sctp) {
1433 mediastream_signaling_->OnDataTransportCreatedForSctp();
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001434 data_channel_->SignalDataReceived.connect(
1435 this, &WebRtcSession::OnDataChannelMessageReceived);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001436 }
wu@webrtc.org91053e72013-08-10 07:18:04 +00001437 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001438}
1439
1440void WebRtcSession::CopySavedCandidates(
1441 SessionDescriptionInterface* dest_desc) {
1442 if (!dest_desc) {
1443 ASSERT(false);
1444 return;
1445 }
1446 for (size_t i = 0; i < saved_candidates_.size(); ++i) {
1447 dest_desc->AddCandidate(saved_candidates_[i]);
1448 delete saved_candidates_[i];
1449 }
1450 saved_candidates_.clear();
1451}
1452
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001453void WebRtcSession::OnDataChannelMessageReceived(
1454 cricket::DataChannel* channel,
1455 const cricket::ReceiveDataParams& params,
1456 const talk_base::Buffer& payload) {
wu@webrtc.org1d1ffc92013-10-16 18:12:02 +00001457 ASSERT(data_channel_type_ == cricket::DCT_SCTP);
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001458 if (params.type == cricket::DMT_CONTROL &&
1459 mediastream_signaling_->IsSctpSidAvailable(params.ssrc)) {
1460 // Received CONTROL on unused sid, process as an OPEN message.
1461 mediastream_signaling_->AddDataChannelFromOpenMessage(params, payload);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001462 }
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001463 // otherwise ignore the message.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001464}
1465
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001466// Returns false if bundle is enabled and rtcp_mux is disabled.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001467bool WebRtcSession::ValidateBundleSettings(const SessionDescription* desc) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001468 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1469 if (!bundle_enabled)
1470 return true;
1471
1472 const cricket::ContentGroup* bundle_group =
1473 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
1474 ASSERT(bundle_group != NULL);
1475
1476 const cricket::ContentInfos& contents = desc->contents();
1477 for (cricket::ContentInfos::const_iterator citer = contents.begin();
1478 citer != contents.end(); ++citer) {
1479 const cricket::ContentInfo* content = (&*citer);
1480 ASSERT(content != NULL);
1481 if (bundle_group->HasContentName(content->name) &&
1482 !content->rejected && content->type == cricket::NS_JINGLE_RTP) {
1483 if (!HasRtcpMuxEnabled(content))
1484 return false;
1485 }
1486 }
1487 // RTCP-MUX is enabled in all the contents.
1488 return true;
1489}
1490
1491bool WebRtcSession::HasRtcpMuxEnabled(
1492 const cricket::ContentInfo* content) {
1493 const cricket::MediaContentDescription* description =
1494 static_cast<cricket::MediaContentDescription*>(content->description);
1495 return description->rtcp_mux();
1496}
1497
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001498bool WebRtcSession::ValidateSessionDescription(
1499 const SessionDescriptionInterface* sdesc,
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001500 cricket::ContentSource source, std::string* err_desc) {
1501 std::string type;
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001502 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001503 return BadSdp(source, type, GetSessionErrorMsg(), err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001504 }
1505
1506 if (!sdesc || !sdesc->description()) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001507 return BadSdp(source, type, kInvalidSdp, err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001508 }
1509
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001510 type = sdesc->type();
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001511 Action action = GetAction(sdesc->type());
1512 if (source == cricket::CS_LOCAL) {
1513 if (!ExpectSetLocalDescription(action))
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001514 return BadLocalSdp(type, BadStateErrMsg(state()), err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001515 } else {
1516 if (!ExpectSetRemoteDescription(action))
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001517 return BadRemoteSdp(type, BadStateErrMsg(state()), err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001518 }
1519
1520 // Verify crypto settings.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +00001521 std::string crypto_error;
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001522 if ((webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED ||
1523 dtls_enabled_) &&
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +00001524 !VerifyCrypto(sdesc->description(), dtls_enabled_, &crypto_error)) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001525 return BadSdp(source, type, crypto_error, err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001526 }
1527
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001528 // Verify ice-ufrag and ice-pwd.
1529 if (!VerifyIceUfragPwdPresent(sdesc->description())) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001530 return BadSdp(source, type, kSdpWithoutIceUfragPwd, err_desc);
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001531 }
1532
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001533 if (!ValidateBundleSettings(sdesc->description())) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001534 return BadSdp(source, type, kBundleWithoutRtcpMux, err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001535 }
1536
1537 // Verify m-lines in Answer when compared against Offer.
1538 if (action == kAnswer) {
1539 const cricket::SessionDescription* offer_desc =
1540 (source == cricket::CS_LOCAL) ? remote_description()->description() :
1541 local_description()->description();
1542 if (!VerifyMediaDescriptions(sdesc->description(), offer_desc)) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001543 return BadAnswerSdp(source, kMlineMismatch, err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001544 }
1545 }
1546
1547 return true;
1548}
1549
1550bool WebRtcSession::ExpectSetLocalDescription(Action action) {
1551 return ((action == kOffer && state() == STATE_INIT) ||
1552 // update local offer
1553 (action == kOffer && state() == STATE_SENTINITIATE) ||
1554 // update the current ongoing session.
1555 (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
1556 (action == kOffer && state() == STATE_SENTACCEPT) ||
1557 (action == kOffer && state() == STATE_INPROGRESS) ||
1558 // accept remote offer
1559 (action == kAnswer && state() == STATE_RECEIVEDINITIATE) ||
1560 (action == kAnswer && state() == STATE_SENTPRACCEPT) ||
1561 (action == kPrAnswer && state() == STATE_RECEIVEDINITIATE) ||
1562 (action == kPrAnswer && state() == STATE_SENTPRACCEPT));
1563}
1564
1565bool WebRtcSession::ExpectSetRemoteDescription(Action action) {
1566 return ((action == kOffer && state() == STATE_INIT) ||
1567 // update remote offer
1568 (action == kOffer && state() == STATE_RECEIVEDINITIATE) ||
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 local offer
1574 (action == kAnswer && state() == STATE_SENTINITIATE) ||
1575 (action == kAnswer && state() == STATE_RECEIVEDPRACCEPT) ||
1576 (action == kPrAnswer && state() == STATE_SENTINITIATE) ||
1577 (action == kPrAnswer && state() == STATE_RECEIVEDPRACCEPT));
1578}
1579
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001580std::string WebRtcSession::GetSessionErrorMsg() {
1581 std::ostringstream desc;
1582 desc << kSessionError << GetErrorCodeString(error()) << ". ";
1583 desc << kSessionErrorDesc << error_desc() << ".";
1584 return desc.str();
1585}
1586
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001587} // namespace webrtc