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
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249

media / audio / audio_output_device.h [blame]

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// Audio rendering unit utilizing audio output stream provided by browser
// process through IPC.
//
// Relationship of classes.
//
//  audio::OutputController              AudioOutputDevice
//           ^                                  ^
//           |                                  |
//           v                 IPC              v
//  audio::OutputStream  <---------> AudioOutputIPC (MojoAudioOutputIPC)
//
// Transportation of audio samples from the render to the browser process
// is done by using shared memory in combination with a sync socket pair
// to generate a low latency transport. The AudioOutputDevice user registers an
// AudioOutputDevice::RenderCallback at construction and will be polled by the
// AudioOutputController for audio to be played out by the underlying audio
// layers.
//
// State sequences.
//
//            Task [IO thread]                  IPC [IO thread]
// RequestDeviceAuthorization -> RequestDeviceAuthorizationOnIOThread ------>
// RequestDeviceAuthorization ->
//             <- OnDeviceAuthorized <- AudioMsg_NotifyDeviceAuthorized <-
//
// Start -> CreateStreamOnIOThread -----> CreateStream ------>
//       <- OnStreamCreated <- AudioMsg_NotifyStreamCreated <-
//       ---> PlayOnIOThread -----------> PlayStream -------->
//
// Optionally Play() / Pause() sequences may occur:
// Play -> PlayOnIOThread --------------> PlayStream --------->
// Pause -> PauseOnIOThread ------------> PauseStream -------->
// (note that Play() / Pause() sequences before
// OnStreamCreated are deferred until OnStreamCreated, with the last valid
// state being used)
//
// AudioOutputDevice::Render => audio transport on audio thread =>
//                               |
// Stop --> ShutDownOnIOThread -------->  CloseStream -> Close
//
// This class utilizes several threads during its lifetime, namely:
// 1. Creating thread.
//    Must be the main render thread.
// 2. Control thread (may be the main render thread or another thread).
//    The methods: Start(), Stop(), Play(), Pause(), SetVolume()
//    must be called on the same thread.
// 3. IO thread (internal implementation detail - not exposed to public API)
//    The thread within which this class receives all the IPC messages and
//    IPC communications can only happen in this thread.
// 4. Audio transport thread (See AudioDeviceThread).
//    Responsible for calling the AudioOutputDeviceThreadCallback
//    implementation that in turn calls AudioRendererSink::RenderCallback
//    which feeds audio samples to the audio layer in the browser process using
//    sync sockets and shared memory.
//
// Implementation notes:
// - The user must call Stop() before deleting the class instance.

#ifndef MEDIA_AUDIO_AUDIO_OUTPUT_DEVICE_H_
#define MEDIA_AUDIO_AUDIO_OUTPUT_DEVICE_H_

#include <memory>
#include <string>

#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "base/synchronization/waitable_event.h"
#include "base/thread_annotations.h"
#include "base/time/time.h"
#include "media/audio/audio_device_thread.h"
#include "media/audio/audio_output_ipc.h"
#include "media/audio/audio_sink_parameters.h"
#include "media/base/audio_parameters.h"
#include "media/base/audio_renderer_sink.h"
#include "media/base/media_export.h"
#include "media/base/output_device_info.h"

namespace base {
class OneShotTimer;
class SingleThreadTaskRunner;
}

namespace media {
class AudioOutputDeviceThreadCallback;

class MEDIA_EXPORT AudioOutputDevice : public AudioRendererSink,
                                       public AudioOutputIPCDelegate {
 public:
  // NOTE: Clients must call Initialize() before using.
  AudioOutputDevice(
      std::unique_ptr<AudioOutputIPC> ipc,
      const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
      const AudioSinkParameters& sink_params,
      base::TimeDelta authorization_timeout);

  AudioOutputDevice(const AudioOutputDevice&) = delete;
  AudioOutputDevice& operator=(const AudioOutputDevice&) = delete;

  // Request authorization to use the device specified in the constructor.
  void RequestDeviceAuthorization();

  // AudioRendererSink implementation.
  void Initialize(const AudioParameters& params,
                  RenderCallback* callback) override;
  void Start() override;
  void Stop() override;
  void Play() override;
  void Pause() override;
  void Flush() override;
  bool SetVolume(double volume) override;
  OutputDeviceInfo GetOutputDeviceInfo() override;
  void GetOutputDeviceInfoAsync(OutputDeviceInfoCB info_cb) override;
  bool IsOptimizedForHardwareParameters() override;
  bool CurrentThreadIsRenderingThread() override;

  // Methods called on IO thread ----------------------------------------------
  // AudioOutputIPCDelegate methods.
  void OnError() override;
  void OnDeviceAuthorized(OutputDeviceStatus device_status,
                          const AudioParameters& output_params,
                          const std::string& matched_device_id) override;
  void OnStreamCreated(base::UnsafeSharedMemoryRegion shared_memory_region,
                       base::SyncSocket::ScopedHandle socket_handle,
                       bool play_automatically) override;
  void OnIPCClosed() override;

  AudioOutputIPC* GetIpcForTesting() { return ipc_.get(); }

 protected:
  // Magic required by ref_counted.h to avoid any code deleting the object
  // accidentally while there are references to it.
  friend class base::RefCountedThreadSafe<AudioOutputDevice>;
  ~AudioOutputDevice() override;

 private:
  enum StartupState {
    IDLE,                       // Authorization not requested.
    AUTHORIZATION_REQUESTED,    // Sent (possibly completed) device
                                // authorization request.
    STREAM_CREATION_REQUESTED,  // Sent (possibly completed) device creation
                                // request. Can Play()/Pause()/Stop().
  };

  // This enum is used for UMA, so the only allowed operation on this definition
  // is to add new states to the bottom, update kMaxValue, and update the
  // histogram "Media.Audio.Render.StreamCallbackError2".
  enum Error {
    kNoError = 0,
    kErrorDuringCreation = 1,
    kErrorDuringRendering = 2,
    kMaxValue = kErrorDuringRendering
  };

  // Methods called on IO thread ----------------------------------------------
  // The following methods are tasks posted on the IO thread that need to
  // be executed on that thread.  They use AudioOutputIPC to send IPC messages
  // upon state changes.
  void RequestDeviceAuthorizationOnIOThread();
  void InitializeOnIOThread(const AudioParameters& params,
                            MayBeDangling<RenderCallback> callback);
  void CreateStreamOnIOThread();
  void PlayOnIOThread();
  void PauseOnIOThread();
  void FlushOnIOThread();
  void ShutDownOnIOThread();
  void SetVolumeOnIOThread(double volume);

  // Process device authorization result on the IO thread.
  void ProcessDeviceAuthorizationOnIOThread(
      OutputDeviceStatus device_status,
      const AudioParameters& output_params,
      const std::string& matched_device_id,
      bool timed_out);

  void NotifyRenderCallbackOfError();

  OutputDeviceInfo GetOutputDeviceInfo_Signaled();
  void OnAuthSignal();

  const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;

  AudioParameters audio_parameters_;

  raw_ptr<RenderCallback, DanglingUntriaged> callback_;

  // A pointer to the IPC layer that takes care of sending requests over to
  // the implementation. May be set to nullptr after errors.
  std::unique_ptr<AudioOutputIPC> ipc_;

  // Current state (must only be accessed from the IO thread).  See comments for
  // State enum above.
  StartupState state_;

  // For UMA stats. May only be accessed on the IO thread.
  Error had_error_ = kNoError;

  // Last set volume.
  double volume_ = 1.0;

  // The media session ID used to identify which input device to be started.
  // Only used by Unified IO.
  base::UnguessableToken session_id_;

  // ID of hardware output device to be used (provided |session_id_| is zero)
  const std::string device_id_;

  // If |device_id_| is empty and |session_id_| is not, |matched_device_id_| is
  // received in OnDeviceAuthorized().
  std::string matched_device_id_;

  // In order to avoid a race between OnStreamCreated and Stop(), we use this
  // guard to control stopping and starting the audio thread.
  base::Lock audio_thread_lock_;
  std::unique_ptr<AudioOutputDeviceThreadCallback> audio_callback_;
  std::unique_ptr<AudioDeviceThread> audio_thread_
      GUARDED_BY(audio_thread_lock_);

  // Temporary hack to ignore OnStreamCreated() due to the user calling Stop()
  // so we don't start the audio thread pointing to a potentially freed
  // |callback_|.
  //
  // TODO(scherkus): Replace this by changing AudioRendererSink to either accept
  // the callback via Start(). See http://crbug.com/151051 for details.
  bool stopping_hack_ GUARDED_BY(audio_thread_lock_);

  base::WaitableEvent did_receive_auth_;
  AudioParameters output_params_;
  OutputDeviceStatus device_status_;

  const base::TimeDelta auth_timeout_;
  std::unique_ptr<base::OneShotTimer> auth_timeout_action_;

  // Pending callback for OutputDeviceInfo if it has not been received by the
  // time a call to GetGetOutputDeviceInfoAsync() is called.
  //
  // Lock for use ONLY with |pending_device_info_cb_| and |did_receive_auth_|,
  // if you add more usage of this lock ensure you have not added a deadlock.
  base::Lock device_info_lock_;
  OutputDeviceInfoCB pending_device_info_cb_ GUARDED_BY(device_info_lock_);
};

}  // namespace media

#endif  // MEDIA_AUDIO_AUDIO_OUTPUT_DEVICE_H_