Path: blob/main/devices/src/virtio/snd/common_backend/stream_info.rs
5394 views
// Copyright 2022 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34use std::fmt;5use std::rc::Rc;6use std::sync::atomic::AtomicBool;7use std::sync::atomic::Ordering;8use std::sync::Arc;9use std::time::Duration;1011use audio_streams::SampleFormat;12use audio_streams::StreamEffect;13use base::error;14use base::warn;15use cros_async::sync::Condvar;16use cros_async::sync::RwLock as AsyncRwLock;17use cros_async::Executor;18use futures::channel::mpsc;19use futures::Future;20use futures::TryFutureExt;21use serde::Deserialize;22use serde::Serialize;2324use super::Error;25use super::PcmResponse;26use super::WorkerStatus;27use crate::virtio::snd::common::*;28use crate::virtio::snd::common_backend::async_funcs::*;29use crate::virtio::snd::common_backend::DirectionalStream;30use crate::virtio::snd::common_backend::SysAsyncStreamObjects;31use crate::virtio::snd::constants::*;32use crate::virtio::snd::sys::SysAudioStreamSource;33use crate::virtio::snd::sys::SysAudioStreamSourceGenerator;34use crate::virtio::DescriptorChain;3536/// Parameters for setting parameters in StreamInfo37#[derive(Copy, Clone, Debug)]38pub struct SetParams {39pub channels: u8,40pub format: SampleFormat,41pub frame_rate: u32,42pub buffer_bytes: usize,43pub period_bytes: usize,44pub dir: u8,45}4647/// StreamInfoBuilder builds a [`StreamInfo`]. It is used when we want to store the parameters to48/// create a [`StreamInfo`] beforehand and actually create it later. (as is the case with VirtioSnd)49///50/// To create a new StreamInfoBuilder, see [`StreamInfo::builder()`].51#[derive(Clone)]52pub struct StreamInfoBuilder {53stream_source_generator: Arc<SysAudioStreamSourceGenerator>,54effects: Vec<StreamEffect>,55card_index: usize,56#[cfg(windows)]57audio_client_guid: Option<String>,58}5960impl StreamInfoBuilder {61/// Creates a StreamInfoBuilder with minimal required fields:62///63/// * `stream_source_generator`: Generator which generates stream source in64/// [`StreamInfo::prepare()`].65/// * `card_index`: The ALSA card index.66pub fn new(67stream_source_generator: Arc<SysAudioStreamSourceGenerator>,68card_index: usize,69) -> Self {70StreamInfoBuilder {71stream_source_generator,72effects: vec![],73card_index,74#[cfg(windows)]75audio_client_guid: None,76}77}7879/// Set the [`StreamEffect`]s to use when creating a stream from the stream source in80/// [`StreamInfo::prepare()`]. The default value is no effects.81pub fn effects(mut self, effects: Vec<StreamEffect>) -> Self {82self.effects = effects;83self84}8586#[cfg(windows)]87pub fn audio_client_guid(mut self, audio_client_guid: Option<String>) -> Self {88self.audio_client_guid = audio_client_guid;89self90}9192/// Builds a [`StreamInfo`].93pub fn build(self) -> StreamInfo {94self.into()95}96}9798/// StreamInfo represents a virtio snd stream.99///100/// To create a StreamInfo, see [`StreamInfo::builder()`] and [`StreamInfoBuilder::build()`].101pub struct StreamInfo {102pub(crate) stream_source: Option<SysAudioStreamSource>,103stream_source_generator: Arc<SysAudioStreamSourceGenerator>,104pub(crate) muted: Rc<AtomicBool>,105pub(crate) channels: u8,106pub(crate) format: SampleFormat,107pub(crate) frame_rate: u32,108buffer_bytes: usize,109pub(crate) period_bytes: usize,110direction: u8, // VIRTIO_SND_D_*111pub state: u32, // VIRTIO_SND_R_PCM_SET_PARAMS -> VIRTIO_SND_R_PCM_STOP, or 0 (uninitialized)112// Stream effects to use when creating a new stream on [`prepare()`].113pub(crate) effects: Vec<StreamEffect>,114115// just_reset set to true after reset. Make invalid state transition return Ok. Set to false116// after a valid state transition to SET_PARAMS or PREPARE.117pub just_reset: bool,118119// Worker related120pub status_mutex: Rc<AsyncRwLock<WorkerStatus>>,121pub sender: Option<mpsc::UnboundedSender<DescriptorChain>>,122worker_future: Option<Box<dyn Future<Output = Result<(), Error>> + Unpin>>,123release_signal: Option<Rc<(AsyncRwLock<bool>, Condvar)>>, // Signal worker on release124card_index: usize,125ex: Option<Executor>, // Executor provided on `prepare()`. Used on `drop()`.126#[cfg(windows)]127pub(crate) playback_stream_cache: Option<(128Arc<AsyncRwLock<Box<dyn audio_streams::AsyncPlaybackBufferStream>>>,129Rc<AsyncRwLock<Box<dyn PlaybackBufferWriter>>>,130)>,131#[cfg(windows)]132pub(crate) audio_client_guid: Option<String>,133}134135#[derive(Clone, Serialize, Deserialize)]136pub struct StreamInfoSnapshot {137pub(crate) channels: u8,138pub(crate) format: SampleFormat,139pub(crate) frame_rate: u32,140buffer_bytes: usize,141pub(crate) period_bytes: usize,142direction: u8, // VIRTIO_SND_D_*143pub state: u32, // VIRTIO_SND_R_PCM_SET_PARAMS -> VIRTIO_SND_R_PCM_STOP, or 0 (uninitialized)144effects: Vec<StreamEffect>,145pub just_reset: bool,146}147148impl fmt::Debug for StreamInfo {149fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {150f.debug_struct("StreamInfo")151.field("muted", &self.muted.load(Ordering::Relaxed))152.field("channels", &self.channels)153.field("format", &self.format)154.field("frame_rate", &self.frame_rate)155.field("buffer_bytes", &self.buffer_bytes)156.field("period_bytes", &self.period_bytes)157.field("direction", &get_virtio_direction_name(self.direction))158.field("state", &get_virtio_snd_r_pcm_cmd_name(self.state))159.field("effects", &self.effects)160.finish()161}162}163164impl Drop for StreamInfo {165fn drop(&mut self) {166if let Some(ex) = self.ex.take() {167if self.state == VIRTIO_SND_R_PCM_START {168match ex.run_until(self.stop()) {169Err(e) => error!("Drop stream error on stop in executor: {}", e),170Ok(Err(e)) => error!("Drop stream error on stop: {}", e),171_ => {}172}173}174if self.state == VIRTIO_SND_R_PCM_PREPARE || self.state == VIRTIO_SND_R_PCM_STOP {175match ex.run_until(self.release()) {176Err(e) => error!("Drop stream error on release in executor: {}", e),177Ok(Err(e)) => error!("Drop stream error on release: {}", e),178_ => {}179}180}181}182}183}184185impl From<StreamInfoBuilder> for StreamInfo {186fn from(builder: StreamInfoBuilder) -> Self {187StreamInfo {188muted: Rc::new(AtomicBool::new(false)),189stream_source: None,190stream_source_generator: builder.stream_source_generator,191channels: 0,192format: SampleFormat::U8,193frame_rate: 0,194buffer_bytes: 0,195period_bytes: 0,196direction: 0,197state: 0,198effects: builder.effects,199just_reset: false,200status_mutex: Rc::new(AsyncRwLock::new(WorkerStatus::Pause)),201sender: None,202worker_future: None,203release_signal: None,204card_index: builder.card_index,205ex: None,206#[cfg(windows)]207playback_stream_cache: None,208#[cfg(windows)]209audio_client_guid: builder.audio_client_guid,210}211}212}213214impl StreamInfo {215/// Creates a minimal [`StreamInfoBuilder`]. See [`StreamInfoBuilder::new()`] for216/// the description of each parameter.217pub fn builder(218stream_source_generator: Arc<SysAudioStreamSourceGenerator>,219card_index: usize,220) -> StreamInfoBuilder {221StreamInfoBuilder::new(stream_source_generator, card_index)222}223224/// Sets parameters of the stream, putting it into [`VIRTIO_SND_R_PCM_SET_PARAMS`] state.225///226/// * `params`: [`SetParams`] for the pcm stream runtime configuration.227pub async fn set_params(&mut self, params: SetParams) -> Result<(), Error> {228if self.state != 0229&& self.state != VIRTIO_SND_R_PCM_SET_PARAMS230&& self.state != VIRTIO_SND_R_PCM_PREPARE231&& self.state != VIRTIO_SND_R_PCM_RELEASE232{233error!(234"[Card {}] Invalid PCM state transition from {} to {}",235self.card_index,236get_virtio_snd_r_pcm_cmd_name(self.state),237get_virtio_snd_r_pcm_cmd_name(VIRTIO_SND_R_PCM_SET_PARAMS)238);239return Err(Error::OperationNotSupported);240}241242// Only required for PREPARE -> SET_PARAMS243self.release_worker().await;244245self.channels = params.channels;246self.format = params.format;247self.frame_rate = params.frame_rate;248self.buffer_bytes = params.buffer_bytes;249self.period_bytes = params.period_bytes;250self.direction = params.dir;251self.state = VIRTIO_SND_R_PCM_SET_PARAMS;252self.just_reset = false;253Ok(())254}255256/// Prepares the stream, putting it into [`VIRTIO_SND_R_PCM_PREPARE`] state.257///258/// * `ex`: [`Executor`] to run the pcm worker.259/// * `tx_send`: Sender for sending `PcmResponse` for tx queue. (playback stream)260/// * `rx_send`: Sender for sending `PcmResponse` for rx queue. (capture stream)261pub async fn prepare(262&mut self,263ex: &Executor,264tx_send: &mpsc::UnboundedSender<PcmResponse>,265rx_send: &mpsc::UnboundedSender<PcmResponse>,266) -> Result<(), Error> {267if self.state == 0 && self.just_reset {268return Ok(());269}270if self.state != VIRTIO_SND_R_PCM_SET_PARAMS271&& self.state != VIRTIO_SND_R_PCM_PREPARE272&& self.state != VIRTIO_SND_R_PCM_RELEASE273{274error!(275"[Card {}] Invalid PCM state transition from {} to {}",276self.card_index,277get_virtio_snd_r_pcm_cmd_name(self.state),278get_virtio_snd_r_pcm_cmd_name(VIRTIO_SND_R_PCM_PREPARE)279);280return Err(Error::OperationNotSupported);281}282self.just_reset = false;283if self.state == VIRTIO_SND_R_PCM_PREPARE {284self.release_worker().await;285}286let frame_size = self.channels as usize * self.format.sample_bytes();287if self.period_bytes % frame_size != 0 {288error!(289"[Card {}] period_bytes must be divisible by frame size",290self.card_index291);292return Err(Error::OperationNotSupported);293}294self.stream_source = Some(295self.stream_source_generator296.generate()297.map_err(Error::GenerateStreamSource)?,298);299let stream_objects = match self.direction {300VIRTIO_SND_D_OUTPUT => SysAsyncStreamObjects {301stream: self302.create_directionstream_output(303frame_size,304#[cfg(windows)]305self.audio_client_guid.clone(),306ex,307)308.await?,309pcm_sender: tx_send.clone(),310},311VIRTIO_SND_D_INPUT => {312let buffer_reader = self.set_up_async_capture_stream(frame_size, ex).await?;313SysAsyncStreamObjects {314stream: DirectionalStream::Input(self.period_bytes, Box::new(buffer_reader)),315pcm_sender: rx_send.clone(),316}317}318_ => unreachable!(),319};320321let (sender, receiver) = mpsc::unbounded();322self.sender = Some(sender);323self.state = VIRTIO_SND_R_PCM_PREPARE;324325self.status_mutex = Rc::new(AsyncRwLock::new(WorkerStatus::Pause));326let period_dur = Duration::from_secs_f64(327self.period_bytes as f64 / frame_size as f64 / self.frame_rate as f64,328);329let release_signal = Rc::new((AsyncRwLock::new(false), Condvar::new()));330self.release_signal = Some(release_signal.clone());331let f = start_pcm_worker(332ex.clone(),333stream_objects.stream,334receiver,335self.status_mutex.clone(),336stream_objects.pcm_sender,337period_dur,338self.card_index,339self.muted.clone(),340release_signal,341);342self.worker_future = Some(Box::new(ex.spawn_local(f).into_future()));343self.ex = Some(ex.clone());344Ok(())345}346347/// Starts the stream, putting it into [`VIRTIO_SND_R_PCM_START`] state.348pub async fn start(&mut self) -> Result<(), Error> {349if self.just_reset {350return Ok(());351}352if self.state != VIRTIO_SND_R_PCM_PREPARE && self.state != VIRTIO_SND_R_PCM_STOP {353error!(354"[Card {}] Invalid PCM state transition from {} to {}",355self.card_index,356get_virtio_snd_r_pcm_cmd_name(self.state),357get_virtio_snd_r_pcm_cmd_name(VIRTIO_SND_R_PCM_START)358);359return Err(Error::OperationNotSupported);360}361self.state = VIRTIO_SND_R_PCM_START;362let mut status = self.status_mutex.lock().await;363if *status != WorkerStatus::Quit {364*status = WorkerStatus::Running;365}366Ok(())367}368369/// Stops the stream, putting it into [`VIRTIO_SND_R_PCM_STOP`] state.370pub async fn stop(&mut self) -> Result<(), Error> {371if self.just_reset {372return Ok(());373}374if self.state != VIRTIO_SND_R_PCM_START {375error!(376"[Card {}] Invalid PCM state transition from {} to {}",377self.card_index,378get_virtio_snd_r_pcm_cmd_name(self.state),379get_virtio_snd_r_pcm_cmd_name(VIRTIO_SND_R_PCM_STOP)380);381return Err(Error::OperationNotSupported);382}383self.state = VIRTIO_SND_R_PCM_STOP;384let mut status = self.status_mutex.lock().await;385if *status != WorkerStatus::Quit {386*status = WorkerStatus::Pause;387}388Ok(())389}390391/// Releases the stream, putting it into [`VIRTIO_SND_R_PCM_RELEASE`] state.392pub async fn release(&mut self) -> Result<(), Error> {393if self.just_reset {394return Ok(());395}396if self.state != VIRTIO_SND_R_PCM_PREPARE && self.state != VIRTIO_SND_R_PCM_STOP {397error!(398"[Card {}] Invalid PCM state transition from {} to {}",399self.card_index,400get_virtio_snd_r_pcm_cmd_name(self.state),401get_virtio_snd_r_pcm_cmd_name(VIRTIO_SND_R_PCM_RELEASE)402);403return Err(Error::OperationNotSupported);404}405self.state = VIRTIO_SND_R_PCM_RELEASE;406self.stream_source = None;407self.release_worker().await;408Ok(())409}410411async fn release_worker(&mut self) {412*self.status_mutex.lock().await = WorkerStatus::Quit;413if let Some(s) = self.sender.take() {414s.close_channel();415}416417if let Some(release_signal) = self.release_signal.take() {418let (lock, cvar) = &*release_signal;419let mut signalled = lock.lock().await;420*signalled = true;421cvar.notify_all();422}423424if let Some(f) = self.worker_future.take() {425f.await426.map_err(|error| {427warn!(428"[Card {}] Failure on releasing the worker_future: {}",429self.card_index, error430)431})432.ok();433}434self.ex.take(); // Remove ex as the worker is finished435}436437pub fn snapshot(&self) -> StreamInfoSnapshot {438StreamInfoSnapshot {439channels: self.channels,440format: self.format,441frame_rate: self.frame_rate,442buffer_bytes: self.buffer_bytes,443period_bytes: self.period_bytes,444direction: self.direction, // VIRTIO_SND_D_*445// VIRTIO_SND_R_PCM_SET_PARAMS -> VIRTIO_SND_R_PCM_STOP, or 0 (uninitialized)446state: self.state,447effects: self.effects.clone(),448just_reset: self.just_reset,449}450}451452pub fn restore(&mut self, state: &StreamInfoSnapshot) {453self.channels = state.channels;454self.format = state.format;455self.frame_rate = state.frame_rate;456self.buffer_bytes = state.buffer_bytes;457self.period_bytes = state.period_bytes;458self.direction = state.direction;459self.effects.clone_from(&state.effects);460self.just_reset = state.just_reset;461}462}463464#[cfg(test)]465mod tests {466use audio_streams::NoopStreamSourceGenerator;467468use super::*;469470fn new_stream() -> StreamInfo {471let card_index = 0;472StreamInfo::builder(473Arc::new(Box::new(NoopStreamSourceGenerator::new())),474card_index,475)476.build()477}478479fn stream_set_params(480mut stream: StreamInfo,481ex: &Executor,482expected_ok: bool,483expected_state: u32,484) -> StreamInfo {485let result = ex.run_until(stream.set_params(SetParams {486channels: 2,487format: SampleFormat::U8,488frame_rate: 48000,489buffer_bytes: 1024,490period_bytes: 512,491dir: VIRTIO_SND_D_OUTPUT,492}));493assert_eq!(result.unwrap().is_ok(), expected_ok);494assert_eq!(stream.state, expected_state);495stream496}497498fn stream_prepare(499mut stream: StreamInfo,500ex: &Executor,501expected_ok: bool,502expected_state: u32,503) -> StreamInfo {504let (tx_send, _) = mpsc::unbounded();505let (rx_send, _) = mpsc::unbounded();506507let result = ex.run_until(stream.prepare(ex, &tx_send, &rx_send));508assert_eq!(result.unwrap().is_ok(), expected_ok);509assert_eq!(stream.state, expected_state);510stream511}512513fn stream_start(514mut stream: StreamInfo,515ex: &Executor,516expected_ok: bool,517expected_state: u32,518) -> StreamInfo {519let result = ex.run_until(stream.start());520assert_eq!(result.unwrap().is_ok(), expected_ok);521assert_eq!(stream.state, expected_state);522stream523}524525fn stream_stop(526mut stream: StreamInfo,527ex: &Executor,528expected_ok: bool,529expected_state: u32,530) -> StreamInfo {531let result = ex.run_until(stream.stop());532assert_eq!(result.unwrap().is_ok(), expected_ok);533assert_eq!(stream.state, expected_state);534stream535}536537fn stream_release(538mut stream: StreamInfo,539ex: &Executor,540expected_ok: bool,541expected_state: u32,542) -> StreamInfo {543let result = ex.run_until(stream.release());544assert_eq!(result.unwrap().is_ok(), expected_ok);545assert_eq!(stream.state, expected_state);546stream547}548549#[test]550fn test_transitions_from_0() {551let ex = Executor::new().expect("Failed to create an executor");552553// Valid transition to: {SET_PARAMS}554stream_set_params(new_stream(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);555556// Invalid transition to: {PREPARE, START, STOP, RELEASE}557stream_prepare(new_stream(), &ex, false, 0);558stream_start(new_stream(), &ex, false, 0);559stream_stop(new_stream(), &ex, false, 0);560stream_release(new_stream(), &ex, false, 0);561}562563#[test]564fn test_transitions_from_set_params() {565let ex = Executor::new().expect("Failed to create an executor");566let new_stream_set_params = || -> StreamInfo {567stream_set_params(new_stream(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS)568};569570// Valid transition to: {SET_PARAMS, PREPARE}571stream_set_params(572new_stream_set_params(),573&ex,574true,575VIRTIO_SND_R_PCM_SET_PARAMS,576);577stream_prepare(new_stream_set_params(), &ex, true, VIRTIO_SND_R_PCM_PREPARE);578579// Invalid transition to: {START, STOP, RELEASE}580stream_start(581new_stream_set_params(),582&ex,583false,584VIRTIO_SND_R_PCM_SET_PARAMS,585);586stream_stop(587new_stream_set_params(),588&ex,589false,590VIRTIO_SND_R_PCM_SET_PARAMS,591);592stream_release(593new_stream_set_params(),594&ex,595false,596VIRTIO_SND_R_PCM_SET_PARAMS,597);598}599600#[test]601fn test_transitions_from_prepare() {602let ex = Executor::new().expect("Failed to create an executor");603let new_stream_prepare = || -> StreamInfo {604let stream = stream_set_params(new_stream(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);605stream_prepare(stream, &ex, true, VIRTIO_SND_R_PCM_PREPARE)606};607608// Valid transition to: {SET_PARAMS, PREPARE, START, RELEASE}609stream_set_params(new_stream_prepare(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);610stream_prepare(new_stream_prepare(), &ex, true, VIRTIO_SND_R_PCM_PREPARE);611stream_start(new_stream_prepare(), &ex, true, VIRTIO_SND_R_PCM_START);612stream_release(new_stream_prepare(), &ex, true, VIRTIO_SND_R_PCM_RELEASE);613614// Invalid transition to: {STOP}615stream_stop(new_stream_prepare(), &ex, false, VIRTIO_SND_R_PCM_PREPARE);616}617618#[test]619fn test_transitions_from_start() {620let ex = Executor::new().expect("Failed to create an executor");621let new_stream_start = || -> StreamInfo {622let mut stream =623stream_set_params(new_stream(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);624stream = stream_prepare(stream, &ex, true, VIRTIO_SND_R_PCM_PREPARE);625stream_start(stream, &ex, true, VIRTIO_SND_R_PCM_START)626};627628// Valid transition to: {STOP}629stream_stop(new_stream_start(), &ex, true, VIRTIO_SND_R_PCM_STOP);630631// Invalid transition to: {SET_PARAMS, PREPARE, START, RELEASE}632stream_set_params(new_stream_start(), &ex, false, VIRTIO_SND_R_PCM_START);633stream_prepare(new_stream_start(), &ex, false, VIRTIO_SND_R_PCM_START);634stream_start(new_stream_start(), &ex, false, VIRTIO_SND_R_PCM_START);635stream_release(new_stream_start(), &ex, false, VIRTIO_SND_R_PCM_START);636}637638#[test]639fn test_transitions_from_stop() {640let ex = Executor::new().expect("Failed to create an executor");641let new_stream_stop = || -> StreamInfo {642let mut stream =643stream_set_params(new_stream(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);644stream = stream_prepare(stream, &ex, true, VIRTIO_SND_R_PCM_PREPARE);645stream = stream_start(stream, &ex, true, VIRTIO_SND_R_PCM_START);646stream_stop(stream, &ex, true, VIRTIO_SND_R_PCM_STOP)647};648649// Valid transition to: {START, RELEASE}650stream_start(new_stream_stop(), &ex, true, VIRTIO_SND_R_PCM_START);651stream_release(new_stream_stop(), &ex, true, VIRTIO_SND_R_PCM_RELEASE);652653// Invalid transition to: {SET_PARAMS, PREPARE, STOP}654stream_set_params(new_stream_stop(), &ex, false, VIRTIO_SND_R_PCM_STOP);655stream_prepare(new_stream_stop(), &ex, false, VIRTIO_SND_R_PCM_STOP);656stream_stop(new_stream_stop(), &ex, false, VIRTIO_SND_R_PCM_STOP);657}658659#[test]660fn test_transitions_from_release() {661let ex = Executor::new().expect("Failed to create an executor");662let new_stream_release = || -> StreamInfo {663let mut stream =664stream_set_params(new_stream(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);665stream = stream_prepare(stream, &ex, true, VIRTIO_SND_R_PCM_PREPARE);666stream_release(stream, &ex, true, VIRTIO_SND_R_PCM_RELEASE)667};668669// Valid transition to: {SET_PARAMS, PREPARE}670stream_set_params(new_stream_release(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);671stream_prepare(new_stream_release(), &ex, true, VIRTIO_SND_R_PCM_PREPARE);672673// Invalid transition to: {START, STOP, RELEASE}674stream_start(new_stream_release(), &ex, false, VIRTIO_SND_R_PCM_RELEASE);675stream_stop(new_stream_release(), &ex, false, VIRTIO_SND_R_PCM_RELEASE);676stream_release(new_stream_release(), &ex, false, VIRTIO_SND_R_PCM_RELEASE);677}678679#[test]680fn test_transitions_from_0_just_reset() {681let ex = Executor::new().expect("Failed to create an executor");682let new_stream_0 = || -> StreamInfo {683let mut stream = new_stream();684stream.just_reset = true;685stream686};687688// Valid transition to: {SET_PARAMS}689// After valid transition, just_reset reset to false690let mut stream = new_stream_0();691stream = stream_set_params(stream, &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);692assert_eq!(stream.just_reset, false);693694// Invalid transition to: {PREPARE, START, STOP, RELEASE}695// Return Ok but state doesn't change696stream_prepare(new_stream_0(), &ex, true, 0);697stream_start(new_stream_0(), &ex, true, 0);698stream_stop(new_stream_0(), &ex, true, 0);699stream_release(new_stream_0(), &ex, true, 0);700}701702#[test]703fn test_transitions_from_set_params_just_reset() {704let ex = Executor::new().expect("Failed to create an executor");705let new_stream_set_params = || -> StreamInfo {706let mut stream =707stream_set_params(new_stream(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);708stream.just_reset = true;709stream710};711712// Valid transition to: {SET_PARAMS, PREPARE}713// After valid transition, just_reset reset to false714let mut stream = new_stream_set_params();715stream = stream_set_params(stream, &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);716assert_eq!(stream.just_reset, false);717718let mut stream = new_stream_set_params();719stream = stream_prepare(stream, &ex, true, VIRTIO_SND_R_PCM_PREPARE);720assert_eq!(stream.just_reset, false);721722// Invalid transition to: {START, STOP, RELEASE}723// Return Ok but state doesn't change724stream_start(725new_stream_set_params(),726&ex,727true,728VIRTIO_SND_R_PCM_SET_PARAMS,729);730stream_stop(731new_stream_set_params(),732&ex,733true,734VIRTIO_SND_R_PCM_SET_PARAMS,735);736stream_release(737new_stream_set_params(),738&ex,739true,740VIRTIO_SND_R_PCM_SET_PARAMS,741);742}743744#[test]745fn test_transitions_from_release_just_reset() {746let ex = Executor::new().expect("Failed to create an executor");747let new_stream_release = || -> StreamInfo {748let mut stream =749stream_set_params(new_stream(), &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);750stream = stream_prepare(stream, &ex, true, VIRTIO_SND_R_PCM_PREPARE);751stream = stream_release(stream, &ex, true, VIRTIO_SND_R_PCM_RELEASE);752stream.just_reset = true;753stream754};755756// Valid transition to: {SET_PARAMS, PREPARE}757// After valid transition, just_reset reset to false758let mut stream = new_stream_release();759stream = stream_set_params(stream, &ex, true, VIRTIO_SND_R_PCM_SET_PARAMS);760assert_eq!(stream.just_reset, false);761762let mut stream = new_stream_release();763stream = stream_prepare(stream, &ex, true, VIRTIO_SND_R_PCM_PREPARE);764assert_eq!(stream.just_reset, false);765766// Invalid transition to: {START, STOP, RELEASE}767// Return Ok but state doesn't change768stream_start(new_stream_release(), &ex, true, VIRTIO_SND_R_PCM_RELEASE);769stream_stop(new_stream_release(), &ex, true, VIRTIO_SND_R_PCM_RELEASE);770stream_release(new_stream_release(), &ex, true, VIRTIO_SND_R_PCM_RELEASE);771}772773#[test]774fn test_stream_info_builder() {775let card_index = 0;776let builder = StreamInfo::builder(777Arc::new(Box::new(NoopStreamSourceGenerator::new())),778card_index,779)780.effects(vec![StreamEffect::EchoCancellation]);781782let stream = builder.build();783assert_eq!(stream.effects, vec![StreamEffect::EchoCancellation]);784}785}786787788