This source file includes following definitions.
- stream_direction_
- Open
- Close
- Start
- Stop
- SetVolume
- GetVolume
- GetBytesLatency
- UnifiedCallback
- StreamError
- DispatchCallback
- ReadWriteAudio
- WriteAudio
- NotifyStreamError
#include "media/audio/cras/cras_unified.h"
#include "base/logging.h"
#include "media/audio/cras/audio_manager_cras.h"
namespace media {
CrasUnifiedStream::CrasUnifiedStream(const AudioParameters& params,
AudioManagerCras* manager)
: client_(NULL),
stream_id_(0),
params_(params),
bytes_per_frame_(0),
is_playing_(false),
volume_(1.0),
manager_(manager),
source_callback_(NULL),
stream_direction_(CRAS_STREAM_OUTPUT) {
DCHECK(manager_);
DCHECK(params_.channels() > 0);
int input_channels = params_.input_channels();
if (input_channels) {
DCHECK(params_.channels() == input_channels);
stream_direction_ = CRAS_STREAM_UNIFIED;
input_bus_ = AudioBus::Create(input_channels,
params_.frames_per_buffer());
}
output_bus_ = AudioBus::Create(params);
}
CrasUnifiedStream::~CrasUnifiedStream() {
DCHECK(!is_playing_);
}
bool CrasUnifiedStream::Open() {
if (params_.sample_rate() <= 0) {
LOG(WARNING) << "Unsupported audio frequency.";
return false;
}
if (AudioManagerCras::BitsToFormat(params_.bits_per_sample()) ==
SND_PCM_FORMAT_UNKNOWN) {
LOG(WARNING) << "Unsupported pcm format";
return false;
}
if (cras_client_create(&client_)) {
LOG(WARNING) << "Couldn't create CRAS client.\n";
client_ = NULL;
return false;
}
if (cras_client_connect(client_)) {
LOG(WARNING) << "Couldn't connect CRAS client.\n";
cras_client_destroy(client_);
client_ = NULL;
return false;
}
if (cras_client_run_thread(client_)) {
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;
}
manager_->ReleaseOutputStream(this);
}
void CrasUnifiedStream::Start(AudioSourceCallback* callback) {
CHECK(callback);
static const int kChannelMap[] = {
CRAS_CH_FL,
CRAS_CH_FR,
CRAS_CH_FC,
CRAS_CH_LFE,
CRAS_CH_RL,
CRAS_CH_RR,
CRAS_CH_FLC,
CRAS_CH_FRC,
CRAS_CH_RC,
CRAS_CH_SL,
CRAS_CH_SR
};
source_callback_ = callback;
if (is_playing_)
return;
cras_audio_format* audio_format = cras_audio_format_create(
AudioManagerCras::BitsToFormat(params_.bits_per_sample()),
params_.sample_rate(),
params_.channels());
if (!audio_format) {
LOG(WARNING) << "Error setting up audio parameters.";
callback->OnError(this);
return;
}
int8 layout[CRAS_CH_MAX] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
for (size_t i = 0; i < arraysize(kChannelMap); ++i)
layout[kChannelMap[i]] = ChannelOrder(params_.channel_layout(),
static_cast<Channels>(i));
if (cras_audio_format_set_channel_layout(audio_format, layout)) {
LOG(WARNING) << "Error setting channel layout.";
callback->OnError(this);
return;
}
cras_stream_params* stream_params = cras_client_unified_params_create(
stream_direction_,
params_.frames_per_buffer(),
CRAS_STREAM_TYPE_DEFAULT,
0,
this,
CrasUnifiedStream::UnifiedCallback,
CrasUnifiedStream::StreamError,
audio_format);
if (!stream_params) {
LOG(WARNING) << "Error setting up stream parameters.";
callback->OnError(this);
cras_audio_format_destroy(audio_format);
return;
}
bytes_per_frame_ = cras_client_format_bytes_per_frame(audio_format);
if (cras_client_add_stream(client_, &stream_id_, stream_params) < 0) {
LOG(WARNING) << "Failed to add the stream";
callback->OnError(this);
cras_audio_format_destroy(audio_format);
cras_client_stream_params_destroy(stream_params);
return;
}
cras_client_set_stream_volume(client_, stream_id_, volume_);
cras_audio_format_destroy(audio_format);
cras_client_stream_params_destroy(stream_params);
is_playing_ = true;
}
void CrasUnifiedStream::Stop() {
if (!client_)
return;
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_;
}
uint32 CrasUnifiedStream::GetBytesLatency(
const struct timespec& latency_ts) {
uint32 latency_usec;
if (latency_ts.tv_sec < 0 || latency_ts.tv_nsec < 0) {
latency_usec = 0;
} else {
latency_usec = (latency_ts.tv_sec * base::Time::kMicrosecondsPerSecond) +
latency_ts.tv_nsec / base::Time::kNanosecondsPerMicrosecond;
}
double frames_latency =
latency_usec * params_.sample_rate() / base::Time::kMicrosecondsPerSecond;
return static_cast<unsigned int>(frames_latency * bytes_per_frame_);
}
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->DispatchCallback(frames,
input_samples,
output_samples,
input_ts,
output_ts);
}
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;
}
uint32 CrasUnifiedStream::DispatchCallback(size_t frames,
uint8* input_samples,
uint8* output_samples,
const timespec* input_ts,
const timespec* output_ts) {
switch (stream_direction_) {
case CRAS_STREAM_OUTPUT:
return WriteAudio(frames, output_samples, output_ts);
case CRAS_STREAM_INPUT:
NOTREACHED() << "CrasUnifiedStream doesn't support input streams.";
return 0;
case CRAS_STREAM_UNIFIED:
return ReadWriteAudio(frames, input_samples, output_samples,
input_ts, output_ts);
default:
break;
}
return 0;
}
uint32 CrasUnifiedStream::ReadWriteAudio(size_t frames,
uint8* input_samples,
uint8* output_samples,
const timespec* input_ts,
const timespec* output_ts) {
DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames()));
DCHECK(source_callback_);
uint32 bytes_per_sample = bytes_per_frame_ / params_.channels();
input_bus_->FromInterleaved(input_samples, frames, bytes_per_sample);
uint32 total_delay_bytes;
timespec latency_ts = {0, 0};
cras_client_calc_capture_latency(input_ts, &latency_ts);
total_delay_bytes = GetBytesLatency(latency_ts);
cras_client_calc_playback_latency(output_ts, &latency_ts);
total_delay_bytes += GetBytesLatency(latency_ts);
int frames_filled = source_callback_->OnMoreIOData(
input_bus_.get(),
output_bus_.get(),
AudioBuffersState(0, total_delay_bytes));
output_bus_->ToInterleaved(frames_filled, bytes_per_sample, output_samples);
return frames_filled;
}
uint32 CrasUnifiedStream::WriteAudio(size_t frames,
uint8* buffer,
const timespec* sample_ts) {
DCHECK_EQ(frames, static_cast<size_t>(output_bus_->frames()));
timespec latency_ts = {0, 0};
cras_client_calc_playback_latency(sample_ts, &latency_ts);
int frames_filled = source_callback_->OnMoreData(
output_bus_.get(), AudioBuffersState(0, GetBytesLatency(latency_ts)));
output_bus_->ToInterleaved(
frames_filled, bytes_per_frame_ / params_.channels(), buffer);
return frames_filled;
}
void CrasUnifiedStream::NotifyStreamError(int err) {
if (source_callback_)
source_callback_->OnError(this);
}
}