Note: We no longer publish the latest version of our code here. We primarily use a kumc-bmi github organization. The heron ETL repository, in particular, is not public. Peers in the informatics community should see MultiSiteDev for details on requesting access.

source: webrtc/talk/app/webrtc/webrtcsession.cc @ 0:4bda6873e34c

pub_scrub_3792 tip
Last change on this file since 0:4bda6873e34c was 0:4bda6873e34c, checked in by Michael Prittie <mprittie@…>, 6 years ago

Scrubbed password for publication.

File size: 54.6 KB
Line 
1/*
2 * libjingle
3 * Copyright 2012, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *  1. Redistributions of source code must retain the above copyright notice,
9 *     this list of conditions and the following disclaimer.
10 *  2. Redistributions in binary form must reproduce the above copyright notice,
11 *     this list of conditions and the following disclaimer in the documentation
12 *     and/or other materials provided with the distribution.
13 *  3. The name of the author may not be used to endorse or promote products
14 *     derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/app/webrtc/webrtcsession.h"
29
30#include <algorithm>
31#include <climits>
32#include <vector>
33
34#include "talk/app/webrtc/jsepicecandidate.h"
35#include "talk/app/webrtc/jsepsessiondescription.h"
36#include "talk/app/webrtc/mediaconstraintsinterface.h"
37#include "talk/app/webrtc/mediastreamsignaling.h"
38#include "talk/app/webrtc/peerconnectioninterface.h"
39#include "talk/app/webrtc/webrtcsessiondescriptionfactory.h"
40#include "talk/base/helpers.h"
41#include "talk/base/logging.h"
42#include "talk/base/stringencode.h"
43#include "talk/media/base/constants.h"
44#include "talk/media/base/videocapturer.h"
45#include "talk/session/media/channel.h"
46#include "talk/session/media/channelmanager.h"
47#include "talk/session/media/mediasession.h"
48
49using cricket::ContentInfo;
50using cricket::ContentInfos;
51using cricket::MediaContentDescription;
52using cricket::SessionDescription;
53using cricket::TransportInfo;
54
55namespace webrtc {
56
57const char MediaConstraintsInterface::kInternalConstraintPrefix[] = "internal";
58
59// Supported MediaConstraints.
60// DSCP constraints.
61const char MediaConstraintsInterface::kEnableDscp[] = "googDscp";
62// DTLS-SRTP pseudo-constraints.
63const char MediaConstraintsInterface::kEnableDtlsSrtp[] =
64    "DtlsSrtpKeyAgreement";
65// DataChannel pseudo constraints.
66const char MediaConstraintsInterface::kEnableRtpDataChannels[] =
67    "RtpDataChannels";
68// This constraint is for internal use only, representing the Chrome command
69// line flag. So it is prefixed with kInternalConstraintPrefix so JS values
70// will be removed.
71const char MediaConstraintsInterface::kEnableSctpDataChannels[] =
72    "deprecatedSctpDataChannels";
73
74// Error messages
75const char kSetLocalSdpFailed[] = "SetLocalDescription failed: ";
76const char kSetRemoteSdpFailed[] = "SetRemoteDescription failed: ";
77const char kCreateChannelFailed[] = "Failed to create channels.";
78const char kBundleWithoutRtcpMux[] = "RTCP-MUX must be enabled when BUNDLE "
79                                     "is enabled.";
80const char kInvalidCandidates[] = "Description contains invalid candidates.";
81const char kInvalidSdp[] = "Invalid session description.";
82const char kMlineMismatch[] =
83    "Offer and answer descriptions m-lines are not matching. "
84    "Rejecting answer.";
85const char kSdpWithoutCrypto[] = "Called with a SDP without crypto enabled.";
86const char kSdpWithoutSdesAndDtlsDisabled[] =
87    "Called with an SDP without SDES crypto and DTLS disabled locally.";
88const char kSdpWithoutIceUfragPwd[] =
89    "Called with an SDP without ice-ufrag and ice-pwd.";
90const char kSessionError[] = "Session error code: ";
91const char kUpdateStateFailed[] = "Failed to update session state: ";
92const char kPushDownOfferTDFailed[] =
93    "Failed to push down offer transport description.";
94const char kPushDownPranswerTDFailed[] =
95    "Failed to push down pranswer transport description.";
96const char kPushDownAnswerTDFailed[] =
97    "Failed to push down answer transport description.";
98
99// Compares |answer| against |offer|. Comparision is done
100// for number of m-lines in answer against offer. If matches true will be
101// returned otherwise false.
102static bool VerifyMediaDescriptions(
103    const SessionDescription* answer, const SessionDescription* offer) {
104  if (offer->contents().size() != answer->contents().size())
105    return false;
106
107  for (size_t i = 0; i < offer->contents().size(); ++i) {
108    if ((offer->contents()[i].name) != answer->contents()[i].name) {
109      return false;
110    }
111  }
112  return true;
113}
114
115// Checks that each non-rejected content has SDES crypto keys or a DTLS
116// fingerprint. Mismatches, such as replying with a DTLS fingerprint to SDES
117// keys, will be caught in Transport negotiation, and backstopped by Channel's
118// |secure_required| check.
119static bool VerifyCrypto(const SessionDescription* desc,
120                         bool dtls_enabled,
121                         std::string* error) {
122  const ContentInfos& contents = desc->contents();
123  for (size_t index = 0; index < contents.size(); ++index) {
124    const ContentInfo* cinfo = &contents[index];
125    if (cinfo->rejected) {
126      continue;
127    }
128
129    // If the content isn't rejected, crypto must be present.
130    const MediaContentDescription* media =
131        static_cast<const MediaContentDescription*>(cinfo->description);
132    const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name);
133    if (!media || !tinfo) {
134      // Something is not right.
135      LOG(LS_ERROR) << kInvalidSdp;
136      *error = kInvalidSdp;
137      return false;
138    }
139    if (media->cryptos().empty()) {
140      if (!tinfo->description.identity_fingerprint) {
141        // Crypto must be supplied.
142        LOG(LS_WARNING) << "Session description must have SDES or DTLS-SRTP.";
143        *error = kSdpWithoutCrypto;
144        return false;
145      }
146      if (!dtls_enabled) {
147        LOG(LS_WARNING) <<
148            "Session description must have SDES when DTLS disabled.";
149        *error = kSdpWithoutSdesAndDtlsDisabled;
150        return false;
151      }
152    }
153  }
154
155  return true;
156}
157
158// Checks that each non-rejected content has ice-ufrag and ice-pwd set.
159static bool VerifyIceUfragPwdPresent(const SessionDescription* desc) {
160  const ContentInfos& contents = desc->contents();
161  for (size_t index = 0; index < contents.size(); ++index) {
162    const ContentInfo* cinfo = &contents[index];
163    if (cinfo->rejected) {
164      continue;
165    }
166
167    // If the content isn't rejected, ice-ufrag and ice-pwd must be present.
168    const TransportInfo* tinfo = desc->GetTransportInfoByName(cinfo->name);
169    if (!tinfo) {
170      // Something is not right.
171      LOG(LS_ERROR) << kInvalidSdp;
172      return false;
173    }
174    if (tinfo->description.ice_ufrag.empty() ||
175        tinfo->description.ice_pwd.empty()) {
176      LOG(LS_ERROR) << "Session description must have ice ufrag and pwd.";
177      return false;
178    }
179  }
180  return true;
181}
182
183// Forces |sdesc->crypto_required| to the appropriate state based on the
184// current security policy, to ensure a failure occurs if there is an error
185// in crypto negotiation.
186// Called when processing the local session description.
187static void UpdateSessionDescriptionSecurePolicy(
188    cricket::SecureMediaPolicy secure_policy,
189    SessionDescription* sdesc) {
190  if (!sdesc) {
191    return;
192  }
193
194  // Updating the |crypto_required_| in MediaContentDescription to the
195  // appropriate state based on the current security policy.
196  for (cricket::ContentInfos::iterator iter = sdesc->contents().begin();
197       iter != sdesc->contents().end(); ++iter) {
198    if (cricket::IsMediaContent(&*iter)) {
199      MediaContentDescription* mdesc =
200          static_cast<MediaContentDescription*> (iter->description);
201      if (mdesc) {
202        mdesc->set_crypto_required(secure_policy == cricket::SEC_REQUIRED);
203      }
204    }
205  }
206}
207
208static bool GetAudioSsrcByTrackId(
209    const SessionDescription* session_description,
210    const std::string& track_id, uint32 *ssrc) {
211  const cricket::ContentInfo* audio_info =
212      cricket::GetFirstAudioContent(session_description);
213  if (!audio_info) {
214    LOG(LS_ERROR) << "Audio not used in this call";
215    return false;
216  }
217
218  const cricket::MediaContentDescription* audio_content =
219      static_cast<const cricket::MediaContentDescription*>(
220          audio_info->description);
221  cricket::StreamParams stream;
222  if (!cricket::GetStreamByIds(audio_content->streams(), "", track_id,
223                               &stream)) {
224    return false;
225  }
226  *ssrc = stream.first_ssrc();
227  return true;
228}
229
230static bool GetTrackIdBySsrc(const SessionDescription* session_description,
231                             uint32 ssrc, std::string* track_id) {
232  ASSERT(track_id != NULL);
233
234  cricket::StreamParams stream_out;
235  const cricket::ContentInfo* audio_info =
236      cricket::GetFirstAudioContent(session_description);
237  if (!audio_info) {
238    return false;
239  }
240  const cricket::MediaContentDescription* audio_content =
241      static_cast<const cricket::MediaContentDescription*>(
242          audio_info->description);
243
244  if (cricket::GetStreamBySsrc(audio_content->streams(), ssrc, &stream_out)) {
245    *track_id = stream_out.id;
246    return true;
247  }
248
249  const cricket::ContentInfo* video_info =
250      cricket::GetFirstVideoContent(session_description);
251  if (!video_info) {
252    return false;
253  }
254  const cricket::MediaContentDescription* video_content =
255      static_cast<const cricket::MediaContentDescription*>(
256          video_info->description);
257
258  if (cricket::GetStreamBySsrc(video_content->streams(), ssrc, &stream_out)) {
259    *track_id = stream_out.id;
260    return true;
261  }
262  return false;
263}
264
265static bool BadSdp(const std::string& desc, std::string* err_desc) {
266  if (err_desc) {
267    *err_desc = desc;
268  }
269  LOG(LS_ERROR) << desc;
270  return false;
271}
272
273static bool BadLocalSdp(const std::string& desc, std::string* err_desc) {
274  std::string set_local_sdp_failed = kSetLocalSdpFailed;
275  set_local_sdp_failed.append(desc);
276  return BadSdp(set_local_sdp_failed, err_desc);
277}
278
279static bool BadRemoteSdp(const std::string& desc, std::string* err_desc) {
280  std::string set_remote_sdp_failed = kSetRemoteSdpFailed;
281  set_remote_sdp_failed.append(desc);
282  return BadSdp(set_remote_sdp_failed, err_desc);
283}
284
285static bool BadSdp(cricket::ContentSource source,
286                   const std::string& desc, std::string* err_desc) {
287  if (source == cricket::CS_LOCAL) {
288    return BadLocalSdp(desc, err_desc);
289  } else {
290    return BadRemoteSdp(desc, err_desc);
291  }
292}
293
294static std::string SessionErrorMsg(cricket::BaseSession::Error error) {
295  std::ostringstream desc;
296  desc << kSessionError << error;
297  return desc.str();
298}
299
300#define GET_STRING_OF_STATE(state)  \
301  case cricket::BaseSession::state:  \
302    result = #state;  \
303    break;
304
305static std::string GetStateString(cricket::BaseSession::State state) {
306  std::string result;
307  switch (state) {
308    GET_STRING_OF_STATE(STATE_INIT)
309    GET_STRING_OF_STATE(STATE_SENTINITIATE)
310    GET_STRING_OF_STATE(STATE_RECEIVEDINITIATE)
311    GET_STRING_OF_STATE(STATE_SENTPRACCEPT)
312    GET_STRING_OF_STATE(STATE_SENTACCEPT)
313    GET_STRING_OF_STATE(STATE_RECEIVEDPRACCEPT)
314    GET_STRING_OF_STATE(STATE_RECEIVEDACCEPT)
315    GET_STRING_OF_STATE(STATE_SENTMODIFY)
316    GET_STRING_OF_STATE(STATE_RECEIVEDMODIFY)
317    GET_STRING_OF_STATE(STATE_SENTREJECT)
318    GET_STRING_OF_STATE(STATE_RECEIVEDREJECT)
319    GET_STRING_OF_STATE(STATE_SENTREDIRECT)
320    GET_STRING_OF_STATE(STATE_SENTTERMINATE)
321    GET_STRING_OF_STATE(STATE_RECEIVEDTERMINATE)
322    GET_STRING_OF_STATE(STATE_INPROGRESS)
323    GET_STRING_OF_STATE(STATE_DEINIT)
324    default:
325      ASSERT(false);
326      break;
327  }
328  return result;
329}
330
331#define GET_STRING_OF_ERROR(err)  \
332  case cricket::BaseSession::err:  \
333    result = #err;  \
334    break;
335
336static std::string GetErrorString(cricket::BaseSession::Error err) {
337  std::string result;
338  switch (err) {
339    GET_STRING_OF_ERROR(ERROR_NONE)
340    GET_STRING_OF_ERROR(ERROR_TIME)
341    GET_STRING_OF_ERROR(ERROR_RESPONSE)
342    GET_STRING_OF_ERROR(ERROR_NETWORK)
343    GET_STRING_OF_ERROR(ERROR_CONTENT)
344    GET_STRING_OF_ERROR(ERROR_TRANSPORT)
345    default:
346      ASSERT(false);
347      break;
348  }
349  return result;
350}
351
352static bool SetSessionStateFailed(cricket::ContentSource source,
353                                  cricket::BaseSession::Error err,
354                                  std::string* err_desc) {
355  std::string set_state_err = kUpdateStateFailed;
356  set_state_err.append(GetErrorString(err));
357  return BadSdp(source, set_state_err, err_desc);
358}
359
360// Help class used to remember if a a remote peer has requested ice restart by
361// by sending a description with new ice ufrag and password.
362class IceRestartAnswerLatch {
363 public:
364  IceRestartAnswerLatch() : ice_restart_(false) { }
365
366  // Returns true if CheckForRemoteIceRestart has been called with a new session
367  // description where ice password and ufrag has changed since last time
368  // Reset() was called.
369  bool Get() const {
370    return ice_restart_;
371  }
372
373  void Reset() {
374    if (ice_restart_) {
375      ice_restart_ = false;
376    }
377  }
378
379  void CheckForRemoteIceRestart(
380      const SessionDescriptionInterface* old_desc,
381      const SessionDescriptionInterface* new_desc) {
382    if (!old_desc || new_desc->type() != SessionDescriptionInterface::kOffer) {
383      return;
384    }
385    const SessionDescription* new_sd = new_desc->description();
386    const SessionDescription* old_sd = old_desc->description();
387    const ContentInfos& contents = new_sd->contents();
388    for (size_t index = 0; index < contents.size(); ++index) {
389      const ContentInfo* cinfo = &contents[index];
390      if (cinfo->rejected) {
391        continue;
392      }
393      // If the content isn't rejected, check if ufrag and password has
394      // changed.
395      const cricket::TransportDescription* new_transport_desc =
396          new_sd->GetTransportDescriptionByName(cinfo->name);
397      const cricket::TransportDescription* old_transport_desc =
398          old_sd->GetTransportDescriptionByName(cinfo->name);
399      if (!new_transport_desc || !old_transport_desc) {
400        // No transport description exist. This is not an ice restart.
401        continue;
402      }
403      if (new_transport_desc->ice_pwd != old_transport_desc->ice_pwd &&
404          new_transport_desc->ice_ufrag != old_transport_desc->ice_ufrag) {
405        LOG(LS_INFO) << "Remote peer request ice restart.";
406        ice_restart_ = true;
407        break;
408      }
409    }
410  }
411
412 private:
413  bool ice_restart_;
414};
415
416WebRtcSession::WebRtcSession(
417    cricket::ChannelManager* channel_manager,
418    talk_base::Thread* signaling_thread,
419    talk_base::Thread* worker_thread,
420    cricket::PortAllocator* port_allocator,
421    MediaStreamSignaling* mediastream_signaling)
422    : cricket::BaseSession(signaling_thread, worker_thread, port_allocator,
423                           talk_base::ToString(talk_base::CreateRandomId64() &
424                                               LLONG_MAX),
425                           cricket::NS_JINGLE_RTP, false),
426      // RFC 3264: The numeric value of the session id and version in the
427      // o line MUST be representable with a "64 bit signed integer".
428      // Due to this constraint session id |sid_| is max limited to LLONG_MAX.
429      channel_manager_(channel_manager),
430      mediastream_signaling_(mediastream_signaling),
431      ice_observer_(NULL),
432      ice_connection_state_(PeerConnectionInterface::kIceConnectionNew),
433      older_version_remote_peer_(false),
434      dtls_enabled_(false),
435      dscp_enabled_(false),
436      data_channel_type_(cricket::DCT_NONE),
437      ice_restart_latch_(new IceRestartAnswerLatch) {
438}
439
440WebRtcSession::~WebRtcSession() {
441  if (voice_channel_.get()) {
442    SignalVoiceChannelDestroyed();
443    channel_manager_->DestroyVoiceChannel(voice_channel_.release());
444  }
445  if (video_channel_.get()) {
446    SignalVideoChannelDestroyed();
447    channel_manager_->DestroyVideoChannel(video_channel_.release());
448  }
449  if (data_channel_.get()) {
450    SignalDataChannelDestroyed();
451    channel_manager_->DestroyDataChannel(data_channel_.release());
452  }
453  for (size_t i = 0; i < saved_candidates_.size(); ++i) {
454    delete saved_candidates_[i];
455  }
456  delete identity();
457}
458
459bool WebRtcSession::Initialize(
460    const PeerConnectionFactoryInterface::Options& options,
461    const MediaConstraintsInterface* constraints,
462    DTLSIdentityServiceInterface* dtls_identity_service) {
463  // TODO(perkj): Take |constraints| into consideration. Return false if not all
464  // mandatory constraints can be fulfilled. Note that |constraints|
465  // can be null.
466  bool value;
467
468  // Enable DTLS by default if |dtls_identity_service| is valid.
469  dtls_enabled_ = (dtls_identity_service != NULL);
470  // |constraints| can override the default |dtls_enabled_| value.
471  if (FindConstraint(
472        constraints,
473        MediaConstraintsInterface::kEnableDtlsSrtp,
474        &value, NULL)) {
475    dtls_enabled_ = value;
476  }
477
478  // Enable creation of RTP data channels if the kEnableRtpDataChannels is set.
479  // It takes precendence over the disable_sctp_data_channels
480  // PeerConnectionFactoryInterface::Options.
481  if (FindConstraint(
482      constraints, MediaConstraintsInterface::kEnableRtpDataChannels,
483      &value, NULL) && value) {
484    LOG(LS_INFO) << "Allowing RTP data engine.";
485    data_channel_type_ = cricket::DCT_RTP;
486  } else {
487    // DTLS has to be enabled to use SCTP.
488    if (!options.disable_sctp_data_channels && dtls_enabled_) {
489      LOG(LS_INFO) << "Allowing SCTP data engine.";
490      data_channel_type_ = cricket::DCT_SCTP;
491    }
492  }
493  if (data_channel_type_ != cricket::DCT_NONE) {
494    mediastream_signaling_->SetDataChannelFactory(this);
495  }
496
497  // Find DSCP constraint.
498  if (FindConstraint(
499        constraints,
500        MediaConstraintsInterface::kEnableDscp,
501        &value, NULL)) {
502    dscp_enabled_ = value;
503  }
504
505  const cricket::VideoCodec default_codec(
506      JsepSessionDescription::kDefaultVideoCodecId,
507      JsepSessionDescription::kDefaultVideoCodecName,
508      JsepSessionDescription::kMaxVideoCodecWidth,
509      JsepSessionDescription::kMaxVideoCodecHeight,
510      JsepSessionDescription::kDefaultVideoCodecFramerate,
511      JsepSessionDescription::kDefaultVideoCodecPreference);
512  channel_manager_->SetDefaultVideoEncoderConfig(
513      cricket::VideoEncoderConfig(default_codec));
514
515  webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
516      signaling_thread(),
517      channel_manager_,
518      mediastream_signaling_,
519      dtls_identity_service,
520      this,
521      id(),
522      data_channel_type_,
523      dtls_enabled_));
524
525  webrtc_session_desc_factory_->SignalIdentityReady.connect(
526      this, &WebRtcSession::OnIdentityReady);
527
528  if (options.disable_encryption) {
529    webrtc_session_desc_factory_->SetSecure(cricket::SEC_DISABLED);
530  }
531
532  return true;
533}
534
535void WebRtcSession::Terminate() {
536  SetState(STATE_RECEIVEDTERMINATE);
537  RemoveUnusedChannelsAndTransports(NULL);
538  ASSERT(voice_channel_.get() == NULL);
539  ASSERT(video_channel_.get() == NULL);
540  ASSERT(data_channel_.get() == NULL);
541}
542
543bool WebRtcSession::StartCandidatesAllocation() {
544  // SpeculativelyConnectTransportChannels, will call ConnectChannels method
545  // from TransportProxy to start gathering ice candidates.
546  SpeculativelyConnectAllTransportChannels();
547  if (!saved_candidates_.empty()) {
548    // If there are saved candidates which arrived before local description is
549    // set, copy those to remote description.
550    CopySavedCandidates(remote_desc_.get());
551  }
552  // Push remote candidates present in remote description to transport channels.
553  UseCandidatesInSessionDescription(remote_desc_.get());
554  return true;
555}
556
557void WebRtcSession::SetSecurePolicy(
558    cricket::SecureMediaPolicy secure_policy) {
559  webrtc_session_desc_factory_->SetSecure(secure_policy);
560}
561
562cricket::SecureMediaPolicy WebRtcSession::SecurePolicy() const {
563  return webrtc_session_desc_factory_->Secure();
564}
565
566bool WebRtcSession::GetSslRole(talk_base::SSLRole* role) {
567  if (local_description() == NULL || remote_description() == NULL) {
568    LOG(LS_INFO) << "Local and Remote descriptions must be applied to get "
569                 << "SSL Role of the session.";
570    return false;
571  }
572
573  // TODO(mallinath) - Return role of each transport, as role may differ from
574  // one another.
575  // In current implementaion we just return the role of first transport in the
576  // transport map.
577  for (cricket::TransportMap::const_iterator iter = transport_proxies().begin();
578       iter != transport_proxies().end(); ++iter) {
579    if (iter->second->impl()) {
580      return iter->second->impl()->GetSslRole(role);
581    }
582  }
583  return false;
584}
585
586void WebRtcSession::CreateOffer(CreateSessionDescriptionObserver* observer,
587                                const MediaConstraintsInterface* constraints) {
588  webrtc_session_desc_factory_->CreateOffer(observer, constraints);
589}
590
591void WebRtcSession::CreateAnswer(CreateSessionDescriptionObserver* observer,
592                                 const MediaConstraintsInterface* constraints) {
593  webrtc_session_desc_factory_->CreateAnswer(observer, constraints);
594}
595
596bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
597                                        std::string* err_desc) {
598  // Takes the ownership of |desc| regardless of the result.
599  talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
600
601  // Validate SDP.
602  if (!ValidateSessionDescription(desc, cricket::CS_LOCAL, err_desc)) {
603    return false;
604  }
605
606  // Update the initiator flag if this session is the initiator.
607  Action action = GetAction(desc->type());
608  if (state() == STATE_INIT && action == kOffer) {
609    set_initiator(true);
610  }
611
612  cricket::SecureMediaPolicy secure_policy =
613      webrtc_session_desc_factory_->Secure();
614  // Update the MediaContentDescription crypto settings as per the policy set.
615  UpdateSessionDescriptionSecurePolicy(secure_policy, desc->description());
616
617  set_local_description(desc->description()->Copy());
618  local_desc_.reset(desc_temp.release());
619
620  // Transport and Media channels will be created only when offer is set.
621  if (action == kOffer && !CreateChannels(local_desc_->description())) {
622    // TODO(mallinath) - Handle CreateChannel failure, as new local description
623    // is applied. Restore back to old description.
624    return BadLocalSdp(kCreateChannelFailed, err_desc);
625  }
626
627  // Remove channel and transport proxies, if MediaContentDescription is
628  // rejected.
629  RemoveUnusedChannelsAndTransports(local_desc_->description());
630
631  if (!UpdateSessionState(action, cricket::CS_LOCAL,
632                          local_desc_->description(), err_desc)) {
633    return false;
634  }
635  // Kick starting the ice candidates allocation.
636  StartCandidatesAllocation();
637
638  // Update state and SSRC of local MediaStreams and DataChannels based on the
639  // local session description.
640  mediastream_signaling_->OnLocalDescriptionChanged(local_desc_.get());
641
642  talk_base::SSLRole role;
643  if (data_channel_type_ == cricket::DCT_SCTP && GetSslRole(&role)) {
644    mediastream_signaling_->OnDtlsRoleReadyForSctp(role);
645  }
646  if (error() != cricket::BaseSession::ERROR_NONE) {
647    return BadLocalSdp(SessionErrorMsg(error()), err_desc);
648  }
649  return true;
650}
651
652bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
653                                         std::string* err_desc) {
654  // Takes the ownership of |desc| regardless of the result.
655  talk_base::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
656
657  // Validate SDP.
658  if (!ValidateSessionDescription(desc, cricket::CS_REMOTE, err_desc)) {
659    return false;
660  }
661
662  // Transport and Media channels will be created only when offer is set.
663  Action action = GetAction(desc->type());
664  if (action == kOffer && !CreateChannels(desc->description())) {
665    // TODO(mallinath) - Handle CreateChannel failure, as new local description
666    // is applied. Restore back to old description.
667    return BadRemoteSdp(kCreateChannelFailed, err_desc);
668  }
669
670  // Remove channel and transport proxies, if MediaContentDescription is
671  // rejected.
672  RemoveUnusedChannelsAndTransports(desc->description());
673
674  // NOTE: Candidates allocation will be initiated only when SetLocalDescription
675  // is called.
676  set_remote_description(desc->description()->Copy());
677  if (!UpdateSessionState(action, cricket::CS_REMOTE,
678                          desc->description(), err_desc)) {
679    return false;
680  }
681
682  // Update remote MediaStreams.
683  mediastream_signaling_->OnRemoteDescriptionChanged(desc);
684  if (local_description() && !UseCandidatesInSessionDescription(desc)) {
685    return BadRemoteSdp(kInvalidCandidates, err_desc);
686  }
687
688  // Copy all saved candidates.
689  CopySavedCandidates(desc);
690  // We retain all received candidates.
691  WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
692      remote_desc_.get(), desc);
693  // Check if this new SessionDescription contains new ice ufrag and password
694  // that indicates the remote peer requests ice restart.
695  ice_restart_latch_->CheckForRemoteIceRestart(remote_desc_.get(),
696                                               desc);
697  remote_desc_.reset(desc_temp.release());
698
699  talk_base::SSLRole role;
700  if (data_channel_type_ == cricket::DCT_SCTP && GetSslRole(&role)) {
701    mediastream_signaling_->OnDtlsRoleReadyForSctp(role);
702  }
703
704  if (error() != cricket::BaseSession::ERROR_NONE) {
705    return BadRemoteSdp(SessionErrorMsg(error()), err_desc);
706  }
707  return true;
708}
709
710bool WebRtcSession::UpdateSessionState(
711    Action action, cricket::ContentSource source,
712    const cricket::SessionDescription* desc,
713    std::string* err_desc) {
714  // If there's already a pending error then no state transition should happen.
715  // But all call-sites should be verifying this before calling us!
716  ASSERT(error() == cricket::BaseSession::ERROR_NONE);
717  if (action == kOffer) {
718    if (!PushdownTransportDescription(source, cricket::CA_OFFER)) {
719      return BadSdp(source, kPushDownOfferTDFailed, err_desc);
720    }
721    SetState(source == cricket::CS_LOCAL ?
722        STATE_SENTINITIATE : STATE_RECEIVEDINITIATE);
723    if (error() != cricket::BaseSession::ERROR_NONE) {
724      return SetSessionStateFailed(source, error(), err_desc);
725    }
726  } else if (action == kPrAnswer) {
727    if (!PushdownTransportDescription(source, cricket::CA_PRANSWER)) {
728      return BadSdp(source, kPushDownPranswerTDFailed, err_desc);
729    }
730    EnableChannels();
731    SetState(source == cricket::CS_LOCAL ?
732        STATE_SENTPRACCEPT : STATE_RECEIVEDPRACCEPT);
733    if (error() != cricket::BaseSession::ERROR_NONE) {
734      return SetSessionStateFailed(source, error(), err_desc);
735    }
736  } else if (action == kAnswer) {
737    if (!PushdownTransportDescription(source, cricket::CA_ANSWER)) {
738      return BadSdp(source, kPushDownAnswerTDFailed, err_desc);
739    }
740    MaybeEnableMuxingSupport();
741    EnableChannels();
742    SetState(source == cricket::CS_LOCAL ?
743        STATE_SENTACCEPT : STATE_RECEIVEDACCEPT);
744    if (error() != cricket::BaseSession::ERROR_NONE) {
745      return SetSessionStateFailed(source, error(), err_desc);
746    }
747  }
748  return true;
749}
750
751WebRtcSession::Action WebRtcSession::GetAction(const std::string& type) {
752  if (type == SessionDescriptionInterface::kOffer) {
753    return WebRtcSession::kOffer;
754  } else if (type == SessionDescriptionInterface::kPrAnswer) {
755    return WebRtcSession::kPrAnswer;
756  } else if (type == SessionDescriptionInterface::kAnswer) {
757    return WebRtcSession::kAnswer;
758  }
759  ASSERT(false && "unknown action type");
760  return WebRtcSession::kOffer;
761}
762
763bool WebRtcSession::ProcessIceMessage(const IceCandidateInterface* candidate) {
764  if (state() == STATE_INIT) {
765     LOG(LS_ERROR) << "ProcessIceMessage: ICE candidates can't be added "
766                   << "without any offer (local or remote) "
767                   << "session description.";
768     return false;
769  }
770
771  if (!candidate) {
772    LOG(LS_ERROR) << "ProcessIceMessage: Candidate is NULL";
773    return false;
774  }
775
776  if (!local_description() || !remote_description()) {
777    LOG(LS_INFO) << "ProcessIceMessage: Remote description not set, "
778                 << "save the candidate for later use.";
779    saved_candidates_.push_back(
780        new JsepIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(),
781                             candidate->candidate()));
782    return true;
783  }
784
785  // Add this candidate to the remote session description.
786  if (!remote_desc_->AddCandidate(candidate)) {
787    LOG(LS_ERROR) << "ProcessIceMessage: Candidate cannot be used";
788    return false;
789  }
790
791  return UseCandidatesInSessionDescription(remote_desc_.get());
792}
793
794bool WebRtcSession::GetTrackIdBySsrc(uint32 ssrc, std::string* id) {
795  if (GetLocalTrackId(ssrc, id)) {
796    if (GetRemoteTrackId(ssrc, id)) {
797      LOG(LS_WARNING) << "SSRC " << ssrc
798                      << " exists in both local and remote descriptions";
799      return true;  // We return the remote track id.
800    }
801    return true;
802  } else {
803    return GetRemoteTrackId(ssrc, id);
804  }
805}
806
807bool WebRtcSession::GetLocalTrackId(uint32 ssrc, std::string* track_id) {
808  if (!BaseSession::local_description())
809    return false;
810  return webrtc::GetTrackIdBySsrc(
811    BaseSession::local_description(), ssrc, track_id);
812}
813
814bool WebRtcSession::GetRemoteTrackId(uint32 ssrc, std::string* track_id) {
815  if (!BaseSession::remote_description())
816      return false;
817  return webrtc::GetTrackIdBySsrc(
818    BaseSession::remote_description(), ssrc, track_id);
819}
820
821std::string WebRtcSession::BadStateErrMsg(
822    const std::string& type, State state) {
823  std::ostringstream desc;
824  desc << "Called with type in wrong state, "
825       << "type: " << type << " state: " << GetStateString(state);
826  return desc.str();
827}
828
829void WebRtcSession::SetAudioPlayout(uint32 ssrc, bool enable,
830                                    cricket::AudioRenderer* renderer) {
831  ASSERT(signaling_thread()->IsCurrent());
832  if (!voice_channel_) {
833    LOG(LS_ERROR) << "SetAudioPlayout: No audio channel exists.";
834    return;
835  }
836  if (!voice_channel_->SetRemoteRenderer(ssrc, renderer)) {
837    // SetRenderer() can fail if the ssrc does not match any playout channel.
838    LOG(LS_ERROR) << "SetAudioPlayout: ssrc is incorrect: " << ssrc;
839    return;
840  }
841  if (!voice_channel_->SetOutputScaling(ssrc, enable ? 1 : 0, enable ? 1 : 0)) {
842    // Allow that SetOutputScaling fail if |enable| is false but assert
843    // otherwise. This in the normal case when the underlying media channel has
844    // already been deleted.
845    ASSERT(enable == false);
846  }
847}
848
849void WebRtcSession::SetAudioSend(uint32 ssrc, bool enable,
850                                 const cricket::AudioOptions& options,
851                                 cricket::AudioRenderer* renderer) {
852  ASSERT(signaling_thread()->IsCurrent());
853  if (!voice_channel_) {
854    LOG(LS_ERROR) << "SetAudioSend: No audio channel exists.";
855    return;
856  }
857  if (!voice_channel_->SetLocalRenderer(ssrc, renderer)) {
858    // SetRenderer() can fail if the ssrc does not match any send channel.
859    LOG(LS_ERROR) << "SetAudioSend: ssrc is incorrect: " << ssrc;
860    return;
861  }
862  if (!voice_channel_->MuteStream(ssrc, !enable)) {
863    // Allow that MuteStream fail if |enable| is false but assert otherwise.
864    // This in the normal case when the underlying media channel has already
865    // been deleted.
866    ASSERT(enable == false);
867    return;
868  }
869  if (enable)
870    voice_channel_->SetChannelOptions(options);
871}
872
873bool WebRtcSession::SetCaptureDevice(uint32 ssrc,
874                                     cricket::VideoCapturer* camera) {
875  ASSERT(signaling_thread()->IsCurrent());
876
877  if (!video_channel_.get()) {
878    // |video_channel_| doesnt't exist. Probably because the remote end doesnt't
879    // support video.
880    LOG(LS_WARNING) << "Video not used in this call.";
881    return false;
882  }
883  if (!video_channel_->SetCapturer(ssrc, camera)) {
884    // Allow that SetCapturer fail if |camera| is NULL but assert otherwise.
885    // This in the normal case when the underlying media channel has already
886    // been deleted.
887    ASSERT(camera == NULL);
888    return false;
889  }
890  return true;
891}
892
893void WebRtcSession::SetVideoPlayout(uint32 ssrc,
894                                    bool enable,
895                                    cricket::VideoRenderer* renderer) {
896  ASSERT(signaling_thread()->IsCurrent());
897  if (!video_channel_) {
898    LOG(LS_WARNING) << "SetVideoPlayout: No video channel exists.";
899    return;
900  }
901  if (!video_channel_->SetRenderer(ssrc, enable ? renderer : NULL)) {
902    // Allow that SetRenderer fail if |renderer| is NULL but assert otherwise.
903    // This in the normal case when the underlying media channel has already
904    // been deleted.
905    ASSERT(renderer == NULL);
906  }
907}
908
909void WebRtcSession::SetVideoSend(uint32 ssrc, bool enable,
910                                 const cricket::VideoOptions* options) {
911  ASSERT(signaling_thread()->IsCurrent());
912  if (!video_channel_) {
913    LOG(LS_WARNING) << "SetVideoSend: No video channel exists.";
914    return;
915  }
916  if (!video_channel_->MuteStream(ssrc, !enable)) {
917    // Allow that MuteStream fail if |enable| is false but assert otherwise.
918    // This in the normal case when the underlying media channel has already
919    // been deleted.
920    ASSERT(enable == false);
921    return;
922  }
923  if (enable && options)
924    video_channel_->SetChannelOptions(*options);
925}
926
927bool WebRtcSession::CanInsertDtmf(const std::string& track_id) {
928  ASSERT(signaling_thread()->IsCurrent());
929  if (!voice_channel_) {
930    LOG(LS_ERROR) << "CanInsertDtmf: No audio channel exists.";
931    return false;
932  }
933  uint32 send_ssrc = 0;
934  // The Dtmf is negotiated per channel not ssrc, so we only check if the ssrc
935  // exists.
936  if (!GetAudioSsrcByTrackId(BaseSession::local_description(), track_id,
937                             &send_ssrc)) {
938    LOG(LS_ERROR) << "CanInsertDtmf: Track does not exist: " << track_id;
939    return false;
940  }
941  return voice_channel_->CanInsertDtmf();
942}
943
944bool WebRtcSession::InsertDtmf(const std::string& track_id,
945                               int code, int duration) {
946  ASSERT(signaling_thread()->IsCurrent());
947  if (!voice_channel_) {
948    LOG(LS_ERROR) << "InsertDtmf: No audio channel exists.";
949    return false;
950  }
951  uint32 send_ssrc = 0;
952  if (!VERIFY(GetAudioSsrcByTrackId(BaseSession::local_description(),
953                                    track_id, &send_ssrc))) {
954    LOG(LS_ERROR) << "InsertDtmf: Track does not exist: " << track_id;
955    return false;
956  }
957  if (!voice_channel_->InsertDtmf(send_ssrc, code, duration,
958                                  cricket::DF_SEND)) {
959    LOG(LS_ERROR) << "Failed to insert DTMF to channel.";
960    return false;
961  }
962  return true;
963}
964
965sigslot::signal0<>* WebRtcSession::GetOnDestroyedSignal() {
966  return &SignalVoiceChannelDestroyed;
967}
968
969bool WebRtcSession::SendData(const cricket::SendDataParams& params,
970                             const talk_base::Buffer& payload,
971                             cricket::SendDataResult* result) {
972  if (!data_channel_.get()) {
973    LOG(LS_ERROR) << "SendData called when data_channel_ is NULL.";
974    return false;
975  }
976  return data_channel_->SendData(params, payload, result);
977}
978
979bool WebRtcSession::ConnectDataChannel(DataChannel* webrtc_data_channel) {
980  if (!data_channel_.get()) {
981    LOG(LS_ERROR) << "ConnectDataChannel called when data_channel_ is NULL.";
982    return false;
983  }
984  data_channel_->SignalReadyToSendData.connect(webrtc_data_channel,
985                                               &DataChannel::OnChannelReady);
986  data_channel_->SignalDataReceived.connect(webrtc_data_channel,
987                                            &DataChannel::OnDataReceived);
988  return true;
989}
990
991void WebRtcSession::DisconnectDataChannel(DataChannel* webrtc_data_channel) {
992  if (!data_channel_.get()) {
993    LOG(LS_ERROR) << "DisconnectDataChannel called when data_channel_ is NULL.";
994    return;
995  }
996  data_channel_->SignalReadyToSendData.disconnect(webrtc_data_channel);
997  data_channel_->SignalDataReceived.disconnect(webrtc_data_channel);
998}
999
1000void WebRtcSession::AddSctpDataStream(uint32 sid) {
1001  if (!data_channel_.get()) {
1002    LOG(LS_ERROR) << "AddDataChannelStreams called when data_channel_ is NULL.";
1003    return;
1004  }
1005  data_channel_->AddRecvStream(cricket::StreamParams::CreateLegacy(sid));
1006  data_channel_->AddSendStream(cricket::StreamParams::CreateLegacy(sid));
1007}
1008
1009void WebRtcSession::RemoveSctpDataStream(uint32 sid) {
1010  if (!data_channel_.get()) {
1011    LOG(LS_ERROR) << "RemoveDataChannelStreams called when data_channel_ is "
1012                  << "NULL.";
1013    return;
1014  }
1015  data_channel_->RemoveRecvStream(sid);
1016  data_channel_->RemoveSendStream(sid);
1017}
1018
1019bool WebRtcSession::ReadyToSendData() const {
1020  return data_channel_.get() && data_channel_->ready_to_send_data();
1021}
1022
1023talk_base::scoped_refptr<DataChannel> WebRtcSession::CreateDataChannel(
1024    const std::string& label,
1025    const DataChannelInit* config) {
1026  if (state() == STATE_RECEIVEDTERMINATE) {
1027    return NULL;
1028  }
1029  if (data_channel_type_ == cricket::DCT_NONE) {
1030    LOG(LS_ERROR) << "CreateDataChannel: Data is not supported in this call.";
1031    return NULL;
1032  }
1033  DataChannelInit new_config = config ? (*config) : DataChannelInit();
1034
1035  if (data_channel_type_ == cricket::DCT_SCTP) {
1036    if (new_config.id < 0) {
1037      talk_base::SSLRole role;
1038      if (GetSslRole(&role) &&
1039          !mediastream_signaling_->AllocateSctpSid(role, &new_config.id)) {
1040        LOG(LS_ERROR) << "No id can be allocated for the SCTP data channel.";
1041        return NULL;
1042      }
1043    } else if (!mediastream_signaling_->IsSctpSidAvailable(new_config.id)) {
1044      LOG(LS_ERROR) << "Failed to create a SCTP data channel "
1045                    << "because the id is already in use or out of range.";
1046      return NULL;
1047    }
1048  }
1049
1050  talk_base::scoped_refptr<DataChannel> channel(
1051      DataChannel::Create(this, data_channel_type_, label, &new_config));
1052  if (channel && !mediastream_signaling_->AddDataChannel(channel))
1053    return NULL;
1054
1055  return channel;
1056}
1057
1058cricket::DataChannelType WebRtcSession::data_channel_type() const {
1059  return data_channel_type_;
1060}
1061
1062bool WebRtcSession::IceRestartPending() const {
1063  return ice_restart_latch_->Get();
1064}
1065
1066void WebRtcSession::ResetIceRestartLatch() {
1067  ice_restart_latch_->Reset();
1068}
1069
1070void WebRtcSession::OnIdentityReady(talk_base::SSLIdentity* identity) {
1071  SetIdentity(identity);
1072}
1073
1074bool WebRtcSession::waiting_for_identity() const {
1075  return webrtc_session_desc_factory_->waiting_for_identity();
1076}
1077
1078void WebRtcSession::SetIceConnectionState(
1079      PeerConnectionInterface::IceConnectionState state) {
1080  if (ice_connection_state_ == state) {
1081    return;
1082  }
1083
1084  // ASSERT that the requested transition is allowed.  Note that
1085  // WebRtcSession does not implement "kIceConnectionClosed" (that is handled
1086  // within PeerConnection).  This switch statement should compile away when
1087  // ASSERTs are disabled.
1088  switch (ice_connection_state_) {
1089    case PeerConnectionInterface::kIceConnectionNew:
1090      ASSERT(state == PeerConnectionInterface::kIceConnectionChecking);
1091      break;
1092    case PeerConnectionInterface::kIceConnectionChecking:
1093      ASSERT(state == PeerConnectionInterface::kIceConnectionFailed ||
1094             state == PeerConnectionInterface::kIceConnectionConnected);
1095      break;
1096    case PeerConnectionInterface::kIceConnectionConnected:
1097      ASSERT(state == PeerConnectionInterface::kIceConnectionDisconnected ||
1098             state == PeerConnectionInterface::kIceConnectionChecking ||
1099             state == PeerConnectionInterface::kIceConnectionCompleted);
1100      break;
1101    case PeerConnectionInterface::kIceConnectionCompleted:
1102      ASSERT(state == PeerConnectionInterface::kIceConnectionConnected ||
1103             state == PeerConnectionInterface::kIceConnectionDisconnected);
1104      break;
1105    case PeerConnectionInterface::kIceConnectionFailed:
1106      ASSERT(state == PeerConnectionInterface::kIceConnectionNew);
1107      break;
1108    case PeerConnectionInterface::kIceConnectionDisconnected:
1109      ASSERT(state == PeerConnectionInterface::kIceConnectionChecking ||
1110             state == PeerConnectionInterface::kIceConnectionConnected ||
1111             state == PeerConnectionInterface::kIceConnectionCompleted ||
1112             state == PeerConnectionInterface::kIceConnectionFailed);
1113      break;
1114    case PeerConnectionInterface::kIceConnectionClosed:
1115      ASSERT(false);
1116      break;
1117    default:
1118      ASSERT(false);
1119      break;
1120  }
1121
1122  ice_connection_state_ = state;
1123  if (ice_observer_) {
1124    ice_observer_->OnIceConnectionChange(ice_connection_state_);
1125  }
1126}
1127
1128void WebRtcSession::OnTransportRequestSignaling(
1129    cricket::Transport* transport) {
1130  ASSERT(signaling_thread()->IsCurrent());
1131  transport->OnSignalingReady();
1132  if (ice_observer_) {
1133    ice_observer_->OnIceGatheringChange(
1134      PeerConnectionInterface::kIceGatheringGathering);
1135  }
1136}
1137
1138void WebRtcSession::OnTransportConnecting(cricket::Transport* transport) {
1139  ASSERT(signaling_thread()->IsCurrent());
1140  // start monitoring for the write state of the transport.
1141  OnTransportWritable(transport);
1142}
1143
1144void WebRtcSession::OnTransportWritable(cricket::Transport* transport) {
1145  ASSERT(signaling_thread()->IsCurrent());
1146  // TODO(bemasc): Expose more API from Transport to detect when
1147  // candidate selection starts or stops, due to success or failure.
1148  if (transport->all_channels_writable()) {
1149    if (ice_connection_state_ ==
1150            PeerConnectionInterface::kIceConnectionChecking ||
1151        ice_connection_state_ ==
1152            PeerConnectionInterface::kIceConnectionDisconnected) {
1153      SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected);
1154    }
1155  } else if (transport->HasChannels()) {
1156    // If the current state is Connected or Completed, then there were writable
1157    // channels but now there are not, so the next state must be Disconnected.
1158    if (ice_connection_state_ ==
1159            PeerConnectionInterface::kIceConnectionConnected ||
1160        ice_connection_state_ ==
1161            PeerConnectionInterface::kIceConnectionCompleted) {
1162      SetIceConnectionState(
1163          PeerConnectionInterface::kIceConnectionDisconnected);
1164    }
1165  }
1166}
1167
1168void WebRtcSession::OnTransportProxyCandidatesReady(
1169    cricket::TransportProxy* proxy, const cricket::Candidates& candidates) {
1170  ASSERT(signaling_thread()->IsCurrent());
1171  ProcessNewLocalCandidate(proxy->content_name(), candidates);
1172}
1173
1174void WebRtcSession::OnCandidatesAllocationDone() {
1175  ASSERT(signaling_thread()->IsCurrent());
1176  if (ice_observer_) {
1177    ice_observer_->OnIceGatheringChange(
1178      PeerConnectionInterface::kIceGatheringComplete);
1179    ice_observer_->OnIceComplete();
1180  }
1181}
1182
1183// Enabling voice and video channel.
1184void WebRtcSession::EnableChannels() {
1185  if (voice_channel_ && !voice_channel_->enabled())
1186    voice_channel_->Enable(true);
1187
1188  if (video_channel_ && !video_channel_->enabled())
1189    video_channel_->Enable(true);
1190
1191  if (data_channel_.get() && !data_channel_->enabled())
1192    data_channel_->Enable(true);
1193}
1194
1195void WebRtcSession::ProcessNewLocalCandidate(
1196    const std::string& content_name,
1197    const cricket::Candidates& candidates) {
1198  int sdp_mline_index;
1199  if (!GetLocalCandidateMediaIndex(content_name, &sdp_mline_index)) {
1200    LOG(LS_ERROR) << "ProcessNewLocalCandidate: content name "
1201                  << content_name << " not found";
1202    return;
1203  }
1204
1205  for (cricket::Candidates::const_iterator citer = candidates.begin();
1206      citer != candidates.end(); ++citer) {
1207    // Use content_name as the candidate media id.
1208    JsepIceCandidate candidate(content_name, sdp_mline_index, *citer);
1209    if (ice_observer_) {
1210      ice_observer_->OnIceCandidate(&candidate);
1211    }
1212    if (local_desc_) {
1213      local_desc_->AddCandidate(&candidate);
1214    }
1215  }
1216}
1217
1218// Returns the media index for a local ice candidate given the content name.
1219bool WebRtcSession::GetLocalCandidateMediaIndex(const std::string& content_name,
1220                                                int* sdp_mline_index) {
1221  if (!BaseSession::local_description() || !sdp_mline_index)
1222    return false;
1223
1224  bool content_found = false;
1225  const ContentInfos& contents = BaseSession::local_description()->contents();
1226  for (size_t index = 0; index < contents.size(); ++index) {
1227    if (contents[index].name == content_name) {
1228      *sdp_mline_index = static_cast<int>(index);
1229      content_found = true;
1230      break;
1231    }
1232  }
1233  return content_found;
1234}
1235
1236bool WebRtcSession::UseCandidatesInSessionDescription(
1237    const SessionDescriptionInterface* remote_desc) {
1238  if (!remote_desc)
1239    return true;
1240  bool ret = true;
1241  for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) {
1242    const IceCandidateCollection* candidates = remote_desc->candidates(m);
1243    for  (size_t n = 0; n < candidates->count(); ++n) {
1244      ret = UseCandidate(candidates->at(n));
1245      if (!ret)
1246        break;
1247    }
1248  }
1249  return ret;
1250}
1251
1252bool WebRtcSession::UseCandidate(
1253    const IceCandidateInterface* candidate) {
1254
1255  size_t mediacontent_index = static_cast<size_t>(candidate->sdp_mline_index());
1256  size_t remote_content_size =
1257      BaseSession::remote_description()->contents().size();
1258  if (mediacontent_index >= remote_content_size) {
1259    LOG(LS_ERROR)
1260        << "UseRemoteCandidateInSession: Invalid candidate media index.";
1261    return false;
1262  }
1263
1264  cricket::ContentInfo content =
1265      BaseSession::remote_description()->contents()[mediacontent_index];
1266  std::vector<cricket::Candidate> candidates;
1267  candidates.push_back(candidate->candidate());
1268  // Invoking BaseSession method to handle remote candidates.
1269  std::string error;
1270  if (OnRemoteCandidates(content.name, candidates, &error)) {
1271    // Candidates successfully submitted for checking.
1272    if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew ||
1273        ice_connection_state_ ==
1274            PeerConnectionInterface::kIceConnectionDisconnected) {
1275      // If state is New, then the session has just gotten its first remote ICE
1276      // candidates, so go to Checking.
1277      // If state is Disconnected, the session is re-using old candidates or
1278      // receiving additional ones, so go to Checking.
1279      // If state is Connected, stay Connected.
1280      // TODO(bemasc): If state is Connected, and the new candidates are for a
1281      // newly added transport, then the state actually _should_ move to
1282      // checking.  Add a way to distinguish that case.
1283      SetIceConnectionState(PeerConnectionInterface::kIceConnectionChecking);
1284    }
1285    // TODO(bemasc): If state is Completed, go back to Connected.
1286  } else {
1287    LOG(LS_WARNING) << error;
1288  }
1289  return true;
1290}
1291
1292void WebRtcSession::RemoveUnusedChannelsAndTransports(
1293    const SessionDescription* desc) {
1294  const cricket::ContentInfo* voice_info =
1295      cricket::GetFirstAudioContent(desc);
1296  if ((!voice_info || voice_info->rejected) && voice_channel_) {
1297    mediastream_signaling_->OnAudioChannelClose();
1298    SignalVoiceChannelDestroyed();
1299    const std::string content_name = voice_channel_->content_name();
1300    channel_manager_->DestroyVoiceChannel(voice_channel_.release());
1301    DestroyTransportProxy(content_name);
1302  }
1303
1304  const cricket::ContentInfo* video_info =
1305      cricket::GetFirstVideoContent(desc);
1306  if ((!video_info || video_info->rejected) && video_channel_) {
1307    mediastream_signaling_->OnVideoChannelClose();
1308    SignalVideoChannelDestroyed();
1309    const std::string content_name = video_channel_->content_name();
1310    channel_manager_->DestroyVideoChannel(video_channel_.release());
1311    DestroyTransportProxy(content_name);
1312  }
1313
1314  const cricket::ContentInfo* data_info =
1315      cricket::GetFirstDataContent(desc);
1316  if ((!data_info || data_info->rejected) && data_channel_) {
1317    mediastream_signaling_->OnDataChannelClose();
1318    SignalDataChannelDestroyed();
1319    const std::string content_name = data_channel_->content_name();
1320    channel_manager_->DestroyDataChannel(data_channel_.release());
1321    DestroyTransportProxy(content_name);
1322  }
1323}
1324
1325// TODO(mallinath) - Add a correct error code if the channels are not creatued
1326// due to BUNDLE is enabled but rtcp-mux is disabled.
1327bool WebRtcSession::CreateChannels(const SessionDescription* desc) {
1328  // Disabling the BUNDLE flag in PortAllocator if offer disabled it.
1329  bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1330  if (state() == STATE_INIT && !bundle_enabled) {
1331    port_allocator()->set_flags(port_allocator()->flags() &
1332                                ~cricket::PORTALLOCATOR_ENABLE_BUNDLE);
1333  }
1334
1335  // Creating the media channels and transport proxies.
1336  const cricket::ContentInfo* voice = cricket::GetFirstAudioContent(desc);
1337  if (voice && !voice->rejected && !voice_channel_) {
1338    if (!CreateVoiceChannel(voice)) {
1339      LOG(LS_ERROR) << "Failed to create voice channel.";
1340      return false;
1341    }
1342  }
1343
1344  const cricket::ContentInfo* video = cricket::GetFirstVideoContent(desc);
1345  if (video && !video->rejected && !video_channel_) {
1346    if (!CreateVideoChannel(video)) {
1347      LOG(LS_ERROR) << "Failed to create video channel.";
1348      return false;
1349    }
1350  }
1351
1352  const cricket::ContentInfo* data = cricket::GetFirstDataContent(desc);
1353  if (data_channel_type_ != cricket::DCT_NONE &&
1354      data && !data->rejected && !data_channel_.get()) {
1355    if (!CreateDataChannel(data)) {
1356      LOG(LS_ERROR) << "Failed to create data channel.";
1357      return false;
1358    }
1359  }
1360
1361  return true;
1362}
1363
1364bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) {
1365  voice_channel_.reset(channel_manager_->CreateVoiceChannel(
1366      this, content->name, true));
1367  if (!voice_channel_.get())
1368    return false;
1369
1370  if (dscp_enabled_) {
1371    cricket::AudioOptions options;
1372    options.dscp.Set(true);
1373    voice_channel_->SetChannelOptions(options);
1374  }
1375  return true;
1376}
1377
1378bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
1379  video_channel_.reset(channel_manager_->CreateVideoChannel(
1380      this, content->name, true, voice_channel_.get()));
1381  if (!video_channel_.get())
1382    return false;
1383
1384  if (dscp_enabled_) {
1385    cricket::VideoOptions options;
1386    options.dscp.Set(true);
1387    video_channel_->SetChannelOptions(options);
1388  }
1389  return true;
1390}
1391
1392bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) {
1393  bool sctp = (data_channel_type_ == cricket::DCT_SCTP);
1394  data_channel_.reset(channel_manager_->CreateDataChannel(
1395      this, content->name, !sctp, data_channel_type_));
1396  if (!data_channel_.get()) {
1397    return false;
1398  }
1399  if (sctp) {
1400    mediastream_signaling_->OnDataTransportCreatedForSctp();
1401    data_channel_->SignalNewStreamReceived.connect(
1402        this, &WebRtcSession::OnNewDataChannelReceived);
1403  }
1404  return true;
1405}
1406
1407void WebRtcSession::CopySavedCandidates(
1408    SessionDescriptionInterface* dest_desc) {
1409  if (!dest_desc) {
1410    ASSERT(false);
1411    return;
1412  }
1413  for (size_t i = 0; i < saved_candidates_.size(); ++i) {
1414    dest_desc->AddCandidate(saved_candidates_[i]);
1415    delete saved_candidates_[i];
1416  }
1417  saved_candidates_.clear();
1418}
1419
1420void WebRtcSession::OnNewDataChannelReceived(
1421    const std::string& label, const DataChannelInit& init) {
1422  ASSERT(data_channel_type_ == cricket::DCT_SCTP);
1423  if (!mediastream_signaling_->AddDataChannelFromOpenMessage(
1424          label, init)) {
1425    LOG(LS_WARNING) << "Failed to create data channel from OPEN message.";
1426    return;
1427  }
1428}
1429
1430// Returns false if bundle is enabled and rtcp_mux is disabled.
1431bool WebRtcSession::ValidateBundleSettings(const SessionDescription* desc) {
1432  bool bundle_enabled = desc->HasGroup(cricket::GROUP_TYPE_BUNDLE);
1433  if (!bundle_enabled)
1434    return true;
1435
1436  const cricket::ContentGroup* bundle_group =
1437      desc->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
1438  ASSERT(bundle_group != NULL);
1439
1440  const cricket::ContentInfos& contents = desc->contents();
1441  for (cricket::ContentInfos::const_iterator citer = contents.begin();
1442       citer != contents.end(); ++citer) {
1443    const cricket::ContentInfo* content = (&*citer);
1444    ASSERT(content != NULL);
1445    if (bundle_group->HasContentName(content->name) &&
1446        !content->rejected && content->type == cricket::NS_JINGLE_RTP) {
1447      if (!HasRtcpMuxEnabled(content))
1448        return false;
1449    }
1450  }
1451  // RTCP-MUX is enabled in all the contents.
1452  return true;
1453}
1454
1455bool WebRtcSession::HasRtcpMuxEnabled(
1456    const cricket::ContentInfo* content) {
1457  const cricket::MediaContentDescription* description =
1458      static_cast<cricket::MediaContentDescription*>(content->description);
1459  return description->rtcp_mux();
1460}
1461
1462bool WebRtcSession::ValidateSessionDescription(
1463    const SessionDescriptionInterface* sdesc,
1464    cricket::ContentSource source, std::string* error_desc) {
1465
1466  if (error() != cricket::BaseSession::ERROR_NONE) {
1467    return BadSdp(source, SessionErrorMsg(error()), error_desc);
1468  }
1469
1470  if (!sdesc || !sdesc->description()) {
1471    return BadSdp(source, kInvalidSdp, error_desc);
1472  }
1473
1474  std::string type = sdesc->type();
1475  Action action = GetAction(sdesc->type());
1476  if (source == cricket::CS_LOCAL) {
1477    if (!ExpectSetLocalDescription(action))
1478      return BadSdp(source, BadStateErrMsg(type, state()), error_desc);
1479  } else {
1480    if (!ExpectSetRemoteDescription(action))
1481      return BadSdp(source, BadStateErrMsg(type, state()), error_desc);
1482  }
1483
1484  // Verify crypto settings.
1485  std::string crypto_error;
1486  if (webrtc_session_desc_factory_->Secure() == cricket::SEC_REQUIRED &&
1487      !VerifyCrypto(sdesc->description(), dtls_enabled_, &crypto_error)) {
1488    return BadSdp(source, crypto_error, error_desc);
1489  }
1490
1491  // Verify ice-ufrag and ice-pwd.
1492  if (!VerifyIceUfragPwdPresent(sdesc->description())) {
1493    return BadSdp(source, kSdpWithoutIceUfragPwd, error_desc);
1494  }
1495
1496  if (!ValidateBundleSettings(sdesc->description())) {
1497    return BadSdp(source, kBundleWithoutRtcpMux, error_desc);
1498  }
1499
1500  // Verify m-lines in Answer when compared against Offer.
1501  if (action == kAnswer) {
1502    const cricket::SessionDescription* offer_desc =
1503        (source == cricket::CS_LOCAL) ? remote_description()->description() :
1504            local_description()->description();
1505    if (!VerifyMediaDescriptions(sdesc->description(), offer_desc)) {
1506      return BadSdp(source, kMlineMismatch, error_desc);
1507    }
1508  }
1509
1510  return true;
1511}
1512
1513bool WebRtcSession::ExpectSetLocalDescription(Action action) {
1514  return ((action == kOffer && state() == STATE_INIT) ||
1515          // update local offer
1516          (action == kOffer && state() == STATE_SENTINITIATE) ||
1517          // update the current ongoing session.
1518          (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
1519          (action == kOffer && state() == STATE_SENTACCEPT) ||
1520          (action == kOffer && state() == STATE_INPROGRESS) ||
1521          // accept remote offer
1522          (action == kAnswer && state() == STATE_RECEIVEDINITIATE) ||
1523          (action == kAnswer && state() == STATE_SENTPRACCEPT) ||
1524          (action == kPrAnswer && state() == STATE_RECEIVEDINITIATE) ||
1525          (action == kPrAnswer && state() == STATE_SENTPRACCEPT));
1526}
1527
1528bool WebRtcSession::ExpectSetRemoteDescription(Action action) {
1529  return ((action == kOffer && state() == STATE_INIT) ||
1530          // update remote offer
1531          (action == kOffer && state() == STATE_RECEIVEDINITIATE) ||
1532          // update the current ongoing session
1533          (action == kOffer && state() == STATE_RECEIVEDACCEPT) ||
1534          (action == kOffer && state() == STATE_SENTACCEPT) ||
1535          (action == kOffer && state() == STATE_INPROGRESS) ||
1536          // accept local offer
1537          (action == kAnswer && state() == STATE_SENTINITIATE) ||
1538          (action == kAnswer && state() == STATE_RECEIVEDPRACCEPT) ||
1539          (action == kPrAnswer && state() == STATE_SENTINITIATE) ||
1540          (action == kPrAnswer && state() == STATE_RECEIVEDPRACCEPT));
1541}
1542
1543}  // namespace webrtc
Note: See TracBrowser for help on using the repository browser.