Skip to main content

Encoder

Struct Encoder 

Source
pub struct Encoder { /* private fields */ }
Expand description

A Constrained Baseline H.264 encoder.

Implementations§

Source§

impl Encoder

Source

pub fn new(cfg: EncoderConfig) -> Result<Self, EncodeError>

Creates an encoder, validating that the configuration is within the implemented subset.

Examples found in repository?
examples/rd_skip_speed.rs (line 63)
57fn encode(frames: &[YuvFrame], w: usize, h: usize, rd_skip: bool) -> (f64, usize) {
58    let mut cfg = EncoderConfig::new(w, h);
59    cfg.qp = std::env::var("RS_QP").ok().and_then(|v| v.parse().ok()).unwrap_or(27);
60    cfg.gop_size = 30;
61    cfg.tune_rd_skip = rd_skip;
62    cfg.tune_rd_skip_fast_t = std::env::var("RS_GATE").ok().and_then(|v| v.parse().ok());
63    let mut enc = Encoder::new(cfg).expect("encoder");
64    let t = std::time::Instant::now();
65    let mut bytes = 0usize;
66    for fr in frames {
67        bytes += enc.encode(fr).len();
68    }
69    (t.elapsed().as_secs_f64(), bytes)
70}
More examples
Hide additional examples
examples/me_snap_ab.rs (line 42)
24fn main() {
25    let a: Vec<String> = std::env::args().skip(1).collect();
26    let (w, h) = a[1].split_once('x').unwrap();
27    let (w, h): (usize, usize) = (w.parse().unwrap(), h.parse().unwrap());
28    let frames = load(&a[0], w, h, std::env::var("RS_FRAMES").ok().and_then(|v| v.parse().ok()).unwrap_or(60));
29    let preset = match std::env::var("RS_PRESET").unwrap_or_default().as_str() {
30        "fast" => Preset::Fast, "quality" => Preset::Quality, _ => Preset::Balanced,
31    };
32    let arm_off: u32 = std::env::var("RS_ARM_OFF").ok().and_then(|v| v.parse().ok()).unwrap_or(0);
33    let arm_on: u32 = std::env::var("RS_ARM_ON").ok().and_then(|v| v.parse().ok()).unwrap_or(3);
34    let run = |on: bool| {
35        let m = if on { arm_on } else { arm_off };
36        let mut cfg = EncoderConfig::new(w, h);
37        cfg.qp = std::env::var("RS_QP").ok().and_then(|v| v.parse().ok()).unwrap_or(27);
38        cfg.gop_size = 30;
39        cfg.preset = preset;
40        cfg.tune_me_snap = m & 1 != 0;
41        cfg.tune_me_subpel_iter = m & 2 != 0;
42        let mut enc = Encoder::new(cfg).expect("enc");
43        let t = std::time::Instant::now();
44        let mut b = 0usize;
45        for f in &frames { b += enc.encode(f).len(); }
46        (t.elapsed().as_secs_f64(), b)
47    };
48    let mut best = [f64::MAX; 2];
49    let mut bytes = [0usize; 2];
50    for pass in 0..10 {
51        let arm = pass % 2;
52        let (t, b) = run(arm == 1);
53        if t < best[arm] { best[arm] = t; }
54        bytes[arm] = b;
55    }
56    let px = (w * h * frames.len()) as f64;
57    println!("arm {arm_off} -> {arm_on} — {} {w}x{h} {} frames {preset:?}", a[0], frames.len());
58    println!("  off : {:>7.1} ms  {:>6.2} Mpx/s  {:>9} bytes", best[0]*1e3, px/best[0]/1e6, bytes[0]);
59    println!("  on  : {:>7.1} ms  {:>6.2} Mpx/s  {:>9} bytes", best[1]*1e3, px/best[1]/1e6, bytes[1]);
60    println!("  speed {:>6.3}x   size {:>+6.2}%", best[0]/best[1],
61             100.0*(bytes[1] as f64/bytes[0] as f64 - 1.0));
62}
examples/me_oracle.rs (line 47)
31fn main() {
32    let a: Vec<String> = std::env::args().skip(1).collect();
33    let (w, h) = a[1].split_once('x').unwrap();
34    let (w, h): (usize, usize) = (w.parse().unwrap(), h.parse().unwrap());
35    let nf: usize = std::env::var("RS_FRAMES").ok().and_then(|v| v.parse().ok()).unwrap_or(20);
36    let frames = load(&a[0], w, h, nf);
37
38    let preset = match std::env::var("RS_PRESET").unwrap_or_default().as_str() {
39        "fast" => Preset::Fast,
40        "quality" => Preset::Quality,
41        _ => Preset::Balanced,
42    };
43    let mut cfg = EncoderConfig::new(w, h);
44    cfg.qp = std::env::var("RS_QP").ok().and_then(|v| v.parse().ok()).unwrap_or(27);
45    cfg.gop_size = 30;
46    cfg.preset = preset;
47    let mut enc = Encoder::new(cfg).expect("encoder");
48    for f in &frames {
49        let _ = enc.encode(f);
50    }
51
52    let p: Vec<u64> = rusty_h264_encoder::ME_PROBE
53        .iter()
54        .map(|c| c.load(std::sync::atomic::Ordering::Relaxed))
55        .collect();
56    let (n, ours, oracle, worse, evals) = (p[0].max(1), p[1], p[2], p[3], p[4]);
57    let (oracle_sp, worse_sp) = (p[5], p[6]);
58    println!("ME oracle — {} ({w}x{h}, {} frames, {preset:?})\n", a[0], frames.len());
59    println!("  searches                {n}");
60    println!("  mean cost   ours        {:>10.1}", ours as f64 / n as f64);
61    println!("  mean cost   exhaustive  {:>10.1}", oracle as f64 / n as f64);
62    println!("  ---> we are {:>6.2}% above the achievable minimum",
63             100.0 * (ours as f64 - oracle as f64) / oracle as f64);
64    println!("  searches the oracle beat {:>9} ({:.1}%)", worse, 100.0 * worse as f64 / n as f64);
65    // the oracle's own 49x49 grid + 8 sub-pel probes are included in `evals`
66    println!("
67  + exhaustive SUB-PEL (all quarter-pel in +-3):");
68    println!("  mean cost   exhaustive  {:>10.1}", oracle_sp as f64 / n as f64);
69    println!("  ---> we are {:>6.2}% above the achievable minimum",
70             100.0 * (ours as f64 - oracle_sp as f64) / oracle_sp as f64);
71    println!("  searches it beat        {:>9} ({:.1}%)", worse_sp, 100.0 * worse_sp as f64 / n as f64);
72    let oracle_evals = 0;
73    println!("\n  cost() evals/search     {:>8.1}  (ours, oracle's {oracle_evals} excluded)",
74             evals as f64 / n as f64 - oracle_evals as f64);
75}
Source

pub fn config(&self) -> &EncoderConfig

The active configuration.

Source

pub fn encode(&mut self, frame: &YuvFrame) -> Vec<u8>

Encodes one frame, returning the Annex-B access unit. Every gop_size frames (and always the first) is coded as an IDR, prefixed with SPS/PPS.

Generation 1 codes every picture as an IDR (all-intra); inter frames arrive with motion compensation later.

Examples found in repository?
examples/rd_skip_speed.rs (line 67)
57fn encode(frames: &[YuvFrame], w: usize, h: usize, rd_skip: bool) -> (f64, usize) {
58    let mut cfg = EncoderConfig::new(w, h);
59    cfg.qp = std::env::var("RS_QP").ok().and_then(|v| v.parse().ok()).unwrap_or(27);
60    cfg.gop_size = 30;
61    cfg.tune_rd_skip = rd_skip;
62    cfg.tune_rd_skip_fast_t = std::env::var("RS_GATE").ok().and_then(|v| v.parse().ok());
63    let mut enc = Encoder::new(cfg).expect("encoder");
64    let t = std::time::Instant::now();
65    let mut bytes = 0usize;
66    for fr in frames {
67        bytes += enc.encode(fr).len();
68    }
69    (t.elapsed().as_secs_f64(), bytes)
70}
More examples
Hide additional examples
examples/me_snap_ab.rs (line 45)
24fn main() {
25    let a: Vec<String> = std::env::args().skip(1).collect();
26    let (w, h) = a[1].split_once('x').unwrap();
27    let (w, h): (usize, usize) = (w.parse().unwrap(), h.parse().unwrap());
28    let frames = load(&a[0], w, h, std::env::var("RS_FRAMES").ok().and_then(|v| v.parse().ok()).unwrap_or(60));
29    let preset = match std::env::var("RS_PRESET").unwrap_or_default().as_str() {
30        "fast" => Preset::Fast, "quality" => Preset::Quality, _ => Preset::Balanced,
31    };
32    let arm_off: u32 = std::env::var("RS_ARM_OFF").ok().and_then(|v| v.parse().ok()).unwrap_or(0);
33    let arm_on: u32 = std::env::var("RS_ARM_ON").ok().and_then(|v| v.parse().ok()).unwrap_or(3);
34    let run = |on: bool| {
35        let m = if on { arm_on } else { arm_off };
36        let mut cfg = EncoderConfig::new(w, h);
37        cfg.qp = std::env::var("RS_QP").ok().and_then(|v| v.parse().ok()).unwrap_or(27);
38        cfg.gop_size = 30;
39        cfg.preset = preset;
40        cfg.tune_me_snap = m & 1 != 0;
41        cfg.tune_me_subpel_iter = m & 2 != 0;
42        let mut enc = Encoder::new(cfg).expect("enc");
43        let t = std::time::Instant::now();
44        let mut b = 0usize;
45        for f in &frames { b += enc.encode(f).len(); }
46        (t.elapsed().as_secs_f64(), b)
47    };
48    let mut best = [f64::MAX; 2];
49    let mut bytes = [0usize; 2];
50    for pass in 0..10 {
51        let arm = pass % 2;
52        let (t, b) = run(arm == 1);
53        if t < best[arm] { best[arm] = t; }
54        bytes[arm] = b;
55    }
56    let px = (w * h * frames.len()) as f64;
57    println!("arm {arm_off} -> {arm_on} — {} {w}x{h} {} frames {preset:?}", a[0], frames.len());
58    println!("  off : {:>7.1} ms  {:>6.2} Mpx/s  {:>9} bytes", best[0]*1e3, px/best[0]/1e6, bytes[0]);
59    println!("  on  : {:>7.1} ms  {:>6.2} Mpx/s  {:>9} bytes", best[1]*1e3, px/best[1]/1e6, bytes[1]);
60    println!("  speed {:>6.3}x   size {:>+6.2}%", best[0]/best[1],
61             100.0*(bytes[1] as f64/bytes[0] as f64 - 1.0));
62}
examples/me_oracle.rs (line 49)
31fn main() {
32    let a: Vec<String> = std::env::args().skip(1).collect();
33    let (w, h) = a[1].split_once('x').unwrap();
34    let (w, h): (usize, usize) = (w.parse().unwrap(), h.parse().unwrap());
35    let nf: usize = std::env::var("RS_FRAMES").ok().and_then(|v| v.parse().ok()).unwrap_or(20);
36    let frames = load(&a[0], w, h, nf);
37
38    let preset = match std::env::var("RS_PRESET").unwrap_or_default().as_str() {
39        "fast" => Preset::Fast,
40        "quality" => Preset::Quality,
41        _ => Preset::Balanced,
42    };
43    let mut cfg = EncoderConfig::new(w, h);
44    cfg.qp = std::env::var("RS_QP").ok().and_then(|v| v.parse().ok()).unwrap_or(27);
45    cfg.gop_size = 30;
46    cfg.preset = preset;
47    let mut enc = Encoder::new(cfg).expect("encoder");
48    for f in &frames {
49        let _ = enc.encode(f);
50    }
51
52    let p: Vec<u64> = rusty_h264_encoder::ME_PROBE
53        .iter()
54        .map(|c| c.load(std::sync::atomic::Ordering::Relaxed))
55        .collect();
56    let (n, ours, oracle, worse, evals) = (p[0].max(1), p[1], p[2], p[3], p[4]);
57    let (oracle_sp, worse_sp) = (p[5], p[6]);
58    println!("ME oracle — {} ({w}x{h}, {} frames, {preset:?})\n", a[0], frames.len());
59    println!("  searches                {n}");
60    println!("  mean cost   ours        {:>10.1}", ours as f64 / n as f64);
61    println!("  mean cost   exhaustive  {:>10.1}", oracle as f64 / n as f64);
62    println!("  ---> we are {:>6.2}% above the achievable minimum",
63             100.0 * (ours as f64 - oracle as f64) / oracle as f64);
64    println!("  searches the oracle beat {:>9} ({:.1}%)", worse, 100.0 * worse as f64 / n as f64);
65    // the oracle's own 49x49 grid + 8 sub-pel probes are included in `evals`
66    println!("
67  + exhaustive SUB-PEL (all quarter-pel in +-3):");
68    println!("  mean cost   exhaustive  {:>10.1}", oracle_sp as f64 / n as f64);
69    println!("  ---> we are {:>6.2}% above the achievable minimum",
70             100.0 * (ours as f64 - oracle_sp as f64) / oracle_sp as f64);
71    println!("  searches it beat        {:>9} ({:.1}%)", worse_sp, 100.0 * worse_sp as f64 / n as f64);
72    let oracle_evals = 0;
73    println!("\n  cost() evals/search     {:>8.1}  (ours, oracle's {oracle_evals} excluded)",
74             evals as f64 / n as f64 - oracle_evals as f64);
75}
Source

pub fn try_encode(&mut self, frame: &YuvFrame) -> Result<Vec<u8>, EncodeError>

Fallible encode: validates the frame against the config.

Source

pub fn encode_all( &self, frames: &[YuvFrame], ) -> Result<Vec<Vec<u8>>, EncodeError>

Batch-encodes every frame, returning one Annex-B access unit per frame.

At constant QP the GOPs are independent — each begins with an IDR that resets the DPB, frame_num and POC, and SPS/PPS precede every IDR — so they are encoded in parallel across CPU cores and the result is byte-identical to calling encode frame-by-frame. With rate control enabled the per-frame QP depends on history, so this falls back to sequential encoding. Within a GOP, P-frames are inherently sequential (each predicts from the previous reconstruction); the parallelism is across GOPs, so it scales with the number of GOPs in the clip.

Trait Implementations§

Source§

impl Debug for Encoder

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.