web_codecs/audio/
encoder.rs1use std::{cell::RefCell, rc::Rc};
2
3use tokio::sync::{mpsc, watch};
4use wasm_bindgen::prelude::*;
5
6use crate::{EncodedFrame, Error};
7
8use super::{AudioData, AudioDecoderConfig};
9
10#[derive(Debug, Default, Clone)]
12pub struct AudioEncoderConfig {
13 pub codec: String,
14 pub channel_count: Option<u32>,
15 pub sample_rate: Option<u32>,
16 pub bitrate: Option<u32>, }
18
19impl AudioEncoderConfig {
20 pub fn new<T: Into<String>>(codec: T) -> Self {
21 Self {
22 codec: codec.into(),
23 channel_count: None,
24 sample_rate: None,
25 bitrate: None,
26 }
27 }
28
29 pub async fn is_supported(&self) -> Result<bool, Error> {
30 let res =
31 wasm_bindgen_futures::JsFuture::from(web_sys::AudioEncoder::is_config_supported(&self.into())).await?;
32
33 let supported = js_sys::Reflect::get(&res, &JsValue::from_str("supported"))
34 .unwrap()
35 .as_bool()
36 .unwrap();
37
38 Ok(supported)
39 }
40
41 pub fn init(self) -> Result<(AudioEncoder, AudioEncoded), Error> {
42 let (frames_tx, frames_rx) = mpsc::unbounded_channel();
43 let (closed_tx, closed_rx) = watch::channel(Ok(()));
44 let config = Rc::new(RefCell::new(None));
45
46 let decoder = AudioEncoder::new(self, config.clone(), frames_tx, closed_tx)?;
47 let decoded = AudioEncoded::new(config, frames_rx, closed_rx);
48
49 Ok((decoder, decoded))
50 }
51}
52
53impl From<&AudioEncoderConfig> for web_sys::AudioEncoderConfig {
54 fn from(this: &AudioEncoderConfig) -> Self {
55 let config = web_sys::AudioEncoderConfig::new(&this.codec);
56
57 if let Some(channels) = this.channel_count {
58 config.set_number_of_channels(channels);
59 }
60
61 if let Some(sample_rate) = this.sample_rate {
62 config.set_sample_rate(sample_rate);
63 }
64
65 if let Some(bit_rate) = this.bitrate {
66 config.set_bitrate(bit_rate as f64);
67 }
68
69 config
70 }
71}
72
73pub struct AudioEncoder {
74 inner: web_sys::AudioEncoder,
75 config: AudioEncoderConfig,
76
77 #[allow(dead_code)]
79 on_error: Closure<dyn FnMut(JsValue)>,
80 #[allow(dead_code)]
81 on_frame: Closure<dyn FnMut(JsValue, JsValue)>,
82}
83
84impl AudioEncoder {
85 fn new(
86 config: AudioEncoderConfig,
87 on_config: Rc<RefCell<Option<AudioDecoderConfig>>>,
88 on_frame: mpsc::UnboundedSender<EncodedFrame>,
89 on_error: watch::Sender<Result<(), Error>>,
90 ) -> Result<Self, Error> {
91 let on_error2 = on_error.clone();
92 let on_error = Closure::wrap(Box::new(move |e: JsValue| {
93 on_error.send_replace(Err(Error::from(e))).ok();
94 }) as Box<dyn FnMut(_)>);
95
96 let on_frame = Closure::wrap(Box::new(move |frame: JsValue, meta: JsValue| {
97 let frame: web_sys::EncodedAudioChunk = frame.unchecked_into();
99 let frame = EncodedFrame::from(frame);
100
101 if let Ok(metadata) = meta.dyn_into::<js_sys::Object>() {
102 if let Ok(config) = js_sys::Reflect::get(&metadata, &"decoderConfig".into()) {
104 if !config.is_falsy() {
105 let config: web_sys::AudioDecoderConfig = config.unchecked_into();
106 let config = AudioDecoderConfig::from(config);
107 on_config.borrow_mut().replace(config);
108 }
109 }
110 }
111
112 if on_frame.send(frame).is_err() {
113 on_error2.send_replace(Err(Error::Dropped)).ok();
114 }
115 }) as Box<dyn FnMut(_, _)>);
116
117 let init = web_sys::AudioEncoderInit::new(on_error.as_ref().unchecked_ref(), on_frame.as_ref().unchecked_ref());
118 let inner: web_sys::AudioEncoder = web_sys::AudioEncoder::new(&init).unwrap();
119 inner.configure(&(&config).into())?;
120
121 Ok(Self {
122 config,
123 inner,
124 on_error,
125 on_frame,
126 })
127 }
128
129 pub fn encode(&mut self, frame: &AudioData) -> Result<(), Error> {
130 self.inner.encode(frame)?;
131 Ok(())
132 }
133
134 pub fn queue_size(&self) -> u32 {
135 self.inner.encode_queue_size()
136 }
137
138 pub fn config(&self) -> &AudioEncoderConfig {
139 &self.config
140 }
141
142 pub async fn flush(&mut self) -> Result<(), Error> {
143 wasm_bindgen_futures::JsFuture::from(self.inner.flush()).await?;
144 Ok(())
145 }
146}
147
148impl Drop for AudioEncoder {
149 fn drop(&mut self) {
150 let _ = self.inner.close();
151 }
152}
153
154pub struct AudioEncoded {
155 config: Rc<RefCell<Option<AudioDecoderConfig>>>,
156 frames: mpsc::UnboundedReceiver<EncodedFrame>,
157 closed: watch::Receiver<Result<(), Error>>,
158}
159
160impl AudioEncoded {
161 fn new(
162 config: Rc<RefCell<Option<AudioDecoderConfig>>>,
163 frames: mpsc::UnboundedReceiver<EncodedFrame>,
164 closed: watch::Receiver<Result<(), Error>>,
165 ) -> Self {
166 Self { config, frames, closed }
167 }
168
169 pub async fn frame(&mut self) -> Result<Option<EncodedFrame>, Error> {
170 tokio::select! {
171 biased;
172 frame = self.frames.recv() => Ok(frame),
173 Ok(()) = self.closed.changed() => Err(self.closed.borrow().clone().err().unwrap()),
174 }
175 }
176
177 pub fn config(&self) -> Option<AudioDecoderConfig> {
178 self.config.borrow().clone()
179 }
180}