这两天看KlayGE引擎,里面的视频纹理。想来在Ogre中也实现一把。
核心思想便是利用VMR9来渲染视频,在Ogre帧监听中更新Ogre纹理。下面给出自己的代码,有需要也可以向我索要,共同学习。
下面给出关键代码:
DShowVMR9Allocator.h
1 2 3 #ifndef _DSHOWVMR9ALLOCATOR_HPP 4 #define _DSHOWVMR9ALLOCATOR_HPP 5 7 8 #ifndef CHECK_HRESULT 9 #define CHECK_HRESULT(x) { HRESULT _hr = x; if (static_cast<HRESULT>(_hr) < 0) { std::stringstream ss;ss << __FILE__ << ": " << __LINE__; throw std::runtime_error(ss.str()); } } 10 #endif 11 12 #include <d3d9.h> 13 #include <strmif.h> 14 #include <vmr9.h> 15 #include <vector> 16 #include"Ogre.h" 17 #include "mutex.hpp" 18 19 20 21 #define USER_ID 0x12345678 22 23 class VMR9Allocator : public IVMRSurfaceAllocator9, IVMRImagePresenter9 24 { 25 public: 26 explicit VMR9Allocator(HWND wnd,Ogre::String texname); 27 virtual~VMR9Allocator(); 28 29 virtual HRESULT STDMETHODCALLTYPE InitializeDevice(DWORD_PTR dwUserID,VMR9AllocationInfo *lpAllocInfo,DWORD *lpNumBuffers); 30 virtual HRESULT STDMETHODCALLTYPE TerminateDevice(DWORD_PTR dwID); 31 virtual HRESULT STDMETHODCALLTYPE GetSurface(DWORD_PTR dwUserID,DWORD SurfaceIndex,DWORD SurfaceFlags,IDirect3DSurface9 **lplpSurface); 32 virtual HRESULT STDMETHODCALLTYPE AdviseNotify(IVMRSurfaceAllocatorNotify9 *lpIVMRSurfAllocNotify); 33 virtual HRESULT STDMETHODCALLTYPE StartPresenting(DWORD_PTR dwUserID); 34 virtual HRESULT STDMETHODCALLTYPE StopPresenting(DWORD_PTR dwUserID); 35 virtual HRESULT STDMETHODCALLTYPE PresentImage(DWORD_PTR dwUserID,VMR9PresentationInfo *lpPresInfo); 36 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,void** ppvObject); 37 virtual ULONG STDMETHODCALLTYPE AddRef(); 38 virtual ULONG STDMETHODCALLTYPE Release(); 39 public: 40 void ResetDecive(); 41 Ogre::TexturePtr PresentTexture(); 42 Ogre::TexturePtr GetTexture()const{ return present_tex_;}; 43 44 protected: 45 void CreateDevice(); 46 void DeleteSurfaces(); 47 private: 48 static int d3DDLLRefCount_; 49 static HMODULE mod_d3d9_; 50 private: 51 HWND wnd_; 52 int ref_count_; 53 54 IDirect3D9* d3d_; 55 IDirect3DDevice9* d3d_device_; 56 57 IVMRSurfaceAllocatorNotify9* vmr_surf_alloc_notify_; 58 std::vector<IDirect3DSurface9*> surfaces_; 59 int cur_surf_index_; 60 IDirect3DSurface9* cache_surf_; 61 62 typedef IDirect3D9* (WINAPI *Direct3DCreate9Func)(UINT SDKVersion); 63 Direct3DCreate9Func DynamicDirect3DCreate9_; 64 D3DPRESENT_PARAMETERS d3dpp_; 65 private: 66 Ogre::TexturePtr present_tex_; 67 Ogre::String texName; 68 private: 69 VMR::mutex mMutex; 70 }; 71 72 #endif
DShowVMR9Allocator.cpp
View Code
VMR9Allocator::VMR9Allocator(HWND wnd,Ogre::String texname): wnd_(wnd),texName(texname), ref_count_(1),cur_surf_index_(0xFFFFFFFF),cache_surf_(nullptr) { if(d3DDLLRefCount_++==0) VMR9Allocator::mod_d3d9_ = ::LoadLibraryW(L"d3d9.dll"); if (nullptr == mod_d3d9_) { ::MessageBoxW(nullptr, L"未能装载d3d9.dll", L"Error", MB_OK); } if (mod_d3d9_ != nullptr) { DynamicDirect3DCreate9_ = reinterpret_cast<Direct3DCreate9Func>(::GetProcAddress(mod_d3d9_, "Direct3DCreate9")); } d3d_ = DynamicDirect3DCreate9_(D3D_SDK_VERSION); this->CreateDevice(); } VMR9Allocator::~VMR9Allocator() { this->DeleteSurfaces(); if( --d3DDLLRefCount_==0) FreeLibrary( VMR9Allocator::mod_d3d9_); }
View Code
HRESULT VMR9Allocator::InitializeDevice(DWORD_PTR dwUserID,VMR9AllocationInfo* lpAllocInfo, DWORD* lpNumBuffers) { if (dwUserID != USER_ID) { return S_OK; } if (nullptr == lpNumBuffers) { return E_POINTER; } if (!vmr_surf_alloc_notify_) { return E_FAIL; } HRESULT hr = S_OK; lpAllocInfo->dwFlags |= VMR9AllocFlag_TextureSurface; this->DeleteSurfaces(); surfaces_.resize(*lpNumBuffers); hr = vmr_surf_alloc_notify_->AllocateSurfaceHelper(lpAllocInfo, lpNumBuffers, &surfaces_[0]); if (FAILED(hr) && !(lpAllocInfo->dwFlags & VMR9AllocFlag_3DRenderTarget)) { this->DeleteSurfaces(); lpAllocInfo->dwFlags &= ~VMR9AllocFlag_TextureSurface; lpAllocInfo->dwFlags |= VMR9AllocFlag_OffscreenSurface; CHECK_HRESULT(vmr_surf_alloc_notify_->AllocateSurfaceHelper(lpAllocInfo, lpNumBuffers, &surfaces_[0])); } //Create Ogre Texture if(present_tex_.isNull()) { printf("创建Ogre纹理\n"); present_tex_=Ogre::TextureManager::getSingleton().createManual( texName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, Ogre::TEX_TYPE_2D, lpAllocInfo->dwWidth, lpAllocInfo->dwHeight, 0, Ogre::PF_BYTE_BGRA, Ogre::TU_DYNAMIC_WRITE_ONLY ); } CHECK_HRESULT(d3d_device_->CreateOffscreenPlainSurface(lpAllocInfo->dwWidth, lpAllocInfo->dwHeight, D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM, &cache_surf_, nullptr)); return S_OK; }
View Code
Ogre::TexturePtr VMR9Allocator::PresentTexture() { if(present_tex_.isNull()) { printf("纹理丢\n"); return Ogre::TexturePtr(); } VMR::TryLock _lock(mMutex); if(!_lock.IsLock()) return Ogre::TexturePtr(); if (FAILED(d3d_device_->TestCooperativeLevel())) { return Ogre::TexturePtr(); } if (cur_surf_index_ < surfaces_.size()) { CHECK_HRESULT(d3d_device_->GetRenderTargetData(surfaces_[cur_surf_index_], cache_surf_)); D3DLOCKED_RECT d3dlocked_rc; if(cache_surf_==NULL) return Ogre::TexturePtr(); CHECK_HRESULT(cache_surf_->LockRect(&d3dlocked_rc, nullptr, D3DLOCK_NOSYSLOCK | D3DLOCK_READONLY)); uint32_t const width = present_tex_->getWidth(); uint32_t const height = present_tex_->getHeight(); uint8_t const * src = static_cast<uint8_t const *>(d3dlocked_rc.pBits); { Ogre::HardwarePixelBufferSharedPtr pixelBuffer = present_tex_->getBuffer(); pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); size_t rowPitch = pixelBox.rowPitch; Ogre::uint32* dst = static_cast<Ogre::uint32*>(pixelBox.data); { for (uint32_t y = 0; y < height; ++ y) { memcpy(dst, src, width * 4); dst += rowPitch; src += d3dlocked_rc.Pitch; } } pixelBuffer->unlock(); } CHECK_HRESULT(cache_surf_->UnlockRect()); } return present_tex_; }
ShowVideoTexture.h
View Code
#pragma once #include <windows.h> #include <control.h> #include <d3d9.h> #include <strmif.h> #include <vmr9.h> #include "Ogre.h" #include "OgreFrameListener.h" #include "string" using namespace std; enum ShowState { SS_Unkown, SS_Uninit, SS_Stopped, SS_Paused, SS_Playing, }; //已解决D3D设备丢失 纹理重设 class ShowVideoTexture:public Ogre::FrameListener,public Ogre::RenderSystem::Listener { public: ShowVideoTexture(HWND hwnd); ~ShowVideoTexture(); bool IsComplete(); void Load(std::string const & fileName); Ogre::TexturePtr PresentTexture(); ShowState State(long msTimeout = -1); bool frameStarted(const Ogre::FrameEvent& evt); void eventOccurred(const Ogre::String & eventName,const Ogre::NameValuePairList *parameters = 0); private: IGraphBuilder* graph_; IBaseFilter* filter_; IMediaControl* media_control_; IMediaEvent* media_event_; IMediaSeeking* media_seek_; IVMRSurfaceAllocator9* vmr_allocator_; ShowState state_; HWND HWnd; float mtimeOffset; float currTimeflow; private: void Init(); void Free(); void DoPlay(); void DoStop(); void DoPause(); public: bool CanPlay() const; bool CanStop() const; bool CanPause() const; bool IsInitialized() const; void Play(); void Stop(); void Pause(); LONGLONG GetMediaLength()const; LONGLONG GetCurrPosition()const; void SetPosition(LONGLONG ); void SetTexUpdateTimeOffset(float timeos){mtimeOffset = timeos;}; Ogre::TexturePtr GetTexture(); string VideofileName; private: void CreateMaterial(Ogre::String matName); Ogre::MaterialPtr mMaterial; bool ResetTexture; public: Ogre::MaterialPtr GetMaterial()const{return mMaterial;}; };
ShowVideoTexture.cpp
View Code
ShowVideoTexture::ShowVideoTexture(HWND hwnd):HWnd(hwnd),vmr_allocator_(NULL),media_seek_(NULL),ResetTexture(false), media_event_(NULL),media_control_(NULL),filter_(NULL),graph_(NULL),mtimeOffset(0.05f),currTimeflow(0) { Ogre::Root::getSingletonPtr()->addFrameListener(this); Ogre::Root::getSingletonPtr()->getRenderSystem()->addListener(this); } ShowVideoTexture::~ShowVideoTexture() { this->Free(); Ogre::Root::getSingletonPtr()->removeFrameListener(this); Ogre::Root::getSingletonPtr()->getRenderSystem()->removeListener(this); }
创建Ogre材质
View Code
void ShowVideoTexture::CreateMaterial(Ogre::String matName) { mMaterial=Ogre::MaterialManager::getSingleton().create(matName,Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); //创建纹理对象并设置参数 Ogre::TextureUnitState *tex= mMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(); tex->setTextureFiltering(Ogre::FO_LINEAR, Ogre::FO_LINEAR, Ogre::FO_NONE); //获得纹理空间并赋上值 tex=mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0); tex->setTexture( GetTexture()); }
View Code
void ShowVideoTexture::Load(std::string const & fileName) { this->Free(); this->Init(); VideofileName = fileName; CHECK_HRESULT(::CoCreateInstance(CLSID_FilterGraph, nullptr, CLSCTX_ALL, IID_IGraphBuilder, reinterpret_cast<void**>(&graph_))); CHECK_HRESULT(::CoCreateInstance(CLSID_VideoMixingRenderer9, nullptr, CLSCTX_INPROC_SERVER, IID_IBaseFilter, reinterpret_cast<void**>(&filter_))); IVMRFilterConfig9* filter_config; { CHECK_HRESULT(filter_->QueryInterface(IID_IVMRFilterConfig9, reinterpret_cast<void**>(&filter_config))); } CHECK_HRESULT(filter_config->SetRenderingMode(VMR9Mode_Renderless)); CHECK_HRESULT(filter_config->SetNumberOfStreams(1)); IVMRSurfaceAllocatorNotify9*vmr_surf_alloc_notify; CHECK_HRESULT(filter_->QueryInterface(IID_IVMRSurfaceAllocatorNotify9, reinterpret_cast<void**>(&vmr_surf_alloc_notify))); std::wstring fn; ConvertToWstr(fn, fileName); // create our surface allocator vmr_allocator_ = new VMR9Allocator(HWnd,fileName); // let the allocator and the notify know about each other CHECK_HRESULT(vmr_surf_alloc_notify->AdviseSurfaceAllocator(static_cast<DWORD_PTR>(USER_ID),vmr_allocator_)); CHECK_HRESULT(vmr_allocator_->AdviseNotify(vmr_surf_alloc_notify)); CHECK_HRESULT(graph_->AddFilter(filter_, fn.c_str())); { CHECK_HRESULT(graph_->QueryInterface(IID_IMediaControl, reinterpret_cast<void**>(&media_control_))); } { CHECK_HRESULT(graph_->QueryInterface(IID_IMediaEvent, reinterpret_cast<void**>(&media_event_))); } CHECK_HRESULT(graph_->RenderFile(fn.c_str(), nullptr)); { CHECK_HRESULT(graph_->QueryInterface(IID_IMediaSeeking, reinterpret_cast<void**>(&media_seek_))); } state_ = SS_Stopped; media_seek_->SetTimeFormat(&TIME_FORMAT_FRAME); CreateMaterial(fileName); }
View Code
bool ShowVideoTexture::frameStarted(const Ogre::FrameEvent& evt) { if(ResetTexture) { printf("重设纹理材质\n"); mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTexture( GetTexture()); ResetTexture = false; return true; } //mMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0)->setTexture( GetTexture()); currTimeflow+=evt.timeSinceLastFrame; if(currTimeflow >mtimeOffset) { currTimeflow = 0; this->PresentTexture(); } // if(IsComplete()) // { // SetPosition(0); // Play(); // } return true; }
View Code
void ShowVideoTexture::eventOccurred(const Ogre::String & eventName,const Ogre::NameValuePairList *parameters) { if (eventName == "DeviceRestored") { printf("重设消息\n"); ((VMR9Allocator*)vmr_allocator_)->ResetDecive(); Play(); ResetTexture = true; } }
View Code
#pragma once #include "Windows.h" namespace VMR { class Lock; class TryLock; class mutex { protected: mutex(mutex&); mutex& operator=(mutex const&); public: mutex() { InitializeCriticalSection(&critical_sec); } ~mutex() { DeleteCriticalSection(&critical_sec); } friend Lock; friend TryLock; private: void Lock() { EnterCriticalSection(&critical_sec); } void UnLock() { LeaveCriticalSection(&critical_sec); } bool TryLock() { return TryEnterCriticalSection(&critical_sec); } private: CRITICAL_SECTION critical_sec; }; class Lock { public: Lock(mutex&mut):mut_(&mut) { mut_->Lock(); } ~Lock() { mut_->UnLock(); } private: Lock(Lock&); Lock& operator=(Lock const&); mutex*mut_; }; class TryLock { public: TryLock(mutex&mut):mut_(&mut),islock(false) { islock = mut_->TryLock(); } ~TryLock() { if(islock) mut_->UnLock(); } bool IsLock()const{return islock;}; private: TryLock(Lock&); TryLock& operator=(TryLock const&); mutex*mut_; bool islock; }; }
VMR9Allocator类用于分配VMR9资源和实现相应接口,也负责实际的Ogre纹理更新,ShowVideoTexture用于创建Ogre材质和提供外部调用的接口,同时ShowVideoTexture也负责相应D3D Device Lost等响应。Ogre很容易device lost,同样VMR9的D3D device也一样,我这里在Ogre d3d device lost后负责视频纹理资源的重新初始化。
鄙人文笔不行,就贴代码吧。欢迎板砖。下面给出效果贴图: