在C#中用DirectShow做的媒體播放機
概述:
我的這個程式僅僅只是告訴大家如何用DirectShow 在C#中做一個播放機,世上並不能
有太多的功能.也許你只要花上五分種就可以解決問題.是的如果你用的是IDE,我感保證
一切都只是用你的滑鼠在你的設計器中點點屬性設定一些東西就可以簡單的完成了.當然
了還是要那麼一點點編碼的.至少是關於DirectShow 介面的.例如,視屏和聲音.
程式中的小問題:
1.如何從你的磁碟上開啟媒體檔案
2.如何讓工具條上的按鈕起用和禁用
3.如何設定狀態列的顯示文字
4.如何控制時間
5.如何使用時間控制項的事件
6.如何用DirectShow來播放媒體檔案
7.如何確定播放狀態等等...
...
使用者介面如:
很簡單是嗎?是的,我說了是個簡單的程式,我只是讓它具備了準系統而已.工具列上
的三個button控制播放,停止和暫停.一個檔案菜單用來開啟媒體檔案和關閉程式.當然
還有一個介紹程式的Info毫無疑問這是最基本的介面配置.
下面介紹DirectShow 介面
播放視屏和音效檔我們要用到DiectX為我們提供的DirectShow組件.使用這個介面
可以讓你方便的播放那些共用的影像和音效檔.你要做的僅僅只是安裝DirectShow介面
和使用它的功能函數和配置正確的介面參數而已.
不幸的是.NET並不正式支援DirectX.是的也許你聽說DirectX9支援是嗎?是的,不過在
最終版敲定的那一天還沒來,我們都得不到最好的效果.但無論如何我們還是要用的不是嗎?
要不這篇文章得作廢了.是的,也許你用過VB,對了,就是它,我們正是要用到那個.
好了,在此之前我們還必須要做件事情.我想你已經猜到了,引用對嗎?還記得XCopy嗎?是的
.NET的優勢.來吧,快點把這個"Interop.QuartzTypeLib.dll"DLL引用進來,就象這樣
看見下面的圖示了嗎?很簡單.
最後別忘了看看你的代碼中是否有那麼一句
using QuartzTypeLib;
有是嗎?編譯器為你加的,如果沒有自己加上好了.
準備工作結束了,該是代碼部分了,這可是程式員無法推卸的責任.否則我們都得下崗了.
程式實現部分:
如何開啟你想要媒體檔案?
還記得嗎?"File -> Open..." 是的幾乎每個使用windows的人都會這樣操作.如何??
很簡單看看下面的代碼:
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "Media Files|*.mpg;*.avi;*.wma;*.mov;*.wav;*.mp2;*.mp3|All Files|*.*";
if (DialogResult.OK == openFileDialog.ShowDialog())
{
.
.
.
看吧很簡單是嗎?記得寫一個函數把它放進去.當你點擊OK按鈕的時候.DirectShow介面就會
得到你想要播放的檔案.
看下面的圖你就知道它是如何工作的.
DirectShow為多媒體流回放提供最基本的服務,這些多媒體流可以是本地檔案,還可以是伺服器傳輸過來的。特別的,DirectShow可以可使用視訊回放,支援以不同的檔案和流格式壓縮視頻內容,包括Windows Media、MPEG、AVI和WAV。
在DirectShow的核心處,服務是組件的模組化集合,稱為過濾器,可以根據媒體類型排列成過濾器圖。過濾器可以操作資料流,如讀入、分析、解碼、格式化或渲染。
過濾器以樹型進行排列,這棵樹稱為過濾器樹,通過過濾器樹管理器(Filter Graph Manager,簡稱FGM)進行管理。使用FGM應用程式可以通過使用Microsoft Windows Media Player控制項間接控制過濾器樹,還可以通過調用COM介面方法直接控制。DirectShow過濾器樹(參閱圖1)由從源到目標渲染器的有向過濾器序列組成,所有這些通過輸入和輸出過濾器引腳串連。過濾器引腳協商它們將支援哪些媒體類型。FGM控制樹過濾器之間的多媒體資料流。因為DirectShow有一個靈活的、可重配置的過濾器樹體繫結構,因此DirectShow可以使用同樣的軟體成分支援多種媒體類型的回放和分流。開發人員還可以通過編寫自己的過濾器擴充DirectShow多媒體支援。
圖1. DirectShow過濾器樹
過濾器
過濾器是註冊的DirectShow類,它執行許多媒體資訊處理任務。這些任務包括:
獲得源資訊(例如,獲得媒體流)
分析(例如,在流上執行包讀入、分離和格式化)
轉換(例如,解碼WMA和MPEG-4音頻和視頻流)
渲染(例如,在適當的時候產生音頻PCM或者視頻RGB/YUV輸出,將資料傳給DirectSound和DirectDraw)
過濾器使用幾種類型的介面,例如引腳、計數器、傳送器和時鐘介面,來執行它們的任務。過濾器實現和開放了許多介面。FGM可以使用這些介面建立、串連和控制樹。過濾器經常實現包含下列方法的IBaseFilter介面:
運行、停止和暫停過濾器狀態。
恢複過濾器和廠商資訊。
得到和設定參考時鐘。
恢複過濾器狀態資訊。
枚舉過濾器引線。
重建過濾器樹時定位引腳
好了介紹了這麼多,你的手也許已經閑不住了.看看下面的代碼是如何?的
CleanUp();
m_objFilterGraph = new FilgraphManager();
m_objFilterGraph.RenderFile(openFileDialog.FileName);
m_objBasicAudio = m_objFilterGraph as IBasicAudio;
try
{
m_objVideoWindow = m_objFilterGraph as IVideoWindow;
m_objVideoWindow.Owner = (int) panel1.Handle;
m_objVideoWindow.WindowStyle = WS_CHILD | WS_CLIPCHILDREN;
m_objVideoWindow.SetWindowPosition(panel1.ClientRectangle.Left,
panel1.ClientRectangle.Top,
panel1.ClientRectangle.Width,
panel1.ClientRectangle.Height);
}
catch (Exception ex)
{
m_objVideoWindow = null;
}
m_objMediaEvent = m_objFilterGraph as IMediaEvent;
m_objMediaEventEx = m_objFilterGraph as IMediaEventEx;
m_objMediaEventEx.SetNotifyWindow((int) this.Handle, WM_GRAPHNOTIFY, 0);
m_objMediaPosition = m_objFilterGraph as IMediaPosition;
m_objMediaControl = m_objFilterGraph as IMediaControl;
//
如何來播放,暫停,停止?
簡單這些函數看字面也知道.
m_objMediaControl.Run();//播放
m_objMediaControl.Pause();//暫停
m_objMediaControl.Stop();//停止
OK,在來看我們是如何控制時間進度的?
//
private void timer1_Tick(object sender, System.EventArgs e)
{
if (m_CurrentStatus == MediaStatus.Running)
{
UpdateStatusBar();
}
}
看見上面那個 UpdateStatusBar();這裡是讓它沒100ms更新一次狀態列.
代碼如下:
private void UpdateStatusBar()
{
switch (m_CurrentStatus)
{
case MediaStatus.None : statusBarPanel1.Text = "Stopped"; break;
case MediaStatus.Paused : statusBarPanel1.Text = "Paused "; break;
case MediaStatus.Running: statusBarPanel1.Text = "Running"; break;
case MediaStatus.Stopped: statusBarPanel1.Text = "Stopped"; break;
}
if (m_objMediaPosition != null)
{
int s = (int) m_objMediaPosition.Duration;
int h = s / 3600;
int m = (s - (h * 3600)) / 60;
s = s - (h * 3600 + m * 60);
statusBarPanel2.Text = String.Format("{0:D2}:{1:D2}:{2:D2}", h, m, s);
s = (int) m_objMediaPosition.CurrentPosition;
h = s / 3600;
m = (s - (h * 3600)) / 60;
s = s - (h * 3600 + m * 60);
statusBarPanel3.Text = String.Format("{0:D2}:{1:D2}:{2:D2}", h, m, s);
}
else
{
statusBarPanel2.Text = "00:00:00";
statusBarPanel3.Text = "00:00:00";
}
}
還有一個問題程式怎麼能夠知道它播放完了????
這會有點麻煩了.好了,想想看有什麼辦法呢?對了,windows是訊息驅動的.那找找看有什麼訊息
好了.有的就EC_COMPLETE 這個.還記得"WndProc" 它嗎??是的,我的老朋友.這次我們必須
要改寫它來捕獲EC_COMPLETE訊息.這個訊息是DirectShow通知父表單,播放結束了.
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_GRAPHNOTIFY)
{
int lEventCode;
int lParam1, lParam2;
while (true)
{
try
{
m_objMediaEventEx.GetEvent(out lEventCode,
out lParam1,
out lParam2,
0);
m_objMediaEventEx.FreeEventParams(lEventCode, lParam1, lParam2);
if (lEventCode == EC_COMPLETE)
{
m_objMediaControl.Stop();
m_objMediaPosition.CurrentPosition = 0;
m_CurrentStatus = MediaStatus.Stopped;
UpdateStatusBar();
UpdateToolBar();
}
}
catch (Exception)
{
break;
}
}
}
base.WndProc(ref m);
}
好了,一切都結束了,現在要做的事就是做些來找一部影片來享受一下自己的成果了.