Skip to content

feat(web-ui): automatic GPU deinterlacing with bwdif and shader-based content detection#610

Merged
stackia merged 17 commits into
mainfrom
feat/web-player-deinterlace
Jul 4, 2026
Merged

feat(web-ui): automatic GPU deinterlacing with bwdif and shader-based content detection#610
stackia merged 17 commits into
mainfrom
feat/web-player-deinterlace

Conversation

@stackia

@stackia stackia commented Jul 3, 2026

Copy link
Copy Markdown
Owner

What

Adds render-side automatic deinterlacing to the web player for interlaced IPTV streams (typically broadcast 1080i content). Deinterlacing runs as a WebGL2 overlay that intercepts each decoded frame via requestVideoFrameCallback, draws the result to a per-slot canvas, and lets the <video> element continue driving playback and audio — the MSE pipeline is untouched.

How it works

Detection — GPU shader pipeline: Content detection runs entirely on the GPU. A marker pass computes a per-pixel comb metric ((above−cur)·(below−cur) thresholding) and an abs-diff motion measure; a multi-pass 2×2 box-filter reduction collapses the result to 8×8, and only those few hundred bytes cross the GPU→CPU boundary. The CPU side just applies thresholds: an M-of-N rolling window (3 combed of 12 samples) declares the source interlaced, and 4 consecutive clean+moving samples revert the verdict when content changes back to progressive. An earlier CPU-sampling implementation of the same heuristic was fully replaced by this pipeline.

Fully async readback: Sample results return through a fence-gated PBO pool drained non-blocking on every frame — readPixels never blocks and getBufferSubData is deferred until one frame after the fence signals, keeping Chrome's accelerated readback shadow copy on the fast path (each PBO is orphaned before reuse). No synchronous GPU→CPU reads anywhere.

Detection cadence: The first 3 frames after start/reset are sampled back-to-back so a verdict lands within the first few frames of channel start; after that a 500 ms steady-state interval picks up mid-stream content changes. While field-order voting is open, sampling temporarily runs per-frame so the vote converges in frames, not seconds.

Two render modes, switched by the verdict: While the verdict is progressive the renderer stays in detection-only mode — frames are uploaded only when a sample is due and nothing is drawn, so progressive content within the gate costs almost nothing between samples. An interlaced verdict switches to full mode: per-frame uploads into a 3-frame texture ring, field-rate bwdif onto the canvas, and the canvas is revealed. Reversion hides the canvas and drops back to detection-only.

Field order (TFF/BFF): A half-height GPU pass compares each row pair's temporal-midpoint interpolation error under both TFF and BFF hypotheses — the wrong assumption averages fields two field-times apart, so motion amplifies the error. A majority vote (min 4 votes, margin 2, max 10 rounds) with abstention on static scenes drives the decision; sampling happens at texel centers so LINEAR filtering never blends the two fields into both hypotheses.

Algorithm — bwdif (GLSL port of FFmpeg's vf_bwdif): Per-pixel motion adaptation keeps both fields in still areas (full vertical resolution, no bob shimmer on static content like logos and subtitles) and reconstructs moving pixels with the edge-preserving spatio-temporal filter, clamped to the temporal neighbourhood. Runs one frame behind via the texture ring at field rate for 50p motion output from 25i input. Chroma keeps a separate motion-adaptive vertical low-pass to remove field-interleaved color combing from progressive 4:2:0 upsampling. The shader was reviewed against libavfilter/bwdifdsp.c and vf_bwdif.c.

Resolution gate: Active for all content up to 1080 (width ≤ 1920, height ≤ 1088), covering 480i, 576i, 720i, and 1080i. Above the gate no algorithm runs at all — no WebGL context, no uploads, no detection. The gate is evaluated on the video element's resize event (no per-frame polling), with a cheap per-frame guard that skips transiently oversized frames during stream switches before the resize event lands.

Lifecycle: The pipeline handles the deinterlace settings toggle, seamless-switch slot swapping (background slots stay at opacity-0 so rVFC keeps firing and detection warms up while hidden — an interlaced verdict is ready the moment the switch completes), rapid channel zapping (verdict + voting reset per source, stale in-flight readbacks are dropped), and WebGL context loss/restore.

Codec metadata plumbing: The demuxer still parses H.264 frame_mbs_only_flag / H.265 field_seq_flag+interlaced_source_flag into a mayBeInterlaced hint exposed via the video-info event. It no longer gates activation (many progressive streams carry interlaced-capable flags); it's retained for a future stream-metadata UI.

User control: A settings dropdown toggle (auto / off) is persisted to player storage.

Testing

devlab gains scan channels for exercising the detector (MCAST_SCAN_CHANNELS):

  • mcast 1080i-tff — true interlaced TFF H.264; heuristic must activate, combing must disappear, vote must pick TFF
  • mcast 1080i-bff — same content bottom-field-first; vote must pick BFF
  • mcast 1080p-combed — weaved TFF content flagged progressive; only the heuristic can activate (validates the shader path end to end)
  • mcast 1080p — progressive control at the gate; detector must not false-positive
  • mcast 2160p — above-gate control; deinterlacing must not activate at all

Verified in Chrome against devlab: TFF and BFF channels detect within the first frames and resolve the correct field order; 1080p stays in detection-only mode with no false positive; 2160p never starts the GPU chain; seamless and non-seamless channel zapping across all of the above behaves; console is clean (no readback performance warnings).

Files changed

  • web-ui/src/mpegts/deinterlace/ — new module: GPU detector, two-mode renderer, bwdif algorithm, GL utilities, type registry
  • web-ui/src/mpegts/demux/ — H.264/H.265 SPS parsing for interlace flags
  • web-ui/src/mpegts/ — plumbing through remuxer, worker messages, player core
  • web-ui/src/components/player/ — settings toggle, video-player slot/canvas integration
  • tools/devlab/ — scan channels and README documentation

🤖 Generated with Claude Code

stackia added 9 commits July 4, 2026 00:19
…ction

Render-side deinterlacing on top of the existing MSE pipeline: a
requestVideoFrameCallback + WebGL2 overlay draws deinterlaced frames to a
per-slot canvas while the video element keeps driving playback and audio.

- Heuristic detection (no metadata): periodic luma sampling with a classic
  comb metric, M-of-N rolling window, sticky verdict, reset on stream switch
- Gated to 1080-class content (height 1080/1088, width <= 1920)
- Pluggable algorithm registry; initial bob renders at field rate (25i->50p)
  with separate chroma low-pass to remove field-interleaved color combing
- Settings toggle (auto/off) persisted to player storage
- devlab: interlaced 1080i scan channel plus 1080p/2160p gating controls
GLSL port of FFmpeg's bwdif filter_line: per-pixel motion adaptation keeps
both fields in still areas (full vertical resolution, no bob shimmer on
static detail like logos/subtitles) and reconstructs moving areas with the
edge-preserving spatio-temporal filter, clamped to the temporal neighborhood.

Runs one frame behind the video (3-frame texture ring) at field rate for 50p
motion. Chroma keeps the separate vertical low-pass since field-interleaved
chroma combing from progressive 4:2:0 upsampling also sits on kept lines.
BFF sources played with a TFF assumption produce two-forward-one-back motion
judder at field rate. Add a field-order vote to the detector: capture two
consecutive frames via requestVideoFrameCallback and compare TFF vs BFF
hypotheses by their temporal-midpoint interpolation error (the wrong order
averages fields two field-times apart, so with motion its error is larger).
Majority vote with abstention on still/ambiguous pairs; activation starts
immediately with the TFF default and re-emits if BFF wins.

Also lower the combed-frame ratio to 0.01 (in-browser measurements: interlaced
0.018-0.022 vs progressive 0.004-0.005; the old 0.02 threshold made detection
flaky on frames with moderate motion) and add a 1080i BFF devlab channel.

Verified in Chrome against devlab: tff=4/0 on the TFF channel, bff=4/0 on the
BFF channel, no false positive on 1080p.
Review against libavfilter/bwdifdsp.c + vf_bwdif.c found three gaps:

- Frame-boundary rows: FFmpeg dispatches y<4 / y+5>h to FILTER_EDGE (plain
  (c+e)/2 with the spatial check only where its +-2 taps fit) instead of
  running the +-3/+-4-tap filters into duplicated edge rows; the shader now
  mirrors that row dispatch instead of relying on texture clamping.
- Chroma was an unconditional vertical low-pass, softening static color
  detail that FFmpeg's per-plane weave path preserves. Now motion-adaptive:
  static pixels pass original chroma through, moving pixels ramp toward the
  [1,2,2,2,1]/8 low-pass (true per-plane bwdif is impossible on RGB frames --
  the progressive 4:2:0 upsample bakes field-mixed chroma into all rows).
- Documented the weave-threshold float mapping (FFmpeg's integer !diff test
  == 0.5/255) and the intentional RGB-vs-YUV deviations.

Verified in Chrome on the 1080i TFF channel: moving comb 0.0026, paused
still 0.0000, static chroma edges preserved.
…e paused frame

- Parse H.264 frame_mbs_only_flag and H.265 field_seq/interlaced_source
  flags, plumb them demuxer -> remuxer -> worker -> player 'video-info'
  event -> deinterlace pipeline. Eligible interlaced streams now start
  deinterlaced immediately instead of waiting for the combing heuristic
  warm-up; the heuristic remains for streams with progressive metadata
  but combed content.
- Prime the renderer with the current video frame on start(), so
  re-enabling deinterlacing while paused replaces the stale canvas frame
  from a previous run. Until the texture ring holds real history the
  shader falls back to spatial-only interpolation (temporal terms would
  compare duplicated frames).
The React player no longer manages deinterlace pipelines, video-info
hinting, or verdict resets. It passes an overlay canvas and mode via
PlayerConfig (deinterlaceCanvas / deinterlaceMode), toggles mode at
runtime with Player.setDeinterlaceMode(), and reacts to the new
deinterlace-active-change event for visibility styling. The core
resets the interlace verdict on loadSegments/stop and hints the
detector from codec metadata internally. The canvas is stripped from
the config posted to the transmux worker (not structured-cloneable).

Also raise COMB_PIXEL_THRESHOLD 121 -> 400 (~±20 luma levels): sharp
progressive 1080p detail scored up to 0.011 at the old threshold and
false-positived the detector; measured in-browser the new threshold
keeps progressive <=0.005 while weaved interlaced stays >=0.011.
…rlace detection

Weaved TFF content encoded and flagged as progressive (no fieldorder
filter, no interlaced encoder flags), so the codec-metadata hint stays
silent and only the heuristic comb detector can activate deinterlacing.
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Documentation preview

The documentation preview has been deployed for this pull request.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa706bc0db

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread web-ui/src/mpegts/deinterlace/detector.ts Outdated
Comment thread web-ui/src/mpegts/deinterlace/renderer.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an automatic, render-side deinterlacing pipeline to the Web UI MPEG-TS player. It detects interlaced streams via codec metadata hints and/or a heuristic combing detector, then renders deinterlaced frames to a WebGL2 overlay canvas while leaving the MSE pipeline and audio timing driven by the original <video> element.

Changes:

  • Introduces a new WebGL2 deinterlacing pipeline (detector + renderer + bwdif GLSL algorithm) and plumbs codec-level “may be interlaced” metadata from demux → remux → worker → player events.
  • Adds runtime user control (auto/on vs off) persisted in player storage and surfaced in the player settings UI.
  • Extends devlab with multicast “scan” channels to exercise interlace detection, field order, and resolution gating.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
web-ui/src/pages/player.tsx Wires persisted “deinterlace” setting into the player page and settings dropdown props.
web-ui/src/mpegts/worker/transmux-worker.ts Emits a new video-info worker event derived from init segment metadata.
web-ui/src/mpegts/worker/messages.ts Adds video-info worker event type (width/height + interlace hint).
web-ui/src/mpegts/types.ts Defines VideoTrackInfo, new player events, and setDeinterlace() API.
web-ui/src/mpegts/remux/mp4-remuxer.ts Attaches videoInfo (dimensions + interlace hint) to video init segments.
web-ui/src/mpegts/player/mpegts-player.ts Forwards video-info messages to the player impl without breaking init batching.
web-ui/src/mpegts/index.ts Creates/owns the deinterlace pipeline, forwards hints, exposes events and setDeinterlace().
web-ui/src/mpegts/demux/ts-demuxer.ts Sets mayBeInterlaced in track metadata from H.264/H.265 parsed flags.
web-ui/src/mpegts/demux/sps-parser.ts Exposes H.264 frame_mbs_only_flag for interlace-capability hinting.
web-ui/src/mpegts/demux/h265-parser.ts Exposes an HEVC “interlaced-capable” hint from VUI/PTL flags.
web-ui/src/mpegts/deinterlace/renderer.ts New: WebGL2 renderer driven by requestVideoFrameCallback with field-rate output.
web-ui/src/mpegts/deinterlace/index.ts New: Pipeline wiring detector verdicts to renderer activation and events.
web-ui/src/mpegts/deinterlace/detector.ts New: Comb-metric heuristic detector + field-order voting logic.
web-ui/src/mpegts/deinterlace/algorithms/types.ts New: Algorithm registry + interface for pluggable GPU deinterlacers.
web-ui/src/mpegts/deinterlace/algorithms/gl-utils.ts New: Shared shader/program helpers and fullscreen triangle vertex shader.
web-ui/src/mpegts/deinterlace/algorithms/bwdif.ts New: GLSL bwdif implementation (motion-adaptive deinterlacing).
web-ui/src/mpegts/config.ts Adds deinterlaceCanvas and deinterlace config knobs with defaults.
web-ui/src/lib/player-storage.ts Persists the deinterlacing setting in local storage.
web-ui/src/i18n/player.ts Adds i18n strings for the deinterlacing toggle.
web-ui/src/components/player/video-player.tsx Adds overlay canvases per slot, hooks player events, toggles runtime deinterlacing.
web-ui/src/components/player/settings-dropdown.tsx Adds the deinterlacing toggle UI control.
tools/devlab/README.md Documents new “interlace-scan” multicast channels for deinterlacing testing.
tools/devlab/devlab.py Adds scan-channel generation (1080i TFF/BFF, combed progressive, 2160p gate).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread web-ui/src/mpegts/deinterlace/detector.ts Outdated
Comment thread web-ui/src/mpegts/deinterlace/detector.ts
Comment thread web-ui/src/components/player/video-player.tsx
- detector: codec metadata hint now requires at least one combed frame
  before activating (hintPending flag), preventing progressive streams
  with permissive frame_mbs_only_flag from being permanently processed by
  bwdif; hint is discarded after a full window with no combing
- detector: guard hintInterlaced() so field-order votes are never
  scheduled while the detector is stopped (resolved via hintPending path
  which defers all work to the timer-driven sample() callback)
- detector: start() now resumes scheduleFieldOrderVote() when re-enabled
  if interlaced is already true but field order is not yet settled
- pipeline: defer onActiveChange(true) until the first WebGL frame is
  drawn (pendingFirstFrame flag + notifyFirstFrameRendered callback),
  preventing a black flash on metadata-hinted streams where the canvas
  has not been painted when the verdict arrives
- video-player: hidden background slot uses opacity-0 instead of
  invisible when deinterlacing is active, keeping rVFC firing for
  seamless-switch warm-up
@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

…nal detection

- Remove codec metadata hint path (hintInterlaced); detection is now
  purely heuristic, avoiding false-positive locks on permissive encoders
- Add motion-gated reversion: sustained progressive-looking frames under
  MOTION_FLOOR motion guard revert the interlaced verdict without resetting
  the channel session
- Replace fixed-offset fast-sample setTimeout with chained rVFC calls
  anchored to the first rendered frame, ensuring pre-samples land on real
  decoded frames instead of firing during stream buffering
- Fix rVFC quota bug: sample() now returns boolean; onFrame only decrements
  remaining when a frame was actually processed, so wasted fires (readyState
  < HAVE_CURRENT_DATA) do not exhaust the fast-sample quota
@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@stackia stackia requested a review from Copilot July 4, 2026 11:53
@stackia

stackia commented Jul 4, 2026

Copy link
Copy Markdown
Owner Author

@codex[agent] review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 3 comments.

Comment thread web-ui/src/mpegts/deinterlace/detector.ts Outdated
Comment thread web-ui/src/mpegts/index.ts
Comment thread web-ui/src/components/player/video-player.tsx Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 090cd7be2b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread web-ui/src/mpegts/index.ts
Comment thread web-ui/src/mpegts/deinterlace/renderer.ts
…ounter reset

- Renderer: accept onContextLost/onContextRestored callbacks, stop the render
  loop on context loss and notify the pipeline so raw video is revealed as a
  fallback. On context restore, re-establish the entire pipeline lazily via
  apply() rather than rebuilding in-place.
- Pipeline: wire context callbacks — emit onActiveChange(false) on loss to
  restore raw video visibility, and re-apply the deinterlace verdict on
  restore to restart shader/texture setup.
- Detector: reset reversionConsecutiveCount on motion-abstained samples so
  that the reversion-to-progressive path requires truly consecutive
  qualifying frames, as documented.
@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@stackia stackia requested a review from Copilot July 4, 2026 12:25
@stackia

stackia commented Jul 4, 2026

Copy link
Copy Markdown
Owner Author

@codex[agent] review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 1 comment.

Comment thread web-ui/src/mpegts/deinterlace/index.ts Outdated
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

Reviewed commit: a06f2ccd5c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Move the Canvas 2D getImageData detection path to a WebGL2 fragment
shader pipeline that reuses the renderer's texture ring:

- Marker pass: computes comb score (R) and abs-diff motion (G) per pixel
  in a 256×H RGBA16F FBO using the same BT.709 luma weights as bwdif.
- Reduction chain: 2×2 box filter passes down to ≤8×8, shared by the
  marker and field-order passes.
- Field-order pass: half-height FBO computing per-row errTff/errBff
  prediction errors, replacing the rVFC drawImage pair in CPU mode.
- PBO async readback: readPixels into a PIXEL_PACK_BUFFER; result is
  retrieved 500 ms later via getBufferSubData — no pipeline stall on
  the bwdif render path, and GPU→CPU transfer drops from ~1.1 MB to
  512 bytes per sample cycle.

Detection automatically falls back to the existing Canvas 2D path when
EXT_color_buffer_float is unavailable or before the renderer's GL
context is created (initial progressive channel scan). All verdict
logic (rolling window, motion-gated reversion, field-order voting) is
shared between both paths via processSampleMetrics().

renderer.ts: add onDetectionFrame callback hook and currentGl getter.
index.ts: drive GPU sampling cadence (3-frame fast phase + 500 ms
steady state) via onDetectionFrame; wire GL context lifecycle.
@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@stackia

stackia commented Jul 4, 2026

Copy link
Copy Markdown
Owner Author

@codex[agent] review d07c40c

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 23 out of 23 changed files in this pull request and generated 4 comments.

Comment thread web-ui/src/mpegts/deinterlace/detector.ts
Comment thread web-ui/src/mpegts/deinterlace/detector.ts Outdated
Comment thread web-ui/src/mpegts/deinterlace/detector.ts Outdated
Comment thread tools/devlab/README.md Outdated
Co-authored-by: stackia <5107241+stackia@users.noreply.github.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d07c40c96e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread web-ui/src/mpegts/deinterlace/index.ts Outdated
Comment thread web-ui/src/mpegts/index.ts
…any algorithm runs

Drop the legacy CPU detection path entirely so only the GPU shader chain
remains, and merge the duplicated CPU/GPU verdict logic into a single engine.
The resolution gate now lives in the pipeline: the whole GPU chain (frame
uploads, bwdif, detection passes) only starts once the source size is known
(via the video `resize` event) and within the SD/HD gate, so oversized streams
run no algorithm at all.
@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

…tion readback fully async

- Split the renderer into detection-only and full-render modes driven by
  the detector verdict: progressive content within the gate now uploads
  frames only on the sampling cadence and never draws, instead of paying
  per-frame uploads + field-rate bwdif with a hidden canvas
- Replace the sync/async readback split with a fence-gated PBO pool
  drained non-blocking on every frame; orphan each PBO before readPixels
  and defer getBufferSubData one frame so Chrome's readback shadow copy
  stays on the accelerated path (no performance warnings)
- Fix field-order voting: sample every frame while voting is open (was
  one vote per 500 ms), sample at texel centers so LINEAR filtering no
  longer blends the two fields (TFF was misdetected as BFF), and keep
  the pass scheduled on static-scene abstain instead of stalling forever
- Cache detection shader uniform locations once per program compile and
  hoist loop-invariant GL state out of the reduction chain
- Skip transiently oversized frames at the rVFC entry to close the
  stream-switch race before the resize event re-evaluates the gate
- Background video slot now uses opacity-0 unconditionally so rVFC keeps
  firing during seamless-switch warm-up (visibility:hidden stopped it)
- Document the 1080i-tff/1080i-bff devlab scan channels
@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@stackia stackia changed the title feat(web-ui): automatic deinterlacing with bwdif and codec-metadata activation feat(web-ui): automatic GPU deinterlacing with bwdif and shader-based content detection Jul 4, 2026
@stackia

stackia commented Jul 4, 2026

Copy link
Copy Markdown
Owner Author

花了一千多块钱的 token (Fable 5),终于做出来了。

@stackia stackia merged commit 17b89dd into main Jul 4, 2026
11 checks passed
@stackia stackia deleted the feat/web-player-deinterlace branch July 4, 2026 17:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants