3
* Copyright 2012, Google Inc.
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions are met:
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.
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.
28
#include "talk/app/webrtc/webrtcsession.h"
30
#include "talk/app/webrtc/jsepicecandidate.h"
31
#include "talk/app/webrtc/jsepsessiondescription.h"
32
#include "talk/app/webrtc/mediastreamsignaling.h"
33
#include "talk/app/webrtc/mediastreaminterface.h"
34
#include "talk/app/webrtc/peerconnection.h"
35
#include "talk/base/helpers.h"
36
#include "talk/base/logging.h"
37
#include "talk/base/stringencode.h"
38
#include "talk/session/phone/channel.h"
39
#include "talk/session/phone/channelmanager.h"
40
#include "talk/session/phone/mediasession.h"
41
#include "talk/session/phone/videocapturer.h"
43
using cricket::MediaContentDescription;
48
MSG_CANDIDATE_TIMEOUT = 101,
49
MSG_CANDIDATE_DISCOVERY_TIMEOUT = 102,
52
// We allow 30 seconds to establish a connection, otherwise it's an error.
53
static const int kCallSetupTimeout = 30 * 1000;
54
static const int kCandidateDiscoveryTimeout = 2000;
56
// Constants for setting the default encoder size.
57
// TODO: Implement proper negotiation of video resolution.
58
static const int kDefaultVideoCodecId = 100;
59
static const int kDefaultVideoCodecFramerate = 30;
60
static const char kDefaultVideoCodecName[] = "VP8";
61
static const int kDefaultVideoCodecWidth = 640;
62
static const int kDefaultVideoCodecHeight = 480;
64
static cricket::ContentAction GetContentAction(JsepInterface::Action action) {
66
case JsepInterface::kOffer:
67
return cricket::CA_OFFER;
68
case JsepInterface::kAnswer:
69
return cricket::CA_ANSWER;
71
ASSERT(!"Not supported action");
73
return cricket::CA_OFFER;
76
static void CopyCandidatesFromSessionDescription(
77
const SessionDescriptionInterface* source_desc,
78
SessionDescriptionInterface* dest_desc) {
81
for (size_t m = 0; m < source_desc->number_of_mediasections(); ++m) {
82
const IceCandidateColletion* source_candidates = source_desc->candidates(m);
83
const IceCandidateColletion* desc_candidates = dest_desc->candidates(m);
84
for (size_t n = 0; n < source_candidates->count(); ++n) {
85
const IceCandidateInterface* new_candidate = source_candidates->at(n);
86
if (!desc_candidates->HasCandidate(new_candidate))
87
dest_desc->AddCandidate(source_candidates->at(n));
92
WebRtcSession::WebRtcSession(cricket::ChannelManager* channel_manager,
93
talk_base::Thread* signaling_thread,
94
talk_base::Thread* worker_thread,
95
cricket::PortAllocator* port_allocator,
96
MediaStreamSignaling* mediastream_signaling)
97
: cricket::BaseSession(signaling_thread, worker_thread, port_allocator,
98
talk_base::ToString(talk_base::CreateRandomId()),
99
cricket::NS_JINGLE_RTP, true),
100
channel_manager_(channel_manager),
101
session_desc_factory_(channel_manager),
103
mediastream_signaling_(mediastream_signaling),
104
ice_observer_(NULL) {
107
WebRtcSession::~WebRtcSession() {
108
if (voice_channel_.get()) {
109
channel_manager_->DestroyVoiceChannel(voice_channel_.release());
111
if (video_channel_.get()) {
112
channel_manager_->DestroyVideoChannel(video_channel_.release());
116
bool WebRtcSession::Initialize() {
117
// By default SRTP-SDES is enabled in WebRtc.
118
set_secure_policy(cricket::SEC_REQUIRED);
119
// Make sure SessionDescriptions only contains the StreamParams we negotiate.
120
session_desc_factory_.set_add_legacy_streams(false);
122
const cricket::VideoCodec default_codec(kDefaultVideoCodecId,
123
kDefaultVideoCodecName, kDefaultVideoCodecWidth, kDefaultVideoCodecHeight,
124
kDefaultVideoCodecFramerate, 0);
125
channel_manager_->SetDefaultVideoEncoderConfig(
126
cricket::VideoEncoderConfig(default_codec));
128
return CreateChannels();
131
bool WebRtcSession::StartIce(IceOptions /*options*/) {
132
if (!local_description()) {
133
LOG(LS_ERROR) << "StartIce called before SetLocalDescription";
137
// TODO: Take IceOptions into consideration and restart of the
142
// Try connecting all transport channels. This is necessary to generate
144
SpeculativelyConnectAllTransportChannels();
145
signaling_thread()->PostDelayed(
146
kCandidateDiscoveryTimeout, this, MSG_CANDIDATE_DISCOVERY_TIMEOUT);
149
if (!UseCandidatesInSessionDescription(remote_desc_.get())) {
150
LOG(LS_WARNING) << "StartIce: Can't use candidates in remote session"
156
void WebRtcSession::set_secure_policy(
157
cricket::SecureMediaPolicy secure_policy) {
158
session_desc_factory_.set_secure(secure_policy);
161
SessionDescriptionInterface* WebRtcSession::CreateOffer(
162
const MediaHints& hints) {
163
cricket::MediaSessionOptions options =
164
mediastream_signaling_->GetMediaSessionOptions(hints);
165
cricket::SessionDescription* desc(
166
session_desc_factory_.CreateOffer(options,
167
BaseSession::local_description()));
168
SessionDescriptionInterface* offer = new JsepSessionDescription(desc);
169
if (local_description())
170
CopyCandidatesFromSessionDescription(local_description(), offer);
174
SessionDescriptionInterface* WebRtcSession::CreateAnswer(
175
const MediaHints& hints,
176
const SessionDescriptionInterface* offer) {
177
cricket::MediaSessionOptions options =
178
mediastream_signaling_->GetMediaSessionOptions(hints);
179
cricket::SessionDescription* desc(
180
session_desc_factory_.CreateAnswer(offer->description(), options,
181
BaseSession::local_description()));
182
SessionDescriptionInterface* answer = new JsepSessionDescription(desc);
183
if (local_description())
184
CopyCandidatesFromSessionDescription(local_description(), answer);
188
bool WebRtcSession::SetLocalDescription(Action action,
189
SessionDescriptionInterface* desc) {
190
cricket::ContentAction type = GetContentAction(action);
191
if ((type == cricket::CA_ANSWER &&
192
state() != STATE_RECEIVEDINITIATE) ||
193
(type == cricket::CA_OFFER &&
194
(state() == STATE_RECEIVEDINITIATE ||
195
state() == STATE_SENTINITIATE))) {
196
LOG(LS_ERROR) << "SetLocalDescription called with action in wrong state, "
197
<< "action: " << type << " state: " << state();
200
if (!desc || !desc->description()) {
201
LOG(LS_ERROR) << "SetLocalDescription called with an invalid session"
206
set_local_description(desc->description()->Copy());
207
local_desc_.reset(desc);
209
if (type == cricket::CA_ANSWER) {
211
SetState(STATE_SENTACCEPT);
213
SetState(STATE_SENTINITIATE);
218
bool WebRtcSession::SetRemoteDescription(Action action,
219
SessionDescriptionInterface* desc) {
220
cricket::ContentAction type = GetContentAction(action);
221
if ((type == cricket::CA_ANSWER &&
222
state() != STATE_SENTINITIATE) ||
223
(type == cricket::CA_OFFER &&
224
(state() == STATE_RECEIVEDINITIATE ||
225
state() == STATE_SENTINITIATE))) {
226
LOG(LS_ERROR) << "SetRemoteDescription called with action in wrong state, "
227
<< "action: " << type << " state: " << state();
230
if (!desc || !desc->description()) {
231
LOG(LS_ERROR) << "SetRemoteDescription called with an invalid session"
236
set_remote_description(desc->description()->Copy());
237
if (type == cricket::CA_ANSWER) {
239
SetState(STATE_RECEIVEDACCEPT);
241
SetState(STATE_RECEIVEDINITIATE);
243
// Update remote MediaStreams.
244
mediastream_signaling_->UpdateRemoteStreams(desc);
246
// Use all candidates in this new session description if ice is started.
247
if (ice_started_ && !UseCandidatesInSessionDescription(desc)) {
248
LOG(LS_ERROR) << "SetRemoteDescription: Argument |desc| contains "
249
<< "invalid candidates";
252
// We retain all received candidates.
253
CopyCandidatesFromSessionDescription(remote_desc_.get(), desc);
254
remote_desc_.reset(desc);
258
bool WebRtcSession::ProcessIceMessage(const IceCandidateInterface* candidate) {
259
if (!remote_description()) {
260
LOG(LS_ERROR) << "Remote description not set";
265
LOG(LS_ERROR) << "ProcessIceMessage: Candidate is NULL";
269
// Add this candidate to the remote session description.
270
if (!remote_desc_->AddCandidate(candidate)) {
271
LOG(LS_ERROR) << "ProcessIceMessage: Candidate cannot be used";
275
if (ice_started_) { // Use this candidate now if we have started ice.
276
return UseCandidate(candidate);
281
void WebRtcSession::OnMessage(talk_base::Message* msg) {
282
switch (msg->message_id) {
283
case MSG_CANDIDATE_TIMEOUT:
284
LOG(LS_ERROR) << "Transport is not in writable state.";
287
case MSG_CANDIDATE_DISCOVERY_TIMEOUT:
289
ice_observer_->OnIceComplete();
296
bool WebRtcSession::SetCaptureDevice(const std::string& name,
297
cricket::VideoCapturer* camera) {
298
// should be called from a signaling thread
299
ASSERT(signaling_thread()->IsCurrent());
301
// TODO: Refactor this when there is support for multiple cameras.
302
const uint32 dummy_ssrc = 0;
303
if (!channel_manager_->SetVideoCapturer(camera, dummy_ssrc)) {
304
LOG(LS_ERROR) << "Failed to set capture device.";
308
const bool start_capture = (camera != NULL);
309
cricket::CaptureResult ret = channel_manager_->SetVideoCapture(start_capture);
310
if (ret != cricket::CR_SUCCESS && ret != cricket::CR_PENDING) {
311
LOG(LS_ERROR) << "Failed to start the capture device.";
318
void WebRtcSession::SetLocalRenderer(const std::string& name,
319
cricket::VideoRenderer* renderer) {
320
ASSERT(signaling_thread()->IsCurrent());
321
// TODO: Fix SetLocalRenderer.
322
// video_channel_->SetLocalRenderer(0, renderer);
325
void WebRtcSession::SetRemoteRenderer(const std::string& name,
326
cricket::VideoRenderer* renderer) {
327
ASSERT(signaling_thread()->IsCurrent());
329
const cricket::ContentInfo* video_info =
330
cricket::GetFirstVideoContent(BaseSession::remote_description());
332
LOG(LS_ERROR) << "Video not received in this call";
335
const cricket::MediaContentDescription* video_content =
336
static_cast<const cricket::MediaContentDescription*>(
337
video_info->description);
338
cricket::StreamParams stream;
339
if (cricket::GetStreamByNickAndName(video_content->streams(), "", name,
341
video_channel_->SetRenderer(stream.first_ssrc(), renderer);
343
// Allow that |stream| does not exist if renderer is null but assert
345
VERIFY(renderer == NULL);
349
void WebRtcSession::OnTransportRequestSignaling(
350
cricket::Transport* transport) {
351
ASSERT(signaling_thread()->IsCurrent());
352
transport->OnSignalingReady();
355
void WebRtcSession::OnTransportConnecting(cricket::Transport* transport) {
356
ASSERT(signaling_thread()->IsCurrent());
357
// start monitoring for the write state of the transport.
358
OnTransportWritable(transport);
361
void WebRtcSession::OnTransportWritable(cricket::Transport* transport) {
362
ASSERT(signaling_thread()->IsCurrent());
363
// If the transport is not in writable state, start a timer to monitor
364
// the state. If the transport doesn't become writable state in 30 seconds
365
// then we are assuming call can't be continued.
366
signaling_thread()->Clear(this, MSG_CANDIDATE_TIMEOUT);
367
if (transport->HasChannels() && !transport->writable()) {
368
signaling_thread()->PostDelayed(
369
kCallSetupTimeout, this, MSG_CANDIDATE_TIMEOUT);
373
void WebRtcSession::OnTransportCandidatesReady(
374
cricket::Transport* transport, const cricket::Candidates& candidates) {
375
ASSERT(signaling_thread()->IsCurrent());
377
cricket::TransportProxy* proxy = GetTransportProxy(transport);
378
if (!VERIFY(proxy != NULL)) {
379
LOG(LS_ERROR) << "No Proxy found";
382
ProcessNewLocalCandidate(proxy->content_name(), candidates);
385
void WebRtcSession::OnTransportChannelGone(cricket::Transport* transport,
386
const std::string& name) {
387
ASSERT(signaling_thread()->IsCurrent());
390
bool WebRtcSession::CreateChannels() {
391
voice_channel_.reset(channel_manager_->CreateVoiceChannel(
392
this, cricket::CN_AUDIO, true));
393
if (!voice_channel_.get()) {
394
LOG(LS_ERROR) << "Failed to create voice channel";
398
video_channel_.reset(channel_manager_->CreateVideoChannel(
399
this, cricket::CN_VIDEO, true, voice_channel_.get()));
400
if (!video_channel_.get()) {
401
LOG(LS_ERROR) << "Failed to create video channel";
405
// TransportProxies and TransportChannels will be created when
406
// CreateVoiceChannel and CreateVideoChannel are called.
410
// Enabling voice and video channel.
411
void WebRtcSession::EnableChannels() {
412
if (!voice_channel_->enabled())
413
voice_channel_->Enable(true);
415
if (!video_channel_->enabled())
416
video_channel_->Enable(true);
419
void WebRtcSession::ProcessNewLocalCandidate(
420
const std::string& content_name,
421
const cricket::Candidates& candidates) {
422
std::string candidate_label;
424
if (!GetLocalCandidateLabel(content_name, &candidate_label)) {
425
LOG(LS_ERROR) << "ProcessNewLocalCandidate: content name "
426
<< content_name << " not found";
430
for (cricket::Candidates::const_iterator citer = candidates.begin();
431
citer != candidates.end(); ++citer) {
432
JsepIceCandidate candidate(candidate_label, *citer);
434
ice_observer_->OnIceCandidate(&candidate);
436
if (local_desc_.get()) {
437
local_desc_->AddCandidate(&candidate);
442
// Returns a label for a local ice candidate given the content name.
443
bool WebRtcSession::GetLocalCandidateLabel(const std::string& content_name,
444
std::string* label) {
445
if (!BaseSession::local_description() || !label)
448
bool content_found = false;
449
const cricket::ContentInfos& contents =
450
BaseSession::local_description()->contents();
451
for (size_t index = 0; index < contents.size(); ++index) {
452
if (contents[index].name == content_name) {
453
*label = talk_base::ToString(index);
454
content_found = true;
458
return content_found;
461
bool WebRtcSession::UseCandidatesInSessionDescription(
462
const SessionDescriptionInterface* remote_desc) {
466
for (size_t m = 0; m < remote_desc->number_of_mediasections(); ++m) {
467
const IceCandidateColletion* candidates = remote_desc->candidates(m);
468
for (size_t n = 0; n < candidates->count(); ++n) {
469
ret = UseCandidate(candidates->at(n));
477
bool WebRtcSession::UseCandidate(
478
const IceCandidateInterface* candidate) {
480
size_t mediacontent_index;
481
size_t remote_content_size =
482
BaseSession::remote_description()->contents().size();
483
if ((!talk_base::FromString<size_t>(candidate->label(),
484
&mediacontent_index)) ||
485
(mediacontent_index >= remote_content_size)) {
486
LOG(LS_ERROR) << "UseRemoteCandidateInSession: Invalid candidate label";
490
cricket::ContentInfo content =
491
BaseSession::remote_description()->contents()[mediacontent_index];
493
std::string local_content_name;
494
if (cricket::IsAudioContent(&content)) {
495
local_content_name = cricket::CN_AUDIO;
496
} else if (cricket::IsVideoContent(&content)) {
497
local_content_name = cricket::CN_VIDEO;
500
// TODO: Justins comment:This is bad encapsulation, suggest we add a
501
// helper to BaseSession to allow us to
502
// pass in candidates without touching the transport proxies.
503
cricket::TransportProxy* proxy = GetTransportProxy(local_content_name);
505
LOG(LS_ERROR) << "No TransportProxy exists with name "
506
<< local_content_name;
509
// CompleteNegotiation will set actual impl's in Proxy.
510
if (!proxy->negotiated())
511
proxy->CompleteNegotiation();
513
// TODO - Add a interface to TransportProxy to accept
514
// a remote candidate.
515
std::vector<cricket::Candidate> candidates;
516
candidates.push_back(candidate->candidate());
517
proxy->impl()->OnRemoteCandidates(candidates);
521
} // namespace webrtc