Windows Media Foundation 底层踩雷

💣 一、 架构初始化与 COM 深坑 (Delphi 特供)

1. 线程 COM 未初始化引发的“见光死”

  • [症状] 在主线程测试一切正常,一旦放入后台截屏/编码线程,CoCreateInstance 直接崩溃罢工。
  • [根本原因] 硬件编码器和解码器都是纯正的 COM 组件。后台 TThread 启动时默认没有 COM 上下文。
  • [破局方案] 必须在编码器和解码器的 Initialize 或线程 Execute 头部强行注入 CoInitialize(nil),并在销毁前调用 CoUninitialize

2. 底层头文件翻译 Bug 与 absolute 内存欺骗

  • [症状] 软硬解自适应时,调用 GetOutputStreamInfoE2033(参数类型不一致)。
  • [根本原因] 翻译接口时将 C++ 中的结构体指针错误翻译成了强类型的 out Pointer。传 Pointer 会导致堆栈溢出,传 Record 无法编译。
  • [破局方案] 祭出 Delphi 黑魔法 absolute 关键字 (LStreamInfoHack: Pointer absolute LStreamInfo;),骗过编译器类型检查,实现内存级安全写入。

3. FillChar 抹杀 COM 引用计数 (显存僵尸)

  • [症状] 提取画面循环中,内存与显存呈线性爆炸式泄漏。
  • [根本原因] 对包含 COM 接口指针(如 pSample)的记录体使用 FillChar 清零,绕过了 Delphi 的垃圾回收,导致旧接口的 _Release 永远不被触发。
  • [破局方案] 彻底禁用 FillChar,改为逐项显式赋值 nil,让 Delphi 编译器自动插入减引用计数代码。

💣 二、 硬件编码端 (Encoder) 的生死劫

4. 异步 MFT 的“锁死”机制

  • [症状] 成功实例化硬件编码器,但调用 ProcessInput 喂入画面时直接返回错误。
  • [根本原因] Windows 8 引入的现代显卡硬件编码器属于异步 MFT。默认情况下它们是被锁死的,拒绝接收任何同步管线的调用。
  • [破局方案] 必须通过 IMFAttributes 接口,向编码器写入 MF_TRANSFORM_ASYNC_UNLOCK = 1 的隐藏属性,强行唤醒其异步硬件加速特性。

5. 格式嗅探短路与 C0000005 越界崩溃

  • [症状] 喂入 DXGI 截取到的 ARGB32 纹理时,显卡驱动层引发段错误崩溃。
  • [根本原因] 编码器输入格式嗅探逻辑使用了 or。显卡驱动总是把 NV12 排在第 0 位,导致嗅探器提前跳出,编码器误以为进来的纹理是 NV12 并按此跨距读取 ARGB32,当场越界。
  • [破局方案] 重构嗅探循环为绝对优先权:优先锁定 ARGB32,实现显存零拷贝直通;若无,则将 NV12 作为备胎。

6. 异步状态机死锁: $C00D36B5E_UNEXPECTED

  • [症状] 编码主循环卡死,或者 ProcessOutput 频繁报错。
  • [根本原因] 硬件编码器是“吞吐极其不平衡”的黑盒。
    • 当它吃饱了(返回 $C00D36B5 MF_E_NOTACCEPTING),如果你继续强塞输入,它会直接崩溃;必须立刻转去调用 ProcessOutput 榨干它。
    • 当它被榨干了(返回 E_UNEXPECTEDMF_E_TRANSFORM_NEED_MORE_INPUT),如果你继续索要输出,它也会报错;此时必须 Break 退出,去抓取下一帧。
  • [破局方案] 建立基于 $C00D36B5 (输入满) 和 E_UNEXPECTED (输出空) 的自适应双向榨取循环,精准踩着硬件的状态机节奏跳舞。

7. 幻影组件:被骗去寻找的 Video Processor

  • [症状] 试图用 CoCreateInstance 唤醒 VideoProcessorMFT 来做 ARGB32 到 NV12 的前置转换,结果在部分机器上报类未注册。
  • [根本原因] 官方 MFT 隐藏在内部注册表中需用 MFTEnumEx 唤醒。但更大的真相是:现代显卡硬件编码器完全支持原生吞入 ARGB32 纹理,我们根本不需要这个软件转换器!
  • [破局方案] 卸载转换器累赘,把输入优先级严格锁定为 ARGB32,实现纯硬件直通压缩。

💣 三、 硬件解码端 (Decoder) 的自适应阵痛

8. DXVA 硬件代理的傲慢

  • [症状] 使用硬件枚举标志寻找纯硬件 H.264 解码器失败。
  • [根本原因] 许多显卡不会注册独立的解码 MFT。必须使用微软官方的“软件代理” (CLSID_CMSH264DecoderMFT),并在初始化时强行喂给它 DXGIDeviceManager,它才会瞬间觉醒为 DXVA 硬件加速状态。

9. 流格式变更 (Stream Change) 引发的 100% CPU 空转

  • [症状] 解码器遇到第一帧 SPS/PPS 时,不吐画面,线程 CPU 飙升至 100%。
  • [根本原因] 解码器确认画面尺寸后,返回 $C00D36B3 请求重新握手。若不重新设置输出格式并直接 Continue,就会陷入每秒数千万次的死循环。
  • [破局方案] 拦截 $C00D36B3,动态索要最新格式并 SetOutputType,解锁输出阀门。

10. 幽灵降级:DXVA 与 CPU 模式的内存争夺战

  • [症状] 解码器偶尔抛出 $80070057 (E_INVALIDARG) 崩溃。
  • [根本原因] 解码器可能悄悄从 DXVA 显存模式(自己分配内存)降级为软件模式(要求调用者分配内存)。如果依然传 pSample := nil,直接崩溃。
  • [破局方案] 每次提取前调用 GetOutputStreamInfo 嗅探标志位 $00000100,实现硬解传 nil,软解分配 IMFMediaBuffer 的双模无缝切换。

💣 四、 色彩科学与内存对齐 (渲染端)

11. 符号位屠杀:shr 导致的霓虹色块溢出

  • [症状] 画面出现刺眼的高亮红蓝杂块。
  • [根本原因] YUV 色差(如 U-128)为负数时,Delphi 的 shr 8 执行逻辑右移,连带符号位一起移入数值区,导致颜色数值爆炸,最终被 Clamping 锁死在 255 极值。
  • [破局方案] 全部替换为 div 256,交由编译器优化为安全的算术右移 (sar)。

12. 16 像素宏块陷阱 (顶部绿条与画面撕裂)

  • [症状] 1080P 画面顶部出现纯绿条,软解模式下画面横向撕裂。
  • [根本原因] H.264 底层强制 16 像素对齐(1080 -> 1088)。多出的 8 行 Padding 数据全为 0,UV 平面被全 0 污染,在色彩空间中表现为高亮纯绿。
  • [破局方案] 渲染前计算真实的 LAlignedHeight := (FHeight + 15) and (not 15),跳过废弃的 Padding 区域精准采点。

13. 色域坍缩:BT.601 导致的画面发灰

  • [症状] 画面缺乏对比度,黑色变深灰,文字边缘有微弱色差。
  • [根本原因] H.264 管线输出 16-235 的 Studio 限制色域。强行用老旧的 BT.601 矩阵映射到 PC 显示器,导致色彩断层。
  • [破局方案] 重写纯汇编级转换引擎,换装专为 1080P 设计的高清 BT.709 满血扩展矩阵。

💣 五、 流媒体状态管控与防漏

14. 幽灵码流:无限膨胀的 TMemoryStream

  • [症状] 编码开启后,主程序 RAM 以每秒数 MB 速度飙升。
  • [根本原因] 对象池复用的 TMemoryStream 携带上一帧残余数据,新一帧数据不断 Append,体积滚雪球。
  • [破局方案] 在每次编码抓取前强制执行 LBin.Clear;喂给解码器前必须 LBin.Position := 0

15. H.264 “双煞”:高延迟与开局重影

  • [症状] 画面拖动滞后严重,且初次渲染时画面产生严重残影/撕裂。
  • [根本原因] 解码器默认开启 B 帧缓冲池导致延迟;首帧如果没有收到完整的 I 帧(关键帧),P 帧差异数据只能糊在空画面上产生重影。
  • [破局方案] 1. 编解码双端注入 MF_LOW_LATENCY 属性击穿缓冲池。
    2. 首帧联调时,向编码器发送 RequestKeyFrame 指令,强制呼叫 I 帧洗地。
posted @ 2026-04-02 16:41  Lonely-妖蛋  阅读(2)  评论(0)    收藏  举报