本文共 15632 字,大约阅读时间需要 52 分钟。
转自:
做的这个directshow的filter属于transform filter。在其间,参考了,directshow的帮助文档,一本外文的介绍directshow的书(这本书不错,里面的代码,虽然感觉有copy directshow帮助文档的成分,但是,讲的挺不错),还有vc知识库中的两篇文章了,这两篇文章也写得挺好的。如果当初我没有看,我想会走不少弯路。
要写directshow的transform filter 已经准备了好久了。曾经用过directshow,不过里面的也就是用现成的filter组成一个filter graph,然后run,就可以了。把自己写filter看作挺难的事情。现在发现,其实写transform filter不难,当然,也只是像我这样简单的。但是,处理的速度确实是问题。
首先,directshow filter是符合com组件规范的,也就是,其是一个com组件,要符合com的规范,需要实现一些函数的,不过,已经有不少的基类了,只需要继承就可以了,这样,对com组件所涉及的知识就少了很多。在编写directshow filter时,只要找准了要继承的基类,然后,实现里面的虚函数等等,写上自己的控制代码,就可以了。
下面先简单的给出一些源代码和说明,具体的解释,以后会做。这个简单的transform filter由三个文件组成:
1。ToGrayFilter.def:由于filter是个基于dll的com组件,所以一般的filter要实现几个入口函数。要导出dll中的函数有两种方法:一种是在定义函数时使用导出关键字__declspec(dllexport),也就是在.h文件中定义函数如下:
extern “C“ __declspec(dllexport) BOOL DllRegisterServer;等等;第二种方法是使用模块定义文件,这也是我在这里用的方法。
//ToGrayFilter.def-----------------------------------------------------------------------
//
LIBRARY ToGrayFilter.ax
EXPORTS
DllMain PRIVATE //dll的入口函数,directshow中实现的是dllEntryPoint DllGetClassObject PRIVATE //用于获得类工厂指针 DllCanUnloadNow PRIVATE //系统空闲时会调用这个函数,确定是否可以卸载DLL DllRegisterServer PRIVATE //将com组件注册到注册表中 DllUnregisterServer PRIVATE //删除注册表中的com组件的注册信息 上面的函数是作为一个典型的自注册com组件dll所必需的5个导出函数。2。ToGrayFilter.h
在这个文件中声明了一个c++类,这个类是从directshow中的一个用于方便用户编写filter的基类中继承的。
//ToGrayFilter.h-------------------------------------------------------------------------------------------------------
// #ifndef TOGRAYFILTER_H_ #define TOGRAYFILTER_H_//
// {5F2265B1-A841-4eb7-871F-5556436042AC} DEFINE_GUID(CLSID_ToGrayFilter, 0x5f2265b1, 0xa841, 0x4eb7, 0x87, 0x1f, 0x55, 0x56, 0x43, 0x60, 0x42, 0xac); class CToGrayFilter:public CTransformFilter { public: CToGrayFilter(TCHAR *pName,LPUNKNOWN pUnk,HRESULT *phr); //constructor ~CToGrayFilter(); //destructorpublic:
// Static object-creation method (for the class factory) //必须有的 // static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr);public:
//implement the base filter 's method,下面5个函数是CTranseformFilter的虚函数,必须实现了的 // HRESULT CheckInputType(const CMediaType *pmtIn); HRESULT GetMediaType(int iPosition, CMediaType *pMediaType); HRESULT CheckTransform(const CMediaType *mtIn,const CMediaType *mtOut); HRESULT DecideBufferSize(IMemAllocator *pAlloc,ALLOCATOR_PROPERTIES *pProp); HRESULT Transform(IMediaSample *pSource,IMediaSample *pDest);//this method is also in the base filter class,but in the base class it does noting,
//implement here just want to get m_VihIn and m_VihOut // HRESULT SetMediaType(PIN_DIRECTION direction, const CMediaType *pmt); private: //my own process method,这是我自己的处理函数:),在Transform这个函数中调用的 // HRESULT ToGray(BYTE *pbInput,BYTE *pbOutput); //a help method ,just copy from helper document,这个函数,最终没有在这个filter中用,因为不知道为何, //感觉有些问题,按理说是不应该这样的。最后是自己直接操作了 void GetVideoInfoParameters( const VIDEOINFOHEADER *pvih, // Pointer to the format header. BYTE * const pbData, // Pointer to the first address in the buffer. bool bYuv, // Is this a YUV format? (true = YUV, false = RGB) DWORD *pdwWidth, // Returns the width in pixels. DWORD *pdwHeight, // Returns the height in pixels. LONG *plStrideInBytes, // Add this to a row to get the new row down. BYTE **ppbTop // Returns a pointer to the first byte in the // top row of pixels. );VIDEOINFOHEADER m_VihIn; // Holds the current video format (input),可以把每一帧图像当作bmp位图
VIDEOINFOHEADER m_VihOut; // Holds the current video format (output)//the imformation about every picture,put here just for speed
// DWORD m_bytePerLine;//the real numbers of bits in one line,just for handy int m_Width;//the width of the bitmap,to use it ,just handy int m_Height;//the height of the bitmap }; #endif 3。ToGrayFilter.cpp// ToGrayFilter.cpp : 定义 DLL 应用程序的入口点。
//#include "stdafx.h"
//the include file for directshow filter
// #include "streams.h" //用到了filter的基类,就要包含这个头文件的 #include <initguid.h> // #include <tchar.h> #include <stdio.h>#include "ToGrayFilter.h"
#pragma warning(disable:4715)
//--------------------------------------------------------------------
CToGrayFilter:: CToGrayFilter(TCHAR *pName,LPUNKNOWN pUnk,HRESULT *phr):
CTransformFilter(pName, pUnk, CLSID_ToGrayFilter) { }CToGrayFilter::~CToGrayFilter()
{ }//------------------------------------------------------------------------------
// //this method check this filter's input pin could receive which kind of media type // HRESULT CToGrayFilter::CheckInputType(const CMediaType *pmtIn) { if ((pmtIn->majortype != MEDIATYPE_Video) || (pmtIn->subtype != MEDIASUBTYPE_RGB24) || (pmtIn->formattype != FORMAT_VideoInfo) || (pmtIn->cbFormat < sizeof(VIDEOINFOHEADER))) { return VFW_E_TYPE_NOT_ACCEPTED; }VIDEOINFOHEADER *pVih =
reinterpret_cast<VIDEOINFOHEADER*>(pmtIn->pbFormat); // Everything is good. return S_OK;}
//the downstream filter check this filter's output pin ,then the method will be used
//now because the media type has not been changed ,so only return the media type of //this filter's input pin // HRESULT CToGrayFilter::GetMediaType(int iPosition, CMediaType *pMediaType) { // The output pin calls this method only if the input pin is connected. ASSERT(m_pInput->IsConnected());// There is only one output type that we want, which is the input type.
if (iPosition < 0) { return E_INVALIDARG; } else if (iPosition == 0) { //this maybe OK now return m_pInput->ConnectionMediaType(pMediaType); } return VFW_S_NO_MORE_ITEMS;}
//this method checks if a proposed output type is compatible with the current input type. //The method is also called if the input pin reconnects after the output pin connects. // HRESULT CToGrayFilter::CheckTransform(const CMediaType *mtIn,const CMediaType *mtOut) { // Check the major type. // if (mtOut->majortype != MEDIATYPE_Video) { return VFW_E_TYPE_NOT_ACCEPTED; }// Check the subtype and format type.
//// Make sure the subtypes match
// if (mtIn->subtype != mtOut->subtype) { return VFW_E_TYPE_NOT_ACCEPTED; } if ((mtOut->formattype != FORMAT_VideoInfo) || (mtOut->cbFormat < sizeof(VIDEOINFOHEADER))) { return VFW_E_TYPE_NOT_ACCEPTED; }// Compare the bitmap information against the input type.
// ASSERT(mtIn->formattype == FORMAT_VideoInfo); BITMAPINFOHEADER *pBmiOut = HEADER(mtOut->pbFormat); BITMAPINFOHEADER *pBmiIn = HEADER(mtIn->pbFormat);if ((pBmiOut->biWidth <= pBmiIn->biWidth) &&
(pBmiOut->biHeight == abs(pBmiIn->biHeight))) { return S_OK; } return VFW_E_TYPE_NOT_ACCEPTED;}
//this method is used during the output pin connection process. //the output pin is responsible for negotiating the allocation of data stream buffers //during the pin connection process, //even if this allocation is actually done by the input pin of the downstream filter. // HRESULT CToGrayFilter::DecideBufferSize(IMemAllocator *pAlloc,ALLOCATOR_PROPERTIES *pProp) { // Make sure the input pin is connected. // if (!m_pInput->IsConnected()) { return E_UNEXPECTED; }// Our strategy here is to use the upstream allocator as the guideline,
// but also defer to the downstream filter's request // when it's compatible with us.// First, find the upstream allocator...
ALLOCATOR_PROPERTIES InputProps;IMemAllocator *pAllocInput = 0;
HRESULT hr = m_pInput->GetAllocator(&pAllocInput);if (FAILED(hr))
{ return hr; }// ...now get the properties.
hr = pAllocInput->GetProperties(&InputProps); pAllocInput->Release();if (FAILED(hr))
{ return hr; }// Buffer alignment should be non-zero [zero alignment makes no sense!].
if (pProp->cbAlign == 0) { pProp->cbAlign = 1; }// Number of buffers must be non-zero.
if (pProp->cbBuffer == 0) { pProp->cBuffers = 1; }// For buffer size, find the maximum of the upstream size and
// the downstream filter's request. pProp->cbBuffer = max(InputProps.cbBuffer, pProp->cbBuffer);// Now set the properties on the allocator that was given to us.
ALLOCATOR_PROPERTIES Actual; hr = pAlloc->SetProperties(pProp, &Actual); if (FAILED(hr)) { return hr; }}
HRESULT CToGrayFilter::Transform(IMediaSample *pSource,IMediaSample *pDest)
{ // Get pointers to the underlying buffers. // BYTE *pBufferIn, *pBufferOut; HRESULT hr; hr = pSource->GetPointer(&pBufferIn); if (FAILED(hr)) { return hr; } hr = pDest->GetPointer(&pBufferOut); if (FAILED(hr)) { return hr; }// Process the data.
// ToGray(pBufferIn,pBufferOut);}
//------------------------------------------------------------------------------
//this method is also in the base filter class,but in the base class it does noting, //implement here just want to get m_VihIn and m_VihOut //HRESULT CToGrayFilter::SetMediaType(PIN_DIRECTION direction,
const CMediaType *pmt) { if (direction == PINDIR_INPUT) { ASSERT(pmt->formattype == FORMAT_VideoInfo); VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)pmt->pbFormat;// WARNING! In general you cannot just copy a VIDEOINFOHEADER
// struct, because the BITMAPINFOHEADER member may be followed by // random amounts of palette entries or color masks. (See VIDEOINFO // structure in the DShow SDK docs.) Here it's OK because we just // want the information that's in the VIDEOINFOHEADER struct itself.CopyMemory(&m_VihIn, pVih, sizeof(VIDEOINFOHEADER));
}
else // Output pin { ASSERT(direction == PINDIR_OUTPUT); ASSERT(pmt->formattype == FORMAT_VideoInfo); VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)pmt->pbFormat;CopyMemory(&m_VihOut, pVih, sizeof(VIDEOINFOHEADER));
}//------------------------------------------------
// // DWORD m_bytePerLine;//the real numbers of bits in one line,just for handy //int m_Width;//the width of the bitmap,to use it ,just handy //int m_Height;//the height of the bitmap m_Width=m_VihIn.bmiHeader.biWidth; m_Height=m_VihIn.bmiHeader.biHeight; m_bytePerLine=(24*m_Width+31)/32*4; return S_OK; }//----------------------------------------------------------------------------------
//my own process method ,to change the data to gray // HRESULT CToGrayFilter::ToGray(BYTE *pbInput,BYTE *pbOutput) { DWORD dwWidth, dwHeight; // Width and height in pixels (input) DWORD dwWidthOut, dwHeightOut; // Width and height in pixels (output) LONG lStrideIn, lStrideOut; // Stride in bytes BYTE *pbSource, *pbTarget; // First byte first row, source & target//下面这一段都没有用了,因为感觉这个函数比较古怪,最后是用自己的处理bmp位图的方法得到数据的
//GetVideoInfoParameters(&m_VihIn, pbInput,false, &dwWidth, &dwHeight,
// &lStrideIn, &pbSource); // GetVideoInfoParameters(&m_VihOut, pbOutput,false, &dwWidthOut, &dwHeightOut, // &lStrideOut, &pbTarget);// Formats should match (except maybe stride).
// ASSERT(dwWidth == dwWidthOut); //ASSERT(abs(dwHeight) == abs(dwHeightOut));//here to process the data
// /*for (DWORD y = 0; y < dwHeight; y++) { WORD *pwTarget = (WORD*)pbTarget; WORD *pwSource = (WORD*)pbSource;//下面是对RGB32的处理,所以,对于RGB24如果这样做,会有问题的
//RGBQUAD *pPixelTarget = (RGBQUAD*)pbTarget;
//RGBQUAD *pPixelSource = (RGBQUAD*)pbSource; // for (DWORD x = 0; x < dwWidth; x++) // { // BYTE grayColor; // grayColor= // (pPixelSource[x].rgbBlue+pPixelSource[x].rgbGreen+pPixelSource[x].rgbRed)/3; // // pPixelTarget[x] is the x'th pixel in the row. // pPixelTarget[x].rgbBlue = pPixelSource[x].rgbBlue; // pPixelTarget[x].rgbGreen = pPixelSource[x].rgbGreen; // pPixelTarget[x].rgbRed = pPixelSource[x].rgbRed; // pPixelTarget[x].rgbReserved = 0; // } }*///want to get the imformation for myself
////也可以在这里,当每一帧图像来了的时候,都重新计算偏移等等,但是没有必要了,而且会影响速度
//DWORD m_bytePerLine;//the real numbers of bits in one line,just for handy //int m_Width;//the width of the bitmap,to use it ,just handy //int m_Height;//the height of the bitmap //m_Width=m_VihIn.bmiHeader.biWidth; //m_Height=m_VihIn.bmiHeader.biHeight; //m_bytePerLine=(24*m_Width+31)/32*4;BYTE rColor,gColor,bColor,changeColor;
for (int i=0;i<m_Height;i++) { for (int j=0;j<m_Width;j++) { rColor=*(pbInput+(m_Height-i-1)*m_bytePerLine+j*3); gColor=*(pbInput+(m_Height-i-1)*m_bytePerLine+j*3+1); bColor=*(pbInput+(m_Height-i-1)*m_bytePerLine+j*3+2);changeColor=(rColor+gColor+bColor)/3;
*(pbOutput+(m_Height-i-1)*m_bytePerLine+j*3)=changeColor;
*(pbOutput+(m_Height-i-1)*m_bytePerLine+j*3+1)=changeColor; *(pbOutput+(m_Height-i-1)*m_bytePerLine+j*3+2)=changeColor; } }return S_OK;
}
//下面这个函数,是从帮助文档中copy的函数,不过感觉没有什么用,实际上在我写的这个filter中也没有用 上
//----------------------------------------------------------------------------
//a helper function ,not write by me,just copy from the help docoment // void CToGrayFilter::GetVideoInfoParameters( const VIDEOINFOHEADER *pvih, // Pointer to the format header. BYTE * const pbData, // Pointer to the first address in the buffer. bool bYuv, // Is this a YUV format? (true = YUV, false = RGB) DWORD *pdwWidth, // Returns the width in pixels. DWORD *pdwHeight, // Returns the height in pixels. LONG *plStrideInBytes, // Add this to a row to get the new row down. BYTE **ppbTop // Returns a pointer to the first byte in the // top row of pixels. ) { LONG lStride;// For 'normal' formats, biWidth is in pixels.
// Expand to bytes and round up to a multiple of 4. if ((pvih->bmiHeader.biBitCount != 0) && (0 == (7 & pvih->bmiHeader.biBitCount))) { lStride = (pvih->bmiHeader.biWidth * (pvih->bmiHeader.biBitCount / 8) + 3) & ~3; } else // Otherwise, biWidth is in bytes. { lStride = pvih->bmiHeader.biWidth; }// If rcTarget is empty, use the whole image.
if (IsRectEmpty(&pvih->rcTarget)) { *pdwWidth = (DWORD)pvih->bmiHeader.biWidth; *pdwHeight = (DWORD)(abs(pvih->bmiHeader.biHeight)); if (pvih->bmiHeader.biHeight < 0 || bYuv) // Top-down bitmap. { *plStrideInBytes = lStride; // Stride goes "down". *ppbTop = pbData; // Top row is first. } else // Bottom-up bitmap. { *plStrideInBytes = -lStride; // Stride goes "up". // Bottom row is first. *ppbTop = pbData + lStride * (*pdwHeight - 1); } } else // rcTarget is NOT empty. Use a sub-rectangle in the image. { *pdwWidth = (DWORD)(pvih->rcTarget.right - pvih->rcTarget.left); *pdwHeight = (DWORD)(pvih->rcTarget.bottom - pvih->rcTarget.top); if (pvih->bmiHeader.biHeight < 0 || bYuv) // Top-down bitmap. { // Same stride as above, but first pixel is modified down // and over by the target rectangle. *plStrideInBytes = lStride; *ppbTop = pbData + lStride * pvih->rcTarget.top + (pvih->bmiHeader.biBitCount * pvih->rcTarget.left) / 8; } else // Bottom-up bitmap. { *plStrideInBytes = -lStride; *ppbTop = pbData + lStride * (pvih->bmiHeader.biHeight - pvih->rcTarget.top - 1) + (pvih->bmiHeader.biBitCount * pvih->rcTarget.left) / 8; } } }
//-----------------------------------------------------------------------------------------------
// CUnknown* WINAPI CToGrayFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr) { CToGrayFilter *pFilter = new CToGrayFilter(NAME("To Gray Filter"), pUnk, pHr); if (pFilter== NULL) { *pHr = E_OUTOFMEMORY; } return pFilter; } //the following three method maybe the same for most of the filters //extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE ,DWORD,LPVOID );
BOOL APIENTRY DllMain(HANDLE hModule,DWORD dwRes,LPVOID pv)
{ return DllEntryPoint((HINSTANCE)hModule,dwRes,pv); }HRESULT WINAPI DllRegisterServer()
{ return AMovieDllRegisterServer2(TRUE); } HRESULT WINAPI DllUnregisterServer() { return AMovieDllRegisterServer2(FALSE); }//---------------------------------------------------
// AMOVIESETUP_FILTER FilterInfo = { &CLSID_ToGrayFilter, // CLSID L"To Gray Filter", // Name MERIT_DO_NOT_USE, // Merit 0, // Number of AMOVIESETUP_PIN structs NULL // Pin registration information }; CFactoryTemplate g_Templates[]={ { L"To Gray Filter", &CLSID_ToGrayFilter, CToGrayFilter::CreateInstance, 0, &FilterInfo, }};
int g_cTemplates=sizeof(g_Templates)/sizeof(g_Templates[0]);上面就是我的程序的所有源代码。这只是一个示例的程序而已,里面有好多东西,其实将来写从CTransformFilter继承的transform filter是完全可以重复使用的,也就是里面的很多东西都差不多都会是这样。代码简单,真正自己的实现只是一个函数而已,里面只是得到像素的值,然后进行转换。也就是说,transformfilter有一个架子,不同的只是对像素的操作而已。
现在匆匆收场了,其实还有好多没有提到的地方,但是现在心情突然不好了,以后再补充吧。
转载地址:http://pzyob.baihongyu.com/