概述:
我的這個程序僅僅只是告訴大家如何用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);
}
好了,一切都結束了,現在要做的事就是做些來找一部影片來享受一下自己的成果了.