OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 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/host/cast_extension_session.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/json/json_reader.h" | |
9 #include "base/json/json_writer.h" | |
10 #include "base/logging.h" | |
11 #include "base/synchronization/waitable_event.h" | |
12 #include "net/url_request/url_request_context_getter.h" | |
13 #include "remoting/host/cast_video_capturer_adapter.h" | |
14 #include "remoting/host/chromium_port_allocator_factory.h" | |
15 #include "remoting/host/client_session.h" | |
16 #include "remoting/proto/control.pb.h" | |
17 #include "remoting/protocol/client_stub.h" | |
18 #include "third_party/libjingle/source/talk/app/webrtc/mediastreaminterface.h" | |
19 #include "third_party/libjingle/source/talk/app/webrtc/test/fakeconstraints.h" | |
20 #include "third_party/libjingle/source/talk/app/webrtc/videosourceinterface.h" | |
21 #include "third_party/webrtc/modules/desktop_capture/mouse_cursor_shape.h" | |
22 | |
23 namespace remoting { | |
24 | |
25 // Used as type attribute of all Cast extension messages. | |
26 const char kMessageType[] = "cast_message"; | |
27 | |
28 // Top-level keys used in all extension messages between host and client. | |
29 // Must keep synced with webapp. | |
30 const char kMessageData[] = "chromoting_data"; | |
Wez
2014/08/12 22:15:01
These two names are confusing; suggest kCastMessag
aiguha
2014/08/13 18:33:27
Yes, I can see how that's confusing.
kMessageType
Wez
2014/08/14 19:22:03
Come to think of it, "cast" arguably shouldn't fea
aiguha
2014/08/15 04:11:39
I originally had it named WebRtcHostExtension[Sess
| |
31 const char kMessageSubject[] = "subject"; | |
32 | |
33 // Keys used to describe the subject of a cast extension message. WebRTC-related | |
34 // message subjects are prepended with "webrtc_". | |
35 const char kSubjectReady[] = "ready"; | |
36 const char kSubjectTest[] = "test"; | |
37 const char kSubjectNewCandidate[] = "webrtc_candidate"; | |
38 const char kSubjectOffer[] = "webrtc_offer"; | |
39 const char kSubjectAnswer[] = "webrtc_answer"; | |
40 | |
41 // WebRTC headers used inside cast extension messages. | |
42 const char kWebRtcCandidate[] = "candidate"; | |
43 const char kWebRtcSessionDescType[] = "type"; | |
44 const char kWebRtcSessionDescSDP[] = "sdp"; | |
45 const char kWebRtcSDPMid[] = "sdpMid"; | |
46 const char kWebRtcSDPMLineIndex[] = "sdpMLineIndex"; | |
47 | |
48 // Constants used over the PeerConnection. | |
49 const char kVideoLabel[] = "cast_video_label"; | |
50 const char kStreamLabel[] = "stream_label"; | |
51 const char kDefaultStunURI[] = "stun:stun.l.google.com:19302"; | |
52 | |
53 const char kWorkerThreadName[] = "CastExtensionSessionWorkerThread"; | |
54 | |
55 // Interval between each call to PollPeerConnectionStats(). | |
56 const int kStatsLogIntervalSec = 10; | |
57 | |
58 // Minimum frame rate for video streaming over the PeerConnection. | |
Wez
2014/08/12 22:15:00
What are the units? Why is this a char rather than
aiguha
2014/08/13 18:33:27
Clarified in the comment now. It's in fps. We're u
| |
59 const char kMinFrameRate[] = "5"; | |
60 | |
61 // A webrtc::SetSessionDescriptionObserver implementation used to receive the | |
62 // results of setting local and remote descriptions of the PeerConnection. | |
63 class CastSetSessionDescriptionObserver | |
64 : public webrtc::SetSessionDescriptionObserver { | |
65 public: | |
66 static CastSetSessionDescriptionObserver* Create() { | |
67 return new rtc::RefCountedObject<CastSetSessionDescriptionObserver>(); | |
68 } | |
69 virtual void OnSuccess() OVERRIDE { | |
70 VLOG(1) << "Setting session description succeeded."; | |
71 } | |
72 virtual void OnFailure(const std::string& error) OVERRIDE { | |
73 LOG(ERROR) << "Setting session description failed: " << error; | |
74 } | |
75 | |
76 protected: | |
77 CastSetSessionDescriptionObserver() {} | |
78 virtual ~CastSetSessionDescriptionObserver() {} | |
79 | |
80 DISALLOW_COPY_AND_ASSIGN(CastSetSessionDescriptionObserver); | |
81 }; | |
82 | |
83 // A webrtc::CreateSessionDescriptionObserver implementation used to receive the | |
84 // results of creating descriptions for this end of the PeerConnection. | |
85 class CastCreateSessionDescriptionObserver | |
86 : public webrtc::CreateSessionDescriptionObserver { | |
87 public: | |
88 static CastCreateSessionDescriptionObserver* Create( | |
89 CastExtensionSession* session) { | |
90 return new rtc::RefCountedObject<CastCreateSessionDescriptionObserver>( | |
91 session); | |
92 } | |
93 virtual void OnSuccess(webrtc::SessionDescriptionInterface* desc) OVERRIDE { | |
94 if (session_ == NULL) { | |
95 LOG(ERROR) << "No Session, cannot create session description."; | |
96 return; | |
97 } | |
98 session_->OnCreateSessionDescription(desc); | |
99 } | |
100 virtual void OnFailure(const std::string& error) OVERRIDE { | |
101 if (session_ == NULL) { | |
102 LOG(ERROR) << "No Session, cannot create session description."; | |
103 return; | |
104 } | |
105 session_->OnCreateSessionDescriptionFailure(error); | |
106 } | |
107 | |
108 protected: | |
109 explicit CastCreateSessionDescriptionObserver(CastExtensionSession* session) | |
110 : session_(session) {} | |
111 virtual ~CastCreateSessionDescriptionObserver() {} | |
112 | |
113 private: | |
114 CastExtensionSession* session_; | |
115 | |
116 DISALLOW_COPY_AND_ASSIGN(CastCreateSessionDescriptionObserver); | |
117 }; | |
118 | |
119 // A webrtc::StatsObserver implementation used to receive statistics about the | |
120 // current PeerConnection. | |
121 class CastStatsObserver : public webrtc::StatsObserver { | |
122 public: | |
123 static CastStatsObserver* Create() { | |
124 return new rtc::RefCountedObject<CastStatsObserver>(); | |
125 } | |
126 | |
127 virtual void OnComplete( | |
128 const std::vector<webrtc::StatsReport>& reports) OVERRIDE { | |
129 typedef webrtc::StatsReport::Values::iterator ValuesIterator; | |
130 | |
131 VLOG(1) << "Received " << reports.size() << " new StatsReports."; | |
132 | |
133 int index; | |
134 std::vector<webrtc::StatsReport>::const_iterator it; | |
135 for (it = reports.begin(), index = 0; it != reports.end(); ++it, ++index) { | |
136 webrtc::StatsReport::Values v = it->values; | |
137 VLOG(1) << "Report " << index << ":"; | |
138 for (ValuesIterator vIt = v.begin(); vIt != v.end(); ++vIt) { | |
139 VLOG(1) << "Stat: " << vIt->name << "=" << vIt->value << "."; | |
140 } | |
141 } | |
142 } | |
143 | |
144 protected: | |
145 CastStatsObserver() {} | |
146 virtual ~CastStatsObserver() {} | |
147 | |
148 DISALLOW_COPY_AND_ASSIGN(CastStatsObserver); | |
149 }; | |
150 | |
151 // TODO(aiguha): Fix PeerConnnection-related tear down crash caused by premature | |
152 // destruction of cricket::CaptureManager (which occurs on releasing | |
153 // |peer_conn_factory_|). For more details, see bug:. | |
aiguha
2014/08/12 01:42:41
Filing the bug shortly.
| |
154 CastExtensionSession::~CastExtensionSession() { | |
155 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
156 CleanupPeerConnection(); | |
157 } | |
158 | |
159 // static | |
160 scoped_ptr<CastExtensionSession> CastExtensionSession::Create( | |
161 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, | |
162 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter, | |
163 const protocol::NetworkSettings& network_settings, | |
164 ClientSessionControl* client_session_control, | |
165 protocol::ClientStub* client_stub) { | |
166 scoped_ptr<CastExtensionSession> cast_extension_session( | |
167 new CastExtensionSession(network_task_runner, | |
168 url_request_context_getter, | |
169 network_settings, | |
170 client_session_control, | |
171 client_stub)); | |
172 if (!cast_extension_session->WrapTasksAndSave()) { | |
173 return scoped_ptr<CastExtensionSession>(); | |
174 } | |
175 if (!cast_extension_session->InitializePeerConnection()) { | |
176 return scoped_ptr<CastExtensionSession>(); | |
177 } | |
178 return cast_extension_session.Pass(); | |
179 } | |
180 | |
181 void CastExtensionSession::OnCreateSessionDescription( | |
182 webrtc::SessionDescriptionInterface* desc) { | |
Wez
2014/08/12 22:15:00
nit: Separate out the logical chunks of code in th
aiguha
2014/08/13 18:33:27
Done.
| |
183 if (!network_task_runner_->BelongsToCurrentThread()) { | |
184 network_task_runner_->PostTask( | |
185 FROM_HERE, | |
186 base::Bind(&CastExtensionSession::OnCreateSessionDescription, | |
187 base::Unretained(this), | |
188 desc)); | |
189 return; | |
190 } | |
191 peer_connection_->SetLocalDescription( | |
192 CastSetSessionDescriptionObserver::Create(), desc); | |
193 scoped_ptr<base::DictionaryValue> json(new base::DictionaryValue()); | |
194 json->SetString(kWebRtcSessionDescType, desc->type()); | |
195 std::string subject = | |
196 (desc->type() == "offer") ? kSubjectOffer : kSubjectAnswer; | |
197 std::string desc_str; | |
198 desc->ToString(&desc_str); | |
199 json->SetString(kWebRtcSessionDescSDP, desc_str); | |
200 std::string json_str; | |
201 if (!base::JSONWriter::Write(json.get(), &json_str)) { | |
202 LOG(ERROR) << "Failed to serialize sdp message."; | |
203 return; | |
204 } | |
205 SendMessageToClient(subject.c_str(), json_str); | |
206 } | |
207 | |
208 void CastExtensionSession::OnCreateSessionDescriptionFailure( | |
209 const std::string& error) { | |
210 VLOG(1) << "Creating Session Description failed: " << error; | |
211 } | |
212 | |
213 // TODO(aiguha): Support the case(s) where we've grabbed the capturer already, | |
214 // but another extension reset the video pipeline. We should remove the | |
215 // stream from the peer connection here, and then attempt to re-setup the | |
216 // peer connection in the OnRenegotiationNeeded() callback. | |
Wez
2014/08/12 22:15:01
nit: File a bug for that work and refer to it here
aiguha
2014/08/13 18:33:28
Acknowledged.
| |
217 scoped_ptr<webrtc::ScreenCapturer> CastExtensionSession::OnCreateVideoCapturer( | |
218 scoped_ptr<webrtc::ScreenCapturer> capturer) { | |
219 if (has_grabbed_capturer_) { | |
220 LOG(ERROR) << "The video pipeline was reset unexpectedly."; | |
221 has_grabbed_capturer_ = false; | |
222 peer_connection_->RemoveStream(stream_.release()); | |
223 return capturer.Pass(); | |
224 } | |
225 | |
226 if (received_offer_) { | |
227 has_grabbed_capturer_ = true; | |
228 if (SetupVideoStream(capturer.Pass())) { | |
229 peer_connection_->CreateAnswer( | |
230 CastCreateSessionDescriptionObserver::Create(this), NULL); | |
231 } else { | |
232 has_grabbed_capturer_ = false; | |
233 // Ignore the received offer, since we failed to setup a video stream. | |
234 received_offer_ = false; | |
235 } | |
236 return scoped_ptr<webrtc::ScreenCapturer>(); | |
237 } | |
238 | |
239 return capturer.Pass(); | |
240 } | |
241 | |
242 bool CastExtensionSession::ModifiesVideoPipeline() const { | |
243 return true; | |
244 } | |
245 | |
246 // Returns true if the ExtensionMessage was of type |kMessageType|, even if | |
Wez
2014/08/12 22:15:00
nit: You mean if its a Cast extension message?
aiguha
2014/08/13 18:33:27
Yes, that would be simpler!
| |
247 // it was badly formed or a resulting action failed. This is done so that | |
248 // the host does not continue to attempt to pass |message| to other | |
249 // HostExtensionSessions. | |
250 bool CastExtensionSession::OnExtensionMessage( | |
251 ClientSessionControl* client_session_control, | |
252 protocol::ClientStub* client_stub, | |
253 const protocol::ExtensionMessage& message) { | |
254 if (message.type() != kMessageType) { | |
255 return false; | |
256 } | |
257 | |
258 scoped_ptr<base::Value> value(base::JSONReader::Read(message.data())); | |
259 base::DictionaryValue* client_message; | |
260 if (!(value && value->GetAsDictionary(&client_message))) { | |
261 LOG(ERROR) << "Could not read cast extension message."; | |
262 return true; | |
263 } | |
264 | |
265 std::string subject; | |
266 if (!client_message->GetString(kMessageSubject, &subject)) { | |
267 LOG(ERROR) << "Invalid Cast Extension Message (missing subject header)."; | |
268 return true; | |
269 } | |
270 | |
271 if (subject == kSubjectOffer && !received_offer_) { | |
272 // Reset the video pipeline so we can grab the screen capturer and setup | |
273 // a video stream. | |
274 if (ParseAndSetRemoteDescription(client_message)) { | |
275 received_offer_ = true; | |
276 client_session_control_->ResetVideoPipeline(); | |
277 } | |
278 } else if (subject == kSubjectAnswer) { | |
279 ParseAndSetRemoteDescription(client_message); | |
280 } else if (subject == kSubjectNewCandidate) { | |
281 ParseAndAddICECandidate(client_message); | |
282 } else { | |
283 VLOG(1) << "Unexpected CastExtension Message: " << message.data(); | |
284 } | |
285 return true; | |
286 } | |
287 | |
288 // TODO(aiguha): To reduce duplication, it would perhaps be | |
289 // better to create a single MouseShapeObserver outside of VideoScheduler | |
290 // that can be attached to the ScreenCapturer on its creation in | |
291 // ClientSession. | |
292 void CastExtensionSession::OnCursorShapeChanged( | |
Wez
2014/08/12 22:15:00
I can find a call to SetMouseShapeObserver in this
aiguha
2014/08/13 18:33:27
Acknowledged.
| |
293 webrtc::MouseCursorShape* cursor_shape) { | |
294 DCHECK(worker_task_runner_->BelongsToCurrentThread()); | |
295 scoped_ptr<webrtc::MouseCursorShape> owned_cursor(cursor_shape); | |
296 | |
297 scoped_ptr<protocol::CursorShapeInfo> cursor_proto( | |
298 new protocol::CursorShapeInfo()); | |
299 cursor_proto->set_width(cursor_shape->size.width()); | |
300 cursor_proto->set_height(cursor_shape->size.height()); | |
301 cursor_proto->set_hotspot_x(cursor_shape->hotspot.x()); | |
302 cursor_proto->set_hotspot_y(cursor_shape->hotspot.y()); | |
303 cursor_proto->set_data(cursor_shape->data); | |
304 | |
305 network_task_runner_->PostTask( | |
306 FROM_HERE, | |
307 base::Bind(&CastExtensionSession::SendCursorShape, | |
308 base::Unretained(this), | |
309 base::Passed(&cursor_proto))); | |
310 } | |
311 | |
312 // Private methods ------------------------------------------------------------ | |
313 | |
314 CastExtensionSession::CastExtensionSession( | |
315 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, | |
316 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter, | |
317 const protocol::NetworkSettings& network_settings, | |
318 ClientSessionControl* client_session_control, | |
319 protocol::ClientStub* client_stub) | |
320 : network_task_runner_(network_task_runner), | |
321 url_request_context_getter_(url_request_context_getter), | |
322 network_settings_(network_settings), | |
323 client_session_control_(client_session_control), | |
324 client_stub_(client_stub), | |
325 stats_observer_(CastStatsObserver::Create()), | |
326 received_offer_(false), | |
327 has_grabbed_capturer_(false), | |
328 network_thread_wrapper_(NULL), | |
329 worker_thread_wrapper_(NULL), | |
330 worker_thread_(kWorkerThreadName) { | |
331 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
332 DCHECK(url_request_context_getter_); | |
333 DCHECK(client_session_control_); | |
334 DCHECK(client_stub_); | |
335 | |
336 base::Thread::Options options(base::MessageLoop::TYPE_IO, 0); | |
Wez
2014/08/12 22:15:01
nit: You could clarify why this needs to be an IO
aiguha
2014/08/13 18:33:27
Done.
| |
337 worker_thread_.StartWithOptions(options); | |
338 worker_task_runner_ = worker_thread_.task_runner(); | |
339 } | |
340 | |
341 bool CastExtensionSession::ParseAndSetRemoteDescription( | |
342 base::DictionaryValue* message) { | |
343 DCHECK(peer_connection_.get() != NULL); | |
344 | |
345 base::DictionaryValue* message_data; | |
346 if (!message->GetDictionary(kMessageData, &message_data)) { | |
347 LOG(ERROR) << "Invalid Cast Extension Message (missing data)."; | |
348 return false; | |
349 } | |
350 | |
351 std::string webrtc_type; | |
352 if (!message_data->GetString(kWebRtcSessionDescType, &webrtc_type)) { | |
353 LOG(ERROR) | |
354 << "Invalid Cast Extension Message (missing webrtc type header)."; | |
355 return false; | |
356 } | |
357 | |
358 std::string sdp; | |
359 if (!message_data->GetString(kWebRtcSessionDescSDP, &sdp)) { | |
360 LOG(ERROR) << "Invalid Cast Extension Message (missing webrtc sdp header)."; | |
361 return false; | |
362 } | |
363 | |
364 webrtc::SdpParseError error; | |
365 webrtc::SessionDescriptionInterface* session_description( | |
366 webrtc::CreateSessionDescription(webrtc_type, sdp, &error)); | |
367 | |
368 if (!session_description) { | |
369 LOG(ERROR) << "Invalid Cast Extension Message (could not parse sdp)."; | |
370 VLOG(1) << "SdpParseError was: " << error.description; | |
371 return false; | |
372 } | |
373 | |
374 peer_connection_->SetRemoteDescription( | |
375 CastSetSessionDescriptionObserver::Create(), session_description); | |
376 return true; | |
377 } | |
378 | |
379 bool CastExtensionSession::ParseAndAddICECandidate( | |
380 base::DictionaryValue* message) { | |
381 DCHECK(peer_connection_.get() != NULL); | |
382 | |
383 base::DictionaryValue* message_data; | |
384 if (!message->GetDictionary(kMessageData, &message_data)) { | |
385 LOG(ERROR) << "Invalid Cast Extension Message (missing data)."; | |
386 return false; | |
387 } | |
388 | |
389 std::string candidate_str; | |
390 std::string sdp_mid; | |
391 int sdp_mlineindex = 0; | |
392 if (!message_data->GetString(kWebRtcSDPMid, &sdp_mid) || | |
393 !message_data->GetInteger(kWebRtcSDPMLineIndex, &sdp_mlineindex) || | |
394 !message_data->GetString(kWebRtcCandidate, &candidate_str)) { | |
395 LOG(ERROR) << "Invalid Cast Extension Message (could not parse)."; | |
396 return false; | |
397 } | |
398 | |
399 rtc::scoped_ptr<webrtc::IceCandidateInterface> candidate( | |
400 webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, candidate_str)); | |
401 if (!candidate.get()) { | |
402 LOG(ERROR) | |
403 << "Invalid Cast Extension Message (could not create candidate)."; | |
404 return false; | |
405 } | |
406 | |
407 if (!peer_connection_->AddIceCandidate(candidate.get())) { | |
408 LOG(ERROR) << "Failed to apply received ICE Candidate to PeerConnection."; | |
409 return false; | |
410 } | |
411 | |
412 VLOG(1) << "Received and Added ICE Candidate: " << candidate_str; | |
413 | |
414 return true; | |
415 } | |
416 | |
417 bool CastExtensionSession::SendMessageToClient(const std::string& subject, | |
418 const std::string& data) { | |
419 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
420 | |
421 if (client_stub_ == NULL) { | |
422 LOG(ERROR) << "No Client Stub. Cannot send message to client."; | |
423 return false; | |
424 } | |
425 | |
426 base::DictionaryValue message_dict; | |
427 message_dict.SetString(kMessageSubject, subject); | |
428 message_dict.SetString(kMessageData, data); | |
429 std::string message_json; | |
430 | |
431 if (!base::JSONWriter::Write(&message_dict, &message_json)) { | |
432 LOG(ERROR) << "Failed to serialize JSON message."; | |
433 return false; | |
434 } | |
435 | |
436 protocol::ExtensionMessage message; | |
437 message.set_type(kMessageType); | |
438 message.set_data(message_json); | |
439 client_stub_->DeliverHostMessage(message); | |
440 return true; | |
441 } | |
442 | |
443 void CastExtensionSession::SendCursorShape( | |
444 scoped_ptr<protocol::CursorShapeInfo> cursor_shape) { | |
445 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
446 if (!client_stub_) | |
447 return; | |
448 | |
449 client_stub_->SetCursorShape(*cursor_shape); | |
450 } | |
451 | |
452 void CastExtensionSession::EnsureTaskAndSetSend(rtc::Thread** ptr, | |
453 base::WaitableEvent* event) { | |
454 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop(); | |
455 jingle_glue::JingleThreadWrapper::current()->set_send_allowed(true); | |
456 *ptr = jingle_glue::JingleThreadWrapper::current(); | |
457 | |
458 if (event != NULL) { | |
459 event->Signal(); | |
460 } | |
461 } | |
462 | |
463 bool CastExtensionSession::WrapTasksAndSave() { | |
464 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
465 | |
466 EnsureTaskAndSetSend(&network_thread_wrapper_); | |
467 if (network_thread_wrapper_ == NULL) | |
468 return false; | |
469 | |
470 base::WaitableEvent wrap_worker_thread_event(true, false); | |
471 worker_task_runner_->PostTask( | |
472 FROM_HERE, | |
473 base::Bind(&CastExtensionSession::EnsureTaskAndSetSend, | |
474 base::Unretained(this), | |
475 &worker_thread_wrapper_, | |
476 &wrap_worker_thread_event)); | |
477 wrap_worker_thread_event.Wait(); | |
478 | |
479 return (worker_thread_wrapper_ != NULL); | |
480 } | |
481 | |
482 bool CastExtensionSession::InitializePeerConnection() { | |
483 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
484 DCHECK(!peer_conn_factory_); | |
485 DCHECK(!peer_connection_); | |
486 DCHECK(worker_thread_wrapper_ != NULL); | |
487 DCHECK(network_thread_wrapper_ != NULL); | |
488 | |
489 peer_conn_factory_ = webrtc::CreatePeerConnectionFactory( | |
490 worker_thread_wrapper_, network_thread_wrapper_, NULL, NULL, NULL); | |
491 | |
492 if (!peer_conn_factory_.get()) { | |
493 CleanupPeerConnection(); | |
494 return false; | |
495 } | |
496 | |
497 VLOG(1) << "Created PeerConnectionFactory successfully."; | |
498 | |
499 webrtc::PeerConnectionInterface::IceServers servers; | |
500 webrtc::PeerConnectionInterface::IceServer server; | |
501 server.uri = kDefaultStunURI; | |
502 servers.push_back(server); | |
503 webrtc::PeerConnectionInterface::RTCConfiguration rtc_config; | |
504 rtc_config.servers = servers; | |
505 | |
506 // DTLS-SRTP is the preferred encryption method. If set to kValueFalse, the | |
507 // peer connection uses SDES. Disabling SDES as well will cause the peer | |
508 // connection to fail to connect. | |
509 // Note: For protection and unprotection of SRTP packets, the libjingle | |
510 // ENABLE_EXTERNAL_AUTH flag must not be set. | |
511 webrtc::FakeConstraints constraints; | |
Wez
2014/08/12 22:15:00
Why are you using FakeConstraints here? Fakes are
aiguha
2014/08/13 18:33:27
The only other existing impl of webrtc::MediaConst
Wez
2014/08/14 19:22:03
Let's use FakeConstraints for now, but add a TODO
| |
512 constraints.AddMandatory(webrtc::MediaConstraintsInterface::kEnableDtlsSrtp, | |
513 webrtc::MediaConstraintsInterface::kValueTrue); | |
514 | |
515 rtc::scoped_refptr<webrtc::PortAllocatorFactoryInterface> | |
516 port_allocator_factory = ChromiumPortAllocatorFactory::Create( | |
517 network_settings_, url_request_context_getter_); | |
518 | |
519 peer_connection_ = peer_conn_factory_->CreatePeerConnection( | |
520 rtc_config, &constraints, port_allocator_factory, NULL, this); | |
521 | |
522 if (!peer_connection_.get()) { | |
523 CleanupPeerConnection(); | |
524 return false; | |
525 } | |
526 | |
527 VLOG(1) << "Created PeerConnection successfully."; | |
528 | |
529 // Send a test message to the client. Then, notify the client to start | |
530 // webrtc offer/answer negotiation. | |
531 if (!SendMessageToClient(kSubjectTest, "Hello, client.") || | |
Wez
2014/08/12 22:15:00
What purposes does the test message serve?
aiguha
2014/08/13 18:33:27
The test message always receives an echo from the
Wez
2014/08/14 19:22:03
So... what purpose does the test message serve? Wh
| |
532 !SendMessageToClient(kSubjectReady, "Host ready to receive offers.")) { | |
533 LOG(ERROR) << "Failed to send messages to client."; | |
534 return false; | |
535 } | |
536 | |
537 return true; | |
538 } | |
539 | |
540 bool CastExtensionSession::SetupVideoStream( | |
541 scoped_ptr<webrtc::ScreenCapturer> screen_capturer) { | |
542 DCHECK(network_task_runner_->BelongsToCurrentThread()); | |
543 DCHECK(screen_capturer); | |
544 | |
545 if (stream_) { | |
546 VLOG(1) << "Already added MediaStream. Aborting Setup."; | |
547 return false; | |
548 } | |
549 | |
550 scoped_ptr<CastVideoCapturerAdapter> cast_video_capturer_adapter( | |
551 new CastVideoCapturerAdapter(screen_capturer.Pass())); | |
552 | |
553 // Set video stream constraints. | |
554 webrtc::FakeConstraints video_constraints; | |
555 video_constraints.AddMandatory( | |
556 webrtc::MediaConstraintsInterface::kMinFrameRate, kMinFrameRate); | |
557 | |
558 rtc::scoped_refptr<webrtc::VideoTrackInterface> video_track = | |
559 peer_conn_factory_->CreateVideoTrack( | |
560 kVideoLabel, | |
561 peer_conn_factory_->CreateVideoSource( | |
562 cast_video_capturer_adapter.release(), &video_constraints)); | |
563 | |
564 stream_ = peer_conn_factory_->CreateLocalMediaStream(kStreamLabel); | |
565 | |
566 if (!stream_->AddTrack(video_track) || | |
567 !peer_connection_->AddStream(stream_, NULL)) | |
Wez
2014/08/12 22:15:00
nit: This is now a multi-line if() so it needs {}
aiguha
2014/08/13 18:33:28
Absolutely agree, sorry about that!
| |
568 return false; | |
569 | |
570 VLOG(1) << "Setup video stream successfully."; | |
571 | |
572 return true; | |
573 } | |
574 | |
575 void CastExtensionSession::PollPeerConnectionStats() { | |
576 if (!connection_active()) { | |
577 VLOG(1) << "Cannot poll stats while PeerConnection is inactive."; | |
578 } | |
579 rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> video_track = | |
580 stream_->FindVideoTrack(kVideoLabel); | |
581 peer_connection_->GetStats( | |
582 stats_observer_, | |
583 video_track.release(), | |
584 webrtc::PeerConnectionInterface::kStatsOutputLevelStandard); | |
585 } | |
586 | |
587 void CastExtensionSession::CleanupPeerConnection() { | |
588 peer_connection_->Close(); | |
589 peer_connection_ = NULL; | |
590 stream_ = NULL; | |
591 peer_conn_factory_ = NULL; | |
592 worker_thread_.Stop(); | |
593 } | |
594 | |
595 bool CastExtensionSession::connection_active() const { | |
596 return peer_connection_.get() != NULL; | |
597 } | |
598 | |
599 // webrtc::PeerConnectionObserver implementation ------------------------------- | |
600 | |
601 void CastExtensionSession::OnError() { | |
602 VLOG(1) << "PeerConnectionObserver: an error occurred."; | |
603 } | |
604 | |
605 void CastExtensionSession::OnSignalingChange( | |
606 webrtc::PeerConnectionInterface::SignalingState new_state) { | |
607 VLOG(1) << "PeerConnectionObserver: SignalingState changed to:" << new_state; | |
608 } | |
609 | |
610 void CastExtensionSession::OnStateChange( | |
611 webrtc::PeerConnectionObserver::StateType state_changed) { | |
612 VLOG(1) << "PeerConnectionObserver: StateType changed to: " << state_changed; | |
613 } | |
614 | |
615 void CastExtensionSession::OnAddStream(webrtc::MediaStreamInterface* stream) { | |
616 VLOG(1) << "PeerConnectionObserver: stream added: " << stream->label(); | |
617 } | |
618 | |
619 void CastExtensionSession::OnRemoveStream( | |
620 webrtc::MediaStreamInterface* stream) { | |
621 VLOG(1) << "PeerConnectionObserver: stream removed: " << stream->label(); | |
622 } | |
623 | |
624 void CastExtensionSession::OnDataChannel( | |
625 webrtc::DataChannelInterface* data_channel) { | |
626 VLOG(1) << "PeerConnectionObserver: data channel: " << data_channel->label(); | |
627 } | |
628 | |
629 void CastExtensionSession::OnRenegotiationNeeded() { | |
630 VLOG(1) << "PeerConnectionObserver: renegotiation needed."; | |
631 } | |
632 | |
633 void CastExtensionSession::OnIceConnectionChange( | |
634 webrtc::PeerConnectionInterface::IceConnectionState new_state) { | |
635 VLOG(1) << "PeerConnectionObserver: IceConnectionState changed to: " | |
636 << new_state; | |
637 | |
638 // TODO(aiguha): Maybe start timer only if enabled by command-line flag or | |
639 // at a particular verbosity level. | |
640 if (!stats_polling_timer_.IsRunning() && | |
641 new_state == webrtc::PeerConnectionInterface::kIceConnectionConnected) { | |
642 stats_polling_timer_.Start( | |
643 FROM_HERE, | |
644 base::TimeDelta::FromSeconds(kStatsLogIntervalSec), | |
645 this, | |
646 &CastExtensionSession::PollPeerConnectionStats); | |
647 } | |
648 } | |
649 | |
650 void CastExtensionSession::OnIceGatheringChange( | |
651 webrtc::PeerConnectionInterface::IceGatheringState new_state) { | |
652 VLOG(1) << "PeerConnectionObserver: IceGatheringState changed to: " | |
653 << new_state; | |
654 } | |
655 | |
656 void CastExtensionSession::OnIceComplete() { | |
657 VLOG(1) << "PeerConnectionObserver: all ICE candidates found."; | |
658 } | |
659 | |
660 void CastExtensionSession::OnIceCandidate( | |
661 const webrtc::IceCandidateInterface* candidate) { | |
662 std::string candidate_str; | |
663 if (!candidate->ToString(&candidate_str)) { | |
664 LOG(ERROR) << "PeerConnectionObserver: failed to serialize candidate."; | |
665 return; | |
666 } | |
667 scoped_ptr<base::DictionaryValue> json(new base::DictionaryValue()); | |
668 json->SetString(kWebRtcSDPMid, candidate->sdp_mid()); | |
669 json->SetInteger(kWebRtcSDPMLineIndex, candidate->sdp_mline_index()); | |
670 json->SetString(kWebRtcCandidate, candidate_str); | |
671 std::string json_str; | |
672 if (!base::JSONWriter::Write(json.get(), &json_str)) { | |
673 LOG(ERROR) << "Failed to serialize candidate message."; | |
674 return; | |
675 } | |
676 SendMessageToClient(kSubjectNewCandidate, json_str); | |
677 } | |
678 | |
679 } // namespace remoting | |
OLD | NEW |