Skip to main content

pito_codecs/h264/enc/
mod.rs

1mod cabac;
2mod cabac_mb;
3mod cavlc;
4mod deblock;
5mod dsp;
6mod frame;
7mod intra;
8mod lookahead;
9mod mbinfo;
10mod mbtree;
11mod nal;
12mod picture;
13mod ratecontrol;
14mod residual;
15mod slice;
16mod syntax;
17mod tables;
18mod transform;
19
20use std::collections::{BTreeMap, VecDeque};
21use std::sync::{Arc, Condvar, Mutex, mpsc};
22
23use cabac::CostTable;
24use deblock::{DeblockParams, MAX_REFS};
25use frame::{Ctx, Job, Scratch, encode_frame};
26use lookahead::{Complexity, LowRes};
27use nal::{
28    NAL_AUD, NAL_IDR, NAL_PPS, NAL_SEI, NAL_SLICE, NAL_SPS, Pps, SliceHeader, SliceType, Sps, Vui,
29};
30use picture::{DONE, Frame, Pic};
31use ratecontrol::RcKind;
32use slice::{EncParams, Lambdas, SliceOut};
33
34use transform::QuantTables;
35
36#[derive(Debug)]
37pub enum Error {
38    InvalidDimensions(u32, u32),
39    InvalidFrameRate(u32, u32),
40    InvalidConfig(String),
41    BadFrame,
42    Failed(String),
43}
44
45impl std::fmt::Display for Error {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        match self {
48            Error::InvalidDimensions(w, h) => {
49                write!(f, "invalid dimensions {w}x{h} (must be even and non-zero)")
50            }
51            Error::InvalidFrameRate(n, d) => write!(f, "invalid frame rate {n}/{d}"),
52            Error::InvalidConfig(s) => write!(f, "invalid configuration: {s}"),
53            Error::BadFrame => write!(f, "input frame planes too small for the configured size"),
54            Error::Failed(s) => write!(f, "{s}"),
55        }
56    }
57}
58
59impl std::error::Error for Error {}
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
62pub enum Profile {
63    Baseline,
64    Main,
65    #[default]
66    High,
67}
68
69#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
70pub enum Preset {
71    Speed,
72    #[default]
73    Balanced,
74    Quality,
75}
76
77#[derive(Clone, Copy, Debug, PartialEq)]
78pub enum RateControl {
79    Crf(f32),
80    Cbr { kbps: u32 },
81    Vbr { target_kbps: u32, max_kbps: u32 },
82    Qp(u8),
83}
84
85#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
86pub enum PacketFormat {
87    #[default]
88    AnnexB,
89    LengthPrefixed,
90}
91
92#[derive(Clone, Debug)]
93pub struct EncoderConfig {
94    pub width: u32,
95    pub height: u32,
96    pub fps_num: u32,
97    pub fps_den: u32,
98    pub profile: Profile,
99    pub preset: Preset,
100    pub rate: RateControl,
101    pub keyint: u32,
102    pub bframes: u8,
103    pub threads: usize,
104    pub format: PacketFormat,
105}
106
107const AQ_STRENGTH: f32 = 0.6;
108
109impl EncoderConfig {
110    pub fn new(width: u32, height: u32, fps_num: u32, fps_den: u32) -> Self {
111        EncoderConfig {
112            width,
113            height,
114            fps_num,
115            fps_den,
116            profile: Profile::High,
117            preset: Preset::Balanced,
118            rate: RateControl::Crf(23.0),
119            keyint: 250,
120            bframes: 2,
121            threads: 0,
122            format: PacketFormat::AnnexB,
123        }
124    }
125}
126
127#[derive(Clone, Copy, Debug)]
128pub struct YuvFrame<'a> {
129    pub y: &'a [u8],
130    pub u: &'a [u8],
131    pub v: &'a [u8],
132    pub y_stride: usize,
133    pub uv_stride: usize,
134}
135
136#[derive(Clone, Debug)]
137pub struct Packet {
138    pub data: Vec<u8>,
139    pub pts: i64,
140    pub dts: i64,
141    pub keyframe: bool,
142}
143
144struct Input {
145    frame: Frame,
146    low: Arc<LowRes>,
147    pts: i64,
148    display: u64,
149    cplx: Complexity,
150    aq: Vec<f32>,
151}
152
153struct La {
154    input: Input,
155    st: SliceType,
156    idr: bool,
157    base: u64,
158    costs: Option<mbtree::Costs>,
159    refs: [Option<u64>; 2],
160}
161
162struct Pool {
163    pool: Option<rayon::ThreadPool>,
164}
165
166impl Pool {
167    fn install<R: Send>(&self, f: impl FnOnce() -> R + Send) -> R {
168        if let Some(p) = &self.pool {
169            return p.install(f);
170        }
171        f()
172    }
173}
174
175struct Done {
176    job: Job,
177    out: std::thread::Result<SliceOut>,
178}
179
180struct Queue {
181    jobs: Mutex<(VecDeque<Job>, bool)>,
182    cv: Condvar,
183}
184
185struct Workers {
186    queue: Arc<Queue>,
187    done: mpsc::Receiver<Done>,
188    handles: Vec<std::thread::JoinHandle<()>>,
189}
190
191impl Workers {
192    fn new(ctx: Arc<Ctx>, n: usize) -> Result<Self, Error> {
193        let queue = Arc::new(Queue {
194            jobs: Mutex::new((VecDeque::new(), false)),
195            cv: Condvar::new(),
196        });
197        let (tx, done) = mpsc::channel();
198        let mut workers = Workers {
199            queue: queue.clone(),
200            done,
201            handles: Vec::with_capacity(n),
202        };
203        for i in 0..n {
204            let (ctx, queue, tx) = (ctx.clone(), queue.clone(), tx.clone());
205            let h = std::thread::Builder::new()
206                .name(format!("h264enc-{i}"))
207                .spawn(move || {
208                    let mut scratch = Scratch::new();
209                    loop {
210                        let job = {
211                            let mut g = queue.jobs.lock().unwrap_or_else(|e| e.into_inner());
212                            loop {
213                                if let Some(j) = g.0.pop_front() {
214                                    break Some(j);
215                                }
216                                if g.1 {
217                                    break None;
218                                }
219                                g = queue.cv.wait(g).unwrap_or_else(|e| e.into_inner());
220                            }
221                        };
222                        let Some(job) = job else { return };
223                        let out = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
224                            encode_frame(&ctx, &job, &mut scratch)
225                        }));
226                        if out.is_err() {
227                            job.pic.publish(DONE, DONE, DONE);
228                        }
229                        if tx.send(Done { job, out }).is_err() {
230                            return;
231                        }
232                    }
233                })
234                .map_err(|e| Error::Failed(format!("cannot start a frame thread: {e}")))?;
235            workers.handles.push(h);
236        }
237        Ok(workers)
238    }
239
240    fn push(&self, job: Job) {
241        let mut g = self.queue.jobs.lock().unwrap_or_else(|e| e.into_inner());
242        g.0.push_back(job);
243        self.queue.cv.notify_one();
244    }
245}
246
247impl Drop for Workers {
248    fn drop(&mut self) {
249        {
250            let mut g = self.queue.jobs.lock().unwrap_or_else(|e| e.into_inner());
251            g.1 = true;
252            self.queue.cv.notify_all();
253        }
254        for h in self.handles.drain(..) {
255            let _ = h.join();
256        }
257    }
258}
259
260pub struct Encoder {
261    cfg: EncoderConfig,
262    ctx: Arc<Ctx>,
263    mbw: usize,
264    mbh: usize,
265    sps: Sps,
266    sps_nal: Vec<u8>,
267    pps_nal: Vec<u8>,
268    rc: ratecontrol::RateControl,
269    pending: VecDeque<Input>,
270    prev_lowres: Option<Arc<LowRes>>,
271    la: VecDeque<La>,
272    la_depth: usize,
273    la_next: u64,
274    anchor: Option<(u64, Arc<LowRes>)>,
275
276    display_count: u64,
277    last_idr: u64,
278    idr_count: u32,
279    next_frame_num: u32,
280    dpb: Vec<Arc<Pic>>,
281    retired: Vec<Arc<Pic>>,
282    pic_pool: Vec<Pic>,
283    display_pts: VecDeque<i64>,
284    dts_offset: Option<i64>,
285    all_pts: Vec<i64>,
286    held: Vec<Packet>,
287    frame_pool: Vec<Frame>,
288    sei_sent: bool,
289    pool: Pool,
290    ref_uid: u32,
291    max_refs: usize,
292    seq: u64,
293    next_out: u64,
294    finished: BTreeMap<u64, Done>,
295    max_in_flight: usize,
296    failed: Option<String>,
297    workers: Workers,
298}
299
300const LOG2_MAX_FRAME_NUM: u32 = 8;
301const LOOKAHEAD: usize = 40;
302const LOOKAHEAD_BYTES: usize = 768 << 20;
303const LOG2_MAX_POC_LSB: u32 = 8;
304
305fn crf_level_kbps(crf: f32, mbw: usize, mbh: usize, fps: f64) -> u32 {
306    let crf = if crf.is_finite() {
307        f64::from(crf.clamp(0.0, 51.0))
308    } else {
309        23.0
310    };
311    let bpp = 3.0 * (-crf / 6.0).exp2();
312    let pixels = (mbw as f64) * (mbh as f64) * 256.0;
313    (pixels * fps * bpp / 1000.0).ceil() as u32
314}
315
316impl Encoder {
317    fn options(&self) -> String {
318        let c = &self.cfg;
319        let rate = match c.rate {
320            RateControl::Qp(q) => format!("rc=cqp qp={q}"),
321            RateControl::Crf(f) => format!("rc=crf crf={:.1}", f.clamp(0.0, 51.0)),
322            RateControl::Cbr { kbps } => format!("rc=cbr bitrate={kbps}"),
323            RateControl::Vbr {
324                target_kbps,
325                max_kbps,
326            } => format!("rc=vbr bitrate={target_kbps} vbv_maxrate={max_kbps}"),
327        };
328        let profile = match c.profile {
329            Profile::Baseline => "baseline",
330            Profile::Main => "main",
331            Profile::High => "high",
332        };
333        let preset = match c.preset {
334            Preset::Speed => "speed",
335            Preset::Balanced => "balanced",
336            Preset::Quality => "quality",
337        };
338        let level = self.sps.level_idc;
339        format!(
340            "{rate} profile={profile} level={}.{} preset={preset} keyint={} bframes={} refs={} cabac={} 8x8dct={} slices=1 aq={AQ_STRENGTH:.2} scenecut=1",
341            level / 10,
342            level % 10,
343            c.keyint,
344            c.bframes,
345            self.max_refs,
346            u8::from(c.profile != Profile::Baseline),
347            u8::from(c.profile == Profile::High),
348        )
349    }
350
351    pub fn new(mut cfg: EncoderConfig) -> Result<Self, Error> {
352        if cfg.width == 0
353            || cfg.height == 0
354            || !cfg.width.is_multiple_of(2)
355            || !cfg.height.is_multiple_of(2)
356            || cfg.width > 16384
357            || cfg.height > 16384
358        {
359            return Err(Error::InvalidDimensions(cfg.width, cfg.height));
360        }
361        if cfg.fps_num == 0 || cfg.fps_den == 0 {
362            return Err(Error::InvalidFrameRate(cfg.fps_num, cfg.fps_den));
363        }
364        if cfg.bframes > 3 {
365            return Err(Error::InvalidConfig("bframes must be 0..=3".into()));
366        }
367        if cfg.profile == Profile::Baseline {
368            cfg.bframes = 0;
369        }
370        cfg.keyint = cfg.keyint.max(1);
371        let mbw = cfg.width.div_ceil(16) as usize;
372        let mbh = cfg.height.div_ceil(16) as usize;
373        let fps = cfg.fps_num as f64 / cfg.fps_den as f64;
374        let cabac = cfg.profile != Profile::Baseline;
375        let high = cfg.profile == Profile::High;
376        let params = match cfg.preset {
377            Preset::Speed => EncParams {
378                mbw,
379                mbh,
380                cabac,
381                t8x8: high,
382                i8x8: false,
383                partitions: false,
384                me_range: 8,
385                subpel_rounds: 0,
386                rd: false,
387                chroma_qp_offset: 0,
388                adaptive_t8: false,
389                always_intra: false,
390            },
391            Preset::Balanced => EncParams {
392                mbw,
393                mbh,
394                cabac,
395                t8x8: high,
396                i8x8: high,
397                partitions: true,
398                me_range: 16,
399                subpel_rounds: 1,
400                rd: true,
401                chroma_qp_offset: 0,
402                adaptive_t8: high,
403                always_intra: false,
404            },
405            Preset::Quality => EncParams {
406                mbw,
407                mbh,
408                cabac,
409                t8x8: high,
410                i8x8: high,
411                partitions: true,
412                me_range: 24,
413                subpel_rounds: 2,
414                rd: true,
415                chroma_qp_offset: 0,
416                adaptive_t8: high,
417                always_intra: true,
418            },
419        };
420        let max_refs = if cfg.bframes > 0 { 2 } else { 1 };
421        let (kbps_for_level, rc_kind, vbv) = match &cfg.rate {
422            RateControl::Qp(q) => (None, RcKind::Qp((*q).min(51)), (None, None)),
423            RateControl::Crf(c) => (
424                Some(crf_level_kbps(*c, mbw, mbh, fps)),
425                RcKind::Crf(c.clamp(0.0, 51.0)),
426                (None, None),
427            ),
428            RateControl::Cbr { kbps } => (
429                Some(*kbps),
430                RcKind::Abr { kbps: *kbps },
431                (Some(*kbps), Some(*kbps)),
432            ),
433            RateControl::Vbr {
434                target_kbps,
435                max_kbps,
436            } => {
437                let buffer = max_kbps.checked_mul(2).ok_or_else(|| {
438                    Error::InvalidConfig(
439                        "VBR buffer rate exceeds the supported integer range".into(),
440                    )
441                })?;
442                (
443                    Some(*max_kbps),
444                    RcKind::Abr { kbps: *target_kbps },
445                    (Some(*max_kbps), Some(buffer)),
446                )
447            }
448        };
449        if matches!(
450            cfg.rate,
451            RateControl::Cbr { kbps: 0 } | RateControl::Vbr { target_kbps: 0, .. }
452        ) {
453            return Err(Error::InvalidConfig("bitrate must be non-zero".into()));
454        }
455        let auto = nal::pick_level(
456            mbw as u32,
457            mbh as u32,
458            fps,
459            max_refs as u32,
460            kbps_for_level,
461            high,
462        );
463        let (profile_idc, constraint) = match cfg.profile {
464            Profile::Baseline => (66u8, 0xC0u8),
465            Profile::Main => (77, 0x40),
466            Profile::High => (100, 0x00),
467        };
468        let reorder = if cfg.bframes > 0 { 1 } else { 0 };
469        let sps = Sps {
470            profile_idc,
471            constraint_flags: constraint,
472            level_idc: auto,
473            width_mbs: mbw as u32,
474            height_mbs: mbh as u32,
475            crop_right: mbw as u32 * 16 - cfg.width,
476            crop_bottom: mbh as u32 * 16 - cfg.height,
477            log2_max_frame_num: LOG2_MAX_FRAME_NUM,
478            log2_max_poc_lsb: LOG2_MAX_POC_LSB,
479            max_num_ref_frames: max_refs as u32,
480            vui: Vui {
481                num_units_in_tick: cfg.fps_den,
482                time_scale: cfg.fps_num.saturating_mul(2),
483                max_num_reorder_frames: reorder,
484                max_dec_frame_buffering: max_refs as u32,
485            },
486        };
487        let pps = Pps {
488            cabac,
489            pic_init_qp: 26,
490            chroma_qp_offset: params.chroma_qp_offset,
491            transform_8x8: params.t8x8,
492            high,
493        };
494        let sps_nal = nal::nal(3, NAL_SPS, &sps.rbsp());
495        let pps_nal = nal::nal(3, NAL_PPS, &pps.rbsp());
496        let threads = if cfg.threads == 0 {
497            std::thread::available_parallelism().map_or(1, |n| n.get())
498        } else {
499            cfg.threads
500        };
501        let pool = Pool {
502            pool: if threads > 1 {
503                rayon::ThreadPoolBuilder::new()
504                    .num_threads(threads)
505                    .build()
506                    .ok()
507            } else {
508                None
509            },
510        };
511        let rc = ratecontrol::RateControl::new(rc_kind, fps, vbv.0, vbv.1);
512        let ctx = Arc::new(Ctx {
513            p: params,
514            qt: QuantTables::new(),
515            cost_tab: CostTable::new(),
516            lam: Lambdas::new(),
517        });
518        let frame_threads = threads.clamp(1, 64);
519        let frame_bytes = (mbw * 16 + 128) * (mbh * 16 + 128) * 2;
520        let la_depth = if matches!(cfg.rate, RateControl::Qp(_)) {
521            0
522        } else {
523            (LOOKAHEAD_BYTES / frame_bytes).clamp(1, LOOKAHEAD)
524        };
525        let workers = Workers::new(ctx.clone(), frame_threads)?;
526        Ok(Encoder {
527            ctx,
528            mbw,
529            mbh,
530            sps,
531            sps_nal,
532            pps_nal,
533            rc,
534            pending: VecDeque::new(),
535            prev_lowres: None,
536            la: VecDeque::new(),
537            la_depth,
538            la_next: 0,
539            anchor: None,
540
541            display_count: 0,
542            last_idr: 0,
543            idr_count: 0,
544            next_frame_num: 0,
545            dpb: Vec::new(),
546            retired: Vec::new(),
547            pic_pool: Vec::new(),
548            display_pts: VecDeque::new(),
549            dts_offset: None,
550            all_pts: Vec::new(),
551            held: Vec::new(),
552            frame_pool: Vec::new(),
553            sei_sent: false,
554            pool,
555            ref_uid: 0,
556            max_refs,
557            seq: 0,
558            next_out: 0,
559            finished: BTreeMap::new(),
560            max_in_flight: frame_threads + 2,
561            failed: None,
562            workers,
563            cfg,
564        })
565    }
566
567    pub fn sps_pps(&self) -> (Vec<u8>, Vec<u8>) {
568        (self.sps_nal.clone(), self.pps_nal.clone())
569    }
570
571    pub fn avcc(&self) -> Vec<u8> {
572        nal::avcc(
573            &self.sps_nal,
574            &self.pps_nal,
575            self.sps.profile_idc,
576            self.sps.constraint_flags,
577            self.sps.level_idc,
578        )
579    }
580
581    pub fn encode(&mut self, frame: &YuvFrame, pts: i64) -> Result<Vec<Packet>, Error> {
582        self.try_encode(frame, pts)
583    }
584
585    fn check(&self) -> Result<(), Error> {
586        match &self.failed {
587            Some(s) => Err(Error::Failed(s.clone())),
588            None => Ok(()),
589        }
590    }
591
592    fn fail<T>(&mut self, r: Result<T, Error>) -> Result<T, Error> {
593        if let Err(Error::Failed(s)) = &r {
594            self.failed = Some(s.clone());
595        }
596        r
597    }
598
599    pub fn try_encode(&mut self, frame: &YuvFrame, pts: i64) -> Result<Vec<Packet>, Error> {
600        self.check()?;
601        let r = self.encode_inner(frame, pts);
602        self.fail(r)
603    }
604
605    fn encode_inner(&mut self, frame: &YuvFrame, pts: i64) -> Result<Vec<Packet>, Error> {
606        let src = self.import(frame)?;
607        let use_aq = !matches!(self.cfg.rate, RateControl::Qp(_));
608        let (mbw, mbh) = (self.mbw, self.mbh);
609        let prev = self.prev_lowres.take();
610        let (lowres, cplx, aq) = self.pool.install(|| {
611            let lowres = Arc::new(LowRes::new(&src));
612            let cplx = lookahead::complexity(&lowres, prev.as_deref());
613            let aq = if use_aq {
614                lookahead::aq_offsets(&src, mbw, mbh, AQ_STRENGTH)
615            } else {
616                Vec::new()
617            };
618            (lowres, cplx, aq)
619        });
620        self.prev_lowres = Some(lowres.clone());
621        let display = self.display_count;
622        self.display_count += 1;
623        self.display_pts.push_back(pts);
624        if self.all_pts.len() < 2 {
625            self.all_pts.push(pts);
626        }
627        let input = Input {
628            frame: src,
629            low: lowres,
630            pts,
631            display,
632            cplx,
633            aq,
634        };
635        let since = display - self.last_idr;
636        let keyint = self.cfg.keyint as u64;
637        let min_gap = (keyint / 10).clamp(1, 25);
638        let span = keyint.saturating_sub(min_gap).max(1) as f64;
639        let bias = 0.01 + 0.39 * (since.saturating_sub(min_gap) as f64 / span).min(1.0);
640        let scenecut = display > 0
641            && since >= min_gap
642            && cplx.inter as f64 >= (1.0 - bias) * cplx.intra as f64;
643        let idr = display == 0 || since >= self.cfg.keyint as u64 || scenecut;
644        let mut out = Vec::new();
645        if idr {
646            self.flush_minigop();
647            self.last_idr = display;
648            self.enqueue(input, SliceType::I, true);
649        } else {
650            self.pending.push_back(input);
651            if self.pending.len() > self.cfg.bframes as usize {
652                self.flush_minigop();
653            }
654        }
655        self.drain(self.la_depth, &mut out)?;
656        Ok(self.release(out, false))
657    }
658
659    pub fn flush(&mut self) -> Result<Vec<Packet>, Error> {
660        self.check()?;
661        let r = self.flush_inner();
662        self.fail(r)
663    }
664
665    fn flush_inner(&mut self) -> Result<Vec<Packet>, Error> {
666        let mut out = Vec::new();
667        self.flush_minigop();
668        self.drain(0, &mut out)?;
669        self.collect(0, &mut out)?;
670        Ok(self.release(out, true))
671    }
672
673    fn release(&mut self, out: Vec<Packet>, flushing: bool) -> Vec<Packet> {
674        self.held.extend(out);
675        if self.dts_offset.is_none() {
676            if self.cfg.bframes == 0 {
677                self.dts_offset = Some(0);
678            } else if self.all_pts.len() >= 2 {
679                let mut v = self.all_pts.clone();
680                v.sort_unstable();
681                self.dts_offset = Some(v[1] - v[0]);
682            } else if flushing {
683                self.dts_offset = Some(0);
684            } else {
685                return Vec::new();
686            }
687        }
688        let off = self.dts_offset.unwrap_or(0);
689        let mut res = std::mem::take(&mut self.held);
690        for p in res.iter_mut() {
691            p.dts -= off;
692        }
693        res
694    }
695
696    fn flush_minigop(&mut self) {
697        let Some(p) = self.pending.pop_back() else {
698            return;
699        };
700        let bs: Vec<Input> = self.pending.drain(..).collect();
701        let prev = self.anchor.clone();
702        self.enqueue(p, SliceType::P, false);
703        let next = self.anchor.clone();
704        for b in bs {
705            self.enqueue_b(b, prev.clone(), next.clone());
706        }
707    }
708
709    fn tree(&self) -> bool {
710        !matches!(self.cfg.rate, RateControl::Qp(_))
711    }
712
713    fn enqueue(&mut self, input: Input, st: SliceType, idr: bool) {
714        let id = self.la_next;
715        self.la_next += 1;
716        let l0 = if idr { None } else { self.anchor.clone() };
717        let costs = self.tree().then(|| {
718            let (cur, r0) = (&input.low, l0.as_ref().map(|a| &*a.1));
719            self.pool.install(|| mbtree::analyse(cur, r0, None))
720        });
721        self.anchor = Some((id, input.low.clone()));
722        self.la.push_back(La {
723            input,
724            st,
725            idr,
726            base: self.last_idr,
727            costs,
728            refs: [l0.map(|a| a.0), None],
729        });
730    }
731
732    fn enqueue_b(
733        &mut self,
734        input: Input,
735        prev: Option<(u64, Arc<LowRes>)>,
736        next: Option<(u64, Arc<LowRes>)>,
737    ) {
738        self.la_next += 1;
739        let costs = self.tree().then(|| {
740            let cur = &input.low;
741            let (r0, r1) = (prev.as_ref().map(|a| &*a.1), next.as_ref().map(|a| &*a.1));
742            self.pool.install(|| mbtree::analyse(cur, r0, r1))
743        });
744        self.la.push_back(La {
745            input,
746            st: SliceType::B,
747            idr: false,
748            base: self.last_idr,
749            costs,
750            refs: [prev.map(|a| a.0), next.map(|a| a.0)],
751        });
752    }
753
754    fn drain(&mut self, depth: usize, out: &mut Vec<Packet>) -> Result<(), Error> {
755        while self.la.len() > depth {
756            let tree = self.tree_offsets();
757            let Some(mut f) = self.la.pop_front() else {
758                break;
759            };
760            if let Some(t) = tree {
761                if f.input.aq.is_empty() {
762                    f.input.aq = t;
763                } else {
764                    for (a, o) in f.input.aq.iter_mut().zip(t) {
765                        *a += o;
766                    }
767                }
768            }
769            self.submit(f.input, f.st, f.idr, f.base, out)?;
770        }
771        Ok(())
772    }
773
774    fn tree_offsets(&self) -> Option<Vec<f32>> {
775        let first = self.la.front()?;
776        first.costs.as_ref()?;
777        let start = self.la_next - self.la.len() as u64;
778        let nodes: Vec<mbtree::Node> = self
779            .la
780            .iter()
781            .filter_map(|f| {
782                Some(mbtree::Node {
783                    costs: f.costs.as_ref()?,
784                    refs: f
785                        .refs
786                        .map(|r| r.and_then(|id| id.checked_sub(start)).map(|i| i as usize)),
787                })
788            })
789            .collect();
790        if nodes.len() != self.la.len() {
791            return None;
792        }
793        let prop = mbtree::propagate(&nodes, self.mbw, self.mbh);
794        Some(mbtree::offsets(nodes[0].costs, &prop[0]))
795    }
796
797    fn import(&mut self, f: &YuvFrame) -> Result<Frame, Error> {
798        let (w, h) = (self.cfg.width as usize, self.cfg.height as usize);
799        let (cw, ch) = (w / 2, h / 2);
800        let need = |stride: usize, rows: usize, w: usize| {
801            stride.checked_mul(rows - 1).and_then(|n| n.checked_add(w))
802        };
803        let fits = |len: usize, stride: usize, rows: usize, w: usize| {
804            stride >= w && need(stride, rows, w).is_some_and(|n| len >= n)
805        };
806        if !fits(f.y.len(), f.y_stride, h, w)
807            || !fits(f.u.len(), f.uv_stride, ch, cw)
808            || !fits(f.v.len(), f.uv_stride, ch, cw)
809        {
810            return Err(Error::BadFrame);
811        }
812        let mut fr = self.new_frame();
813        for (pl, src, stride, pw, ph) in [
814            (&mut fr.y, f.y, f.y_stride, w, h),
815            (&mut fr.u, f.u, f.uv_stride, cw, ch),
816            (&mut fr.v, f.v, f.uv_stride, cw, ch),
817        ] {
818            let aw = pl.w;
819            for y in 0..pl.h {
820                let sy = y.min(ph - 1);
821                let o = pl.idx(0, y as isize);
822                pl.data[o..o + pw].copy_from_slice(&src[sy * stride..sy * stride + pw]);
823                let last = pl.data[o + pw - 1];
824                pl.data[o + pw..o + aw].fill(last);
825            }
826        }
827        Ok(fr)
828    }
829
830    fn new_frame(&mut self) -> Frame {
831        self.frame_pool
832            .pop()
833            .unwrap_or_else(|| Frame::new(self.mbw * 16, self.mbh * 16))
834    }
835
836    fn recycle(&mut self, f: Frame) {
837        if self.frame_pool.len() < 8 {
838            self.frame_pool.push(f);
839        }
840    }
841
842    fn submit(
843        &mut self,
844        input: Input,
845        mut st: SliceType,
846        idr: bool,
847        base: u64,
848        out: &mut Vec<Packet>,
849    ) -> Result<(), Error> {
850        if idr {
851            let old = std::mem::take(&mut self.dpb);
852            self.retired.extend(old);
853            self.next_frame_num = 0;
854        }
855        self.reclaim();
856        let poc = 2 * (input.display as i64 - base as i64) as i32;
857        let by_id = self
858            .dpb
859            .iter()
860            .enumerate()
861            .max_by_key(|(_, r)| r.id)
862            .map(|(i, _)| i);
863        let (mut l0, mut l1) = match st {
864            SliceType::I => (None, None),
865            SliceType::P => (by_id, None),
866            SliceType::B => (
867                self.dpb
868                    .iter()
869                    .enumerate()
870                    .filter(|(_, r)| r.poc < poc)
871                    .max_by_key(|(_, r)| r.poc)
872                    .map(|(i, _)| i),
873                self.dpb
874                    .iter()
875                    .enumerate()
876                    .filter(|(_, r)| r.poc > poc)
877                    .min_by_key(|(_, r)| r.poc)
878                    .map(|(i, _)| i),
879            ),
880        };
881        if st == SliceType::B && (l0.is_none() || l1.is_none()) {
882            st = SliceType::P;
883            l0 = by_id;
884            l1 = None;
885        }
886        if st == SliceType::P && l0.is_none() {
887            st = SliceType::I;
888        }
889        let is_ref = st != SliceType::B;
890        let cplx = match st {
891            SliceType::I => input.cplx.intra,
892            _ => input.cplx.inter,
893        } as f64;
894        let qpf = self.rc.frame_qp(st, cplx);
895        let qp = qpf.round().clamp(0.0, 51.0) as u8;
896        let aq: Vec<i8> = if input.aq.is_empty() {
897            Vec::new()
898        } else {
899            input
900                .aq
901                .iter()
902                .map(|&o| ((qpf + o as f64).round().clamp(0.0, 51.0) as i32 - qp as i32) as i8)
903                .collect()
904        };
905        let nal_ref_idc = if idr {
906            3
907        } else if is_ref {
908            2
909        } else {
910            0
911        };
912        let hdr = SliceHeader {
913            slice_type: st,
914            nal_ref_idc,
915            idr,
916            idr_pic_id: self.idr_count % 2,
917            frame_num: self.next_frame_num % (1 << LOG2_MAX_FRAME_NUM),
918            log2_max_frame_num: LOG2_MAX_FRAME_NUM,
919            poc_lsb: (poc as u32) % (1 << LOG2_MAX_POC_LSB),
920            log2_max_poc_lsb: LOG2_MAX_POC_LSB,
921            l0_modification: None,
922            cabac: self.ctx.p.cabac,
923            slice_qp: qp as i32,
924            pic_init_qp: 26,
925            disable_deblocking: 0,
926            alpha_offset_div2: 0,
927            beta_offset_div2: 0,
928        };
929        if idr {
930            self.idr_count += 1;
931        }
932        let refs = [
933            l0.map(|i| self.dpb[i].clone()),
934            l1.map(|i| self.dpb[i].clone()),
935        ];
936        let mut ids = [[-1i32; MAX_REFS]; 2];
937        for (l, r) in refs.iter().enumerate() {
938            if let Some(r) = r {
939                ids[l][0] = r.id as i32;
940            }
941        }
942        let mut pic = self
943            .pic_pool
944            .pop()
945            .unwrap_or_else(|| Pic::new(self.mbw * 16, self.mbh * 16));
946        let id = if is_ref {
947            self.ref_uid += 1;
948            self.ref_uid
949        } else {
950            0
951        };
952        pic.reset(poc, id);
953        let pic = Arc::new(pic);
954        if is_ref {
955            if self.dpb.len() >= self.max_refs
956                && let Some(oldest) = self
957                    .dpb
958                    .iter()
959                    .enumerate()
960                    .min_by_key(|(_, r)| r.id)
961                    .map(|(i, _)| i)
962            {
963                let r = self.dpb.remove(oldest);
964                self.retired.push(r);
965            }
966            self.dpb.push(pic.clone());
967            self.next_frame_num = (self.next_frame_num + 1) % (1 << LOG2_MAX_FRAME_NUM);
968        }
969        let job = Job {
970            seq: self.seq,
971            src: input.frame,
972            pts: input.pts,
973            st,
974            idr,
975            qp,
976            qpf,
977            cplx,
978            aq,
979            hdr,
980            nal_ref_idc,
981            nal_type: if idr { NAL_IDR } else { NAL_SLICE },
982            pic,
983            refs,
984            publish: is_ref,
985            dp: DeblockParams {
986                alpha_offset: 0,
987                beta_offset: 0,
988                chroma_qp_offset: self.ctx.p.chroma_qp_offset,
989                ref_ids: ids,
990            },
991        };
992        self.seq += 1;
993        self.workers.push(job);
994        self.collect(self.max_in_flight, out)
995    }
996
997    fn reclaim(&mut self) {
998        let mut i = 0;
999        while i < self.retired.len() {
1000            if Arc::strong_count(&self.retired[i]) == 1 {
1001                let r = self.retired.swap_remove(i);
1002                if let Ok(p) = Arc::try_unwrap(r)
1003                    && self.pic_pool.len() < 4
1004                {
1005                    self.pic_pool.push(p);
1006                }
1007            } else {
1008                i += 1;
1009            }
1010        }
1011    }
1012
1013    fn collect(&mut self, max_in_flight: usize, out: &mut Vec<Packet>) -> Result<(), Error> {
1014        loop {
1015            let in_flight = (self.seq - self.next_out) as usize;
1016            let next = if in_flight > max_in_flight {
1017                match self.workers.done.recv() {
1018                    Ok(d) => Some(d),
1019                    Err(_) => return Err(Error::Failed("the frame threads stopped".into())),
1020                }
1021            } else {
1022                self.workers.done.try_recv().ok()
1023            };
1024            let Some(d) = next else { break };
1025            self.finished.insert(d.job.seq, d);
1026            while let Some(d) = self.finished.remove(&self.next_out) {
1027                self.next_out += 1;
1028                self.finish_frame(d, out)?;
1029            }
1030        }
1031        Ok(())
1032    }
1033
1034    fn finish_frame(&mut self, d: Done, out: &mut Vec<Packet>) -> Result<(), Error> {
1035        let res = match d.out {
1036            Ok(r) => r,
1037            Err(e) => {
1038                let why = e
1039                    .downcast_ref::<&str>()
1040                    .map(|s| s.to_string())
1041                    .or_else(|| e.downcast_ref::<String>().cloned())
1042                    .unwrap_or_else(|| "unknown cause".into());
1043                return Err(Error::Failed(format!(
1044                    "frame {} failed in its thread: {why}",
1045                    d.job.seq
1046                )));
1047            }
1048        };
1049        let job = d.job;
1050        let st = job.st;
1051        let annexb = self.cfg.format == PacketFormat::AnnexB;
1052        let mut nals: Vec<Vec<u8>> = Vec::new();
1053        if annexb {
1054            let t = match st {
1055                SliceType::I => 0,
1056                SliceType::P => 1,
1057                SliceType::B => 2,
1058            };
1059            nals.push(nal::nal(0, NAL_AUD, &nal::aud_rbsp(t)));
1060        }
1061        if job.idr && annexb {
1062            nals.push(self.sps_nal.clone());
1063            nals.push(self.pps_nal.clone());
1064        }
1065        if !self.sei_sent {
1066            self.sei_sent = true;
1067            let text = format!(
1068                "pito-codecs {} - H.264 encoder - options: {}",
1069                env!("CARGO_PKG_VERSION"),
1070                self.options()
1071            );
1072            nals.push(nal::nal(0, NAL_SEI, &nal::sei_user_data_rbsp(&text)));
1073        }
1074        nals.push(res.nal);
1075        let mut data = Vec::with_capacity(nals.iter().map(|n| n.len() + 4).sum());
1076        for n in &nals {
1077            if annexb {
1078                data.extend_from_slice(&[0, 0, 0, 1]);
1079            } else {
1080                data.extend_from_slice(&(n.len() as u32).to_be_bytes());
1081            }
1082            data.extend_from_slice(n);
1083        }
1084        self.rc.update(st, job.qpf, job.cplx, data.len() as u64 * 8);
1085        let pos = self
1086            .display_pts
1087            .iter()
1088            .enumerate()
1089            .min_by_key(|(_, p)| **p)
1090            .map(|(i, _)| i);
1091        let dts_base = pos
1092            .and_then(|p| self.display_pts.remove(p))
1093            .unwrap_or(job.pts);
1094        let Job {
1095            src,
1096            pic,
1097            refs,
1098            pts,
1099            idr,
1100            ..
1101        } = job;
1102        drop(refs);
1103        self.recycle(src);
1104        if !pic_is_ref(&pic) {
1105            if let Ok(p) = Arc::try_unwrap(pic)
1106                && self.pic_pool.len() < 4
1107            {
1108                self.pic_pool.push(p);
1109            }
1110        } else {
1111            drop(pic);
1112        }
1113        self.reclaim();
1114        out.push(Packet {
1115            data,
1116            pts,
1117            dts: dts_base,
1118            keyframe: idr,
1119        });
1120        Ok(())
1121    }
1122}
1123
1124fn pic_is_ref(p: &Pic) -> bool {
1125    p.id != 0
1126}