diff options
Diffstat (limited to 'cras/client/libcras/src/libcras.rs')
-rw-r--r-- | cras/client/libcras/src/libcras.rs | 699 |
1 files changed, 0 insertions, 699 deletions
diff --git a/cras/client/libcras/src/libcras.rs b/cras/client/libcras/src/libcras.rs deleted file mode 100644 index 402a4a27..00000000 --- a/cras/client/libcras/src/libcras.rs +++ /dev/null @@ -1,699 +0,0 @@ -// 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. -//! Provides an interface for playing and recording audio through CRAS server. -//! -//! `CrasClient` implements `StreamSource` trait and it can create playback or capture -//! stream - `CrasStream` which can be a -//! - `PlaybackBufferStream` for audio playback or -//! - `CaptureBufferStream` for audio capture. -//! -//! # Example of file audio playback -//! -//! `PlaybackBuffer`s to be filled with audio samples are obtained by calling -//! `next_playback_buffer` from `CrasStream`. -//! -//! Users playing audio fill the provided buffers with audio. When a `PlaybackBuffer` is dropped, -//! the samples written to it are committed to the `CrasStream` it came from. -//! -//! -//! ``` -//! // An example of playing raw audio data from a given file -//! use std::env; -//! use std::fs::File; -//! use std::io::{Read, Write}; -//! use std::thread::{spawn, JoinHandle}; -//! type Result<T> = std::result::Result<T, BoxError>; -//! -//! use libcras::{BoxError, CrasClient, CrasClientType}; -//! use audio_streams::{SampleFormat, StreamSource}; -//! -//! const BUFFER_SIZE: usize = 256; -//! const FRAME_RATE: u32 = 44100; -//! const NUM_CHANNELS: usize = 2; -//! const FORMAT: SampleFormat = SampleFormat::S16LE; -//! -//! # fn main() -> Result<()> { -//! # let args: Vec<String> = env::args().collect(); -//! # match args.len() { -//! # 2 => { -//! let mut cras_client = CrasClient::new()?; -//! cras_client.set_client_type(CrasClientType::CRAS_CLIENT_TYPE_TEST); -//! let (_control, mut stream) = cras_client -//! .new_playback_stream(NUM_CHANNELS, FORMAT, FRAME_RATE, BUFFER_SIZE)?; -//! -//! // Plays 1000 * BUFFER_SIZE samples from the given file -//! let mut file = File::open(&args[1])?; -//! let mut local_buffer = [0u8; BUFFER_SIZE * NUM_CHANNELS * 2]; -//! for _i in 0..1000 { -//! // Reads data to local buffer -//! let _read_count = file.read(&mut local_buffer)?; -//! -//! // Gets writable buffer from stream and -//! let mut buffer = stream.next_playback_buffer()?; -//! // Writes data to stream buffer -//! let _write_frames = buffer.write(&local_buffer)?; -//! } -//! // Stream and client should gracefully be closed out of this scope -//! # } -//! # _ => { -//! # println!("{} /path/to/playback_file.raw", args[0]); -//! # } -//! # }; -//! # Ok(()) -//! # } -//! ``` -//! -//! # Example of file audio capture -//! -//! `CaptureBuffer`s which contain audio samples are obtained by calling -//! `next_capture_buffer` from `CrasStream`. -//! -//! Users get captured audio samples from the provided buffers. When a `CaptureBuffer` is dropped, -//! the number of read samples will be committed to the `CrasStream` it came from. -//! ``` -//! use std::env; -//! use std::fs::File; -//! use std::io::{Read, Write}; -//! use std::thread::{spawn, JoinHandle}; -//! type Result<T> = std::result::Result<T, BoxError>; -//! -//! use libcras::{BoxError, CrasClient, CrasClientType}; -//! use audio_streams::{SampleFormat, StreamSource}; -//! -//! const BUFFER_SIZE: usize = 256; -//! const FRAME_RATE: u32 = 44100; -//! const NUM_CHANNELS: usize = 2; -//! const FORMAT: SampleFormat = SampleFormat::S16LE; -//! -//! # fn main() -> Result<()> { -//! # let args: Vec<String> = env::args().collect(); -//! # match args.len() { -//! # 2 => { -//! let mut cras_client = CrasClient::new()?; -//! cras_client.set_client_type(CrasClientType::CRAS_CLIENT_TYPE_TEST); -//! let (_control, mut stream) = cras_client -//! .new_capture_stream(NUM_CHANNELS, FORMAT, FRAME_RATE, BUFFER_SIZE)?; -//! -//! // Capture 1000 * BUFFER_SIZE samples to the given file -//! let mut file = File::create(&args[1])?; -//! let mut local_buffer = [0u8; BUFFER_SIZE * NUM_CHANNELS * 2]; -//! for _i in 0..1000 { -//! -//! // Gets readable buffer from stream and -//! let mut buffer = stream.next_capture_buffer()?; -//! // Reads data to local buffer -//! let read_count = buffer.read(&mut local_buffer)?; -//! // Writes data to file -//! let _read_frames = file.write(&local_buffer[..read_count])?; -//! } -//! // Stream and client should gracefully be closed out of this scope -//! # } -//! # _ => { -//! # println!("{} /path/to/capture_file.raw", args[0]); -//! # } -//! # }; -//! # Ok(()) -//! # } -//! ``` -use std::io; -use std::mem; -use std::os::unix::{ - io::{AsRawFd, RawFd}, - net::UnixStream, -}; -use std::{error, fmt}; - -pub use audio_streams::BoxError; -use audio_streams::{ - capture::{CaptureBufferStream, NoopCaptureStream}, - shm_streams::{NullShmStream, ShmStream, ShmStreamSource}, - BufferDrop, NoopStreamControl, PlaybackBufferStream, SampleFormat, StreamControl, - StreamDirection, StreamEffect, StreamSource, -}; -use cras_sys::gen::*; -pub use cras_sys::gen::{ - CRAS_CLIENT_TYPE as CrasClientType, CRAS_NODE_TYPE as CrasNodeType, - CRAS_STREAM_EFFECT as CrasStreamEffect, -}; -pub use cras_sys::{AudioDebugInfo, CrasIodevInfo, CrasIonodeInfo, Error as CrasSysError}; -use sys_util::{PollContext, PollToken, SharedMemory}; - -mod audio_socket; -use crate::audio_socket::AudioSocket; -mod cras_server_socket; -use crate::cras_server_socket::CrasServerSocket; -pub use crate::cras_server_socket::CrasSocketType; -mod cras_shm; -use crate::cras_shm::CrasServerState; -pub mod cras_shm_stream; -use crate::cras_shm_stream::CrasShmStream; -mod cras_stream; -use crate::cras_stream::{CrasCaptureData, CrasPlaybackData, CrasStream, CrasStreamData}; -mod cras_client_message; -use crate::cras_client_message::*; - -#[derive(Debug)] -pub enum Error { - CrasClientMessageError(cras_client_message::Error), - CrasStreamError(cras_stream::Error), - CrasSysError(cras_sys::Error), - IoError(io::Error), - SysUtilError(sys_util::Error), - MessageTypeError, - UnexpectedExit, -} - -impl error::Error for Error {} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::CrasClientMessageError(ref err) => err.fmt(f), - Error::CrasStreamError(ref err) => err.fmt(f), - Error::CrasSysError(ref err) => err.fmt(f), - Error::IoError(ref err) => err.fmt(f), - Error::SysUtilError(ref err) => err.fmt(f), - Error::MessageTypeError => write!(f, "Message type error"), - Error::UnexpectedExit => write!(f, "Unexpected exit"), - } - } -} - -type Result<T> = std::result::Result<T, Error>; - -impl From<io::Error> for Error { - fn from(io_err: io::Error) -> Self { - Error::IoError(io_err) - } -} - -impl From<sys_util::Error> for Error { - fn from(sys_util_err: sys_util::Error) -> Self { - Error::SysUtilError(sys_util_err) - } -} - -impl From<cras_stream::Error> for Error { - fn from(err: cras_stream::Error) -> Self { - Error::CrasStreamError(err) - } -} - -impl From<cras_client_message::Error> for Error { - fn from(err: cras_client_message::Error) -> Self { - Error::CrasClientMessageError(err) - } -} - -/// A CRAS server client, which implements StreamSource and ShmStreamSource. -/// It can create audio streams connecting to CRAS server. -pub struct CrasClient<'a> { - server_socket: CrasServerSocket, - server_state: CrasServerState<'a>, - client_id: u32, - next_stream_id: u32, - cras_capture: bool, - client_type: CRAS_CLIENT_TYPE, -} - -impl<'a> CrasClient<'a> { - /// Blocks creating a `CrasClient` with registered `client_id` - /// - /// # Results - /// - /// * `CrasClient` - A client to interact with CRAS server - /// - /// # Errors - /// - /// Returns error if error occurs while handling server message or message - /// type is incorrect - pub fn new() -> Result<Self> { - Self::with_type(CrasSocketType::Legacy) - } - - /// Tries to create a `CrasClient` with a given `CrasSocketType`. - /// - /// # Errors - /// - /// Returns error if error occurs while handling server message or message - /// type is incorrect. - pub fn with_type(socket_type: CrasSocketType) -> Result<Self> { - // Create a connection to the server. - let mut server_socket = CrasServerSocket::with_type(socket_type)?; - // Gets client ID and server state fd from server - if let ServerResult::Connected(client_id, server_state_fd) = - CrasClient::wait_for_message(&mut server_socket)? - { - Ok(Self { - server_socket, - server_state: CrasServerState::try_new(server_state_fd)?, - client_id, - next_stream_id: 0, - cras_capture: false, - client_type: CRAS_CLIENT_TYPE::CRAS_CLIENT_TYPE_UNKNOWN, - }) - } else { - Err(Error::MessageTypeError) - } - } - - /// Enables capturing audio through CRAS server. - pub fn enable_cras_capture(&mut self) { - self.cras_capture = true; - } - - /// Set the type of this client to report to CRAS when connecting streams. - pub fn set_client_type(&mut self, client_type: CRAS_CLIENT_TYPE) { - self.client_type = client_type; - } - - /// Sets the system volume to `volume`. - /// - /// Send a message to the server to request setting the system volume - /// to `volume`. No response is returned from the server. - /// - /// # Errors - /// - /// If writing the message to the server socket failed. - pub fn set_system_volume(&mut self, volume: u32) -> Result<()> { - let header = cras_server_message { - length: mem::size_of::<cras_set_system_volume>() as u32, - id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_SET_SYSTEM_VOLUME, - }; - let msg = cras_set_system_volume { header, volume }; - - self.server_socket.send_server_message_with_fds(&msg, &[])?; - Ok(()) - } - - /// Sets the system mute status to `mute`. - /// - /// Send a message to the server to request setting the system mute - /// to `mute`. No response is returned from the server. - /// - /// # Errors - /// - /// If writing the message to the server socket failed. - pub fn set_system_mute(&mut self, mute: bool) -> Result<()> { - let header = cras_server_message { - length: mem::size_of::<cras_set_system_mute>() as u32, - id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_SET_SYSTEM_MUTE, - }; - let msg = cras_set_system_mute { - header, - mute: mute as i32, - }; - - self.server_socket.send_server_message_with_fds(&msg, &[])?; - Ok(()) - } - - /// Gets the system volume. - /// - /// Read the current value for system volume from the server shared memory. - pub fn get_system_volume(&self) -> u32 { - self.server_state.get_system_volume() - } - - /// Gets the system mute. - /// - /// Read the current value for system mute from the server shared memory. - pub fn get_system_mute(&self) -> bool { - self.server_state.get_system_mute() - } - - /// Gets a list of output devices - /// - /// Read a list of the currently attached output devices from the server shared memory. - pub fn output_devices(&self) -> impl Iterator<Item = CrasIodevInfo> { - self.server_state.output_devices() - } - - /// Gets a list of input devices - /// - /// Read a list of the currently attached input devices from the server shared memory. - pub fn input_devices(&self) -> impl Iterator<Item = CrasIodevInfo> { - self.server_state.input_devices() - } - - /// Gets a list of output nodes - /// - /// Read a list of the currently attached output nodes from the server shared memory. - pub fn output_nodes(&self) -> impl Iterator<Item = CrasIonodeInfo> { - self.server_state.output_nodes() - } - - /// Gets a list of input nodes - /// - /// Read a list of the currently attached input nodes from the server shared memory. - pub fn input_nodes(&self) -> impl Iterator<Item = CrasIonodeInfo> { - self.server_state.input_nodes() - } - - /// Gets the server's audio debug info. - /// - /// Sends a message to the server requesting an update of audio debug info, - /// waits for the response, and then reads the info from the server state. - /// - /// # Errors - /// - /// * If sending the message to the server failed. - /// * If an unexpected response message is received. - pub fn get_audio_debug_info(&mut self) -> Result<AudioDebugInfo> { - let header = cras_server_message { - length: mem::size_of::<cras_dump_audio_thread>() as u32, - id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_DUMP_AUDIO_THREAD, - }; - let msg = cras_dump_audio_thread { header }; - - self.server_socket.send_server_message_with_fds(&msg, &[])?; - - match CrasClient::wait_for_message(&mut self.server_socket)? { - ServerResult::DebugInfoReady => Ok(self - .server_state - .get_audio_debug_info() - .map_err(Error::CrasSysError)?), - _ => Err(Error::MessageTypeError), - } - } - - // Gets next server_stream_id from client and increment stream_id counter. - fn next_server_stream_id(&mut self) -> u32 { - let res = self.next_stream_id; - self.next_stream_id += 1; - self.server_stream_id(res) - } - - // Gets server_stream_id from given stream_id - fn server_stream_id(&self, stream_id: u32) -> u32 { - (self.client_id << 16) | stream_id - } - - // Creates general stream with given parameters - fn create_stream<'b, T: BufferDrop + CrasStreamData<'b>>( - &mut self, - device_index: Option<u32>, - block_size: u32, - direction: CRAS_STREAM_DIRECTION, - rate: u32, - channel_num: usize, - format: SampleFormat, - ) -> Result<CrasStream<'b, T>> { - let stream_id = self.next_server_stream_id(); - - // Prepares server message - let audio_format = - cras_audio_format_packed::new(format.into(), rate, channel_num, direction); - let msg_header = cras_server_message { - length: mem::size_of::<cras_connect_message>() as u32, - id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_CONNECT_STREAM, - }; - let server_cmsg = cras_connect_message { - header: msg_header, - proto_version: CRAS_PROTO_VER, - direction, - stream_id, - stream_type: CRAS_STREAM_TYPE::CRAS_STREAM_TYPE_DEFAULT, - buffer_frames: block_size, - cb_threshold: block_size, - flags: 0, - format: audio_format, - dev_idx: device_index.unwrap_or(CRAS_SPECIAL_DEVICE::NO_DEVICE as u32), - effects: 0, - client_type: self.client_type, - client_shm_size: 0, - buffer_offsets: [0, 0], - }; - - // Creates AudioSocket pair - let (sock1, sock2) = UnixStream::pair()?; - - // Sends `CRAS_SERVER_CONNECT_STREAM` message - let socks = [sock2.as_raw_fd()]; - self.server_socket - .send_server_message_with_fds(&server_cmsg, &socks)?; - - let audio_socket = AudioSocket::new(sock1); - loop { - let result = CrasClient::wait_for_message(&mut self.server_socket)?; - if let ServerResult::StreamConnected(_stream_id, header_fd, samples_fd) = result { - return CrasStream::try_new( - stream_id, - self.server_socket.try_clone()?, - block_size, - direction, - rate, - channel_num, - format.into(), - audio_socket, - header_fd, - samples_fd, - ) - .map_err(Error::CrasStreamError); - } - } - } - - /// Creates a new playback stream pinned to the device at `device_index`. - /// - /// # Arguments - /// - /// * `device_index` - The device to which the stream will be attached. - /// * `num_channels` - The count of audio channels for the stream. - /// * `format` - The format to use for stream audio samples. - /// * `frame_rate` - The sample rate of the stream. - /// * `buffer_size` - The transfer size granularity in frames. - #[allow(clippy::type_complexity)] - pub fn new_pinned_playback_stream( - &mut self, - device_index: u32, - num_channels: usize, - format: SampleFormat, - frame_rate: u32, - buffer_size: usize, - ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError> - { - Ok(( - Box::new(NoopStreamControl::new()), - Box::new(self.create_stream::<CrasPlaybackData>( - Some(device_index), - buffer_size as u32, - CRAS_STREAM_DIRECTION::CRAS_STREAM_OUTPUT, - frame_rate, - num_channels, - format, - )?), - )) - } - - /// Creates a new capture stream pinned to the device at `device_index`. - /// - /// This is useful for, among other things, capturing from a loopback - /// device. - /// - /// # Arguments - /// - /// * `device_index` - The device to which the stream will be attached. - /// * `num_channels` - The count of audio channels for the stream. - /// * `format` - The format to use for stream audio samples. - /// * `frame_rate` - The sample rate of the stream. - /// * `buffer_size` - The transfer size granularity in frames. - #[allow(clippy::type_complexity)] - pub fn new_pinned_capture_stream( - &mut self, - device_index: u32, - num_channels: usize, - format: SampleFormat, - frame_rate: u32, - buffer_size: usize, - ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn CaptureBufferStream>), BoxError> { - Ok(( - Box::new(NoopStreamControl::new()), - Box::new(self.create_stream::<CrasCaptureData>( - Some(device_index), - buffer_size as u32, - CRAS_STREAM_DIRECTION::CRAS_STREAM_INPUT, - frame_rate, - num_channels, - format, - )?), - )) - } - - // Blocks handling the first server message received from `socket`. - fn wait_for_message(socket: &mut CrasServerSocket) -> Result<ServerResult> { - #[derive(PollToken)] - enum Token { - ServerMsg, - } - let poll_ctx: PollContext<Token> = - PollContext::new().and_then(|pc| pc.add(socket, Token::ServerMsg).and(Ok(pc)))?; - - let events = poll_ctx.wait()?; - // Check the first readable message - let tokens: Vec<Token> = events.iter_readable().map(|e| e.token()).collect(); - tokens - .get(0) - .ok_or(Error::UnexpectedExit) - .and_then(|ref token| { - match token { - Token::ServerMsg => ServerResult::handle_server_message(socket), - } - .map_err(Into::into) - }) - } - - /// Returns any open file descriptors needed by CrasClient. - /// This function is shared between StreamSource and ShmStreamSource. - fn keep_fds(&self) -> Vec<RawFd> { - vec![self.server_socket.as_raw_fd()] - } -} - -impl<'a> StreamSource for CrasClient<'a> { - #[allow(clippy::type_complexity)] - fn new_playback_stream( - &mut self, - num_channels: usize, - format: SampleFormat, - frame_rate: u32, - buffer_size: usize, - ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn PlaybackBufferStream>), BoxError> - { - Ok(( - Box::new(NoopStreamControl::new()), - Box::new(self.create_stream::<CrasPlaybackData>( - None, - buffer_size as u32, - CRAS_STREAM_DIRECTION::CRAS_STREAM_OUTPUT, - frame_rate, - num_channels, - format, - )?), - )) - } - - #[allow(clippy::type_complexity)] - fn new_capture_stream( - &mut self, - num_channels: usize, - format: SampleFormat, - frame_rate: u32, - buffer_size: usize, - ) -> std::result::Result<(Box<dyn StreamControl>, Box<dyn CaptureBufferStream>), BoxError> { - if self.cras_capture { - Ok(( - Box::new(NoopStreamControl::new()), - Box::new(self.create_stream::<CrasCaptureData>( - None, - buffer_size as u32, - CRAS_STREAM_DIRECTION::CRAS_STREAM_INPUT, - frame_rate, - num_channels, - format, - )?), - )) - } else { - Ok(( - Box::new(NoopStreamControl::new()), - Box::new(NoopCaptureStream::new( - num_channels, - format, - frame_rate, - buffer_size, - )), - )) - } - } - - fn keep_fds(&self) -> Option<Vec<RawFd>> { - Some(CrasClient::keep_fds(self)) - } -} - -impl<'a> ShmStreamSource for CrasClient<'a> { - fn new_stream( - &mut self, - direction: StreamDirection, - num_channels: usize, - format: SampleFormat, - frame_rate: u32, - buffer_size: usize, - effects: &[StreamEffect], - client_shm: &SharedMemory, - buffer_offsets: [u64; 2], - ) -> std::result::Result<Box<dyn ShmStream>, BoxError> { - if direction == StreamDirection::Capture && !self.cras_capture { - return Ok(Box::new(NullShmStream::new( - buffer_size, - num_channels, - format, - frame_rate, - ))); - } - - let buffer_size = buffer_size as u32; - - // Prepares server message - let stream_id = self.next_server_stream_id(); - let audio_format = cras_audio_format_packed::new( - format.into(), - frame_rate, - num_channels, - direction.into(), - ); - let msg_header = cras_server_message { - length: mem::size_of::<cras_connect_message>() as u32, - id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_CONNECT_STREAM, - }; - - let server_cmsg = cras_connect_message { - header: msg_header, - proto_version: CRAS_PROTO_VER, - direction: direction.into(), - stream_id, - stream_type: CRAS_STREAM_TYPE::CRAS_STREAM_TYPE_DEFAULT, - buffer_frames: buffer_size, - cb_threshold: buffer_size, - flags: 0, - format: audio_format, - dev_idx: CRAS_SPECIAL_DEVICE::NO_DEVICE as u32, - effects: effects.iter().collect::<CrasStreamEffect>().into(), - client_type: self.client_type, - client_shm_size: client_shm.size(), - buffer_offsets, - }; - - // Creates AudioSocket pair - let (sock1, sock2) = UnixStream::pair()?; - - // Sends `CRAS_SERVER_CONNECT_STREAM` message - let fds = [sock2.as_raw_fd(), client_shm.as_raw_fd()]; - self.server_socket - .send_server_message_with_fds(&server_cmsg, &fds)?; - - loop { - let result = CrasClient::wait_for_message(&mut self.server_socket)?; - if let ServerResult::StreamConnected(_stream_id, header_fd, _samples_fd) = result { - let audio_socket = AudioSocket::new(sock1); - let stream = CrasShmStream::try_new( - stream_id, - self.server_socket.try_clone()?, - audio_socket, - direction, - num_channels, - frame_rate, - format, - header_fd, - client_shm.size() as usize, - )?; - return Ok(Box::new(stream)); - } - } - } - - fn keep_fds(&self) -> Vec<RawFd> { - CrasClient::keep_fds(self) - } -} |