
一、为什么要用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做相应帧的拷贝和截取三、我的成品项目地址(等我全部完善好之后再行公开)四、ffmpegDXGI录屏的总体流程1、初始化DXGI设备2、初始化ffmpeg编码器、输出文件···上下文最终输出文件格式为 DXGI.mp4网上有教程自行解决3、将DXGI获取到的桌面录制图像拷贝到CPU中进一步送入编码器并接受pkt包4、调用相关APIav_interleaved_write_frame()写入文件5、写入文件尾释放所有句柄、上下文资源、alloc的空间防止内存泄漏五、我知道目前为止遇到的问题1、AcquireNextFrame() 返回超时如何处理使用frame始终保存最新的一帧数据用memcpy()就行超时时如果达到帧间隔直接将上一帧的frame送给编码器但是一定要记得更新frame-pts如果你是用定时器按照帧间隔的方式去处理新的 frame 的那么一定也要跟新上一帧获取数据时的时间也就是delta_time我在项目中用的是windows的QPChttps://learn.microsoft.com/zh-cn/windows/win32/sysinfo/acquiring-high-resolution-time-stamps这里是微软官方给出的使用实例很简单如果觉得复杂你就用ffmpeg提供的计时器来吧没什么区别在vs2026,debug模式下设定fps 25delta_time循环与40 - 41ms两个数值可见效率明显比GDI快了很多倍2、编码器上下文 max_b_frames 0; 否则编码器会报错 pts dts如何解决以及为什么自己查3、其他都是些参数没配置好的问题额没有什么参考价值自行解决。六、当然当你完成上述的所有步骤并且在60fps帧数下音视频还是能够保持同步也就是不会出现GDI的现象时你就可以将编码得到的东西推送到流、服务器还是通过RTMP协议推流出去实现直播效果都是可以的(可以用现成的服务器或者直接自己写一个流媒体服务器一般来说不涉及到协程调度的逻辑都是比较好实现的主要是非阻塞各种模块要做好当然太细的东西对于学习阶段的人没必要这么一个东西走出来能实现秒开就可以了不要太过苛求)