1 // Copyright 2019 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 use std::time::Duration; 5 use std::{error, fmt}; 6 7 use audio_streams::{ 8 shm_streams::{BufferSet, ServerRequest, ShmStream}, 9 BoxError, SampleFormat, StreamDirection, 10 }; 11 use cras_sys::gen::CRAS_AUDIO_MESSAGE_ID; 12 use sys_util::error; 13 14 use crate::audio_socket::{AudioMessage, AudioSocket}; 15 use crate::cras_server_socket::CrasServerSocket; 16 use crate::cras_shm::{self, CrasAudioHeader, CrasAudioShmHeaderFd}; 17 18 #[derive(Debug)] 19 pub enum Error { 20 MessageTypeError, 21 CaptureBufferTooSmall, 22 } 23 24 impl error::Error for Error {} 25 26 impl fmt::Display for Error { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result27 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 28 match self { 29 Error::MessageTypeError => write!(f, "Message type error"), 30 Error::CaptureBufferTooSmall => write!( 31 f, 32 "Capture buffer too small, must have size at least 'used_size'." 33 ), 34 } 35 } 36 } 37 38 /// An object that handles interactions with CRAS for a shm stream. 39 /// The object implements `ShmStream` and so can be used to wait for 40 /// `ServerRequest` and `BufferComplete` messages. 41 pub struct CrasShmStream<'a> { 42 stream_id: u32, 43 server_socket: CrasServerSocket, 44 audio_socket: AudioSocket, 45 direction: StreamDirection, 46 header: CrasAudioHeader<'a>, 47 frame_size: usize, 48 num_channels: usize, 49 frame_rate: u32, 50 // The index of the next buffer within SHM to set the buffer offset for. 51 next_buffer_idx: usize, 52 } 53 54 impl<'a> CrasShmStream<'a> { 55 /// Attempt to creates a CrasShmStream with the given arguments. 56 /// 57 /// # Arguments 58 /// 59 /// * `stream_id` - The server's ID for the stream. 60 /// * `server_socket` - The socket that is connected to the server. 61 /// * `audio_socket` - The socket for audio request and audio available messages. 62 /// * `direction` - The direction of the stream, `Playback` or `Capture`. 63 /// * `num_channels` - The number of audio channels for the stream. 64 /// * `format` - The format to use for the stream's samples. 65 /// * `header_fd` - The file descriptor for the audio header shm area. 66 /// * `samples_len` - The size of the audio samples shm area. 67 /// 68 /// # Returns 69 /// 70 /// `CrasShmStream` - CRAS client stream. 71 /// 72 /// # Errors 73 /// 74 /// * If `header_fd` could not be successfully mmapped. 75 #[allow(clippy::too_many_arguments)] 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>76 pub fn try_new( 77 stream_id: u32, 78 server_socket: CrasServerSocket, 79 audio_socket: AudioSocket, 80 direction: StreamDirection, 81 num_channels: usize, 82 frame_rate: u32, 83 format: SampleFormat, 84 header_fd: CrasAudioShmHeaderFd, 85 samples_len: usize, 86 ) -> Result<Self, BoxError> { 87 let header = cras_shm::create_header(header_fd, samples_len)?; 88 Ok(Self { 89 stream_id, 90 server_socket, 91 audio_socket, 92 direction, 93 header, 94 frame_size: format.sample_bytes() * num_channels, 95 num_channels, 96 frame_rate, 97 // We have either sent zero or two offsets to the server, so we will 98 // need to update index 0 next. 99 next_buffer_idx: 0, 100 }) 101 } 102 } 103 104 impl<'a> Drop for CrasShmStream<'a> { 105 /// Send the disconnect stream message and log an error if sending fails. drop(&mut self)106 fn drop(&mut self) { 107 if let Err(e) = self.server_socket.disconnect_stream(self.stream_id) { 108 error!("CrasShmStream::drop error: {}", e); 109 } 110 } 111 } 112 113 impl<'a> ShmStream for CrasShmStream<'a> { frame_size(&self) -> usize114 fn frame_size(&self) -> usize { 115 self.frame_size 116 } 117 num_channels(&self) -> usize118 fn num_channels(&self) -> usize { 119 self.num_channels 120 } 121 frame_rate(&self) -> u32122 fn frame_rate(&self) -> u32 { 123 self.frame_rate 124 } 125 wait_for_next_action_with_timeout( &mut self, timeout: Duration, ) -> Result<Option<ServerRequest>, BoxError>126 fn wait_for_next_action_with_timeout( 127 &mut self, 128 timeout: Duration, 129 ) -> Result<Option<ServerRequest>, BoxError> { 130 let expected_id = match self.direction { 131 StreamDirection::Playback => CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_REQUEST_DATA, 132 StreamDirection::Capture => CRAS_AUDIO_MESSAGE_ID::AUDIO_MESSAGE_DATA_READY, 133 }; 134 135 match self 136 .audio_socket 137 .read_audio_message_with_timeout(Some(timeout))? 138 { 139 Some(AudioMessage::Success { id, frames }) if id == expected_id => { 140 Ok(Some(ServerRequest::new(frames as usize, self))) 141 } 142 None => Ok(None), 143 _ => Err(Box::new(Error::MessageTypeError)), 144 } 145 } 146 } 147 148 impl BufferSet for CrasShmStream<'_> { callback(&mut self, offset: usize, frames: usize) -> Result<(), BoxError>149 fn callback(&mut self, offset: usize, frames: usize) -> Result<(), BoxError> { 150 self.header 151 .set_buffer_offset(self.next_buffer_idx, offset)?; 152 self.next_buffer_idx ^= 1; 153 let frames = frames as u32; 154 155 match self.direction { 156 StreamDirection::Playback => { 157 self.header.commit_written_frames(frames)?; 158 159 // Notify CRAS that we've made playback data available. 160 self.audio_socket.data_ready(frames)? 161 } 162 StreamDirection::Capture => { 163 let used_size = self.header.get_used_size(); 164 // Because CRAS doesn't know how long our buffer in shm is, we 165 // must make sure that there are always at least buffer_size 166 // frames available so that it doesn't write outside the buffer. 167 if frames < (used_size / self.frame_size) as u32 { 168 return Err(Box::new(Error::CaptureBufferTooSmall)); 169 } 170 171 self.header.commit_read_frames(frames)?; 172 self.audio_socket.capture_ready(frames)?; 173 } 174 } 175 176 Ok(()) 177 } 178 ignore(&mut self) -> Result<(), BoxError>179 fn ignore(&mut self) -> Result<(), BoxError> { 180 // We send an empty buffer for an ignored playback request since the 181 // server will not read from a 0-length buffer. We don't do anything for 182 // an ignored capture request, since we don't have a way to communicate 183 // buffer length to the server, and we don't want the server writing 184 // data to offsets within the SHM area that aren't audio buffers. 185 if self.direction == StreamDirection::Playback { 186 self.callback(0, 0)?; 187 } 188 189 Ok(()) 190 } 191 } 192