| 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
|
|
|