本篇介绍如何使用d3d 9抓取屏幕图像,并将图像转化为RGB数据,转化后的数据可直接传递给FFmpeg进行编解码。
获取桌面数据容易,转为RGB网上的例子就少了,大多只会使用D3D api保存为一个图片,本文解决了这个难题,如果你想使用D3D抓图然后使用FFMPEG进行录制会对你有所帮助。
1.创建D3D对象
_d3d = Direct3DCreate9(D3D_SDK_VERSION);
2.获取display配置
memset(&_d3d_displaymodel, 0, sizeof(_d3d_displaymodel));
HRESULT hr = _d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &_d3d_displaymodel);
if (FAILED(hr))
{
SafeRelease(_d3d);
return error;
}
3.创建设备对象
D3DPRESENT_PARAMETERS d3dParam;
memset(&d3dParam, 0, sizeof(d3dParam));
d3dParam.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
d3dParam.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dParam.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dParam.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
d3dParam.Windowed = TRUE;
d3dParam.BackBufferWidth = 0;
d3dParam.BackBufferHeight = 0;
d3dParam.BackBufferFormat = D3DFMT_UNKNOWN;
d3dParam.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dParam.hDeviceWindow = hWnd;
// create d3ddevice
hr = _d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dParam, &_d3d_device);
if (FAILED(hr))
{
SafeRelease(_d3d_device);
return error;
}
4. 创建CreateOffscreenPlainSurface对象
hr = _d3d_device->CreateOffscreenPlainSurface(_d3d_displaymodel.Width, _d3d_displaymodel.Height,
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &_d3d_surface, NULL);
if (FAILED(hr))
{
SafeRelease(_d3d_device);
SafeRelease(_d3d_surface);
return error;
}
5.获取数据
_d3d_device->GetFrontBufferData(0, _d3d_surface);
D3DLOCKED_RECT lr;
_d3d_surface->LockRect(&lr,NULL,
D3DLOCK_NO_DIRTY_UPDATE|
D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY);
6.转为为RGB数据
int BITSPERPIXEL = 32;
DWORD* pBuf = (DWORD*)lr.pBits;
int nPitch = lr.Pitch;
for( int i=0 ; i < winheight ; i++)
{
memcpy((BYTE*) _buffer + i *
winwidth * BITSPERPIXEL/8 ,
(BYTE*) pBuf+ i* nPitch ,
winwidth* BITSPERPIXEL/8);
}
注意桌面缓存不使用BackBuffer,所以这里一定要用GetFrontBufferData,GetBackBuffer是拿不到数据的。
完整代码:
#ifndef RECORD_WINDOW_D3D
#define RECORD_WINDOW_D3D
#include "record_desktop.h"
#include <Windows.h>
#include <d3d9.h>
#pragma comment(lib,"d3d9.lib")
namespace RecorderSP {
template <typename T>
inline void SafeRelease(T* &p)
{
if (NULL != p)
{
p->Release();
p = NULL;
}
}
class RecordWindowD3D :
public RecordObject
{
public:
RecordWindowD3D();
~RecordWindowD3D();
virtual int init(
const Record_Rect &rect,
const int fps);
virtual int start();
virtual int pause();
virtual int resume();
virtual int stop();
protected:
virtual void clean_up();
private:
void draw_cursor(HDC hdc);
int do_record();
void do_sleep(int64_t dur, int64_t pre, int64_t now);
void record_func();
uint8_t *_buffer;
uint32_t _buffer_size;
uint32_t _width, _height;
std::atomic_bool _draw_cursor;
HDC _hdc;
HBITMAP _bmp, _bmp_old;
CURSORINFO _ci;
IDirect3D9* _d3d{NULL};
IDirect3DDevice9* _d3d_device{NULL};
IDirect3DSurface9* _d3d_surface{NULL};
D3DDISPLAYMODE _d3d_displaymodel;
};
}
#endif
/******************************************************************************************源文件*********************************************************************************/
#include "record_window_d3d.h"
#include "error_define.h"
#include "log_helper.h"
#include <iostream>
namespace RecorderSP {
RecordWindowD3D::RecordWindowD3D()
{
_data_type = Record_Desktop_Data_Types::AT_DESKTOP_BGRA;
_buffer = NULL;
_buffer_size = 0;
_draw_cursor = true;
_hdc = NULL;
_bmp = NULL;
_bmp_old = NULL;
_ci = { 0 };
}
RecordWindowD3D::~RecordWindowD3D()
{
stop();
clean_up();
}
int RecordWindowD3D::init(const Record_Rect & rect, const int fps)
{
int error = AE_NO;
if (_inited == true) {
return error;
}
_d3d = Direct3DCreate9(D3D_SDK_VERSION);
if (_d3d == NULL)
return error;
_fps = fps;
_rect = rect;
do {
_width = rect.right - rect.left;
_height = rect.bottom - rect.top;
_buffer_size = (_width * 32 + 31) / 32 * _height * 4;
_buffer = new uint8_t[_buffer_size];
_start_time = av_gettime_relative();
_time_base = { 1,AV_TIME_BASE };
_pixel_fmt = AV_PIX_FMT_BGRA;
_inited = true;
} while (0);
al_info("init gdi finished,error: %s %ld", err2str(error), GetLastError());
return error;
}
int RecordWindowD3D::start()
{
if (_running == true) {
al_warn("record desktop gdi is already running");
return AE_NO;
}
if (_inited == false) {
return AE_NEED_INIT;
}
_running = true;
_thread = std::thread(std::bind(&RecordWindowD3D::record_func, this));
return AE_NO;
}
int RecordWindowD3D::pause()
{
_paused = true;
return AE_NO;
}
int RecordWindowD3D::resume()
{
_paused = false;
return AE_NO;
}
int RecordWindowD3D::stop()
{
_running = false;
if (_thread.joinable())
_thread.join();
return AE_NO;
}
void RecordWindowD3D::clean_up()
{
_inited = false;
if (_buffer)
delete[] _buffer;
_buffer = nullptr;
}
void RecordWindowD3D::draw_cursor(HDC hdc)
{
if (!(_ci.flags & CURSOR_SHOWING))
return;
//is cursor in the tartet zone
if (_ci.ptScreenPos.x < _rect.left ||
_ci.ptScreenPos.x > _rect.right ||
_ci.ptScreenPos.y < _rect.top ||
_ci.ptScreenPos.y > _rect.bottom
)
return;
HICON icon;
ICONINFO ii;
icon = CopyIcon(_ci.hCursor);
if (!icon)
return;
int dstx = 0, dsty = 0;
dstx = abs(_ci.ptScreenPos.x - _rect.left);
dsty = abs(_ci.ptScreenPos.y - _rect.top);
if (GetIconInfo(icon, &ii)) {
POINT pos;
DrawIconEx(hdc, dstx, dsty, icon, 0, 0, 0, NULL, DI_NORMAL);
DeleteObject(ii.hbmColor);
DeleteObject(ii.hbmMask);
}
DestroyIcon(icon);
}
int RecordWindowD3D::do_record()
{
int error = AE_ERROR;
do {
std::string windowname = get_main_record_window();
HWND hWnd = ::FindWindowA(NULL, windowname.c_str());
memset(&_d3d_displaymodel, 0, sizeof(_d3d_displaymodel));
HRESULT hr = _d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &_d3d_displaymodel);
if (FAILED(hr))
{
SafeRelease(_d3d);
return error;
}
//get window rect
RECT rtWnd;
::GetClientRect(hWnd, &rtWnd);
int winwidth = rtWnd.right-rtWnd.left;
int winheight = rtWnd.bottom - rtWnd.top;
//init d3d param
D3DPRESENT_PARAMETERS d3dParam;
memset(&d3dParam, 0, sizeof(d3dParam));
d3dParam.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
d3dParam.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dParam.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dParam.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
d3dParam.Windowed = TRUE;
d3dParam.BackBufferWidth = 0;
d3dParam.BackBufferHeight = 0;
d3dParam.BackBufferFormat = D3DFMT_UNKNOWN;
d3dParam.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dParam.hDeviceWindow = hWnd;
// create d3ddevice
hr = _d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dParam, &_d3d_device);
if (FAILED(hr))
{
SafeRelease(_d3d_device);
return error;
}
IDirect3DSurface9 *pBackBuffer = NULL;
hr = _d3d_device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
if (FAILED(hr))
{
SafeRelease(_d3d_device);
SafeRelease(_d3d_surface);
return error;
}
hr = _d3d_device->CreateOffscreenPlainSurface(_d3d_displaymodel.Width, _d3d_displaymodel.Height,
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &_d3d_surface, NULL);
if (FAILED(hr))
{
SafeRelease(_d3d_device);
SafeRelease(_d3d_surface);
return error;
}
// D3DSURFACE_DESC surfacedesc;
// hr = pBackBuffer->GetDesc(&surfacedesc);
// if (FAILED(hr))
// {
// SafeRelease(_d3d_device);
// SafeRelease(_d3d_surface);
// SafeRelease(pBackBuffer);
// return error;
// }
// _d3d_device->GetRenderTargetData(pBackBuffer,_d3d_surface);
// D3DLOCKED_RECT lr;
// hr = pBackBuffer->LockRect(&lr,NULL, D3DLOCK_NO_DIRTY_UPDATE|D3DLOCK_NOSYSLOCK|
// D3DLOCK_READONLY);
// if (FAILED(hr))
// {
// SafeRelease(_d3d_device);
// SafeRelease(_d3d_surface);
// SafeRelease(pBackBuffer);
// return error;
// }
_d3d_device->GetFrontBufferData(0, _d3d_surface);
D3DLOCKED_RECT lr;
_d3d_surface->LockRect(&lr,NULL,
D3DLOCK_NO_DIRTY_UPDATE|
D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY);
int BITSPERPIXEL = 32;
DWORD* pBuf = (DWORD*)lr.pBits;
int nPitch = lr.Pitch;
for( int i=0 ; i < winheight ; i++)
{
memcpy((BYTE*) _buffer + i *
winwidth * BITSPERPIXEL/8 ,
(BYTE*) pBuf+ i* nPitch ,
winwidth* BITSPERPIXEL/8);
}
_d3d_surface->UnlockRect();
// RECT rtCapture;
// ::GetWindowRect(hWnd, &rtCapture);
// ID3DXBuffer* pBuffer=NULL;
// hr = D3DXSaveSurfaceToFileInMemory(pBuffer, D3DXIFF_BMP, _d3d_surface, NULL, rtCapture);
// if (FAILED(hr))
// {
// SafeRelease(_d3d_device);
// SafeRelease(_d3d_surface);
// return error;
// }
SafeRelease(_d3d_device);
SafeRelease(_d3d_surface);
// //draw cursor
// memset(&_ci, 0, sizeof(CURSORINFO));
// _ci.cbSize = sizeof(CURSORINFO);
// if (GetCursorInfo(&_ci)) {
// draw_cursor(hdc_mem);
// }
// BITMAPINFOHEADER bi;
// bi.biSize = sizeof(BITMAPINFOHEADER);
// bi.biWidth = _width;
// bi.biHeight = _height * (-1);
// bi.biPlanes = 1;
// bi.biBitCount = 32;//should get from system color bits
// bi.biCompression = BI_RGB;
// bi.biSizeImage = 0;
// bi.biXPelsPerMeter = 0;
// bi.biYPelsPerMeter = 0;
// bi.biClrUsed = 0;
// bi.biClrImportant = 0;
// //scan colors by line order
// int ret = GetDIBits(hdc_mem, hbm_mem, 0, _height, _buffer, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
// if (ret <= 0 || ret == ERROR_INVALID_PARAMETER) {
// al_error("get dibits failed:%lu", GetLastError());
// error = AE_GDI_GET_DIBITS_FAILED;
// break;
// }
error = AE_NO;
} while (0);
return AE_NO;
}
void RecordWindowD3D::do_sleep(int64_t dur, int64_t pre, int64_t now)
{
int64_t delay = now - pre;
dur = delay > dur ? max(0, dur - (delay - dur)) : (dur + dur - delay);
//al_debug("%lld", delay);
if(dur)
av_usleep(dur);
}
void RecordWindowD3D::record_func()
{
AVFrame *frame = av_frame_alloc();
int64_t pre_pts = 0;
int64_t dur = AV_TIME_BASE / _fps;
int ret = AE_NO;
while (_running)
{
ret = do_record();
if (ret != AE_NO) {
if (_on_local_error) _on_local_error(ret);
if (_on_publish_error) _on_publish_error(ret);
break;
}
frame->pts = av_gettime_relative();
frame->pkt_dts = frame->pts;
frame->width = _width;
frame->height = _height;
frame->format = AV_PIX_FMT_BGRA;
frame->pict_type = AV_PICTURE_TYPE_I;
frame->pkt_size = _width * _height * 4;
av_image_fill_arrays(frame->data,
frame->linesize,
_buffer,
AV_PIX_FMT_BGRA,
_width,
_height,
1
);
if (_on_local_data)
_on_local_data(frame);
if (_on_publish_data)
_on_publish_data(frame);
do_sleep(dur, pre_pts, frame->pts);
pre_pts = frame->pts;
}
av_frame_free(&frame);
}
}
使用D3D 9抓取屏幕图像并转为RGB数据

这篇博客介绍了如何利用Direct3D 9捕获屏幕图像并将其转换为RGB数据,以便与FFmpeg进行编解码。详细步骤包括创建D3D对象、获取显示配置、创建设备对象、创建CreateOffscreenPlainSurface对象以及将数据转换为RGB格式。适用于需要使用D3D抓图和FFmpeg录制的场景。
1903

被折叠的 条评论
为什么被折叠?



