Skip to content

VideoPlayerVideo: only double the frame rate for interlaced streams - #79

Merged
Portisch merged 1 commit into
CoreELEC:aml-5.15.196-22.0from
cinema-ONE:fix-25p-interlace-forcing
Aug 30, 2026
Merged

VideoPlayerVideo: only double the frame rate for interlaced streams#79
Portisch merged 1 commit into
CoreELEC:aml-5.15.196-22.0from
cinema-ONE:fix-25p-interlace-forcing

Conversation

@cinema-ONE

Copy link
Copy Markdown
Contributor

Description

CVideoPlayerVideo::OpenStream() rewrites any 25 or 29.97 fps stream to 50 / 59.94 and asserts SetVideoInterlaced(true), from the container fps hint alone — there is no interlace test. A progressive 25p file is declared 50 fps interlaced before a frame is decoded.

Neither self-correcting path recovers it on Amlogic. CalcFrameRate()'s skipHalving guard trusts the interlace flag this code just set, and logs skipping halve: interlaced stream, keeping fps 50.000000 (measured 25.000000) every window. The ProcessDecoderOutput() rescue requires m_vfmt == "progressive" from /sys/class/deinterlace/di0/frame_format, but that node reads interlace or null — never the word progressive — so it cannot fire.

Nothing else asserts interlace on this path: DVDVideoCodecFFmpeg sets the flag for software decode, but DVDVideoCodecAmlogic does not, so the fps heuristic is the only source.

Visible effect: a 25p file plays at 2160p50 with the deinterlacer flagged on, and videoscreen.whitelistdoublerefreshrate = false cannot prevent it — the doubling happens in OpenStream(), before Resolution.cpp is consulted, so the setting has nothing left to veto.

Fix

CVideoPlayer::OpenStream() already performs the same doubling, with the same constants and the same SetVideoInterlaced(true), gated on hint.interlaced — and computes the rate as fpsscale / (fpsrate * (hint.interlaced ? 2 : 1)), consulting the flag twice. This gates the second copy the same way, so the two agree:

if (hint.interlaced)
{
  if (MathUtils::FloatEquals(static_cast<float>(m_fFrameRate), 25.0f, 0.01f))
  ...
}

hint.interlaced comes from DVDDemuxFFmpeg on field_order in {TT, BB, TB, BT}.

Testing

Homatics Box R 4K Plus (S905X4-K), CoreELEC 22 built from this branch, played over SMB.

Content field_order Before After di0/frame_format
h264 1080p25 progressive 50.00 25.00 null
mpeg2 576i25 tt 50.00 50.00 interlace
mpeg2 576i25 bb 50.00 50.00 interlace
h264 1080i29.97 (mkv) tt 59.94 59.94 interlace

The progressive case now logs framerate: 25.00 and Display resolution ADJUST : 3840x2160 @ 25.00 instead of @ 50.00; the interlaced cases are unchanged, both branches and both field orders.

Surveyed 2470 sample files for the regression risk — content that is interlaced but not flagged. 47 interlaced files, all correctly flagged tt/bb, including h264 1080i inside MKV; zero interlaced-but-unflagged (25 plausible candidates frame-checked). The one case I could construct is x264 --interlaced muxed straight to MKV, which reports progressive while frames are interlaced; the same bitstream in MPEG-TS reports tt, and remuxing real interlaced content TS → MKV preserves it.


Written by my AI co-author (Claude Code); posted from my account.

OpenStream() rewrote every 25 or 29.97 fps stream to 50 or 59.94 and asserted
SetVideoInterlaced(true) from the container fps hint alone, so a progressive
25p file was declared 50fps interlaced before a frame was decoded.

VideoPlayer.cpp already performs the same doubling with the same constants,
gated on hint.interlaced. Gate this copy the same way so the two agree.

Neither self-correcting path recovers on Amlogic: CalcFrameRate's skipHalving
guard trusts the interlace flag this code just set, and the vfmt rescue reads
/sys/class/deinterlace/di0/frame_format, which reads "null" when the
deinterlacer is not engaged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Portisch
Portisch merged commit 91c1c6d into CoreELEC:aml-5.15.196-22.0 Aug 30, 2026
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.

2 participants