VC++ 屏幕捕获(GDI)

屏幕捕获RGB数据回调函数(CVideoCapture.h):

#ifndef _VIDEODATA_CALLBACK_
#define _VIDEODATA_CALLBACK_
typedef void(*LPVideoDataRealCallback)(unsigned char *pRgbData, int size, int width, int height, void* pContext);
#endif

调用接口定义(CVideoCapture.h):

// GDI方式,设置视频回调函数,并开始捕获视频数据
AVFILTER_API void* GDI_VideoSourceStartCapture(int left, int top, int width, int height, LPVideoDataRealCallback pCallback, void* pUser);
// 停止视频数据捕获
AVFILTER_API bool GDI_VideoSourceStopCapture(void* pSource);

实现源码(CVideoCapture.c):

#include <stdio.h>
#include "CVideoCapture.h"
#include <stdlib.h>
#include <windows.h>
#include <process.h>

#pragma comment(lib,"user32")
#pragma comment(lib,"gdi32")
typedef struct tVideoSource
{
	int		m_nLeft;//捕获区域坐标值-左
	int		m_nTop;//捕获区域坐标值-上
	int		m_nWidth;//捕获区域-宽度
	int		m_nHeight;//捕获区域-高度

	bool					m_bCaptureCursor;//是否抓取光标
	bool					m_bActive;//是否激活,即是否获取到视频帧
	HANDLE					m_hCaptureThread;//捕获线程句柄
	HANDLE					m_hStopSignal;//线程停止信号

	CRITICAL_SECTION		m_csMemLock;//数据访问互斥锁
	void*					m_memRawBuffer;//RGB数据存放缓冲区
	int						m_nMemSize;//RGB数据缓冲区大小

	BITMAPINFO				m_bmpInfo;//位图信息
	HDC						m_hScreenDC;//屏幕上下文-DC
	HDC						m_hMemDC;//内存上下文-DC,此处采用的双缓存(其实没必要,只有绘图才需要双缓存,避免闪烁)
	HBITMAP					m_hBitmap;//位图句柄
	HBITMAP					m_hOldBitmap;//旧位图句柄

	LPVideoDataRealCallback m_pVideoDataRealCalBack;//实时捕获数据回调函数
	void* m_pVideoDataRealCalBackUser;//实时捕获数据用户接受对象
}VideoSource, *LPVideoSource;

//申请一个视频源对象
void AllocVideoResource(LPVideoSource* ppSource);
//释放视频源对象
void ReleaseVideoResource(LPVideoSource pSource);
//开始捕获视频数据
bool StartVideoCapture(LPVideoSource pSource);
//停止捕获视频数据
void StopVideoCapture(LPVideoSource pSource);
//视频数据循环捕获线程
DWORD WINAPI OnVideoCaptureThread(LPVOID param);
//视频数据捕获函数
bool DoVideoCapture(LPVideoSource pSource);
//视频数据捕获函数
bool ProcessVideoCaptureData(LPVideoSource pSource);

//申请一个视频源对象
void AllocVideoResource(LPVideoSource* ppSource)
{
	//参数初始化
	*ppSource = (LPVideoSource)malloc(sizeof(VideoSource));
	((LPVideoSource)*ppSource)->m_bActive = false;
	((LPVideoSource)*ppSource)->m_bCaptureCursor = false;
	((LPVideoSource)*ppSource)->m_hCaptureThread = NULL;
	((LPVideoSource)*ppSource)->m_hStopSignal = CreateEvent(NULL, TRUE, FALSE, NULL);
	((LPVideoSource)*ppSource)->m_memRawBuffer = NULL;
	((LPVideoSource)*ppSource)->m_pVideoDataRealCalBack = NULL;
	((LPVideoSource)*ppSource)->m_pVideoDataRealCalBackUser = NULL;
	DeleteObject(((LPVideoSource)*ppSource)->m_hOldBitmap);
	DeleteObject(((LPVideoSource)*ppSource)->m_hBitmap);
	DeleteDC(((LPVideoSource)*ppSource)->m_hMemDC);
	ReleaseDC(NULL, ((LPVideoSource)*ppSource)->m_hScreenDC);
	InitializeCriticalSection(&((LPVideoSource)*ppSource)->m_csMemLock);
}

//释放视频源对象
void ReleaseVideoResource(LPVideoSource pSource)
{
	StopVideoCapture(pSource);
	ReleaseDC(NULL, pSource->m_hScreenDC);
	DeleteDC(pSource->m_hMemDC);
	free(((LPVideoSource)pSource)->m_memRawBuffer);
	((LPVideoSource)pSource)->m_memRawBuffer = NULL;
	DeleteCriticalSection(&((LPVideoSource)pSource)->m_csMemLock);
	free(pSource);
	pSource = NULL;
}

//开始捕获视频数据
bool StartVideoCapture(LPVideoSource pSource)
{
	// 释放之前申请的的存放图像的缓存
	free(((LPVideoSource)pSource)->m_memRawBuffer);
	((LPVideoSource)pSource)->m_memRawBuffer = NULL;
	((LPVideoSource)pSource)->m_nMemSize = 0;

	//计算所需存放图像的缓存大小
	((LPVideoSource)pSource)->m_nMemSize = ((LPVideoSource)pSource)->m_nWidth * ((LPVideoSource)pSource)->m_nHeight * 3;//获取的图像位图深度24位,所以是*3
	((LPVideoSource)pSource)->m_memRawBuffer = (char*)malloc(((LPVideoSource)pSource)->m_nMemSize);
	memset(((LPVideoSource)pSource)->m_memRawBuffer, 0, ((LPVideoSource)pSource)->m_nMemSize);

	//创建捕获图像的线程
	unsigned int dwThreadId;
	pSource->m_hCaptureThread = (HANDLE)_beginthreadex(NULL,
		0,
		&OnVideoCaptureThread,
		pSource,
		THREAD_PRIORITY_NORMAL,
		&dwThreadId);
	if (!pSource->m_hCaptureThread)
		return false;

	((LPVideoSource)pSource)->m_bActive = true;
	return true;
}

//停止捕获视频数据
void StopVideoCapture(LPVideoSource pSource)
{
	//发送线程停止工作信号
	SetEvent(pSource->m_hStopSignal);
	//等待线程安全退出
	if (pSource->m_bActive)
	{
		WaitForSingleObject(pSource->m_hCaptureThread, INFINITE);
	}

	pSource->m_nMemSize = 0;
}

//视频数据循环捕获线程
DWORD WINAPI OnVideoCaptureThread(LPVOID param)
{
	LPVideoSource pSource = (LPVideoSource)param;
	DoVideoCapture(pSource);
	_endthreadex(0);
	return 0;
}

//视频数据循环捕获线程-处理线程
bool DoVideoCapture(LPVideoSource pSource)
{
	bool         bReconnect = false;
	DWORD        dwTimeout = 1;

	// 等待超时进入下一次图像数据获取
	while (WaitForSingleObject(pSource->m_hStopSignal, dwTimeout) == WAIT_TIMEOUT)
	{
		if (ProcessVideoCaptureData(pSource) )
		{
			//获取图像成功,回调数据给应用层用户处理
			if (pSource->m_pVideoDataRealCalBack )
			{
				pSource->m_pVideoDataRealCalBack((LPBYTE)pSource->m_memRawBuffer, pSource->m_nMemSize, pSource->m_nWidth, pSource->m_nHeight, pSource->m_pVideoDataRealCalBackUser);
			}
		}
	}

	//关闭信号
	CloseHandle(pSource->m_hStopSignal);
	pSource->m_hStopSignal = NULL;
	//关闭线程句柄
	CloseHandle(pSource->m_hCaptureThread);
	pSource->m_hCaptureThread = NULL;
	((LPVideoSource)pSource)->m_bActive = false;
	return true;
}

//视频数据捕获函数
bool ProcessVideoCaptureData(LPVideoSource pSource)
{
	POINT ptCursor;		//光标位置
	HCURSOR hCurSor;	//光标句柄
	ICONINFO icoInfo;	//图标信息
	CURSORINFO cursorInfo;//光标信息
	
	//将屏幕信息绘制到内存上下文里面去
	BitBlt(pSource->m_hMemDC, 0, 0, pSource->m_nWidth, pSource->m_nHeight, pSource->m_hScreenDC, pSource->m_nLeft, pSource->m_nTop, SRCCOPY);

	//抓取屏幕鼠标信息
	if (pSource->m_bCaptureCursor)
	{
		GetCursorPos(&ptCursor);
		ptCursor.x -= pSource->m_nLeft;
		ptCursor.y -= pSource->m_nTop;

		memset(&cursorInfo, 0, sizeof(cursorInfo));
		cursorInfo.cbSize = sizeof(cursorInfo);
		GetCursorInfo(&cursorInfo);
		hCurSor = cursorInfo.hCursor;
		if (hCurSor)
		{
			if (GetIconInfo(hCurSor, &icoInfo))
			{
				ptCursor.x -= icoInfo.xHotspot;
				ptCursor.y -= icoInfo.yHotspot;
				if (icoInfo.hbmMask) DeleteObject(icoInfo.hbmMask);
				if (icoInfo.hbmColor) DeleteObject(icoInfo.hbmColor);
			}

			DrawIcon(pSource->m_hMemDC, ptCursor.x, ptCursor.y, hCurSor);
		}
	}

	//从内存上下文里面获取图像数据信息
	GetDIBits(pSource->m_hMemDC, pSource->m_hBitmap, 0, pSource->m_nHeight, (PRGBTRIPLE)pSource->m_memRawBuffer, (LPBITMAPINFO)&pSource->m_bmpInfo, DIB_RGB_COLORS);
	return true;
}

//设置图像数据回调函数
void SetVideoCaptureDataRealCallback(LPVideoSource pSource, LPVideoDataRealCallback pVideoDataRealCalBack, void* pUser)
{
	pSource->m_pVideoDataRealCalBack = pVideoDataRealCalBack;
	pSource->m_pVideoDataRealCalBackUser = pUser;
}

// GDI方式,设置视频回调函数,并开始捕获视频数据
AVFILTER_API void* GDI_VideoSourceStartCapture(int left, int top, int width, int height, LPVideoDataRealCallback pCallback, void* pUser)
{
	RECT rect = { left, top, left + width, top + height };
	int nWidth = rect.right - rect.left;
	int nHeight = rect.bottom - rect.top;

	//
	// 对齐4字节边界
	// 因为GDI的原因,分辨率必须要是4字节的边界,否则抓出来的图像重新渲染的话,
	// 会出现花屏现象,这里处理的目的就是为了保证是4字节的边界。
	//
	int nMod = nWidth % 4;
	if (nMod)
	{
		rect.left += (nMod / 2);
		rect.right -= (nMod / 2);
	}

	nMod = nHeight % 4;
	if (nMod)
	{
		rect.top += (nMod / 2);
		rect.bottom -= (nMod / 2);
	}

	// 申请一个视频数据源
	LPVideoSource pSource = NULL;
	AllocVideoResource(&pSource);

	//保存捕获区域参数
	pSource->m_nLeft = rect.left;
	pSource->m_nTop = rect.top;
	pSource->m_nWidth = rect.right - rect.left;
	pSource->m_nHeight = rect.bottom - rect.top;
	pSource->m_bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	pSource->m_bmpInfo.bmiHeader.biWidth = rect.right - rect.left;
	pSource->m_bmpInfo.bmiHeader.biHeight = rect.bottom - rect.top;
	pSource->m_bmpInfo.bmiHeader.biPlanes = 1;
	pSource->m_bmpInfo.bmiHeader.biBitCount = 24;
	pSource->m_bmpInfo.bmiHeader.biCompression = BI_RGB;
	pSource->m_hScreenDC = GetDC(NULL);
	if (pSource->m_hScreenDC == NULL)
		goto exit;
	
	//创建一个与指定设备兼容的内存设备上下文环境(DC)
	pSource->m_hMemDC = CreateCompatibleDC(pSource->m_hScreenDC);	
	if (pSource->m_hMemDC == NULL)
		goto exit;
	
	//创建与指定的设备环境相关的设备兼容的位图
	pSource->m_hBitmap = CreateCompatibleBitmap(pSource->m_hScreenDC, pSource->m_nWidth, pSource->m_nHeight);
	
	if (pSource->m_hBitmap == NULL)
		goto exit;
	
	//这里把m_hBitmap的位图选择到m_hMemDC,之后这个m_hMemDC就拥有和m_hBitmap同样大小的绘图区域
	pSource->m_hOldBitmap = (HBITMAP)SelectObject(pSource->m_hMemDC, pSource->m_hBitmap);
	SetVideoCaptureDataRealCallback(pSource, pCallback, pUser);
	if (pSource && StartVideoCapture(pSource))
	{
		return pSource;
	}
	else
	{
		goto exit;
	}

exit:
	ReleaseVideoResource(pSource);
	pSource = NULL;
	return pSource;
}

// 停止视频数据捕获
AVFILTER_API bool GDI_VideoSourceStopCapture(void* pSource)
{
	if (pSource == NULL )
		return true;

	SetVideoCaptureDataRealCallback(pSource, NULL, NULL);
	StopVideoCapture(pSource);
	return true;
}

GDI方式在1920*1080分辨率下,实时性较差(帧率大概20帧/秒左右)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值