033 DirectShow之介面實戰篇

Post date: 2015/5/5 上午 06:25:55

現今自己編程做一個多媒體播放工具是一件很令人開心愉悅的事情,但如果使用MediaPlay控制項開發則會受到很多限制,自己的很多好的創意想法都無法 或者很難實現,如果利用微軟的DirectX介面開發則可以充分的將作者的獨特想法付諸於實現,何樂而不為呢!!不過關於DirectShow介面的開發 說明文檔實在是少之又少,僅有的一些不是英文的就是一些關於理論方面的,真正關於介面實戰編程而且是用Delphi開發工具實現的更是鳳毛麟角,使很多人 都望而卻步。在這裏,我把我應用Directshow開發的心得以及我搜集到一些資料重新整理編輯出來公佈,希望對所有由此興趣的同仁有所幫助,就算達到 了我的目的。廢話少說,進入正文。

既然是介面實戰篇,就先把一些常用的介面列出來,讓大家有一些基本的認識,都是用來做什麼的,什麼時候我們會需要用到此介面。

IFilterGraph : 過濾通道介面

IFilterGraph2 : 增強的IFilterGraph

IGraphBuilder : 最為重用的COM介面,用於手動或者自動構造過濾通道Filter Graph Manager

IMediaControl : 用來控制流媒體,例如流的啟動和停止暫停等,播放控制介面

IMediaEvent : 播放事件介面 ,該介面在Filter Graph發生一些事件時用來創建事件的標誌資訊

並傳送給應用程式

IMediaEventEx : 擴展播放事件介面

IMediaPosition : 播放的位置和速度控制介面(控制播放位置只能為設置時間控制方式)

IMediaSeeking : 另一個播放的位置和播放速度控制介面,在位置選擇方面功能較強.設置播放格式,

多種控制播放方式.

常用的有:(1)TIME_FORMAT_MEDIA_TIME單位100納秒。

(2)TIME_FORMAT_FRAME按幀播放

IBasicAudio : 聲音控制介面

IBasicVideo : 圖像控制介面(串列傳輸速率,寬度,長度等資訊)

IVideoWindow: 顯示視窗控制介面 (有關播放視窗的一切控制,包括caption顯示,視窗位置控制等)

ISampleGrabber: 捕獲圖像介面(可用於抓圖控制)

IVideoFrameStep :控制單幀播放的介面

好了,熟悉了應用DirectShow應用開發常用的介面後,我們就通過一個實例媒體播放器來熟悉掌握這些介面,實例的代碼雖然簡單,但五臟俱全,功能強大,同時也瞭解一下應用DirectShow開發一般常用的步驟。

大體說來,一般使用DirectShow介面編程無非3個步驟,初始化介面,利用介面中的控制函數使用控制操作,最後釋放介面。(當然這裏假定你已經擁有 了directshow.pas等必須單元,如果沒有的話請在網上查找或者向我索要)(注:以下變數沒有定義,需自己定義使用)

(1) 初始化介面部分

首先,需要定義需要使用的介面變數

GraphBuilder: IGraphBuilder;

MediaControl: IMediaControl;

MediaSeeking: IMediaSeeking;

MediaPosition: IMediaPosition;

MediaEventEx: IMediaEvent;

BasicAudio: IBasicAudio;

BasicVideo: IBasicVideo;

VideoWindow: IVideoWindow;

SampleGrabber: ISampleGrabber;

VideoFrameStep: IVideoFrameStep;

(1)然後需要使用CoCreateInstance函數創建一個Filter Graph Manager 實例,CoCreateInstance(TGUID(CLSID_FilterGraph),nil, CLSCTX_INPROC_SERVER,

TGUID(IID_IGraphBuilder),GraphBuilder)

因為需要抓圖使用IsampleGrabber介面,需要創建SampleGrabber實例,

var Filter: IBaseFilter;

CoCreateInstance(CLSID_SampleGrabber, nil, CLSCTX_INPROC_SERVER,

IID_IBaseFilter, Filter);

(2) 調用QueryInterface函數獲取來獲取指標,好以後操作控制

Filter.QueryInterface(IID_ISampleGrabber, SampleGrabber);

GraphBuilder.AddFilter(Filter, 'Grabber');

GraphBuilder.QueryInterface(IID_IMediaControl, MediaControl);

GraphBuilder.QueryInterface(IID_IMediaPosition, MediaPosition);

GraphBuilder.QueryInterface(IID_IMediaSeeking, MediaSeeking);

GraphBuilder.QueryInterface(IID_IMediaEventEx, MediaEventEx);

GraphBuilder.QueryInterface(IID_IVideoFrameStep, VideoFrameStep);

GraphBuilder.QueryInterface(IID_IBasicAudio, BasicAudio);

GraphBuilder.QueryInterface(IID_IBasicVideo, BasicVideo);

GraphBuilder.QueryInterface(IID_IVideoWindow, VideoWindow);

當然為了安全起見,可以對以上每個過程進行是否成功判斷,給出資訊,否則很有可能出現問題找不到頭緒。好了,一切準備成功,就可以進入第三步,開始我們的控制操作了。

(3)通過介面提供的函數開始控制

哦,差點忘記一件重要的事情,在上面調用QueryInterface之前,還有兩件重要的事情要做,第一,要建立一個Unicode(wide character)字串,保存檔案名。

var _wfile: array[0..MAX_PATH - 1] of wchar;

MultiByteToWideChar(CP_ACP, 0, pChar(播放檔案名), -1, @_wfile, MAX_PATH);

然後需要成功RenderFile才可以控制操作GraphBuilder.RenderFile(_wfile, nil);

當然在顯示的時候要把顯示表單和控制項關連起來,這裏需要通過IvideoWindow介面方法,VideoWindow. put_Owner(Edit1.Handle);

VideoWindow. put_WindowStyle(DSVIDEO_WINDOW_CHILD_STYLE);

VideoWindow.SetWindowPosition(0,0, Edit1.ClientWidth, Edit1.ClientHeight);

得到圖像的一些必要資訊,使用IbasicVideo介面中的方法,一些變數自己定義,

BasicVideo.GetVideoSize(VideoWidth, VideoHeight);

BasicVideo.get_BitRate(VideoBitRate);

BasicVideo.get_AvgTimePerFrame(PerFrame);

得到當前檔的總時間以及播放時間,需要使用ImediaSeeking介面方法,

MediaSeeking.GetDuration(Duration);//得到總時間

MediaSeeking.GetCurrentPosition(CurrentPos);//得到當前播放時間

也可以通過IMediaSeeking::SetPositions方法設置開始和結束時間。

哦,這裏得到的單位好像是毫米級的,可以自己轉化為秒級的.

還有,如果想以後能夠單幀控制播放,在這裏也需要設定播放格式為按幀播放。

MediaSeeking.SetTimeFormat(Time_Format_Frame);

播放,停止,暫停等控制

這些需要使用ImediaControl介面的方法,控制起來很簡單,分別為

MediaControl.Play;

MediaControl.Stop;

MediaControl.Pause;

播放速度的設定

需要使用ImediaPosition的方法。

MediaPosition.put_Rate(1);//正常

MediaPosition.put_Rate(0.25);//慢速

MediaPosition.put_Rate(2);//快速

單幀播放控制

需要使用IvideoFrameStep的方法

VideoFrameStep.Step(1, nil);

音量控制

需要使用IbasicAudio的方法

增加音量:

BasicAudio.get_Volume (&volume);//得到音量

volume:= volume +volumestep;

BasicAudio.put_Volume (volume);//增加一定的音量的分貝

減小音量:

BasicAudio.get_Volume (&volume); //得到音量

volume:= volume -volumestep;

BasicAudio.putVolume (volume); //減小一定音量的分貝

顯示放大縮小控制

只需改變Edit1的大小,然後使用IvideoWindow介面方法即可

VideoWindow.SetWindowPosition(0, 0, Edit1.Width, Edit1.Height);

單幀捕獲,抓圖

其實很多介面都提供了此功能,但是我更傾向於使用IsampleGrabber介面來實現,相對來說,效率高些。

這個控制起來做的工作稍微多些,首先,在打開文件的時候

var MediaType: TAM_MEDIA_TYPE;

ZeroMemory(@MediaType, SizeOf(TAM_MEDIA_TYPE));

MediaType.majortype := MEDIATYPE_Video;//視頻流

MediaType.subtype := MEDIASUBTYPE_RGB24;//24位圖像

MediaType.formattype := FORMAT_VideoInfo;

SampleGrabber.SetMediaType(MediaType);//關聯介面

SampleGrabber.SetBufferSamples(True);

然後在抓圖按鈕事件中如下操作

var

MediaType: TAM_MEDIA_TYPE;

VideoInfoHeader: TVideoInfoHeader;

BitmapInfo: TBitmapInfo;

Bitmap: HBitmap;

Buffer: Pointer;

BufferSize: Integer;

begin

SampleGrabber.GetConnectedMediaType(MediaType);

ZeroMemory(@VideoInfoHeader, SizeOf(TVideoInfoHeader));

CopyMemory(@VideoInfoHeader, MediaType.pbFormat, SizeOf(VideoInfoHeader));

ZeroMemory(@BitmapInfo, SizeOf(TBitmapInfo));

CopyMemory(@BitmapInfo, @VideoInfoHeader.bmiHeader, SizeOf(VideoInfoHeader.bmiHeader));

Bitmap:=CreateDIBSection(0, BitmapInfo, DIB_RGB_COLORS, Buffer, 0, 0);

SampleGrabber.GetCurrentBuffer(BufferSize, Buffer);

Image1.Picture.Bitmap.Handle:=Bitmap

end;

即可。

在 這裏,先總結這麼多,希望對大家有所幫助,這些只是DirectX的一個皮毛,它可以實現的功能十分強大,我也只是把我在實際中的遇到的問題總結出來供大 家參考,後面的工作還很多,我想我會逐步的更深入的總結這方面的經驗發表出來與大家分享,好了,DirectShow介面施展篇到這裏該完結了,如果大家 有補充或者想法,請發表出來以便我總結整理,謝謝大家。