Home | History | Annotate | Download | only in audio
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "media/audio/audio_output_resampler.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/compiler_specific.h"
     10 #include "base/metrics/histogram.h"
     11 #include "base/single_thread_task_runner.h"
     12 #include "base/time/time.h"
     13 #include "build/build_config.h"
     14 #include "media/audio/audio_io.h"
     15 #include "media/audio/audio_output_dispatcher_impl.h"
     16 #include "media/audio/audio_output_proxy.h"
     17 #include "media/audio/sample_rates.h"
     18 #include "media/base/audio_converter.h"
     19 #include "media/base/limits.h"
     20 
     21 namespace media {
     22 
     23 class OnMoreDataConverter
     24     : public AudioOutputStream::AudioSourceCallback,
     25       public AudioConverter::InputCallback {
     26  public:
     27   OnMoreDataConverter(const AudioParameters& input_params,
     28                       const AudioParameters& output_params);
     29   virtual ~OnMoreDataConverter();
     30 
     31   // AudioSourceCallback interface.
     32   virtual int OnMoreData(AudioBus* dest,
     33                          AudioBuffersState buffers_state) OVERRIDE;
     34   virtual void OnError(AudioOutputStream* stream) OVERRIDE;
     35 
     36   // Sets |source_callback_|.  If this is not a new object, then Stop() must be
     37   // called before Start().
     38   void Start(AudioOutputStream::AudioSourceCallback* callback);
     39 
     40   // Clears |source_callback_| and flushes the resampler.
     41   void Stop();
     42 
     43   bool started() { return source_callback_ != nullptr; }
     44 
     45  private:
     46   // AudioConverter::InputCallback implementation.
     47   virtual double ProvideInput(AudioBus* audio_bus,
     48                               base::TimeDelta buffer_delay) OVERRIDE;
     49 
     50   // Ratio of input bytes to output bytes used to correct playback delay with
     51   // regard to buffering and resampling.
     52   const double io_ratio_;
     53 
     54   // Source callback.
     55   AudioOutputStream::AudioSourceCallback* source_callback_;
     56 
     57   // Last AudioBuffersState object received via OnMoreData(), used to correct
     58   // playback delay by ProvideInput() and passed on to |source_callback_|.
     59   AudioBuffersState current_buffers_state_;
     60 
     61   const int input_bytes_per_second_;
     62 
     63   // Handles resampling, buffering, and channel mixing between input and output
     64   // parameters.
     65   AudioConverter audio_converter_;
     66 
     67   DISALLOW_COPY_AND_ASSIGN(OnMoreDataConverter);
     68 };
     69 
     70 // Record UMA statistics for hardware output configuration.
     71 static void RecordStats(const AudioParameters& output_params) {
     72   // Note the 'PRESUBMIT_IGNORE_UMA_MAX's below, these silence the PRESUBMIT.py
     73   // check for uma enum max usage, since we're abusing UMA_HISTOGRAM_ENUMERATION
     74   // to report a discrete value.
     75   UMA_HISTOGRAM_ENUMERATION(
     76       "Media.HardwareAudioBitsPerChannel",
     77       output_params.bits_per_sample(),
     78       limits::kMaxBitsPerSample);  // PRESUBMIT_IGNORE_UMA_MAX
     79   UMA_HISTOGRAM_ENUMERATION(
     80       "Media.HardwareAudioChannelLayout", output_params.channel_layout(),
     81       CHANNEL_LAYOUT_MAX + 1);
     82   UMA_HISTOGRAM_ENUMERATION(
     83       "Media.HardwareAudioChannelCount", output_params.channels(),
     84       limits::kMaxChannels);  // PRESUBMIT_IGNORE_UMA_MAX
     85 
     86   AudioSampleRate asr;
     87   if (ToAudioSampleRate(output_params.sample_rate(), &asr)) {
     88     UMA_HISTOGRAM_ENUMERATION(
     89         "Media.HardwareAudioSamplesPerSecond", asr, kAudioSampleRateMax + 1);
     90   } else {
     91     UMA_HISTOGRAM_COUNTS(
     92         "Media.HardwareAudioSamplesPerSecondUnexpected",
     93         output_params.sample_rate());
     94   }
     95 }
     96 
     97 // Record UMA statistics for hardware output configuration after fallback.
     98 static void RecordFallbackStats(const AudioParameters& output_params) {
     99   UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true);
    100   // Note the 'PRESUBMIT_IGNORE_UMA_MAX's below, these silence the PRESUBMIT.py
    101   // check for uma enum max usage, since we're abusing UMA_HISTOGRAM_ENUMERATION
    102   // to report a discrete value.
    103   UMA_HISTOGRAM_ENUMERATION(
    104       "Media.FallbackHardwareAudioBitsPerChannel",
    105       output_params.bits_per_sample(),
    106       limits::kMaxBitsPerSample);  // PRESUBMIT_IGNORE_UMA_MAX
    107   UMA_HISTOGRAM_ENUMERATION(
    108       "Media.FallbackHardwareAudioChannelLayout",
    109       output_params.channel_layout(), CHANNEL_LAYOUT_MAX + 1);
    110   UMA_HISTOGRAM_ENUMERATION(
    111       "Media.FallbackHardwareAudioChannelCount", output_params.channels(),
    112       limits::kMaxChannels);  // PRESUBMIT_IGNORE_UMA_MAX
    113 
    114   AudioSampleRate asr;
    115   if (ToAudioSampleRate(output_params.sample_rate(), &asr)) {
    116     UMA_HISTOGRAM_ENUMERATION(
    117         "Media.FallbackHardwareAudioSamplesPerSecond",
    118         asr, kAudioSampleRateMax + 1);
    119   } else {
    120     UMA_HISTOGRAM_COUNTS(
    121         "Media.FallbackHardwareAudioSamplesPerSecondUnexpected",
    122         output_params.sample_rate());
    123   }
    124 }
    125 
    126 // Converts low latency based |output_params| into high latency appropriate
    127 // output parameters in error situations.
    128 void AudioOutputResampler::SetupFallbackParams() {
    129 // Only Windows has a high latency output driver that is not the same as the low
    130 // latency path.
    131 #if defined(OS_WIN)
    132   // Choose AudioParameters appropriate for opening the device in high latency
    133   // mode.  |kMinLowLatencyFrameSize| is arbitrarily based on Pepper Flash's
    134   // MAXIMUM frame size for low latency.
    135   static const int kMinLowLatencyFrameSize = 2048;
    136   const int frames_per_buffer =
    137       std::max(params_.frames_per_buffer(), kMinLowLatencyFrameSize);
    138 
    139   output_params_ = AudioParameters(
    140       AudioParameters::AUDIO_PCM_LINEAR, params_.channel_layout(),
    141       params_.sample_rate(), params_.bits_per_sample(),
    142       frames_per_buffer);
    143   device_id_ = "";
    144   Initialize();
    145 #endif
    146 }
    147 
    148 AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager,
    149                                            const AudioParameters& input_params,
    150                                            const AudioParameters& output_params,
    151                                            const std::string& output_device_id,
    152                                            const base::TimeDelta& close_delay)
    153     : AudioOutputDispatcher(audio_manager, input_params, output_device_id),
    154       close_delay_(close_delay),
    155       output_params_(output_params),
    156       streams_opened_(false) {
    157   DCHECK(input_params.IsValid());
    158   DCHECK(output_params.IsValid());
    159   DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
    160 
    161   // Record UMA statistics for the hardware configuration.
    162   RecordStats(output_params);
    163 
    164   Initialize();
    165 }
    166 
    167 AudioOutputResampler::~AudioOutputResampler() {
    168   DCHECK(callbacks_.empty());
    169 }
    170 
    171 void AudioOutputResampler::Initialize() {
    172   DCHECK(!streams_opened_);
    173   DCHECK(callbacks_.empty());
    174   dispatcher_ = new AudioOutputDispatcherImpl(
    175       audio_manager_, output_params_, device_id_, close_delay_);
    176 }
    177 
    178 bool AudioOutputResampler::OpenStream() {
    179   DCHECK(task_runner_->BelongsToCurrentThread());
    180 
    181   if (dispatcher_->OpenStream()) {
    182     // Only record the UMA statistic if we didn't fallback during construction
    183     // and only for the first stream we open.
    184     if (!streams_opened_ &&
    185         output_params_.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
    186       UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false);
    187     }
    188     streams_opened_ = true;
    189     return true;
    190   }
    191 
    192   // If we've already tried to open the stream in high latency mode or we've
    193   // successfully opened a stream previously, there's nothing more to be done.
    194   if (output_params_.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY ||
    195       streams_opened_ || !callbacks_.empty()) {
    196     return false;
    197   }
    198 
    199   DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
    200 
    201   // Record UMA statistics about the hardware which triggered the failure so
    202   // we can debug and triage later.
    203   RecordFallbackStats(output_params_);
    204 
    205   // Only Windows has a high latency output driver that is not the same as the
    206   // low latency path.
    207 #if defined(OS_WIN)
    208   DLOG(ERROR) << "Unable to open audio device in low latency mode.  Falling "
    209               << "back to high latency audio output.";
    210 
    211   SetupFallbackParams();
    212   if (dispatcher_->OpenStream()) {
    213     streams_opened_ = true;
    214     return true;
    215   }
    216 #endif
    217 
    218   DLOG(ERROR) << "Unable to open audio device in high latency mode.  Falling "
    219               << "back to fake audio output.";
    220 
    221   // Finally fall back to a fake audio output device.
    222   output_params_.Reset(
    223       AudioParameters::AUDIO_FAKE, params_.channel_layout(),
    224       params_.channels(), params_.sample_rate(),
    225       params_.bits_per_sample(), params_.frames_per_buffer());
    226   Initialize();
    227   if (dispatcher_->OpenStream()) {
    228     streams_opened_ = true;
    229     return true;
    230   }
    231 
    232   return false;
    233 }
    234 
    235 bool AudioOutputResampler::StartStream(
    236     AudioOutputStream::AudioSourceCallback* callback,
    237     AudioOutputProxy* stream_proxy) {
    238   DCHECK(task_runner_->BelongsToCurrentThread());
    239 
    240   OnMoreDataConverter* resampler_callback = nullptr;
    241   CallbackMap::iterator it = callbacks_.find(stream_proxy);
    242   if (it == callbacks_.end()) {
    243     resampler_callback = new OnMoreDataConverter(params_, output_params_);
    244     callbacks_[stream_proxy] = resampler_callback;
    245   } else {
    246     resampler_callback = it->second;
    247   }
    248 
    249   resampler_callback->Start(callback);
    250   bool result = dispatcher_->StartStream(resampler_callback, stream_proxy);
    251   if (!result)
    252     resampler_callback->Stop();
    253   return result;
    254 }
    255 
    256 void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy,
    257                                            double volume) {
    258   DCHECK(task_runner_->BelongsToCurrentThread());
    259   dispatcher_->StreamVolumeSet(stream_proxy, volume);
    260 }
    261 
    262 void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
    263   DCHECK(task_runner_->BelongsToCurrentThread());
    264   dispatcher_->StopStream(stream_proxy);
    265 
    266   // Now that StopStream() has completed the underlying physical stream should
    267   // be stopped and no longer calling OnMoreData(), making it safe to Stop() the
    268   // OnMoreDataConverter.
    269   CallbackMap::iterator it = callbacks_.find(stream_proxy);
    270   if (it != callbacks_.end())
    271     it->second->Stop();
    272 }
    273 
    274 void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) {
    275   DCHECK(task_runner_->BelongsToCurrentThread());
    276   dispatcher_->CloseStream(stream_proxy);
    277 
    278   // We assume that StopStream() is always called prior to CloseStream(), so
    279   // that it is safe to delete the OnMoreDataConverter here.
    280   CallbackMap::iterator it = callbacks_.find(stream_proxy);
    281   if (it != callbacks_.end()) {
    282     delete it->second;
    283     callbacks_.erase(it);
    284   }
    285 }
    286 
    287 void AudioOutputResampler::Shutdown() {
    288   DCHECK(task_runner_->BelongsToCurrentThread());
    289 
    290   // No AudioOutputProxy objects should hold a reference to us when we get
    291   // to this stage.
    292   DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
    293 
    294   dispatcher_->Shutdown();
    295   DCHECK(callbacks_.empty());
    296 }
    297 
    298 OnMoreDataConverter::OnMoreDataConverter(const AudioParameters& input_params,
    299                                          const AudioParameters& output_params)
    300     : io_ratio_(static_cast<double>(input_params.GetBytesPerSecond()) /
    301                 output_params.GetBytesPerSecond()),
    302       source_callback_(nullptr),
    303       input_bytes_per_second_(input_params.GetBytesPerSecond()),
    304       audio_converter_(input_params, output_params, false) {}
    305 
    306 OnMoreDataConverter::~OnMoreDataConverter() {
    307   // Ensure Stop() has been called so we don't end up with an AudioOutputStream
    308   // calling back into OnMoreData() after destruction.
    309   CHECK(!source_callback_);
    310 }
    311 
    312 void OnMoreDataConverter::Start(
    313     AudioOutputStream::AudioSourceCallback* callback) {
    314   CHECK(!source_callback_);
    315   source_callback_ = callback;
    316 
    317   // While AudioConverter can handle multiple inputs, we're using it only with
    318   // a single input currently.  Eventually this may be the basis for a browser
    319   // side mixer.
    320   audio_converter_.AddInput(this);
    321 }
    322 
    323 void OnMoreDataConverter::Stop() {
    324   CHECK(source_callback_);
    325   source_callback_ = nullptr;
    326   audio_converter_.RemoveInput(this);
    327 }
    328 
    329 int OnMoreDataConverter::OnMoreData(AudioBus* dest,
    330                                     AudioBuffersState buffers_state) {
    331   current_buffers_state_ = buffers_state;
    332   audio_converter_.Convert(dest);
    333 
    334   // Always return the full number of frames requested, ProvideInput()
    335   // will pad with silence if it wasn't able to acquire enough data.
    336   return dest->frames();
    337 }
    338 
    339 double OnMoreDataConverter::ProvideInput(AudioBus* dest,
    340                                          base::TimeDelta buffer_delay) {
    341   // Adjust playback delay to include |buffer_delay|.
    342   // TODO(dalecurtis): Stop passing bytes around, it doesn't make sense since
    343   // AudioBus is just float data.  Use TimeDelta instead.
    344   AudioBuffersState new_buffers_state;
    345   new_buffers_state.pending_bytes =
    346       io_ratio_ * (current_buffers_state_.total_bytes() +
    347                    buffer_delay.InSecondsF() * input_bytes_per_second_);
    348 
    349   // Retrieve data from the original callback.
    350   const int frames = source_callback_->OnMoreData(dest, new_buffers_state);
    351 
    352   // Zero any unfilled frames if anything was filled, otherwise we'll just
    353   // return a volume of zero and let AudioConverter drop the output.
    354   if (frames > 0 && frames < dest->frames())
    355     dest->ZeroFramesPartial(frames, dest->frames() - frames);
    356   return frames > 0 ? 1 : 0;
    357 }
    358 
    359 void OnMoreDataConverter::OnError(AudioOutputStream* stream) {
    360   source_callback_->OnError(stream);
    361 }
    362 
    363 }  // namespace media
    364