本人之前写了一个博客:ffmpeg录制桌面(自己用gdi抓图)
当时设置的帧率是10,设置的比较低,原因在于,设置高了,1秒钟抓不到对应的图片数量,会导致最终生成文件播放起来,时间变短。比如设置帧率为20,但是由于电脑的分辨率比较高,一秒钟只能抓10帧。则录制一分钟时,会发现,最终播放起来只有30秒。这显然是有问题的。
当然,我们设置帧率为10也不安全,因为电脑由于各种原因,可能一秒钟也抓不了10帧,当然,我们也不能再将帧率往下设置了,太憋屈了。
此时修改frame->pkt_dts可以达到这种效果,比如写文件时,AVStream的时间基是1000,帧率设置为20.
第一帧的pkt_dts设置为0,则第二帧正常应该为50,第三帧为100,…,第20帧的pkt_dts为950,第21帧的pkt_dts为1000.
但是现在出问题了,电脑性能不够,一秒钟只能抓10张图片,则第一帧的pkt_dts设置为0,第二张的设置为100,这样才能保证最后生成的文件播放起来是ok的;但是这样有个不好的结果,就是最终显示的文件的帧率只有10,而不是设置的20。
所以本篇采取在设置帧率的前提下,尽力交付。比如设置帧率为10时,则如果实际能抓到的张数大于10,则进行相应的Sleep;如果实际能抓到的张数小于10,比如8.则dts设置正确即可,最终生成的文件的帧率是8.
代码结构如下:

CaptureScreen.cpp的内容如下:
//#include "stdafx.h"
#include "CaptureScreen.h"
CCaptureScreen::CCaptureScreen(void)
{
m_hdib = NULL;
m_hSavedCursor = NULL;
hScreenDC = NULL;
hMemDC = NULL;
hbm = NULL;
m_width = 1920;
m_height = 1080;
FetchCursorHandle();
}
//
// 释放资源
//
CCaptureScreen::~CCaptureScreen(void)
{
if (hbm)
{
DeleteObject(hbm);
hbm = NULL;
}
if (m_hdib){
free(m_hdib);
m_hdib = NULL;
}
if (hScreenDC){
::ReleaseDC(NULL, hScreenDC);
}
if (hMemDC) {
DeleteDC(hMemDC);
}
}
//
// 初始化
//
int CCaptureScreen::Init(int iPosX, int iPosY, int iWidth, int iHeight)
{
HWND hDeskTop = GetDesktopWindow();
RECT rc;
GetWindowRect(hDeskTop, &rc);
hScreenDC = ::GetDC(NULL);
int iSizeX = GetDeviceCaps(hScreenDC, HORZRES);
if (hScreenDC == NULL) return 0;
hMemDC = ::CreateCompatibleDC(NULL);
if (hMemDC == NULL) return 0;
m_iXPos = iPosX;
m_iYPos = iPosY;
m_width = iWidth;
m_height = iHeight;
if (!m_hdib){
m_hdib = (PRGBTRIPLE)malloc(m_width * m_height * 3);//24位图像大小
}
//位图头信息结构体
pbi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pbi.bmiHeader.biWidth = m_width;
pbi.bmiHeader.biHeight = m_height;
pbi.bmiHeader.biPlanes = 1;
pbi.bmiHeader.biBitCount = 24;
pbi.bmiHeader.biCompression = BI_RGB;
hbm = CreateCompatibleBitmap(hScreenDC, m_width, m_height);
SelectObject(hMemDC, hbm);
wLineLen = ((m_width * 24 + 31) & 0xffffffe0) / 8;
wColSize = sizeof(RGBQUAD)* ((24 <= 8) ? 1 << 24 : 0);
dwSize = (DWORD)(UINT)wLineLen * (DWORD)(UINT)m_height;
return 1;
}
//抓取屏幕数据
BYTE* CCaptureScreen::CaptureImage()
{
VOID* alpbi = CaptureScreenFrame(m_iXPos, m_iYPos, m_width, m_height);
return (BYTE*)(alpbi);
}
void* CCaptureScreen::CaptureScreenFrame(int left, int top, int width, int height)
{
if (hbm == NULL || hMemDC == NULL || hScreenDC == NULL) return NULL;
//BitBlt(hMemDC, 0, 0, width, height, hScreenDC, left, top, SRCCOPY);
BOOL bRet = StretchBlt(hMemDC, 0, 0, width, height, hScreenDC, left, top, width, height, SRCCOPY)

该博客探讨了在使用FFmpeg录制桌面时遇到的帧率同步问题。作者通过GDI抓图实现桌面录制,并详细解释了帧率设置与实际抓图数量不匹配导致的播放时间缩短问题。为解决此问题,提出了调整pkt_dts的方法以确保文件播放的正确性。此外,还讨论了根据实际抓图能力动态调整Sleep时间来维持帧率稳定的做法。
最低0.47元/天 解锁文章
2308

被折叠的 条评论
为什么被折叠?



