From 2eda368502ffbf7a0e9fe8aeee781c1e3d7fe9d9 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Tue, 15 Apr 2025 14:46:59 -0700 Subject: [PATCH] Make a few nits for the WebCodecs API. --- web-codecs/src/audio/data.rs | 8 +++---- web-codecs/src/audio/decoder.rs | 10 ++++---- web-codecs/src/audio/encoder.rs | 40 +++++++++++++++---------------- web-codecs/src/video/decoder.rs | 20 +++++++--------- web-codecs/src/video/encoder.rs | 42 +++++++++++++++------------------ 5 files changed, 55 insertions(+), 65 deletions(-) diff --git a/web-codecs/src/audio/data.rs b/web-codecs/src/audio/data.rs index 35cd226..3f338e3 100644 --- a/web-codecs/src/audio/data.rs +++ b/web-codecs/src/audio/data.rs @@ -20,15 +20,15 @@ impl AudioData { self.0.format() } - pub fn sample_rate(&self) -> f32 { - self.0.sample_rate() + pub fn sample_rate(&self) -> u32 { + self.0.sample_rate() as u32 } - pub fn number_of_frames(&self) -> u32 { + pub fn frame_count(&self) -> u32 { self.0.number_of_frames() } - pub fn number_of_channels(&self) -> u32 { + pub fn channel_count(&self) -> u32 { self.0.number_of_channels() } diff --git a/web-codecs/src/audio/decoder.rs b/web-codecs/src/audio/decoder.rs index 041f091..7af9a56 100644 --- a/web-codecs/src/audio/decoder.rs +++ b/web-codecs/src/audio/decoder.rs @@ -14,17 +14,17 @@ pub struct AudioDecoderConfig { pub description: Option, /// The number of channels in the audio. - pub channels: u32, + pub channel_count: u32, /// The sample rate of the audio. pub sample_rate: u32, } impl AudioDecoderConfig { - pub fn new>(codec: T, channels: u32, sample_rate: u32) -> Self { + pub fn new>(codec: T, channel_count: u32, sample_rate: u32) -> Self { Self { codec: codec.into(), - channels, + channel_count, sample_rate, ..Default::default() } @@ -83,7 +83,7 @@ impl AudioDecoderConfig { impl From<&AudioDecoderConfig> for web_sys::AudioDecoderConfig { fn from(this: &AudioDecoderConfig) -> Self { - let config = web_sys::AudioDecoderConfig::new(&this.codec, this.channels, this.sample_rate); + let config = web_sys::AudioDecoderConfig::new(&this.codec, this.channel_count, this.sample_rate); if let Some(description) = &this.description { config.set_description(&js_sys::Uint8Array::from(description.as_ref())); @@ -113,7 +113,7 @@ impl From for AudioDecoderConfig { Self { codec: this.get_codec(), description, - channels, + channel_count: channels, sample_rate, } } diff --git a/web-codecs/src/audio/encoder.rs b/web-codecs/src/audio/encoder.rs index d5aa47c..5ae80c4 100644 --- a/web-codecs/src/audio/encoder.rs +++ b/web-codecs/src/audio/encoder.rs @@ -1,3 +1,5 @@ +use std::{cell::RefCell, rc::Rc}; + use tokio::sync::{mpsc, watch}; use wasm_bindgen::prelude::*; @@ -5,21 +7,22 @@ use crate::{EncodedFrame, Error}; use super::{AudioData, AudioDecoderConfig}; +// TODO support the full specification: https://developer.mozilla.org/en-US/docs/Web/API/AudioEncoder/configure #[derive(Debug, Default, Clone)] pub struct AudioEncoderConfig { pub codec: String, - pub channels: Option, + pub channel_count: Option, pub sample_rate: Option, - pub bit_rate: Option, // bits per second + pub bitrate: Option, // bits per second } impl AudioEncoderConfig { pub fn new>(codec: T) -> Self { Self { codec: codec.into(), - channels: None, + channel_count: None, sample_rate: None, - bit_rate: None, + bitrate: None, } } @@ -38,10 +41,10 @@ impl AudioEncoderConfig { pub fn init(self) -> Result<(AudioEncoder, AudioEncoded), Error> { let (frames_tx, frames_rx) = mpsc::unbounded_channel(); let (closed_tx, closed_rx) = watch::channel(Ok(())); - let (config_tx, config_rx) = watch::channel(None); + let config = Rc::new(RefCell::new(None)); - let decoder = AudioEncoder::new(self, config_tx, frames_tx, closed_tx)?; - let decoded = AudioEncoded::new(config_rx, frames_rx, closed_rx); + let decoder = AudioEncoder::new(self, config.clone(), frames_tx, closed_tx)?; + let decoded = AudioEncoded::new(config, frames_rx, closed_rx); Ok((decoder, decoded)) } @@ -51,7 +54,7 @@ impl From<&AudioEncoderConfig> for web_sys::AudioEncoderConfig { fn from(this: &AudioEncoderConfig) -> Self { let config = web_sys::AudioEncoderConfig::new(&this.codec); - if let Some(channels) = this.channels { + if let Some(channels) = this.channel_count { config.set_number_of_channels(channels); } @@ -59,8 +62,8 @@ impl From<&AudioEncoderConfig> for web_sys::AudioEncoderConfig { config.set_sample_rate(sample_rate); } - if let Some(bit_rate) = this.bit_rate { - config.set_bitrate(bit_rate); + if let Some(bit_rate) = this.bitrate { + config.set_bitrate(bit_rate as f64); } config @@ -81,7 +84,7 @@ pub struct AudioEncoder { impl AudioEncoder { fn new( config: AudioEncoderConfig, - on_config: watch::Sender>, + on_config: Rc>>, on_frame: mpsc::UnboundedSender, on_error: watch::Sender>, ) -> Result { @@ -101,7 +104,7 @@ impl AudioEncoder { if !config.is_falsy() { let config: web_sys::AudioDecoderConfig = config.unchecked_into(); let config = AudioDecoderConfig::from(config); - on_config.send_replace(Some(config)); + on_config.borrow_mut().replace(config); } } } @@ -149,14 +152,14 @@ impl Drop for AudioEncoder { } pub struct AudioEncoded { - config: watch::Receiver>, + config: Rc>>, frames: mpsc::UnboundedReceiver, closed: watch::Receiver>, } impl AudioEncoded { fn new( - config: watch::Receiver>, + config: Rc>>, frames: mpsc::UnboundedReceiver, closed: watch::Receiver>, ) -> Self { @@ -171,12 +174,7 @@ impl AudioEncoded { } } - pub async fn config(&self) -> Option { - self.config - .clone() - .wait_for(|config| config.is_some()) - .await - .ok()? - .clone() + pub fn config(&self) -> Option { + self.config.borrow().clone() } } diff --git a/web-codecs/src/video/decoder.rs b/web-codecs/src/video/decoder.rs index 91593d7..7e13798 100644 --- a/web-codecs/src/video/decoder.rs +++ b/web-codecs/src/video/decoder.rs @@ -45,6 +45,14 @@ impl VideoDecoderConfig { /// Check if the configuration is supported by this browser. /// Returns an error if the configuration is invalid, and false if just unsupported. pub async fn is_supported(&self) -> Result { + if self.resolution.is_none_or(|d| d.width == 0 || d.height == 0) { + return Err(Error::InvalidDimensions); + } + + if self.display.is_none_or(|d| d.width == 0 || d.height == 0) { + return Err(Error::InvalidDimensions); + } + let res = wasm_bindgen_futures::JsFuture::from(web_sys::VideoDecoder::is_config_supported(&self.into())).await?; @@ -56,18 +64,6 @@ impl VideoDecoderConfig { Ok(supported) } - pub fn is_valid(&self) -> Result<(), Error> { - if self.resolution.is_none_or(|d| d.width == 0 || d.height == 0) { - return Err(Error::InvalidDimensions); - } - - if self.display.is_none_or(|d| d.width == 0 || d.height == 0) { - return Err(Error::InvalidDimensions); - } - - Ok(()) - } - pub fn build(self) -> Result<(VideoDecoder, VideoDecoded), Error> { let (frames_tx, frames_rx) = mpsc::unbounded_channel(); let (closed_tx, closed_rx) = watch::channel(Ok(())); diff --git a/web-codecs/src/video/encoder.rs b/web-codecs/src/video/encoder.rs index d6e8048..317c02f 100644 --- a/web-codecs/src/video/encoder.rs +++ b/web-codecs/src/video/encoder.rs @@ -10,7 +10,7 @@ use super::{Dimensions, VideoDecoderConfig, VideoFrame}; use derive_more::Display; #[derive(Debug, Display, Clone, Copy)] -pub enum VideoEncoderBitrate { +pub enum VideoBitrateMode { #[display("constant")] Constant, @@ -28,11 +28,11 @@ pub struct VideoEncoderConfig { pub display: Option, pub hardware_acceleration: Option, pub latency_optimized: Option, - pub bit_rate: Option, // bits per second - pub frame_rate: Option, // frames per second + pub bitrate: Option, // bits per second + pub framerate: Option, // frames per second pub alpha_preserved: Option, // keep alpha channel pub scalability_mode: Option, - pub bitrate_mode: Option, + pub bitrate_mode: Option, // NOTE: This is a custom configuration /// The maximum duration of a Group of Pictures (GOP) before forcing a new keyframe. @@ -47,8 +47,8 @@ impl VideoEncoderConfig { display: None, hardware_acceleration: None, latency_optimized: None, - bit_rate: None, - frame_rate: None, + bitrate: None, + framerate: None, alpha_preserved: None, scalability_mode: None, bitrate_mode: None, @@ -85,10 +85,10 @@ impl VideoEncoderConfig { pub fn init(self) -> Result<(VideoEncoder, VideoEncoded), Error> { let (frames_tx, frames_rx) = mpsc::unbounded_channel(); let (closed_tx, closed_rx) = watch::channel(Ok(())); - let (config_tx, config_rx) = watch::channel(None); + let config = Rc::new(RefCell::new(None)); - let decoder = VideoEncoder::new(self, config_tx, frames_tx, closed_tx)?; - let decoded = VideoEncoded::new(config_rx, frames_rx, closed_rx); + let decoder = VideoEncoder::new(self, config.clone(), frames_tx, closed_tx)?; + let decoded = VideoEncoded::new(config, frames_rx, closed_rx); Ok((decoder, decoded)) } @@ -117,11 +117,11 @@ impl From<&VideoEncoderConfig> for web_sys::VideoEncoderConfig { }); } - if let Some(value) = this.bit_rate { - config.set_bitrate(value); + if let Some(value) = this.bitrate { + config.set_bitrate(value as f64); } - if let Some(value) = this.frame_rate { + if let Some(value) = this.framerate { config.set_framerate(value); } @@ -168,7 +168,7 @@ pub struct VideoEncoder { impl VideoEncoder { fn new( config: VideoEncoderConfig, - on_config: watch::Sender>, + on_config: Rc>>, on_frame: mpsc::UnboundedSender, on_error: watch::Sender>, ) -> Result { @@ -191,7 +191,7 @@ impl VideoEncoder { if !config.is_falsy() { let config: web_sys::VideoDecoderConfig = config.unchecked_into(); let config = VideoDecoderConfig::from(config); - on_config.send_replace(Some(config)); + on_config.borrow_mut().replace(config); } } } @@ -264,14 +264,14 @@ impl Drop for VideoEncoder { } pub struct VideoEncoded { - config: watch::Receiver>, + config: Rc>>, frames: mpsc::UnboundedReceiver, closed: watch::Receiver>, } impl VideoEncoded { fn new( - config: watch::Receiver>, + config: Rc>>, frames: mpsc::UnboundedReceiver, closed: watch::Receiver>, ) -> Self { @@ -286,12 +286,8 @@ impl VideoEncoded { } } - pub async fn config(&self) -> Option { - self.config - .clone() - .wait_for(|config| config.is_some()) - .await - .ok()? - .clone() + /// Returns the decoder config, after the first frame has been encoded. + pub fn config(&self) -> Option { + self.config.borrow().clone() } }