chromium/fuchsia_web/webengine/renderer/web_engine_audio_output_device.h

// 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_