当前位置: 首页 > news >正文

FFmpeg+DXGI:解决GDI录屏音画不同步

一、为什么要用ffmpeg + DXGI,直接用windows原生的GDI不好吗?

我之前做的一个录屏录播推流端程序,使用那玩意,一但设置的帧率超过20fps,音视频就会严重不同步,具体表现为视频跟不上音频,但这个同步没有一点关系,原因:

(1)假设设定帧率为60fps,也就是现实世界1s内发生的事用60张间隔基本一致的图像描述出来,这个60fps,一般是用户给编码器上下文给定的参数

(2)假设硬件设备1s内确实能够捕捉60帧图像数据,那么送达编码器这里来,编码器给每一帧都打上合适的显示时间戳,那么最终录制完成的视频发生的速率肯定是没有问题的,和现实世界1s内发生的事件的速率是一致的

(3)但是,这终归是假设,GDI并不能够稳定的产出这60张图像数据,暂且当做GDI对应的硬件设备1s仅仅能够提供20张图像数据,现在来看编码器会如何做

(4)编码器将收到的第2帧打上pts = 16.6ms(这个数值是错的,编码器不会这样,具体要看你给的时间基,这里仅仅是为了方便讨论),但是当时GDI捕获这张照片是在显示世界中的 1000 / 20 = 50ms,你看,差距出来了吧!如果继续往后推论,在1s内,编码器编码了20帧,这20帧明显是为了描述显示世界中的发生的持续了1s的事件,但是编码器仅仅会按照用户当时设置的60fps去给他打时间戳,也就是这个20帧的pts依此为:0,16.6,33.2,···,333.3ms,最终播放器接收到的时间戳也是这样的,你想,播放器用1s的时间,肯定是要按照60fps的设定播放60帧,是吧?但是这60帧描述了现实世界中1s发生的事件吗?很明显不是嘛!这个60帧,描述了现实世界中3s发生的事

(5)也就是现实世界中3s的事件,被播放器1s放完了,能不快嘛,这是你的参数设置有问题?不是嘛,是硬件不行嘛,所以我才想到用DXGI这个高性能的屏幕抓取工具去做,就是为了解决这个问题!(其实DXGI做的不是主动去在获取屏幕的活,不是主动的!是被动的,具体去看微软的官方文档中如何介绍,你大概就懂他的原理了,如果仅仅是用来录制屏幕,理解到捉取屏幕就够了,嗯嗯,不影响使用)

(6)好,讨论到(5)其实你就明白了这篇文章产生的原因,但是我们的假设还没有做完,如果是硬件设备1s没提供的帧数 >> 60(用户设定的帧率)呢?那就不用管了,也就是1s需要60帧,结果硬件给了120帧,怎么办,在帧间隔内的丢了就行,只拿需要的,但是之前是因为硬件给不了这么多,你总不可能用AI变出来额外的40帧吧?(虽然好像有那么点道理,但是不多),硬件给了120帧,阔绰!我特么直接扔了

二、DXGI在 windows上的使用教程:https://zhuanlan.zhihu.com/p/1898859341521588602
该作者在他的知乎博客以及github上开源了DXGI截取桌面图像为BMP格式文件的项目,如果想要达到录制屏幕的效果,需要自行结合ffmpeg做相应帧的拷贝和截取

三、我的成品项目地址:(等我全部完善好之后,再行公开)

四、ffmpeg+DXGI录屏的总体流程:
1、初始化DXGI设备

2、初始化ffmpeg编码器、输出文件···上下文,最终输出文件格式为 DXGI.mp4,网上有教程,自行解决

3、将DXGI获取到的桌面录制图像拷贝到CPU中,进一步送入编码器,并接受pkt包

4、调用相关API,av_interleaved_write_frame()写入文件

5、写入文件尾,释放所有句柄、上下文资源、alloc的空间,防止内存泄漏

五、我知道目前为止遇到的问题:

1、AcquireNextFrame() 返回超时如何处理?

使用frame始终保存最新的一帧数据,用memcpy()就行,超时时,如果达到帧间隔,直接将上一帧的frame送给编码器,但是一定要记得更新frame->pts,如果你是用定时器按照帧间隔的方式去处理新的 frame 的,那么一定也要跟新上一帧获取数据时的时间,也就是delta_time,我在项目中用的是windows的QPC,https://learn.microsoft.com/zh-cn/windows/win32/sysinfo/acquiring-high-resolution-time-stamps
这里是微软官方给出的使用实例,很简单,如果觉得复杂,你就用ffmpeg提供的计时器来吧,没什么区别,在vs2026,debug模式下,设定fps = 25,delta_time循环与40 - 41ms两个数值,可见效率明显比GDI快了很多倍

2、编码器上下文 max_b_frames = 0; 否则编码器会报错: pts < dts,如何解决以及为什么?自己查

3、其他都是些参数没配置好的问题,额,没有什么参考价值,自行解决。

六、当然,当你完成上述的所有步骤,并且在60fps帧数下,音视频还是能够保持同步,也就是不会出现GDI的现象时,你就可以将编码得到的东西,推送到流、服务器,还是通过RTMP协议推流出去实现直播效果,都是可以的,(可以用现成的服务器或者直接自己写一个流媒体服务器,一般来说,不涉及到协程调度的逻辑都是比较好实现的,主要是非阻塞各种模块要做好,当然太细的东西,对于学习阶段的人没必要,这么一个东西走出来,能实现秒开就可以了,不要太过苛求)

http://www.jsqmd.com/news/512775/

相关文章:

  • 纽约大学突破:AI推理过程实现故事化透明呈现能力
  • OpenCode 开源 AI 编程 Agent 完全指南:从安装到实战的 8 个步骤(2026最新)
  • BERT算法学习1-嵌入层结构
  • 用MATLAB复刻经典电话通信:手把手教你实现PCM编码与双机文件传输
  • Vue前端与.Net后端SM2加密通信实战:从sm-crypto到BouncyCastle的完整流程
  • 四种轻松无损去除 Gemini 水印的方法
  • 聊聊国产6.6kW OBC硬核设计
  • Gemini认证:下一代安全验证技术革新
  • 函数调用寄存器规则
  • 美妆品牌,快速搭建小程序商城
  • 基于单片机无线防丢报警器设计 [单片机]-计算机毕业设计源码+LW文档
  • 佳维视工控一体机在水质检测仪中的应用
  • 如何在ESP32上构建你的AI伙伴:Xiaozhi-ESP32开源项目深度探索
  • Git误操作急救手册:拯救代码必备
  • 写代码 vs 拖模块:1949AI拆一个自动化流程的两种实现
  • 桌面温湿度天气时钟 原理图设计 (SchDoc)
  • 如何备份红米手机短信(6 种行之有效的方法)
  • 2013-2024年各省级数字经济指数数据+Stata代码
  • [特殊字符] 重磅!智慧港口评级落地!AI硬核技术,助力港口冲击一级(引领型)标杆!
  • A 股 Level-2 行情数据 API 实战指南
  • 告别Appium!用Python+facebook-wda搞定iOS自动化测试(保姆级环境搭建与实战)
  • 【Keepalived】主备模式MASTER/BACKUP的vrrp实例配置详解
  • 新能源汽车电池壳体孔深光学3D轮廓测量-激光频率梳3D轮廓技术
  • OSI七层模型实战解析:从理论到网络通信的完美落地
  • 3月必看!防雨布行业内口碑好的品牌分析情况,市场防雨布企业推荐优质品牌选购指南 - 品牌推荐师
  • 单例模式(懒汉式)
  • C语言学习与未来规划
  • 高效HR的AI工具箱:21个精准提示词,重塑核心工作流(即拿即用版)
  • RDMA-InfiniBand和RoCEv2
  • 电动船舶在线监测管理系统方案