feat: read approximate frames - #11
Merged
Merged
Conversation
With a list of frame numbers, `approximate` reads each one as the key frame nearest to it, when that key frame is no further away than the number of frames given. Decoding a frame in the middle of a group of pictures costs every frame from the key frame on; a key frame costs one. The tolerance is what makes it usable: `approximate=5` moves a frame by at most five frames and reads the rest exactly, so the error stays inside a number the caller chose, while `approximate=True` accepts any distance. A video with a key frame every ten seconds would otherwise answer three requests a second apart with the same frame. `Selection::Frames` carries the tolerance, so the type says the option goes with `frames` and not with a slice, and `selected` maps each index through `Seeker::nearest_key` before decoding. Nothing else changes: the index pass still reads every packet, and two numbers that snap to the same key frame each get that frame, so the caller still receives one frame per number asked for, in the order asked for. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The random access demo now runs in two modes. `Exact` decodes from the key frame in front of each number, `approximate=45` reads the nearest key frame within 45 frames, and the buttons above the figure switch between them: the counters, the steps, the zoomed window, the frames handed back and the call under the figure all come from the plan of the mode showing, so the difference is the drawing rather than a sentence about it. Both plans are built by `seekPlan` from the constants at the top of `main.js`, as everything else on the page is. The heading said "pay for twelve frames", a number that appears nowhere in the demo, and now says what the figure shows. The page itself was a grid of hairlines on flat grey. It is now warm paper with objects on it: cards carry a dashed inner edge, buttons press down, code sits in a recessed slab, and the strip of frames has sprocket holes. Two shadows do all the depth, `--lift` for what sits on the page and `--well` for what is cut into it. Each accent is one `--x-rgb` triple that every wash in every diagram reuses, so amber still means frames, cyan the decoder thread and magenta the GPU. Two typefaces replace the system stack: Plus Jakarta Sans for text and headlines, JetBrains Mono for code, numbers and labels. Both are self-hosted latin-only woff2 files of about 30 KB under the SIL Open Font License, so the page still makes no third-party request and still has no build step. The social card is no longer a separate composition that drifts from the site: `og-card.html` lays it out against `styles.css`, and `README.md` has the command that renders it. `AGENTS.md` now says to keep the landing page in step with the code it documents. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reading a frame in the middle of a group of pictures costs every frame from the key frame on. When a frame nearby will do,
approximatereads the key frame nearest to each number inframesinstead, which costs one decoded frame.The tolerance is the point.
approximate=Trueaccepts any distance, which in a video with a key frame every ten seconds answers three requests a second apart with the same frame; a number bounds the error instead, and a frame with no key frame that close is still read exactly.False,0andNoneread every frame exactly, andapproximatewith a slice raisesValueError.What changed
Selection::Framescarries the tolerance, so the type says the option goes withframesrather than a slice.selectedmaps each index through the newSeeker::nearest_keybefore decoding, andSeeker::frameis untouched.read,read_batchesandFrameReadertakeapproximate; the docs, the README and the reference describe it.Tests
Nine cases in
tests/test_select.pycover snapping to the nearest key frame, a tolerance that leaves distant frames exact, key frames that stand for themselves, repeats, the off switch, batches and both errors.test_approximate_frames_decode_lessintests/test_benchmark.pymeasures the saving: 0.018s exact against 0.010s approximate on the test video.The landing page
The random access demo gained an
Exact/approximate=45switch that redraws the whole figure, so the difference is visible rather than described, and the heading now names what the figure shows.The page is also restyled: warm paper, cards with a dashed inner edge, buttons that press, code in recessed slabs, sprocket holes on the strip of frames, and two self-hosted typefaces (Plus Jakarta Sans and JetBrains Mono, ~30 KB each, SIL OFL). No build step and no third-party request, as before. The social card is now rendered from
web/og-card.htmlagainst the same stylesheet, so it cannot drift from the site.🤖 Generated with Claude Code