blob: f209a9df362aad0e3a2a388e8e0c725f7052537a [file] [log] [blame]
// Copyright 2018 The Chromium OS 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_perception/audio_receiver.h"
#include <alsa/asoundlib.h>
#include <utility>
#include "base/logging.h"
namespace mri {
namespace {
// Callback when getting a new audio frame.
int OnAudioSamples(struct cras_client* client,
cras_stream_id_t stream_id,
uint8_t* captured_samples,
uint8_t* playback_samples,
unsigned int num_samples,
const struct timespec* captured_time,
const struct timespec* playback_time,
void* user_arg) {
mri::AudioReceiver* audio_receiver = (mri::AudioReceiver*)user_arg;
audio_receiver->ProcessAudioSamples(captured_samples, num_samples);
return num_samples;
}
// Callback when getting audio capture error.
int OnAudioCaptureError(struct cras_client* client,
cras_stream_id_t stream_id,
int err,
void* arg) {
LOG(ERROR) << "Audio capture error with code: " << err;
return err;
}
} // namespace
void AudioReceiver::ProcessAudioSamples(const uint8_t* samples,
unsigned int num_frames) {
const int bytes_per_frame = cras_client_format_bytes_per_frame(audio_format_);
const int total_bytes = bytes_per_frame * num_frames;
for (auto& handler_pair : frame_handler_id_to_audio_frame_handler_map_) {
handler_pair.second(samples, total_bytes);
}
}
AudioReceiver::AudioReceiver(const std::string& device_id)
: device_id_(device_id), frame_handler_counter_(0) {}
bool AudioReceiver::CaptureFormatMatches(const AudioStreamParams& requested) {
return params_.frequency_in_hz() == requested.frequency_in_hz() &&
params_.num_channels() == requested.num_channels() &&
params_.frame_size() == requested.frame_size() &&
params_.sample_format() == requested.sample_format();
}
int AudioReceiver::GetFrameHandlerCount() {
return frame_handler_id_to_audio_frame_handler_map_.size();
}
bool AudioReceiver::SetAudioStreamParams(const AudioStreamParams& params) {
DestroyParams();
snd_pcm_format_t sample_format;
switch (params.sample_format()) {
case SampleFormat::SND_PCM_FORMAT_S32_LE:
sample_format = ::SND_PCM_FORMAT_S32_LE;
break;
case SampleFormat::SND_PCM_FORMAT_S16_LE:
sample_format = ::SND_PCM_FORMAT_S16_LE;
break;
default:
LOG(ERROR) << "Sample format unknown or not supported.";
return false;
}
audio_format_ = cras_audio_format_create(
sample_format, params.frequency_in_hz(), params.num_channels());
if (!audio_format_) {
LOG(ERROR) << "Failed to create CrAS audio format.";
DestroyParams();
return false;
}
audio_capture_params_ = cras_client_unified_params_create(
CRAS_STREAM_INPUT, params.frame_size(), CRAS_STREAM_TYPE_DEFAULT, 0, this,
OnAudioSamples, OnAudioCaptureError, audio_format_);
if (!audio_capture_params_) {
LOG(ERROR) << "Failed to create CrAS audio capture format.";
DestroyParams();
return false;
}
params_ = params;
return true;
}
AudioStreamParams AudioReceiver::GetAudioStreamParams() {
return params_;
}
bool AudioReceiver::StartAudioCaptureForDeviceIdx(struct cras_client* client,
int dev_idx) {
// Create a pinned stream. Return 0 on success, or negative error code on
// failure.
int rc = cras_client_add_pinned_stream(client, dev_idx, &stream_id_,
audio_capture_params_);
if (rc != 0) {
LOG(ERROR) << "Failed to add pinned stream with error code: " << rc;
return false;
}
return true;
}
int AudioReceiver::AddAudioFrameHandler(
ChromeAudioServiceClient::AudioFrameHandler handler) {
frame_handler_counter_++;
frame_handler_id_to_audio_frame_handler_map_[frame_handler_counter_] =
std::move(handler);
return frame_handler_counter_;
}
bool AudioReceiver::RemoveFrameHandler(int frame_handler_id) {
if (frame_handler_id_to_audio_frame_handler_map_.find(frame_handler_id) ==
frame_handler_id_to_audio_frame_handler_map_.end()) {
// Frame handler not found.
return false;
}
frame_handler_id_to_audio_frame_handler_map_.erase(frame_handler_id);
return true;
}
void AudioReceiver::StopAudioCapture(struct cras_client* client) {
cras_client_rm_stream(client, stream_id_);
}
void AudioReceiver::DestroyParams() {
if (audio_format_) {
cras_audio_format_destroy(audio_format_);
audio_format_ = nullptr;
}
if (audio_capture_params_) {
cras_client_stream_params_destroy(audio_capture_params_);
audio_capture_params_ = nullptr;
}
}
} // namespace mri