CapView 1.8
Two picture bugs that had been there a while, a new empty state, and a landing page.
The viewer says when nothing is coming in
A capture card with nothing on its analogue input keeps delivering frames — it just delivers whatever an open wire decodes to. Asking "are frames arriving" therefore answers nothing on composite, so CapView now asks the pixels instead.
No signal has two appearances and they look nothing like each other. An unterminated input is snow, which is full of contrast; a muted card or an unlocked decoder is a flat field with none. Snow is decidable — that much contrast with that little correlation between frames cannot be a picture. Flat is not, because a black loading screen measures identically, so it is only reported after it has held for a while.
When there is nothing to show, the picture is replaced by the CapView icon and one line saying why, instead of the old text card.
The thresholds were set against real captures rather than guessed, and the first attempt was wrong in both directions: fast motion on a live 1080p60 source reaches a frame-to-frame change of 52 where a synthetic estimate had predicted 17, so the snow threshold moved from 48 to 65 and now has real margin. Snow also has to persist for two seconds, because a hard cut between two busy scenes can clear the thresholds for a single measurement and a viewer that blinks mid-game is worse than one that takes a moment to notice a pulled cable.
Sharpening actually does something now
The offset was measured in destination pixels while sampling the source texture, so every step was srcSize / dstSize source texels — less than one whenever the picture was enlarged. Both halves then collapsed together: the neighbours were sampled between texels so the unsharp sum shrank, and the clamp is built from those same samples so its headroom shrank with it.
Measured against a bandwidth-limited edge on a 720×576 source, the mean change per pixel at full strength:
| Window | 1:1 | 2× | 4× | 4K |
|---|---|---|---|---|
| before | 0.71 | 0.38 | 0.12 | 0.06 |
| after | 0.71 | 1.52 | 1.77 | 1.90 |
At 1:1 the two agree, which is why it was not obviously broken. Beyond that it faded out in proportion to how large you made the window — which is exactly the regime a 240p or 576i picture is normally watched in. Shrinking the picture had the opposite fault, stepping across more than one texel and over-sharpening.
If you had the sharpening slider turned up, it will now do considerably more than it used to. That is the fix working, but it is worth a look.
Colour survives colour edges
Turning up colour shimmer used to put a grey fringe around anything gold on a blue background. The tempting diagnosis — that keeping the original luma was done wrongly — is provably not it: adding Luma(rgb) − Luma(soft) to all three channels is algebraically identical to averaging Cb and Cr and recombining, to the last decimal.
The actual cause is that gold and blue are close to complementary, so the mean of their chroma genuinely is grey. No unweighted average across that edge can return anything else. Each neighbour is now weighted by how close its colour is to the centre's, which drops the saturation lost at such an edge from 0.42 to 0.09 while leaving smoothing inside flat areas completely unchanged — inside a flat area there is no colour distance to discount, so the weighting is free on the half of the job that matters.
Every second field is no longer dropped while the settings are open
On an interlaced source, the second field was scheduled behind lastWait_ == WAIT_TIMEOUT, which is only true when nothing else woke the message loop. Open the settings window and there is a steady stream of messages, so that branch never ran and the next arriving picture reset the field index before the second field had been drawn.
It now asks the clock instead of asking how the wait ended. Same shape of mistake as the timer that could not compete with the drag loop's message flood: a schedule must not be conditional on the queue being quiet.
Documentation
- A landing page at https://nuclearmeltdown.github.io/CapView/
- The README is a third of its former length; the detail moved into a wiki of 22 pages, one per feature, covering what the code does and what was measured to arrive at it
- Before-and-after images for the composite filter and the deinterlacer, with the crop chosen by measuring where the filter actually acts rather than by eye
Still open
The preview occasionally flickers black in places when the settings have their own window, on an interlaced source. Not reproduced here across two attempts — screen capture at twenty frames a second is the wrong instrument against a sixty frame preview. Stale scissor rectangles, undefined back-buffer content and repeated frame-latency calls have each been ruled out by inspection. The dropped-field bug above was found on the same trail and is fixed, but claiming it as the cause without a reproduction would be guessing twice.