1use std::ffi::CString;
21use std::ptr;
22use std::sync::{Condvar, Mutex};
23
24use codec_sys::svtav1::*;
25
26use super::codec::{
27 Codec, FRAME_ANCHOR, VIDEO_HEADER_LEN, VPX_QINDEX, av1_is_key, frame_type_from_key,
28 push_video_header, vpx_level,
29};
30use super::reference::{Reference, ReferenceSlots, SlotPlan, SlotRefresh};
31use super::session::{Pending, Planes, Quality, RateSettings, encode_threads};
32use crate::RustCaptureSettings;
33
34const RTC_MIN_LEVEL: u32 = 10;
38
39const PRESET: i8 = 11;
43
44const ANCHOR_PRESET: i8 = 10;
50
51const MAX_BITRATE_BPS: u64 = 100_000_000;
53
54const MANAGED_REFS: u8 = 4;
57
58const KEYFRAME_BY_REOPEN: bool = SVT_AV1_VERSION_MAJOR < 2;
61
62const FEED_BEFORE_RELEASE: bool =
65 SVT_AV1_VERSION_MAJOR < 1 || (SVT_AV1_VERSION_MAJOR == 1 && SVT_AV1_VERSION_MINOR < 5);
66
67pub(crate) static LIFECYCLE: Mutex<usize> = Mutex::new(0);
73
74pub(crate) static RELEASED: Condvar = Condvar::new();
76
77pub struct SvtAv1Encoder {
79 handle: *mut EbComponentType,
80 config: Box<EbSvtAv1EncConfiguration>,
81 input: Box<(EbBufferHeaderType, EbSvtIOFormat)>,
83 planes: Planes,
84 threads: i32,
85 quality: Quality,
86 rate: RateSettings,
87 omit_headers: bool,
88 fresh: bool,
89 events: Events,
91 preset: i8,
93 references: Option<ReferenceSlots>,
95 anchors: Vec<u32>,
97 last_reference: Reference,
98 next_pts: u64,
99 pending: Pending,
100 held: Option<u32>,
102 restate_target: bool,
104 last_quality: Option<u32>,
106}
107
108fn anchor_id(pts: u64) -> u32 {
110 (pts % u32::MAX as u64) as u32 + 1
111}
112
113unsafe impl Send for SvtAv1Encoder {}
114
115fn error(what: &str, code: EbErrorType) -> String {
116 format!("{what} (SVT-AV1 error {code:#x})")
117}
118
119impl Drop for SvtAv1Encoder {
120 fn drop(&mut self) {
121 self.close();
122 }
123}
124
125impl SvtAv1Encoder {
126 pub fn new(settings: &RustCaptureSettings, rgba: bool) -> Result<Self, String> {
128 let _ = rgba;
129 let width = settings.width.max(1) as usize;
130 let height = settings.height.max(1) as usize;
131 let mut me = Self {
132 handle: ptr::null_mut(),
133 config: Box::new(unsafe { std::mem::zeroed() }),
134 input: Box::new(unsafe { std::mem::zeroed() }),
135 planes: Planes::new(
136 width,
137 height,
138 false,
139 if settings.video_bit_depth >= 10 {
140 10
141 } else {
142 8
143 },
144 ),
145 threads: encode_threads(),
146 quality: Quality::new(Codec::Av1.quantizer(settings.video_crf)),
147 rate: RateSettings::new(settings),
148 omit_headers: settings.omit_stripe_headers,
149 fresh: true,
150 events: Events::default(),
151 preset: PRESET,
152 references: None,
153 anchors: Vec::new(),
154 last_reference: Reference::Untracked,
155 next_pts: 0,
156 pending: Pending::default(),
157 held: None,
158 restate_target: false,
159 last_quality: None,
160 };
161 me.open()?;
162 Ok(me)
163 }
164
165 fn level(q: u32) -> u32 {
167 vpx_level(Codec::Av1, q).max(RTC_MIN_LEVEL)
168 }
169
170 fn set(&mut self, name: &str, value: &str) -> Result<(), String> {
171 let (n, v) = (CString::new(name).unwrap(), CString::new(value).unwrap());
172 let code =
173 unsafe { svt_av1_enc_parse_parameter(&mut *self.config, n.as_ptr(), v.as_ptr()) };
174 if code != EB_ErrorNone {
175 return Err(error(&format!("SVT-AV1 refused {name}={value}"), code));
176 }
177 Ok(())
178 }
179
180 fn open(&mut self) -> Result<(), String> {
182 self.close();
183 let mut live = LIFECYCLE.lock().unwrap_or_else(|e| e.into_inner());
184 let code = unsafe { init_handle(&mut self.handle, &mut *self.config) };
185 if code != EB_ErrorNone || self.handle.is_null() {
186 return Err(error("SVT-AV1 handed out no encoder handle", code));
187 }
188 *live += 1;
189 let rate = self.rate;
190 let bps = rate.bps();
191 let tracks = HAS_EVENTS && rate.cbr;
192 {
193 let cfg = &mut *self.config;
194 cfg.enc_mode = if tracks { ANCHOR_PRESET } else { PRESET };
195 self.preset = cfg.enc_mode;
196 cfg.source_width = self.planes.width as u32;
197 cfg.source_height = self.planes.height as u32;
198 cfg.frame_rate_numerator = rate.fps.num;
199 cfg.frame_rate_denominator = rate.fps.den;
200 cfg.encoder_bit_depth = self.planes.bit_depth;
201 cfg.encoder_color_format = EB_YUV420;
202 cfg.color_primaries = EB_CICP_CP_BT_709;
203 cfg.transfer_characteristics = 1;
204 cfg.matrix_coefficients = 1;
205 cfg.color_range = 0;
206 cfg.intra_refresh_type = 2;
207 if rate.cbr {
208 let (lo, hi) = (
209 Codec::Av1.quantizer_bound(rate.min_qp),
210 Codec::Av1.quantizer_bound(rate.max_qp),
211 );
212 cfg.target_bit_rate = bps as u32;
213 cfg.rate_control_mode = 2;
214 cfg.max_qp_allowed = if hi > 0 { Self::level(hi) } else { 63 };
215 cfg.min_qp_allowed = if lo > 0 {
216 Self::level(lo)
217 } else {
218 RTC_MIN_LEVEL
219 };
220 let vbv = (rate.vbv() as u64).max(bps / 50);
223 cfg.maximum_buffer_size_ms = (vbv * 1000 / bps.max(1)) as i64;
224 }
225 if tracks {
226 set_managed_refs(cfg, MANAGED_REFS);
227 }
228 }
229 if HAS_RTC {
231 self.set("rtc", "1")?;
232 }
233 self.set("pred-struct", "1")?;
234 self.set("lookahead", "0")?;
235 self.set("keyint", "-1")?;
236 self.set("tile-columns", "0")?;
237 self.set("tile-rows", "0")?;
238 self.set("lp", &self.threads.min(6).to_string())?;
239 if tracks {
240 self.set("hierarchical-levels", "0")?;
241 }
242 if rate.cbr {
243 self.set("rc", "2")?;
244 } else {
245 self.set("rc", "0")?;
246 self.set("qp", &Self::level(self.quality.current).to_string())?;
247 }
248 let code = unsafe { svt_av1_enc_set_parameter(self.handle, &mut *self.config) };
249 if code != EB_ErrorNone {
250 return Err(error("SVT-AV1 refused the parameters", code));
251 }
252 let code = unsafe { svt_av1_enc_init(self.handle) };
253 if code != EB_ErrorNone {
254 return Err(error("SVT-AV1 refused the session", code));
255 }
256 let (header, io) = &mut *self.input;
257 *header = unsafe { std::mem::zeroed() };
258 header.size = std::mem::size_of::<EbBufferHeaderType>() as u32;
259 header.p_buffer = io as *mut EbSvtIOFormat as *mut u8;
260 let [y, u, v] = self.planes.pointers();
261 io.luma = y;
262 io.cb = u;
263 io.cr = v;
264 io.y_stride = self.planes.width as u32;
265 io.cb_stride = self.planes.chroma_width() as u32;
266 io.cr_stride = self.planes.chroma_width() as u32;
267 header.n_alloc_len = self.planes.byte_len() as u32;
268 self.fresh = true;
269 self.events = Events::default();
270 self.references = tracks.then(ReferenceSlots::new);
271 self.anchors.clear();
272 Ok(())
273 }
274
275 fn close(&mut self) {
277 if self.handle.is_null() {
278 return;
279 }
280 unsafe {
281 if self.fresh && FEED_BEFORE_RELEASE {
282 let header = &mut self.input.0;
283 header.n_filled_len = header.n_alloc_len;
284 header.flags = 0;
285 header.pic_type = EB_AV1_KEY_PICTURE;
286 self.fresh = svt_av1_enc_send_picture(self.handle, header) != EB_ErrorNone;
287 }
288 if !self.fresh {
289 let mut last: EbBufferHeaderType = std::mem::zeroed();
290 last.pic_type = EB_AV1_INVALID_PICTURE;
291 last.flags = EB_BUFFERFLAG_EOS;
292 svt_av1_enc_send_picture(self.handle, &mut last);
293 loop {
294 let mut packet: *mut EbBufferHeaderType = ptr::null_mut();
295 if svt_av1_enc_get_packet(self.handle, &mut packet, 1) != EB_ErrorNone {
296 break;
297 }
298 let Some(header) = packet.as_ref() else { break };
299 let done = header.flags & EB_BUFFERFLAG_EOS != 0;
300 svt_av1_enc_release_out_buffer(&mut packet);
301 if done {
302 break;
303 }
304 }
305 }
306 let mut live = LIFECYCLE.lock().unwrap_or_else(|e| e.into_inner());
307 svt_av1_enc_deinit(self.handle);
308 svt_av1_enc_deinit_handle(self.handle);
309 *live -= 1;
310 RELEASED.notify_all();
311 }
312 self.handle = ptr::null_mut();
313 }
314
315 pub fn codec(&self) -> Codec {
316 Codec::Av1
317 }
318
319 pub fn library(&self) -> &'static str {
321 "svt-av1"
322 }
323
324 pub fn is_fullcolor(&self) -> bool {
325 false
326 }
327
328 pub fn bit_depth(&self) -> u32 {
330 self.planes.bit_depth
331 }
332
333 pub fn is_full_range(&self) -> bool {
334 false
335 }
336
337 pub fn last_reference(&self) -> Reference {
338 if self.references.is_some() {
339 self.last_reference
340 } else {
341 Reference::Untracked
342 }
343 }
344
345 pub fn holds_quantizer(&self) -> bool {
352 self.rate.cbr && HAS_EVENTS
353 }
354
355 pub fn last_quality(&self) -> Option<u32> {
357 self.last_quality
358 }
359
360 pub fn invalidate_reference(&mut self, frame_id: u16) -> bool {
363 let Some(references) = self.references.as_mut() else {
364 return false;
365 };
366 references.invalidate(frame_id);
367 true
368 }
369
370 pub fn acknowledge_reference(&mut self, frame_id: u16, held: bool) {
373 if let Some(references) = self.references.as_mut() {
374 references.acknowledge(frame_id, held);
375 }
376 }
377
378 pub fn hold_quantizer(&mut self, crf: u32) {
383 self.held = Some(Codec::Av1.quantizer(crf as i32));
384 }
385
386 pub fn reconfigure_rate(&mut self, settings: &RustCaptureSettings) -> Result<(), String> {
390 let Some(rate) = self.rate.changed(settings) else {
391 return Ok(());
392 };
393 let live = HAS_EVENTS
394 && rate.fps == self.rate.fps
395 && rate.vbv_multiplier == self.rate.vbv_multiplier
396 && rate.bps() <= MAX_BITRATE_BPS;
397 self.rate = rate;
398 if !live {
399 return self.open();
400 }
401 self.events.target_bit_rate = rate.bps() as u32;
402 Ok(())
403 }
404
405 pub fn encode_host(
407 &mut self,
408 pixels: &[u8],
409 stride: usize,
410 rgba: bool,
411 frame_number: u64,
412 crf: u32,
413 force_idr: bool,
414 ) -> Result<Vec<u8>, String> {
415 let quality_changed = !self.rate.cbr
416 && self
417 .quality
418 .update(Codec::Av1.quantizer(crf as i32))
419 .is_some();
420 if quality_changed || (force_idr && !self.fresh && KEYFRAME_BY_REOPEN) {
421 self.open()?;
422 }
423 self.planes
424 .convert(pixels, stride, rgba, false, false, self.threads as usize)?;
425 let pts = self.next_pts;
426 let mut key = force_idr || self.fresh;
427 let mut plan = SlotPlan::KEY;
428 if let Some(references) = &self.references {
429 plan = references.plan(key);
430 key = plan.predict_from == 0;
431 let anchor = |slot: u8| {
432 references
433 .slot(slot)
434 .map_or(0, |(_, held, _)| anchor_id(held))
435 };
436 let refreshed = |slot: u8| plan.refresh.refreshes(slot);
437 let stored = if refreshed(SlotRefresh::GOLDEN | SlotRefresh::ALTREF) {
438 anchor_id(references.next_pts())
439 } else {
440 0
441 };
442 let kept = [SlotRefresh::GOLDEN, SlotRefresh::ALTREF].map(|slot| {
443 if refreshed(slot) {
444 stored
445 } else {
446 anchor(slot)
447 }
448 });
449 let from = if key || plan.predict_from == SlotRefresh::LAST {
450 0
451 } else {
452 anchor(plan.predict_from)
453 };
454 let released = self
455 .anchors
456 .iter()
457 .copied()
458 .find(|a| !kept.contains(a) && *a != from);
459 self.events.store = stored;
460 self.events.predict_from = from;
461 let preset = if from != 0 { ANCHOR_PRESET } else { PRESET };
462 if preset != self.preset {
463 self.events.preset = preset;
464 self.preset = preset;
465 }
466 self.events.clear = if key { 0 } else { released.unwrap_or(0) };
467 }
468 {
469 let header = &mut self.input.0;
470 header.n_filled_len = header.n_alloc_len;
471 header.pts = pts as i64;
472 header.flags = 0;
473 header.pic_type = if key {
474 EB_AV1_KEY_PICTURE
475 } else {
476 EB_AV1_INVALID_PICTURE
477 };
478 }
479 if self.rate.cbr && HAS_EVENTS {
480 let held_key = key && self.held.is_some();
481 if held_key || self.restate_target {
482 let factor = if held_key {
485 super::HELD_KEY_BUDGET_S * self.rate.fps.fps()
486 } else {
487 1.0
488 };
489 self.events.target_bit_rate =
490 (self.rate.bps() as f64 * factor.max(1.0)).min(MAX_BITRATE_BPS as f64) as u32;
491 }
492 self.restate_target = held_key;
493 }
494 let held = self.held.take().is_some();
495 let sent = self.events;
496 let code = unsafe { send_picture(self.handle, &mut self.input.0, sent) };
497 if code != EB_ErrorNone {
498 return Err(error("SVT-AV1 refused the frame", code));
499 }
500 self.events = Events::default();
501 self.next_pts += 1;
502 self.fresh = false;
503 self.pending.push(pts, frame_number as u16);
504
505 let mut output = Vec::new();
506 loop {
507 let mut packet: *mut EbBufferHeaderType = ptr::null_mut();
508 let code = unsafe { svt_av1_enc_get_packet(self.handle, &mut packet, 0) };
509 if code == EB_NoErrorEmptyQueue {
510 break;
511 }
512 if code != EB_ErrorNone {
513 return Err(error("SVT-AV1 handed out no packet", code));
514 }
515 let Some(p) = (unsafe { packet.as_ref() }) else {
516 break;
517 };
518 let bytes =
519 unsafe { std::slice::from_raw_parts(p.p_buffer, p.n_filled_len as usize) }.to_vec();
520 let packet_pts = p.pts.max(0) as u64;
521 if !held {
522 self.last_quality =
523 Some(Codec::Av1.quality_index(VPX_QINDEX[(p.qp as usize).min(63)] as u32));
524 }
525 unsafe { svt_av1_enc_release_out_buffer(&mut packet) };
526 let id = self.pending.take(packet_pts).unwrap_or(frame_number as u16);
527 let is_key = av1_is_key(&bytes);
528 let mut anchor = 0;
529 if let Some(references) = self.references.as_mut() {
530 if !is_key && references.shared_anchor(plan) {
531 anchor = FRAME_ANCHOR;
532 }
533 self.last_reference =
534 references.record(id, if is_key { SlotPlan::KEY } else { plan });
535 if is_key {
536 self.anchors.clear();
537 }
538 self.anchors.retain(|&a| a != sent.clear);
539 if sent.store != 0 {
540 self.anchors.push(sent.store);
541 }
542 }
543 if !self.omit_headers {
544 output.reserve(VIDEO_HEADER_LEN + bytes.len());
545 push_video_header(
546 &mut output,
547 Codec::Av1,
548 frame_type_from_key(is_key) | anchor,
549 id,
550 0,
551 self.planes.width as u16,
552 self.planes.height as u16,
553 self.last_reference(),
554 );
555 }
556 output.extend_from_slice(&bytes);
557 if self.pending.is_empty() {
558 break;
559 }
560 }
561 Ok(output)
562 }
563}
564
565#[cfg(test)]
566mod tests {
567 use super::*;
568
569 #[test]
571 fn a_session_let_go_before_its_first_frame_closes() {
572 let (done, closed) = std::sync::mpsc::channel();
573 std::thread::spawn(move || {
574 let settings = RustCaptureSettings {
575 width: 64,
576 height: 64,
577 target_fps: 30.0,
578 codec: Codec::Av1,
579 ..Default::default()
580 };
581 drop(SvtAv1Encoder::new(&settings, false).expect("session"));
582 let _ = done.send(());
583 });
584 assert!(
585 closed
586 .recv_timeout(std::time::Duration::from_secs(30))
587 .is_ok(),
588 "the release never returned"
589 );
590 }
591
592 #[test]
595 fn the_frame_rate_reaches_the_library_as_its_fraction() {
596 for (num, den) in [(60000u32, 1001u32), (120000, 1001), (144000, 1001), (60, 1)] {
597 let fps = num as f64 / den as f64;
598 let settings = RustCaptureSettings {
599 width: 64,
600 height: 64,
601 target_fps: fps,
602 codec: Codec::Av1,
603 video_cbr_mode: true,
604 video_bitrate_kbps: 2000,
605 ..Default::default()
606 };
607 let mut enc = SvtAv1Encoder::new(&settings, false).expect("session");
608 assert_eq!(
609 (
610 enc.config.frame_rate_numerator,
611 enc.config.frame_rate_denominator
612 ),
613 (num, den)
614 );
615 enc.reconfigure_rate(&RustCaptureSettings {
616 target_fps: 30.0,
617 ..settings.clone()
618 })
619 .expect("30 fps");
620 enc.reconfigure_rate(&settings).expect("back");
621 assert_eq!(
622 (
623 enc.config.frame_rate_numerator,
624 enc.config.frame_rate_denominator
625 ),
626 (num, den)
627 );
628 }
629 }
630
631 #[test]
633 fn sessions_open_and_close_on_many_threads_at_once() {
634 let settings = RustCaptureSettings {
635 width: 64,
636 height: 64,
637 target_fps: 30.0,
638 codec: Codec::Av1,
639 ..Default::default()
640 };
641 let frame = vec![0u8; 64 * 64 * 4];
642 std::thread::scope(|s| {
643 for _ in 0..4 {
644 s.spawn(|| {
645 for n in 0..4 {
646 let mut enc = SvtAv1Encoder::new(&settings, false).expect("session");
647 enc.encode_host(&frame, 64 * 4, false, n, 30, true)
648 .expect("frame");
649 }
650 });
651 }
652 });
653 }
654}