功能描述:
1、用NVAPI 进行D3D9抓屏,抓屏后创建HANDLE进行数据交换
2、D3D11利用D3D9传入的HANDLE使用OpenSharedResource来打开句柄实现D3D9到D3D11的数据传递
需要的库:
1、NVAPI 安装完成后需要运行NVIDIA Capture SDK\bin目录下的NvFBCEnable.exe使能NVFBC抓屏
2、DXSDK_Jun10
步奏:
一、抓屏
1、初始化设备
//初始化D3d设备 deviceID为显示器ID
BOOL NVAPIGrab::InitD3D(unsigned int deviceID)
{
HRESULT hr = S_OK;
D3DPRESENT_PARAMETERS d3dpp;
D3DADAPTER_IDENTIFIER9 adapterId;
unsigned int iAdapter = NULL;
//判断能否抓对应显示器的数据
Direct3DCreate9Ex(D3D_SDK_VERSION, &m_pD3DEx);
if (deviceID >= m_pD3DEx->GetAdapterCount())
{
printf("Error: (deviceID=%d) is not a valid GPU device. Headless video devices will not be detected. <<\n\n", deviceID);
return FALSE;
}
hr = m_pD3DEx->GetAdapterIdentifier(deviceID, 0, &adapterId);
if (hr != S_OK)
{
printf("Error: (deviceID=%d) is not a valid GPU device. <<\n\n", deviceID);
return FALSE;
}
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = true;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.BackBufferWidth = m_width;
d3dpp.BackBufferHeight = m_height;
d3dpp.BackBufferCount = 1;
d3dpp.SwapEffect = D3DSWAPEFFECT_COPY;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
d3dpp.Flags = D3DPRESENTFLAG_VIDEO;
DWORD dwBehaviorFlags = D3DCREATE_FPU_PRESERVE | D3DCREATE_MULTITHREADED | D3DCREATE_HARDWARE_VERTEXPROCESSING;
hr = m_pD3DEx->CreateDeviceEx(deviceID, D3DDEVTYPE_HAL, NULL, dwBehaviorFlags, &d3dpp, NULL, &m_pD3D9Device);
if (FAILED(hr))
{
OutputDebugString(L"CreateDevice Filed!");
return FALSE;
}
return TRUE;
}
2、创建抓屏表面
//创建抓屏表面
BOOL NVAPIGrab::InitD3DSurfaces()
{
HRESULT hr = E_FAIL;
if (m_pD3D9Device)
{
hr = m_pD3D9Device->CreateOffscreenPlainSurface(m_width, m_height, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pD3D9Surf, NULL);
if (FAILED(hr))
{
fprintf(stderr, "Failed to create D3D9 surfaces for output. Error 0x%08x\n", hr);
return FALSE;
}
}
return TRUE;
}
3、初始化抓屏
BOOL NVAPIGrab::InitGrab(HANDLE & sharehand)
{
DWORD maxDisplayWidth = -1, maxDisplayHeight = -1;
NVFBC_TODX9VID_OUT_BUF NvFBC_OutBuf;
//加载NvFBCLibary
m_pNVFBCLib = new NvFBCLibrary();
if (!m_pNVFBCLib->load())
{
fprintf(stderr, "Unable to load the NvFBC library.\n");
return FALSE;
}
m_bNvFBCLibLoaded = true;
if (!InitD3D(0))
{
fprintf(stderr, "Unable to create a D3D9Ex Device.\n");
ReleaseGrab();
return FALSE;
}
if (!InitD3DSurfaces())
{
fprintf(stderr, "Unable to create a D3D9Ex Device.\n");
ReleaseGrab();
return FALSE;
}
m_pNvFBCDX9 = (NvFBCToDx9Vid *)m_pNVFBCLib->create(NVFBC_TO_DX9_VID, &maxDisplayWidth, &maxDisplayHeight, 0, (void *)m_pD3D9Device);
if (!m_pNvFBCDX9)
{
fprintf(stderr, "Failed to create an instance of NvFBCToDx9Vid.\n");
ReleaseGrab();
return FALSE;
}
NvFBC_OutBuf.pPrimary = m_pD3D9Surf;
NVFBC_TODX9VID_SETUP_PARAMS DX9SetupParams = { 0 };
DX9SetupParams.dwVersion = NVFBC_TODX9VID_SETUP_PARAMS_V2_VER;
DX9SetupParams.bWithHWCursor = 0;
DX9SetupParams.bStereoGrab = 0;
DX9SetupParams.bDiffMap = 0;
DX9SetupParams.ppBuffer = &NvFBC_OutBuf;
DX9SetupParams.eMode = NVFBC_TODX9VID_ARGB;
DX9SetupParams.dwNumBuffers = 1;
//设置抓屏信息
if (NVFBC_SUCCESS != m_pNvFBCDX9->NvFBCToDx9VidSetUp(&DX9SetupParams))
{
fprintf(stderr, "Failed when calling NvFBCDX9->NvFBCToDX9VidSetup()\n");
ReleaseGrab();
return FALSE;
}
//创建一个新的Texture用于D3D9传递数据给D3D11
D3DSURFACE_DESC _desc;
m_pD3D9Surf->GetDesc(&_desc);
HRESULT hr = m_pD3D9Device->CreateTexture(m_width, m_height, 1, D3DUSAGE_RENDERTARGET, _desc.Format, D3DPOOL_DEFAULT, &m_pGrabResult, &m_hGrabShare);
if (hr != D3D_OK)
return FALSE;
sharehand = m_hGrabShare;
return TRUE;
}
4、抓一帧数据
BOOL NVAPIGrab::GrabOneFarme()
{
NVFBC_TODX9VID_GRAB_FRAME_PARAMS fbcDX9GrabParams = { 0 };
NvFBCFrameGrabInfo frameGrabInfo = { 0 };
NVFBCRESULT fbcRes = NVFBC_SUCCESS;
fbcDX9GrabParams.dwVersion = NVFBC_TODX9VID_GRAB_FRAME_PARAMS_VER;
fbcDX9GrabParams.dwFlags = NVFBC_TODX9VID_NOWAIT;
fbcDX9GrabParams.eGMode = NVFBC_TODX9VID_SOURCEMODE_SCALE;
fbcDX9GrabParams.dwTargetWidth = m_width;
fbcDX9GrabParams.dwTargetHeight = m_height;
fbcDX9GrabParams.pNvFBCFrameGrabInfo = &frameGrabInfo;
fbcRes = m_pNvFBCDX9->NvFBCToDx9VidGrabFrame(&fbcDX9GrabParams);
if (fbcRes == NVFBC_SUCCESS)
{
//抓屏成功
//将OffscreenSurface的数据给m_pGrabResult
IDirect3DSurface9* pGrabSurface = NULL;
m_pGrabResult->GetSurfaceLevel(0, &pGrabSurface);
m_pD3D9Device->StretchRect(m_pD3D9Surf, NULL, pGrabSurface, NULL, D3DTEXF_NONE);
CString strIniFileB;
strIniFileB.Format(_T(".\\%d.bmp"), cont);
D3DXSaveTextureToFile(strIniFileB, D3DXIFF_BMP, m_pGrabResult, NULL);
cont++;
m_pD3D9Device->Present(0, 0, 0, 0);//提交一次避免数据没有刷新
return TRUE;
}
else
{
return FALSE;
}
return TRUE;
}
二、D3D11
1、初始化
BOOL MyD3D11::InitD3D(IDXGIAdapter* pAdapter, HANDLE TargetHand)
{
//创建D3D11
UINT uCreateDeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
D3D_FEATURE_LEVEL flvl[] = { D3D_FEATURE_LEVEL_11_0,D3D_FEATURE_LEVEL_11_0,D3D_FEATURE_LEVEL_10_0 };
HRESULT hr = ::D3D11CreateDevice(
pAdapter,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
uCreateDeviceFlags,
flvl,
sizeof(flvl) / sizeof(D3D_FEATURE_LEVEL),
D3D11_SDK_VERSION,
&m_pD3D11Device,
NULL,
&m_pD3D11Context
);
if (FAILED(hr))
{
OutputDebugString(L"Create Compute Device Filed");
return FALSE;
}
if (m_pImageTexture != NULL)
{
m_pImageTexture->Release();
m_pImageTexture = NULL;
}
//通过句柄绑定D3D9和D3D11的共享
ID3D11Resource* pResource = NULL;
if (TargetHand != NULL)
{
hr = m_pD3D11Device->OpenSharedResource(TargetHand, __uuidof(ID3D11Resource), (LPVOID*)&pResource);
if (pResource == NULL || FAILED(hr))
{
OutputDebugString(L"OpenSharedResource Filed");
return FALSE;
}
pResource->QueryInterface(__uuidof(ID3D11Texture2D), (void**)(&m_pImageTexture));
pResource->Release();
// m_pD3D11Context->Flush();
}
return TRUE;
}
2、处理图片
BOOL MyD3D11::DealSharedPicture()
{
CString strIniFileC;
strIniFileC.Format(_T(".\\Pre_%d.dds"), cont);
HRESULT hr = D3DX11SaveTextureToFile(m_pD3D11Context, m_pImageTexture, D3DX11_IFF_DDS, strIniFileC);
cont++;
return TRUE;
}
三、主函数
#include"NVAPIGrab.h"
#include"MyD3D11.h"
void main()
{
int cont = 0;
NVAPIGrab* Grab = new NVAPIGrab();
MyD3D11* D3D11 = new MyD3D11();
HANDLE ShareHandle;
Grab->InitGrab(ShareHandle);
D3D11->InitD3D(NULL, ShareHandle);
while (cont < 10)
{
cont++;
if (Grab->GrabOneFarme())
{
D3D11->DealSharedPicture();
}
else
{
break;
}
}
if (Grab != NULL)
{
Grab->ReleaseGrab();
Grab = NULL;
}
if (D3D11 != NULL)
{
D3D11->ReleaseD3D();
D3D11 = NULL;
}
return;
}
注意:
一、D3D9
1、D3D9抓屏必须用CreateOffscreenPlainSurface来创建离屏表面进行抓屏,但是OffcreenSurface句柄共享到D3D11后,保存Texture到本地会导致D3D设备停止工作。所以从新创建了一个Texture,通过其HANDLE来和D3D11进行数据交换。
2、抓到一帧数据后切记D3D9Device要调用Present(0, 0, 0, 0)来进行数据提交。否则第一帧D3D11拿到的数据为空。
二、D3D11
1、D3D11获取数据时,直接调用OpenSharedResource,即可将D3D9内存与D3D11的内存进行绑定。绑定后一单D3D9传入HANDLE所指向的内容发生变化,D3D11对应位置也会变化。所以改函数只需调用一次即可。后面直接用D3D11对应的Texture。
2、如果D3D9的HANDLE是OffcreenSurface的HANDLE数据也能进行交换,但是D3D11调用保存图片设备会停止工作。
源码路径