1. 程式人生 > >關於新手 DirectShow寫source filter的具體流程

關於新手 DirectShow寫source filter的具體流程

對於vc中DirectShow開發環境的配置,這裡不做講解。下面開始:

(vc 6.0 + DirectShow 9.0)
我也記得剛學時候的迷茫,所以會盡量詳細每個過程,所以很多是sdk的例子我沒改動它,沒講的是我提供的原始碼裡面我加有比較詳細的註釋,可以配合我提供的原始碼一起看。


第一步:建立工程

File->New->Project選擇Win32 Dynamic-Link Library,(由於是個demo,名字我用的Push_Test_01)->Next後選

擇A simple DLL project(這裡為了避免自己寫DllMain的麻煩,所以沒選An empty DLL project)->可以Finish了到這裡工程建立結束。



第二步:相關設定和需要加入的檔案等操作



首先將Debug方式改為Release。接著Project->Seetings->Link裡的Output file name從Release/Push_Test_01.dll改為Release/Push_Test_01.ax。

在工程目錄下建立一個文字檔案,修改名字為Push_Test_01.def。將其加入工程:Project->Add to project->Files 選擇Push_Test_01.def後加入。對Push_Test_01.def進行修改,FileView->Source Files 雙擊Push_Test_01.def後輸入:

LIBRARY     Push_Test_01.ax

EXPORTS
            DllMain                            PRIVATE
            DllGetClassObject        PRIVATE
            DllCanUnloadNow       PRIVATE
            DllRegisterServer         PRIVATE
            DllUnregisterServer     PRIVATE


確定project->Seetings->link下Object/library modules裡面為:
strmbase.lib msvcrt.lib quartz.lib vfw32.lib winmm.lib kernel32.lib advapi32.lib version.lib 
largeint.lib user32.lib gdi32.lib comctl32.lib ole32.lib olepro32.lib oleaut32.lib uuid.lib


新增標頭檔案:
#include <streams.h>
#include <olectl.h>
#include <initguid.h>


生成全球唯一標識(可以使用cmd命令:guidgen.exe)

DEFINE_GUID(CLSID_PushTest,
   0xfd501041, 0x8ebe, 0x11ce, 0x81, 0x83, 0x00, 0xaa, 0x00, 0x57, 0x7d, 0xa1);


第三步:註冊等函式的新增

首先修改入口函式,並添加註冊和反註冊函式,操作後的內容如下:

//註冊
STDAPI DllRegisterServer()
{
    return AMovieDllRegisterServer2(TRUE);


}

//反註冊
STDAPI DllUnregisterServer()
{
    return AMovieDllRegisterServer2(FALSE);


}


//filter的入口函式
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);


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


此時編譯會有class CFactoryTemplate沒實現等錯誤,下面我們來實現它。


新增下面的程式碼,每個地方我基本都加了大體意思的註釋:

/**************開始填寫註冊資訊***************/

//媒體型別
const AMOVIESETUP_MEDIATYPE sudOpPinTypes =
{
    &MEDIATYPE_Video,             // Major type 主型別
    &MEDIASUBTYPE_NULL      // Minor type sub型別,可以為MEDIASUBTYPE_NULL
};


//pin的資訊
const AMOVIESETUP_PIN sudOpPin =
{
   L"My OutPutPin",                // Pin string name      pin的名字
   FALSE,                  // Is it rendered       輸入pin有用,輸出pin一般為FALSE
   TRUE,                   // Is it an output      TRUE表示是輸出pin,不然是輸入pin
   FALSE,                  // Can we have none   是否能不例項化
   FALSE,                  // Can we have many   是否能建立多個同這樣型別的pin
   &CLSID_NULL,   // Connects to filter 連線的filter類
   NULL,                   // Connects to pin      該pin要連線的pin的類
   1,                            // Number of types   該pin支援的媒體型別
   &sudOpPinTypes          // Pin details    該pin的媒體型別的描述


};
const AMOVIESETUP_FILTER sudBallax =
{
    &CLSID_PushTest,    // Filter CLSID    該filter的類標誌
   L"Push_Test",              // String name    該filter的名字
   MERIT_DO_NOT_USE,       // Filter merit    該filter的Merit值
   1,                                              // Number pins    該filter的pin的數目
   &sudOpPin                            // Pin details    該filter的pin的描述
};


//建立例項時用,有類,名字等需要的資訊

CFactoryTemplate g_Templates[] = 

{


     L"Push_Test"                  //filter的名字
   , &CLSID_PushTest       //物件的類標識  
    , PushTestFilter::CreateInstance    //建立一個例項用的函式
    , NULL                                                   //
    , &sudBallax                                         //filter的註冊資訊
}
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);


通過上面的註釋,我們看到該filter有一個輸出pin,支援Video型別等等資訊。
這裡主要對PushTestFilter::CreateInstance //建立一個例項用的函式
說明一下!!PushTestFilter就是我們的filter類!!在下面實現它。


第四步:filter類的實現


新增新類PushTestFilter,使其繼承自CSource。這就是我們的filter類,在這個類裡面沒有過多的操作,就只


有2個函式而已:


//filter的主類,繼承自CSource
class PushTestFilter : public CSource 
{
public:
     // 唯一能建立該類例項的介面
    static CUnknown * WINAPI CreateInstance(LPUNKNOWN lpunk, HRESULT *phr);


private:
    //只能通過CreateInstance()的呼叫建立例項
    PushTestFilter(LPUNKNOWN lpunk, HRESULT *phr);
};


這裡有2點需要注意:
    *  建構函式PushTestFilter()是private的,不是一般的public!
    *  CreateInstance()函式是static的,因為它不能通過物件來呼叫!


2個函式的具體實現如下:

//建構函式,注意這裡是private屬性的,不是public,
//所以要建立它的例項,只能是通過CreateInstance()函式的方式
PushTestFilter::PushTestFilter(LPUNKNOWN lpunk, HRESULT *phr):
    CSource(NAME("PushTest"), lpunk, CLSID_PushTest)
{
ASSERT(phr);

CAutoLock cAutoLock(&m_cStateLock);

//m_paStreams是從CSource基類繼承來的指標陣列。由於這個demo我們只有1個pin,所以分配了1個空間
m_paStreams = (CSourceStream **) new PushTesiPin*[1];
if(m_paStreams == NULL)
{
   if(phr)
    *phr = E_OUTOFMEMORY;
  
   return;

}

//為剛分配的那個空間付值,這就自動給filter加入了一個pin,析構的時候會自動釋放
m_paStreams[0] = new PushTesiPin(phr,this,L"Push_Test");
if(m_paStreams[0] == NULL)
{
   if(phr)
    *phr = E_OUTOFMEMORY;
  
   return;
}
}


//CreateInstance()該函式是static屬性的,因為不能通過物件來呼叫
CUnknown * WINAPI PushTestFilter::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr)
{
ASSERT(phr);
//這裡呼叫了private屬性的建構函式
CUnknown *punk = new PushTestFilter(lpunk, phr);
if(punk == NULL)
{
   if(phr)
    *phr = E_OUTOFMEMORY;
}
return punk;   
}

這裡的類PushTesiPin就是我們的pin類,在後面要實現!!其實主要的操作是在pin類PushTesiPin裡面的。


第五步:pin類的實現


新增類PushTesiPin,使其繼承自CSourceStream。這裡需要過載的函式會多一點!不過沒關係!我會一個一個的進行說明。


主要是這3個:

//由於我們的filter就一種媒體型別,所以過載了GetMediaType(CMediaType *pMediaType),如果有多種型別,就應該過載另外2個函數了,具體參考基類CSourceStream
HRESULT GetMediaType(CMediaType *pMediaType);

//這個函式是用來設定Sample大小的,在pin連線成功後會被呼叫
HRESULT DecideBufferSize(IMemAllocator *pIMemAlloc,ALLOCATOR_PROPERTIES *pProperties);

//對Sample資料的填充
HRESULT FillBuffer(IMediaSample *pms);


其實這個filter沒做別的,就相當與將sdk下的PushSource例子自己再寫了一遍,主要是為了說明這個過程,到這裡相信你應該有個大概的概念了,那麼恭喜!