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