本文使用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. How would you differentiate JDK, JRE, JVM, and JIT?

    Q5. How would you differentiate JDK, JRE, JVM, and JIT?A5. There is no better way to get the big pic ...

  2. matlab基础向1-6:基础语法

    1.软件中如何运行代码? 命令行直接写代码,回车执行,也可以在文件里编写代码,比如有文件hello.m,点击“Run”直接运行或者在命令行窗口里输入“hello+回车”运行. 2.清空命令行 clc+ ...

  3. arduino 开发视频

    http://blog.uctronics.com/downloads/shields/ArduCAM_Camera_Shield_V2_DS.pdf http://www.arducam.com/k ...

  4. Docker网络配置、Docker部署分布式项目

    目标 1.Docker网络配置 2.Docker部署SpringCloud项目 Docker网络配置 Docker网络模式介绍 Docker在创建容器时有四种网络模式:bridge/host/cont ...

  5. [RN] React Native 使用 AsyncStorage 存储 缓存数据

    React Native 使用 AsyncStorage 存储 缓存数据 AsyncStorage是一个简单的.异步的.持久化的Key-Value存储系统,它对于App来说是全局性的.这是官网上对它的 ...

  6. JAVA基础--MySQL

    环境信息 安装环境 :Ubuntu 14 Desktop(桌面版) m安装版本 :MySQL 5.7.23 安装步骤 linux环境下安装过程很简单 三条命令行即可安装完成: 1. sudo apt- ...

  7. shell 输出文件各行的长度

    test.sh #!/bin/bash string="hello,shell,split,test" array=(${string//,/ }) for var in ${ar ...

  8. android studio如何连接夜神模拟器

    原创 2018-02-05 21:35:03 会飞的鱼儿android 阅读数 16706 文章标签: 夜神模拟器连接夜神模拟器的简单方式 更多 分类专栏: Android   版权声明:本文为博主原 ...

  9. 阿里云mysql数据库恢复到本地

    本地环境为win10,mysql引擎为InnoDB 第一步:服务里面停掉mysql 第二步:把my.ini 的 innodb_force_recovery  设置为0 第三步:把.frm和.idb文件 ...

  10. OSS利用ossutil64上传文件

     ossutil64下载地址: http://gosspublic.alicdn.com/ossutil/1.6.0/ossutil64?spm=a2c4g.11186623.2.12.3638159 ...