Compare commits

...
Author SHA1 Message Date
enricobuehler db15c2615d fix(vkdecode): HEVC decoded from the wrong references, and told drivers the
wrong slice offsets

Two independent defects, both in this crate. pf-bitstream is untouched — its
HEVC plans were sound all along, which the D3D11VA rung proves by rendering
correctly from the same AuPlans.

The corrupter: StdVideoDecodeH265PictureInfo's RefPicSetStCurrBefore,
StCurrAfter and LtCurr carry DPB SLOT indices. We wrote positions in the
reference list. libavcodec's Vulkan HEVC hwaccel — the implementation every
driver is validated against — writes the index into its own DPB array and
passes that same value as slotIndex, while packing pReferenceSlots densely
over the used entries; the two numberings are provably different there, and
the RPS arrays follow the slot.

The two readings coincide on a freshly anchored stream, because the references
then occupy slots 0..n in reference-list order. They first diverge at the
vendored vector's first B picture, AU 3, where refs are slots 0, 2, 1 — so we
named slots 1 and 2 where the picture wanted 2 and 1, and every later access
unit inherited the error through its own references. That is why this shipped
and why review could not see it: correct for the opening pictures, wrong from
the first reordering onward.

It also accounts for the measurements exactly. Display order maps to decode
order as display 0 from AU 0, display 3 from AU 1, display 2 from AU 2,
display 1 from AU 3 — so the three frames that matched on AMD are precisely
the three access units where positions and slots agree, and 250 - 3 = 247 is
the divergence count that was measured. From AU 6 the named slots stop being
merely wrong and become unbindable by that operation, which is where NVIDIA
stopped reporting a verdict at all.

The diagnostic: every slice offset must point at a THREE-byte start code.
libavcodec discards the stream's prefix and writes 00 00 01, so that is the
only pattern drivers are validated on, and pf-dxvadec's packer already
normalised for exactly this reason and said so in its docs. This path uploaded
the prefix verbatim. 249 of 250 HEVC slice segments in the vendored vector
carry a four-byte prefix; all 500 H.264 slices carry three.

That derives the driver's own complaint bit for bit. A decoder reaching the
slice header by a fixed skip lands on the NAL header's second byte, reads
first_slice_segment_in_pic_flag as 0, and then takes six bits of the real
slice header as the tail of a long ue(v): 0xd0 gives 115, 0xe0 gives 119. A
P-slice header and a B-slice header — which is why exactly two bogus pps_id
values ever appeared.

⚠ H.264 was NOT protected structurally, only by its encoder's convention, and
the real host does not share that convention: every one of 1514 H.264 access
units and 1133 HEVC access units captured from an NVENC host prefixes its
slices with FOUR bytes. The vendored H.264 vector is therefore not
representative of what ships, and its bit-exactness was passing on a prefix
form the field never sends. The normalisation lives in the shared ring layer
and covers both codecs for that reason.

rebased_offsets is replaced by pack_slices, which trims the leading zero byte
and computes the offsets from the trimmed lengths in one call, so the bytes and
the offsets cannot drift apart; upload and the CPU test go through the same
pack_into.

Hardware, after the fix — H.264 AND H.265 both 250/250 bit-identical to
libavcodec, all four smoke legs green:

  NVIDIA RTX 4090      610.88     Windows   coincide
  AMD Adrenalin        25.10.30.02 Windows  distinct
  NVIDIA RTX 5070 Ti   610.43.03  Linux     coincide

On glass on the 4090 against a real NVENC host, 2800x1260 HEVC through the
auto ladder: 73 one-second windows all native-vulkan, fps avg 59.3 of 60,
decode 1.1 ms, e2e 4.5 ms p50, and ZERO driver-reported status failures where
the same session before the fix logged 1489 in 181 seconds and had dragged ABR
down to a 5 Mb/s target. No refusals, demotions, PlanWarnings, concealment,
DEVICE_LOSTs or panics.

Both defects now have CPU tests that were confirmed FAILING before the fix:
one walks every access unit of both vendored vectors and asserts each declared
offset opens on a three-byte start code, its own NAL header and a
first_slice_segment_in_pic_flag consistent with the segment index; the other
resolves every RPS entry by slot and asserts that 247 access units disagree
with the positional reading, so it cannot go vacuous on a stream where the two
happen to agree.
2026-08-06 09:56:25 +02:00
enricobuehler 5c6b09a5c5 test(vkdecode): the HEVC GPU legs, which find M3 broken on every driver
M3 was recorded as code complete. Its exit criteria named the HEVC gpu_smoke
and gpu_parity legs, and the goldens for them were committed — 250 per-frame
NV12 hashes, cross-checked between two independent FFmpeg builds, with a
header saying they are "consumed the same way by the HEVC parity test". No
such test existed. Both GPU files were H.264 only, with zero references to
h265, so nothing had ever decoded a single HEVC frame through this crate on
hardware.

They exist now, and the first run answered. On AMD Adrenalin 25.10.30.02
(distinct mode, queryResultStatusSupport=false) 247 of 250 frames diverge
from libavcodec, and that device's smoke leg PASSES — because smoke only
reads the driver's verdict and that driver reports none. That is the Ally X
class, reproduced in-house on demand: output that is wrong everywhere the
picture is looked at and clean everywhere the decoder is asked. Both NVIDIA
drivers reject the stream outright and name the cause themselves,
"Invalid PPS/SPS id in slice header (pps_id=119 / 115)" — the identical two
values, and the smoke leg dies at the identical AU 9, on a 4090 under 610.88
on Windows and on an RTX 5070 Ti under 610.43.03 on Linux. Same wrong values,
same access unit, two GPU generations, two operating systems: deterministic,
and therefore ours rather than any driver's.

It is not an ordering fault. Five of the divergent hashes appear nowhere in
the 250 goldens, so the pixels are wrong rather than correct-but-reordered.
Parity dies at frame 1 while smoke dies at AU 9 only because smoke holds four
frames before it looks; the first inter-predicted picture is already corrupt.

The legs are committed ahead of the fix deliberately. They are the regression
test for the defect, they are #[ignore]d so no CI leg changes colour, and the
evidence above is worth recording in the order it was obtained.

Adding a third and fourth copy of ~150 lines of unsafe Vulkan bring-up was
not acceptable, so it moved to tests/common. The two behavioural differences
between the callers are now named parameters rather than accidents: the parity
legs read back on a graphics queue and require one, while the smoke legs
accept a decode-only device and fall back to the decode family — which also
decides whether pool images are EXCLUSIVE or CONCURRENT, so it is load-bearing
rather than cosmetic. H.264 came through the refactor unchanged, verified two
ways: argument-by-argument against the previous file, and on hardware, still
250/250 bit-identical on NVIDIA Windows, AMD Windows and now NVIDIA Linux.

The loader is deliberately leaked at teardown. ash::Entry owns the Arc<Library>,
so dropping it unloads the Vulkan loader with every ICD and implicit layer;
harmless while each binary held one GPU leg, but each now holds two, and the
second would re-open a loader the first had torn down.

Three guards run without a GPU, because everything above is #[ignore]d: the
golden file's count and digest shape, the HEVC access-unit split agreeing with
what the CPU planner emits (with iraps == 1 pinning "no CRA anywhere", so a
re-synced vector that opens with one fails here rather than as a frame-count
mismatch on the fleet), the vector staying Main 4:2:0 8-bit since both legs
hard-code that probe, and a refusal to run the smoke legs with
PF_VKD_TEST_READBACK set, which would quietly grow the pool a usage flag
production never carries.
2026-08-06 09:47:30 +02:00
enricobuehler 9c10b97e31 fix(client): M5's DXVA bytes now answer to libavcodec's own
The native D3D11VA rung was committed with zero hardware evidence and, more
to the point, zero evidence of any kind: every claim it made about the DXVA
structures rested on reading the specification and reading libavcodec, and
reading is exactly the method that produced the four defects the last review
found. Three of those lived where a smoke test cannot look — in the buffer
descriptors — so a passing session would have proved nothing about them.

So this compares our submission against libavcodec's, byte for byte, on the
same access units of the same two vendored vectors. The reference side comes
from an FFmpeg n8.1 cross-built for Windows with the DXVA paths instrumented
(the recipe is in the harness docs); the comparison covers picture parameters,
quantisation matrices AND the buffer descriptors, 250 AUs per codec:

    H.264 / HEVC picture parameters    250 AUs, no undocumented divergence
    H.264 / HEVC quantisation matrices 250 AUs, no undocumented divergence
    H.264 / HEVC buffer descriptors    250 AUs, no undocumented divergence

It found a real defect immediately. The DXVA short slice record is TEN bytes
— dxva.h packs these bitstream-layout structs to a byte — and this crate
declared it `repr(C)`, which pads {u32,u32,u16} to twelve. libavcodec's own
descriptors say so twice over: 20 bytes of slice control for a two-slice
H.264 picture, 10 for a one-slice HEVC one. Record 0 survives either way
(its fields sit at 0/4/8 regardless), so the mistake is invisible on a
single-slice stream and displaces every later record by two bytes on a
multi-slice one — which punktfunk hosts emit. Both records are now
`repr(C, packed)`, and the HEVC slice-control test grew a second record
because one record is the shape that hid this.

The audit that followed matters more than the fix. Per-field offset asserts
cannot see TAIL padding, which is what this was, so all six hand-declared
structs now also assert that their size equals the last field's offset plus
that field's own size. Under that rule the slice records were the only place
packed and natural alignment disagree — 1040, 232, 224 and 1000 were right
all along, and now provably rather than luckily. The module docs claimed
`repr(C)` "reproduces MSVC's default packing exactly for that shape"; that
was a guess wearing a proof's clothes, and it is gone.

Two differences are documented rather than fixed, each with the argument for
why it is inert. libavcodec seeds prev_poc_msb = 1 << 16 at every IDR, so its
POCs are the specification's plus 65536 uniformly; every use a driver makes
of those fields is a difference, and references match on FrameNumList, so the
harness compares POCs relative to that constant and requires it to hold on
every AU rather than importing a magic number into a derivation the Vulkan
rung shares. And HEVC's loop_filter_across_tiles_enabled_flag is inferred 1
by 7.4.3.3.1 when the PPS codes no tiles while libav leaves it 0, with tiles
disabled either way. Both ride a channel that always prints, and both are
guarded by tests that synthesise the differences an allowance must NOT
absorb — a documented divergence that swallows a real defect would be worse
than no harness at all.

Everything checkable without a capture is now a non-ignored test: the buffer
set and order per codec, NumMBsInBuffer's codec asymmetry (mb_width*mb_height
on H.264's bitstream and slice-control buffers, zero everywhere for HEVC),
the three 7.4.5 scaling-list cases, contiguous slice records tiling DataSize,
the 128-byte padding charged to the last record and no other. That is the
part which would have caught the last round's defects with no hardware at all.
2026-08-06 09:46:53 +02:00
enricobuehler 31087697a9 feat(client): M5 — native D3D11VA decode, pin-only pending hardware
The Windows fallback rung, and auto's first choice on Intel, now has a
native implementation driven by pf-bitstream's plans instead of
libavcodec. New crate pf-dxvadec holds everything that can be a pure
function — the DXVA structure layouts, both codec conversions, bitstream
packing, config selection — deliberately CROSS-PLATFORM, because a
cfg(windows) module is verified by a remote cargo check and nothing
else, and this milestone's riskiest code is exactly the part no local
test can see. Only the FFI lives in video_d3d11_native.rs.

windows-rs does not generate dxva.h at the pinned rev, so the DXVA
structures are hand-declared: compile-time assertions on every struct
size AND every field offset, packed bitfield words as plain integers
with named builders and the bit positions written beside the C
declaration, and a const zeroed() per struct so construction needs no
unsafe at all. The crate's only unsafe is a sealed byte view over those
PODs. Review round 13 checked all seven layouts field by field in
declaration order — sizes, widths, array lengths, the PicEntry
index/flag packing, and every named bit's position and width.

The decode pool reproduces libavcodec's rather than inventing one:
ONE texture with ArraySize = pool size, BIND_DECODER and nothing else,
MiscFlags 0, aligned 16 for H.264 and 128 for HEVC. That is deliberate.
This rung's predecessor records that a hand-built pool which validated
on NVIDIA was rejected by Intel at the first SubmitDecoderBuffers — and
Intel is the vendor this rung exists for. The VideoProcessorBlt into
shareable RGBA is untouched: importing a multiplanar NV12 D3D11 texture
into Vulkan device-losts on NVIDIA, so that hand-off is load-bearing
field-proven code. It was extracted into a shared HandoffRing so both
rungs fill one implementation; the review diffed the blit statement by
statement, including the keyed-mutex pairing.

Review round 13's four defects are fixed. The blocking one: the HEVC
quantisation matrix was submitted unconditionally, and the vendored
parser leaves it ALL ZEROS unless the stream codes one — unlike FFmpeg,
which seeds the spec defaults. On a stream saying 'use the default
matrices' the driver is obliged to apply what it is handed, so every
residual would dequantise to zero and the picture would drift to flat
prediction. It is now gated on scaling_list_enabled_flag exactly as
libav gates it, with the Table 7-5/7-6 defaults supplied when enabled
but uncoded.

Second: NumMBsInBuffer was 0 where libav's H.264 path sets
mb_width * mb_height. This module's whole method is verbatim
reproduction on precisely the call that once failed for Intel, so an
omitted descriptor field is the same class of bug as the pool.

Third, and the one to watch on hardware: RefFrameList carried the
frame's reference set rather than the pictures marked used for
reference. Vulkan defines pReferenceSlots as the slots this operation
uses, so a subset is correct there; DXVA defines RefFrameList as a
statement about the DPB. The list DERIVATION survives a subset — which
is exactly why a smoke test would have passed — but a long-term
reference held across frames that none of them name would vanish and
reappear, and a driver keeping per-reference state is entitled to
discard it in between. That is the Ally X symptom shape. pf-bitstream
now exposes a per-AU DPB snapshot for both codecs and the converters
build the array from it, frame references first, marked tail appended.
121 of the 250 vendored AUs carry a marked picture the frame never
names, so this is exercised, not theoretical.

Fourth: the session identity omitted bit depth and chroma, while the
Windows host flips an HDR desktop to PQ in-band with a new SPS — a
depth change at unchanged size would have decoded 10-bit samples into
an NV12 pool. Identity now derives from the SPS per AU and rebuilds.

Wired PIN-ONLY (PUNKTFUNK_DECODER=native-d3d11va), absent from every
auto arm. Nothing has decoded a frame yet, and M2's discipline was that
auto admission comes only after hardware parity. A runtime streak
demotes to the FFmpeg D3D11VA rung first, then software.

Also scaffolded: a byte-diff harness against libavcodec's own DXVA
picture parameters, with the FFmpeg patch and capture recipe in its
docs. Nothing here is checked against libav's actual bytes the way M3
was checked against its pixels, and that is the cheap way to buy the
confidence before hardware.

Gates: fmt clean; container clippy -D warnings zero across
pf-client-core + pf-presenter + pf-vkdecode + pf-dxvadec +
punktfunk-core; tests 73/131/63/129/354 green; cargo check --workspace
clean; Windows cargo check and clippy -D warnings clean on .173.
2026-08-06 06:11:21 +02:00
enricobuehler 2a57ee36f8 feat(client): M4 — the decoder's own verdict reaches the session
This program exists because a field corruption was architecturally
undetectable through FFmpeg: no decode-status read, no corrupt-frame
flag, errors only as scraped log lines, and no recovery-point signal so
intra-refresh healing was invisible. The native decoder has all of those.
M4 is where they stop being internal.

DecodeHealth counts, per session and without allocating per frame, what
the three answers actually are: damaged (the stream arrived incomplete),
refused (the rung would not decode it at all) and driver-failed (the
hardware says it could not decode what arrived), plus the current and
worst concealment run — the figures that separate one bad AU from a
stream that never came back. They ride the stats line additively, so an
FFmpeg session and a healthy native session emit byte-identical output
to today. The status-query capability is reported too: without it a
clean report cannot be told from an unmeasured one, which is the whole
nb_queries=0 lesson.

The headline is local recovery. Until now the pump could only learn that
intra-refresh healing finished from wire flags the host sends; absent
those it froze until the 500 ms backstop forced an IDR. The parsed
recovery-point SEI now feeds the re-anchor gate directly, so a session
lifts on the picture that is actually clean. Wire semantics are
untouched for every client that never calls it.

Detection now asks for recovery instead of erroring — an integrity
warning ticking the error streak would demote the native rung on exactly
the lossy links it exists to diagnose, where an FFmpeg rung conceals
silently and keeps its job.

Review round 12 found that trade had removed the escape hatch entirely.
Concealment returning Ok(None) reset the demotion streak, and worse: the
driver-verdict ledger is only populated when a frame ships, so under
continuous concealment no verdict was ever read and the erroring arm
could not fire at all. A host framing regression of the 0.23.0
slice-wire class — which does not self-heal, and which a keyframe does
not clear — would have frozen indefinitely with no demotion and a clean
integrity line, where before it demoted to FFmpeg-Vulkan and showed a
picture. Now only an answer that proves the rung works clears the
streak: a shipped frame, or a clean no-frame. Concealment neither ticks
nor clears, so a lossy link still cannot demote a healthy rung while a
driver failure interleaved with concealment reaches the threshold again.

Two more honesty defects from the same round. A rung refusing every AU
reported no integrity line at all — the founding failure mode, wearing
the shape of a clean bill of health; refusals are now counted. And
driver-failed could be non-zero on a device that cannot produce driver
verdicts, because a degraded timeline read looked the same as one; the
attribution is now withheld inside the counter rather than at call
sites, so the self-contradictory line is unrepresentable.

Local recovery also no longer trusts any recovery-point SEI: only one
whose target advances past an outstanding wave counts as a new wave, so
an encoder re-announcing the current wave with a decreasing count — legal,
and what x264 intra-refresh does — cannot lift the freeze early onto a
partially stale picture. Frames buffered across an arm are dropped by
decode order for the same reason.

Fault injection is a first-class tool now (PUNKTFUNK_AU_FAULT, inert
unless set, env read once). Its test replays the vendored vectors
through the real planners and asserts a negative the plan assumed away:
truncation and bit flips are PROVABLY invisible to the parser — Annex-B
carries no NALU length, so a cut slice is just a shorter slice and a
flipped payload byte is syntactically perfect. Only dropped AUs are
parser-detectable; the rest need the driver verdict, which is why the
status query matters. The H.265 leg found a second: three of that
vector's faulted AUs are sub-layer non-reference pictures, so dropping
them damages nothing and silence is correct — the test asserts both
verdicts and guards that neither half goes vacuous.

Per-frame decode latency was deliberately NOT built. Polling answers
only 'complete by now', and the pump polls once per AU, so every sample
would quantise up by as much as a frame interval — 8.3 ms at 120 Hz
against decodes of 0.1-2 ms. Sampling faster needs a spin or a second
thread on a decoder that is deliberately not Sync. A blocking per-frame
wait is the field scar that once capped a stream at 51 fps. The honest
sampled stat stands.

Also fixed, pre-existing: the re-anchor gate re-armed on every damaged
AU, so sustained damage permanently zeroed the mark count — meaning the
wire's two-mark rule could never complete on exactly the lossy links it
was written for.

Field note recorded while wiring this: intra_refresh_recovery is set by
exactly one encoder backend (Linux libav-NVENC under
PUNKTFUNK_INTRA_REFRESH). AMF and QSV run a wave with no wire mark, and
AMF emits no recovery-point SEI either, so AMD/Windows intra-refresh
sessions still have no clean recovery point by either route.

Gates: fmt clean; container clippy -D warnings zero across
pf-client-core + pf-presenter + pf-vkdecode + punktfunk-core; tests
69/131/129/354/41 plus 5 fault-detection green; cargo check --workspace
clean.
2026-08-06 04:30:40 +02:00
enricobuehler e4d8573475 feat(client): the native rung now decodes HEVC as well as H.264
The last piece of M3 WP-2 — VkH265Decoder was built and hardware-gated
but nothing drove it. video_vk_native.rs holds a two-arm codec enum and
forwards to it; the ledger, release tokens, status-query settling and
timeline waits are byte-for-byte what they were, since they were always
codec-agnostic over one DecodedVkFrame contract. The forwarders are
written out per arm rather than macro'd so the unchanged H.264 arm is
visible to a reviewer.

The picture's own format now reaches the presenter, which picks bit
depth and MSB packing from it instead of assuming the H.264 envelope.
That incidentally fixes a live bug on the SHIPPING FFmpeg-Vulkan path:
it derived ten-bit-ness by comparing against the 10-bit 4:2:0 format
alone, so a 10-bit two-plane 4:4:4 surface — which its own format table
accepts, and which NVIDIA reports for HEVC RExt — got 8-bit range and
transfer maths. Reachable today with Full chroma plus 10-bit: decoded
correctly, displayed wrong.

Review round 11 caught a regression this WP would otherwise have
shipped. pf-vkdecode refuses a stream whose (chroma, depth) pair has no
picture format on the device, but the session is built lazily from the
first SPS, so the refusal arrived AFTER construction — past the point
where a native init failure falls through to FFmpeg-Vulkan. It burned
the error streak instead and demoted to VAAPI/D3D11VA, which on
NVIDIA/Linux means software. Turning on Full chroma on any non-NVIDIA
GPU was enough: a 4K HEVC session that ran on FFmpeg-Vulkan before this
branch would have landed on software decode.

Both halves are fixed. The negotiated chroma and bit depth — already at
the call site, the PyroWave arm four lines up uses them — are threaded
into the backend, which probes the same caps path ensure_state would
run, so the whole class refuses at CONSTRUCTION where the fall-through
already exists. For the legs no negotiation can carry (a level above
maxLevelIdc, an SPS that disagrees with the Welcome) the decoder latches
'never delivered a frame' and routes that first streak to FFmpeg-Vulkan
rather than down the hardware ladder. H.264 is deliberately not probed:
its envelope is fixed, so a probe would only add a profile guess on the
bit-exact path; it gets the latch as its backstop.

Two more from the round. Planner warnings are typed again rather than
Debug strings — pf-vkdecode simply lacked the h265 re-export its h264
twin already had — which restores the H.264 log rendering exactly and
unblocks M4, whose job is counting concealment by kind. And concealment
is now the integrity set only: NonZeroReorder is documented spec-legal
and fully planned, but the client treated every warning as damage, so
the opening IDR and every ABR renegotiation's IDR were released unshown
and re-anchored — a visible hitch on a healthy stream.

Also: a raw-format newtype so a neighbouring i32 field cannot be passed
to the colour maths, the presenter's depth table now pinned against
pf-vkdecode's actual output vocabulary rather than the FFmpeg lane's,
a per-format warn latch, and four stale docs.

Gates: fmt clean; container clippy -D warnings zero across
pf-client-core + pf-presenter + pf-vkdecode; tests 69/125/108/40 green;
cargo check --workspace clean.
2026-08-06 03:02:09 +02:00
enricobuehler 6d8f3b45b5 feat(pf-vkdecode): the GPU half of HEVC decode — session, pools, recording
M3 WP-2 complete. caps_h265.rs builds the profile the stream actually
needs (profile idc + chroma + bit depths, all three stated on every
Vulkan object) and resolves its picture format — Main to NV12, Main 10
to P010, RExt 4:4:4 to the two-plane 4:4:4 formats — validating it
against the format list of every role the chosen arrangement creates
images in. A Main 10 stream on an 8-bit-only device is refused BEFORE a
session exists, never narrowed: decoding 10-bit into an 8-bit surface is
the silent-wrongness class this crate exists to refuse. session_h265.rs
adds the three-array parameters ledger; decoder_h265.rs adds
VkH265Decoder, mirroring VkH264Decoder method-for-method so the client
wiring is a two-arm dispatch away.

H.264 and H.265 now SHARE the machinery instead of duplicating it:
derive_arrangement (one coincide/distinct/layered decision table),
ring::rebased_offsets (the slices-only rebase — non-VCL NALUs in the
decode range hang VCN firmware), session::bind_session_memory, and a
parameterised build_frame. A DecodeProfile enum replaces the bare
profile idc that images.rs and ring.rs used to take: both codecs' idc
types are c_uint, so handing an H.265 idc to the H.264 path COMPILED
SILENTLY and built a mismatched profile chain. That is now
unrepresentable.

The VPS leg is the ledger's real work. The vendored parser attaches a
VPS to an SPS only when it saw the NALU, and clients join live streams,
so VpsSource is Parsed-or-FromSps and is stored BY VALUE: re-activating
a VPS-less SPS is Current (no churn), but the real VPS arriving under
the same id is a content change and RECREATES onto it, because Vulkan
cannot replace a stored parameter set.

Review round 10 (adversarial) confirmed the hardware-proven H.264 path
is NOT regressed — derive_arrangement's check order and error identity
are byte-for-byte the original, build_frame's call sites still pass the
granularity-aligned extent (the 1088-row scar stays shut), and
rebased_offsets reproduces the deleted inline loop for every input while
moving the sum to u64 so overflow errors instead of wrapping. Also
verified: the refs-order contract on every path, the RESULT_STATUS caps
gate (each of reset/begin/end individually gated, no pool created when
unsupported — recording one on RADV hangs its VCN), pNext lifetimes, and
that no panic is reachable on stream input.

Its 10 findings are fixed. The two that mattered:

- A failed decode stranded a DPB slot. Once plan_to_vk_h265 had mutated
  the slot map, five later failure paths returned without restoring it,
  so planner and slot map both believed a picture was resident while no
  image held it — and every later AU referencing it failed, where H.264
  soft-degrades and keeps delivering. Fail-closed is kept (substituting
  a reference silently is the corruption-hiding this program exists to
  end) but made RECOVERABLE: a latch flushes the planner to AwaitingIdr
  and resets the bindings on the next decode, which composes with the
  client already requesting a keyframe on every decode error. The fix
  deliberately covers pre-mutation failures too — those strand the
  picture the other way round and wedge identically.
- DecodedVkFrame carried no picture format, so a Main 10 frame would
  decode correctly and be rendered with 8-bit transfer/range math. It
  now carries one, stamped from the pool so it is truthful for both
  decoders by construction. The presenter comment says depth 8 is
  because only H.264 is WIRED, not a decoder limit.

Plus: bind_session_memory freed allocations before the session that may
hold them was destroyed (an ordering regression from the extraction,
with a SAFETY comment asserting the opposite) — the bind-stage exit now
hands them back so Drop destroys first; max_level_idc is codec-tagged
rather than an H.264 type carrying H.265 code points; and the decode
family's videoCodecOperations is now checked, turning 'create an H.265
session on a device without the extension' from UB into a clean ladder
demote.

Deferred by design: no HEVC gpu_smoke/gpu_parity yet (its goldens are
already in tests/data/test-25fps-h265.nv12.sha256), and no codec
dispatch in the client — both later legs.

Gates: fmt clean; mac clippy zero warnings, pf-vkdecode 106 +
pf-bitstream 69 green; container clippy -D warnings zero for
pf-client-core + pf-presenter + pf-vkdecode, tests 69/121/106 green.

HARDWARE (.173, after the refactor — review saying the proven path is
safe is not the GPU saying it): gpu_parity '250 frames bit-identical to
libavcodec software decode' on BOTH the NVIDIA 4090 (610.88, coincide
mode) and the AMD iGPU (Adrenalin 25.10.30.02, distinct mode), gpu_smoke
green on both. Two independent drivers, both DPB modes, still bit-exact.
The smoke trace also shows the new videoCodecOperations capture reading
DECODE_H264 | DECODE_H265 | DECODE_AV1 off the real decode family.
2026-08-06 01:59:39 +02:00
enricobuehler c985438db1 test(pf-bitstream): replay real host captures through the planners + HEVC goldens
M0's capture hook has been in since 119ec0dd with nothing consuming its
output. corpus_replay.rs is that consumer: point PF_CORPUS at an
au-<stamp>.<codec> capture and every AU walks back through the H.264 or
H.265 planner, asserting no errors and no warnings — a clean capture of
a healthy session must plan whole. Ignored by default (captures are
hundreds of MB and live outside the repo).

It earns its keep immediately. Captured on .173 against the live host
(NVENC, 2800x1260, ~30 s each, client-side codec pin only — no host
config touched):

  h265  1133/1133 AUs planned, 0 errors, 0 warnings
  h264  1514/1514 AUs planned, 0 errors, 0 warnings

The HEVC number is the point: it is the FIRST validation of the WP-1
h265 planner against real host output rather than the vendored
conformance vectors, and it lands before the client's HEVC rung exists
to produce on-glass evidence.

Two real-capture facts the harness had to learn, both from this run:
ending a capture means killing the client, so the final .idx line is
routinely half-written and the final AU's bytes may not all have landed.
Both are tolerated at the TAIL only — a malformed line anywhere else, or
a gap the data cannot cover mid-file, still fails loudly rather than
silently replaying a subset.

Also adds tests/data/test-25fps-h265.nv12.sha256: 250 per-frame NV12
hashes of the vendored HEVC vector from libavcodec's software decoder,
cross-checked frame-for-frame between two independent FFmpeg builds
(8.0.1 in pf-lxcheck2, 8.1.1 from Homebrew) — the sibling of the H.264
goldens, ready for WP-2's parity leg.

Gates: fmt clean; pf-bitstream clippy clean, 69 tests green (the replay
stays ignored in normal runs).
2026-08-06 01:08:50 +02:00
enricobuehler 55bc664eca docs(pf-bitstream): the two upstream cros-codecs bugs are now reported
PROVENANCE deviations #6 and #7 carried 'report upstream' — done:
- chromeos/cros-codecs#99: h264 PictureData display_resolution
  double-counts the left/top crop and underflow-panics on
  parser-valid crop offsets (answers their open #81).
- chromeos/cros-codecs#100: h265 parse_slice_header index-OOB panic
  when num_long_term_sps + num_long_term_pics > 16 — a hostile-input
  panic on the LTR path (an instance of their #78).

Both reports offer the downstream patch for the AOSP tree.
2026-08-06 00:44:18 +02:00
enricobuehler a34f4051fc feat(pf-vkdecode): the CPU half of HEVC decode — StdVideo H265 conversion + slot map
M3 WP-2, first half. params_h265.rs: VPS/SPS/PPS -> StdVideoH265* with
owned pointer-backing (the params.rs contract), scaling lists incl. the
32x32 two-matrix quirk and +8 DC convention, short-term RPS re-encoded
from the parser's RESOLVED DeltaPoc arrays back into delta_poc_sX_minus1
syntax under monotonicity checks, fallback_vps_from_sps for streams
whose VPS NALU was lost. pic_h265.rs: plan_to_vk_h265 — h265 AuPlan ->
StdVideoDecodeH265PictureInfo + per-reference infos; the binding set is
the union of the three current RPS sets with the Std index arrays
indexing into refs (0xFF unused; the GPU half must lay pReferenceSlots
out in refs order); NumDeltaPocsOfRefRpsIdx from the predicted-from
candidate; transactional SlotMap lifecycle identical to pic.rs. SlotMap
reused unmodified — HEVC's ceiling equals H.264's 16+1.

Envelope fails closed: Main/Main10/MainStill/RExt only, 4:2:0-8/10 +
4:4:4 only (separate_colour_plane_flag rejected — ChromaArrayType 0 in
disguise), SCC palette predictors out, >64 ST RPS sets / >16 per side /
>32 LT SPS candidates out, checked narrowing on every narrower Std
field. No panics on untrusted input.

Review round 9 (adversarial): RPS re-encode math, Std field-by-field
conformance, transactionality and slot ceiling verified clean; 6
findings fixed pre-commit. Headline (BLOCKING): long_term_ref_pics_
present_flag=1 with num=0 left pLongTermRefPicsSps NULL — the header
demands a valid pointer whenever the flag is set, and flag=1/num=0 is
exactly the punktfunk LTR/RFI recovery stream shape; the all-zero
backing now rides whenever the flag is set. Also: the slice_offsets doc
in BOTH pic modules claimed submit-as-planned while decoder.rs packs
slices-only and rebases (non-VCL NALUs in the decode range hang VCN
firmware) — reworded so the HEVC GPU half cannot implement the hang; a
concealment-produced ST/LT duplicate now ORs the long-term flag across
occurrences; NumDeltaPocs clamps became a typed error; dead
UnmappableLevelIdc variant dropped.

Deferred to the GPU half: HEVC caps/profile chain, session parameters
(VPS leg in the ledger), P010/4:4:4 pool selection, recording, and the
pReferenceSlots-in-refs-order contract consumption.

Gates: fmt clean; mac pf-vkdecode 80 + pf-bitstream 69 green, clippy
clean; container clippy -D warnings zero (pf-client-core, pf-presenter,
pf-vkdecode) + tests green (69/121/80).
2026-08-06 00:41:23 +02:00
enricobuehler 370b0ab494 feat(client): native Vulkan decode joins the automatic ladder, above FFmpeg-Vulkan
Closes M2. The 2026-08-05 ladder decision: WP-D shut with bit-exact
parity vs libavcodec (250/250 AUs on RADV, AMD-proprietary and NVIDIA)
and a 92-minute clean soak, and the program's goal is dropping FFmpeg
from the client — so on H.264 sessions where caps pass, auto now tries
pf-vkdecode FIRST, exactly where the ladder would reach FFmpeg-Vulkan.
No bake period.

native_vulkan_gate widens from by-name-only to the auto family
(auto/""/hardware); the vendor-first rungs are untouched (Linux
Intel/unknown still VAAPI-first, Windows Intel/unknown still
D3D11VA-first — NVIDIA and ALL AMD go native first). A native INIT
failure or caps refusal logs and falls through to FFmpeg-Vulkan, so
admission can't cost a session its decoder at start; runtime error
streaks ride the existing demotion machinery unchanged (past
FFmpeg-Vulkan to VAAPI/D3D11VA/software — a native→FFmpeg-Vulkan
runtime rung is deliberately absent, FFmpeg is on its way out).
PUNKTFUNK_DECODER=native-vulkan stays as the explicit pin; vulkan
keeps naming the FFmpeg backend specifically. A native_tried guard
keeps a failed pin init from re-attempting construction in auto.

Review round 8 (adversarial): no blocking code defect — no demote
bounce-back (Decoder::new is session-start-only; demotion mutates in
place), no double attempt, no cfg imbalance. 5 findings fixed: two doc
overclaims ("nothing regresses" now scoped to init; the ladder
enumerations no longer claim desktop-AMD Linux is VAAPI-first —
prefer_vulkan_first is vendor-wide), stale opt-in claims in Cargo.toml,
stale user-facing ladder text (console-ui row, trust.rs decoder field,
session README incl. the env-knob list), and the gate test now pins the
H264 codec-op bit to the literal 0x1 so a typo'd constant can't make
native silently never engage.

Gates: fmt clean; container clippy -D warnings zero for pf-client-core +
pf-presenter + pf-vkdecode; container tests green (pf-client-core lib +
pf-vkdecode + pf-bitstream); pf-console-ui check clean; mac
pf-vkdecode/pf-bitstream/cros-codecs 167 tests green.

On-glass sanity CLOSED 2026-08-05 ~22:10 UTC on .173 (4090, coincide
mode), decoder=auto and NO env var: the ladder picked native on its
own ("pf-vkdecode auto rung" log line), 525/526 stats windows on
native-vulkan over ~8m46s / 31550 frames, fps 0/59.4/61 with 6
windows <55 incl. startup zeros, bad-signature grep over the whole
log EMPTY, zero TDR events, host service Running after teardown.
2026-08-06 00:12:35 +02:00
enricobuehler cf5db2d485 feat(pf-bitstream): H.265 DecodePlan layer — M3's AU-to-hardware contract
H265Planner mirrors the H.264 layer's contract exactly: plan_au -> AuPlan
{ picture, slices with ref lists by stable PicId, DpbUpdate, warnings },
same concealment posture (warnings never abort, in-place reference
substitution preserving ref_idx positions, outputs survive failed AUs,
flush gates on AwaitingIdr, any IRAP resumes). Ported logic: RPS 8.3.2
(short-term AND long-term incl. PocLsbLt/MSB-cycle - the hosts' RFI
recovery rides long-term refs), ref lists 8.3.3/8.3.4, DPB C.5.2.2/C.5.2.3
via the vendored dpb; POC 8.3.1 from the vendored PictureData. Written
fresh: the plan surface, AU walk, envelope gates (multilayer, interlaced,
SCC self-reference, DPB>16, conf-window overflow - checked at EVERY
activation, not just parse), HEVC recovery-point SEI (prefix NALU 39,
se(v) recovery_poc_cnt), VUI colour with E.3.1 inference, and a test-only
HEVC bitstream synthesizer (upstream has none).

Upstream deviations worth naming (all in-code with spec anchors): the
empty-RPS inter slice cannot infinite-loop (upstream bug); RASL behind a
joined CRA refuses BEFORE any state change (PlanError::RaslSkipped - the
WP-2 wiring must map it to skip, not reanchor; module docs carry the
contract note); MaxPicOrderCntLsb reads from the ACTIVATING SPS (upstream
latches at parse - a latent multi-SPS bug); C.5.2.2's exemption is
picture 0 of the BITSTREAM (EobNut), never first-after-EOS.

Vendored parser gained PROVENANCE deviation 7 (report upstream): hostile
slice headers with num_long_term_sps+num_long_term_pics > 16 indexed out
of bounds of SliceHeader's [_;16] arrays - a production panic on exactly
the long-term-reference path, now a parse error.

Port review round 7: 10 findings (3 blocking: the vendor panic, an
EOS-boundary output interleave, an envelope bypass through PPS-only SPS
rebind reaching wrapping crop arithmetic) - 9 fixed with a regression
test each, 1 documented as the WP-2 contract note. Known follow-up: the
h264 AU-tail truncation detector shares h265's dead-arm shape (its arm
also cuts reserved NALU types, so the fix is not identical - deferred).

Tests: 29 h265 planner + 2 HEVC SEI + full test-25fps.h265/bear/bbb clip
walks with real invariants (every stored id output exactly once,
ascending POC per IRAP period). Gates: fmt clean; clippy -D warnings zero
(mac + pf-lxcheck2 incl. pf-client-core/pf-presenter); tests 45+69 mac,
69+121+53 container.
2026-08-05 23:29:45 +02:00
enricobuehler dc0766b2f3 fix(client): the native rung now follows the stream's colour and reports true decode latency
The round-4 residuals, closed after the WP-D hardware verdict:

- VUI colour plumbing (the one silent-wrong): the picture's ACTIVE SPS's
  colour signalling (H.273 code points + range, with E.2.1's 'unspecified'
  inference where the VUI is silent — the vendored parser's defaults ARE the
  inferred values, verified) rides PicturePlan -> DecodedVkFrame ->
  NativeVkFrame per frame, never latched: the Windows host switches an HDR
  desktop to PQ/BT.2020 IN-BAND while the Welcome still says SDR. Before
  this, the native path would have painted PQ washed out, silently.
- Native decode-latency stat: the deliberately-deferred NativeVk arm of the
  pump's sampled once-per-stats-window decode measurement now feeds - the
  frame's (semaphore, semaphore_value) is the decode-done signal, resolved
  through the shipped ledger before a bounded, pure-measurement
  vkWaitSemaphores (VkH264Decoder::wait_decoded).
- The renegotiation-teardown window is settled as NO HOLE: rebuild_state now
  documents the full safety argument (graveyarded pools stay intact under
  presenter holds, tokens route strictly by generation, session objects die
  only post-drain with the generation gate INSIDE read_status), and the two
  backend comments that wrongly claimed stale pools were 'gone' are fixed.
- VK_KHR_unified_image_layouts stays deferred (fleet drivers lack it).

Adversarial review round 6: 3 minor findings (2 doc fixes applied; the
SPS-replaced-without-PPS-resend divergence stays a documented envelope
assumption - hosts re-send both at every keyframe, and a hardening
PlanWarning could cost real frames on a false positive).

Gates: fmt clean; clippy -D warnings zero (mac + pf-lxcheck2 container,
incl. pf-client-core/pf-presenter); tests 45+30+53 mac, 30+121+53 container.
2026-08-05 22:22:03 +02:00
enricobuehler e6d6498a49 test(pf-vkdecode): frame-hash parity vs libavcodec — bit-exact on the whole fleet
WP-D parity A/B. gpu_parity (ignored) decodes the conformance vector,
reads every frame back through the presenter's exact contract (wait,
layout round-trip, signal-back, release), crops at the copy so pitch
can never leak, and compares SHA-256s in display order against goldens
from ffmpeg software decode — cross-checked bit-identical between
ffmpeg 8.0.1 (linux) and 8.1.1 (macOS), so the reference is the spec,
not one build. PF_VKD_TEST_READBACK=1 is the one test-only hook (ORs
TRANSFER_SRC into pool usage; production pools stay zero-copy-tight).

Fleet verdict: 250/250 frames bit-identical to libavcodec on RADV
(Mesa 26.0.3, distinct), AMD proprietary Windows (25.10.30.02,
distinct) and NVIDIA Windows (610.88, coincide) — H.264 decode is
exactly specified, and the native path meets the spec on every driver
and both DPB arrangements.
2026-08-05 20:23:18 +02:00
enricobuehler ca92dab6fd fix(pf-vkdecode): prefer DEVICE_LOCAL, never require it — NVIDIA runs; both DPB modes hardware-green
Session-memory and image allocation now honor each binding's
memoryTypeBits with DEVICE_LOCAL preferred, not required: NVIDIA 610.88
legally places a video-session binding in host-visible-only memory and
the hard requirement refused the whole device. The bitstream ring keeps
its hard HOST_VISIBLE|COHERENT need. Smoke test gains
PF_VKD_SMOKE_VENDOR device pinning + attribution and a final-state
print (DPB mode now observed, not inferred).

On-glass matrix after this fix (.173, vendor-pinned): NVIDIA 4090
PASSES in COINCIDE mode — the first end-to-end run of the RESULT_STATUS
query path, ~44 per-frame driver verdicts on the recording pattern that
hangs RADV's VCN — and Adrenalin re-passes in distinct mode unchanged.
With RADV's distinct pass, both DPB arrangements and three of four
desktop drivers are now hardware-validated; Intel remains a clean caps
refusal (no SAMPLED on decode outputs — its rung stays D3D11VA).

Gates: fmt clean, clippy -D warnings zero, 45+27+53 green both
platforms.
2026-08-05 20:03:25 +02:00
enricobuehler 6331ae7fd9 fix(pf-vkdecode): zero-copy pool model + the two faults the first hardware run found
WP-D leg 1 (.25 RADV, distinct mode) root causes, both real:
1. Output starvation: the fixed 4-deep ring lost to a stream that keeps
   max_dpb_frames+1 = 8 pictures pending. Zero-copy fix (user
   requirement, no copies): one picture pool of required_slots +
   HOLD_HEADROOM(8) images decoupled from DPB slots — a re-activated
   slot binds a fresh free image, so a delivered picture is never a
   decode target; the WP-B pin layer became dead and is deleted.
   Per-image timeline semaphores carry the AVVkFrame contract: decode
   signals value+1, the presenter waits and signals back, later decodes
   wait the image's latest value — layout traffic ordered against
   reference reads with no copy anywhere.
2. RESULT_STATUS queries HANG RADV's VCN firmware (ring timeout,
   DEVICE_LOST): queryResultStatusSupport=false on the decode family.
   Queries are now caps-gated; without them poll/wait degrade to
   timeline-completion verdicts (FFmpeg parity — and the likely reason
   upstream never wired nb_queries). The Ally-X-class detection runs
   where drivers advertise the query; .173 probes NVIDIA/Windows-AMD.

Also: slice-only bitstream feeding (the field-proven consumer shape),
graveyarded pool retirement keyed by release tokens + generation,
decode-current-AU-before-status attribution, take_ready drained,
H264-bit gating, teardown short-circuit on disconnected channel.

On-glass: 48 AUs green on .25 holding 4 frames like the real client.
Gates: fmt clean, container clippy -D warnings zero, 27+121+52 green
both platforms.
2026-08-05 19:12:20 +02:00
enricobuehler d0659d2b61 feat(client): wire the native Vulkan decoder in behind PUNKTFUNK_DECODER=native-vulkan
M2 WP-C. video_vk_native.rs adapts the presenter's VulkanDecodeDevice to
pf-vkdecode (queue lock shared only when the families actually collide —
the one case the 2026-07-09 DEVICE_LOST race proved matters), and the
presenter consumes DecodedImage::NativeVk on its own device: no handle
import, no AVVkFrame co-authoring — wait the timeline, barrier to
sampled, existing crop-aware CSC, barrier back, release after the fence.

Frame lifetime is a token: presented, retired, displaced or dropped
mid-demotion, the guard's drop sends it exactly once; the backend
releases the decoder slot only after the status query resolves, so a
recycled slot can never report a false Failed. Driver-reported decode
failures and plan warnings ride the existing streak/reanchor machinery —
the Ally X corruption class is now a visible error, not a silent frame.

Opt-in only until WP-D's on-glass parity verdict; H.264 sessions only;
failures demote to the existing ladder. Known WP-D items recorded in
code: coincide-mode cross-queue reference overlap, renegotiation
teardown window, VUI colour plumbing.

Gates: fmt clean; container clippy -D warnings zero for pf-client-core +
pf-presenter + pf-vkdecode; 121+53+27 tests green.
2026-08-05 17:57:12 +02:00
enricobuehler 540c0d3027 feat(pf-vkdecode): the GPU half — session, DPB pools, decode recording, status queries
M2 WP-B. VkVideoSessionKHR lifecycle with drain-before-destroy on
parameters recreation, DPB pools in both coincide and distinct modes
(caps-derived, usage/flags validated against the driver's format
properties), an aligned bitstream ring, vkCmdDecodeVideoKHR recording
with one-shot RESET re-armed on failed submits, timeline-semaphore
completion, and the per-op RESULT_STATUS query ring — the signal
FFmpeg's hwaccel never reads and the reason this program exists.

Frame lifetime is two-phase by construction: release_frame pins a
delivered frame's slot against reuse, closing the coincide-mode
overwrite the adversarial review round proved (a full DPB handed a
just-returned frame's image back as the same call's decode target).
Nine review findings fixed pre-commit; a counterfactual test pins the
collision. Generation-stamped frames, memory-type misses as errors,
granularity-aligned extents, level gate.

AuPlan now carries its activated SPS/PPS (Rc) so backends never
re-parse. GPU smoke test (ignored) decodes 48 AUs past DPB-full with
releases — the fleet runs it in WP-D.

Gates: fmt clean, clippy -D warnings zero, 45+27+53 tests green on
macOS and the linux/amd64 container.
2026-08-05 16:44:44 +02:00
enricobuehler d24f7fc6ac feat(pf-vkdecode): the CPU half of native Vulkan decode — StdVideo conversion + slot map
M2 WP-A (design/client-native-decode.md §3.2). AuPlan -> StdVideo
parameter sets (owned pointer backings), per-AU decode info with slice
start-code offsets, and a PicId->slot map that never evicts on its own.
Deliberate rejections over silent claims: FMO, separate colour planes,
DPBs deeper than 16 frames (unbounded VUI ue(v)) all fail closed.

pf-bitstream API grew what the review proved necessary: RefPic carries
the true top/bottom field order counts (a single poc fabricated
BottomFieldOrderCnt whenever the PPS signals pic-order deltas), the
>16-frame DPB envelope gate, and an MMCO5 rebase warning.

Adversarial review round two: 8 findings fixed pre-commit, including
transactional slot mutation (an error path could permanently desync
the map) and count/pointer coherence on type-1 POC offsets.

Gates: fmt clean, clippy -D warnings zero, 45+26+21 tests green on
macOS and the linux/amd64 container.
2026-08-05 15:03:39 +02:00
enricobuehler 0a359525a7 feat(pf-bitstream): H.264 DecodePlan layer — the AU-to-hardware contract
Adapted from cros-codecs decoder/stateless/h264.rs (POC 8.2.1, ref lists
8.2.4 incl. modification, sliding-window + MMCO/LTR marking 8.2.5,
frame_num-gap handling 8.2.5.2), minus the fd-coupled backend trait.
H264Planner::plan_au maps one wire AU to picture params, per-slice byte
ranges + ref lists keyed by stable PicIds, and a DPB update; recovery-
point SEI parsing is new code (upstream reads no SEI payloads).

Concealment posture, deliberately different from upstream's aborts:
frame_num gaps, failed RPLM/MMCO and mis-split AUs degrade to warnings
the session turns into recovery asks, gap placeholders substitute
in-place so ref_idx mapping never shifts, and DPB outputs queued during
a failed AU survive to the next plan.

An adversarial review round fixed 11 findings before this commit;
one was an upstream cros-codecs bug our conformance-window test exposed
(display_resolution double-subtracts the crop offset and underflow-
panics — PROVENANCE deviation #6, worth reporting upstream).

Gates: fmt clean, clippy -D warnings zero, 45+24 tests green on macOS
and the linux/amd64 container.
2026-08-05 13:13:00 +02:00
enricobuehler 896bb47235 refactor(pf-bitstream): the parser layer is now compiler-enforced unsafe-free
#![forbid(unsafe_code)] on both crates. Upstream's codec module was one
production unsafe away: build_ref_pic_lists turned DPB borrows into
indices via pointer offset_from — same pointer-identity mapping now
expressed as position(ptr::eq) over the <=16-entry DPB (PROVENANCE #5).
Three test-only mem::zeroed() asserts became Default::default(), an
identical value for the all-integer PredWeightTable.

Honest coverage note: build_ref_pic_lists has no callers inside the
vendored subset (its consumer was the non-vendored stateless layer), so
the rewrite is equivalence-by-construction until the DecodePlan layer
exercises it against goldens.
2026-08-05 11:25:41 +02:00
enricobuehler b5e54aea6e feat(client): vendor the cros-codecs parser layer + pf-bitstream skeleton
M1 of design/client-native-decode.md. The vendored snapshot (AOSP mirror
main @ 5ff6d693ffae, BSD-3, PROVENANCE.md) is the codec module only —
H.264/H.265/AV1/VP9 parsers, DPBs and their test vectors, which now run
as 45 conformance tests in our CI. pf-bitstream sits where upstream's
Linux-only decoder::stateless half would and starts with vendor-pinning
smoke tests: a re-sync that shifts parser behavior trips in-tree, not in
a decode session.
2026-08-05 11:19:51 +02:00
enricobuehler 119ec0dd83 feat(client): PUNKTFUNK_DUMP_VIDEO captures the exact decoder input
Fixture-corpus enabler for the native-decode program (M0,
design/client-native-decode.md): every AU exactly as the pump hands it
to decode_frame — the raw concatenation plus a sidecar .idx carrying
the AU boundaries and wire flags a byte stream cannot. Best-effort by
design: any I/O error disables the capture, never the stream.
2026-08-05 11:02:37 +02:00
enricobuehler 110ac9b663 Merge pull request 'fix(stall): T2 amplification kill — resume-edge pacing + ABR starved-window guard' (#53) from worktree-stall-ride-through into main
apple / swift (push) Successful in 1m26s
ci / docs-site (push) Successful in 1m15s
ci / web (push) Successful in 1m36s
ci / rust-arm64 (push) Successful in 3m4s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 22s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 9s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 6s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 5s
deb / build-publish (push) Successful in 3m43s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 6s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 26s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 30s
deb / build-publish-client-arm64 (push) Successful in 4m11s
deb / build-publish-host (push) Successful in 4m27s
docker / builders-arm64cross (push) Successful in 5s
docker / deploy-docs (push) Successful in 33s
arch / build-publish (push) Successful in 7m29s
android / android (push) Successful in 8m0s
ci / rust (push) Successful in 9m20s
flatpak / build-publish (push) Successful in 5m36s
release / apple (push) Successful in 11m4s
windows-host / package (push) Successful in 12m31s
windows-host / winget-source (push) Skipped
windows-host / canary-manifest (push) Successful in 16s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 2m37s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Successful in 2m59s
apple / screenshots (push) Successful in 5m52s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 1m19s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 2m41s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 18m35s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 18m1s
2026-08-05 06:35:30 +00:00
enricobuehler 1d6f4760f3 Merge branch 'main' into worktree-stall-ride-through
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 1m8s
apple / swift (pull_request) Successful in 1m20s
apple / screenshots (pull_request) Skipped
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 2m7s
android / android (pull_request) Successful in 3m10s
ci / web (pull_request) Successful in 1m9s
ci / rust-arm64 (pull_request) Successful in 1m38s
ci / docs-site (pull_request) Successful in 1m25s
ci / rust (pull_request) Successful in 6m32s
2026-08-05 06:22:41 +00:00
enricobuehler 9dfbc2f895 Merge pull request 'fix(client-core): pad-audio references the WASAPI module by its mounted name' (#57) from fix/pad-audio-wasapi-module-path into main
ci / web (push) Successful in 1m7s
ci / rust-arm64 (push) Successful in 1m22s
apple / swift (push) Successful in 1m27s
ci / docs-site (push) Successful in 1m24s
deb / build-publish-client-arm64 (push) Successful in 2m46s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 6s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 7s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 5s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 6s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
deb / build-publish-host (push) Successful in 4m8s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 49s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m12s
deb / build-publish (push) Successful in 6m26s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 3m43s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Failing after 16s
docker / builders-arm64cross (push) Successful in 19s
apple / screenshots (push) Successful in 5m53s
android / android (push) Successful in 8m51s
arch / build-publish (push) Successful in 9m37s
ci / rust (push) Successful in 10m25s
docker / deploy-docs (push) Failing after 3m52s
flatpak / build-publish (push) Canceled after 5m43s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 5m20s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Canceled after 5m45s
windows / build (aarch64-pc-windows-msvc) (push) Canceled after 0s
windows / build (x86_64-pc-windows-msvc) (push) Canceled after 0s
2026-08-05 06:22:31 +00:00
enricobuehler 56adb47026 fix(client-core): pad-audio references the WASAPI module by its mounted name
ci / web (pull_request) Successful in 56s
apple / swift (pull_request) Successful in 1m25s
apple / screenshots (pull_request) Skipped
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 1m40s
ci / docs-site (pull_request) Successful in 2m33s
ci / rust-arm64 (pull_request) Successful in 2m43s
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 2m17s
android / android (pull_request) Successful in 4m12s
ci / rust (pull_request) Successful in 6m21s
The Windows build of pf-client-core has been red on main since the
pad-audio merge (#23): pad_audio.rs calls
`crate::audio_wasapi::device_by_id`, but lib.rs mounts audio_wasapi.rs AS
`crate::audio` via the #[path] per-OS swap — the `audio_wasapi` module
name never exists. Windows-gated call site, so every Linux leg stayed
green while both `windows / build` targets failed E0433.

One-line rename to the mounted path (+ the comment that pointed readers
at the phantom name). Verification is the PR's own windows leg — the
crate builds on no other platform this path compiles on.
2026-08-05 08:15:02 +02:00
enricobuehler 52a9d02355 Merge pull request 'fix(deps): close the undici, fast-uri, postcss and brace-expansion advisories' (#55) from worktree-audit-undici into main
audit / cargo-audit (push) Successful in 43s
audit / bun-audit (plugin-kit) (push) Successful in 16s
audit / bun-audit (sdk) (push) Successful in 17s
audit / bun-audit (web) (push) Successful in 21s
audit / docs-site-audit (push) Successful in 18s
audit / pnpm-audit (push) Successful in 9s
ci / web (push) Successful in 1m14s
ci / docs-site (push) Successful in 1m23s
ci / rust-arm64 (push) Successful in 2m20s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 13s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 7s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 5s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 5s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 4s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 6s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 46s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 9s
deb / build-publish-client-arm64 (push) Successful in 2m45s
deb / build-publish (push) Successful in 5m12s
audit / license-gate (push) Successful in 5m44s
deb / build-publish-host (push) Successful in 4m56s
arch / build-publish (push) Successful in 11m54s
docker / builders-arm64cross (push) Successful in 49s
ci / rust (push) Successful in 10m46s
docker / deploy-docs (push) Failing after 3m45s
windows-host / package (push) Successful in 17m5s
windows-host / winget-source (push) Skipped
windows-host / canary-manifest (push) Successful in 14s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 16m2s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 19m10s
Reviewed-on: #55
2026-08-05 05:51:06 +00:00
enricobuehler e5ca213339 fix(core/abr): a starved window is never a decode-knee sample
apple / swift (pull_request) Successful in 1m25s
apple / screenshots (pull_request) Skipped
windows / build (aarch64-pc-windows-msvc) (pull_request) Failing after 6m2s
windows / build (x86_64-pc-windows-msvc) (pull_request) Failing after 2m57s
ci / rust-arm64 (pull_request) Successful in 1m24s
ci / web (pull_request) Successful in 1m13s
ci / docs-site (pull_request) Successful in 1m47s
android / android (pull_request) Successful in 5m30s
ci / rust (pull_request) Successful in 9m50s
Stall program T2 (amplification kill), the phantom-latch half. A deciding
window that delivered under a quarter of the target rate (a host-side
capture stall, an outage, a mid-window pause) carries starvation-shaped
distress — a jump-to-live flush, a keyframe-ask burst — that the decode-cap
latch read as decoder evidence: under a periodic capture stall (the RDNA4
standby-sink field cases, one stall every ~5 s) every edge offers another
'backoff' at the SAME rate, and one pair latches a phantom decoder knee at
whatever rate the display driver happened to interrupt. The session then
fights the cap's re-probe ladder (+12.5% per 16-128 clean windows) for
minutes on a decoder that was never the problem.

Starved windows still back off (real damage deserves the safe response) but
take the same 'not a knee sample either way' arm as a draining backoff:
they neither latch a decode cap nor erase the reference a genuine choke
set, so a real knee's pair still finds itself around the interruption. The
¼ bar sits deliberately far under the ×¾ utilization bar climbs require.

Gates: 44 abr tests green (2 new: the stall-cycle no-latch scenario and the
reference-preservation scenario), full core lib suite 346 green
(--features quic), fmt + clippy clean.
2026-08-05 00:28:25 +02:00
enricobuehler e5416646f9 fix(host/send): a stall-resume frame paces at the proven rate instead of blasting
Stall program T2 (amplification kill), the resume-burst half. The native
pace budget was min(0.9 × time-to-deadline, overflow at ~3× stream rate) —
for steady-state frames the rate term is smaller and decides, but for an
OVERSIZED frame (a capture-stall resume carrying seconds of scene delta, a
cold IDR) the deadline term clamped a multi-interval overflow into the
remainder of ONE: an instantaneous many-×-stream-rate blast that overruns
the socket tx-buffer and loses the very frame that would have ended the
freeze. Field fingerprint across three RDNA4 standby-sink cases:
WSAENOBUFS(10055) + loss_ppm spikes at stall edges, then a recovery-IDR
round trip per retry while the client shows 'current bitrate 0.1'.

The budget is now the overflow's wire time at the pace rate itself
(send_pacing::native_budget, pure + unit-tested), bounded by an absolute
100 ms ceiling so a pathological frame can't park the send thread; the
deadline stays a target, never a license to blast. Steady-state frames
produce byte-identical schedules (the rate term already decided);
PUNKTFUNK_PACE_FACTOR=0 keeps the legacy deadline-only spread; the
GameStream plane's Moonlight-pinned schedule is untouched.

Gates: host clippy --all-targets -D warnings + 9 send_pacing tests green
(linux/amd64 container), fmt clean.
2026-08-05 00:28:13 +02:00
enricobuehler b79d90b463 fix(deps): close the undici, fast-uri, postcss and brace-expansion advisories
ci / web (pull_request) Successful in 1m8s
ci / rust-arm64 (pull_request) Successful in 1m33s
ci / docs-site (pull_request) Successful in 1m21s
ci / rust (pull_request) Failing after 7m49s
audit.yml's three blocking bun-audit legs (web, sdk, plugin-kit) were all red on
main. Ten findings in sdk and plugin-kit, eight in web; every one of them a
transitive dependency, none reachable by bumping a direct dep.

web already carried the right mechanism — an `overrides` block whose `undici` and
`fast-uri` pins had simply gone stale — so it needed four bumps, not a new idea:
undici 7.28.0 -> ^7.29.0 and fast-uri 3.1.4 -> ^3.1.5 for the reported advisories,
plus postcss ^8.5.10 -> ^8.5.25 and brace-expansion ^5.0.8 -> ^5.0.9 for two more
that were published after the failing run and would have gone red on the next
audit anyway. All four stay inside their current major.

sdk and plugin-kit were harder and the fix deserves an explanation. Their single
finding is undici 8.7.0/8.8.0 pulled in by @effect/platform-node, a devDependency
pinned at 4.0.0-beta.98. That dependency already declares `undici: ^8.7.0`, which
permits the fixed 8.10.0 — the vulnerable version survives purely as a stale
lockfile resolution. Nothing bumps it in place: `bun update` only walks direct
dependencies, `bun install --force` preserves a resolution that still satisfies
its range, and every platform-node release through beta.103 declares the same
`^8.7.0`, so moving the dep changes nothing. Bun rejects the scoped form outright
("Bun currently does not support nested resolutions"), so a flat `overrides` entry
is the only mechanism available, and it necessarily also moves sdk's top-level
undici from 7.x to 8.x.

That is safe here, and was verified rather than assumed. The only source use is
sdk/src/config.ts, which does `new Agent({ connect: { ca } })` behind a dynamic
import and a try/catch with a documented plain-fetch fallback; `Agent` and its
`connect` option are unchanged between undici 7 and 8. sdk typechecks and its 72
tests pass against 8.10.0; plugin-kit typechecks and its 20 tests pass. Both trees
now dedupe to a single undici 8.10.0.

Consumers are deliberately untouched: `overrides` apply only at the root of the
tree that declares them and are not honored when the package is installed as a
dependency, so sdk's published `optionalDependencies: { undici: "^7.0.0" }` is
left alone — a consumer resolves the latest 7.x, which is the fixed 7.29.0. The
override governs this repo's own tree, which is exactly what audit.yml checks.
Worth knowing: sdk's dev tree therefore exercises undici 8 while consumers get 7.

One trap found on the way. Running `bun install` over plugin-kit's existing
lockfile emitted a lockfile with two byte-identical `@punktfunk/host` entries —
its `file:../sdk` dependency crossed with the new override — and bun then refuses
its own output with "Error loading lockfile: InvalidPackageKey". That reads as a
tooling error rather than a finding, so it would have taken the audit gate down
while looking like something else entirely. Regenerating the lockfile from scratch
produces a valid single entry; all three lockfiles are checked for duplicate keys.

Also worth recording, because it nearly shipped: deleting the pinned nested entry
from a lockfile makes `bun audit` report "No vulnerabilities found" while the
vulnerable copy is still installed on disk. bun audit reads the lockfile, not
node_modules. That is a vacuous green, not a fix, and was rejected.

Verified: `bun audit` clean in all three trees; web builds and typechecks (its
typecheck needs the build first, which generates routeTree.gen); sdk 72/72 and
plugin-kit 20/20 tests pass.
2026-08-05 00:22:25 +02:00
enricobuehler 8983ec04b9 Merge pull request 'feat(pad-audio): DualSense voice-coil haptics + speaker, host to client' (#23) from feat/android-pad-audio into main
audit / bun-audit (plugin-kit) (push) Failing after 30s
audit / cargo-audit (push) Successful in 35s
apple / swift (push) Successful in 1m20s
audit / bun-audit (sdk) (push) Failing after 23s
audit / bun-audit (web) (push) Failing after 19s
audit / pnpm-audit (push) Successful in 12s
audit / docs-site-audit (push) Successful in 22s
ci / web (push) Successful in 1m7s
ci / rust-arm64 (push) Successful in 1m25s
ci / docs-site (push) Successful in 1m9s
android / android (push) Successful in 6m30s
audit / license-gate (push) Successful in 6m28s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 28s
deb / build-publish-client-arm64 (push) Successful in 3m8s
deb / build-publish (push) Successful in 4m48s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 10s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
arch / build-publish (push) Failing after 10m5s
ci / rust (push) Failing after 7m23s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 8s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 6s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 38s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 24s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Successful in 31s
docker / builders-arm64cross (push) Successful in 12s
deb / build-publish-host (push) Successful in 5m34s
release / apple (push) Successful in 9m30s
apple / screenshots (push) Successful in 5m56s
windows-host / package (push) Successful in 18m17s
windows-host / winget-source (push) Skipped
windows-host / canary-manifest (push) Successful in 16s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Failing after 1m53s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Failing after 1m40s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 16m29s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 17m10s
windows / build (aarch64-pc-windows-msvc) (push) Failing after 2m16s
flatpak / build-publish (push) Successful in 18m26s
docker / deploy-docs (push) Successful in 18m42s
windows / build (x86_64-pc-windows-msvc) (push) Failing after 2m10s
2026-08-04 21:56:37 +00:00
170 changed files with 64275 additions and 379 deletions
Generated
+175 -24
View File
@@ -65,7 +65,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05b07e8e73d720a1f2e4b6014766e6039fd2e96a4fa44e2a78d0e1fa2ff49826"
dependencies = [
"android_log-sys",
"env_filter",
"env_filter 0.1.4",
"log",
]
@@ -446,7 +446,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
dependencies = [
"annotate-snippets",
"bitflags",
"bitflags 2.13.0",
"cexpr",
"clang-sys",
"itertools 0.13.0",
@@ -475,6 +475,12 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.13.0"
@@ -556,7 +562,7 @@ version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc8d9aa793480744cd9a0524fef1a2e197d9eaa0f739cde19d16aba530dcb95"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"cairo-sys-rs",
"glib",
"libc",
@@ -885,6 +891,15 @@ dependencies = [
"itertools 0.10.5",
]
[[package]]
name = "cros-codecs"
version = "0.0.5"
dependencies = [
"env_logger",
"log",
"serde_json",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
@@ -991,6 +1006,37 @@ version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
[[package]]
name = "defmt"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1"
dependencies = [
"bitflags 1.3.2",
"defmt-macros",
]
[[package]]
name = "defmt-macros"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8"
dependencies = [
"defmt-parser",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "defmt-parser"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
dependencies = [
"thiserror 2.0.18",
]
[[package]]
name = "der"
version = "0.7.10"
@@ -1101,6 +1147,29 @@ dependencies = [
"regex",
]
[[package]]
name = "env_filter"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900d271a03799a1ee8d1ca9b19893b48ca674a9284fefcfb85f05e74ed314217"
dependencies = [
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.11.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de671bd27a75a797dc9ae289ba1e77276e75e2026408aab65185384e2d5cd3f6"
dependencies = [
"anstream",
"anstyle",
"env_filter 2.0.0",
"jiff",
"log",
]
[[package]]
name = "equivalent"
version = "1.0.2"
@@ -1193,7 +1262,7 @@ version = "8.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c4bd5ab1ac61f29c634df1175d350ded29cf74c3c6d4f7030431a5ae3c7d5d"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"ffmpeg-sys-next",
"libc",
]
@@ -1584,7 +1653,7 @@ version = "0.22.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c207e04e51605dcf7b2924c41591b3a10e1438eaac5bcf448fb91f325381104a"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"futures-channel",
"futures-core",
"futures-executor",
@@ -2127,6 +2196,42 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "jiff"
version = "0.2.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc"
dependencies = [
"defmt",
"jiff-core",
"jiff-static",
"log",
"portable-atomic",
"portable-atomic-util",
"serde_core",
]
[[package]]
name = "jiff-core"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09"
dependencies = [
"defmt",
]
[[package]]
name = "jiff-static"
version = "0.2.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204"
dependencies = [
"jiff-core",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "jni"
version = "0.21.1"
@@ -2291,7 +2396,7 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6b8cfa2a7656627b4c92c6b9ef929433acd673d5ab3708cda1b18478ac00df4"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"cc",
"convert_case",
"cookie-factory",
@@ -2493,7 +2598,7 @@ dependencies = [
name = "ndk"
version = "0.9.0"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"jni-sys 0.3.1",
"log",
"ndk-sys",
@@ -2517,7 +2622,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"cfg-if",
"cfg_aliases",
"libc",
@@ -2530,7 +2635,7 @@ version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"cfg-if",
"cfg_aliases",
"libc",
@@ -2848,6 +2953,14 @@ version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "pf-bitstream"
version = "0.24.0"
dependencies = [
"cros-codecs",
"tracing",
]
[[package]]
name = "pf-capture"
version = "0.24.0"
@@ -2879,8 +2992,10 @@ dependencies = [
"ffmpeg-next",
"mdns-sd",
"opus",
"pf-dxvadec",
"pf-ffvk",
"pf-update-check",
"pf-vkdecode",
"pipewire",
"punktfunk-core",
"pyrowave-sys",
@@ -2935,6 +3050,16 @@ dependencies = [
"bytemuck",
]
[[package]]
name = "pf-dxvadec"
version = "0.24.0"
dependencies = [
"cros-codecs",
"pf-bitstream",
"pf-vkdecode",
"tracing",
]
[[package]]
name = "pf-encode"
version = "0.24.0"
@@ -3075,7 +3200,7 @@ version = "0.24.0"
dependencies = [
"anyhow",
"ashpd",
"bitflags",
"bitflags 2.13.0",
"bytemuck",
"futures-util",
"hex",
@@ -3102,6 +3227,17 @@ dependencies = [
"x11rb",
]
[[package]]
name = "pf-vkdecode"
version = "0.24.0"
dependencies = [
"ash",
"cros-codecs",
"pf-bitstream",
"sha2",
"tracing",
]
[[package]]
name = "pf-win-display"
version = "0.24.0"
@@ -3153,7 +3289,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9688b89abf11d756499f7c6190711d6dbe5a3acdb30c8fbf001d6596d06a8d44"
dependencies = [
"anyhow",
"bitflags",
"bitflags 2.13.0",
"libc",
"libspa",
"libspa-sys",
@@ -3207,7 +3343,7 @@ version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"crc32fast",
"fdeflate",
"flate2",
@@ -3251,6 +3387,21 @@ dependencies = [
"universal-hash",
]
[[package]]
name = "portable-atomic"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3"
[[package]]
name = "portable-atomic-util"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
dependencies = [
"portable-atomic",
]
[[package]]
name = "potential_utf"
version = "0.1.5"
@@ -3311,7 +3462,7 @@ checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744"
dependencies = [
"bit-set",
"bit-vec",
"bitflags",
"bitflags 2.13.0",
"num-traits",
"rand 0.9.4",
"rand_chacha 0.9.0",
@@ -3777,7 +3928,7 @@ version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags",
"bitflags 2.13.0",
]
[[package]]
@@ -3934,7 +4085,7 @@ version = "0.40.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11438310b19e3109b6446c33d1ed5e889428cf2e278407bc7896bc4aaea43323"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
@@ -3973,7 +4124,7 @@ version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"errno",
"libc",
"linux-raw-sys",
@@ -4127,7 +4278,7 @@ version = "0.18.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25bd22eb1bbc9137e914022b4994ed35591eea0884e9e3e98e6d9895cad6e1d2"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"libc",
"sdl3-image-sys",
"sdl3-mixer-sys",
@@ -4222,7 +4373,7 @@ version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"core-foundation",
"core-foundation-sys",
"libc",
@@ -4428,7 +4579,7 @@ version = "0.87.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f7d94f3e7537c71ad4cf132eb26e3be8c8a886ed3649c4525c089041fc312b2"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"lazy_static",
"skia-bindings",
]
@@ -5301,7 +5452,7 @@ version = "0.31.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"rustix",
"wayland-backend",
"wayland-scanner",
@@ -5313,7 +5464,7 @@ version = "0.32.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"wayland-backend",
"wayland-client",
"wayland-scanner",
@@ -5325,7 +5476,7 @@ version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"wayland-backend",
"wayland-client",
"wayland-protocols",
@@ -5338,7 +5489,7 @@ version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"wayland-backend",
"wayland-client",
"wayland-protocols",
@@ -5690,7 +5841,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d24d6bcc7f734a4091ecf8d7a64c5f7d7066f45585c1861eba06449909609c8a"
dependencies = [
"bitflags",
"bitflags 2.13.0",
"widestring",
"windows-sys 0.52.0",
]
+4
View File
@@ -5,6 +5,8 @@ members = [
"crates/punktfunk-host",
"crates/punktfunk-host/vendor/usbip-sim",
"crates/punktfunk-tray",
"crates/pf-bitstream",
"crates/pf-bitstream/vendor/cros-codecs",
"crates/pf-client-core",
"crates/pf-clipboard",
"crates/pf-presenter",
@@ -23,6 +25,8 @@ members = [
"crates/pf-capture",
"crates/pf-inject",
"crates/pf-vdisplay",
"crates/pf-vkdecode",
"crates/pf-dxvadec",
"crates/pyrowave-sys",
"crates/libvpl-sys",
"clients/probe",
+28 -7
View File
@@ -49,19 +49,40 @@ path + per-stage latency equation); any tier but Off also emits the stdout mirro
`--no-default-features` is the ~5 MB power-user build — same streaming, stats on stdout
only, no Skia anywhere in the dependency tree.
Decode follows the Settings preference (auto: Vulkan Video → VAAPI → software on Linux,
Vulkan Video → D3D11VA → software on Windows): FFmpeg's Vulkan Video decoder runs on the
presenter's own device where the stack supports it (every vendor, zero copy); VAAPI
dmabufs import per-plane elsewhere (D3D11VA textures on Windows); software is the
universal fallback. 10-bit Main10 and HDR10 are advertised
(`VIDEO_CAP_10BIT|HDR`): P010 decodes through all three paths, and PQ streams present
Decode follows the Settings preference (auto is vendor-ordered: hardware Vulkan Video →
VAAPI → software on Linux, hardware Vulkan Video → D3D11VA → software on Windows, with
VAAPI/D3D11VA first on Intel; on H.264 and HEVC the native pf-vkdecode Vulkan decoder
is tried immediately before FFmpeg-Vulkan): the Vulkan decoders run on the presenter's own
device where the stack supports it (every vendor, zero copy); VAAPI dmabufs import
per-plane elsewhere (D3D11VA textures on Windows); software is the universal fallback.
10-bit Main10 and HDR10 are advertised (`VIDEO_CAP_10BIT|HDR`): P010 decodes through the
native, FFmpeg-Vulkan, VAAPI/D3D11VA and software paths alike, and PQ streams present
on an HDR10/ST.2084 swapchain when the desktop offers one (KDE HDR, gamescope) or
tone-map in-shader to SDR when it doesn't (`PUNKTFUNK_TONEMAP_PEAK` tunes the rolloff,
default ≈1000 nits). The host still gates the upgrade behind its `PUNKTFUNK_10BIT`
policy.
Debug/bisect knobs: `PUNKTFUNK_DECODER=vulkan|vaapi|d3d11va|software`, `PUNKTFUNK_PRESENT_MODE=
Debug/bisect knobs: `PUNKTFUNK_DECODER=native-vulkan|vulkan|vaapi|d3d11va|software`, `PUNKTFUNK_PRESENT_MODE=
mailbox|fifo|immediate|fifo_relaxed` (default MAILBOX, FIFO where the surface offers no
MAILBOX — AMD on Windows), `PUNKTFUNK_VK_DEVICE=<index>` (multi-GPU), and
`PUNKTFUNK_HW_FAULT=import` (fault every VAAPI dmabuf import — proves the three-strike
demotion to software on healthy hardware).
`PUNKTFUNK_AU_FAULT=drop|truncate|flip[:period]` deliberately corrupts decoder input on the
native Vulkan lane (default period 60 — one AU a second at 60 fps; inert everywhere else, and
inert entirely if the value doesn't parse). `drop` swallows the AU, so the next one references a
picture that was never decoded — the bitstream planner catches it immediately. `truncate` delivers
a picture whose slice data stops mid-frame and `flip` alters one byte deep in the payload: both
parse perfectly, so only the driver's per-frame decode-status query can see them, and neither is
visible at all on a driver without `queryResultStatusSupport` or on any FFmpeg lane. Watch the
result on the Detailed stats line's `integrity:` term (`damaged` = concealment the planner caught,
`refused` = AUs the decoder rejected outright, `driver-failed` = the hardware's own verdict, `run`
= consecutive frames with no picture, `worst run` = the longest such stretch of the session — the
once-a-second `run` sample misses the bad moment almost every time — and `no driver status` = this
device cannot answer the driver question at all). A session that lands on any other lane says so
in the log rather than faulting silently.
Note that `PUNKTFUNK_AU_DUMP` records the AU as it arrived from the HOST, while the fault injector
runs later, at the native decoder's own entry. On a faulted run the dump is therefore the clean
bitstream — reconstruct the damaged bytes from the spec if you need them (the injector is pure and
deterministic).
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "pf-bitstream"
description = "Client-side bitstream layer for native decode: AU parsing, POC/DPB/reference derivation and per-AU DecodePlans (H.264/HEVC/AV1) on the vendored cros-codecs parsers — the layer libavcodec used to be (design/client-native-decode.md §3.1)"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
authors.workspace = true
repository.workspace = true
[dependencies]
cros-codecs = { path = "vendor/cros-codecs" }
tracing = "0.1"
[lints]
workspace = true
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+156
View File
@@ -0,0 +1,156 @@
//! The client's bitstream layer for native decode (design/client-native-decode.md §3.1):
//! everything a stateless hardware decoder needs to know about an AU before submission —
//! parsed headers, POC, DPB state, reference lists (including MMCO/LTR, which the hosts'
//! RFI recovery actively uses), recovery-point SEI — derived once here and consumed by
//! every backend (Vulkan `StdVideo*`, DXVA picparams, libva buffers).
//!
//! Parsing primitives come from the vendored cros-codecs parser layer
//! (`vendor/cros-codecs`, see its PROVENANCE.md); this crate owns what upstream keeps in
//! its Linux-only `decoder::stateless` half — the per-AU orchestration — plus the pieces
//! upstream lacks (SEI payload parsing: their parsers classify SEI NALUs but never read
//! them).
//!
//! Scope discipline: punktfunk clients decode punktfunk hosts — zero-reorder, no
//! B-frames, progressive, parameter sets from encoders we control. Implement to spec
//! where cheap; reject-with-log outside that envelope rather than half-decode.
//!
//! Nothing in this crate may touch a GPU API, an OS handle, or the network: CPU-only by
//! construction, so its tests run on every CI leg including macOS. And no `unsafe`,
//! compiler-enforced — this layer exists to replace C parsers; it does not get to
//! reintroduce their failure mode.
#![forbid(unsafe_code)]
pub mod h264;
pub mod h265;
pub mod sei;
// The vendor-pinning smoke tests below assert against byte counts and golden values from
// the vendored snapshot's own test vectors; a cros-codecs re-sync that shifts parser
// behavior must trip HERE, in our tree, not in a decode session.
#[cfg(test)]
mod vendor_smoke {
use std::io::Cursor;
use cros_codecs::bitstream_utils::IvfIterator;
use cros_codecs::codec::av1::parser::ObuAction;
use cros_codecs::codec::av1::parser::ParsedObu;
use cros_codecs::codec::h264::parser::Nalu as H264Nalu;
use cros_codecs::codec::h264::parser::Parser as H264Parser;
use cros_codecs::codec::h265::parser::Nalu as H265Nalu;
use cros_codecs::codec::h265::parser::Parser as H265Parser;
const H264_25FPS: &[u8] =
include_bytes!("../vendor/cros-codecs/src/codec/h264/test_data/test-25fps.h264");
const H265_25FPS: &[u8] =
include_bytes!("../vendor/cros-codecs/src/codec/h265/test_data/test-25fps.h265");
const AV1_25FPS: &[u8] =
include_bytes!("../vendor/cros-codecs/src/codec/av1/test_data/test-25fps.ivf.av1");
const VP9_25FPS: &[u8] =
include_bytes!("../vendor/cros-codecs/src/codec/vp9/test_data/test-25fps.vp9");
#[test]
fn h264_parses_the_vendored_vector_to_its_goldens() {
let mut cursor = Cursor::new(H264_25FPS);
let mut parser = H264Parser::default();
let (mut nalus, mut sps, mut slices) = (0u32, 0u32, 0u32);
let mut coded = (0u32, 0u32);
while let Ok(nalu) = H264Nalu::next(&mut cursor) {
nalus += 1;
if let Ok(s) = parser.parse_sps(&nalu) {
sps += 1;
coded = (
(s.pic_width_in_mbs_minus1 as u32 + 1) * 16,
(s.pic_height_in_map_units_minus1 as u32 + 1) * 16,
);
continue;
}
if parser.parse_pps(&nalu).is_ok() {
continue;
}
if parser.parse_slice_header(nalu).is_ok() {
slices += 1;
}
}
// 759 is upstream's own golden for this stream (chromium h264_parser_unittest lineage).
assert_eq!(nalus, 759);
assert_eq!(sps, 4);
assert_eq!(slices, 500);
assert_eq!(coded, (320, 240));
}
#[test]
fn h265_parses_the_vendored_vector() {
let mut cursor = Cursor::new(H265_25FPS);
let mut parser = H265Parser::default();
let (mut nalus, mut sps, mut slices) = (0u32, 0u32, 0u32);
while let Ok(nalu) = H265Nalu::next(&mut cursor) {
nalus += 1;
if parser.parse_sps(&nalu).is_ok() {
sps += 1;
continue;
}
if parser.parse_pps(&nalu).is_ok() {
continue;
}
if parser.parse_slice_header(nalu).is_ok() {
slices += 1;
}
}
assert_eq!(nalus, 254);
assert_eq!(sps, 1);
assert_eq!(slices, 250);
}
#[test]
fn av1_walks_obus_and_maintains_ref_slots_across_the_stream() {
let mut parser = cros_codecs::codec::av1::parser::Parser::default();
let (mut obus, mut frames) = (0u32, 0u32);
for packet in IvfIterator::new(AV1_25FPS) {
let mut consumed = 0;
while let Ok(action) = parser.read_obu(&packet[consumed..]) {
let obu = match action {
ObuAction::Process(obu) => obu,
ObuAction::Drop(n) => {
consumed += n as usize;
continue;
}
};
consumed += obu.bytes_used;
obus += 1;
// `ref_frame_update` is the parser's ref-slot bookkeeping; without it,
// inter frames fail with "Reference is invalid" — the parser validates
// reference integrity rather than trusting the stream.
match parser.parse_obu(obu).expect("parse_obu") {
ParsedObu::FrameHeader(fh) => {
frames += 1;
parser.ref_frame_update(&fh).expect("ref slot update");
}
ParsedObu::Frame(f) => {
frames += 1;
parser.ref_frame_update(&f.header).expect("ref slot update");
}
_ => {}
}
}
}
// 525 is upstream's own golden (cross-checked against GStreamer's OBU walk).
assert_eq!(obus, 525);
assert_eq!(frames, 274);
}
#[test]
fn vp9_splits_superframes_and_parses_headers() {
let mut parser = cros_codecs::codec::vp9::parser::Parser::default();
let (mut chunks, mut frames) = (0u32, 0u32);
for packet in IvfIterator::new(VP9_25FPS) {
chunks += 1;
frames += parser
.parse_chunk(packet.as_ref())
.expect("vp9 chunk")
.len() as u32;
}
assert_eq!(chunks, 250);
// > chunks proves superframe splitting engaged.
assert_eq!(frames, 269);
}
}
+346
View File
@@ -0,0 +1,346 @@
//! SEI payload parsing — the piece the vendored parser layer lacks: upstream classifies
//! SEI NALUs but never reads a payload. punktfunk needs exactly one payload type per
//! codec: the recovery point SEI, which hosts emit on RFI recovery so the client knows
//! where a decode-from-here point lands. Every other payload type is skipped by its
//! declared size.
//!
//! Both codecs put the recovery point at payload type 6 with the same D.1 message
//! framing, but the payload syntax differs: H.264 (D.1.8/D.2.8) counts recovery in
//! `frame_num` increments (`recovery_frame_cnt`, ue(v)) and carries a slice-group bit
//! pair; H.265 (D.2.8/D.3.8) counts in picture order (`recovery_poc_cnt`, se(v) — it
//! can be negative) and has no slice-group field. Hence two parsers over one shared
//! message walk.
/// Recovery point SEI (D.2.8).
///
/// `recovery_frame_cnt` counts in `frame_num` increments from the AU carrying the SEI to
/// the picture at which output is exact (`exact_match`) or approximate. `broken_link` set
/// means pictures before the recovery point may be visually broken and must not be shown.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RecoveryPoint {
pub recovery_frame_cnt: u32,
pub exact_match: bool,
pub broken_link: bool,
}
/// Recovery point SEI, H.265 flavour (D.3.8).
///
/// `recovery_poc_cnt` is the POC delta from the picture carrying the SEI to the
/// recovery-point picture — se(v)-coded, so unlike H.264's `recovery_frame_cnt` it can
/// be NEGATIVE (a recovery point among leading pictures). `exact_match`/`broken_link`
/// keep their H.264 semantics.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RecoveryPointHevc {
pub recovery_poc_cnt: i32,
pub exact_match: bool,
pub broken_link: bool,
}
/// Parse the first recovery point SEI message out of an H.264 SEI NALU.
///
/// `sei_payload` are the bytes of the NALU after its one-byte NAL header, emulation
/// prevention bytes still in place (they are removed here — 7.4.1 RBSP extraction).
/// `Ok(None)` means the NALU parsed cleanly but carries no recovery point.
pub fn parse_recovery_point(sei_payload: &[u8]) -> Result<Option<RecoveryPoint>, String> {
let rbsp = strip_emulation_prevention(sei_payload);
let Some(payload) = first_recovery_point_payload(&rbsp)? else {
return Ok(None);
};
let mut r = BitCursor::new(payload);
let recovery_frame_cnt = r.read_ue()?;
let exact_match = r.read_bit()? != 0;
let broken_link = r.read_bit()? != 0;
// changing_slice_group_idc u(2): parsed to keep the reader honest, unused —
// slice groups are outside every profile punktfunk hosts emit.
let _changing_slice_group_idc = r.read_bits(2)?;
Ok(Some(RecoveryPoint {
recovery_frame_cnt,
exact_match,
broken_link,
}))
}
/// Parse the first recovery point SEI message out of an H.265 prefix SEI NALU.
///
/// `sei_payload` are the bytes of the NALU after its TWO-byte NAL header (H.265 NALU
/// headers are 16 bits), emulation prevention still in place. Only prefix SEI NALUs
/// (type 39) can carry a recovery point — D.2.1 lists it as prefix-only, so suffix SEI
/// NALUs (type 40) need never reach here.
pub fn parse_recovery_point_hevc(sei_payload: &[u8]) -> Result<Option<RecoveryPointHevc>, String> {
let rbsp = strip_emulation_prevention(sei_payload);
let Some(payload) = first_recovery_point_payload(&rbsp)? else {
return Ok(None);
};
let mut r = BitCursor::new(payload);
let recovery_poc_cnt = r.read_se()?;
let exact_match = r.read_bit()? != 0;
let broken_link = r.read_bit()? != 0;
Ok(Some(RecoveryPointHevc {
recovery_poc_cnt,
exact_match,
broken_link,
}))
}
/// Walk the D.1 SEI message framing (shared verbatim between H.264 and H.265) and
/// return the payload bytes of the first recovery point message (payload type 6 in
/// both codecs), if any. `rbsp` is already emulation-prevention-stripped.
fn first_recovery_point_payload(rbsp: &[u8]) -> Result<Option<&[u8]>, String> {
let mut i = 0usize;
while i < rbsp.len() && !is_rbsp_trailing(rbsp, i) {
// D.1: payload type and size are ff-coded — 0xFF bytes each add 255 until a
// non-0xFF byte terminates the value. The run length is unbounded, so the type
// accumulates saturating: an adversarial ~16M-byte 0xFF run must not overflow
// (a saturated type simply never matches 6). The size accumulator is a usize
// whose use is bounds-checked below.
let mut payload_type = 0u32;
while i < rbsp.len() && rbsp[i] == 0xFF {
payload_type = payload_type.saturating_add(255);
i += 1;
}
if i >= rbsp.len() {
return Err("truncated SEI payload type".into());
}
payload_type = payload_type.saturating_add(u32::from(rbsp[i]));
i += 1;
let mut payload_size = 0usize;
while i < rbsp.len() && rbsp[i] == 0xFF {
payload_size += 255;
i += 1;
}
if i >= rbsp.len() {
return Err("truncated SEI payload size".into());
}
payload_size += usize::from(rbsp[i]);
i += 1;
let end = i
.checked_add(payload_size)
.filter(|&end| end <= rbsp.len())
.ok_or_else(|| "SEI payload overruns the NALU".to_string())?;
if payload_type == 6 {
return Ok(Some(&rbsp[i..end]));
}
i = end;
}
Ok(None)
}
/// 7.4.1: within the RBSP, `00 00 03` encodes two zero bytes; the `03` is the emulation
/// prevention byte and is dropped.
fn strip_emulation_prevention(data: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(data.len());
let mut zeros = 0usize;
for &byte in data {
if zeros >= 2 && byte == 0x03 {
zeros = 0;
continue;
}
zeros = if byte == 0 { zeros + 1 } else { 0 };
out.push(byte);
}
out
}
/// `more_rbsp_data()` at a byte-aligned message boundary: the remainder is trailing bits
/// iff it is the stop bit (0x80) followed by nothing but zero bytes.
fn is_rbsp_trailing(rbsp: &[u8], i: usize) -> bool {
rbsp[i] == 0x80 && rbsp[i + 1..].iter().all(|&b| b == 0)
}
/// Minimal MSB-first bit reader over an already-unescaped RBSP slice. The vendored
/// `BitReader` is `pub(crate)` to the vendored crate, so this crate carries its own.
struct BitCursor<'a> {
data: &'a [u8],
/// Position in bits from the start of `data`.
pos: usize,
}
impl<'a> BitCursor<'a> {
fn new(data: &'a [u8]) -> Self {
Self { data, pos: 0 }
}
fn read_bit(&mut self) -> Result<u32, String> {
let byte = *self
.data
.get(self.pos / 8)
.ok_or("SEI payload out of bits")?;
let bit = (byte >> (7 - self.pos % 8)) & 1;
self.pos += 1;
Ok(u32::from(bit))
}
fn read_bits(&mut self, count: usize) -> Result<u32, String> {
debug_assert!(count <= 31);
let mut out = 0u32;
for _ in 0..count {
out = (out << 1) | self.read_bit()?;
}
Ok(out)
}
/// ue(v), spec 9.1.
fn read_ue(&mut self) -> Result<u32, String> {
let mut leading_zeros = 0usize;
while self.read_bit()? == 0 {
leading_zeros += 1;
if leading_zeros > 31 {
return Err("invalid exp-Golomb code in SEI payload".into());
}
}
let suffix = self.read_bits(leading_zeros)?;
((1u32 << leading_zeros) - 1)
.checked_add(suffix)
.ok_or_else(|| "exp-Golomb value overflows u32".to_string())
}
/// se(v), spec 9.1.1: the ue(v) code point k maps to (1)^(k+1) · ⌈k/2⌉.
fn read_se(&mut self) -> Result<i32, String> {
let k = self.read_ue()?;
let magnitude = k.div_ceil(2);
let magnitude =
i32::try_from(magnitude).map_err(|_| "exp-Golomb value overflows i32".to_string())?;
Ok(if k % 2 == 1 { magnitude } else { -magnitude })
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_minimal_recovery_point_message_parses_to_its_field_values() {
// Message: type 6, size 1. Payload bits: ue(0)='1', exact=0, broken=0, csg=00,
// then payload alignment '1' + zeros -> 0b1000_0100. NALU trailing 0x80.
let sei = [0x06, 0x01, 0x84, 0x80];
assert_eq!(
parse_recovery_point(&sei).unwrap(),
Some(RecoveryPoint {
recovery_frame_cnt: 0,
exact_match: false,
broken_link: false
})
);
}
#[test]
fn recovery_frame_cnt_and_both_flags_round_trip_through_the_bit_reader() {
// ue(5)='00110', exact=1, broken=1, csg=00, alignment -> 0b0011_0110 0b0100_0000.
let sei = [0x06, 0x02, 0x36, 0x40, 0x80];
assert_eq!(
parse_recovery_point(&sei).unwrap(),
Some(RecoveryPoint {
recovery_frame_cnt: 5,
exact_match: true,
broken_link: true
})
);
}
#[test]
fn earlier_messages_and_ff_coded_types_are_skipped_to_reach_the_recovery_point() {
// First message: ff-coded payload type 255 (0xFF 0x00), size 1, payload 0x55.
// Second message: type 5 (user data), size 3. Third: the recovery point.
let sei = [
0xFF, 0x00, 0x01, 0x55, // type 255
0x05, 0x03, 0xAA, 0xBB, 0xCC, // type 5
0x06, 0x01, 0x84, // recovery point
0x80,
];
assert_eq!(
parse_recovery_point(&sei).unwrap(),
Some(RecoveryPoint {
recovery_frame_cnt: 0,
exact_match: false,
broken_link: false
})
);
}
#[test]
fn emulation_prevention_bytes_inside_the_payload_are_removed_before_reading() {
// Unescaped payload (7 bytes): ue with a 22-zero prefix => recovery_frame_cnt
// 2^22-1 = 4194303, exact=1, broken=0, csg=00, alignment. Its first bytes are
// 00 00 02, which the escaper must have written as 00 00 03 02 on the wire.
let sei = [
0x06, 0x07, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x04, 0x40, 0x80,
];
assert!(sei.windows(3).any(|w| w == [0x00, 0x00, 0x03]));
assert_eq!(
parse_recovery_point(&sei).unwrap(),
Some(RecoveryPoint {
recovery_frame_cnt: 4194303,
exact_match: true,
broken_link: false
})
);
}
#[test]
fn a_sei_nalu_without_a_recovery_point_yields_none_not_an_error() {
let sei = [0x05, 0x01, 0x00, 0x80];
assert_eq!(parse_recovery_point(&sei).unwrap(), None);
}
#[test]
fn a_payload_size_overrunning_the_nalu_is_a_parse_error() {
let sei = [0x06, 0x0A, 0x00];
assert!(parse_recovery_point(&sei).is_err());
}
#[test]
fn the_hevc_recovery_point_parses_its_se_coded_poc_count() {
// recovery_poc_cnt se(0) = '1', exact = 0, broken = 0, payload alignment:
// 0b1001_0000.
let sei = [0x06, 0x01, 0x90, 0x80];
assert_eq!(
parse_recovery_point_hevc(&sei).unwrap(),
Some(RecoveryPointHevc {
recovery_poc_cnt: 0,
exact_match: false,
broken_link: false
})
);
// se(-1) = '011' (ue code point 2), exact = 1, broken = 0, alignment:
// 0b0111_0100 — the negative range H.264's ue(v) syntax cannot express.
let sei = [0x06, 0x01, 0x74, 0x80];
assert_eq!(
parse_recovery_point_hevc(&sei).unwrap(),
Some(RecoveryPointHevc {
recovery_poc_cnt: -1,
exact_match: true,
broken_link: false
})
);
}
#[test]
fn the_hevc_parser_skips_earlier_messages_and_reports_absence_as_none() {
// User-data message first, then the recovery point (poc_cnt se(3): ue code
// point 5 = '00110', exact = 1, broken = 1, alignment: 0b0011_0111).
let sei = [
0x05, 0x02, 0xAA, 0xBB, // type 5
0x06, 0x01, 0x37, // recovery point
0x80,
];
assert_eq!(
parse_recovery_point_hevc(&sei).unwrap(),
Some(RecoveryPointHevc {
recovery_poc_cnt: 3,
exact_match: true,
broken_link: true
})
);
let sei = [0x05, 0x01, 0x00, 0x80];
assert_eq!(parse_recovery_point_hevc(&sei).unwrap(), None);
}
}
+232
View File
@@ -0,0 +1,232 @@
//! Corpus replay: walk a captured real-host stream through the planners.
//!
//! The M0 capture hook (`PUNKTFUNK_DUMP_VIDEO=<dir>` on any desktop client) writes
//! the exact decoder input of a live session — `au-<stamp>.<codec>` plus an `.idx`
//! sidecar carrying `offset len flags complete` per AU. This harness feeds those AUs
//! back through [`pf_bitstream::h264::H264Planner`] / [`pf_bitstream::h265::H265Planner`]
//! and asserts the planner survives a REAL host stream: every AU plans (bar the
//! deliberate skips), no panic, and the warnings are only the ones a clean capture may
//! legitimately produce.
//!
//! Why this exists separately from the vendored conformance vectors: those prove we
//! match the spec's own test streams, and the on-glass sessions prove the whole pipe —
//! but between the two sits "does the planner handle what OUR five host encoder
//! families actually emit", which is the question the corpus was captured to answer.
//! For HEVC this is the ONLY pre-wiring validation against real host output (the
//! client's HEVC rung is still being built), so it runs long before M3 finishes.
//!
//! Ignored by default: captures are hundreds of megabytes and live outside the repo.
//! Run one explicitly —
//!
//! ```text
//! PF_CORPUS=/path/to/au-1785970273.h265 \
//! cargo test -p pf-bitstream --test corpus_replay -- --ignored --nocapture
//! ```
//!
//! The `.idx` sidecar is found next to the data file (`<data>.idx`); the codec comes
//! from the extension, matching the capture hook's own naming convention.
use std::path::Path;
use std::path::PathBuf;
/// One captured access unit: its byte range in the data file, plus the wire bits the
/// byte stream itself cannot carry.
struct CapturedAu {
offset: usize,
len: usize,
/// The wire `flags` byte (`USER_FLAG_*`) — kept for the RFI/intra-refresh legs,
/// which discriminate on it.
_flags: u32,
complete: bool,
}
/// Parse the `.idx` sidecar: one `offset len flags complete` line per AU, `#` comments
/// and blank lines skipped (the hook writes none today, but a hand-trimmed corpus file
/// is a thing a human will produce).
///
/// A malformed FINAL line is dropped with a note instead of failing: ending a capture
/// means killing the client, so the last buffered line is routinely half-written (the
/// hook's own docs call a truncated last AU acceptable). Anywhere else a malformed line
/// means the sidecar is corrupt and the run must not quietly replay a subset.
fn read_index(path: &Path) -> Vec<CapturedAu> {
let text = std::fs::read_to_string(path)
.unwrap_or_else(|e| panic!("cannot read the index sidecar {}: {e}", path.display()));
let lines: Vec<&str> = text
.lines()
.filter(|line| !line.trim().is_empty() && !line.trim_start().starts_with('#'))
.collect();
let last = lines.len().saturating_sub(1);
let mut out = Vec::with_capacity(lines.len());
for (n, line) in lines.iter().enumerate() {
match parse_index_line(line) {
Some(au) => out.push(au),
None if n == last => {
println!("note: dropping a truncated final index line ({line:?})");
}
None => panic!("index line {n} is malformed: {line:?}"),
}
}
out
}
/// One `offset len flags complete` line, or `None` when it is not four parsable fields.
fn parse_index_line(line: &str) -> Option<CapturedAu> {
let mut it = line.split_whitespace();
let num = |raw: &str| -> Option<u64> {
match raw.strip_prefix("0x") {
Some(hex) => u64::from_str_radix(hex, 16).ok(),
None => raw.parse().ok(),
}
};
let offset = num(it.next()?)?;
let len = num(it.next()?)?;
let flags = num(it.next()?)?;
let complete = num(it.next()?)?;
Some(CapturedAu {
offset: offset as usize,
len: len as usize,
_flags: flags as u32,
complete: complete != 0,
})
}
/// The capture named by `PF_CORPUS`, or `None` when the variable is unset.
fn corpus_from_env() -> Option<(PathBuf, Vec<u8>, Vec<CapturedAu>)> {
let path = PathBuf::from(std::env::var_os("PF_CORPUS")?);
let data = std::fs::read(&path)
.unwrap_or_else(|e| panic!("cannot read the capture {}: {e}", path.display()));
let mut idx = path.clone().into_os_string();
idx.push(".idx");
let mut index = read_index(Path::new(&idx));
// Same truncation story on the data side: the final AU's bytes may not all have
// reached the file before the client died. Drop AUs the data cannot cover — but
// only from the tail, so a short file can never silently hide a middle gap.
let covered = index
.iter()
.take_while(|au| au.offset.saturating_add(au.len) <= data.len())
.count();
if covered < index.len() {
println!(
"note: dropping {} index entr{} past the end of the data file (truncated capture)",
index.len() - covered,
if index.len() - covered == 1 {
"y"
} else {
"ies"
},
);
index.truncate(covered);
}
assert!(!index.is_empty(), "the capture's index is empty");
Some((path, data, index))
}
/// Per-AU outcome tally — what the run reports and asserts on.
#[derive(Default)]
struct Tally {
planned: usize,
skipped: usize,
errors: Vec<String>,
warnings: Vec<String>,
partial: usize,
}
impl Tally {
/// A clean capture of a healthy session must plan every complete AU. Errors are
/// hard failures; warnings are printed and capped — `MissingReference` on a stream
/// that never lost a packet would mean the planner invented a gap.
fn assert_clean(&self, total: usize) {
println!(
"planned {} / skipped {} / partial-AUs-ignored {} / errors {} / warnings {} \
(of {total} captured AUs)",
self.planned,
self.skipped,
self.partial,
self.errors.len(),
self.warnings.len(),
);
for w in self.warnings.iter().take(20) {
println!(" warning: {w}");
}
for e in self.errors.iter().take(20) {
println!(" ERROR: {e}");
}
assert!(
self.errors.is_empty(),
"{} AUs failed to plan — first: {}",
self.errors.len(),
self.errors[0],
);
assert!(
self.warnings.is_empty(),
"{} planner warnings on a clean capture — first: {}",
self.warnings.len(),
self.warnings[0],
);
assert!(self.planned > 0, "no AU planned at all");
}
}
#[test]
#[ignore = "needs a capture: PF_CORPUS=<au-file> (see the module docs)"]
fn a_captured_host_stream_replays_through_the_planner() {
let Some((path, data, index)) = corpus_from_env() else {
panic!("PF_CORPUS is unset — see the module docs for the invocation");
};
let ext = path
.extension()
.and_then(|e| e.to_str())
.unwrap_or_default()
.to_owned();
println!(
"replaying {} ({} bytes, {} AUs, codec {ext})",
path.display(),
data.len(),
index.len(),
);
let mut tally = Tally::default();
// The planners take one COMPLETE AU. A partial AU (the wire's shard split) is the
// pump's business, not the planner's — count and skip rather than feed a fragment.
let complete: Vec<&CapturedAu> = index.iter().filter(|au| au.complete).collect();
tally.partial = index.len() - complete.len();
match ext.as_str() {
"h265" => {
let mut planner = pf_bitstream::h265::H265Planner::new();
for (i, au) in complete.iter().enumerate() {
let bytes = &data[au.offset..au.offset + au.len];
match planner.plan_au(bytes) {
Ok(plan) => {
tally.planned += 1;
for w in &plan.warnings {
tally.warnings.push(format!("AU {i}: {w:?}"));
}
}
// The spec's own skip (8.1.3): decode nothing, show nothing, the
// stream is healthy — never an error (the WP-2 contract note).
Err(pf_bitstream::h265::PlanError::RaslSkipped { .. }) => tally.skipped += 1,
Err(e) => tally.errors.push(format!("AU {i}: {e}")),
}
}
}
"h264" => {
let mut planner = pf_bitstream::h264::H264Planner::new();
for (i, au) in complete.iter().enumerate() {
let bytes = &data[au.offset..au.offset + au.len];
match planner.plan_au(bytes) {
Ok(plan) => {
tally.planned += 1;
for w in &plan.warnings {
tally.warnings.push(format!("AU {i}: {w:?}"));
}
}
Err(e) => tally.errors.push(format!("AU {i}: {e}")),
}
}
}
other => panic!("no planner for a .{other} capture (h264/h265 only today)"),
}
tally.assert_clean(index.len());
}
+17
View File
@@ -0,0 +1,17 @@
# Vendored snapshot — see PROVENANCE.md. Deliberately NOT opted into workspace lints
# or workspace package inheritance: upstream code stays as close to pristine as the
# trim allows, so re-syncing against the AOSP tree stays a diff, not an archaeology dig.
[package]
name = "cros-codecs"
version = "0.0.5"
license = "BSD-3-Clause"
description = "Vendored cros-codecs parser layer (codec module only) for pf-bitstream"
edition = "2021"
[dependencies]
log = "0.4"
# Upstream's in-tree unit tests (kept — they are the conformance goldens) want these.
[dev-dependencies]
env_logger = "0.11"
serde_json = "1"
+26
View File
@@ -0,0 +1,26 @@
Copyright 2022 The ChromiumOS Authors
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+72
View File
@@ -0,0 +1,72 @@
# Vendored: cros-codecs (parser layer only)
- **Upstream:** <https://android.googlesource.com/platform/system/cros-codecs/> (the
authoritative AOSP tree). Snapshot taken from the read-only GitHub mirror
<https://github.com/chromeos/cros-codecs>, branch `main`,
commit **`5ff6d693ffae0b36935b8fc13092c733b4c2646f`**, fetched 2026-08-05.
- **License:** BSD-3-Clause (`LICENSE`, copied verbatim). Attribution headers retained
in every source file.
- **Why vendored, not a crates.io dependency:** the GitHub repo is a read-only mirror
and the crates.io release lags it; a pinned, reviewed snapshot is the supply-chain
posture punktfunk already uses elsewhere (`clients/android/native/vendor/ndk`,
`punktfunk-host/vendor/usbip-sim`). Decision of record:
punktfunk-planning `design/client-native-decode.md` §8.1.
## What was taken
`src/codec/{h264,h265,av1,vp9}` (parsers, DPBs, picture types, NALU/OBU machinery,
their `test_data` vectors — they double as punktfunk's conformance corpus),
`src/bitstream_utils.rs`, `LICENSE`. Upstream designed the `codec` module for exactly
this extraction — its module doc: "There shall be no dependencies from other modules of
this crate to this module, so that it can be turned into a crate of its own if needed
in the future."
## What was left behind
- `decoder/`, `encoder/`, `backend/`, `c2_wrapper/`, `video_frame`, `image_processing`,
`utils` — the Linux-only halves (libva/v4l2/gbm/nix). punktfunk's `pf-bitstream` +
`pf-vkdecode` occupy that layer.
- `codec/vp8` — VP9 has no dependency on it (verified) and no punktfunk host will ever
emit VP8.
## Deviations from pristine upstream
1. `src/lib.rs` — rewritten: keeps only the module decls and `Resolution` /
`ResolutionRoundMode` (the sole root items `codec` references), both copied verbatim;
adds crate-level `#![allow(clippy::all, mismatched_lifetime_syntaxes)]` — vendored
code is not held to the workspace lint bar (CI's `-D warnings` legs would fail on
upstream style otherwise).
2. `src/codec.rs` — one line removed (`pub mod vp8;`).
3. `Cargo.toml` — rewritten: `log` is the only dependency the vendored subset needs,
plus `env_logger`/`serde_json` dev-dependencies for upstream's in-tree tests.
4. `cargo fmt` normalization under the workspace's rustfmt config (mechanical only).
5. **Zero-unsafe, enforced**: `#![forbid(unsafe_code)]` added to lib.rs. Upstream's codec
module had exactly one production `unsafe` (h264/dpb.rs `build_ref_pic_lists`: ref→index
via pointer `offset_from`) — replaced with a safe `position(ptr::eq)` over the ≤16-entry
DPB — and three test-only `mem::zeroed()` asserts, replaced with `Default::default()`
(`PredWeightTable` derives `Default`; all-integer struct, identical value). The layer
facing untrusted bytes is now compiler-verified free of unsafe — the property that
motivates replacing libavcodec's C parsers in the first place.
6. `src/codec/h264/picture.rs``PictureData::new_from_slice`: `display_resolution`
computed as `visible_rect.max` instead of `max - min`. `Sps::visible_rectangle()`
returns the crop offset in `min` and the visible *size* in `max` (see its
definition: `max.x = width - crop_left - crop_right`); upstream's subtraction
double-counts the left/top crop and, worse, panics on u32 underflow for a
large-but-parser-valid `frame_crop_left_offset` (e.g. 100 crop units on a 320-wide
SPS). Found by pf-bitstream's conformance-window tests; upstream never hits it
because real encoders crop right/bottom only. **Reported upstream 2026-08-06:
<https://github.com/chromeos/cros-codecs/issues/99>.**
7. `src/codec/h265/parser.rs``parse_slice_header`: reject
`num_long_term_sps + num_long_term_pics > 16` before the long-term RPS loop.
Upstream bounds the pair only by `MAX_LONG_TERM_REF_PIC_SETS` (32) combined, while
every long-term array in `SliceHeader` (`poc_lsb_lt`, `used_by_curr_pic_lt`,
`delta_poc_msb_present_flag`, `delta_poc_msb_cycle_lt`, `lt_idx_sps`) is `[_; 16]`
— a hostile slice header with 17+ entries panics the parser with an
index-out-of-bounds (bounds checks stay on in release). Found by pf-bitstream's
H.265 planner review; regression-tested there
(`a_hostile_long_term_count_is_a_parse_error_not_a_panic`). **Reported upstream
2026-08-06: <https://github.com/chromeos/cros-codecs/issues/100>.**
Re-sync procedure: fetch the AOSP tree, re-apply this trim, diff `codec/` +
`bitstream_utils.rs` (expect near-zero conflicts), update the commit pin above.
@@ -0,0 +1,788 @@
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::borrow::Cow;
use std::fmt;
use std::io::Cursor;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Write;
use std::marker::PhantomData;
use crate::codec::h264::parser::Nalu as H264Nalu;
use crate::codec::h265::parser::Nalu as H265Nalu;
/// A bit reader for codec bitstreams. It properly handles emulation-prevention
/// bytes and stop bits for H264.
#[derive(Clone)]
pub(crate) struct BitReader<'a> {
/// A reference into the next unread byte in the stream.
data: Cursor<&'a [u8]>,
/// Contents of the current byte. First unread bit starting at position 8 -
/// num_remaining_bits_in_curr_bytes.
curr_byte: u8,
/// Number of bits remaining in `curr_byte`
num_remaining_bits_in_curr_byte: usize,
/// Used in emulation prevention byte detection.
prev_two_bytes: u16,
/// Number of emulation prevention bytes (i.e. 0x000003) we found.
num_epb: usize,
/// Whether or not we need emulation prevention logic.
needs_epb: bool,
/// How many bits have been read so far.
position: u64,
}
#[derive(Debug)]
pub(crate) enum GetByteError {
OutOfBits,
}
impl fmt::Display for GetByteError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "reader ran out of bits")
}
}
#[derive(Debug)]
pub(crate) enum ReadBitsError {
TooManyBitsRequested(usize),
GetByte(GetByteError),
ConversionFailed,
}
impl fmt::Display for ReadBitsError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ReadBitsError::TooManyBitsRequested(bits) => {
write!(f, "more than 31 ({}) bits were requested", bits)
}
ReadBitsError::GetByte(_) => write!(f, "failed to advance the current byte"),
ReadBitsError::ConversionFailed => {
write!(f, "failed to convert read input to target type")
}
}
}
}
impl From<GetByteError> for ReadBitsError {
fn from(err: GetByteError) -> Self {
ReadBitsError::GetByte(err)
}
}
impl<'a> BitReader<'a> {
pub fn new(data: &'a [u8], needs_epb: bool) -> Self {
Self {
data: Cursor::new(data),
curr_byte: Default::default(),
num_remaining_bits_in_curr_byte: Default::default(),
prev_two_bytes: 0xffff,
num_epb: Default::default(),
needs_epb: needs_epb,
position: 0,
}
}
/// Read a single bit from the stream.
pub fn read_bit(&mut self) -> Result<bool, String> {
let bit = self.read_bits::<u32>(1)?;
match bit {
1 => Ok(true),
0 => Ok(false),
_ => panic!("Unexpected value {}", bit),
}
}
/// Read up to 31 bits from the stream. Note that we don't want to read 32
/// bits even though we're returning a u32 because that would break the
/// read_bits_signed() function. 31 bits should be overkill for compressed
/// header parsing anyway.
pub fn read_bits<U: TryFrom<u32>>(&mut self, num_bits: usize) -> Result<U, String> {
if num_bits > 31 {
return Err(ReadBitsError::TooManyBitsRequested(num_bits).to_string());
}
let mut bits_left = num_bits;
let mut out = 0u32;
while self.num_remaining_bits_in_curr_byte < bits_left {
out |= (self.curr_byte as u32) << (bits_left - self.num_remaining_bits_in_curr_byte);
bits_left -= self.num_remaining_bits_in_curr_byte;
self.move_to_next_byte().map_err(|err| err.to_string())?;
}
out |= (self.curr_byte >> (self.num_remaining_bits_in_curr_byte - bits_left)) as u32;
out &= (1 << num_bits) - 1;
self.num_remaining_bits_in_curr_byte -= bits_left;
self.position += num_bits as u64;
U::try_from(out).map_err(|_| ReadBitsError::ConversionFailed.to_string())
}
/// Reads a two's complement signed integer of length |num_bits|.
pub fn read_bits_signed<U: TryFrom<i32>>(&mut self, num_bits: usize) -> Result<U, String> {
let mut out: i32 = self
.read_bits::<u32>(num_bits)?
.try_into()
.map_err(|_| ReadBitsError::ConversionFailed.to_string())?;
if out >> (num_bits - 1) != 0 {
out |= -1i32 ^ ((1 << num_bits) - 1);
}
U::try_from(out).map_err(|_| ReadBitsError::ConversionFailed.to_string())
}
/// Reads an unsigned integer from the stream and checks if the stream is byte aligned.
pub fn read_bits_aligned<U: TryFrom<u32>>(&mut self, num_bits: usize) -> Result<U, String> {
if self.num_remaining_bits_in_curr_byte % 8 != 0 {
return Err("Attempted unaligned read_le()".into());
}
Ok(self.read_bits(num_bits).map_err(|err| err.to_string())?)
}
/// Skip `num_bits` bits from the stream.
pub fn skip_bits(&mut self, mut num_bits: usize) -> Result<(), String> {
while num_bits > 0 {
let n = std::cmp::min(num_bits, 31);
self.read_bits::<u32>(n)?;
num_bits -= n;
}
Ok(())
}
/// Returns the amount of bits left in the stream
pub fn num_bits_left(&mut self) -> usize {
let cur_pos = self.data.position();
// This should always be safe to unwrap.
let end_pos = self.data.seek(SeekFrom::End(0)).unwrap();
let _ = self.data.seek(SeekFrom::Start(cur_pos));
((end_pos - cur_pos) as usize) * 8 + self.num_remaining_bits_in_curr_byte
}
/// Returns the number of emulation-prevention bytes read so far.
pub fn num_epb(&self) -> usize {
self.num_epb
}
/// Whether the stream still has RBSP data. Implements more_rbsp_data(). See
/// the spec for more details.
pub fn has_more_rsbp_data(&mut self) -> bool {
if self.num_remaining_bits_in_curr_byte == 0 && self.move_to_next_byte().is_err() {
// no more data at all in the rbsp
return false;
}
// If the next bit is the stop bit, then we should only see unset bits
// until the end of the data.
if (self.curr_byte & ((1 << (self.num_remaining_bits_in_curr_byte - 1)) - 1)) != 0 {
return true;
}
let mut buf = [0u8; 1];
let orig_pos = self.data.position();
while let Ok(_) = self.data.read_exact(&mut buf) {
if buf[0] != 0 {
self.data.set_position(orig_pos);
return true;
}
}
false
}
/// Reads an Unsigned Exponential golomb coding number from the next bytes in the
/// bitstream. This may advance the state of position within the bitstream even if the
/// read operation is unsuccessful. See H264 Annex B specification 9.1 for details.
pub fn read_ue<U: TryFrom<u32>>(&mut self) -> Result<U, String> {
let mut num_bits = 0;
while self.read_bits::<u32>(1)? == 0 {
num_bits += 1;
if num_bits > 31 {
return Err("invalid stream".into());
}
}
let value = ((1u32 << num_bits) - 1)
.checked_add(self.read_bits::<u32>(num_bits)?)
.ok_or::<String>("read number cannot fit in 32 bits".into())?;
U::try_from(value).map_err(|_| "conversion error".into())
}
pub fn read_ue_bounded<U: TryFrom<u32>>(&mut self, min: u32, max: u32) -> Result<U, String> {
let ue = self.read_ue()?;
if ue > max || ue < min {
Err(format!(
"Value out of bounds: expected {} - {}, got {}",
min, max, ue
))
} else {
Ok(U::try_from(ue).map_err(|_| String::from("Conversion error"))?)
}
}
pub fn read_ue_max<U: TryFrom<u32>>(&mut self, max: u32) -> Result<U, String> {
self.read_ue_bounded(0, max)
}
/// Reads a signed exponential golomb coding number. Instead of using two's
/// complement, this scheme maps even integers to positive numbers and odd
/// integers to negative numbers. The least significant bit indicates the
/// sign. See H264 Annex B specification 9.1.1 for details.
pub fn read_se<U: TryFrom<i32>>(&mut self) -> Result<U, String> {
let ue = self.read_ue::<u32>()? as i32;
if ue % 2 == 0 {
Ok(U::try_from(-(ue / 2)).map_err(|_| String::from("Conversion error"))?)
} else {
Ok(U::try_from(ue / 2 + 1).map_err(|_| String::from("Conversion error"))?)
}
}
pub fn read_se_bounded<U: TryFrom<i32>>(&mut self, min: i32, max: i32) -> Result<U, String> {
let se = self.read_se()?;
if se < min || se > max {
Err(format!(
"Value out of bounds, expected between {}-{}, got {}",
min, max, se
))
} else {
Ok(U::try_from(se).map_err(|_| String::from("Conversion error"))?)
}
}
/// Read little endian multi-byte integer.
pub fn read_le<U: TryFrom<u32>>(&mut self, num_bits: u8) -> Result<U, String> {
let mut t = 0;
for i in 0..num_bits {
let byte = self.read_bits_aligned::<u32>(8)?;
t += byte << (i * 8)
}
Ok(U::try_from(t).map_err(|_| String::from("Conversion error"))?)
}
/// Return the position of this bitstream in bits.
pub fn position(&self) -> u64 {
self.position
}
fn get_byte(&mut self) -> Result<u8, GetByteError> {
let mut buf = [0u8; 1];
self.data
.read_exact(&mut buf)
.map_err(|_| GetByteError::OutOfBits)?;
Ok(buf[0])
}
fn move_to_next_byte(&mut self) -> Result<(), GetByteError> {
let mut byte = self.get_byte()?;
if self.needs_epb {
if self.prev_two_bytes == 0 && byte == 0x03 {
// We found an epb
self.num_epb += 1;
// Read another byte
byte = self.get_byte()?;
// We need another 3 bytes before another epb can happen.
self.prev_two_bytes = 0xffff;
}
self.prev_two_bytes = (self.prev_two_bytes << 8) | u16::from(byte);
}
self.num_remaining_bits_in_curr_byte = 8;
self.curr_byte = byte;
Ok(())
}
}
/// Iterator over IVF packets.
pub struct IvfIterator<'a> {
cursor: Cursor<&'a [u8]>,
}
impl<'a> IvfIterator<'a> {
pub fn new(data: &'a [u8]) -> Self {
let mut cursor = Cursor::new(data);
// Skip the IVH header entirely.
cursor.seek(std::io::SeekFrom::Start(32)).unwrap();
Self { cursor }
}
}
impl<'a> Iterator for IvfIterator<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<Self::Item> {
// Make sure we have a header.
let mut len_buf = [0u8; 4];
self.cursor.read_exact(&mut len_buf).ok()?;
let len = ((len_buf[3] as usize) << 24)
| ((len_buf[2] as usize) << 16)
| ((len_buf[1] as usize) << 8)
| (len_buf[0] as usize);
// Skip PTS.
self.cursor.seek(std::io::SeekFrom::Current(8)).ok()?;
let start = self.cursor.position() as usize;
let _ = self
.cursor
.seek(std::io::SeekFrom::Current(len as i64))
.ok()?;
let end = self.cursor.position() as usize;
Some(&self.cursor.get_ref()[start..end])
}
}
/// Helper struct for synthesizing IVF file header
pub struct IvfFileHeader {
pub magic: [u8; 4],
pub version: u16,
pub header_size: u16,
pub codec: [u8; 4],
pub width: u16,
pub height: u16,
pub framerate: u32,
pub timescale: u32,
pub frame_count: u32,
pub unused: u32,
}
impl Default for IvfFileHeader {
fn default() -> Self {
Self {
magic: Self::MAGIC,
version: 0,
header_size: 32,
codec: Self::CODEC_VP9,
width: 320,
height: 240,
framerate: 1,
timescale: 1000,
frame_count: 1,
unused: Default::default(),
}
}
}
impl IvfFileHeader {
pub const MAGIC: [u8; 4] = *b"DKIF";
pub const CODEC_VP8: [u8; 4] = *b"VP80";
pub const CODEC_VP9: [u8; 4] = *b"VP90";
pub const CODEC_AV1: [u8; 4] = *b"AV01";
pub fn new(codec: [u8; 4], width: u16, height: u16, framerate: u32, frame_count: u32) -> Self {
let default = Self::default();
Self {
codec,
width,
height,
framerate: framerate * default.timescale,
frame_count,
..default
}
}
}
impl IvfFileHeader {
/// Writes header into writer
pub fn writo_into(&self, writer: &mut impl std::io::Write) -> std::io::Result<()> {
writer.write_all(&self.magic)?;
writer.write_all(&self.version.to_le_bytes())?;
writer.write_all(&self.header_size.to_le_bytes())?;
writer.write_all(&self.codec)?;
writer.write_all(&self.width.to_le_bytes())?;
writer.write_all(&self.height.to_le_bytes())?;
writer.write_all(&self.framerate.to_le_bytes())?;
writer.write_all(&self.timescale.to_le_bytes())?;
writer.write_all(&self.frame_count.to_le_bytes())?;
writer.write_all(&self.unused.to_le_bytes())?;
Ok(())
}
}
/// Helper struct for synthesizing IVF frame header
pub struct IvfFrameHeader {
pub frame_size: u32,
pub timestamp: u64,
}
impl IvfFrameHeader {
/// Writes header into writer
pub fn writo_into(&self, writer: &mut impl std::io::Write) -> std::io::Result<()> {
writer.write_all(&self.frame_size.to_le_bytes())?;
writer.write_all(&self.timestamp.to_le_bytes())?;
Ok(())
}
}
/// Iterator NALUs in a bitstream.
pub struct NalIterator<'a, Nalu>(Cursor<&'a [u8]>, PhantomData<Nalu>);
impl<'a, Nalu> NalIterator<'a, Nalu> {
pub fn new(stream: &'a [u8]) -> Self {
Self(Cursor::new(stream), PhantomData)
}
}
impl<'a> Iterator for NalIterator<'a, H264Nalu<'a>> {
type Item = Cow<'a, [u8]>;
fn next(&mut self) -> Option<Self::Item> {
H264Nalu::next(&mut self.0).map(|n| n.data).ok()
}
}
impl<'a> Iterator for NalIterator<'a, H265Nalu<'a>> {
type Item = Cow<'a, [u8]>;
fn next(&mut self) -> Option<Self::Item> {
H265Nalu::next(&mut self.0).map(|n| n.data).ok()
}
}
#[derive(Debug)]
pub enum BitWriterError {
InvalidBitCount,
Io(std::io::Error),
}
impl fmt::Display for BitWriterError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
BitWriterError::InvalidBitCount => write!(f, "invalid bit count"),
BitWriterError::Io(x) => write!(f, "{}", x.to_string()),
}
}
}
impl From<std::io::Error> for BitWriterError {
fn from(err: std::io::Error) -> Self {
BitWriterError::Io(err)
}
}
pub type BitWriterResult<T> = std::result::Result<T, BitWriterError>;
pub struct BitWriter<W: Write> {
out: W,
nth_bit: u8,
curr_byte: u8,
}
impl<W: Write> BitWriter<W> {
pub fn new(writer: W) -> Self {
Self {
out: writer,
curr_byte: 0,
nth_bit: 0,
}
}
/// Writes fixed bit size integer (up to 32 bit)
pub fn write_f<T: Into<u32>>(&mut self, bits: usize, value: T) -> BitWriterResult<usize> {
let value = value.into();
if bits > 32 {
return Err(BitWriterError::InvalidBitCount);
}
let mut written = 0;
for bit in (0..bits).rev() {
let bit = (1 << bit) as u32;
self.write_bit((value & bit) == bit)?;
written += 1;
}
Ok(written)
}
/// Takes a single bit that will be outputed to [`std::io::Write`]
pub fn write_bit(&mut self, bit: bool) -> BitWriterResult<()> {
self.curr_byte |= (bit as u8) << (7u8 - self.nth_bit);
self.nth_bit += 1;
if self.nth_bit == 8 {
self.out.write_all(&[self.curr_byte])?;
self.nth_bit = 0;
self.curr_byte = 0;
}
Ok(())
}
/// Immediately outputs any cached bits to [`std::io::Write`]
pub fn flush(&mut self) -> BitWriterResult<()> {
if self.nth_bit != 0 {
self.out.write_all(&[self.curr_byte])?;
self.nth_bit = 0;
self.curr_byte = 0;
}
self.out.flush()?;
Ok(())
}
/// Returns `true` if ['Self`] hold data that wasn't written to [`std::io::Write`]
pub fn has_data_pending(&self) -> bool {
self.nth_bit != 0
}
pub(crate) fn inner(&self) -> &W {
&self.out
}
pub(crate) fn inner_mut(&mut self) -> &mut W {
&mut self.out
}
}
impl<W: Write> Drop for BitWriter<W> {
fn drop(&mut self) {
if let Err(e) = self.flush() {
log::error!("Unable to flush bits {e:?}");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ivf_file_header() {
let mut hdr = IvfFileHeader {
version: 0,
codec: IvfFileHeader::CODEC_VP9,
width: 256,
height: 256,
framerate: 30_000,
timescale: 1_000,
frame_count: 1,
..Default::default()
};
let mut buf = Vec::new();
hdr.writo_into(&mut buf).unwrap();
const EXPECTED: [u8; 32] = [
0x44, 0x4b, 0x49, 0x46, 0x00, 0x00, 0x20, 0x00, 0x56, 0x50, 0x39, 0x30, 0x00, 0x01,
0x00, 0x01, 0x30, 0x75, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
];
assert_eq!(&buf, &EXPECTED);
hdr.width = 1920;
hdr.height = 800;
hdr.framerate = 24;
hdr.timescale = 1;
hdr.frame_count = 100;
buf.clear();
hdr.writo_into(&mut buf).unwrap();
const EXPECTED2: [u8; 32] = [
0x44, 0x4b, 0x49, 0x46, 0x00, 0x00, 0x20, 0x00, 0x56, 0x50, 0x39, 0x30, 0x80, 0x07,
0x20, 0x03, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
];
assert_eq!(&buf, &EXPECTED2);
}
#[test]
fn test_ivf_frame_header() {
let mut hdr = IvfFrameHeader {
frame_size: 199249,
timestamp: 0,
};
let mut buf = Vec::new();
hdr.writo_into(&mut buf).unwrap();
const EXPECTED: [u8; 12] = [
0x51, 0x0a, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
assert_eq!(&buf, &EXPECTED);
hdr.timestamp = 1;
hdr.frame_size = 52;
buf.clear();
hdr.writo_into(&mut buf).unwrap();
const EXPECTED2: [u8; 12] = [
0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
assert_eq!(&buf, &EXPECTED2);
}
#[test]
fn test_bitwriter_f1() {
let mut buf = Vec::<u8>::new();
{
let mut writer = BitWriter::new(&mut buf);
writer.write_f(1, true).unwrap();
writer.write_f(1, false).unwrap();
writer.write_f(1, false).unwrap();
writer.write_f(1, false).unwrap();
writer.write_f(1, true).unwrap();
writer.write_f(1, true).unwrap();
writer.write_f(1, true).unwrap();
writer.write_f(1, true).unwrap();
}
assert_eq!(buf, vec![0b10001111u8]);
}
#[test]
fn test_bitwriter_f3() {
let mut buf = Vec::<u8>::new();
{
let mut writer = BitWriter::new(&mut buf);
writer.write_f(3, 0b100u8).unwrap();
writer.write_f(3, 0b101u8).unwrap();
writer.write_f(3, 0b011u8).unwrap();
}
assert_eq!(buf, vec![0b10010101u8, 0b10000000u8]);
}
#[test]
fn test_bitwriter_f4() {
let mut buf = Vec::<u8>::new();
{
let mut writer = BitWriter::new(&mut buf);
writer.write_f(4, 0b1000u8).unwrap();
writer.write_f(4, 0b1011u8).unwrap();
}
assert_eq!(buf, vec![0b10001011u8]);
}
// These tests are adapted from the chromium tests at media/video/h264_bit_reader_unitttest.cc
#[test]
fn read_stream_without_escape_and_trailing_zero_bytes() {
const RBSP: [u8; 6] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xa0];
let mut reader = BitReader::new(&RBSP, true);
assert_eq!(reader.read_bits::<u32>(1).unwrap(), 0);
assert_eq!(reader.num_bits_left(), 47);
assert!(reader.has_more_rsbp_data());
assert_eq!(reader.read_bits::<u32>(8).unwrap(), 0x02);
assert_eq!(reader.num_bits_left(), 39);
assert!(reader.has_more_rsbp_data());
assert_eq!(reader.read_bits::<u32>(31).unwrap(), 0x23456789);
assert_eq!(reader.num_bits_left(), 8);
assert!(reader.has_more_rsbp_data());
assert_eq!(reader.read_bits::<u32>(1).unwrap(), 1);
assert_eq!(reader.num_bits_left(), 7);
assert!(reader.has_more_rsbp_data());
assert_eq!(reader.read_bits::<u32>(1).unwrap(), 0);
assert_eq!(reader.num_bits_left(), 6);
assert!(!reader.has_more_rsbp_data());
}
#[test]
fn single_byte_stream() {
const RBSP: [u8; 1] = [0x18];
let mut reader = BitReader::new(&RBSP, true);
assert_eq!(reader.num_bits_left(), 8);
assert!(reader.has_more_rsbp_data());
assert_eq!(reader.read_bits::<u32>(4).unwrap(), 1);
assert!(!reader.has_more_rsbp_data());
}
#[test]
fn stop_bit_occupy_full_byte() {
const RBSP: [u8; 2] = [0xab, 0x80];
let mut reader = BitReader::new(&RBSP, true);
assert_eq!(reader.num_bits_left(), 16);
assert!(reader.has_more_rsbp_data());
assert_eq!(reader.read_bits::<u32>(8).unwrap(), 0xab);
assert_eq!(reader.num_bits_left(), 8);
assert!(!reader.has_more_rsbp_data());
}
// Check that read_ue behaves properly with input at the limits.
#[test]
fn read_ue() {
// Regular value.
let mut reader = BitReader::new(&[0b0001_1010], true);
assert_eq!(reader.read_ue::<u32>().unwrap(), 12);
assert_eq!(reader.data.position(), 1);
assert_eq!(reader.num_remaining_bits_in_curr_byte, 1);
// 0 value.
let mut reader = BitReader::new(&[0b1000_0000], true);
assert_eq!(reader.read_ue::<u32>().unwrap(), 0);
assert_eq!(reader.data.position(), 1);
assert_eq!(reader.num_remaining_bits_in_curr_byte, 7);
// No prefix stop bit.
let mut reader = BitReader::new(&[0b0000_0000], true);
reader.read_ue::<u32>().unwrap_err();
// u32 max value: 31 0-bits, 1 bit marker, 31 bits 1-bits.
let mut reader = BitReader::new(
&[
0b0000_0000,
0b0000_0000,
0b0000_0000,
0b0000_0001,
0b1111_1111,
0b1111_1111,
0b1111_1111,
0b1111_1110,
],
true,
);
assert_eq!(reader.read_ue::<u32>().unwrap(), 0xffff_fffe);
assert_eq!(reader.data.position(), 8);
assert_eq!(reader.num_remaining_bits_in_curr_byte, 1);
}
// Check that emulation prevention is being handled correctly.
#[test]
fn skip_epb_when_enabled() {
let mut reader = BitReader::new(&[0x00, 0x00, 0x03, 0x01], false);
assert_eq!(reader.read_bits::<u32>(8).unwrap(), 0x00);
assert_eq!(reader.read_bits::<u32>(8).unwrap(), 0x00);
assert_eq!(reader.read_bits::<u32>(8).unwrap(), 0x03);
assert_eq!(reader.read_bits::<u32>(8).unwrap(), 0x01);
let mut reader = BitReader::new(&[0x00, 0x00, 0x03, 0x01], true);
assert_eq!(reader.read_bits::<u32>(8).unwrap(), 0x00);
assert_eq!(reader.read_bits::<u32>(8).unwrap(), 0x00);
assert_eq!(reader.read_bits::<u32>(8).unwrap(), 0x01);
}
#[test]
fn read_signed_bits() {
let mut reader = BitReader::new(&[0b1111_0000], false);
assert_eq!(reader.read_bits_signed::<i32>(4).unwrap(), -1);
}
}
+17
View File
@@ -0,0 +1,17 @@
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Parsers for various kinds of encoded streams.
//!
//! This module does not provide any actual decoding tools - that's the job of the
//! [crate::decoder] module. However the parsers of this module are heavily used in order to
//! implement stateless decoding.
//!
//! There shall be no dependencies from other modules of this crate to this module, so that it
//! can be turned into a crate of its own if needed in the future.
pub mod av1;
pub mod h264;
pub mod h265;
pub mod vp9;
@@ -0,0 +1,9 @@
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
mod helpers;
pub mod parser;
pub mod reader;
pub mod synthesizer;
pub mod writer;
@@ -0,0 +1,186 @@
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::codec::av1::parser::NUM_REF_FRAMES;
const DIV_LUT: [i32; 257] = [
16384, 16320, 16257, 16194, 16132, 16070, 16009, 15948, 15888, 15828, 15768, 15709, 15650,
15592, 15534, 15477, 15420, 15364, 15308, 15252, 15197, 15142, 15087, 15033, 14980, 14926,
14873, 14821, 14769, 14717, 14665, 14614, 14564, 14513, 14463, 14413, 14364, 14315, 14266,
14218, 14170, 14122, 14075, 14028, 13981, 13935, 13888, 13843, 13797, 13752, 13707, 13662,
13618, 13574, 13530, 13487, 13443, 13400, 13358, 13315, 13273, 13231, 13190, 13148, 13107,
13066, 13026, 12985, 12945, 12906, 12866, 12827, 12788, 12749, 12710, 12672, 12633, 12596,
12558, 12520, 12483, 12446, 12409, 12373, 12336, 12300, 12264, 12228, 12193, 12157, 12122,
12087, 12053, 12018, 11984, 11950, 11916, 11882, 11848, 11815, 11782, 11749, 11716, 11683,
11651, 11619, 11586, 11555, 11523, 11491, 11460, 11429, 11398, 11367, 11336, 11305, 11275,
11245, 11215, 11185, 11155, 11125, 11096, 11067, 11038, 11009, 10980, 10951, 10923, 10894,
10866, 10838, 10810, 10782, 10755, 10727, 10700, 10673, 10645, 10618, 10592, 10565, 10538,
10512, 10486, 10460, 10434, 10408, 10382, 10356, 10331, 10305, 10280, 10255, 10230, 10205,
10180, 10156, 10131, 10107, 10082, 10058, 10034, 10010, 9986, 9963, 9939, 9916, 9892, 9869,
9846, 9823, 9800, 9777, 9754, 9732, 9709, 9687, 9664, 9642, 9620, 9598, 9576, 9554, 9533, 9511,
9489, 9468, 9447, 9425, 9404, 9383, 9362, 9341, 9321, 9300, 9279, 9259, 9239, 9218, 9198, 9178,
9158, 9138, 9118, 9098, 9079, 9059, 9039, 9020, 9001, 8981, 8962, 8943, 8924, 8905, 8886, 8867,
8849, 8830, 8812, 8793, 8775, 8756, 8738, 8720, 8702, 8684, 8666, 8648, 8630, 8613, 8595, 8577,
8560, 8542, 8525, 8508, 8490, 8473, 8456, 8439, 8422, 8405, 8389, 8372, 8355, 8339, 8322, 8306,
8289, 8273, 8257, 8240, 8224, 8208, 8192,
];
const DIV_LUT_BITS: u32 = 8;
const DIV_LUT_PREC_BITS: u32 = 14;
/// Implements FloorLog2(x), which is defined to be the floor of the base 2
/// logarithm of the input x.
///
/// The input x will always be an integer, and will always be greater than or equal to 1.
/// This function extracts the location of the most significant bit in x.
pub fn floor_log2(mut x: u32) -> u32 {
assert!(x > 0);
let mut s = 0;
while x != 0 {
x >>= 1;
s += 1;
}
s - 1
}
/// Implements 5.9.3. Get relative distance function
pub fn get_relative_dist(enable_order_hint: bool, order_hint_bits: i32, a: i32, b: i32) -> i32 {
if !enable_order_hint {
0
} else {
let diff = a - b;
let m = 1 << (order_hint_bits - 1);
(diff & (m - 1)) - (diff & m)
}
}
/// Implements find_latest_backward from section 7.8.
pub fn find_latest_backward(
shifted_order_hints: &[i32; NUM_REF_FRAMES],
used_frame: &[bool; NUM_REF_FRAMES],
cur_frame_hint: i32,
latest_order_hint: &mut i32,
) -> i32 {
let mut _ref = -1;
for i in 0..NUM_REF_FRAMES {
let hint = shifted_order_hints[i];
if !used_frame[i] && hint >= cur_frame_hint && (_ref < 0 || hint >= *latest_order_hint) {
_ref = i as i32;
*latest_order_hint = hint;
}
}
_ref
}
/// Implements find_earliest_backward from section 7.8.
pub fn find_earliest_backward(
shifted_order_hints: &[i32; NUM_REF_FRAMES],
used_frame: &[bool; NUM_REF_FRAMES],
cur_frame_hint: i32,
earliest_order_hint: &mut i32,
) -> i32 {
let mut _ref = -1;
for i in 0..NUM_REF_FRAMES {
let hint = shifted_order_hints[i];
if !used_frame[i] && hint >= cur_frame_hint && (_ref < 0 || hint < *earliest_order_hint) {
_ref = i as i32;
*earliest_order_hint = hint;
}
}
_ref
}
/// Implements find_latest_forward from section 7.8.
pub fn find_latest_forward(
shifted_order_hints: &[i32; NUM_REF_FRAMES],
used_frame: &[bool; NUM_REF_FRAMES],
cur_frame_hint: i32,
latest_order_hint: &mut i32,
) -> i32 {
let mut _ref = -1;
for i in 0..NUM_REF_FRAMES {
let hint = shifted_order_hints[i];
if !used_frame[i] && hint < cur_frame_hint && (_ref < 0 || hint >= *latest_order_hint) {
_ref = i as i32;
*latest_order_hint = hint;
}
}
_ref
}
pub fn tile_log2(blk_size: u32, target: u32) -> u32 {
let mut k = 0;
while (blk_size << k) < target {
k += 1;
}
k
}
pub fn clip3(x: i32, y: i32, z: i32) -> i32 {
if z < x {
x
} else if z > y {
y
} else {
z
}
}
/// 5.9.29
pub fn inverse_recenter(r: i32, v: i32) -> i32 {
if v > 2 * r {
v
} else if v & 1 != 0 {
r - ((v + 1) >> 1)
} else {
r + (v >> 1)
}
}
/// Implements Round2. See 4.7: mathematical functions.
pub fn round2(x: u32, n: u32) -> u32 {
(x + 2u32.pow(n - 1)) / 2u32.pow(n)
}
/// Implements Round2Signed. See 4.7: mathematical functions.
pub fn round2signed(x: i32, n: u32) -> Result<i32, String> {
if x >= 0 {
i32::try_from(round2(x as u32, n)).map_err(|e| e.to_string())
} else {
let x = x as i64;
let val = i32::try_from(round2(-x as u32, n)).map_err(|e| e.to_string())?;
Ok(-val)
}
}
/// Implements 7.11.3.7. Resolve divisor process
pub fn resolve_divisor(d: i32) -> Result<(u32, i32), String> {
let abs_d = u32::try_from(d.abs()).unwrap(); // abs cannot return a negative
let n = floor_log2(abs_d);
let e = abs_d - (1 << n);
let f = if n > DIV_LUT_BITS {
round2(e, n - DIV_LUT_BITS)
} else {
e << (DIV_LUT_BITS - n)
};
let div_shift = n + DIV_LUT_PREC_BITS;
let div_factor = if d < 0 {
-DIV_LUT[f as usize]
} else {
DIV_LUT[f as usize]
};
Ok((div_shift, div_factor))
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,251 @@
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::bitstream_utils::BitReader;
use crate::codec::av1::helpers;
use super::parser::AnnexBState;
pub(crate) struct Reader<'a>(pub BitReader<'a>);
impl<'a> Reader<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self(BitReader::new(data, false))
}
/// Implements uvlc(): Variable length unsigned n-bit number appearing
/// directly in the bitstream. See 4.10.3
pub fn read_uvlc(&mut self) -> Result<u32, String> {
let mut leading_zeroes = 0;
loop {
let done = self.0.read_bit()?;
if done {
break;
}
leading_zeroes += 1;
}
if leading_zeroes >= 32 {
return Ok(u32::MAX);
}
let value = self.0.read_bits::<u32>(leading_zeroes)?;
Ok(value + (1 << leading_zeroes) - 1)
}
/// Implements leb128(): Unsigned integer represented by a variable number
/// of little-endian bytes. See 4.10.5
pub fn read_leb128(&mut self) -> Result<u32, String> {
let mut value = 0u64;
for i in 0..8 {
let byte = u64::from(self.0.read_bits_aligned::<u32>(8)?);
value |= (byte & 0x7f) << (i * 7);
if byte & 0x80 == 0 {
break;
}
}
Ok(value as u32)
}
/// Implements su(n): Signed integer converted from an n bits unsigned
/// integer in the bitstream. (The unsigned integer corresponds to the
/// bottom n bits of the signed integer.). See 4.10.6
pub fn read_su(&mut self, num_bits: usize) -> Result<i32, String> {
let mut value: i32 = self
.0
.read_bits::<u32>(num_bits)?
.try_into()
.map_err(|_| String::from("Read more than 31 signed bits!"))?;
let sign_mask = 1 << (num_bits - 1);
if (value & sign_mask) != 0 {
value -= 2 * sign_mask;
}
Ok(value)
}
/// Implements ns(n): Unsigned encoded integer with maximum number of values
/// n (i.e. output in range 0..n-1). See 4.10.7
pub fn read_ns(&mut self, num_bits: usize) -> Result<u32, String> {
let w = helpers::floor_log2(num_bits as u32) + 1;
let m = (1 << w) - num_bits as u32;
let v = self.0.read_bits::<u32>(
usize::try_from(w).map_err(|_| String::from("Invalid num_bits"))? - 1,
)?;
if v < m.into() {
return Ok(v);
}
let extra_bit = self.0.read_bit()?;
Ok((v << 1) - u32::from(m) + u32::from(extra_bit))
}
/// Implements 5.9.13: Delta quantizer syntax.
pub fn read_delta_q(&mut self) -> Result<i32, String> {
let delta_coded = self.0.read_bit()?;
if delta_coded {
self.read_su(7)
} else {
Ok(0)
}
}
pub fn more_data_in_bitstream(&mut self) -> bool {
self.0.num_bits_left() > 0
}
pub(crate) fn consumed(&self, start_pos: u32) -> u32 {
(self.0.position() / 8) as u32 - start_pos
}
/// Get the length of the current OBU in AnnexB format.
pub fn current_annexb_obu_length(
&mut self,
annexb_state: &mut AnnexBState,
) -> Result<Option<usize>, String> {
if !self.more_data_in_bitstream() {
return Ok(None);
}
#[allow(clippy::comparison_chain)]
if annexb_state.temporal_unit_consumed == annexb_state.temporal_unit_size {
annexb_state.temporal_unit_size = 0;
} else if annexb_state.temporal_unit_consumed > annexb_state.temporal_unit_size {
return Err(format!(
"temporal_unit_size is {} but we consumed {} bytes",
annexb_state.temporal_unit_size, annexb_state.temporal_unit_consumed,
));
}
if annexb_state.temporal_unit_size == 0 {
annexb_state.temporal_unit_size = self.read_leb128()?;
if annexb_state.temporal_unit_size == 0 {
return Ok(None);
}
}
let start_pos = self.consumed(0);
#[allow(clippy::comparison_chain)]
if annexb_state.frame_unit_consumed == annexb_state.frame_unit_size {
annexb_state.frame_unit_size = 0;
} else if annexb_state.frame_unit_consumed > annexb_state.frame_unit_size {
return Err(format!(
"frame_unit_size is {} but we consumed {} bytes",
annexb_state.frame_unit_size, annexb_state.frame_unit_consumed,
));
}
if annexb_state.frame_unit_size == 0 {
annexb_state.frame_unit_size = self.read_leb128()?;
if annexb_state.frame_unit_size == 0 {
return Ok(None);
}
annexb_state.temporal_unit_consumed += self.consumed(start_pos);
}
let start_pos = self.consumed(0);
let obu_length = self.read_leb128()?;
let consumed = self.consumed(start_pos);
annexb_state.temporal_unit_consumed += consumed;
annexb_state.frame_unit_consumed += consumed;
Ok(Some(obu_length.try_into().unwrap()))
}
/// Implements 5.3.4.
pub fn read_trailing_bits(&mut self, mut num_bits: u64) -> Result<(), String> {
let trailing_one_bit = self.0.read_bit()?;
num_bits -= 1;
if !trailing_one_bit {
return Err("bad padding: trailing_one_bit is not set".into());
}
while num_bits > 0 {
let trailing_zero_bit = self.0.read_bit()?;
if trailing_zero_bit {
return Err("bad padding: trailing_zero_bit is set".into());
}
num_bits -= 1;
}
Ok(())
}
fn decode_subexp(&mut self, num_syms: i32) -> Result<u32, String> {
let mut i = 0;
let mut mk = 0;
let k = 3;
loop {
let b2 = if i != 0 { k + i - 1 } else { k };
let a = 1 << b2;
if num_syms <= mk + 3 * a {
let num_bits = num_syms - mk;
let subexp_final_bits = self.read_ns(num_bits as usize)?;
return Ok(subexp_final_bits);
} else {
let subexp_more_bits = self.0.read_bit()?;
if subexp_more_bits {
i += 1;
mk += a;
} else {
let num_bits = b2 as usize;
let subexp_bits = self.0.read_bits::<u32>(num_bits)?;
return Ok(subexp_bits + mk as u32);
}
}
}
}
/// Implements 5.9.27.
pub fn decode_unsigned_subexp_with_ref(&mut self, mx: i32, r: i32) -> Result<u32, String> {
let v = self.decode_subexp(mx)?;
if (r << 1) <= mx {
Ok(helpers::inverse_recenter(r, v.try_into().unwrap())
.try_into()
.unwrap())
} else {
let res = mx - 1 - helpers::inverse_recenter(mx - 1 - r, v.try_into().unwrap());
Ok(res.try_into().unwrap())
}
}
/// Implements 5.9.26.
pub fn decode_signed_subexp_with_ref(
&mut self,
low: i32,
high: i32,
r: i32,
) -> Result<i32, String> {
let x = self.decode_unsigned_subexp_with_ref(high - low, r - low)?;
Ok(i32::try_from(x).unwrap() + low)
}
/// Implements 5.3.5 Byte alignment syntax
pub fn byte_alignment(&mut self) -> Result<(), String> {
while (self.0.position() & 7) != 0 {
self.0.read_bit()?;
}
Ok(())
}
}
impl<'a> Clone for Reader<'a> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,8 @@
#!/bin/bash
# Generates the CRCs for all .av1 files in the current directory using ffmpeg.
for f in `ls *.av1`; do
ffmpeg -i $f -pix_fmt nv12 -f framehash -hash crc32 - |grep -v '^#' |awk '{print $6}' >$f.crc
ffmpeg -i $f -pix_fmt nv12 -f framehash -hash md5 - |grep -v '^#' |awk '{print $6}' >$f.md5
done
@@ -0,0 +1,260 @@
{
"profile": "AV1PROFILE_PROFILE_MAIN",
"width": 320,
"height": 240,
"frame_rate": 25,
"num_frames": 250,
"num_fragments": 250,
"md5_checksums": [
"83dab175e49c33a6e3ece5c3758d1bf6",
"cedb4e25453dba430cb6ee830c1643f3",
"ef14d142df162819eee800f3930a9e95",
"11849ccb72cfdabc8f70e33271cd916f",
"7c9856ed61566f399a1eda2243e199ca",
"9394ed10354e986ecdfa2b753b921d2b",
"573005df5ba8f982980c1f427dfb472e",
"a96d9a913b1712f37f73b6c47b577cdf",
"91fc52b076badd1e6cbddb9bca5c5cc3",
"6253dde984bc5282f01005d7ebb55fe9",
"ad88146fe374423e7c597a922049c76c",
"ca7d1ddea7269e476b43a805bd5dd50b",
"e3d1eeec3cbfa363a2222d274890be84",
"fd7902a1b7352e04dc3940a010482a67",
"cada3ee9e33c11c99f89c3f439e2723e",
"1723ee290ae8be930f2904cb5aaf9de4",
"bf48d536e70a6a0a6549e221a833ebe4",
"f13b2e454420b1e0e5ab0adbbf2ce72f",
"31c799b4bb0971b798970e23b906cd25",
"3d925424bf645a225caf64757944a6e5",
"ac55ba724f54dd068350fd2818064dd5",
"a7125b8b0dc1e464e76ee64956897d22",
"f2a00db38d83cc778fa9fbdb3f4515e3",
"c804823f3401558a0283c4e77888af20",
"662d71a06319faef70a0981b664481d2",
"ad3a221a5f1f9d1a615733155a194385",
"8b071335d6cee4b227782415b1cb8a13",
"f3d98bfac8e5083233b88386b539b790",
"739c73e71590e64db0629911039962b4",
"0944990b0ada4012cc686b06833264ef",
"77e306874ec1b0c91668f8df0953831b",
"ca376820c5248cccb221ec8cb4b0eb9e",
"86a72cec3aaf50e393880ac8d4139921",
"b892ef1ff0c169b683b35cbaa9462ee3",
"b5feafe6c294d29adb1b05138803be36",
"3c3b150801f2dc48d300380c9a932890",
"16b06dfb6e426a1b6d88e17de83f5e2c",
"b1633b1c661a645bd1b04317b30fdbfa",
"56d08d66ad8042ba295c3f86025d44cf",
"78f6697fbb3af79dbd614f495c372031",
"afb700399aacde4c8c895def06fd8594",
"e7ae993d18af5c58047196d8369eee3e",
"65d5d77181388229606972ea99f5191a",
"bd336774b22d502d8fdca07d881d737e",
"5cabd4479d94c040f86892fa41860ca9",
"9ab949d5ede2f25889a9612b39f6f158",
"bb54bb8d7782c4b2d63f201a8d338e7d",
"9bf73fc10b4bb19021fd07586a0401d1",
"a37621cda632ac5f58e03272d226e53b",
"5c3ca20aa646d72b69f6e56592fd2f0b",
"b8ff47ec622ce73e70cfc913a3f8116e",
"1d020f74b9d3adf17b3f3da6992fcb52",
"0236c4d4a9aa68ba59eeaeb6b010db26",
"78a8e16aebbe5fc3d1e6051153031a51",
"78a54c1182b8ce007a2c10d17915272d",
"c7ac4f87168bbddd01155f54c79bc132",
"e074be8ece629e08d6d93029b8d34ce2",
"edfba91c624f4cdc558c693b33165542",
"f3d005151909922bfed7a905693de7c0",
"80118fa45af8b3e486928aae49d85b94",
"e7663b97fc9b26348876d5fa64a54d5d",
"69200a2a34bd4beccf4d473b4a4976f3",
"82f6c63a1d87037c08fba3bf16ef9bf8",
"6e3033f25eb56670a3c45c12822f340c",
"6085ac2c582fd3048c240d3dc46e3455",
"b4bbf1a7ae3f57b1d8f0ed65070e010b",
"e7f0aad67af5c7719eb89e457cefe6b4",
"6d5fdc1d68e136ce9095696be59b617b",
"bf15759321de7b21ec3c1bd2cb98ca69",
"fc4d67815a94c2e18e7d8b0b7d866641",
"0d96425a800252d500fd25da7dccb94d",
"5438c9e83cc90f030ee9b1876ec28bdc",
"a88915fd11ad765299cef49c8e65e8ab",
"4995a3442fa74849f3c45888037f9b42",
"82693761e531127d2a98cdc5615cdb92",
"07b9601bebc32dc33fd806f90cee586f",
"d68df0d6b1dae0e4428a17717723dbe0",
"2851422da5bcceebf57ae37a02a1fb57",
"ae9ff5d20ea6ba6105fb5ed3e0109834",
"a0f956663545e54acbaecf684d0841ff",
"23589649e474e155583e6742fa79098c",
"9031fbd94bd7a0508b48f8081018d447",
"b72920bce007e8f0584d615529dda39f",
"0638d4c62dbf43087b4831f2e39314fe",
"7d34a23c9b78a61da7a60c520784e204",
"0a3cf1af9095a7cb2787cf79ed257c59",
"1c05ab5abe793c14ee53ea80e170d6cc",
"02d0b03fe10b1b6675b86390fe317778",
"7639df2f62c4f958a90337b19a591530",
"6080ee3519bd5dd88fbb4050459c126b",
"75ce7282839cda34f7ca6111d6353cdb",
"2214093ad2bb512a223808ed78fe3369",
"eb9bfad6ef08a5f28b531203db3fcb43",
"cf4fd68dd57df907f35cc55fe5930fb8",
"0aaf799fc8509acd1168cd667f789c9c",
"e36ebe2332eb5a9a0ddd03469493d3cb",
"49ec5c818b0fcc02fbe3f5636417a2da",
"8430b81f1a90c325d116cec424d3c32c",
"c67e1e1f7fec9f9ab688f1d54cf2f4d4",
"fd98fabb3db5dc2d841dc50af7395130",
"8af0c8f486057cbae86939fa248c8238",
"e1888b1f32d2991682261f28f860cb27",
"9c0455744df622675a6d5232d790f5b1",
"acc73e6665f54bdaa98acead49be5f65",
"5c6e6167e6c05411a22c348a2c807fc5",
"89d09cb95dc2e212efed266b4b687b35",
"9d35702693b699ac0ccb4bffc559e2e8",
"afbeb04e4e00a1b0a4407b4a34815a63",
"6f3515d4331d83abfd1dbdfb2de78189",
"be6cb0da9149ad5d68269d0ed657cd38",
"9e9f498bc71df3097caa266089a77fba",
"40d9198beea17a254ee6b74544317a7a",
"e2f04358338ae115635e621cf229df56",
"5da22d2d7c5c304fc6198f809206de1d",
"4951c1ad5ea945a098c6a4d7fad163fa",
"5a25300d1ef28b60f9e137b47b2e4364",
"5954dd49f459ea4c0984dba30076db89",
"820a838b57e43271a55f2f17c3e0676f",
"b486477fe004ed8ee213afe8ecdb35c6",
"0715a96ada76fe2d713cdfc2b9abf11d",
"c30544fc39df1c7876043115ceed355f",
"1f7ce18debf0e339db20c68c1f0bf8b8",
"e10229f1f8a95d2e59a086e5d6ac4faa",
"8ad6c73b38d32fd1712a1fd67750d364",
"30fdf5b06ae3fd2c0df9e631ea1e1048",
"0026b1e7db5e25651ecaaf19685b2dc2",
"0b53f3f2ca17d00138b4678580d63ab6",
"cb08de2030a42826ef573082188d3614",
"e14242c776b3a32da936d1f6484e29ba",
"41a43d3ee9021bcda09efa1b218cc65a",
"9b884d243e8b330edb8f569e416960f1",
"5737c4f611cdc9871df094ba51a6a6d5",
"9d47768c1a15c0b20d28545ad5e6ca53",
"8c981af4e6c3432a330b6d8e330bc03a",
"195910783821f51cb3dad54f651c1a75",
"659833dc03dc5f929eb140e188f2b1f6",
"f2f30eaa72c5a093d67cebfda15fac73",
"3c521e3025340933cf826027a994114b",
"469b72a8db2ac77c70a792c8035b0288",
"e2cf1d7930d44341c5c363b0ceaa6c84",
"469d4283cca04e78ac2cb9407531db67",
"02416a6c77174796ecbd61adab5177b9",
"f89077b3dfb54119157e04dff24b79bd",
"2d1a8fc14b649e343288b5972abe1fdf",
"3d059186dab49cd8d13584dc365862db",
"765ef42f457dee16234df6992ec74cd2",
"957589c357d82631477177e6250c713c",
"2473af8395d08e9396216c082f854d6f",
"6a117d16f8e8ffe87aebec05844d6a0a",
"8e467cfcbd66ed80fca648248af56b3f",
"71e39c4304e09688750aa85e9d040f4a",
"3071ee85d87ef8029619d4777c799c2b",
"cc86f09e3f228001e806fd3afe5e1271",
"3828673cf67828240005f7bf9fe37412",
"12700b04cc22fba7688ae1709e48b886",
"a1045aa13b0007d23f22ad8e83c4bdb0",
"a12e6c2d2805336c407417b7503670fe",
"11dd1907c6785eccbedeba6a5708c516",
"3fb8be1276b83db1ccd8206cc69ec736",
"980ce03ae4e6f2eceb314f37290e0dd2",
"c39395d9b6108f1bd4ab1f004c44a5f0",
"a7244c89efd3b00611c92ebaf98793e8",
"fbede5a2957c023216e79cc31ab32946",
"bb40544a3adfe992e07a8eb3e5bf6966",
"0f71f57100699e95fff51dabde86cb42",
"6ba085efefacf385728423ca99bd4629",
"4df2d956234cb2129ebefdd90f28185a",
"d0a0d4263ef4f32d59b004752a5b94af",
"a9deed8bf550f4ee370730e1d993d4dc",
"eca65ba94930bf78854ce991c05e3d9c",
"4eef9fa5bca7ee74e97a0ac6e12e5f53",
"84e97d688ba1922f9ed71d72cea67259",
"407dc974664f93be14c53a2b291cc422",
"31eb9e589470cfbe4e72e1c441d56e3f",
"ea4f696ad2ae150a1f0e622874f67fca",
"7552fea18e053b4771c0490015488291",
"6863cd478581244fff72f6a0021a9fb8",
"9118deacdd49db27c317eeec868cd870",
"50290c87c5ee1c558bd304fc0f4a15ea",
"5aef6d78538151648450fd391c9deaec",
"a7e3feb23e01e0b4556446e01b2d81eb",
"6918645d2b0bd511247c6ed372bf213e",
"2f9661df4d114ab9f3a98a255a8177f2",
"baaea6f9f05a7a2bc10721f1afe6f57b",
"366ab99c236a8e84e658bad69d82a1ba",
"f950a6508c051ef32e4c04be56fcc719",
"9e586c73c3b9daebc608600efbee33b0",
"e9fb7690077e8cbac15d5564130a8d7c",
"ef3c8d2b5376753c26a795bf403cdcc1",
"be46eec37b67dfc8077705ce89588a7c",
"dba1b08cfeddbcf4e8d79621ee343bd4",
"a4a612bcb6c33c7799433c998c5f12d1",
"5f45fa214bc860825510d034f1ff26de",
"1766dd6fe6080c408a4baca135609f1c",
"34dc16d50d7afeb93a874563f1f5e4fd",
"0a5314f217972a9271c4792921f61153",
"7c228f1f5ccc6f7307526531f79a307d",
"188544479f5a44b6a90b6be62e33611a",
"9cb9be5574c6a2c06862a3ed605c68b5",
"845b033cc82472d8e647f2a3bb3eb653",
"2c6b66c68107fd13395a1ed5f0639355",
"0436424e24e562c52ab7e7063b04c129",
"21248608746bb252faa347028f5343d7",
"81607476ae05bbf7386dea9d8eac352b",
"fadc83171e49e779ebe5d81769049cef",
"e768c96238a8cf14caf8d03420981265",
"c57aa4d8e2f7c9101868246de4de1c13",
"b405a01f8eb1d4ffade0e49f593e5a8f",
"76f02bab5b932bf4bcf4ce06bdf0e42c",
"51b09ce7117e15cf654ad76e13263f0c",
"45f650b845e31b87b522574cc7afde80",
"858605e70124ef7130742b713b079ad9",
"5d955329a007742ecab7153d2e69b262",
"c4139450cb6ffcac62b764d62083bc31",
"8b13de43cffa8d12dd17b8e749a375b2",
"b000fbdb48be876302bc336aeb6deba1",
"88e2b7abbc33f70231889113582f8f8c",
"d9f31d4cfeee31da719b3567d5d11a19",
"7c2a6cc9be9ce8cb96b5a1616909941b",
"954c2be78496f0235ab137a98a5ae11a",
"9efd13fe15e8900f67a6c76103c9ce78",
"b04aedbee4993ff955ebba1dcb8a04e1",
"83b045a2a43445f97d82c1faaf33b332",
"1cf416bf7bf4a2124932d4b1bb0aca94",
"ac17d329b587b065512269fa3def8279",
"fcf2b7ab8b7d53741c737f75c3587a6f",
"0aaaaca0aa165a6786e2161043aa1e72",
"8d19a0848fad7b32a15522cc0b24b4d1",
"be0a1be3f90ff47b80be31be021c28f1",
"c9e423c59186d93100c56ad8f14c0fb4",
"052dbb80693750c716acce06f3ad7266",
"6a2c8002df49d3832b7e1abd70af542f",
"92bec3aa91edfd2cc5dee7d1f66ac189",
"579d29110e7a7a71152aedb9a462c79f",
"b0bb435df9dd211dd5da3c547c8887b8",
"fb7a3cf05afd06668287f5dcc9c20a55",
"bbd27b9b555f361478f9516e839077a5",
"a74dc95fe982709b25434bcc53a91973",
"27f82418568440caea8df7bcdf56eae7",
"d5ff54f75c8fa6d60428816945ee5e89",
"4b6677c4cc9866f477ecda5618411c8d",
"d56046f25113a82b090bf9a91291587c",
"cb56aa0f4343c7da821c04e55e8af21a",
"c633ea8a4c20ea4d70c8c241d0293e9b",
"dd2b157ce8a010a61908170ecb3e31b0",
"3c3509c92b03a702ac1f4a28b5abd3d5",
"d1400e54b8b61241cabad5291d6e3a43",
"c29470cd6afb0aee4ae4a8d0622a468d",
"e35764f7bd48746d37478b2d6f3f2755",
"3dcf1fd38fbcc1d45f98574b17ac5710"
]
}
@@ -0,0 +1,250 @@
6aea6152
cb4a90b3
fd83e35f
074bd081
216fcc04
c73ca1e4
fbfb2a30
cd587935
e8bc2912
051517b7
f3e9831d
812e7bba
3e2054e6
7446385e
8b75d043
f930d9a7
7bf6b591
253c5389
11a25f1f
c5101d08
ee1c0aae
ce055a9f
0ed4a046
aa0a72c3
87f7a598
aa3a422e
0ee0e533
5ce3d683
cc7e88e5
3e1c7774
bd6708d3
0ba6a0ee
ac00d2da
f1222e65
3c6aadc8
a28e7327
fd2d0d8c
e0a791bf
90a33d7e
63ae28ce
26230e31
215e9021
6ca1295b
01604116
15f8d3c9
6f28a1d4
8d54633d
08cccd41
0b082923
0aaae5a1
98310e98
33f12578
9fd072e0
36a1220e
a278c894
828dcf3f
3573505c
861874b7
dd78b1fd
310ca9a0
74d670f3
93c48c50
814991b1
fca836a7
9299e121
a8d3b267
661a1c05
50038462
bc866fe1
46e2d826
7035f78c
00c2ed17
6b64a0c6
315f4761
df4c3ef7
7ad4996a
3185972f
19af5f17
01c4e3a3
3728bd7e
16adc384
5798878c
d0ac34ee
56f76f8a
2b6f3f47
4a5ec1a9
75f0027c
7a1dd1f6
50c0f14f
978d00e7
a1c430b8
7c67432a
4615b4cb
c53c7308
876d9f98
b0015dd1
73288a1e
d843f40a
4127f309
3adbe013
9e84104c
9f98680d
380a36e2
2202f414
ca931e02
182b20e1
815a6be2
59cc5d53
0a05d420
d53b5f2d
98c613aa
e4de4fc1
2b8f6d29
48ce9586
d6e822dd
c8674577
75dfa0e9
7ad6ed39
f7725767
a44e0f11
bcd743cd
1ccf1835
8ceb6153
0a43af09
79bab263
855dd65a
a37d34fe
be063b4d
aec5df0c
7b12399b
c5ad7294
2f609050
8bb2dcba
c19aa763
700eb99b
11bf5174
e74d5eda
f54e54ef
0173e328
162372d6
3c8f18a3
ce8579c7
91bdbf86
745edb27
df783664
e7462cfd
30d8d761
f15474e6
4c5bb874
27c36dd2
c40981c0
1cc4b7c0
ebd96a40
a5f4d31e
cd1a957a
0b0d8e1d
c5d30b99
d7d6b75e
0ea512b8
dd12fced
29eab68d
e4bb47d1
e3060770
0483e6ca
296b084f
d11abff6
c7589394
b187d62e
2e217b3c
b516603b
287c8b71
2f24a994
7ea617d0
7ba0024e
7067ae3e
95650cbd
7b2e7deb
d1b6da6d
76f22a5a
9ce6fac7
1e5cf6c7
bbe20b47
2f2cc450
3f0297f3
a2084503
06487daa
5d06ae48
02cbba1b
b386b4f4
47623370
1acb6e9d
5b55fd00
70d43ae3
8a0703c0
4d62a79b
e358a27d
b89fcec0
59508160
f2c9f57d
2b57ec09
9427c163
91169e76
eda7cda1
289c7bc0
821dc9d5
b003d6ce
3d8dbc53
d6c3324a
76a7b9c9
a4d4b21b
51ce1f2c
49cc8861
b4fc2309
c435862e
3bceba0f
7ee0c219
cb04facd
b2fad23b
2b8cf4f1
fcdcbad2
f3c038c6
71e639e9
282325bf
52c64dc8
9ebe8705
f8679dc3
906116a2
9f740960
3cad2af8
c7765bb4
c8f38650
1e5d7d36
3374dc4c
e83476e5
6a9c0d00
08b8cb58
bd74ea9e
399bce4c
1577335d
cdbbea48
d1373c35
cc62212a
8a5e59e2
75f5f297
8c5f3776
fce09105
92b2d17e
416e8530
968d1450
d20688a8
@@ -0,0 +1,250 @@
ce5386b1beaa89f73a668d3720717fb4
8f5d21bf3d78d9c242609bdd8df6cfd6
7d75a95c82171c8cf4b09d694d4cbef9
20d4bebdc51702ee7f40e187747696c7
9c55dc575db2a189c5576537afca2c54
68a15a8cdbc0aeaf10dff65db4d1a810
dfd03584b10852eb16901d52578d8f8e
a9a0cffed57bba344c9366ce91408e26
9fdf7ac0fc07f2391da56462faf3e2e0
b83d2da8f82e1a06d26bbb514b471f4b
17c92a58fb68d0b3b02cb5b9105dbb37
1e5bd3c05fb67f2aefc01611ed43d117
eb287667a027f3e5325bfb27652021a6
67e388816af98b99e4b9134516205c52
35cba13cd8a6f23d6dfd95803af0886b
45966e4a1b83dce6a2b0c259fea10cae
416a95236e46a7bb0f11cf1147631979
f23ab59d3c4efbb8eb2d80d67a2e5136
ab7bce374a3468c03483418ccffb52de
bdaaf22c6c4e58d393d7c051d4d0ae8a
6f7de2de3e8d79c726f0f2c2fe52ee58
d2fc7067797bbffe3765a238ff803b85
769dbc781b43759287da0b2888e8338b
2abd608af57978290e24c4cd62e4904f
4fb54d7b0a71a4d3ca95b56d7b3d9a81
262ba91321a0edac67c01e6ddcc2e4f7
4c9c2d967fb853ecdc6042f223a7d138
f21ccd6a4eb5f561b0a0eff9cb1956ec
3c206ed277864249e7eaade75e71c815
7167fed00621472662b19a0215dd00c8
76e6f142e710ff5d7f6797d3fc1b8dfd
4d814bb1fec0a2bca9d92140d5734ae0
65b576bf469b182c8954591fd15dd21c
abb7e8f11617aa0c9bf6870e537b008d
02b86ea6b2f8699097d6899ab1f073c4
610786c84b9fc118679afb09404c15ae
7feb8fae65767f848ebbefa730f15d68
12677768a4266571e85b750b51ef9fc8
5b6541b178f6a885d0439b381e501967
352cc90ecc1f3e26c19970f106c475fa
9c5215a9fce00f0ec30615649171f8e4
27e593a4b30c0b64939c62e5e0488e5a
d8a5fd54958527ecefc642f6730e4164
754dfeb4337108e534ba70bab429777a
821ad11c37ffda3ad5438b57a9523e9f
add12a889a5e08a5358e2e0e086fe522
fa100278f49cbb7f9c9d7dc5e12fc0e1
ef46a151df454084d0f0bc644adeb47d
c02f8023a4be8a3249bcf6e1b223dffa
41a11da85806e85fa86a9de41e1cd93f
e792d89b5df357d1b03b1812a762832c
90f72aead8124bc6ce51495b4b99da75
af25c00b1990b778b50f529ae81efa30
900f66f0e222a8d253153711a23a5ee9
0850cad30d59e1b5e5e2943e6b916d0a
03ca961e9876ee96b14711c44a9f2a69
fa6bf9c30dd8618fdab499e9dd217582
4e09e08e1f99bf210d4757281ab4c836
dbf2bc5355a6846a028a7b3140fdef34
b38e1973cabab58f8137cf66fc7fa3aa
0ee4998cda155ca2823f5c9336a62cc2
4f9918b4782a59a0361619389bd22d54
c5c82a59505a9888b95e012a068b3254
5eabc71dca6f57407ba306db416c5031
41b8b1155bf6144aeb822acbff051e1a
12e7d40ccd4298400cf3b5577370bff8
2f22b7979183c9c3b26838c7e84a49a8
68af2e17a487001d9374f6b38ed950af
3a55bc482844ecc366e10544c38cc890
6a08d19bbecb554837c9922b9818455a
b2f95f48c1214a5dfb8f4528afcbf901
c3c4fc391a1a040c71cb65a8c654a5b1
86786c55d138bb9a4a4165522cd15253
b329ece66a9dab6bebab9e3687629e7c
7d6d818e7fe9a9b335c72ee6aedf9980
317956c14cb119eda584de03dbf089cc
d095660e8dd16899be9e916e0ff7946c
12571880bd56bed1bbbd034094fabbaa
7f702375e8c43036e36ddc07b543afb2
e254d4c9854b28db694fb5477144551e
24a4b250bc0461ad4894334838fbb645
16b3dca0bfcad463498a62bf8a3f06a8
77dbe27a373d87a6fc1012caa166a0a5
1b304ea04baf90e1bf11d6296f64f933
42c94efb8ab8ceb6ff98497ef0981c66
1074398b6c210e833b4135c7d2caeb91
3bce8712a7ba7b037a2cc85e4f72aea4
42c1bc84350e82cbead374b41732d8d5
76f8fc8e26d1b64e8afb8f263ec3f795
ef9f8b042eb888f5408ccb500c9a157f
d46505a331887bf49e157f8de4c17a98
0be7df49394337bb277b63a6b9147520
deb977415048b0b7fa1fd02878c1b10e
a0395e32b4cc279f8545b791cee5d394
c7dcd8741840799886a1f3b5208c0b36
02b1297f58b9e699f232b9ed7e8bbcd0
48852b209e405d7aaab50c5810448579
d8a5598b2e7183d72cd3a81e54f301ce
dfe2e4af7518f0d1f04fd825069d575b
e1a70679c95173e6a1504b7c52dd0d3c
16ae515dfb1268052bfe330a1cbbbffa
f17c090f2140cfd3060558ceefe4d1bd
58b1ba1fbbd76670c9218a846a8f6baf
f543d8380c2d095f9b62d4ffaaee7467
1b16d3e1d688a5a905ff22680ad803cb
12e16d58422d5a5899379f11c6ca5b9a
f1986c3e4a4545356656f76c27db0ae7
324a3d45f631aed6c758888254ae07ad
49532c06798f685e2a41dc683b081f79
95934c5a6e2cc8de08c8b8342936cf38
67d89790392f18908c24cc36bbe50362
c4f963cd490f9241f4959cca6a6d35ce
b21db693ba10898ec3205cb0cb343a02
72e63227e74f2473a44da365fa9bc02f
220d247b6c0f9512256dfcdf95582148
86c290279ffa61ca615f1f8f8877e807
6651d56c548fd4c5e8b2c86354c28ca3
d863321ec53cd582d34a8ee5e88a5576
f262bda775cf67026d8d226620c9d4aa
15816d939647890021ac31aa1c3b2f75
229b295a4bc44d8522580dd4383812d0
833afe87641e34e60f2d127f44a7cbeb
62cc76ec3a853878ebfff04ebbc8addc
40fca93e2d6a976de641cdbfb56257b5
65d654e9145527f8fcd24608d64f68f1
1b2b0c68b6d9305f46642109b92a234b
85aa859b29f2caeb60343cf53718e451
3aa590c6c8a4dee128f1e7fcb2b82a2a
699cb26687934746b722e2dfbc1d2c82
9cc9b7f51cbdbaf15bc8eec9c1a1cf7c
b1be900fe6a6eb21a563754420c5f38e
47976686aff65b42661ae0fb2685f462
6b3f96a1be97c4c2a371d5935672096a
ac9b60c754c95ccbaf56f68544712dd6
7dab91b68788d2ed61cd40d3073091a8
35b367c591e71f11ddcd9dcc75be79e7
d3321d793265630e81e6d52b20e58f44
9d97047182efd2ba0594a569f6f2ce04
14c772a342a28bd6785bb722e67fc767
288802e107c373c9d28eeb3f8726214e
1c9d8ff04c346a59ea4f3a349dc4b891
2c857a65df4619d06f424ddcccbd2221
10a0b3e4e7825d21fc22bcff34a3b26c
78c8a5ee6e6880a3af222bfc27de62a1
9e16c4346a69ff9127bb6c9287d98859
1f1d95fa30c9c3a58f41d9657aa0e6ec
5e8c6b3b4690da7f4175abe288d0c509
0bf315da2ec1e135963cc4c9cf73ea1a
2992fdf295438ec9f8dc7979b6d4e5ff
a3d43afab0119aa3b6e38a81d244c388
889d049dc64457637ad089e18e718a23
46016c611b9323fe8f4206bd393a6741
f555b79be75ba2a1a741f467192b693e
242d4b4c323c7c1e9648de52ec8d7c2c
383d2ee4d28919f193f28448ba28ddc6
2c7cd221bd1025547fc654ca6717553d
e16de72e05b548758832102ffb39d242
5cecf3fc2823225f0f1748a1fedadf35
4e709cb904317a43ffd94739281d32bc
b6dfd4657668ed2c1ebaff9fa92702ab
3c618cd95706cc25a0565fc0ed00e352
dca8dbc91708a94525f16c064454a2ce
fe3fbff45e30dcc3fa4f57ae78114f7f
0f7bc1eab5cf9faeaa127c0243675253
19b6fca6e3f0d039074753265436d2c5
fce950fc2ec2526c0b29b6869ff58dee
4c9c0b19668dc20a9fac84e13c590deb
8fe06eb38ee870ffd7cb9f065476ac78
515ce9eaa9a6970f809475e416325123
6d58868e827dd9dca698c5b24380fea7
072951a0e1d06aa829d716a86ae498fe
07f23add152c44f754eac930093e6b59
e98798eb29c162ddfff7ec72aa7b65dc
62f4e1ca0294a3944224fb4a4280ed56
28fc910d3b7a926f63e7bfaf18878cc1
40065fe5c0e32a16622988bc1485a038
b2623ec75309e28c6c0a4b179e4acd6d
3ea6b991014bde2185be7a6c92745c28
5fde25c7fe79a9c0e2b86eb1878de02c
5bf42dc3006f4c601d01ee4820e44d38
44500b37f18b2eb499894bc1ff1efdcc
f5bfe3352c4281083083e51ef1fe0a72
abf9c6ba639f68d9b8f8c0d9d97a19d3
75f6dc9ccbc6a1381785bf9440a95744
4db0fd27cac20f59718fe00c5dc13202
9e0d611017698f2d1716f8d9d81a8bf7
13c7015f10b37b3dbe1618cde5edc2ee
70e2a0182471290a46961a0d62611053
47ba942fc3396104b3731ea40eb97339
bf10238458969233c0062b09a68f22a7
115b7375a9aa156b8203dd88acdc5672
1b39aa22702c993aa1e7606e9afad9c5
c85e67c08e02538f151c6276662ba69b
89dcf41c5adb944bcd46ca3f67666a75
90fd81f9b572ede873313f4faa58da4b
00badd56e7d5b2dddb10ef7de05be515
a13c3404ac033c9fce8a6435f2a4a416
af83e8f7af9785c96ff61c354417aab4
5176b27945cc7686520b1425144dc4d3
e5f248be2c0e83ad0a6bfd2eff759f6e
cd693cce87898dafa5c68dbaeeb151fa
2a47245fc3da1229e3dc288f216c4e20
c6f8e88fbf5566000f8f86b0d4bf9020
3e88c5f7b571a292c998cac33b41215b
2a2e8271ecba2cec040ef343317e2ae9
8e693282602bde3452061affde3ce514
992acab07cf4d6aa5b95b4c4cc33cf0c
6db1ad3494be9c3c9758b00d956bd2cb
824d130a16d7bc2c6d6b26c31c2a0e05
d286bd8f5cdfd65b695815ff68bb191f
4c5bbeecda37bf4fb6dd79f266aac3d6
c4f64e991e13dfc3913246f730c31e87
459d8fd53975479aa977c61dc3ef1b9e
ece59558539b52684433b54bf2c7e214
4362122d7ed40dd5ad0886cdf901539e
66ffe4fb31a5e4fa9462a3aa57224476
86586a04e75ebde21903f407e6037e50
93b2e3af91cb24ffafa6418c76cc1ce7
41ca4598aac764fa944d8f578b2ab5ab
6ef7ce05836d63520925817c220a5274
c4e902503f3d5602ba2454907d7809f7
8f3cb447682bebd3a230740e11d35dff
4340ec4c3e62118b2749264393fc78ee
8178fbeab83126f2111ab74c23d241c9
db298bcb2a999b7ac2037991ef078d32
f586895d6dca606e2d62dfdc92cc2021
b5aaf4b91dd7b6cda3a2635c603c4500
0f024b59afa957b59f5ebb9b63ff1b1a
41acc378b08b5d9b7da2f644b79aebdf
cd173062829e6e86eb094b8d40d16579
ef55ddf0fa94dcb4f275033bb55e8466
48a3efa1f9d8291d99ce434fc5474fb6
731a649c764fa964d6e2ce640b34c25a
5f9ac7b1dca56c8dab9ce1f92341094b
e9b0ee7355299aaf55cce1e240050125
8a285fc7c728cdfa1f4b651c64eab7e5
05823b86e463ad514a4cd7eacd9c01bb
0ce34318119eb0e8921b5e798789bea0
e46f4c793b42136b1eefbece7b867052
1331c7fcae627f290299b978299a9b48
285c5783029a7b23374e91cce19df0de
670fba4fad181308c15778f38e619646
46a3350a39c21f45bafed75d339ed101
b1d6f3230e58d2647d1aa4a5446061ed
f6eb5936ec9bb3212b14716da8329839
1ecb00343900a550ad33ba10777df23d
4b9f70244adda8e0a42f4da7341b8d84
6caafeb26793a011ebef352ef1d1aa34
723ab0797281e7ee36109b53140a324d
7a5e9be8c7c8307aa22c8b48708424a2
@@ -0,0 +1,203 @@
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::fmt;
use std::io::Write;
use crate::bitstream_utils::BitWriter;
use crate::bitstream_utils::BitWriterError;
#[derive(Debug)]
pub enum ObuWriterError {
BitWriterError(BitWriterError),
UnalignedLeb128,
}
impl fmt::Display for ObuWriterError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ObuWriterError::BitWriterError(x) => write!(f, "{}", x.to_string()),
ObuWriterError::UnalignedLeb128 => {
write!(f, "attempted to write leb128 on unaligned position")
}
}
}
}
impl From<BitWriterError> for ObuWriterError {
fn from(err: BitWriterError) -> Self {
ObuWriterError::BitWriterError(err)
}
}
pub type ObuWriterResult<T> = std::result::Result<T, ObuWriterError>;
pub struct ObuWriter<W: Write>(BitWriter<W>);
impl<W: Write> ObuWriter<W> {
pub fn new(writer: W) -> Self {
Self(BitWriter::new(writer))
}
/// Writes fixed bit size integer. Corresponds to `f(n)` in AV1 spec defined in 4.10.2.
pub fn write_f<T: Into<u32>>(&mut self, bits: usize, value: T) -> ObuWriterResult<usize> {
self.0
.write_f(bits, value)
.map_err(ObuWriterError::BitWriterError)
}
/// Writes variable length unsigned n-bit number. Corresponds to `uvlc()` in AV1 spec
/// defined in 4.10.3.
pub fn write_uvlc<T: Into<u32>>(&mut self, value: T) -> ObuWriterResult<usize> {
let value: u32 = value.into();
if value == u32::MAX {
return self.write_f(32, 0u32);
}
let value = value + 1;
let leading_zeros = (32 - value.leading_zeros()) as usize;
Ok(self.write_f(leading_zeros - 1, 0u32)? + self.write_f(leading_zeros, value)?)
}
/// Writes unsigned little-endian n-byte integer. Corresponds to `le(n)` in AV1 spec
/// defined in 4.10.4.
pub fn write_le<T: Into<u32>>(&mut self, n: usize, value: T) -> ObuWriterResult<usize> {
let value: u32 = value.into();
let mut value = value.to_le();
for _ in 0..n {
self.write_f(4, value & 0xff)?;
value >>= 8;
}
Ok(n)
}
/// Writes unsigned integer represented by a variable number of little-endian bytes.
/// Corresponds to `leb128()` in AV1 spec defined in 4.10.4.
///
/// Note: Despite the name, the AV1 4.10.4 limits the value to [`u32::MAX`] = (1 << 32) - 1.
pub fn write_leb128<T: Into<u32>>(
&mut self,
value: T,
min_bytes: usize,
) -> ObuWriterResult<usize> {
if !self.aligned() {
return Err(ObuWriterError::UnalignedLeb128);
}
let value: u32 = value.into();
let mut value: u32 = value.to_le();
let mut bytes = 0;
for _ in 0..8 {
bytes += 1;
if value >= 0x7f || bytes < min_bytes {
self.write_f(8, 0x80 | (value & 0x7f))?;
value >>= 7;
} else {
self.write_f(8, value & 0x7f)?;
break;
}
}
assert!(value < 0x7f);
Ok(bytes)
}
pub fn write_su<T: Into<i32>>(&mut self, bits: usize, value: T) -> ObuWriterResult<usize> {
let mut value: i32 = value.into();
if value < 0 {
value += 1 << bits;
}
assert!(value >= 0);
self.write_f(bits, value.unsigned_abs())
}
pub fn aligned(&self) -> bool {
!self.0.has_data_pending()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codec::av1::reader::Reader;
const TEST_VECTOR: &[u32] = &[
// some random test values
u32::MAX,
1,
2,
3,
4,
10,
20,
7312,
8832,
10123,
47457,
21390213,
u32::MIN,
u32::MAX - 1,
];
#[test]
fn test_uvlc() {
for &value in TEST_VECTOR {
let mut buf = Vec::<u8>::new();
ObuWriter::new(&mut buf).write_uvlc(value).unwrap();
if value == u32::MAX {
// force stop uvlc
buf.push(0x80);
}
let read = Reader::new(&buf).read_uvlc().unwrap();
assert_eq!(read, value, "failed testing {}", value);
}
}
#[test]
fn test_leb128() {
for &value in TEST_VECTOR {
let mut buf = Vec::<u8>::new();
ObuWriter::new(&mut buf).write_leb128(value, 0).unwrap();
let read = Reader::new(&buf).read_leb128().unwrap();
assert_eq!(read, value, "failed testing {}", value);
}
}
#[test]
fn test_su() {
let vector = TEST_VECTOR
.iter()
.map(|e| *e as i32)
.chain(TEST_VECTOR.iter().map(|e| -(*e as i32)));
for value in vector {
let bits = 32 - value.abs().leading_zeros() as usize + 1; // For sign
if bits >= 32 {
// Skip too big nubmers
continue;
}
let mut buf = Vec::<u8>::new();
ObuWriter::new(&mut buf).write_su(bits, value).unwrap();
let read = Reader::new(&buf).read_su(bits as usize).unwrap();
assert_eq!(read, value, "failed testing {}", value);
}
}
}
@@ -0,0 +1,10 @@
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
pub mod dpb;
pub mod nalu;
pub mod nalu_writer;
pub mod parser;
pub mod picture;
pub mod synthesizer;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,124 @@
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::borrow::Cow;
use std::fmt::Debug;
use std::io::Cursor;
use std::io::Seek;
use std::io::SeekFrom;
#[allow(clippy::len_without_is_empty)]
pub trait Header: Sized {
/// Parse the NALU header, returning it.
fn parse<T: AsRef<[u8]>>(cursor: &mut Cursor<T>) -> Result<Self, String>;
/// Whether this header type indicates EOS.
fn is_end(&self) -> bool;
/// The length of the header.
fn len(&self) -> usize;
}
#[derive(Debug)]
pub struct Nalu<'a, U> {
pub header: U,
/// The mapping that backs this NALU. Possibly shared with the other NALUs
/// in the Access Unit.
pub data: Cow<'a, [u8]>,
pub size: usize,
pub offset: usize,
}
impl<'a, U> Nalu<'a, U>
where
U: Debug + Header,
{
/// Find the next Annex B encoded NAL unit.
pub fn next(cursor: &mut Cursor<&'a [u8]>) -> Result<Nalu<'a, U>, String> {
let bitstream = cursor.clone().into_inner();
let pos = usize::try_from(cursor.position()).map_err(|err| err.to_string())?;
// Find the start code for this NALU
let current_nalu_offset = match Nalu::<'a, U>::find_start_code(cursor, pos) {
Some(offset) => offset,
None => return Err("No NAL found".into()),
};
let mut start_code_offset = pos + current_nalu_offset;
// If the preceding byte is 00, then we actually have a four byte SC,
// i.e. 00 00 00 01 Where the first 00 is the "zero_byte()"
if start_code_offset > 0 && cursor.get_ref()[start_code_offset - 1] == 00 {
start_code_offset -= 1;
}
// The NALU offset is its offset + 3 bytes to skip the start code.
let nalu_offset = pos + current_nalu_offset + 3;
// Set the bitstream position to the start of the current NALU
cursor.set_position(u64::try_from(nalu_offset).map_err(|err| err.to_string())?);
let hdr = U::parse(cursor)?;
// Find the start of the subsequent NALU.
let mut next_nalu_offset = match Nalu::<'a, U>::find_start_code(cursor, nalu_offset) {
Some(offset) => offset,
None => {
let cur_pos = cursor.position();
let end_pos = cursor
.seek(SeekFrom::End(0))
.map_err(|err| err.to_string())?;
let _ = cursor
.seek(SeekFrom::Start(cur_pos))
.map_err(|err| err.to_string())?;
(end_pos - cur_pos) as usize
} // Whatever data is left must be part of the current NALU
};
while next_nalu_offset > 0 && cursor.get_ref()[nalu_offset + next_nalu_offset - 1] == 00 {
// Discard trailing_zero_8bits
next_nalu_offset -= 1;
}
let nal_size = if hdr.is_end() {
// the NALU is comprised of only the header
hdr.len()
} else {
next_nalu_offset
};
Ok(Nalu {
header: hdr,
data: Cow::from(&bitstream[start_code_offset..nalu_offset + nal_size]),
size: nal_size,
offset: nalu_offset - start_code_offset,
})
}
}
impl<'a, U> Nalu<'a, U>
where
U: Debug,
{
fn find_start_code(data: &mut Cursor<&'a [u8]>, offset: usize) -> Option<usize> {
// discard all zeroes until the start code pattern is found
data.get_ref()[offset..]
.windows(3)
.position(|window| window == [0x00, 0x00, 0x01])
}
pub fn into_owned(self) -> Nalu<'static, U> {
Nalu {
header: self.header,
size: self.size,
offset: self.offset,
data: Cow::Owned(self.data.into_owned()),
}
}
}
impl<'a, U> AsRef<[u8]> for Nalu<'a, U> {
fn as_ref(&self) -> &[u8] {
&self.data[self.offset..self.offset + self.size]
}
}
@@ -0,0 +1,311 @@
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::fmt;
use std::io::Write;
use crate::bitstream_utils::BitWriter;
use crate::bitstream_utils::BitWriterError;
/// Internal wrapper over [`std::io::Write`] for possible emulation prevention
struct EmulationPrevention<W: Write> {
out: W,
prev_bytes: [Option<u8>; 2],
/// Emulation prevention enabled.
ep_enabled: bool,
}
impl<W: Write> EmulationPrevention<W> {
fn new(writer: W, ep_enabled: bool) -> Self {
Self {
out: writer,
prev_bytes: [None; 2],
ep_enabled,
}
}
fn write_byte(&mut self, curr_byte: u8) -> std::io::Result<()> {
if self.prev_bytes[1] == Some(0x00) && self.prev_bytes[0] == Some(0x00) && curr_byte <= 0x03
{
self.out.write_all(&[0x00, 0x00, 0x03, curr_byte])?;
self.prev_bytes = [None; 2];
} else {
if let Some(byte) = self.prev_bytes[1] {
self.out.write_all(&[byte])?;
}
self.prev_bytes[1] = self.prev_bytes[0];
self.prev_bytes[0] = Some(curr_byte);
}
Ok(())
}
/// Writes a H.264 NALU header.
fn write_header(&mut self, idc: u8, type_: u8) -> NaluWriterResult<()> {
self.out.write_all(&[
0x00,
0x00,
0x00,
0x01,
(idc & 0b11) << 5 | (type_ & 0b11111),
])?;
Ok(())
}
fn has_data_pending(&self) -> bool {
self.prev_bytes[0].is_some() || self.prev_bytes[1].is_some()
}
}
impl<W: Write> Write for EmulationPrevention<W> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
if !self.ep_enabled {
self.out.write_all(buf)?;
return Ok(buf.len());
}
for byte in buf {
self.write_byte(*byte)?;
}
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
if let Some(byte) = self.prev_bytes[1].take() {
self.out.write_all(&[byte])?;
}
if let Some(byte) = self.prev_bytes[0].take() {
self.out.write_all(&[byte])?;
}
self.out.flush()
}
}
impl<W: Write> Drop for EmulationPrevention<W> {
fn drop(&mut self) {
if let Err(e) = self.flush() {
log::error!("Unable to flush pending bytes {e:?}");
}
}
}
#[derive(Debug)]
pub enum NaluWriterError {
Overflow,
Io(std::io::Error),
BitWriterError(BitWriterError),
}
impl fmt::Display for NaluWriterError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
NaluWriterError::Overflow => write!(f, "value increment caused value overflow"),
NaluWriterError::Io(x) => write!(f, "{}", x.to_string()),
NaluWriterError::BitWriterError(x) => write!(f, "{}", x.to_string()),
}
}
}
impl From<std::io::Error> for NaluWriterError {
fn from(err: std::io::Error) -> Self {
NaluWriterError::Io(err)
}
}
impl From<BitWriterError> for NaluWriterError {
fn from(err: BitWriterError) -> Self {
NaluWriterError::BitWriterError(err)
}
}
pub type NaluWriterResult<T> = std::result::Result<T, NaluWriterError>;
/// A writer for H.264 bitstream. It is capable of outputing bitstream with
/// emulation-prevention.
pub struct NaluWriter<W: Write>(BitWriter<EmulationPrevention<W>>);
impl<W: Write> NaluWriter<W> {
pub fn new(writer: W, ep_enabled: bool) -> Self {
Self(BitWriter::new(EmulationPrevention::new(writer, ep_enabled)))
}
/// Writes fixed bit size integer (up to 32 bit) output with emulation
/// prevention if enabled. Corresponds to `f(n)` in H.264 spec.
pub fn write_f<T: Into<u32>>(&mut self, bits: usize, value: T) -> NaluWriterResult<usize> {
self.0
.write_f(bits, value)
.map_err(NaluWriterError::BitWriterError)
}
/// An alias to [`Self::write_f`] Corresponds to `n(n)` in H.264 spec.
pub fn write_u<T: Into<u32>>(&mut self, bits: usize, value: T) -> NaluWriterResult<usize> {
self.write_f(bits, value)
}
/// Writes a number in exponential golumb format.
pub fn write_exp_golumb(&mut self, value: u32) -> NaluWriterResult<()> {
let value = value.checked_add(1).ok_or(NaluWriterError::Overflow)?;
let bits = 32 - value.leading_zeros() as usize;
let zeros = bits - 1;
self.write_f(zeros, 0u32)?;
self.write_f(bits, value)?;
Ok(())
}
/// Writes a unsigned integer in exponential golumb format.
/// Coresponds to `ue(v)` in H.264 spec.
pub fn write_ue<T: Into<u32>>(&mut self, value: T) -> NaluWriterResult<()> {
let value = value.into();
self.write_exp_golumb(value)
}
/// Writes a signed integer in exponential golumb format.
/// Coresponds to `se(v)` in H.264 spec.
pub fn write_se<T: Into<i32>>(&mut self, value: T) -> NaluWriterResult<()> {
let value: i32 = value.into();
let abs_value: u32 = value.unsigned_abs();
if value <= 0 {
self.write_ue(2 * abs_value)
} else {
self.write_ue(2 * abs_value - 1)
}
}
/// Returns `true` if ['Self`] hold data that wasn't written to [`std::io::Write`]
pub fn has_data_pending(&self) -> bool {
self.0.has_data_pending() || self.0.inner().has_data_pending()
}
/// Writes a H.264 NALU header.
pub fn write_header(&mut self, idc: u8, _type: u8) -> NaluWriterResult<()> {
self.0.flush()?;
self.0.inner_mut().write_header(idc, _type)?;
Ok(())
}
/// Returns `true` if next bits will be aligned to 8
pub fn aligned(&self) -> bool {
!self.0.has_data_pending()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bitstream_utils::BitReader;
#[test]
fn simple_bits() {
let mut buf = Vec::<u8>::new();
{
let mut writer = NaluWriter::new(&mut buf, false);
writer.write_f(1, true).unwrap();
writer.write_f(1, false).unwrap();
writer.write_f(1, false).unwrap();
writer.write_f(1, false).unwrap();
writer.write_f(1, true).unwrap();
writer.write_f(1, true).unwrap();
writer.write_f(1, true).unwrap();
writer.write_f(1, true).unwrap();
}
assert_eq!(buf, vec![0b10001111u8]);
}
#[test]
fn simple_first_few_ue() {
fn single_ue(value: u32) -> Vec<u8> {
let mut buf = Vec::<u8>::new();
{
let mut writer = NaluWriter::new(&mut buf, false);
writer.write_ue(value).unwrap();
}
buf
}
assert_eq!(single_ue(0), vec![0b10000000u8]);
assert_eq!(single_ue(1), vec![0b01000000u8]);
assert_eq!(single_ue(2), vec![0b01100000u8]);
assert_eq!(single_ue(3), vec![0b00100000u8]);
assert_eq!(single_ue(4), vec![0b00101000u8]);
assert_eq!(single_ue(5), vec![0b00110000u8]);
assert_eq!(single_ue(6), vec![0b00111000u8]);
assert_eq!(single_ue(7), vec![0b00010000u8]);
assert_eq!(single_ue(8), vec![0b00010010u8]);
assert_eq!(single_ue(9), vec![0b00010100u8]);
}
#[test]
fn writer_reader() {
let mut buf = Vec::<u8>::new();
{
let mut writer = NaluWriter::new(&mut buf, false);
writer.write_ue(10u32).unwrap();
writer.write_se(-42).unwrap();
writer.write_se(3).unwrap();
writer.write_ue(5u32).unwrap();
}
let mut reader = BitReader::new(&buf, true);
assert_eq!(reader.read_ue::<u32>().unwrap(), 10);
assert_eq!(reader.read_se::<i32>().unwrap(), -42);
assert_eq!(reader.read_se::<i32>().unwrap(), 3);
assert_eq!(reader.read_ue::<u32>().unwrap(), 5);
let mut buf = Vec::<u8>::new();
{
let mut writer = NaluWriter::new(&mut buf, false);
writer.write_se(30).unwrap();
writer.write_ue(100u32).unwrap();
writer.write_se(-402).unwrap();
writer.write_ue(50u32).unwrap();
}
let mut reader = BitReader::new(&buf, true);
assert_eq!(reader.read_se::<i32>().unwrap(), 30);
assert_eq!(reader.read_ue::<u32>().unwrap(), 100);
assert_eq!(reader.read_se::<i32>().unwrap(), -402);
assert_eq!(reader.read_ue::<u32>().unwrap(), 50);
}
#[test]
fn writer_emulation_prevention() {
fn test(input: &[u8], bitstream: &[u8]) {
let mut buf = Vec::<u8>::new();
{
let mut writer = NaluWriter::new(&mut buf, true);
for byte in input {
writer.write_f(8, *byte).unwrap();
}
}
assert_eq!(buf, bitstream);
{
let mut reader = BitReader::new(&buf, true);
for byte in input {
assert_eq!(*byte, reader.read_bits::<u8>(8).unwrap());
}
}
}
test(&[0x00, 0x00, 0x00], &[0x00, 0x00, 0x03, 0x00]);
test(&[0x00, 0x00, 0x01], &[0x00, 0x00, 0x03, 0x01]);
test(&[0x00, 0x00, 0x02], &[0x00, 0x00, 0x03, 0x02]);
test(&[0x00, 0x00, 0x03], &[0x00, 0x00, 0x03, 0x03]);
test(&[0x00, 0x00, 0x00, 0x00], &[0x00, 0x00, 0x03, 0x00, 0x00]);
test(&[0x00, 0x00, 0x00, 0x01], &[0x00, 0x00, 0x03, 0x00, 0x01]);
test(&[0x00, 0x00, 0x00, 0x02], &[0x00, 0x00, 0x03, 0x00, 0x02]);
test(&[0x00, 0x00, 0x00, 0x03], &[0x00, 0x00, 0x03, 0x00, 0x03]);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,443 @@
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::cell::RefCell;
use std::ops::Deref;
use std::rc::Rc;
use std::rc::Weak;
use log::debug;
use crate::codec::h264::parser::MaxLongTermFrameIdx;
use crate::codec::h264::parser::RefPicMarking;
use crate::codec::h264::parser::Slice;
use crate::codec::h264::parser::SliceType;
use crate::codec::h264::parser::Sps;
use crate::Resolution;
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum Field {
#[default]
Frame,
Top,
Bottom,
}
impl Field {
/// Returns the field of opposite parity.
pub fn opposite(&self) -> Self {
match *self {
Field::Frame => Field::Frame,
Field::Top => Field::Bottom,
Field::Bottom => Field::Top,
}
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum Reference {
#[default]
None,
ShortTerm,
LongTerm,
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum IsIdr {
#[default]
No,
Yes {
idr_pic_id: u16,
},
}
/// The rank of a field, i.e. whether it is the first or second one to be parsed from the stream.
/// This is unrelated to the `Field` type, as the first field can be either `Top` or `Bottom`.
#[derive(Default, Debug)]
pub enum FieldRank {
/// Frame has a single field.
#[default]
Single,
/// Frame is interlaced, and this is the first field (with a reference to the second one).
First(Weak<RefCell<PictureData>>),
/// Frame is interlaced, and this is the second field (with a reference to the first one).
Second(Rc<RefCell<PictureData>>),
}
#[derive(Default)]
pub struct PictureData {
pub pic_order_cnt_type: u8,
pub top_field_order_cnt: i32,
pub bottom_field_order_cnt: i32,
pub pic_order_cnt: i32,
pub pic_order_cnt_msb: i32,
pub pic_order_cnt_lsb: i32,
pub delta_pic_order_cnt_bottom: i32,
pub delta_pic_order_cnt0: i32,
pub delta_pic_order_cnt1: i32,
pub pic_num: i32,
pub long_term_pic_num: u32,
pub frame_num: u32,
pub frame_num_offset: u32,
pub frame_num_wrap: i32,
pub long_term_frame_idx: u32,
pub coded_resolution: Resolution,
pub display_resolution: Resolution,
pub type_: SliceType,
pub nal_ref_idc: u8,
pub is_idr: IsIdr,
reference: Reference,
pub ref_pic_list_modification_flag_l0: i32,
pub abs_diff_pic_num_minus1: i32,
// Does memory management op 5 needs to be executed after this
// picture has finished decoding?
pub has_mmco_5: bool,
// Created by the decoding process for gaps in frame_num.
// Not for decode or output.
pub nonexisting: bool,
pub field: Field,
// Values from slice_hdr to be used during reference marking and
// memory management after finishing this picture.
pub ref_pic_marking: RefPicMarking,
field_rank: FieldRank,
pub timestamp: u64,
}
/// A `PictureData` within a `Rc<RefCell>` which field rank is guaranteed to be correct.
///
/// The field rank of `PictureData` is only final after both fields have been constructed - namely,
/// the first field can only point to the second one after the latter is available as a Rc. Methods
/// [`PictureData::into_rc`] and [`PictureData::split_frame`] take care of this, and is this only
/// producer of this type, ensuring all instances are correct.
#[derive(Default, Debug, Clone)]
pub struct RcPictureData {
pic: Rc<RefCell<PictureData>>,
}
impl Deref for RcPictureData {
type Target = Rc<RefCell<PictureData>>;
fn deref(&self) -> &Self::Target {
&self.pic
}
}
impl PictureData {
pub fn new_non_existing(frame_num: u32, timestamp: u64) -> Self {
PictureData {
frame_num,
nonexisting: true,
nal_ref_idc: 1,
field: Field::Frame,
pic_num: frame_num as i32,
reference: Reference::ShortTerm,
timestamp,
..Default::default()
}
}
/// Create a new picture from a `slice`, `sps`, and `timestamp`.
///
/// `first_field` is set if this picture is the second field of a frame.
pub fn new_from_slice(
slice: &Slice,
sps: &Sps,
timestamp: u64,
first_field: Option<&RcPictureData>,
) -> Self {
let hdr = &slice.header;
let nalu_hdr = &slice.nalu.header;
let is_idr = if nalu_hdr.idr_pic_flag {
IsIdr::Yes {
idr_pic_id: hdr.idr_pic_id,
}
} else {
IsIdr::No
};
let field = if hdr.field_pic_flag {
if hdr.bottom_field_flag {
Field::Bottom
} else {
Field::Top
}
} else {
Field::Frame
};
let reference = if nalu_hdr.ref_idc != 0 {
Reference::ShortTerm
} else {
Reference::None
};
let pic_num = if !hdr.field_pic_flag {
hdr.frame_num
} else {
2 * hdr.frame_num + 1
};
let (
pic_order_cnt_lsb,
delta_pic_order_cnt_bottom,
delta_pic_order_cnt0,
delta_pic_order_cnt1,
) = match sps.pic_order_cnt_type {
0 => (
hdr.pic_order_cnt_lsb,
hdr.delta_pic_order_cnt_bottom,
Default::default(),
Default::default(),
),
1 => (
Default::default(),
Default::default(),
hdr.delta_pic_order_cnt[0],
hdr.delta_pic_order_cnt[1],
),
_ => (
Default::default(),
Default::default(),
Default::default(),
Default::default(),
),
};
let coded_resolution = Resolution::from((sps.width(), sps.height()));
let visible_rect = sps.visible_rectangle();
// punktfunk deviation (PROVENANCE.md #6): `Sps::visible_rectangle()` returns
// the crop offset in `min` and the visible SIZE in `max` (not a corner);
// upstream's `max - min` double-counts the left/top crop and panics on a u32
// underflow for large-but-parser-valid left/top offsets.
let display_resolution = Resolution {
width: visible_rect.max.x,
height: visible_rect.max.y,
};
let mut pic = PictureData {
pic_order_cnt_type: sps.pic_order_cnt_type,
pic_order_cnt_lsb: i32::from(pic_order_cnt_lsb),
delta_pic_order_cnt_bottom,
delta_pic_order_cnt0,
delta_pic_order_cnt1,
pic_num: i32::from(pic_num),
frame_num: u32::from(hdr.frame_num),
nal_ref_idc: nalu_hdr.ref_idc,
is_idr,
reference,
field,
ref_pic_marking: hdr.dec_ref_pic_marking.clone(),
coded_resolution,
display_resolution,
timestamp,
..Default::default()
};
if let Some(first_field) = first_field {
pic.set_first_field_to(first_field);
}
pic
}
/// Whether the current picture is a reference, either ShortTerm or LongTerm.
pub fn is_ref(&self) -> bool {
!matches!(self.reference, Reference::None)
}
/// Whether this picture is a second field.
pub fn is_second_field(&self) -> bool {
matches!(self.field_rank, FieldRank::Second(..))
}
/// Returns the field rank of this picture, including a reference to its other field.
pub fn field_rank(&self) -> &FieldRank {
&self.field_rank
}
/// Returns a reference to the picture's Reference
pub fn reference(&self) -> &Reference {
&self.reference
}
/// Mark the picture as a reference picture.
pub fn set_reference(&mut self, reference: Reference, apply_to_other_field: bool) {
log::debug!("Set reference of {:#?} to {:?}", self, reference);
self.reference = reference;
if apply_to_other_field {
if let Some(other_field) = self.other_field() {
log::debug!(
"other_field: Set reference of {:#?} to {:?}",
&other_field.borrow(),
reference
);
other_field.borrow_mut().reference = reference;
}
}
}
/// Get a reference to the picture's other field, if there is any
/// and its reference is still valid.
pub fn other_field(&self) -> Option<Rc<RefCell<PictureData>>> {
match &self.field_rank {
FieldRank::Single => None,
FieldRank::First(other_field) => other_field.upgrade(),
FieldRank::Second(other_field) => Some(other_field.clone()),
}
}
/// Set this picture's second field.
fn set_second_field_to(&mut self, other_field: &Rc<RefCell<Self>>) {
self.field_rank = FieldRank::First(Rc::downgrade(other_field));
}
/// Whether the current picture is the second field of a complementary ref pair.
pub fn is_second_field_of_complementary_ref_pair(&self) -> bool {
self.is_ref()
&& matches!(self.field_rank(), FieldRank::Second(first_field) if first_field.borrow().is_ref())
}
/// Set this picture's first field.
fn set_first_field_to(&mut self, other_field: &Rc<RefCell<Self>>) {
self.field_rank = FieldRank::Second(other_field.clone());
}
pub fn pic_num_f(&self, max_pic_num: i32) -> i32 {
if !matches!(self.reference(), Reference::LongTerm) {
self.pic_num
} else {
max_pic_num
}
}
pub fn long_term_pic_num_f(&self, max_long_term_frame_idx: MaxLongTermFrameIdx) -> u32 {
if matches!(self.reference(), Reference::LongTerm) {
self.long_term_pic_num
} else {
2 * max_long_term_frame_idx.to_value_plus1()
}
}
/// Consume this picture and return a Rc'd version.
///
/// If the picture was a second field, adjust the field of the first field to point to this
/// one.
pub fn into_rc(self) -> RcPictureData {
let self_rc = Rc::new(RefCell::new(self));
if let FieldRank::Second(first_field) = self_rc.borrow().field_rank() {
first_field.borrow_mut().set_second_field_to(&self_rc);
}
RcPictureData { pic: self_rc }
}
/// Split a frame into two complementary fields that reference one another.
pub fn split_frame(mut self) -> (RcPictureData, RcPictureData) {
assert!(matches!(self.field, Field::Frame));
assert!(matches!(self.field_rank, FieldRank::Single));
debug!(
"Splitting picture (frame_num, POC) ({:?}, {:?})",
self.frame_num, self.pic_order_cnt
);
let second_pic_order_cnt = if self.top_field_order_cnt < self.bottom_field_order_cnt {
self.field = Field::Top;
self.pic_order_cnt = self.top_field_order_cnt;
self.bottom_field_order_cnt
} else {
self.field = Field::Bottom;
self.pic_order_cnt = self.bottom_field_order_cnt;
self.top_field_order_cnt
};
let second_field = PictureData {
top_field_order_cnt: self.top_field_order_cnt,
bottom_field_order_cnt: self.bottom_field_order_cnt,
frame_num: self.frame_num,
reference: self.reference,
nonexisting: self.nonexisting,
pic_order_cnt: second_pic_order_cnt,
field: self.field.opposite(),
..Default::default()
};
debug!(
"Split into picture (frame_num, POC) ({:?}, {:?}), field: {:?}",
self.frame_num, self.pic_order_cnt, self.field
);
debug!(
"Split into picture (frame_num, POC) ({:?}, {:?}), field {:?}",
second_field.frame_num, second_field.pic_order_cnt, second_field.field
);
let first_field = Rc::new(RefCell::new(self));
let second_field = Rc::new(RefCell::new(second_field));
first_field.borrow_mut().set_second_field_to(&second_field);
second_field.borrow_mut().set_first_field_to(&first_field);
(
RcPictureData { pic: first_field },
RcPictureData { pic: second_field },
)
}
}
impl std::fmt::Debug for PictureData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PictureData")
.field("pic_order_cnt_type", &self.pic_order_cnt_type)
.field("top_field_order_cnt", &self.top_field_order_cnt)
.field("bottom_field_order_cnt", &self.bottom_field_order_cnt)
.field("pic_order_cnt", &self.pic_order_cnt)
.field("pic_order_cnt_msb", &self.pic_order_cnt_msb)
.field("pic_order_cnt_lsb", &self.pic_order_cnt_lsb)
.field(
"delta_pic_order_cnt_bottom",
&self.delta_pic_order_cnt_bottom,
)
.field("delta_pic_order_cnt0", &self.delta_pic_order_cnt0)
.field("delta_pic_order_cnt1", &self.delta_pic_order_cnt1)
.field("pic_num", &self.pic_num)
.field("long_term_pic_num", &self.long_term_pic_num)
.field("frame_num", &self.frame_num)
.field("frame_num_offset", &self.frame_num_offset)
.field("frame_num_wrap", &self.frame_num_wrap)
.field("long_term_frame_idx", &self.long_term_frame_idx)
.field("coded_resolution", &self.coded_resolution)
.field("display_resolution", &self.display_resolution)
.field("type_", &self.type_)
.field("nal_ref_idc", &self.nal_ref_idc)
.field("is_idr", &self.is_idr)
.field("reference", &self.reference)
.field(
"ref_pic_list_modification_flag_l0",
&self.ref_pic_list_modification_flag_l0,
)
.field("abs_diff_pic_num_minus1", &self.abs_diff_pic_num_minus1)
.field("has_mmco_5", &self.has_mmco_5)
.field("nonexisting", &self.nonexisting)
.field("field", &self.field)
.field("ref_pic_marking", &self.ref_pic_marking)
.field("field_rank", &self.field_rank)
.finish()
}
}
@@ -0,0 +1,593 @@
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::fmt;
use std::io::Write;
use crate::codec::h264::nalu_writer::NaluWriter;
use crate::codec::h264::nalu_writer::NaluWriterError;
use crate::codec::h264::parser::HrdParams;
use crate::codec::h264::parser::NaluType;
use crate::codec::h264::parser::Pps;
use crate::codec::h264::parser::Sps;
use crate::codec::h264::parser::DEFAULT_4X4_INTER;
use crate::codec::h264::parser::DEFAULT_4X4_INTRA;
use crate::codec::h264::parser::DEFAULT_8X8_INTER;
use crate::codec::h264::parser::DEFAULT_8X8_INTRA;
mod private {
pub trait NaluStruct {}
}
impl private::NaluStruct for Sps {}
impl private::NaluStruct for Pps {}
#[derive(Debug)]
pub enum SynthesizerError {
Unsupported,
NaluWriter(NaluWriterError),
}
impl fmt::Display for SynthesizerError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SynthesizerError::Unsupported => write!(f, "tried to synthesize unsupported settings"),
SynthesizerError::NaluWriter(x) => write!(f, "{}", x.to_string()),
}
}
}
impl From<NaluWriterError> for SynthesizerError {
fn from(err: NaluWriterError) -> Self {
SynthesizerError::NaluWriter(err)
}
}
pub type SynthesizerResult<T> = Result<T, SynthesizerError>;
/// A helper to output typed NALUs to [`std::io::Write`] using [`NaluWriter`].
pub struct Synthesizer<'n, N: private::NaluStruct, W: Write> {
writer: NaluWriter<W>,
nalu: &'n N,
}
/// Extended Sample Aspect Ratio - H.264 Table E-1
const EXTENDED_SAR: u8 = 255;
impl<N: private::NaluStruct, W: Write> Synthesizer<'_, N, W> {
fn u<T: Into<u32>>(&mut self, bits: usize, value: T) -> SynthesizerResult<()> {
self.writer.write_u(bits, value)?;
Ok(())
}
fn f<T: Into<u32>>(&mut self, bits: usize, value: T) -> SynthesizerResult<()> {
self.writer.write_f(bits, value)?;
Ok(())
}
fn ue<T: Into<u32>>(&mut self, value: T) -> SynthesizerResult<()> {
self.writer.write_ue(value)?;
Ok(())
}
fn se<T: Into<i32>>(&mut self, value: T) -> SynthesizerResult<()> {
self.writer.write_se(value)?;
Ok(())
}
fn scaling_list(&mut self, list: &[u8], default: &[u8]) -> SynthesizerResult<()> {
// H.264 7.3.2.1.1.1
if list == default {
self.se(-8)?;
return Ok(());
}
// The number of list values we want to encode.
let mut run = list.len();
// Check how many values at the end of the matrix are the same,
// so we can save on encoding those.
for j in (1..list.len()).rev() {
if list[j - 1] != list[j] {
break;
}
run -= 1;
}
// Encode deltas.
let mut last_scale = 8;
for scale in &list[0..run] {
let delta_scale = *scale as i32 - last_scale;
self.se(delta_scale)?;
last_scale = *scale as i32;
}
// Didn't encode all values, encode -|last_scale| to set decoder's
// |next_scale| (H.264 7.3.2.1.1.1) to zero, i.e. decoder should repeat
// last values in matrix.
if run < list.len() {
self.se(-last_scale)?;
}
Ok(())
}
fn default_scaling_list(i: usize) -> &'static [u8] {
// H.264 Table 7-2
match i {
0 => &DEFAULT_4X4_INTRA[..],
1 => &DEFAULT_4X4_INTRA[..],
2 => &DEFAULT_4X4_INTRA[..],
3 => &DEFAULT_4X4_INTER[..],
4 => &DEFAULT_4X4_INTER[..],
5 => &DEFAULT_4X4_INTER[..],
6 => &DEFAULT_8X8_INTRA[..],
7 => &DEFAULT_8X8_INTER[..],
8 => &DEFAULT_8X8_INTRA[..],
9 => &DEFAULT_8X8_INTER[..],
10 => &DEFAULT_8X8_INTRA[..],
11 => &DEFAULT_8X8_INTER[..],
_ => unreachable!(),
}
}
fn rbsp_trailing_bits(&mut self) -> SynthesizerResult<()> {
self.f(1, 1u32)?;
while !self.writer.aligned() {
self.f(1, 0u32)?;
}
Ok(())
}
}
impl<'n, W: Write> Synthesizer<'n, Sps, W> {
pub fn synthesize(
ref_idc: u8,
sps: &'n Sps,
writer: W,
ep_enabled: bool,
) -> SynthesizerResult<()> {
let mut s = Self {
writer: NaluWriter::<W>::new(writer, ep_enabled),
nalu: sps,
};
s.writer.write_header(ref_idc, NaluType::Sps as u8)?;
s.seq_parameter_set_data()?;
s.rbsp_trailing_bits()
}
fn hrd_parameters(&mut self, hrd_params: &HrdParams) -> SynthesizerResult<()> {
self.ue(hrd_params.cpb_cnt_minus1)?;
self.u(4, hrd_params.bit_rate_scale)?;
self.u(4, hrd_params.cpb_size_scale)?;
for i in 0..=(hrd_params.cpb_cnt_minus1 as usize) {
self.ue(hrd_params.bit_rate_value_minus1[i])?;
self.ue(hrd_params.cpb_size_value_minus1[i])?;
self.u(1, hrd_params.cbr_flag[i])?;
}
self.u(5, hrd_params.initial_cpb_removal_delay_length_minus1)?;
self.u(5, hrd_params.cpb_removal_delay_length_minus1)?;
self.u(5, hrd_params.dpb_output_delay_length_minus1)?;
self.u(5, hrd_params.time_offset_length)?;
Ok(())
}
fn vui_parameters(&mut self) -> SynthesizerResult<()> {
// H.264 E.1.1
let vui_params = &self.nalu.vui_parameters;
self.u(1, vui_params.aspect_ratio_info_present_flag)?;
if vui_params.aspect_ratio_info_present_flag {
self.u(8, vui_params.aspect_ratio_idc)?;
if vui_params.aspect_ratio_idc == EXTENDED_SAR {
self.u(16, vui_params.sar_width)?;
self.u(16, vui_params.sar_height)?;
}
}
self.u(1, vui_params.overscan_info_present_flag)?;
if vui_params.overscan_info_present_flag {
self.u(1, vui_params.overscan_appropriate_flag)?;
}
self.u(1, vui_params.video_signal_type_present_flag)?;
if vui_params.video_signal_type_present_flag {
self.u(3, vui_params.video_format)?;
self.u(1, vui_params.video_full_range_flag)?;
self.u(1, vui_params.colour_description_present_flag)?;
if vui_params.colour_description_present_flag {
self.u(8, vui_params.colour_primaries)?;
self.u(8, vui_params.transfer_characteristics)?;
self.u(8, vui_params.matrix_coefficients)?;
}
}
self.u(1, vui_params.chroma_loc_info_present_flag)?;
if vui_params.chroma_loc_info_present_flag {
self.ue(vui_params.chroma_sample_loc_type_top_field)?;
self.ue(self.nalu.vui_parameters.chroma_sample_loc_type_bottom_field)?;
}
self.u(1, vui_params.timing_info_present_flag)?;
if vui_params.timing_info_present_flag {
self.u(32, vui_params.num_units_in_tick)?;
self.u(32, vui_params.time_scale)?;
self.u(1, vui_params.fixed_frame_rate_flag)?;
}
self.u(1, vui_params.nal_hrd_parameters_present_flag)?;
if vui_params.nal_hrd_parameters_present_flag {
self.hrd_parameters(&vui_params.nal_hrd_parameters)?;
}
self.u(1, vui_params.vcl_hrd_parameters_present_flag)?;
if vui_params.vcl_hrd_parameters_present_flag {
self.hrd_parameters(&vui_params.vcl_hrd_parameters)?;
}
if vui_params.nal_hrd_parameters_present_flag || vui_params.vcl_hrd_parameters_present_flag
{
self.u(1, vui_params.low_delay_hrd_flag)?;
}
self.u(1, vui_params.pic_struct_present_flag)?;
self.u(1, vui_params.bitstream_restriction_flag)?;
if vui_params.bitstream_restriction_flag {
self.u(1, vui_params.motion_vectors_over_pic_boundaries_flag)?;
self.ue(vui_params.max_bytes_per_pic_denom)?;
self.ue(vui_params.max_bits_per_mb_denom)?;
self.ue(vui_params.log2_max_mv_length_horizontal)?;
self.ue(vui_params.log2_max_mv_length_vertical)?;
self.ue(vui_params.max_num_reorder_frames)?;
self.ue(vui_params.max_dec_frame_buffering)?;
}
Ok(())
}
fn seq_parameter_set_data(&mut self) -> SynthesizerResult<()> {
// H.264 7.3.2.1.1
self.u(8, self.nalu.profile_idc)?;
self.u(1, self.nalu.constraint_set0_flag)?;
self.u(1, self.nalu.constraint_set1_flag)?;
self.u(1, self.nalu.constraint_set2_flag)?;
self.u(1, self.nalu.constraint_set3_flag)?;
self.u(1, self.nalu.constraint_set4_flag)?;
self.u(1, self.nalu.constraint_set5_flag)?;
self.u(2, /* reserved_zero_2bits */ 0u32)?;
self.u(8, self.nalu.level_idc as u32)?;
self.ue(self.nalu.seq_parameter_set_id)?;
if self.nalu.profile_idc == 100
|| self.nalu.profile_idc == 110
|| self.nalu.profile_idc == 122
|| self.nalu.profile_idc == 244
|| self.nalu.profile_idc == 44
|| self.nalu.profile_idc == 83
|| self.nalu.profile_idc == 86
|| self.nalu.profile_idc == 118
|| self.nalu.profile_idc == 128
|| self.nalu.profile_idc == 138
|| self.nalu.profile_idc == 139
|| self.nalu.profile_idc == 134
|| self.nalu.profile_idc == 135
{
self.ue(self.nalu.chroma_format_idc)?;
if self.nalu.chroma_format_idc == 3 {
self.u(1, self.nalu.separate_colour_plane_flag)?;
}
self.ue(self.nalu.bit_depth_luma_minus8)?;
self.ue(self.nalu.bit_depth_chroma_minus8)?;
self.u(1, self.nalu.qpprime_y_zero_transform_bypass_flag)?;
self.u(1, self.nalu.seq_scaling_matrix_present_flag)?;
if self.nalu.seq_scaling_matrix_present_flag {
let scaling_list_count = if self.nalu.chroma_format_idc != 3 {
8
} else {
12
};
for i in 0..scaling_list_count {
// Assume if scaling lists are zeroed that they are not present.
if i < 6 {
if self.nalu.scaling_lists_4x4[i] == [0; 16] {
self.u(1, /* seq_scaling_list_present_flag */ false)?;
} else {
self.u(1, /* seq_scaling_list_present_flag */ true)?;
self.scaling_list(
&self.nalu.scaling_lists_4x4[i],
Self::default_scaling_list(i),
)?;
}
} else if self.nalu.scaling_lists_8x8[i - 6] == [0; 64] {
self.u(1, /* seq_scaling_list_present_flag */ false)?;
} else {
self.u(1, /* seq_scaling_list_present_flag */ true)?;
self.scaling_list(
&self.nalu.scaling_lists_8x8[i - 6],
Self::default_scaling_list(i),
)?;
}
}
}
}
self.ue(self.nalu.log2_max_frame_num_minus4)?;
self.ue(self.nalu.pic_order_cnt_type)?;
if self.nalu.pic_order_cnt_type == 0 {
self.ue(self.nalu.log2_max_pic_order_cnt_lsb_minus4)?;
} else if self.nalu.pic_order_cnt_type == 1 {
self.u(1, self.nalu.delta_pic_order_always_zero_flag)?;
self.se(self.nalu.offset_for_non_ref_pic)?;
self.se(self.nalu.offset_for_top_to_bottom_field)?;
self.ue(self.nalu.num_ref_frames_in_pic_order_cnt_cycle)?;
for offset_for_ref_frame in &self.nalu.offset_for_ref_frame {
self.se(*offset_for_ref_frame)?;
}
}
self.ue(self.nalu.max_num_ref_frames)?;
self.u(1, self.nalu.gaps_in_frame_num_value_allowed_flag)?;
self.ue(self.nalu.pic_width_in_mbs_minus1)?;
self.ue(self.nalu.pic_height_in_map_units_minus1)?;
self.u(1, self.nalu.frame_mbs_only_flag)?;
if !self.nalu.frame_mbs_only_flag {
self.u(1, self.nalu.mb_adaptive_frame_field_flag)?;
}
self.u(1, self.nalu.direct_8x8_inference_flag)?;
self.u(1, self.nalu.frame_cropping_flag)?;
if self.nalu.frame_cropping_flag {
self.ue(self.nalu.frame_crop_left_offset)?;
self.ue(self.nalu.frame_crop_right_offset)?;
self.ue(self.nalu.frame_crop_top_offset)?;
self.ue(self.nalu.frame_crop_bottom_offset)?;
}
self.u(1, self.nalu.vui_parameters_present_flag)?;
if self.nalu.vui_parameters_present_flag {
self.vui_parameters()?;
}
Ok(())
}
}
impl<'n, W: Write> Synthesizer<'n, Pps, W> {
pub fn synthesize(
ref_idc: u8,
pps: &'n Pps,
writer: W,
ep_enabled: bool,
) -> SynthesizerResult<()> {
let mut s = Self {
writer: NaluWriter::<W>::new(writer, ep_enabled),
nalu: pps,
};
s.writer.write_header(ref_idc, NaluType::Pps as u8)?;
s.pic_parameter_set_rbsp()?;
s.rbsp_trailing_bits()
}
fn pic_parameter_set_rbsp(&mut self) -> SynthesizerResult<()> {
self.ue(self.nalu.pic_parameter_set_id)?;
self.ue(self.nalu.seq_parameter_set_id)?;
self.u(1, self.nalu.entropy_coding_mode_flag)?;
self.u(1, self.nalu.bottom_field_pic_order_in_frame_present_flag)?;
self.ue(self.nalu.num_slice_groups_minus1)?;
if self.nalu.num_slice_groups_minus1 > 0 {
return Err(SynthesizerError::Unsupported);
}
self.ue(self.nalu.num_ref_idx_l0_default_active_minus1)?;
self.ue(self.nalu.num_ref_idx_l1_default_active_minus1)?;
self.u(1, self.nalu.weighted_pred_flag)?;
self.u(2, self.nalu.weighted_bipred_idc)?;
self.se(self.nalu.pic_init_qp_minus26)?;
self.se(self.nalu.pic_init_qs_minus26)?;
self.se(self.nalu.chroma_qp_index_offset)?;
self.u(1, self.nalu.deblocking_filter_control_present_flag)?;
self.u(1, self.nalu.constrained_intra_pred_flag)?;
self.u(1, self.nalu.redundant_pic_cnt_present_flag)?;
if !(self.nalu.transform_8x8_mode_flag
|| self.nalu.pic_scaling_matrix_present_flag
|| self.nalu.second_chroma_qp_index_offset != 0)
{
return Ok(());
}
self.u(1, self.nalu.transform_8x8_mode_flag)?;
self.u(1, self.nalu.pic_scaling_matrix_present_flag)?;
if self.nalu.pic_scaling_matrix_present_flag {
let mut scaling_list_count = 6;
if self.nalu.transform_8x8_mode_flag {
if self.nalu.sps.chroma_format_idc != 3 {
scaling_list_count += 2;
} else {
scaling_list_count += 6;
}
}
for i in 0..scaling_list_count {
// Assume if scaling lists are zeroed that they are not present.
if i < 6 {
if self.nalu.scaling_lists_4x4[i] == [0; 16] {
self.u(1, /* seq_scaling_list_present_flag */ false)?;
} else {
self.u(1, /* seq_scaling_list_present_flag */ true)?;
self.scaling_list(
&self.nalu.scaling_lists_4x4[i],
Self::default_scaling_list(i),
)?;
}
} else if self.nalu.scaling_lists_8x8[i - 6] == [0; 64] {
self.u(1, /* seq_scaling_list_present_flag */ false)?;
} else {
self.u(1, /* seq_scaling_list_present_flag */ true)?;
self.scaling_list(
&self.nalu.scaling_lists_8x8[i - 6],
Self::default_scaling_list(i),
)?;
}
}
}
self.se(self.nalu.second_chroma_qp_index_offset)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use super::*;
use crate::codec::h264::parser::Nalu;
use crate::codec::h264::parser::NaluType;
use crate::codec::h264::parser::Parser;
use crate::codec::h264::parser::Profile;
#[test]
fn synthesize_sps() {
let raw_sps_buf = [0x00, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x0a, 0xfb, 0x88];
let mut raw_sps = Cursor::new(&raw_sps_buf[..]);
let nalu = Nalu::next(&mut raw_sps).unwrap();
assert_eq!(nalu.header.type_, NaluType::Sps);
let mut parser = Parser::default();
let sps = parser.parse_sps(&nalu).unwrap();
let mut buf = Vec::<u8>::new();
Synthesizer::<'_, Sps, _>::synthesize(0, sps, &mut buf, false).unwrap();
assert_eq!(buf, raw_sps_buf);
let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
if write_to_file {
let mut out = std::fs::File::create("sps.h264").unwrap();
out.write_all(&buf).unwrap();
out.flush().unwrap();
}
let mut cursor = Cursor::new(&buf[..]);
let nalu = Nalu::next(&mut cursor).unwrap();
let mut parser = Parser::default();
let sps2 = parser.parse_sps(&nalu).unwrap();
assert_eq!(sps, sps2);
}
#[test]
fn synthesize_sps_scaling_lists() {
let sps = Sps {
profile_idc: Profile::High as u8,
seq_scaling_matrix_present_flag: true,
scaling_lists_4x4: [[
11, 20, 10, 20, 10, 22, 10, 20, 10, 20, 13, 20, 10, 20, 10, 24,
]; 6],
scaling_lists_8x8: [
[
33, 20, 10, 21, 33, 20, 12, 20, 33, 23, 10, 20, 33, 20, 10, 20, 33, 24, 10, 20,
33, 20, 15, 20, 33, 20, 10, 26, 33, 20, 17, 20, 33, 28, 10, 20, 33, 20, 10, 20,
33, 29, 10, 20, 33, 20, 11, 20, 33, 20, 10, 20, 33, 20, 10, 20, 33, 20, 10, 20,
33, 20, 10, 20,
],
[
10, 77, 11, 20, 10, 77, 12, 20, 10, 77, 13, 20, 10, 77, 14, 20, 10, 77, 15, 20,
10, 77, 16, 20, 10, 77, 17, 20, 10, 77, 18, 20, 10, 77, 19, 20, 10, 77, 10, 20,
10, 77, 10, 21, 10, 77, 10, 22, 10, 77, 10, 23, 10, 77, 10, 24, 10, 77, 10, 26,
10, 77, 10, 28,
],
[0; 64],
[0; 64],
[0; 64],
[0; 64],
],
frame_mbs_only_flag: true,
..Default::default()
};
let mut buf = Vec::<u8>::new();
Synthesizer::<'_, Sps, _>::synthesize(0, &sps, &mut buf, false).unwrap();
let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
if write_to_file {
let mut out = std::fs::File::create("sps.h264").unwrap();
out.write_all(&buf).unwrap();
out.flush().unwrap();
}
let mut cursor = Cursor::new(&buf[..]);
let nalu = Nalu::next(&mut cursor).unwrap();
let mut parser = Parser::default();
let sps2 = parser.parse_sps(&nalu).unwrap();
assert_eq!(sps.scaling_lists_4x4, sps2.scaling_lists_4x4);
assert_eq!(sps.scaling_lists_8x8, sps2.scaling_lists_8x8);
}
#[test]
fn synthesize_pps() {
let raw_sps_pps = [
0x00, 0x00, 0x00, 0x01, 0x07, 0x4d, 0x40, 0x0d, 0xa9, 0x18, 0x28, 0x3e, 0x60, 0x0d,
0x41, 0x80, 0x41, 0xad, 0xb0, 0xad, 0x7b, 0xdf, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08,
0xde, 0x09, 0x88,
];
let mut buf = Vec::<u8>::new();
let mut out = Cursor::new(&mut buf);
let mut cursor = Cursor::new(&raw_sps_pps[..]);
let mut parser: Parser = Default::default();
while let Ok(nalu) = Nalu::next(&mut cursor) {
match nalu.header.type_ {
NaluType::Sps => {
let sps = parser.parse_sps(&nalu).unwrap();
Synthesizer::<'_, Sps, _>::synthesize(0, sps, &mut out, false).unwrap();
}
NaluType::Pps => {
let pps = parser.parse_pps(&nalu).unwrap();
Synthesizer::<'_, Pps, _>::synthesize(0, pps, &mut out, false).unwrap();
}
_ => panic!(),
}
}
let write_to_file = std::option_env!("CROS_CODECS_TEST_WRITE_TO_FILE") == Some("true");
if write_to_file {
let mut out = std::fs::File::create("sps_pps.h264").unwrap();
out.write_all(&buf).unwrap();
out.flush().unwrap();
let mut out = std::fs::File::create("sps_pps_ref.h264").unwrap();
out.write_all(&raw_sps_pps).unwrap();
out.flush().unwrap();
}
assert_eq!(buf, raw_sps_pps);
}
}
@@ -0,0 +1,3 @@
43656b2f
c9dd1361
62d34555
@@ -0,0 +1,3 @@
45ba0c1a27d0ff82fd7a969631adffe6
cee875ded4998c9810a14f9497a11f72
d1e0b9347134ba7a07cbcf249066f022
@@ -0,0 +1,3 @@
ee936370
0e5e577c
bfe430af
@@ -0,0 +1,3 @@
fe6701d54768bc37c76e630435e8fe02
3298d47365ac1f9dc0942ece9e104246
7cb9209603f53ba995f7eee20b2bbf4b
@@ -0,0 +1,2 @@
9fc67012
7f0b441e
@@ -0,0 +1,2 @@
115a8e6899b71c04e32a0254ba62b30a
1fa3f5a930e08943e6d6bfbd5d43004e
@@ -0,0 +1 @@
7dd66ef1
@@ -0,0 +1 @@
d2304abbf0349ec63324741bf723960d
@@ -0,0 +1,70 @@
# H.264 Test Data
This document lists the test data used by the H.264 decoder.
Unless otherwise noted, the CRCs were computed using GStreamer's VA-API decoder in
`gst-plugins-bad`.
## 16x16-I.h264
A 16x16 progressive byte-stream encoded I-frame to make it easier to spot errors on the libva trace.
Encoded with the following GStreamer pipeline:
```
gst-launch-1.0 videotestsrc num-buffers=1 ! video/x-raw,format=I420,width=16,height=16 ! \
x264enc ! video/x-h264,profile=constrained-baseline,stream-format=byte-stream ! \
filesink location="/tmp/16x16-I.h264"
```
## 16x16-I-P.h264
A 16x16 progressive byte-stream encoded I-frame and P-frame to make it easier to spot errors on the
libva trace. Encoded with the following GStreamer pipeline:
```
gst-launch-1.0 videotestsrc num-buffers=2 ! video/x-raw,format=I420,width=16,height=16 ! \
x264enc b-adapt=false ! video/x-h264,profile=constrained-baseline,stream-format=byte-stream ! \
filesink location="/tmp/16x16-I-P.h264"
```
## 16x16-I-P-B-P.h264
A 16x16 progressive byte-stream encoded I-P-B-P sequence to make it easier to it easier to spot
errors on the libva trace. Encoded with the following GStreamer pipeline:
```
gst-launch-1.0 videotestsrc num-buffers=3 ! video/x-raw,format=I420,width=16,height=16 ! \
x264enc b-adapt=false bframes=1 ! video/x-h264,profile=constrained-baseline,stream-format=byte-stream ! \
filesink location="/tmp/16x16-I-B-P.h264"
```
## 16x16-I-P-B-P-high.h264
A 16x16 progressive byte-stream encoded I-P-B-P sequence to make it easier to it easier to spot
errors on the libva trace. Also tests whether the decoder supports the high profile. Encoded with
the following GStreamer pipeline:
```
gst-launch-1.0 videotestsrc num-buffers=3 ! video/x-raw,format=I420,width=16,height=16 ! \
x264enc b-adapt=false bframes=1 ! video/x-h264,profile=high,stream-format=byte-stream ! \
filesink location="/tmp/16x16-I-B-P-high.h264"
```
## test-25fps.h264
Same as Chromium's `test-25fps.h264`. The slice data in `test-25fps-h264-slice-data-*.bin` was
manually extracted from GStreamer using GDB.
## test-25fps-interlaced.h264
Adapted from Chromium's `test-25fps.h264`. Same file as above, but encoded as interlaced instead
using the following ffmpeg command:
```
ffmpeg -i \
src/third_party/blink/web_tests/media/content/test-25fps.mp4 \
-flags +ilme+ildct -vbsf h264_mp4toannexb -an test-25fps.h264
```
This test makes sure that the interlaced logic in the decoder actually works, specially that "frame
splitting" works, as the fields here were encoded as frames.
@@ -0,0 +1,8 @@
#!/bin/bash
# Generates the CRCs for all .h264 files in the current directory using ffmpeg.
for f in `ls *.h264`; do
ffmpeg -i $f -pix_fmt nv12 -f framehash -hash crc32 - |grep -v '^#' |awk '{print $6}' >$f.crc
ffmpeg -i $f -pix_fmt nv12 -f framehash -hash md5 - |grep -v '^#' |awk '{print $6}' >$f.md5
done
@@ -0,0 +1 @@
ЁЃ`_яяяяяяяяя_щЏ°`Џ}ЎЇяяяѓЌ%ђ\jЭaБfїЪwц”[bњѓћУJAПяь1ъШ?' ю4€Т_®3Iџ?Р`Wqaц– 6ЙFzzZшХ+ЯяГ0шI`‡Э–2рЛЄ}†-“~ ‘ґшн‚_яьC 88lи5фУг¶в0ЬшkЋЂ
@@ -0,0 +1,250 @@
8e0a948b
0d25f469
612a0a5c
0a452520
6c83e97b
d1598a6e
26892325
e7eb5f61
e527446a
f85a9d52
16bfd3c5
e837620a
38484895
050d070f
b30cfcd9
bec9e023
9b272332
f6a542ba
bf978015
1767b90a
f3755595
5ae99ff9
5b8b86a6
44cd3b51
d99e1fab
17761096
271806c4
b0d17f38
01f033d7
6ae775c1
42933e3b
11f61f13
4ecab452
8e50b509
dfde6c00
06703f5c
92a02866
c6f3a13b
4371bed0
5dd9e344
cfd668a3
abfc6c45
ffbc45ac
be1cbdf9
a6dd2c68
fc838af2
d8f88c05
e377a83d
8acea967
cfcae361
47ec0343
c5af87e3
a4c1d94e
080ca745
74f48838
00480284
f2da9c1d
8bb000aa
32857438
e7fcb9ae
9bbb834f
3889cf50
a3ec330c
6963e8b3
8de3e2f3
8e1d1dc1
242fd47d
e11cc789
9e558667
2ff16820
d75fad55
5f4a5907
83182bc7
450343a5
dad31a6d
bb365f64
cdd2d57b
99c0e687
52eecf8a
951cd566
f29f0fef
d1165eae
0df626f8
6fbdfc15
7f5180af
c1f6a321
7d9b9418
a0f25570
c5af5562
acc18caa
0d3d93a1
cce8f670
7804b84a
ed7999c0
38dbf871
09143151
866c7c00
c782f291
65605fc3
8f37e317
aaf653bf
2894b605
7eeba8f6
cad6e5a6
bce25e79
cffbc84e
c29dc1b4
99ac23e8
7805efed
78623121
88767543
a5e2df6f
555355b0
4e646cd0
1fa3064f
dc4f65f4
77c52338
41287dca
0ea69260
2fb243a8
3de20c11
ad7fd2c0
79f73884
cf89a598
301e0a62
3e26fed7
8e8dacbe
628cfc2b
5c7fe45f
06892492
fb7d8b50
ddf98de9
5fb4c485
501ccf38
e5f40baf
07ab7574
8596934d
f7066878
27e166e6
10a37320
39dc9664
ca9e1642
5d23f171
dafd915b
ddcbc72c
56b4b83d
3bbde005
41ed4060
68caa834
957b84b9
5f6bbbfc
98e64dae
ebd05871
c851d8e9
f73fbbe4
7451ac0b
cbfd7a78
901fe589
9ed86bd9
1e9fef87
faca6981
98b7bd7a
e1d4bdf1
b6a95dee
3955fb57
90d28016
64472746
5f2b76df
478197b2
c59fda8c
169c2bfe
74210735
94662d44
f16e4ef6
f2134ee3
f881db32
0d927db1
ab77556a
12a65d29
632965b4
807d10f7
339e5f7e
be2c9336
bc593f8b
c9dfe52d
fc738a40
17699e28
e8be4231
b4264279
895a00b1
41eb9726
e804873a
c6a4b014
bdc91323
14b6934d
dd31a422
0d379528
4e4b45cd
9f6773f8
326f3f46
6180b23d
61329916
01a1ecf3
8b77abcf
4508213e
dfc43ab1
0937305a
a9d22bba
541a1ba4
5eb3490a
4702020a
190299ca
202ef749
911daf20
b7b63628
3bbb965c
25971699
3cf16f5f
1e95278a
273193c1
48794404
a5a25d98
7c85b782
d17a5be3
c218d70a
8ce5be9b
eed76688
7fdd906e
136a9e51
87674a67
0c1f44d8
6e09307d
f4f2d7f0
855c52e4
e63633cc
8baecd74
08371d8b
18949a47
c253b241
c94aa045
e272c69c
f2b73158
6b6d3a84
76e9bf4d
4c05b45e
1a774816
47e007d1
37e715a7
8d8bd4f6
733862ca
@@ -0,0 +1,250 @@
b318f483b819b982a9756e53d7e15648
f0257b3a3e42c43334b5114f7af458c2
7e53dcfd409361c9747f2e15498bffec
d149b038ca22572e33a1f395e5ecee08
eb27dd6ebe6c63da3383569c0775a831
2cbbf7c8d7519b6f5e8ac14363b4bfa5
ca1a19e2110b2129f11e8b49127bdd85
286936def7ad4df160b360d65ac9b46e
b33f9984878150925a7967072e99d49b
075757bf9b1875431fff1fef1f8d757b
68d3706a9b387f97e6ddbfd4abc57ad0
bdde980b77c72c36b4f090c9b5da5344
2574458dd66165304420f6ea813d63e5
35fcaf6785eb36fc903eb5279b6c29aa
ec4f4e19d7e54e26d3dfcc30e46f8a57
bffdfe52242097b034c1716a4f3c2c28
5fb0282d92050c9a5c7b3c12dd5ced0f
6167bc48f2e77dad220a3a6aa67b05bc
d3cffb696153090eb014e6a88802bd4e
9d175a5550156b1e8bca6099b4c09f61
7fbd2aaeedd6f3d6cb9603910c7cf2c2
56e21b1c18fff15a62e9933e442fe8ff
546312d22a42fc9b67b08e18d6b82852
2509014b171245b4a3b692527c2781f3
fb31a14a29920145563197157f60b0cf
64985fa4c405b139aa60202819bee68c
7886fd15be015e09715906507fd7ee6b
f86709eedd0ea8413c30e5417dab459a
0d3b8df30ef8188a889dc7cdeb02b21f
255db4b53038918896afe70d26f7a52c
c35945881b96b536573f7eb5a35d9bd3
809a7dcc7966d196b61e0645025e959b
fe2130868d0026602d29492cfb77365b
1f341d6cb526c6cb2b18a4919d13dd3f
14a9658c25c52651b75b009cf505c268
f1b76a3abadb11253729430c7f333fa8
cd204999e6f7912ba9caa4f625314a7d
3701e818d60637dfc56407d88b79feda
819907f586f4c7515106b4083ddbad47
c43789f2fcd506cd7ac5116c9ef4d15d
19ae171488b106301ee54078b6f5b5df
21ca7fe9e1ab4f13ef5a7f8a041d471d
6d23d3b81f1c4c787e0ee9f62f5a9849
3c9ea82d2bd157595b69138c327133a6
506a237ea2ec916734d9511fbe79be10
5001323d2e1b5410def30db2f59e4261
f4e315814b0812511fbe50d5c061e324
a687ca61e888fe0e36db694a870a62f5
9f6f17e40f587c27ba7a24227f279285
1e09cb2dcafb86be51648f2a4e14dc24
db5fff370bd822000ca204fd64bac350
0ad486dd80931116e0eedc3c2938a201
c2a0ee00978debffcbd633efe9299d97
21fe48e7b1666fedfeb8a6e7b67ed7e9
bd2f4438022e4007207af7db7e488674
f4c6654ae5325c06d7248b423d2dfff0
f24ccbfb2bb7856c3a6a03570b01d1df
253e90c855cb01f47c98b86c02ab5038
9b1dc8a79e1d93d8756ed0cfcbfe5137
aea93cf9423bc3dfc384faaca064397c
fb030c4f06693f3610802851ceb11232
9307c5df0bef220e2dac16807f7f237e
1934c1a1a72b0646d0e9e71c15dc4987
5ddc040a414fc5a141ff949e4b127d4a
a1d5f1d432006062b5d6b9443c717ad2
0589d0ebf3c8d2364237e80d41a2b475
ddcebf0cefb4f874243424fac2e3b6de
cdf1dfcfc39fa58d1f9092bf38ba7e7f
167ce118cb7d087ea441c56fe4d35fb1
20dc2343a99d4f973832173523c2f5b1
3c54348b7b75234d86ee3dcf298b00dc
187f13356155b3966596693b3ba7f6fc
e64ec5b7d6554e8976f9d55620ae8411
8e4d7d9d357d5146b824655e99284939
221da170942678c1bb96f106bdacca65
10397b2182e00d3c2f3521adb1125b1e
820a2cbffd802eb8444d7d321903855d
fb6543d46d8733d2fe8505f058b14e4b
088bb42d269720bc1d023537f7b57213
5d34e835cf4fe44f26e6ea7df3699323
7525e57a778a4b298ab8a1f529dea231
f6611d41cae1236372a61794515f3286
fa807ac173b70892aa9fbbfe5cf097bf
b30a12a5c07b96c5d2a7ef716a40e214
b699c6d2b59396338c025cf72e28235e
567c19149bb2089eae35fb58e6d3ca9f
d3d47ec2f02bc7d564192a98fad66e34
bd8293548cf345333c892503bc02762e
84c3e877420d17c1536836f1b4b0cf42
55c15bb8eb4cd81545314e0ab6535c73
e47f3fd5c096033b0d1d3e9de8e05dfe
63e69377f86e999221ce62dd101fb530
8ca70fbe70b840ffc159f2ba5890b472
e5da1d908351429ba3875de6e541a294
0cd2b4b3d9dc052d4f3d82235a469809
8c47d7a5e67175912811cf24ce1be9df
b3a7c2128d325519e6e788ffa19b867b
bf803115b52d1452e2f46355deba01a4
5db69b7973fd9e6da6aaf7836219d495
4466c1bf9d8cb075799e7259b06c3dfd
0df28c89649a7ada81ed6b73995dacd3
60adb4f96fdf22479e7d1edc583d532f
af2802fee9230f40f7662f6eb6318518
f1a5beadc8a5ba88b74d31e211b3da2b
407d4028cf27cfe3cb265d932dd01ad8
837ef722923ce9df10ead5332e2ad1b4
d468d3075b95985c692d1182cf45c1f0
cf750f31df41c4fd0f64797d7e776243
bd0881cfb36014d96dbbd8d1872b449a
b3fdd5a0235590e0606eb8223425c206
c2130fcb9907554280a2f1024bf5b58e
0b4cc296d9d7354718341a0b241d5417
09be4cf122c20e92a8460571af96236d
2c8763ee3dffb8a2ab123a0b98909531
bc07e96aa9fd80daee6b6747f3eb10d9
5191f574d1aed73c2fea26ba696ecd73
cd863713a5588c5df97f1272bbbc8537
1ed61be740e1c0dbca9e803537e913fe
6ff899d76a0599bf67b4898078d10c8d
ee1f3a668c150ed6dfae060e97dc46cc
d73c55aa581d36c4f7f534f1e03c9418
d9689cd24ae9b08375530f7ec41a574a
08846be9ad2afa27ed851634d9cab4e5
1668585e3d72b2bc736fd5b582b17ee8
5d2d6df2d6c40044086e8faf5e20fa76
53e50b205ac2de14ac79d2a658ef4327
e73d59cfc81ec50e7aa5ca9087aaeae4
2ca1fe1fc342c4c3dcb28089e82604a3
b1cce1a87a2486705b59517c895e817f
86143d602317e1302c8d1825bfb201d2
7c962811ce295e6ab9c4650cae19319e
be87fbaa40ac8989649700e9a82d0ce9
d3fd645e47efd43950972463ffd74e3d
6ed763a90aca846393467bd37bf6ecc2
f888abbf25d47985669b2ecbbda04063
1c64db945e018359d80762d0f1e82f85
a5c8ff3c974ebbb722e9d6ef92de4c3e
7611866a4cb7d346752bc4700621779e
075ddd7b4bf5bcc6d308065b94c9e4e6
0a59ab9ec9d8228b1b3d554bcbdafa26
af6014ace2c40bada1bac0da4ca1855b
86955b7f88078d167b381366229f8a42
f8c66e921bf190453f6c64ba9dbeb3d5
c353624e039f8f5a6ea83196d7559886
bb418dee4a5aab8c9c1e5f4d32a4bc8a
b56240f383319fc8ceddf6864f715a5b
544ab0aebf0e6b5bac10f5c27329ef52
dde30afce82bbb95752b39662d79648e
ffe0f8f122b0b575c17a745d98af418a
ba476f1704cc103f303dc88d6aa5fd31
ddfc7326b1e137d4338da33585c94fc4
32a313e1553677d00ac43cb8610186bc
4fed1c0a1959a9dca55c1d126c704ce7
cc073c0fcb57c2a0ac7785d3e36289cb
7e91a82a927a14d0be7a4ccdce702222
85a1e2124c2acb27c2eae947e371332d
b8dad5132989583791ea3180ede734e3
b749f5cfa51035c2c901c8f51fdae26f
63c8d810da89f6d93895744cf6bc6092
c4c32e4c185cf04da9e6f6cb4b228c52
6d48d28f00b6cbd78b4ddfa056c4b24f
a56ec3b1d282706257e9539d97708745
3487a2b855eef272eca5c04ee2b826dd
5d35907998c38c521a0bb08dd114900b
f01a320fef7ed3cfa74fd5e7b7214491
3655861d44a93225eab5a6f6aa4dbd9f
adc67f4388f9e4aa42232f19db02690c
096cebe6b60bc924def53b2d9413febb
8200dc7f73ae39a7fbeaf6b7fdf11e2e
ccc47ba3d01aaeb9859cd998d3472efe
727e467f3789d4b3805318dd600e8d41
ab74f71926d8e1b5d0fb89904c4f0e56
2ef13831a4100e211309658d5c02a177
796c34d7ccc6790fccabc9be3e42db9c
b1fb2e39582d47ab8172702288b2fcbc
ac5d037d7457af598c9b7fca0eadb384
7a82d0ec070abcbb2d1ef16bc8af47e7
ed68a5b6911d53f073b778294192e44f
55b14258fc94575ccea51bc4bcf6cb61
2502f7cd3fd06ad7994acd5ac8995508
afbfe5035e3cdede39a1250b1d91eff4
c7bd0e18288966e3fc902e401a1cf92a
a3243a3f3ed1f32bf4e2d5ee06cf69ab
3538beb96dff67a1d7b81a317950d038
ff24aa585a23d93671add56e45ffdca3
912bcec63bdabb5a4b472df4b4f557e1
d59ad48f9b4378ce89b4bfffec9b916e
ea4e4951dc51b56e07bf01c0d4171549
7197584855f20ff4903c247939967dca
9f7482cc0d499ab79f2eb5480807ec6e
eaadda1f5ff577d617e6c4c479b5f563
a4c9e70aebeaf8b4f986fbf8bd8aa902
10f3080e8374ba0397f1e296fbb60350
b021caa467751d617842f66bb4e62b47
444ce58ff856af0ee2ba0e99ae37950b
b61d88b82f582f32723ee5dfdb1d34de
55f921778031bd848ebddb7dc0d63db7
bb9ad6dd7d13d4e412bdfec31783992d
a4d4f8ce07d3496017024897233b09dc
ffd49b1bb1ebb2afa89ac73305762656
69d843e05f072e3616e9fbeefe1feb9a
4758c49357c312d6187a3cc7322d40a9
0200eae7e77a5f7eb0ab6a52b5934c43
6f6de279c5b1bf22064d6173e0f4c659
dd1f5fc9e67d0f2375e72bfaefa72590
bffd8a460d7ee4746ced0abd2e108562
2d638249a3d755b2c778f6b384b862e8
1e5322f8df0406fdbd2c99d5eeed0a68
41e1a633a6adfaa70198f5872272013c
9900e6ccab80f965f0f42f5f466e51f6
0da72ec72eb2eae9f410267cee90d1b7
c808dd41b542e64db7ee2b6770eb99b3
2d8b514f3e2257521b2ab872ea7d4ea4
be6a7befa9d91157e6a70bee2cdb9516
ccc15622babeb66a984b81d80a713966
caa22e1262b249b7e0dbac4bdd59e391
d759cf8e65cb2b805a7101b32bfe64d2
43da07daa63a0ad6c39e5d3b468b7df9
2cf45923c9b52de4b23a34ae07616bfa
1c5dd7b336a9cfcfa7772e4df2f62075
8261e8791fc1d8a5097af6ed2c1487b8
e97b262b8f2056851ba19b6b1b23a378
567442abf4ee97d491572bd868d50daa
cf25196079d433cdd802e87d4211ad3e
e7295385f1842ede9e7e5e48602fac00
6e44cb843be327f938e1558fd22d9aee
e8efa873da70fb0c55e95ccb3b98dd0c
1f520e477d527dfee845187c8bba3433
da789f8b71e7618b6b614e7f38c6eb06
4e9f640a8f317b0da5d3f24e87708866
922134326b2d0223cbd3a679adc908e4
272aa55d0137df113bee04f8c06e4d46
6e5f5d3f6d2c88fdc80f26c3af4baf39
2fec6a2b1154841837b77a456627de14
71c562a6801157465fd5df54b87504b9
eec76ebe71275d86b69a1ca42a8da947
42e10d7a5b99affc5d9ed41006e701f2
ae0aa8ce889e3ff30b7840b7a2b74737
06bebb86bff1e08d071adf6284bbe3b1
0fbe06a9de9f8195b03364748b6455e3
46f2d55d237687a1b9d3b326c56da6c3
3ffbbb180f1c3bf8303967a0840297af
19cd3d6d0641fe34da63c78cfc94cb72
953d1514d529804060f71d1430fdcf7e
961279642b7ca4f0ddc329c2c0d7a819
cc4ffd1fe4dab49b3a5ad06948fd3875
32f0f8ff923031ac246925020c11868c
ab56616053efde4cc28e379d689d380c
253c3fa01be26ad95522f95bb3927132
0b137261e168b892d7b6b2255198d545
@@ -0,0 +1,250 @@
3eae0fa4
671432fb
36f2633d
0d4c5ed0
5401b3b1
132ea2eb
638474a8
ddaac73d
55d506dd
a895940a
552aa819
3df1c8cb
4570f1a7
e355e911
29220d98
e1d6c064
44b2001e
264b8f82
d13e3c00
36b2feb1
dcee531a
f8a44834
a913018c
90f486e7
9a1101d0
15332185
842e0474
bff7bbf9
dc8cd701
835bd3cd
2a3ea506
67e8e53b
ef5b8038
97c89f86
ef8a9d7c
792b083e
388e20b0
c9b83d6c
868e8252
d77091cc
33a41580
67912531
d3a7e0cc
fd5ac6f7
ed96bee7
8b4a495c
1219c05f
b8050ff4
c9c27101
465b4659
01fb0b1e
7a01e4d9
1a6333b7
bd4e9d8e
fe812caa
4bb09d4d
64b15b3d
cba3b242
720daf5f
44efaa8c
14ae7ec5
6c6c8c3d
3bbaa125
7b4872f6
a59dd7f2
83925660
b0679228
f52ef9fb
2850560d
01dfd505
94a48963
7fcc0cca
47528c61
032dc2f3
bb8e0fb1
63914082
b9b4c36c
c69f899d
49a9c94e
77af09d5
07cdcc3d
eabc68d5
62421f8d
2811bd16
e3d5a2e5
17d95adb
55bcd9cb
d785f280
45e97423
c7457a9c
3e2b380f
57b67280
d02339e4
065e3598
d5977df8
8ae372e3
54e17f4c
ba6e24c1
a8a11da8
ed23d1fd
c1f3ed84
4a46e699
fea03186
8bf1c40d
7ee9d985
fd3fafcc
f2b62184
aceda460
97d31d6c
54aa313e
d95b8ea8
70c3792a
677ee6ad
ca53aba4
1351a9f5
99a1bb43
c01cff4c
b1cb013c
ef543a58
f5c2ae90
ca5387ad
7b5ca533
78d125a3
5e04f1b8
8f620bd6
24ffe5c1
5fda8ca9
ae38f186
2d86f080
2b94cc5d
697a09f7
a6187634
dae9cce8
74bb20a4
18a41fd8
a4d92f33
e902181c
18b9f932
e5ee4cda
1ccb55bb
6ae0fe2e
05129a95
66d634d7
0dceffae
00814ab6
6370890a
c6d35660
8eb21a54
9ccd4c7e
b3a01706
79d5382b
d56f2dda
b6bd04c6
becb8b5d
0860f480
1febbb26
c45e567d
9b12f235
916e5b2b
7a0c2458
cdfd327b
666d700e
d5743e0e
a8ead497
2de13dfe
1ef94a75
8a05fd88
e8498198
b3f388bf
3ad2a8ed
9a871a37
1e994a52
3c9c0f61
9e67b705
b37baee1
c57c176a
8cd83bd6
ed5d5bef
d933b481
75882924
662a5466
956e24ed
f55aa48e
16273797
1dbffc93
dadd656e
7bec0c75
aabdd998
136aa991
72abfe71
b2be52f9
df04bac8
fc14d7f9
f34ae6f1
1ea238aa
d77bd8f7
821bb618
4c543487
81bc3b15
afd3f64e
843d503a
54bf670f
f447048a
51221d02
f20efc3b
e492c81f
a2cff836
41ffd6d6
8e1877af
a866b3ba
c7751eaf
6d80911d
a8701556
0185616d
ce91cb8b
a8b341b7
49c4f47d
d3001a6b
6410d282
ee71f778
190d2d8f
05c09706
e25514d3
7a8e74df
543ac711
4477777e
40803303
3cfae743
bf691cc7
c94b4dc2
7127479d
2aaaa442
a36b21a2
f8fcf82a
e024d413
7e080f9d
937d6382
3e0a998f
f2af5462
64df59a0
9a9d533f
9e2bd916
58084d1b
d638f9c7
04d186be
1b3b2b54
48a8fa7d
d20ab14c
c4b87b7e
0ec6efbf
@@ -0,0 +1,260 @@
{
"profile": "H264PROFILE_MAIN",
"width": 320,
"height": 240,
"frame_rate": 25,
"num_frames": 250,
"num_fragments": 258,
"md5_checksums": [
"a5dad6170eb13fc5cbc6fe3511d44053",
"e056362baaf13dd0f888e67a681ab381",
"ee0c33d2b92e0443ca5770bd0c56911f",
"0c8f0226fd484358b69e9fce6294a888",
"a0809d811b6273bb63ecfdb74097e0df",
"0cada76917c6dbca2093352b3beaa2e9",
"63a5dae178cfa9e10fc6fdae7957f38d",
"7e473898faa27f0372c30ccf7c1702e3",
"6d3e896e1748f259207b2be30d24a0ad",
"7a8a94778f723b6a4da79be367577dd0",
"abbcff2c3d72fe2ccf8205c0e47142cb",
"4790caa46b6c5998d627d53cf8a45ed0",
"6d183cdb8d57e3c5cb5e5340c71214e0",
"2594ab487dcd844860fe120e92e9513b",
"1b365becbe416007e1fb269dfe2bf0d5",
"ae6953f149e85d1170a27e22cf8fea89",
"b6e8a55010fdd0a2a2f9b5aa26f24994",
"0ea165fcde3b1a71bbb2e16c615c9e5a",
"0dd539e2735c216bce229ada7e0e7722",
"9d6b6e80b820a773a16060cd73d0b047",
"f9375adb2bebacaed22305ba32af4583",
"9185b111100dd0431b23ed799ca02b70",
"bdbc5de6197178cc0ef70cdc07b5c34a",
"505517fa76a772b8af27670302b5bb80",
"df7a95b296257c8ec2c9fed399376631",
"8d8d754f34f3118fb0d9ca69da2bf6b0",
"f5d0ef57ef80fb4efffc2d4530f0d60e",
"262eee5c2f052fcff8d330c5aa62353a",
"90ff465117bfc3cc8b6363b204326a0d",
"dc2445266fec9498194b185f4dd41fb2",
"878f7dcb0df9ddeaa8470f73bbf0d1e6",
"848d8910f7c95fbe27bf00d26ce3fc97",
"e01547b08bd83922ccd677733ea21472",
"fdc049f772de5fb88493d8ec58bf808e",
"9f74486171798cbd0d4da6c6bf0ff00f",
"557ff971fa63589cd4ae687abb778e15",
"0fec7b663d136fcdab088178c7c240a2",
"65b1f1ae4a015b9da106ba97baa929f1",
"5e495f76502f2010c30c08d0ea7b6371",
"96067b4e79207c0430f68ab0a4be612b",
"5a5ff39ae498ba16009071e2b19a1047",
"cdf81c7f4c94d199cb252cb0f880ba0f",
"3e482b016234911fda0acb5a2b31b1e8",
"0eba802cbbab73ab3ea6c1713bf38249",
"4e3c939c00b2f439b9bb0e4f748cd693",
"7da28a3926f11c1ad21ada44919326b7",
"bb6ddd70a7c21190a9896deb89f0393f",
"3bbc5a5d4855fdef5cff6a72e080db59",
"8809185828397c4f188225a3a261319e",
"0e6763c0673aad271cd13fc5c6c5b05f",
"7dd4aa0e1a6ac5d308ca8437d3b33420",
"4b38d5173b9e9f03df4c7d02ae380f7c",
"a252d94720017908735d566322be9e25",
"a547bf19701974e914d556b55e5cc876",
"d9f65e78870600cd4b53cb19ecbde3e0",
"22d740be86b5ae270b34a6a05f306169",
"c7d9428660fdffccc6cff0a038a65c8f",
"50dcb76ce04decbc97e17897a03120f5",
"b39d19ae0f6a8dba61eb15f3a27512cf",
"bd1dbca640bef318da29f3d6de5bafc4",
"5eafb79ab0af0235020931cb2b411e97",
"050393b1311b31b5d711cb3a84fa0398",
"f8827e35815f52022a340129bc005860",
"5c6f844ab5b0fab77e98ea3e5a2c6d30",
"f29317ec99a14eeea363fa70d55dfe7a",
"9fe58ebd66d22be9a7a443baa2733f83",
"f7cdb42ccf66afa9cfe598fdd6243869",
"ec68961bf9e81cedb36269a0bf94c851",
"16b64cdafde9e955bf5930c4dbeb2f8e",
"0c412adf668372e1676957e58e876518",
"fb0179693e77dfa0e4873552f81415e0",
"9d31a15e3fe050695d0a7a77387dc7b6",
"737fa9ddc16c371672ab7c43645b69e5",
"3c0723cf264055cdade0834c1fd0c503",
"53b4f854484bab3f7fa8043729e14bf0",
"6fa2c2fd8930dfc46e37ea8a2a153ea5",
"a2d85585fa27d9f000aeed5da5d1722a",
"7da150a3fbfad35c7c7e233147f97927",
"f33db156c0c29a6125904477c7a52e0c",
"8a4b651ac53d128e8bd530f947ecc393",
"0ac0408a68e2fee3f359941c3c7664c4",
"0af391a381b14b44c61e67baf5716869",
"626d9be039035d65f3e6e40f3a1849b7",
"388532467f13b64b84280fc2df75157c",
"79ac74b267d1e31a9760e4797f3bd9af",
"3cb436415cc056b2d2e0b56c5ec99b14",
"66b2a9581b65a9f8381dab9b4e3cb107",
"13f8261ab7bcc21ab46848b861cab446",
"9fd2eb5e88ce9137ba774b8e5743f842",
"531d9b50624f55446efe0ce2eb168cbc",
"be08a1e2b214db9d3b8a7995bdf8c401",
"fbb6f7ff4a4fec07e4609e6292846873",
"70924eb573abb590615100d48f54ef92",
"7dc5a37f2365231da2145dfbcbcf4a1b",
"ca1ef1e6859945533b4afc4bf1a12ab3",
"289ee446a3b431865d539ca2d3a50d59",
"bc1d07902b4572ea615b6653c01f9b52",
"fae9de5e08b65986e9e43df1e5e474e3",
"ea4bb6045444a9c3085dcf546eb71016",
"162392f165140997cf35e436c03366dd",
"1a39e8d92602106ed428487c1e543541",
"383cb9b29e98c471ff4de80a3abfe0cb",
"cc70ac50536aa7b063375423dad34096",
"e2e11c7ade1b414be1e51f626b9939a6",
"2eb1b386c45d627ec119abbc84ea8dd9",
"e788b25c134e1a0f258af9a88b1e1e2f",
"10fb71dee1a7653cb6cc2d19e58b04cb",
"8d20c4bbf93fe920a470f4dfb7f2d130",
"9ff22a54b4589962f1d475a4d308f96f",
"b5f27d12234ab57a97fd8d8595738aac",
"6673fddc74d578fdf5e716218211b7ad",
"e1cc1721e9c048e480214a8cb1369f08",
"cc0d3ce9313fbc02005f4eacf08246f0",
"7e275f2e832e8b4518e2aec5925323e6",
"747af41fea71a7c443b58632ee06b6b7",
"fe8a0cc71b908e241692e36284c9e2a6",
"cdba33dbf114170b84ce01177b1eaa7d",
"0eb286cac90b1e17f4e02c8625df85b9",
"620252c40523dee3f0d3ed75d2d2bfb2",
"ab8165d3282c27f4166c44a3f947154f",
"25cb402b0681d921c819e00e5b8f77ad",
"8ff4db1bb234ca8f2f6f8f4e45dc5088",
"f30db0bbdee5bb63bdf04f5d944352a2",
"985a0d075da06266437d3257e85e6d0f",
"c53c0d7258484d806b908d2c69376688",
"9390be2306908ee719ed6135ae3e0661",
"3f74122552fa39da806b039fdcafe885",
"2c7650ec0a56db5dce916508157d282f",
"44f7fa9c7a71d3968830b2dc97005ed2",
"0e5a240b5b64b1f86db4f603248d2d48",
"dcc1268f74789b61f0120445d86dc3a7",
"1df397a99a63248fe2a283d9398e3bf1",
"72da4e7b2d0b9327bfc198ea86f259ff",
"7152dd7b90eaf1699d5d03a29f41fa96",
"ba8f38171542fd2de0e63fc4dd5ca4d1",
"452e1a61ecef97d95eedbda97f27626d",
"b5163a55f4d3da0cc563f2d8308aa8ea",
"0594c34726be28517ff0180cc826031a",
"0da52fffcf3ab88f9f1e9a0c5b6c277a",
"787b744b305354ac0127af6f7ab6e5b4",
"682d202d5b4b154c1e1d87fea635bfe9",
"b2dbacace18349a8c4bc4af6d0109caf",
"25fbe415ae8031b5f4260222520618f3",
"bab2de7762b7e0db9c8b1583b2ca2fe6",
"1d8b3418ff6170f7492f8b8098dfbe3c",
"89cf144aac4e2932d2b30683ceb8a5df",
"b952fe86a6275745f461e2f0243a8f2f",
"1c5e35bb8d3f117ad22b80c3208ab36b",
"8cba5bec1dbe1e9045cfc458fc3be807",
"e3eb152d091b689b142fb52fde68acfe",
"a90e523e20ef52a9708c27dd32a32e51",
"276c2cb2e4dc2dee97014b913d20ceef",
"bb1c76cafaed84030fbdf88cbd38bd10",
"dd7155ad9cdb7914394f376585716ce4",
"fd088933a0743c3f014c5cde815f8c0d",
"cb41f67f3730d052129aa534e4cae33b",
"349d01c4e208ae68fa5fbfa5c0b259c2",
"44571e3c02368efe154dc942a86db41d",
"9d50ad1788986f0e71aaeed49bfde214",
"9e102fe1eb1cbfd67370cfe2ec676c42",
"16eeab863b12b958d9da04ec16b76049",
"697873e7878031ba7e5fb57fcf8bb3ba",
"7d1c52765bd7dc87b01b96963085eca5",
"dbc235422231280b8b314ac38bf03943",
"bad7ce4deaf3ba172e30ee67770d5495",
"6c7b0cf1bcb5da8ac7805d722d4c8e52",
"92a923806b59470f83a1121c0a2b1282",
"790f8f46429044540879beec078c2b9e",
"fee37fef18b74dfc5fac61dd343297a4",
"adeb2bf5bddd3ee4c3977570f7957eb2",
"42da9b4d6561a9faa4646c1938665d8d",
"96ef3aa375d97217ac52921f11f53393",
"b4b9c2e6dccb82477037b237bc29beb7",
"78f5063f7255796d6b954ffa63f171bc",
"c48f05d7bd5865ba64bb670ff950528c",
"737413a29e706d1ce3cb47476881d72b",
"69da9015e20f16edccf22676850577b8",
"9b76caf25e9566b9a507d2bc9d381d54",
"c7820c8373e48a8219421ce9f01ab923",
"1482abead594e3f5dcc4bc73e5cb3ccd",
"ae8504362f1612eaf12fd4c99fb09849",
"de613970f131a752cceef714c8cee331",
"22a358e5278844f125e72d277397fc91",
"a57f9588335e6afe9b346a674b8c4b02",
"85e88cea5a43e5c7984df07a0b5bed61",
"28ef730a1eade5e972beba4e46273451",
"238dc0244e981538ef2c57c4667164a5",
"7dbead8203f5037fd4ccaf9e662995dc",
"a15b6b70fd457142da0841378b31b9e0",
"e0e51fa5868886349846a60b105721f0",
"afe444099e5c600d3ccaa8711c9099c0",
"67eca422c9f2301e0f9f00674392ff31",
"d21bc2ee616b28d5501334a56570b229",
"93d9d78079d426c690b6c3f707fcc7ab",
"50cc4faf4fa623f78f47865cfd638093",
"89457f854433da6e28fe3e7d921d632a",
"028a95319145051732f732422ad75473",
"ef7124eb0ad577d2fb7d4fbd67146242",
"d5f82be488a5bb797cbfe4449c96b13e",
"06e79dfe8b046868ef7b80eeca6f213c",
"52ecabe5b54e0bcdd9e61855b07403d9",
"ad0f3e305e4c98d31a7aa7b38bd23feb",
"fc8177e47267379c6d86cb66c70a9ecb",
"78f8589a7a57c08808c16bf976e97eb7",
"f32ef46b3a6364450a4c84dc59a3fdb1",
"6dbda2170360e7f601c76ab2d8054ef1",
"d7447d04d50b84be9fb685de827fb799",
"bd678e5ecd23870c85f0d3ac3b304548",
"caa25dceb0de2d4918b62bf290641b36",
"5e231beb0c3c7f29770db53fa311efae",
"dd68639cb5083b06a1ea1c850028cd6f",
"fa1c9525569aba0ef23804b5f586e78b",
"d75607ea7fe696715bf898aa537dc1b2",
"bb0d9294e588699ce1cf9a3e6a477340",
"ef2787371909de8fa59c9f45c41eca96",
"7835e9094edcddfb16b147525cd8792e",
"42dbe4b5aadf06ffca6f24b465b5ba3c",
"36f6aa385cd30d73424113c1a6ff0aad",
"cf2ac7f34a03a9e086858debebafb447",
"9816f6620aafc8cad3a17e089c0bb3e8",
"72db245f772dfcbd40489b96c8f2dd81",
"555b6cb91b77a820359dc17e3a85c7c9",
"88044cb9b0719ef254908df5694daf16",
"82d5c83ae707acc6a24c5e6ea8369a53",
"d6ab871f03fb9c3ebd2c16f7b1cd7778",
"7eb6daaedc6863c8ca86605862cf55fc",
"473e3dc83b70a0639735c211c6fa0fc1",
"3b249d9a5747aaae5d21b54cea85f9f4",
"1e270e073d634b47675716d555a285fc",
"7b2123f7dba790ee1fa3c2f9de87fc19",
"5e331a4678035837e9ed06957d13e05d",
"fc79c2f6c81d68ea24b5f66373ba1b6f",
"a29698a51f90cdeebe78133e9cbb741a",
"e4b88aad03803aa0e4432545b2f4d851",
"c214f459aa112b1d78bc37a599208891",
"b429e6cc68732d804d94929f89c31158",
"bb31ded86e464207d3b0b28b544806ad",
"92bdd6f9949b8ce7e610345551fcfeec",
"a0eccd9dd12fa381f36d0d877c9efa69",
"f205c3e0255e4ff75bd86d3fd4ff0fbb",
"950096d65b623715914809acf4c0a557",
"b87ef8d2938e34894ed1bbf609fb99a2",
"0a303937d3043c39e8904cc0ad181555",
"05458b392f6555d1343949122653d6af",
"d3dd90431ded20aae2f5ff0b65802fa9",
"34df02751bd5cc9efe159cb85d0e22a2",
"b32744532a99fc2657cdfbc85fd87be7",
"160a29e8413328fde6623493a4da522f",
"a2029dbf4304d50965dc2aad44b930c4",
"1c082bc656c7752f21118f10b8677a6c"
]
}
@@ -0,0 +1,250 @@
776f98580836e9d1f6de6cd5eaa26541
b99c6281ace0eeaf0e5718808a568c2b
133d598e8d3bc1ad6dad666799ef3b8c
a0a05e3c48e4351dcf08934e55a53147
79ecb0b46cf15db887390ee78403c27d
b8ad982874240fee89b026eab547af09
fb7de18f21faa1480d0f1c50b643b8a5
a750adaf7e7a955744c377f3ba9bd99e
3b5effc41ded56870353e19aeca14793
adacf75ba5386d3442d30c8092fb4384
0fb60fb5707a4b34c4a54f156c532423
bcafabeb810e85570de61e4bd1b83466
096823e6e69b2240cfc5f1c08a23d145
d182e6428b339c9ec59081c4c196c4b5
0c7edf230a9ca3112cf4895f2af7a835
f6aca8639f2d838dba473658547a2928
8eb680b4878e0c9e0bcf089b760b2267
478b442172760bf7c3c7fe003ad73f21
10e0f6e66e9739c36caae0cc2eaa97d3
046a852108fc096d1d553a6583590d4a
45d7284eafcbc5cccc618c12bc6af4c1
8b5426beaac95a80df290220654c1dbf
226d2eeae31131078f6d257e5ee89285
2e1774cd1b8776a772b6dcb69773c5eb
f31ec82bece107ec42c68a53b808e949
8a9ba366d610b45d268848d3176ea179
17a5556be0efb4a31949857a46cee27e
f2077c849aebec971fa1cbc99d1ee8ee
a067dd84f656e0367c4b3bad1862c533
8be7cef3ee5b1edca2902e0afb28a116
5adae2c59438976603e9b4d7da49e210
44390208c61cda26906a26c85264ed6e
222501ba177acc3aeda64927b0646d4e
0d5fdc758b4632fd7792ce22bcfe717c
144f01c1f347bb9f4dc27df962d37b4f
c88762634ecfa06f1d12a4ce8a025af5
2f5304392f987f4522e1ed7519475321
1cbef99d860f038da6b8b2769d6c8e22
8b5209b0fd0c84b96b9707c77778fe4c
d78469cd4fba6d81b440d1fcb6ed1a71
11d869672accf917e3f4f706f8f4a9d4
ef985cbc882bac5bf8639f8d82680e8f
96e113dc4f4d7526b49fb5ea0f8e901d
ce418516be51f76d27509e2fd550f986
7bd8928c90578e0d2fd8c9c1989946f9
5a09fc3b51daba42beef52fce8e15881
ab0430d72c7fd2288830fb99aae787e3
7a5b68603fd986c7f6027f82fce47cdf
305ec7419408437f8daeff01e3aad6cc
0038fd9154185e9f0a5fded4e8dca4dd
375019f32ed04e8484864f9fc51afd21
6d8fb78c18042b55884d0a9e8c7ed7ea
f83931a19f8de74dfb93082c228a73bb
04a585694299a82dc862b108f1691dbf
fbc073e705710480225f17d16ccd6527
1d9e07daeb879c11029397f80513aec9
3a1ba3f2da5307b17c68803bea449df1
8d2f9d9a9d1188dbb90b987b321b9cb6
57c52b47a13cc8aeab3ab005cceb45fd
c52eee2498f6d567bd48e5a7a4046b51
a401da6fb4349055cf343cd44a974497
2def25c6377a6c1b069003f838f6ec31
67331ed57c8a7f189727f84e9a5469ec
8d279d4327fe50f2ff21ee53eb6cf015
9e5e8223bdc1abbfba3cb65700452b70
eb211e3d53c1fe49e89aa6ad729fc13c
fc5a3e258868dfb2aedde494f8f475cd
1fe91a15c43a4ad8f7007234de8ac222
9d43fdbd942765778da18fa223f43cc6
733b6b775a4a0421fc091216705ef652
9ab696e0d1dac6105f84ea8ca9465756
d5686e35b935d8990e5dee6d1bcf4723
706aba57f814480fa8a17e078b6352f8
0e5f8befc07ff3317ca174fd587e25bc
f95f242b2e2eafea3f5a20cdf0c34d89
0f1a8078ce533787c9cb30c9429dd450
a226c15cc7e0c0b7e1c4de8c620c07b7
36edee26f6f02c1576019034648cd4c8
c79dea598375f06c3a24d75f5b77e8cb
eb6fd06a0916ba6d60be7f4ad8561c0b
d022fdb8aa0e7cdc1fd83680b9b8a0fd
b5bb9c5a4cb36aa9c6b4b58076268d3d
38416e5de6f0092bbf61efe8ed7f76cb
a2ce2bf815fffc1fca8d99a4c97afb50
6beb9dbfdc0edb74498a49c2d81106b3
7b13ce77fc66031f036a8667076790f2
569dcc3536bb44110d6b3a54bf0dee88
6e08f74630a07154df6461ae25a955c9
af8235bf965e972834a181f9210b9e09
f9e4b7b0ffbbb24431e2ec6b6b337436
d87cfba6630357b213316c6608583be5
ec3382387784e715ea321fc05ce95372
bc0fef2061b2e64ab5a40e0174a49ea7
91d25f646606b2f9992688ef4edf4abb
008e89f1070b0d8e2477d7e9fb9a86c9
69bf6be50b0c1f9305f2522a1b01deef
11db65d54b6c6b6c39062a3beaf82427
efe6a4e563d48c7c86341a0f30e8fefe
8df7408615732649f592c7830cb0f00e
2fe4ec387ce34336dd8b968dac027b62
0ae9182a07b24f27298e8eff4a74b541
c0215167505dc21651dbef4565d0ae9c
4453511aab349d4e9444949ce7b5c138
29921ddff3b0ba42de2b36249f06a70a
9e8fb2d42c7c90f64c346023186c1d46
76e252269587a07138b016f57d09d5d4
a3d3e21942d46be6f98cac225bac9fa1
47ebd96106eb758c2ab1d7f6325a46ae
fea2c0b6ff517b07aec90c975d3e929f
a58220f49b95e399365f5a9c083923d6
0ba65b271d8d95d5b1c4ba96a6281e48
980589673748a3e2cfd37920e330d1e2
5c800089bdd1626fb2d3b9b51520b2a1
5aea26dea4436520f9643ed92409daae
48499bbd0c06d1873249ddd2a5dc76a0
6905e3dc0ee6930b3678d56898bfc5ef
76c17247950a478a67b6adc5e4baa195
18c123fbb6833207b27e7a400529a607
9a41b6d8f3c52ebd3a1517fb822d09e1
10cb9cde61fe48ed38bbe9d50755eaaf
1899fe52ca9524948a641acba633c087
03b254591b13d993575b960b8a5e5bfe
2cbcffec5b410eb6ecdd05c48e7f0526
69fd3b0f432bb70404a2a8deddd33856
f0a8ad93606ae12b229aff1ec0e2c3ee
580ae926ff57ac9ae54f35b000012b97
87e3e0f6204e2ad502eb5153c65b2632
d129b9408e42287088d7613d3bfbcd12
dbc5c280fb9001496ed8b45602b4de1b
f9c1efcff8549db819571d9a6cd55480
9d0a444c988a2027cbc41c2163be63db
1070020a0a6afb6c597d61b8524381ba
a21ab68bc243e08536feb2624647f7db
2e718c27e0d3266618907e40cc4a28a6
4669c75778da3184aba250b6310b6d1b
4e841515c3647e65a00b63dee84e67eb
f867fae5ebf1ed714c5a70f3fe10adbc
90cacccf6932ba8660e08b586f7597f7
cb0774848fc16d1536418ab7e5ada88a
c3e74fbbd564f993828e8cbeb876f31e
90fe0f8363b9a3fb33b9b2bb4db254a4
d38e11dcb17af7dd7e700355aa92e003
9bb3f94f84033c4937adf84586e2be95
4b4aedbc5dfa015b897b765e566feb96
acd1ceb766b0a4e15f25870f0244f549
32119d41be592c1f669ffd0b3a7888eb
7e7775b06fa9b72f771ec6b8d481a6d0
4415957d5a0d1526db1c19bc17b5d395
334f9ae780437dca2a5fc3377d46f6e5
35c3b43d748aad62f7410306ea3c3f99
1ab4c3340132c3f5fa6ef55ba7b08127
a61818d54edda2029762e87b9b431c34
1af968ff49189f25e28b9d8afcd46a92
80a8b78662dcd2aa4b1615b1325757a2
10ad96ed6e46f5b054603f66ed6bf29b
362457c91a605e04d3334b03b349ec78
7704dc866db3238023317a0eac5b8249
4231732950cb318403a45f38a46ad46b
a8482c9229879f410b309fc77983e3d1
45063084d5e5dfab462adaf5fcb43795
5028d5d62a02bf9c6843602c9843f54d
bc0d1e086e96d1347cb800a1d1aae611
343d41fca235543df80e8ecee45f2248
e68ed7bc5e4f4d5b92202011cdcda940
d8735dcc4a5b6f28f8724616e256b759
c261e7a13a9b507af279ed948acbe386
4d5af215399a10792b02e6a372aadb02
381abc836088178c39cc7da9b588b5a3
406e3372afbd23924539b43f6fb9dc63
032c29160e0529defc053d863b543926
eb4b9c324770d1f900191833ccc4c603
7540374c617d048db93719f2f74869e0
8d851c4ee3ed3b42a8f8abc37dc037d9
0cc60cb97509cd32f8de9a33248ec2a5
bcb5a48c5816ecf1c701c1887fd72531
2d0681c5f0c5daa293ac07c83ef0c852
936265a67adf4d3bc271d0ef0513c4bf
58e976105d9eb6e0715927143ddfad37
b6a82350ee6b0f00fb5323b32e8b8dd4
5cbc1b4da91aed315f7988ee9b4f6ae7
446945e9f46db22cc480474572957f43
8cdbbe21312c86754be28c1af186c85f
c696ac596e16de134f9026c2fc89fcb9
ac368b1a10748d0e18452527fdb67cd9
f1eb8157957c7a2a84b5310c2825c5fd
7fd5969b27e06110ee5eb52c0b7f895f
93a4c8093abe3a4e37cb317584adec57
3495c733a5c07428d7014799147f903b
5fb650661b0891f52cc9d908e3425033
399e846bf54ea08b854adc57b33bb940
5377988dae4eb18ac1d50e0add7938a1
f6f14c9520459a1bde9378e1faa59ca0
93cce870e1a92b6ab841ccf2a27b2770
7f79cd2127ac7c289414e993005828a6
ed4d0faa60421d65cbefcc8ea9a83f00
8c6b723120855441c69ef4ddd7e5194b
9d675f33810dcac032f38e224e4bb65d
a99ad6ea3c77cfb3dd94f8b39a909a34
9d2f4e964ba17ef2c7d0496231b1c47b
a6f1e6419f610d1f33888bcc7b16c6d1
f7e92cd6ee5c8aa99f7be89fd4b9f79f
9f95bf8beae3f90eeaa45f8a6003f0aa
361311a47b6cefc08b8e8af37fd68038
afc33b5877411da4349e013560a15e17
3811debaef6b08f2e9338e9d04f6f02c
7fd97c64677f845e98a4a0dc97a7711c
7a7392b1f2525842393dba40e92077cb
24ec025a0d2e7c2a57ff336f0a9baba1
72f4ad6e07a788cd9356f01c92d94c8a
3a801f8e16b8fc40b03be96b9db43e08
c89e78f74a7dd4c649413129534b9367
026609682e9ef750ea8e3f196c017c56
2cc76327407153e015d1ae69f14f643f
9aa38da2abb78d2b237f89ccf3fd052d
e6f44e4485cef73ffa5a649aaeb92293
ed8a7d1abce5a3f45d771ded4b8d0ddf
b477e09272a812756950266e6d9d21a3
7f7822c20694075ba7f74a1cc58ed326
da4bd23e36c0cd2683ad3a08ccb92381
a9e2a88720bfa413e5e3b2fd4c1306bc
3e91bbfe96a7462186151f6e76e36233
8fb00375e1d7595fa4544359f349eaed
106d0cc1aad3fa1014b8a34ebe7a4a3c
1692448922564e97d70d04cc12d55e81
3537b9b91dcae2df5d3a09805a7ffc4d
111ddd889dc595e18f97a93e36f7d3f2
8e8875c00cfc5394e731e8c2d4846f07
3f728c9ea9954a093405040047271fb4
f93ef278c1c3dff430da16ee3596a83f
51c0bd2ac650dd07df5b3630aee3705e
f33a19dd85ef67cdf5a0d92f3f1fbecf
0a6a7457190fa0fa9989dc71fee2152b
f4b157a6ef24d5d2a1027120fc419a91
dd69ac1443293ea2f753eda6e94101bc
0f431525cb6ac56aa05484cf2a067303
4aa02c74b3054a4ae3b13a5fbdeccd1f
4f7c553a7830426c493b59685b371223
738fc54e143b460de42f6279a9ef1672
a437cbbd9abf89d9e8b1e69b4ef48b86
afe6cb281c9ce601a1f4ab4d673f734f
4756e9d1ebb100cdf77ff9e6f9d182f6
a65853ac0ade5103a5840542e6e16c32
f5abe519e7bf7078d4c581ed477b80bc
d75c1f88e47baeef857fc43e8ad25ba0
7200f686415f52d0d3ac0684eca69fb2
31674be1dabca7c3fabef1c7481d6538
4aa521f1811346604b61a29c658ab020
fd049c4f80a58b6e2f4aea4d02b941d7
ef6b331ba064b6637de7e821d5d6d935
f23fa47c8cc237fa2f878b0bfc508986
@@ -0,0 +1,7 @@
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
pub mod dpb;
pub mod parser;
pub mod picture;
@@ -0,0 +1,297 @@
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::cell::Ref;
use std::cell::RefCell;
use std::cell::RefMut;
use std::rc::Rc;
use crate::codec::h265::parser::Sps;
use crate::codec::h265::picture::PictureData;
use crate::codec::h265::picture::Reference;
// Shortcut to refer to a DPB entry.
//
// The first member of the tuple is the `PictureData` for the frame.
//
// The second member is the backend handle of the frame.
#[derive(Clone, Debug)]
pub struct DpbEntry<T>(pub Rc<RefCell<PictureData>>, pub T);
pub struct Dpb<T> {
/// List of `PictureData` and backend handles to decoded pictures.
entries: Vec<DpbEntry<T>>,
/// The maximum number of pictures that can be stored.
max_num_pics: usize,
}
impl<T: Clone> Dpb<T> {
/// Returns an iterator over the underlying H265 pictures stored in the
/// DPB.
pub fn pictures(&self) -> impl Iterator<Item = Ref<'_, PictureData>> {
self.entries.iter().map(|h| h.0.borrow())
}
/// Returns a mutable iterator over the underlying H265 pictures stored in
/// the DPB.
pub fn pictures_mut(&mut self) -> impl Iterator<Item = RefMut<'_, PictureData>> {
self.entries.iter().map(|h| h.0.borrow_mut())
}
/// Returns the length of the DPB.
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Get a reference to the whole DPB entries.
pub fn entries(&self) -> &Vec<DpbEntry<T>> {
&self.entries
}
/// Set the dpb's max num pics.
pub fn set_max_num_pics(&mut self, max_num_pics: usize) {
self.max_num_pics = max_num_pics;
}
/// Get a reference to the dpb's max num pics.
pub fn max_num_pics(&self) -> usize {
self.max_num_pics
}
/// Mark all pictures in the DPB as unused for reference.
pub fn mark_all_as_unused_for_ref(&mut self) {
for mut picture in self.pictures_mut() {
picture.set_reference(Reference::None);
}
}
/// Gets the position of `needle` in the DPB, if any.
fn get_position(&self, needle: &Rc<RefCell<PictureData>>) -> Option<usize> {
self.entries
.iter()
.position(|handle| Rc::ptr_eq(&handle.0, needle))
}
/// Finds a reference picture in the DPB using `poc`.
pub fn find_ref_by_poc(&self, poc: i32) -> Option<DpbEntry<T>> {
let position = self
.pictures()
.position(|p| p.is_ref() && p.pic_order_cnt_val == poc);
log::debug!("find_ref_by_poc: {}, found position {:?}", poc, position);
Some(self.entries[position?].clone())
}
/// Finds a reference picture in the DPB using `poc` and `mask`.
pub fn find_ref_by_poc_masked(&self, poc: i32, mask: i32) -> Option<DpbEntry<T>> {
let position = self
.pictures()
.position(|p| p.is_ref() && p.pic_order_cnt_val & mask == poc);
log::debug!("find_ref_by_poc: {}, found position {:?}", poc, position);
Some(self.entries[position?].clone())
}
/// Finds a short term reference picture in the DPB using `poc`.
pub fn find_short_term_ref_by_poc(&self, poc: i32) -> Option<DpbEntry<T>> {
let position = self.pictures().position(|p| {
matches!(p.reference(), Reference::ShortTerm) && p.pic_order_cnt_val == poc
});
log::debug!(
"find_short_term_ref_by_poc: {}, found position {:?}",
poc,
position
);
Some(self.entries[position?].clone())
}
/// Drains the DPB by continuously invoking the bumping process.
pub fn drain(&mut self) -> Vec<DpbEntry<T>> {
log::debug!("Draining the DPB.");
let mut pics = vec![];
while let Some(pic) = self.bump(true) {
pics.push(pic);
}
pics
}
/// Whether the DPB needs bumping. See C.5.2.2.
pub fn needs_bumping(&mut self, sps: &Sps) -> bool {
let num_needed_for_output = self.pictures().filter(|pic| pic.needed_for_output).count();
let highest_tid = sps.max_sub_layers_minus1;
let max_num_reorder_pics = sps.max_num_reorder_pics[usize::from(highest_tid)];
let max_latency_increase_plus1 = sps.max_latency_increase_plus1[usize::from(highest_tid)];
let pic_over_max_latency = self.pictures().find(|pic| {
pic.needed_for_output && pic.pic_latency_cnt >= i32::from(max_latency_increase_plus1)
});
let max_dec_pic_buffering =
usize::from(sps.max_dec_pic_buffering_minus1[usize::from(highest_tid)]) + 1;
num_needed_for_output > max_num_reorder_pics.into()
|| (max_latency_increase_plus1 != 0 && pic_over_max_latency.is_some())
|| self.entries().len() >= max_dec_pic_buffering
}
/// Find the lowest POC in the DPB that can be bumped.
fn find_lowest_poc_for_bumping(&self) -> Option<DpbEntry<T>> {
let lowest = self
.pictures()
.filter(|pic| pic.needed_for_output)
.min_by_key(|pic| pic.pic_order_cnt_val)?;
let position = self
.entries
.iter()
.position(|handle| handle.0.borrow().pic_order_cnt_val == lowest.pic_order_cnt_val)
.unwrap();
Some(self.entries[position].clone())
}
/// See C.5.2.4 "Bumping process".
pub fn bump(&mut self, flush: bool) -> Option<DpbEntry<T>> {
let handle = self.find_lowest_poc_for_bumping()?;
let mut pic = handle.0.borrow_mut();
pic.needed_for_output = false;
log::debug!("Bumping POC {} from the dpb", pic.pic_order_cnt_val);
log::trace!("{:#?}", pic);
if !pic.is_ref() || flush {
let index = self.get_position(&handle.0).unwrap();
log::debug!(
"Removed POC {} from the dpb: reference: {}, flush: {}",
pic.pic_order_cnt_val,
pic.is_ref(),
flush
);
log::trace!("{:#?}", pic);
self.entries.remove(index);
}
Some(handle.clone())
}
/// See C.5.2.3. Happens when we are done decoding the picture.
pub fn needs_additional_bumping(&mut self, sps: &Sps) -> bool {
let num_needed_for_output = self.pictures().filter(|pic| pic.needed_for_output).count();
let highest_tid = sps.max_sub_layers_minus1;
let max_num_reorder_pics = sps.max_num_reorder_pics[usize::from(highest_tid)];
let max_latency_increase_plus1 = sps.max_latency_increase_plus1[usize::from(highest_tid)];
let pic_over_max_latency = self.pictures().find(|pic| {
pic.needed_for_output && pic.pic_latency_cnt >= i32::from(max_latency_increase_plus1)
});
num_needed_for_output > max_num_reorder_pics.into()
|| (max_latency_increase_plus1 != 0 && pic_over_max_latency.is_some())
}
/// Clears the DPB, dropping all the pictures.
pub fn clear(&mut self) {
log::debug!("Clearing the DPB");
let max_num_pics = self.max_num_pics;
*self = Default::default();
self.max_num_pics = max_num_pics;
}
/// Removes all pictures which are marked as "not needed for output" and
/// "unused for reference". See C.5.2.2
pub fn remove_unused(&mut self) {
log::debug!("Removing unused pictures from DPB.");
self.entries.retain(|e| {
let pic = e.0.borrow();
let retain = pic.needed_for_output || pic.is_ref();
log::debug!("Retaining pic POC: {}: {}", pic.pic_order_cnt_val, retain);
retain
})
}
/// Store a picture and its backend handle in the DPB.
pub fn store_picture(
&mut self,
picture: Rc<RefCell<PictureData>>,
handle: T,
) -> Result<(), String> {
if self.entries.len() >= self.max_num_pics {
return Err("Can't add a picture to the DPB: DPB is full.".into());
}
let mut pic = picture.borrow_mut();
log::debug!(
"Stored picture POC {:?}, the DPB length is {:?}",
pic.pic_order_cnt_val,
self.entries.len()
);
if pic.pic_output_flag {
pic.needed_for_output = true;
pic.pic_latency_cnt = 0;
} else {
pic.needed_for_output = false;
}
// C.3.4.
// After all the slices of the current picture have been decoded, this
// picture is marked as "used for short-term reference".
pic.set_reference(Reference::ShortTerm);
drop(pic);
for mut pic in self.pictures_mut() {
pic.pic_latency_cnt += 1;
}
self.entries.push(DpbEntry(picture, handle));
Ok(())
}
/// Returns all the references in the DPB.
pub fn get_all_references(&self) -> Vec<DpbEntry<T>> {
self.entries
.iter()
.filter(|e| e.0.borrow().is_ref())
.cloned()
.collect()
}
}
impl<T: Clone> Default for Dpb<T> {
fn default() -> Self {
// See https://github.com/rust-lang/rust/issues/26925 on why this can't
// be derived.
Self {
entries: Default::default(),
max_num_pics: Default::default(),
}
}
}
impl<T: Clone> std::fmt::Debug for Dpb<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let pics = self
.entries
.iter()
.map(|h| &h.0)
.enumerate()
.collect::<Vec<_>>();
f.debug_struct("Dpb")
.field("pictures", &pics)
.field("max_num_pics", &self.max_num_pics)
.finish()
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,158 @@
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::codec::h265::parser::NaluType;
use crate::codec::h265::parser::Slice;
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum Reference {
#[default]
None,
ShortTerm,
LongTerm,
}
/// Data associated with an h.265 picture. Most fields are extracted from the
/// slice header and kept for future processing.
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct PictureData {
// Fields extracted from the slice header. These are the CamelCase
// variables, unless noted otherwise.
pub nalu_type: NaluType,
pub no_rasl_output_flag: bool,
pub pic_output_flag: bool,
pub valid_for_prev_tid0_pic: bool,
pub slice_pic_order_cnt_lsb: i32,
pub pic_order_cnt_msb: i32,
pub pic_order_cnt_val: i32,
pub no_output_of_prior_pics_flag: bool,
// Internal state.
pub first_picture_after_eos: bool,
reference: Reference,
pub pic_latency_cnt: i32,
pub needed_for_output: bool,
pub short_term_ref_pic_set_size_bits: u32,
}
impl PictureData {
/// Instantiates a new `PictureData` from a slice.
///
/// See 8.1.3 Decoding process for a coded picture with nuh_layer_id equal
/// to 0.
///
/// This will also call the picture order count process (clause 8.3.1) to
/// correctly initialize the POC values.
pub fn new_from_slice(
slice: &Slice,
first_picture_in_bitstream: bool,
first_picture_after_eos: bool,
prev_tid0_pic: Option<&PictureData>,
max_pic_order_cnt_lsb: i32,
) -> Self {
let hdr = &slice.header;
let nalu_type = slice.nalu.header.type_;
// We assume HandleCraAsBlafFLag == 0, as it is only set through
// external means, which we do not provide.
let mut pic_order_cnt_msb = 0;
let slice_pic_order_cnt_lsb: i32 = hdr.pic_order_cnt_lsb.into();
// Compute the output flags:
// The value of NoRaslOutputFlag is equal to 1 for each IDR access
// unit, each BLA access unit, and each CRA access unit that is the
// first access unit in the bitstream in decoding order, is the first
// access unit that follows an end of sequence NAL unit in decoding
// order, or has HandleCraAsBlaFlag equal to 1.
let no_rasl_output_flag = nalu_type.is_idr()
|| nalu_type.is_bla()
|| (nalu_type.is_cra() && first_picture_in_bitstream)
|| first_picture_after_eos;
let pic_output_flag = if slice.nalu.header.type_.is_rasl() && no_rasl_output_flag {
false
} else {
hdr.pic_output_flag
};
// Compute the Picture Order Count. See 8.3.1 Decoding Process for
// Picture Order Count
if !(nalu_type.is_irap() && no_rasl_output_flag) {
if let Some(prev_tid0_pic) = prev_tid0_pic {
// Equation (8-1)
let prev_pic_order_cnt_lsb = prev_tid0_pic.slice_pic_order_cnt_lsb;
let prev_pic_order_cnt_msb = prev_tid0_pic.pic_order_cnt_msb;
if (slice_pic_order_cnt_lsb < prev_pic_order_cnt_lsb)
&& (prev_pic_order_cnt_lsb - slice_pic_order_cnt_lsb)
>= (max_pic_order_cnt_lsb / 2)
{
pic_order_cnt_msb = prev_pic_order_cnt_msb + max_pic_order_cnt_lsb;
} else if (slice_pic_order_cnt_lsb > prev_pic_order_cnt_lsb)
&& (slice_pic_order_cnt_lsb - prev_pic_order_cnt_lsb)
> (max_pic_order_cnt_lsb / 2)
{
pic_order_cnt_msb = prev_pic_order_cnt_msb - max_pic_order_cnt_lsb;
} else {
pic_order_cnt_msb = prev_pic_order_cnt_msb;
}
}
}
// Compute whether this picture will be a valid prevTid0Pic, i.e.:
//
// Let prevTid0Pic be the previous picture in decoding order that has
// TemporalId equal to 0 and that is not a RASL, RADL or SLNR picture.
//
// Use this flag to correctly set up the field in the decoder during
// `finish_picture`.
let valid_for_prev_tid0_pic = slice.nalu.header.nuh_temporal_id() == 0
&& !nalu_type.is_radl()
&& !nalu_type.is_rasl()
&& !nalu_type.is_slnr();
let no_output_of_prior_pics_flag =
if nalu_type.is_irap() && no_rasl_output_flag && !first_picture_in_bitstream {
nalu_type.is_cra() || hdr.no_output_of_prior_pics_flag
} else {
false
};
Self {
nalu_type,
no_rasl_output_flag,
no_output_of_prior_pics_flag,
pic_output_flag,
valid_for_prev_tid0_pic,
slice_pic_order_cnt_lsb,
pic_order_cnt_msb,
// Equation (8-2)
pic_order_cnt_val: pic_order_cnt_msb + slice_pic_order_cnt_lsb,
first_picture_after_eos,
reference: Default::default(),
pic_latency_cnt: 0,
needed_for_output: false,
short_term_ref_pic_set_size_bits: hdr.st_rps_bits,
}
}
/// Whether the current picture is a reference, either ShortTerm or LongTerm.
pub fn is_ref(&self) -> bool {
!matches!(self.reference, Reference::None)
}
pub fn set_reference(&mut self, reference: Reference) {
log::debug!(
"Set reference of POC {} to {:?}",
self.pic_order_cnt_val,
reference
);
self.reference = reference;
}
pub fn reference(&self) -> &Reference {
&self.reference
}
}
@@ -0,0 +1,3 @@
2904d4d2
d8e11777
a1108fed
@@ -0,0 +1,3 @@
c45648e1d3dd68913e998bd6ddb0f633
8a44f604b2518c6caa7bb422907c8d17
fe6ac1f24e248f3dba54087245380dd8
@@ -0,0 +1,2 @@
2407c115
396fe8d4
@@ -0,0 +1,2 @@
afcabbf0be007e76b1f496e199eb07fa
cebc9437bfc8501523c432d3fee3621d
@@ -0,0 +1 @@
a5a83a48
@@ -0,0 +1 @@
15caee73c93560200b9e240c6f5d7d0f
@@ -0,0 +1,17 @@
# H.265 Test Data
This document lists the test data used by the H.265 parser.
## bear.hevc
Same as Chromium's `bbb.hevc`.
## bear.hevc
Same as Chromium's `bear.hevc`.
## test-25fps.hevc
Same as Chromium's `test-25fps.hevc`.
The slice data for the first two slices in this stream was extracted manually from GStreamer using GDB.
@@ -0,0 +1,60 @@
97255112
c317718d
bd113d45
186209fe
75726ee3
e4a90dbd
1da8aec0
f537bcee
bdcead02
edd12fd6
9814391e
ea813192
8e3f606c
7298a718
c25ed8f9
54585aa5
c5d6811c
83ffc178
ad9b6189
20e3b289
ea852c44
38af63d3
222e116b
cbf5b144
5cc11d3f
561aba19
c1b0021e
97465654
3f69533a
95724dac
4fd1865b
00fc6666
262e9ecc
753e21c5
73820a63
6023ad07
6baf0154
6b658956
c7f3d4d0
b7fe7729
652e5667
76b64fad
98f934ef
64a737be
e561fe00
2bd0799f
53d86edb
07cad162
c4857692
9ab13929
c445eb2a
f0e890de
1db971fc
d03cc3fa
69d2cdba
172562d2
356c55d0
515bfbd6
5d8dfbc2
ec0874fb
@@ -0,0 +1,60 @@
e4519fce4b5a50a8ae38d2b42864e550
c7f6707ea7b2aaa3a9c027e00bf2064b
21c016eb7a57804d8637b03bc7910b31
f744d8b67d4836629beac1ca4efdad9b
8e593571ad75b9b19c3b14b44033bc03
3f76c8397f7829d948ddb50c7050b4bb
d91e343dbdb64dfd15f09abc4c460b06
62d5c1de7a7ff8b4cdfb51cb33746bcd
01a4c4d5f770d3cdc5429264f6a1a742
760de93c5a25439a89378fbe50c26f3d
d56f52f9252745321c723121824e5d21
be626892c70d6fd412d7bff504852529
ae49266f3b90dd1238f7ad83ae86886e
7a9fe15547cbcb51557bfd0b8370e0ee
a5b81f4e52fe54f67d109caa200486ec
34395343b832af13dbe37ebe745506e0
36d6e24c8a000b4f8d5c1aabba85c185
9be3b6e5b0b77c136b5abe8a22bded59
5122057efd33ed5634b755a7b0581382
abaf35e215001a6abe0dbc63f5b4ace1
62ba3a52072f80993c24cf9a3abda06f
21b225675443d0142e104ec88f7e9569
b561e6555b65ba6cebf0d73eefe4f319
ab2b1a568bed6f9bafe79cf15dfd85b5
e8c3645830f80e082e8b02e3cc599cc2
82d655fb233fea3fc3377b4d3dfc11ff
4a7c2a51149c1939cffc8cfc87fdb953
32295652a5ff998c90fa18fbfc066fdc
ce8974e2519a2babc43fb29106459cd2
589fa92d80565556776c2108f676c439
2bc1878534bf66caa6d686d0dad5a936
b3d81309bb62ed349bed2c0fb17a5593
c941ae94ab205aa1c27b9b06df4b57de
e782fa5f5a7302b8246c29880e9c2f9d
eeabb3a6c5fa1ea0f17a9fb911f5b612
26f27eafea985de929a48287162fc641
da4f4da7a16a4326c0c458f3dba12ef6
7697150fa654713af6a6ecc07ad7d3dc
67696b8c27169cbdbc18d093f794b328
96ba8f5d17e7b35e78f5ac78a816a364
1ab2c0d535d26c72d730e101600f42af
f8b9d8fc231ccd64c551c829ef25089a
eb004174e79dc8dbb533922639a725e2
fd519318eb8c47e5fac6fc767246bfed
471dbd17c2a374ab7dcaf64c345c1884
fc28a8f484573893f6f501a4ade8ac37
1e2dd38a14d5c13ef76e89d142f73951
0fd6a338f4ecb3df053d105bc95e6ee1
a42fd1e8bb069ab5c501fbfd655fc1b0
f252d07215ddd6687fb3019f2710c939
c8df7b9cbc16a26d782494b8cd312be4
966a2f3fcb066179feef80e68f13e212
b99b1100b6950144e70fa47310451340
9b3502c7dc1a4ca2579a29f6b17af84d
9a81be9720c64ce3b343fd20bc17bb14
e5a67048a9c1ae74b8a97c5a27d6d2b2
1527a719633c75b67ae6c36ebe550812
86b1329ec84332fec1a75330e7d7a174
6fa5b5faf93d70e96ffa215e4628ad9a
ed6279f7b90727abc8a193745170a318
@@ -0,0 +1,30 @@
0a6ac0dd
350b4669
61a8b9ad
f566d7c6
8d8ab332
52468dae
44bdbf5f
935e3db3
814109bc
68dc2234
d5b47d8b
852f3e1f
3f54c08b
9e33e4ae
047c4f0d
10cebb43
87774650
08adc4be
837797a6
17f80256
221e943f
e06ff206
7fb43061
e5dc2425
37928778
3a787cf0
14ea1afd
0331b84f
bdfa1606
28216ab7
@@ -0,0 +1,30 @@
037e7ff0ab754f16dc254edbecaf5bc0
47d9c4d6427726b74dd8586aeea147e4
1fae73f309640f704f8358fc0bae4c39
579c2fe2b1fa83ab3082e30daebba728
a4e01659d6bc12626d445a02fa5f977e
3bcadc5d9985e44338edca617fd84541
ac92b5ee1f79b5bda8f662eefab40085
03d24eba6675f51496b9338d97c39308
77f9d5f1e2e5bd40164336d9fe7970ef
355f2e0f8e8c4702ad45c790415004df
1d9563a8ccdf00b2116edb508b665e7e
44eb70aeba2c5eba6b0ce931d6fff3e5
c5fab1da55bd8177342d4e174bdc1474
30f0d9a37231141722ba65c61bd59d09
a1e8fdc8b33af447e4486c17d028b706
11cd37f438399a595745dff608965aa2
8025ff7ac2d9c9cda4039900834c1182
e934c10259100457b44bd13a8630a3e0
9f745c40aae4b6c31d57a0a43b39a4d2
041ea32d187a011baafda69c1f079b2e
d8a172fed23b9f0b4213bc7f8f1375a9
22e9e048f8e04410307068b6bc2e79d7
3c9d59aa49756060f8e78a5d608db8e6
5482d7c76a6ff60c9c32fffc27c39873
44a4d11b3ecdc827cc21ff732e1c7c7d
aeaaff7c5b23b4f03d939de6c12e5006
afa6ece7a21db66eece97e2e0713f29c
d84b7794f4a4968ea7fadf5cba7333d0
f9644eca3981a123b917938c7a4963fb
e02c049b9e516e10c05ce4469856e37d
@@ -0,0 +1,8 @@
#!/bin/bash
# Generates the CRCs for all .h265 files in the current directory using ffmpeg.
for f in `ls *.h265`; do
ffmpeg -i $f -pix_fmt nv12 -f framehash -hash crc32 - |grep -v '^#' |awk '{print $6}' >$f.crc
ffmpeg -i $f -pix_fmt nv12 -f framehash -hash md5 - |grep -v '^#' |awk '{print $6}' >$f.md5
done
@@ -0,0 +1,250 @@
97f807e6
ee7f1354
6fa0cec6
9285fe16
b291ad06
155fee7a
01427b08
37610d09
404fa256
2935ba29
fe05c389
f4330069
549eff98
5150e3fb
10a15359
0673d70d
6041a014
104cba40
47a31b16
b5bc8a04
3a16605a
da5384bc
156479bd
dc555188
458bffed
2cc6fc99
15abaa1b
bdddda11
fffa939a
54a83ce1
82ba8102
0ac14f5a
49e4c2c8
4ba7c6e5
44f0bcb2
c370a456
6361c839
5c0fdfc5
dcd0b111
adad6054
67b38f70
8decebb8
d64dacb4
4025f908
d7154e44
93fbddde
be5cf4ed
d4492e49
44d25551
fcfbdbcf
8698aa50
00025a9a
45f6d961
507fa2e0
bda2b113
d735c28e
13d54b15
2b0e2c70
fd1938c9
e7b016c9
511bbb25
52754511
f4c71342
9572d1c1
279aa775
bee7e9aa
0e916552
6bd17554
0616896f
f2fbed3a
f2c2f64e
b34fea4e
fb9f27d0
7bf93797
58fe6b72
d6dbb367
a4351497
6001755c
54e3ef07
04bdff16
f50bb46e
619d9976
c7438682
71186e2d
9f90810b
26fa0f46
e276d45f
de8947eb
98c35c3e
5cd09c8d
6ffcf799
d0af871b
b6f20a85
b6064e3d
f8af1a1d
6d749e92
484768ab
779f6743
f98c027a
1579feb1
8efcd176
b7ab75fd
e7911455
1878ec1f
db6c2d82
8ad48e21
9757e7ab
fd22cb3b
e441e63e
12bcd5e3
f76a42e8
261856b7
ab00bc6f
03c25d9f
65ef13d7
b66512c9
415b3d05
0dff93cb
cb541aaa
0ee6c61b
d6276a2c
900d44ad
0c408967
2d539b08
5479da37
298f03dd
bac454b8
81b81c33
a99b8c77
2cc2e8af
4053184a
24be8904
5c46ee1c
30f2825f
311a5956
8379cadc
16b91eea
9e765763
c1115845
8bf28db3
40b89f88
c785befb
b863d8a7
d65c8552
1571d9c1
38c47640
a04a8084
d0881cc9
e76534e9
1b7b3ec0
fa89a1e6
91a77797
50eabb2b
1ffea9fd
36da14d7
4d21ead9
0e9f3a19
5c3bc82b
e408152c
aff45223
4016bb65
9055be73
966f4f40
1b232cde
19d409a7
9cf5ada9
b983a480
4329c339
1b8c3818
0a7e1997
cebc35c9
a5483e3c
aa1ff50f
ad76567f
c1b9bd1a
8340988a
ecd60321
8c12fdcf
e3744c63
99c0fa4c
e1bdfb1f
a047f435
7e3b8ebb
106e6518
fb09be12
d535a54d
23e91694
1e68c0cb
53844c1a
c8180fa6
a58b3a20
bf8a3c82
fbd86177
27041f33
88fe20d5
c0dbae26
1677f210
1d40f687
e37f402a
35d23b41
8d0bd70e
1439b309
bbe21347
e03ae4c5
13b745d7
e5116c4e
70569ef4
c85f4731
b5b6de1f
9dc8c6ba
d7e7ec20
fd5fdeb9
1de6cf05
968fe4ea
c658ebd9
a3427991
1d5aa7ef
6ae06a27
41abbdb5
a9104c0c
db2726a1
d7f4ce5c
9ce1a83d
70252457
6744fda6
a5b475f8
5718ff91
d40f157d
50e7af4c
a49ec566
1eda24b9
b77eda17
aac39c22
2352e08d
a701b790
cd916c2e
5ef244e6
b9ee5202
4a8b8eec
60a8976e
f98cbcb1
d5a8094f
7b7c1585
c679bf1d
e6bb3547
4f05e0eb
d63111c5
b8b260c0
a18d9b72
a7c1ca74
@@ -0,0 +1,261 @@
{
"profile": "HEVCPROFILE_MAIN",
"width": 320,
"height": 240,
"frame_rate": 25,
"num_frames": 250,
"num_fragments": 254,
"md5_checksums": [
"9d45302f59628259021c0d580f38e577",
"cfafb8cdd693af7b24292d98e28d6966",
"a9e93b65a59b752063f259c40e7d9fe7",
"8b2445ed2bde8128fe090ebf112299b3",
"af2fb4344c89078811eea7979ea87be8",
"031f754980442cb3d8c59e83f637f16b",
"3d579a05e18b85c5bf8e1079ebdb76d5",
"9414c26b10b707ce582bea5e067be2b4",
"18599153c70945dfde292dae24b81521",
"7a324a7822e3a94df8b5a767f42ae5b9",
"83ff81efb040a2d247abd639a66b76b7",
"0e5b73811f510ac2ed2b976b27d4a3d9",
"787e4f184a285d5c3071c14085ca528d",
"8f9d7714962d32e211ca88e3f1c7f46d",
"a40888217689af118a13d33ba1acee02",
"0dc38b3c25ef2e35a1a5d000f24066bd",
"f8bfaca952ef6158cac2b4139079ff3c",
"04dd4c10c44fe65111eec52cef42b79e",
"52e07c85f11add8e08473228c97d9e96",
"5d2d01028f9549b67452343e1a37159a",
"3e86b75a1c9cd4fc82371091dc9270ef",
"091c1d95044708b1699a704f7fe00e97",
"566eabd3b2112b1e60687c2646a03d7d",
"7febb218fcfcd622e78cadc575e00d74",
"c661462a644194e0e3f48cb76180f991",
"8a98f0f76e624e718ab7ee1f8b3d06d7",
"a74cee3ff950de4dfa7c049e72372d8a",
"2003bb8048e0778cd7b684d3d290e238",
"9583a569bef2a181e4b14ef9d7515965",
"28fc9c431f8895224ee76a7a6a069644",
"1f86380ff7e1ce3a549ec193f5442915",
"8ce13d9e3659b928c7b768c470863673",
"3e842fddf70acc3989b99a784d2d3a64",
"2161a6b08f354b9213673af0f10595a4",
"706ae034f4622cb729016b5efbf71761",
"9ab6c4ec2708fc446bd6e57c0943f9ce",
"77905702a6b7b1ad3a364914115ae1c1",
"f35817cdbb7fd60cd39363f8116474a5",
"ae41c4d453cf07939f652f79c6f2cc23",
"938bd9a612612c55a018f6a746eee14e",
"72b4d96412d20cdecce2861eacdf7e85",
"f392537536b52626f5bc338f8d5f6261",
"e7efe9c3e54ee42cfb1552c512844283",
"2560af598073488d3c49ef8c59d6010d",
"8a78183c164f50e206a7f376264103ab",
"3f4235a1536c838c0bdc273314ded61e",
"5887551fbb1436f5bc9fa6e63b9d9da0",
"77fb5a36e251287e8b93e8c12279f54f",
"36c1d5aef11aa43bff133907478abc07",
"7988ba44232599cc54acaa71bfcac8ee",
"a420156487134f7fd8b2bffc86c2c466",
"a27f4e7c47212d83dce57ba52e4bf728",
"651da9f2f35798255fa618affc9d5140",
"736f41a6fc11267e14d865a44a5024d5",
"5a0c9f520021a5e027287905443326c0",
"838c3ef02a0d419f79b0a5cb96100712",
"5d135fae4da8f33e842fe1d56b771baa",
"9f5b762f5d0a9e6b4373aca87c04f872",
"6a28827f8b5217636a003eba52bff2ab",
"117254f82ee37acee64c1695da6efa61",
"b9c023f5a72cbf120872794f94025c55",
"70ed7dfa85fa637e446a4aa941f2b9fe",
"7dcfc63d25fe08bc05c0c1b919148b57",
"830c4e8958e84338f7b0a2ae8ecac5b7",
"518e7456c889f30d1a4afdf2d0618f4b",
"8a5aa2e84a77756adf020b9b5570a47e",
"906878b088da07f0b5b61e2975aed09f",
"6f8a49de618fceee2c2284037ccb4181",
"f0f751f49e75b38373c2a5a4f2a8d9b8",
"a4ae82030d893596129f19c51158e595",
"35d0a14a2c4086c4ca4f010d2e9f0adb",
"9c39936e95c658ada88bb6e8885f4639",
"11bac53610bf107cfa9a80cc7d40d7ef",
"3b1377f3a2bbc03be2a04ccce3f26d02",
"520e6cb06673ece706cc96db877bd606",
"8ab7f21cf1de8ce902f15ce812bdbdbe",
"f140e1564aac1f71728922702083f9e9",
"51828833d2bcb377fa0be61b7cb82579",
"426f15aa7c01101d16237eb33995e4bf",
"8f00d6311bb2ce1d290433126e480114",
"1feaca010259e859bd3363b5c1d04f35",
"2bb0e4dd7b3a115bc0f445497e35dc08",
"fc640243683109158e0dff5dcea50842",
"867f0ecd82aae7740bd9b251b9179350",
"85111785a9ed8b2b8b70b09e12154428",
"40ab1b731a9bf8f8e441c70ccc19b1dd",
"3100c669e824c8aec2b2d67561db1349",
"d05552052cd675c02caf5e1c671be35b",
"47fc5cd75aca27be5d6dadde030fa3d5",
"da2cc8eafa37e9a057e9a569ee541637",
"d2b4a325071110060e847fd3e8ef1f52",
"1dcccb0cf3a74b3153cb9b7c09a7f377",
"8c91eb6aa2594899c0101d537505a121",
"13abb158d7c22362ef1b73b5f15bfecf",
"35ad01fb07169fc0050cdb996de53d9e",
"d25ce524d14d95fd99607d42e13b7937",
"c2ff502083b013873ab203d2af83337e",
"68567f7ce7b9b19e5f85872442c54f79",
"5ae94fee674131ecfd45a6775854ec96",
"1c9f092f1e02caca84086b9873e1af89",
"6510b165141b38e47d7cdc23e44cf39c",
"913b03bbb72f8ccba56440632377b342",
"3cf908dcf12bd787901571e601af20d7",
"25518a7a40234f05661aaefea13518b2",
"6d3512a84091b9a8934da296e8dfc5c4",
"8ca79a3ace49c1126687674fffaaa5a2",
"e644740d09c3e2f3598fce3839497dab",
"8342af1710d8fec8f10a64da2ca9ea01",
"7dc733dc534e2cca2422027db2a3e34a",
"50fd7033960cf1d08bb558f7f7b7743d",
"0cab05f4bc4f716df0c741a34a128f3f",
"ff12d8589ee2ba883462db3496625218",
"cfb4baf656a92ffad4f8e29e086a22ed",
"6276eaef79a25eeb77e15c26121b522e",
"5af976088fc10e9b263bf12d27af2131",
"27510f90e0599ab31df00771f712cfe7",
"5e120f23d16415907b3bca93c61a7798",
"f0822f6593be64c890a73b5e19fa0aba",
"6f00bf23a01f93f6cdea4c5ec9d429ba",
"acf06eafb385d75956ac11b4394d8a96",
"99f22f1087710da1e13fa51638b1cd1b",
"206d93afe06167faf8560300ae7d9e02",
"f0ac45f756a2ea398aa8c5cc6557048f",
"314169ee8e31d3d654e6b60b63ea6b94",
"b709914ad6a828552323fd7814aa3a69",
"16eedf463a872c4e4f9645e2e7f731c2",
"9ecaeec7815eda54aeb7ac937df6688e",
"d3f880557ed0e69595d2887a879c46bf",
"79afbc938ea2f2e0968b9cf31c7d653c",
"4e76d541aa8796cfe4409da6f2d2afbc",
"5a9b81dee85caeaeb1e5eccb8a5017cf",
"977e990539d1cf3088ffe511f7346953",
"2e0e69b8c37d91453b95cd3e4635ab4f",
"3768fcaea9f36b873b054d02e7386a44",
"600c3f478e859e5114fca6188e0832c3",
"7a02082f7f1f104c3f01be9707be6978",
"b5ba5c7504bdee1e10d74c3ebdd6d6b8",
"baecb5bc56ac7ff840164b35bdf555c4",
"33584ab96f9c3b6d7808e3e117b5b8a2",
"3cfbd17bcdec301728b833feb9f3c7fa",
"74daa5525ff24e90e6a1772a8e3055cf",
"a37c2933e7445a765c8f3f18f3384c93",
"e2b7199fcef9de93d4296cb3667bceae",
"adc2b4e150ece651e7bc1174a78f79c2",
"8568addcb675d4086158303460594703",
"81a2b4bf06a0bbcc851779890cd73756",
"c3f3f15b6ab9e47ce9242e32ff4951a6",
"0495b5f57f93e37a3cfe3eff9f52319e",
"36ffcdf358c7a42c427a6c827117a428",
"9bc6ade4706bcd242b6b9b4a50fab72c",
"584b7f4b14d531f8c8ac045a505da5d2",
"393180967f07275e834e21127cf40ff6",
"5dc3b033f2f26a4c0f113c60fe2d30bb",
"157e3c22f8f6a319bd77a7c7e9689d96",
"24395b2b073cb0d7448a7eec6ecb5edb",
"6fc1d9efc86af03f5939f3aa1a1f9dfd",
"ded02a863d25e29f9d1a78e8953cca16",
"275c63d4d60e0bcb63613bec7d3ebba3",
"042a6db4563ec75d5ff675a1e0e06647",
"872755f0743242d0b8fdc42ac5b81d4d",
"0aee140a64076aa43c8f9fb74d18796b",
"ba738328a5377253b7067843fe48cbe4",
"ba4fcdd5085f5bb3b33420c0eb97b5ab",
"c393ac063ba4ee7370595f1528b00e98",
"39ef16ba57122f6dd3bbec8aa83c596f",
"ce7baa5845683d115d17e2822238ea56",
"afd200eb4ed02c2ff93a8e04503490ac",
"df783cda0056926ae9687bcdff01d6cd",
"24129bf695474e4944cbf79c27dc49d5",
"bb62851ee3852094c3722b71c469764d",
"04a64b8e4f5a26bb03eeed2d6e63e7ad",
"54732dc13d72d9f57580ebf69943d927",
"b88ad31f39b05cf5f305be2ddb073f6f",
"9cd5b43a79eb5ee177c8bba8ac15d168",
"950909eefb9170dee1d45b3969e00427",
"23e208e28a0bde09a3f046aa167bed38",
"ed2706a02ba04c9b4a1c1b8cfffb04a3",
"588a2708e07d0b338a2ed07cbea6af80",
"23059a68238c316e7822233ed2f6eb7a",
"e7cebf9ac14981339822d58b36cd4167",
"3428cef2bda3072f21dcc48bb54d3042",
"2110db4e61e860db243e6ddfd48e84d0",
"0f2ea09489ff2b0b7202ece325580741",
"ab6aad6bcb8fb77126ee5a2ebb039d23",
"36882f3914929a863054d0a216dc570d",
"e6b9a67f2314c460d0e8dadc84e979d8",
"582bdd8b90609e15502a5c9f70ca9baa",
"9fa97542b318a264b496d49c73bb2d19",
"8da8a03cf4017c330226f4718ed449f7",
"b7f15e01dca2d411491444b724ffbd68",
"0c336bd2a32f5fb269803611d3e623a4",
"c1b129d76a277373c6748927436794a1",
"f303f6cb6a53b5998b3df6d273e502fb",
"99b90cdc84ff584deff8b85be4de29f2",
"3beb71970b6ad6d42ad08ac2b077bb23",
"f4952ee5cf74f1a1c65ca51c1dcac9b4",
"1636578dcfcfb9fc7dbabe14e3f85322",
"20242c41b958e6fcaf16ce3c5e5ac2df",
"1f5398fde49a13097929bb51ee4c6c5c",
"6b426035d763bbfd14b24ccea8e7bfd1",
"66123deb830f5d697ffdfb48e8310ed6",
"189ba318c114f516bb9c7598319facb6",
"a41c4331dd165fed91a4520efefa60c9",
"96e3507c43dd3966884ba5f841673dc4",
"a4308628349e5b94d6151d128d62f8f9",
"24b9cb3b410c8bff36af293af5f1f6c4",
"c5f17e29d80e18551fa11edc9c533e3d",
"ca89232bbc3b590a12c21e4392437a92",
"150542ffea542c14a0ec1ca46add7d83",
"f0074cb65b559bc01f92ee0b2b2d851c",
"c20e6eaa9005a856a1295c5c43b849b5",
"61c579376b7d08d44982cd58efd2f2f3",
"cd7c18087349de540dc8c7ee53bca62e",
"545dda07c02e9ef635d10da90129712e",
"af193247eb37e7e2b237bec00a68fea5",
"a1a1368be1b97d9eb671dc59d2529e72",
"e51153a36891bc8c22edae72954af0af",
"82a2a2e229ee457b2a50e747fc05db45",
"6988aa461cb3dc59fb5b04aaff4af962",
"7617af042cf22ef700c27e3893878474",
"d84e5115883391ae8b4136de46eeb056",
"0674289ea495ed353988d4a2a39b1622",
"bbf870e8ff3e047fe87b37895ccb30a8",
"c62e8b15d60c0f45522c9607dcaef52c",
"6a2535d22f6c34f09868be21ada6063d",
"b54d58f9772149b9e40ba765a6f0c7a0",
"7a2bb00940c3ea825a5994fc6e1a5862",
"daccd8eb96035f7427c2ac984ef2669b",
"e93b78e2adbf25f11a13e4476cb5eb56",
"4621a7e39895cc326486e84f4000438a",
"f6cfda926b4214d72983d1abb8d7f0bf",
"15222df4f32a096b2178364382bb02b5",
"5825ed69a82645fda8a6e86b4c48aeaa",
"690fbef93df5dbc0316eff2a04e30244",
"0eeee51628e3a3d6943266a0a95bf4ff",
"7204f8130ec97d750287c03a613bdf23",
"fade1a84465a00a62c91325ecfdb788e",
"e061571ce0382db61922852144786a27",
"e369e19a152688b1dcc910784d41739d",
"bdc6190b3e945bea34219fc4a1849926",
"c0dd752a1b1143e6b41d411fd423ae17",
"08fe1c3af42005990a61685a2f35383c",
"029de50a465d0f816070c3d5f17bde37",
"bc75689a45409bffc566ff7f099030c2",
"2e577e6c6a15e33a7b3f26b01ab1497a",
"08038a3f13e60ba8bdde60a09ae429a3",
"1f6e4ac664308335b10cb31da28f03c3",
"e7c9e3c8ff869ffba99c64385dd58db5",
"855c9cc4f404f747d2af60dff6b3db3a",
"b6ae874fc675a3fd0f69cf36515cf8e5"
]
}
@@ -0,0 +1,250 @@
3aa1404b3c67f08effee1b22a1edfe35
126904a814874104c62aaf430100013e
57a22d9cefafaf079f065bfb4c30153a
5e03e8591340813fe3f84b0f02a31570
6ead9483dd1cb7e15c325525081aec55
379af5d20d2745f7ef89ee902651d159
d3ad92e815413ac0df5a152dff1513ef
a5a2774087b82062477b966c956ee507
6372aea236df163e92abf32a23955586
99f10be97458407433c6bc7e76e453c0
699aa7d9609dde914f7a315da43b85a4
b29a0c1424632d002dd6fc65a85ae702
fbb0bbe60d3574300c4324350dd9cf94
abd49b7181e7533e32d582cb89f4a890
ece092ba9c23de4fd597d1d590b2fd5c
71fbd2135bcb03651fa3fce07fe212c2
ab2f4e90aa360da6a67eb0b4b94d89a5
c3ebfa439f56a5d4a1274642ccf5fe88
e3646565b25d19be356d725c9ccdf571
10ad345a9199940edc4cf259fd740d67
b25f027b25711e949ca188e9b7c071b7
79f38a78fa14b251c3d13b25829be484
2f471f7f9e841bf8d0a71fec8c99edab
4386d538763d73d5f7ca8727aa0a1d0d
be88a7beb8bfd95c5eb03493f0a3d5ad
0beaeb365979f8355db831d8a39805c2
c854fb790d5189b93bae7f5a8ee3c634
557d551050575b0d7c36c103e587ed51
bf1c245bf54695757f00d9a04defe688
9f21b22a7e887e382b83a5fc070212e1
97f63b86fb8dcdd3eb4e80be92672475
9f6df014df10bd5983c15aebc5d9b0ae
9c8437e809cf388325101c45720d2965
caf27527cf3cdef68449a8515bc271ab
1d2ffaed9291656e526ba9f33cc6edba
85a827b0a35ed63bf1ccd33aa8fa2fbb
c8d67d86ea8b4ff56591288f3b354881
a690a754d9a51ae87a000f41d6157324
cb31f37e307feeaeff6aa0df28ec4a63
0ae07bc4b807aa17d831312ac7f8be00
4a73a078bab877b40f4230d905b2c657
4f99a4b8b320f540ac2ce76ee08a5131
0e8043071109eaa1f983fa9f89c9893d
9827495323c5df10b900bed10caac240
cf1e190b0cbe8a395630d9dbb8926f3e
aa8511fa4ddfcfb9c68d7cafb8dea11c
a49a2fc1c52f716d1fdb4eb75a148658
5f25b4f699445d8af438b77a58554eb6
734344be019030baf3801a2c238a3b81
9d82b03748e1f39e795ed2e7c8048233
26656158f2dc3e44b8ecfc302b6ba50d
2bdc0637fb6f1834bca11ed7a884d3d5
a4c41fb0b47337bec3f98732bc2d72d6
21b8fec70f82c45281e86651f3c10b4a
4bd700ca4c31f0b67c85a11d57013310
d3c6545314d53d68ba842d5f65784230
2f1826430cdc477c82bbf48c7178d4d0
11cb5dcc7932047eff832efca8dabff9
0d00bd23075c075240df89baf1533931
7b2e8f4c9ce1edd3d88613bd38e1347a
224d1d502dfe17e65280afdb0944a768
55f5e305c52979a4280310e91eb99c3e
7b021942409264048613a0bac9f261fa
98962ca6ee07c708b1cebf93d92ec706
5901a19c4a240037bb1b1efe7ee0afb5
6f49b4efe4155cf2e209c17d9e4bed3a
5fb93030e6f55756004b5abf3e5a9f47
b6f1473717ef4ef16cff8721a51f53ce
e66e2f9382528cbbf4041dc1b315e8b3
5104df3dd59e8f8c029ceb1da0dbe170
aac0cd3df566a3b7d5a72bc5202c7752
b97493d2bd5da6a25462f09dbe86a8a6
c9dc6f8e721de915864d6a39be93171d
c67a196f47a9d8aca36d33dabd1c60c6
f98dca89ac1d9da1e4ecd2f00748f8ca
4b223546c3f83f24c50ef023bdcfe096
4a8d486fb79d01706cd51a2c7d446afb
a25a13b07a66c230edd3ac6aa97f2d70
a478b43a9b2f7cb8086056ff981f3d82
3a86ca51898cb11bfbf47f32c4e1948e
d48f1ec0f0740d506553472b5038001c
1e0597118f13b08c78fcb8d3e445e89d
0d3a037c2ea82a7adf739987eb77a4b3
70cf1159356ad666fbff7eb33a4e220b
49aca942deb5340e899ec93a70c467bb
a4d8a4fe1963990af2167ddd193d2a2e
7da7b5197dcb138788d66742913d7238
a806177db926b0c4b469b52329c32bc2
3b0da86ca6b69266628b474ba4c6dbf5
7547a5b2c8b5d4613e81ee3b6d7003f2
80afa451394825bf9f61b000bd001857
40807c933935dd92abb61e2b2da1fcc8
508fdefd102f9e1f7804d39eae916655
91d9ee56f07f46d1ec191e662bd935f8
807c81583b0073a2746d752a4b1f42df
30734e3d8a710b269f5a29ce589b8a57
4cdde1ffffc3dabe5b7e8f5c0ff8f084
be6c0266091a2f3363dae1df8f724c9c
8858b0f16fb5babda61c4a6beb0523ca
16ffb13b8ff3aec78e8066cef06913f6
3aed790a2749291489b12520da443dad
c57ba43539016f901db55073386d5cd9
0f8f41751bd1fdc06b72c4d82d350d84
aef1772f3fdcef057f6924a40da7c2d8
f5f8b1460650b41f06cf8391bc18e426
a729195174db4ceea1973eb6165a9d6e
e5022f7d2feaf57faa1e38ff1fc5bf68
c694bf08869619a1bcc289127a408ed0
0caaeabd4abbcc6d3bbca0b8b5d7cdb1
971bdb6165a17e75773adeff5cf65fba
5bcba13d46fd61c73341d2d362962872
95bc9f0f82248551d9b41d799beb08ac
7495973421c51dd51958d54b542ecc29
7205d43bd4aeb3675aafb0b257d43136
0720c6b6c9936f8457aeb9172c030a53
6560c536d9c02d726f2f1d3737087ece
83dbde13fa583e0e6444dd6efb011b34
23080ac9e24b9698a2abf602ccfd2d09
ff9c9e733f7e8ba1d74fcc40b7f89762
65a541bfb561ec6cd945283e00175039
4c57d75ca3f96e852f24f11b5b9944f8
1e42dc0503db109faa0ce0a4c2d1eb5b
284fc104d59d56c359631879d4ca5ae4
a433eeff0d24300f106528ff3ee97dd9
8b3cc99a2ba8b51ab37ff8e43ec39d88
c883537d57780448f19b1fa22b731ddc
34ebc05648788fa25e14aecc00c941b0
2a522bbb0f652b825f4546bd483b002a
1cd101eecab9fa491786310326aa02aa
c34b70539d2cf79ba8c4814ca0b2b2f4
6a445c83d65f30a519d6f1257de3a5d8
7d006c2ced504c486d09fbb5c70d8f48
1ecaaf8b8b79c0134d224408feb4b12e
4f6fc08223fa794700c7fe2e13552aef
89c50b145e15de68a635acdc56118fdf
cf7d298515c15f7b969998211ed76d5c
22007d9a686417c64151ce79d890f1f6
091caca7499775a91fba09b45d0efeba
6141cbf235f3d5f9ee7921795ac12948
b5f9f13c06aaba11aba62edd8680a619
cb81aeb1845c13741d7306e05ec620b5
9e38b9005b5f87093069b0733186f92a
2cb31fe3452d1e1b188ac8071bbd2145
38e1dc763e76422f652b4791ab0d00a0
491418db6354101d9e9221f9a366ef4e
9dd1f141bac7936ce04b31d0d0e80db7
f53ea317e8e16c5c556454b5139c6a3f
e4b632d8bd400354cf8d03bfac61987f
61195150e317268a4d8809db46344c01
ff66c261ab7e00745d5f6dbd696dc4de
5e090a50613045d8f71460a9de77547e
83b8182ba7a6b77117eeb729f9d6fd53
941a4dfa7c33d3a84993505f406e58aa
5b2dab7f75283b9d05a1baeb465afb8f
ccccd7a54b1b162b47267ad3c633588b
b649cc2512d02ded5f8fb1a5e6c4e468
36d2229b2a85ca5eb5e627d4fefe5d0e
58f856aabbd3bb8df15d226c2c86f53a
6218b94752b4936b2a9332ad78ed3c78
46317cf4bccec620087f52c7da5af936
6f1d6769eafa76baa638f5eacd68bea8
240996e0719b183d04808ed92b36ec02
24cb70c1de6c09acb298836f591ccf60
62abeb55d923017366c3c0b5913b290c
2074b8a3aaed5d41493578617834f865
9bab5c6da1b475040a1ef3ad96039f16
61a3914b0a1c2b563cc6be02f7c52c39
9214566cdf649774366168904ba5a631
b29a21da4b59070f8938175cf982610e
712b8106393fa454db36d409829fd446
7303c34c4ee40273c96d97aa193254d1
34aac7ac642a02409cea8ddc0e79ca5a
c905bb704eb57a5803b5f628a1885e65
cfaeb64926d2cc463d2b039e25796e2c
631aa39682688ca2a9fa1b5ac046c84b
35e029947bb6ca6436f2287e56194910
9559137575c4bdf3e4e32794c30d566e
b26eb2e52cd85d5c62d02ceef8beca5e
eda954629e3db49ce785db968dfd7435
0c4db062f0a361a085e02a82c62ae623
25505fb97d5a024c6a8c756b94417c85
7145b53631fcdaf67d46f578dc061709
86196a74f056323957abe0db608e063b
4a2cec3448951953c76676511ce24068
34b6f3e81afd9002411772b291d376f9
0a889a8d77a4f7d001734e5590a1143d
b2dd18c9a76ff8df04202b70a08e8480
4966084a71252407273f57638148c878
a9a0c3cd10496210ad5f423c67fd388c
c7cf572ff8d7d859e255a1865012af47
2d3fad5c6403435842002f02012c23ed
e45d5b0fa1f296600bd8f6bdb91d59ff
b308537943d50f9cf6afb9289826f21f
1a6eacecb41a9984e8324900b7eafe43
cee6bc8991e350ff29b2002d47e473c6
fcb9de16c63bb0504dc97c3936f01479
20ba7f47ea85ec2c1ede90415f69f96c
edfc01d0213d0da38817b95c7f2090fd
e89dd0d6f9982683375fd43fc10f8d33
3937797142661006c3287488b3218b12
33f13996becd2161301afdc6397c0582
d8df3b5bbfd905c12c244c0252e49760
fbd6b8d0fe1da3e6fe5464720212b94f
b48262d193c09a00ba217cf056832696
ca7bb0ae9e2baca1669dee5b4181cba3
556adb32715d449d18032f2b1b0d5bb8
2cda45ece79f91c91d97dbecbfdb77ff
02be10a3e8fcef9ddaf31ec127117346
a1bd20b8e168dca0743acee02e266bb8
37959f49652a6a63c78700852ec32f99
612cbca363753f225e4236e4ef7cbf08
3593229c535f68e9285c13b91d2a84d0
76dfdfdcae9abe98b65b0183f56d81b0
b01c94b49b38b5f1b3cefcb0bb5baf4e
aa41e02d44472fc5bf4b7d70eedf6317
1f08d61e6780d63803337b2ee8120ac3
d96c5b0c7616a32308a3873a93055940
fbedfad1f84c85373ed740fa7d9faee9
8900bc3aea596ba8957fc57edd2590d5
660524d84aa663b32198423942525e2a
ce749f243fdaa7ddbf79ef3ee145f8bb
bb22ba1c54cb0c1535d64a1b947ffcff
fe1af0925d781c8e733d0bbb93234d6a
6d29a1bdb45482834fc37adbfa4de834
77f09e7535a1745cfde19b7e262b4a6f
cb6d2d2d5e94ab426166ccc46b8df0ad
a49a9e733db88096180a27411c66d211
3d7caaddb5f71b238fb2c253ab835396
7feab55c011e16389d52ee203187f55b
c06bc6b54bad2330d11c7ad711c8ee9a
49569cc2cd80e956e0bd0ee178abee42
392eadde235ec3f44c16e347040e7dc3
d358513ffb159bbe5d93f3cebf24f4e7
b2b6c35f846328ffe100c2d6bcff2091
868971765ea4221a6f8854847c65b2b3
3b0d0a5b426029b3ccc7eba61e4a8fdb
0230e17adf575c13decdd0d225fed9a8
24ccb608f3f4ee193f9553709ac112c0
8da1c9cddc689c17b6a40df060769c6f
52c6bd707266df417b6ca83e83befe49
95fea0bccde2991e6fe7476444517c3b
bd0cd099fa62bc0db33593e28c2b140c
04c8032d5f34150d4f011c6322fdc5c6
71735c0de711d3e34f053927e25c1e26
98853c3c86949059572bd5d711b91b26
cc9a35cb0bde5916476212203f7af380
5346c2a8f3fee6ff9a3068b52bcf590f
b13980ab3982fb964f80606acdf0dfb4
d3137b6453aa267bc67be303db77630e
e71431bb7d535ddad95ed24bcea40451
+10
View File
@@ -0,0 +1,10 @@
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![warn(clippy::missing_panics_doc)]
#![warn(clippy::panic)]
#![warn(clippy::unwrap_used)]
pub mod lookups;
pub mod parser;
@@ -0,0 +1,116 @@
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// The DC Quantization lookup table for bit_depth = 8, as per "8.6.1 Dequantization functions"
pub const DC_QLOOKUP: [i16; 256] = [
4, 8, 8, 9, 10, 11, 12, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26, 26, 27,
28, 29, 30, 31, 32, 32, 33, 34, 35, 36, 37, 38, 38, 39, 40, 41, 42, 43, 43, 44, 45, 46, 47, 48,
48, 49, 50, 51, 52, 53, 53, 54, 55, 56, 57, 57, 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 66, 67,
68, 69, 70, 70, 71, 72, 73, 74, 74, 75, 76, 77, 78, 78, 79, 80, 81, 81, 82, 83, 84, 85, 85, 87,
88, 90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 108, 110, 111, 113, 114, 116, 117,
118, 120, 121, 123, 125, 127, 129, 131, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154,
156, 158, 161, 164, 166, 169, 172, 174, 177, 180, 182, 185, 187, 190, 192, 195, 199, 202, 205,
208, 211, 214, 217, 220, 223, 226, 230, 233, 237, 240, 243, 247, 250, 253, 257, 261, 265, 269,
272, 276, 280, 284, 288, 292, 296, 300, 304, 309, 313, 317, 322, 326, 330, 335, 340, 344, 349,
354, 359, 364, 369, 374, 379, 384, 389, 395, 400, 406, 411, 417, 423, 429, 435, 441, 447, 454,
461, 467, 475, 482, 489, 497, 505, 513, 522, 530, 539, 549, 559, 569, 579, 590, 602, 614, 626,
640, 654, 668, 684, 700, 717, 736, 755, 775, 796, 819, 843, 869, 896, 925, 955, 988, 1022,
1058, 1098, 1139, 1184, 1232, 1282, 1336,
];
/// The DC Quantization lookup table for bit_depth = 10, as per "8.6.1 Dequantization functions"
pub const DC_QLOOKUP_10: [i16; 256] = [
4, 9, 10, 13, 15, 17, 20, 22, 25, 28, 31, 34, 37, 40, 43, 47, 50, 53, 57, 60, 64, 68, 71, 75,
78, 82, 86, 90, 93, 97, 101, 105, 109, 113, 116, 120, 124, 128, 132, 136, 140, 143, 147, 151,
155, 159, 163, 166, 170, 174, 178, 182, 185, 189, 193, 197, 200, 204, 208, 212, 215, 219, 223,
226, 230, 233, 237, 241, 244, 248, 251, 255, 259, 262, 266, 269, 273, 276, 280, 283, 287, 290,
293, 297, 300, 304, 307, 310, 314, 317, 321, 324, 327, 331, 334, 337, 343, 350, 356, 362, 369,
375, 381, 387, 394, 400, 406, 412, 418, 424, 430, 436, 442, 448, 454, 460, 466, 472, 478, 484,
490, 499, 507, 516, 525, 533, 542, 550, 559, 567, 576, 584, 592, 601, 609, 617, 625, 634, 644,
655, 666, 676, 687, 698, 708, 718, 729, 739, 749, 759, 770, 782, 795, 807, 819, 831, 844, 856,
868, 880, 891, 906, 920, 933, 947, 961, 975, 988, 1001, 1015, 1030, 1045, 1061, 1076, 1090,
1105, 1120, 1137, 1153, 1170, 1186, 1202, 1218, 1236, 1253, 1271, 1288, 1306, 1323, 1342, 1361,
1379, 1398, 1416, 1436, 1456, 1476, 1496, 1516, 1537, 1559, 1580, 1601, 1624, 1647, 1670, 1692,
1717, 1741, 1766, 1791, 1817, 1844, 1871, 1900, 1929, 1958, 1990, 2021, 2054, 2088, 2123, 2159,
2197, 2236, 2276, 2319, 2363, 2410, 2458, 2508, 2561, 2616, 2675, 2737, 2802, 2871, 2944, 3020,
3102, 3188, 3280, 3375, 3478, 3586, 3702, 3823, 3953, 4089, 4236, 4394, 4559, 4737, 4929, 5130,
5347,
];
/// The DC Quantization lookup table for bit_depth = 12, as per "8.6.1 Dequantization functions"
pub const DC_QLOOKUP_12: [i16; 256] = [
4, 12, 18, 25, 33, 41, 50, 60, 70, 80, 91, 103, 115, 127, 140, 153, 166, 180, 194, 208, 222,
237, 251, 266, 281, 296, 312, 327, 343, 358, 374, 390, 405, 421, 437, 453, 469, 484, 500, 516,
532, 548, 564, 580, 596, 611, 627, 643, 659, 674, 690, 706, 721, 737, 752, 768, 783, 798, 814,
829, 844, 859, 874, 889, 904, 919, 934, 949, 964, 978, 993, 1008, 1022, 1037, 1051, 1065, 1080,
1094, 1108, 1122, 1136, 1151, 1165, 1179, 1192, 1206, 1220, 1234, 1248, 1261, 1275, 1288, 1302,
1315, 1329, 1342, 1368, 1393, 1419, 1444, 1469, 1494, 1519, 1544, 1569, 1594, 1618, 1643, 1668,
1692, 1717, 1741, 1765, 1789, 1814, 1838, 1862, 1885, 1909, 1933, 1957, 1992, 2027, 2061, 2096,
2130, 2165, 2199, 2233, 2267, 2300, 2334, 2367, 2400, 2434, 2467, 2499, 2532, 2575, 2618, 2661,
2704, 2746, 2788, 2830, 2872, 2913, 2954, 2995, 3036, 3076, 3127, 3177, 3226, 3275, 3324, 3373,
3421, 3469, 3517, 3565, 3621, 3677, 3733, 3788, 3843, 3897, 3951, 4005, 4058, 4119, 4181, 4241,
4301, 4361, 4420, 4479, 4546, 4612, 4677, 4742, 4807, 4871, 4942, 5013, 5083, 5153, 5222, 5291,
5367, 5442, 5517, 5591, 5665, 5745, 5825, 5905, 5984, 6063, 6149, 6234, 6319, 6404, 6495, 6587,
6678, 6769, 6867, 6966, 7064, 7163, 7269, 7376, 7483, 7599, 7715, 7832, 7958, 8085, 8214, 8352,
8492, 8635, 8788, 8945, 9104, 9275, 9450, 9639, 9832, 10031, 10245, 10465, 10702, 10946, 11210,
11482, 11776, 12081, 12409, 12750, 13118, 13501, 13913, 14343, 14807, 15290, 15812, 16356,
16943, 17575, 18237, 18949, 19718, 20521, 21387,
];
/// The AC Quantization lookup table for bit_depth = 8, as per "8.6.1 Dequantization functions"
pub const AC_QLOOKUP: [i16; 256] = [
4, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, 128, 130, 132, 134, 136, 138,
140, 142, 144, 146, 148, 150, 152, 155, 158, 161, 164, 167, 170, 173, 176, 179, 182, 185, 188,
191, 194, 197, 200, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239, 243, 247, 251, 255, 260,
265, 270, 275, 280, 285, 290, 295, 300, 305, 311, 317, 323, 329, 335, 341, 347, 353, 359, 366,
373, 380, 387, 394, 401, 408, 416, 424, 432, 440, 448, 456, 465, 474, 483, 492, 501, 510, 520,
530, 540, 550, 560, 571, 582, 593, 604, 615, 627, 639, 651, 663, 676, 689, 702, 715, 729, 743,
757, 771, 786, 801, 816, 832, 848, 864, 881, 898, 915, 933, 951, 969, 988, 1007, 1026, 1046,
1066, 1087, 1108, 1129, 1151, 1173, 1196, 1219, 1243, 1267, 1292, 1317, 1343, 1369, 1396, 1423,
1451, 1479, 1508, 1537, 1567, 1597, 1628, 1660, 1692, 1725, 1759, 1793, 1828,
];
/// The AC Quantization lookup table for bit_depth = 10, as per "8.6.1 Dequantization functions"
pub const AC_QLOOKUP_10: [i16; 256] = [
4, 9, 11, 13, 16, 18, 21, 24, 27, 30, 33, 37, 40, 44, 48, 51, 55, 59, 63, 67, 71, 75, 79, 83,
88, 92, 96, 100, 105, 109, 114, 118, 122, 127, 131, 136, 140, 145, 149, 154, 158, 163, 168,
172, 177, 181, 186, 190, 195, 199, 204, 208, 213, 217, 222, 226, 231, 235, 240, 244, 249, 253,
258, 262, 267, 271, 275, 280, 284, 289, 293, 297, 302, 306, 311, 315, 319, 324, 328, 332, 337,
341, 345, 349, 354, 358, 362, 367, 371, 375, 379, 384, 388, 392, 396, 401, 409, 417, 425, 433,
441, 449, 458, 466, 474, 482, 490, 498, 506, 514, 523, 531, 539, 547, 555, 563, 571, 579, 588,
596, 604, 616, 628, 640, 652, 664, 676, 688, 700, 713, 725, 737, 749, 761, 773, 785, 797, 809,
825, 841, 857, 873, 889, 905, 922, 938, 954, 970, 986, 1002, 1018, 1038, 1058, 1078, 1098,
1118, 1138, 1158, 1178, 1198, 1218, 1242, 1266, 1290, 1314, 1338, 1362, 1386, 1411, 1435, 1463,
1491, 1519, 1547, 1575, 1603, 1631, 1663, 1695, 1727, 1759, 1791, 1823, 1859, 1895, 1931, 1967,
2003, 2039, 2079, 2119, 2159, 2199, 2239, 2283, 2327, 2371, 2415, 2459, 2507, 2555, 2603, 2651,
2703, 2755, 2807, 2859, 2915, 2971, 3027, 3083, 3143, 3203, 3263, 3327, 3391, 3455, 3523, 3591,
3659, 3731, 3803, 3876, 3952, 4028, 4104, 4184, 4264, 4348, 4432, 4516, 4604, 4692, 4784, 4876,
4972, 5068, 5168, 5268, 5372, 5476, 5584, 5692, 5804, 5916, 6032, 6148, 6268, 6388, 6512, 6640,
6768, 6900, 7036, 7172, 7312,
];
/// The AC Quantization lookup table for bit_depth = 12, as per "8.6.1 Dequantization functions"
pub const AC_QLOOKUP_12: [i16; 256] = [
4, 13, 19, 27, 35, 44, 54, 64, 75, 87, 99, 112, 126, 139, 154, 168, 183, 199, 214, 230, 247,
263, 280, 297, 314, 331, 349, 366, 384, 402, 420, 438, 456, 475, 493, 511, 530, 548, 567, 586,
604, 623, 642, 660, 679, 698, 716, 735, 753, 772, 791, 809, 828, 846, 865, 884, 902, 920, 939,
957, 976, 994, 1012, 1030, 1049, 1067, 1085, 1103, 1121, 1139, 1157, 1175, 1193, 1211, 1229,
1246, 1264, 1282, 1299, 1317, 1335, 1352, 1370, 1387, 1405, 1422, 1440, 1457, 1474, 1491, 1509,
1526, 1543, 1560, 1577, 1595, 1627, 1660, 1693, 1725, 1758, 1791, 1824, 1856, 1889, 1922, 1954,
1987, 2020, 2052, 2085, 2118, 2150, 2183, 2216, 2248, 2281, 2313, 2346, 2378, 2411, 2459, 2508,
2556, 2605, 2653, 2701, 2750, 2798, 2847, 2895, 2943, 2992, 3040, 3088, 3137, 3185, 3234, 3298,
3362, 3426, 3491, 3555, 3619, 3684, 3748, 3812, 3876, 3941, 4005, 4069, 4149, 4230, 4310, 4390,
4470, 4550, 4631, 4711, 4791, 4871, 4967, 5064, 5160, 5256, 5352, 5448, 5544, 5641, 5737, 5849,
5961, 6073, 6185, 6297, 6410, 6522, 6650, 6778, 6906, 7034, 7162, 7290, 7435, 7579, 7723, 7867,
8011, 8155, 8315, 8475, 8635, 8795, 8956, 9132, 9308, 9484, 9660, 9836, 10028, 10220, 10412,
10604, 10812, 11020, 11228, 11437, 11661, 11885, 12109, 12333, 12573, 12813, 13053, 13309,
13565, 13821, 14093, 14365, 14637, 14925, 15213, 15502, 15806, 16110, 16414, 16734, 17054,
17390, 17726, 18062, 18414, 18766, 19134, 19502, 19886, 20270, 20670, 21070, 21486, 21902,
22334, 22766, 23214, 23662, 24126, 24590, 25070, 25551, 26047, 26559, 27071, 27599, 28143,
28687, 29247,
];
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,41 @@
# VP9 Test Data
This document lists the test data used by the VP9 decoder.
Unless otherwise noted, the CRCs were computed using GStreamer's VA-API decoder in
`gst-plugins-bad`.
## test-25fps.vp9
Same as Chromium's `test-25fps.vp9`.
## vp90_2_10_show_existing_frame2_vp9
Test taken from `libvpx` official test suite.
## vp90_2_10_show_existing_frame_vp9
Test taken from `libvpx` official test suite.
## resolution_change_500frames_vp9
Same as Chromium's `test_resolution_change_500frames_vp9`.
More information can be gathered from the Chromium documentation:
```
Dumped compressed stream of videos on
[http://crosvideo.appspot.com](http://crosvideo.appspot.com) manually
changing resolutions at random. Those contain 144p, 240p, 360p, 480p, 720p, and
1080p frames. Those frame sizes can be found by
ffprobe -show_frames resolution_change_500frames.vp9
```
## vp9-superframe.bin
Raw dump of a VP9 superframe. Extracted from GStreamer. Available at
```
gst-plugins-bad/tests/check/libs/vp9parser.c
```
@@ -0,0 +1,8 @@
#!/bin/bash
# Generates the CRCs for all .vp9 and .ivf files in the current directory using ffmpeg.
for f in `ls *.vp9 *.ivf`; do
ffmpeg -i $f -pix_fmt nv12 -f framehash -hash crc32 - |grep -v '^#' |awk '{print $6}' >$f.crc
ffmpeg -i $f -pix_fmt nv12 -f framehash -hash md5 - |grep -v '^#' |awk '{print $6}' >$f.md5
done
@@ -0,0 +1,500 @@
30434565
3f479427
1a3815b4
003efaf8
a326e30b
5e48c696
4a95cdff
d094e6b9
f88fb600
3166c8fe
0631ba57
0f994c4b
fcfd01d4
0d6085ca
83f02696
9a6281b1
e477c455
83fb3e76
0dab98b1
2704ae4a
4c735dae
665987f7
fd3a4848
89ab7ede
99abfab3
c602e1de
22b58401
3cf8420e
5a33eecf
847e256e
acdcec41
ae5c98ac
cb7b8f57
5f58e096
8f5cf9eb
c2bb56a9
72708cd9
d60f26e0
ac56d0e5
56fae066
c8f77144
957ea038
75a28a96
602ba7b8
8dfa67dc
145d0c16
07d84b15
d5ff745b
89b9c4fd
476ddf3b
73e3833f
eb946288
39916706
a62f2ffa
b62fce6f
cec06605
d139ea59
5250ceb5
b75ccc92
c79f5e51
8bf78f86
2f55a022
0b4d2c33
ee21ac93
e5880dfb
98ebd835
d2bd50b7
e63409b9
c7f17438
b4671d62
6e012ab2
1d71249b
1cc7e30c
8ad0ebf3
b3bf9734
8f45ae14
185495d4
edcd3b82
b7b7bc38
e62517e2
3e5767dd
a0b59888
1517dc9b
213303bb
1c0e1d69
48849645
c41fe238
3423ddb4
e8e9bcb9
aca22302
85142fc3
034b1fa8
4fecedcb
6317d541
b098f009
7d13c5cf
b6b6127a
1ee289ad
3a3f70bc
a6b8e785
185c7dea
08025f9e
a6647fdc
1916ab72
d2906114
2c833567
86374614
7dc4ad6c
aa9e4b12
9a7e5485
4e89084c
b0e7e971
1c7d3e11
6f1f48ef
d2eb406d
4952e5ce
8ad0dee8
5581a685
f430f28c
18adef19
f81c45a5
66ecef00
6b8023cf
58d96699
5db8ddfc
2f8d6d4c
150426dc
9d14824a
69879063
465d1e5a
f4d03906
3bbb4545
74e922a6
be20217a
dca8cce3
7d1e9b5b
edcc5564
5d6611d8
0698b853
a62b3ecb
b34c5938
e26c121e
39cc64a8
cc34cef7
81cf2401
d575fff6
1a7f0bdc
f0c6d006
5b106cfe
9f2888e0
6f2a63ce
c1155ec4
12da69bf
c1dbed84
671c4163
5dad13b0
d2017cee
496bb413
30434565
3f479427
1a3815b4
003efaf8
a326e30b
5e48c696
4a95cdff
d094e6b9
f88fb600
3166c8fe
0631ba57
0f994c4b
fcfd01d4
0d6085ca
83f02696
9a6281b1
e477c455
83fb3e76
0dab98b1
2704ae4a
4c735dae
665987f7
fd3a4848
89ab7ede
99abfab3
c602e1de
22b58401
3cf8420e
5a33eecf
847e256e
acdcec41
ae5c98ac
cb7b8f57
5f58e096
8f5cf9eb
c2bb56a9
72708cd9
d60f26e0
ac56d0e5
56fae066
c8f77144
957ea038
75a28a96
602ba7b8
8dfa67dc
145d0c16
07d84b15
d5ff745b
89b9c4fd
476ddf3b
7aebabfc
5098261a
6be66915
2c0dac16
46e4498a
4bb011a9
1db45608
51d4b563
0c70020d
5fe5a5d3
9fd0b920
db24b6fd
2900f825
ae09ecfa
7c220368
58cb372f
b4da90fe
c62f659c
b6b990bf
665a5990
aad5e55c
aff234d3
c6850001
366cb3ab
b95a04db
f34882e5
93d19d35
37834afb
998b8b4b
ab26528d
4f39e702
5bd3b3fd
ecb5a096
d434b006
d44a715e
37f1c963
367b4f79
154ef078
8a909d4e
7cd512da
2bb88121
e2bfd733
dba85108
eb49fd76
e2c9f19f
25a38992
742619f3
e9a279b4
90c4de29
4ff923d4
e4b56000
1a6b4200
839fedd8
87650ead
0345d74c
691fa48c
cedb2e03
0b3e2577
5f5b60b5
95b19c13
633d49e3
2da01d07
a114b659
8080d857
f52eab8e
b5348da2
12453318
a6589d2b
189c908d
eb83c448
4e09feba
506361d6
72de5e28
49644af2
ec508bb2
efcd9e22
947abf26
47d491a9
f3819b93
19bf5692
a1a6ff76
ed33d700
c4def3ad
ef10c624
fdabd9d9
6ad10a8e
d719e93a
593c7aae
1e3ccd7a
bf625826
3f174d5c
222db35e
0884b2e8
f3b5be46
07794925
21c9233f
35041427
10d883a8
765d6db0
54dfccc9
4801da6f
06c7e866
69208a52
3381d557
c3f91b72
1dece653
0828a727
6b808625
4db6f222
64982c8e
5782aad6
dc1ab775
51ab86b2
c9f02103
4ebe8270
85d90d4f
62194ea7
9c4d98db
15e04acf
955f3ec1
6a9205c9
6a915ccc
013d43c2
4a7e4926
bce87d78
8db1db9b
f0e7eba1
cec75219
1cb9aa20
37163dab
b619ad9a
c3348021
ac44a4df
5ef80c45
cf3238b6
db148947
cfebcfeb
68c2ba8d
97bd3333
38b9a39e
88239b6e
21dada0c
3a3240ee
309c773d
782b8c8c
a18fa947
d96adf14
d130b2bf
13ca5914
5d0252f8
94482817
f859fd3b
454ca588
bacf1f63
9685ff99
12bfbea9
758d0396
d1dc7f1a
f332a5dd
95ed5526
320373cf
66ae44d0
d7f60521
174e8778
d8f1cb9a
505edb44
e218879d
8ee171c8
7f0d1f7d
00a0f4a2
b77fcb6e
9d9e118b
3ef2a6ba
bc3ec3cb
ab1f4821
dcb9c237
aedf52c5
5191f352
e58687bd
74584d91
42310f91
c1276911
c81f69ec
ffe3479f
0dcd9a22
00c1d7cf
6308e6dc
0a7a9866
032125a4
68f91bf4
e488c252
a7534dcf
24d336a9
98a8bbb9
caad1bf4
64f2157e
52a304ac
f15eb12c
c6dbedca
da11a1f8
be321a1f
ae891efa
0ec7ace0
ce45b0d5
77137631
5a0b7286
19498745
e1ba1f6a
9f76aa6c
c409dd1b
92536c65
bb9ef77a
2240c34a
4b698459
52bf0ddf
2b357e41
b821dd12
9c428cc4
f58cb68b
3f1adb2c
ebbfd9c5
f47f62d8
7305e1be
79ef7a93
a83ac98c
ee9fdddc
b2a237fb
22dab010
fc03b4c2
90cd6a71
7cfac9af
50472042
8d46b423
34ca61f2
01cc9d54
b191f0a7
64330c48
25c84ef5
e3d608b4
1faf320a
dc42008e
a4478667
54cc09fd
cbf1e2b5
82c03181
76a575cb
c88fe2a9
92f328a2
d792a66b
ae89fbd0
742429d3
1045723d
084976fc
ea999295
2c79adc2
ca4fb5c6
0ae9e41d
e3d52ad3
d453b8fc
eb24dbb1
1220c85f
5b631b96
eaab2c2a
c10c82b3
01cf2040
381ac249
431bc361
57df9be3
2010ccd8
6e5d770b
7934dea1
37a6d295
69b537b1
1c5b6117
5aef90c9
22c21587
f45a95f0
46ff5fdd
a2c00527
bf2c3a89
ca3def6f
40e4600e
12255f84
6439fa9e
89f0b37d
13f60bfb
6ede3385
e2eb309f
c1dd01a5
cecc9d99
d1845fd9
dd87d2b0
@@ -0,0 +1,2 @@
d5a38844026bf51029f97b815fb04b19
401c6bf17f34a1185d5381009e58ba65
@@ -0,0 +1,250 @@
1abf0870
32ab9360
fd2a8755
795c3488
05605011
c836c6f6
9811bd68
2ea553c0
2cc69486
9784cc5d
dc95c93c
9abb41bc
fbc1180f
a4f5e378
0ba301f7
49af5c91
1a33dc74
c3841cc4
3d22678d
9ba82c05
acd3f53e
5e147078
3710613c
55c0edc8
c4a734bc
ee06bb4f
57ffbd97
4e3b52e4
9848ca34
a5f6a263
7bf13fc1
d7cf6fcb
a49569c9
5b9b87a3
e1732f37
fec42824
1e8514df
7580ba76
e07ae78f
065a11ed
e254d9f5
67ca9179
b74ad987
8f0151b8
c9d5220f
34788052
c8269a62
98108c54
43db9efa
1d6e8a25
790b24cf
93badd6b
43276faa
ba4561aa
78d7e0cd
aa11db2f
98c23282
0d27f401
4450596e
a511b3fd
45aaf3f4
8b6afa2e
798ddc36
c16d2441
90096480
32d5f72e
ba19403e
e2d0ad1b
f5306855
184309d4
f1134993
49a1ae44
e724a782
76ecef33
7b48a1c8
80e678a4
14217558
21d6bf24
907ca9e4
71bdf4b9
be9b2301
bf953977
a5c17a0a
b7c27ba3
dd13bf91
600d03eb
fd2d8874
a2d8af0e
4c21a3db
48353f6e
e3e6b60c
ef3b9c28
1a4df0bd
af18c549
4e2da67b
77a51623
86b999df
f5b45a41
b0d5b913
46b5ed49
f64a266d
ff157d94
67b1e678
705a8536
0a7f2695
02c328d4
ddbee2a6
259fe83b
9164f5e4
f7b740e2
07026fdc
8887118e
ee734407
7544e6a0
d1cd9b46
ed639972
1c791217
52ca157e
b3ac782e
3592f784
ec3a0a53
ba29434f
0c9dafcb
4659b0c9
c645af06
5b9c4202
5a89bde5
c7e3498e
c202031f
51b8e7d9
bec84a33
163fe7a8
4e05e423
d1e3f398
dce754c5
795ccbdb
5d1e3e29
737f110a
20d97337
f112e2db
cf7148ee
c305f875
f18e07aa
bfdf8135
6a216daf
8fbe0aaf
639343a0
0e2152b1
695c3d1f
22065e7b
98acf61d
7a3fcb35
dcf38665
b7a69707
03066f3d
1ed4cf65
193be090
6f3ada5e
018ce957
0eddb953
9348cf1f
6e6d4172
28ec3fe9
529bfd00
084d2315
f3556816
cab6043d
0262486d
7d3c4498
61a96b90
1cfe545a
e92f9e8a
57d3eca4
45051fcc
c73e5a15
6bf04540
3e5cbfe6
e9315867
c59ffd57
a7eae6b6
ee526a3e
978aebb2
adcf6a3c
979a7dc1
fc3f7b6e
2e716a71
878e3d42
3144b8d5
84224d07
408a0c1d
af9a7070
c86e81e7
7e04a9fa
b190255a
5ce924bd
8799f259
29e5e7da
be77bd1c
8e001635
75f47834
fdde4db9
264b2528
5586fe04
c2a06cc1
4a3fb4a2
45f4f1fc
0fee683a
987dea92
83b24331
b4acfdcc
03679bf5
8f9ae6f4
5cef71dd
947f45b6
2aac1e37
028426d1
2dad1dde
14a9c696
c59d9bac
54de7644
094a7b74
ae7e70be
43d26c10
6232d8c9
cc65bf7d
59ee8a5a
807addde
e7d82793
7fe92022
4283f764
d9a91c0c
a77f54b3
02aea8de
b44fb9ce
1a116375
46304da0
247c2899
a9d9cb2f
88bcfc60
b2ba9844
333d1188
4c2f12ac
a4d54033
be8e652e
1a37abc4
2ca65b02
95d7a371
55840fec
ed610cf4
c0809111
@@ -0,0 +1,260 @@
{
"profile": "VP9PROFILE_PROFILE0",
"width": 320,
"height": 240,
"frame_rate": 25,
"num_frames": 250,
"num_fragments": 250,
"md5_checksums": [
"a6ddd21f5f4e7424b6e7a1f2925fb33b",
"41c77adcfd29abfaad62a057855adeaa",
"afdb44531614034e4a4a90c805a5d3b1",
"1fb247507d6e076feefb7281846d4938",
"597957a1bb001769a675d5be58db3271",
"c023f0ad8c3051e536ac9c9bb1d5eab2",
"64fd982393c290d092e1cda39cd429c7",
"915e863eaba1a7957f554b5cce006e1e",
"ace4ad6c26023dbf12f4c5e897e996f8",
"166ba862f99f7e0145f0b3a8a46c53a4",
"bf401e2e9084c80cb3c22d2d05dcc627",
"94dfc278df94cc355b6019096133befd",
"c88a98cb020b5cb9372b528ea876e3e3",
"7c05949db143606204b3ede34fbc0472",
"ebb8798f465fbe8083bd0255a41e17da",
"59e6ad68fb6e7c78383426c2dcb768df",
"57f2f71ab176f78b815182c107649b56",
"0713b3db71e138b41e1c64d5f76292e2",
"913558ea3033285c2ef414c29cd9e36d",
"f66bcbf90aad3f2973d7210ba221c5ac",
"02371beb715f84b4cc366ac78c9597ed",
"332355fc07a4eadc59ff5b5f0514347a",
"5bc08aa98cb182e58bab48967c26c938",
"cf1a6f1bc177d45bbbd06a21417973ef",
"c69f55a8cce9b1af971f96989c2ed50e",
"51b067bd3faf4b624e265dbf3bba6e43",
"9323f02a491fa931645c5add54109890",
"2fe699e08540e1a211e259909248f91e",
"e4bb4daddbdbc8c9c1b7398adfd2664c",
"9356a0ac6701f2231c6a12d6d3eccfed",
"597e71f32915badf51be8ffc255aab74",
"08a0dcd28bcaa495e95d15d2645d9c03",
"48742951820e0b22038c17f715f55027",
"df8b6480a30359d33f0fc5ff5f22cbb2",
"25d9f7a3dfc3bd6f7e581af20daa749f",
"fcb79ce3d922d1478e34399f29fd69a4",
"6de189116760547566fa6e3885a9b2d5",
"29da43f8a80e83a54db3c83016578bf5",
"b327993a3a9782630e8f38830b99f1eb",
"6a25c1b232dff00e78565f2886bfa728",
"fad26a4f6912ca0527c5cea767eca873",
"9d05717db2bdb502179161b9be1d4604",
"47f91eb36e06b5c7fff2c538f3026c39",
"a5611dd903d184a7ad3b3c5a02027694",
"9b977c83aafc80e375655d81406fb3d1",
"6eb2923e12b36b91b1d38a50c0a477d7",
"14acfd2a71f163186da78c84b34ce0b9",
"9f5395258aaff37e8c87b6c07df978b5",
"d5a22ec4e0ef752d3877afa7ca1db26c",
"005415db7bb57768a6ba92d4a43afab1",
"40cb57177361e07b523c95a206f20be8",
"e645c544fe2c8426bbe9ff1b24c6be8b",
"94a976505f27a90649f553dc7a193fe9",
"3b435d0e9e6af6ecf3676cb7299ef6d3",
"c169aebf19b7c6f3717ee0e097036507",
"c970f90040212ca7a392ca5cb7c8708e",
"243932bc414d8de7fb3c1bdfb4fcfa91",
"3ff138cd7f150eebf63a2f16a0ddc3f7",
"03ad4bcd723e5f2a3e96de33effac6cb",
"5416a2155944c3c23167d5a00e8e12b2",
"0620389d338d9fea5c6867f261f35452",
"377b5550cccafcd5e3142da159791add",
"68b1733c716380a04d0dbf21fc1f30d4",
"c9b872ed955bb13dc0819661f2b96692",
"fcc79ce029edff15bb7c2c5130d421a7",
"933967daa7eb201accf440799113547e",
"b64df022a685c95ca39c6c860e1f5ab9",
"8cb58810932a51ab29b10c691dcd50f8",
"5dfd36a6f1d476780724792b7a33b45b",
"25580f9904ff6397ddbf57dd93623aaa",
"77d61f46a7421182f43385fb7de3ad28",
"1e7adc2c2f99538d5c629cf906e82a65",
"60336a4c5b6dffcac7f3d42cd8c8d8cf",
"7ed47daa771ef93fc2eb05cf123184ed",
"bc03e5d85743ecb2b9f408c4814ccc03",
"5096b7c2eee1d8d7bf7fc825db35fc6b",
"38c7db4fb6532a9c827a6d2c0ca15640",
"93188336537ca3b189075b83afcb4304",
"4b0a2f9d16710b28e1b5b4f2a6757101",
"c14cfb8c07018c8926d849d0d6910d1c",
"d0d67b916206b75134f4b254d41a5747",
"2390a0cfa71bced2b9fb3637cff30921",
"3ab325d11b4014ef6f70734127fd4d31",
"9964596397e69118de09fb1e44fe01ef",
"89522f41b2b45984a1a27d54c59b41c0",
"b3270c89e1278984d133f4c2b7fc0a70",
"fdd6bb9f2b4b89584294221de8291107",
"79e5d6ec50f8d136e8a01bd0f150416a",
"1066212964911081dd41a8c184716589",
"5aff5ab98073ca06b2cd02b044ed2bac",
"52fa9f744f083400fa1013a9a296783a",
"ed586b59b27f1f3147d0c33cf94618e9",
"ebe31226ad166d53db606dfd46f65c4e",
"9208a53c77d7a69273cbbd386bdaf38b",
"5cbc9121a9decf62cdf538fc3b6ac6ff",
"de33f01b71d0b84bd6641864e5e03c1d",
"68459b19450133bebfe87e8658840d84",
"f93988c89aff87855e8a46a388231ea0",
"02a92e92e273e30d11c65a9c17afbdc0",
"194ee974b89f3c7ed85efc0b3067bcf8",
"111c441b0ce29b52370d79a7c040a319",
"c5c668728b0a0def951aabb873747c64",
"9fc4ce859ad1060df8583f11650e5e69",
"ac7ea5ac33d992834e18d5a80f7865bb",
"ede582c26d225a3cb903504a2817685c",
"4d491df588bdebd988d8d89e1f3aeae0",
"3d24041ab9920d06219f02b762d1ea92",
"5bd75a66e942a73ae09bb24936335869",
"5d335e05b9b578f104306a3577acbfdf",
"866816a72e249b6cf0f9f6e33b65ac78",
"afaaa9d734c02fedb83ebf6524e9bab6",
"d7d1b2774ef4ddcb191fc9fe6bf28c29",
"482908be24d0988e8e6b40df91c1bb1c",
"1752814ee8e6d3097cca487d503edb0a",
"128c52fa88a9e3df2b928519812fd3f2",
"cb41c09b32c25ad921c87538b62f70f6",
"a1be95f5d67cf17a1540557605398b5e",
"28fcc49f81a46bfb2fa1e39b7ddbef2c",
"1e6fe0400d792d432c03a6fbb7a346da",
"fd357048fef2312acd9bd1a84f08dd0d",
"2f4925cb7f740454ca31ecad6da072fa",
"721c71f2297e2cde48d23e2d14209b70",
"eb39d3512e79c54299a2f2360d001523",
"d3b3c4661b95a8102a091dadb3f61a8f",
"abed47e70a4fcea8b5905e27bc4d91d2",
"33144b402f5b60a0d7109727d678367f",
"347710490553e286bb41635a87d51440",
"e5d90bfd73c660c136f18708cf691902",
"2ccb92441cacb6fdc449092df990f7c1",
"cd457fae93314e50d5c2b7f1f3cec91a",
"d00e8d32b7b8e03211ec61f8329944c0",
"7481f50fca5fb8758a12932b40eea3f8",
"24cda7a1abbbd3f90286e529ad0ee446",
"fe49c757004f028382165c8ea24208e7",
"d903372b98ebe31896a591a1a6cabe0e",
"28f937e819685a1b5e73cd404e998fb0",
"5bd6d6f0037891cad42864acc85b9824",
"c5c6a4e219aebda78e3e9d2c91a48564",
"0a367f3982856c5bd984c2f866b255db",
"8d62ebc501823bce65b840cfedf2e75d",
"49b0c80de766ff81ab183cd611d1d118",
"da984ebe368315c820f2a1caaafd2534",
"710b15893183f93f2497c2e98ba56e0b",
"3ec5a99fcf6f43eedff8dd9995aa4704",
"67b9d9e89fb300ae3ebb03b396fee273",
"6194ca3c338f64f144326486d118e793",
"e85a130d9149590919e72a2f8ecc5f1b",
"62fbf66d14bc819353086e442c581616",
"bbc6b58d27c1623bc87dd81793dd72a9",
"5804683f0f89bdb8e43508e93ed2d17a",
"72fb2bd71150381878acc658ea547020",
"351e4493573d81f4b8cdbf4a94fa2dd5",
"573f6ebdef825cc73a6aefa219518add",
"f671a8002c56f70623b1d36f6d1ccce9",
"bc8f0deb204095ec70effde543c4f087",
"a0e79d0c3e9c3734c90471e17cfaa402",
"1e85a120057917174101240f66fbe12c",
"bde29019b9d44035c78ae593d882c0df",
"f1036a0a0190b198558aeae5c8539100",
"7d50b367eeac4a7ea9d0f797f3f286b8",
"133a4ed3fb42fe986117df1fe34f07d9",
"1e899dad271bb94f8d768c2025d527d3",
"258bcce7005861b2a6c2e547c81d66a3",
"30723accdb0788aea00ff42610c0dd99",
"a60ab6066b2b8258194cb10d8e10c206",
"321b86641c6e978e98b36ac367982397",
"be2b37e04fde984631e06ee04c8749f8",
"af1de1b1e1b4104aa99b563527828c68",
"c10f648540156e7379ed764ad424e233",
"716f1d5d894f0caeb6e060c365b1c68e",
"4da20c2960132d909767cb328a2c70d7",
"a5d0eb676eabcb9b808c1b7c6b312c3e",
"c06cabd771e2c42a9e2c3ca7738525a0",
"e87e67d2e0ffbd9762a4e99ddeba53be",
"3eb39b20537cd8cd12f15ccd6848f672",
"f3212f83868cbf07b06774c0283b25f2",
"746d5d5355f10892237e9d0dab554485",
"1956d40b46bf714d8bec22d784c54611",
"fba00831cfc6f6d948917c0e02a22ca0",
"c744b4f54702e34f47c0f4c4f40ea615",
"7c7adfc5fa5d03b5b7e07cd6e8293b4a",
"5825b5ba1960c3b1a88aa1e10e5f0474",
"d90b42024fda5312a0d886cdc2ce20f8",
"0592eee5f90d9dbd2ac17b03ccc0ed2a",
"2ff9315ce6dd9f4c6ec501c481dceba8",
"0a3ed552d91de9a403120f2e118cfad0",
"ee32ab17ec770aba340cb68e181275d0",
"3a36fa6fb7140d2354ba1ff2f1d287ec",
"77e8b7f41eb1cd46c389c9c3fecfed44",
"7f8f710b7bbd5a033300d0e4fb47f71d",
"28eefbb77d26f698bb658ed7f58cb17c",
"811d2afc4e6f5e8efcfc03563d1374d2",
"53d51c878f1aa62fb2753d67f2decc02",
"1a20e2c1da568ac283173c60807d7bb7",
"f77541cb2afd1633cb4e5294e99de8a2",
"e859d77a2cc87972cc7df32e6c625f00",
"18d8d2ab6cf205ce70316ce7a3e7e3b8",
"fa2e35da2ad12625ed5f50be46bdd61c",
"463169e7371dc37f3d082a5f166b904e",
"5e7f7593bf77b346cbf9906741623d94",
"60089de0fd61bf8547cff25da75d79db",
"609dff1ad553656a98bd6ea178f093c1",
"34ba858360b9943770e3b2c9594dbbdd",
"02dbd49e186b241f42c59f9f77d74e13",
"0632f6c62ee2b72e3dcd3ebbaf3f8e59",
"0cf0fe04e5bda8159176da2150ae56dc",
"ea48f3c5ee4fda7b7c44ad0f9aed7ce5",
"25a1a468110f7819dfa688bf08bb08c8",
"702f29070dae81d71fd4b6ec16766967",
"bb8e273c432a96b4c8fa92fde5210a54",
"702cccf5fe0f912c4a369e0c1b0d1d6c",
"bbdef9ec58db645eef1b52129e914dcb",
"3816969dc45df4e2d39db44f460bc0cc",
"db25e3bb20d5a30bf9170897e3075732",
"43875d9f264821d179f0d861cd5c82e6",
"467945c3124b2e4a4d65ce6a55e4f889",
"47ad1273bfc4b8575515498fb3e68570",
"8cf9200d7ce629344fad9e7772c5f099",
"a2d784b07f4f64ba41309138f408bf3b",
"aa7de9e0f6765a7b5a9dd6a37de1e474",
"4e82c66108b942323c95223d729731b0",
"4d7dc2ac3395345977678913400d671f",
"817546d45e1d74c5f5211002947ce94a",
"d4b41f15f106231c194db91bba1c9350",
"a61f9dd98999e8b90093817f280046f7",
"3a6943d566df3f8d9aa342554f2a30f6",
"1738e3cb6680ce1b09c9dd25b7b51d16",
"846b40c6d1023862b5ab86c558cc75e6",
"8a6156e1d440d1694e9df3e7c0b0a2dc",
"f6be779252c9d6d9409c94a899f23090",
"11086af958413f0ae757571535d7ecc8",
"701254468010c99da0d1f98a39099bed",
"2624e584871b8fed98168aaa31c7d264",
"8fec6cc28ab6d0072a9ded2861e13f96",
"700b06457ff7f99b47e5412c2fec7324",
"b34f8ff874d0c9e4730ad94cde30722e",
"814a5d8724ac31d3af0d1076c4d3c2e1",
"342befc9829aa640b36de1085e3849c7",
"e7879857be414f0215a5716dafcce729",
"874ea752b65d583b44615f23ec00e3de",
"8b555f7686aec7c61df83504275b0491",
"7a1763fab40e8e049c8320e603be4bab",
"e50ef5703efbd0b38266057a05b4e56f",
"014bb33e138fb10157a062cd4906a032",
"94224a6b01c4b088d429a78421b777d7",
"8bb86793ac81fc54f3a6c9ff354ff9f4",
"6b90162e6ba9308ac891770528a0b2a1",
"5a63e607041536fa3f633c769f0a9e17",
"3b33f8e78890e8a41d8cd93ebee7078e",
"fbb59915d51c7f56386777e70ba2dddc"
]
}
@@ -0,0 +1,250 @@
c147a6c80d9209e5a1e992d4e610a95e
807f9c05d06ccfb7f6a77018f52c07d0
df198689de44a69924d8cfe791e5dc84
856efa94272c5b0fabec7e1179563806
b5586069cc2a58a38d0271136949c760
5a0a2be53ff9bc19f131469bee430262
40374e2eabce3cb67b9f0d9992f31edc
2c0881752530fa3fec18f3aa3277f938
c86341015022064f0070cc8b70f2edbf
ad05d275a93b63de6327436b48cf9e80
4ed78beec93f00af0d6cf78cac200494
9273549b482b591a9da3543c490d4847
59b84f1b3e971e27212a8f690d8920c0
07b78dec9c8070b40e87fdaa18b4627f
ee7e9db173349fe2ac2c199831bd5eed
ccaabbed17e58a6f77da8a5b00f9f24b
436aada1f8eeef8b11bfb454310461a3
2bb8c17d108b4b5b9594461efe48ffe8
04e89161b5ddd1cee2850f19c1ea9af7
bf527e3dca6c046d5b0aba1cdcd95f28
564fce13d1ddbaa1d587bae33130e0f1
e7f84fd6b22935634144d85818ed0ce0
389109c513f164483e1cdf67d1d17e20
888e96338562ea5ac11882ff897bc45e
0734d839755f4b660e282fbdfd303da4
86ba362ca374dea486493877bc83830b
1549542e46f6c9a412779461b3b2e1c1
b90ec322166c72e41e9605f7a0c00e34
2e293965ced430e72dc44daac571ae96
8952e34cad20b1ad4460b0510c255caa
d6cfbb47f87f2c6d27bdec96fd86bc80
57fa0881469337f201b01d025c255070
e3c562edb81b8ed8bdda8ead100b47f7
60faf70b449db2ce46010a71d10e9a37
2d5fd1fa5fd07021be4be51e279496d7
8094a24162877f21e8e2c876631d911c
cc675882c3a3998f19875e291c1f158a
4a049974b54cdf78f45c775bbe21cfc5
6f78137fee79f8882b8bee2b5830aebc
25e75b7a102c32988bb676743d46da7f
bae608bf6d8b156669cc884ab37e6d7d
883651afc0bdaebea1a7906f51b1f07a
71047b3c17d1aeb3eb3cdad867a6cc1c
fa5b4d415cca2d417e1ee5fd2177eb6f
fe4dccf6135bb46db063b1b1b09a1b1b
4f111590afad4f98c70ef2f7ae2ab302
c39c4467809fdf8b396f2f4643c72a39
5eb42d6e49b71cc75748052104cdaee1
07398ff06268387fd10a956b12dff28e
5e568ba441a984e117d5a9362267e4f1
6bcbfebe110cc1014a325b0be3ebac52
3ad42d2755b6e9fdbd2d92abd212d636
a2a368e017d5cb835d0953a00a6bf2cd
c3955a9d37d474036e1457d486093aab
584fcc9af0395b6e6c3f28d64473bcc2
511a81a66dfd9c83b15c561a81fa45d6
62364a344fd1f9706c011a197c6b0cf2
3f1c26938b57f0973fb73c064dfa9bf3
dab959b019d40ca4114893d774945fa8
1c33008422a5351bc61423912fabc669
f20ec6445ed4d41421fb6f3fde5bbaca
ca43b98fd0e7c94c60b403f2acc2e948
a5efd2ae15724d229993842a9c919011
da547900d3e9eb01bf4765e2354fe88a
7259dc31f78637c9a70ef43906fa5807
be346c4bb4516f67883802c200179e06
8c8f6991b9124c0aa7dbdfad68bd2c9c
6049aa8416cf2a3b7df3cddd86b44e64
363eaac4e65bad5556a3e175d643123a
b6822e13f6db695c822afd73e0b989ef
61c375d0f9dfedc67304e920c08301f1
7a46f29451ca727ef7361420cffeabdb
80edef5ace96379c40db611885141c34
1697174088d86b17d6b372c20aecadea
148fca7be383caf8adf8433a25e4d4cf
c2eac8e9ca853bea1e76acbcd774e2a9
c8310d2b03328f17f9ea602df338d279
fe641d1b7be37c948e30bde65f609fff
00c13ad8d2766ab091d96960cf6609e1
44d4d30bdcffdab69e16d4f07aa66c9c
62fbf74f8cdca21c0c7814ef3c515a4c
9762ed61bb1c78c1dc84bd67d05c8016
31a4ac35433525329bafd764405058ce
6bf0c3b23613d7fbcb04e8b715eb96e6
a20bde62736096ad152544f195afc41b
0b1f0a21f64623b268ee55f8ecb0dc27
8d77996095e9a258d3505bbf17fda0b4
49eb262a315de652dadb7a22c1e8d601
d466dd053e522f653be20c2d81540c03
6e1afad04dd7f9f72a59c5a173686ab8
9726e527b395492dcb6ff72a0d5a4796
f272035189417682ab283889a9dc1fac
2bbd94a07488d59f8147388f0c394a40
f7d750c1a7a88d1684f42cb535430280
1ba751a04d93c7bcdfce9391001997e3
68cd83e38eb422ced19838e7167765c4
f003f95a69519492fbf6b08969a0c261
5701ccb9205417896a193a800d32c38f
b712418e2276267553b1056782a7e96f
1be965ee1da3c8dc4e1e5bd01f850ef5
02da1c68b982cd7a30e104320d04b36b
61e0a8d7df2d01909aa463701a3bab30
dcad9c4c4bb4855c2fc31c47f5a0c12c
c0de7fba39fcee42577dde05f4d8b0af
6052cb7ece90c1c64861fff7421a9d69
ef669757e94008168aff1646b5c04c0c
e03f821f36ca1b59c2054a5aa72cb82e
a32fee7e3e39f3e69f69c940a9782493
dfbe0be3fce78c0c6bea90058b5321c3
669b2200b96bbe5049a37ef88f86b1a2
d95206a7ecad0f0d154d59b8b81c398f
f1939cbbc7f9803b03551e0ee8e976e4
54a25909ecfc627c161ac9bd5dde0dd1
be6a415aec10a459061cab5f856c1dae
398d56c536ac7b051fb8a96d7c12e147
06b0b34464aacfb4221d4a1d17781a70
d25f64611b9d7bebb6124c2f0c39250c
94c2b637e08ee1ee8c79eb662a199450
7febb4a4f4b711ea70e86e7054b41277
04b884c1af9ac9806495c65de8dcb748
00dd0c5d65933dfa721a61e3683c54a1
ab6974a3b1aa3a510e84ffa04b4b3f89
297d608c848d13c71c42289326d169f2
89c514f843b17cc07ec1c4c08feabee9
8055e52cacff9cddfe49432d20d0bfd7
ccefb98fa05dc5863b6deb905ff1c3ac
11406e832f5a98f031c717a30d4f3861
f173396fc7e7bffd8d6df55551dcf51a
54d7421b70b4d182e3fb4dee07c70178
2e43573aace01f9fdb6a6dd6537407f1
eb6c3ca7906821794693205a27609dcd
fd686d26cde34b7d11527a9794813e3f
9b82ec0d251d5e294ccae92896b0bbb2
509ffb563baa8b528e463dd0c1606229
6344839db52a21e64cf93fb23a1cf8a9
a0781ac759a9017b75a8e6ee58261adc
00cdccc8b20608e9fadba7cab2a52aa4
d2f5a572735fb09ff0a2765366980985
9ae66ea9466618e8d30f3dfaaf05cdc4
8e18ba56741b48ce88c23baac7b60d83
1530c8f53fe1edd5e290eb70969c4e0d
9fd6cf8b4992dd5a83617fa0540fdb49
eeddde09d799b59f9676957095f7b545
9acb5a05373f57a9dca7ba745bea066f
d85400d97728f0850a53de94eaaefa1f
88ea3f31c331b394a01f6f4ea44dbe73
38ca4b29acc8922e012e274b2306b484
4d3ac7cc0f7df2363eeafc1d3916904b
e7307e525f8a833de862cd1b953757be
ee6208f9f33ed8680e2bc04babad0843
2e45bfc6975708fd3e7e0c4524755231
3dddbaf8b60bfc79492749d5230ec23d
713eba2ea4aebf9d73c525971d6e9bd4
396a002c7217fefd9c760f63ecfd0e44
1ba18897ba27c7913dc78eca0b465a6c
9f80d56ce61776d19688b71fa20e66b6
fe0fd66f26f13ab470e7696ed57440ab
7ac35e1a6f9de5a5247c55d9971ed02c
b45218ddbeaf8b1c1cc61836d49bf331
647c6c98430ad0f6304d1f7ab54b0791
a5b2c31a77130f10c81f9b1e8c0b36db
f85a9d6ab3fa171217d01c9eddd520fe
d294eb44a6972cd33c6c9ccd492ee4d9
c776f43b7b879779221a2cb6fe363062
dfbe59edcc434a5a2efe95aca98929fd
c5b7bc6f751ae1da4c4d9b596c208300
aee6993c994fe114192cd440f2529ba2
c16a35af9d237d8bb00bfd78d7910177
5b17eb3d2946acc9936f54b092236e18
35ab79392d55fe4383171693cc4f1b07
65bb08bf3f1fd317baf8249bc39fccce
6e9db766a1563d160a646d3cee97e82e
dd4c49639c8c3d7bfb950efeec7af608
32ba1749595850aa1f7e5430c168f1a9
9aefbafbc6e058822f743c48cbeea6e6
e73cbbb909c9a6e6e016b454313e00b1
b5cdf19a48d33b8476b5b751a2f0398a
6ab09966690b0ea5c87269d4a0547597
43ee16fe7b45c986fa99441b8c0a64d2
1ef3e648b93a33b31084f030223dc2d3
619b4e87d66c772be269e32443bfd768
f30c876850197446f344597f63299fce
7c8a639e3f8175fd35516a1f03517d8e
b487f19e64626e5982f52f8de4238732
4b41df592b2c341b9821c2c1b7385607
f781fa740d9c3148343e1a133ee1235a
e4e7c51802d9156046e746804d2c3960
3a63be1c6da417534b10a261604797ee
dce7b40962fa4ba57d0c2f3ffd5cd939
dace0df358316264cdedac24fe984907
6f6199ca0aeb0aad15eb613c37fd7db6
cce509a1501aaa3739dadcd1351d83d9
772877b6930a6f69b717a4d286f70259
d8f658e3732b7ae095c89d2cfc29fe2c
a5e57567b758e1636bba4bb605598846
7f0cc4258ce46fb86d310efa337d5dd9
3c9586b001cc1e61d7df35c4f793f8f1
94a0e6e24b394725a66b6c9c8ba3072b
4c01c47409d56a9ba57c421ce529b015
f52cda4db0cd197d5a4577cc3e1d7ed9
f2c479c08e12dc3db08cc803d0a2a74e
f5dfcc218ae104eba96007de1aceae2e
1a6e97ef60fd4dce42ce071d2e2eacbe
91e4d4e2361fcff17b342f4bccaab971
9d47703a6842aaac5af6c5d030518e7e
eece44627dbea68991083644fc2381cd
51869793a192ffcf48cf21da662ecc9e
163be4c547f021ca8cb28a846eebf391
344f9a7106be2be501b8e4058be63db1
fa2fe8613470850bc170ca1f12b290cd
b8de6ae393a5942f594106575cc6ba9f
14a91b5383dc58a04629135783aa9530
2eb55d859f402efccaedf26bdd5e7902
590f580c70c9786da51aa8c51179e56a
74fac2eaf11337dfb26e3526fca79fae
d88e4d50c166be51882d8661fbeccf96
88af6551d54e4ed178dce48233d2e5c0
28fa953a8f34f100e579ca2c7009f38d
985fd0061c8219c4696c77f6bf115b5d
bd145b09d36a4fe829198e996fbb5a94
c0fbb7395a9445007c6f48cfe663d7af
0af2f819e0d26a06f5e2fc093f9c543b
4a618df8ff7b52d9488d55640d455bb3
83042b532a5feaa5a5330c36c70484b4
39b3379e8a674abaf69c67fa2d9a309f
2510362eadc9668087a4464bef146a44
afd98f5903cf76c395904e0bb634b4aa
c8a6d4f5db71bb848292eada0ce9b5c9
4437d4d02ae857c35cafb4a86a9c819c
b5bc681646a7065caf5a5a3ec93b976a
7a7b91f1c78eba0b5da0203dabd3d972
b4346ff7c1cb9074e51c7f0ef962e2a0
d5958a235c658ef6af3177d59a2f980d
047e5c8dbf486d1017e335c38005711d
96a7fd8916f7b2bf5c192b2b52a5566a
5c555d4a1e27a0d72bfa06b1c655cd78
80d6238d61e3f2281f7567c4816b20b7
72160290ff45fe6fdb995555508fc592
4696ab63675996dc280b290f51b8d095
2d2440a00a59b3ec9edcd09e9899f30b
4493b8a3446daf20f34541bde3c24d41
0b4bc2afc7e9af942c1dc79fd120862f
1758e3ad8cf7cf842955968ef22c68be
5811fbff95273c5413591fbc96b07a0c
dad847f2b0916baded9b5f219b99c213
69d82cb277d48c60c9baf260b55c8792
8c4d98b207141ca2fa344fcc7415d164
a5421ea000aca629c3185e9270d34c8f
5dda27e72e1d5fff2614b14967aa89db
14e209d94a0bf732aaab43f272825d0e

Some files were not shown because too many files have changed in this diff Show More