包裝ffmpeg中的codecs成為DirectShow中的transform filter

来源:互联网 发布:广州数据分析培训机构 编辑:程序博客网 时间:2024/05/17 21:55

ffmpeg是個優秀的開放原始碼專案,其中提供了許多常見、不常見的video/audiocodecs。你也可以輕易的找到它的Win32 build。透過ffmpeg,我們可以很容易的解決video/audio的編解問題。不過,在Windows上,許多人是基於DirectShow來做video/audio的處理,例如撥放或轉換格式。不過,ffmpeg並沒有提供與DirectShow整合的方式及介面,為此,有另一個同享大名的專案 ffdshow,則提供了DirectShowfilters,使得應用DirectShow的程式員,可以更容易的利用許多開放原始碼的codecs於自己的開發之中。


不過,我在最近的應用中,發覺得使用ffdshow的一些問題。許多使用者的電腦上可能已經安裝了ffdshow,但使用者的版本以及所設定的選項,未必和你所預期的一致。另外,有一個發現是,倘若你略去系統已安裝的ffdshow.axffdshow filtersActiveX),直接載入自己所distributeffdshow.ax,也有可能導致一些不正確的結果(原因我還不清楚,但產生的結果就是filterspins無法連接)。再者,由於ffdshow有點龐大,有時候在特定的應用中,我們並不需要威力如此強大的filters。最後,ffdshowGPL,也許也不符合某些應用下的版權限制條件。


所以,最後我把腦筋動到ffmpeg去,它同樣也是ffdshow所應用到的專案之一,而且是LGPL。如果可以基於ffmpeg,為它設計出包裝用的DirectShow filter的話,就可以讓自己的DirectShow應用程式善用ffmpeg提供的資源。


所以,以下以FLV1YouTube影片所用的格式)的decoder為例,說明如何基於ffmpeg,做出一個DirectShowtransform filter


程式碼可於此處下載取得。要編譯它,你得先裝上Directshow SDK以及ffmpegavcodec.libavformat.lib


首先,你得先弄出個典型transform filter的殼。在這邊我利用一個叫做DirectShow Transform Filter
AppWizard
VC Wizard來為我產生一個叫做FLVDecodertransform filter


再來就是要把這個幾乎是殼的filter加上我們想要的東西。首先是更動input/output pinsmedia type 參考FLVDecoder原始碼中的FLVDecoder.cppsudPinTypessudPinTypes2的宣告。我們限制輸入pinMEDIATYPE_Video/CLSID_FLV1,而輸出pinMEDIATYPE_Video/
MEDIASUBTYPE_RGB32
。另外,修改psudPins中對於輸出pinmedia type,將其指向sudPinTypes2


在修改完pin腳的宣告後,便要設定filter本身對於輸入型別及輸出型別的檢查,為此我們為修CFLVDecoder:: canPerformTransform
()
CFLVDecoder::CheckTransform(),分別檢查所欲檢查的media type是否為MEDIATYPE_Video/CLSID_FLV1MEDIASUBTYPE_RGB32


為了便於說明起見,本例中的程式碼,hardcode320x200的解析度。


接著,我們修改CFLVDecoder::DecideBufferSize(),以便通知下游的filter,我們傳送過去的media sample究竟有多大,由於是RGB32sample,所以大小也正好是寬乘上高再乘上4


CFLVDecoder::GetMediaType()也會需要做調整,以便讓下游的filter能夠知道CFLVDecoderoutput pinmedia sample確切的特性。


Wizard產生出來的CFLVDecoder::Transform(IMediaSample
*pOut)
被我們完全忽略掉了。我們接下來,會直接修改CFLVDecoder::Transform(IMediaSample *pIn, IMediaSample
*pOut)
,並在CFLVDecoder::copyMediaSample()中實作解碼的部份。


完成以上的動作,基本上就是把Wizard產生出來的殼修改成我們想要的面貌。接下來的動作,便是整合ffmpeg的部份。


我把整合ffmpeg的部份都放到ffmpegext.cpp裡去了,其中只有兩個小函式。第一個是初始化用的ffmpegInit(),另一個則是進行實際解碼動作的ffmpegDecode()


CFLVDecoer如何整合它們呢?首先在CFLVDecoder::CreateInstance()中,也就是filterinstance被產生時,我們得呼叫ffmpegInit()來進行相關的初始化動作。接著,在CFLVDecoder::copyMediaSample()中,原先Wizard只是利用CopyMemory(),來將來源sample的資料複製至目的sample去,我們註解掉這行來取消這個行為。接著換上:


ffmpegDecode(pSourceBuffer,
lSourceSize, pDestBuffer);


如此將來源sample的資料交由ffmpeg解碼,並將解碼後的結果置於目的samplebuffer中。如此一來,便大功告成了。我們可以build出一個叫做FLVDecoder.axfilter,在註冊之後,打開graphedit.exe拉出以下的graph




其中用到一個叫做FLVSplitterfilter,可以在此處下載。值得注意的是,這個開放原始碼的專案中也有附一個FLV Decoder,不過,它可是不能解YouTube所用的FLV1


按下graphedit.exe的撥放鈕後,就可以欣賞到名揚國際的扯鈴高手的精采表演了:




從頭到尾,我們還寫不到100行程式碼。


有機會,我們再來介紹如何利用ffmpegFLV Splitter也拆掉吧。

原创粉丝点击