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