DirectShow

要使 C# 代碼引用 COM 對象和接口,需要在 C# 內部版本中包含 COM 接口的 .NET 框架定義。完成此操作的最簡單方法是使用 TlbImp.exe(類型庫導入程序),它是一個包括在 .NET 框架 SDK 中的命令行工具。TlbImp 將 COM 類型庫轉換爲 .NET 框架元數據,從而有效地創建一個可以從任何託管語言調用的託管包裝。用 TlbImp 創建的 .NET 框架元數據可以通過 /R 編譯器選項包括在 C# 內部版本中。如果使用 Visual Studio 開發環境,則只需添加對 COM 類型庫的引用,將爲您自動完成此轉換。
例如,我們要播放當前目錄下的demo.avi文件,需要用到包含在位於 Windows 系統目錄中的 Quartz.dll 中的媒體播放機。(c:\winnt\system32\quartz.dll)。可在命令行中運行TlbImp文件(D:\ Microsoft Visual Studio .NET\FrameworkSDK\Bin\Tlbimp.exe)
tlbimp c:\winnt\system32\quartz.dll /out:QuartzTypeLib.dll
請注意,得到的 DLL 需要命名爲 QuartzTypeLib,以便 .NET 框架可以在運行時正確加載包含類型。
生成程序時使用 C# 編譯器選項 /R 以包含 QuartzTypeLib.dll 文件;如果使用 Visual Studio 開發環境,直接添加引用即可(using QuartzTypeLib)。
然後就可以使用此程序顯示影片了。
具體編寫代碼時,用到了RenderFile 和 Run 方法。例:
private void menuItemOpen_Click(object sender, System.EventArgs e)
{
FilgraphManager m_FilGraphManager = null;
IBasicAudio m_BasicAudio = null;
IVideoWindow m_VideoWindow = null;
IMediaEvent m_MediaEvent = null;
IMediaEventEx m_MediaEventEx = null;
IMediaPosition m_MediaPosition = null;
IMediaControl m_MediaControl = null;
OpenFileDialog OpenDialog = new OpenFileDialog();
OpenDialog.Filter = "Media Files|*.mpg;*.avi;*.wma;*.mov;*.wav;*.mp2;*.mp3|All Files|*.*";                //本例用對話框讀入要顯示的影片文件名
if (DialogResult.OK == OpenDialog.ShowDialog())
{
m_FilGraphManager = new FilgraphManager();              
m_FilGraphManager.RenderFile(OpenDialog.FileName);
m_BasicAudio = m_FilGraphManager as IBasicAudio ;
try
{
m_VideoWindow = m_FilGraphManager as IVideoWindow;
m_VideoWindow.Owner = (int) panel1.Handle;
m_VideoWindow.WindowStyle = WS_CHILD | WS_CLIPCHILDREN;
//此設置可以不顯示播放器的title,使播放器像嵌在窗體中。
//可設置 private const int WS_CHILD = 0x40000000;
//      private const int WS_CLIPCHILDREN = 0x2000000;
m_VideoWindow.SetWindowPosition(panel1.ClientRectangle.Left,
panel1.ClientRectangle.Top,
panel1.ClientRectangle.Width,
panel1.ClientRectangle.Height);
// 在panel1中顯示,要求影片可隨panel1大小而變化。
}
catch (Exception)
{
m_VideoWindow = null;
}
m_MediaEvent = m_FilGraphManager as IMediaEvent;
m_MediaEventEx = m_FilGraphManager as IMediaEventEx;
m_MediaEventEx.SetNotifyWindow((int) this.Handle,WM_GRAPHNOTIFY, 0);
m_MediaPosition = m_FilGraphManager as IMediaPosition;
m_MediaControl = m_FilGraphManager as IMediaControl;
this.Text = "DirectShow - [" + OpenDialog.FileName + "]";
m_MediaControl.Run();
}
}
也可以加入pause,stop命令來控制影片的播放。
m_MediaControl.Pause()
m_MediaControl.Stop()
摘要:瞭解如何在 Microsoft Visual C# .NET 中使用 DirectShow 控件,如何開發一個媒體播放器。按照本文介紹的操作步驟,您可以創建一個簡單 Visual C# 應用程序,用來播放數字音頻和視頻。   
簡介
  Microsoft Visual C# 是世界上最流行的編程語言,利用 Visual C# 的最新版本 Visual C# .NET,您能夠快速、有效地開發基於 Windows 窗體的應用程序,還可以爲嵌入了 Microsoft Windows Media? Player 9 Series ActiveX 控件的應用程序添加新鮮、有趣而又非常實用的功能。
  DirectShow 控件是一個標準的 ActiveX 控件,提供了大量的功能。DirectShow控件提供的功能包括:
  · 數字媒體文件和流媒體的高級播放功能。     
  · 使用播放列表的功能。
  · 播放 DVD 和 CD 的功能。
  · 訪問 Windows Media Player 中的 Media Library(媒體庫)。
  · 處理元數據的功能。
  · 支持字幕。
  · 支持多種語言的音頻。
  · 控制網絡連通性和訪問相關統計信息的功能。
  下面我們來看看構造這個媒體播放器要達到什麼樣的目標,確定了目標也就確定了代碼量和程序的複雜程度。本文的媒體播放器要達到如下目標:
  · 是一個菜單驅動的簡單AWT應用。
  · 包含一個“文件”菜單,文件菜單包含三個菜單項:
  · “打開”,用來打開媒體文件。
  · “循環”,是播放一次(默認),還是重複播放。
  · “退出”,退出程序。
  · 可以在多種平臺上運行。
  · 核心功能通過JMF(Java Media Framework)API實現。
  按照本文介紹的步驟,您將創建一個基於 Windows 窗體的基本應用程序,並在其中嵌入 Player 控件。您創建的示例應用程序具有如下特點:
  · 創建 DirectShow 控件的一個實例。
  · 利用 Windows Media Player 主互操作程序集提供組件對象模型 (COM) 互操作性。
  · 允許用戶打開並播放 Windows Media 文件,尤其是文件擴展名爲 .wma 或 .wmv 的文件。
  · 創建供用戶播放、暫停和停止數字媒體內容的傳輸控制按鈕。
  · 顯示當前數字媒體文件的標題。
  · 演示如何使用 Player 對象模型,包括使用屬性、方法和事件的示例。
  我的這個程序僅僅只是告訴大家如何用DirectShow 在C#中做一個播放機,
  在這個程序中我們經要解決的一些小問題:
  1.如何從你的磁盤上打開媒體文件
  2.如何讓工具條上的按鈕起用和禁用
  3.如何設置狀態欄的顯示文字
  4.如何控制時間 
  5.如何使用時間控件的事件
 
  6.如何用DirectShow來播放媒體文件
  7.如何確定播放狀態等等...
  下圖顯示了您將要創建的應用程序,其中正在播放名爲“Melow”的數字音頻文件,同時呈現了可視化效果。

圖 1
  · 本文假設您已經具備一定的 Visual C# 和 Visual Studio.NET 集成開發環境知識。
準備工作
  在開始創建應用程序之前,您需要安裝必要的軟件並註冊主互操作程序集 (QuartzTypeLib)。
  這裏簡單介紹DirectShow 接口:
  播放視屏和聲音文件我們要用到DiectX爲我們提供的DirectShow組件.使用這個接口可以讓你方便的播放那些共用的影像和聲音文件.你要做的僅僅只是安裝DirectShow接口和使用它的功能函數和配置正確的接口參數而已.
  不幸的是.NET並不正式支持DirectX.是的也許你聽說DirectX9支持是嗎?是的,不過在最終版敲定的那一天還沒來,我們都得不到最好的效果.但無論如何我們還是要用的不是嗎?要不這篇文章得作廢了.是的,也許你用過VB,對了,就是它,我們正是要用到那個.
  開始項目
  在安裝必要軟件並註冊 QuartzTypeLib之後,您就可以啓動 Visual C#,開始爲示例應用程序創建項目。下面我將給大家介紹這一過程的操作步驟。
  創建項目
  按以下步驟創建一個空的項目:
  1. 啓動 Visual Studio .NET,然後單擊 New Project(新建項目)。
  2. 在 Visual C# Projects(Visual C# 項目)文件夾中單擊 Windows Application(Windows 應用程序),鍵入新項目的名稱(最好爲 DirectShow),然後單擊 OK(確定)。
  Visual C# 使用默認的 Windows 窗體“Form1”創建一個新的項目。
  3. 這個名稱並沒有特別的意義或用處,所以請在 Properties(屬性)窗口中將窗體名稱更改爲 frmPlayMedia,將窗體文本更改爲“媒體播放器”。
  在項目中添加對 DirectShow的引用
  按照以下步驟在項目中添加一個對 DirectShow的引用:
  1. 打開 Visual Studio 工具箱,然後單擊 Components(組件)顯示該面板。
  2. 右擊面板,然後單擊 Customize Toolbox(自定義工具箱),顯示對話框。
  3. 在 COM Components(COM 組件)選項卡上,選中 Interop.QuartzTypeLib.dll。(如果 Interop.QuartzTypeLib.dll 由於某種原因未列出,則單擊 Browse [瀏覽] 並查找名爲 QuartzTypeLib.dll的文件。)
  4. 單擊 OK(確定)關閉對話框。

圖 2
  要在代碼中使用 DirectShow,您需要添加一行代碼,以引用 DirectShow命名空間。在窗體代碼窗口的頂部,將以下代碼添加到所有聲明語句之前:
using QuartzTypeLib;
  using語句必須在所有 Options 語句(本項目中並未使用)之後,並且在所有其他代碼之前。添加該語句後。
開發應用程序
  創建通過 PIA 與 Framework 連接的 Player 控件實例之後,您可以向窗體中添加所需的其他元素,並編寫完成實際操作的代碼。
添加 Windows 窗體控件
  1. 在 View(視圖)菜單中,單擊 Designer(設計器),或者單擊 Solution Explorer(解決方案資源管理器)中的 View Designer(視圖設計器)按鈕,切換到窗體設計器。
  2. 在窗體上增加文件、播放、信息等菜單。
  3. 在工具箱的 Windows Forms(Windows 窗體)面板中,爲您的窗體添加一個工具欄、一個狀態欄和圖片imageList。
  4. 在 Properties(屬性)窗口中,將工具欄的名稱更改爲 toolBar1,將在Buttons上增加4個按鈕。狀態欄的名稱更改爲 statusBar1,並分別增加三個Panel。
  5. 在工具箱的面板中,爲您的窗體添加一個面版panel1。
  6. 增加一個定時器timer1。
  7. 調整控件在窗體中的排列方式,使之符合您的需要而且方便用戶使用。下圖爲 Visual Studio Designer(設計器)中完成後的窗體佈局。

圖 3
  編寫代碼
  如何打開你想要媒體文件?
  第一步是編寫在 frmPlayMedia中打開 Windows Media 文件的代碼。要自動切換到 Code(代碼)視圖並編輯打開菜單的 Click 事件處理程序 (menuItem2_Click) 的代碼,請雙擊窗體上的“工具欄”按鈕。將以下代碼添加到事件處理程序中:
  還記得嗎"文件 -> 打開..." 是的幾乎每個使用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多媒體支持。
  過濾器
  過濾器是註冊的DirectShow類,它執行許多媒體信息處理任務。這些任務包括:
   獲得源信息(例如,獲得媒體流)
   分析(例如,在流上執行包讀入、分離和格式化)
   轉換(例如,解碼WMA和MPEG-4音頻和視頻流)
   渲染(例如,在適當的時候產生音頻PCM或者視頻RGB/YUV輸出,將數據傳給DirectSound和DirectDraw)
  過濾器使用幾種類型的接口,例如引腳、計數器、傳送器和時鐘接口,來執行它們的任務。過濾器實現和開放了許多接口。FGM可以使用這些接口創建、連接和控制樹。過濾器經常實現包含下列方法的IBaseFilter接口:
   運行、停止和暫停過濾器狀態。
   恢復過濾器和廠商信息。
   得到和設置參考時鐘。
   恢復過濾器狀態信息。
   枚舉過濾器引線。
   重建過濾器樹時定位引腳
  用戶單擊“打開”時,這段代碼將顯示一個對話框,供用戶在計算機上瀏覽並選擇要播放的 .wma 或 .wmv 文件。用戶選擇文件(並單擊“確定”)時,代碼將 Player 的 URL 屬性設置爲用戶選擇的文件。由於 Player 的 autoStart 屬性在默認情況下設置爲 True,所以 Player 立即打開並播放用戶選擇的數字媒體文件。
  接下來,添加播放/暫停按鈕的代碼。在代碼窗口中,在停止、暫停菜單中單擊,然後,在方法名稱列表中單擊 Click。將以下代碼添加到 Visual C# 爲您創建的Click 事件處理程序中:
  看看下面的代碼是如何實現的:
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();//停止
// 這段代碼非常簡單。當用戶單擊播放/暫停按鈕時,代碼將檢查 Player 的 playState 屬性。如果 Player 正在播放數字媒體文件,代碼就會暫停文件的播放; 如果 Player 已經暫停或停止,代碼就再次啓動 Player 播放文件。
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);
}
  只要播放狀態改變,上述代碼就會運行。如果 Player 正在播放(用戶打開文件時就處於播放狀態,因爲 autoStart 設置爲 True),代碼將啓用播放/暫停按鈕和停止按鈕,以便用戶執行操作。之後,代碼將播放/暫停按鈕的文字更改爲“暫停”,這樣用戶就可以使用該按鈕暫停播放過程。最後,代碼檢索當前數字媒體文件的標題,並更新標題標籤的文字以顯示標題。
  如果 Player 被暫停(用戶單擊了播放/暫停按鈕),代碼會將播放/暫停按鈕的文字更改爲“播放”,以提示用戶使用該按鈕可以恢復播放。
  如果 Player 被停止(用戶單擊了停止按鈕),代碼將禁用停止按鈕(因 Player 已經停止工作)並將播放/暫停按鈕的文字恢復爲默認值“播放”。
  一切都結束了,現在要做的事就是做些來找一部影片來享受一下自己的成果了.
  編寫完示例項目的代碼之後,您可以生成並運行解決方案。
  生成解決方案
  在 Build(生成)菜單中單擊 Build Solution(生成解決方案)。Visual Studio 開始編譯並生成項目。如果鍵入內容全部正確,生成過程將順利完成,不會出現任何錯誤。如果生成報告錯誤,則請檢查您的代碼並糾正錯誤。
  使用示例應用程序
  要在調試器中運行項目,請按鍵盤上的 F5 鍵。如果出現“查看生成的代碼”主題中介紹的未處理的異常,則應該停止調試會話,刪除或註釋掉生成代碼中的相應行,然後再按 F5 鍵。
  您可以單擊“打開”查找 .wma 或 .wmv 文件(究竟選擇何種文件,取決於您在“打開”對話框中選擇的文件類型)。選擇某個文件並單擊“確定”之後,“打開”對話框關閉,開始播放數字媒體文件,傳輸控制按鈕的狀態也隨之改變。這時您就可以利用傳輸控制按鈕來暫停、重新開始或完全停止播放。   回
前公司在製作播客系統(Web程序)中,用到從視頻截圖功能.
下邊是截圖CatchImg方法,可從大多數的視頻文件中截圖成功,大家可測試;
如果截圖不成功,大多是因爲視頻本身的問題,如編碼標準或加了密.
但從在線錄製的視頻Flv文件中截圖,還未發現截圖失敗;
----------------------------------------------------------------------------------------------------------------------------
/// <summary>
/// @從視頻文件截圖,生成在視頻文件所在文件夾
/// 在Web.Config 中需要兩個前置配置項:
/// 1.ffmpeg.exe文件的路徑
/// <add key="ffmpeg" value="E:\ffmpeg\ffmpeg.exe" />
/// 2.截圖的尺寸大小
/// <add key="CatchFlvImgSize" value="240x180" />
/// 3.視頻處理程序ffmpeg.exe
/// </summary>
/// <param name="vFileName">視頻文件地址,如:/Web/FlvFile/User1/00001.Flv</param>
/// <returns>成功:返回圖片虛擬地址; 失敗:返回空字符串</returns>
public string CatchImg(string vFileName)
{
//取得ffmpeg.exe的路徑,路徑配置在Web.Config中,如:<add key="ffmpeg" value="E:\ffmpeg\ffmpeg.exe" />
string ffmpeg=System.Configuration.ConfigurationSettings.AppSettings["ffmpeg"];
if ( (!System.IO.File.Exists(ffmpeg)) || (!System.IO.File.Exists(vFileName)) )
{
return "";
}
//獲得圖片相對路徑/最後存儲到數據庫的路徑,如:/Web/FlvFile/User1/00001.jpg
string flv_img = System.IO.Path.ChangeExtension(vFileName,".jpg") ;
//圖片絕對路徑,如:D:\Video\Web\FlvFile\User1\0001.jpg
string flv_img_p = HttpContext.Current.Server.MapPath(flv_img);
//截圖的尺寸大小,配置在Web.Config中,如:<add key="CatchFlvImgSize" value="240x180" />
string FlvImgSize=System.Configuration.ConfigurationSettings.AppSettings["CatchFlvImgSize"];
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo(ffmpeg);
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
//此處組合成ffmpeg.exe文件需要的參數即可,此處命令在ffmpeg 0.4.9調試通過
startInfo.Arguments = " -i " + vFileName + " -y -f image2 -t 0.001 -s " + FlvImgSize + " " + flv_img_p ;
try
{
System.Diagnostics.Process.Start(startInfo);
}
catch
{
return "";
}
///注意:圖片截取成功後,數據由內存緩存寫到磁盤需要時間較長,大概在3,4秒甚至更長;
///這兒需要延時後再檢測,我服務器延時8秒,即如果超過8秒圖片仍不存在,認爲截圖失敗;
///此處略去延時代碼.如有那位知道如何捕捉ffmpeg.exe截圖失敗消息,請告知,先謝過!
if ( System.IO.File.Exists(flv_img_p))
{
return flv_img;
}
return "";
}
待解決問題:
就是我無法從ffmpeg.exe捕捉截圖失敗消息~
不知能看到這篇日誌的行家可否有辦法取得,我目前只能通過檢測圖片是否生成來判斷成功與否,但時間較慢,因爲這個檢測程序就讓用戶要多等大概4,5秒時間

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/yandong19861103/archive/2009/03/05/3960003.aspx
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章