OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 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 "chrome/browser/media/cast_remoting_connector.h" | |
6 | |
7 #include <stdio.h> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/bind_helpers.h" | |
11 #include "base/callback.h" | |
12 #include "base/logging.h" | |
13 #include "base/memory/ptr_util.h" | |
14 #include "base/strings/string_number_conversions.h" | |
15 #include "base/strings/string_piece.h" | |
16 #include "base/strings/stringprintf.h" | |
17 #include "chrome/browser/media/cast_remoting_sender.h" | |
18 #include "chrome/browser/media/router/media_router.h" | |
19 #include "chrome/browser/media/router/media_router_factory.h" | |
20 #include "chrome/browser/media/router/media_source_helper.h" | |
21 #include "chrome/browser/media/router/route_message.h" | |
22 #include "chrome/browser/media/router/route_message_observer.h" | |
23 #include "content/public/browser/browser_thread.h" | |
24 #include "mojo/public/cpp/bindings/strong_binding.h" | |
25 | |
26 DEFINE_WEB_CONTENTS_USER_DATA_KEY(CastRemotingConnector); | |
27 | |
28 using content::BrowserThread; | |
29 using media::mojom::RemotingStartFailReason; | |
30 using media::mojom::RemotingStopReason; | |
31 | |
32 namespace { | |
33 | |
34 // Simple command messages sent from/to the connector to/from the Media Router | |
35 // Cast Provider to start/stop media remoting to a Cast device. | |
36 // | |
37 // Field separator (for tokenizing parts of messages). | |
38 constexpr char kMessageFieldSeparator = ':'; | |
39 // Message sent by CastRemotingConnector to Cast provider to start remoting. | |
40 // Example: | |
41 // "START_CAST_REMOTING:session=1f" | |
42 constexpr char kStartRemotingMessageFormat[] = | |
43 "START_CAST_REMOTING:session=%x"; | |
44 // Message sent by CastRemotingConnector to Cast provider to start the remoting | |
45 // RTP stream(s). Example: | |
46 // "START_CAST_REMOTING_STREAMS:session=1f:audio=N:video=Y" | |
47 constexpr char kStartStreamsMessageFormat[] = | |
48 "START_CAST_REMOTING_STREAMS:session=%x:audio=%c:video=%c"; | |
49 // Start acknowledgement message sent by Cast provider to CastRemotingConnector | |
50 // once remoting RTP streams have been set up. Examples: | |
51 // "STARTED_CAST_REMOTING_STREAMS:session=1f:audio_stream_id=2e:" | |
52 // "video_stream_id=3d" | |
53 // "STARTED_CAST_REMOTING_STREAMS:session=1f:video_stream_id=b33f" | |
54 constexpr char kStartedStreamsMessageFormatPartial[] = | |
55 "STARTED_CAST_REMOTING_STREAMS:session=%x"; | |
56 constexpr char kStartedStreamsMessageAudioIdSpecifier[] = ":audio_stream_id="; | |
57 constexpr char kStartedStreamsMessageVideoIdSpecifier[] = ":video_stream_id="; | |
58 // Stop message sent by CastRemotingConnector to Cast provider. Example: | |
59 // "STOP_CAST_REMOTING:session=1f" | |
60 constexpr char kStopRemotingMessageFormat[] = | |
61 "STOP_CAST_REMOTING:session=%x"; | |
62 // Stop acknowledgement message sent by Cast provider to CastRemotingConnector | |
63 // once remoting is available again after the last session ended. Example: | |
64 // "STOPPED_CAST_REMOTING:session=1f" | |
65 constexpr char kStoppedMessageFormat[] = | |
66 "STOPPED_CAST_REMOTING:session=%x"; | |
67 // Failure message sent by Cast provider to CastRemotingConnector any time there | |
68 // was a fatal error (e.g., the Cast provider failed to set up the RTP streams, | |
69 // or there was some unexpected external event). Example: | |
70 // "FAILED_CAST_REMOTING:session=1f" | |
71 constexpr char kFailedMessageFormat[] = "FAILED_CAST_REMOTING:session=%x"; | |
72 | |
73 // Returns true if the given |message| matches the given |format| and the | |
74 // session ID in the |message| is equal to the |expected_session_id|. | |
75 bool IsMessageForSession(const std::string& message, const char* format, | |
76 unsigned int expected_session_id) { | |
77 unsigned int session_id; | |
78 if (sscanf(message.c_str(), format, &session_id) == 1) | |
79 return session_id == expected_session_id; | |
80 return false; | |
81 } | |
82 | |
83 // Scans |message| for |specifier| and extracts the remoting stream ID that | |
84 // follows the specifier. Returns a negative value on error. | |
85 int32_t GetStreamIdFromStartedMessage(const std::string& message, | |
dcheng
2016/09/20 08:20:12
Nit: StringPiece here will net slightly more effic
miu
2016/09/21 03:15:50
Done.
| |
86 const char* specifier, | |
87 size_t specifier_length) { | |
88 auto start = message.find(specifier, 0, specifier_length); | |
89 if (start == std::string::npos) | |
90 return -1; | |
91 start += specifier_length; | |
92 if (start + 1 >= message.size()) | |
93 return -1; // Must be at least one hex digit following the specifier. | |
94 int parsed_value; | |
95 if (!base::HexStringToInt( | |
96 message.substr(start, message.find(kMessageFieldSeparator, start)), | |
97 &parsed_value) || | |
98 parsed_value < 0 || | |
99 parsed_value > std::numeric_limits<int32_t>::max()) { | |
100 return -1; // Non-hex digits, or outside valid range. | |
101 } | |
102 return static_cast<int32_t>(parsed_value); | |
103 } | |
104 | |
105 } // namespace | |
106 | |
107 class CastRemotingConnector::FrameRemoterFactory | |
108 : public media::mojom::RemoterFactory { | |
109 public: | |
110 // |render_frame_host| represents the source render frame. | |
111 explicit FrameRemoterFactory(content::RenderFrameHost* render_frame_host) | |
112 : host_(render_frame_host) { | |
113 DCHECK(host_); | |
114 } | |
115 ~FrameRemoterFactory() final {} | |
116 | |
117 void Create(media::mojom::RemotingSourcePtr source, | |
118 media::mojom::RemoterRequest request) final { | |
119 CastRemotingConnector::Get(content::WebContents::FromRenderFrameHost(host_)) | |
120 ->CreateBridge(std::move(source), std::move(request)); | |
121 } | |
122 | |
123 private: | |
124 content::RenderFrameHost* const host_; | |
125 | |
126 DISALLOW_COPY_AND_ASSIGN(FrameRemoterFactory); | |
127 }; | |
128 | |
129 class CastRemotingConnector::RemotingBridge : public media::mojom::Remoter { | |
130 public: | |
131 // Constructs a "bridge" to delegate calls between the given |source| and | |
132 // |connector|. |connector| must outlive this instance. | |
133 RemotingBridge(media::mojom::RemotingSourcePtr source, | |
134 CastRemotingConnector* connector) | |
135 : source_(std::move(source)), connector_(connector) { | |
136 DCHECK(connector_); | |
137 source_.set_connection_error_handler(base::Bind( | |
138 &RemotingBridge::Stop, base::Unretained(this), | |
139 RemotingStopReason::SOURCE_GONE)); | |
140 connector_->RegisterBridge(this); | |
141 } | |
142 | |
143 ~RemotingBridge() final { | |
144 connector_->DeregisterBridge(this, RemotingStopReason::SOURCE_GONE); | |
145 } | |
146 | |
147 // The CastRemotingConnector calls these to call back to the RemotingSource. | |
148 void OnSinkAvailable() { source_->OnSinkAvailable(); } | |
149 void OnSinkGone() { source_->OnSinkGone(); } | |
150 void OnStarted() { source_->OnStarted(); } | |
151 void OnStartFailed(RemotingStartFailReason reason) { | |
152 source_->OnStartFailed(reason); | |
153 } | |
154 void OnMessageFromSink(const std::vector<uint8_t>& message) { | |
155 source_->OnMessageFromSink(message); | |
156 } | |
157 void OnStopped(RemotingStopReason reason) { source_->OnStopped(reason); } | |
158 | |
159 // media::mojom::Remoter implementation. The source calls these to start/stop | |
160 // media remoting and send messages to the sink. These simply delegate to the | |
161 // CastRemotingConnector, which mediates to establish only one remoting | |
162 // session among possibly multiple requests. The connector will respond to | |
163 // this request by calling one of: OnStarted() or OnStartFailed(). | |
164 void Start() final { | |
165 connector_->StartRemoting(this); | |
166 } | |
167 void StartDataStreams( | |
168 mojo::ScopedDataPipeConsumerHandle audio_pipe, | |
169 mojo::ScopedDataPipeConsumerHandle video_pipe, | |
170 media::mojom::RemotingDataStreamSenderRequest audio_sender_request, | |
171 media::mojom::RemotingDataStreamSenderRequest video_sender_request) | |
172 final { | |
173 connector_->StartRemotingDataStreams( | |
174 this, std::move(audio_pipe), std::move(video_pipe), | |
175 std::move(audio_sender_request), std::move(video_sender_request)); | |
176 } | |
177 void Stop(RemotingStopReason reason) final { | |
178 connector_->StopRemoting(this, reason); | |
179 } | |
180 void SendMessageToSink(const std::vector<uint8_t>& message) final { | |
181 connector_->SendMessageToSink(this, message); | |
182 } | |
183 | |
184 private: | |
185 media::mojom::RemotingSourcePtr source_; | |
186 CastRemotingConnector* const connector_; | |
187 | |
188 DISALLOW_COPY_AND_ASSIGN(RemotingBridge); | |
189 }; | |
190 | |
191 class CastRemotingConnector::MessageObserver | |
192 : public media_router::RouteMessageObserver { | |
193 public: | |
194 MessageObserver(media_router::MediaRouter* router, | |
195 const media_router::MediaRoute::Id& route_id, | |
196 CastRemotingConnector* connector) | |
197 : RouteMessageObserver(router, route_id), connector_(connector) {} | |
198 ~MessageObserver() final {} | |
199 | |
200 private: | |
201 void OnMessagesReceived( | |
202 const std::vector<media_router::RouteMessage>& messages) final { | |
203 connector_->ProcessMessagesFromRoute(messages); | |
204 } | |
205 | |
206 CastRemotingConnector* const connector_; | |
207 }; | |
208 | |
209 // static | |
210 CastRemotingConnector* CastRemotingConnector::Get( | |
211 content::WebContents* contents) { | |
212 CastRemotingConnector* connector = FromWebContents(contents); | |
213 if (connector) | |
214 return connector; | |
215 connector = new CastRemotingConnector(contents); | |
216 // The following transfers ownership of |connector| to WebContents. | |
217 contents->SetUserData(UserDataKey(), connector); | |
218 return connector; | |
219 } | |
220 | |
221 // static | |
222 void CastRemotingConnector::CreateRemoterFactory( | |
223 content::RenderFrameHost* render_frame_host, | |
224 media::mojom::RemoterFactoryRequest request) { | |
225 mojo::MakeStrongBinding( | |
226 base::MakeUnique<FrameRemoterFactory>(render_frame_host), | |
227 std::move(request)); | |
228 } | |
229 | |
230 CastRemotingConnector::CastRemotingConnector(content::WebContents* contents) | |
231 : CastRemotingConnector( | |
232 media_router::MediaRouterFactory::GetApiForBrowserContext( | |
233 contents->GetBrowserContext()), | |
234 media_router::MediaSourceForTabContentRemoting(contents).id()) {} | |
235 | |
236 CastRemotingConnector::CastRemotingConnector( | |
237 media_router::MediaRouter* router, | |
238 const media_router::MediaSource::Id& route_source_id) | |
239 : media_router::MediaRoutesObserver(router, route_source_id), | |
240 session_counter_(0), | |
241 active_bridge_(nullptr), | |
242 weak_factory_(this) {} | |
243 | |
244 CastRemotingConnector::~CastRemotingConnector() { | |
245 // Remoting should not be active at this point, and this instance is expected | |
246 // to outlive all bridges. See comment in CreateBridge(). | |
247 DCHECK(!active_bridge_); | |
248 DCHECK(bridges_.empty()); | |
249 } | |
250 | |
251 void CastRemotingConnector::CreateBridge(media::mojom::RemotingSourcePtr source, | |
252 media::mojom::RemoterRequest request) { | |
253 // Create a new RemotingBridge, which will become owned by the message pipe | |
254 // associated with |request|. |this| CastRemotingConnector should be valid | |
255 // for the full lifetime of the bridge because it can be deduced that the | |
256 // connector will always outlive the mojo message pipe: A single WebContents | |
257 // will destroy the render frame tree (which destroys all associated mojo | |
258 // message pipes) before CastRemotingConnector. To ensure this assumption is | |
259 // not broken by future design changes in external modules, a DCHECK() has | |
260 // been placed in the CastRemotingConnector destructor as a sanity-check. | |
261 mojo::MakeStrongBinding( | |
262 base::MakeUnique<RemotingBridge>(std::move(source), this), | |
263 std::move(request)); | |
264 } | |
265 | |
266 void CastRemotingConnector::RegisterBridge(RemotingBridge* bridge) { | |
267 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
268 DCHECK(bridges_.find(bridge) == bridges_.end()); | |
269 | |
270 bridges_.insert(bridge); | |
271 if (message_observer_ && !active_bridge_) | |
272 bridge->OnSinkAvailable(); | |
273 } | |
274 | |
275 void CastRemotingConnector::DeregisterBridge(RemotingBridge* bridge, | |
276 RemotingStopReason reason) { | |
277 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
278 DCHECK(bridges_.find(bridge) != bridges_.end()); | |
279 | |
280 if (bridge == active_bridge_) | |
281 StopRemoting(bridge, reason); | |
282 bridges_.erase(bridge); | |
283 } | |
284 | |
285 void CastRemotingConnector::StartRemoting(RemotingBridge* bridge) { | |
286 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
287 DCHECK(bridges_.find(bridge) != bridges_.end()); | |
288 | |
289 // Refuse to start if there is no remoting route available, or if remoting is | |
290 // already active. | |
291 if (!message_observer_) { | |
292 bridge->OnStartFailed(RemotingStartFailReason::ROUTE_TERMINATED); | |
293 return; | |
294 } | |
295 if (active_bridge_) { | |
296 bridge->OnStartFailed(RemotingStartFailReason::CANNOT_START_MULTIPLE); | |
297 return; | |
298 } | |
299 | |
300 // Notify all other sources that the sink is no longer available for remoting. | |
301 // A race condition is possible, where one of the other sources will try to | |
302 // start remoting before receiving this notification; but that attempt will | |
303 // just fail later on. | |
304 for (RemotingBridge* notifyee : bridges_) { | |
305 if (notifyee == bridge) | |
306 continue; | |
307 notifyee->OnSinkGone(); | |
308 } | |
309 | |
310 active_bridge_ = bridge; | |
311 | |
312 // Send a start message to the Cast Provider. | |
313 ++session_counter_; // New remoting session ID. | |
314 SendMessageToProvider( | |
315 base::StringPrintf(kStartRemotingMessageFormat, session_counter_)); | |
316 | |
317 bridge->OnStarted(); | |
318 } | |
319 | |
320 void CastRemotingConnector::StartRemotingDataStreams( | |
321 RemotingBridge* bridge, | |
322 mojo::ScopedDataPipeConsumerHandle audio_pipe, | |
323 mojo::ScopedDataPipeConsumerHandle video_pipe, | |
324 media::mojom::RemotingDataStreamSenderRequest audio_sender_request, | |
325 media::mojom::RemotingDataStreamSenderRequest video_sender_request) { | |
326 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
327 | |
328 // Refuse to start if there is no remoting route available, or if remoting is | |
329 // not active for this |bridge|. | |
330 if (!message_observer_ || active_bridge_ != bridge) | |
331 return; | |
332 // Also, if neither audio nor video pipe was provided, or if a request for a | |
333 // RemotingDataStreamSender was not provided for a data pipe, error-out early. | |
334 if ((!audio_pipe.is_valid() && !video_pipe.is_valid()) || | |
335 (audio_pipe.is_valid() && !audio_sender_request.is_pending()) || | |
336 (video_pipe.is_valid() && !video_sender_request.is_pending())) { | |
337 StopRemoting(active_bridge_, RemotingStopReason::DATA_SEND_FAILED); | |
338 return; | |
339 } | |
340 | |
341 // Hold on to the data pipe handles and interface requests until one/both | |
342 // CastRemotingSenders are created and ready for use. | |
343 pending_audio_pipe_ = std::move(audio_pipe); | |
344 pending_video_pipe_ = std::move(video_pipe); | |
345 pending_audio_sender_request_ = std::move(audio_sender_request); | |
346 pending_video_sender_request_ = std::move(video_sender_request); | |
347 | |
348 // Send a "start streams" message to the Cast Provider. The provider is | |
349 // responsible for creating and setting up a remoting Cast Streaming session | |
350 // that will result in new CastRemotingSender instances being created here in | |
351 // the browser process. | |
352 SendMessageToProvider(base::StringPrintf( | |
353 kStartStreamsMessageFormat, session_counter_, | |
354 pending_audio_sender_request_.is_pending() ? 'Y' : 'N', | |
355 pending_video_sender_request_.is_pending() ? 'Y' : 'N')); | |
356 } | |
357 | |
358 void CastRemotingConnector::StopRemoting(RemotingBridge* bridge, | |
359 RemotingStopReason reason) { | |
360 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
361 | |
362 if (active_bridge_ != bridge) | |
363 return; | |
364 | |
365 active_bridge_ = nullptr; | |
366 | |
367 // Explicitly close the data pipes (and related requests) just in case the | |
368 // "start streams" operation was interrupted. | |
369 pending_audio_pipe_.reset(); | |
370 pending_video_pipe_.reset(); | |
371 pending_audio_sender_request_.PassMessagePipe().reset(); | |
dcheng
2016/09/20 08:20:12
Would it make sense to call ResetWithReason here?
miu
2016/09/21 03:15:50
This is brand-new, so I hadn't considered it. Havi
| |
372 pending_video_sender_request_.PassMessagePipe().reset(); | |
373 | |
374 // Cancel all outstanding callbacks related to the remoting session. | |
375 weak_factory_.InvalidateWeakPtrs(); | |
376 | |
377 // Prevent the source from trying to start again until the Cast Provider has | |
378 // indicated the stop operation has completed. | |
379 bridge->OnSinkGone(); | |
380 // Note: At this point, all sources should think the sink is gone. | |
381 | |
382 SendMessageToProvider( | |
383 base::StringPrintf(kStopRemotingMessageFormat, session_counter_)); | |
384 // Note: Once the Cast Provider sends back an acknowledgement message, all | |
385 // sources will be notified that the remoting sink is available again. | |
386 | |
387 bridge->OnStopped(reason); | |
388 } | |
389 | |
390 void CastRemotingConnector::SendMessageToSink( | |
391 RemotingBridge* bridge, const std::vector<uint8_t>& message) { | |
392 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
393 | |
394 // During an active remoting session, simply pass all binary messages through | |
395 // to the sink. | |
396 if (!message_observer_ || active_bridge_ != bridge) | |
397 return; | |
398 media_router::MediaRoutesObserver::router()->SendRouteBinaryMessage( | |
399 message_observer_->route_id(), | |
400 base::MakeUnique<std::vector<uint8_t>>(message), | |
401 base::Bind(&CastRemotingConnector::HandleSendMessageResult, | |
402 weak_factory_.GetWeakPtr())); | |
403 } | |
404 | |
405 void CastRemotingConnector::SendMessageToProvider(const std::string& message) { | |
406 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
407 | |
408 if (!message_observer_) | |
409 return; | |
410 | |
411 if (active_bridge_) { | |
412 media_router::MediaRoutesObserver::router()->SendRouteMessage( | |
413 message_observer_->route_id(), message, | |
414 base::Bind(&CastRemotingConnector::HandleSendMessageResult, | |
415 weak_factory_.GetWeakPtr())); | |
416 } else { | |
417 struct Helper { | |
418 static void IgnoreSendMessageResult(bool ignored) {} | |
419 }; | |
420 media_router::MediaRoutesObserver::router()->SendRouteMessage( | |
421 message_observer_->route_id(), message, | |
422 base::Bind(&Helper::IgnoreSendMessageResult)); | |
423 } | |
424 } | |
425 | |
426 void CastRemotingConnector::ProcessMessagesFromRoute( | |
427 const std::vector<media_router::RouteMessage>& messages) { | |
dcheng
2016/09/20 08:20:12
For fuzzing, I think this is one of the interfaces
miu
2016/09/21 03:15:50
Acknowledged.
| |
428 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
429 | |
430 for (const media_router::RouteMessage& message : messages) { | |
431 switch (message.type) { | |
432 case media_router::RouteMessage::TEXT: | |
433 // This is a notification message from the Cast Provider, about the | |
434 // execution state of the media remoting session between Chrome and the | |
435 // remote device. | |
436 DCHECK(message.text); | |
dcheng
2016/09/20 08:20:12
Not for this CL, but we should consider making the
miu
2016/09/21 03:15:50
Agreed.
| |
437 | |
438 // If this is a "start streams" acknowledgement message, the | |
439 // CastRemotingSenders should now be available to begin consuming from | |
440 // the data pipes. | |
441 if (active_bridge_ && | |
442 IsMessageForSession(*message.text, | |
443 kStartedStreamsMessageFormatPartial, | |
444 session_counter_)) { | |
445 if (pending_audio_sender_request_.is_pending()) { | |
446 cast::CastRemotingSender::FindAndBind( | |
447 GetStreamIdFromStartedMessage( | |
448 *message.text, kStartedStreamsMessageAudioIdSpecifier, | |
449 sizeof(kStartedStreamsMessageAudioIdSpecifier) - 1), | |
dcheng
2016/09/20 08:20:12
Nit: use strlen here and below, our compilers shou
miu
2016/09/21 03:15:50
Done.
| |
450 std::move(pending_audio_pipe_), | |
451 std::move(pending_audio_sender_request_), | |
452 base::Bind(&CastRemotingConnector::OnDataSendFailed, | |
453 weak_factory_.GetWeakPtr())); | |
454 } | |
455 if (pending_video_sender_request_.is_pending()) { | |
456 cast::CastRemotingSender::FindAndBind( | |
457 GetStreamIdFromStartedMessage( | |
458 *message.text, kStartedStreamsMessageVideoIdSpecifier, | |
459 sizeof(kStartedStreamsMessageVideoIdSpecifier) - 1), | |
460 std::move(pending_video_pipe_), | |
461 std::move(pending_video_sender_request_), | |
462 base::Bind(&CastRemotingConnector::OnDataSendFailed, | |
463 weak_factory_.GetWeakPtr())); | |
464 } | |
465 break; | |
466 } | |
467 | |
468 // If this is a failure message, call StopRemoting(). | |
469 if (active_bridge_ && | |
470 IsMessageForSession(*message.text, kFailedMessageFormat, | |
471 session_counter_)) { | |
472 StopRemoting(active_bridge_, RemotingStopReason::UNEXPECTED_FAILURE); | |
473 break; | |
474 } | |
475 | |
476 // If this is a stop acknowledgement message, indicating that the last | |
477 // session was stopped, notify all sources that the sink is once again | |
478 // available. | |
479 if (IsMessageForSession(*message.text, kStoppedMessageFormat, | |
480 session_counter_)) { | |
481 if (active_bridge_) { | |
482 // Hmm...The Cast Provider was in a state that disagrees with this | |
483 // connector. Attempt to resolve this by shutting everything down to | |
484 // effectively reset to a known state. | |
485 LOG(WARNING) << "BUG: Cast Provider sent 'stopped' message during " | |
486 "an active remoting session."; | |
487 StopRemoting(active_bridge_, | |
488 RemotingStopReason::UNEXPECTED_FAILURE); | |
489 } | |
490 for (RemotingBridge* notifyee : bridges_) | |
491 notifyee->OnSinkAvailable(); | |
492 break; | |
493 } | |
494 | |
495 LOG(WARNING) << "BUG: Unexpected message from Cast Provider: " | |
496 << *message.text; | |
497 break; | |
498 | |
499 case media_router::RouteMessage::BINARY: // This is for the source. | |
500 DCHECK(message.binary); | |
501 | |
502 // All binary messages are passed through to the source during an active | |
503 // remoting session. | |
504 if (active_bridge_) | |
505 active_bridge_->OnMessageFromSink(*message.binary); | |
506 break; | |
507 } | |
508 } | |
509 } | |
510 | |
511 void CastRemotingConnector::HandleSendMessageResult(bool success) { | |
512 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
513 // A single message send failure is treated as fatal to an active remoting | |
514 // session. | |
515 if (!success && active_bridge_) | |
516 StopRemoting(active_bridge_, RemotingStopReason::MESSAGE_SEND_FAILED); | |
517 } | |
518 | |
519 void CastRemotingConnector::OnDataSendFailed() { | |
520 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
521 // A single data send failure is treated as fatal to an active remoting | |
522 // session. | |
523 if (active_bridge_) | |
524 StopRemoting(active_bridge_, RemotingStopReason::DATA_SEND_FAILED); | |
525 } | |
526 | |
527 void CastRemotingConnector::OnRoutesUpdated( | |
528 const std::vector<media_router::MediaRoute>& routes, | |
529 const std::vector<media_router::MediaRoute::Id>& joinable_route_ids) { | |
530 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
531 | |
532 // If a remoting route has already been identified, check that it still | |
533 // exists. Otherwise, shut down messaging and any active remoting, and notify | |
534 // the sources that remoting is no longer available. | |
535 if (message_observer_) { | |
536 for (const media_router::MediaRoute& route : routes) { | |
537 if (message_observer_->route_id() == route.media_route_id()) | |
538 return; // Remoting route still exists. Take no further action. | |
539 } | |
540 message_observer_.reset(); | |
541 if (active_bridge_) | |
542 StopRemoting(active_bridge_, RemotingStopReason::ROUTE_TERMINATED); | |
543 for (RemotingBridge* notifyee : bridges_) | |
544 notifyee->OnSinkGone(); | |
545 } | |
546 | |
547 // There shouldn't be an active RemotingBridge at this point, since there is | |
548 // currently no known remoting route. | |
549 DCHECK(!active_bridge_); | |
550 | |
551 // Scan |routes| for a new remoting route. If one is found, begin processing | |
552 // messages on the route, and notify the sources that remoting is now | |
553 // available. | |
554 if (!routes.empty()) { | |
555 const media_router::MediaRoute& route = routes.front(); | |
556 message_observer_.reset(new MessageObserver( | |
557 media_router::MediaRoutesObserver::router(), route.media_route_id(), | |
558 this)); | |
559 // TODO(miu): In the future, scan the route ID for sink capabilities | |
560 // properties and pass these to the source in the OnSinkAvailable() | |
561 // notification. | |
562 for (RemotingBridge* notifyee : bridges_) | |
563 notifyee->OnSinkAvailable(); | |
564 } | |
565 } | |
OLD | NEW |