打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
用于directshow的一些辅助函数&&VC+DirectShow对视频进行图片处理
2月12日
用于directshow的一些辅助函数
没有进行类的封装,只是把一些函数集中在一个文件中。
一个是函数的声明文件DShowUtilities.h,一个是函数的定义文件DShowUtilities.cpp
这里面的函数都是从微软自己带的源代码或是帮助文档中copy过来的。
首先是DShowUtilities.h文件
#ifndef DShowUtilities_H_
#define DShowUtilities_H_
#include <DShow.h>
#pragma comment(lib,"Strmiids.lib")
//这只是一个很小的函数而已,只是一些例子代码中会用到
#define SAFE_RELEASE(x) { if (x) x->Release(); x = NULL; }
HRESULT AddFilterByCLSID(
IGraphBuilder *pGraph,  // Pointer to the Filter Graph Manager.
const GUID& clsid,      // CLSID of the filter to create.
LPCWSTR wszName,        // A name for the filter.
IBaseFilter **ppF);      // Receives a pointer to the filter.
HRESULT GetUnconnectedPin(
IBaseFilter *pFilter,   // Pointer to the filter.
PIN_DIRECTION PinDir,   // Direction of the pin to find.
IPin **ppPin);           // Receives a pointer to the pin.
HRESULT ConnectFilters(
IGraphBuilder *pGraph, // Filter Graph Manager.
IPin *pOut,            // Output pin on the upstream filter.
IBaseFilter *pDest);    // Downstream filter.
HRESULT ConnectFilters(
IGraphBuilder *pGraph,
IBaseFilter *pSrc,
IBaseFilter *pDest);
//判断pin是否有此mediaType,stolen from sdk amcap
BOOL HasMediaType(IPin* pPin,  REFGUID majorType );
//stolen from sdk amcap,其中用到上面的函数HasMediaType
BOOL IsVideoPin( IPin* pPin );
// Tear down everything downstream of a given filter
//将一个给定的filter下的所有filter都删除,通过递归调用实现
//stolen from sdk amcap
void NukeDownstream(IBaseFilter *pf,IGraphBuilder *pFg);
// Tear down everything downstream of the capture filters, so we can build
// a different capture graph.  Notice that we never destroy the capture filters
// and WDM filters upstream of them, because then all the capture settings
// we've set would be lost.
//将capture filter后面的filter删除,这样就可以建立一个新的capture graph。
//不删除capture filter的原因在于如果删除,那么,我们原先的设置就白设了
//stolen from sdk amcap
void TearDownGraph(IGraphBuilder *pFg,IVideoWindow *pVW,IBaseFilter *pVCap);
#endif
下面的代码是DShowUtilities.cpp里面的,是这些函数的实现
//DShowUtilities.cpp--------------------------------------------------------
#include "stdafx.h"
#include "DShowUtilities.h"
/*-----------------------------------------------------------------------------------
The following function creates a filter with a specified class identifier (CLSID)
and adds it to the filter graph
USE Example: Add AVI Mux filter to the graph
IBaseFilter *pMux;
hr = AddFilterByCLSID(pGraph, CLSID_AviDest, L"AVI Mux", &pMux);
if (SUCCEEDED(hr))
{
//...............
pMux->Release();
}
----------------------------------------------------------------------------------*/
HRESULT AddFilterByCLSID(
IGraphBuilder *pGraph,  // Pointer to the Filter Graph Manager.
const GUID& clsid,      // CLSID of the filter to create.
LPCWSTR wszName,        // A name for the filter.
IBaseFilter **ppF)      // Receives a pointer to the filter.
{
if (!pGraph || ! ppF) return E_POINTER;
*ppF = 0;
IBaseFilter *pF = 0;
HRESULT hr = CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER,
IID_IBaseFilter, reinterpret_cast<void**>(&pF));
if (SUCCEEDED(hr))
{
hr = pGraph->AddFilter(pF, wszName);
if (SUCCEEDED(hr))
*ppF = pF;
else
pF->Release();
}
return hr;
}
/*----------------------------------------------------------------------------------
This function find an unconnected pin on a filter
The following function searches on a filter for an unconnected pin,
either input or output. It returns the first matching pin.
Finding unconnected pins is useful when you are connecting filters.
USE Example:
IPin *pOut = NULL;
HRESULT hr = GetUnconnectedPin(pFilter, PINDIR_OUTPUT, &pOut);
if (SUCCEEDED(hr))
{
//...........
pOut->Release();
}
-----------------------------------------------------------------------------------*/
HRESULT GetUnconnectedPin(
IBaseFilter *pFilter,   // Pointer to the filter.
PIN_DIRECTION PinDir,   // Direction of the pin to find.
IPin **ppPin)           // Receives a pointer to the pin.
{
*ppPin = 0;
IEnumPins *pEnum = 0;
IPin *pPin = 0;
HRESULT hr = pFilter->EnumPins(&pEnum);
if (FAILED(hr))
{
return hr;
}
while (pEnum->Next(1, &pPin, NULL) == S_OK)
{
PIN_DIRECTION ThisPinDir;
pPin->QueryDirection(&ThisPinDir);
if (ThisPinDir == PinDir)
{
IPin *pTmp = 0;
hr = pPin->ConnectedTo(&pTmp);
if (SUCCEEDED(hr))  // Already connected, not the pin we want.
{
pTmp->Release();
}
else  // Unconnected, this is the pin we want.
{
pEnum->Release();
*ppPin = pPin;
return S_OK;
}
}
pPin->Release();
}
pEnum->Release();
// Did not find a matching pin.
return E_FAIL;
}
/*--------------------------------------------------------------------------------
The following function connects an output pin from one filter to
the first available input pin on another filter.
里面调用了前面定义的函数GetUnconnectedPin
---------------------------------------------------------------------------------*/
HRESULT ConnectFilters(
IGraphBuilder *pGraph, // Filter Graph Manager.
IPin *pOut,            // Output pin on the upstream filter.
IBaseFilter *pDest)    // Downstream filter.
{
if ((pGraph == NULL) || (pOut == NULL) || (pDest == NULL))
{
return E_POINTER;
}
#ifdef debug
PIN_DIRECTION PinDir;
pOut->QueryDirection(&PinDir);
_ASSERTE(PinDir == PINDIR_OUTPUT);
#endif
// Find an input pin on the downstream filter.
IPin *pIn = 0;
HRESULT hr = GetUnconnectedPin(pDest, PINDIR_INPUT, &pIn);//调用了前面定义的函数
if (FAILED(hr))
{
return hr;
}
// Try to connect them.
hr = pGraph->Connect(pOut, pIn);
pIn->Release();
return hr;
}
/*---------------------------------------------------------------------------------
Here is an overloaded version of the same function. The second parameter is
a pointer to a filter, rather than a pin.
The function connects the first filter to the second filter
USE Example:
The following example use this example to connect AVI Mux filter to File Writer
filter
IBaseFilter *pMux, *pWrite;
hr = AddFilterByCLSID(pGraph, CLSID_AviDest, L"AVI Mux", &pMux);
if (SUCCEEDED(hr))
{
hr = AddFilterByCLSID(pGraph, CLSID_FileWriter, L"File Writer", &pWrite);
if (SUCCEEDED(hr))
{
hr = ConnectFilters(pGraph, pMux, pWrite);
//Use IFileSinkFilter to set the file name (not shown).
pWrite->Release();
}
pMux->Release();
}
这里面用了前面定义的那个ConnectFilters函数和GetUnconnectedPin函数
----------------------------------------------------------------------------------*/
HRESULT ConnectFilters(
IGraphBuilder *pGraph,
IBaseFilter *pSrc,
IBaseFilter *pDest)
{
if ((pGraph == NULL) || (pSrc == NULL) || (pDest == NULL))
{
return E_POINTER;
}
// Find an output pin on the first filter.
IPin *pOut = 0;
HRESULT hr = GetUnconnectedPin(pSrc, PINDIR_OUTPUT, &pOut);//用了前面定义的函数
if (FAILED(hr))
{
return hr;
}
hr = ConnectFilters(pGraph, pOut, pDest);//用了前面定义的函数
pOut->Release();
return hr;
}
/*---------------------------------------------------------------------------------
判断pin是否有此mediaType,stolen from sdk amcap
--------------------------------------------------------------------------------*/
BOOL HasMediaType(IPin* pPin,  REFGUID majorType )
{
if( !pPin )
{
return FALSE;
}
IEnumMediaTypes* pMediaTypes;
HRESULT hr = pPin->EnumMediaTypes( &pMediaTypes );
if( FAILED( hr ) )
{
return FALSE;
}
hr = pMediaTypes->Reset();
if( FAILED( hr ) )
{
return FALSE;
}
ULONG           fetched;
AM_MEDIA_TYPE   *mediaType;
while( S_OK == pMediaTypes->Next( 1, &mediaType, &fetched ) )
{
if( IsEqualGUID( mediaType->majortype, majorType ) )
{
//     DeleteMediaType( mediaType );
return TRUE;
}
//        DeleteMediaType( mediaType );
}
return FALSE;
}
/*--------------------------------------------------------------------------------
判断是否是video pin,stolen from sdk amcap
此函数用到上面的函数HasMediaType
--------------------------------------------------------------------------------*/
BOOL IsVideoPin( IPin* pPin )
{
return HasMediaType( pPin, MEDIATYPE_Video );//上面的函数
}
/*---------------------------------------------------------------------------------
Tear down everything downstream of a given filter
将一个GraphBuilder中的给定的filter下的所有filter都删除,通过递归调用实现
stolen from amcap
----------------------------------------------------------------------------------*/
void NukeDownstream(IBaseFilter *pf,IGraphBuilder *pFg)
{
IPin *pP=0, *pTo=0;
ULONG u;
IEnumPins *pins = NULL;
PIN_INFO pininfo;
if (!pf)
return;
HRESULT hr = pf->EnumPins(&pins);
pins->Reset();//通过这个函数调用,获得最新的数据?
while(hr == NOERROR)
{
hr = pins->Next(1, &pP, &u);//找到filter上的pin
if(hr == S_OK && pP)
{
pP->ConnectedTo(&pTo);//找到与这个pin相连的另一个filter上的pin
if(pTo)
{
hr = pTo->QueryPinInfo(&pininfo);
if(hr == NOERROR)
{
if(pininfo.dir == PINDIR_INPUT)//只寻找input的pin
{
NukeDownstream(pininfo.pFilter,pFg);//递归调用
pFg->Disconnect(pTo);//要将pin的两端同时移除
pFg->Disconnect(pP);
pFg->RemoveFilter(pininfo.pFilter);//在递归调用中,
//最先删除的是只有input pin的filter
}
pininfo.pFilter->Release();
}
pTo->Release();
}
pP->Release();
}
}//end while
if(pins)
pins->Release();
}
/*---------------------------------------------------------------------------------
Tear down everything downstream of the capture filters, so we can build
a different capture graph.  Notice that we never destroy the capture filters
and WDM filters upstream of them, because then all the capture settings
we've set would be lost.
将capture filter后面的filter删除,这样就可以建立一个新的capture graph。
不删除capture filter的原因在于如果删除,那么,我们原先的设置就白设了
stolen from sdk amcap
---------------------------------------------------------------------------------*/
void TearDownGraph(IGraphBuilder *pFg,IVideoWindow *pVW,IBaseFilter *pVCap)
{
if(pVW)//IVideoWindow停止视频显示
{
// stop drawing in our window, or we may get wierd repaint effects
pVW->put_Owner(NULL);
pVW->put_Visible(OAFALSE);
pVW->Release();
pVW = NULL;
}
// destroy the graph downstream of our capture filters
if(pVCap)
NukeDownstream(pVCap,pFg);//这个函数 Tear down everything downstream of a given filter
//但是,这个filter却是会保留的
//然后应该是对一些filter的release
}
16:08 | 添加评论 | 固定链接 | 写入日志 | DirectShow
VC+DirectShow对视频进行图片处理
现在的图像越来越花巧了,有浮雕、马赛克、相框等特效,看得人眼花缭乱。本来图像特效没什么稀奇的,在PhotoShop等图像处理软件中我们早已见得多了,不过用在视频上就令人感觉有点神奇。我一直都想拥有这些效果,但我的摄像头是很早就买到的,没福气奢望驱动程序给它带来的全新精彩。刚好我学习DirectShow有一段时间了,既为了挑战自己(我从未写过令自己感到满意的程序),也为了检验学习成果,我就下了决心用DirectShow实现这些效果。几经努力,终于有了一些成绩,我完成了其中一些效果,并发现程序可以用在DirectShow支持的影音文件上,又自己把它应用到D3D中去,感觉还不错。
先看看效果吧,以激励斗志。我是对着摄像头广告中的效果图来做程序的,我怕编程的热情像以前那样很快冷却,只留下一堆乱糟糟的代码,我需要它来不断兴奋被代码搞得昏头转向的大脑。
(图一:几种效果) (图二:D3D中的摄像展示)
编程的思路是这样的:写一个DirectShow的VideoRenderer Filter用于实时获取图像,之后在用DirectShow连接并使用摄像头、播放视频时用上该Filter,这样就可以实时处理视频的图片并进行显示了。
由于要用到DirectShow,我想在这里说一些关于DirectShow的初级知识,初学或没学过DirectShow的朋友就请耐下性子来听我这个初学者罗嗦一阵子(要学DirectShow的朋友可以看天极的DirectShow相关文章,介绍很详细)。
在新版的DX9 SDK中已看不到DirectShow的影子了(DirectShow发展到了尽头?),不过DirectShow还可用,而且相当有用。听说VC.net自带有DX8 SDK开发文件,但我仍希望您能找到DX8 SDK和VC6。DX8 SDK帮助文件对DirectShow详细的说明和SDK丰富的DirectShow例子对DirectShow开发是大有裨益的。我在开发Filter过程中发现VC.net不能编译通过,出现“InterLockedExchange 重定义”的编译错误,VC6则一切正常,我把这归咎为微软的问题,菜鸟的我无力解决。是了,我只用VC6编写Filter,其它编码使用的是VC.net,VC.net可完成一般的DirectShow编程,我本人更喜欢VC.net的编码环境。
“请问Filter是什么?”这个问题在我脑中很久了,DirectShow的功能是由Filter搭建起来的,但我很久以来都被“Filter”这个词困惑着,直到现在才有些眉目。当然,我学识浅,很可能会说错,请海涵。Filter在影音风暴等软件中称为“滤镜”,在微软中国上一些译文把它译为“筛选器”,五花八门的(看来没有核心技术真的很被动)。而Filter在数字信号处理等专业课程中称为“滤波器”!这下您应该知道一点了吧,DirectShow对视频、音频的处理过程就是数字信号处理的过程,可以把数字信号处理的理论应用于此,微软为此把这个DirectShow部件称为Filter。
再说Sample,我把它译为“采样”,也就是一个数据包,可理解为摄像头摄像或声卡录音时每扫描一次得到的数据、音视频文件每一帧的数据。
开发VideoRenderer Filter
Filter要做以下工作:接受24bit RGB格式的图片,这由上级Filter肢解视频得到,并把它处理成32bit ARGB图片,之后传给外部函数进行进一步处理。
我要Filter这样工作的理由是:几乎所有的视频Filter都接受24bit RGB格式,不用担心会连接失败;32bit ARGB可以很好地支持MMX加速,如果你会用MMX的话,我在本文中会涉及一点MMX,不过和我水平相同,都是初级的;调用外部函数能提供更多的灵活性,不用费尽心思在Filter中封装图像处理函数,可以在写程序时随能力和水平提高而加入新的处理函数,同时也保证了能够及时处理。
怎么样,Filter要做的很少很简单吧,与此一样,写一个Filter也比想象中的简单,我们一步步地看看。
新建一个简单的DLL项目,设置名称为VR,删除VR.cpp中的DllMain函数,添加VR.h和VR.def两个文件,在VR.def中加入以下代码,以完成函数导出。
LIBRARY VR
EXPORTS
DllMain PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
再做些没有创意的东西 —— Filter注册、类工厂定义等,在VR.cpp中加入,我是从DirectShow的Filter例子中复制,再略加修改得来的。
#include "stdafx.h"
#include "VR.h"
#pragma comment(lib,"strmbase.lib")
#pragma comment(lib,"winmm.lib")
// Setup data
const AMOVIESETUP_MEDIATYPE sudIpPinTypes =
{
&MEDIATYPE_Video, // MajorType
&MEDIASUBTYPE_NULL // MinorType
};
const AMOVIESETUP_PIN sudIpPin =
{
L"Input", // The Pins name
FALSE, // Is rendered
FALSE, // Is an output pin
FALSE, // Allowed none
FALSE, // Allowed many
&CLSID_NULL, // Connects to filter
NULL, // Connects to pin
1, // Number of types
&sudIpPinTypes // Pin details
};
const AMOVIESETUP_FILTER sudVRAx =
{
&CLSID_lwVideoRenderer, // Filter CLSID /**/
L"lwVideoRenderer", // String name /**/
MERIT_NORMAL, // Filter merit
1, // Number of pins
&sudIpPin // Pin details
};
// List of class IDs and creator functions for the class factory. This
// provides the link between the OLE entry point in the DLL and an object
// being created. The class factory will call the static CreateInstance
// function when it is asked to create a CLSID_VideoRenderer object
CFactoryTemplate g_Templates[] = {
{ L"lwVideoRenderer" /**/
, &CLSID_lwVideoRenderer /**/
, CVideoRenderer::CreateInstance
, NULL
, &sudVRAx },
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
// DllRegisterServer
// Used to register and unregister the filter
STDAPI DllRegisterServer()
{
return AMovieDllRegisterServer2( TRUE );
} // DllRegisterServer
// DllUnregisterServer
STDAPI DllUnregisterServer()
{
return AMovieDllRegisterServer2( FALSE );
} // DllUnregisterServer
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
// DllMain
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved);
}// DllMain
经过一番复制后,需要增加一些简单的业务逻辑。我们先来完成Filter的类定义,从CBaseVideoRendeer派生一个新类,重写四个函数就可奠定这个Filter的基本功能,如下,在VR.h中加入。
#include <streams.h>
// 回调类定义
class FunCLS
{public: virtual void procFun(BITMAPINFO* pBmpInfo, BYTE* pb){return;};
};
// 回调函数指针定义
typedef void (CALLBACK* pProcFun)(BITMAPINFO* pBmpInfo,BYTE* pb);
// {F81331DB-2E46-43e7-8709-BE57205D8914} Filter的全局标识符
static const GUID CLSID_lwVideoRenderer =
{ 0xf81331db, 0x2e46, 0x43e7, { 0x87, 0x9, 0xbe, 0x57, 0x20, 0x5d, 0x89, 0x14 } };
// Filter 类定义
class CVideoRenderer : public CBaseVideoRenderer
{
public:
// 创建进程。
static CUnknown * WINAPI CreateInstance(LPUNKNOWN, HRESULT *);
// 构造、释构函数
CVideoRenderer(LPUNKNOWN pUnk,HRESULT* phr);
~CVideoRenderer();
public:
// 检查是否有可以接受格式的数据
HRESULT CheckMediaType(const CMediaType* pmt);
// 设置具体的数据格式,如视频图像的宽、高等
HRESULT SetMediaType(const CMediaType* pmt);
// 递交数据,即显示、呈现数据
HRESULT DoRenderSample(IMediaSample* pMediaSample);
private:
BITMAPINFO m_bmpInfo; // 图片信息
BYTE* m_pCopyBuffer; // 复制缓冲区
UINT m_pixelNum; // 像素点的数目
FunCLS* m_pFunCLS; // 回调类指针
pProcFun m_pPF; // 回调函数指针
};
我在上面曾提过在Filter中要在接受到新数据时调用外部函数进行处理,因此我定义了一个回调类(我自己称呼的)和一个回调函数指针。这样可以把回调类作为MFC视图类的一个基类,以方便地使用MFC视图类中的成员变量。而同时提供回调函数指针就可以满足同时播放多个视频文件、使用多个摄像头的需要。这是我在使用中感到有必要而后来修改得来的,使Filter的使用具有足够的灵活性。下面看看以上Filter类中函数的具体实现。
//===========================================================
// 创建进程。
CUnknown* WINAPI CVideoRenderer::CreateInstance(LPUNKNOWN pUnk,HRESULT* phr)
{
return new CVideoRenderer(pUnk,phr);
}
//===========================================================
// 构造函数
CVideoRenderer::CVideoRenderer(LPUNKNOWN pUnk,HRESULT *phr) : CBaseVideoRenderer(CLSID_lwVideoRenderer,"lw Video Renderer",pUnk,phr)
{
m_pCopyBuffer = NULL;
m_pFunCLS = NULL;
m_pPF = NULL;
m_pixelNum = 0;
}
//===========================================================
// 释构函数
CVideoRenderer::~CVideoRenderer()
{
if(this->m_pCopyBuffer){
delete [] m_pCopyBuffer;
}
}
//===========================================================
// 检查媒体类型
HRESULT CVideoRenderer::CheckMediaType(const CMediaType* pmt)
{
VIDEOINFO *pvi;
// 只接受视频
if( *pmt->FormatType() != FORMAT_VideoInfo ) {
return E_INVALIDARG;
}
// 只接受 RGB24 格式,即 R、G、B各 1 Byte
pvi = (VIDEOINFO *)pmt->Format();
if(IsEqualGUID( *pmt->Type(),MEDIATYPE_Video) && IsEqualGUID( *pmt->Subtype(),MEDIASUBTYPE_RGB24)){
return S_OK;
}
return E_INVALIDARG;
}
//===========================================================
// 设置媒体类型,获取图像的各种信息(宽、高等具体信息),处理图像要用到
HRESULT CVideoRenderer::SetMediaType(const CMediaType* pmt)
{
VIDEOINFO *pviBmp; // Bitmap info header
pviBmp = (VIDEOINFO *)pmt->Format();
memset(&m_bmpInfo,0,sizeof(BITMAPINFO)); // 清零
m_bmpInfo.bmiHeader = pviBmp->bmiHeader;
// 改为 32bit,因为我会把它处理成 32bit 的
m_bmpInfo.bmiHeader.biBitCount = 32;
// 当然,缓冲区大小也变了
m_bmpInfo.bmiHeader.biSizeImage = m_bmpInfo.bmiHeader.biSizeImage * 4 / 3;
// 开辟新 32bit 图片的缓冲区
if(m_pCopyBuffer){ delete [] m_pCopyBuffer;}
m_pCopyBuffer = new BYTE[m_bmpInfo.bmiHeader.biSizeImage];
m_pixelNum = m_bmpInfo.bmiHeader.biWidth * m_bmpInfo.bmiHeader.biHeight;
return S_OK;
}
//===========================================================
// 处理媒体采样
HRESULT CVideoRenderer::DoRenderSample(IMediaSample* pMediaSample)
{
// 获取采样的数据区指针,即 24bit 图片的数据区指针
BYTE* pb = NULL;
pMediaSample->GetPointer(&pb);
if(!pb){
return E_FAIL;
}
// 加锁!锁住我要操作的数据区,以防处理到一半的时候被打断而造成错误
// 其实就是多线程编程中经常使用的临界区的类形式,
// 利用构造函数和释构函数来进入和退出临界区
// m_RendererLock 是 CBaseVideoRenderer 的成员,继承得来。
CAutoLock cAutoLock(&this->m_RendererLock);
// 把 24bit 图片处理成 32bit
BYTE* pb32 = m_pCopyBuffer; // 指向 32bit 缓冲区的指针
for(UINT i = 0; i < m_pixelNum; i ++){
pb32[0] = pb[0];
pb32[1] = pb[1];
pb32[2] = pb[2];
pb32[3] = 0xff; // 0xff 即 255
pb += 3;
pb32 += 4;
}
// 如果有回调类,进行回调处理
if(m_pFunCLS){
m_pFunCLS->procFun(&m_bmpInfo,m_pCopyBuffer);
}
// 如果有回调函数,进行处理
if(m_pPF){
m_pPF(&m_bmpInfo,m_pCopyBuffer);
}
return S_OK;
}
至此,一个简单的 Filter 完成了,可以编译成功、用regsvr32.exe 注册并到GraphEdit.exe 中进行测试了。不过如果要在程序中使用的话,您会发现无法设置回调函数或回调类。这个Filter 是如此的无用,除了IBaseFilter 接口的基本功能外我们从它身上得不到任何有用的东西。所以,还得给它写个接口,让我们可以设置一些东西。写接口也不是难事,只要有一个接口的例子,随便谁都可以对照写出一个来,我就抄写了一个。新建一个 IVRControl.h 文件,加入下面代码。
// {244DF760-7E93-4cf0-92F4-DCB79F646B7E} 接口的 GUID
static const GUID IID_IVRControl = {0x244df760, 0x7e93, 0x4cf0, {0x92, 0xf4, 0xdc, 0xb7, 0x9f, 0x64, 0x6b, 0x7e}};
// 接口定义
DECLARE_INTERFACE_(IVRControl, IUnknown)
{
STDMETHOD(GetBmpInfo) (THIS_ // 方法一:获取图片信息
BITMAPINFO** ppBmpInfo ) PURE;
STDMETHOD(GetPointer) (THIS_ // 方法二:获取缓冲区指针
BYTE** ppb // 缓冲区指针的指针 ) PURE;
STDMETHOD(SetFunCLS) (THIS_ // 方法三:设置回调类
FunCLS* pFunCLS // 回调类指针 ) PURE;
STDMETHOD(SetFun) (THIS_ // 方法四:设置回调函数
pProcFun pPF ) PURE;
};
写完接口后就要实现它,在VR.h 中添加 #include "IVRControl.h",而把接口作为Filter 类的一个基类,像这样:
class CVideoRenderer : public CBaseVideoRenderer, public IVRControl
在CVideoRenderer 类中加入接口函数和询问接口函数:
// 询问接口,一般可以不要的,但这里需要使用接口,也重载了
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
// 接口函数
DECLARE_IUNKNOWN;
STDMETHODIMP GetBmpInfo(BITMAPINFO** ppBmpInfo);
STDMETHODIMP GetPointer(BYTE** ppb);
STDMETHODIMP SetFunCLS(FunCLS* pFunCLS);
STDMETHODIMP SetFun(pProcFun pPF);
再在VR.cpp 中加入上述函数的具体实现代码:
//===========================================================
// 询问接口
STDMETHODIMP CVideoRenderer::NonDelegatingQueryInterface(REFIID riid,void** ppv)
{
CheckPointer(ppv,E_POINTER);
if(riid == IID_IVRControl){
// 返回接口。这里有个细节:返回接口时,Filter 的引用计数会增一,所以外部程序用完接口后也要对它进行释放
return GetInterface((IVRControl*) this,ppv);
}else{
return CBaseVideoRenderer::NonDelegatingQueryInterface(riid,ppv);
}
}
//===========================================================
// 以下为接口函数的具体实现,只是简单的赋值
STDMETHODIMP CVideoRenderer::GetBmpInfo(BITMAPINFO** ppBmpInfo)
{
*ppBmpInfo = &this->m_bmpInfo;
return S_OK;
}
STDMETHODIMP CVideoRenderer::GetPointer(BYTE** ppb)
{
*ppb = m_pCopyBuffer;
return S_OK;
}
STDMETHODIMP CVideoRenderer::SetFunCLS(FunCLS* pFunCLS)
{
m_pFunCLS = pFunCLS;
return S_OK;
}
STDMETHODIMP CVideoRenderer::SetFun(pProcFun pPF)
{
m_pPF = pPF;
return S_OK;
}
不知您注意到了没有:接口其实就是一个虚基类。类在 C++ 等现代编程语言中无处不在,也没什么好惊奇的,只是有利于更好理解。再有一个,看似功能强大的接口可能偏偏很容易实现,它依附于对象,它的复杂可能都隐藏在对象内了。
可以看出在接口定义中也要用到回调类和回调函数指针的定义,所以我把它们连同 Filter CLSID 的定义一起移到 IVRControl.h 文件的开头,使用到此 Filter 时只把 IVRControl.h 这一个文件包含进去就行了。
不错,我们已经一步步、一个个函数的把设想中的 Filter 写出来了,已成功完成了Filter,以 Release 模式把它编译出来足有80多K,用 UPX 压缩后就是30 多K。这样把代码铺出来看,好像蛮多的,不过我在敲代码时一点也不觉得,因为每个函数所做的的确很少,循着逻辑规矩、步步为营地写真的很easy。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
用DirectShow实现视频采集(三)
DirectShow音频采集
基于DirectShow视频及图片捕获软件的开发
创建一个filter实例(transform filter)
DirectShow 媒体文件回放总结
Windows Embedded Compact 7中的多媒体编程(上)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服