Repository navigation
Improve channel menu, background startup refresh, and 4K hardware decoding - #10
Conversation
- Stronger focus highlight (filled blue + white outline) that stays distinct from the "selected" row, plus a now-playing marker - Show the global channel number used by number-key zapping - Use the currently airing program as each row's subtitle, loaded off the main thread and refreshed per row; fall back to the group title - Keep the EPG preview pane out of the focus path so Back returns to the channel list instead of closing the whole menu - Add a key hint at the bottom of the EPG pane - Fix group selection calling notifyDataSetChanged, which dropped focus to the player view - Stop the info overlay's auto-hide timer from closing the channel menu Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With "refresh M3U on startup" enabled, playback used to wait for the refresh, and the refresh occupied the main executor. A slow source kept the screen blank and the channel menu unresponsive for nearly 50s. Start playing from the cached channel list right away and refresh on a dedicated executor. On success, swap in the new list without interrupting playback or rebuilding an open channel menu. Only wait for the refresh when the cache is empty. If the pre-refresh URLs all fail, retry once with the refreshed ones. Wrap the database writes in ChannelRepository.loadSource in a transaction, since sources are deleted and re-inserted and a channel switch during the background refresh could otherwise see none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CCTV-4K is tagged hvc1.2.4.L180 (Main10, level 6.0), while the p230's Amlogic HEVC decoder only advertises level 5.1. MediaCodecVideoRenderer then reported FORMAT_EXCEEDS_CAPABILITIES and the FFmpeg renderer FORMAT_HANDLED, so track selection sent 4K 10-bit video to the CPU and it dropped frames constantly. The same channel plays fine with "hardware only", so the level tag is just conservative. In modes where video is hardware-first with FFmpeg fallback, wrap the MediaCodec video renderer and report FORMAT_HANDLED when a hardware decoder supports the mime type and profile and the resolution fits, and only the level exceeds. Real gaps such as an unsupported profile still fall back to FFmpeg. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 933dc729fb
ℹ️ 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".
| return false; | ||
| } | ||
|
|
||
| private static boolean supportsProfile(MediaCodecInfo info, int profile) { | ||
| for (CodecProfileLevel profileLevel : info.getProfileLevels()) { | ||
| if (profileLevel.profile == profile) { |
There was a problem hiding this comment.
Restrict level relaxation to verified decoder cases
When a stream genuinely exceeds a decoder's advertised level, matching the profile and resolution/frame rate does not establish that the decoder can handle it: codec levels also constrain properties such as bitrate, sample rate, and decoded-picture buffering. This loop therefore promotes every over-level AVC/HEVC/etc. format to FORMAT_HANDLED, causing the hardware renderer to win over FFmpeg even on devices whose level declaration is accurate; those streams can then fail or stutter instead of using the previously selected software fallback. Limit the workaround to known affected codecs/decoders or otherwise verify the constraints represented by the level before returning true.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
不修改。确实存在码流真超出 Level、硬解却被判为支持的情况,但只在 1080p 及以下才有实际影响,因为 4K 交给软解在电视盒子上本来就跑不动。会出现这种情况的是硬解只声明到 Level 4.1 一类的老设备,年代已经比较久,项目不再支持,所以保留目前的放宽条件。
| if (isFinishing() || refreshed.isEmpty()) { | ||
| return; | ||
| } | ||
| allChannels = refreshed; |
There was a problem hiding this comment.
Reconcile state when the refreshed playlist is empty
When a successful HTTP response parses to zero channels, ChannelRepository.loadSource has already committed deletion of every channel and source, but this early return leaves allChannels and currentChannel pointing at the deleted rows. For an intentionally emptied subscription or a transient 200 response with empty/malformed content, the current session continues showing stale channels, and selecting one queries an empty source list and immediately fails; update the in-memory state to reflect the empty database or reject the empty refresh before committing it.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
已在 9a31ae0 修复,并且是从源头修的:ChannelRepository.loadSource 在解析出 0 个频道时抛出 IOException,不再写库。这样源站临时返回空页面时,本地缓存不会被清掉,内存里的 allChannels 和数据库也保持一致。所有调用方(启动刷新、设置页、Web 管理页)本来就会捕获这个异常,按刷新失败处理。新增的 ChannelRepositoryTest 覆盖了这种情况。
loadSource deletes every channel missing from the parsed playlist, so a transient empty or error page from the source wiped the local cache. Now that startup plays from the cache first, that cache matters more. Throw an IOException instead of writing when nothing was parsed; all callers already handle refresh failures by keeping the existing data. Add a test-only constructor so the repository can run against an in-memory database and a stubbed OkHttpClient. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
概述
这个 PR 包含三个相互独立的改动,都已在 p230(Android 7.1,S905X)真机上验证;第四个提交是针对 Codex 审查意见的修复。
1. 频道菜单更适合遥控器操作(12f960d)
notifyDataSetChanged,改为只刷新新旧两行ScrollView上,这时按返回会直接关掉整个菜单面板宽度,以及焦点在面板内时不自动隐藏,这两点是有意保持的设计,这次没有改。
2. 启动时刷新 M3U 改为后台进行(651f096)
ChannelRepository.loadSource的写库操作包进了事务真机结果:冷启动从近 50 秒不出画面缩短到约 1 秒起播,菜单也能马上打开。
3. 4K HEVC 只是 Level 标高时仍走硬解(933dc72)
CCTV-4K 的码流标为
hvc1.2.4.L180(Main10 Level 6.0),而 Amlogic 硬解只声明支持到 Level 5.1,结果轨道选择把视频交给了 FFmpeg 软解,4K 10bit 几秒就丢一两百帧。求索纪录 4K 标的是 Level 5.1,所以一直正常。新增
LevelTolerantVideoRenderer:在"硬解优先"和"音频软解 + 视频硬解"两种模式下,如果硬件解码器支持该 mime 和 Profile,分辨率也在能力范围内,只是 Level 超出,就按"支持"处理。Profile 不支持这类真正解不了的情况,仍然交给 FFmpeg。真机结果:CCTV-4K 改为
OMX.amlogic.hevc.decoder.awesome硬解,没有丢帧;求索 4K 和普通高清台照常走硬解。4. M3U 刷新解析为空时不清空缓存(9a31ae0,针对审查意见)
原来
loadSource只要 HTTP 返回 200,就会删掉解析结果里没出现的所有频道;源站临时返回空页面时,本地缓存会被清空。现在启动改成先用缓存起播,这份缓存就更不能被一次坏响应清掉。改为解析出 0 个频道时抛出IOException、不写库,所有调用方本来就会把它当作刷新失败、保留原有数据。新增ChannelRepositoryTest,用内存数据库和桩 OkHttpClient 验证这一点。测试
./gradlew testDebugUnitTest和assembleDebugLevelTolerantVideoRendererTest(4 个用例);WiTVRenderersFactoryTest改为按getName()判断,并新增"哪些模式会包装渲染器"的断言未在真机上验证
🤖 Generated with Claude Code