blob: 2f70ea096d0087e370a8846137ebe15aade4eaa0 [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),
wu@webrtc.orgde305012013-10-31 15:40:38 +0000437 dscp_enabled_(false),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000438 data_channel_type_(cricket::DCT_NONE),
439 ice_restart_latch_(new IceRestartAnswerLatch) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000440}
441
442WebRtcSession::~WebRtcSession() {
443 if (voice_channel_.get()) {
444 SignalVoiceChannelDestroyed();
445 channel_manager_->DestroyVoiceChannel(voice_channel_.release());
446 }
447 if (video_channel_.get()) {
448 SignalVideoChannelDestroyed();
449 channel_manager_->DestroyVideoChannel(video_channel_.release());
450 }
451 if (data_channel_.get()) {
452 SignalDataChannelDestroyed();
453 channel_manager_->DestroyDataChannel(data_channel_.release());
454 }
455 for (size_t i = 0; i < saved_candidates_.size(); ++i) {
456 delete saved_candidates_[i];
457 }
458 delete identity();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000459}
460
wu@webrtc.org91053e72013-08-10 07:18:04 +0000461bool WebRtcSession::Initialize(
wu@webrtc.org97077a32013-10-25 21:18:33 +0000462 const PeerConnectionFactoryInterface::Options& options,
wu@webrtc.org91053e72013-08-10 07:18:04 +0000463 const MediaConstraintsInterface* constraints,
464 DTLSIdentityServiceInterface* dtls_identity_service) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000465 // TODO(perkj): Take |constraints| into consideration. Return false if not all
466 // mandatory constraints can be fulfilled. Note that |constraints|
467 // can be null.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000468 bool value;
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000469
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000470 if (options.disable_encryption) {
471 dtls_enabled_ = false;
472 } else {
473 // Enable DTLS by default if |dtls_identity_service| is valid.
474 dtls_enabled_ = (dtls_identity_service != NULL);
475 // |constraints| can override the default |dtls_enabled_| value.
476 if (FindConstraint(
477 constraints,
478 MediaConstraintsInterface::kEnableDtlsSrtp,
479 &value, NULL)) {
480 dtls_enabled_ = value;
481 }
sergeyu@chromium.orga59696b2013-09-13 23:48:58 +0000482 }
483
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000484 // Enable creation of RTP data channels if the kEnableRtpDataChannels is set.
wu@webrtc.org97077a32013-10-25 21:18:33 +0000485 // It takes precendence over the disable_sctp_data_channels
486 // PeerConnectionFactoryInterface::Options.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000487 if (FindConstraint(
488 constraints, MediaConstraintsInterface::kEnableRtpDataChannels,
489 &value, NULL) && value) {
490 LOG(LS_INFO) << "Allowing RTP data engine.";
491 data_channel_type_ = cricket::DCT_RTP;
wu@webrtc.org91053e72013-08-10 07:18:04 +0000492 } else {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000493 // DTLS has to be enabled to use SCTP.
wu@webrtc.org97077a32013-10-25 21:18:33 +0000494 if (!options.disable_sctp_data_channels && dtls_enabled_) {
wu@webrtc.org91053e72013-08-10 07:18:04 +0000495 LOG(LS_INFO) << "Allowing SCTP data engine.";
496 data_channel_type_ = cricket::DCT_SCTP;
497 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000498 }
499 if (data_channel_type_ != cricket::DCT_NONE) {
500 mediastream_signaling_->SetDataChannelFactory(this);
501 }
502
wu@webrtc.orgde305012013-10-31 15:40:38 +0000503 // Find DSCP constraint.
504 if (FindConstraint(
505 constraints,
506 MediaConstraintsInterface::kEnableDscp,
507 &value, NULL)) {
508 dscp_enabled_ = value;
509 }
510
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000511 const cricket::VideoCodec default_codec(
512 JsepSessionDescription::kDefaultVideoCodecId,
513 JsepSessionDescription::kDefaultVideoCodecName,
514 JsepSessionDescription::kMaxVideoCodecWidth,
515 JsepSessionDescription::kMaxVideoCodecHeight,
516 JsepSessionDescription::kDefaultVideoCodecFramerate,
517 JsepSessionDescription::kDefaultVideoCodecPreference);
518 channel_manager_->SetDefaultVideoEncoderConfig(
519 cricket::VideoEncoderConfig(default_codec));
wu@webrtc.org91053e72013-08-10 07:18:04 +0000520
521 webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
522 signaling_thread(),
523 channel_manager_,
524 mediastream_signaling_,
525 dtls_identity_service,
526 this,
527 id(),
528 data_channel_type_,
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000529 dtls_enabled_));
wu@webrtc.org91053e72013-08-10 07:18:04 +0000530
531 webrtc_session_desc_factory_->SignalIdentityReady.connect(
532 this, &WebRtcSession::OnIdentityReady);
mallinath@webrtc.org7e809c32013-09-30 18:59:08 +0000533
wu@webrtc.org97077a32013-10-25 21:18:33 +0000534 if (options.disable_encryption) {
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000535 webrtc_session_desc_factory_->SetSdesPolicy(cricket::SEC_DISABLED);
mallinath@webrtc.org7e809c32013-09-30 18:59:08 +0000536 }
537
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000538 return true;
539}
540
541void WebRtcSession::Terminate() {
542 SetState(STATE_RECEIVEDTERMINATE);
543 RemoveUnusedChannelsAndTransports(NULL);
544 ASSERT(voice_channel_.get() == NULL);
545 ASSERT(video_channel_.get() == NULL);
546 ASSERT(data_channel_.get() == NULL);
547}
548
549bool WebRtcSession::StartCandidatesAllocation() {
550 // SpeculativelyConnectTransportChannels, will call ConnectChannels method
551 // from TransportProxy to start gathering ice candidates.
552 SpeculativelyConnectAllTransportChannels();
553 if (!saved_candidates_.empty()) {
554 // If there are saved candidates which arrived before local description is
555 // set, copy those to remote description.
556 CopySavedCandidates(remote_desc_.get());
557 }
558 // Push remote candidates present in remote description to transport channels.
559 UseCandidatesInSessionDescription(remote_desc_.get());
560 return true;
561}
562
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000563void WebRtcSession::SetSdesPolicy(cricket::SecurePolicy secure_policy) {
564 webrtc_session_desc_factory_->SetSdesPolicy(secure_policy);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000565}
566
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000567cricket::SecurePolicy WebRtcSession::SdesPolicy() const {
568 return webrtc_session_desc_factory_->SdesPolicy();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000569}
570
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000571bool WebRtcSession::GetSslRole(talk_base::SSLRole* role) {
572 if (local_description() == NULL || remote_description() == NULL) {
573 LOG(LS_INFO) << "Local and Remote descriptions must be applied to get "
574 << "SSL Role of the session.";
575 return false;
576 }
577
578 // TODO(mallinath) - Return role of each transport, as role may differ from
579 // one another.
580 // In current implementaion we just return the role of first transport in the
581 // transport map.
582 for (cricket::TransportMap::const_iterator iter = transport_proxies().begin();
583 iter != transport_proxies().end(); ++iter) {
584 if (iter->second->impl()) {
585 return iter->second->impl()->GetSslRole(role);
586 }
587 }
588 return false;
589}
590
wu@webrtc.org91053e72013-08-10 07:18:04 +0000591void WebRtcSession::CreateOffer(CreateSessionDescriptionObserver* observer,
592 const MediaConstraintsInterface* constraints) {
593 webrtc_session_desc_factory_->CreateOffer(observer, constraints);
594}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000595
wu@webrtc.org91053e72013-08-10 07:18:04 +0000596void WebRtcSession::CreateAnswer(CreateSessionDescriptionObserver* observer,
597 const MediaConstraintsInterface* constraints) {
598 webrtc_session_desc_factory_->CreateAnswer(observer, constraints);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000599}
600
601bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
602 std::string* err_desc) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000603 // Takes the ownership of |desc| regardless of the result.
604 talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
605
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000606 // Validate SDP.
607 if (!ValidateSessionDescription(desc, cricket::CS_LOCAL, err_desc)) {
608 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000609 }
610
611 // Update the initiator flag if this session is the initiator.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000612 Action action = GetAction(desc->type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000613 if (state() == STATE_INIT && action == kOffer) {
614 set_initiator(true);
615 }
616
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000617 cricket::SecurePolicy sdes_policy =
618 webrtc_session_desc_factory_->SdesPolicy();
619 cricket::CryptoType crypto_required = dtls_enabled_ ?
620 cricket::CT_DTLS : (sdes_policy == cricket::SEC_REQUIRED ?
621 cricket::CT_SDES : cricket::CT_NONE);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000622 // Update the MediaContentDescription crypto settings as per the policy set.
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +0000623 UpdateSessionDescriptionSecurePolicy(crypto_required, desc->description());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000624
625 set_local_description(desc->description()->Copy());
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000626 local_desc_.reset(desc_temp.release());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000627
628 // Transport and Media channels will be created only when offer is set.
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000629 if (action == kOffer && !CreateChannels(local_desc_->description())) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000630 // TODO(mallinath) - Handle CreateChannel failure, as new local description
631 // is applied. Restore back to old description.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000632 return BadLocalSdp(desc->type(), kCreateChannelFailed, err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000633 }
634
635 // Remove channel and transport proxies, if MediaContentDescription is
636 // rejected.
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000637 RemoveUnusedChannelsAndTransports(local_desc_->description());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000638
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000639 if (!UpdateSessionState(action, cricket::CS_LOCAL, err_desc)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000640 return false;
641 }
642 // Kick starting the ice candidates allocation.
643 StartCandidatesAllocation();
644
645 // Update state and SSRC of local MediaStreams and DataChannels based on the
646 // local session description.
647 mediastream_signaling_->OnLocalDescriptionChanged(local_desc_.get());
648
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000649 talk_base::SSLRole role;
650 if (data_channel_type_ == cricket::DCT_SCTP && GetSslRole(&role)) {
651 mediastream_signaling_->OnDtlsRoleReadyForSctp(role);
652 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000653 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000654 return BadLocalSdp(desc->type(), GetSessionErrorMsg(), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000655 }
656 return true;
657}
658
659bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
660 std::string* err_desc) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000661 // Takes the ownership of |desc| regardless of the result.
662 talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
663
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000664 // Validate SDP.
665 if (!ValidateSessionDescription(desc, cricket::CS_REMOTE, err_desc)) {
666 return false;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000667 }
668
669 // Transport and Media channels will be created only when offer is set.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +0000670 Action action = GetAction(desc->type());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000671 if (action == kOffer && !CreateChannels(desc->description())) {
672 // TODO(mallinath) - Handle CreateChannel failure, as new local description
673 // is applied. Restore back to old description.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000674 return BadRemoteSdp(desc->type(), kCreateChannelFailed, err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000675 }
676
677 // Remove channel and transport proxies, if MediaContentDescription is
678 // rejected.
679 RemoveUnusedChannelsAndTransports(desc->description());
680
681 // NOTE: Candidates allocation will be initiated only when SetLocalDescription
682 // is called.
683 set_remote_description(desc->description()->Copy());
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000684 if (!UpdateSessionState(action, cricket::CS_REMOTE, err_desc)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000685 return false;
686 }
687
688 // Update remote MediaStreams.
689 mediastream_signaling_->OnRemoteDescriptionChanged(desc);
690 if (local_description() && !UseCandidatesInSessionDescription(desc)) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000691 return BadRemoteSdp(desc->type(), kInvalidCandidates, err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000692 }
693
694 // Copy all saved candidates.
695 CopySavedCandidates(desc);
696 // We retain all received candidates.
wu@webrtc.org91053e72013-08-10 07:18:04 +0000697 WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
698 remote_desc_.get(), desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000699 // Check if this new SessionDescription contains new ice ufrag and password
700 // that indicates the remote peer requests ice restart.
701 ice_restart_latch_->CheckForRemoteIceRestart(remote_desc_.get(),
702 desc);
henrike@webrtc.org28654cb2013-07-22 21:07:49 +0000703 remote_desc_.reset(desc_temp.release());
wu@webrtc.orgcecfd182013-10-30 05:18:12 +0000704
705 talk_base::SSLRole role;
706 if (data_channel_type_ == cricket::DCT_SCTP && GetSslRole(&role)) {
707 mediastream_signaling_->OnDtlsRoleReadyForSctp(role);
708 }
709
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000710 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000711 return BadRemoteSdp(desc->type(), GetSessionErrorMsg(), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000712 }
713 return true;
714}
715
716bool WebRtcSession::UpdateSessionState(
717 Action action, cricket::ContentSource source,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000718 std::string* err_desc) {
719 // If there's already a pending error then no state transition should happen.
720 // But all call-sites should be verifying this before calling us!
721 ASSERT(error() == cricket::BaseSession::ERROR_NONE);
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000722 std::string td_err;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000723 if (action == kOffer) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000724 if (!PushdownTransportDescription(source, cricket::CA_OFFER, &td_err)) {
725 return BadOfferSdp(source, MakeTdErrorString(td_err), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000726 }
727 SetState(source == cricket::CS_LOCAL ?
728 STATE_SENTINITIATE : STATE_RECEIVEDINITIATE);
729 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000730 return BadOfferSdp(source, GetSessionErrorMsg(), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000731 }
732 } else if (action == kPrAnswer) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000733 if (!PushdownTransportDescription(source, cricket::CA_PRANSWER, &td_err)) {
734 return BadPranswerSdp(source, MakeTdErrorString(td_err), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000735 }
736 EnableChannels();
737 SetState(source == cricket::CS_LOCAL ?
738 STATE_SENTPRACCEPT : STATE_RECEIVEDPRACCEPT);
739 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000740 return BadPranswerSdp(source, GetSessionErrorMsg(), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000741 }
742 } else if (action == kAnswer) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000743 if (!PushdownTransportDescription(source, cricket::CA_ANSWER, &td_err)) {
744 return BadAnswerSdp(source, MakeTdErrorString(td_err), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000745 }
746 MaybeEnableMuxingSupport();
747 EnableChannels();
748 SetState(source == cricket::CS_LOCAL ?
749 STATE_SENTACCEPT : STATE_RECEIVEDACCEPT);
750 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000751 return BadAnswerSdp(source, GetSessionErrorMsg(), err_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000752 }
753 }
754 return true;
755}
756
757WebRtcSession::Action WebRtcSession::GetAction(const std::string& type) {
758 if (type == SessionDescriptionInterface::kOffer) {
759 return WebRtcSession::kOffer;
760 } else if (type == SessionDescriptionInterface::kPrAnswer) {
761 return WebRtcSession::kPrAnswer;
762 } else if (type == SessionDescriptionInterface::kAnswer) {
763 return WebRtcSession::kAnswer;
764 }
765 ASSERT(false && "unknown action type");
766 return WebRtcSession::kOffer;
767}
768
769bool WebRtcSession::ProcessIceMessage(const IceCandidateInterface* candidate) {
770 if (state() == STATE_INIT) {
771 LOG(LS_ERROR) << "ProcessIceMessage: ICE candidates can't be added "
772 << "without any offer (local or remote) "
773 << "session description.";
774 return false;
775 }
776
777 if (!candidate) {
778 LOG(LS_ERROR) << "ProcessIceMessage: Candidate is NULL";
779 return false;
780 }
781
782 if (!local_description() || !remote_description()) {
783 LOG(LS_INFO) << "ProcessIceMessage: Remote description not set, "
784 << "save the candidate for later use.";
785 saved_candidates_.push_back(
786 new JsepIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(),
787 candidate->candidate()));
788 return true;
789 }
790
791 // Add this candidate to the remote session description.
792 if (!remote_desc_->AddCandidate(candidate)) {
793 LOG(LS_ERROR) << "ProcessIceMessage: Candidate cannot be used";
794 return false;
795 }
796
mallinath@webrtc.org67ee6b92014-02-03 16:57:16 +0000797 return UseCandidate(candidate);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000798}
799
800bool WebRtcSession::GetTrackIdBySsrc(uint32 ssrc, std::string* id) {
801 if (GetLocalTrackId(ssrc, id)) {
802 if (GetRemoteTrackId(ssrc, id)) {
803 LOG(LS_WARNING) << "SSRC " << ssrc
804 << " exists in both local and remote descriptions";
805 return true; // We return the remote track id.
806 }
807 return true;
808 } else {
809 return GetRemoteTrackId(ssrc, id);
810 }
811}
812
813bool WebRtcSession::GetLocalTrackId(uint32 ssrc, std::string* track_id) {
814 if (!BaseSession::local_description())
815 return false;
816 return webrtc::GetTrackIdBySsrc(
817 BaseSession::local_description(), ssrc, track_id);
818}
819
820bool WebRtcSession::GetRemoteTrackId(uint32 ssrc, std::string* track_id) {
821 if (!BaseSession::remote_description())
822 return false;
823 return webrtc::GetTrackIdBySsrc(
824 BaseSession::remote_description(), ssrc, track_id);
825}
826
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000827std::string WebRtcSession::BadStateErrMsg(State state) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000828 std::ostringstream desc;
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000829 desc << "Called in wrong state: " << GetStateString(state);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000830 return desc.str();
831}
832
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000833void WebRtcSession::SetAudioPlayout(uint32 ssrc, bool enable,
834 cricket::AudioRenderer* renderer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000835 ASSERT(signaling_thread()->IsCurrent());
836 if (!voice_channel_) {
837 LOG(LS_ERROR) << "SetAudioPlayout: No audio channel exists.";
838 return;
839 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000840 if (!voice_channel_->SetRemoteRenderer(ssrc, renderer)) {
841 // SetRenderer() can fail if the ssrc does not match any playout channel.
842 LOG(LS_ERROR) << "SetAudioPlayout: ssrc is incorrect: " << ssrc;
843 return;
844 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000845 if (!voice_channel_->SetOutputScaling(ssrc, enable ? 1 : 0, enable ? 1 : 0)) {
846 // Allow that SetOutputScaling fail if |enable| is false but assert
847 // otherwise. This in the normal case when the underlying media channel has
848 // already been deleted.
849 ASSERT(enable == false);
850 }
851}
852
853void WebRtcSession::SetAudioSend(uint32 ssrc, bool enable,
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000854 const cricket::AudioOptions& options,
855 cricket::AudioRenderer* renderer) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000856 ASSERT(signaling_thread()->IsCurrent());
857 if (!voice_channel_) {
858 LOG(LS_ERROR) << "SetAudioSend: No audio channel exists.";
859 return;
860 }
henrike@webrtc.org1e09a712013-07-26 19:17:59 +0000861 if (!voice_channel_->SetLocalRenderer(ssrc, renderer)) {
862 // SetRenderer() can fail if the ssrc does not match any send channel.
863 LOG(LS_ERROR) << "SetAudioSend: ssrc is incorrect: " << ssrc;
864 return;
865 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000866 if (!voice_channel_->MuteStream(ssrc, !enable)) {
867 // Allow that MuteStream fail if |enable| is false but assert otherwise.
868 // This in the normal case when the underlying media channel has already
869 // been deleted.
870 ASSERT(enable == false);
871 return;
872 }
873 if (enable)
874 voice_channel_->SetChannelOptions(options);
875}
876
wu@webrtc.orgb9a088b2014-02-13 23:18:49 +0000877void WebRtcSession::SetAudioPlayoutVolume(uint32 ssrc, double volume) {
878 ASSERT(signaling_thread()->IsCurrent());
879 ASSERT(volume >= 0 && volume <= 10);
880 if (!voice_channel_) {
881 LOG(LS_ERROR) << "SetAudioPlayoutVolume: No audio channel exists.";
882 return;
883 }
884
885 if (!voice_channel_->SetOutputScaling(ssrc, volume, volume))
886 ASSERT(false);
887}
888
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000889bool WebRtcSession::SetCaptureDevice(uint32 ssrc,
890 cricket::VideoCapturer* camera) {
891 ASSERT(signaling_thread()->IsCurrent());
892
893 if (!video_channel_.get()) {
894 // |video_channel_| doesnt't exist. Probably because the remote end doesnt't
895 // support video.
896 LOG(LS_WARNING) << "Video not used in this call.";
897 return false;
898 }
899 if (!video_channel_->SetCapturer(ssrc, camera)) {
900 // Allow that SetCapturer fail if |camera| is NULL but assert otherwise.
901 // This in the normal case when the underlying media channel has already
902 // been deleted.
903 ASSERT(camera == NULL);
904 return false;
905 }
906 return true;
907}
908
909void WebRtcSession::SetVideoPlayout(uint32 ssrc,
910 bool enable,
911 cricket::VideoRenderer* renderer) {
912 ASSERT(signaling_thread()->IsCurrent());
913 if (!video_channel_) {
914 LOG(LS_WARNING) << "SetVideoPlayout: No video channel exists.";
915 return;
916 }
917 if (!video_channel_->SetRenderer(ssrc, enable ? renderer : NULL)) {
918 // Allow that SetRenderer fail if |renderer| is NULL but assert otherwise.
919 // This in the normal case when the underlying media channel has already
920 // been deleted.
921 ASSERT(renderer == NULL);
922 }
923}
924
925void WebRtcSession::SetVideoSend(uint32 ssrc, bool enable,
926 const cricket::VideoOptions* options) {
927 ASSERT(signaling_thread()->IsCurrent());
928 if (!video_channel_) {
929 LOG(LS_WARNING) << "SetVideoSend: No video channel exists.";
930 return;
931 }
932 if (!video_channel_->MuteStream(ssrc, !enable)) {
933 // Allow that MuteStream fail if |enable| is false but assert otherwise.
934 // This in the normal case when the underlying media channel has already
935 // been deleted.
936 ASSERT(enable == false);
937 return;
938 }
939 if (enable && options)
940 video_channel_->SetChannelOptions(*options);
941}
942
943bool WebRtcSession::CanInsertDtmf(const std::string& track_id) {
944 ASSERT(signaling_thread()->IsCurrent());
945 if (!voice_channel_) {
946 LOG(LS_ERROR) << "CanInsertDtmf: No audio channel exists.";
947 return false;
948 }
949 uint32 send_ssrc = 0;
950 // The Dtmf is negotiated per channel not ssrc, so we only check if the ssrc
951 // exists.
952 if (!GetAudioSsrcByTrackId(BaseSession::local_description(), track_id,
953 &send_ssrc)) {
954 LOG(LS_ERROR) << "CanInsertDtmf: Track does not exist: " << track_id;
955 return false;
956 }
957 return voice_channel_->CanInsertDtmf();
958}
959
960bool WebRtcSession::InsertDtmf(const std::string& track_id,
961 int code, int duration) {
962 ASSERT(signaling_thread()->IsCurrent());
963 if (!voice_channel_) {
964 LOG(LS_ERROR) << "InsertDtmf: No audio channel exists.";
965 return false;
966 }
967 uint32 send_ssrc = 0;
968 if (!VERIFY(GetAudioSsrcByTrackId(BaseSession::local_description(),
969 track_id, &send_ssrc))) {
970 LOG(LS_ERROR) << "InsertDtmf: Track does not exist: " << track_id;
971 return false;
972 }
973 if (!voice_channel_->InsertDtmf(send_ssrc, code, duration,
974 cricket::DF_SEND)) {
975 LOG(LS_ERROR) << "Failed to insert DTMF to channel.";
976 return false;
977 }
978 return true;
979}
980
981sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() {
982 return &SignalVoiceChannelDestroyed;
983}
984
wu@webrtc.org78187522013-10-07 23:32:02 +0000985bool WebRtcSession::SendData(const cricket::SendDataParams& params,
986 const talk_base::Buffer& payload,
987 cricket::SendDataResult* result) {
988 if (!data_channel_.get()) {
989 LOG(LS_ERROR) << "SendData called when data_channel_ is NULL.";
990 return false;
991 }
992 return data_channel_->SendData(params, payload, result);
993}
994
995bool WebRtcSession::ConnectDataChannel(DataChannel* webrtc_data_channel) {
996 if (!data_channel_.get()) {
997 LOG(LS_ERROR) << "ConnectDataChannel called when data_channel_ is NULL.";
998 return false;
999 }
wu@webrtc.org78187522013-10-07 23:32:02 +00001000 data_channel_->SignalReadyToSendData.connect(webrtc_data_channel,
1001 &DataChannel::OnChannelReady);
1002 data_channel_->SignalDataReceived.connect(webrtc_data_channel,
1003 &DataChannel::OnDataReceived);
wu@webrtc.org78187522013-10-07 23:32:02 +00001004 return true;
1005}
1006
1007void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001008 if (!data_channel_.get()) {
1009 LOG(LS_ERROR) << "DisconnectDataChannel called when data_channel_ is NULL.";
1010 return;
1011 }
wu@webrtc.org78187522013-10-07 23:32:02 +00001012 data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
1013 data_channel_->SignalDataReceived.disconnect(webrtc_data_channel);
1014}
1015
sergeyu@chromium.orga23f0ca2013-11-13 22:48:52 +00001016void WebRtcSession::AddSctpDataStream(uint32 sid) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001017 if (!data_channel_.get()) {
1018 LOG(LS_ERROR) << "AddDataChannelStreams called when data_channel_ is NULL.";
1019 return;
1020 }
sergeyu@chromium.orga23f0ca2013-11-13 22:48:52 +00001021 data_channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(sid));
1022 data_channel_->AddSendStream(cricket::StreamParams::CreateLegacy(sid));
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001023}
1024
sergeyu@chromium.orga23f0ca2013-11-13 22:48:52 +00001025void WebRtcSession::RemoveSctpDataStream(uint32 sid) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001026 if (!data_channel_.get()) {
1027 LOG(LS_ERROR) << "RemoveDataChannelStreams called when data_channel_ is "
1028 << "NULL.";
1029 return;
1030 }
sergeyu@chromium.orga23f0ca2013-11-13 22:48:52 +00001031 data_channel_->RemoveRecvStream(sid);
1032 data_channel_->RemoveSendStream(sid);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001033}
1034
wu@webrtc.org07a6fbe2013-11-04 18:41:34 +00001035bool WebRtcSession::ReadyToSendData() const {
1036 return data_channel_.get() && data_channel_->ready_to_send_data();
1037}
1038
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001039talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
wu@webrtc.org78187522013-10-07 23:32:02 +00001040 const std::string& label,
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001041 const InternalDataChannelInit* config) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001042 if (state() == STATE_RECEIVEDTERMINATE) {
1043 return NULL;
1044 }
1045 if (data_channel_type_ == cricket::DCT_NONE) {
1046 LOG(LS_ERROR) << "CreateDataChannel: Data is not supported in this call.";
1047 return NULL;
1048 }
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001049 InternalDataChannelInit new_config =
1050 config ? (*config) : InternalDataChannelInit();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001051 if (data_channel_type_ == cricket::DCT_SCTP) {
1052 if (new_config.id < 0) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001053 talk_base::SSLRole role;
1054 if (GetSslRole(&role) &&
1055 !mediastream_signaling_->AllocateSctpSid(role, &new_config.id)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001056 LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel.";
1057 return NULL;
1058 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001059 } else if (!mediastream_signaling_->IsSctpSidAvailable(new_config.id)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001060 LOG(LS_ERROR) << "Failed to create a SCTP data channel "
1061 << "because the id is already in use or out of range.";
1062 return NULL;
1063 }
1064 }
wu@webrtc.org91053e72013-08-10 07:18:04 +00001065
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001066 talk_base::scoped_refptr<DataChannel> channel(DataChannel::Create(
1067 this, data_channel_type_, label, new_config));
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001068 if (channel && !mediastream_signaling_->AddDataChannel(channel))
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001069 return NULL;
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001070
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001071 return channel;
1072}
1073
1074cricket::DataChannelType WebRtcSession::data_channel_type() const {
1075 return data_channel_type_;
1076}
1077
wu@webrtc.org91053e72013-08-10 07:18:04 +00001078bool WebRtcSession::IceRestartPending() const {
1079 return ice_restart_latch_->Get();
1080}
1081
1082void WebRtcSession::ResetIceRestartLatch() {
1083 ice_restart_latch_->Reset();
1084}
1085
1086void WebRtcSession::OnIdentityReady(talk_base::SSLIdentity* identity) {
1087 SetIdentity(identity);
1088}
1089
1090bool WebRtcSession::waiting_for_identity() const {
1091 return webrtc_session_desc_factory_->waiting_for_identity();
1092}
1093
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001094void WebRtcSession::SetIceConnectionState(
1095 PeerConnectionInterface::IceConnectionState state) {
1096 if (ice_connection_state_ == state) {
1097 return;
1098 }
1099
1100 // ASSERT that the requested transition is allowed. Note that
1101 // WebRtcSession does not implement "kIceConnectionClosed" (that is handled
1102 // within PeerConnection). This switch statement should compile away when
1103 // ASSERTs are disabled.
1104 switch (ice_connection_state_) {
1105 case PeerConnectionInterface::kIceConnectionNew:
1106 ASSERT(state == PeerConnectionInterface::kIceConnectionChecking);
1107 break;
1108 case PeerConnectionInterface::kIceConnectionChecking:
1109 ASSERT(state == PeerConnectionInterface::kIceConnectionFailed ||
1110 state == PeerConnectionInterface::kIceConnectionConnected);
1111 break;
1112 case PeerConnectionInterface::kIceConnectionConnected:
1113 ASSERT(state == PeerConnectionInterface::kIceConnectionDisconnected ||
1114 state == PeerConnectionInterface::kIceConnectionChecking ||
1115 state == PeerConnectionInterface::kIceConnectionCompleted);
1116 break;
1117 case PeerConnectionInterface::kIceConnectionCompleted:
1118 ASSERT(state == PeerConnectionInterface::kIceConnectionConnected ||
1119 state == PeerConnectionInterface::kIceConnectionDisconnected);
1120 break;
1121 case PeerConnectionInterface::kIceConnectionFailed:
1122 ASSERT(state == PeerConnectionInterface::kIceConnectionNew);
1123 break;
1124 case PeerConnectionInterface::kIceConnectionDisconnected:
1125 ASSERT(state == PeerConnectionInterface::kIceConnectionChecking ||
1126 state == PeerConnectionInterface::kIceConnectionConnected ||
1127 state == PeerConnectionInterface::kIceConnectionCompleted ||
1128 state == PeerConnectionInterface::kIceConnectionFailed);
1129 break;
1130 case PeerConnectionInterface::kIceConnectionClosed:
1131 ASSERT(false);
1132 break;
1133 default:
1134 ASSERT(false);
1135 break;
1136 }
1137
1138 ice_connection_state_ = state;
1139 if (ice_observer_) {
1140 ice_observer_->OnIceConnectionChange(ice_connection_state_);
1141 }
1142}
1143
1144void WebRtcSession::OnTransportRequestSignaling(
1145 cricket::Transport* transport) {
1146 ASSERT(signaling_thread()->IsCurrent());
1147 transport->OnSignalingReady();
1148 if (ice_observer_) {
1149 ice_observer_->OnIceGatheringChange(
1150 PeerConnectionInterface::kIceGatheringGathering);
1151 }
1152}
1153
1154void WebRtcSession::OnTransportConnecting(cricket::Transport* transport) {
1155 ASSERT(signaling_thread()->IsCurrent());
1156 // start monitoring for the write state of the transport.
1157 OnTransportWritable(transport);
1158}
1159
1160void WebRtcSession::OnTransportWritable(cricket::Transport* transport) {
1161 ASSERT(signaling_thread()->IsCurrent());
1162 // TODO(bemasc): Expose more API from Transport to detect when
1163 // candidate selection starts or stops, due to success or failure.
1164 if (transport->all_channels_writable()) {
mallinath@webrtc.org385857d2014-02-14 00:56:12 +00001165 // By the time |SignalTransportWritable| arrives, the excess channels may
1166 // already have been pruned, so that the Transport is Completed. The
1167 // specification requires that transitions from Checking to Completed pass
1168 // through Connected. This check enforces that requirement.
1169 // (Direct transitions from Connected and Disconnected to Completed are
1170 // allowed.)
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001171 if (ice_connection_state_ ==
mallinath@webrtc.org385857d2014-02-14 00:56:12 +00001172 PeerConnectionInterface::kIceConnectionChecking) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001173 SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected);
1174 }
mallinath@webrtc.org385857d2014-02-14 00:56:12 +00001175
1176 SetIceConnectionState(transport->completed() ?
1177 PeerConnectionInterface::kIceConnectionCompleted :
1178 PeerConnectionInterface::kIceConnectionConnected);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001179 } else if (transport->HasChannels()) {
1180 // If the current state is Connected or Completed, then there were writable
1181 // channels but now there are not, so the next state must be Disconnected.
1182 if (ice_connection_state_ ==
1183 PeerConnectionInterface::kIceConnectionConnected ||
1184 ice_connection_state_ ==
1185 PeerConnectionInterface::kIceConnectionCompleted) {
1186 SetIceConnectionState(
1187 PeerConnectionInterface::kIceConnectionDisconnected);
1188 }
1189 }
1190}
1191
mallinath@webrtc.org385857d2014-02-14 00:56:12 +00001192void WebRtcSession::OnTransportCompleted(cricket::Transport* transport) {
1193 ASSERT(signaling_thread()->IsCurrent());
1194 SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted);
1195}
1196
1197void WebRtcSession::OnTransportFailed(cricket::Transport* transport) {
1198 ASSERT(signaling_thread()->IsCurrent());
1199 SetIceConnectionState(PeerConnectionInterface::kIceConnectionFailed);
1200}
1201
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001202void WebRtcSession::OnTransportProxyCandidatesReady(
1203 cricket::TransportProxy* proxy, const cricket::Candidates& candidates) {
1204 ASSERT(signaling_thread()->IsCurrent());
1205 ProcessNewLocalCandidate(proxy->content_name(), candidates);
1206}
1207
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001208void WebRtcSession::OnCandidatesAllocationDone() {
1209 ASSERT(signaling_thread()->IsCurrent());
1210 if (ice_observer_) {
1211 ice_observer_->OnIceGatheringChange(
1212 PeerConnectionInterface::kIceGatheringComplete);
1213 ice_observer_->OnIceComplete();
1214 }
1215}
1216
1217// Enabling voice and video channel.
1218void WebRtcSession::EnableChannels() {
1219 if (voice_channel_ && !voice_channel_->enabled())
1220 voice_channel_->Enable(true);
1221
1222 if (video_channel_ && !video_channel_->enabled())
1223 video_channel_->Enable(true);
1224
1225 if (data_channel_.get() && !data_channel_->enabled())
1226 data_channel_->Enable(true);
1227}
1228
1229void WebRtcSession::ProcessNewLocalCandidate(
1230 const std::string& content_name,
1231 const cricket::Candidates& candidates) {
1232 int sdp_mline_index;
1233 if (!GetLocalCandidateMediaIndex(content_name, &sdp_mline_index)) {
1234 LOG(LS_ERROR) << "ProcessNewLocalCandidate: content name "
1235 << content_name << " not found";
1236 return;
1237 }
1238
1239 for (cricket::Candidates::const_iterator citer = candidates.begin();
1240 citer != candidates.end(); ++citer) {
1241 // Use content_name as the candidate media id.
1242 JsepIceCandidate candidate(content_name, sdp_mline_index, *citer);
1243 if (ice_observer_) {
1244 ice_observer_->OnIceCandidate(&candidate);
1245 }
1246 if (local_desc_) {
1247 local_desc_->AddCandidate(&candidate);
1248 }
1249 }
1250}
1251
1252// Returns the media index for a local ice candidate given the content name.
1253bool WebRtcSession::GetLocalCandidateMediaIndex(const std::string& content_name,
1254 int* sdp_mline_index) {
1255 if (!BaseSession::local_description() || !sdp_mline_index)
1256 return false;
1257
1258 bool content_found = false;
1259 const ContentInfos& contents = BaseSession::local_description()->contents();
1260 for (size_t index = 0; index < contents.size(); ++index) {
1261 if (contents[index].name == content_name) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001262 *sdp_mline_index = static_cast<int>(index);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001263 content_found = true;
1264 break;
1265 }
1266 }
1267 return content_found;
1268}
1269
1270bool WebRtcSession::UseCandidatesInSessionDescription(
1271 const SessionDescriptionInterface* remote_desc) {
1272 if (!remote_desc)
1273 return true;
1274 bool ret = true;
1275 for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) {
1276 const IceCandidateCollection* candidates = remote_desc->candidates(m);
1277 for (size_t n = 0; n < candidates->count(); ++n) {
1278 ret = UseCandidate(candidates->at(n));
1279 if (!ret)
1280 break;
1281 }
1282 }
1283 return ret;
1284}
1285
1286bool WebRtcSession::UseCandidate(
1287 const IceCandidateInterface* candidate) {
1288
1289 size_t mediacontent_index = static_cast<size_t>(candidate->sdp_mline_index());
1290 size_t remote_content_size =
1291 BaseSession::remote_description()->contents().size();
1292 if (mediacontent_index >= remote_content_size) {
1293 LOG(LS_ERROR)
1294 << "UseRemoteCandidateInSession: Invalid candidate media index.";
1295 return false;
1296 }
1297
1298 cricket::ContentInfo content =
1299 BaseSession::remote_description()->contents()[mediacontent_index];
1300 std::vector<cricket::Candidate> candidates;
1301 candidates.push_back(candidate->candidate());
1302 // Invoking BaseSession method to handle remote candidates.
1303 std::string error;
1304 if (OnRemoteCandidates(content.name, candidates, &error)) {
1305 // Candidates successfully submitted for checking.
1306 if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew ||
1307 ice_connection_state_ ==
1308 PeerConnectionInterface::kIceConnectionDisconnected) {
1309 // If state is New, then the session has just gotten its first remote ICE
1310 // candidates, so go to Checking.
1311 // If state is Disconnected, the session is re-using old candidates or
1312 // receiving additional ones, so go to Checking.
1313 // If state is Connected, stay Connected.
1314 // TODO(bemasc): If state is Connected, and the new candidates are for a
1315 // newly added transport, then the state actually _should_ move to
1316 // checking. Add a way to distinguish that case.
1317 SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking);
1318 }
1319 // TODO(bemasc): If state is Completed, go back to Connected.
1320 } else {
1321 LOG(LS_WARNING) << error;
1322 }
1323 return true;
1324}
1325
1326void WebRtcSession::RemoveUnusedChannelsAndTransports(
1327 const SessionDescription* desc) {
1328 const cricket::ContentInfo* voice_info =
1329 cricket::GetFirstAudioContent(desc);
1330 if ((!voice_info || voice_info->rejected) && voice_channel_) {
1331 mediastream_signaling_->OnAudioChannelClose();
1332 SignalVoiceChannelDestroyed();
1333 const std::string content_name = voice_channel_->content_name();
1334 channel_manager_->DestroyVoiceChannel(voice_channel_.release());
1335 DestroyTransportProxy(content_name);
1336 }
1337
1338 const cricket::ContentInfo* video_info =
1339 cricket::GetFirstVideoContent(desc);
1340 if ((!video_info || video_info->rejected) && video_channel_) {
1341 mediastream_signaling_->OnVideoChannelClose();
1342 SignalVideoChannelDestroyed();
1343 const std::string content_name = video_channel_->content_name();
1344 channel_manager_->DestroyVideoChannel(video_channel_.release());
1345 DestroyTransportProxy(content_name);
1346 }
1347
1348 const cricket::ContentInfo* data_info =
1349 cricket::GetFirstDataContent(desc);
1350 if ((!data_info || data_info->rejected) && data_channel_) {
1351 mediastream_signaling_->OnDataChannelClose();
1352 SignalDataChannelDestroyed();
1353 const std::string content_name = data_channel_->content_name();
1354 channel_manager_->DestroyDataChannel(data_channel_.release());
1355 DestroyTransportProxy(content_name);
1356 }
1357}
1358
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001359// TODO(mallinath) - Add a correct error code if the channels are not creatued
1360// due to BUNDLE is enabled but rtcp-mux is disabled.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001361bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
1362 // Disabling the BUNDLE flag in PortAllocator if offer disabled it.
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001363 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1364 if (state() == STATE_INIT && !bundle_enabled) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001365 port_allocator()->set_flags(port_allocator()->flags() &
1366 ~cricket::PORTALLOCATOR_ENABLE_BUNDLE);
1367 }
1368
1369 // Creating the media channels and transport proxies.
1370 const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(desc);
1371 if (voice && !voice->rejected && !voice_channel_) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001372 if (!CreateVoiceChannel(voice)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001373 LOG(LS_ERROR) << "Failed to create voice channel.";
1374 return false;
1375 }
1376 }
1377
1378 const cricket::ContentInfo* video = cricket::GetFirstVideoContent(desc);
1379 if (video && !video->rejected && !video_channel_) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001380 if (!CreateVideoChannel(video)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001381 LOG(LS_ERROR) << "Failed to create video channel.";
1382 return false;
1383 }
1384 }
1385
1386 const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc);
1387 if (data_channel_type_ != cricket::DCT_NONE &&
1388 data && !data->rejected && !data_channel_.get()) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001389 if (!CreateDataChannel(data)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001390 LOG(LS_ERROR) << "Failed to create data channel.";
1391 return false;
1392 }
1393 }
1394
1395 return true;
1396}
1397
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001398bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001399 voice_channel_.reset(channel_manager_->CreateVoiceChannel(
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001400 this, content->name, true));
wu@webrtc.orgde305012013-10-31 15:40:38 +00001401 if (!voice_channel_.get())
1402 return false;
1403
1404 if (dscp_enabled_) {
1405 cricket::AudioOptions options;
1406 options.dscp.Set(true);
1407 voice_channel_->SetChannelOptions(options);
1408 }
1409 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001410}
1411
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001412bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001413 video_channel_.reset(channel_manager_->CreateVideoChannel(
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001414 this, content->name, true, voice_channel_.get()));
wu@webrtc.orgde305012013-10-31 15:40:38 +00001415 if (!video_channel_.get())
1416 return false;
1417
1418 if (dscp_enabled_) {
1419 cricket::VideoOptions options;
1420 options.dscp.Set(true);
1421 video_channel_->SetChannelOptions(options);
1422 }
1423 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001424}
1425
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001426bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001427 bool sctp = (data_channel_type_ == cricket::DCT_SCTP);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001428 data_channel_.reset(channel_manager_->CreateDataChannel(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001429 this, content->name, !sctp, data_channel_type_));
wu@webrtc.org91053e72013-08-10 07:18:04 +00001430 if (!data_channel_.get()) {
1431 return false;
1432 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001433 if (sctp) {
1434 mediastream_signaling_->OnDataTransportCreatedForSctp();
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001435 data_channel_->SignalDataReceived.connect(
1436 this, &WebRtcSession::OnDataChannelMessageReceived);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001437 }
wu@webrtc.org91053e72013-08-10 07:18:04 +00001438 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001439}
1440
1441void WebRtcSession::CopySavedCandidates(
1442 SessionDescriptionInterface* dest_desc) {
1443 if (!dest_desc) {
1444 ASSERT(false);
1445 return;
1446 }
1447 for (size_t i = 0; i < saved_candidates_.size(); ++i) {
1448 dest_desc->AddCandidate(saved_candidates_[i]);
1449 delete saved_candidates_[i];
1450 }
1451 saved_candidates_.clear();
1452}
1453
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001454void WebRtcSession::OnDataChannelMessageReceived(
1455 cricket::DataChannel* channel,
1456 const cricket::ReceiveDataParams& params,
1457 const talk_base::Buffer& payload) {
wu@webrtc.org1d1ffc92013-10-16 18:12:02 +00001458 ASSERT(data_channel_type_ == cricket::DCT_SCTP);
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001459 if (params.type == cricket::DMT_CONTROL &&
1460 mediastream_signaling_->IsSctpSidAvailable(params.ssrc)) {
1461 // Received CONTROL on unused sid, process as an OPEN message.
1462 mediastream_signaling_->AddDataChannelFromOpenMessage(params, payload);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001463 }
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001464 // otherwise ignore the message.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001465}
1466
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001467// Returns false if bundle is enabled and rtcp_mux is disabled.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001468bool WebRtcSession::ValidateBundleSettings(const SessionDescription* desc) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001469 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1470 if (!bundle_enabled)
1471 return true;
1472
1473 const cricket::ContentGroup* bundle_group =
1474 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
1475 ASSERT(bundle_group != NULL);
1476
1477 const cricket::ContentInfos& contents = desc->contents();
1478 for (cricket::ContentInfos::const_iterator citer = contents.begin();
1479 citer != contents.end(); ++citer) {
1480 const cricket::ContentInfo* content = (&*citer);
1481 ASSERT(content != NULL);
1482 if (bundle_group->HasContentName(content->name) &&
1483 !content->rejected && content->type == cricket::NS_JINGLE_RTP) {
1484 if (!HasRtcpMuxEnabled(content))
1485 return false;
1486 }
1487 }
1488 // RTCP-MUX is enabled in all the contents.
1489 return true;
1490}
1491
1492bool WebRtcSession::HasRtcpMuxEnabled(
1493 const cricket::ContentInfo* content) {
1494 const cricket::MediaContentDescription* description =
1495 static_cast<cricket::MediaContentDescription*>(content->description);
1496 return description->rtcp_mux();
1497}
1498
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001499bool WebRtcSession::ValidateSessionDescription(
1500 const SessionDescriptionInterface* sdesc,
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001501 cricket::ContentSource source, std::string* err_desc) {
1502 std::string type;
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001503 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001504 return BadSdp(source, type, GetSessionErrorMsg(), err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001505 }
1506
1507 if (!sdesc || !sdesc->description()) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001508 return BadSdp(source, type, kInvalidSdp, err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001509 }
1510
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001511 type = sdesc->type();
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001512 Action action = GetAction(sdesc->type());
1513 if (source == cricket::CS_LOCAL) {
1514 if (!ExpectSetLocalDescription(action))
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001515 return BadLocalSdp(type, BadStateErrMsg(state()), err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001516 } else {
1517 if (!ExpectSetRemoteDescription(action))
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001518 return BadRemoteSdp(type, BadStateErrMsg(state()), err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001519 }
1520
1521 // Verify crypto settings.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +00001522 std::string crypto_error;
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001523 if ((webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED ||
1524 dtls_enabled_) &&
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +00001525 !VerifyCrypto(sdesc->description(), dtls_enabled_, &crypto_error)) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001526 return BadSdp(source, type, crypto_error, err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001527 }
1528
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001529 // Verify ice-ufrag and ice-pwd.
1530 if (!VerifyIceUfragPwdPresent(sdesc->description())) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001531 return BadSdp(source, type, kSdpWithoutIceUfragPwd, err_desc);
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001532 }
1533
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001534 if (!ValidateBundleSettings(sdesc->description())) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001535 return BadSdp(source, type, kBundleWithoutRtcpMux, err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001536 }
1537
1538 // Verify m-lines in Answer when compared against Offer.
1539 if (action == kAnswer) {
1540 const cricket::SessionDescription* offer_desc =
1541 (source == cricket::CS_LOCAL) ? remote_description()->description() :
1542 local_description()->description();
1543 if (!VerifyMediaDescriptions(sdesc->description(), offer_desc)) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001544 return BadAnswerSdp(source, kMlineMismatch, err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001545 }
1546 }
1547
1548 return true;
1549}
1550
1551bool WebRtcSession::ExpectSetLocalDescription(Action action) {
1552 return ((action == kOffer && state() == STATE_INIT) ||
1553 // update local offer
1554 (action == kOffer && state() == STATE_SENTINITIATE) ||
1555 // update the current ongoing session.
1556 (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
1557 (action == kOffer && state() == STATE_SENTACCEPT) ||
1558 (action == kOffer && state() == STATE_INPROGRESS) ||
1559 // accept remote offer
1560 (action == kAnswer && state() == STATE_RECEIVEDINITIATE) ||
1561 (action == kAnswer && state() == STATE_SENTPRACCEPT) ||
1562 (action == kPrAnswer && state() == STATE_RECEIVEDINITIATE) ||
1563 (action == kPrAnswer && state() == STATE_SENTPRACCEPT));
1564}
1565
1566bool WebRtcSession::ExpectSetRemoteDescription(Action action) {
1567 return ((action == kOffer && state() == STATE_INIT) ||
1568 // update remote offer
1569 (action == kOffer && state() == STATE_RECEIVEDINITIATE) ||
1570 // update the current ongoing session
1571 (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
1572 (action == kOffer && state() == STATE_SENTACCEPT) ||
1573 (action == kOffer && state() == STATE_INPROGRESS) ||
1574 // accept local offer
1575 (action == kAnswer && state() == STATE_SENTINITIATE) ||
1576 (action == kAnswer && state() == STATE_RECEIVEDPRACCEPT) ||
1577 (action == kPrAnswer && state() == STATE_SENTINITIATE) ||
1578 (action == kPrAnswer && state() == STATE_RECEIVEDPRACCEPT));
1579}
1580
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001581std::string WebRtcSession::GetSessionErrorMsg() {
1582 std::ostringstream desc;
1583 desc << kSessionError << GetErrorCodeString(error()) << ". ";
1584 desc << kSessionErrorDesc << error_desc() << ".";
1585 return desc.str();
1586}
1587
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001588} // namespace webrtc