blob: 8d985835b7190c7579361c7bf1dc6479b79f1070 [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());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001162 if (transport->all_channels_writable()) {
henrike@webrtc.org05376342014-03-10 15:53:12 +00001163 SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001164 } else if (transport->HasChannels()) {
1165 // If the current state is Connected or Completed, then there were writable
1166 // channels but now there are not, so the next state must be Disconnected.
1167 if (ice_connection_state_ ==
1168 PeerConnectionInterface::kIceConnectionConnected ||
1169 ice_connection_state_ ==
1170 PeerConnectionInterface::kIceConnectionCompleted) {
1171 SetIceConnectionState(
1172 PeerConnectionInterface::kIceConnectionDisconnected);
1173 }
1174 }
1175}
1176
mallinath@webrtc.org385857d2014-02-14 00:56:12 +00001177void WebRtcSession::OnTransportCompleted(cricket::Transport* transport) {
1178 ASSERT(signaling_thread()->IsCurrent());
1179 SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted);
1180}
1181
1182void WebRtcSession::OnTransportFailed(cricket::Transport* transport) {
1183 ASSERT(signaling_thread()->IsCurrent());
1184 SetIceConnectionState(PeerConnectionInterface::kIceConnectionFailed);
1185}
1186
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001187void WebRtcSession::OnTransportProxyCandidatesReady(
1188 cricket::TransportProxy* proxy, const cricket::Candidates& candidates) {
1189 ASSERT(signaling_thread()->IsCurrent());
1190 ProcessNewLocalCandidate(proxy->content_name(), candidates);
1191}
1192
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001193void WebRtcSession::OnCandidatesAllocationDone() {
1194 ASSERT(signaling_thread()->IsCurrent());
1195 if (ice_observer_) {
1196 ice_observer_->OnIceGatheringChange(
1197 PeerConnectionInterface::kIceGatheringComplete);
1198 ice_observer_->OnIceComplete();
1199 }
1200}
1201
1202// Enabling voice and video channel.
1203void WebRtcSession::EnableChannels() {
1204 if (voice_channel_ && !voice_channel_->enabled())
1205 voice_channel_->Enable(true);
1206
1207 if (video_channel_ && !video_channel_->enabled())
1208 video_channel_->Enable(true);
1209
1210 if (data_channel_.get() && !data_channel_->enabled())
1211 data_channel_->Enable(true);
1212}
1213
1214void WebRtcSession::ProcessNewLocalCandidate(
1215 const std::string& content_name,
1216 const cricket::Candidates& candidates) {
1217 int sdp_mline_index;
1218 if (!GetLocalCandidateMediaIndex(content_name, &sdp_mline_index)) {
1219 LOG(LS_ERROR) << "ProcessNewLocalCandidate: content name "
1220 << content_name << " not found";
1221 return;
1222 }
1223
1224 for (cricket::Candidates::const_iterator citer = candidates.begin();
1225 citer != candidates.end(); ++citer) {
1226 // Use content_name as the candidate media id.
1227 JsepIceCandidate candidate(content_name, sdp_mline_index, *citer);
1228 if (ice_observer_) {
1229 ice_observer_->OnIceCandidate(&candidate);
1230 }
1231 if (local_desc_) {
1232 local_desc_->AddCandidate(&candidate);
1233 }
1234 }
1235}
1236
1237// Returns the media index for a local ice candidate given the content name.
1238bool WebRtcSession::GetLocalCandidateMediaIndex(const std::string& content_name,
1239 int* sdp_mline_index) {
1240 if (!BaseSession::local_description() || !sdp_mline_index)
1241 return false;
1242
1243 bool content_found = false;
1244 const ContentInfos& contents = BaseSession::local_description()->contents();
1245 for (size_t index = 0; index < contents.size(); ++index) {
1246 if (contents[index].name == content_name) {
henrike@webrtc.org28654cb2013-07-22 21:07:49 +00001247 *sdp_mline_index = static_cast<int>(index);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001248 content_found = true;
1249 break;
1250 }
1251 }
1252 return content_found;
1253}
1254
1255bool WebRtcSession::UseCandidatesInSessionDescription(
1256 const SessionDescriptionInterface* remote_desc) {
1257 if (!remote_desc)
1258 return true;
1259 bool ret = true;
1260 for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) {
1261 const IceCandidateCollection* candidates = remote_desc->candidates(m);
1262 for (size_t n = 0; n < candidates->count(); ++n) {
1263 ret = UseCandidate(candidates->at(n));
1264 if (!ret)
1265 break;
1266 }
1267 }
1268 return ret;
1269}
1270
1271bool WebRtcSession::UseCandidate(
1272 const IceCandidateInterface* candidate) {
1273
1274 size_t mediacontent_index = static_cast<size_t>(candidate->sdp_mline_index());
1275 size_t remote_content_size =
1276 BaseSession::remote_description()->contents().size();
1277 if (mediacontent_index >= remote_content_size) {
1278 LOG(LS_ERROR)
1279 << "UseRemoteCandidateInSession: Invalid candidate media index.";
1280 return false;
1281 }
1282
1283 cricket::ContentInfo content =
1284 BaseSession::remote_description()->contents()[mediacontent_index];
1285 std::vector<cricket::Candidate> candidates;
1286 candidates.push_back(candidate->candidate());
1287 // Invoking BaseSession method to handle remote candidates.
1288 std::string error;
1289 if (OnRemoteCandidates(content.name, candidates, &error)) {
1290 // Candidates successfully submitted for checking.
1291 if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew ||
1292 ice_connection_state_ ==
1293 PeerConnectionInterface::kIceConnectionDisconnected) {
1294 // If state is New, then the session has just gotten its first remote ICE
1295 // candidates, so go to Checking.
1296 // If state is Disconnected, the session is re-using old candidates or
1297 // receiving additional ones, so go to Checking.
1298 // If state is Connected, stay Connected.
1299 // TODO(bemasc): If state is Connected, and the new candidates are for a
1300 // newly added transport, then the state actually _should_ move to
1301 // checking. Add a way to distinguish that case.
1302 SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking);
1303 }
1304 // TODO(bemasc): If state is Completed, go back to Connected.
1305 } else {
1306 LOG(LS_WARNING) << error;
1307 }
1308 return true;
1309}
1310
1311void WebRtcSession::RemoveUnusedChannelsAndTransports(
1312 const SessionDescription* desc) {
1313 const cricket::ContentInfo* voice_info =
1314 cricket::GetFirstAudioContent(desc);
1315 if ((!voice_info || voice_info->rejected) && voice_channel_) {
1316 mediastream_signaling_->OnAudioChannelClose();
1317 SignalVoiceChannelDestroyed();
1318 const std::string content_name = voice_channel_->content_name();
1319 channel_manager_->DestroyVoiceChannel(voice_channel_.release());
1320 DestroyTransportProxy(content_name);
1321 }
1322
1323 const cricket::ContentInfo* video_info =
1324 cricket::GetFirstVideoContent(desc);
1325 if ((!video_info || video_info->rejected) && video_channel_) {
1326 mediastream_signaling_->OnVideoChannelClose();
1327 SignalVideoChannelDestroyed();
1328 const std::string content_name = video_channel_->content_name();
1329 channel_manager_->DestroyVideoChannel(video_channel_.release());
1330 DestroyTransportProxy(content_name);
1331 }
1332
1333 const cricket::ContentInfo* data_info =
1334 cricket::GetFirstDataContent(desc);
1335 if ((!data_info || data_info->rejected) && data_channel_) {
1336 mediastream_signaling_->OnDataChannelClose();
1337 SignalDataChannelDestroyed();
1338 const std::string content_name = data_channel_->content_name();
1339 channel_manager_->DestroyDataChannel(data_channel_.release());
1340 DestroyTransportProxy(content_name);
1341 }
1342}
1343
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001344// TODO(mallinath) - Add a correct error code if the channels are not creatued
1345// due to BUNDLE is enabled but rtcp-mux is disabled.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001346bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
1347 // Disabling the BUNDLE flag in PortAllocator if offer disabled it.
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001348 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1349 if (state() == STATE_INIT && !bundle_enabled) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001350 port_allocator()->set_flags(port_allocator()->flags() &
1351 ~cricket::PORTALLOCATOR_ENABLE_BUNDLE);
1352 }
1353
1354 // Creating the media channels and transport proxies.
1355 const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(desc);
1356 if (voice && !voice->rejected && !voice_channel_) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001357 if (!CreateVoiceChannel(voice)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001358 LOG(LS_ERROR) << "Failed to create voice channel.";
1359 return false;
1360 }
1361 }
1362
1363 const cricket::ContentInfo* video = cricket::GetFirstVideoContent(desc);
1364 if (video && !video->rejected && !video_channel_) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001365 if (!CreateVideoChannel(video)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001366 LOG(LS_ERROR) << "Failed to create video channel.";
1367 return false;
1368 }
1369 }
1370
1371 const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc);
1372 if (data_channel_type_ != cricket::DCT_NONE &&
1373 data && !data->rejected && !data_channel_.get()) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001374 if (!CreateDataChannel(data)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001375 LOG(LS_ERROR) << "Failed to create data channel.";
1376 return false;
1377 }
1378 }
1379
1380 return true;
1381}
1382
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001383bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001384 voice_channel_.reset(channel_manager_->CreateVoiceChannel(
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001385 this, content->name, true));
wu@webrtc.orgde305012013-10-31 15:40:38 +00001386 if (!voice_channel_.get())
1387 return false;
1388
1389 if (dscp_enabled_) {
1390 cricket::AudioOptions options;
1391 options.dscp.Set(true);
1392 voice_channel_->SetChannelOptions(options);
1393 }
1394 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001395}
1396
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001397bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001398 video_channel_.reset(channel_manager_->CreateVideoChannel(
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001399 this, content->name, true, voice_channel_.get()));
wu@webrtc.orgde305012013-10-31 15:40:38 +00001400 if (!video_channel_.get())
1401 return false;
1402
1403 if (dscp_enabled_) {
1404 cricket::VideoOptions options;
1405 options.dscp.Set(true);
1406 video_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::CreateDataChannel(const cricket::ContentInfo* content) {
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001412 bool sctp = (data_channel_type_ == cricket::DCT_SCTP);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001413 data_channel_.reset(channel_manager_->CreateDataChannel(
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001414 this, content->name, !sctp, data_channel_type_));
wu@webrtc.org91053e72013-08-10 07:18:04 +00001415 if (!data_channel_.get()) {
1416 return false;
1417 }
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001418 if (sctp) {
1419 mediastream_signaling_->OnDataTransportCreatedForSctp();
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001420 data_channel_->SignalDataReceived.connect(
1421 this, &WebRtcSession::OnDataChannelMessageReceived);
wu@webrtc.orgcecfd182013-10-30 05:18:12 +00001422 }
wu@webrtc.org91053e72013-08-10 07:18:04 +00001423 return true;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001424}
1425
1426void WebRtcSession::CopySavedCandidates(
1427 SessionDescriptionInterface* dest_desc) {
1428 if (!dest_desc) {
1429 ASSERT(false);
1430 return;
1431 }
1432 for (size_t i = 0; i < saved_candidates_.size(); ++i) {
1433 dest_desc->AddCandidate(saved_candidates_[i]);
1434 delete saved_candidates_[i];
1435 }
1436 saved_candidates_.clear();
1437}
1438
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001439void WebRtcSession::OnDataChannelMessageReceived(
1440 cricket::DataChannel* channel,
1441 const cricket::ReceiveDataParams& params,
1442 const talk_base::Buffer& payload) {
wu@webrtc.org1d1ffc92013-10-16 18:12:02 +00001443 ASSERT(data_channel_type_ == cricket::DCT_SCTP);
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001444 if (params.type == cricket::DMT_CONTROL &&
1445 mediastream_signaling_->IsSctpSidAvailable(params.ssrc)) {
1446 // Received CONTROL on unused sid, process as an OPEN message.
1447 mediastream_signaling_->AddDataChannelFromOpenMessage(params, payload);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001448 }
henrika@webrtc.orgaebb1ad2014-01-14 10:00:58 +00001449 // otherwise ignore the message.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001450}
1451
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001452// Returns false if bundle is enabled and rtcp_mux is disabled.
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001453bool WebRtcSession::ValidateBundleSettings(const SessionDescription* desc) {
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001454 bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1455 if (!bundle_enabled)
1456 return true;
1457
1458 const cricket::ContentGroup* bundle_group =
1459 desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
1460 ASSERT(bundle_group != NULL);
1461
1462 const cricket::ContentInfos& contents = desc->contents();
1463 for (cricket::ContentInfos::const_iterator citer = contents.begin();
1464 citer != contents.end(); ++citer) {
1465 const cricket::ContentInfo* content = (&*citer);
1466 ASSERT(content != NULL);
1467 if (bundle_group->HasContentName(content->name) &&
1468 !content->rejected && content->type == cricket::NS_JINGLE_RTP) {
1469 if (!HasRtcpMuxEnabled(content))
1470 return false;
1471 }
1472 }
1473 // RTCP-MUX is enabled in all the contents.
1474 return true;
1475}
1476
1477bool WebRtcSession::HasRtcpMuxEnabled(
1478 const cricket::ContentInfo* content) {
1479 const cricket::MediaContentDescription* description =
1480 static_cast<cricket::MediaContentDescription*>(content->description);
1481 return description->rtcp_mux();
1482}
1483
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001484bool WebRtcSession::ValidateSessionDescription(
1485 const SessionDescriptionInterface* sdesc,
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001486 cricket::ContentSource source, std::string* err_desc) {
1487 std::string type;
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001488 if (error() != cricket::BaseSession::ERROR_NONE) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001489 return BadSdp(source, type, GetSessionErrorMsg(), err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001490 }
1491
1492 if (!sdesc || !sdesc->description()) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001493 return BadSdp(source, type, kInvalidSdp, err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001494 }
1495
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001496 type = sdesc->type();
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001497 Action action = GetAction(sdesc->type());
1498 if (source == cricket::CS_LOCAL) {
1499 if (!ExpectSetLocalDescription(action))
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001500 return BadLocalSdp(type, BadStateErrMsg(state()), err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001501 } else {
1502 if (!ExpectSetRemoteDescription(action))
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001503 return BadRemoteSdp(type, BadStateErrMsg(state()), err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001504 }
1505
1506 // Verify crypto settings.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +00001507 std::string crypto_error;
henrike@webrtc.orgb90991d2014-03-04 19:54:57 +00001508 if ((webrtc_session_desc_factory_->SdesPolicy() == cricket::SEC_REQUIRED ||
1509 dtls_enabled_) &&
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +00001510 !VerifyCrypto(sdesc->description(), dtls_enabled_, &crypto_error)) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001511 return BadSdp(source, type, crypto_error, err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001512 }
1513
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001514 // Verify ice-ufrag and ice-pwd.
1515 if (!VerifyIceUfragPwdPresent(sdesc->description())) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001516 return BadSdp(source, type, kSdpWithoutIceUfragPwd, err_desc);
mallinath@webrtc.org19f27e62013-10-13 17:18:27 +00001517 }
1518
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001519 if (!ValidateBundleSettings(sdesc->description())) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001520 return BadSdp(source, type, kBundleWithoutRtcpMux, err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001521 }
1522
1523 // Verify m-lines in Answer when compared against Offer.
1524 if (action == kAnswer) {
1525 const cricket::SessionDescription* offer_desc =
1526 (source == cricket::CS_LOCAL) ? remote_description()->description() :
1527 local_description()->description();
1528 if (!VerifyMediaDescriptions(sdesc->description(), offer_desc)) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001529 return BadAnswerSdp(source, kMlineMismatch, err_desc);
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001530 }
1531 }
1532
1533 return true;
1534}
1535
1536bool WebRtcSession::ExpectSetLocalDescription(Action action) {
1537 return ((action == kOffer && state() == STATE_INIT) ||
1538 // update local offer
1539 (action == kOffer && state() == STATE_SENTINITIATE) ||
1540 // update the current ongoing session.
1541 (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
1542 (action == kOffer && state() == STATE_SENTACCEPT) ||
1543 (action == kOffer && state() == STATE_INPROGRESS) ||
1544 // accept remote offer
1545 (action == kAnswer && state() == STATE_RECEIVEDINITIATE) ||
1546 (action == kAnswer && state() == STATE_SENTPRACCEPT) ||
1547 (action == kPrAnswer && state() == STATE_RECEIVEDINITIATE) ||
1548 (action == kPrAnswer && state() == STATE_SENTPRACCEPT));
1549}
1550
1551bool WebRtcSession::ExpectSetRemoteDescription(Action action) {
1552 return ((action == kOffer && state() == STATE_INIT) ||
1553 // update remote offer
1554 (action == kOffer && state() == STATE_RECEIVEDINITIATE) ||
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 local offer
1560 (action == kAnswer && state() == STATE_SENTINITIATE) ||
1561 (action == kAnswer && state() == STATE_RECEIVEDPRACCEPT) ||
1562 (action == kPrAnswer && state() == STATE_SENTINITIATE) ||
1563 (action == kPrAnswer && state() == STATE_RECEIVEDPRACCEPT));
1564}
1565
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001566std::string WebRtcSession::GetSessionErrorMsg() {
1567 std::ostringstream desc;
1568 desc << kSessionError << GetErrorCodeString(error()) << ". ";
1569 desc << kSessionErrorDesc << error_desc() << ".";
1570 return desc.str();
1571}
1572
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001573} // namespace webrtc