Skip to main content

wmv_decoder/
ffi.rs

1//! Minimal C ABI for WMV2 decoding.
2//!
3//! This module is behind the `ffi` feature.
4//! It decodes one assembled WMV2 frame payload at a time and exposes the internal
5//! YUV420p planes.
6
7use std::ffi::c_void;
8
9use crate::api::Wmv2Decoder;
10
11#[repr(C)]
12pub struct Wmv2FrameView {
13    pub width: u32,
14    pub height: u32,
15
16    pub y_ptr: *const u8,
17    pub y_len: usize,
18    pub y_stride: usize,
19
20    pub cb_ptr: *const u8,
21    pub cb_len: usize,
22    pub cb_stride: usize,
23
24    pub cr_ptr: *const u8,
25    pub cr_len: usize,
26    pub cr_stride: usize,
27}
28
29struct Opaque {
30    dec: Wmv2Decoder,
31    has_frame: bool,
32}
33
34/// Create a WMV2 decoder.
35///
36/// `extradata` is copied.
37///
38/// # Safety
39///
40/// If `extradata` is non-null and `extradata_len` is nonzero, it must point to
41/// `extradata_len` initialized bytes in one allocation, readable and unchanged
42/// for this call. The length must not exceed `isize::MAX`.
43#[unsafe(no_mangle)]
44pub unsafe extern "C" fn wmv2_decoder_create(
45    width: u32,
46    height: u32,
47    extradata: *const u8,
48    extradata_len: usize,
49) -> *mut c_void {
50    let extra = unsafe {
51        if extradata.is_null() || extradata_len == 0 {
52            &[][..]
53        } else {
54            std::slice::from_raw_parts(extradata, extradata_len)
55        }
56    };
57
58    let dec = Wmv2Decoder::new(width, height, extra);
59    let opaque = Box::new(Opaque {
60        dec,
61        has_frame: false,
62    });
63    Box::into_raw(opaque) as *mut c_void
64}
65
66/// Destroy a WMV2 decoder. A null handle is ignored.
67///
68/// # Safety
69///
70/// A non-null `handle` must be a live handle returned by `wmv2_decoder_create`.
71/// No other call may use it concurrently. After this call, neither the handle
72/// nor any frame pointers obtained from it may be used.
73#[unsafe(no_mangle)]
74pub unsafe extern "C" fn wmv2_decoder_destroy(handle: *mut c_void) {
75    if handle.is_null() {
76        return;
77    }
78    unsafe {
79        let _ = Box::from_raw(handle as *mut Opaque);
80    }
81}
82
83/// Decode one assembled WMV2 frame payload.
84///
85/// Returns:
86///   1  = decoded frame available
87///   0  = no frame (header not found)
88///  -1  = invalid arguments
89///  -2  = decode error
90///
91/// # Safety
92///
93/// A non-null `handle` must be a live handle returned by `wmv2_decoder_create`,
94/// with exclusive access for this call. When all arguments pass the null and
95/// length checks, `payload` must point to `payload_len` initialized bytes in
96/// one allocation, readable and unchanged for this call. The length must not
97/// exceed `isize::MAX`, and the payload must not overlap decoder-owned memory.
98/// Previously returned frame pointers must not be accessed during this call.
99#[unsafe(no_mangle)]
100pub unsafe extern "C" fn wmv2_decoder_decode(
101    handle: *mut c_void,
102    payload: *const u8,
103    payload_len: usize,
104    is_key_frame: i32,
105) -> i32 {
106    if handle.is_null() {
107        return -1;
108    }
109    if payload.is_null() || payload_len == 0 {
110        return -1;
111    }
112
113    let opaque = unsafe { &mut *(handle as *mut Opaque) };
114    let data = unsafe { std::slice::from_raw_parts(payload, payload_len) };
115
116    match opaque.dec.decode_frame(data, is_key_frame != 0) {
117        Ok(Some(_)) => {
118            opaque.has_frame = true;
119            1
120        }
121        Ok(None) => {
122            opaque.has_frame = false;
123            0
124        }
125        Err(_) => {
126            opaque.has_frame = false;
127            -2
128        }
129    }
130}
131
132/// Get the most recently decoded frame view.
133///
134/// The returned pointers may be read until the next decode or destroy call.
135/// They must not be used to modify the frame data.
136///
137/// Returns:
138///   1  = success
139///   0  = no decoded frame available
140///  -1  = invalid arguments
141///
142/// # Safety
143///
144/// A non-null `handle` must be a live handle returned by `wmv2_decoder_create`,
145/// with exclusive access for this call. If both pointers are non-null, `out`
146/// must be aligned and valid for writing one `Wmv2FrameView`, and must not
147/// overlap decoder-owned memory. No other access to `out` may occur during
148/// this call.
149#[unsafe(no_mangle)]
150pub unsafe extern "C" fn wmv2_decoder_get_frame(
151    handle: *mut c_void,
152    out: *mut Wmv2FrameView,
153) -> i32 {
154    if handle.is_null() || out.is_null() {
155        return -1;
156    }
157
158    let opaque = unsafe { &mut *(handle as *mut Opaque) };
159    if !opaque.has_frame {
160        return 0;
161    }
162
163    let f = opaque.dec.current_frame();
164    let y_stride = f.width as usize;
165    let uv_stride = (f.width as usize) / 2;
166
167    unsafe {
168        *out = Wmv2FrameView {
169            width: f.width,
170            height: f.height,
171            y_ptr: f.y.as_ptr(),
172            y_len: f.y.len(),
173            y_stride,
174            cb_ptr: f.cb.as_ptr(),
175            cb_len: f.cb.len(),
176            cb_stride: uv_stride,
177            cr_ptr: f.cr.as_ptr(),
178            cr_len: f.cr.len(),
179            cr_stride: uv_stride,
180        };
181    }
182
183    1
184}