diff options
author | dgreid@chromium.org <dgreid@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-16 21:06:54 +0000 |
---|---|---|
committer | dgreid@chromium.org <dgreid@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-16 21:06:54 +0000 |
commit | e78fa0960b66aaba6182ac9bf4d2ae8b9a83b92b (patch) | |
tree | 7ebcd23eb5799f5e0ecfc2429034de983984aa54 /media | |
parent | 0fc80bd47b7ea8cf08085a317ae422a0941886cd (diff) | |
download | chromium_src-e78fa0960b66aaba6182ac9bf4d2ae8b9a83b92b.zip chromium_src-e78fa0960b66aaba6182ac9bf4d2ae8b9a83b92b.tar.gz chromium_src-e78fa0960b66aaba6182ac9bf4d2ae8b9a83b92b.tar.bz2 |
media/audio/linux: Add CrasInputStream.
This parallels the CrasOutputStream that was added previously. ChromeOS
already talks to CRAS for audio input, through an alsa plug in. Remove
that unneeded layer. Only built when use_cras is set.
A following patch will pull the CRAS client connection up a level and
share one client connection for all input and output streams that are
active.
BUG=chromium-os:25410
TEST=Newly added cras_input_unittest to media_unittests. Manual test
with apprtc and youtube.com/my_webcam.
Review URL: https://chromiumcodereview.appspot.com/10592014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@146881 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/audio/linux/audio_manager_linux.cc | 35 | ||||
-rw-r--r-- | media/audio/linux/cras_input.cc | 278 | ||||
-rw-r--r-- | media/audio/linux/cras_input.h | 105 | ||||
-rw-r--r-- | media/audio/linux/cras_input_unittest.cc | 212 | ||||
-rw-r--r-- | media/media.gyp | 5 |
5 files changed, 617 insertions, 18 deletions
diff --git a/media/audio/linux/audio_manager_linux.cc b/media/audio/linux/audio_manager_linux.cc index 0b34aaa..8a8cad2 100644 --- a/media/audio/linux/audio_manager_linux.cc +++ b/media/audio/linux/audio_manager_linux.cc @@ -18,6 +18,7 @@ #include "media/audio/pulse/pulse_output.h" #endif #if defined(USE_CRAS) +#include "media/audio/linux/cras_input.h" #include "media/audio/linux/cras_output.h" #endif #include "media/base/limits.h" @@ -267,36 +268,34 @@ AudioInputStream* AudioManagerLinux::MakeLowLatencyInputStream( AudioOutputStream* AudioManagerLinux::MakeOutputStream( const AudioParameters& params) { - AudioOutputStream* stream = NULL; #if defined(USE_CRAS) if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseCras)) { - stream = new CrasOutputStream(params, this); - } else { + return new CrasOutputStream(params, this); + } #endif + #if defined(USE_PULSEAUDIO) if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUsePulseAudio)) { - stream = new PulseAudioOutputStream(params, this); - } else { -#endif - std::string device_name = AlsaPcmOutputStream::kAutoSelectDevice; - if (CommandLine::ForCurrentProcess()->HasSwitch( - switches::kAlsaOutputDevice)) { - device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kAlsaOutputDevice); - } - stream = new AlsaPcmOutputStream(device_name, params, wrapper_.get(), this); -#if defined(USE_PULSEAUDIO) + return new PulseAudioOutputStream(params, this); } #endif -#if defined(USE_CRAS) + + std::string device_name = AlsaPcmOutputStream::kAutoSelectDevice; + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kAlsaOutputDevice)) { + device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kAlsaOutputDevice); } -#endif - DCHECK(stream); - return stream; + return new AlsaPcmOutputStream(device_name, params, wrapper_.get(), this); } AudioInputStream* AudioManagerLinux::MakeInputStream( const AudioParameters& params, const std::string& device_id) { +#if defined(USE_CRAS) + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUseCras)) { + return new CrasInputStream(params, this); + } +#endif std::string device_name = (device_id == AudioManagerBase::kDefaultDeviceId) ? AlsaPcmInputStream::kAutoSelectDevice : device_id; diff --git a/media/audio/linux/cras_input.cc b/media/audio/linux/cras_input.cc new file mode 100644 index 0000000..6de405e --- /dev/null +++ b/media/audio/linux/cras_input.cc @@ -0,0 +1,278 @@ +// 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 "media/audio/linux/cras_input.h" + +#include <math.h> + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/logging.h" +#include "base/time.h" +#include "media/audio/audio_manager.h" +#include "media/audio/linux/alsa_util.h" +#include "media/audio/linux/audio_manager_linux.h" + +namespace media { + +CrasInputStream::CrasInputStream(const AudioParameters& params, + AudioManagerLinux* manager) + : audio_manager_(manager), + bytes_per_frame_(0), + callback_(NULL), + client_(NULL), + params_(params), + started_(false), + stream_id_(0) { + DCHECK(audio_manager_); +} + +CrasInputStream::~CrasInputStream() { + DCHECK(!client_); +} + +bool CrasInputStream::Open() { + if (client_) { + NOTREACHED() << "CrasInputStream already open"; + return false; // Already open. + } + + // Sanity check input values. + if (params_.sample_rate() <= 0) { + DLOG(WARNING) << "Unsupported audio frequency."; + return false; + } + + if (AudioParameters::AUDIO_PCM_LINEAR != params_.format() && + AudioParameters::AUDIO_PCM_LOW_LATENCY != params_.format()) { + DLOG(WARNING) << "Unsupported audio format."; + return false; + } + + snd_pcm_format_t pcm_format = + alsa_util::BitsToFormat(params_.bits_per_sample()); + if (pcm_format == SND_PCM_FORMAT_UNKNOWN) { + DLOG(WARNING) << "Unsupported bits/sample: " << params_.bits_per_sample(); + return false; + } + + // Create the client and connect to the CRAS server. + if (cras_client_create(&client_) < 0) { + DLOG(WARNING) << "Couldn't create CRAS client.\n"; + client_ = NULL; + return false; + } + + if (cras_client_connect(client_)) { + DLOG(WARNING) << "Couldn't connect CRAS client.\n"; + cras_client_destroy(client_); + client_ = NULL; + return false; + } + + // Then start running the client. + if (cras_client_run_thread(client_)) { + DLOG(WARNING) << "Couldn't run CRAS client.\n"; + cras_client_destroy(client_); + client_ = NULL; + return false; + } + + return true; +} + +void CrasInputStream::Close() { + if (client_) { + cras_client_stop(client_); + cras_client_destroy(client_); + client_ = NULL; + } + + if (callback_) { + callback_->OnClose(this); + callback_ = NULL; + } + + // Signal to the manager that we're closed and can be removed. + // Should be last call in the method as it deletes "this". + audio_manager_->ReleaseInputStream(this); +} + +void CrasInputStream::Start(AudioInputCallback* callback) { + DCHECK(client_); + DCHECK(callback); + + // If already playing, stop before re-starting. + if (started_) + return; + + callback_ = callback; + + // Prepare |audio_format| and |stream_params| for the stream we + // will create. + cras_audio_format* audio_format = cras_audio_format_create( + alsa_util::BitsToFormat(params_.bits_per_sample()), + params_.sample_rate(), + params_.channels()); + if (!audio_format) { + DLOG(WARNING) << "Error setting up audio parameters."; + callback_->OnError(this, -ENOMEM); + callback_ = NULL; + return; + } + + unsigned int frames_per_packet = params_.frames_per_buffer(); + cras_stream_params* stream_params = cras_client_stream_params_create( + CRAS_STREAM_INPUT, + frames_per_packet, // Total latency. + frames_per_packet, // Call back when this many ready. + frames_per_packet, // Minimum Callback level ignored for capture streams. + CRAS_STREAM_TYPE_DEFAULT, + 0, // Unused flags. + this, + CrasInputStream::SamplesReady, + CrasInputStream::StreamError, + audio_format); + if (!stream_params) { + DLOG(WARNING) << "Error setting up stream parameters."; + callback_->OnError(this, -ENOMEM); + callback_ = NULL; + cras_audio_format_destroy(audio_format); + return; + } + + // Before starting the stream, save the number of bytes in a frame for use in + // the callback. + bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format); + + // Adding the stream will start the audio callbacks. + if (cras_client_add_stream(client_, &stream_id_, stream_params) == 0) { + audio_manager_->IncreaseActiveInputStreamCount(); + } else { + DLOG(WARNING) << "Failed to add the stream."; + callback_->OnError(this, -EIO); + callback_ = NULL; + } + + // Done with config params. + cras_audio_format_destroy(audio_format); + cras_client_stream_params_destroy(stream_params); + + started_ = true; +} + +void CrasInputStream::Stop() { + DCHECK(client_); + + if (!callback_ || !started_) + return; + + // Removing the stream from the client stops audio. + cras_client_rm_stream(client_, stream_id_); + + audio_manager_->DecreaseActiveInputStreamCount(); + + started_ = false; +} + +// Static callback asking for samples. Run on high priority thread. +int CrasInputStream::SamplesReady(cras_client* client, + cras_stream_id_t stream_id, + uint8* samples, + size_t frames, + const timespec* sample_ts, + void* arg) { + CrasInputStream* me = static_cast<CrasInputStream*>(arg); + me->ReadAudio(frames, samples, sample_ts); + return frames; +} + +// Static callback for stream errors. +int CrasInputStream::StreamError(cras_client* client, + cras_stream_id_t stream_id, + int err, + void* arg) { + CrasInputStream* me = static_cast<CrasInputStream*>(arg); + me->NotifyStreamError(err); + return 0; +} + +void CrasInputStream::ReadAudio(size_t frames, + uint8* buffer, + const timespec* sample_ts) { + DCHECK(callback_); + + timespec latency_ts = {0, 0}; + + // Determine latency and pass that on to the sink. sample_ts is the wall time + // indicating when the first sample in the buffer was captured. Convert that + // to latency in bytes. + cras_client_calc_capture_latency(sample_ts, &latency_ts); + double latency_usec = + latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond + + latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond; + double frames_latency = + latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond; + unsigned int bytes_latency = + static_cast<unsigned int>(frames_latency * bytes_per_frame_); + + // Update the AGC volume level once every second. Note that, |volume| is + // also updated each time SetVolume() is called through IPC by the + // render-side AGC. + double normalized_volume = 0.0; + QueryAgcVolume(&normalized_volume); + + callback_->OnData(this, + buffer, + frames * bytes_per_frame_, + bytes_latency, + normalized_volume); +} + +void CrasInputStream::NotifyStreamError(int err) { + if (callback_) + callback_->OnError(this, err); +} + +double CrasInputStream::GetMaxVolume() { + DCHECK(client_); + + // Capture gain is returned as dB * 100 (150 => 1.5dBFS). Convert the dB + // value to a ratio before returning. + double dB = cras_client_get_system_max_capture_gain(client_) / 100.0; + return GetVolumeRatioFromDecibels(dB); +} + +void CrasInputStream::SetVolume(double volume) { + DCHECK(client_); + + // Convert from the passed volume ratio, to dB * 100. + double dB = GetDecibelsFromVolumeRatio(volume); + cras_client_set_system_capture_gain(client_, static_cast<long>(dB * 100.0)); + + // Update the AGC volume level based on the last setting above. Note that, + // the volume-level resolution is not infinite and it is therefore not + // possible to assume that the volume provided as input parameter can be + // used directly. Instead, a new query to the audio hardware is required. + // This method does nothing if AGC is disabled. + UpdateAgcVolume(); +} + +double CrasInputStream::GetVolume() { + if (!client_) + return 0.0; + + long dB = cras_client_get_system_capture_gain(client_) / 100.0; + return GetVolumeRatioFromDecibels(dB); +} + +double CrasInputStream::GetVolumeRatioFromDecibels(double dB) const { + return pow(10, dB / 20.0); +} + +double CrasInputStream::GetDecibelsFromVolumeRatio(double volume_ratio) const { + return 20 * log10(volume_ratio); +} + +} // namespace media diff --git a/media/audio/linux/cras_input.h b/media/audio/linux/cras_input.h new file mode 100644 index 0000000..e1dce4c --- /dev/null +++ b/media/audio/linux/cras_input.h @@ -0,0 +1,105 @@ +// 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. + +#ifndef MEDIA_AUDIO_LINUX_CRAS_INPUT_H_ +#define MEDIA_AUDIO_LINUX_CRAS_INPUT_H_ + +#include <cras_client.h> + +#include <string> + +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/time.h" +#include "media/audio/audio_input_stream_impl.h" +#include "media/audio/audio_io.h" +#include "media/audio/audio_parameters.h" + +namespace media { + +class AudioManagerLinux; + +// Provides an input stream for audio capture based on CRAS, the ChromeOS Audio +// Server. This object is not thread safe and all methods should be invoked in +// the thread that created the object. +class CrasInputStream : public AudioInputStreamImpl { + public: + // The ctor takes all the usual parameters, plus |manager| which is the + // audio manager who is creating this object. + CrasInputStream(const AudioParameters& params, AudioManagerLinux* manager); + + // The dtor is typically called by the AudioManager only and it is usually + // triggered by calling AudioOutputStream::Close(). + virtual ~CrasInputStream(); + + // Implementation of AudioInputStream. + virtual bool Open() OVERRIDE; + virtual void Start(AudioInputCallback* callback) OVERRIDE; + virtual void Stop() OVERRIDE; + virtual void Close() OVERRIDE; + virtual double GetMaxVolume() OVERRIDE; + virtual void SetVolume(double volume) OVERRIDE; + virtual double GetVolume() OVERRIDE; + + private: + // Handles requests to get samples from the provided buffer. This will be + // called by the audio server when it has samples ready. + static int SamplesReady(cras_client* client, + cras_stream_id_t stream_id, + uint8* samples, + size_t frames, + const timespec* sample_ts, + void* arg); + + // Handles notificaiton that there was an error with the playback stream. + static int StreamError(cras_client* client, + cras_stream_id_t stream_id, + int err, + void* arg); + + // Reads one or more buffers of audio from the device, passes on to the + // registered callback. Called from SamplesReady(). + void ReadAudio(size_t frames, uint8* buffer, const timespec* sample_ts); + + // Deals with an error that occured in the stream. Called from StreamError(). + void NotifyStreamError(int err); + + // Convert from dB * 100 to a volume ratio. + double GetVolumeRatioFromDecibels(double dB) const; + + // Convert from a volume ratio to dB. + double GetDecibelsFromVolumeRatio(double volume_ratio) const; + + // Non-refcounted pointer back to the audio manager. + // The AudioManager indirectly holds on to stream objects, so we don't + // want circular references. Additionally, stream objects live on the audio + // thread, which is owned by the audio manager and we don't want to addref + // the manager from that thread. + AudioManagerLinux* audio_manager_; + + // Size of frame in bytes. + uint32 bytes_per_frame_; + + // Callback to pass audio samples too, valid while recording. + AudioInputCallback* callback_; + + // The client used to communicate with the audio server. + cras_client* client_; + + // PCM parameters for the stream. + AudioParameters params_; + + // True if the stream has been started. + bool started_; + + // ID of the playing stream. + cras_stream_id_t stream_id_; + + DISALLOW_COPY_AND_ASSIGN(CrasInputStream); +}; + +} // namespace media + +#endif // MEDIA_AUDIO_LINUX_ALSA_INPUT_H_ diff --git a/media/audio/linux/cras_input_unittest.cc b/media/audio/linux/cras_input_unittest.cc new file mode 100644 index 0000000..5d9a1c8 --- /dev/null +++ b/media/audio/linux/cras_input_unittest.cc @@ -0,0 +1,212 @@ +// 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 <unistd.h> + +#include <string> + +#include "base/synchronization/waitable_event.h" +#include "base/test/test_timeouts.h" +#include "base/time.h" +#include "media/audio/linux/audio_manager_linux.h" +#include "media/audio/linux/cras_input.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; +using testing::AtLeast; +using testing::Ge; +using testing::InvokeWithoutArgs; +using testing::StrictMock; + +namespace media { + +class MockAudioInputCallback : public AudioInputStream::AudioInputCallback { + public: + MOCK_METHOD5(OnData, void( + AudioInputStream*, const uint8*, uint32, uint32, double)); + MOCK_METHOD2(OnError, void(AudioInputStream*, int)); + MOCK_METHOD1(OnClose, void(AudioInputStream*)); +}; + +class MockAudioManagerLinuxInput : public AudioManagerLinux { + public: + // We need to override this function in order to skip checking the number + // of active output streams. It is because the number of active streams + // is managed inside MakeAudioInputStream, and we don't use + // MakeAudioInputStream to create the stream in the tests. + virtual void ReleaseInputStream(AudioInputStream* stream) OVERRIDE { + DCHECK(stream); + delete stream; + } +}; + +class CrasInputStreamTest : public testing::Test { + protected: + CrasInputStreamTest() { + mock_manager_.reset(new StrictMock<MockAudioManagerLinuxInput>()); + } + + virtual ~CrasInputStreamTest() { + } + + CrasInputStream* CreateStream(ChannelLayout layout) { + return CreateStream(layout, kTestFramesPerPacket); + } + + CrasInputStream* CreateStream(ChannelLayout layout, + int32 samples_per_packet) { + AudioParameters params(kTestFormat, + layout, + kTestSampleRate, + kTestBitsPerSample, + samples_per_packet); + return new CrasInputStream(params, mock_manager_.get()); + } + + void CaptureSomeFrames(const AudioParameters ¶ms, + unsigned int duration_ms) { + CrasInputStream* test_stream = new CrasInputStream(params, + mock_manager_.get()); + + ASSERT_TRUE(test_stream->Open()); + + // Allow 8 frames variance for SRC in the callback. Different numbers of + // samples can be provided when doing non-integer SRC. For example + // converting from 192k to 44.1k is a ratio of 4.35 to 1. + MockAudioInputCallback mock_callback; + unsigned int expected_size = (kTestFramesPerPacket - 8) * + params.channels() * + params.bits_per_sample() / 8; + + base::WaitableEvent event(false, false); + + EXPECT_CALL(mock_callback, + OnData(test_stream, _, Ge(expected_size), _, _)) + .WillOnce(InvokeWithoutArgs(&event, &base::WaitableEvent::Signal)); + + test_stream->Start(&mock_callback); + + // Wait for samples to be captured. + EXPECT_TRUE(event.TimedWait(TestTimeouts::action_timeout())); + + test_stream->Stop(); + + EXPECT_CALL(mock_callback, OnClose(test_stream)).Times(1); + test_stream->Close(); + } + + static const unsigned int kTestBitsPerSample; + static const unsigned int kTestCaptureDurationMs; + static const ChannelLayout kTestChannelLayout; + static const AudioParameters::Format kTestFormat; + static const uint32 kTestFramesPerPacket; + static const int kTestSampleRate; + + scoped_ptr<StrictMock<MockAudioManagerLinuxInput> > mock_manager_; + + private: + DISALLOW_COPY_AND_ASSIGN(CrasInputStreamTest); +}; + +const unsigned int CrasInputStreamTest::kTestBitsPerSample = 16; +const unsigned int CrasInputStreamTest::kTestCaptureDurationMs = 250; +const ChannelLayout CrasInputStreamTest::kTestChannelLayout = + CHANNEL_LAYOUT_STEREO; +const AudioParameters::Format CrasInputStreamTest::kTestFormat = + AudioParameters::AUDIO_PCM_LINEAR; +const uint32 CrasInputStreamTest::kTestFramesPerPacket = 1000; +const int CrasInputStreamTest::kTestSampleRate = 44100; + +TEST_F(CrasInputStreamTest, OpenMono) { + CrasInputStream* test_stream = CreateStream(CHANNEL_LAYOUT_MONO); + EXPECT_TRUE(test_stream->Open()); + test_stream->Close(); +} + +TEST_F(CrasInputStreamTest, OpenStereo) { + CrasInputStream* test_stream = CreateStream(CHANNEL_LAYOUT_STEREO); + EXPECT_TRUE(test_stream->Open()); + test_stream->Close(); +} + +TEST_F(CrasInputStreamTest, BadBitsPerSample) { + AudioParameters bad_bps_params(kTestFormat, + kTestChannelLayout, + kTestSampleRate, + kTestBitsPerSample - 1, + kTestFramesPerPacket); + CrasInputStream* test_stream = + new CrasInputStream(bad_bps_params, mock_manager_.get()); + EXPECT_FALSE(test_stream->Open()); + test_stream->Close(); +} + +TEST_F(CrasInputStreamTest, BadFormat) { + AudioParameters bad_format_params(AudioParameters::AUDIO_LAST_FORMAT, + kTestChannelLayout, + kTestSampleRate, + kTestBitsPerSample, + kTestFramesPerPacket); + CrasInputStream* test_stream = + new CrasInputStream(bad_format_params, mock_manager_.get()); + EXPECT_FALSE(test_stream->Open()); + test_stream->Close(); +} + +TEST_F(CrasInputStreamTest, BadSampleRate) { + AudioParameters bad_rate_params(kTestFormat, + kTestChannelLayout, + 0, + kTestBitsPerSample, + kTestFramesPerPacket); + CrasInputStream* test_stream = + new CrasInputStream(bad_rate_params, mock_manager_.get()); + EXPECT_FALSE(test_stream->Open()); + test_stream->Close(); +} + +TEST_F(CrasInputStreamTest, SetGetVolume) { + CrasInputStream* test_stream = CreateStream(CHANNEL_LAYOUT_MONO); + EXPECT_TRUE(test_stream->Open()); + + double max_volume = test_stream->GetMaxVolume(); + EXPECT_GE(max_volume, 1.0); + + test_stream->SetVolume(max_volume / 2); + + double new_volume = test_stream->GetVolume(); + + EXPECT_GE(new_volume, 0.0); + EXPECT_LE(new_volume, max_volume); + + test_stream->Close(); +} + +TEST_F(CrasInputStreamTest, CaptureFrames) { + const unsigned int rates[] = + {8000, 16000, 22050, 32000, 44100, 48000, 96000, 192000}; + + for (unsigned int i = 0; i < ARRAY_SIZE(rates); i++) { + SCOPED_TRACE(testing::Message() << "Mono " << rates[i] << "Hz"); + AudioParameters params_mono(kTestFormat, + CHANNEL_LAYOUT_MONO, + rates[i], + kTestBitsPerSample, + kTestFramesPerPacket); + CaptureSomeFrames(params_mono, kTestCaptureDurationMs); + } + + for (unsigned int i = 0; i < ARRAY_SIZE(rates); i++) { + SCOPED_TRACE(testing::Message() << "Stereo " << rates[i] << "Hz"); + AudioParameters params_stereo(kTestFormat, + CHANNEL_LAYOUT_STEREO, + rates[i], + kTestBitsPerSample, + kTestFramesPerPacket); + CaptureSomeFrames(params_stereo, kTestCaptureDurationMs); + } +} + +} // namespace media diff --git a/media/media.gyp b/media/media.gyp index d4995d8..8d8a1e1 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -86,6 +86,8 @@ 'audio/linux/alsa_util.h', 'audio/linux/alsa_wrapper.cc', 'audio/linux/alsa_wrapper.h', + 'audio/linux/cras_input.cc', + 'audio/linux/cras_input.h', 'audio/linux/cras_output.cc', 'audio/linux/cras_output.h', 'audio/openbsd/audio_manager_openbsd.cc', @@ -398,6 +400,8 @@ ], }, { # else: use_cras == 0 'sources!': [ + 'audio/linux/cras_input.cc', + 'audio/linux/cras_input.h', 'audio/linux/cras_output.cc', 'audio/linux/cras_output.h', ], @@ -758,6 +762,7 @@ 'conditions': [ ['use_cras == 1', { 'sources': [ + 'audio/linux/cras_input_unittest.cc', 'audio/linux/cras_output_unittest.cc', ], 'defines': [ |