T4.2: VideoToolbox H.264 encoder/decoder traits (macOS, MVP)
This commit is contained in:
107
crates/wzp-video/src/videotoolbox.rs
Normal file
107
crates/wzp-video/src/videotoolbox.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
//! Apple VideoToolbox H.264 encoder / decoder (macOS only).
|
||||
|
||||
use crate::decoder::VideoDecoder;
|
||||
use crate::encoder::{VideoEncoder, VideoError, VideoFrame};
|
||||
|
||||
/// macOS VideoToolbox H.264 encoder.
|
||||
///
|
||||
/// Wraps `VTCompressionSession`. Minimum viable: API compiles and is
|
||||
/// instantiable; full hardware encode/decode lands in a follow-up task.
|
||||
pub struct VideoToolboxEncoder {
|
||||
width: u32,
|
||||
height: u32,
|
||||
bitrate_bps: u32,
|
||||
force_keyframe: bool,
|
||||
}
|
||||
|
||||
impl VideoToolboxEncoder {
|
||||
/// Create a new encoder.
|
||||
///
|
||||
/// * `width` / `height` — frame dimensions in pixels.
|
||||
/// * `bitrate_bps` — target bitrate in bits per second.
|
||||
pub fn new(width: u32, height: u32, bitrate_bps: u32) -> Result<Self, VideoError> {
|
||||
Ok(Self {
|
||||
width,
|
||||
height,
|
||||
bitrate_bps,
|
||||
force_keyframe: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoEncoder for VideoToolboxEncoder {
|
||||
fn encode(&mut self, _frame: &VideoFrame) -> Result<Vec<u8>, VideoError> {
|
||||
// TODO(T4.2-MVP): Wire VTCompressionSession.
|
||||
// For now return an empty AU so the API compiles and callers can
|
||||
// integrate the shape.
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
fn request_keyframe(&mut self) {
|
||||
self.force_keyframe = true;
|
||||
}
|
||||
|
||||
fn is_keyframe(&self, packet: &[u8]) -> bool {
|
||||
if packet.is_empty() {
|
||||
return false;
|
||||
}
|
||||
let nal_type = packet[0] & 0x1F;
|
||||
// NAL type 5 = IDR slice (keyframe).
|
||||
nal_type == 5
|
||||
}
|
||||
}
|
||||
|
||||
/// macOS VideoToolbox H.264 decoder.
|
||||
///
|
||||
/// Wraps `VTDecompressionSession`. Minimum viable: API compiles and is
|
||||
/// instantiable.
|
||||
pub struct VideoToolboxDecoder {
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
impl VideoToolboxDecoder {
|
||||
/// Create a new decoder.
|
||||
pub fn new(width: u32, height: u32) -> Result<Self, VideoError> {
|
||||
Ok(Self { width, height })
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoDecoder for VideoToolboxDecoder {
|
||||
fn decode(&mut self, _access_unit: &[u8]) -> Result<Option<VideoFrame>, VideoError> {
|
||||
// TODO(T4.2-MVP): Wire VTDecompressionSession.
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn encoder_instantiates() {
|
||||
let enc = VideoToolboxEncoder::new(1280, 720, 2_000_000);
|
||||
assert!(enc.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decoder_instantiates() {
|
||||
let dec = VideoToolboxDecoder::new(1280, 720);
|
||||
assert!(dec.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn is_keyframe_detects_idr() {
|
||||
let enc = VideoToolboxEncoder::new(1280, 720, 2_000_000).unwrap();
|
||||
assert!(enc.is_keyframe(&[0x65, 0x01, 0x02]));
|
||||
assert!(!enc.is_keyframe(&[0x41, 0x01, 0x02]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_keyframe_sets_flag() {
|
||||
let mut enc = VideoToolboxEncoder::new(1280, 720, 2_000_000).unwrap();
|
||||
assert!(!enc.force_keyframe);
|
||||
enc.request_keyframe();
|
||||
assert!(enc.force_keyframe);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user