diff options
author | satish@chromium.org <satish@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-02 21:48:54 +0000 |
---|---|---|
committer | satish@chromium.org <satish@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-02 21:48:54 +0000 |
commit | 8f168ea0f33a957a489227d30ba8c8b6e79b51f1 (patch) | |
tree | e5aed0b85ec73e970cf7c22d0dc15897ddf38baa /media/audio | |
parent | 87339f0bdb7fa60bad0616348a6a2aa705d4716a (diff) | |
download | chromium_src-8f168ea0f33a957a489227d30ba8c8b6e79b51f1.zip chromium_src-8f168ea0f33a957a489227d30ba8c8b6e79b51f1.tar.gz chromium_src-8f168ea0f33a957a489227d30ba8c8b6e79b51f1.tar.bz2 |
Implement audio recording for Linux via ALSA.
There are no new unit tests because a cross platform unit test added in CL 3357004 covers this code.
BUG=53598
TEST=media_unittests --gtest_filter=AudioInputTest.*
Review URL: http://codereview.chromium.org/3299005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@58409 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/audio')
-rw-r--r-- | media/audio/linux/alsa_input.cc | 214 | ||||
-rw-r--r-- | media/audio/linux/alsa_input.h | 68 | ||||
-rw-r--r-- | media/audio/linux/alsa_output.cc | 96 | ||||
-rw-r--r-- | media/audio/linux/alsa_output.h | 4 | ||||
-rw-r--r-- | media/audio/linux/alsa_output_unittest.cc | 4 | ||||
-rw-r--r-- | media/audio/linux/alsa_util.cc | 100 | ||||
-rw-r--r-- | media/audio/linux/alsa_util.h | 34 | ||||
-rw-r--r-- | media/audio/linux/alsa_wrapper.cc | 11 | ||||
-rw-r--r-- | media/audio/linux/alsa_wrapper.h | 4 | ||||
-rw-r--r-- | media/audio/linux/audio_manager_linux.cc | 40 |
10 files changed, 492 insertions, 83 deletions
diff --git a/media/audio/linux/alsa_input.cc b/media/audio/linux/alsa_input.cc new file mode 100644 index 0000000..d2be907 --- /dev/null +++ b/media/audio/linux/alsa_input.cc @@ -0,0 +1,214 @@ +// Copyright (c) 2010 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/alsa_input.h" + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/scoped_ptr.h" +#include "base/time.h" +#include "media/audio/linux/alsa_util.h" +#include "media/audio/linux/alsa_wrapper.h" + +namespace { + +const int kNumPacketsInRingBuffer = 3; + +// If a read failed with no audio data, try again after this duration. +const int kNoAudioReadAgainTimeoutMs = 20; + +const char kDefaultDevice1[] = "default"; +const char kDefaultDevice2[] = "plug:default"; + +} // namespace + +const char* AlsaPcmInputStream::kAutoSelectDevice = ""; + +AlsaPcmInputStream::AlsaPcmInputStream(const std::string& device_name, + const AudioParameters& params, + int samples_per_packet, + AlsaWrapper* wrapper) + : device_name_(device_name), + params_(params), + samples_per_packet_(samples_per_packet), + bytes_per_packet_(samples_per_packet_ * + (params.channels * params.bits_per_sample) / 8), + wrapper_(wrapper), + packet_duration_ms_( + (samples_per_packet_ * base::Time::kMillisecondsPerSecond) / + params.sample_rate), + callback_(NULL), + device_handle_(NULL), + ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { +} + +bool AlsaPcmInputStream::Open() { + if (device_handle_) + return false; // Already open. + + snd_pcm_format_t pcm_format = alsa_util::BitsToFormat( + params_.bits_per_sample); + if (pcm_format == SND_PCM_FORMAT_UNKNOWN) { + LOG(WARNING) << "Unsupported bits per sample: " + << params_.bits_per_sample; + return false; + } + + int latency_us = packet_duration_ms_ * kNumPacketsInRingBuffer * + base::Time::kMicrosecondsPerMillisecond; + if (device_name_ == kAutoSelectDevice) { + device_handle_ = alsa_util::OpenCaptureDevice(wrapper_, kDefaultDevice1, + params_.channels, + params_.sample_rate, + pcm_format, latency_us); + if (!device_handle_) { + device_handle_ = alsa_util::OpenCaptureDevice(wrapper_, kDefaultDevice2, + params_.channels, + params_.sample_rate, + pcm_format, latency_us); + } + } else { + device_handle_ = alsa_util::OpenCaptureDevice(wrapper_, + device_name_.c_str(), + params_.channels, + params_.sample_rate, + pcm_format, latency_us); + } + + if (device_handle_) + audio_packet_.reset(new uint8[bytes_per_packet_]); + + return device_handle_ != NULL; +} + +void AlsaPcmInputStream::Start(AudioInputCallback* callback) { + DCHECK(!callback_ && callback); + callback_ = callback; + int error = wrapper_->PcmPrepare(device_handle_); + if (error < 0) { + HandleError("PcmPrepare", error); + } else { + error = wrapper_->PcmStart(device_handle_); + if (error < 0) + HandleError("PcmStart", error); + } + + if (error < 0) { + callback_ = NULL; + } else { + // We start reading data a little later than when the packet might have got + // filled, to accommodate some delays in the audio driver. This could + // also give us a smooth read sequence going forward. + int64 delay_ms = packet_duration_ms_ + kNoAudioReadAgainTimeoutMs; + next_read_time_ = base::Time::Now() + base::TimeDelta::FromMilliseconds( + delay_ms); + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + task_factory_.NewRunnableMethod(&AlsaPcmInputStream::ReadAudio), + delay_ms); + } +} + +bool AlsaPcmInputStream::Recover(int original_error) { + int error = wrapper_->PcmRecover(device_handle_, original_error, 1); + if (error < 0) { + // Docs say snd_pcm_recover returns the original error if it is not one + // of the recoverable ones, so this log message will probably contain the + // same error twice. + LOG(WARNING) << "Unable to recover from \"" + << wrapper_->StrError(original_error) << "\": " + << wrapper_->StrError(error); + return false; + } + + if (original_error == -EPIPE) { // Buffer underrun/overrun. + // For capture streams we have to repeat the explicit start() to get + // data flowing again. + error = wrapper_->PcmStart(device_handle_); + if (error < 0) { + HandleError("PcmStart", error); + return false; + } + } + + return true; +} + +void AlsaPcmInputStream::ReadAudio() { + DCHECK(callback_); + + snd_pcm_sframes_t frames = wrapper_->PcmAvailUpdate(device_handle_); + if (frames < 0) { // Potentially recoverable error? + LOG(WARNING) << "PcmAvailUpdate(): " << wrapper_->StrError(frames); + Recover(frames); + } + + if (frames < samples_per_packet_) { + // Not enough data yet or error happened. In both cases wait for a very + // small duration before checking again. + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + task_factory_.NewRunnableMethod(&AlsaPcmInputStream::ReadAudio), + kNoAudioReadAgainTimeoutMs); + return; + } + + int num_packets = frames / samples_per_packet_; + while (num_packets--) { + int frames_read = wrapper_->PcmReadi(device_handle_, audio_packet_.get(), + samples_per_packet_); + if (frames_read == samples_per_packet_) { + callback_->OnData(this, audio_packet_.get(), bytes_per_packet_); + } else { + LOG(WARNING) << "PcmReadi returning less than expected frames: " + << frames_read << " vs. " << samples_per_packet_ + << ". Dropping this packet."; + } + } + + next_read_time_ += base::TimeDelta::FromMilliseconds(packet_duration_ms_); + int64 delay_ms = (next_read_time_ - base::Time::Now()).InMilliseconds(); + if (delay_ms < 0) { + LOG(WARNING) << "Audio read callback behind schedule by " + << (packet_duration_ms_ - delay_ms) << " (ms)."; + delay_ms = 0; + } + + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + task_factory_.NewRunnableMethod(&AlsaPcmInputStream::ReadAudio), + delay_ms); +} + +void AlsaPcmInputStream::Stop() { + if (!device_handle_ || !callback_) + return; + + task_factory_.RevokeAll(); // Cancel the next scheduled read. + int error = wrapper_->PcmDrop(device_handle_); + if (error < 0) + HandleError("PcmDrop", error); +} + +void AlsaPcmInputStream::Close() { + // Check in case we were already closed or not initialized yet. + if (!device_handle_ || !callback_) + return; + + task_factory_.RevokeAll(); // Cancel the next scheduled read. + int error = alsa_util::CloseDevice(wrapper_, device_handle_); + if (error < 0) + HandleError("PcmClose", error); + + audio_packet_.reset(); + device_handle_ = NULL; + callback_->OnClose(this); +} + +void AlsaPcmInputStream::HandleError(const char* method, int error) { + LOG(WARNING) << method << ": " << wrapper_->StrError(error); + callback_->OnError(this, error); +} + diff --git a/media/audio/linux/alsa_input.h b/media/audio/linux/alsa_input.h new file mode 100644 index 0000000..9a326e4 --- /dev/null +++ b/media/audio/linux/alsa_input.h @@ -0,0 +1,68 @@ +// Copyright (c) 2010 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_ALSA_INPUT_H_ +#define MEDIA_AUDIO_LINUX_ALSA_INPUT_H_ + +#include <alsa/asoundlib.h> + +#include <string> + +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "media/audio/audio_io.h" +#include "media/audio/audio_parameters.h" + +class AlsaWrapper; + +// Provides an input stream for audio capture based on the ALSA PCM interface. +// This object is not thread safe and all methods should be invoked in the +// thread that created the object. +class AlsaPcmInputStream : public AudioInputStream { + public: + // Pass this to the constructor if you want to attempt auto-selection + // of the audio recording device. + static const char* kAutoSelectDevice; + + // Create a PCM Output stream for the ALSA device identified by + // |device_name|. If unsure of what to use for |device_name|, use + // |kAutoSelectDevice|. + AlsaPcmInputStream(const std::string& device_name, + const AudioParameters& params, + int samples_per_packet, + AlsaWrapper* wrapper); + + // Implementation of AudioOutputStream. + virtual bool Open(); + virtual void Start(AudioInputCallback* callback); + virtual void Stop(); + virtual void Close(); + + private: + // Logs the error and invokes any registered callbacks. + void HandleError(const char* method, int error); + + // Reads one or more packets of audio from the device, passes on to the + // registered callback and schedules the next read. + void ReadAudio(); + + // Recovers from any device errors if possible. + bool Recover(int error); + + std::string device_name_; + AudioParameters params_; + int samples_per_packet_; + int bytes_per_packet_; + AlsaWrapper* wrapper_; + int packet_duration_ms_; // Length of each recorded packet in milliseconds. + AudioInputCallback* callback_; // Valid during a recording session. + base::Time next_read_time_; // Scheduled time for the next read callback. + snd_pcm_t* device_handle_; // Handle to the ALSA PCM recording device. + ScopedRunnableMethodFactory<AlsaPcmInputStream> task_factory_; + scoped_ptr<uint8> audio_packet_; // Data buffer used for reading audio data. + + DISALLOW_COPY_AND_ASSIGN(AlsaPcmInputStream); +}; + +#endif // MEDIA_AUDIO_LINUX_ALSA_INPUT_H_ diff --git a/media/audio/linux/alsa_output.cc b/media/audio/linux/alsa_output.cc index e3670d8..422f91ba 100644 --- a/media/audio/linux/alsa_output.cc +++ b/media/audio/linux/alsa_output.cc @@ -82,6 +82,7 @@ #include "base/stl_util-inl.h" #include "base/time.h" #include "media/audio/audio_util.h" +#include "media/audio/linux/alsa_util.h" #include "media/audio/linux/alsa_wrapper.h" #include "media/audio/linux/audio_manager_linux.h" #include "media/base/data_buffer.h" @@ -119,25 +120,6 @@ namespace { // TODO(fbarchard): Resample audio from higher frequency to 48000. const int kAlsaMaxSampleRate = 48000; -snd_pcm_format_t BitsToFormat(char bits_per_sample) { - switch (bits_per_sample) { - case 8: - return SND_PCM_FORMAT_U8; - - case 16: - return SND_PCM_FORMAT_S16; - - case 24: - return SND_PCM_FORMAT_S24; - - case 32: - return SND_PCM_FORMAT_S32; - - default: - return SND_PCM_FORMAT_UNKNOWN; - } -} - // While the "default" device may support multi-channel audio, in Alsa, only // the device names surround40, surround41, surround50, etc, have a defined // channel mapping according to Lennart: @@ -243,7 +225,7 @@ AlsaPcmOutputStream::AlsaPcmOutputStream(const std::string& device_name, MessageLoop* message_loop) : shared_data_(MessageLoop::current()), requested_device_name_(device_name), - pcm_format_(BitsToFormat(params.bits_per_sample)), + pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample)), channels_(params.channels), sample_rate_(params.sample_rate), bytes_per_sample_(params.bits_per_sample / 8), @@ -391,7 +373,11 @@ void AlsaPcmOutputStream::OpenTask(uint32 packet_size) { } } else { device_name_ = requested_device_name_; - playback_handle_ = OpenDevice(device_name_, channels_, latency_micros_); + playback_handle_ = alsa_util::OpenPlaybackDevice(wrapper_, + device_name_.c_str(), + channels_, sample_rate_, + pcm_format_, + latency_micros_); } // Finish initializing the stream if the device was opened successfully. @@ -454,7 +440,8 @@ void AlsaPcmOutputStream::CloseTask() { DCHECK_EQ(message_loop_, MessageLoop::current()); // Shutdown the audio device. - if (playback_handle_ && !CloseDevice(playback_handle_)) { + if (playback_handle_ && + alsa_util::CloseDevice(wrapper_, playback_handle_) < 0) { LOG(WARNING) << "Unable to close audio device. Leaking handle."; } playback_handle_ = NULL; @@ -727,53 +714,6 @@ std::string AlsaPcmOutputStream::FindDeviceForChannels(uint32 channels) { return guessed_device; } -snd_pcm_t* AlsaPcmOutputStream::OpenDevice(const std::string& device_name, - uint32 channels, - unsigned int latency) { - snd_pcm_t* handle = NULL; - int error = wrapper_->PcmOpen(&handle, device_name.c_str(), - SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); - if (error < 0) { - LOG(ERROR) << "Cannot open audio device (" << device_name << "): " - << wrapper_->StrError(error); - return NULL; - } - - // Configure the device for software resampling. - if ((error = wrapper_->PcmSetParams(handle, - pcm_format_, - SND_PCM_ACCESS_RW_INTERLEAVED, - channels, - sample_rate_, - 1, // soft_resample -- let ALSA resample - latency)) < 0) { - LOG(ERROR) << "Unable to set PCM parameters for (" << device_name - << "): " << wrapper_->StrError(error) - << " -- Format: " << pcm_format_ - << " Channels: " << channels - << " Latency (us): " << latency; - if (!CloseDevice(handle)) { - // TODO(ajwong): Retry on certain errors? - LOG(WARNING) << "Unable to close audio device. Leaking handle."; - } - return NULL; - } - - return handle; -} - -bool AlsaPcmOutputStream::CloseDevice(snd_pcm_t* handle) { - std::string name = wrapper_->PcmName(handle); - int error = wrapper_->PcmClose(handle); - if (error < 0) { - LOG(ERROR) << "Error closing audio device (" << name << "): " - << wrapper_->StrError(error); - return false; - } - - return true; -} - snd_pcm_sframes_t AlsaPcmOutputStream::GetAvailableFrames() { DCHECK_EQ(message_loop_, MessageLoop::current()); @@ -812,13 +752,19 @@ snd_pcm_t* AlsaPcmOutputStream::AutoSelectDevice(unsigned int latency) { // Step 1. if (!device_name_.empty()) { - if ((handle = OpenDevice(device_name_, channels_, latency)) != NULL) { + if ((handle = alsa_util::OpenPlaybackDevice(wrapper_, device_name_.c_str(), + channels_, sample_rate_, + pcm_format_, + latency)) != NULL) { return handle; } // Step 2. device_name_ = kPlugPrefix + device_name_; - if ((handle = OpenDevice(device_name_, channels_, latency)) != NULL) { + if ((handle = alsa_util::OpenPlaybackDevice(wrapper_, device_name_.c_str(), + channels_, sample_rate_, + pcm_format_, + latency)) != NULL) { return handle; } } @@ -837,13 +783,17 @@ snd_pcm_t* AlsaPcmOutputStream::AutoSelectDevice(unsigned int latency) { // Step 3. device_name_ = kDefaultDevice; - if ((handle = OpenDevice(device_name_, default_channels, latency)) != NULL) { + if ((handle = alsa_util::OpenPlaybackDevice(wrapper_, device_name_.c_str(), + default_channels, sample_rate_, + pcm_format_, latency)) != NULL) { return handle; } // Step 4. device_name_ = kPlugPrefix + device_name_; - if ((handle = OpenDevice(device_name_, default_channels, latency)) != NULL) { + if ((handle = alsa_util::OpenPlaybackDevice(wrapper_, device_name_.c_str(), + default_channels, sample_rate_, + pcm_format_, latency)) != NULL) { return handle; } diff --git a/media/audio/linux/alsa_output.h b/media/audio/linux/alsa_output.h index f6fd0d6..cbc50b3 100644 --- a/media/audio/linux/alsa_output.h +++ b/media/audio/linux/alsa_output.h @@ -142,10 +142,6 @@ class AlsaPcmOutputStream : static uint32 FramesToMicros(uint32 frames, uint32 sample_rate); static uint32 FramesToMillis(uint32 frames, uint32 sample_rate); std::string FindDeviceForChannels(uint32 channels); - snd_pcm_t* OpenDevice(const std::string& device_name, - uint32 channels, - uint32 latency); - bool CloseDevice(snd_pcm_t* handle); snd_pcm_sframes_t GetAvailableFrames(); // Attempts to find the best matching linux audio device for the given number diff --git a/media/audio/linux/alsa_output_unittest.cc b/media/audio/linux/alsa_output_unittest.cc index 23d86d32..35ba8a3 100644 --- a/media/audio/linux/alsa_output_unittest.cc +++ b/media/audio/linux/alsa_output_unittest.cc @@ -43,6 +43,9 @@ class MockAlsaWrapper : public AlsaWrapper { MOCK_METHOD3(PcmWritei, snd_pcm_sframes_t(snd_pcm_t* handle, const void* buffer, snd_pcm_uframes_t size)); + MOCK_METHOD3(PcmReadi, snd_pcm_sframes_t(snd_pcm_t* handle, + void* buffer, + snd_pcm_uframes_t size)); MOCK_METHOD3(PcmRecover, int(snd_pcm_t* handle, int err, int silent)); MOCK_METHOD7(PcmSetParams, int(snd_pcm_t* handle, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int channels, @@ -54,6 +57,7 @@ class MockAlsaWrapper : public AlsaWrapper { MOCK_METHOD1(PcmName, const char*(snd_pcm_t* handle)); MOCK_METHOD1(PcmAvailUpdate, snd_pcm_sframes_t(snd_pcm_t* handle)); MOCK_METHOD1(PcmState, snd_pcm_state_t(snd_pcm_t* handle)); + MOCK_METHOD1(PcmStart, int(snd_pcm_t* handle)); MOCK_METHOD1(StrError, const char*(int errnum)); }; diff --git a/media/audio/linux/alsa_util.cc b/media/audio/linux/alsa_util.cc new file mode 100644 index 0000000..27d0fc9 --- /dev/null +++ b/media/audio/linux/alsa_util.cc @@ -0,0 +1,100 @@ +// Copyright (c) 2010 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/alsa_util.h" + +#include <string> + +#include "base/logging.h" +#include "media/audio/linux/alsa_wrapper.h" + +namespace { + +snd_pcm_t* OpenDevice(AlsaWrapper* wrapper, + const char* device_name, + snd_pcm_stream_t type, + int channels, + int sample_rate, + snd_pcm_format_t pcm_format, + int latency_us) { + snd_pcm_t* handle = NULL; + int error = wrapper->PcmOpen(&handle, device_name, type, SND_PCM_NONBLOCK); + if (error < 0) { + LOG(WARNING) << "PcmOpen: " << device_name << "," + << wrapper->StrError(error); + return NULL; + } + + error = wrapper->PcmSetParams(handle, pcm_format, + SND_PCM_ACCESS_RW_INTERLEAVED, channels, + sample_rate, 1, latency_us); + if (error < 0) { + LOG(WARNING) << "PcmSetParams: " << device_name << ", " + << wrapper->StrError(error) << " - Format: " << pcm_format + << " Channels: " << channels << " Latency: " << latency_us; + if (!alsa_util::CloseDevice(wrapper, handle)) { + // TODO(ajwong): Retry on certain errors? + LOG(WARNING) << "Unable to close audio device. Leaking handle."; + } + return NULL; + } + + return handle; +} + +} // namespace + +namespace alsa_util { + +snd_pcm_format_t BitsToFormat(int bits_per_sample) { + switch (bits_per_sample) { + case 8: + return SND_PCM_FORMAT_U8; + + case 16: + return SND_PCM_FORMAT_S16; + + case 24: + return SND_PCM_FORMAT_S24; + + case 32: + return SND_PCM_FORMAT_S32; + + default: + return SND_PCM_FORMAT_UNKNOWN; + } +} + +int CloseDevice(AlsaWrapper* wrapper, snd_pcm_t* handle) { + std::string device_name = wrapper->PcmName(handle); + int error = wrapper->PcmClose(handle); + if (error < 0) { + LOG(ERROR) << "PcmClose: " << device_name << ", " + << wrapper->StrError(error); + } + + return error; +} + +snd_pcm_t* OpenCaptureDevice(AlsaWrapper* wrapper, + const char* device_name, + int channels, + int sample_rate, + snd_pcm_format_t pcm_format, + int latency_us) { + return OpenDevice(wrapper, device_name, SND_PCM_STREAM_CAPTURE, channels, + sample_rate, pcm_format, latency_us); +} + +snd_pcm_t* OpenPlaybackDevice(AlsaWrapper* wrapper, + const char* device_name, + int channels, + int sample_rate, + snd_pcm_format_t pcm_format, + int latency_us) { + return OpenDevice(wrapper, device_name, SND_PCM_STREAM_PLAYBACK, channels, + sample_rate, pcm_format, latency_us); +} + +} // namespace alsa_util diff --git a/media/audio/linux/alsa_util.h b/media/audio/linux/alsa_util.h new file mode 100644 index 0000000..5c9a0a0 --- /dev/null +++ b/media/audio/linux/alsa_util.h @@ -0,0 +1,34 @@ +// Copyright (c) 2010 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_ALSA_UTIL_H_ +#define MEDIA_AUDIO_LINUX_ALSA_UTIL_H_ + +#include <alsa/asoundlib.h> + +class AlsaWrapper; + +namespace alsa_util { + +snd_pcm_format_t BitsToFormat(int bits_per_sample); + +snd_pcm_t* OpenCaptureDevice(AlsaWrapper* wrapper, + const char* device_name, + int channels, + int sample_rate, + snd_pcm_format_t pcm_format, + int latency_us); + +snd_pcm_t* OpenPlaybackDevice(AlsaWrapper* wrapper, + const char* device_name, + int channels, + int sample_rate, + snd_pcm_format_t pcm_format, + int latency_us); + +int CloseDevice(AlsaWrapper* wrapper, snd_pcm_t* handle); + +} + +#endif // MEDIA_AUDIO_LINUX_ALSA_UTIL_H_ diff --git a/media/audio/linux/alsa_wrapper.cc b/media/audio/linux/alsa_wrapper.cc index a70856a..8270430 100644 --- a/media/audio/linux/alsa_wrapper.cc +++ b/media/audio/linux/alsa_wrapper.cc @@ -51,6 +51,12 @@ snd_pcm_sframes_t AlsaWrapper::PcmWritei(snd_pcm_t* handle, return snd_pcm_writei(handle, buffer, size); } +snd_pcm_sframes_t AlsaWrapper::PcmReadi(snd_pcm_t* handle, + void* buffer, + snd_pcm_uframes_t size) { + return snd_pcm_readi(handle, buffer, size); +} + int AlsaWrapper::PcmRecover(snd_pcm_t* handle, int err, int silent) { return snd_pcm_recover(handle, err, silent); } @@ -83,3 +89,8 @@ snd_pcm_state_t AlsaWrapper::PcmState(snd_pcm_t* handle) { const char* AlsaWrapper::StrError(int errnum) { return snd_strerror(errnum); } + +int AlsaWrapper::PcmStart(snd_pcm_t* handle) { + return snd_pcm_start(handle); +} + diff --git a/media/audio/linux/alsa_wrapper.h b/media/audio/linux/alsa_wrapper.h index 4469687..88bdb5a 100644 --- a/media/audio/linux/alsa_wrapper.h +++ b/media/audio/linux/alsa_wrapper.h @@ -28,6 +28,9 @@ class AlsaWrapper { virtual snd_pcm_sframes_t PcmWritei(snd_pcm_t* handle, const void* buffer, snd_pcm_uframes_t size); + virtual snd_pcm_sframes_t PcmReadi(snd_pcm_t* handle, + void* buffer, + snd_pcm_uframes_t size); virtual int PcmRecover(snd_pcm_t* handle, int err, int silent); virtual int PcmSetParams(snd_pcm_t* handle, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int channels, @@ -38,6 +41,7 @@ class AlsaWrapper { virtual const char* PcmName(snd_pcm_t* handle); virtual snd_pcm_sframes_t PcmAvailUpdate(snd_pcm_t* handle); virtual snd_pcm_state_t PcmState(snd_pcm_t* handle); + virtual int PcmStart(snd_pcm_t* handle); virtual const char* StrError(int errnum); diff --git a/media/audio/linux/audio_manager_linux.cc b/media/audio/linux/audio_manager_linux.cc index 40a8783..b22ec44 100644 --- a/media/audio/linux/audio_manager_linux.cc +++ b/media/audio/linux/audio_manager_linux.cc @@ -8,10 +8,19 @@ #include "base/logging.h" #include "media/audio/fake_audio_input_stream.h" #include "media/audio/fake_audio_output_stream.h" +#include "media/audio/linux/alsa_input.h" #include "media/audio/linux/alsa_output.h" #include "media/audio/linux/alsa_wrapper.h" +#include "media/base/limits.h" #include "media/base/media_switches.h" +namespace { + +const int kMaxInputChannels = 2; +const int kMaxSamplesPerPacket = media::Limits::kMaxSampleRate; + +} // namespace + // Implementation of AudioManager. bool AudioManagerLinux::HasAudioOutputDevices() { // TODO(ajwong): Make this actually query audio devices. @@ -19,8 +28,8 @@ bool AudioManagerLinux::HasAudioOutputDevices() { } bool AudioManagerLinux::HasAudioInputDevices() { - // TODO(satish): implement. - return false; + // TODO(satish): Make this actually query audio devices. + return true; } AudioOutputStream* AudioManagerLinux::MakeAudioOutputStream( @@ -36,9 +45,10 @@ AudioOutputStream* AudioManagerLinux::MakeAudioOutputStream( } std::string device_name = AlsaPcmOutputStream::kAutoSelectDevice; - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAlsaDevice)) { + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kAlsaOutputDevice)) { device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kAlsaDevice); + switches::kAlsaOutputDevice); } AlsaPcmOutputStream* stream = new AlsaPcmOutputStream(device_name, params, wrapper_.get(), this, @@ -51,11 +61,29 @@ AudioOutputStream* AudioManagerLinux::MakeAudioOutputStream( AudioInputStream* AudioManagerLinux::MakeAudioInputStream( AudioParameters params, int samples_per_packet) { + if (!params.IsValid() || params.channels > kMaxInputChannels || + samples_per_packet < 0 || samples_per_packet > kMaxSamplesPerPacket) + return NULL; + if (params.format == AudioParameters::AUDIO_MOCK) { return FakeAudioInputStream::MakeFakeStream(params, samples_per_packet); + } else if (params.format != AudioParameters::AUDIO_PCM_LINEAR) { + return NULL; + } + + if (!initialized()) + return NULL; + + std::string device_name = AlsaPcmOutputStream::kAutoSelectDevice; + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAlsaInputDevice)) { + device_name = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kAlsaInputDevice); } - // TODO(satish): implement. - return NULL; + + AlsaPcmInputStream* stream = new AlsaPcmInputStream( + device_name, params, samples_per_packet, wrapper_.get()); + + return stream; } AudioManagerLinux::AudioManagerLinux() { |