#![forbid(unsafe_code)]
pub(super) const WORKSPACE_PING_PONG_SLOTS: usize = 2;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(
dead_code,
reason = "ForceKey is a documented hook, not wired by any caller yet (mirrors gop::FrameRequest::ForceIdr)"
)]
pub(super) enum FrameRequest {
Auto,
ForceKey,
}
#[derive(Debug, Clone, Copy)]
pub(super) struct FrameDecision {
pub(super) is_key: bool,
pub(super) setup_slot: usize,
pub(super) refresh_frame_flags: u8,
pub(super) reference_slot: Option<usize>,
}
#[derive(Debug)]
pub(super) struct GopState {
gop_size: u32,
frames_since_key: u32,
last_written: Option<usize>,
}
impl GopState {
pub(super) const fn new(gop_size: u32) -> Self {
Self {
gop_size,
frames_since_key: 0,
last_written: None,
}
}
pub(super) fn decide(&mut self, request: FrameRequest) -> FrameDecision {
let is_key = matches!(request, FrameRequest::ForceKey)
|| self.frames_since_key == 0
|| self.frames_since_key >= self.gop_size;
if is_key {
self.frames_since_key = 0;
}
let (setup_slot, refresh_frame_flags, reference_slot) = if is_key {
(0usize, 0xffu8, None)
} else {
let prev = self.last_written.unwrap_or(0);
let setup_slot = (prev + 1) % WORKSPACE_PING_PONG_SLOTS;
let refresh_frame_flags = (1u8 << setup_slot) | 0xfc;
(setup_slot, refresh_frame_flags, Some(prev))
};
self.last_written = Some(setup_slot);
self.frames_since_key += 1;
FrameDecision {
is_key,
setup_slot,
refresh_frame_flags,
reference_slot,
}
}
}
#[cfg(test)]
#[path = "vp9_gop_tests.rs"]
mod tests;