summaryrefslogtreecommitdiffstats
path: root/media/audio
diff options
context:
space:
mode:
authorsatish@chromium.org <satish@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-02 21:48:54 +0000
committersatish@chromium.org <satish@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-02 21:48:54 +0000
commit8f168ea0f33a957a489227d30ba8c8b6e79b51f1 (patch)
treee5aed0b85ec73e970cf7c22d0dc15897ddf38baa /media/audio
parent87339f0bdb7fa60bad0616348a6a2aa705d4716a (diff)
downloadchromium_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.cc214
-rw-r--r--media/audio/linux/alsa_input.h68
-rw-r--r--media/audio/linux/alsa_output.cc96
-rw-r--r--media/audio/linux/alsa_output.h4
-rw-r--r--media/audio/linux/alsa_output_unittest.cc4
-rw-r--r--media/audio/linux/alsa_util.cc100
-rw-r--r--media/audio/linux/alsa_util.h34
-rw-r--r--media/audio/linux/alsa_wrapper.cc11
-rw-r--r--media/audio/linux/alsa_wrapper.h4
-rw-r--r--media/audio/linux/audio_manager_linux.cc40
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() {