use crate::{codec::UnifiedOpusDecoder, AudioPacket, NetEq, NetEqConfig, RtpHeader};
#[cfg(feature = "matomo-logger")]
use matomo_logger::worker as matomo_worker;
use serde_wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct WebNetEq {
neteq: std::cell::RefCell<Option<NetEq>>,
sample_rate: u32,
channels: u8,
additional_delay_ms: u32,
}
#[wasm_bindgen]
impl WebNetEq {
#[wasm_bindgen(constructor)]
pub fn new(
sample_rate: u32,
channels: u8,
additional_delay_ms: u32,
) -> Result<WebNetEq, JsValue> {
Ok(WebNetEq {
neteq: std::cell::RefCell::new(None), sample_rate,
channels,
additional_delay_ms,
})
}
#[wasm_bindgen]
pub async fn init(&self) -> Result<(), JsValue> {
let cfg = NetEqConfig {
sample_rate: self.sample_rate,
channels: self.channels,
additional_delay_ms: self.additional_delay_ms,
..Default::default()
};
let mut neteq = NetEq::new(cfg).map_err(Self::map_err)?;
let decoder = UnifiedOpusDecoder::new(self.sample_rate, self.channels)
.await
.map_err(Self::map_err)?;
log::info!("NetEq initialized with decoder: {}", decoder.decoder_type());
neteq.register_decoder(111, Box::new(decoder));
*self.neteq.borrow_mut() = Some(neteq);
Ok(())
}
#[wasm_bindgen(js_name = isInitialized)]
pub fn is_initialized(&self) -> bool {
self.neteq.borrow().is_some()
}
#[wasm_bindgen]
pub fn insert_packet(
&self,
seq_no: u16,
timestamp: u32,
payload: &[u8],
) -> Result<(), JsValue> {
let mut neteq_ref = self.neteq.borrow_mut();
let neteq = neteq_ref
.as_mut()
.ok_or_else(|| JsValue::from_str("NetEq not initialized. Call init() first."))?;
let hdr = RtpHeader::new(seq_no, timestamp, 0x1234_5678, 111, false);
let packet = AudioPacket::new(hdr, payload.to_vec(), self.sample_rate, self.channels, 20);
neteq.insert_packet(packet).map_err(Self::map_err)
}
#[wasm_bindgen]
pub fn get_audio(&self) -> Result<js_sys::Float32Array, JsValue> {
let mut neteq_ref = self.neteq.borrow_mut();
let neteq = neteq_ref
.as_mut()
.ok_or_else(|| JsValue::from_str("NetEq not initialized. Call init() first."))?;
let frame = neteq.get_audio().map_err(Self::map_err)?;
let out = js_sys::Float32Array::from(frame.samples.as_slice());
Ok(out)
}
#[wasm_bindgen(js_name = getStatistics)]
pub fn get_statistics(&self) -> Result<JsValue, JsValue> {
let neteq_ref = self.neteq.borrow();
let neteq = neteq_ref
.as_ref()
.ok_or_else(|| JsValue::from_str("NetEq not initialized. Call init() first."))?;
let stats = neteq.get_statistics();
serde_wasm_bindgen::to_value(&stats).map_err(|e| JsValue::from_str(&format!("{:?}", e)))
}
fn map_err(e: crate::NetEqError) -> JsValue {
JsValue::from_str(&e.to_string())
}
}
#[wasm_bindgen(js_name = initNetEq)]
pub fn init_net_eq() {
#[cfg(all(feature = "matomo-logger", target_arch = "wasm32"))]
{
use log::LevelFilter;
if let Err(_e) = matomo_worker::init_with_bridge(LevelFilter::Info, LevelFilter::Debug, {
js_sys::Function::new_no_args("self.postMessage(arguments[0]);")
}) {
use web_sys::console;
console::error_1(&"[neteq-worker] Failed to initialize matomo worker bridge".into());
}
}
}