blob: 1dddf1856b962068df976b066007b4eaee5f2d5c [file] [log] [blame]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::collections::VecDeque;
use std::rc::Rc;
use crate::encoder::stateless::Predictor;
use crate::encoder::EncodeError;
use crate::encoder::EncodeResult;
use crate::encoder::FrameMetadata;
use crate::encoder::RateControl;
use crate::encoder::Tunings;
/// Implementation of [`LowDelay`] prediction structure. See [`LowDelay`] for details.
///
/// [`LowDelay`]: crate::encoder::PredictionStructure::LowDelay
pub(crate) struct LowDelay<Picture, Reference, Delegate, Request> {
/// Pending frames for encoding
pub(super) queue: VecDeque<(Picture, FrameMetadata)>,
/// Availabe frames for references
pub(super) references: VecDeque<Rc<Reference>>,
/// Current frame counter
pub(super) counter: usize,
/// The number of frames between intra frames
pub(super) limit: u16,
/// Codec specific delegate. Holds codec specific state. Is also used to differentiate
/// [`LowDelay`] implementations between codecs.
pub(super) delegate: Delegate,
/// Currently set tunings for the stream
pub(super) tunings: Tunings,
/// Pending [`Tunings`] to be applied with `counter` value when to set them.
/// In this way we ensure that the requested frames prior tuning request are encoded using
/// previous tunings. This is especially important for bitrate, this way we ensure the bitrate
/// changes when requested.
pub(super) tunings_queue: VecDeque<(usize, Tunings)>,
pub(super) _phantom: std::marker::PhantomData<Request>,
}
/// Helper trait enabling forcing [`LowDelay`] to implement codec specific functions.
pub(crate) trait LowDelayDelegate<Picture, Reference, Request> {
/// Creates keyframe or IDR request for the codec backend
fn request_keyframe(
&mut self,
input: Picture,
input_meta: FrameMetadata,
idr: bool,
) -> EncodeResult<Request>;
/// Creates interframe request for the codec backend
fn request_interframe(
&mut self,
input: Picture,
input_meta: FrameMetadata,
) -> EncodeResult<Request>;
/// Checks if the `_tunings` can be applied
fn try_tunings(&self, _tunings: &Tunings) -> EncodeResult<()> {
Err(EncodeError::Unsupported)
}
/// Applies `_tunings`
fn apply_tunings(&mut self, _tunings: &Tunings) -> EncodeResult<()> {
Err(EncodeError::Unsupported)
}
}
impl<Picture, Reference, Delegate, Request> LowDelay<Picture, Reference, Delegate, Request>
where
Self: LowDelayDelegate<Picture, Reference, Request>,
{
fn pop_tunings(&mut self) -> EncodeResult<()> {
while let Some((when_counter, _)) = self.tunings_queue.front() {
if self.counter < *when_counter {
log::trace!(
"Pending tuning skipped counter={} scheduled={}",
self.counter,
when_counter
);
break;
}
// SAFETY: checked in loop condition
let (_, tunings) = self.tunings_queue.pop_front().unwrap();
log::info!("Applying tuning {tunings:?}");
self.apply_tunings(&tunings)?;
self.tunings = tunings;
}
Ok(())
}
fn next_request(&mut self) -> EncodeResult<Vec<Request>> {
log::trace!("Pending frames in the queue: {}", self.queue.len());
let mut requests = Vec::new();
while let Some((input, meta)) = self.queue.pop_front() {
self.pop_tunings()?;
if self.counter == 0 || meta.force_keyframe {
log::trace!("Requesting keyframe/IDR for timestamp={}", meta.timestamp);
// If first frame in the sequence or forced IDR then clear references and create
// keyframe request.
// TODO: Maybe don't clear references on just keyframe (!= IDR)
self.references.clear();
let request = self.request_keyframe(input, meta, self.counter == 0)?;
requests.push(request);
self.counter = self.counter.wrapping_add(1) % (self.limit as usize);
} else if self.references.is_empty() {
log::trace!("Awaiting more reconstructed frames");
// There is no enough frames reconstructed
self.queue.push_front((input, meta));
break;
} else {
log::trace!("Requesting interframe for timestamp={}", meta.timestamp);
let request = self.request_interframe(input, meta)?;
requests.push(request);
self.counter = self.counter.wrapping_add(1) % (self.limit as usize);
break;
}
}
Ok(requests)
}
}
impl<Picture, Reference, Delegate, Request> Predictor<Picture, Reference, Request>
for LowDelay<Picture, Reference, Delegate, Request>
where
Self: LowDelayDelegate<Picture, Reference, Request>,
{
fn new_frame(
&mut self,
input: Picture,
frame_metadata: FrameMetadata,
) -> EncodeResult<Vec<Request>> {
log::trace!("New frame added to queue timestamp={}", frame_metadata.timestamp);
// Add new frame in the request queue and request new encoding if possible
self.queue.push_back((input, frame_metadata));
self.next_request()
}
fn reconstructed(&mut self, reference: Reference) -> EncodeResult<Vec<Request>> {
log::trace!("A frame was reconstructed");
// Add new reconstructed surface and request next encoding if possible
self.references.push_back(Rc::new(reference));
self.next_request()
}
fn tune(&mut self, tunings: Tunings) -> EncodeResult<()> {
log::trace!("Tuning requested with {tunings:?}");
if !RateControl::is_same_variant(&self.tunings.rate_control, &tunings.rate_control) {
// TODO(bgrzesik): consider enabling switching between RateControl variants
log::error!("Changing RateControl variant is not supported at the moment");
return Err(EncodeError::Unsupported);
}
// Check if the tunings are or will be the same, in such case we skip.
let skip = match self.tunings_queue.front() {
Some((_, preceeding_tunnings)) if preceeding_tunnings == &tunings => true,
None if self.tunings == tunings => true,
_ => false,
};
if skip {
log::debug!("Tuning skipped, the requested values are the same.");
return Ok(());
}
// Check if applying tunings will succeed.
self.try_tunings(&tunings)?;
let when_counter = self.counter + self.queue.len();
self.tunings_queue.push_back((when_counter, tunings));
Ok(())
}
fn drain(&mut self) -> EncodeResult<Vec<Request>> {
// [`LowDelay`] will not hold any frames, therefore the drain function shall never be called.
Err(EncodeError::InvalidInternalState)
}
}
#[cfg(test)]
mod tests {
use crate::Fourcc;
use super::*;
#[derive(Debug, PartialEq, Eq)]
enum MockRequest {
KeyFrameRequest(u32, Tunings),
InterframerRequest(u32, Tunings),
}
struct MockDelegate;
impl LowDelayDelegate<u32, u32, MockRequest> for LowDelay<u32, u32, MockDelegate, MockRequest> {
fn request_interframe(
&mut self,
input: u32,
_input_meta: FrameMetadata,
) -> EncodeResult<MockRequest> {
Ok(MockRequest::InterframerRequest(input, self.tunings.clone()))
}
fn request_keyframe(
&mut self,
input: u32,
_input_meta: FrameMetadata,
_idr: bool,
) -> EncodeResult<MockRequest> {
Ok(MockRequest::KeyFrameRequest(input, self.tunings.clone()))
}
fn try_tunings(&self, _tunings: &Tunings) -> EncodeResult<()> {
Ok(())
}
fn apply_tunings(&mut self, _tunings: &Tunings) -> EncodeResult<()> {
Ok(())
}
}
fn dummy_frame_meta(timestamp: u64, force_keyframe: bool) -> FrameMetadata {
FrameMetadata {
timestamp,
layout: crate::FrameLayout {
format: (Fourcc::from(b"NV12"), 0),
size: crate::Resolution { width: 0, height: 0 },
planes: vec![],
},
force_keyframe,
}
}
/// This test ensures that Tunings change applies to only frames following the change
#[test]
fn test_tuning_delay() {
let _ = env_logger::try_init();
let tunings_prev = Tunings { framerate: 1, ..Default::default() };
let mut predictor: LowDelay<u32, u32, MockDelegate, MockRequest> = LowDelay {
queue: Default::default(),
references: Default::default(),
counter: 0,
limit: 1028,
delegate: MockDelegate,
tunings: tunings_prev.clone(),
tunings_queue: Default::default(),
_phantom: Default::default(),
};
let mut requests = Vec::new();
requests.extend(predictor.new_frame(0, dummy_frame_meta(0, false)).unwrap());
requests.extend(predictor.new_frame(1, dummy_frame_meta(1, false)).unwrap());
requests.extend(predictor.new_frame(2, dummy_frame_meta(2, false)).unwrap());
requests.extend(predictor.new_frame(3, dummy_frame_meta(3, false)).unwrap());
let tunings_next = Tunings { framerate: 2, ..Default::default() };
predictor.tune(tunings_next.clone()).unwrap();
assert_eq!(predictor.tunings, tunings_prev);
requests.extend(predictor.new_frame(4, dummy_frame_meta(4, false)).unwrap());
requests.extend(predictor.reconstructed(0).unwrap());
requests.extend(predictor.reconstructed(1).unwrap());
requests.extend(predictor.reconstructed(2).unwrap());
assert_eq!(predictor.tunings, tunings_prev);
requests.extend(predictor.reconstructed(3).unwrap());
assert_eq!(predictor.tunings, tunings_next);
requests.extend(predictor.reconstructed(4).unwrap());
assert_eq!(
requests,
vec![
MockRequest::KeyFrameRequest(0, tunings_prev.clone()),
MockRequest::InterframerRequest(1, tunings_prev.clone()),
MockRequest::InterframerRequest(2, tunings_prev.clone()),
MockRequest::InterframerRequest(3, tunings_prev.clone()),
MockRequest::InterframerRequest(4, tunings_next.clone()),
]
);
}
#[test]
fn test_keyframes() {
const FRAME_COUNT: u32 = 1028;
const IDR_PERIOD: u16 = 37;
const KEYFRAME_REQUEST_PERIOD: u32 = 31;
let _ = env_logger::try_init();
let tunings = Tunings::default();
let mut predictor: LowDelay<u32, u32, MockDelegate, MockRequest> = LowDelay {
queue: Default::default(),
references: Default::default(),
counter: 0,
limit: IDR_PERIOD,
delegate: MockDelegate,
tunings: tunings.clone(),
tunings_queue: Default::default(),
_phantom: Default::default(),
};
let mut requests = Vec::new();
for i in 0..FRAME_COUNT {
let keyframe = i % KEYFRAME_REQUEST_PERIOD == 0;
requests.extend(predictor.new_frame(i, dummy_frame_meta(i as u64, keyframe)).unwrap());
}
for i in 0..FRAME_COUNT {
requests.extend(predictor.reconstructed(i).unwrap());
}
let mut expected = Vec::new();
for i in 0..FRAME_COUNT {
if (i % IDR_PERIOD as u32 == 0) || (i % KEYFRAME_REQUEST_PERIOD) == 0 {
expected.push(MockRequest::KeyFrameRequest(i, tunings.clone()));
} else {
expected.push(MockRequest::InterframerRequest(i, tunings.clone()));
}
}
assert_eq!(requests, expected);
}
}