1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166

fuchsia_web / webengine / renderer / web_engine_audio_output_device.h [blame]

// 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/raw_ptr.h"
#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_|.
  raw_ptr<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_