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

Side by Side Diff: remoting/host/cast_extension_session.cc

Issue 399253002: CastExtension Impl for Chromoting Host (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Changes based on review Created 6 years, 4 months 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 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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698