这两天看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后负责视频纹理资源的重新初始化。

鄙人文笔不行,就贴代码吧。欢迎板砖。下面给出效果贴图:

posted on 2013-03-14 20:03  蜀山  阅读(1285)  评论(0编辑  收藏  举报