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}