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.请大家及时更新哈~ 拆分录屏代码 从 ...
随机推荐
- MapTask工作机制
(1)Read阶段:MapTask通过用户编写的RecordReader,从输入InputSplit中解析出一个个key/value. (2)Map阶段:该节点主要是将解析出的key/value交给用 ...
- 证明StringBuffer线程安全,StringBuilder线程不安全
证明StringBuffer线程安全,StringBuilder线程不安全证明StringBuffer线程安全StringBuilder线程不安全测试思想测试代码结果源码分析测试思想分别用1000个线 ...
- 015_matlab运行C语言
视频教程:https://v.qq.com/x/page/q3039wsuged.html 资料下载:https://download.csdn.net/download/xiaoguoge11/12 ...
- MongoDB 启动报错
1.配置MongoDB ls /etc/mongod.conf 可以根据此配置文件启动 或者根据自己需求进行配置文件的变更 重要提醒: 如果变更MongoDB配置文件中:日志与数据文件目录,那么要把这 ...
- css实现块级元素水平垂直居中的方法?
父级给相对定位,子级给绝对定位,margin设置为auto,上下左右值设为0. 父级给相对定位,子级给绝对定位,设置left和top为50%,再向左和向上移动负的子级一半. 父级设置display:f ...
- React 如何适用less
1.使用 create-react-app 创建的项目,默认情况下是看不到 webpack 相关的配置文件,我们需要给它暴露出来,使用下面命令即可: npm run eject 2.添加less np ...
- Java动态代理-实战
Java动态代理-实战 只要是写Java的,动态代理就一个必须掌握的知识点,当然刚开始接触的时候,理解的肯定比较浅,渐渐的会深入一些,这篇文章通过实战例子帮助大家深入理解动态代理. 说动态代理之前,要 ...
- Shell 逐行读取文件的4中方法
方法1:while循环中执行效率最高,最常用的方法. function while_read_LINE_bottm(){ While read LINE do echo $LINE done < ...
- 二分法递归版本(c++)
利用二分法求解在区间[0,π/2]上的根 #include<iostream> #include <cmath> using namespace std; double dic ...
- 《Maven实战》整理
一.maven介绍 Maven是优秀的构建工具,能够帮我们自动化构建过程,从清理.编译.测试到生成报告,再到打包和部署. Maven能帮助我们标准化构建过程.在Maven之前,十个项目可能有十种构建方 ...