cros_codecs/encoder/stateless/
h264.rs

1// Copyright 2024 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::rc::Rc;
6
7use crate::codec::h264::parser::Pps;
8use crate::codec::h264::parser::SliceHeader;
9use crate::codec::h264::parser::Sps;
10use crate::encoder::h264::EncoderConfig;
11use crate::encoder::h264::H264;
12use crate::encoder::stateless::h264::predictor::LowDelayH264;
13use crate::encoder::stateless::BackendPromise;
14use crate::encoder::stateless::BitstreamPromise;
15use crate::encoder::stateless::FrameMetadata;
16use crate::encoder::stateless::Predictor;
17use crate::encoder::stateless::StatelessBackendResult;
18use crate::encoder::stateless::StatelessCodec;
19use crate::encoder::stateless::StatelessEncoderBackendImport;
20use crate::encoder::stateless::StatelessEncoderExecute;
21use crate::encoder::stateless::StatelessVideoEncoderBackend;
22use crate::encoder::EncodeResult;
23use crate::encoder::PredictionStructure;
24use crate::encoder::Tunings;
25use crate::BlockingMode;
26
27mod predictor;
28
29#[cfg(feature = "vaapi")]
30pub mod vaapi;
31
32#[derive(Copy, Clone, PartialEq, Eq, Debug)]
33pub enum IsReference {
34    No,
35    ShortTerm,
36    LongTerm,
37}
38
39#[derive(Clone, Debug)]
40pub struct DpbEntryMeta {
41    /// Picture order count
42    poc: u16,
43    frame_num: u32,
44    is_reference: IsReference,
45}
46
47/// Frame structure used in the backend representing currently encoded frame or references used
48/// for its encoding.
49pub struct DpbEntry<R> {
50    /// Reconstructed picture
51    recon_pic: R,
52    /// Decoded picture buffer entry metadata
53    meta: DpbEntryMeta,
54}
55
56/// Stateless H.264 encoder backend input.
57pub struct BackendRequest<P, R> {
58    sps: Rc<Sps>,
59    pps: Rc<Pps>,
60    header: SliceHeader,
61
62    /// Input frame to be encoded
63    input: P,
64
65    /// Input frame metadata
66    input_meta: FrameMetadata,
67
68    /// DPB entry metadata
69    dpb_meta: DpbEntryMeta,
70
71    /// Reference lists
72    ref_list_0: Vec<Rc<DpbEntry<R>>>,
73    ref_list_1: Vec<Rc<DpbEntry<R>>>,
74
75    /// Period between intra frames
76    intra_period: u32,
77
78    /// Period between intra frame and P frame
79    ip_period: u32,
80
81    /// Number of macroblock to be encoded in slice
82    num_macroblocks: usize,
83
84    /// True whenever the result is IDR
85    is_idr: bool,
86
87    /// [`Tunings`] for the frame
88    tunings: Tunings,
89
90    /// Container for the request output. [`StatelessH264EncoderBackend`] impl shall move it and
91    /// append the slice data to it. This prevents unnecessary copying of bitstream around.
92    coded_output: Vec<u8>,
93}
94
95/// Wrapper type for [`BackendPromise<Output = R>`], with additional
96/// metadata.
97pub struct ReferencePromise<P>
98where
99    P: BackendPromise,
100{
101    /// Slice data and reconstructed surface promise
102    recon: P,
103
104    /// [`DpbEntryMeta`] of reconstructed surface
105    dpb_meta: DpbEntryMeta,
106}
107
108impl<P> BackendPromise for ReferencePromise<P>
109where
110    P: BackendPromise,
111{
112    type Output = DpbEntry<P::Output>;
113
114    fn is_ready(&self) -> bool {
115        self.recon.is_ready()
116    }
117
118    fn sync(self) -> StatelessBackendResult<Self::Output> {
119        let recon_pic = self.recon.sync()?;
120
121        log::trace!("synced recon picture frame_num={}", self.dpb_meta.frame_num);
122
123        Ok(DpbEntry {
124            recon_pic,
125            meta: self.dpb_meta,
126        })
127    }
128}
129
130impl<Backend> StatelessCodec<Backend> for H264
131where
132    Backend: StatelessVideoEncoderBackend<H264>,
133{
134    type Reference = DpbEntry<Backend::Reconstructed>;
135
136    type Request = BackendRequest<Backend::Picture, Backend::Reconstructed>;
137
138    type CodedPromise = BitstreamPromise<Backend::CodedPromise>;
139
140    type ReferencePromise = ReferencePromise<Backend::ReconPromise>;
141}
142
143/// Trait for stateless encoder backend for H.264
144pub trait StatelessH264EncoderBackend: StatelessVideoEncoderBackend<H264> {
145    /// Submit a [`BackendRequest`] to the backend. This operation returns both a
146    /// [`StatelessVideoEncoderBackend::CodedPromise`] and a
147    /// [`StatelessVideoEncoderBackend::ReconPromise`] with resulting slice data.
148    fn encode_slice(
149        &mut self,
150        request: BackendRequest<Self::Picture, Self::Reconstructed>,
151    ) -> StatelessBackendResult<(Self::ReconPromise, Self::CodedPromise)>;
152}
153
154pub type StatelessEncoder<Handle, Backend> =
155    crate::encoder::stateless::StatelessEncoder<H264, Handle, Backend>;
156
157impl<Handle, Backend> StatelessEncoderExecute<H264, Handle, Backend>
158    for StatelessEncoder<Handle, Backend>
159where
160    Backend: StatelessH264EncoderBackend,
161{
162    fn execute(
163        &mut self,
164        request: BackendRequest<Backend::Picture, Backend::Reconstructed>,
165    ) -> EncodeResult<()> {
166        let meta = request.input_meta.clone();
167        let dpb_meta = request.dpb_meta.clone();
168
169        // The [`BackendRequest`] has a frame from predictor. Decreasing internal counter.
170        self.predictor_frame_count -= 1;
171
172        log::trace!("submitting new request");
173        let (recon, bitstream) = self.backend.encode_slice(request)?;
174
175        // Wrap promise from backend with headers and metadata
176        let slice_promise = BitstreamPromise { bitstream, meta };
177
178        self.output_queue.add_promise(slice_promise);
179
180        let ref_promise = ReferencePromise { recon, dpb_meta };
181
182        self.recon_queue.add_promise(ref_promise);
183
184        Ok(())
185    }
186}
187
188impl<Handle, Backend> StatelessEncoder<Handle, Backend>
189where
190    Backend: StatelessH264EncoderBackend,
191    Backend: StatelessEncoderBackendImport<Handle, Backend::Picture>,
192{
193    fn new_h264(backend: Backend, config: EncoderConfig, mode: BlockingMode) -> EncodeResult<Self> {
194        let predictor: Box<dyn Predictor<_, _, _>> = match config.pred_structure {
195            PredictionStructure::LowDelay { limit } => Box::new(LowDelayH264::new(config, limit)),
196        };
197
198        Self::new(backend, mode, predictor)
199    }
200}