本文使用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实现录屏功能的更多相关文章

  1. 简析hotjar录屏功能实现原理

    简析hotjar录屏功能实现原理 众所周知,hotjar中录屏功能是其重要的一个卖点,看着很牛X酷炫的样子,今天就简单的分析一下其可能实现(这里只根据其请求加上个人理解分析,并不代表hotjar中真实 ...

  2. iOS的录屏功能

    iOS的录屏功能其实没什么好说的,因为网上的教程很多,但是网上的Demo无一例外几乎都有一个bug,那就是iPad上会出现闪退,这也体现了国内的教程文档的一个特点,就是抄袭,教程几乎千篇一律,bug也 ...

  3. 【转载】华为荣耀V9的手机录屏功能如何开启

    手机录屏有时候对我们的帮助很大,例如可以录制相应的APP使用教程.微信小程序使用流量讲解视频等,针对于软件开发人员等来说,手机录屏功能针对功能演示视频非常的有帮助.在华为荣耀V9手机中,进行手机录屏有 ...

  4. Qt编写GIF录屏工具(开源)

    在平时的写作过程中,经常需要将一些操作动作和效果图截图成gif格式,使得涵盖的信息更全面更生动,有时候可以将整个操作过程和运行效果录制成MP4,但是文件体积比较大,而且很多网站不便于上传,基本上都支持 ...

  5. 原生 js 录屏功能

    <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8& ...

  6. EV录屏 --- 免费无水印,集视频录制与直播功能于一身的桌面录屏软件, 支持录屏涂鸦、实时按键显示、视频体积压缩等实用功能

    https://www.ieway.cn/index.html 免费无水印,集视频录制与直播功能于一身的桌面录屏软件,支持录屏涂鸦.实时按键显示.视频体积压缩等实用功能 EVCapture 3.9.7 ...

  7. 手游录屏直播技术详解 | 直播 SDK 性能优化实践

    在上期<直播推流端弱网优化策略 >中,我们介绍了直播推流端是如何优化的.本期,将介绍手游直播中录屏的实现方式. 直播经过一年左右的快速发展,衍生出越来越丰富的业务形式,也覆盖越来越广的应用 ...

  8. 基于FFMpeg的C#录屏全攻略

    最近负责一个录屏的小项目,需要录制Windows窗口内容并压缩保存到指定文件夹,本想使用已有的录屏软件,但是本着学习的态度去探索了FFMpeg,本文主要介绍基于FFMpeg开源项目的C#录屏软件开发. ...

  9. Fundebug前端JavaScript插件更新至1.7.1,拆分录屏代码,还原部分Script error.

    摘要: BUG监控插件压缩至18K. 1.7.1拆分了录屏代码,BUG监控插件压缩至18K,另外我们还原了部分Script error,帮助用户更方便地Debug.请大家及时更新哈~ 拆分录屏代码 从 ...

随机推荐

  1. 轻松学习之三——IMP指针的作用

    http://www.jianshu.com/p/425a39d43d16 可能大家一直看到有许多朋友在Runtime相关文章中介绍IMP指针的概念,那么IMP究竟有什么实际作用呢?让我们先从一个函数 ...

  2. 自定义枚举 --- Swagger文档展示

    在其它两篇文章中,已经解决的自定义枚举在MyBatis以及Rest接口的转换,但是在Springfox中还存在问题,不能使用code来作为api.本文通过扩展Springfox,实现了对自定义枚举的良 ...

  3. [Git] --amend

    Change a Commit Message that Hasn't Been Pushed Yet If you make a mistake in a commit message but HA ...

  4. [教程]Ubuntu16.04安装TeX Live

    [教程]Ubuntu16.04安装TeX Live step 1 戳这里下载镜像 (只需要下载texlive.iso) 在终端输入 sudo apt-get install perl-tk step ...

  5. BZOJ 1801: [Ahoi2009]中国象棋

    题目描述 //每行每列最多放两个,可以讨论第i-1行到第i行的每一种情况 #include<complex> #include<cstdio> using namespace ...

  6. LSF 作业系统常用命令

    LSF(Load Sharing Facility)是一个被广泛使用的作业管理系统,具有高吞吐.配置灵活的优点.通过 LSF 集中监控和调度,可以充分利用计算机的CPU.内存.磁盘等资源. bqueu ...

  7. JS简单获取当前日期时间的方法(yyyy-MM-dd hh:mm:ss)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xht ...

  8. Django纪要

    Django流程 mvt 创建工程 路由的引导 web 应用程序b发送请求 -->uwsgi-->Django框架-->接收请求处理业务逻辑返回响应-->b本质 接收请求 业务 ...

  9. Object changed by Unknown

    https://documentation.red-gate.com/soc7/troubleshooting/object-changed-by-unknown https://documentat ...

  10. How to Install Ruby on CentOS/RHEL 7/6

    How to Install Ruby on CentOS/RHEL 7/6 . Ruby is a dynamic, object-oriented programming language foc ...