1 // Copyright 2016 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #ifndef MEDIA_REMOTING_RENDERER_CONTROLLER_H_
6 #define MEDIA_REMOTING_RENDERER_CONTROLLER_H_
10 #include "base/functional/callback.h"
11 #include "base/memory/raw_ptr.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/threading/thread_checker.h"
14 #include "base/timer/timer.h"
15 #include "build/build_config.h"
16 #include "build/buildflag.h"
17 #include "media/base/media_observer.h"
18 #include "media/media_buildflags.h"
19 #include "media/mojo/mojom/remoting.mojom.h"
20 #include "media/mojo/mojom/remoting_common.mojom.h"
21 #include "media/remoting/metrics.h"
22 #include "mojo/public/cpp/bindings/pending_receiver.h"
23 #include "mojo/public/cpp/bindings/pending_remote.h"
24 #include "mojo/public/cpp/bindings/receiver.h"
25 #include "mojo/public/cpp/bindings/remote.h"
27 #if BUILDFLAG(ENABLE_MEDIA_REMOTING_RPC)
28 #include "third_party/openscreen/src/cast/streaming/rpc_messenger.h" // nogncheck
29 #include "third_party/openscreen/src/util/weak_ptr.h" // nogncheck
40 // This class monitors player events as a MediaObserver and may trigger the
41 // switch of the media renderer between local playback and remoting.
42 class RendererController final : public mojom::RemotingSource,
43 public MediaObserver {
46 mojo::PendingReceiver<mojom::RemotingSource> source_receiver,
47 mojo::PendingRemote<mojom::Remoter> remoter);
49 RendererController(const RendererController&) = delete;
50 RendererController& operator=(const RendererController&) = delete;
52 ~RendererController() override;
54 // mojom::RemotingSource implementations.
55 void OnSinkAvailable(mojom::RemotingSinkMetadataPtr metadata) override;
56 void OnSinkGone() override;
57 void OnStarted() override;
58 void OnStartFailed(mojom::RemotingStartFailReason reason) override;
59 void OnMessageFromSink(const std::vector<uint8_t>& message) override;
60 void OnStopped(mojom::RemotingStopReason reason) override;
62 // MediaObserver implementation.
63 void OnBecameDominantVisibleContent(bool is_dominant) override;
64 void OnMetadataChanged(const PipelineMetadata& metadata) override;
65 void OnRemotePlaybackDisabled(bool disabled) override;
66 void OnMediaRemotingRequested() override;
67 void OnPlaying() override;
68 void OnPaused() override;
69 void OnFrozen() override;
70 void OnDataSourceInitialized(const GURL& url_after_redirects) override;
71 void OnHlsManifestDetected() override;
72 void SetClient(MediaObserverClient* client) override;
74 base::WeakPtr<RendererController> GetWeakPtr() {
75 return weak_factory_.GetWeakPtr();
78 // Used by AdaptiveRendererFactory to query whether to create a Media
80 bool remote_rendering_started() const {
81 DCHECK(thread_checker_.CalledOnValidThread());
82 return remote_rendering_started_;
85 using DataPipeStartCallback = base::OnceCallback<void(
86 mojo::PendingRemote<mojom::RemotingDataStreamSender> audio,
87 mojo::PendingRemote<mojom::RemotingDataStreamSender> video,
88 mojo::ScopedDataPipeProducerHandle audio_handle,
89 mojo::ScopedDataPipeProducerHandle video_handle)>;
90 // Creates up to two data pipes with a byte capacity of |data_pipe_capacity|:
91 // one for audio if |audio| is true and one for |video| if video is true. The
92 // controller then starts processing the consumer ends of the data pipes,
93 // with the producer ends supplied to the |done_callback|.
94 void StartDataPipe(uint32_t data_pipe_capacity,
97 DataPipeStartCallback done_callback);
99 #if BUILDFLAG(ENABLE_MEDIA_REMOTING_RPC)
100 openscreen::WeakPtr<openscreen::cast::RpcMessenger> GetRpcMessenger();
103 // Called by CourierRenderer when it encountered a fatal error. This will
104 // cause remoting to shut down. Media remoting might be re-tried after the
105 // media element stops and re-starts being the dominant visible content in the
107 void OnRendererFatalError(StopTrigger stop_trigger);
110 friend class RendererControllerTest;
112 bool has_audio() const {
113 return pipeline_metadata_.has_audio &&
114 pipeline_metadata_.audio_decoder_config.IsValidConfig();
117 bool has_video() const {
118 return pipeline_metadata_.has_video &&
119 pipeline_metadata_.video_decoder_config.IsValidConfig();
122 // Called when the session availability state may have changed. Each call to
123 // this method could cause a remoting session to be started or stopped; and if
124 // that happens, the |start_trigger| or |stop_trigger| must be the reason.
125 void UpdateFromSessionState(StartTrigger start_trigger,
126 StopTrigger stop_trigger);
128 bool IsVideoCodecSupported() const;
129 bool IsAudioCodecSupported() const;
130 bool IsAudioOrVideoSupported() const;
132 // Returns |kCompatible| if all of the technical requirements for the media
133 // pipeline and remote rendering are being met, and the first detected
134 // reason if incompatible. This does not include environmental conditions,
135 // such as the content being dominant in the viewport, available network
137 RemotingCompatibility GetVideoCompatibility() const;
138 RemotingCompatibility GetAudioCompatibility() const;
139 RemotingCompatibility GetCompatibility() const;
141 // Determines whether to enter or leave Remoting mode and switches if
142 // necessary. Each call to this method could cause a remoting session to be
143 // started or stopped; and if that happens, the |start_trigger| or
144 // |stop_trigger| must be the reason.
145 void UpdateAndMaybeSwitch(StartTrigger start_trigger,
146 StopTrigger stop_trigger);
148 // Activates or deactivates the remote playback monitoring based on whether
149 // the element is compatible with Remote Playback API.
150 void UpdateRemotePlaybackAvailabilityMonitoringState();
152 // Queries on remoting sink capabilities.
153 bool HasVideoCapability(mojom::RemotingSinkVideoCapability capability) const;
154 bool HasAudioCapability(mojom::RemotingSinkAudioCapability capability) const;
155 bool HasFeatureCapability(mojom::RemotingSinkFeature capability) const;
156 bool SinkSupportsRemoting() const;
157 bool ShouldBeRemoting();
159 // Start `pixel_rate_timer_` to calculate pixel rate.
160 void MaybeStartCalculatePixelRateTimer();
161 void DoCalculatePixelRate(int decoded_frame_count_before_delay,
162 base::TimeTicks delayed_start_time);
163 PixelRateSupport GetPixelRateSupport() const;
165 // Callback from RpcMessenger when sending message to remote sink.
166 void SendMessageToSink(std::vector<uint8_t> message);
168 #if BUILDFLAG(IS_ANDROID)
169 bool IsAudioRemotePlaybackSupported() const;
170 bool IsVideoRemotePlaybackSupported() const;
171 bool IsRemotePlaybackSupported() const;
172 #endif // BUILDFLAG(IS_ANDROID)
174 #if BUILDFLAG(ENABLE_MEDIA_REMOTING_RPC)
175 // Handles dispatching of incoming and outgoing RPC messages.
176 openscreen::cast::RpcMessenger rpc_messenger_;
179 const mojo::Receiver<mojom::RemotingSource> receiver_;
180 const mojo::Remote<mojom::Remoter> remoter_;
182 // When the sink is available for remoting, this describes its metadata. When
183 // not available, this is empty. Updated by OnSinkAvailable/Gone().
184 mojom::RemotingSinkMetadataPtr sink_metadata_;
186 // Indicates whether remoting is started.
187 bool remote_rendering_started_ = false;
189 // Indicates whether remote playback is currently disabled. This starts out as
190 // true, and should be updated at least once via a call to
191 // OnRemotePlaybackDisabled() at some point in the future. A web page
192 // typically sets/removes the disableRemotePlayback attribute on a
193 // HTMLMediaElement to disable/enable remoting of its content. Please see the
194 // Remote Playback API spec for more details:
195 // https://w3c.github.io/remote-playback
196 bool is_remote_playback_disabled_ = true;
198 // Indicates whether video is the dominant visible content in the tab.
199 bool is_dominant_content_ = false;
201 // Indicates whether video is paused.
202 bool is_paused_ = true;
204 // Indicates whether OnRendererFatalError() has been called. This indicates
205 // one of several possible problems: 1) An environmental problem such as
206 // out-of-memory, insufficient network bandwidth, etc. 2) The receiver may
207 // have been unable to play-out the content correctly (e.g., not capable of a
208 // high frame rate at a high resolution). 3) An implementation bug. In any
209 // case, once a renderer encounters a fatal error, remoting will be shut down.
210 // The value gets reset after the media element stops being the dominant
211 // visible content in the tab.
212 bool encountered_renderer_fatal_error_ = false;
214 // This is used to check all the methods are called on the current thread in
216 base::ThreadChecker thread_checker_;
218 // Current pipeline metadata.
219 PipelineMetadata pipeline_metadata_;
221 // Current data source information.
222 GURL url_after_redirects_;
224 bool is_hls_ = false;
226 // True if the browser has requested to start remoting without fullscreening
227 // the media content. The value is reset to False when it switches back to
229 bool is_media_remoting_requested_ = false;
231 // Records session events of interest.
232 SessionMetricsRecorder metrics_recorder_;
234 // Not owned by this class. Can only be set once by calling SetClient().
235 raw_ptr<MediaObserverClient> client_ = nullptr;
237 // This timer is used to calculate the media content's pixel rate. The timer
238 // is stopped when the media playback is paused or when the media metadata
240 base::OneShotTimer pixel_rate_timer_;
242 // Current pixel rate. Its value is reset to 0 when
243 // pipeline_metadata_.natural_size changes.
244 double pixels_per_second_ = 0;
246 raw_ptr<const base::TickClock> clock_;
248 base::WeakPtrFactory<RendererController> weak_factory_{this};
251 } // namespace remoting
254 #endif // MEDIA_REMOTING_RENDERER_CONTROLLER_H_