• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::fs::File;
6 use std::io::Error as IOError;
7 use std::slice;
8 use std::sync::atomic::AtomicUsize;
9 use std::sync::atomic::Ordering;
10 use std::sync::Arc;
11 use std::time::Duration;
12 use std::time::Instant;
13 
14 use async_trait::async_trait;
15 use audio_streams::AsyncBufferCommit;
16 use audio_streams::AsyncPlaybackBuffer;
17 use audio_streams::AsyncPlaybackBufferStream;
18 use audio_streams::AudioStreamsExecutor;
19 use audio_streams::BoxError;
20 use audio_streams::NoopStreamControl;
21 use audio_streams::SampleFormat;
22 use audio_streams::StreamControl;
23 use audio_streams::StreamSource;
24 use audio_streams::StreamSourceGenerator;
25 use base::warn;
26 use base::MappedRegion;
27 use base::MemoryMapping;
28 use base::MemoryMappingBuilder;
29 use base::MmapError;
30 use thiserror::Error as ThisError;
31 
32 #[derive(ThisError, Debug)]
33 pub enum Error {
34     #[error("Failed to build memory mapping: {0}")]
35     BuildMemoryMapping(MmapError),
36     #[error("Failed to clone file descriptor: {0}")]
37     Clone(IOError),
38     #[error("Not implemented")]
39     Unimplemented,
40 }
41 
42 /// An Audio Stream that can be used to write playback buffer to a file.
43 /// `FileStream` doesn't directly open and write to file. It receives
44 /// an mmap of a file instead.
45 ///
46 /// Note that `FileStream` also needs the mmap-ed file has allocated some spaces
47 /// to be written. If the playback buffer exceeds the allocated spaces,
48 /// it will invoke `panic!`
49 pub struct FileStream {
50     /// A MemoryMapping that will hold the copy of the playback buffer.
51     memory_mapping: AudioMemoryMapping,
52     /// Number of bytes that has been written.
53     offset: Arc<AtomicUsize>,
54     /// Number of bytes in a single audio frame.
55     frame_size: usize,
56     /// Length of the current playback buffer in bytes.
57     buffer_mem_length: usize,
58 
59     /// Duration of an audio in milliseconds for the current `buffer_size`.
60     interval_ms: Duration,
61     /// Time marker of correct time to return next buffer.
62     next_frame: Duration,
63     /// Timestamp that records when the stream starts.
64     start_time: Option<Instant>,
65     /// Type that will be called before the buffer is dropped.
66     buffer_drop: FileStreamBufferCommit,
67 }
68 
69 impl FileStream {
new( memory_mapping: AudioMemoryMapping, offset: Arc<AtomicUsize>, frame_size: usize, buffer_mem_length: usize, interval_ms: Duration, ) -> Self70     fn new(
71         memory_mapping: AudioMemoryMapping,
72         offset: Arc<AtomicUsize>,
73         frame_size: usize,
74         buffer_mem_length: usize,
75         interval_ms: Duration,
76     ) -> Self {
77         let max_offset = memory_mapping.size();
78         FileStream {
79             memory_mapping,
80             offset: offset.clone(),
81             frame_size,
82             buffer_mem_length,
83 
84             interval_ms,
85             next_frame: interval_ms,
86             start_time: None,
87             buffer_drop: FileStreamBufferCommit {
88                 frame_size,
89                 offset,
90                 max_offset,
91             },
92         }
93     }
94 }
95 
96 #[async_trait(?Send)]
97 impl AsyncPlaybackBufferStream for FileStream {
next_playback_buffer<'a>( &'a mut self, ex: &dyn AudioStreamsExecutor, ) -> Result<AsyncPlaybackBuffer<'a>, BoxError>98     async fn next_playback_buffer<'a>(
99         &'a mut self,
100         ex: &dyn AudioStreamsExecutor,
101     ) -> Result<AsyncPlaybackBuffer<'a>, BoxError> {
102         if let Some(start_time) = self.start_time {
103             let elapsed = start_time.elapsed();
104             if elapsed < self.next_frame {
105                 ex.delay(self.next_frame - elapsed).await?;
106             }
107             self.next_frame += self.interval_ms;
108         } else {
109             self.start_time = Some(Instant::now());
110             self.next_frame = self.interval_ms;
111         }
112 
113         let offset = self.offset.load(Ordering::Relaxed);
114         let buffer = self
115             .memory_mapping
116             .get_slice_mut(offset, self.buffer_mem_length);
117 
118         Ok(AsyncPlaybackBuffer::new(
119             self.frame_size,
120             buffer,
121             &mut self.buffer_drop,
122         )?)
123     }
124 }
125 
126 struct FileStreamSource {
127     file: File,
128     file_size: usize,
129     offset: Arc<AtomicUsize>,
130 }
131 
132 impl FileStreamSource {
new(file: File, file_size: usize, offset: Arc<AtomicUsize>) -> Self133     fn new(file: File, file_size: usize, offset: Arc<AtomicUsize>) -> Self {
134         FileStreamSource {
135             file,
136             file_size,
137             offset,
138         }
139     }
140 }
141 
142 impl StreamSource for FileStreamSource {
new_async_playback_stream( &mut self, num_channels: usize, format: SampleFormat, frame_rate: u32, buffer_size: usize, _ex: &dyn AudioStreamsExecutor, ) -> Result<(Box<dyn StreamControl>, Box<dyn AsyncPlaybackBufferStream>), BoxError>143     fn new_async_playback_stream(
144         &mut self,
145         num_channels: usize,
146         format: SampleFormat,
147         frame_rate: u32,
148         buffer_size: usize,
149         _ex: &dyn AudioStreamsExecutor,
150     ) -> Result<(Box<dyn StreamControl>, Box<dyn AsyncPlaybackBufferStream>), BoxError> {
151         let memory_mapping = MemoryMappingBuilder::new(self.file_size)
152             .from_file(&self.file)
153             .build()
154             .map_err(Error::BuildMemoryMapping)?;
155 
156         let frame_size = format.sample_bytes() * num_channels;
157         let buffer_mem_length = buffer_size * frame_size;
158         let memory_mapping = AudioMemoryMapping::new(memory_mapping, buffer_mem_length);
159         let interval_ms = Duration::from_millis(buffer_size as u64 * 1000 / frame_rate as u64);
160         Ok((
161             Box::new(NoopStreamControl::new()),
162             Box::new(FileStream::new(
163                 memory_mapping,
164                 self.offset.clone(),
165                 frame_size,
166                 buffer_mem_length,
167                 interval_ms,
168             )),
169         ))
170     }
171 
new_playback_stream( &mut self, _num_channels: usize, _format: SampleFormat, _frame_rate: u32, _buffer_size: usize, ) -> Result< ( Box<dyn StreamControl>, Box<dyn audio_streams::PlaybackBufferStream>, ), BoxError, >172     fn new_playback_stream(
173         &mut self,
174         _num_channels: usize,
175         _format: SampleFormat,
176         _frame_rate: u32,
177         _buffer_size: usize,
178     ) -> Result<
179         (
180             Box<dyn StreamControl>,
181             Box<dyn audio_streams::PlaybackBufferStream>,
182         ),
183         BoxError,
184     > {
185         Err(Box::new(Error::Unimplemented))
186     }
187 }
188 
189 /// `FileStreamSourceGenerator` is a struct that implements [`StreamSourceGenerator`]
190 /// for `FileStreamSource`.
191 pub struct FileStreamSourceGenerator {
192     /// File descriptor which will be used to write playback buffer.
193     file: File,
194     /// Size of the output file in bytes.
195     file_size: usize,
196     /// Number of bytes that has been written to the file.
197     offset: Arc<AtomicUsize>,
198 }
199 
200 impl FileStreamSourceGenerator {
201     /// Creates a new `FileStreamSourceGenerator` by given arguments.
202     /// It expects `file` has `file_size` of bytes allocated spaces.
203     ///
204     /// # Arguments
205     ///
206     /// * `file` - The file where audio playback buffer will be written.
207     /// * `file_size` - The size of bytes allocated for playback_file.
new(file: File, file_size: usize) -> Self208     pub fn new(file: File, file_size: usize) -> Self {
209         FileStreamSourceGenerator {
210             file,
211             file_size,
212             offset: Arc::new(AtomicUsize::new(0)),
213         }
214     }
215 }
216 
217 impl StreamSourceGenerator for FileStreamSourceGenerator {
generate(&self) -> Result<Box<dyn StreamSource>, BoxError>218     fn generate(&self) -> Result<Box<dyn StreamSource>, BoxError> {
219         Ok(Box::new(FileStreamSource::new(
220             self.file.try_clone().map_err(Error::Clone)?,
221             self.file_size,
222             self.offset.clone(),
223         )))
224     }
225 }
226 
227 struct FileStreamBufferCommit {
228     frame_size: usize,
229     offset: Arc<AtomicUsize>,
230     max_offset: usize,
231 }
232 
233 #[async_trait(?Send)]
234 impl AsyncBufferCommit for FileStreamBufferCommit {
commit(&mut self, nwritten: usize)235     async fn commit(&mut self, nwritten: usize) {
236         let written_bytes = nwritten * self.frame_size;
237         if self.offset.load(Ordering::Relaxed) + written_bytes < self.max_offset {
238             self.offset.fetch_add(written_bytes, Ordering::Relaxed);
239         }
240     }
241 }
242 
243 struct AudioMemoryMapping {
244     memory_mapping: MemoryMapping,
245     zero_buffer: Vec<u8>,
246 }
247 
248 impl AudioMemoryMapping {
new(memory_mapping: MemoryMapping, buffer_mem_length: usize) -> Self249     fn new(memory_mapping: MemoryMapping, buffer_mem_length: usize) -> Self {
250         AudioMemoryMapping {
251             memory_mapping,
252             zero_buffer: vec![0; buffer_mem_length],
253         }
254     }
255 
get_slice_mut(&mut self, offset: usize, len: usize) -> &mut [u8]256     fn get_slice_mut(&mut self, offset: usize, len: usize) -> &mut [u8] {
257         if offset + len >= self.memory_mapping.size() {
258             warn!("Accessing unallocated region");
259             return &mut self.zero_buffer;
260         }
261         // safe because the region returned is owned by self.memory_mapping
262         unsafe { slice::from_raw_parts_mut(self.memory_mapping.as_ptr().add(offset), len) }
263     }
264 
size(&self) -> usize265     fn size(&self) -> usize {
266         self.memory_mapping.size()
267     }
268 }
269