Skip to content

Commit 3e549e5

Browse files
committed
vvenc: improve API and map some enums
- User passes output buffer to encode()/flush() rather than encoder instance having to allocate it. - Map ChromaFormat and SliceType - Add some getter/setters to Config
1 parent d7d414a commit 3e549e5

File tree

3 files changed

+199
-69
lines changed

3 files changed

+199
-69
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
members = ["vvenc-sys"] # , "vvencli"]
33

44
[workspace.package]
5-
name = "vvenc"
65
edition = "2021"
76
rust-version = "1.80"
87
repository = "https://github.com/cadubentzen/vvenc-rs"

src/lib.rs

Lines changed: 187 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use vvenc_sys::*;
88
#[derive(Debug)]
99
pub struct Encoder {
1010
inner: Arc<Mutex<InnerEncoder>>,
11-
au: AccessUnit,
1211
}
1312

1413
#[derive(Debug)]
@@ -34,13 +33,16 @@ impl Encoder {
3433
match ret {
3534
ErrorCodes_VVENC_OK => Ok(Self {
3635
inner: Arc::new(Mutex::new(InnerEncoder { encoder })),
37-
au: AccessUnit::new(&config),
3836
}),
3937
_ => Err(Error::new(ret)),
4038
}
4139
}
4240

43-
pub fn encode<'a>(&mut self, frame: Frame<'a>) -> Result<Option<&AccessUnit>, Error> {
41+
pub fn encode<'a, 'b>(
42+
&mut self,
43+
frame: Frame<'a>,
44+
out_data: &'b mut [u8],
45+
) -> Result<Option<AccessUnit<'b>>, Error> {
4446
let mut yuv_buffer = vvencYUVBuffer {
4547
planes: [
4648
vvencYUVPlane {
@@ -67,12 +69,13 @@ impl Encoder {
6769
ctsValid: frame.cts.is_some(),
6870
};
6971

72+
let mut au = AccessUnit::new(out_data);
7073
let mut encode_done = false;
7174
let ret = unsafe {
7275
vvenc_encode(
7376
self.inner.lock().unwrap().encoder.as_ptr(),
7477
&mut yuv_buffer,
75-
&mut self.au.inner,
78+
&mut au.inner,
7679
&mut encode_done,
7780
)
7881
};
@@ -81,21 +84,17 @@ impl Encoder {
8184
return Err(Error::new(ret));
8285
}
8386

84-
// TODO: double check if encode_done handling is correct or if we lose a frame like that
85-
if self.au.inner.payloadUsedSize == 0 {
86-
return Ok(None);
87-
}
88-
89-
Ok(Some(&self.au))
87+
Ok((!au.payload().is_empty()).then_some(au))
9088
}
9189

92-
pub fn flush(&mut self) -> Result<Option<&AccessUnit>, Error> {
90+
pub fn flush<'b>(&mut self, out_data: &'b mut [u8]) -> Result<Option<AccessUnit<'b>>, Error> {
91+
let mut au = AccessUnit::new(out_data);
9392
let mut encode_done = false;
9493
let ret = unsafe {
9594
vvenc_encode(
9695
self.inner.lock().unwrap().encoder.as_ptr(),
9796
std::ptr::null_mut(),
98-
&mut self.au.inner,
97+
&mut au.inner,
9998
&mut encode_done,
10099
)
101100
};
@@ -104,11 +103,32 @@ impl Encoder {
104103
return Err(Error::new(ret));
105104
}
106105

107-
if self.au.inner.payloadUsedSize == 0 {
108-
return Ok(None);
106+
Ok((!au.payload().is_empty()).then_some(au))
107+
}
108+
109+
pub fn config(&self) -> Result<Config, Error> {
110+
let mut inner = unsafe { std::mem::zeroed() };
111+
let ret =
112+
unsafe { vvenc_get_config(self.inner.lock().unwrap().encoder.as_ptr(), &mut inner) };
113+
if ret != ErrorCodes_VVENC_OK {
114+
return Err(Error::new(ret));
115+
}
116+
Ok(Config { inner })
117+
}
118+
119+
pub fn reconfigure(&mut self, mut config: Config) -> Result<(), Error> {
120+
let ret = unsafe {
121+
vvenc_reconfig(
122+
self.inner.lock().unwrap().encoder.as_ptr(),
123+
&mut config.inner,
124+
)
125+
};
126+
127+
if ret != ErrorCodes_VVENC_OK {
128+
return Err(Error::new(ret));
109129
}
110130

111-
Ok(Some(&self.au))
131+
Ok(())
112132
}
113133
}
114134

@@ -125,9 +145,9 @@ impl Config {
125145
target_bitrate: i32,
126146
qp: i32,
127147
preset: Preset,
128-
) -> Self {
129-
unsafe {
130-
let mut inner = std::mem::zeroed();
148+
) -> Result<Self, Error> {
149+
let mut inner = unsafe { std::mem::zeroed() };
150+
let ret = unsafe {
131151
vvenc_init_default(
132152
&mut inner,
133153
width,
@@ -136,37 +156,38 @@ impl Config {
136156
target_bitrate,
137157
qp,
138158
preset.to_ffi(),
139-
);
140-
Self { inner }
159+
)
160+
};
161+
162+
if ret != ErrorCodes_VVENC_OK {
163+
return Err(Error::new(ret));
141164
}
165+
166+
Ok(Self { inner })
142167
}
143-
}
144168

145-
#[derive(Debug, Clone, Copy, PartialEq)]
146-
pub enum Preset {
147-
Faster,
148-
Fast,
149-
Medium,
150-
Slow,
151-
Slower,
152-
MediumLowDecNrg,
153-
FirstPass,
154-
ToolTest,
155-
}
169+
pub fn source_width(&self) -> i32 {
170+
self.inner.m_SourceWidth
171+
}
156172

157-
impl Preset {
158-
#[inline]
159-
fn to_ffi(self) -> vvencPresetMode {
160-
match self {
161-
Self::Faster => vvencPresetMode_VVENC_FASTER,
162-
Self::Fast => vvencPresetMode_VVENC_FAST,
163-
Self::Medium => vvencPresetMode_VVENC_MEDIUM,
164-
Self::Slow => vvencPresetMode_VVENC_SLOW,
165-
Self::Slower => vvencPresetMode_VVENC_SLOWER,
166-
Self::MediumLowDecNrg => vvencPresetMode_VVENC_MEDIUM_LOWDECNRG,
167-
Self::FirstPass => vvencPresetMode_VVENC_FIRSTPASS,
168-
Self::ToolTest => vvencPresetMode_VVENC_TOOLTEST,
173+
pub fn source_height(&self) -> i32 {
174+
self.inner.m_SourceHeight
175+
}
176+
177+
pub fn chroma_format(&self) -> ChromaFormat {
178+
ChromaFormat::from_ffi(self.inner.m_internChromaFormat)
179+
}
180+
181+
pub fn set_chroma_format(&mut self, chroma_format: ChromaFormat) {
182+
self.inner.m_internChromaFormat = chroma_format.to_ffi();
183+
}
184+
185+
pub fn set_preset(&mut self, preset: Preset) -> Result<(), Error> {
186+
let ret = unsafe { vvenc_init_preset(&mut self.inner, preset.to_ffi()) };
187+
if ret != ErrorCodes_VVENC_OK {
188+
return Err(Error::new(ret));
169189
}
190+
Ok(())
170191
}
171192
}
172193

@@ -225,35 +246,138 @@ pub struct Plane<'a> {
225246
}
226247

227248
#[derive(Debug)]
228-
pub struct AccessUnit {
229-
// FIXME: make inner private
230-
pub inner: vvencAccessUnit,
249+
pub struct AccessUnit<'a> {
250+
inner: vvencAccessUnit,
251+
data: &'a [u8],
231252
}
232253

233-
impl AccessUnit {
234-
fn new(config: &Config) -> Self {
235-
// Allocate enough space for the AU payloads from the given config. Same as in EncApp.cpp from libvvenc
236-
#[allow(non_upper_case_globals)]
237-
let au_size_scale = match config.inner.m_internChromaFormat {
238-
vvencChromaFormat_VVENC_CHROMA_400 | vvencChromaFormat_VVENC_CHROMA_420 => 2,
239-
_ => 3,
240-
};
241-
let payload_size =
242-
au_size_scale * config.inner.m_SourceHeight * config.inner.m_SourceWidth + 1024;
254+
impl<'a> AccessUnit<'a> {
255+
fn new(data: &'a mut [u8]) -> Self {
243256
let inner = unsafe {
244257
let mut inner = std::mem::zeroed();
245258
vvenc_accessUnit_default(&mut inner);
246-
vvenc_accessUnit_alloc_payload(&mut inner, payload_size);
259+
inner.payload = data.as_mut_ptr();
260+
inner.payloadSize = data.len() as i32;
261+
inner.payloadUsedSize = 0;
247262
inner
248263
};
249-
Self { inner }
264+
Self { inner, data }
265+
}
266+
267+
pub fn payload(&self) -> &[u8] {
268+
&self.data[..self.inner.payloadUsedSize as usize]
269+
}
270+
271+
pub fn cts(&self) -> Option<u64> {
272+
self.inner.ctsValid.then_some(self.inner.cts)
273+
}
274+
275+
pub fn dts(&self) -> Option<u64> {
276+
self.inner.dtsValid.then_some(self.inner.dts)
277+
}
278+
279+
pub fn rap(&self) -> bool {
280+
self.inner.rap
281+
}
282+
283+
pub fn slice_type(&self) -> SliceType {
284+
SliceType::from_ffi(self.inner.sliceType)
285+
}
286+
287+
pub fn is_ref_pic(&self) -> bool {
288+
self.inner.refPic
289+
}
290+
291+
pub fn temporal_layer(&self) -> i32 {
292+
self.inner.temporalLayer
293+
}
294+
295+
pub fn poc(&self) -> u64 {
296+
self.inner.poc
250297
}
251298
}
252299

253-
impl Drop for AccessUnit {
254-
fn drop(&mut self) {
255-
unsafe {
256-
vvenc_accessUnit_free_payload(&mut self.inner);
300+
#[derive(Debug, Clone, Copy, PartialEq)]
301+
pub enum Preset {
302+
Faster,
303+
Fast,
304+
Medium,
305+
Slow,
306+
Slower,
307+
MediumLowDecNrg,
308+
FirstPass,
309+
ToolTest,
310+
Unknown(i32),
311+
}
312+
313+
impl Preset {
314+
#[inline]
315+
fn to_ffi(self) -> vvencPresetMode {
316+
match self {
317+
Self::Faster => vvencPresetMode_VVENC_FASTER,
318+
Self::Fast => vvencPresetMode_VVENC_FAST,
319+
Self::Medium => vvencPresetMode_VVENC_MEDIUM,
320+
Self::Slow => vvencPresetMode_VVENC_SLOW,
321+
Self::Slower => vvencPresetMode_VVENC_SLOWER,
322+
Self::MediumLowDecNrg => vvencPresetMode_VVENC_MEDIUM_LOWDECNRG,
323+
Self::FirstPass => vvencPresetMode_VVENC_FIRSTPASS,
324+
Self::ToolTest => vvencPresetMode_VVENC_TOOLTEST,
325+
Self::Unknown(value) => value,
326+
}
327+
}
328+
}
329+
330+
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
331+
pub enum ChromaFormat {
332+
Chroma400,
333+
Chroma420,
334+
Chroma422,
335+
Chroma444,
336+
Unknown(u32),
337+
}
338+
339+
impl ChromaFormat {
340+
#[inline]
341+
fn to_ffi(self) -> vvencChromaFormat {
342+
match self {
343+
Self::Chroma400 => vvencChromaFormat_VVENC_CHROMA_400,
344+
Self::Chroma420 => vvencChromaFormat_VVENC_CHROMA_420,
345+
Self::Chroma422 => vvencChromaFormat_VVENC_CHROMA_422,
346+
Self::Chroma444 => vvencChromaFormat_VVENC_CHROMA_444,
347+
Self::Unknown(value) => value,
348+
}
349+
}
350+
351+
#[inline]
352+
fn from_ffi(value: vvencChromaFormat) -> Self {
353+
#[allow(non_upper_case_globals)]
354+
match value {
355+
vvencChromaFormat_VVENC_CHROMA_400 => Self::Chroma400,
356+
vvencChromaFormat_VVENC_CHROMA_420 => Self::Chroma420,
357+
vvencChromaFormat_VVENC_CHROMA_422 => Self::Chroma422,
358+
vvencChromaFormat_VVENC_CHROMA_444 => Self::Chroma444,
359+
_ => Self::Unknown(value),
360+
}
361+
}
362+
}
363+
364+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
365+
pub enum SliceType {
366+
B,
367+
P,
368+
I,
369+
Unknown(u32),
370+
}
371+
372+
impl SliceType {
373+
#[inline]
374+
fn from_ffi(value: vvencSliceType) -> Self {
375+
#[allow(non_upper_case_globals)]
376+
match value {
377+
vvencSliceType_VVENC_B_SLICE => Self::B,
378+
vvencSliceType_VVENC_P_SLICE => Self::P,
379+
vvencSliceType_VVENC_I_SLICE => Self::I,
380+
_ => Self::Unknown(value),
257381
}
258382
}
259383
}

tests/basic.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,15 @@ use vvenc::*;
44
fn basic() {
55
const WIDTH: i32 = 160;
66
const HEIGHT: i32 = 120;
7-
let config = Config::new(WIDTH, HEIGHT, 24, 0, 32, Preset::Faster);
7+
let config = Config::new(WIDTH, HEIGHT, 24, 0, 32, Preset::Faster).unwrap();
88
let mut encoder = Encoder::new(config).unwrap();
9+
let config = encoder.config().unwrap();
10+
let au_size_scale = match config.chroma_format() {
11+
ChromaFormat::Chroma400 | ChromaFormat::Chroma420 => 2,
12+
_ => 3,
13+
};
14+
let mut data =
15+
vec![0u8; (au_size_scale * config.source_height() * config.source_width() + 1024) as usize];
916

1017
let y_size = (WIDTH * HEIGHT) as usize;
1118
let uv_size = (WIDTH * HEIGHT / 4) as usize;
@@ -38,8 +45,8 @@ fn basic() {
3845
cts: None,
3946
};
4047

41-
assert!(encoder.encode(frame).unwrap().is_none());
42-
let au = encoder.flush().unwrap().unwrap();
43-
assert!(au.inner.payloadUsedSize > 0);
44-
assert!(encoder.flush().unwrap_err() == Error::RestartRequired);
48+
assert!(encoder.encode(frame, &mut data).unwrap().is_none());
49+
let au = encoder.flush(&mut data).unwrap().unwrap();
50+
assert!(au.payload().len() > 0);
51+
assert!(encoder.flush(&mut data).unwrap_err() == Error::RestartRequired);
4552
}

0 commit comments

Comments
 (0)