blob: 33597f2f0b3756b209b6a91899e4d666f240b174 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_MEDIA_CAST_REMOTING_CONNECTOR_H_
#define CHROME_BROWSER_MEDIA_CAST_REMOTING_CONNECTOR_H_
#include <set>
#include <vector>
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/supports_user_data.h"
#include "components/sessions/core/session_id.h"
#include "media/mojo/interfaces/mirror_service_remoting.mojom.h"
#include "media/mojo/interfaces/remoting.mojom.h"
#include "mojo/public/cpp/bindings/binding.h"
namespace content {
class RenderFrameHost;
class WebContents;
} // namespace content
namespace media_router {
class MediaRouter;
}
// CastRemotingConnector connects a single source (a media element in a render
// frame) with a single sink (a media player in a remote device). There is one
// instance of a CastRemotingConnector per source WebContents (representing a
// collection of render frames), and it is created on-demand. The source in the
// render process represents itself by providing a media::mojom::RemotingSource
// service instance. The sink is represented by a MediaRemoter in the Cast Media
// Router Provider that handles the communication with the remote device. The
// CastRemotingConnector and the MediaRemoter can communicate with each other
// through the media::mojom::MirrorServiceRemoter and
// media::mojom::MirrorServiceRemotingSource interfaces when a sink that is
// capable of remoting is available.
//
// Whenever a candidate media source is created in a render frame,
// ChromeContentBrowserClient will call CreateMediaRemoter() to instantiate a
// media::mojom::Remoter associated with the connector. A corresponding
// media::mojom::RemotingSource is provided by the caller for communications
// back to the media source in the render frame: The connector uses this to
// notify when a sink becomes available for remoting, and to pass binary
// messages from the sink back to the source.
//
// When the CastRemotingConnector is created, it registers itself in the
// media_router::MediaRouter with a tab ID that uniquely identifies it. When a
// mirroring route is created and available for remoting, the Cast MRP will
// create a MediaRemoter and notify MediaRouter, which notifies the
// CastRemotingConnector registered under the tab ID being remoted. At this
// point, the CastRemotingConnector can communicate with the MediaRemoter. When
// CastRemotingConnector gets notified that a sink is available, it notifies all
// RemotingSources, and some time later a RemotingSource can request the start
// of a remoting session.
//
// Note that only one RemotingSource can remote media at a time. Therefore,
// CastRemotingConnector must mediate among simultaneous requests for media
// remoting, and only allow one at a time. Currently, the policy is "first come,
// first served."
//
// When starting a remoting session, the Cast MRP will also set up a Cast
// Streaming session to provide a bitstream transport for the media. Once this
// is done, the MediaRemoter notifies the CastRemotingConnector. Then,
// CastRemotingConnector knows it can look-up and pass the mojo data pipe
// handles to CastRemotingSenders, and the remoting session will be fully active
// The CastRemotingConnector is responsible for passing small binary messages
// between the source and sink, while the CastRemotingSender handles the
// high-volume media data transfer.
//
// Please see the unit tests in cast_remoting_connector_unittest.cc as a
// reference for how CastRemotingConnector and a MediaRemoter interact to
// start/execute/stop remoting sessions.
//
// TODO(xjz): Remove media::mojom::MirrorServiceRemotingSource interface and
// implementation after Mirroring Service is launched.
class CastRemotingConnector : public base::SupportsUserData::Data,
public media::mojom::MirrorServiceRemotingSource,
public media::mojom::RemotingSource {
public:
~CastRemotingConnector() final;
// Returns the instance of the CastRemotingConnector associated with
// |source_contents|, creating a new instance if needed. Returns nullptr if
// |source_contents| doesn't have a valid tab ID.
static CastRemotingConnector* Get(content::WebContents* source_contents);
// Used by ChromeContentBrowserClient to request a binding to a new
// Remoter for each new source in a render frame.
static void CreateMediaRemoter(content::RenderFrameHost* render_frame_host,
media::mojom::RemotingSourcePtr source,
media::mojom::RemoterRequest request);
// Called when a MediaRemoter is created and started in the Cast MRP. This
// call connects the CastRemotingConnector with the MediaRemoter. Remoting
// sessions can only be started after this is called.
void ConnectToService(
media::mojom::MirrorServiceRemotingSourceRequest source_request,
media::mojom::MirrorServiceRemoterPtr remoter);
// Called at the start of mirroring to reset the permission.
void ResetRemotingPermission();
// Used by Mirroring Service to connect the media remoter with this source.
void ConnectWithMediaRemoter(media::mojom::RemoterPtr remoter,
media::mojom::RemotingSourceRequest request);
private:
// Allow unit tests access to the private constructor and CreateBridge()
// method, since unit tests don't have a complete browser (i.e., with a
// WebContents and RenderFrameHost) to work with.
friend class CastRemotingConnectorTest;
// Implementation of the media::mojom::Remoter service for a single source in
// a render frame. This is just a "lightweight bridge" that delegates calls
// back-and-forth between a CastRemotingConnector and a
// media::mojom::RemotingSource. An instance of this class is owned by its
// mojo message pipe.
class RemotingBridge;
// Main constructor. |tab_id| refers to any remoted content managed
// by this instance (i.e., any remoted content from one tab/WebContents).
using CancelPermissionRequestCallback = base::OnceClosure;
// Called with true to mean "allowed", false to mean "not allowed".
using PermissionResultCallback = base::OnceCallback<void(bool)>;
using PermissionRequestCallback =
base::RepeatingCallback<CancelPermissionRequestCallback(
PermissionResultCallback)>;
CastRemotingConnector(media_router::MediaRouter* router,
SessionID tab_id,
PermissionRequestCallback request_callback);
// Creates a RemotingBridge that implements the requested Remoter service, and
// binds it to the interface |request|.
void CreateBridge(media::mojom::RemotingSourcePtr source,
media::mojom::RemoterRequest request);
// Called by the RemotingBridge constructor/destructor to register/deregister
// an instance. This allows this connector to broadcast notifications to all
// active sources.
void RegisterBridge(RemotingBridge* bridge);
void DeregisterBridge(RemotingBridge* bridge,
media::mojom::RemotingStopReason reason);
// media::mojom::MirrorServiceRemotingSource implementation.
// media::mojom::RemotingSource implementation.
void OnSinkAvailable(media::mojom::RemotingSinkMetadataPtr metadata) override;
void OnMessageFromSink(const std::vector<uint8_t>& message) override;
void OnStopped(media::mojom::RemotingStopReason reason) override;
// media::mojom::MirrorServiceRemotingSource implementation.
void OnError() override;
// media::mojom::RemotingSource implementation.
void OnSinkGone() override;
void OnStarted() override;
void OnStartFailed(media::mojom::RemotingStartFailReason reason) override;
// These methods are called by RemotingBridge to forward media::mojom::Remoter
// calls from a source through to this connector. They ensure that only one
// source is allowed to be in a remoting session at a time, and that no source
// may interfere with any other.
void StartRemoting(RemotingBridge* bridge);
void StartRemotingDataStreams(
RemotingBridge* bridge,
mojo::ScopedDataPipeConsumerHandle audio_pipe,
mojo::ScopedDataPipeConsumerHandle video_pipe,
media::mojom::RemotingDataStreamSenderRequest audio_sender_request,
media::mojom::RemotingDataStreamSenderRequest video_sender_request);
void StopRemoting(RemotingBridge* bridge,
media::mojom::RemotingStopReason reason,
bool is_initiated_by_source);
void SendMessageToSink(RemotingBridge* bridge,
const std::vector<uint8_t>& message);
void EstimateTransmissionCapacity(
media::mojom::Remoter::EstimateTransmissionCapacityCallback callback);
// Called after permission check. Either call |remoter_| to start remoting or
// notify the source that start fails due to no permission.
void StartRemotingIfPermitted();
// Called when RTP streams are started.
void OnDataStreamsStarted(
mojo::ScopedDataPipeConsumerHandle audio_pipe,
mojo::ScopedDataPipeConsumerHandle video_pipe,
media::mojom::RemotingDataStreamSenderRequest audio_sender_request,
media::mojom::RemotingDataStreamSenderRequest video_sender_request,
int32_t audio_stream_id,
int32_t video_stream_id);
// Error handlers for message sending during an active remoting session. When
// a failure occurs, these immediately force-stop remoting.
void OnDataSendFailed();
// Called when any connection error/lost occurs with the MediaRemoter.
void OnMirrorServiceStopped();
media_router::MediaRouter* const media_router_;
const SessionID tab_id_;
// The callback to get permission.
const PermissionRequestCallback permission_request_callback_;
// Describes the remoting sink's metadata and its enabled features. The sink's
// metadata is updated by the mirror service calling OnSinkAvailable() and
// cleared when remoting stops.
media::mojom::RemotingSinkMetadata sink_metadata_;
// Set of registered RemotingBridges, maintained by RegisterBridge() and
// DeregisterBridge(). These pointers are always valid while they are in this
// set.
std::set<RemotingBridge*> bridges_;
// When non-null, an active remoting session is taking place, with this
// pointing to the RemotingBridge being used to communicate with the source.
RemotingBridge* active_bridge_;
// TODO(xjz): Remove these after Mirroring Service is launched.
mojo::Binding<media::mojom::MirrorServiceRemotingSource> deprecated_binding_;
media::mojom::MirrorServiceRemoterPtr deprecated_remoter_;
mojo::Binding<media::mojom::RemotingSource> binding_;
media::mojom::RemoterPtr remoter_;
// Permission is checked the first time remoting requested to start for each
// casting session.
base::Optional<bool> remoting_allowed_;
// This callback is non-null when a dialog is showing to get user's
// permission, and is reset when the dialog closes.
CancelPermissionRequestCallback permission_request_cancel_callback_;
// Produces weak pointers that are only valid for the current remoting
// session. This is used to cancel any outstanding callbacks when a remoting
// session is stopped.
base::WeakPtrFactory<CastRemotingConnector> weak_factory_;
// Key used with the base::SupportsUserData interface to search for an
// instance of CastRemotingConnector owned by a WebContents.
static const void* const kUserDataKey;
DISALLOW_COPY_AND_ASSIGN(CastRemotingConnector);
};
#endif // CHROME_BROWSER_MEDIA_CAST_REMOTING_CONNECTOR_H_