Chromium Code Reviews

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.
Jump to:
View unified diff |
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 event_handler_ = event_handler;
125 // FIXME: use authenticator.
kelvinp 2015/11/11 23:38:48 Should this be a TODO statement so that it is cons
Sergey Ulanov 2015/11/17 01:40:32 Done.
126
127 base::PostTaskAndReplyWithResult(
128 worker_task_runner_.get(), FROM_HERE, base::Bind(&InitAndGetRtcThread),
129 base::Bind(&WebrtcTransport::DoStart, weak_factory_.GetWeakPtr()));
130 }
131
132 bool WebrtcTransport::ProcessTransportInfo(XmlElement* transport_info) {
133 if (transport_info->Name() != QName(kTransportNamespace, "transport"))
134 return false;
135
136 if (!peer_connection_)
137 return false;
138
139 XmlElement* session_description = transport_info->FirstNamed(
140 QName(kTransportNamespace, "session-description"));
141 if (session_description) {
142 webrtc::PeerConnectionInterface::SignalingState expected_state =
143 role_ == TransportRole::SERVER
144 ? webrtc::PeerConnectionInterface::kStable
145 : webrtc::PeerConnectionInterface::kHaveLocalOffer;
146 if (peer_connection_->signaling_state() != expected_state) {
147 LOG(ERROR) << "Received unexpected WebRTC session_description. ";
148 return false;
149 }
150
151 std::string type = session_description->Attr(QName(std::string(), "type"));
152 std::string sdp = session_description->BodyText();
153 if (type.empty() || sdp.empty()) {
154 LOG(ERROR) << "Incorrect session_description format.";
155 return false;
156 }
157
158 webrtc::SdpParseError error;
159 scoped_ptr<webrtc::SessionDescriptionInterface> session_description(
160 webrtc::CreateSessionDescription(type, sdp, &error));
161 if (!session_description) {
162 LOG(ERROR) << "Failed to parse the offer: " << error.description
163 << " line: " << error.line;
164 return false;
165 }
166
167 peer_connection_->SetRemoteDescription(
168 SetSessionDescriptionObserver::Create(
169 base::Bind(&WebrtcTransport::OnRemoteDescriptionSet,
170 weak_factory_.GetWeakPtr())),
171 session_description.release());
172 }
173
174 XmlElement* candidate_element;
175 QName candidate_qname(kTransportNamespace, "candidate");
176 for (candidate_element = transport_info->FirstNamed(candidate_qname);
177 candidate_element;
178 candidate_element = candidate_element->NextNamed(candidate_qname)) {
179 std::string candidate_str = candidate_element->BodyText();
180 std::string sdp_mid =
181 candidate_element->Attr(QName(std::string(), "sdpMid"));
182 std::string sdp_mlineindex_str =
183 candidate_element->Attr(QName(std::string(), "sdpMLineIndex"));
184 int sdp_mlineindex;
185 if (candidate_str.empty() || sdp_mid.empty() ||
186 !base::StringToInt(sdp_mlineindex_str, &sdp_mlineindex)) {
187 LOG(ERROR) << "Failed to parse incoming candidates.";
188 return false;
189 }
190
191 webrtc::SdpParseError error;
192 scoped_ptr<webrtc::IceCandidateInterface> candidate(
193 webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, candidate_str,
194 &error));
195 if (!candidate) {
196 LOG(ERROR) << "Failed to parse incoming candidate: " << error.description
197 << " line: " << error.line;
198 return false;
199 }
200
201 if (peer_connection_->signaling_state() ==
202 webrtc::PeerConnectionInterface::kStable) {
203 if (!peer_connection_->AddIceCandidate(candidate.get())) {
204 LOG(ERROR) << "Failed to add incoming ICE candidate.";
205 return false;
206 }
207 } else {
208 pending_incoming_candidates_.push_back(candidate.Pass());
209 }
210 }
211
212 return true;
213 }
214
215 DatagramChannelFactory* WebrtcTransport::GetDatagramChannelFactory() {
216 NOTIMPLEMENTED();
217 return nullptr;
218 }
219
220 StreamChannelFactory* WebrtcTransport::GetStreamChannelFactory() {
221 // TODO(sergeyu): Implement data stream support.
222 NOTIMPLEMENTED();
223 return nullptr;
224 }
225
226 StreamChannelFactory* WebrtcTransport::GetMultiplexedChannelFactory() {
227 return GetStreamChannelFactory();
228 }
229
230 void WebrtcTransport::DoStart(rtc::Thread* worker_thread) {
231 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
232
233 // TODO(sergeyu): Investigate if it's possible to avoid Send().
234 jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true);
235
236 peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(
237 worker_thread, rtc::Thread::Current(),
238 new webrtc::FakeAudioDeviceModule(), nullptr, nullptr);
239
240 webrtc::PeerConnectionInterface::IceServer stun_server;
241 stun_server.urls.push_back("stun:stun.l.google.com:19302");
242 webrtc::PeerConnectionInterface::RTCConfiguration rtc_config;
243 rtc_config.servers.push_back(stun_server);
244
245 webrtc::FakeConstraints constraints;
246 constraints.AddMandatory(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp,
247 webrtc::MediaConstraintsInterface::kValueTrue);
248
249 peer_connection_ = peer_connection_factory_->CreatePeerConnection(
250 rtc_config, &constraints, port_allocator_factory_, nullptr, this);
251
252 if (role_ == TransportRole::CLIENT) {
253 webrtc::FakeConstraints offer_config;
254 offer_config.AddMandatory(
255 webrtc::MediaConstraintsInterface::kOfferToReceiveVideo,
256 webrtc::MediaConstraintsInterface::kValueTrue);
257 offer_config.AddMandatory(
258 webrtc::MediaConstraintsInterface::kOfferToReceiveAudio,
259 webrtc::MediaConstraintsInterface::kValueFalse);
260 peer_connection_->CreateOffer(
261 CreateSessionDescriptionObserver::Create(
262 base::Bind(&WebrtcTransport::OnLocalSessionDescriptionCreated,
263 weak_factory_.GetWeakPtr())),
264 &offer_config);
265 }
266 }
267
268 void WebrtcTransport::OnLocalSessionDescriptionCreated(
269 scoped_ptr<webrtc::SessionDescriptionInterface> description,
270 const std::string& error) {
271 if (!peer_connection_)
272 return;
273
274 if (!description) {
275 LOG(ERROR) << "PeerConnection offer creation failed: " << error;
276 Close(CHANNEL_CONNECTION_ERROR);
277 return;
278 }
279
280 std::string description_sdp;
281 if (!description->ToString(&description_sdp)) {
282 LOG(ERROR) << "Failed to serialize description.";
283 Close(CHANNEL_CONNECTION_ERROR);
284 return;
285 }
286
287 // Format and send the session description to the peer.
288 scoped_ptr<XmlElement> transport_info(
289 new XmlElement(QName(kTransportNamespace, "transport"), true));
290 XmlElement* offer_tag =
291 new XmlElement(QName(kTransportNamespace, "session-description"));
292 transport_info->AddElement(offer_tag);
293 offer_tag->SetAttr(QName(std::string(), "type"), description->type());
294 offer_tag->SetBodyText(description_sdp);
295
296 event_handler_->OnOutgoingTransportInfo(transport_info.Pass());
kelvinp 2015/11/11 23:38:48 Do we need to post back to the caller thread?
Sergey Ulanov 2015/11/17 01:40:32 No. All code in this class runs on the network thr
297
298 peer_connection_->SetLocalDescription(
299 SetSessionDescriptionObserver::Create(base::Bind(
300 &WebrtcTransport::OnLocalDescriptionSet, weak_factory_.GetWeakPtr())),
301 description.release());
302 }
303
304 void WebrtcTransport::OnLocalDescriptionSet(bool success,
305 const std::string& error) {
306 if (!peer_connection_)
307 return;
308
309 if (!success) {
310 LOG(ERROR) << "Failed to set local description: " << error;
311 Close(CHANNEL_CONNECTION_ERROR);
312 return;
313 }
314
315 AddPendingCandidatesIfPossible();
316 }
317
318 void WebrtcTransport::OnRemoteDescriptionSet(bool success,
319 const std::string& error) {
320 if (!peer_connection_)
321 return;
322
323 if (!success) {
324 LOG(ERROR) << "Failed to set local description: " << error;
325 Close(CHANNEL_CONNECTION_ERROR);
326 return;
327 }
328
329 // Create and send answer on the server.
330 if (role_ == TransportRole::SERVER) {
331 peer_connection_->CreateAnswer(
332 CreateSessionDescriptionObserver::Create(
333 base::Bind(&WebrtcTransport::OnLocalSessionDescriptionCreated,
334 weak_factory_.GetWeakPtr())),
335 nullptr);
336 }
337
338 AddPendingCandidatesIfPossible();
339 }
340
341 void WebrtcTransport::Close(ErrorCode error) {
342 weak_factory_.InvalidateWeakPtrs();
343 peer_connection_->Close();
344 peer_connection_ = nullptr;
345 peer_connection_factory_ = nullptr;
346
347 if (error != OK)
348 event_handler_->OnTransportError(error);
349 }
350
351 void WebrtcTransport::OnSignalingChange(
352 webrtc::PeerConnectionInterface::SignalingState new_state) {
353 }
354
355 void WebrtcTransport::OnAddStream(webrtc::MediaStreamInterface* stream) {
356 unclaimed_streams_.push_back(stream);
357 LOG(ERROR) << "Stream added " << stream->label();
358 }
359
360 void WebrtcTransport::OnRemoveStream(webrtc::MediaStreamInterface* stream) {
361 LOG(ERROR) << "Stream removed " << stream->label();
362 }
363
364 void WebrtcTransport::OnDataChannel(
365 webrtc::DataChannelInterface* data_channel) {}
366
367 void WebrtcTransport::OnRenegotiationNeeded() {
368 // TODO(sergeyu): Figure out what needs to happen here.
369 }
370
371 void WebrtcTransport::OnIceConnectionChange(
372 webrtc::PeerConnectionInterface::IceConnectionState new_state) {
373 if (new_state == webrtc::PeerConnectionInterface::kIceConnectionConnected)
374 event_handler_->OnTransportConnected();
375 }
376
377 void WebrtcTransport::OnIceGatheringChange(
378 webrtc::PeerConnectionInterface::IceGatheringState new_state) {
379 }
380
381 void WebrtcTransport::OnIceCandidate(
382 const webrtc::IceCandidateInterface* candidate) {
383 scoped_ptr<XmlElement> candidate_element(
384 new XmlElement(QName(kTransportNamespace, "candidate")));
385 std::string candidate_str;
386 if (!candidate->ToString(&candidate_str)) {
387 LOG(ERROR) << "Failed to serialize local candidate.";
388 return;
389 }
390 candidate_element->SetBodyText(candidate_str);
391 candidate_element->SetAttr(QName(std::string(), "sdpMid"),
392 candidate->sdp_mid());
393 candidate_element->SetAttr(QName(std::string(), "sdpMLineIndex"),
394 base::IntToString(candidate->sdp_mline_index()));
395
396 EnsurePendingTransportInfoMessage();
397 pending_transport_info_message_->AddElement(candidate_element.release());
398 }
399
400 void WebrtcTransport::EnsurePendingTransportInfoMessage() {
401 // |transport_info_timer_| must be running iff
402 // |pending_transport_info_message_| exists.
403 DCHECK_EQ(pending_transport_info_message_ != nullptr,
404 transport_info_timer_.IsRunning());
405
406 if (!pending_transport_info_message_) {
407 pending_transport_info_message_.reset(
408 new XmlElement(QName(kTransportNamespace, "transport"), true));
409
410 // Delay sending the new candidates in case we get more candidates
411 // that we can send in one message.
412 transport_info_timer_.Start(
413 FROM_HERE, base::TimeDelta::FromMilliseconds(kTransportInfoSendDelayMs),
414 this, &WebrtcTransport::SendTransportInfo);
415 }
416 }
417
418 void WebrtcTransport::SendTransportInfo() {
419 DCHECK(pending_transport_info_message_);
420 event_handler_->OnOutgoingTransportInfo(
421 pending_transport_info_message_.Pass());
422 pending_transport_info_message_.reset();
423 }
424
425 void WebrtcTransport::AddPendingCandidatesIfPossible() {
426 if (peer_connection_->signaling_state() ==
427 webrtc::PeerConnectionInterface::kStable) {
428 for (auto candidate : pending_incoming_candidates_) {
429 if (!peer_connection_->AddIceCandidate(candidate)) {
430 LOG(ERROR) << "Failed to add incoming candidate";
431 Close(INCOMPATIBLE_PROTOCOL);
432 return;
433 }
434 }
435 pending_incoming_candidates_.clear();
436 }
437 }
438
439 WebrtcTransportFactory::WebrtcTransportFactory(
440 SignalStrategy* signal_strategy,
441 rtc::scoped_refptr<webrtc::PortAllocatorFactoryInterface>
442 port_allocator_factory,
443 TransportRole role)
444 : signal_strategy_(signal_strategy),
445 port_allocator_factory_(port_allocator_factory),
446 role_(role),
447 worker_thread_("ChromotingWebrtcWorkerThread") {
448 worker_thread_.StartWithOptions(
449 base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
450 }
451
452 WebrtcTransportFactory::~WebrtcTransportFactory() {}
453
454 scoped_ptr<Transport> WebrtcTransportFactory::CreateTransport() {
455 return make_scoped_ptr(new WebrtcTransport(port_allocator_factory_, role_,
456 worker_thread_.task_runner()));
457 }
458
459 } // namespace protocol
460 } // namespace remoting
OLDNEW

Powered by Google App Engine