blob: 93ab714b3d563d4c07bf095c83cbfaef6773a3dc [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 MEDIA_REMOTING_RENDERER_CONTROLLER_H_
#define MEDIA_REMOTING_RENDERER_CONTROLLER_H_
#include <memory>
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "media/base/media_observer.h"
#include "media/media_buildflags.h"
#include "media/mojo/mojom/remoting.mojom.h"
#include "media/mojo/mojom/remoting_common.mojom.h"
#include "media/remoting/metrics.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#if BUILDFLAG(ENABLE_MEDIA_REMOTING_RPC)
#include "third_party/openscreen/src/cast/streaming/rpc_messenger.h" // nogncheck
#include "third_party/openscreen/src/util/weak_ptr.h" // nogncheck
#endif
namespace base {
class TickClock;
}
namespace media {
namespace remoting {
// This class monitors player events as a MediaObserver and may trigger the
// switch of the media renderer between local playback and remoting.
class RendererController final : public mojom::RemotingSource,
public MediaObserver {
public:
RendererController(
mojo::PendingReceiver<mojom::RemotingSource> source_receiver,
mojo::PendingRemote<mojom::Remoter> remoter);
RendererController(const RendererController&) = delete;
RendererController& operator=(const RendererController&) = delete;
~RendererController() override;
// mojom::RemotingSource implementations.
void OnSinkAvailable(mojom::RemotingSinkMetadataPtr metadata) override;
void OnSinkGone() override;
void OnStarted() override;
void OnStartFailed(mojom::RemotingStartFailReason reason) override;
void OnMessageFromSink(const std::vector<uint8_t>& message) override;
void OnStopped(mojom::RemotingStopReason reason) override;
// MediaObserver implementation.
void OnBecameDominantVisibleContent(bool is_dominant) override;
void OnMetadataChanged(const PipelineMetadata& metadata) override;
void OnRemotePlaybackDisabled(bool disabled) override;
void OnPlaying() override;
void OnPaused() override;
void OnDataSourceInitialized(const GURL& url_after_redirects) override;
void OnHlsManifestDetected() override;
void SetClient(MediaObserverClient* client) override;
base::WeakPtr<RendererController> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
// Used by AdaptiveRendererFactory to query whether to create a Media
// Remoting Renderer.
bool remote_rendering_started() const {
DCHECK(thread_checker_.CalledOnValidThread());
return remote_rendering_started_;
}
using DataPipeStartCallback = base::OnceCallback<void(
mojo::PendingRemote<mojom::RemotingDataStreamSender> audio,
mojo::PendingRemote<mojom::RemotingDataStreamSender> video,
mojo::ScopedDataPipeProducerHandle audio_handle,
mojo::ScopedDataPipeProducerHandle video_handle)>;
// Creates up to two data pipes with a byte capacity of |data_pipe_capacity|:
// one for audio if |audio| is true and one for |video| if video is true. The
// controller then starts processing the consumer ends of the data pipes,
// with the producer ends supplied to the |done_callback|.
void StartDataPipe(uint32_t data_pipe_capacity,
bool audio,
bool video,
DataPipeStartCallback done_callback);
#if BUILDFLAG(ENABLE_MEDIA_REMOTING_RPC)
openscreen::WeakPtr<openscreen::cast::RpcMessenger> GetRpcMessenger();
#endif
// Called by CourierRenderer when it encountered a fatal error. This will
// cause remoting to shut down. Media remoting might be re-tried after the
// media element stops and re-starts being the dominant visible content in the
// tab.
void OnRendererFatalError(StopTrigger stop_trigger);
private:
friend class RendererControllerTest;
bool has_audio() const {
return pipeline_metadata_.has_audio &&
pipeline_metadata_.audio_decoder_config.IsValidConfig();
}
bool has_video() const {
return pipeline_metadata_.has_video &&
pipeline_metadata_.video_decoder_config.IsValidConfig();
}
// Called when the session availability state may have changed. Each call to
// this method could cause a remoting session to be started or stopped; and if
// that happens, the |start_trigger| or |stop_trigger| must be the reason.
void UpdateFromSessionState(StartTrigger start_trigger,
StopTrigger stop_trigger);
bool IsVideoCodecSupported() const;
bool IsAudioCodecSupported() const;
bool IsAudioOrVideoSupported() const;
// Returns |kCompatible| if all of the technical requirements for the media
// pipeline and remote rendering are being met, and the first detected
// reason if incompatible. This does not include environmental conditions,
// such as the content being dominant in the viewport, available network
// bandwidth, etc.
RemotingCompatibility GetVideoCompatibility() const;
RemotingCompatibility GetAudioCompatibility() const;
RemotingCompatibility GetCompatibility() const;
// Determines whether to enter or leave Remoting mode and switches if
// necessary. Each call to this method could cause a remoting session to be
// started or stopped; and if that happens, the |start_trigger| or
// |stop_trigger| must be the reason.
void UpdateAndMaybeSwitch(StartTrigger start_trigger,
StopTrigger stop_trigger);
// Activates or deactivates the remote playback monitoring based on whether
// the element is compatible with Remote Playback API.
void UpdateRemotePlaybackAvailabilityMonitoringState();
// Start |delayed_start_stability_timer_| to ensure all preconditions are met
// and held stable for a short time before starting remoting.
void WaitForStabilityBeforeStart(StartTrigger start_trigger);
// Cancel the start of remoting.
void CancelDelayedStart();
// Called when the delayed start ends. |decoded_frame_count_before_delay| is
// the total number of frames decoded before the delayed start began.
// |delayed_start_time| is the time that the delayed start began.
void OnDelayedStartTimerFired(StartTrigger start_trigger,
unsigned decoded_frame_count_before_delay,
base::TimeTicks delayed_start_time);
// Records in a histogram and returns whether the receiver supports the given
// pixel rate.
bool RecordPixelRateSupport(double pixels_per_second);
// Queries on remoting sink capabilities.
bool HasVideoCapability(mojom::RemotingSinkVideoCapability capability) const;
bool HasAudioCapability(mojom::RemotingSinkAudioCapability capability) const;
bool HasFeatureCapability(mojom::RemotingSinkFeature capability) const;
bool SinkSupportsRemoting() const;
// Callback from RpcMessenger when sending message to remote sink.
void SendMessageToSink(std::vector<uint8_t> message);
#if defined(OS_ANDROID)
bool IsAudioRemotePlaybackSupported() const;
bool IsVideoRemotePlaybackSupported() const;
bool IsRemotePlaybackSupported() const;
#endif // defined(OS_ANDROID)
#if BUILDFLAG(ENABLE_MEDIA_REMOTING_RPC)
// Handles dispatching of incoming and outgoing RPC messages.
openscreen::cast::RpcMessenger rpc_messenger_;
#endif
const mojo::Receiver<mojom::RemotingSource> receiver_;
const mojo::Remote<mojom::Remoter> remoter_;
// When the sink is available for remoting, this describes its metadata. When
// not available, this is empty. Updated by OnSinkAvailable/Gone().
mojom::RemotingSinkMetadata sink_metadata_;
// Indicates whether remoting is started.
bool remote_rendering_started_ = false;
// Indicates whether remote playback is currently disabled. This starts out as
// true, and should be updated at least once via a call to
// OnRemotePlaybackDisabled() at some point in the future. A web page
// typically sets/removes the disableRemotePlayback attribute on a
// HTMLMediaElement to disable/enable remoting of its content. Please see the
// Remote Playback API spec for more details:
// https://w3c.github.io/remote-playback
bool is_remote_playback_disabled_ = true;
// Indicates whether video is the dominant visible content in the tab.
bool is_dominant_content_ = false;
// Indicates whether video is paused.
bool is_paused_ = true;
// Indicates whether OnRendererFatalError() has been called. This indicates
// one of several possible problems: 1) An environmental problem such as
// out-of-memory, insufficient network bandwidth, etc. 2) The receiver may
// have been unable to play-out the content correctly (e.g., not capable of a
// high frame rate at a high resolution). 3) An implementation bug. In any
// case, once a renderer encounters a fatal error, remoting will be shut down.
// The value gets reset after the media element stops being the dominant
// visible content in the tab.
bool encountered_renderer_fatal_error_ = false;
// When this is true, remoting will never start again for the lifetime of this
// controller.
bool permanently_disable_remoting_ = false;
// This is used to check all the methods are called on the current thread in
// debug builds.
base::ThreadChecker thread_checker_;
// Current pipeline metadata.
PipelineMetadata pipeline_metadata_;
// Current data source information.
GURL url_after_redirects_;
bool is_hls_ = false;
// Records session events of interest.
SessionMetricsRecorder metrics_recorder_;
// Not owned by this class. Can only be set once by calling SetClient().
MediaObserverClient* client_ = nullptr;
// When this is running, it indicates that remoting will be started later
// when the timer gets fired. The start will be canceled if there is any
// precondition change that does not allow for remoting duting this period.
// TODO(xjz): Estimate whether the transmission bandwidth is sufficient to
// remote the content while this timer is running.
base::OneShotTimer delayed_start_stability_timer_;
const base::TickClock* clock_;
base::WeakPtrFactory<RendererController> weak_factory_{this};
};
} // namespace remoting
} // namespace media
#endif // MEDIA_REMOTING_RENDERER_CONTROLLER_H_