电脑接电视当显示器:电视后开机,播放器只占屏幕一角

电视完全断电、电脑先开机、播放器只占屏幕一角——问题不在播放器,而在显示目标重新接入的时序。本文记录从“主动切分辨率”到“只观察、防抖、确认后再动作”的完整改造。

先说清楚使用场景

本文讨论的不是“电脑投屏到电视”,也不是“电视自己播放视频”,而是:

一台 Windows 电脑通过 HDMI 连接电视,并把电视直接作为 Windows 显示器使用。

电脑上的 PotPlayer 负责播放视频,电视只承担显示输出。这个问题在电视是唯一显示器时最明显,在电视作为复制或扩展显示目标时也可能出现。

典型情况是:

  • 电视完全断电
  • 电脑先启动并自动播放视频
  • 之后电视重新上电
  • Windows 完成显示目标切换
  • 播放器没有重新适配,只占屏幕一小块

如果电视先开机,或者使用始终保持连接的普通显示器,通常不会遇到这个时序问题。


问题从哪里来

有一个很常见但容易被低估的场景:

  1. Windows 电脑通过 HDMI 连接电视,并将电视作为显示器使用。
  2. 先给电脑开机,电视还处于完全断电状态。
  3. 播放器按 Windows 当前的默认分辨率启动。
  4. 之后再给电视上电。
  5. Windows 识别到电视的 EDID,桌面分辨率发生变化。
  6. 播放器却没有重新适配,只占屏幕的一小块。

问题表面看起来是“播放器没有全屏”,实际上涉及三个不同层次:

  • Windows 在显示目标切换时,会先进入临时显示模式。
  • 播放器在窗口创建时记录了一套尺寸。
  • 外部脚本如果抢在显示切换完成之前操作,反而会放大问题。

电脑接电视当显示器:电视后开机,播放器只占屏幕一角

显示目标切换完成后,窗口没有重新适配,只占屏幕一角;目标是自动铺满。

这次改造的目标很明确:电视无论什么时候上电,最终都能自动全屏,不需要人工重新打开视频。

因此,后文提到的“电视连接”“电视上电”和“显示目标重新接入”,都指电视作为电脑显示器重新进入 Windows 显示链路,而不是电视播放网络视频或接收投屏。


先明确一个关键前提

电视不使用待机,而是每次彻底断电。

因此每次开电视,对 Windows 来说都相当于一次显示器重新接入。不能把“遥控器关机”和“拔掉电源”混为一谈。前者可能仍保留 HDMI 信号和 EDID,后者才会真正触发显示目标消失。

改造开始前,现场实测过一次完全断电状态:

  • 活动 PnP 显示器数量为 0
  • 系统退回默认的 1024×768
  • 真实电视不在设备列表中
  • 播放器仍保持进程,但窗口跟随默认桌面

这一步很重要。只有把断电状态和上电状态都测出来,才能知道脚本到底应该等待什么。


为什么“主动切分辨率”不可靠

最初的脚本采用了一个很直觉的思路:

  • 每 4 秒读取一次当前分辨率
  • 发现分辨率变化就重启播放器
  • 同时主动调用显示模式切换接口,尝试切到期望分辨率

实机运行后,这个方案暴露了明显问题。

电视重新握手时,分辨率并不会直接从旧值跳到最终值。它可能短暂经过默认分辨率、临时模式或低分辨率状态。脚本只采样其中一个瞬间,就可能做出错误判断。

历史日志复盘后发现:

  • 分辨率变化共检测到 19 次
  • 其中 13 次是向下切到低分辨率
  • 脚本却在这 13 次低分辨率瞬态中重启了播放器
  • 真正从低分辨率恢复高分辨率的次数,反而只有 4 次
  • 主动切换接口的失败次数远高于成功次数

这说明脚本看起来“有时有效”,实际只是偶尔命中了正确的时序。

可靠方案首先要放弃一个危险想法:监视器脚本不应该负责修改分辨率。

分辨率的协商交给 Windows、显卡驱动和电视完成。监听器只负责观察最终状态,然后决定是否处理播放器。


驱动层 EDID 模拟为什么没采用

EDID 模拟是一个理论上的方案,可以让电脑在没有真实电视时仍看到一个固定的显示目标。

但现场核显驱动没有提供“EDID 模拟”“强制常驻输出”等可用能力。驱动控制面板里也没有适合直接启用的对应选项。

在没有更新驱动、加硬件或引入其他设备的前提下,这个方案无法可靠落地。

于是重点转向应用层的状态观察。


DPI 感知为什么必须单独处理

这次排查里还遇到一个很容易误判的问题:不同进程读到的 DPI 不一致。

服务会话中的 WMI 和注册表值,可能返回默认值、缓存值或过期配置。它们不一定等于当前登录会话里真实生效的 DPI。

最终使用的是 Per-Monitor V2 DPI 感知,并在登录用户会话中读取有效 DPI:

SetProcessDpiAwarenessContext(
    DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
)

随后使用:

  • GetAwarenessFromDpiAwarenessContext
  • GetDpiForMonitor
  • EnumDisplaySettingsEx

这样读取到的才是当前显示目标的物理分辨率和有效缩放,而不是服务会话里的推测值。

最终实测中,两个测试环境的有效 DPI 都显示为 144,也就是 150%。早期报告里出现的 100% 和 300%,被确认是混用了非交互会话数据,不能作为可靠依据。


最终方案:只观察,不修改

最终脚本的核心原则只有一句话:

Windows 负责显示模式,脚本只负责等待稳定状态并管理播放器。

完整流程如下:

读取物理分辨率和 DPI
        |
        v
连续 5 次相同?
        |
       否 -> 继续等待
        |
       是
        v
达到目标分辨率?
        |
       否 -> 等待电视或显示设置就绪
        |
       是
        v
再等待 3 秒并复核
        |
        v
比较播放器窗口和目标屏矩形
        |
        +---- 已全屏 -> 不动作
        |
        +---- 窗口不符 -> 同一代次最多重启一次
        |
        +---- 播放器不存在 -> 启动,受频率限制

参数设置为:

  • 每 2 秒读取一次
  • 连续 5 次一致才视为稳定
  • 稳定后再等待 3 秒复核
  • 目标分辨率按机器配置
  • 窗口矩形误差允许 2 像素

从电视首次达到目标分辨率,到确认全屏,实际测试在 8 到 17 秒之间,满足 20 秒内完成的目标。


为什么不只监听显示变化事件

显示变化事件能及时告诉我们“有东西变了”,但不能告诉我们“现在是否已经稳定”。

一次电视重新接入,可能连续产生多个模式变化。只依赖事件,很容易在第一个临时状态就执行动作。

因此最终方案采用连续采样。事件可以作为未来优化项,但稳定判定必须依赖一组连续一致的读数。


播放器窗口矩形比对

只看分辨率还不够,因为播放器进程存在,不代表窗口已经铺满。

方案使用两层检查:

  1. 找出播放器进程的所有可见窗口。
  2. 选择面积最大的窗口,读取其可见边界。

窗口矩形读取优先使用:

DwmGetWindowAttribute(DWMWA_EXTENDED_FRAME_BOUNDS)

失败时回退到:

GetWindowRect

只有窗口四条边和目标屏矩形一致,才认为已经全屏。

这样做避免了一个常见误区:

“播放器进程正在运行,所以应该不用处理。”

进程存在和画面铺满并不是同一件事。


用“分辨率代次”限制重启

如果每轮都判断为尺寸不符,脚本会陷入反复重启。

解决方案是给每次分辨率变化分配一个代次:

  1. 分辨率发生变化,代次加一。
  2. 同一代次内已经处理过窗口不符,就不再重复重启。
  3. 只有新的稳定分辨率出现,才允许新的处理。

这样可以保证一次显示切换最多触发一次播放器重启。

同时,如果当前窗口已经和目标屏完全一致,动作就是“无”。实测中,很多断电重连场景下播放器会自动适配,因此并不需要重启。


播放器不存在时怎么办

还有一种情况:电视已经就绪,但播放器进程不存在。

这时脚本允许启动播放器,但增加滚动时间窗限制:

  • 每 60 分钟最多启动 3 次
  • 超过限制后只写日志,不再继续拉起

这个规则避免了播放器因文件损坏、解码器异常等问题反复启动,形成风暴。


循环播放怎么处理

播放器的命令行参数可以提供全屏参数,但循环播放通常依赖播放器自身的设置。

最终脚本采用两个动作:

  1. 启动时传入全屏参数。
  2. 启动前只读检查循环播放设置。

如果循环设置缺失或不匹配,脚本记录警告,但不会拒绝启动。这样可以保证主功能优先,同时把配置问题明确留在日志中。


日志应该记录什么

日志不能只写“成功”或“失败”,而应足够还原一次恢复过程。

最终日志包含:

  • 时间
  • 当前分辨率
  • 刷新率
  • DPI
  • 是否稳定
  • 首次达到目标分辨率的时间
  • 距首次达到目标分辨率经过的秒数
  • 播放器是否存在
  • 播放器窗口矩形
  • 执行的动作和原因
  • 是否确认全屏

一条简化后的日志类似:

resolution=3840x2160@30 dpi=144 stable=false action=none reason=debouncing
resolution=3840x2160@30 dpi=144 stable=true action=none reason=already_fullscreen
fullscreen_confirmed=true target_age_seconds=8.1

这比只记录“最终成功”更有价值,因为可以清楚看到等待、防抖和动作之间的因果关系。


自启动入口必须统一

这类问题很容易被多个历史脚本进一步放大。

现场曾有多个入口同时存在:

  • 播放器启动任务
  • 分辨率监听任务
  • 验证任务
  • 视频看门狗
  • 启动文件夹中的监听脚本

它们会互相启动、重启和覆盖状态,最终结果取决于时间竞争。

改造后的原则是:

  • 只保留一个监听任务
  • 只由监听器启动播放器
  • 删除旧启动项和旧任务
  • 启动前备份原任务 XML、旧脚本和启动项

部署完成后,同时检查:

  • 新监听器进程数量为 1
  • 播放器进程数量为 1
  • 旧任务数量为 0
  • 旧启动项数量为 0

验证结果

两个不同分辨率环境都完成了三项测试。

冷启动

电视完全断电,主机启动,进入桌面一分钟后再给电视上电。

结果:

  • 从首次达到目标分辨率到确认全屏:16.7 至 16.8 秒
  • 播放器启动一次
  • 没有重复重启

播放中完全断电再上电

电视播放过程中直接断电,再重新上电。

结果:

  • 约 8 秒确认全屏
  • 播放器进程保持稳定
  • 没有不必要的重启

手动切换分辨率

把分辨率降到较低值,再恢复到目标值。

结果:

  • 低分辨率阶段只等待
  • 恢复目标值后约 8 秒确认全屏
  • 播放器进程保持稳定

这个方案最重要的三个经验

1. 不要用监听器修改显示模式

监听器应该解决“何时处理应用”,而不是替操作系统和驱动做显示协商。

2. 一次变化不等于稳定

电视握手会产生多个中间状态。连续稳定样本比单次事件可靠得多。

3. 进程存在不等于画面正确

必须检查窗口矩形,并允许“什么都不做”也是成功结果。


结语

这类问题的难点并不在于“怎么重启播放器”,而在于判断什么时候应该重启、什么时候不应该动。

最终方案没有使用复杂的驱动技巧,也没有继续尝试强制切换分辨率。它做的是把显示变化当成一个状态机,通过 DPI 感知、连续采样、窗口矩形比对和单次动作限制,把原本依赖人工和运气的操作变成可观察、可回滚、可验证的流程。

对于电视完全断电、显示目标重新接入的场景,这种“只观察、防抖、确认后再动作”的思路,也适用于其他桌面播放器和展示终端。

Windows相关使用技巧

Windows 域控委派改密码权限:为什么有些用户能改,有些不能?

2026-10-21 11:04:27

流量卡知识与历史套餐

【已下线·历史资料】电信东南卡:资费、流量与限制记录

2025-12-11 0:55:40

❯
搜索