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
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275

media / audio / apple / audio_low_latency_input.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.
//
// Implementation of AudioInputStream for Mac OS X using the special AUHAL
// input Audio Unit present in OS 10.4 and later.
// The AUHAL input Audio Unit is for low-latency audio I/O.
//
// Overview of operation:
//
// - An object of AUAudioInputStream is created by the AudioManager
//   factory: audio_man->MakeAudioInputStream().
// - Next some thread will call Open(), at that point the underlying
//   AUHAL output Audio Unit is created and configured.
// - Then some thread will call Start(sink).
//   Then the Audio Unit is started which creates its own thread which
//   periodically will provide the sink with more data as buffers are being
//   produced/recorded.
// - At some point some thread will call Stop(), which we handle by directly
//   stopping the AUHAL output Audio Unit.
// - The same thread that called stop will call Close() where we cleanup
//   and notify the audio manager, which likely will destroy this object.
//
// Implementation notes:
//
// - It is recommended to first acquire the native sample rate of the default
//   input device and then use the same rate when creating this object.
// - Calling Close() also leads to self destruction.
// - The latency consists of two parts:
//   1) Hardware latency, which includes Audio Unit latency, audio device
//      latency;
//   2) The delay between the actual recording instant and the time when the
//      data packet is provided as a callback.
//
#ifndef MEDIA_AUDIO_APPLE_AUDIO_LOW_LATENCY_INPUT_H_
#define MEDIA_AUDIO_APPLE_AUDIO_LOW_LATENCY_INPUT_H_

#include <AudioUnit/AudioUnit.h>

#include <memory>
#include <vector>

#include "base/atomicops.h"
#include "base/cancelable_callback.h"
#include "base/memory/raw_ptr.h"
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "media/audio/agc_audio_stream.h"
#include "media/audio/audio_io.h"
#include "media/audio/system_glitch_reporter.h"
#include "media/base/amplitude_peak_detector.h"
#include "media/base/audio_block_fifo.h"
#include "media/base/audio_glitch_info.h"
#include "media/base/audio_parameters.h"

#if BUILDFLAG(IS_MAC)
#include "media/audio/mac/audio_manager_mac.h"
#else
#include "media/audio/ios/audio_manager_ios.h"
#endif

namespace media {
class AudioManagerApple;

class MEDIA_EXPORT AUAudioInputStream
    : public AgcAudioStream<AudioInputStream> {
 public:
  // The ctor takes all the usual parameters, plus |manager| which is the
  // the audio manager who is creating this object.
  AUAudioInputStream(AudioManagerApple* manager,
                     const AudioParameters& input_params,
                     AudioDeviceID audio_device_id,
                     const AudioManager::LogCallback& log_callback);

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

  // The dtor is typically called by the AudioManager only and it is usually
  // triggered by calling AudioInputStream::Close().
  ~AUAudioInputStream() override;

  // Implementation of AudioInputStream.
  AudioInputStream::OpenOutcome Open() override;
  void Start(AudioInputCallback* callback) override;
  void Stop() override;
  void Close() override;
  double GetMaxVolume() override;
  void SetVolume(double volume) override;
  double GetVolume() override;
  bool IsMuted() override;
  void SetOutputDeviceForAec(const std::string& output_device_id) override;

  // Returns true if the audio unit is active/running.
  // The result is based on the kAudioOutputUnitProperty_IsRunning property
  // which exists for output units.
  bool IsRunning();

  AudioDeviceID device_id() const { return input_device_id_; }
  size_t requested_buffer_size() const {
    return input_params_.frames_per_buffer();
  }
  AudioUnit audio_unit() const { return audio_unit_; }

  static bool IsEchoCancellationSupported(AudioDeviceID audio_device_id,
                                          const AudioParameters& params);

  // Fan out the data from the first half of audio_buffer into interleaved
  // stereo across the whole of audio_buffer. Public for testing only.
  static void UpmixMonoToStereoInPlace(AudioBuffer* audio_buffer,
                                       int bytes_per_sample);

 private:
  bool OpenAUHAL();
  bool OpenVoiceProcessingAU();

  // Callback functions called on a real-time priority I/O thread from the audio
  // unit. These methods are called when recorded audio is available.
  static OSStatus DataIsAvailable(void* context,
                                  AudioUnitRenderActionFlags* flags,
                                  const AudioTimeStamp* time_stamp,
                                  UInt32 bus_number,
                                  UInt32 number_of_frames,
                                  AudioBufferList* io_data);
  OSStatus OnDataIsAvailable(AudioUnitRenderActionFlags* flags,
                             const AudioTimeStamp* time_stamp,
                             UInt32 bus_number,
                             UInt32 number_of_frames);

  // Pushes recorded data to consumer of the input audio stream.
  OSStatus Provide(UInt32 number_of_frames,
                   AudioBufferList* io_data,
                   const AudioTimeStamp* time_stamp);

  // Gets the current capture time.
  base::TimeTicks GetCaptureTime(const AudioTimeStamp* input_time_stamp);

  // Issues the OnError() callback to the |sink_|.
  void HandleError(OSStatus err, const base::Location& location = FROM_HERE);

  // Helper methods to set and get atomic |input_callback_is_active_|.
  void SetInputCallbackIsActive(bool active);
  bool GetInputCallbackIsActive();

  // Checks if a stream was started successfully and the audio unit also starts
  // to call InputProc() as it should. This method is called once when a timer
  // expires some time after calling Start().
  void CheckInputStartupSuccess();

  // Uninitializes the audio unit if needed.
  void CloseAudioUnit();

  // Reinitializes the AudioUnit to use a new output device.
  void ReinitializeVoiceProcessingAudioUnit();

  // Adds extra UMA stats when it has been detected that startup failed.
  void AddHistogramsForFailedStartup();

  // Updates capture timestamp, current lost frames, and total lost frames and
  // glitches.
  void UpdateCaptureTimestamp(const AudioTimeStamp* timestamp);

  // Called from the dtor and when the stream is reset.
  void ReportAndResetStats();

  // Logs a message both to the log callback and to the console.
  void LogMessageEverywhere(const char* function_name,
                            const std::string& message);

  // Verifies that Open(), Start(), Stop() and Close() are all called on the
  // creating thread which is the main browser thread (CrBrowserMain) on Mac.
  THREAD_CHECKER(thread_checker_);

  // Our creator, the audio manager needs to be notified when we close.
  const raw_ptr<AudioManagerApple> manager_;

  // The audio parameters requested when creating the stream.
  const AudioParameters input_params_;

  // Stores the number of frames that we actually get callbacks for.
  // This may be different from what we ask for, so we use this for stats in
  // order to understand how often this happens and what are the typical values.
  size_t number_of_frames_provided_ = 0;

  // Pointer to the object that will receive the recorded audio samples.
  raw_ptr<AudioInputCallback> sink_ = nullptr;

  // Structure that holds the desired output format of the stream.
  // Note that, this format can differ from the device(=input) format.
  AudioStreamBasicDescription format_;

  // The special Audio Unit called AUHAL, which allows us to pass audio data
  // directly from a microphone, through the HAL, and to our application.
  // The AUHAL also enables selection of non default devices.
  AudioUnit audio_unit_{0};

  // The UID refers to the current input audio device.
  const AudioDeviceID input_device_id_;

  // Provides a mechanism for encapsulating one or more buffers of audio data.
  AudioBufferList audio_buffer_list_;

  // Temporary storage for recorded data. The InputProc() renders into this
  // array as soon as a frame of the desired buffer size has been recorded.
  std::unique_ptr<uint8_t[]> audio_data_buffer_;

  // Fixed capture hardware latency.
  base::TimeDelta hardware_latency_;

  // FIFO used to accumulates recorded data.
  media::AudioBlockFifo fifo_;

  // Used to defer Start() to workaround http://crbug.com/160920.
  base::CancelableOnceClosure deferred_start_cb_;

  // Contains time of last successful call to AudioUnitRender().
  // Initialized first time in Start() and then updated for each valid
  // audio buffer. Used to detect long error sequences and to take actions
  // if length of error sequence is above a certain limit.
  base::TimeTicks last_success_time_;

  // Flags to indicate if we have gotten an input callback.
  // |got_input_callback_| is only accessed on the OS audio thread in
  // OnDataIsAvailable() and is set to true when the first callback comes. It
  // acts as a gate to only set |input_callback_is_active_| atomically once.
  // |got_input_callback_| is reset to false in Stop() on the main thread. This
  // is safe since after stopping the audio unit there is no current callback
  // ongoing and no further callbacks coming.
  bool got_input_callback_ = false;
  base::subtle::Atomic32 input_callback_is_active_ = false;

  // Timer which triggers CheckInputStartupSuccess() to verify that input
  // callbacks have started as intended after a successful call to Start().
  // This timer lives on the main browser thread.
  std::unique_ptr<base::OneShotTimer> input_callback_timer_;

  // Set to true when we've successfully called SuppressNoiseReduction to
  // disable ambient noise reduction.
  bool noise_reduction_suppressed_ = false;

  // Controls whether or not we use the kAudioUnitSubType_VoiceProcessingIO
  // voice processing component that provides echo cancellation, ducking
  // and gain control on Sierra and later.
  bool use_voice_processing_ = false;

  // The of the output device to cancel echo from.
  AudioDeviceID output_device_id_for_aec_ = kAudioObjectUnknown;

  // Stores the timestamp of the previous audio buffer provided by the OS.
  // We use this in combination with |last_number_of_frames_| to detect when
  // the OS has decided to skip providing frames (i.e. a glitch).
  // This can happen in case of high CPU load or excessive blocking on the
  // callback audio thread.
  // These variables are only touched on the callback thread and then read
  // in the dtor (when no longer receiving callbacks).
  // NOTE: Float64 and UInt32 types are used for native API compatibility.
  Float64 last_sample_time_ = 0;
  UInt32 last_number_of_frames_ = 0;

  // Used to aggregate and report glitch metrics to UMA (periodically) and to
  // text logs (when a stream ends).
  SystemGlitchReporter glitch_reporter_;

  // Used to accumulate glitches to be passed to the AudioInputCallback.
  AudioGlitchInfo::Accumulator glitch_accumulator_;

  AmplitudePeakDetector peak_detector_;

  // Callback to send statistics info.
  AudioManager::LogCallback log_callback_;
};

}  // namespace media

#endif  // MEDIA_AUDIO_APPLE_AUDIO_LOW_LATENCY_INPUT_H_