QT+OPENCV实现录屏功能
本文使用QT+opencv来实现对指定窗体画面录制,并保存为avi文件。
(1)获取窗体界面
QScreen类有一个grabWindow函数,可以用来获取窗体的画面,这个函数使用很简单,就是传入窗体句柄和要截取的坐标。但是这个函数有一个缺陷,它是通过截取桌面画面的方式,而不是通过
窗体获取界面,所以当你的窗体被其他窗体遮挡时,就无法截取完整的窗体界面,如果你是要录制整个桌面画面,那用这个函数就可以了,下面的方法调用GDI函数来实现,即使窗体被遮挡时仍然能够获取到完整界面,但是窗体最小化时也一样无法获取。
/*
* 函数功能:获取窗体指定窗体图像
* 参 数:hd:窗体句柄
* pm:保存获取到的图片
* x:截取的起始x坐标,
* y:截取的起始y坐标,
* w:截取的宽度
* h:截取的高度
*/
bool GetGDIBitmap(HWND hd,QPixmap &pm, int x, int y, int w, int h)
{
if(hd==NULL)
return false;
HDC hDC;
hDC=GetDCEx(hd,NULL,DCX_PARENTCLIP );
HDC hMemDC; //内存缓冲设备环境
HBITMAP hbmMem,hbmOld; //内存缓冲设备环境中的位图
RECT rc;
rc.left=x;
rc.top=y;
rc.right=x+w;
rc.bottom=y+h;
//判断边境值
RECT clientrc;
::GetClientRect(hd,&clientrc); int xc =;
int cx =;
int cy =; if(rc.bottom>clientrc.bottom || rc.bottom<)
rc.bottom=clientrc.bottom; if(rc.right>clientrc.right || rc.right<)
rc.right=clientrc.right; // 24位图的BITMAPINFO
BITMAPINFO *pBITMAPINFO = (BITMAPINFO*)malloc(sizeof(BITMAPINFOHEADER));
memset(pBITMAPINFO, , sizeof(BITMAPINFOHEADER));
BITMAPINFOHEADER *pInfo_Header = (BITMAPINFOHEADER *)pBITMAPINFO;
pInfo_Header->biSize = sizeof(BITMAPINFOHEADER);
pInfo_Header->biWidth = rc.right - rc.left;
pInfo_Header->biHeight = (rc.bottom - rc.top);
pInfo_Header->biPlanes = ;
pInfo_Header->biBitCount = ;
pInfo_Header->biCompression = BI_RGB; hMemDC=CreateCompatibleDC(hDC); //创建内存兼容设备环境
//创建内存兼容位图
hbmMem=CreateCompatibleBitmap(hDC,pInfo_Header->biWidth,pInfo_Header->biHeight); hbmOld=(HBITMAP)SelectObject(hMemDC,hbmMem);
//将内存设备环境中的内容绘制到物理设备环境 hDC
BitBlt(hMemDC,,,pInfo_Header->biWidth,pInfo_Header->biHeight,hDC,cx+rc.left,xc+cy+rc.top,CAPTUREBLT|SRCCOPY);
HBITMAP hBitmap=(HBITMAP)SelectObject(hMemDC,hbmOld); // 获得数据buf
DWORD bufSize=(pInfo_Header->biWidth * + ) / * * pInfo_Header->biHeight;
BYTE * pBuffer = new BYTE[bufSize]; int aHeight=pInfo_Header->biHeight; if(::GetDIBits(hMemDC, hBitmap, , aHeight, pBuffer,pBITMAPINFO, DIB_RGB_COLORS) == )
{
return false;
} bool bret=BitmapToPixmap(hBitmap,pm); ReleaseDC(hd,hDC);
//释放资源
DeleteObject(hbmMem);
DeleteObject(hbmOld);
DeleteDC(hMemDC);
free(pBITMAPINFO);
::DeleteObject(hBitmap);
delete [] pBuffer;
return bret;
} /*
* 函数功能:将bitmap转为QPixmap
*/
bool BitmapToPixmap(HBITMAP hBitmap, QPixmap &pm)
{
HDC hDC;
//设备描述表
int iBits;
//当前显示分辨率下每个像素所占字节数
WORD wBitCount;
//位图中每个像素所占字节数
//定义调色板大小, 位图中像素字节大小 , 位图文件大小 , 写入文件字节数
DWORD dwPaletteSize=,dwBmBitsSize,dwDIBSize;
BITMAP Bitmap;
//位图属性结构
BITMAPFILEHEADER bmfHdr;
//位图文件头结构
BITMAPINFOHEADER bi;
//位图信息头结构
LPBITMAPINFOHEADER lpbi;
//指向位图信息头结构
HANDLE hDib, hPal;
HPALETTE hOldPal=NULL;
//定义文件,分配内存句柄,调色板句柄 //计算位图文件每个像素所占字节数
hDC = CreateDC(L"DISPLAY",NULL,NULL,NULL);
iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
DeleteDC(hDC);
if (iBits <= )
wBitCount = ;
else if (iBits <= )
wBitCount = ;
else if (iBits <= )
wBitCount = ;
else if (iBits <= )
wBitCount = ;
else
wBitCount = ;
//计算调色板大小
if (wBitCount <= )
dwPaletteSize=(<<wBitCount)*sizeof(RGBQUAD); //设置位图信息头结构
GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap);
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = Bitmap.bmWidth;
bi.biHeight = Bitmap.bmHeight;
bi.biPlanes = ;
bi.biBitCount = wBitCount;
bi.biCompression = BI_RGB;
bi.biSizeImage = ;
bi.biXPelsPerMeter = ;
bi.biYPelsPerMeter = ;
bi.biClrUsed = ;
bi.biClrImportant = ; dwBmBitsSize = ((Bitmap.bmWidth*wBitCount+)/)**Bitmap.bmHeight;
//为位图内容分配内存
hDib = GlobalAlloc(GHND,dwBmBitsSize+dwPaletteSize+sizeof(BITMAPINFOHEADER));
lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
*lpbi = bi;
// 处理调色板
hPal = GetStockObject(DEFAULT_PALETTE);
if (hPal)
{
hDC = ::GetDC(NULL);
hOldPal=SelectPalette(hDC,(HPALETTE)hPal,FALSE);
RealizePalette(hDC);
}
// 获取该调色板下新的像素值
GetDIBits(hDC,hBitmap,,(UINT)Bitmap.bmHeight,(LPSTR)lpbi+sizeof(BITMAPINFOHEADER)+dwPaletteSize, (BITMAPINFO *)lpbi,DIB_RGB_COLORS);
//恢复调色板
if (hOldPal)
{
SelectPalette(hDC, hOldPal, TRUE);
RealizePalette(hDC);
::ReleaseDC(NULL, hDC);
}
// 设置位图文件头
bmfHdr.bfType = 0x4D42; // "BM"
dwDIBSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwPaletteSize+dwBmBitsSize;
bmfHdr.bfSize = dwDIBSize;
bmfHdr.bfReserved1 = ;
bmfHdr.bfReserved2 = ;
bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER)+(DWORD)sizeof(BITMAPINFOHEADER)+dwPaletteSize; std::vector<uchar>buffer;
uchar *p=(uchar*)&bmfHdr;
// 写入位图文件头
buffer.insert(buffer.end(),p,p+sizeof(BITMAPFILEHEADER));
// 写入位图文件其余内容
p=(uchar*)lpbi;
buffer.insert(buffer.end(),p,p+sizeof(BITMAPINFOHEADER)+dwPaletteSize+dwBmBitsSize);
//清除
GlobalUnlock(hDib);
GlobalFree(hDib);
pm=QPixmap::fromImage(QImage::fromData(buffer.data(),buffer.size()));
return true;
}
(2)录制画面
bool g_needstop =false;
void Record()
{
RECT rect;
//获取窗体位置大小
GetWindowRect(hd,&rect);
cv::Size frameSize;
frameSize.width=rect.right-rect.left;
frameSize.height=rect.bottom-rect.top;
cv::VideoWriter VideoWriter;
if(!VideoWriter.open("d:\\1.avi",CV_FOURCC('M', 'J', 'P', 'G'),,frameSize))
return;
while(!g_needstop)
{
QPixmap pm;
GetGDIBitmap(hd,pm,,,frameSize.width,frameSize.height);
VideoWriter.write(ImageToMat(pm.toImage()));
}
VideoWriter.release();
} Mat ImageToMat(QImage img,QString imgFormat)
{
if(img.isNull())
return Mat();
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
img.save(&buffer,imgFormat.toLatin1().data());
_InputArray arrSrc(ba.data(), ba.size());
Mat mat = cv::imdecode(arrSrc, CV_LOAD_IMAGE_COLOR);
return mat;
}
(3)播放视频
void Play()
{
cv::VideoCapture Capture;
if(!Capture.open("d:\\1.avi"))
return;
Mat frame;
//逐帧读取画面
while(Capture.read(frame))
{
//转成QImage格式用于显示
QImage img = MatToImage(frame);
emit Frame(img);
QThread::msleep();
}
Capture.release();
emit PlayFinsh();
} QImage MatToImage(Mat mat)
{
if(mat.type() == CV_8UC1)
{
QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);
// Set the color table (used to translate colour indexes to qRgb values)
image.setColorCount();
for(int i = ; i < ; i++)
{
image.setColor(i, qRgb(i, i, i));
}
// Copy input Mat
uchar *pSrc = mat.data;
for(int row = ; row < mat.rows; row ++)
{
uchar *pDest = image.scanLine(row);
memcpy(pDest, pSrc, mat.cols);
pSrc += mat.step;
}
return image;
}
// 8-bits unsigned, NO. OF CHANNELS = 3
else if(mat.type() == CV_8UC3)
{
// Copy input Mat
const uchar *pSrc = (const uchar*)mat.data;
// Create QImage with same dimensions as input Mat
QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
return image.rgbSwapped();
}
else if(mat.type() == CV_8UC4)
{
qDebug() << "CV_8UC4";
// Copy input Mat
const uchar *pSrc = (const uchar*)mat.data;
// Create QImage with same dimensions as input Mat
QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);
return image.copy();
}
else
{
qDebug() << "ERROR: Mat could not be converted to QImage.";
return QImage();
}
}
QT+OPENCV实现录屏功能的更多相关文章
- 简析hotjar录屏功能实现原理
简析hotjar录屏功能实现原理 众所周知,hotjar中录屏功能是其重要的一个卖点,看着很牛X酷炫的样子,今天就简单的分析一下其可能实现(这里只根据其请求加上个人理解分析,并不代表hotjar中真实 ...
- iOS的录屏功能
iOS的录屏功能其实没什么好说的,因为网上的教程很多,但是网上的Demo无一例外几乎都有一个bug,那就是iPad上会出现闪退,这也体现了国内的教程文档的一个特点,就是抄袭,教程几乎千篇一律,bug也 ...
- 【转载】华为荣耀V9的手机录屏功能如何开启
手机录屏有时候对我们的帮助很大,例如可以录制相应的APP使用教程.微信小程序使用流量讲解视频等,针对于软件开发人员等来说,手机录屏功能针对功能演示视频非常的有帮助.在华为荣耀V9手机中,进行手机录屏有 ...
- Qt编写GIF录屏工具(开源)
在平时的写作过程中,经常需要将一些操作动作和效果图截图成gif格式,使得涵盖的信息更全面更生动,有时候可以将整个操作过程和运行效果录制成MP4,但是文件体积比较大,而且很多网站不便于上传,基本上都支持 ...
- 原生 js 录屏功能
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8& ...
- EV录屏 --- 免费无水印,集视频录制与直播功能于一身的桌面录屏软件, 支持录屏涂鸦、实时按键显示、视频体积压缩等实用功能
https://www.ieway.cn/index.html 免费无水印,集视频录制与直播功能于一身的桌面录屏软件,支持录屏涂鸦.实时按键显示.视频体积压缩等实用功能 EVCapture 3.9.7 ...
- 手游录屏直播技术详解 | 直播 SDK 性能优化实践
在上期<直播推流端弱网优化策略 >中,我们介绍了直播推流端是如何优化的.本期,将介绍手游直播中录屏的实现方式. 直播经过一年左右的快速发展,衍生出越来越丰富的业务形式,也覆盖越来越广的应用 ...
- 基于FFMpeg的C#录屏全攻略
最近负责一个录屏的小项目,需要录制Windows窗口内容并压缩保存到指定文件夹,本想使用已有的录屏软件,但是本着学习的态度去探索了FFMpeg,本文主要介绍基于FFMpeg开源项目的C#录屏软件开发. ...
- Fundebug前端JavaScript插件更新至1.7.1,拆分录屏代码,还原部分Script error.
摘要: BUG监控插件压缩至18K. 1.7.1拆分了录屏代码,BUG监控插件压缩至18K,另外我们还原了部分Script error,帮助用户更方便地Debug.请大家及时更新哈~ 拆分录屏代码 从 ...
随机推荐
- linux下使用openssl生成https的crt和key证书
x509证书一般会用到三类文,key,csr,crt Key 是私用密钥openssl格,通常是rsa算法. Csr 是证书请求文件,用于申请证书.在制作csr文件的时,必须使用自己的私钥来签署申,还 ...
- Eclipse 常用快捷键——IDEA 常用快捷键
Eclipse 常用快捷键 熟练 单选注释 ctrl + / 多行注释 ctrl + shift + / 向下复制一行 (Duplicate Lines) ctrl+alt+down 删除一行或选中行 ...
- SpringMVC+Mybatis学习
简单Web项目搭建: 一.流程 1. 导包 n个springMVC: 2个mybatis<其中一个是mybatis-spring>: 3个jackson包: 2. xml配置 web.xm ...
- 项目(1-1)ES32获取mpu9250数据网页交互显示
教程 https://www.hackster.io/donowak/esp32-mpu9250-3d-orientation-visualisation-467dc1 项目地址 https://gi ...
- Eclipse中如何配置lombok
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/qq_41441210/article/d ...
- 洛谷P1084 运输计划
题目 题目要求使一条边边权为0时,m条路径的长度最大值的最小值. 考虑二分此长度最大值 首先需要用lca求出树上两点间的路径长度.然后取所有比mid大的路径的交集,判断有哪些边在这些路径上都有出现,然 ...
- ES6 - 开篇
一些关于es6简单的介绍与了解.初始认知有限,循序完善. ES6: 又叫ES2015,是2015年推出的JavaScript新版本. 相应的,后边推出的ES7.8.9.10等都依次是上一版本发出后一年 ...
- 东芝300D粉盒清零
东芝300D粉盒清零 1:打开前盖 2:按"OK"键3秒,等 显示 "更换硒鼓"(注:不用选 是/否,直接进入第3步) 3:按"启用"键 4 ...
- Excel中筛选两个表中相同的数据和快速填充一列的公式
将两个工作表放在一个文件中,使用if函数和countif函数判断 =if(判断条件countif(区域,条件),真值,[假值]) 实例 =if(countif(Sheet2!$A$1:$A$44,A2 ...
- linux 上使用yum 安装openjdk1.8
使用yum查找jdk: yum search java|grep jdk # yum search java|grep jdk ldapjdk-javadoc.noarch : Javadoc for ...