diff options
-rw-r--r-- | remoting/host/audio_capturer_linux.cc (renamed from remoting/host/audio_capturer.cc) | 0 | ||||
-rw-r--r-- | remoting/host/audio_capturer_mac.cc | 15 | ||||
-rw-r--r-- | remoting/host/audio_capturer_win.cc | 266 | ||||
-rw-r--r-- | remoting/remoting.gyp | 4 |
4 files changed, 284 insertions, 1 deletions
diff --git a/remoting/host/audio_capturer.cc b/remoting/host/audio_capturer_linux.cc index 1901dfc..1901dfc 100644 --- a/remoting/host/audio_capturer.cc +++ b/remoting/host/audio_capturer_linux.cc diff --git a/remoting/host/audio_capturer_mac.cc b/remoting/host/audio_capturer_mac.cc new file mode 100644 index 0000000..1901dfc --- /dev/null +++ b/remoting/host/audio_capturer_mac.cc @@ -0,0 +1,15 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "remoting/host/audio_capturer.h" + +namespace remoting { + +scoped_ptr<AudioCapturer> AudioCapturer::Create() { + NOTIMPLEMENTED(); + return scoped_ptr<AudioCapturer>(NULL); +} + +} // namespace remoting diff --git a/remoting/host/audio_capturer_win.cc b/remoting/host/audio_capturer_win.cc new file mode 100644 index 0000000..02e3d64 --- /dev/null +++ b/remoting/host/audio_capturer_win.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <windows.h> +#include <audioclient.h> +#include <avrt.h> +#include <mmdeviceapi.h> +#include <mmreg.h> +#include <mmsystem.h> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/timer.h" +#include "base/win/scoped_co_mem.h" +#include "base/win/scoped_com_initializer.h" +#include "base/win/scoped_comptr.h" +#include "remoting/host/audio_capturer.h" +#include "remoting/proto/audio.pb.h" + +namespace { +const int kChannels = 2; +const int kBitsPerSample = 16; +const int kSamplesPerSecond = 44100; +const int kBitsPerByte = 8; +// Conversion factor from 100ns to 1ms. +const int kHnsToMs = 10000; +} // namespace + +namespace remoting { + +class AudioCapturerWin : public AudioCapturer { + public: + AudioCapturerWin(); + virtual ~AudioCapturerWin(); + + // AudioCapturer interface. + virtual void Start(const PacketCapturedCallback& callback) OVERRIDE; + virtual void Stop() OVERRIDE; + virtual bool IsRunning() OVERRIDE; + + private: + // Receives all packets from the audio capture endpoint buffer and pushes them + // to the network. + void DoCapture(); + + PacketCapturedCallback callback_; + + scoped_ptr<base::RepeatingTimer<AudioCapturerWin> > capture_timer_; + base::TimeDelta audio_device_period_; + + base::win::ScopedCoMem<WAVEFORMATEX> wave_format_ex_; + base::win::ScopedComPtr<IAudioCaptureClient> audio_capture_client_; + base::win::ScopedComPtr<IAudioClient> audio_client_; + base::win::ScopedComPtr<IMMDevice> mm_device_; + scoped_ptr<base::win::ScopedCOMInitializer> com_initializer_; + + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(AudioCapturerWin); +}; + +AudioCapturerWin::AudioCapturerWin() { + thread_checker_.DetachFromThread(); +} + +AudioCapturerWin::~AudioCapturerWin() { +} + +void AudioCapturerWin::Start(const PacketCapturedCallback& callback) { + DCHECK(!audio_capture_client_.get()); + DCHECK(!audio_client_.get()); + DCHECK(!mm_device_.get()); + DCHECK(static_cast<PWAVEFORMATEX>(wave_format_ex_) == NULL); + DCHECK(thread_checker_.CalledOnValidThread()); + + callback_ = callback; + + // Initialize the capture timer. + capture_timer_.reset(new base::RepeatingTimer<AudioCapturerWin>()); + + HRESULT hr = S_OK; + com_initializer_.reset(new base::win::ScopedCOMInitializer()); + + base::win::ScopedComPtr<IMMDeviceEnumerator> mm_device_enumerator; + hr = mm_device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator)); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to create IMMDeviceEnumerator. Error " << hr; + return; + } + + // Get the audio endpoint. + hr = mm_device_enumerator->GetDefaultAudioEndpoint(eRender, + eConsole, + mm_device_.Receive()); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get IMMDevice. Error " << hr; + return; + } + + // Get an audio client. + hr = mm_device_->Activate(__uuidof(IAudioClient), + CLSCTX_ALL, + NULL, + audio_client_.ReceiveVoid()); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get an IAudioClient. Error " << hr; + return; + } + + REFERENCE_TIME device_period; + hr = audio_client_->GetDevicePeriod(&device_period, NULL); + if (FAILED(hr)) { + LOG(ERROR) << "IAudioClient::GetDevicePeriod failed. Error " << hr; + return; + } + audio_device_period_ = base::TimeDelta::FromMilliseconds( + device_period / kChannels / kHnsToMs); + + // Get the wave format. + hr = audio_client_->GetMixFormat(&wave_format_ex_); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get WAVEFORMATEX. Error " << hr; + return; + } + + // Set the wave format + switch (wave_format_ex_->wFormatTag) { + case WAVE_FORMAT_IEEE_FLOAT: + // Intentional fall-through. + case WAVE_FORMAT_PCM: + wave_format_ex_->wFormatTag = WAVE_FORMAT_PCM; + wave_format_ex_->nChannels = kChannels; + wave_format_ex_->nSamplesPerSec = kSamplesPerSecond; + wave_format_ex_->wBitsPerSample = kBitsPerSample; + wave_format_ex_->nBlockAlign = kChannels * kBitsPerSample / kBitsPerByte; + wave_format_ex_->nAvgBytesPerSec = + kSamplesPerSecond * kChannels * kBitsPerSample / kBitsPerByte; + break; + case WAVE_FORMAT_EXTENSIBLE: { + PWAVEFORMATEXTENSIBLE wave_format_extensible = + reinterpret_cast<WAVEFORMATEXTENSIBLE*>( + static_cast<WAVEFORMATEX*>(wave_format_ex_)); + if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, + wave_format_extensible->SubFormat)) { + wave_format_extensible->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + wave_format_extensible->Samples.wValidBitsPerSample = kBitsPerSample; + + wave_format_extensible->Format.nChannels = kChannels; + wave_format_extensible->Format.nSamplesPerSec = kSamplesPerSecond; + wave_format_extensible->Format.wBitsPerSample = kBitsPerSample; + wave_format_extensible->Format.nBlockAlign = + kChannels * kBitsPerSample / kBitsPerByte; + wave_format_extensible->Format.nAvgBytesPerSec = + kSamplesPerSecond * kChannels * kBitsPerSample / kBitsPerByte; + } else { + LOG(ERROR) << "Failed to force 16-bit samples"; + return; + } + break; + } + default: + LOG(ERROR) << "Failed to force 16-bit samples"; + return; + } + + // Initialize the IAudioClient. + hr = audio_client_->Initialize(AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_LOOPBACK, + 0, + 0, + wave_format_ex_, + NULL); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to initialize IAudioClient. Error " << hr; + return; + } + + // Get an IAudioCaptureClient. + hr = audio_client_->GetService(__uuidof(IAudioCaptureClient), + audio_capture_client_.ReceiveVoid()); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to get an IAudioCaptureClient. Error " << hr; + return; + } + + // Start the IAudioClient. + hr = audio_client_->Start(); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to start IAudioClient. Error " << hr; + return; + } + + // Start capturing. + capture_timer_->Start(FROM_HERE, + audio_device_period_, + this, + &AudioCapturerWin::DoCapture); +} + +void AudioCapturerWin::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(IsRunning()); + + capture_timer_.reset(); + mm_device_.Release(); + audio_client_.Release(); + audio_capture_client_.Release(); + wave_format_ex_.Reset(NULL); + com_initializer_.reset(); + + thread_checker_.DetachFromThread(); +} + +bool AudioCapturerWin::IsRunning() { + DCHECK(thread_checker_.CalledOnValidThread()); + return capture_timer_.get() != NULL; +} + +void AudioCapturerWin::DoCapture() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(IsRunning()); + + // Fetch all packets from the audio capture endpoint buffer. + while (true) { + UINT32 next_packet_size; + HRESULT hr = audio_capture_client_->GetNextPacketSize(&next_packet_size); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to GetNextPacketSize. Error " << hr; + return; + } + + if (next_packet_size <= 0) { + return; + } + + BYTE* data; + UINT32 frames; + DWORD flags; + hr = audio_capture_client_->GetBuffer( + &data, &frames, &flags, NULL, NULL); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to GetBuffer. Error " << hr; + return; + } + + scoped_ptr<AudioPacket> packet = scoped_ptr<AudioPacket>(new AudioPacket()); + packet->set_data(data, frames * wave_format_ex_->nBlockAlign); + + callback_.Run(packet.Pass()); + + hr = audio_capture_client_->ReleaseBuffer(frames); + if (FAILED(hr)) { + LOG(ERROR) << "Failed to ReleaseBuffer. Error " << hr; + return; + } + } +} + +scoped_ptr<AudioCapturer> AudioCapturer::Create() { + return scoped_ptr<AudioCapturer>(new AudioCapturerWin()); +} + +} // namespace remoting diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 59e9b78..2ea6506 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -1106,8 +1106,10 @@ '../crypto/crypto.gyp:crypto', ], 'sources': [ - 'host/audio_capturer.cc', 'host/audio_capturer.h', + 'host/audio_capturer_linux.cc', + 'host/audio_capturer_mac.cc', + 'host/audio_capturer_win.cc', 'host/audio_scheduler.cc', 'host/audio_scheduler.h', 'host/capture_scheduler.cc', |