H264解码之D3D显示YUV

概述

备注:本文主要针对DirectX 9.0版本来讨论的。
在开始这篇文章之前,我们先阐述一下一些名词:DX、DDraw、DirectShow、D3D、DirectX、DirectDraw等。

关系

首先我们理一理他们之间的关系,这些关键词统称DirectX,简称DX,它有一下成员:

  • DirectX Graphics: 集成了以前的DirectDraw(简称DDraw) 和Direct3D(简称D3D)技术。
    • DirectDraw主要负责2D加速,以实现对显卡内存和系统内存的直接操作;
    • Direct3D主要提供三维绘图硬件接口,它是开发三维DirectX游戏的基础。
  • DirectInput: 主要支持输入服务(包括鼠标、键盘、游戏杆等),同时支持输出设备。
  • DirectPlay: 主要提供多人网络游戏的通信、组织功能。
  • DirectSetup: 主要提供自动安装DirectX组件的API功能。
  • DirectMusic: 主要支持MIDI音乐合成和播放功能。
  • DirectSound: 主要提供音频捕捉、回放、音效处理、硬件加速、直接设备访问等功能。
  • DirectShow: 为Windows平台上处理各种格式的媒体文件的回放、音视频采集等高性能要求的多媒体应用,提供了完整的解决方案。
  • DirectX Media Objects: DirectShow Filter 的简化模型,提供更方便的流数据处理方案。

参考

链接DirectX和DirectShow介绍和区别
链接DirectShow和DirectX有什么区别

接口介绍

显示方式

D3D显示YUV方式有两种:纹理(Texture)方式和表面(Surface)方式。而Texture方式又可以包含使用Shader及不使用Shader方式。

纹理方式

使用shader的纹理方式

这部分代码参考的前辈的项目,现在把连接发出来:D3D三层Texture纹理经像素着色器实现渲染YUV420P 第二版
直接上代码:头文件d3dUtility.h

//////////////////////////////////////////////////////////////////////////////////////////////////
// 
// File: d3dUtility.h
// 
// Author: Frank Luna (C) All Rights Reserved
//
// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0 
//
// Desc: Provides utility functions for simplifying common tasks.
//          
//////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef __d3dUtilityH__
#define __d3dUtilityH__

#include <d3dx9.h>
#include <string>
#include <limits>
#include <Windows.h>

namespace d3d
{
	//
	// Init
	//
	bool InitD3D(
		HINSTANCE hInstance,       // [in] Application instance.
		int width, int height,     // [in] Backbuffer dimensions.
		bool windowed,             // [in] Windowed (true)or full screen (false).
		D3DDEVTYPE deviceType,     // [in] HAL or REF
		IDirect3DDevice9** device);// [out]The created device.

	int EnterMsgLoop( 
		bool (*ptr_display)(float timeDelta));

	LRESULT CALLBACK WndProc(
		HWND hwnd,
		UINT msg, 
		WPARAM wParam,
		LPARAM lParam);

	//
	// Cleanup
	//
	template<class T> void Release(T t)
	{
		if( t )
		{
			t->Release();
			t = 0;
		}
	}
		
	template<class T> void Delete(T t)
	{
		if( t )
		{
			delete t;
			t = 0;
		}
	}

	//
	// Colors
	//
	const D3DXCOLOR      WHITE( D3DCOLOR_XRGB(255, 255, 255) );
	const D3DXCOLOR      BLACK( D3DCOLOR_XRGB(  0,   0,   0) );
	const D3DXCOLOR        RED( D3DCOLOR_XRGB(255,   0,   0) );
	const D3DXCOLOR      GREEN( D3DCOLOR_XRGB(  0, 255,   0) );
	const D3DXCOLOR       BLUE( D3DCOLOR_XRGB(  0,   0, 255) );
	const D3DXCOLOR     YELLOW( D3DCOLOR_XRGB(255, 255,   0) );
	const D3DXCOLOR       CYAN( D3DCOLOR_XRGB(  0, 255, 255) );
	const D3DXCOLOR    MAGENTA( D3DCOLOR_XRGB(255,   0, 255) );

	//
	// Lights
	//

	D3DLIGHT9 InitDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color);
	D3DLIGHT9 InitPointLight(D3DXVECTOR3* position, D3DXCOLOR* color);
	D3DLIGHT9 InitSpotLight(D3DXVECTOR3* position, D3DXVECTOR3* direction, D3DXCOLOR* color);

	//
	// Materials
	//

	D3DMATERIAL9 InitMtrl(D3DXCOLOR a, D3DXCOLOR d, D3DXCOLOR s, D3DXCOLOR e, float p);

	const D3DMATERIAL9 WHITE_MTRL  = InitMtrl(WHITE, WHITE, WHITE, BLACK, 2.0f);
	const D3DMATERIAL9 RED_MTRL    = InitMtrl(RED, RED, RED, BLACK, 2.0f);
	const D3DMATERIAL9 GREEN_MTRL  = InitMtrl(GREEN, GREEN, GREEN, BLACK, 2.0f);
	const D3DMATERIAL9 BLUE_MTRL   = InitMtrl(BLUE, BLUE, BLUE, BLACK, 2.0f);
	const D3DMATERIAL9 YELLOW_MTRL = InitMtrl(YELLOW, YELLOW, YELLOW, BLACK, 2.0f);

	//
	// Bounding Objects
	//

	struct BoundingBox
	{
		BoundingBox();

		bool isPointInside(D3DXVECTOR3& p);

		D3DXVECTOR3 _min;
		D3DXVECTOR3 _max;
	};

	struct BoundingSphere
	{
		BoundingSphere();

		D3DXVECTOR3 _center;
		float       _radius;
	};

	//
	// Constants
	//

	const float INFINITY0 = FLT_MAX;
	const float EPSILON  = 0.001f;

	//
	// Drawing
	//

	// Function references "desert.bmp" internally.  This file must
	// be in the working directory.
	bool DrawBasicScene(
		IDirect3DDevice9* device,// Pass in 0 for cleanup.
		float scale);            // uniform scale 

	//
	// Vertex Structures
	//

	struct Vertex
	{
		Vertex(){}
		Vertex(float x, float y, float z, 
			float nx, float ny, float nz,
			float u, float v)
		{
			_x  = x;  _y  = y;  _z  = z;
			_nx = nx; _ny = ny; _nz = nz;
			_u  = u;  _v  = v;
		}
		float _x, _y, _z;
		float _nx, _ny, _nz;
		float _u, _v;

		static const DWORD FVF;
	};

	//
	// Randomness
	//

	// Desc: Return random float in [lowBound, highBound] interval.
	float GetRandomFloat(float lowBound, float highBound);
	

	// Desc: Returns a random vector in the bounds specified by min and max.
	void GetRandomVector(
		D3DXVECTOR3* out,
		D3DXVECTOR3* min,
		D3DXVECTOR3* max);

	//
	// Conversion
	//
	DWORD FtoDw(float f);
}

#endif // __d3dUtilityH__

cpp文件:d3dUtility.cpp

//////////////////////////////////////////////////////////////////////////////////////////////////
// 
// File: d3dUtility.cpp
// 
// Author: Frank Luna (C) All Rights Reserved
//
// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0 
//
// Desc: Provides utility functions for simplifying common tasks.
//          
//////////////////////////////////////////////////////////////////////////////////////////////////

#include "d3dUtility.h"

// vertex formats
const DWORD d3d::Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;


bool d3d::InitD3D(
	HINSTANCE hInstance,
	int width, int height,
	bool windowed,
	D3DDEVTYPE deviceType,
	IDirect3DDevice9** device)
{
	//
	// Create the main application window.
	//

	WNDCLASS wc;

	wc.style         = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc   = (WNDPROC)d3d::WndProc; 
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hInstance;
	wc.hIcon         = LoadIcon(0, IDI_APPLICATION);
	wc.hCursor       = LoadCursor(0, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
	wc.lpszMenuName  = 0;
	wc.lpszClassName = "Direct3D9App";

	if( !RegisterClass(&wc) ) 
	{
		::MessageBox(0, "RegisterClass() - FAILED", 0, 0);
		return false;
	}
		
	HWND hwnd = 0;
	hwnd = ::CreateWindow("Direct3D9App", "Direct3D9App", 
		WS_EX_TOPMOST,
		0, 0, 1024, 768,
		0 /*parent hwnd*/, 0 /* menu */, hInstance, 0 /*extra*/); 

	if( !hwnd )
	{
		::MessageBox(0, "CreateWindow() - FAILED", 0, 0);
		return false;
	}

	::ShowWindow(hwnd, SW_SHOW);
	::UpdateWindow(hwnd);

	//
	// Init D3D: 
	//

	HRESULT hr = 0;

	// Step 1: Create the IDirect3D9 object.

	IDirect3D9* d3d9 = 0;
    d3d9 = Direct3DCreate9(D3D_SDK_VERSION);

    if( !d3d9 )
	{
		::MessageBox(0, "Direct3DCreate9() - FAILED", 0, 0);
		return false;
	}

	// Step 2: Check for hardware vp.

	D3DCAPS9 caps;
	d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, deviceType, &caps);

	int vp = 0;
	if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
		vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
	else
		vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;

	// Step 3: Fill out the D3DPRESENT_PARAMETERS structure.

	D3DDISPLAYMODE        d3ddm;
	UINT adapter = D3DADAPTER_DEFAULT;

	IDirect3D9_GetAdapterDisplayMode(d3d9, adapter, &d3ddm);

	// 默认不使用多采样
	D3DMULTISAMPLE_TYPE multiType = D3DMULTISAMPLE_NONE;
	if(d3d9->CheckDeviceMultiSampleType(D3DADAPTER_DEFAULT,
		D3DDEVTYPE_HAL, D3DFMT_A8R8G8B8, !windowed,
		D3DMULTISAMPLE_4_SAMPLES,
		NULL) == D3D_OK)
	{
		// 保存多采样类型
		multiType = D3DMULTISAMPLE_4_SAMPLES;
	}
 
	D3DPRESENT_PARAMETERS d3dpp;
	d3dpp.BackBufferWidth            = width;
	d3dpp.BackBufferHeight           = height;
	d3dpp.BackBufferFormat           = D3DFMT_A8R8G8B8;
	d3dpp.BackBufferCount            = 1;
	d3dpp.MultiSampleType            = multiType;
	d3dpp.MultiSampleQuality         = 0;
	d3dpp.SwapEffect                 = D3DSWAPEFFECT_DISCARD; 
	d3dpp.hDeviceWindow              = hwnd;
	d3dpp.Windowed                   = windowed;
	d3dpp.EnableAutoDepthStencil     = true; 
	d3dpp.AutoDepthStencilFormat     = D3DFMT_D24S8;
	d3dpp.Flags                      = 0;
	d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
	d3dpp.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;

	// Step 4: Create the device.

	hr = d3d9->CreateDevice(
		D3DADAPTER_DEFAULT, // primary adapter
		deviceType,         // device type
		hwnd,               // window associated with device
		vp,                 // vertex processing
	    &d3dpp,             // present parameters
	    device);            // return created device

	if( FAILED(hr) )
	{
		// try again using a 16-bit depth buffer
		d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
		
		hr = d3d9->CreateDevice(
			D3DADAPTER_DEFAULT,
			deviceType,
			hwnd,
			vp,
			&d3dpp,
			device);

		if( FAILED(hr) )
		{
			d3d9->Release(); // done with d3d9 object
			::MessageBox(0, "CreateDevice() - FAILED", 0, 0);
			return false;
		}
	}

	d3d9->Release(); // done with d3d9 object
	
	return true;
}

int d3d::EnterMsgLoop( bool (*ptr_display)(float timeDelta) )
{
	MSG msg;
	::ZeroMemory(&msg, sizeof(MSG));

	//static float lastTime = (float)timeGetTime(); 
	DWORD lastTime = GetTickCount();

	while(msg.message != WM_QUIT)
	{
		if(::PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			::TranslateMessage(&msg);
			::DispatchMessage(&msg);
		}
		else
        {	
			//float currTime  = (float)timeGetTime();
			DWORD currTime = GetTickCount();
			float timeDelta = (currTime - lastTime)*0.001f;

			ptr_display(timeDelta);

			lastTime = currTime;
        }
    }
    return msg.wParam;
}

D3DLIGHT9 d3d::InitDirectionalLight(D3DXVECTOR3* direction, D3DXCOLOR* color)
{
	D3DLIGHT9 light;
	::ZeroMemory(&light, sizeof(light));

	light.Type      = D3DLIGHT_DIRECTIONAL;
	light.Ambient   = *color * 0.4f;
	light.Diffuse   = *color;
	light.Specular  = *color * 0.6f;
	light.Direction = *direction;

	return light;
}

D3DLIGHT9 d3d::InitPointLight(D3DXVECTOR3* position, D3DXCOLOR* color)
{
	D3DLIGHT9 light;
	::ZeroMemory(&light, sizeof(light));

	light.Type      = D3DLIGHT_POINT;
	light.Ambient   = *color * 0.4f;
	light.Diffuse   = *color;
	light.Specular  = *color * 0.6f;
	light.Position  = *position;
	light.Range        = 1000.0f;
	light.Falloff      = 1.0f;
	light.Attenuation0 = 1.0f;
	light.Attenuation1 = 0.0f;
	light.Attenuation2 = 0.0f;

	return light;
}

D3DLIGHT9 d3d::InitSpotLight(D3DXVECTOR3* position, D3DXVECTOR3* direction, D3DXCOLOR* color)
{
	D3DLIGHT9 light;
	::ZeroMemory(&light, sizeof(light));

	light.Type      = D3DLIGHT_SPOT;
	light.Ambient   = *color * 0.4f;
	light.Diffuse   = *color;
	light.Specular  = *color * 0.6f;
	light.Position  = *position;
	light.Direction = *direction;
	light.Range        = 1000.0f;
	light.Falloff      = 1.0f;
	light.Attenuation0 = 1.0f;
	light.Attenuation1 = 0.0f;
	light.Attenuation2 = 0.0f;
	light.Theta        = 0.5f;
	light.Phi          = 0.7f;

	return light;
}

D3DMATERIAL9 d3d::InitMtrl(D3DXCOLOR a, D3DXCOLOR d, D3DXCOLOR s, D3DXCOLOR e, float p)
{
	D3DMATERIAL9 mtrl;
	mtrl.Ambient  = a;
	mtrl.Diffuse  = d;
	mtrl.Specular = s;
	mtrl.Emissive = e;
	mtrl.Power    = p;
	return mtrl;
}

d3d::BoundingBox::BoundingBox()
{
	// infinite small 
	_min.x = d3d::INFINITY0;
	_min.y = d3d::INFINITY0;
	_min.z = d3d::INFINITY0;

	_max.x = -d3d::INFINITY0;
	_max.y = -d3d::INFINITY0;
	_max.z = -d3d::INFINITY0;
}

bool d3d::BoundingBox::isPointInside(D3DXVECTOR3& p)
{
	if( p.x >= _min.x && p.y >= _min.y && p.z >= _min.z &&
		p.x <= _max.x && p.y <= _max.y && p.z <= _max.z )
	{
		return true;
	}
	else
	{
		return false;
	}
}

d3d::BoundingSphere::BoundingSphere()
{
	_radius = 0.0f;
}

bool d3d::DrawBasicScene(IDirect3DDevice9* device, float scale)
{
	static IDirect3DVertexBuffer9* floor  = 0;
	static IDirect3DTexture9*      tex    = 0;
	static ID3DXMesh*              pillar = 0;

	HRESULT hr = 0;

	if( device == 0 )
	{
		if( floor && tex && pillar )
		{
			// they already exist, destroy them
			d3d::Release<IDirect3DVertexBuffer9*>(floor);
			d3d::Release<IDirect3DTexture9*>(tex);
			d3d::Release<ID3DXMesh*>(pillar);
		}
	}
	else if( !floor && !tex && !pillar )
	{
		// they don't exist, create them
		device->CreateVertexBuffer(
			6 * sizeof(d3d::Vertex),
			0, 
			d3d::Vertex::FVF,
			D3DPOOL_MANAGED,
			&floor,
			0);

		Vertex* v = 0;
		floor->Lock(0, 0, (void**)&v, 0);

		v[0] = Vertex(-20.0f, -2.5f, -20.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
		v[1] = Vertex(-20.0f, -2.5f,  20.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
		v[2] = Vertex( 20.0f, -2.5f,  20.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);

		v[3] = Vertex(-20.0f, -2.5f, -20.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f);
		v[4] = Vertex( 20.0f, -2.5f,  20.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
		v[5] = Vertex( 20.0f, -2.5f, -20.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f);

		floor->Unlock();

		D3DXCreateCylinder(device, 0.5f, 0.5f, 5.0f, 20, 20, &pillar, 0);

		D3DXCreateTextureFromFile(
			device,
			"desert.bmp",
			&tex);
	}
	else
	{
		//
		// Pre-Render Setup
		//
		device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
		device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
		device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);

		D3DXVECTOR3 dir(0.707f, -0.707f, 0.707f);
		D3DXCOLOR col(1.0f, 1.0f, 1.0f, 1.0f);
		D3DLIGHT9 light = d3d::InitDirectionalLight(&dir, &col);

		device->SetLight(0, &light);
		device->LightEnable(0, true);
		device->SetRenderState(D3DRS_NORMALIZENORMALS, true);
		device->SetRenderState(D3DRS_SPECULARENABLE, true);

		//
		// Render
		//

		D3DXMATRIX T, R, P, S;

		D3DXMatrixScaling(&S, scale, scale, scale);

		// used to rotate cylinders to be parallel with world's y-axis
		D3DXMatrixRotationX(&R, -D3DX_PI * 0.5f);

		// draw floor
		D3DXMatrixIdentity(&T);
		T = T * S;
		device->SetTransform(D3DTS_WORLD, &T);
		device->SetMaterial(&d3d::WHITE_MTRL);
		device->SetTexture(0, tex);
		device->SetStreamSource(0, floor, 0, sizeof(Vertex));
		device->SetFVF(Vertex::FVF);
		device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
		
		// draw pillars
		device->SetMaterial(&d3d::BLUE_MTRL);
		device->SetTexture(0, 0);
		for(int i = 0; i < 5; i++)
		{
			D3DXMatrixTranslation(&T, -5.0f, 0.0f, -15.0f + (i * 7.5f));
			P = R * T * S;
			device->SetTransform(D3DTS_WORLD, &P);
			pillar->DrawSubset(0);

			D3DXMatrixTranslation(&T, 5.0f, 0.0f, -15.0f + (i * 7.5f));
			P = R * T * S;
			device->SetTransform(D3DTS_WORLD, &P);
			pillar->DrawSubset(0);
		}
	}
	return true;
}

float d3d::GetRandomFloat(float lowBound, float highBound)
{
	if( lowBound >= highBound ) // bad input
		return lowBound;

	// get random float in [0, 1] interval
	float f = (rand() % 10000) * 0.0001f; 

	// return float in [lowBound, highBound] interval. 
	return (f * (highBound - lowBound)) + lowBound; 
}

void d3d::GetRandomVector(
	  D3DXVECTOR3* out,
	  D3DXVECTOR3* min,
	  D3DXVECTOR3* max)
{
	out->x = GetRandomFloat(min->x, max->x);
	out->y = GetRandomFloat(min->y, max->y);
	out->z = GetRandomFloat(min->z, max->z);
}

DWORD d3d::FtoDw(float f)
{
	return *((DWORD*)&f);
}

调用:

//////////////////////////////////////////////////////////////////////////////////////////////////
// 
// File: ps_multitex.cpp
// 
// Author: Frank Luna (C) All Rights Reserved
//
// System: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP, MSVC++ 7.0 
//
// Desc: Deomstrates multi-texturing using a pixel shader.  You will have
//       to switch to the REF device to run this sample if your hardware
//       doesn't support pixel shaders.
//          
//////////////////////////////////////////////////////////////////////////////////////////////////

#include "d3dUtility.h"

//
// Globals
//

IDirect3DDevice9* Device = 0; 

const int Width  = 320;
const int Height = 180;

IDirect3DPixelShader9* MultiTexPS = 0;
ID3DXConstantTable* MultiTexCT    = 0;

IDirect3DVertexBuffer9* QuadVB = 0;

IDirect3DTexture9* YTex      = 0;
IDirect3DTexture9* UTex = 0;
IDirect3DTexture9* VTex    = 0;

D3DXHANDLE YTexHandle      = 0;
D3DXHANDLE UTexHandle = 0;
D3DXHANDLE VTexHandle    = 0;

D3DXCONSTANT_DESC YTexDesc;
D3DXCONSTANT_DESC UTexDesc;
D3DXCONSTANT_DESC VTexDesc;


//YUV file
FILE *infile = NULL;
unsigned char buf[Width*Height*3/2];
unsigned char *plane[3];
// 
// Structs
//

struct MultiTexVertex
{
	MultiTexVertex(float x, float y, float z,
		float u0, float v0,
		float u1, float v1,
		float u2, float v2)
	{
		 _x =  x;  _y =  y; _z = z;
		_u0 = u0; _v0 = v0; 
		_u1 = u1; _v1 = v1;
		_u2 = u2, _v2 = v2;
	}

	float _x, _y, _z;
	float _u0, _v0;
	float _u1, _v1;
	float _u2, _v2;

	static const DWORD FVF;
};
const DWORD MultiTexVertex::FVF = D3DFVF_XYZ | D3DFVF_TEX3; 

//
// Framework functions
//
bool Setup()
{
	HRESULT hr = 0;

	//
	// Create geometry.
	//

	Device->CreateVertexBuffer(
		6 * sizeof(MultiTexVertex), 
		D3DUSAGE_WRITEONLY,
		MultiTexVertex::FVF,
		D3DPOOL_MANAGED,
		&QuadVB,
		0);

	MultiTexVertex* v = 0;
	QuadVB->Lock(0, 0, (void**)&v, 0);

	v[0] = MultiTexVertex(-10.0f, -10.0f, 5.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
	v[1] = MultiTexVertex(-10.0f,  10.0f, 5.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
	v[2] = MultiTexVertex( 10.0f,  10.0f, 5.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);

	v[3] = MultiTexVertex(-10.0f, -10.0f, 5.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
	v[4] = MultiTexVertex( 10.0f,  10.0f, 5.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f);
	v[5] = MultiTexVertex( 10.0f, -10.0f, 5.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f);

	QuadVB->Unlock();

	//
	// Compile shader
	//

	ID3DXBuffer* shader      = 0;
	ID3DXBuffer* errorBuffer = 0;

	hr = D3DXCompileShaderFromFile(
		"ps_multitex.txt",
		0,
		0,
		"Main", // entry point function name
		"ps_2_0",
		D3DXSHADER_DEBUG | D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY,
		&shader,
		&errorBuffer,
		&MultiTexCT);

	// output any error messages
	if( errorBuffer )
	{
		::MessageBox(0, (char*)errorBuffer->GetBufferPointer(), 0, 0);
		d3d::Release<ID3DXBuffer*>(errorBuffer);
	}

	if(FAILED(hr))
	{
		::MessageBox(0, "D3DXCompileShaderFromFile() - FAILED", 0, 0);
		return false;
	}

	//
	// Create Pixel Shader
	//
	hr = Device->CreatePixelShader(
		(DWORD*)shader->GetBufferPointer(),
		&MultiTexPS);

	if(FAILED(hr))
	{
		::MessageBox(0, "CreateVertexShader - FAILED", 0, 0);
		return false;
	}

	d3d::Release<ID3DXBuffer*>(shader);

	//
	// Create textures.
	//

	Device->CreateTexture ( Width, Height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &YTex, NULL ) ;
	Device->CreateTexture ( Width / 2, Height / 2, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &UTex, NULL ) ;
	Device->CreateTexture ( Width / 2, Height / 2, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &VTex, NULL ) ;

	if((infile=fopen("test_yuv420p_320x180.yuv", "rb"))==NULL){
		printf("cannot open this file\n");
		return false;
	}

	//
	// Set Projection Matrix
	//

	D3DXMATRIX P;
	D3DXMatrixPerspectiveFovLH(
			&P,	D3DX_PI * 0.25f, 
			(float)Width / (float)Height, 1.0f, 1000.0f);

	Device->SetTransform(D3DTS_PROJECTION, &P);

	//
	// Disable lighting.
	//

	Device->SetRenderState(D3DRS_LIGHTING, false);

	// 
	// Get Handles
	//

	YTexHandle      = MultiTexCT->GetConstantByName(0, "YTex");
	UTexHandle		= MultiTexCT->GetConstantByName(0, "UTex");
	VTexHandle		= MultiTexCT->GetConstantByName(0, "VTex");

	//
	// Set constant descriptions:
	//

	UINT count;
	
	MultiTexCT->GetConstantDesc(YTexHandle,      &YTexDesc, &count);
	MultiTexCT->GetConstantDesc(UTexHandle, &UTexDesc, &count);
	MultiTexCT->GetConstantDesc(VTexHandle,    &VTexDesc, &count);

	MultiTexCT->SetDefaults(Device);

	return true;
}

void Cleanup()
{
	d3d::Release<IDirect3DVertexBuffer9*>(QuadVB);

	d3d::Release<IDirect3DTexture9*>(YTex);
	d3d::Release<IDirect3DTexture9*>(UTex);
	d3d::Release<IDirect3DTexture9*>(VTex);

	d3d::Release<IDirect3DPixelShader9*>(MultiTexPS);
	d3d::Release<ID3DXConstantTable*>(MultiTexCT);
}

bool Display(float timeDelta)
{
	if (fread(buf, 1, Width*Height*3/2, infile) != Width*Height*3/2){
		// Loop
		fseek(infile, 0, SEEK_SET);
		fread(buf, 1, Width*Height*3/2, infile);
	}

	if( buf != NULL && Device )
	{
		// 
		// Update the scene: Allow user to rotate around scene.
		//
		
		static float angle  = (3.0f * D3DX_PI) / 2.0f;
		static float radius = 20.0f;
		
		if( ::GetAsyncKeyState(VK_LEFT) & 0x8000f )
			angle -= 0.5f * timeDelta;

		if( ::GetAsyncKeyState(VK_RIGHT) & 0x8000f )
			angle += 0.5f * timeDelta;

		if( ::GetAsyncKeyState(VK_UP) & 0x8000f )
			radius -= 2.0f * timeDelta;

		if( ::GetAsyncKeyState(VK_DOWN) & 0x8000f )
			radius += 2.0f * timeDelta;

		D3DXVECTOR3 position( cosf(angle) * radius, 0.0f, sinf(angle) * radius );
		D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
		D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
		D3DXMATRIX V;
		D3DXMatrixLookAtLH(&V, &position, &target, &up);

		Device->SetTransform(D3DTS_VIEW, &V);
		
		//
		// Render
		//

		Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0);

		plane[0] = buf;
		plane[1] = plane[0] + Width*Height;
		plane[2] = plane[1] + Width*Height/4;

		D3DLOCKED_RECT d3d_rect;
		byte *pSrc = buf;
		//Locks a rectangle on a texture resource.
		//And then we can manipulate pixel data in it.
		LRESULT lRet = YTex->LockRect(0, &d3d_rect, 0, 0);
		if (FAILED(lRet)){
			return false;
		}
		// Copy pixel data to texture
		byte *pDest = (byte *)d3d_rect.pBits;
		int stride = d3d_rect.Pitch; 
		for(int i = 0;i < Height;i ++){
			memcpy(pDest + i * stride,plane[0] + i * Width, Width);
		}

		YTex->UnlockRect(0);

		D3DLOCKED_RECT d3d_rect1;
		lRet = UTex->LockRect(0, &d3d_rect1, 0, 0);
		if (FAILED(lRet)){
			return false;
		}
		// Copy pixel data to texture
		byte *pDest1 = (byte *)d3d_rect1.pBits;
		int stride1 = d3d_rect1.Pitch; 
		for(int i = 0;i < Height/2;i ++){
			memcpy(pDest1 + i * stride1,plane[1] + i * Width / 2, Width / 2);
		}

		UTex->UnlockRect(0);

		D3DLOCKED_RECT d3d_rect2;
		lRet =  VTex->LockRect(0, &d3d_rect2, 0, 0);
		if (FAILED(lRet)){
			return false;
		}
		// Copy pixel data to texture
		byte *pDest2 = (byte *)d3d_rect2.pBits;
		int stride2 = d3d_rect2.Pitch; 
		for(int i = 0;i < Height/2;i ++){
			memcpy(pDest2 + i * stride2,plane[2] + i * Width / 2, Width / 2);
		}

		VTex->UnlockRect(0);


		Device->BeginScene();

		Device->SetPixelShader(MultiTexPS);
		Device->SetFVF(MultiTexVertex::FVF);
		Device->SetStreamSource(0, QuadVB, 0, sizeof(MultiTexVertex));

		// Y tex
		Device->SetTexture(     YTexDesc.RegisterIndex, YTex);
		Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
		Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
		Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
		Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
		Device->SetSamplerState(YTexDesc.RegisterIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);

		// U tex
		Device->SetTexture(     UTexDesc.RegisterIndex, UTex);
		Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
		Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
		Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
		Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
		Device->SetSamplerState(UTexDesc.RegisterIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);

		// string tex
		Device->SetTexture(     VTexDesc.RegisterIndex, VTex);
		Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
		Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
		Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
		Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
		Device->SetSamplerState(VTexDesc.RegisterIndex, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);

		Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);
		
		Device->EndScene();
		Device->Present(0, 0, 0, 0);
	}
	return true;
}

//
// WndProc
//
LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch( msg )
	{
	case WM_DESTROY:
		::PostQuitMessage(0);
		break;
		
	case WM_KEYDOWN:
		if( wParam == VK_ESCAPE )
			::DestroyWindow(hwnd);

		break;
	}
	return ::DefWindowProc(hwnd, msg, wParam, lParam);
}

//
// WinMain
//
int WINAPI WinMain(HINSTANCE hinstance,
				   HINSTANCE prevInstance, 
				   PSTR cmdLine,
				   int showCmd)
{
	if(!d3d::InitD3D(hinstance,
		Width, Height, true, D3DDEVTYPE_HAL, &Device))
	{
		::MessageBox(0, "InitD3D() - FAILED", 0, 0);
		return 0;
	}
		
	if(!Setup())
	{
		::MessageBox(0, "Setup() - FAILED", 0, 0);
		return 0;
	}
	d3d::EnterMsgLoop( Display );
	Cleanup();
	Device->Release();
	return 0;
}

这里我提示一下:在运行项目时,D3DXCompileShaderFromFile会返回失败,甚至会崩溃报错,这里大家注意一下它的第一个参数ps_multitex.txt的目录,你可以先把它写成绝对目录试试。
这里还有一个问题是:显示没有拉伸,我没找到拉伸的方式。

不使用shader的纹理方式

这里代码参考雷神的代码:
最简单的视音频播放示例4:Direct3D播放RGB(通过Texture)

/**
 * 最简单的Direct3D播放视频的例子(Direct3D播放RGB)[Texture]
 * Simplest Video Play Direct3D (Direct3D play RGB)[Texture]
 *
 * 雷霄骅 Lei Xiaohua
 * leixiaohua1020@126.com
 * 中国传媒大学/数字电视技术
 * Communication University of China / Digital TV Technology
 * http://blog.csdn.net/leixiaohua1020
 *
 * 本程序使用Direct3D播放RGB/YUV视频像素数据。使用D3D中的Texture渲染数据。
 * 相对于使用Surface渲染视频数据来说,使用Texture渲染视频数据功能更加灵活,
 * 但是学习起来也会相对复杂一些。
 *
 * 函数调用步骤如下:
 *
 * [初始化]
 * Direct3DCreate9():获得IDirect3D9
 * IDirect3D9->CreateDevice():通过IDirect3D9创建Device(设备)
 * IDirect3DDevice9->CreateTexture():通过Device创建一个Texture(纹理)。
 * IDirect3DDevice9->CreateVertexBuffer():通过Device创建一个VertexBuffer(顶点缓存)。
 * IDirect3DVertexBuffer9->Lock():锁定顶点缓存。
 * memcpy():填充顶点缓存。
 * IDirect3DVertexBuffer9->Unlock():解锁顶点缓存。
 *
 * [循环渲染数据]
 * IDirect3DTexture9->LockRect():锁定纹理。
 * memcpy():填充纹理数据
 * IDirect3DTexture9->UnLockRect():解锁纹理。
 * IDirect3DDevice9->BeginScene():开始绘制。
 * IDirect3DDevice9->SetTexture():设置当前要渲染的纹理。
 * IDirect3DDevice9->SetStreamSource():绑定VertexBuffer。
 * IDirect3DDevice9->SetFVF():设置Vertex格式。
 * IDirect3DDevice9->DrawPrimitive():渲染。
 * IDirect3DDevice9->EndScene():结束绘制。
 * IDirect3DDevice9->Present():显示出来。
 *
 * This software plays RGB/YUV raw video data using Direct3D.
 * It uses Texture in D3D to render the pixel data.
 * Compared to another method (use Surface), it's more flexible
 * but a little difficult.
 *
 * The process is shown as follows:
 *
 * [Init]
 * Direct3DCreate9():Get IDirect3D9.
 * IDirect3D9->CreateDevice():Create a Device.
 * IDirect3DDevice9->CreateTexture():Create a Texture.
 * IDirect3DDevice9->CreateVertexBuffer():Create a VertexBuffer.
 * IDirect3DVertexBuffer9->Lock():Lock VertexBuffer.
 * memcpy():Fill VertexBuffer.
 * IDirect3DVertexBuffer9->Unlock():UnLock VertexBuffer.
 *
 * [Loop to Render data]
 * IDirect3DTexture9->LockRect():Lock Texture.
 * memcpy():Fill pixel data...
 * IDirect3DTexture9->UnLockRect():UnLock Texture.
 * IDirect3DDevice9->BeginScene():Begin to draw.
 * IDirect3DDevice9->SetTexture():Set current Texture.
 * IDirect3DDevice9->SetStreamSource():Bind VertexBuffer.
 * IDirect3DDevice9->SetFVF():Set Vertex Format.
 * IDirect3DDevice9->DrawPrimitive():Render.
 * IDirect3DDevice9->EndScene():End drawing.
 * IDirect3DDevice9->Present():Show on the screen.
 */

#include <stdio.h>
#include <tchar.h>
#include <d3d9.h>

//Flexible Vertex Format, FVF
typedef struct
{
	FLOAT       x,y,z;      // vertex untransformed position
	FLOAT       rhw;        // eye distance
	D3DCOLOR    diffuse;    // diffuse color
	FLOAT       tu, tv;     // texture relative coordinates
} CUSTOMVERTEX;

CRITICAL_SECTION  m_critial;
HWND     m_hVideoWnd;  // 视频窗口

IDirect3D9 *m_pDirect3D9= NULL;
IDirect3DDevice9 *m_pDirect3DDevice= NULL;
IDirect3DTexture9 *m_pDirect3DTexture= NULL;
IDirect3DVertexBuffer9 *m_pDirect3DVertexBuffer= NULL;

// Custom flexible vertex format (FVF), which describes custom vertex structure
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)


//Select one of the Texture mode (Set '1'):
#define TEXTURE_DEFAULT 1
//Rotate the texture
#define TEXTURE_ROTATE  0
//Show half of the Texture
#define TEXTURE_HALF    0

//Width, Height
const int screen_w=500,screen_h=500;
const int pixel_w=320,pixel_h=180;
FILE *fp=NULL;
//Bit per Pixel
const int bpp=32;

unsigned char buffer[pixel_w*pixel_h*bpp/8];


void Cleanup()
{
	EnterCriticalSection(&m_critial);
	if(m_pDirect3DVertexBuffer)
		m_pDirect3DVertexBuffer->Release();
	if(m_pDirect3DTexture)
		m_pDirect3DTexture->Release();
	if(m_pDirect3DDevice)
		m_pDirect3DDevice->Release();
	if(m_pDirect3D9)
		m_pDirect3D9->Release();
	LeaveCriticalSection(&m_critial);
}


int InitD3D( HWND hwnd, unsigned long lWidth, unsigned long lHeight )
{
	HRESULT lRet;
	InitializeCriticalSection(&m_critial);

	Cleanup();
	EnterCriticalSection(&m_critial);
	// Create IDirect3D
	m_pDirect3D9 = Direct3DCreate9( D3D_SDK_VERSION );
	if ( m_pDirect3D9 == NULL ){
		LeaveCriticalSection(&m_critial);
		return -1;
	}

	if ( lWidth == 0 || lHeight == 0 ){
		RECT rt;
		GetClientRect( hwnd, &rt );
		lWidth = rt.right-rt.left;
		lHeight = rt.bottom-rt.top;
	}
	
	/*
	//Get Some Info
	//Retrieves device-specific information about a device.
	D3DCAPS9 d3dcaps;
	lRet=m_pDirect3D9->GetDeviceCaps(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,&d3dcaps);
	int hal_vp = 0;
	if( d3dcaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT ){
		//save in hal_vp the fact that hardware vertex processing is supported.
		hal_vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;
	}
	// get D3DDISPLAYMODE
	D3DDISPLAYMODE d3dDisplayMode;
	lRet = m_pDirect3D9->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3dDisplayMode );
	if ( FAILED(lRet) ){
		LeaveCriticalSection(&m_critial);
		return -1;
	}
	*/

	//D3DPRESENT_PARAMETERS Describes the presentation parameters.
	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory( &d3dpp, sizeof(d3dpp) );
	d3dpp.BackBufferWidth = lWidth;   
	d3dpp.BackBufferHeight = lHeight;
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;	
	//d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
	d3dpp.BackBufferCount        = 1;
	d3dpp.MultiSampleType   = D3DMULTISAMPLE_NONE;	
	d3dpp.SwapEffect  = D3DSWAPEFFECT_COPY;	
	d3dpp.hDeviceWindow  = hwnd;
	d3dpp.Windowed   = TRUE;
	d3dpp.EnableAutoDepthStencil = FALSE;
	d3dpp.Flags = D3DPRESENTFLAG_VIDEO;
	d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_DEFAULT;

	m_hVideoWnd = hwnd;

	//Creates a device to represent the display adapter.
	//Adapter:		Ordinal number that denotes the display adapter. D3DADAPTER_DEFAULT is always the primary display 
	//D3DDEVTYPE:	D3DDEVTYPE_HAL((Hardware Accelerator), or D3DDEVTYPE_SW(SoftWare)
	//BehaviorFlags:D3DCREATE_SOFTWARE_VERTEXPROCESSING, or D3DCREATE_HARDWARE_VERTEXPROCESSING
	lRet = m_pDirect3D9->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, NULL,
		D3DCREATE_SOFTWARE_VERTEXPROCESSING|D3DCREATE_MULTITHREADED, &d3dpp, &m_pDirect3DDevice );
	
	/*
	//Set some property
	//SetSamplerState()
	// Texture coordinates outside the range [0.0, 1.0] are set
	// to the texture color at 0.0 or 1.0, respectively.
	IDirect3DDevice9_SetSamplerState(m_pDirect3DDevice, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
	IDirect3DDevice9_SetSamplerState(m_pDirect3DDevice, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
	// Set linear filtering quality
	IDirect3DDevice9_SetSamplerState(m_pDirect3DDevice, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
	IDirect3DDevice9_SetSamplerState(m_pDirect3DDevice, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
	//SetRenderState()
	//set maximum ambient light
	IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,0));
	// Turn off culling
	IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_CULLMODE, D3DCULL_NONE);
	// Turn off the zbuffer
	IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_ZENABLE, D3DZB_FALSE);
	// Turn off lights
	IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_LIGHTING, FALSE);
	// Enable dithering
	IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_DITHERENABLE, TRUE);
	// disable stencil
	IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_STENCILENABLE, FALSE);
	// manage blending
	IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_ALPHABLENDENABLE, TRUE);
	IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
	IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);
	IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_ALPHATESTENABLE,TRUE);
	IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_ALPHAREF, 0x10);
	IDirect3DDevice9_SetRenderState(m_pDirect3DDevice, D3DRS_ALPHAFUNC,D3DCMP_GREATER);
	// Set texture states
	IDirect3DDevice9_SetTextureStageState(m_pDirect3DDevice, 0, D3DTSS_COLOROP,D3DTOP_MODULATE);
	IDirect3DDevice9_SetTextureStageState(m_pDirect3DDevice, 0, D3DTSS_COLORARG1,D3DTA_TEXTURE);
	IDirect3DDevice9_SetTextureStageState(m_pDirect3DDevice, 0, D3DTSS_COLORARG2,D3DTA_DIFFUSE);
	// turn off alpha operation
	IDirect3DDevice9_SetTextureStageState(m_pDirect3DDevice, 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
	*/


	//Creates a texture resource.
	//Usage: 
	//D3DUSAGE_SOFTWAREPROCESSING: If this flag is used, vertex processing is done in software. 
	//							If this flag is not used, vertex processing is done in hardware.
	//D3DPool: 
	//D3D3POOL_DEFAULT:	Resources are placed in the hardware memory (Such as video memory)
	//D3D3POOL_MANAGED:	Resources are placed automatically to device-accessible memory as needed.
	//D3DPOOL_SYSTEMMEM:  Resources are placed in system memory.

	lRet = m_pDirect3DDevice->CreateTexture(lWidth, lHeight, 1, D3DUSAGE_SOFTWAREPROCESSING,
		D3DFMT_X8R8G8B8,
		D3DPOOL_MANAGED,
		&m_pDirect3DTexture, NULL );


	if ( FAILED(lRet) ){
		LeaveCriticalSection(&m_critial);
		return -1;
	}
	// Create Vertex Buffer
	lRet = m_pDirect3DDevice->CreateVertexBuffer( 4 * sizeof(CUSTOMVERTEX),
		0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &m_pDirect3DVertexBuffer, NULL );
	if ( FAILED(lRet) ){
		LeaveCriticalSection(&m_critial);
		return -1;
	}

	/* -0.5f is a "feature" of DirectX and it seems to apply to Direct3d also */
#if TEXTURE_HALF
	CUSTOMVERTEX vertices[] ={
		{-0.5f,			-0.5f,			0.0f,	1.0f,D3DCOLOR_ARGB(255, 255, 255, 255),0.0f,0.0f},
		{lWidth-0.5f,	-0.5f,			0.0f,	1.0f,D3DCOLOR_ARGB(255, 255, 255, 255),0.5f,0.0f},
		{lWidth - 0.5f,	lHeight-0.5f,	0.0f,	1.0f,D3DCOLOR_ARGB(255, 255, 255, 255),0.5f,1.0f},
		{-0.5f,			lHeight-0.5f,	0.0f,	1.0f,D3DCOLOR_ARGB(255, 255, 255, 255),0.0f,1.0f}
	};
#elif TEXTURE_ROTATE
	//Rotate Texture?
	CUSTOMVERTEX vertices[] ={
		{lWidth/4-0.5f,		-0.5f,			0.0f,	1.0f,D3DCOLOR_ARGB(255, 255, 255, 255),0.0f,0.0f},
		{lWidth-0.5f,		lHeight/4-0.5f,	0.0f,	1.0f,D3DCOLOR_ARGB(255, 255, 255, 255),1.0f,0.0f},
		{lWidth*3/4-0.5f,	lHeight-0.5f,	0.0f,	1.0f,D3DCOLOR_ARGB(255, 255, 255, 255),1.0f,1.0f},
		{-0.5f,				lHeight*3/4-0.5f,0.0f,	1.0f,D3DCOLOR_ARGB(255, 255, 255, 255),0.0f,1.0f}
	};
#else
	CUSTOMVERTEX vertices[] ={
		{-0.5f,			-0.5f,			0.0f,	1.0f,D3DCOLOR_ARGB(255, 255, 255, 255),0.0f,0.0f},
		{lWidth-0.5f,	-0.5f,			0.0f,	1.0f,D3DCOLOR_ARGB(255, 255, 255, 255),1.0f,0.0f},
		{lWidth - 0.5f,	lHeight-0.5f,	0.0f,	1.0f,D3DCOLOR_ARGB(255, 255, 255, 255),1.0f,1.0f},
		{-0.5f,			lHeight-0.5f,	0.0f,	1.0f,D3DCOLOR_ARGB(255, 255, 255, 255),0.0f,1.0f}
	};
#endif


	// Fill Vertex Buffer
	CUSTOMVERTEX *pVertex;
	lRet = m_pDirect3DVertexBuffer->Lock( 0, 4 * sizeof(CUSTOMVERTEX), (void**)&pVertex, 0 );
	if ( FAILED(lRet) ){
		LeaveCriticalSection(&m_critial);
		return -1;
	}
	memcpy(pVertex, vertices, sizeof(vertices));

	m_pDirect3DVertexBuffer->Unlock();
	LeaveCriticalSection(&m_critial);
	return 0;
}


bool Render()
{
	LRESULT lRet;
	//Read Data
	//RGB
	if (fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp) != pixel_w*pixel_h*bpp/8){
		// Loop
		fseek(fp, 0, SEEK_SET);
		fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp);
	}

	if(buffer == NULL || m_pDirect3DDevice == NULL) 
		return false;
	//Clears one or more surfaces
	lRet = m_pDirect3DDevice->Clear(0, NULL, D3DCLEAR_TARGET,
		D3DCOLOR_XRGB(0, 255, 0), 1.0f, 0);
	
	D3DLOCKED_RECT d3d_rect;
	//Locks a rectangle on a texture resource.
	//And then we can manipulate pixel data in it.
	lRet = m_pDirect3DTexture->LockRect( 0, &d3d_rect, 0, 0 );
	if ( FAILED(lRet) ){
		return false;
	}
	// Copy pixel data to texture
	byte *pSrc = buffer;
	byte *pDest = (byte *)d3d_rect.pBits;
	int stride = d3d_rect.Pitch;
	unsigned long i = 0;


	int pixel_w_size=pixel_w*bpp/8;
	for(unsigned long i=0; i< pixel_h; i++){
		memcpy( pDest, pSrc, pixel_w_size );
		pDest += stride;
		pSrc += pixel_w_size;
	}

	m_pDirect3DTexture->UnlockRect( 0 );

	//Begin the scene
	if ( FAILED(m_pDirect3DDevice->BeginScene()) ){
		return false;
	}

	lRet = m_pDirect3DDevice->SetTexture( 0, m_pDirect3DTexture );

	
	//Binds a vertex buffer to a device data stream.
	m_pDirect3DDevice->SetStreamSource( 0, m_pDirect3DVertexBuffer,
		0, sizeof(CUSTOMVERTEX) );
	//Sets the current vertex stream declaration.
	lRet = m_pDirect3DDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
	//Renders a sequence of nonindexed, geometric primitives of the 
	//specified type from the current set of data input streams.
	m_pDirect3DDevice->DrawPrimitive( D3DPT_TRIANGLEFAN, 0, 2 );
	m_pDirect3DDevice->EndScene();
	//Presents the contents of the next buffer in the sequence of back 
	//buffers owned by the device.
	m_pDirect3DDevice->Present( NULL, NULL, NULL, NULL );
	return true;
}


LRESULT WINAPI MyWndProc(HWND hwnd, UINT msg, WPARAM wparma, LPARAM lparam)
{
	switch(msg){
	case WM_DESTROY:
		Cleanup();
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, msg, wparma, lparam);
}

int WINAPI WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in LPSTR lpCmdLine, __in int nShowCmd )
{
	WNDCLASSEX wc;
	ZeroMemory(&wc, sizeof(wc));

	wc.cbSize = sizeof(wc);
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.lpfnWndProc = (WNDPROC)MyWndProc;
	wc.lpszClassName = L"D3D";
	wc.style = CS_HREDRAW | CS_VREDRAW;

	RegisterClassEx(&wc);

	HWND hwnd = NULL;
	hwnd = CreateWindow(L"D3D", L"Simplest Video Play Direct3D (Texture)", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hInstance, NULL);
	if (hwnd==NULL){
		return -1;
	}


	if(InitD3D( hwnd, pixel_w, pixel_h)==E_FAIL){
		return -1;
	}

	ShowWindow(hwnd, nShowCmd);
	UpdateWindow(hwnd);

	fp=fopen("../test_bgra_320x180.rgb","rb+");

	if(fp==NULL){
		printf("Cannot open this file.\n");
		return -1;
	}

	MSG msg;
	ZeroMemory(&msg, sizeof(msg));

	while (msg.message != WM_QUIT){
		//PeekMessage, not GetMessage
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else{
			Sleep(40);
			Render();
		}
	}


	UnregisterClass(L"D3D", hInstance);
	return 0;
}

表面(Surface)方式

首先我们看看网上的方式:参考链接使用D3D渲染YUV_RGB
这里就不贴代码了,这里主要有一个问题就是,在播放窗口变大后,图片拉伸是模糊的,没有按照分辨率去拉伸,我下面贴出我的代码:头文件d3dDispaly.h

#pragma once
#include <stdio.h>
#include <tchar.h>
#include <d3d9.h>

class d3dDispaly{

public:
	d3dDispaly();
	~d3dDispaly();

public:
	bool init(HWND hwnd);
	void uninit();
	bool inputYUVData(LPBYTE pBuffer, long nwidth, long nheight);

private:
	bool __setVideoSize(long lWidth, long lHeight);
	void __drawImage();

private:
	HWND m_hwnd;
	int m_nImgWidth;
	int m_nImgHeight;
	IDirect3D9* m_pD3D;
	IDirect3DDevice9* m_pd3dDevice;
	IDirect3DSurface9* m_pd3dSurface;
	IDirect3DSurface9* m_pBackBuffer;
	RECT m_rtViewport;
};

cpp文件:d3dDispaly.cpp

//#include "stdafx.h"
#include "d3dDispaly.h"

d3dDispaly::d3dDispaly() :
	m_nImgWidth(0), m_nImgHeight(0), m_pD3D(NULL),
	m_pd3dDevice(NULL), m_pd3dSurface(NULL), m_hwnd(NULL),
	m_pBackBuffer(NULL)
{

}
d3dDispaly::~d3dDispaly()
{
}


bool d3dDispaly::init(HWND hwnd)
{
	m_hwnd = hwnd;

	m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);
	if (m_pD3D == NULL)
	{
		OutputDebugStringA("D3D****************Direct3DCreate9 Fail*******\n");
		return false;
	}

	D3DDISPLAYMODE d3dDisplayMode;
	m_pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3dDisplayMode);

	D3DCAPS9 d3dcaps;
	m_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dcaps);
	int hal_vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
	if (d3dcaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
		hal_vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;

	D3DPRESENT_PARAMETERS d3dpp;
	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = d3dDisplayMode.Format;//D3DFMT_UNKNOWN;
	d3dpp.BackBufferWidth = d3dDisplayMode.Width;
	d3dpp.BackBufferHeight = d3dDisplayMode.Height;
	d3dpp.Flags = D3DPRESENTFLAG_VIDEO;
	d3dpp.hDeviceWindow = m_hwnd;


	HRESULT result = m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hwnd, hal_vp, &d3dpp, &m_pd3dDevice);
	if (FAILED(result))
	{
		D3DERR_OUTOFVIDEOMEMORY;
		char buf[100] = { 0 };
		sprintf_s(buf, 100, "D3D****************CreateDevice-1 Fail[%d]*******\n", result);
		OutputDebugStringA(buf);
		if (FAILED(m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, m_hwnd, hal_vp, &d3dpp, &m_pd3dDevice)))
		{
			OutputDebugStringA("D3D****************CreateDevice-2 Fail*******\n");
			return false;
		}
	}

	m_rtViewport.left = 0;
	m_rtViewport.right = d3dDisplayMode.Width;
	m_rtViewport.top = 0;
	m_rtViewport.bottom = d3dDisplayMode.Height;
	return 0;
}

void d3dDispaly::uninit()
{
	if (m_pD3D != NULL)
	{
		m_pD3D->Release();
		m_pD3D = NULL;
	}

	if (m_pd3dDevice != NULL)
	{
		m_pd3dDevice->Release();
		m_pd3dDevice = NULL;
	}

	if (m_pd3dSurface != NULL)
	{
		m_pd3dSurface->Release();
		m_pd3dSurface = NULL;
	}

	if (m_pBackBuffer != NULL)
	{
		m_pBackBuffer->Release();
		m_pBackBuffer = NULL;
	}

	m_nImgWidth = 0;
	m_nImgHeight = 0;
}

bool d3dDispaly::inputYUVData(LPBYTE pBuffer, long nwidth, long nheight)
{
	if (NULL == m_pd3dDevice)
	{
		return false;
	}
	if (m_nImgWidth != nwidth || m_nImgHeight != nheight)
	{
		if (!__setVideoSize(nwidth, nheight))
			return false;
	}

	if (m_pd3dSurface == NULL) return false;
	D3DLOCKED_RECT d3d_rect;
	if (FAILED(m_pd3dSurface->LockRect(&d3d_rect, NULL, D3DLOCK_DONOTWAIT)))
	{
		//OutputDebugStringA("D3D****************LockRect Fail*******\n");
		return false;
	}

	const int w = m_nImgWidth, h = m_nImgHeight;
	BYTE* const p = (BYTE *)d3d_rect.pBits;
	const int stride = d3d_rect.Pitch;
	int i = 0;
	for (i = 0; i < h; i++)
		memcpy(p + i * stride, pBuffer + i * w, w);

	for (i = 0; i < h / 2; i++)
		memcpy(p + stride * h + i * stride / 2, pBuffer + w * h + w * h / 4 + i * w / 2, w / 2);

	for (i = 0; i < h / 2; i++)
		memcpy(p + stride * h + stride * h / 4 + i * stride / 2, pBuffer + w * h + i * w / 2, w / 2);

	if (FAILED(m_pd3dSurface->UnlockRect()))
	{
		//OutputDebugStringA("D3D****************UnlockRect Fail*******\n");
		return false;
	}

	__drawImage();
	return true;
}

bool d3dDispaly::__setVideoSize(long lWidth, long lHeight)
{
	if (FAILED(m_pd3dDevice->CreateOffscreenPlainSurface(lWidth, lHeight, (D3DFORMAT)MAKEFOURCC('Y', 'V', '1', '2'), D3DPOOL_DEFAULT, &m_pd3dSurface, NULL)))
	{
		OutputDebugStringA("D3D****************CreateOffscreenPlainSurface Fail*******\n");
		return false;
	}

	m_pd3dDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &m_pBackBuffer);
	if (!m_pBackBuffer)
	{
		OutputDebugStringA("D3D****************GetBackBuffer Fail*******\n");
		return false;
	}

	m_nImgWidth = lWidth;
	m_nImgHeight = lHeight;

	return true;
}

void d3dDispaly::__drawImage()
{
	if (m_pd3dDevice != NULL)
	{
		//m_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
		if (SUCCEEDED(m_pd3dDevice->BeginScene()))
		{
			m_pd3dDevice->StretchRect(m_pd3dSurface/*NULL*/, NULL, m_pBackBuffer, &m_rtViewport, D3DTEXF_LINEAR);
			m_pd3dDevice->EndScene();
		}
		m_pd3dDevice->Present(NULL, NULL, NULL, NULL);
	}
}

这里需要指出的是init函数中的代码,不能放在__setVideoSize函数中,因为D3D初始化较耗性能,如果在初始化的同时就压数据,很容易引起卡顿问题。

posted @ 2019-06-12 17:28  SunkingYang  阅读(1972)  评论(0编辑  收藏  举报