• 用VMR9实现Ogre视频纹理


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

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

  • 相关阅读:
    通过POST请求上传文件
    接口测试及常用接口测试工具
    maven-surefire-plugin插件
    BDD框架之Cucumber研究
    一分钟认识:Cucumber框架
    ACM团队周赛题解(3)
    C++11新增容器以及元组
    ACM团队周赛题解(2)
    C++11部分特性
    ACM团队周赛题解(1)
  • 原文地址:https://www.cnblogs.com/lxzCode/p/Ogre_video_texture_VMR9.html
Copyright © 2020-2023  润新知