Home | History | Annotate | Download | only in host
      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 "remoting/host/audio_capturer_win.h"
      6 
      7 #include <windows.h>
      8 #include <avrt.h>
      9 #include <mmreg.h>
     10 #include <mmsystem.h>
     11 
     12 #include <algorithm>
     13 #include <stdlib.h>
     14 
     15 #include "base/logging.h"
     16 
     17 namespace {
     18 const int kChannels = 2;
     19 const int kBytesPerSample = 2;
     20 const int kBitsPerSample = kBytesPerSample * 8;
     21 // Conversion factor from 100ns to 1ms.
     22 const int k100nsPerMillisecond = 10000;
     23 
     24 // Tolerance for catching packets of silence. If all samples have absolute
     25 // value less than this threshold, the packet will be counted as a packet of
     26 // silence. A value of 2 was chosen, because Windows can give samples of 1 and
     27 // -1, even when no audio is playing.
     28 const int kSilenceThreshold = 2;
     29 
     30 // Lower bound for timer intervals, in milliseconds.
     31 const int kMinTimerInterval = 30;
     32 
     33 // Upper bound for the timer precision error, in milliseconds.
     34 // Timers are supposed to be accurate to 20ms, so we use 30ms to be safe.
     35 const int kMaxExpectedTimerLag = 30;
     36 }  // namespace
     37 
     38 namespace remoting {
     39 
     40 AudioCapturerWin::AudioCapturerWin()
     41     : sampling_rate_(AudioPacket::SAMPLING_RATE_INVALID),
     42       silence_detector_(kSilenceThreshold),
     43       last_capture_error_(S_OK) {
     44     thread_checker_.DetachFromThread();
     45 }
     46 
     47 AudioCapturerWin::~AudioCapturerWin() {
     48 }
     49 
     50 bool AudioCapturerWin::Start(const PacketCapturedCallback& callback) {
     51   DCHECK(!audio_capture_client_.get());
     52   DCHECK(!audio_client_.get());
     53   DCHECK(!mm_device_.get());
     54   DCHECK(static_cast<PWAVEFORMATEX>(wave_format_ex_) == NULL);
     55   DCHECK(thread_checker_.CalledOnValidThread());
     56 
     57   callback_ = callback;
     58 
     59   // Initialize the capture timer.
     60   capture_timer_.reset(new base::RepeatingTimer<AudioCapturerWin>());
     61 
     62   HRESULT hr = S_OK;
     63 
     64   base::win::ScopedComPtr<IMMDeviceEnumerator> mm_device_enumerator;
     65   hr = mm_device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator));
     66   if (FAILED(hr)) {
     67     LOG(ERROR) << "Failed to create IMMDeviceEnumerator. Error " << hr;
     68     return false;
     69   }
     70 
     71   // Get the audio endpoint.
     72   hr = mm_device_enumerator->GetDefaultAudioEndpoint(eRender,
     73                                                      eConsole,
     74                                                      mm_device_.Receive());
     75   if (FAILED(hr)) {
     76     LOG(ERROR) << "Failed to get IMMDevice. Error " << hr;
     77     return false;
     78   }
     79 
     80   // Get an audio client.
     81   hr = mm_device_->Activate(__uuidof(IAudioClient),
     82                             CLSCTX_ALL,
     83                             NULL,
     84                             audio_client_.ReceiveVoid());
     85   if (FAILED(hr)) {
     86     LOG(ERROR) << "Failed to get an IAudioClient. Error " << hr;
     87     return false;
     88   }
     89 
     90   REFERENCE_TIME device_period;
     91   hr = audio_client_->GetDevicePeriod(&device_period, NULL);
     92   if (FAILED(hr)) {
     93     LOG(ERROR) << "IAudioClient::GetDevicePeriod failed. Error " << hr;
     94     return false;
     95   }
     96   // We round up, if |device_period| / |k100nsPerMillisecond|
     97   // is not a whole number.
     98   int device_period_in_milliseconds =
     99       1 + ((device_period - 1) / k100nsPerMillisecond);
    100   audio_device_period_ = base::TimeDelta::FromMilliseconds(
    101       std::max(device_period_in_milliseconds, kMinTimerInterval));
    102 
    103   // Get the wave format.
    104   hr = audio_client_->GetMixFormat(&wave_format_ex_);
    105   if (FAILED(hr)) {
    106     LOG(ERROR) << "Failed to get WAVEFORMATEX. Error " << hr;
    107     return false;
    108   }
    109 
    110   // Set the wave format
    111   switch (wave_format_ex_->wFormatTag) {
    112     case WAVE_FORMAT_IEEE_FLOAT:
    113       // Intentional fall-through.
    114     case WAVE_FORMAT_PCM:
    115       if (!AudioCapturer::IsValidSampleRate(wave_format_ex_->nSamplesPerSec)) {
    116         LOG(ERROR) << "Host sampling rate is neither 44.1 kHz nor 48 kHz.";
    117         return false;
    118       }
    119       sampling_rate_ = static_cast<AudioPacket::SamplingRate>(
    120           wave_format_ex_->nSamplesPerSec);
    121 
    122       wave_format_ex_->wFormatTag = WAVE_FORMAT_PCM;
    123       wave_format_ex_->nChannels = kChannels;
    124       wave_format_ex_->wBitsPerSample = kBitsPerSample;
    125       wave_format_ex_->nBlockAlign = kChannels * kBytesPerSample;
    126       wave_format_ex_->nAvgBytesPerSec =
    127           sampling_rate_ * kChannels * kBytesPerSample;
    128       break;
    129     case WAVE_FORMAT_EXTENSIBLE: {
    130       PWAVEFORMATEXTENSIBLE wave_format_extensible =
    131           reinterpret_cast<WAVEFORMATEXTENSIBLE*>(
    132           static_cast<WAVEFORMATEX*>(wave_format_ex_));
    133       if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
    134                       wave_format_extensible->SubFormat)) {
    135         if (!AudioCapturer::IsValidSampleRate(
    136                 wave_format_extensible->Format.nSamplesPerSec)) {
    137           LOG(ERROR) << "Host sampling rate is neither 44.1 kHz nor 48 kHz.";
    138           return false;
    139         }
    140         sampling_rate_ = static_cast<AudioPacket::SamplingRate>(
    141             wave_format_extensible->Format.nSamplesPerSec);
    142 
    143         wave_format_extensible->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
    144         wave_format_extensible->Samples.wValidBitsPerSample = kBitsPerSample;
    145 
    146         wave_format_extensible->Format.nChannels = kChannels;
    147         wave_format_extensible->Format.nSamplesPerSec = sampling_rate_;
    148         wave_format_extensible->Format.wBitsPerSample = kBitsPerSample;
    149         wave_format_extensible->Format.nBlockAlign =
    150             kChannels * kBytesPerSample;
    151         wave_format_extensible->Format.nAvgBytesPerSec =
    152             sampling_rate_ * kChannels * kBytesPerSample;
    153       } else {
    154         LOG(ERROR) << "Failed to force 16-bit samples";
    155         return false;
    156       }
    157       break;
    158     }
    159     default:
    160       LOG(ERROR) << "Failed to force 16-bit PCM";
    161       return false;
    162   }
    163 
    164   // Initialize the IAudioClient.
    165   hr = audio_client_->Initialize(
    166       AUDCLNT_SHAREMODE_SHARED,
    167       AUDCLNT_STREAMFLAGS_LOOPBACK,
    168       (kMaxExpectedTimerLag + audio_device_period_.InMilliseconds()) *
    169       k100nsPerMillisecond,
    170       0,
    171       wave_format_ex_,
    172       NULL);
    173   if (FAILED(hr)) {
    174     LOG(ERROR) << "Failed to initialize IAudioClient. Error " << hr;
    175     return false;
    176   }
    177 
    178   // Get an IAudioCaptureClient.
    179   hr = audio_client_->GetService(__uuidof(IAudioCaptureClient),
    180                                  audio_capture_client_.ReceiveVoid());
    181   if (FAILED(hr)) {
    182     LOG(ERROR) << "Failed to get an IAudioCaptureClient. Error " << hr;
    183     return false;
    184   }
    185 
    186   // Start the IAudioClient.
    187   hr = audio_client_->Start();
    188   if (FAILED(hr)) {
    189     LOG(ERROR) << "Failed to start IAudioClient. Error " << hr;
    190     return false;
    191   }
    192 
    193   silence_detector_.Reset(sampling_rate_, kChannels);
    194 
    195   // Start capturing.
    196   capture_timer_->Start(FROM_HERE,
    197                         audio_device_period_,
    198                         this,
    199                         &AudioCapturerWin::DoCapture);
    200   return true;
    201 }
    202 
    203 void AudioCapturerWin::Stop() {
    204   DCHECK(thread_checker_.CalledOnValidThread());
    205   DCHECK(IsStarted());
    206 
    207   capture_timer_.reset();
    208   mm_device_.Release();
    209   audio_client_.Release();
    210   audio_capture_client_.Release();
    211   wave_format_ex_.Reset(NULL);
    212 
    213   thread_checker_.DetachFromThread();
    214 }
    215 
    216 bool AudioCapturerWin::IsStarted() {
    217   DCHECK(thread_checker_.CalledOnValidThread());
    218   return capture_timer_.get() != NULL;
    219 }
    220 
    221 void AudioCapturerWin::DoCapture() {
    222   DCHECK(AudioCapturer::IsValidSampleRate(sampling_rate_));
    223   DCHECK(thread_checker_.CalledOnValidThread());
    224   DCHECK(IsStarted());
    225 
    226   // Fetch all packets from the audio capture endpoint buffer.
    227   HRESULT hr = S_OK;
    228   while (true) {
    229     UINT32 next_packet_size;
    230     HRESULT hr = audio_capture_client_->GetNextPacketSize(&next_packet_size);
    231     if (FAILED(hr))
    232       break;
    233 
    234     if (next_packet_size <= 0) {
    235       return;
    236     }
    237 
    238     BYTE* data;
    239     UINT32 frames;
    240     DWORD flags;
    241     hr = audio_capture_client_->GetBuffer(&data, &frames, &flags, NULL, NULL);
    242     if (FAILED(hr))
    243       break;
    244 
    245     if ((flags & AUDCLNT_BUFFERFLAGS_SILENT) == 0 &&
    246         !silence_detector_.IsSilence(
    247             reinterpret_cast<const int16*>(data), frames * kChannels)) {
    248       scoped_ptr<AudioPacket> packet =
    249           scoped_ptr<AudioPacket>(new AudioPacket());
    250       packet->add_data(data, frames * wave_format_ex_->nBlockAlign);
    251       packet->set_encoding(AudioPacket::ENCODING_RAW);
    252       packet->set_sampling_rate(sampling_rate_);
    253       packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2);
    254       packet->set_channels(AudioPacket::CHANNELS_STEREO);
    255 
    256       callback_.Run(packet.Pass());
    257     }
    258 
    259     hr = audio_capture_client_->ReleaseBuffer(frames);
    260     if (FAILED(hr))
    261       break;
    262   }
    263 
    264   // There is nothing to capture if the audio endpoint device has been unplugged
    265   // or disabled.
    266   if (hr == AUDCLNT_E_DEVICE_INVALIDATED)
    267     return;
    268 
    269   // Avoid reporting the same error multiple times.
    270   if (FAILED(hr) && hr != last_capture_error_) {
    271     last_capture_error_ = hr;
    272     LOG(ERROR) << "Failed to capture an audio packet: 0x"
    273                << std::hex << hr << std::dec << ".";
    274   }
    275 }
    276 
    277 bool AudioCapturer::IsSupported() {
    278   return true;
    279 }
    280 
    281 scoped_ptr<AudioCapturer> AudioCapturer::Create() {
    282   return scoped_ptr<AudioCapturer>(new AudioCapturerWin());
    283 }
    284 
    285 }  // namespace remoting
    286