web_codecs/audio/
decoder.rs

1use bytes::{Bytes, BytesMut};
2use tokio::sync::{mpsc, watch};
3use wasm_bindgen::prelude::*;
4
5use super::AudioData;
6use crate::{EncodedFrame, Error};
7
8#[derive(Debug, Default, Clone)]
9pub struct AudioDecoderConfig {
10	/// The codec mimetype string.
11	pub codec: String,
12
13	/// Some codec formats use a description to configure the decoder.
14	pub description: Option<Bytes>,
15
16	/// The number of channels in the audio.
17	pub channel_count: u32,
18
19	/// The sample rate of the audio.
20	pub sample_rate: u32,
21}
22
23impl AudioDecoderConfig {
24	pub fn new<T: Into<String>>(codec: T, channel_count: u32, sample_rate: u32) -> Self {
25		Self {
26			codec: codec.into(),
27			channel_count,
28			sample_rate,
29			..Default::default()
30		}
31	}
32
33	/// Check if the configuration is supported by this browser.
34	/// Returns an error if the configuration is invalid, and false if just unsupported.
35	pub async fn is_supported(&self) -> Result<bool, Error> {
36		let res =
37			wasm_bindgen_futures::JsFuture::from(web_sys::AudioDecoder::is_config_supported(&self.into())).await?;
38
39		let supported = js_sys::Reflect::get(&res, &JsValue::from_str("supported"))
40			.unwrap()
41			.as_bool()
42			.unwrap();
43
44		Ok(supported)
45	}
46
47	pub fn build(self) -> Result<(AudioDecoder, AudioDecoded), Error> {
48		let (frames_tx, frames_rx) = mpsc::unbounded_channel();
49		let (closed_tx, closed_rx) = watch::channel(Ok(()));
50		let closed_tx2 = closed_tx.clone();
51
52		let on_error = Closure::wrap(Box::new(move |e: JsValue| {
53			closed_tx.send_replace(Err(Error::from(e))).ok();
54		}) as Box<dyn FnMut(_)>);
55
56		let on_frame = Closure::wrap(Box::new(move |e: JsValue| {
57			let frame: web_sys::AudioData = e.unchecked_into();
58			let frame = AudioData::from(frame);
59
60			if frames_tx.send(frame).is_err() {
61				closed_tx2.send_replace(Err(Error::Dropped)).ok();
62			}
63		}) as Box<dyn FnMut(_)>);
64
65		let init = web_sys::AudioDecoderInit::new(on_error.as_ref().unchecked_ref(), on_frame.as_ref().unchecked_ref());
66		let inner: web_sys::AudioDecoder = web_sys::AudioDecoder::new(&init).unwrap();
67		inner.configure(&(&self).into())?;
68
69		let decoder = AudioDecoder {
70			inner,
71			on_error,
72			on_frame,
73		};
74
75		let decoded = AudioDecoded {
76			frames: frames_rx,
77			closed: closed_rx,
78		};
79
80		Ok((decoder, decoded))
81	}
82}
83
84impl From<&AudioDecoderConfig> for web_sys::AudioDecoderConfig {
85	fn from(this: &AudioDecoderConfig) -> Self {
86		let config = web_sys::AudioDecoderConfig::new(&this.codec, this.channel_count, this.sample_rate);
87
88		if let Some(description) = &this.description {
89			config.set_description(&js_sys::Uint8Array::from(description.as_ref()));
90		}
91
92		config
93	}
94}
95
96impl From<web_sys::AudioDecoderConfig> for AudioDecoderConfig {
97	fn from(this: web_sys::AudioDecoderConfig) -> Self {
98		let description = this.get_description().map(|d| {
99			// TODO: An ArrayBuffer, a TypedArray, or a DataView containing a sequence of codec-specific bytes, commonly known as "extradata".
100			let buffer = js_sys::Uint8Array::new(&d);
101			let size = buffer.byte_length() as usize;
102
103			let mut payload = BytesMut::with_capacity(size);
104			payload.resize(size, 0);
105			buffer.copy_to(&mut payload);
106
107			payload.freeze()
108		});
109
110		let channels = this.get_number_of_channels();
111		let sample_rate = this.get_sample_rate();
112
113		Self {
114			codec: this.get_codec(),
115			description,
116			channel_count: channels,
117			sample_rate,
118		}
119	}
120}
121
122pub struct AudioDecoder {
123	inner: web_sys::AudioDecoder,
124
125	// These are held to avoid dropping them.
126	#[allow(dead_code)]
127	on_error: Closure<dyn FnMut(JsValue)>,
128	#[allow(dead_code)]
129	on_frame: Closure<dyn FnMut(JsValue)>,
130}
131
132impl AudioDecoder {
133	pub fn decode(&self, frame: EncodedFrame) -> Result<(), Error> {
134		let chunk_type = match frame.keyframe {
135			true => web_sys::EncodedAudioChunkType::Key,
136			false => web_sys::EncodedAudioChunkType::Delta,
137		};
138
139		let chunk = web_sys::EncodedAudioChunkInit::new(
140			&js_sys::Uint8Array::from(frame.payload.as_ref()),
141			frame.timestamp.as_micros() as _,
142			chunk_type,
143		);
144
145		let chunk = web_sys::EncodedAudioChunk::new(&chunk)?;
146		self.inner.decode(&chunk)?;
147
148		Ok(())
149	}
150
151	pub async fn flush(&self) -> Result<(), Error> {
152		wasm_bindgen_futures::JsFuture::from(self.inner.flush()).await?;
153		Ok(())
154	}
155
156	pub fn queue_size(&self) -> u32 {
157		self.inner.decode_queue_size()
158	}
159}
160
161impl Drop for AudioDecoder {
162	fn drop(&mut self) {
163		let _ = self.inner.close();
164	}
165}
166
167pub struct AudioDecoded {
168	frames: mpsc::UnboundedReceiver<AudioData>,
169	closed: watch::Receiver<Result<(), Error>>,
170}
171
172impl AudioDecoded {
173	pub async fn next(&mut self) -> Result<Option<AudioData>, Error> {
174		tokio::select! {
175			biased;
176			frame = self.frames.recv() => Ok(frame),
177			Ok(()) = self.closed.changed() => Err(self.closed.borrow().clone().err().unwrap()),
178		}
179	}
180}