d3d9播放yuv
一、几个概念
后台缓冲surface,前台surface,交换链,离屏surface
后台缓冲surface和前台缓冲surface总是成对出现的,当我们进行绘图操作时,画面有可能出现闪烁,这是因为当前绘制的一幅图像没有同时出现在屏幕上导致的,更详细的可以看这篇文章。前台surface + 后台surface就可以解决这个问题,前台surface表示我们可以看到的画面,后台缓冲surface相当于一个缓冲区,每次绘图是在后台surface上进行的,然后完了再把两个surface做一个交换,这样后台surface就可以显示到屏幕上了。
交换链:就是将前台surface和后台surface做交换的
离屏surface:离屏surface是一个永远看不到的表面,通常用来存放位图,并对其中的一些数据做一些处理。
二、d3d9渲染yuv流程
1、创建D3D9:Direct3DCreate9
IDirect3D9 * WINAPI Direct3DCreate9(UINT SDKVersion);
SDKVersion设置为D3D_SDK_VERSION
IDirect3D9是一个显示3D图形的物理设备的C++对象。它可以用于获得物理设备的信息和创建一个IDirect3DDevice9接口
可以通过它的GetAdapterDisplayMode()函数获取当前主显卡输出的分辨率,刷新频率等参数
D3DDISPLAYMODE d3dDisplayMode;
d3d9->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3dDisplayMode );
2、创建D3D9Device:CreateDevice
HRESULT CreateDevice(
UINT Adapter,
D3DDEVTYPE DeviceType,
HWND hFocusWindow,
DWORD BehaviorFlags,
D3DPRESENT_PARAMETERS *pPresentationParameters,
IDirect3DDevice9 **ppReturnedDeviceInterface
);
参数介绍:
Adapter:表示显示适配器的序号。D3DADAPTER_DEFAULT始终是主要的显示适配器
DeviceType:D3DDEVTYPE枚举类型的成员,它表示所需的设备类型。如果所需的设备类型不可用,则该方法将失败
D3DDEVTYPE_HAL :硬件加速
D3DDEVTYPE_REF :
D3DDEVTYPE_SW :软件
D3DDEVTYPE_NULLREF :
D3DDEVTYPE_FORCE_DWORD :
hFocusWindow:Windows窗口句柄
BehaviorFlags:设置为软件顶点处理D3DCREATE_SOFTWARE_VERTEXPROCESSING,或者硬件顶点处理D3DCREATE_HARDWARE_VERTEXPROCESSING
pPresentationParameters:用于D3D9Device的一些参数
ppReturnedDeviceInterface:返回创建的device
下来我们看一下关于 D3DPRESENT_PARAMETERS 的设置
typedef struct _D3DPRESENT_PARAMETERS_
{
UINT BackBufferWidth;
UINT BackBufferHeight;
D3DFORMAT BackBufferFormat;
UINT BackBufferCount;
D3DMULTISAMPLE_TYPE MultiSampleType;
DWORD MultiSampleQuality;
D3DSWAPEFFECT SwapEffect;
HWND hDeviceWindow;
BOOL Windowed;
BOOL EnableAutoDepthStencil;
D3DFORMAT AutoDepthStencilFormat;
DWORD Flags;
/* FullScreen_RefreshRateInHz must be zero for Windowed mode */
UINT FullScreen_RefreshRateInHz;
UINT PresentationInterval;
} D3DPRESENT_PARAMETERS;
成员介绍:
BackBufferWidth:后台缓冲surface的宽
BackBufferHeight:后台缓冲surface的高
一般将其设置为frame width和frame height
BackBufferFormat:后台缓冲surface的像素格式
BackBufferCount:后台缓冲surface的个数
MultiSampleType:全屏抗锯齿的类型
MultiSampleQuality:全屏抗锯齿的质量等级
SwapEffect:指定表面在交换链中是如何被交换的
D3DSWAPEFFECT_DISCARD:后台缓冲表面区的东西被复制到屏幕上后,后台缓冲表面区的东西就没有什么用了,可以丢弃了。
D3DSWAPEFFECT_FLIP: 后台缓冲表面拷贝到前台表面,保持后台缓冲表面内容不变。当后台缓冲表面大于1个时使用。
D3DSWAPEFFECT_COPY: 同上。当后台缓冲表面等于1个时使用。
一般设置为D3DSWAPEFFECT_DISCARD
hDeviceWindow:窗口句柄
Windowed:设为true则为窗口模式,false则为全屏模式
EnableAutoDepthStencil:设为true,D3D将自动创建深度/模版缓冲
AutoDepthStencilFormat:深度/模版缓冲的格式
Flags:
FullScreen_RefreshRateInHz:刷新率,设定D3DPRESENT_RATE_DEFAULT使用默认刷新率
PresentationInterval:设置刷新的间隔,可以用以下方式:
D3DPRENSENT_INTERVAL_DEFAULT,则说明在显示一个渲染画面的时候必要等候显示器刷新完一次屏幕。例如显示器刷新率设为80Hz的话,则一秒最多可以显示80个渲染画面。
D3DPRENSENT_INTERVAL_IMMEDIATE:表示可以以实时的方式来显示渲染画面
3、创建D3D9Surface:CreateOffscreenPlainSurface创建一个离屏surface
HRESULT CreateOffscreenPlainSurface(
UINT Width, // 离屏surface的宽
UINT Height, // 离屏surface的高
D3DFORMAT Format, // 离屏surface的格式YV12
D3DPOOL Pool, // D3DPOOL定义了资源对应的内存类型
// D3D3POOL_DEFAULT: 默认值,表示存在于显卡的显存中。
// D3D3POOL_MANAGED:由Direct3D*调度内存的位置(显存或者缓存中)。
// D3DPOOL_SYSTEMMEM: 表示位于内存中。
IDirect3DSurface9 **ppSurface, // 要创建的surface
HANDLE *pSharedHandle
);
4、显示画面,显示画面的流程为:LockRect(获取离屏的surface) --> 拷贝数据到离屏的surface --> UnlockRect --> Clear --> BeginScene --> GetBackBuffer(获取后台缓冲区的buffer) --> StretchRect(将一个矩形区域的像素从设备内存的一个Surface转移到另一个Surface上) --> EndScene --> Present (显示)--> Release
三、具体代码
d3d9_player.h
#pragma once
#include <cstdint>
#include <d3d9.h>
enum PixFormat {
I420,
NV12,
NV21
};
class D3D9Player {
public:
D3D9Player();
~D3D9Player();
void Init(uint32_t width, uint32_t height, PixFormat pix_format);
void CreateRenderWindow();
void SetWindow(void* handle);
void Render(uint8_t* data);
private:
void InitRender();
void CreateSurface();
void DesrtoySurface();
void Cleanup();
private:
uint32_t width_{};
uint32_t height_{};
PixFormat pix_format_ = I420;
CRITICAL_SECTION critical_{};
IDirect3D9* d3d9_{};
IDirect3DDevice9* d3d9_device_{};
IDirect3DSurface9* d3d9_surface_{};
RECT view_rect_{};
HWND render_window_{};
};
d3d9_palyer.cpp
#include "d3d9_player.h"
#include <iostream>
#pragma comment(lib, "d3d9.lib")
D3D9Player::D3D9Player() {
}
D3D9Player::~D3D9Player() {
DesrtoySurface();
Cleanup();
}
void D3D9Player::Init(uint32_t width, uint32_t height, PixFormat pix_format) {
width_ = width;
height_ = height;
pix_format_ = pix_format;
view_rect_.left = 0;
view_rect_.top = 0;
view_rect_.right = width;
view_rect_.bottom = height;
}
void D3D9Player::CreateRenderWindow() {
}
void D3D9Player::SetWindow(void* handle) {
render_window_ = (HWND)(handle);
InitRender();
CreateSurface();
}
void D3D9Player::Render(uint8_t* data) {
D3DLOCKED_RECT d3d_rect;
if (d3d9_surface_ == nullptr) {
return;
}
auto hr = d3d9_surface_->LockRect(&d3d_rect, NULL, D3DLOCK_DONOTWAIT);
if (FAILED(hr)) {
std::cout << "lock rect error" << std::endl;
return;
}
int y_stride = d3d_rect.Pitch;
int v_stride = y_stride / 2;
int u_stride = y_stride / 2;
uint8_t* y_dest = (uint8_t*)d3d_rect.pBits;
uint8_t* v_dest = y_dest + height_ * y_stride;
uint8_t* u_dest = v_dest + height_ / 2 * v_stride;
for (uint32_t i = 0; i < height_; i++) {
memcpy(y_dest + i * y_stride, data + i * width_, width_);
}
for (uint32_t i = 0; i < height_ / 2; i++) {
memcpy(v_dest + i * v_stride, data + width_*height_ + i * width_ / 2,
width_ / 2);
}
for (uint32_t i = 0; i < height_ / 2; i++) {
memcpy(u_dest + i * u_stride, data + width_ * height_ * 5 / 4 + i * width_ / 2,
width_ / 2);
}
hr = d3d9_surface_->UnlockRect();
if (FAILED(hr)) {
std::cout << "unlock rect error" << std::endl;
return;
}
d3d9_device_->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0),
1.0f, 0);
d3d9_device_->BeginScene();
IDirect3DSurface9* d3d_surface = NULL;
d3d9_device_->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO,
&d3d_surface);
d3d9_device_->StretchRect(d3d9_surface_, NULL, d3d_surface, &view_rect_,
D3DTEXF_LINEAR);
d3d9_device_->EndScene();
d3d9_device_->Present(NULL, NULL, NULL, NULL);
d3d_surface->Release();
}
void D3D9Player::InitRender() {
std::cout << "init render" << std::endl;
InitializeCriticalSection(&critical_);
Cleanup();
d3d9_ = Direct3DCreate9(D3D_SDK_VERSION);
if (d3d9_ == nullptr) {
return;
}
RECT r;
GetClientRect(render_window_, &r);
int x = GetSystemMetrics(SM_CXSCREEN);
int y = GetSystemMetrics(SM_CYSCREEN);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.hDeviceWindow = (HWND)render_window_;
d3dpp.Flags = D3DPRESENTFLAG_VIDEO;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferWidth = width_;
d3dpp.BackBufferHeight = height_;
HRESULT hr = d3d9_->CreateDevice(
D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, render_window_,
D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3d9_device_);
}
void D3D9Player::Cleanup() {
EnterCriticalSection(&critical_);
if (d3d9_surface_) {
d3d9_surface_->Release();
d3d9_surface_ = nullptr;
}
if (d3d9_device_) {
d3d9_device_->Release();
d3d9_device_ = nullptr;
}
if (d3d9_) {
d3d9_->Release();
d3d9_ = nullptr;
}
LeaveCriticalSection(&critical_);
}
void D3D9Player::CreateSurface() {
if (d3d9_device_ == nullptr) {
return;
}
HRESULT hr = d3d9_device_->CreateOffscreenPlainSurface(
width_, height_, (D3DFORMAT)MAKEFOURCC('Y', 'V', '1', '2'),
D3DPOOL_DEFAULT, &d3d9_surface_, NULL);
if (FAILED(hr)) {
std::cout << "create plain surface error" << std::endl;
return;
}
std::cout << "create surface" << std::endl;
}
void D3D9Player::DesrtoySurface() {
EnterCriticalSection(&critical_);
if (d3d9_surface_) {
d3d9_surface_->Release();
d3d9_surface_ = nullptr;
}
LeaveCriticalSection(&critical_);
}
main.cpp
#include <Windows.h>
#include <iostream>
#include <fstream>
#include <thread>
#include <chrono>
#include "d3d9_player.h"
HWND CreateMyWindow(uint32_t width, uint32_t height) {
WNDCLASS window;
memset(&window, 0, sizeof(window));
window.lpfnWndProc = (WNDPROC)DefWindowProc;
window.hInstance = GetModuleHandle(NULL);
window.hCursor = LoadCursor(NULL, IDC_ARROW);
window.lpszClassName = "yuv_player";
if (!RegisterClass(&window)) {
return nullptr;
}
DWORD dwStyle = NULL;
HWND wnd = CreateWindowEx(NULL, window.lpszClassName, "YUV Player", dwStyle,
100, 100, width, height, nullptr, nullptr,
GetModuleHandle(NULL), nullptr);
ShowWindow(wnd, SW_SHOWDEFAULT);
UpdateWindow(wnd);
return wnd;
}
int main(int argc,char* argv[]) {
std::string filename = "480x272_yuv420p.yuv";
uint32_t width = 480;
uint32_t height = 272;
std::shared_ptr<D3D9Player> yuv_player = std::make_shared<D3D9Player>();
yuv_player->Init(width, height, I420);
// yuv_player->CreateRenderWindow();
HWND window = CreateMyWindow(width,height);
yuv_player->SetWindow(window);
uint8_t* data = new uint8_t[width * height * 3 / 2];
std::ifstream fin(filename, std::ios::in | std::ios::binary);
while (!fin.eof()) {
fin.read((char*)data, width * height * 3 / 2);
yuv_player->Render(data);
std::this_thread::sleep_for(std::chrono::milliseconds(33));
}
delete[] data;
fin.close();
getchar();
return 0;
}
四、参考资料
https://blog.csdn.net/leixiaohua1020/article/details/40279297
下一篇: 性能优化-service进程防杀