Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(219)

Unified Diff: media/audio/linux/cras_input.cc

Issue 10592014: media/audio/linux: Add CrasInputStream. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Now better after input from xians. Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: media/audio/linux/cras_input.cc
diff --git a/media/audio/linux/cras_input.cc b/media/audio/linux/cras_input.cc
new file mode 100644
index 0000000000000000000000000000000000000000..cd150a385a1bb4e2728dd5e1e2a7befa27d1f7b2
--- /dev/null
+++ b/media/audio/linux/cras_input.cc
@@ -0,0 +1,253 @@
+// 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 "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),
+ sample_rate_(params.sample_rate()),
+ num_channels_(params.channels()),
scherkus (not reviewing) 2012/06/21 21:39:46 if you save params_ do you need to save sample_rat
dgreid 2012/06/22 05:36:32 Nice, I don't really need these two or pcm_format_
+ params_(params),
+ pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample())),
+ 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;
+ }
+
+ 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);
+
+ // 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_ && callback);
scherkus (not reviewing) 2012/06/21 21:39:46 split these DCHECKs into two so if they fail we kn
dgreid 2012/06/22 05:36:32 Done.
+ callback_ = callback;
+
+ // Prepare |audio_format| and |stream_params| for the stream we
+ // will create.
+ cras_audio_format* audio_format = cras_audio_format_create(
+ pcm_format_,
+ sample_rate_,
+ num_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);
+}
+
+void CrasInputStream::Stop() {
+ if (!client_ || !callback_)
scherkus (not reviewing) 2012/06/21 21:39:46 is this a DCHECK/programmer error?
dgreid 2012/06/22 05:36:32 Half and half, changed client_ to DCHECK. I left
+ return;
+
+ // Removing the stream from the client stops audio.
+ cras_client_rm_stream(client_, stream_id_);
+
+ audio_manager_->DecreaseActiveInputStreamCount();
+}
+
+// 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};
scherkus (not reviewing) 2012/06/21 21:39:46 nit: remove extra space before =
dgreid 2012/06/22 05:36:32 Done.
+
+ // 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 * 1e6) + latency_ts.tv_nsec * 1e-3;
scherkus (not reviewing) 2012/06/21 21:39:46 magic numbers! you can use base/time.h for kMicro
dgreid 2012/06/22 05:36:32 Done.
+ double frames_latency = latency_usec * sample_rate_ * 1e-6;
+ 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_);
+
+ // Return the max volume as a double to match what the alsa input does.
+ return static_cast<double>(cras_client_get_system_max_capture_gain(client_));
scherkus (not reviewing) 2012/06/21 21:39:46 ditto here -- see my concerns about casting below
dgreid 2012/06/22 05:36:32 it's weird, see below.
+}
+
+void CrasInputStream::SetVolume(double volume) {
+ DCHECK(client_);
+
+ cras_client_set_system_capture_gain(client_, static_cast<long>(volume));
+
+ // 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_)
scherkus (not reviewing) 2012/06/21 21:39:46 SetVolume() when client_ == NULL is DCHECK() but g
dgreid 2012/06/22 05:36:32 Not that I can find. The only path to get here th
+ return 0.0;
+
+ long current_volume = cras_client_get_system_capture_gain(client_);
+
+ // Only want the gain, limit reported level to zero.
+ if (current_volume < 0)
+ return 0.0;
+
+ return static_cast<double>(current_volume);
scherkus (not reviewing) 2012/06/21 21:39:46 what's the range of value for current_volume? doe
dgreid 2012/06/22 05:36:32 I thought this was really strange too, but went wi
+}
+
+} // namespace media

Powered by Google App Engine
This is Rietveld 408576698