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

Unified Diff: media/audio/cras/cras_output.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: Rebase and remove redundant code. Created 7 years, 10 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/cras/cras_output.cc
diff --git a/media/audio/cras/cras_output.cc b/media/audio/cras/cras_output.cc
deleted file mode 100644
index 1995d96eb2cbbddb966f3ac7df576c4c9f15a0ca..0000000000000000000000000000000000000000
--- a/media/audio/cras/cras_output.cc
+++ /dev/null
@@ -1,348 +0,0 @@
-// 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.
-//
-// The object has one error state: |state_| == kInError. When |state_| ==
-// kInError, all public API functions will fail with an error (Start() will call
-// the OnError() function on the callback immediately), or no-op themselves with
-// the exception of Close(). Even if an error state has been entered, if Open()
-// has previously returned successfully, Close() must be called.
-
-#include "media/audio/cras/cras_output.h"
-
-#include <cras_client.h>
-
-#include "base/logging.h"
-#include "media/audio/audio_util.h"
-#include "media/audio/cras/audio_manager_cras.h"
-#include "media/audio/linux/alsa_util.h"
-
-namespace media {
-
-// Helps make log messages readable.
-std::ostream& operator<<(std::ostream& os,
- CrasOutputStream::InternalState state) {
- switch (state) {
- case CrasOutputStream::kInError:
- os << "kInError";
- break;
- case CrasOutputStream::kCreated:
- os << "kCreated";
- break;
- case CrasOutputStream::kIsOpened:
- os << "kIsOpened";
- break;
- case CrasOutputStream::kIsPlaying:
- os << "kIsPlaying";
- break;
- case CrasOutputStream::kIsStopped:
- os << "kIsStopped";
- break;
- case CrasOutputStream::kIsClosed:
- os << "kIsClosed";
- break;
- default:
- os << "UnknownState";
- break;
- };
- return os;
-}
-
-// Overview of operation:
-// 1) An object of CrasOutputStream 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.
-
-CrasOutputStream::CrasOutputStream(const AudioParameters& params,
- AudioManagerCras* 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())),
- state_(kCreated),
- volume_(1.0),
- manager_(manager),
- source_callback_(NULL),
- audio_bus_(AudioBus::Create(params)) {
- // We must have a manager.
- DCHECK(manager_);
-
- // Sanity check input values.
- if (params.sample_rate() <= 0) {
- LOG(WARNING) << "Unsupported audio frequency.";
- TransitionTo(kInError);
- return;
- }
-
- if (AudioParameters::AUDIO_PCM_LINEAR != params.format() &&
- AudioParameters::AUDIO_PCM_LOW_LATENCY != params.format()) {
- LOG(WARNING) << "Unsupported audio format.";
- TransitionTo(kInError);
- return;
- }
-
- if (pcm_format_ == SND_PCM_FORMAT_UNKNOWN) {
- LOG(WARNING) << "Unsupported bits per sample: " << params.bits_per_sample();
- TransitionTo(kInError);
- return;
- }
-}
-
-CrasOutputStream::~CrasOutputStream() {
- InternalState current_state = state();
- DCHECK(current_state == kCreated ||
- current_state == kIsClosed ||
- current_state == kInError);
-}
-
-bool CrasOutputStream::Open() {
- if (!CanTransitionTo(kIsOpened)) {
- NOTREACHED() << "Invalid state: " << state();
- return false;
- }
-
- // We do not need to check if the transition was successful because
- // CanTransitionTo() was checked above, and it is assumed that this
- // object's public API is only called on one thread so the state cannot
- // transition out from under us.
- TransitionTo(kIsOpened);
-
- // 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;
- TransitionTo(kInError);
- return false;
- }
- err = cras_client_connect(client_);
- if (err) {
- LOG(WARNING) << "Couldn't connect CRAS client.\n";
- cras_client_destroy(client_);
- client_ = NULL;
- TransitionTo(kInError);
- 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;
- TransitionTo(kInError);
- return false;
- }
-
- return true;
-}
-
-void CrasOutputStream::Close() {
- // Sanity Check that we can transition to closed.
- if (TransitionTo(kIsClosed) != kIsClosed) {
- NOTREACHED() << "Unable to transition Closed.";
- return;
- }
-
- 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 CrasOutputStream::Start(AudioSourceCallback* callback) {
- CHECK(callback);
- source_callback_ = callback;
-
- // Only start if we can enter the playing state.
- if (TransitionTo(kIsPlaying) != kIsPlaying)
- 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.";
- TransitionTo(kInError);
- callback->OnError(this, -ENOMEM);
- return;
- }
- cras_stream_params* stream_params = cras_client_stream_params_create(
- CRAS_STREAM_OUTPUT,
- samples_per_packet_ * 2, // Total latency.
- samples_per_packet_ / 2, // Call back when this many left.
- samples_per_packet_, // Call back with at least this much space.
- CRAS_STREAM_TYPE_DEFAULT,
- 0,
- this,
- CrasOutputStream::PutSamples,
- CrasOutputStream::StreamError,
- audio_format);
- if (stream_params == NULL) {
- LOG(WARNING) << "Error setting up stream parameters.";
- TransitionTo(kInError);
- 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";
- TransitionTo(kInError);
- 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);
-}
-
-void CrasOutputStream::Stop() {
- if (!client_)
- return;
- // Removing the stream from the client stops audio.
- cras_client_rm_stream(client_, stream_id_);
- TransitionTo(kIsStopped);
-}
-
-void CrasOutputStream::SetVolume(double volume) {
- if (!client_)
- return;
- volume_ = static_cast<float>(volume);
- cras_client_set_stream_volume(client_, stream_id_, volume_);
-}
-
-void CrasOutputStream::GetVolume(double* volume) {
- *volume = volume_;
-}
-
-// Static callback asking for samples.
-int CrasOutputStream::PutSamples(cras_client* client,
- cras_stream_id_t stream_id,
- uint8* samples,
- size_t frames,
- const timespec* sample_ts,
- void* arg) {
- CrasOutputStream* me = static_cast<CrasOutputStream*>(arg);
- return me->Render(frames, samples, sample_ts);
-}
-
-// Static callback for stream errors.
-int CrasOutputStream::StreamError(cras_client* client,
- cras_stream_id_t stream_id,
- int err,
- void* arg) {
- CrasOutputStream* me = static_cast<CrasOutputStream*>(arg);
- me->NotifyStreamError(err);
- return 0;
-}
-
-// Note this is run from a real time thread, so don't waste cycles here.
-uint32 CrasOutputStream::Render(size_t frames,
- uint8* buffer,
- const timespec* sample_ts) {
- timespec latency_ts = {0, 0};
-
- // Determine latency and pass that on to the source.
- cras_client_calc_playback_latency(sample_ts, &latency_ts);
-
- // Treat negative latency (if we are too slow to render) as 0.
- uint32 latency_usec;
- if (latency_ts.tv_sec < 0 || latency_ts.tv_nsec < 0) {
- latency_usec = 0;
- } else {
- latency_usec = (latency_ts.tv_sec * 1000000) +
- latency_ts.tv_nsec / 1000;
- }
-
- uint32 frames_latency = latency_usec * frame_rate_ / 1000000;
- uint32 bytes_latency = frames_latency * bytes_per_frame_;
- DCHECK_EQ(frames, static_cast<size_t>(audio_bus_->frames()));
- int frames_filled = source_callback_->OnMoreData(
- audio_bus_.get(), AudioBuffersState(0, bytes_latency));
- // Note: If this ever changes to output raw float the data must be clipped and
- // sanitized since it may come from an untrusted source such as NaCl.
- audio_bus_->ToInterleaved(
- frames_filled, bytes_per_frame_ / num_channels_, buffer);
- return frames_filled;
-}
-
-void CrasOutputStream::NotifyStreamError(int err) {
- // This will remove the stream from the client.
- if (state_ == kIsClosed || state_ == kInError)
- return; // Don't care about error if we aren't using it.
- TransitionTo(kInError);
- if (source_callback_)
- source_callback_->OnError(this, err);
-}
-
-bool CrasOutputStream::CanTransitionTo(InternalState to) {
- switch (state_) {
- case kCreated:
- return to == kIsOpened || to == kIsClosed || to == kInError;
-
- case kIsOpened:
- return to == kIsPlaying || to == kIsStopped ||
- to == kIsClosed || to == kInError;
-
- case kIsPlaying:
- return to == kIsPlaying || to == kIsStopped ||
- to == kIsClosed || to == kInError;
-
- case kIsStopped:
- return to == kIsPlaying || to == kIsStopped ||
- to == kIsClosed || to == kInError;
-
- case kInError:
- return to == kIsClosed || to == kInError;
-
- case kIsClosed:
- return false;
- }
- return false;
-}
-
-CrasOutputStream::InternalState
-CrasOutputStream::TransitionTo(InternalState to) {
- if (!CanTransitionTo(to)) {
- state_ = kInError;
- } else {
- state_ = to;
- }
- return state_;
-}
-
-CrasOutputStream::InternalState CrasOutputStream::state() {
- return state_;
-}
-
-} // namespace media

Powered by Google App Engine
This is Rietveld 408576698