| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef FUCHSIA_WEB_WEBENGINE_RENDERER_WEB_ENGINE_AUDIO_OUTPUT_DEVICE_H_ |
| #define FUCHSIA_WEB_WEBENGINE_RENDERER_WEB_ENGINE_AUDIO_OUTPUT_DEVICE_H_ |
| |
| #include <fuchsia/media/cpp/fidl.h> |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/shared_memory_mapping.h" |
| #include "base/synchronization/lock.h" |
| #include "base/time/time.h" |
| #include "base/timer/timer.h" |
| #include "fuchsia_web/webengine/web_engine_export.h" |
| #include "media/base/audio_renderer_sink.h" |
| |
| namespace base { |
| class SingleThreadTaskRunner; |
| class WritableSharedMemoryMapping; |
| } // namespace base |
| |
| // AudioRendererSink implementation for WebEngine. It sends audio to |
| // AudioConsumer provided by the OS. Unlike AudioOutputDevice this class sends |
| // to the system directly from the renderer process. All work is performed on |
| // the TaskRunner passed to Create(). It must be an IO thread to allow FIDL |
| // usage. AudioRendererSink can be used on a different thread. |
| class WEB_ENGINE_EXPORT WebEngineAudioOutputDevice |
| : public media::AudioRendererSink { |
| public: |
| static scoped_refptr<WebEngineAudioOutputDevice> Create( |
| fidl::InterfaceHandle<fuchsia::media::AudioConsumer> |
| audio_consumer_handle, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner); |
| |
| // Same as above, but creates a WebEngineAudioOutputDevice that runs on the |
| // default audio thread. |
| static scoped_refptr<WebEngineAudioOutputDevice> CreateOnDefaultThread( |
| fidl::InterfaceHandle<fuchsia::media::AudioConsumer> |
| audio_consumer_handle); |
| |
| // AudioRendererSink implementation. |
| void Initialize(const media::AudioParameters& params, |
| RenderCallback* callback) override; |
| void Start() override; |
| void Stop() override; |
| void Pause() override; |
| void Play() override; |
| void Flush() override; |
| bool SetVolume(double volume) override; |
| media::OutputDeviceInfo GetOutputDeviceInfo() override; |
| void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override; |
| bool IsOptimizedForHardwareParameters() override; |
| bool CurrentThreadIsRenderingThread() override; |
| |
| private: |
| friend class WebEngineAudioOutputDeviceTest; |
| |
| explicit WebEngineAudioOutputDevice( |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner); |
| ~WebEngineAudioOutputDevice() override; |
| |
| void BindAudioConsumerOnAudioThread( |
| fidl::InterfaceHandle<fuchsia::media::AudioConsumer> |
| audio_consumer_handle); |
| |
| // AudioRendererSink handlers for the audio thread. |
| void InitializeOnAudioThread(const media::AudioParameters& params); |
| void StartOnAudioThread(); |
| void StopOnAudioThread(); |
| void PauseOnAudioThread(); |
| void PlayOnAudioThread(); |
| void FlushOnAudioThread(); |
| void SetVolumeOnAudioThread(double volume); |
| |
| // Initializes |stream_sink_|. |
| void CreateStreamSink(); |
| |
| // Sends current volume to |volume_control_|. |
| void UpdateVolume(); |
| |
| // Polls current |audio_consumer_| status. |
| void WatchAudioConsumerStatus(); |
| |
| // Callback for AudioConsumer::WatchStatus(). |
| void OnAudioConsumerStatusChanged(fuchsia::media::AudioConsumerStatus status); |
| |
| // Schedules next PumpSamples() to pump next audio packet. |
| void SchedulePumpSamples(); |
| |
| // Pumps a single packet to AudioConsumer and calls SchedulePumpSamples() to |
| // pump the next packet. |
| void PumpSamples(base::TimeTicks playback_time); |
| |
| // Callback for StreamSink::SendPacket(). |
| void OnStreamSendDone(size_t buffer_index); |
| |
| // Reports an error and shuts down the AudioConsumer connection. |
| void ReportError(); |
| |
| // Task runner used for all activity. Normally this is the audio thread owned |
| // by FuchsiaAudioDeviceFactory. All AudioRendererSink methods are called on |
| // another thread (normally the main renderer thread on which this object is |
| // created). |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner_; |
| |
| fuchsia::media::AudioConsumerPtr audio_consumer_; |
| fuchsia::media::StreamSinkPtr stream_sink_; |
| fuchsia::media::audio::VolumeControlPtr volume_control_; |
| |
| media::AudioParameters params_; |
| |
| // Lock used to control access to |callback_|. |
| base::Lock callback_lock_; |
| |
| // Callback passed to Initialize(). It's set on the main thread (that calls |
| // Initialize() and Stop()), but used on the audio thread (which corresponds |
| // to the |task_runner_|). This is necessary because AudioRendererSink must |
| // guarantee that the callback is not called after Stop(). |callback_lock_| is |
| // used to synchronize access to the |callback_|. |
| RenderCallback* callback_ GUARDED_BY(callback_lock_) = nullptr; |
| |
| // Mapped memory for buffers shared with |stream_sink_|. |
| std::vector<base::WritableSharedMemoryMapping> stream_sink_buffers_; |
| |
| // Indices of unused buffers in |stream_sink_buffers_|. |
| std::vector<size_t> available_buffers_indices_; |
| |
| float volume_ = 1.0; |
| |
| // Current position in the stream in samples since the stream was started. |
| size_t media_pos_frames_ = 0; |
| |
| // Current minimum lead time returned by the |audio_consumer_|. |
| base::TimeDelta min_lead_time_; |
| |
| // Current timeline parameters provided by the |audio_consumer_| in the last |
| // AudioConsumerStatus. See |
| // https://fuchsia.dev/reference/fidl/fuchsia.media#TimelineFunction for |
| // details on how these parameters are used. |timeline_reference_time_| is set |
| // to null value when there is no presentation timeline (i.e. playback isn't |
| // active). |
| base::TimeTicks timeline_reference_time_; |
| base::TimeDelta timeline_subject_time_; |
| uint32_t timeline_reference_delta_; |
| uint32_t timeline_subject_delta_; |
| |
| // Set to true between DoPause() and DoPlay(). AudioConsumer implementations |
| // should drop |presentation_timeline| when the stream is paused, but the |
| // state is updated asynchronously. This flag is used to avoid sending packets |
| // until the state is updated. |
| bool paused_ = false; |
| |
| // Timer for PumpSamples(). |
| base::OneShotTimer pump_samples_timer_; |
| |
| // AudioBus used in PumpSamples(). Stored here to avoid re-allocating it for |
| // every packet. |
| std::unique_ptr<media::AudioBus> audio_bus_; |
| }; |
| |
| #endif // FUCHSIA_WEB_WEBENGINE_RENDERER_WEB_ENGINE_AUDIO_OUTPUT_DEVICE_H_ |