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

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

Issue 11959018: Add a unified audio I/O backend for ChromeOS. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 7 years, 11 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_unified.cc
diff --git a/media/audio/linux/cras_unified.cc b/media/audio/linux/cras_unified.cc
new file mode 100644
index 0000000000000000000000000000000000000000..63111dc8ef50b7484e3156965e17987aaaa33fd5
--- /dev/null
+++ b/media/audio/linux/cras_unified.cc
@@ -0,0 +1,239 @@
+// 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_unified.h"
+
+#include <cras_client.h>
+
+#include "base/logging.h"
+#include "media/audio/audio_util.h"
+#include "media/audio/linux/alsa_util.h"
+#include "media/audio/linux/audio_manager_linux.h"
+
+namespace media {
+
+// TODO(dgreid): support more than hard-coded stereo input.
+// Ideally we would like to receive this value as a constructor argument.
+// crbug.com/147326
+static const int kDefaultInputChannels = 2;
+
+// Overview of operation:
+// 1) An object of CrasUnifiedStream is created by the AudioManager
+// factory: audio_man->MakeAudioStream().
+// 2) Next some thread will call Open(), at that point a client is created and
+// configured for the correct format and sample rate.
+// 3) Then Start(source) is called and a stream is added to the CRAS client
+// which will create its own thread that periodically calls the source for more
+// data as buffers are being consumed.
+// 4) When finished Stop() is called, which is handled by stopping the stream.
+// 5) Finally Close() is called. It cleans up and notifies the audio manager,
+// which likely will destroy this object.
+
+CrasUnifiedStream::CrasUnifiedStream(const AudioParameters& params,
+ AudioManagerLinux* manager)
+ : client_(NULL),
+ stream_id_(0),
+ samples_per_packet_(params.frames_per_buffer()),
+ bytes_per_frame_(0),
+ frame_rate_(params.sample_rate()),
+ num_channels_(params.channels()),
+ pcm_format_(alsa_util::BitsToFormat(params.bits_per_sample())),
+ is_playing_(false),
+ volume_(1.0),
+ manager_(manager),
+ source_callback_(NULL),
+ input_bus_(AudioBus::Create(kDefaultInputChannels,
+ params.frames_per_buffer())),
+ output_bus_(AudioBus::Create(params)) {
+ // We must have a manager.
+ DCHECK(manager_);
+}
+
+CrasUnifiedStream::~CrasUnifiedStream() {
+ DCHECK(!is_playing_);
+}
+
+bool CrasUnifiedStream::Open() {
+ // Sanity check input values.
+ if (frame_rate_ <= 0) {
+ LOG(WARNING) << "Unsupported audio frequency.";
+ return false;
+ }
+
+ if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) {
+ LOG(WARNING) << "Unsupported pcm format";
+ return false;
+ }
+
+ // Create the client and connect to the CRAS server.
+ int err = cras_client_create(&client_);
+ if (err < 0) {
+ LOG(WARNING) << "Couldn't create CRAS client.\n";
+ client_ = NULL;
+ return false;
+ }
+
+ err = cras_client_connect(client_);
+ if (err) {
+ LOG(WARNING) << "Couldn't connect CRAS client.\n";
+ cras_client_destroy(client_);
+ client_ = NULL;
+ return false;
+ }
+
+ // Then start running the client.
+ err = cras_client_run_thread(client_);
+ if (err) {
+ LOG(WARNING) << "Couldn't run CRAS client.\n";
+ cras_client_destroy(client_);
+ client_ = NULL;
+ return false;
+ }
+
+ return true;
+}
+
+void CrasUnifiedStream::Close() {
+ if (client_) {
+ cras_client_stop(client_);
+ cras_client_destroy(client_);
+ client_ = NULL;
+ }
+
+ // Signal to the manager that we're closed and can be removed.
+ // Should be last call in the method as it deletes "this".
+ manager_->ReleaseOutputStream(this);
+}
+
+void CrasUnifiedStream::Start(AudioSourceCallback* callback) {
+ CHECK(callback);
+ source_callback_ = callback;
+
+ // Only start if we can enter the playing state.
+ if (is_playing_)
+ return;
+
+ // Prepare |audio_format| and |stream_params| for the stream we
+ // will create.
+ cras_audio_format* audio_format = cras_audio_format_create(
+ pcm_format_,
+ frame_rate_,
+ num_channels_);
+ if (audio_format == NULL) {
+ LOG(WARNING) << "Error setting up audio parameters.";
+ callback->OnError(this, -ENOMEM);
+ return;
+ }
+
+ cras_stream_params* stream_params = cras_client_unified_params_create(
+ CRAS_STREAM_UNIFIED,
+ samples_per_packet_,
+ CRAS_STREAM_TYPE_DEFAULT,
+ 0,
+ this,
+ CrasUnifiedStream::UnifiedCallback,
+ CrasUnifiedStream::StreamError,
+ audio_format);
+ if (stream_params == NULL) {
+ LOG(WARNING) << "Error setting up stream parameters.";
+ callback->OnError(this, -ENOMEM);
+ 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 requesting data.
+ int err = cras_client_add_stream(client_, &stream_id_, stream_params);
+ if (err < 0) {
+ LOG(WARNING) << "Failed to add the stream";
+ callback->OnError(this, err);
+ cras_audio_format_destroy(audio_format);
+ cras_client_stream_params_destroy(stream_params);
+ return;
+ }
+
+ // Set initial volume.
+ cras_client_set_stream_volume(client_, stream_id_, volume_);
+
+ // Done with config params.
+ cras_audio_format_destroy(audio_format);
+ cras_client_stream_params_destroy(stream_params);
+
+ is_playing_ = true;
+}
+
+void CrasUnifiedStream::Stop() {
+ if (!client_)
+ return;
+
+ // Removing the stream from the client stops audio.
+ cras_client_rm_stream(client_, stream_id_);
+
+ is_playing_ = false;
+}
+
+void CrasUnifiedStream::SetVolume(double volume) {
+ if (!client_)
+ return;
+ volume_ = static_cast<float>(volume);
+ cras_client_set_stream_volume(client_, stream_id_, volume_);
+}
+
+void CrasUnifiedStream::GetVolume(double* volume) {
+ *volume = volume_;
+}
+
+// Static callback asking for samples.
+int CrasUnifiedStream::UnifiedCallback(cras_client* client,
+ cras_stream_id_t stream_id,
+ uint8* input_samples,
+ uint8* output_samples,
+ unsigned int frames,
+ const timespec* input_ts,
+ const timespec* output_ts,
+ void* arg) {
+ CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
+ return me->Render(frames, input_samples, output_samples, input_ts, output_ts);
+}
+
+// Static callback for stream errors.
+int CrasUnifiedStream::StreamError(cras_client* client,
+ cras_stream_id_t stream_id,
+ int err,
+ void* arg) {
+ CrasUnifiedStream* me = static_cast<CrasUnifiedStream*>(arg);
+ me->NotifyStreamError(err);
+ return 0;
+}
+
+// Note this is run from a real time thread, so don't waste cycles here.
+uint32 CrasUnifiedStream::Render(size_t frames,
+ uint8* input_samples,
+ uint8* output_samples,
+ const timespec* input_ts,
+ const timespec* output_ts) {
+ unsigned int bytes_per_sample = bytes_per_frame_ / num_channels_;
+
+ DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames()));
+
+ input_bus_->FromInterleaved(input_samples, frames, bytes_per_sample);
+
+ int frames_filled = source_callback_->OnMoreIOData(
+ input_bus_.get(), output_bus_.get(), AudioBuffersState(0, 0));
+
+ output_bus_->ToInterleaved(frames_filled, bytes_per_sample, output_samples);
+
+ return frames_filled;
+}
+
+void CrasUnifiedStream::NotifyStreamError(int err) {
+ // This will remove the stream from the client.
+ if (source_callback_)
+ source_callback_->OnError(this, err);
+}
+
+} // namespace media

Powered by Google App Engine
This is Rietveld 408576698