Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(568)

Side by Side Diff: remoting/protocol/webrtc_transport.cc

Issue 1427003009: Implement WebrtcTransport (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@transport_session.h
Patch Set: Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "remoting/protocol/webrtc_transport.h"
6
7 #include "base/callback_helpers.h"
8 #include "base/single_thread_task_runner.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/task_runner_util.h"
11 #include "jingle/glue/thread_wrapper.h"
12 #include "third_party/libjingle/source/talk/app/webrtc/test/fakeconstraints.h"
13 #include "third_party/webrtc/libjingle/xmllite/xmlelement.h"
14 #include "third_party/webrtc/modules/audio_device/include/fake_audio_device.h"
15
16 using buzz::QName;
17 using buzz::XmlElement;
18
19 namespace remoting {
20 namespace protocol {
21
22 namespace {
23
24 // Delay after candidate creation before sending transport-info message to
25 // accumulate multiple candidates. This is an optimization to reduce number of
26 // transport-info messages.
27 const int kTransportInfoSendDelayMs = 20;
28
29 // XML namespace for the transport elements.
30 const char kTransportNamespace[] = "google:remoting:webrtc";
31
32 rtc::Thread* InitAndGetRtcThread() {
33 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
34
35 // TODO(sergeyu): Investigate if it's possible to avoid Send().
36 jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true);
37
38 return jingle_glue::JingleThreadWrapper::current();
39 }
40
41 // A webrtc::CreateSessionDescriptionObserver implementation used to receive the
42 // results of creating descriptions for this end of the PeerConnection.
43 class CreateSessionDescriptionObserver
44 : public webrtc::CreateSessionDescriptionObserver {
45 public:
46 typedef base::Callback<void(
47 scoped_ptr<webrtc::SessionDescriptionInterface> description,
48 const std::string& error)> ResultCallback;
49
50 static CreateSessionDescriptionObserver* Create(
51 const ResultCallback& result_callback) {
52 return new rtc::RefCountedObject<CreateSessionDescriptionObserver>(
53 result_callback);
54 }
55 void OnSuccess(webrtc::SessionDescriptionInterface* desc) override {
56 base::ResetAndReturn(&result_callback_)
57 .Run(make_scoped_ptr(desc), std::string());
58 }
59 void OnFailure(const std::string& error) override {
60 base::ResetAndReturn(&result_callback_).Run(nullptr, error);
61 }
62
63 protected:
64 explicit CreateSessionDescriptionObserver(
65 const ResultCallback& result_callback)
66 : result_callback_(result_callback) {}
67 ~CreateSessionDescriptionObserver() override {}
68
69 private:
70 ResultCallback result_callback_;
71
72 DISALLOW_COPY_AND_ASSIGN(CreateSessionDescriptionObserver);
73 };
74
75 // A webrtc::SetSessionDescriptionObserver implementation used to receive the
76 // results of setting local and remote descriptions of the PeerConnection.
77 class SetSessionDescriptionObserver
78 : public webrtc::SetSessionDescriptionObserver {
79 public:
80 typedef base::Callback<void(bool success, const std::string& error)>
81 ResultCallback;
82
83 static SetSessionDescriptionObserver* Create(
84 const ResultCallback& result_callback) {
85 return new rtc::RefCountedObject<SetSessionDescriptionObserver>(
86 result_callback);
87 }
88
89 void OnSuccess() override {
90 base::ResetAndReturn(&result_callback_).Run(true, std::string());
91 }
92
93 void OnFailure(const std::string& error) override {
94 base::ResetAndReturn(&result_callback_).Run(false, error);
95 }
96
97 protected:
98 SetSessionDescriptionObserver(const ResultCallback& result_callback)
99 : result_callback_(result_callback) {}
100 ~SetSessionDescriptionObserver() override {}
101
102 private:
103 ResultCallback result_callback_;
104
105 DISALLOW_COPY_AND_ASSIGN(SetSessionDescriptionObserver);
106 };
107
108 } // namespace
109
110 WebrtcTransport::WebrtcTransport(
111 rtc::scoped_refptr<webrtc::PortAllocatorFactoryInterface>
112 port_allocator_factory,
113 TransportRole role,
114 scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner)
115 : port_allocator_factory_(port_allocator_factory),
116 role_(role),
117 worker_task_runner_(worker_task_runner),
118 weak_factory_(this) {}
119
120 WebrtcTransport::~WebrtcTransport() {}
121
122 void WebrtcTransport::Start(EventHandler* event_handler,
123 Authenticator* authenticator) {
124 DCHECK(thread_checker_.CalledOnValidThread());
125
126 event_handler_ = event_handler;
127
128 // TODO(sergeyu): Use the |authenticator| to authenticate PeerConnection.
129
130 base::PostTaskAndReplyWithResult(
131 worker_task_runner_.get(), FROM_HERE, base::Bind(&InitAndGetRtcThread),
132 base::Bind(&WebrtcTransport::DoStart, weak_factory_.GetWeakPtr()));
133 }
134
135 bool WebrtcTransport::ProcessTransportInfo(XmlElement* transport_info) {
136 DCHECK(thread_checker_.CalledOnValidThread());
137
138 if (transport_info->Name() != QName(kTransportNamespace, "transport"))
139 return false;
140
141 if (!peer_connection_)
142 return false;
143
144 XmlElement* session_description = transport_info->FirstNamed(
145 QName(kTransportNamespace, "session-description"));
146 if (session_description) {
147 webrtc::PeerConnectionInterface::SignalingState expected_state =
148 role_ == TransportRole::SERVER
149 ? webrtc::PeerConnectionInterface::kStable
150 : webrtc::PeerConnectionInterface::kHaveLocalOffer;
151 if (peer_connection_->signaling_state() != expected_state) {
152 LOG(ERROR) << "Received unexpected WebRTC session_description. ";
153 return false;
154 }
155
156 std::string type = session_description->Attr(QName(std::string(), "type"));
157 std::string sdp = session_description->BodyText();
158 if (type.empty() || sdp.empty()) {
159 LOG(ERROR) << "Incorrect session_description format.";
160 return false;
161 }
162
163 webrtc::SdpParseError error;
164 scoped_ptr<webrtc::SessionDescriptionInterface> session_description(
165 webrtc::CreateSessionDescription(type, sdp, &error));
166 if (!session_description) {
167 LOG(ERROR) << "Failed to parse the offer: " << error.description
168 << " line: " << error.line;
169 return false;
170 }
171
172 peer_connection_->SetRemoteDescription(
173 SetSessionDescriptionObserver::Create(
174 base::Bind(&WebrtcTransport::OnRemoteDescriptionSet,
175 weak_factory_.GetWeakPtr())),
176 session_description.release());
177 }
178
179 XmlElement* candidate_element;
180 QName candidate_qname(kTransportNamespace, "candidate");
181 for (candidate_element = transport_info->FirstNamed(candidate_qname);
182 candidate_element;
183 candidate_element = candidate_element->NextNamed(candidate_qname)) {
184 std::string candidate_str = candidate_element->BodyText();
185 std::string sdp_mid =
186 candidate_element->Attr(QName(std::string(), "sdpMid"));
187 std::string sdp_mlineindex_str =
188 candidate_element->Attr(QName(std::string(), "sdpMLineIndex"));
189 int sdp_mlineindex;
190 if (candidate_str.empty() || sdp_mid.empty() ||
191 !base::StringToInt(sdp_mlineindex_str, &sdp_mlineindex)) {
192 LOG(ERROR) << "Failed to parse incoming candidates.";
193 return false;
194 }
195
196 webrtc::SdpParseError error;
197 scoped_ptr<webrtc::IceCandidateInterface> candidate(
198 webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, candidate_str,
199 &error));
200 if (!candidate) {
201 LOG(ERROR) << "Failed to parse incoming candidate: " << error.description
202 << " line: " << error.line;
203 return false;
204 }
205
206 if (peer_connection_->signaling_state() ==
207 webrtc::PeerConnectionInterface::kStable) {
208 if (!peer_connection_->AddIceCandidate(candidate.get())) {
209 LOG(ERROR) << "Failed to add incoming ICE candidate.";
210 return false;
211 }
212 } else {
213 pending_incoming_candidates_.push_back(candidate.Pass());
214 }
215 }
216
217 return true;
218 }
219
220 DatagramChannelFactory* WebrtcTransport::GetDatagramChannelFactory() {
221 DCHECK(thread_checker_.CalledOnValidThread());
222 NOTIMPLEMENTED();
223 return nullptr;
224 }
225
226 StreamChannelFactory* WebrtcTransport::GetStreamChannelFactory() {
227 DCHECK(thread_checker_.CalledOnValidThread());
228 // TODO(sergeyu): Implement data stream support.
229 NOTIMPLEMENTED();
230 return nullptr;
231 }
232
233 StreamChannelFactory* WebrtcTransport::GetMultiplexedChannelFactory() {
234 DCHECK(thread_checker_.CalledOnValidThread());
235 return GetStreamChannelFactory();
236 }
237
238 void WebrtcTransport::DoStart(rtc::Thread* worker_thread) {
239 DCHECK(thread_checker_.CalledOnValidThread());
240
241 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
242
243 // TODO(sergeyu): Investigate if it's possible to avoid Send().
244 jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true);
245
246 fake_audio_device_module_.reset(new webrtc::FakeAudioDeviceModule());
247
248 peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(
249 worker_thread, rtc::Thread::Current(),
250 fake_audio_device_module_.get(), nullptr, nullptr);
251
252 webrtc::PeerConnectionInterface::IceServer stun_server;
253 stun_server.urls.push_back("stun:stun.l.google.com:19302");
254 webrtc::PeerConnectionInterface::RTCConfiguration rtc_config;
255 rtc_config.servers.push_back(stun_server);
256
257 webrtc::FakeConstraints constraints;
258 constraints.AddMandatory(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp,
259 webrtc::MediaConstraintsInterface::kValueTrue);
260
261 peer_connection_ = peer_connection_factory_->CreatePeerConnection(
262 rtc_config, &constraints, port_allocator_factory_, nullptr, this);
263
264 if (role_ == TransportRole::CLIENT) {
265 webrtc::FakeConstraints offer_config;
266 offer_config.AddMandatory(
267 webrtc::MediaConstraintsInterface::kOfferToReceiveVideo,
268 webrtc::MediaConstraintsInterface::kValueTrue);
269 offer_config.AddMandatory(
270 webrtc::MediaConstraintsInterface::kOfferToReceiveAudio,
271 webrtc::MediaConstraintsInterface::kValueFalse);
272 peer_connection_->CreateOffer(
273 CreateSessionDescriptionObserver::Create(
274 base::Bind(&WebrtcTransport::OnLocalSessionDescriptionCreated,
275 weak_factory_.GetWeakPtr())),
276 &offer_config);
277 }
278 }
279
280 void WebrtcTransport::OnLocalSessionDescriptionCreated(
281 scoped_ptr<webrtc::SessionDescriptionInterface> description,
282 const std::string& error) {
283 DCHECK(thread_checker_.CalledOnValidThread());
284
285 if (!peer_connection_)
286 return;
287
288 if (!description) {
289 LOG(ERROR) << "PeerConnection offer creation failed: " << error;
290 Close(CHANNEL_CONNECTION_ERROR);
291 return;
292 }
293
294 std::string description_sdp;
295 if (!description->ToString(&description_sdp)) {
296 LOG(ERROR) << "Failed to serialize description.";
297 Close(CHANNEL_CONNECTION_ERROR);
298 return;
299 }
300
301 // Format and send the session description to the peer.
302 scoped_ptr<XmlElement> transport_info(
303 new XmlElement(QName(kTransportNamespace, "transport"), true));
304 XmlElement* offer_tag =
305 new XmlElement(QName(kTransportNamespace, "session-description"));
306 transport_info->AddElement(offer_tag);
307 offer_tag->SetAttr(QName(std::string(), "type"), description->type());
308 offer_tag->SetBodyText(description_sdp);
309
310 event_handler_->OnOutgoingTransportInfo(transport_info.Pass());
311
312 peer_connection_->SetLocalDescription(
313 SetSessionDescriptionObserver::Create(base::Bind(
314 &WebrtcTransport::OnLocalDescriptionSet, weak_factory_.GetWeakPtr())),
315 description.release());
316 }
317
318 void WebrtcTransport::OnLocalDescriptionSet(bool success,
319 const std::string& error) {
320 DCHECK(thread_checker_.CalledOnValidThread());
321
322 if (!peer_connection_)
323 return;
324
325 if (!success) {
326 LOG(ERROR) << "Failed to set local description: " << error;
327 Close(CHANNEL_CONNECTION_ERROR);
328 return;
329 }
330
331 AddPendingCandidatesIfPossible();
332 }
333
334 void WebrtcTransport::OnRemoteDescriptionSet(bool success,
335 const std::string& error) {
336 DCHECK(thread_checker_.CalledOnValidThread());
337
338 if (!peer_connection_)
339 return;
340
341 if (!success) {
342 LOG(ERROR) << "Failed to set local description: " << error;
343 Close(CHANNEL_CONNECTION_ERROR);
344 return;
345 }
346
347 // Create and send answer on the server.
348 if (role_ == TransportRole::SERVER) {
349 peer_connection_->CreateAnswer(
350 CreateSessionDescriptionObserver::Create(
351 base::Bind(&WebrtcTransport::OnLocalSessionDescriptionCreated,
352 weak_factory_.GetWeakPtr())),
353 nullptr);
354 }
355
356 AddPendingCandidatesIfPossible();
357 }
358
359 void WebrtcTransport::Close(ErrorCode error) {
360 DCHECK(thread_checker_.CalledOnValidThread());
361
362 weak_factory_.InvalidateWeakPtrs();
363 peer_connection_->Close();
364 peer_connection_ = nullptr;
365 peer_connection_factory_ = nullptr;
366
367 if (error != OK)
368 event_handler_->OnTransportError(error);
369 }
370
371 void WebrtcTransport::OnSignalingChange(
372 webrtc::PeerConnectionInterface::SignalingState new_state) {
373 DCHECK(thread_checker_.CalledOnValidThread());
374 }
375
376 void WebrtcTransport::OnAddStream(webrtc::MediaStreamInterface* stream) {
377 DCHECK(thread_checker_.CalledOnValidThread());
378 LOG(ERROR) << "Stream added " << stream->label();
379 }
380
381 void WebrtcTransport::OnRemoveStream(webrtc::MediaStreamInterface* stream) {
382 DCHECK(thread_checker_.CalledOnValidThread());
383 LOG(ERROR) << "Stream removed " << stream->label();
384 }
385
386 void WebrtcTransport::OnDataChannel(
387 webrtc::DataChannelInterface* data_channel) {
388 DCHECK(thread_checker_.CalledOnValidThread());
389 // TODO(sergeyu): Use the data channel.
390 }
391
392 void WebrtcTransport::OnRenegotiationNeeded() {
393 DCHECK(thread_checker_.CalledOnValidThread());
394 // TODO(sergeyu): Figure out what needs to happen here.
395 }
396
397 void WebrtcTransport::OnIceConnectionChange(
398 webrtc::PeerConnectionInterface::IceConnectionState new_state) {
399 DCHECK(thread_checker_.CalledOnValidThread());
400
401 if (new_state == webrtc::PeerConnectionInterface::kIceConnectionConnected)
402 event_handler_->OnTransportConnected();
403 }
404
405 void WebrtcTransport::OnIceGatheringChange(
406 webrtc::PeerConnectionInterface::IceGatheringState new_state) {
407 DCHECK(thread_checker_.CalledOnValidThread());
408 }
409
410 void WebrtcTransport::OnIceCandidate(
411 const webrtc::IceCandidateInterface* candidate) {
412 DCHECK(thread_checker_.CalledOnValidThread());
413
414 scoped_ptr<XmlElement> candidate_element(
415 new XmlElement(QName(kTransportNamespace, "candidate")));
416 std::string candidate_str;
417 if (!candidate->ToString(&candidate_str)) {
418 LOG(ERROR) << "Failed to serialize local candidate.";
419 return;
420 }
421 candidate_element->SetBodyText(candidate_str);
422 candidate_element->SetAttr(QName(std::string(), "sdpMid"),
423 candidate->sdp_mid());
424 candidate_element->SetAttr(QName(std::string(), "sdpMLineIndex"),
425 base::IntToString(candidate->sdp_mline_index()));
426
427 EnsurePendingTransportInfoMessage();
428 pending_transport_info_message_->AddElement(candidate_element.release());
429 }
430
431 void WebrtcTransport::EnsurePendingTransportInfoMessage() {
432 DCHECK(thread_checker_.CalledOnValidThread());
433
434 // |transport_info_timer_| must be running iff
435 // |pending_transport_info_message_| exists.
436 DCHECK_EQ(pending_transport_info_message_ != nullptr,
437 transport_info_timer_.IsRunning());
438
439 if (!pending_transport_info_message_) {
440 pending_transport_info_message_.reset(
441 new XmlElement(QName(kTransportNamespace, "transport"), true));
442
443 // Delay sending the new candidates in case we get more candidates
444 // that we can send in one message.
445 transport_info_timer_.Start(
446 FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs),
447 this, &WebrtcTransport::SendTransportInfo);
448 }
449 }
450
451 void WebrtcTransport::SendTransportInfo() {
452 DCHECK(thread_checker_.CalledOnValidThread());
453 DCHECK(pending_transport_info_message_);
454
455 event_handler_->OnOutgoingTransportInfo(
456 pending_transport_info_message_.Pass());
457 pending_transport_info_message_.reset();
458 }
459
460 void WebrtcTransport::AddPendingCandidatesIfPossible() {
461 DCHECK(thread_checker_.CalledOnValidThread());
462
463 if (peer_connection_->signaling_state() ==
464 webrtc::PeerConnectionInterface::kStable) {
465 for (auto candidate : pending_incoming_candidates_) {
466 if (!peer_connection_->AddIceCandidate(candidate)) {
467 LOG(ERROR) << "Failed to add incoming candidate";
468 Close(INCOMPATIBLE_PROTOCOL);
469 return;
470 }
471 }
472 pending_incoming_candidates_.clear();
473 }
474 }
475
476 WebrtcTransportFactory::WebrtcTransportFactory(
477 SignalStrategy* signal_strategy,
478 rtc::scoped_refptr<webrtc::PortAllocatorFactoryInterface>
479 port_allocator_factory,
480 TransportRole role)
481 : signal_strategy_(signal_strategy),
482 port_allocator_factory_(port_allocator_factory),
483 role_(role),
484 worker_thread_("ChromotingWebrtcWorkerThread") {
485 worker_thread_.StartWithOptions(
486 base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
487 }
488
489 WebrtcTransportFactory::~WebrtcTransportFactory() {}
490
491 scoped_ptr<Transport> WebrtcTransportFactory::CreateTransport() {
492 return make_scoped_ptr(new WebrtcTransport(port_allocator_factory_, role_,
493 worker_thread_.task_runner()));
494 }
495
496 } // namespace protocol
497 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698