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