diff options
Diffstat (limited to 'cras/client/libcras/src')
-rw-r--r-- | cras/client/libcras/src/audio_socket.rs | 316 | ||||
-rw-r--r-- | cras/client/libcras/src/cras_client_message.rs | 200 | ||||
-rw-r--r-- | cras/client/libcras/src/cras_server_socket.rs | 124 | ||||
-rw-r--r-- | cras/client/libcras/src/cras_shm.rs | 1308 | ||||
-rw-r--r-- | cras/client/libcras/src/cras_shm_stream.rs | 191 | ||||
-rw-r--r-- | cras/client/libcras/src/cras_stream.rs | 224 | ||||
-rw-r--r-- | cras/client/libcras/src/libcras.rs | 699 |
7 files changed, 0 insertions, 3062 deletions
diff --git a/cras/client/libcras/src/audio_socket.rs b/cras/client/libcras/src/audio_socket.rs deleted file mode 100644 index ac56144c..00000000 --- a/cras/client/libcras/src/audio_socket.rs +++ /dev/null @@ -1,316 +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. -use std::io; -use std::io::{Read, Write}; -use std::mem; -use std::os::unix::{ - io::{AsRawFd, RawFd}, - net::UnixStream, -}; -use std::time::Duration; - -use cras_sys::gen::{audio_message, CRAS_AUDIO_MESSAGE_ID}; -use data_model::DataInit; -use sys_util::{PollContext, PollToken}; - -/// A structure for interacting with the CRAS server audio thread through a `UnixStream::pair`. -pub struct AudioSocket { - socket: UnixStream, -} - -/// Audio message results which are exchanged by `CrasStream` and CRAS audio server. -/// through an audio socket. -#[allow(dead_code)] -#[derive(Debug)] -pub enum AudioMessage { - /// * `id` - Audio message id, which is a `enum CRAS_AUDIO_MESSAGE_ID`. - /// * `frames` - A `u32` indicating the read or written frame count. - Success { - id: CRAS_AUDIO_MESSAGE_ID, - frames: u32, - }, - /// * `error` - Error code when a error occurs. - Error(i32), -} - -/// Converts AudioMessage to raw audio_message for CRAS audio server. -impl Into<audio_message> for AudioMessage { - fn into(self) -> audio_message { - match self { - AudioMessage::Success { id, frames } => audio_message { - id, - error: 0, - frames, - }, - AudioMessage::Error(error) => audio_message { - id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_REQUEST_DATA, - error, - frames: 0, - }, - } - } -} - -/// Converts AudioMessage from raw audio_message from CRAS audio server. -impl From<audio_message> for AudioMessage { - fn from(message: audio_message) -> Self { - match message.error { - 0 => AudioMessage::Success { - id: message.id as CRAS_AUDIO_MESSAGE_ID, - frames: message.frames, - }, - error => AudioMessage::Error(error), - } - } -} - -impl AudioSocket { - /// Creates `AudioSocket` from a `UnixStream`. - /// - /// # Arguments - /// `socket` - A `UnixStream`. - pub fn new(socket: UnixStream) -> Self { - AudioSocket { socket } - } - - fn read_from_socket<T>(&mut self) -> io::Result<T> - where - T: Sized + DataInit + Default, - { - let mut message: T = Default::default(); - let rc = self.socket.read(message.as_mut_slice())?; - if rc == mem::size_of::<T>() { - Ok(message) - } else { - Err(io::Error::new(io::ErrorKind::Other, "Read truncated data.")) - } - } - - /// Blocks reading an `audio message`. - /// - /// # Returns - /// `AudioMessage` - AudioMessage enum. - /// - /// # Errors - /// Returns io::Error if error occurs. - pub fn read_audio_message(&mut self) -> io::Result<AudioMessage> { - match self.read_audio_message_with_timeout(None)? { - None => Err(io::Error::new(io::ErrorKind::Other, "Unexpected exit")), - Some(message) => Ok(message), - } - } - - /// Blocks waiting for an `audio message` until `timeout` occurs. If `timeout` - /// is None, blocks indefinitely. - /// - /// # Returns - /// Some(AudioMessage) - AudioMessage enum if we receive a message before timeout. - /// None - If the timeout expires. - /// - /// # Errors - /// Returns io::Error if error occurs. - pub fn read_audio_message_with_timeout( - &mut self, - timeout: Option<Duration>, - ) -> io::Result<Option<AudioMessage>> { - #[derive(PollToken)] - enum Token { - AudioMsg, - } - let poll_ctx: PollContext<Token> = - match PollContext::new().and_then(|pc| pc.add(self, Token::AudioMsg).and(Ok(pc))) { - Ok(pc) => pc, - Err(e) => { - return Err(io::Error::new( - io::ErrorKind::Other, - format!("Failed to create PollContext: {}", e), - )); - } - }; - let events = { - let result = match timeout { - None => poll_ctx.wait(), - Some(duration) => poll_ctx.wait_timeout(duration), - }; - match result { - Ok(v) => v, - Err(e) => { - return Err(io::Error::new( - io::ErrorKind::Other, - format!("Failed to poll: {:?}", e), - )); - } - } - }; - - // Check the first readable message - let tokens: Vec<Token> = events.iter_readable().map(|e| e.token()).collect(); - match tokens.get(0) { - None => Ok(None), - Some(&Token::AudioMsg) => { - let raw_msg: audio_message = self.read_from_socket()?; - Ok(Some(AudioMessage::from(raw_msg))) - } - } - } - - /// Sends raw audio message with given AudioMessage enum. - /// - /// # Arguments - /// * `msg` - enum AudioMessage, which could be `Success` with message id - /// and frames or `Error` with error code. - /// - /// # Errors - /// Returns error if `libc::write` fails. - fn send_audio_message(&mut self, msg: AudioMessage) -> io::Result<()> { - let msg: audio_message = msg.into(); - let rc = self.socket.write(msg.as_slice())?; - if rc < mem::size_of::<audio_message>() { - Err(io::Error::new(io::ErrorKind::Other, "Sent truncated data.")) - } else { - Ok(()) - } - } - - /// Sends the data ready message with written frame count. - /// - /// # Arguments - /// * `frames` - An `u32` indicating the written frame count. - pub fn data_ready(&mut self, frames: u32) -> io::Result<()> { - self.send_audio_message(AudioMessage::Success { - id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_DATA_READY, - frames, - }) - } - - /// Sends the capture ready message with read frame count. - /// - /// # Arguments - /// - /// * `frames` - An `u32` indicating the number of read frames. - pub fn capture_ready(&mut self, frames: u32) -> io::Result<()> { - self.send_audio_message(AudioMessage::Success { - id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_DATA_CAPTURED, - frames, - }) - } -} - -impl AsRawFd for AudioSocket { - fn as_raw_fd(&self) -> RawFd { - self.socket.as_raw_fd() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - // PartialEq for comparing AudioMessage in tests - impl PartialEq for AudioMessage { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - ( - AudioMessage::Success { id, frames }, - AudioMessage::Success { - id: other_id, - frames: other_frames, - }, - ) => id == other_id && frames == other_frames, - (AudioMessage::Error(err), AudioMessage::Error(other_err)) => err == other_err, - _ => false, - } - } - } - - fn init_audio_socket_pair() -> (AudioSocket, AudioSocket) { - let (sock1, sock2) = UnixStream::pair().unwrap(); - let sender = AudioSocket::new(sock1); - let receiver = AudioSocket::new(sock2); - (sender, receiver) - } - - #[test] - fn audio_socket_send_and_recv_audio_message() { - let (mut sender, mut receiver) = init_audio_socket_pair(); - let message_succ = AudioMessage::Success { - id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_REQUEST_DATA, - frames: 0, - }; - sender.send_audio_message(message_succ).unwrap(); - let res = receiver.read_audio_message().unwrap(); - assert_eq!( - res, - AudioMessage::Success { - id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_REQUEST_DATA, - frames: 0 - } - ); - - let message_err = AudioMessage::Error(123); - sender.send_audio_message(message_err).unwrap(); - let res = receiver.read_audio_message().unwrap(); - assert_eq!(res, AudioMessage::Error(123)); - } - - #[test] - fn audio_socket_data_ready_send_and_recv() { - let (sock1, sock2) = UnixStream::pair().unwrap(); - let mut audio_socket_send = AudioSocket::new(sock1); - let mut audio_socket_recv = AudioSocket::new(sock2); - audio_socket_send.data_ready(256).unwrap(); - - // Test receiving by using raw audio_message since CRAS audio server use this. - let audio_msg: audio_message = audio_socket_recv.read_from_socket().unwrap(); - let ref_audio_msg = audio_message { - id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_DATA_READY, - error: 0, - frames: 256, - }; - // Use brace to copy unaligned data locally - assert_eq!({ audio_msg.id }, { ref_audio_msg.id }); - assert_eq!({ audio_msg.error }, { ref_audio_msg.error }); - assert_eq!({ audio_msg.frames }, { ref_audio_msg.frames }); - } - - #[test] - fn audio_socket_capture_ready() { - let (sock1, sock2) = UnixStream::pair().unwrap(); - let mut audio_socket_send = AudioSocket::new(sock1); - let mut audio_socket_recv = AudioSocket::new(sock2); - audio_socket_send - .capture_ready(256) - .expect("Failed to send capture ready message."); - - // Test receiving by using raw audio_message since CRAS audio server use this. - let audio_msg: audio_message = audio_socket_recv - .read_from_socket() - .expect("Failed to read audio message from AudioSocket."); - let ref_audio_msg = audio_message { - id: CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_DATA_CAPTURED, - error: 0, - frames: 256, - }; - // Use brace to copy unaligned data locally - assert_eq!({ audio_msg.id }, { ref_audio_msg.id }); - assert_eq!({ audio_msg.error }, { ref_audio_msg.error }); - assert_eq!({ audio_msg.frames }, { ref_audio_msg.frames }); - } - - #[test] - fn audio_socket_send_when_broken_pipe() { - let sock1 = { - let (sock1, _) = UnixStream::pair().unwrap(); - sock1 - }; - let mut audio_socket = AudioSocket::new(sock1); - let res = audio_socket.data_ready(256); - //Broken pipe - assert_eq!( - res.expect_err("Result should be an error.").kind(), - io::Error::from_raw_os_error(32).kind(), - "Error should be broken pipe.", - ); - } -} diff --git a/cras/client/libcras/src/cras_client_message.rs b/cras/client/libcras/src/cras_client_message.rs deleted file mode 100644 index c1c5ec5c..00000000 --- a/cras/client/libcras/src/cras_client_message.rs +++ /dev/null @@ -1,200 +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. -use std::{array::TryFromSliceError, convert::TryInto, error, fmt, io, mem, os::unix::io::RawFd}; - -use cras_sys::gen::{ - cras_client_connected, cras_client_message, cras_client_stream_connected, - CRAS_CLIENT_MAX_MSG_SIZE, - CRAS_CLIENT_MESSAGE_ID::{self, *}, -}; -use data_model::DataInit; -use sys_util::ScmSocket; - -use crate::cras_server_socket::CrasServerSocket; -use crate::cras_shm::*; -use crate::cras_stream; - -#[derive(Debug)] -pub enum Error { - IoError(io::Error), - SysUtilError(sys_util::Error), - CrasStreamError(cras_stream::Error), - ArrayTryFromSliceError(TryFromSliceError), - InvalidSize, - MessageTypeError, - MessageNumFdError, - MessageTruncated, - MessageIdError, - MessageFromSliceError, -} - -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::SysUtilError(ref err) => err.fmt(f), - Error::MessageTypeError => write!(f, "Message type error"), - Error::CrasStreamError(ref err) => err.fmt(f), - Error::ArrayTryFromSliceError(ref err) => err.fmt(f), - Error::MessageNumFdError => write!(f, "Message the number of fds is not matched"), - Error::MessageTruncated => write!(f, "Read truncated message"), - Error::MessageIdError => write!(f, "No such id"), - Error::MessageFromSliceError => write!(f, "Message from slice error"), - Error::InvalidSize => write!(f, "Invalid data size"), - } - } -} - -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<TryFromSliceError> for Error { - fn from(err: TryFromSliceError) -> Self { - Error::ArrayTryFromSliceError(err) - } -} - -/// A handled server result from one message sent from CRAS server. -pub enum ServerResult { - /// client_id, CrasServerStateShmFd - Connected(u32, CrasServerStateShmFd), - /// stream_id, header_fd, samples_fd - StreamConnected(u32, CrasAudioShmHeaderFd, CrasShmFd), - DebugInfoReady, -} - -impl ServerResult { - /// Reads and handles one server message and converts `CrasClientMessage` into `ServerResult` - /// with error handling. - /// - /// # Arguments - /// * `server_socket`: A reference to `CrasServerSocket`. - pub fn handle_server_message(server_socket: &CrasServerSocket) -> Result<ServerResult> { - let message = CrasClientMessage::try_new(&server_socket)?; - match message.get_id()? { - CRAS_CLIENT_MESSAGE_ID::CRAS_CLIENT_CONNECTED => { - let cmsg: &cras_client_connected = message.get_message()?; - // CRAS server should return a shared memory area which contains - // `cras_server_state`. - let server_state_fd = unsafe { CrasServerStateShmFd::new(message.fds[0]) }; - Ok(ServerResult::Connected(cmsg.client_id, server_state_fd)) - } - CRAS_CLIENT_MESSAGE_ID::CRAS_CLIENT_STREAM_CONNECTED => { - let cmsg: &cras_client_stream_connected = message.get_message()?; - // CRAS should return two shared memory areas the first which has - // mem::size_of::<cras_audio_shm_header>() bytes, and the second which has - // `samples_shm_size` bytes. - Ok(ServerResult::StreamConnected( - cmsg.stream_id, - // Safe because CRAS ensures that the first fd contains a cras_audio_shm_header - unsafe { CrasAudioShmHeaderFd::new(message.fds[0]) }, - // Safe because CRAS ensures that the second fd has length 'samples_shm_size' - unsafe { CrasShmFd::new(message.fds[1], cmsg.samples_shm_size as usize) }, - )) - } - CRAS_CLIENT_MESSAGE_ID::CRAS_CLIENT_AUDIO_DEBUG_INFO_READY => { - Ok(ServerResult::DebugInfoReady) - } - _ => Err(Error::MessageTypeError), - } - } -} - -// A structure for raw message with fds from CRAS server. -struct CrasClientMessage { - fds: [RawFd; 2], - data: [u8; CRAS_CLIENT_MAX_MSG_SIZE as usize], - len: usize, -} - -/// The default constructor won't be used outside of this file and it's an optimization to prevent -/// having to copy the message data from a temp buffer. -impl Default for CrasClientMessage { - // Initializes fields with default values. - fn default() -> Self { - Self { - fds: [-1; 2], - data: [0; CRAS_CLIENT_MAX_MSG_SIZE as usize], - len: 0, - } - } -} - -impl CrasClientMessage { - // Reads a message from server_socket and checks validity of the read result - fn try_new(server_socket: &CrasServerSocket) -> Result<CrasClientMessage> { - let mut message: Self = Default::default(); - let (len, fd_nums) = server_socket.recv_with_fds(&mut message.data, &mut message.fds)?; - - if len < mem::size_of::<cras_client_message>() { - Err(Error::MessageTruncated) - } else { - message.len = len; - message.check_fd_nums(fd_nums)?; - Ok(message) - } - } - - // Check if `fd nums` of a read result is valid - fn check_fd_nums(&self, fd_nums: usize) -> Result<()> { - match self.get_id()? { - CRAS_CLIENT_CONNECTED => match fd_nums { - 1 => Ok(()), - _ => Err(Error::MessageNumFdError), - }, - CRAS_CLIENT_STREAM_CONNECTED => match fd_nums { - // CRAS should return two shared memory areas the first which has - // mem::size_of::<cras_audio_shm_header>() bytes, and the second which has - // `samples_shm_size` bytes. - 2 => Ok(()), - _ => Err(Error::MessageNumFdError), - }, - CRAS_CLIENT_AUDIO_DEBUG_INFO_READY => match fd_nums { - 0 => Ok(()), - _ => Err(Error::MessageNumFdError), - }, - _ => Err(Error::MessageTypeError), - } - } - - // Gets the message id - fn get_id(&self) -> Result<CRAS_CLIENT_MESSAGE_ID> { - let offset = mem::size_of::<u32>(); - match u32::from_le_bytes(self.data[offset..offset + 4].try_into()?) { - id if id == (CRAS_CLIENT_CONNECTED as u32) => Ok(CRAS_CLIENT_CONNECTED), - id if id == (CRAS_CLIENT_STREAM_CONNECTED as u32) => Ok(CRAS_CLIENT_STREAM_CONNECTED), - id if id == (CRAS_CLIENT_AUDIO_DEBUG_INFO_READY as u32) => { - Ok(CRAS_CLIENT_AUDIO_DEBUG_INFO_READY) - } - _ => Err(Error::MessageIdError), - } - } - - // Gets a reference to the message content - fn get_message<T: DataInit>(&self) -> Result<&T> { - if self.len != mem::size_of::<T>() { - return Err(Error::InvalidSize); - } - T::from_slice(&self.data[..mem::size_of::<T>()]).ok_or(Error::MessageFromSliceError) - } -} diff --git a/cras/client/libcras/src/cras_server_socket.rs b/cras/client/libcras/src/cras_server_socket.rs deleted file mode 100644 index 4a7d9151..00000000 --- a/cras/client/libcras/src/cras_server_socket.rs +++ /dev/null @@ -1,124 +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. -use std::os::unix::io::{AsRawFd, RawFd}; -use std::{io, mem}; - -use cras_sys::gen::{cras_disconnect_stream_message, cras_server_message, CRAS_SERVER_MESSAGE_ID}; -use sys_util::{net::UnixSeqpacket, ScmSocket}; - -use data_model::DataInit; - -/// Server socket type to connect. -pub enum CrasSocketType { - /// A server socket type supports only playback function. - Legacy, - /// A server socket type supports both playback and capture functions. - Unified, -} - -impl CrasSocketType { - fn sock_path(&self) -> &str { - match self { - Self::Legacy => "/run/cras/.cras_socket", - Self::Unified => "/run/cras/.cras_unified", - } - } -} - -/// A socket connecting to the CRAS audio server. -pub struct CrasServerSocket { - socket: UnixSeqpacket, -} - -impl CrasServerSocket { - pub fn new() -> io::Result<CrasServerSocket> { - Self::with_type(CrasSocketType::Legacy) - } - - /// Creates a `CrasServerSocket` with given `CrasSocketType`. - /// - /// # Errors - /// - /// Returns the `io::Error` generated when connecting to the socket on failure. - pub fn with_type(socket_type: CrasSocketType) -> io::Result<CrasServerSocket> { - Ok(CrasServerSocket { - socket: UnixSeqpacket::connect(socket_type.sock_path())?, - }) - } - - /// Sends a sized and packed server messge to the server socket. The message - /// must implement `Sized` and `DataInit`. - /// # Arguments - /// * `message` - A sized and packed message. - /// * `fds` - A slice of fds to send. - /// - /// # Returns - /// * Length of written bytes in `usize`. - /// - /// # Errors - /// Return error if the socket fails to write message to server. - pub fn send_server_message_with_fds<M: Sized + DataInit>( - &self, - message: &M, - fds: &[RawFd], - ) -> io::Result<usize> { - match fds.len() { - 0 => self.socket.send(message.as_slice()), - _ => { - let ioslice = io::IoSlice::new(message.as_slice()); - match self.send_with_fds(&[ioslice], fds) { - Ok(len) => Ok(len), - Err(err) => Err(io::Error::new(io::ErrorKind::Other, format!("{}", err))), - } - } - } - } - - /// Creates a clone of the underlying socket. The returned clone can also be - /// used to communicate with the cras server. - pub fn try_clone(&self) -> io::Result<CrasServerSocket> { - let new_sock = self.socket.try_clone()?; - Ok(CrasServerSocket { socket: new_sock }) - } - - /// Send a message to request disconnection of the given stream. - /// - /// Builds a `cras_disconnect_stream_message` containing `stream_id` and - /// sends it to the server. - /// No response is expected. - /// - /// # Arguments - /// - /// * `stream_id` - The id of the stream that should be disconnected. - /// - /// # Errors - /// - /// * If the message was not written to the server socket successfully. - pub fn disconnect_stream(&self, stream_id: u32) -> io::Result<()> { - let msg_header = cras_server_message { - length: mem::size_of::<cras_disconnect_stream_message>() as u32, - id: CRAS_SERVER_MESSAGE_ID::CRAS_SERVER_DISCONNECT_STREAM, - }; - let server_cmsg = cras_disconnect_stream_message { - header: msg_header, - stream_id, - }; - self.send_server_message_with_fds(&server_cmsg, &[]) - .map(|_| ()) - } -} - -// For using `recv_with_fds` and `send_with_fds`. -impl ScmSocket for CrasServerSocket { - fn socket_fd(&self) -> RawFd { - self.socket.as_raw_fd() - } -} - -// For using `PollContex`. -impl AsRawFd for CrasServerSocket { - fn as_raw_fd(&self) -> RawFd { - self.socket.as_raw_fd() - } -} diff --git a/cras/client/libcras/src/cras_shm.rs b/cras/client/libcras/src/cras_shm.rs deleted file mode 100644 index 05533753..00000000 --- a/cras/client/libcras/src/cras_shm.rs +++ /dev/null @@ -1,1308 +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. -use std::convert::TryFrom; -use std::io; -use std::mem; -use std::os::unix::io::{AsRawFd, RawFd}; -use std::ptr; -use std::ptr::NonNull; -use std::slice; -use std::sync::atomic::{self, Ordering}; -use std::thread; - -use cras_sys::gen::{ - audio_dev_debug_info, audio_stream_debug_info, cras_audio_shm_header, cras_iodev_info, - cras_ionode_info, cras_server_state, CRAS_MAX_IODEVS, CRAS_MAX_IONODES, CRAS_NUM_SHM_BUFFERS, - CRAS_SERVER_STATE_VERSION, CRAS_SHM_BUFFERS_MASK, MAX_DEBUG_DEVS, MAX_DEBUG_STREAMS, -}; -use cras_sys::{ - AudioDebugInfo, AudioDevDebugInfo, AudioStreamDebugInfo, CrasIodevInfo, CrasIonodeInfo, -}; -use data_model::{VolatileRef, VolatileSlice}; -use sys_util::warn; - -/// A structure wrapping a fd which contains a shared `cras_audio_shm_header`. -/// * `shm_fd` - A shared memory fd contains a `cras_audio_shm_header` -pub struct CrasAudioShmHeaderFd { - fd: CrasShmFd, -} - -impl CrasAudioShmHeaderFd { - /// Creates a `CrasAudioShmHeaderFd` by shared memory fd - /// # Arguments - /// * `fd` - A shared memory file descriptor, which will be owned by the resulting structure and - /// the fd will be closed on drop. - /// - /// # Returns - /// A structure wrapping a `CrasShmFd` with the input fd and `size` which equals to - /// the size of `cras_audio_shm_header`. - /// - /// To use this function safely, we need to make sure - /// - The input fd is a valid shared memory fd. - /// - The input shared memory fd won't be used by others. - /// - The shared memory area in the input fd contains a `cras_audio_shm_header`. - pub unsafe fn new(fd: libc::c_int) -> Self { - Self { - fd: CrasShmFd::new(fd, mem::size_of::<cras_audio_shm_header>()), - } - } -} - -/// A wrapper for the raw structure `cras_audio_shm_header` with -/// size information for the separate audio samples shm area and several -/// `VolatileRef` to sub fields for safe access to the header. -pub struct CrasAudioHeader<'a> { - addr: *mut libc::c_void, - /// Size of the buffer for samples in CrasAudioBuffer - samples_len: usize, - used_size: VolatileRef<'a, u32>, - frame_size: VolatileRef<'a, u32>, - read_buf_idx: VolatileRef<'a, u32>, - write_buf_idx: VolatileRef<'a, u32>, - read_offset: [VolatileRef<'a, u32>; CRAS_NUM_SHM_BUFFERS as usize], - write_offset: [VolatileRef<'a, u32>; CRAS_NUM_SHM_BUFFERS as usize], - buffer_offset: [VolatileRef<'a, u64>; CRAS_NUM_SHM_BUFFERS as usize], -} - -// It is safe to send audio buffers between threads as this struct has exclusive ownership of the -// pointers contained in it. -unsafe impl<'a> Send for CrasAudioHeader<'a> {} - -/// An unsafe macro for getting `VolatileRef` for a field from a given NonNull pointer. -/// It Supports -/// - Nested sub-field -/// - Element of an array field -/// -/// To use this macro safely, we need to -/// - Make sure the pointer address is readable and writable for its structure. -/// - Make sure all `VolatileRef`s generated from this macro have exclusive ownership for the same -/// pointer. -#[macro_export] -macro_rules! vref_from_addr { - ($addr:ident, $($field:ident).*) => { - VolatileRef::new(&mut $addr.as_mut().$($field).* as *mut _) - }; - - ($addr:ident, $field:ident[$idx:tt]) => { - VolatileRef::new(&mut $addr.as_mut().$field[$idx] as *mut _) - }; -} - -// Generates error when an index is out of range. -fn index_out_of_range() -> io::Error { - io::Error::new(io::ErrorKind::InvalidInput, "Index out of range.") -} - -impl<'a> CrasAudioHeader<'a> { - // Creates a `CrasAudioHeader` with given `CrasAudioShmHeaderFd` and `samples_len` - fn new(header_fd: CrasAudioShmHeaderFd, samples_len: usize) -> io::Result<Self> { - // Safe because the creator of CrasAudioShmHeaderFd already - // ensured that header_fd contains a cras_audio_shm_header. - let mmap_addr = unsafe { - cras_mmap( - header_fd.fd.size, - libc::PROT_READ | libc::PROT_WRITE, - header_fd.fd.as_raw_fd(), - )? - }; - - let mut addr = NonNull::new(mmap_addr as *mut cras_audio_shm_header) - .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Failed to create header."))?; - - // Safe because we know that mmap_addr (contained in addr) contains a - // cras_audio_shm_header, and the mapped area will be exclusively - // owned by this struct. - unsafe { - Ok(CrasAudioHeader { - addr: addr.as_ptr() as *mut libc::c_void, - samples_len, - used_size: vref_from_addr!(addr, config.used_size), - frame_size: vref_from_addr!(addr, config.frame_bytes), - read_buf_idx: vref_from_addr!(addr, read_buf_idx), - write_buf_idx: vref_from_addr!(addr, write_buf_idx), - read_offset: [ - vref_from_addr!(addr, read_offset[0]), - vref_from_addr!(addr, read_offset[1]), - ], - write_offset: [ - vref_from_addr!(addr, write_offset[0]), - vref_from_addr!(addr, write_offset[1]), - ], - buffer_offset: [ - vref_from_addr!(addr, buffer_offset[0]), - vref_from_addr!(addr, buffer_offset[1]), - ], - }) - } - } - - /// Calculates the length of a buffer with the given offset. This length will - /// be `used_size`, unless the offset is closer than `used_size` to the end - /// of samples, in which case the length will be as long as possible. - /// - /// If that buffer length is invalid (too small to hold a frame of audio data), - /// then returns an error. - /// The returned buffer length will be rounded down to a multiple of `frame_size`. - fn buffer_len_from_offset(&self, offset: usize) -> io::Result<usize> { - if offset > self.samples_len { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - format!( - "Buffer offset {} exceeds the length of samples area ({}).", - offset, self.samples_len - ), - )); - } - - let used_size = self.get_used_size(); - let frame_size = self.get_frame_size(); - - // We explicitly allow a buffer shorter than used_size, but only - // at the end of the samples area. - // This is useful if we're playing a file where the number of samples is - // not a multiple of used_size (meaning the length of the samples area - // won't be either). Then, the last buffer played will be smaller than - // used_size. - let mut buffer_length = used_size.min(self.samples_len - offset); - if buffer_length < frame_size { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - format!( - "Buffer offset {} gives buffer length {} smaller than frame size {}.", - offset, buffer_length, frame_size - ), - )); - } - - // Round buffer_length down to a multiple of frame size - buffer_length = buffer_length / frame_size * frame_size; - Ok(buffer_length) - } - - /// Gets the base of the write buffer and the writable length (rounded to `frame_size`). - /// Does not take into account the write offset. - /// - /// # Returns - /// - /// * (`usize`, `usize`) - write buffer base as an offset from the start of - /// the samples area and buffer length in bytes. - pub fn get_write_offset_and_len(&self) -> io::Result<(usize, usize)> { - let idx = self.get_write_buf_idx() as usize; - let offset = self.get_buffer_offset(idx)?; - let len = self.buffer_len_from_offset(offset)?; - - Ok((offset, len)) - } - - /// Gets the buffer offset of the read buffer. - /// - /// # Returns - /// - /// * `usize` - read offset in bytes - pub fn get_read_buffer_offset(&self) -> io::Result<usize> { - let idx = self.get_read_buf_idx() as usize; - self.get_buffer_offset(idx) - } - - /// Gets the offset of a buffer from the start of samples. - /// - /// # Arguments - /// `index` - 0 <= `index` < `CRAS_NUM_SHM_BUFFERS`. The index of the buffer - /// for which we want the `buffer_offset`. - /// - /// # Returns - /// * `usize` - buffer offset in bytes - fn get_buffer_offset(&self, idx: usize) -> io::Result<usize> { - let buffer_offset = self - .buffer_offset - .get(idx) - .ok_or_else(index_out_of_range)? - .load() as usize; - self.check_buffer_offset(idx, buffer_offset)?; - Ok(buffer_offset) - } - - /// Gets the number of bytes per frame from the shared memory structure. - /// - /// # Returns - /// - /// * `usize` - Number of bytes per frame - pub fn get_frame_size(&self) -> usize { - self.frame_size.load() as usize - } - - /// Gets the max size in bytes of each shared memory buffer within - /// the samples area. - /// - /// # Returns - /// - /// * `usize` - Value of `used_size` fetched from the shared memory header. - pub fn get_used_size(&self) -> usize { - self.used_size.load() as usize - } - - /// Gets the index of the current written buffer. - /// - /// # Returns - /// `u32` - the returned index is less than `CRAS_NUM_SHM_BUFFERS`. - fn get_write_buf_idx(&self) -> u32 { - self.write_buf_idx.load() & CRAS_SHM_BUFFERS_MASK - } - - fn get_read_buf_idx(&self) -> u32 { - self.read_buf_idx.load() & CRAS_SHM_BUFFERS_MASK - } - - /// Switches the written buffer. - fn switch_write_buf_idx(&mut self) { - self.write_buf_idx - .store(self.get_write_buf_idx() as u32 ^ 1u32) - } - - /// Switches the buffer to read. - fn switch_read_buf_idx(&mut self) { - self.read_buf_idx - .store(self.get_read_buf_idx() as u32 ^ 1u32) - } - - /// Checks if the offset value for setting write_offset or read_offset is - /// out of range or not. - /// - /// # Arguments - /// `idx` - The index of the buffer for which we're checking the offset. - /// `offset` - 0 <= `offset` <= `used_size` && `buffer_offset[idx]` + `offset` <= - /// `samples_len`. Writable or readable size equals to 0 when offset equals - /// to `used_size`. - /// - /// # Errors - /// Returns an error if `offset` is out of range or if idx is not a valid - /// buffer idx. - fn check_rw_offset(&self, idx: usize, offset: u32) -> io::Result<()> { - let buffer_len = self.buffer_len_from_offset(self.get_buffer_offset(idx)?)?; - if offset as usize > buffer_len { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - format!( - "Offset {} is larger than buffer size {}.", - offset, buffer_len - ), - )); - } - - Ok(()) - } - - /// Sets `write_offset[idx]` to the count of written bytes. - /// - /// # Arguments - /// `idx` - 0 <= `idx` < `CRAS_NUM_SHM_BUFFERS` - /// `offset` - 0 <= `offset` <= `used_size` && `offset` + `used_size` <= - /// `samples_len`. Writable size equals to 0 when offset equals to - /// `used_size`. - /// - /// # Errors - /// Returns an error if `offset` is out of range. - fn set_write_offset(&mut self, idx: usize, offset: u32) -> io::Result<()> { - self.check_rw_offset(idx, offset)?; - let write_offset = self.write_offset.get(idx).ok_or_else(index_out_of_range)?; - write_offset.store(offset); - Ok(()) - } - - /// Sets `read_offset[idx]` to count of written bytes. - /// - /// # Arguments - /// `idx` - 0 <= `idx` < `CRAS_NUM_SHM_BUFFERS` - /// `offset` - 0 <= `offset` <= `used_size` && `offset` + `used_size` <= - /// `samples_len`. Readable size equals to 0 when offset equals to - /// `used_size`. - /// - /// # Errors - /// Returns error if index out of range. - fn set_read_offset(&mut self, idx: usize, offset: u32) -> io::Result<()> { - self.check_rw_offset(idx, offset)?; - let read_offset = self.read_offset.get(idx).ok_or_else(index_out_of_range)?; - read_offset.store(offset); - Ok(()) - } - - /// Check that `offset` is a valid buffer offset for the buffer at `idx` - /// An offset is not valid if it is - /// * outside of the samples area - /// * overlaps some other buffer `[other_offset, other_offset + used_size)` - /// * is close enough to the end of the samples area that the buffer would - /// be shorter than `frame_size`. - fn check_buffer_offset(&self, idx: usize, offset: usize) -> io::Result<()> { - let start = offset; - let end = start + self.buffer_len_from_offset(start)?; - - let other_idx = (idx ^ 1) as usize; - let other_start = self - .buffer_offset - .get(other_idx) - .ok_or_else(index_out_of_range)? - .load() as usize; - let other_end = other_start + self.buffer_len_from_offset(other_start)?; - if start < other_end && other_start < end { - // Special case: occasionally we get the same buffer offset twice - // from the intel8x0 kernel driver in crosvm's AC97 device, and we - // don't want to crash in that case. - if start == other_start && end == other_end { - warn!( - "Setting buffer {} to same index/offset as buffer {}, [{}, {})", - idx, other_idx, other_start, other_end - ); - } else { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - format!( - "Setting buffer {} to [{}, {}) overlaps buffer {} at [{}, {})", - idx, start, end, other_idx, other_start, other_end, - ), - )); - } - } - Ok(()) - } - - /// Sets the location of the audio buffer `idx` within the samples area to - /// `offset`, so that CRAS will read/write samples for that buffer from that - /// offset. - /// - /// # Arguments - /// `idx` - 0 <= `idx` < `CRAS_NUM_SHM_BUFFERS` - /// `offset` - 0 <= `offset` && `offset` + `frame_size` <= `samples_len` - /// - /// # Errors - /// If `idx` is out of range - /// If the offset is invalid, which can happen if `offset` is - /// * outside of the samples area - /// * overlaps some other buffer `[other_offset, other_offset + used_size)` - /// * is close enough to the end of the samples area that the buffer would - /// be shorter than `frame_size`. - pub fn set_buffer_offset(&mut self, idx: usize, offset: usize) -> io::Result<()> { - self.check_buffer_offset(idx, offset)?; - - let buffer_offset = self.buffer_offset.get(idx).ok_or_else(index_out_of_range)?; - buffer_offset.store(offset as u64); - Ok(()) - } - - /// Commits written frames by switching the current buffer to the other one - /// after samples are ready and indexes of current buffer are all set. - /// - Sets `write_offset` of current buffer to `frame_count * frame_size` - /// - Sets `read_offset` of current buffer to `0`. - /// - /// # Arguments - /// - /// * `frame_count` - Number of frames written to the current buffer - /// - /// # Errors - /// - /// * Returns error if `frame_count` is larger than buffer size - /// - /// This function is safe because we switch `write_buf_idx` after letting - /// `write_offset` and `read_offset` ready and we read / write shared memory - /// variables with volatile operations. - pub fn commit_written_frames(&mut self, frame_count: u32) -> io::Result<()> { - // Uses `u64` to prevent possible overflow - let byte_count = frame_count as u64 * self.get_frame_size() as u64; - if byte_count > self.get_used_size() as u64 { - Err(io::Error::new( - io::ErrorKind::InvalidInput, - "frame_count * frame_size is larger than used_size", - )) - } else { - let idx = self.get_write_buf_idx() as usize; - // Sets `write_offset` of current buffer to frame_count * frame_size - self.set_write_offset(idx, byte_count as u32)?; - // Sets `read_offset` of current buffer to `0`. - self.set_read_offset(idx, 0)?; - // Switch to the other buffer - self.switch_write_buf_idx(); - Ok(()) - } - } - - /// Get readable frames in current buffer. - /// - /// # Returns - /// - /// * `usize` - number of readable frames. - /// - /// # Errors - /// - /// Returns error if index out of range. - pub fn get_readable_frames(&self) -> io::Result<usize> { - let idx = self.get_read_buf_idx() as usize; - let read_offset = self.read_offset.get(idx).ok_or_else(index_out_of_range)?; - let write_offset = self.write_offset.get(idx).ok_or_else(index_out_of_range)?; - let nframes = - (write_offset.load() as i32 - read_offset.load() as i32) / self.get_frame_size() as i32; - if nframes < 0 { - Ok(0) - } else { - Ok(nframes as usize) - } - } - - /// Commit read frames from reader, . - /// - Sets `read_offset` of current buffer to `read_offset + frame_count * frame_size`. - /// If `read_offset` is larger than or equal to `write_offset`, then - /// - Sets `read_offset` and `write_offset` to `0` and switch `read_buf_idx`. - /// - /// # Arguments - /// - /// * `frame_count` - Read frames in current read buffer. - /// - /// # Errors - /// - /// Returns error if index out of range. - pub fn commit_read_frames(&mut self, frame_count: u32) -> io::Result<()> { - let idx = self.get_read_buf_idx() as usize; - let read_offset = self.read_offset.get(idx).ok_or_else(index_out_of_range)?; - let write_offset = self.write_offset.get(idx).ok_or_else(index_out_of_range)?; - read_offset.store(read_offset.load() + frame_count * self.get_frame_size() as u32); - if read_offset.load() >= write_offset.load() { - read_offset.store(0); - write_offset.store(0); - self.switch_read_buf_idx(); - } - Ok(()) - } -} - -impl<'a> Drop for CrasAudioHeader<'a> { - fn drop(&mut self) { - // Safe because all references must be gone by the time drop is called. - unsafe { - libc::munmap(self.addr as *mut _, mem::size_of::<cras_audio_shm_header>()); - } - } -} - -// To use this safely, we need to make sure -// - The given fd contains valid space which is larger than `len` + `offset` -unsafe fn cras_mmap_offset( - len: usize, - prot: libc::c_int, - fd: libc::c_int, - offset: usize, -) -> io::Result<*mut libc::c_void> { - if offset > libc::off_t::max_value() as usize { - return Err(io::Error::new( - io::ErrorKind::InvalidInput, - "Requested offset is out of range of `libc::off_t`.", - )); - } - // It's safe because we handle its returned results. - match libc::mmap( - ptr::null_mut(), - len, - prot, - libc::MAP_SHARED, - fd, - offset as libc::off_t, - ) { - libc::MAP_FAILED => Err(io::Error::last_os_error()), - shm_ptr => Ok(shm_ptr), - } -} - -// To use this safely, we need to make sure -// - The given fd contains valid space which is larger than `len` -unsafe fn cras_mmap( - len: usize, - prot: libc::c_int, - fd: libc::c_int, -) -> io::Result<*mut libc::c_void> { - cras_mmap_offset(len, prot, fd, 0) -} - -/// An unsafe macro for getting a `VolatileSlice` representing an entire array -/// field from a given NonNull pointer. -/// -/// To use this macro safely, we need to -/// - Make sure the pointer address is readable and writeable for its struct. -/// - Make sure all `VolatileSlice`s generated from this macro have exclusive ownership for the same -/// pointer. -/// - Make sure the length of the array field is non-zero. -#[macro_export] -macro_rules! vslice_from_addr { - ($addr:ident, $($field:ident).*) => {{ - let ptr = &mut $addr.as_mut().$($field).* as *mut _ as *mut u8; - let size = std::mem::size_of_val(&$addr.as_mut().$($field).*); - VolatileSlice::from_raw_parts(ptr, size) - }}; -} - -/// A structure that points to RO shared memory area - `cras_server_state` -/// The structure is created from a shared memory fd which contains the structure. -#[derive(Debug)] -pub struct CrasServerState<'a> { - addr: *mut libc::c_void, - volume: VolatileRef<'a, u32>, - mute: VolatileRef<'a, i32>, - num_output_devs: VolatileRef<'a, u32>, - output_devs: VolatileSlice<'a>, - num_input_devs: VolatileRef<'a, u32>, - input_devs: VolatileSlice<'a>, - num_output_nodes: VolatileRef<'a, u32>, - num_input_nodes: VolatileRef<'a, u32>, - output_nodes: VolatileSlice<'a>, - input_nodes: VolatileSlice<'a>, - update_count: VolatileRef<'a, u32>, - debug_info_num_devs: VolatileRef<'a, u32>, - debug_info_devs: VolatileSlice<'a>, - debug_info_num_streams: VolatileRef<'a, u32>, - debug_info_streams: VolatileSlice<'a>, -} - -// It is safe to send server_state between threads as this struct has exclusive -// ownership of the shared memory area contained in it. -unsafe impl<'a> Send for CrasServerState<'a> {} - -impl<'a> CrasServerState<'a> { - /// Create a CrasServerState - pub fn try_new(state_fd: CrasServerStateShmFd) -> io::Result<Self> { - // Safe because the creator of CrasServerStateShmFd already - // ensured that state_fd contains a cras_server_state. - let mmap_addr = - unsafe { cras_mmap(state_fd.fd.size, libc::PROT_READ, state_fd.fd.as_raw_fd())? }; - - let mut addr = NonNull::new(mmap_addr as *mut cras_server_state).ok_or_else(|| { - io::Error::new(io::ErrorKind::Other, "Failed to create CrasServerState.") - })?; - - // Safe because we know that addr is a non-null pointer to cras_server_state. - let state_version = unsafe { vref_from_addr!(addr, state_version) }; - if state_version.load() != CRAS_SERVER_STATE_VERSION { - return Err(io::Error::new( - io::ErrorKind::Other, - format!( - "CrasServerState version {} does not match expected version {}", - state_version.load(), - CRAS_SERVER_STATE_VERSION - ), - )); - } - - // Safe because we know that mmap_addr (contained in addr) contains a - // cras_server_state, and the mapped area will be exclusively - // owned by this struct. - unsafe { - Ok(CrasServerState { - addr: addr.as_ptr() as *mut libc::c_void, - volume: vref_from_addr!(addr, volume), - mute: vref_from_addr!(addr, mute), - num_output_devs: vref_from_addr!(addr, num_output_devs), - num_input_devs: vref_from_addr!(addr, num_input_devs), - output_devs: vslice_from_addr!(addr, output_devs), - input_devs: vslice_from_addr!(addr, input_devs), - num_output_nodes: vref_from_addr!(addr, num_output_nodes), - num_input_nodes: vref_from_addr!(addr, num_input_nodes), - output_nodes: vslice_from_addr!(addr, output_nodes), - input_nodes: vslice_from_addr!(addr, input_nodes), - update_count: vref_from_addr!(addr, update_count), - debug_info_num_devs: vref_from_addr!(addr, audio_debug_info.num_devs), - debug_info_devs: vslice_from_addr!(addr, audio_debug_info.devs), - debug_info_num_streams: vref_from_addr!(addr, audio_debug_info.num_streams), - debug_info_streams: vslice_from_addr!(addr, audio_debug_info.streams), - }) - } - } - - /// Gets the system volume. - /// - /// Read the current value for system volume from shared memory. - pub fn get_system_volume(&self) -> u32 { - self.volume.load() - } - - /// Gets the system mute. - /// - /// Read the current value for system mute from shared memory. - pub fn get_system_mute(&self) -> bool { - self.mute.load() != 0 - } - - /// Runs a closure safely such that it can be sure that the server state - /// was not updated during the read. - /// This can be used for an "atomic" read of non-atomic data from the - /// state shared memory. - fn synchronized_state_read<F, T>(&self, mut func: F) -> T - where - F: FnMut() -> T, - { - // Waits until the server has completed a state update before returning - // the current update count. - let begin_server_state_read = || -> u32 { - loop { - let update_count = self.update_count.load(); - if update_count % 2 == 0 { - atomic::fence(Ordering::Acquire); - return update_count; - } else { - thread::yield_now(); - } - } - }; - - // Checks that the update count has not changed since the start - // of the server state read. - let end_server_state_read = |count: u32| -> bool { - let result = count == self.update_count.load(); - atomic::fence(Ordering::Release); - result - }; - - // Get the state's update count and run the provided closure. - // If the update count has not changed once the closure is finished, - // return the result, otherwise repeat the process. - loop { - let update_count = begin_server_state_read(); - let result = func(); - if end_server_state_read(update_count) { - return result; - } - } - } - - /// Gets a list of output devices - /// - /// Read a list of the currently attached output devices from shared memory. - pub fn output_devices(&self) -> impl Iterator<Item = CrasIodevInfo> { - let mut devs: Vec<cras_iodev_info> = vec![Default::default(); CRAS_MAX_IODEVS as usize]; - let num_devs = self.synchronized_state_read(|| { - self.output_devs.copy_to(&mut devs); - self.num_output_devs.load() - }); - devs.into_iter() - .take(num_devs as usize) - .map(CrasIodevInfo::from) - } - - /// Gets a list of input devices - /// - /// Read a list of the currently attached input devices from shared memory. - pub fn input_devices(&self) -> impl Iterator<Item = CrasIodevInfo> { - let mut devs: Vec<cras_iodev_info> = vec![Default::default(); CRAS_MAX_IODEVS as usize]; - let num_devs = self.synchronized_state_read(|| { - self.input_devs.copy_to(&mut devs); - self.num_input_devs.load() - }); - devs.into_iter() - .take(num_devs as usize) - .map(CrasIodevInfo::from) - } - - /// Gets a list of output nodes - /// - /// Read a list of the currently attached output nodes from shared memory. - pub fn output_nodes(&self) -> impl Iterator<Item = CrasIonodeInfo> { - let mut nodes: Vec<cras_ionode_info> = vec![Default::default(); CRAS_MAX_IONODES as usize]; - let num_nodes = self.synchronized_state_read(|| { - self.output_nodes.copy_to(&mut nodes); - self.num_output_nodes.load() - }); - nodes - .into_iter() - .take(num_nodes as usize) - .map(CrasIonodeInfo::from) - } - - /// Gets a list of input nodes - /// - /// Read a list of the currently attached input nodes from shared memory. - pub fn input_nodes(&self) -> impl Iterator<Item = CrasIonodeInfo> { - let mut nodes: Vec<cras_ionode_info> = vec![Default::default(); CRAS_MAX_IONODES as usize]; - let num_nodes = self.synchronized_state_read(|| { - self.input_nodes.copy_to(&mut nodes); - self.num_input_nodes.load() - }); - nodes - .into_iter() - .take(num_nodes as usize) - .map(CrasIonodeInfo::from) - } - - /// Get audio debug info - /// - /// Loads the server's audio_debug_info struct and converts it into an - /// idiomatic rust representation. - /// - /// # Errors - /// * If any of the stream debug information structs are invalid. - pub fn get_audio_debug_info(&self) -> Result<AudioDebugInfo, cras_sys::Error> { - let mut devs: Vec<audio_dev_debug_info> = vec![Default::default(); MAX_DEBUG_DEVS as usize]; - let mut streams: Vec<audio_stream_debug_info> = - vec![Default::default(); MAX_DEBUG_STREAMS as usize]; - let (num_devs, num_streams) = self.synchronized_state_read(|| { - self.debug_info_devs.copy_to(&mut devs); - self.debug_info_streams.copy_to(&mut streams); - ( - self.debug_info_num_devs.load(), - self.debug_info_num_streams.load(), - ) - }); - let dev_info = devs - .into_iter() - .take(num_devs as usize) - .map(AudioDevDebugInfo::from) - .collect(); - let stream_info = streams - .into_iter() - .take(num_streams as usize) - .map(AudioStreamDebugInfo::try_from) - .collect::<Result<Vec<_>, _>>()?; - Ok(AudioDebugInfo::new(dev_info, stream_info)) - } -} - -impl<'a> Drop for CrasServerState<'a> { - /// Call `munmap` for `addr`. - fn drop(&mut self) { - unsafe { - // Safe because all references must be gone by the time drop is called. - libc::munmap(self.addr, mem::size_of::<cras_server_state>()); - } - } -} - -/// A structure holding the mapped shared memory area used to exchange -/// samples with CRAS. The shared memory is owned exclusively by this structure, -/// and will be cleaned up on drop. -/// * `addr` - The address of the mapped shared memory. -/// * `len` - Length of the mapped shared memory in bytes. -pub struct CrasAudioBuffer { - addr: *mut u8, - len: usize, -} - -// It is safe to send audio buffers between threads as this struct has exclusive ownership of the -// shared memory area contained in it. -unsafe impl Send for CrasAudioBuffer {} - -impl CrasAudioBuffer { - fn new(samples_fd: CrasShmFd) -> io::Result<Self> { - // This is safe because we checked that the size of the shm in samples_fd - // was at least samples_fd.size when it was created. - let addr = unsafe { - cras_mmap( - samples_fd.size, - libc::PROT_READ | libc::PROT_WRITE, - samples_fd.as_raw_fd(), - )? as *mut u8 - }; - Ok(Self { - addr, - len: samples_fd.size, - }) - } - - /// Provides a mutable slice to be filled with audio samples. - pub fn get_buffer(&mut self) -> &mut [u8] { - // This is safe because it takes a mutable reference to self, and there can only be one - // taken at a time. Although this is shared memory, the reader side must have it mapped as - // read only. - unsafe { slice::from_raw_parts_mut(self.addr, self.len) } - } -} - -impl Drop for CrasAudioBuffer { - fn drop(&mut self) { - // Safe because all references must be gone by the time drop is called. - unsafe { - libc::munmap(self.addr as *mut _, self.len); - } - } -} - -/// Creates header and buffer from given shared memory fds. -pub fn create_header_and_buffers<'a>( - header_fd: CrasAudioShmHeaderFd, - samples_fd: CrasShmFd, -) -> io::Result<(CrasAudioHeader<'a>, CrasAudioBuffer)> { - let header = CrasAudioHeader::new(header_fd, samples_fd.size)?; - let buffer = CrasAudioBuffer::new(samples_fd)?; - - Ok((header, buffer)) -} - -/// Creates header from header shared memory fds. Use this function -/// when mapping the samples shm is not necessary, for instance with a -/// client-provided shm stream. -pub fn create_header<'a>( - header_fd: CrasAudioShmHeaderFd, - samples_len: usize, -) -> io::Result<CrasAudioHeader<'a>> { - Ok(CrasAudioHeader::new(header_fd, samples_len)?) -} - -/// A structure wrapping a fd which contains a shared memory area and its size. -/// * `fd` - The shared memory file descriptor, a `libc::c_int`. -/// * `size` - Size of the shared memory area. -pub struct CrasShmFd { - fd: libc::c_int, - size: usize, -} - -impl CrasShmFd { - /// Creates a `CrasShmFd` by shared memory fd and size - /// # Arguments - /// * `fd` - A shared memory file descriptor, which will be owned by the resulting structure and - /// the fd will be closed on drop. - /// * `size` - Size of the shared memory. - /// - /// # Returns - /// * `CrasShmFd` - Wrap the input arguments without doing anything. - /// - /// To use this function safely, we need to make sure - /// - The input fd is a valid shared memory fd. - /// - The input shared memory fd won't be used by others. - /// - The input fd contains memory size larger than `size`. - pub unsafe fn new(fd: libc::c_int, size: usize) -> CrasShmFd { - CrasShmFd { fd, size } - } -} - -impl AsRawFd for CrasShmFd { - fn as_raw_fd(&self) -> RawFd { - self.fd - } -} - -impl Drop for CrasShmFd { - fn drop(&mut self) { - // It's safe here if we make sure - // - the input fd is valid and - // - `CrasShmFd` is the only owner - // in `new` function - unsafe { - libc::close(self.fd); - } - } -} - -/// A structure wrapping a fd which contains a shared `cras_server_state`. -/// * `shm_fd` - A shared memory fd contains a `cras_server_state` -pub struct CrasServerStateShmFd { - fd: CrasShmFd, -} - -impl CrasServerStateShmFd { - /// Creates a `CrasServerStateShmFd` by shared memory fd - /// # Arguments - /// * `fd` - A shared memory file descriptor, which will be owned by the resulting structure and - /// the fd will be closed on drop. - /// - /// # Returns - /// A structure wrapping a `CrasShmFd` with the input fd and `size` which equals to - /// the size of `cras_server_sate`. - /// - /// To use this function safely, we need to make sure - /// - The input fd is a valid shared memory fd. - /// - The input shared memory fd won't be used by others. - /// - The shared memory area in the input fd contains a `cras_server_state`. - pub unsafe fn new(fd: libc::c_int) -> Self { - Self { - fd: CrasShmFd::new(fd, mem::size_of::<cras_server_state>()), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use std::fs::File; - use std::os::unix::io::IntoRawFd; - use std::sync::{Arc, Mutex}; - use std::thread; - use sys_util::{kernel_has_memfd, SharedMemory}; - - #[test] - fn cras_audio_header_switch_test() { - if !kernel_has_memfd() { - return; - } - let mut header = create_cras_audio_header(20); - assert_eq!(0, header.get_write_buf_idx()); - header.switch_write_buf_idx(); - assert_eq!(1, header.get_write_buf_idx()); - } - - #[test] - fn cras_audio_header_write_offset_test() { - if !kernel_has_memfd() { - return; - } - let mut header = create_cras_audio_header(20); - header.frame_size.store(2); - header.used_size.store(5); - header.set_buffer_offset(0, 12).unwrap(); - - assert_eq!(0, header.write_offset[0].load()); - // Index out of bound - assert!(header.set_write_offset(2, 5).is_err()); - // Offset out of bound - // Buffer length is 4, since that's the largest multiple of frame_size - // less than used_size. - assert!(header.set_write_offset(0, 6).is_err()); - assert_eq!(0, header.write_offset[0].load()); - assert!(header.set_write_offset(0, 5).is_err()); - assert_eq!(0, header.write_offset[0].load()); - assert!(header.set_write_offset(0, 4).is_ok()); - assert_eq!(4, header.write_offset[0].load()); - } - - #[test] - fn cras_audio_header_read_offset_test() { - if !kernel_has_memfd() { - return; - } - let mut header = create_cras_audio_header(20); - header.frame_size.store(2); - header.used_size.store(5); - header.set_buffer_offset(0, 12).unwrap(); - - assert_eq!(0, header.read_offset[0].load()); - // Index out of bound - assert!(header.set_read_offset(2, 5).is_err()); - // Offset out of bound - // Buffer length is 4, since that's the largest multiple of frame_size - // less than used_size. - assert!(header.set_read_offset(0, 6).is_err()); - assert_eq!(0, header.read_offset[0].load()); - assert!(header.set_read_offset(0, 5).is_err()); - assert_eq!(0, header.read_offset[0].load()); - assert!(header.set_read_offset(0, 4).is_ok()); - assert_eq!(4, header.read_offset[0].load()); - } - - #[test] - fn cras_audio_header_commit_written_frame_test() { - if !kernel_has_memfd() { - return; - } - let mut header = create_cras_audio_header(20); - header.frame_size.store(2); - header.used_size.store(10); - header.read_offset[0].store(10); - header.set_buffer_offset(0, 10).unwrap(); - - assert!(header.commit_written_frames(5).is_ok()); - assert_eq!(header.write_offset[0].load(), 10); - assert_eq!(header.read_offset[0].load(), 0); - assert_eq!(header.write_buf_idx.load(), 1); - } - - #[test] - fn cras_audio_header_get_readable_frames_test() { - if !kernel_has_memfd() { - return; - } - let header = create_cras_audio_header(20); - header.frame_size.store(2); - header.used_size.store(10); - header.read_offset[0].store(2); - header.write_offset[0].store(10); - let frames = header - .get_readable_frames() - .expect("Failed to get readable frames."); - assert_eq!(frames, 4); - } - - #[test] - fn cras_audio_header_commit_read_frames_test() { - if !kernel_has_memfd() { - return; - } - let mut header = create_cras_audio_header(20); - header.frame_size.store(2); - header.used_size.store(10); - header.read_offset[0].store(2); - header.write_offset[0].store(10); - header - .commit_read_frames(3) - .expect("Failed to commit read frames."); - assert_eq!(header.get_read_buf_idx(), 0); - assert_eq!(header.read_offset[0].load(), 8); - - header - .commit_read_frames(1) - .expect("Failed to commit read frames."); - // Read buffer should be switched - assert_eq!(header.get_read_buf_idx(), 1); - assert_eq!(header.read_offset[0].load(), 0); - assert_eq!(header.read_offset[0].load(), 0); - } - - #[test] - fn cras_audio_header_get_write_offset_and_len() { - if !kernel_has_memfd() { - return; - } - let header = create_cras_audio_header(30); - header.frame_size.store(2); - header.used_size.store(10); - header.write_buf_idx.store(0); - header.read_offset[0].store(0); - header.write_offset[0].store(0); - header.buffer_offset[0].store(0); - - header.read_buf_idx.store(1); - header.read_offset[1].store(0); - header.write_offset[1].store(0); - header.buffer_offset[1].store(10); - - // standard offsets and lens - let (offset, len) = header.get_write_offset_and_len().unwrap(); - assert_eq!(offset, 0); - assert_eq!(len, 10); - - header.write_buf_idx.store(1); - header.read_buf_idx.store(0); - let (offset, len) = header.get_write_offset_and_len().unwrap(); - assert_eq!(offset, 10); - assert_eq!(len, 10); - - // relocate buffer offsets - header.buffer_offset[1].store(16); - let (offset, len) = header.get_write_offset_and_len().unwrap(); - assert_eq!(offset, 16); - assert_eq!(len, 10); - - header.buffer_offset[0].store(5); - header.write_buf_idx.store(0); - let (offset, len) = header.get_write_offset_and_len().unwrap(); - assert_eq!(offset, 5); - assert_eq!(len, 10); - - header.write_buf_idx.store(0); - header.buffer_offset[0].store(2); - header.read_buf_idx.store(1); - header.buffer_offset[1].store(10); - let result = header.get_write_offset_and_len(); - // Should be an error as write buffer would overrun into other buffer. - assert!(result.is_err()); - - header.buffer_offset[0].store(24); - header.buffer_offset[1].store(10); - let (offset, len) = header.get_write_offset_and_len().unwrap(); - // Should be ok since we're only running up against the end of samples. - assert_eq!(offset, 24); - assert_eq!(len, 6); - - header.buffer_offset[0].store(25); - let (offset, len) = header.get_write_offset_and_len().unwrap(); - // Should be ok, but we'll truncate len to frame_size. - assert_eq!(offset, 25); - assert_eq!(len, 4); - - header.buffer_offset[0].store(29); - let result = header.get_write_offset_and_len(); - // Should be an error as buffer is smaller than frame_size. - assert!(result.is_err()); - } - - #[test] - fn cras_audio_header_set_buffer_offset() { - if !kernel_has_memfd() { - return; - } - let mut header = create_cras_audio_header(30); - header.frame_size.store(2); - header.used_size.store(10); - header.write_buf_idx.store(0); - header.read_offset[0].store(0); - header.write_offset[0].store(0); - header.buffer_offset[0].store(0); - - header.read_buf_idx.store(1); - header.read_offset[1].store(0); - header.write_offset[1].store(0); - header.buffer_offset[1].store(10); - - // Setting buffer_offset to exactly overlap with other buffer is okay - assert!(header.set_buffer_offset(0, 10).is_ok()); - - // Setting buffer_offset to partially overlap other buffer is not okay - assert!(header.set_buffer_offset(0, 9).is_err()); - - header.buffer_offset[0].store(0); - header.write_offset[1].store(8); - // With samples, it's still an error. - assert!(header.set_buffer_offset(0, 9).is_err()); - - // Setting the offset past the end of the other buffer is okay - assert!(header.set_buffer_offset(0, 20).is_ok()); - - // Setting buffer offset such that buffer length is less than used_size - // is okay, but only at the end of the samples area. - assert!(header.set_buffer_offset(0, 21).is_ok()); - assert!(header.set_buffer_offset(0, 27).is_ok()); - - // It's not okay if we get a buffer with length less than frame_size. - assert!(header.set_buffer_offset(0, 29).is_err()); - assert!(header.set_buffer_offset(0, 30).is_err()); - - // If we try to overlap another buffer with that other buffer at the end, - // it's not okay, unless it's the exact same index. - assert!(header.set_buffer_offset(1, 25).is_err()); - assert!(header.set_buffer_offset(1, 27).is_ok()); - assert!(header.set_buffer_offset(1, 28).is_err()); - - // Setting buffer offset past the end of samples is an error. - assert!(header.set_buffer_offset(0, 33).is_err()); - } - - #[test] - fn create_header_and_buffers_test() { - if !kernel_has_memfd() { - return; - } - let header_fd = cras_audio_header_fd(); - let samples_fd = cras_audio_samples_fd(20); - let res = create_header_and_buffers(header_fd, samples_fd); - res.expect("Failed to create header and buffer."); - } - - fn create_shm(size: usize) -> File { - let mut shm = SharedMemory::new(None).expect("failed to create shm"); - shm.set_size(size as u64).expect("failed to set shm size"); - shm.into() - } - - fn create_cras_audio_header<'a>(samples_len: usize) -> CrasAudioHeader<'a> { - CrasAudioHeader::new(cras_audio_header_fd(), samples_len).unwrap() - } - - fn cras_audio_header_fd() -> CrasAudioShmHeaderFd { - let size = mem::size_of::<cras_audio_shm_header>(); - let shm = create_shm(size); - unsafe { CrasAudioShmHeaderFd::new(shm.into_raw_fd()) } - } - - fn cras_audio_samples_fd(size: usize) -> CrasShmFd { - let shm = create_shm(size); - unsafe { CrasShmFd::new(shm.into_raw_fd(), size) } - } - - #[test] - fn cras_mmap_pass() { - if !kernel_has_memfd() { - return; - } - let shm = create_shm(100); - let rc = unsafe { cras_mmap(10, libc::PROT_READ, shm.as_raw_fd()) }; - assert!(rc.is_ok()); - unsafe { libc::munmap(rc.unwrap(), 10) }; - } - - #[test] - fn cras_mmap_failed() { - if !kernel_has_memfd() { - return; - } - let rc = unsafe { cras_mmap(10, libc::PROT_READ, -1) }; - assert!(rc.is_err()); - } - - #[test] - fn cras_server_state() { - let size = mem::size_of::<cras_server_state>(); - let shm = create_shm(size); - unsafe { - let addr = cras_mmap(size, libc::PROT_WRITE, shm.as_raw_fd()) - .expect("failed to mmap state shm"); - { - let state: &mut cras_server_state = &mut *(addr as *mut cras_server_state); - state.state_version = CRAS_SERVER_STATE_VERSION; - state.volume = 47; - state.mute = 1; - } - libc::munmap(addr, size); - }; - let state_fd = unsafe { CrasServerStateShmFd::new(shm.into_raw_fd()) }; - let state = - CrasServerState::try_new(state_fd).expect("try_new failed for valid server_state fd"); - assert_eq!(state.get_system_volume(), 47); - assert_eq!(state.get_system_mute(), true); - } - - #[test] - fn cras_server_state_old_version() { - let size = mem::size_of::<cras_server_state>(); - let shm = create_shm(size); - unsafe { - let addr = cras_mmap(size, libc::PROT_WRITE, shm.as_raw_fd()) - .expect("failed to mmap state shm"); - { - let state: &mut cras_server_state = &mut *(addr as *mut cras_server_state); - state.state_version = CRAS_SERVER_STATE_VERSION - 1; - state.volume = 29; - state.mute = 0; - } - libc::munmap(addr, size); - }; - let state_fd = unsafe { CrasServerStateShmFd::new(shm.into_raw_fd()) }; - CrasServerState::try_new(state_fd) - .expect_err("try_new succeeded for invalid state version"); - } - - #[test] - fn cras_server_sync_state_read() { - let size = mem::size_of::<cras_server_state>(); - let shm = create_shm(size); - let addr = unsafe { cras_mmap(size, libc::PROT_WRITE, shm.as_raw_fd()).unwrap() }; - let state: &mut cras_server_state = unsafe { &mut *(addr as *mut cras_server_state) }; - state.state_version = CRAS_SERVER_STATE_VERSION; - state.update_count = 14; - state.volume = 12; - - let state_fd = unsafe { CrasServerStateShmFd::new(shm.into_raw_fd()) }; - let state_struct = CrasServerState::try_new(state_fd).unwrap(); - - // Create a lock so that we can block the reader while we change the - // update_count; - let lock = Arc::new(Mutex::new(())); - let thread_lock = lock.clone(); - let reader_thread = { - let _guard = lock.lock().unwrap(); - - // Create reader thread that will get the value of volume. Since we - // hold the lock currently, this will block until we release the lock. - let reader_thread = thread::spawn(move || { - state_struct.synchronized_state_read(|| { - let _guard = thread_lock.lock().unwrap(); - state_struct.volume.load() - }) - }); - - // Update volume and change update count so that the synchronized read - // will not return (odd update count means update in progress). - state.volume = 27; - state.update_count = 15; - - reader_thread - }; - - // The lock has been released, but the reader thread should still not - // terminate, because of the update in progress. - - // Yield thread to give reader_thread a chance to get scheduled. - thread::yield_now(); - { - let _guard = lock.lock().unwrap(); - - // Update volume and change update count to indicate the write has - // finished. - state.volume = 42; - state.update_count = 16; - } - - let read_value = reader_thread.join().unwrap(); - assert_eq!(read_value, 42); - } -} diff --git a/cras/client/libcras/src/cras_shm_stream.rs b/cras/client/libcras/src/cras_shm_stream.rs deleted file mode 100644 index f72cc07c..00000000 --- a/cras/client/libcras/src/cras_shm_stream.rs +++ /dev/null @@ -1,191 +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. -use std::time::Duration; -use std::{error, fmt}; - -use audio_streams::{ - shm_streams::{BufferSet, ServerRequest, ShmStream}, - BoxError, SampleFormat, StreamDirection, -}; -use cras_sys::gen::CRAS_AUDIO_MESSAGE_ID; -use sys_util::error; - -use crate::audio_socket::{AudioMessage, AudioSocket}; -use crate::cras_server_socket::CrasServerSocket; -use crate::cras_shm::{self, CrasAudioHeader, CrasAudioShmHeaderFd}; - -#[derive(Debug)] -pub enum Error { - MessageTypeError, - CaptureBufferTooSmall, -} - -impl error::Error for Error {} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::MessageTypeError => write!(f, "Message type error"), - Error::CaptureBufferTooSmall => write!( - f, - "Capture buffer too small, must have size at least 'used_size'." - ), - } - } -} - -/// An object that handles interactions with CRAS for a shm stream. -/// The object implements `ShmStream` and so can be used to wait for -/// `ServerRequest` and `BufferComplete` messages. -pub struct CrasShmStream<'a> { - stream_id: u32, - server_socket: CrasServerSocket, - audio_socket: AudioSocket, - direction: StreamDirection, - header: CrasAudioHeader<'a>, - frame_size: usize, - num_channels: usize, - frame_rate: u32, - // The index of the next buffer within SHM to set the buffer offset for. - next_buffer_idx: usize, -} - -impl<'a> CrasShmStream<'a> { - /// Attempt to creates a CrasShmStream with the given arguments. - /// - /// # Arguments - /// - /// * `stream_id` - The server's ID for the stream. - /// * `server_socket` - The socket that is connected to the server. - /// * `audio_socket` - The socket for audio request and audio available messages. - /// * `direction` - The direction of the stream, `Playback` or `Capture`. - /// * `num_channels` - The number of audio channels for the stream. - /// * `format` - The format to use for the stream's samples. - /// * `header_fd` - The file descriptor for the audio header shm area. - /// * `samples_len` - The size of the audio samples shm area. - /// - /// # Returns - /// - /// `CrasShmStream` - CRAS client stream. - /// - /// # Errors - /// - /// * If `header_fd` could not be successfully mmapped. - #[allow(clippy::too_many_arguments)] - pub fn try_new( - stream_id: u32, - server_socket: CrasServerSocket, - audio_socket: AudioSocket, - direction: StreamDirection, - num_channels: usize, - frame_rate: u32, - format: SampleFormat, - header_fd: CrasAudioShmHeaderFd, - samples_len: usize, - ) -> Result<Self, BoxError> { - let header = cras_shm::create_header(header_fd, samples_len)?; - Ok(Self { - stream_id, - server_socket, - audio_socket, - direction, - header, - frame_size: format.sample_bytes() * num_channels, - num_channels, - frame_rate, - // We have either sent zero or two offsets to the server, so we will - // need to update index 0 next. - next_buffer_idx: 0, - }) - } -} - -impl<'a> Drop for CrasShmStream<'a> { - /// Send the disconnect stream message and log an error if sending fails. - fn drop(&mut self) { - if let Err(e) = self.server_socket.disconnect_stream(self.stream_id) { - error!("CrasShmStream::drop error: {}", e); - } - } -} - -impl<'a> ShmStream for CrasShmStream<'a> { - fn frame_size(&self) -> usize { - self.frame_size - } - - fn num_channels(&self) -> usize { - self.num_channels - } - - fn frame_rate(&self) -> u32 { - self.frame_rate - } - - fn wait_for_next_action_with_timeout( - &mut self, - timeout: Duration, - ) -> Result<Option<ServerRequest>, BoxError> { - let expected_id = match self.direction { - StreamDirection::Playback => CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_REQUEST_DATA, - StreamDirection::Capture => CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_DATA_READY, - }; - - match self - .audio_socket - .read_audio_message_with_timeout(Some(timeout))? - { - Some(AudioMessage::Success { id, frames }) if id == expected_id => { - Ok(Some(ServerRequest::new(frames as usize, self))) - } - None => Ok(None), - _ => Err(Box::new(Error::MessageTypeError)), - } - } -} - -impl BufferSet for CrasShmStream<'_> { - fn callback(&mut self, offset: usize, frames: usize) -> Result<(), BoxError> { - self.header - .set_buffer_offset(self.next_buffer_idx, offset)?; - self.next_buffer_idx ^= 1; - let frames = frames as u32; - - match self.direction { - StreamDirection::Playback => { - self.header.commit_written_frames(frames)?; - - // Notify CRAS that we've made playback data available. - self.audio_socket.data_ready(frames)? - } - StreamDirection::Capture => { - let used_size = self.header.get_used_size(); - // Because CRAS doesn't know how long our buffer in shm is, we - // must make sure that there are always at least buffer_size - // frames available so that it doesn't write outside the buffer. - if frames < (used_size / self.frame_size) as u32 { - return Err(Box::new(Error::CaptureBufferTooSmall)); - } - - self.header.commit_read_frames(frames)?; - self.audio_socket.capture_ready(frames)?; - } - } - - Ok(()) - } - - fn ignore(&mut self) -> Result<(), BoxError> { - // We send an empty buffer for an ignored playback request since the - // server will not read from a 0-length buffer. We don't do anything for - // an ignored capture request, since we don't have a way to communicate - // buffer length to the server, and we don't want the server writing - // data to offsets within the SHM area that aren't audio buffers. - if self.direction == StreamDirection::Playback { - self.callback(0, 0)?; - } - - Ok(()) - } -} diff --git a/cras/client/libcras/src/cras_stream.rs b/cras/client/libcras/src/cras_stream.rs deleted file mode 100644 index f6004802..00000000 --- a/cras/client/libcras/src/cras_stream.rs +++ /dev/null @@ -1,224 +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. -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) - } -} 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) - } -} |