# effectcraft-hevcenc Clean-room, pure-Rust HEVC / H.265 encoder for EffectCraft's MP4 export (layer L0, `std` only, no `unsafe`, builds for `wasm32-unknown-unknown`). ## What it produces - **Profiles:** Main (8-bit) and Main 10 (10-bit), 4:2:0, Main tier. `general_level_idc` is taken from the config or chosen as the lowest level whose MaxLumaPs, MaxLumaSr (and, in bitrate mode, MaxBR) fit (Table A.8). - **Parameter sets:** VPS, SPS (with VUI: BT.709 colour description, `video_full_range_flag`, timing info from the frame rate) and PPS, as raw NAL units (`Encoder::parameter_sets`) and as an `hvcC` record (ISO/IEC 14496-15 §8.3.3.1, `lengthSizeMinusOne` 3, complete VPS / SPS / PPS arrays, `Encoder::hvcc`). - **Pictures:** one slice per picture; `IDR_N_LP` every `keyint` pictures (`1` = all intra, `0` = only the first), `TRAIL_R` P pictures otherwise, each referencing the previous picture (one short-term RPS in the SPS, `num_ref_idx_l0_active` 1, no reordering, pts = dts). Packets are length-prefixed (4-byte big-endian) NAL units without parameter sets. - **Picture size:** any even width / height; the coded size is padded to a multiple of 8 by edge replication and cropped with the conformance window. Odd sizes are rejected with `Error::InvalidConfig` (4:2:0 conformance-window offsets are in units of two luma samples). ## Coding tools - 64x64 CTUs, coding units 64x64 .. 8x8 chosen by rate-distortion optimisation: every candidate is actually predicted, transformed, quantised and reconstructed, and its rate is measured with a CABAC bit estimator that evolves the same context states as the real coder. Split decisions compare the best unsplit candidate with the recursively coded children (early exit after a skip). - **Intra:** all 35 modes (SATD search on a coarse grid plus refinement and the MPMs), 2Nx2N at 8..32 and NxN (four 4x4 PUs) at 8x8, chroma in DM mode, MPM / remaining-mode coding, reference substitution, [1 2 1] and strong (32x32) smoothing, DC / angular boundary filters. - **Inter (P):** skip and merge (spatial candidates + zero candidates, `MaxNumMergeCand` 5) and AMVP with mvd coding; integer motion search (predictor starts + square pattern, steps 16..1) and half / quarter-sample refinement on pre-interpolated half-sample planes; the coded prediction uses the normative 8-tap / 4-tap interpolation filters and reference-picture edge clamping. - **Transforms:** DST-VII 4x4 (intra luma) and DCT 4..32 (inverse by even/odd decomposition, bit-identical to the matrix form), dead-zone quantisation (1/3 intra, 1/6 inter), flat scaling, sign data hiding, inferred transform splits only (`max_transform_hierarchy_depth_*` 0, so 64x64 CUs use four 32x32 TUs). - **Entropy coding:** CABAC (context initialisation by slice QP and init type, decision / bypass / terminate bins, flush), `end_of_slice_segment_flag`, byte alignment, emulation prevention. - **In-loop filters:** the deblocking filter is on (the encoder deblocks its own reconstruction, so references match the decoder); SAO is disabled. - **Rate control:** `ConstantQp(qp)` or `Bitrate { kbps }`, a per-picture QP from a bits-x-Qstep model per picture type (I pictures get roughly 4x the bits of a P picture) with a buffer that spreads over- and undershoot over the following pictures. Not implemented: B pictures / reordering, multiple references, TMVP, AMP and other inter partition shapes, residual quadtree splits, transform skip, scaling lists, PCM, SAO, RDOQ, cu_qp_delta / adaptive quantisation, tiles / WPP / multiple slices, range extensions (4:2:2, 4:4:4, > 10 bits). ## Measurements On a shared, heavily loaded machine, opt-level 3: about 0.45 s of CPU per 1920x1080 picture (ffmpeg `testsrc2`, QP 22 / 32). On ffmpeg's `mandelbrot` 1080p source (8 pictures, IPPP, QP 32) the stream is about 12 % larger at about 0.2 dB lower mean PSNR-Y than x265 `--preset medium` at the same QP without psy tuning (110.5 kB / 42.3 dB vs 98.3 kB / 42.5 dB). The synthetic test image reaches 45.8 dB PSNR-Y at QP 22. ## Tests `cargo test -p effectcraft-hevcenc`: - every stream is decoded with the first-party `filmcraft-hevc` decoder and compared bit-exactly with the encoder's reconstruction (`Encoder::last_reconstruction`, hidden API); - when `ffmpeg` is on `PATH`, the Annex-B elementary stream is decoded with `ffmpeg -v error -xerror` (no stderr output allowed) and compared bit-exactly as well (skipped otherwise). ffmpeg is used only as an external test oracle; - covered: 8-bit and 10-bit, all-intra and IP, QP 0 noise (escape codes), QP 0..51, strong deblocking at high QP, sizes 8x8 .. 200x136 with cropping, POC wrap (300 pictures without IDR), bitrate mode within 2x of the target, `hvcC` layout; - `bench_1080p` (ignored) reports speed, size and PSNR on 1080p content generated by ffmpeg. ## Specifications - ITU-T H.265 (V8, 08/2021) | ISO/IEC 23008-2, *High efficiency video coding*: 7 (syntax and semantics), 8.4 - 8.7 (intra / inter prediction, scaling and transforms, deblocking), 9.3 (CABAC, including the informative encoder description of 9.3.4), Annex A (profiles, tiers and levels), Annex E (VUI). - ISO/IEC 14496-15 (2019), §8.3.3.1 HEVCDecoderConfigurationRecord. Implemented from the specification text. Constant tables (CABAC context layout and initValues, rangeTabLPS / transIdx, scan orders, the DCT / DST matrices, intra angles, interpolation filters, the chroma QP table, the deblocking beta / tC tables) and the decoder-side reconstruction arithmetic (intra reference substitution / filtering / prediction, fractional interpolation, deblocking) are mirrored from FilmCraft's first-party, spec-derived `filmcraft-hevc` decoder (same organisation, MIT OR Apache-2.0), so that encoder and decoder reconstruct identically. No third-party encoder or decoder source (x265, libde265, ffmpeg, HM, ...) was consulted.