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