blob: f600480265383a87c97e1d016dca734f59d46df5 [file] [log] [blame]
// Copyright 2019 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.
use std::cmp::min;
use std::io;
use std::marker::PhantomData;
use std::{error, fmt};
use audio_streams::{
capture::{CaptureBuffer, CaptureBufferStream},
BoxError, BufferDrop, PlaybackBuffer, PlaybackBufferStream,
};
use cras_sys::gen::{snd_pcm_format_t, CRAS_AUDIO_MESSAGE_ID, CRAS_STREAM_DIRECTION};
use sys_util::error;
use crate::audio_socket::{AudioMessage, AudioSocket};
use crate::cras_server_socket::CrasServerSocket;
use crate::cras_shm::*;
#[derive(Debug)]
pub enum Error {
IoError(io::Error),
MessageTypeError,
}
impl error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::IoError(ref err) => err.fmt(f),
Error::MessageTypeError => write!(f, "Message type error"),
}
}
}
impl From<io::Error> for Error {
fn from(io_err: io::Error) -> Error {
Error::IoError(io_err)
}
}
/// A trait controls the state of `CrasAudioHeader` and
/// interacts with server's audio thread through `AudioSocket`.
pub trait CrasStreamData<'a>: Send {
// Creates `CrasStreamData` with only `AudioSocket`.
fn new(audio_sock: AudioSocket, header: CrasAudioHeader<'a>) -> Self;
fn header_mut(&mut self) -> &mut CrasAudioHeader<'a>;
fn audio_sock_mut(&mut self) -> &mut AudioSocket;
}
/// `CrasStreamData` implementation for `PlaybackBufferStream`.
pub struct CrasPlaybackData<'a> {
audio_sock: AudioSocket,
header: CrasAudioHeader<'a>,
}
impl<'a> CrasStreamData<'a> for CrasPlaybackData<'a> {
fn new(audio_sock: AudioSocket, header: CrasAudioHeader<'a>) -> Self {
Self { audio_sock, header }
}
fn header_mut(&mut self) -> &mut CrasAudioHeader<'a> {
&mut self.header
}
fn audio_sock_mut(&mut self) -> &mut AudioSocket {
&mut self.audio_sock
}
}
impl<'a> BufferDrop for CrasPlaybackData<'a> {
fn trigger(&mut self, nframes: usize) {
let log_err = |e| error!("BufferDrop error: {}", e);
if let Err(e) = self.header.commit_written_frames(nframes as u32) {
log_err(e);
}
if let Err(e) = self.audio_sock.data_ready(nframes as u32) {
log_err(e);
}
}
}
/// `CrasStreamData` implementation for `CaptureBufferStream`.
pub struct CrasCaptureData<'a> {
audio_sock: AudioSocket,
header: CrasAudioHeader<'a>,
}
impl<'a> CrasStreamData<'a> for CrasCaptureData<'a> {
fn new(audio_sock: AudioSocket, header: CrasAudioHeader<'a>) -> Self {
Self { audio_sock, header }
}
fn header_mut(&mut self) -> &mut CrasAudioHeader<'a> {
&mut self.header
}
fn audio_sock_mut(&mut self) -> &mut AudioSocket {
&mut self.audio_sock
}
}
impl<'a> BufferDrop for CrasCaptureData<'a> {
fn trigger(&mut self, nframes: usize) {
let log_err = |e| error!("BufferDrop error: {}", e);
if let Err(e) = self.header.commit_read_frames(nframes as u32) {
log_err(e);
}
if let Err(e) = self.audio_sock.capture_ready(nframes as u32) {
log_err(e);
}
}
}
#[allow(dead_code)]
pub struct CrasStream<'a, T: CrasStreamData<'a> + BufferDrop> {
stream_id: u32,
server_socket: CrasServerSocket,
block_size: u32,
direction: CRAS_STREAM_DIRECTION,
rate: u32,
num_channels: usize,
format: snd_pcm_format_t,
/// A structure for stream to interact with server audio thread.
controls: T,
/// The `PhantomData` is used by `controls: T`
phantom: PhantomData<CrasAudioHeader<'a>>,
audio_buffer: CrasAudioBuffer,
}
impl<'a, T: CrasStreamData<'a> + BufferDrop> CrasStream<'a, T> {
/// Creates a CrasStream by given arguments.
///
/// # Returns
/// `CrasStream` - CRAS client stream.
#[allow(clippy::too_many_arguments)]
pub fn try_new(
stream_id: u32,
server_socket: CrasServerSocket,
block_size: u32,
direction: CRAS_STREAM_DIRECTION,
rate: u32,
num_channels: usize,
format: snd_pcm_format_t,
audio_sock: AudioSocket,
header_fd: CrasAudioShmHeaderFd,
samples_fd: CrasShmFd,
) -> Result<Self, Error> {
let (header, audio_buffer) = create_header_and_buffers(header_fd, samples_fd)?;
Ok(Self {
stream_id,
server_socket,
block_size,
direction,
rate,
num_channels,
format,
controls: T::new(audio_sock, header),
phantom: PhantomData,
audio_buffer,
})
}
fn wait_request_data(&mut self) -> Result<(), Error> {
match self.controls.audio_sock_mut().read_audio_message()? {
AudioMessage::Success {
id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_REQUEST_DATA,
..
} => Ok(()),
_ => Err(Error::MessageTypeError),
}
}
fn wait_data_ready(&mut self) -> Result<u32, Error> {
match self.controls.audio_sock_mut().read_audio_message()? {
AudioMessage::Success {
id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_DATA_READY,
frames,
} => Ok(frames),
_ => Err(Error::MessageTypeError),
}
}
}
impl<'a, T: CrasStreamData<'a> + BufferDrop> Drop for CrasStream<'a, T> {
/// A blocking drop function, sends the disconnect message to `CrasClient` and waits for
/// the return message.
/// Logs an error message to stderr if the method fails.
fn drop(&mut self) {
if let Err(e) = self.server_socket.disconnect_stream(self.stream_id) {
error!("CrasStream::Drop error: {}", e);
}
}
}
impl<'a, T: CrasStreamData<'a> + BufferDrop> PlaybackBufferStream for CrasStream<'a, T> {
fn next_playback_buffer(&mut self) -> Result<PlaybackBuffer, BoxError> {
// Wait for request audio message
self.wait_request_data()?;
let header = self.controls.header_mut();
let frame_size = header.get_frame_size();
let (offset, len) = header.get_write_offset_and_len()?;
let buf = &mut self.audio_buffer.get_buffer()[offset..offset + len];
PlaybackBuffer::new(frame_size, buf, &mut self.controls).map_err(Box::from)
}
}
impl<'a, T: CrasStreamData<'a> + BufferDrop> CaptureBufferStream for CrasStream<'a, T> {
fn next_capture_buffer(&mut self) -> Result<CaptureBuffer, BoxError> {
// Wait for data ready message
let frames = self.wait_data_ready()?;
let header = self.controls.header_mut();
let frame_size = header.get_frame_size();
let shm_frames = header.get_readable_frames()?;
let len = min(shm_frames, frames as usize) * frame_size;
let offset = header.get_read_buffer_offset()?;
let buf = &mut self.audio_buffer.get_buffer()[offset..offset + len];
CaptureBuffer::new(frame_size, buf, &mut self.controls).map_err(Box::from)
}
}