Files
craft-libs/crates/video/tools/vp9_extract_tables.py
Dylan M. Taylor 6ac2a6a34a craft-video: VP9 decoder from FilmCraft, with DeckCraft's memory budget (vp9-decode) (#821)
* craft-video: VP9 decoder from FilmCraft (`vp9-decode` feature)

FilmCraft's crates/vp9 at 4c0d2ad7 becomes craft_video::vp9::decode. The encoder moves to
vp9/encode and is still exported from craft_video::vp9. Feature vp9 now turns on vp9-decode and
vp9-encode, so either half can be built without the other.

The decoder's tests find ffmpeg and the fixture directory through craft-media-testkit; a fixture
that cannot be generated fails under CRAFT_MEDIA_REQUIRE_ORACLES=1. craft-video is built at
opt-level 3 in the dev profile, as FilmCraft built its codecs.

* craft-video: VP9 decoder checks its picture store against a memory budget

DeckCraft's copy of the decoder refuses a frame size whose pictures (eight reference slots, the
new frame and those in flight) would not fit a byte budget. The budget type comes with it as
craft_video::budget; FilmCraft's 2^26 luma sample cap stays.
2026-11-11 13:47:53 -05:00

110 lines
5.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Extract the VP9 constant tables from the text of the public VP9 Bitstream & Decoding Process
Specification (v0.6, WebM project / Google).
Usage:
curl -L -o vp9.pdf 'https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf'
pdftotext -layout vp9.pdf vp9.txt
python3 vp9_extract_tables.py vp9.txt > ../src/vp9/decode/spec_tables.rs
Only the specification text is read. Every table is emitted flat (row-major, as printed) with its
element count checked against the declared dimensions.
"""
import re
import sys
from pathlib import Path
def _spec_path(arg):
"""Resolve a CLI path argument and require it to be an existing regular file."""
path = Path(arg).resolve()
if not path.is_file():
sys.exit(f"not a readable file: {arg}")
return path
text = open(_spec_path(sys.argv[1]), encoding="utf-8").read().replace("−", "-").replace("–", "-")
lines = [l for l in text.split("\n") if "Copyright" not in l and "VP9 Bitstream & Decoding Process Specification" not in l]
text = "\n".join(lines)
def table(name, count):
"""Integers of the table `name[...] = { ... }` (the last definition in the text)."""
starts = [m.start() for m in re.finditer(r"\b%s\s*\[" % re.escape(name), text)]
for s in reversed(starts):
eq = text.find("=", s)
brace = text.find("{", s)
if eq < 0 or brace < 0 or eq > brace or text[s:eq].count("\n") > 2:
continue
depth = 0
i = brace
while True:
ch = text[i]
if ch == "{":
depth += 1
elif ch == "}":
depth -= 1
if depth == 0:
break
i += 1
body = text[brace:i]
body = re.sub(r"//[^\n]*", "", body)
body = re.sub(r"/\*.*?\*/", "", body, flags=re.S)
vals = [int(v) for v in re.findall(r"-?\d+", body)]
assert len(vals) == count, (name, len(vals), count)
return vals
raise AssertionError("table not found: " + name)
def emit(rust_name, ty, vals, per_line=16):
print("pub const %s: [%s; %d] = [" % (rust_name, ty, len(vals)))
for k in range(0, len(vals), per_line):
print(" " + ", ".join(str(v) for v in vals[k:k + per_line]) + ",")
print("];")
print()
print("// Generated by tools/extract_tables.py from the VP9 Bitstream & Decoding Process Specification")
print("// v0.6 (section numbers refer to that document). Do not edit by hand.")
print()
emit("INV_MAP_TABLE", "u8", table("inv_map_table", 255))
emit("SUBPEL_FILTERS", "i16", table("subpel_filters", 4 * 16 * 8), 8)
emit("DC_QLOOKUP", "i32", table("dc_qlookup", 3 * 256))
emit("AC_QLOOKUP", "i32", table("ac_qlookup", 3 * 256))
emit("COS64_LOOKUP", "i32", table("cos64_lookup", 33))
for n, c in [("4x4", 16), ("8x8", 64), ("16x16", 256)]:
for kind in ["default", "col", "row"]:
emit("%s_SCAN_%s" % (kind.upper(), n.upper()), "u16", table("%s_scan_%s" % (kind, n), c))
emit("DEFAULT_SCAN_32X32", "u16", table("default_scan_32x32", 1024))
emit("COEFBAND_4X4", "u8", table("coefband_4x4", 16))
emit("COEFBAND_8X8PLUS", "u8", table("coefband_8x8plus", 1024))
emit("ENERGY_CLASS", "u8", table("energy_class", 12))
emit("PARETO_TABLE", "u8", table("pareto_table", 128 * 8), 8)
emit("MV_REF_BLOCKS", "i8", table("mv_ref_blocks", 13 * 8 * 2))
emit("MODE_2_COUNTER", "u8", table("mode_2_counter", 14))
emit("KF_PARTITION_PROBS", "u8", table("kf_partition_probs", 16 * 3), 3)
emit("KF_Y_MODE_PROBS", "u8", table("kf_y_mode_probs", 10 * 10 * 9), 9)
emit("KF_UV_MODE_PROBS", "u8", table("kf_uv_mode_probs", 10 * 9), 9)
emit("DEFAULT_PARTITION_PROBS", "u8", table("default_partition_probs", 16 * 3), 3)
emit("DEFAULT_Y_MODE_PROBS", "u8", table("default_y_mode_probs", 4 * 9), 9)
emit("DEFAULT_UV_MODE_PROBS", "u8", table("default_uv_mode_probs", 10 * 9), 9)
emit("DEFAULT_SKIP_PROB", "u8", table("default_skip_prob", 3))
emit("DEFAULT_IS_INTER_PROB", "u8", table("default_is_inter_prob", 4))
emit("DEFAULT_COMP_MODE_PROB", "u8", table("default_comp_mode_prob", 5))
emit("DEFAULT_COMP_REF_PROB", "u8", table("default_comp_ref_prob", 5))
emit("DEFAULT_SINGLE_REF_PROB", "u8", table("default_single_ref_prob", 10), 2)
emit("DEFAULT_MV_SIGN_PROB", "u8", table("default_mv_sign_prob", 2))
emit("DEFAULT_MV_BITS_PROB", "u8", table("default_mv_bits_prob", 20), 10)
emit("DEFAULT_MV_CLASS0_BIT_PROB", "u8", table("default_mv_class0_bit_prob", 2))
emit("DEFAULT_TX_PROBS", "u8", table("default_tx_probs", 4 * 2 * 3), 3)
emit("DEFAULT_INTER_MODE_PROBS", "u8", table("default_inter_mode_probs", 7 * 3), 3)
emit("DEFAULT_INTERP_FILTER_PROBS", "u8", table("default_interp_filter_probs", 8), 2)
emit("DEFAULT_MV_JOINT_PROBS", "u8", table("default_mv_joint_probs", 3))
emit("DEFAULT_MV_CLASS_PROBS", "u8", table("default_mv_class_probs", 20), 10)
emit("DEFAULT_MV_CLASS0_FR_PROBS", "u8", table("default_mv_class0_fr_probs", 12), 3)
emit("DEFAULT_MV_CLASS0_HP_PROB", "u8", table("default_mv_class0_hp_prob", 2))
emit("DEFAULT_MV_FR_PROBS", "u8", table("default_mv_fr_probs", 6), 3)
emit("DEFAULT_MV_HP_PROB", "u8", table("default_mv_hp_prob", 2))
emit("DEFAULT_COEF_PROBS", "u8", table("default_coef_probs", 4 * 2 * 2 * 6 * 6 * 3), 3)