如何設計自訂的transform filter)

來源:互聯網
上載者:User

對於DIrectShow的初學者而言,最大的困難莫過於嘗試設計自訂的filter。
設計自訂的transform filter是困難的
因為 首先filter是一種dll (尾碼名為.ax)而編寫dll工程需要一定的VC基礎 所以建議先補充一點dll的知識
其次 dll的註冊,GUID的產生和工程的配置都很麻煩。
再次 網上缺乏現成的transform filter的例子。DirectShow給的源碼比如NULLINPLACE 和CONTRAST都太複雜,都帶有對話方塊和屬性頁面,不適合初學者,而且這些例子 沒有一個涉及到映像格式的轉換,而transform filter最大的公用就是媒體類型的轉換,因此這些例子不適用
作為一個初學者,我深深受到這些問題的困擾,經過刻苦鑽研終於走出了這個泥潭,豁然開朗。於是把它記錄下來,希望可以對其他人有協助,也作為對08年的一個小結。

我的例子是 設計一個 transform filter 把 YUY2 16bit 的媒體轉化為RGB24 24bit的類型。
原因是我的網路攝影機只支援YUY2 16bit這種格式, 我想得到位元影像。。順便學習一下Filter的設計

以下為具體步驟:

一 配置開發環境

   1. VC中在Tools->Options->Directories 設定好DirectX SDK的標頭檔和庫檔案路徑   
   2. 編譯了基類源碼,產生strmbasd.lib (debug版), strmbase.lib(release版)
   3. VC嚮導建立一個win32 DLL(empty)工程
   4. Setting->Link->Output file name: YUV2RGBfilter.ax
   5. Setting->Link加入strmbasd.lib winmm.lib quartz.lib vfw32.lib   (注意路徑)
   6. 定義一個同名.def檔案,加入到工程,內容如下:
         
        LIBRARY YUV2RGBfilter.ax
        EXPORTS
           DllMain              PRIVATE  
           DllGetClassObject    PRIVATE
           DllCanUnloadNow      PRIVATE
           DllRegisterServer    PRIVATE
           DllUnregisterServer PRIVATE

   7.建立一個類 YUV2RGBfilter 建立他的cpp檔案和h檔案
  
   8. 在YUV2RGBfilter.cpp中定義DLL的入口函數及註冊   放在cpp檔案的最後

//
// DllEntryPoint
//
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);

BOOL APIENTRY DllMain(HANDLE hModule,
                      DWORD dwReason,
                      LPVOID lpReserved)
{
return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved);
}

////////////////////////////////////////////////////////////////////////
//
// Exported entry points for registration and unregistration
// (in this case they only call through to default implementations).
//
////////////////////////////////////////////////////////////////////////

STDAPI DllRegisterServer()
{
    return AMovieDllRegisterServer2( TRUE );
}

STDAPI DllUnregisterServer()
{
     return AMovieDllRegisterServer2( FALSE );
}

   9. cpp檔案中要包含的標頭檔

#include <streams.h>
#include <windows.h>
#include <initguid.h>
#include <olectl.h>
#if (1100 > _MSC_VER)
#include <olectlid.h>
#endif
#include "Y2Ruids.h"         // our own public guids
#include "YUV2RGBfilter.h"
  
二 開發Filter

   1. 產生GUID( 命令列模式下運行guidgen工具) 為他建立一個檔案Y2Ruids.h 單獨引用
#include <initguid.h>
// YUV2toRGB24 Filter Object
// {F91FC8FD-B1A6-49b0-A308-D6EDEAF405DA}
DEFINE_GUID(CLSID_YUV2toRGB24,
0xf91fc8fd, 0xb1a6, 0x49b0, 0xa3, 0x8, 0xd6, 0xed, 0xea, 0xf4, 0x5, 0xda);
      
   2. 構造CYUV2RGBfilter類 繼承自CTransformFilter    寫在TransformFilter.h中

// ----------------------------------------------------------------------------
// Class definitions of CYUV2RGBfilter
// ----------------------------------------------------------------------------
//
//
class CYUV2RGBfilter : public CTransformFilter
{

    public:
        static CUnknown * WINAPI CreateInstance(LPUNKNOWN punk, HRESULT *phr);

        STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
  
        DECLARE_IUNKNOWN;
// override pure virtual function
        HRESULT CheckInputType(const CMediaType *mtIn);
        HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut);
        HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProp);
        HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
        HRESULT Transform(IMediaSample *pIn, IMediaSample *pOut);

    private:
        //Constructor
        CYUV2RGBfilter(TCHAR *tszName, LPUNKNOWN punk, HRESULT *phr);
   // member function
   VOID ChangeFormat(AM_MEDIA_TYPE* pAdjustedType);
   DWORD ConvertYUV2toRGB(BYTE* yuv, BYTE* rgb, DWORD dsize);

    // member variable
   const long m_lBufferRequest;
        CCritSec m_Y2RLock;     // To serialise access.
};

   3. 按格式改寫建構函式
        //
// CNullInPlace::Constructor
//
CYUV2RGBfilter::CYUV2RGBfilter(TCHAR *tszName,LPUNKNOWN punk,HRESULT *phr) :
    CTransformFilter(tszName, punk, CLSID_YUV2toRGB24),
    m_lBufferRequest(1)
{
ASSERT(tszName);
    ASSERT(phr);
   
} // CYUV2RGBfilter

   4. 改寫CTransformFilter五個純虛函數(最重要的地方)

        HRESULT CheckInputType(const CMediaType *mtIn);
        HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut);
        HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProp);
        HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
        HRESULT Transform(IMediaSample *pIn, IMediaSample *pOut);

   5. 設計自己的私人函數 完成一定的功能

   6. 註冊Filter資訊
      
// 註冊資訊

//setup data

const AMOVIESETUP_MEDIATYPE
sudPinTypes =   { &MEDIATYPE_Video                // clsMajorType
                , &MEDIASUBTYPE_NULL } ;       // clsMinorType

const AMOVIESETUP_PIN
psudPins[] = { { L"Input"            // strName
               , FALSE               // bRendered
               , FALSE               // bOutput
               , FALSE               // bZero
               , FALSE               // bMany
               , &CLSID_NULL         // clsConnectsToFilter
               , L"Output"           // strConnectsToPin
               , 1                   // nTypes
               , &sudPinTypes }      // lpTypes
             , { L"Output"           // strName
               , FALSE               // bRendered
               , TRUE                // bOutput
               , FALSE               // bZero
               , FALSE               // bMany
               , &CLSID_NULL         // clsConnectsToFilter
               , L"Input"            // strConnectsToPin
               , 1                   // nTypes
               , &sudPinTypes } };   // lpTypes

const AMOVIESETUP_FILTER
sudYUV2RGB = { &CLSID_YUV2toRGB24                 // clsID
            , L"YUV2RGB"                // strName
            , MERIT_DO_NOT_USE                // dwMerit
            , 2                               // nPins
            , psudPins };                     // lpPin

//
// Needed for the CreateInstance mechanism
//
CFactoryTemplate g_Templates[1]=
    {   {L"YUV2RGB"
        , &CLSID_YUV2toRGB24
        , CYUV2RGBfilter::CreateInstance
        , NULL
        , &sudYUV2RGB }
    };
int g_cTemplates = sizeof(g_Templates)/sizeof(g_Templates[0]);

編譯成功後產生GrayFilter.ax
命令列運行regsvr32 GrayFilter.ax註冊即可 不用反覆註冊,只用註冊一次,如若修改只需將重新編譯的.ax覆蓋原來的就行了
調試最好在graphEdit中經行 比較方便。

以上就是設計一個filter的總體步驟。

三 下面就關鍵點 五個重載的純虛函數做詳細介紹。 這才是最關鍵的地方。

        HRESULT CheckInputType(const CMediaType *mtIn);
        HRESULT CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut);
        HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProp);
        HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
        HRESULT Transform(IMediaSample *pIn, IMediaSample *pOut);
這五個函數全部是都純虛函數 ,是CTransformFilter為我們提供的介面,必須重載他們才能執行個體化。
初學者最大的困擾莫過於,是誰調用了這些函數。這些函數調用的時候實參是從哪來的。我一開始就被這些問題困擾。其實DX的協助文檔裡就講的很清楚了只是我一開始沒認真看;

CheckInputType是由tranformfiltr的輸入pin調用的用來檢查本Filter的輸入媒體是否合法;
CheckTransform是由tranformfiltr的輸出pin調用的用來檢查本filter的輸出是否和合法;
GetMediaType是有由tranformfiltr的輸出pin調用的用來擷取該輸出連接埠支援的媒體格式供下遊filter的枚舉
DecideBufferSize是由tranformfiltr的輸出pin調用的來確定buffer的數量和大小
上遊filter通過調用filter上輸入pin上的IMemInputPin::Receive方法,將sample傳遞到filter,filter調用CTransformFilter::Transform方法來處理資料

整個過程就是
   輸入pin調用CheckInputType來篩選上遊過來的媒體類型,如果可以接受 就有輸出pin通GetMediaType來枚舉輸出媒體類型,進一步通過輸出pin的CheckTransform來找到與輸入媒體類型相融合的輸出媒 體類型並選中。在通過DecideBufferSize確定輸出buffer的屬性,所有的檢查和篩選通過以後就可以串連了, 並通過tranform 將輸入pin上的sample 傳個輸出pin輸出媒體的類型是由GetMediaType來確定的, 只要媒體類型對應了就可以成功串連但是資料的傳送還是要通過transform來實現。理論上對於沒有壓縮的視頻, 一個sample就是一幀的資料,可以精確的量化處理。

要實現輸出pin上媒體格式的轉化 就必須在在GetMediaType函數中修改新的媒體格式,然後在checkTransform中確認 輸出的媒體格式是不是期望的輸出。例如 要將YUY2 16bit的媒體格式改為RGB8 8bit的媒體格式 就要做如下修改:

在GetMediaType中

CheckPointer(pMediaType,E_POINTER);
   VIDEOINFO   vih;  
   memset(&vih, 0, sizeof(vih));  
   vih.bmiHeader.biCompression   =   0;  
   vih.bmiHeader.biBitCount      =   8;  
   vih.bmiHeader.biSize          =   40;  
   vih.bmiHeader.biWidth         =   640;  
   vih.bmiHeader.biHeight        =   480;
   vih.bmiHeader.biPlanes        =   1;  
   vih.bmiHeader.biSizeImage     =   307200;  
   vih.bmiHeader.biClrImportant =   0;  
      vih.bmiHeader.biClrUsed   =   256;

//alter the pallete
   for (UINT i=0; i<256; i++)
   {
    vih.bmiColors[i].rgbBlue=(BYTE)i;
    vih.bmiColors[i].rgbRed=(BYTE)i;
    vih.bmiColors[i].rgbGreen=(BYTE)i;
    vih.bmiColors[i].rgbReserved=(BYTE)0;
    }
   pMediaType->SetType(&MEDIATYPE_Video);  
   pMediaType->SetFormatType(&FORMAT_VideoInfo);  
   pMediaType->SetFormat((BYTE*)&vih, sizeof(vih));  
   pMediaType->SetSubtype(&MEDIASUBTYPE_RGB8);  
   pMediaType->SetSampleSize(307200);

return NOERROR;

然後在checkTransform中確認是否是期望的輸出

   BITMAPINFOHEADER *pNewType = HEADER(mtOut->Format());

   if ((pNewType->biPlanes==1)
    &&(pNewType->biBitCount==8)
    &&(pNewType->biWidth==640)
    &&(pNewType->biHeight==480)
    &&(pNewType->biClrUsed==256)
    &&(pNewType->biSizeImage==307200))
   {
    return S_OK;
   }

我的實現過程如下

// GetMediaType
//
// I support one type, namely the type of the input pin
// We must be connected to support the single output type
//
HRESULT CYUV2RGBfilter::GetMediaType(int iPosition, CMediaType *pMediaType)
{
    // Is the input pin connected

    if(m_pInput->IsConnected() == FALSE)
    {
        return E_UNEXPECTED;
    }

    // This should never happen

    if(iPosition < 0)
    {
        return E_INVALIDARG;
    }

    // Do we have more items to offer

    if(iPosition > 0)
    {
        return VFW_S_NO_MORE_ITEMS;
    }

    CheckPointer(pMediaType,E_POINTER);

if (iPosition == 0)
{
   HRESULT hr = m_pInput->ConnectionMediaType(pMediaType);
   if (FAILED(hr))
   {
    return hr;
   }
}
// make some appropriate change
ASSERT(pMediaType->formattype == FORMAT_VideoInfo);
pMediaType->subtype = MEDIASUBTYPE_RGB24;
VIDEOINFOHEADER *pVih =
reinterpret_cast<VIDEOINFOHEADER*>(pMediaType->pbFormat);
pVih->bmiHeader.biCompression = 0;
pVih->bmiHeader.biSizeImage = DIBSIZE(pVih->bmiHeader);
pVih->bmiHeader.biBitCount = 24;
pVih->bmiHeader.biHeight = 480;
pVih->bmiHeader.biWidth = 640;
return S_OK;
} // GetMediaType

//
// CheckInputType
//
// Check the input type is OK, return an error otherwise
//
HRESULT CYUV2RGBfilter::CheckInputType(const CMediaType *mtIn)
{
    CheckPointer(mtIn,E_POINTER);
   
    // Check this is a VIDEOINFO type

    if(*mtIn->FormatType() != FORMAT_VideoInfo)
    {
        return E_INVALIDARG;
    }

    if((IsEqualGUID(*mtIn->Type(), MEDIATYPE_Video)) &&
       (IsEqualGUID(*mtIn->Subtype(), MEDIASUBTYPE_YUY2)))
    {
        VIDEOINFO *pvi = (VIDEOINFO *) mtIn->Format();
        if ((pvi->bmiHeader.biBitCount == 16)
    &&(pvi->bmiHeader.biCompression==0))
    return S_OK;
   else
    return FALSE;
    }
    else
    {
        return FALSE;
    }
} // CheckInputType

// CheckTransform
//
// To be able to transform the formats must be compatible
//mtIn YUV2 16bit
//mtOut RGB24 24bit
HRESULT CYUV2RGBfilter::CheckTransform(const CMediaType *mtIn, const CMediaType *mtOut)
{
    CheckPointer(mtIn,E_POINTER);
    CheckPointer(mtOut,E_POINTER);

    HRESULT hr;
    if(FAILED(hr = CheckInputType(mtIn)))
    {
        return hr;
    }
   
    // format must be a VIDEOINFOHEADER
    if((*mtOut->FormatType() != FORMAT_VideoInfo)
   ||(mtOut->cbFormat<sizeof(VIDEOINFOHEADER ))
   ||(mtOut->subtype!=MEDIASUBTYPE_RGB24))
    {
        return E_INVALIDARG;
    }
   BITMAPINFOHEADER *pBmiOut = HEADER(mtOut->pbFormat);
   if ((pBmiOut->biPlanes!=1)
    ||(pBmiOut->biBitCount!=24)
    ||(pBmiOut->biCompression!=0)
    ||(pBmiOut->biWidth!=640)
    ||(pBmiOut->biHeight!=480))
   {
    return E_INVALIDARG;
   }

   return S_OK;
}
// CheckTransform

HRESULT CYUV2RGBfilter::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProperties)
{
    CheckPointer(pAlloc,E_POINTER);
    CheckPointer(pProperties,E_POINTER);

    // Is the input pin connected

    if(m_pInput->IsConnected() == FALSE)
    {
        return E_UNEXPECTED;
    }

    HRESULT hr = NOERROR;
    pProperties->cBuffers = 1;
    pProperties->cbBuffer = m_pInput->CurrentMediaType().GetSampleSize()*2; //output is double of the input samples

    ASSERT(pProperties->cbBuffer);

    // If we don't have fixed sized samples we must guess some size

    if(!m_pInput->CurrentMediaType().bFixedSizeSamples)
    {
        if(pProperties->cbBuffer < 100000)
        {
            // nothing more than a guess!!
            pProperties->cbBuffer = 100000;
        }
    }

    // Ask the allocator to reserve us some sample memory, NOTE the function
    // can succeed (that is return NOERROR) but still not have allocated the
    // memory that we requested, so we must check we got whatever we wanted

    ALLOCATOR_PROPERTIES Actual;

    hr = pAlloc->SetProperties(pProperties,&Actual);
    if(FAILED(hr))
    {
        return hr;
    }

    ASSERT(Actual.cBuffers == 1);

    if(pProperties->cBuffers > Actual.cBuffers ||
        pProperties->cbBuffer > Actual.cbBuffer)
    {
        return E_FAIL;
    }

    return NOERROR;

} // DecideBufferSize

//
// Transform
//
// Copy the input sample into the output sample
//
//
HRESULT CYUV2RGBfilter::Transform(IMediaSample *pIn, IMediaSample *pOut)
{
    CheckPointer(pIn,E_POINTER);
    CheckPointer(pOut,E_POINTER);

    // Copy the sample data
    BYTE *pSourceBuffer, *pDestBuffer;
    long lSourceSize = pIn->GetActualDataLength();
    long lDestSize = (long)(lSourceSize*1.5);

    pIn->GetPointer(&pSourceBuffer);
    pOut->GetPointer(&pDestBuffer);
//change data
    ConvertYUV2toRGB(pSourceBuffer,pDestBuffer,lSourceSize);
//memset(pDestBuffer,100,lDestSize);
    REFERENCE_TIME TimeStart, TimeEnd;
    if(NOERROR == pIn->GetTime(&TimeStart, &TimeEnd))
    {
        pOut->SetTime(&TimeStart, &TimeEnd);
    }

    LONGLONG MediaStart, MediaEnd;
    if(pIn->GetMediaTime(&MediaStart,&MediaEnd) == NOERROR)
    {
        pOut->SetMediaTime(&MediaStart,&MediaEnd);
    }

    // Copy the Sync point property

    HRESULT hr = pIn->IsSyncPoint();
    if(hr == S_OK)
    {
        pOut->SetSyncPoint(TRUE);
    }
    else if(hr == S_FALSE)
    {
        pOut->SetSyncPoint(FALSE);
    }
    else
    { // an unexpected error has occured...
        return E_UNEXPECTED;
    }

//
    AM_MEDIA_TYPE* pMediaType;
    pIn->GetMediaType(&pMediaType);
ChangeFormat(pMediaType);
    // Copy the media type
    pOut->SetMediaType(pMediaType);

    // Copy the preroll property

    hr = pIn->IsPreroll();
    if(hr == S_OK)
    {
        pOut->SetPreroll(TRUE);
    }
    else if(hr == S_FALSE)
    {
        pOut->SetPreroll(FALSE);
    }
    else
    { // an unexpected error has occured...
        return E_UNEXPECTED;
    }

    // Copy the discontinuity property

    hr = pIn->IsDiscontinuity();

    if(hr == S_OK)
    {
        pOut->SetDiscontinuity(TRUE);
    }
    else if(hr == S_FALSE)
    {
        pOut->SetDiscontinuity(FALSE);
    }
    else
    { // an unexpected error has occured...
        return E_UNEXPECTED;
    }

    // Copy the actual data length
//KASSERT((long)lDestSize <= pOut->GetSize());
    pOut->SetActualDataLength(lDestSize);

    return S_OK;

} // Transform

經過這些步驟就能得到符合功能要求的transform filter
同時經過以上步驟也能對filter開發有個大體的瞭解

部分類容參考了http://tieba.baidu.com/f?kz=143218826

轉自:http://hi.baidu.com/gragonraja/blog/item/b5b6e182c848cc97f603a697.html

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.