DirectShow 是 Microsoft 提供的多媒體串流架構,透過 COM 介面支援音訊/視訊播放、DVD、串流媒體、播放清單等功能。本文示範如何在 Visual C# Windows Forms 專案中,透過 QuartzTypeLib (DirectShow 的 Interop 組件) 建立一個具備開啟檔案、播放/暫停/停止、進度顯示、播放完成事件處理的媒體播放器。
QuartzTypeLib.dll(通常隨 DirectX 安裝自動註冊)DirectShowPlayer。frmPlayMedia,Text = 「媒體播放器」。using QuartzTypeLib;在表單上配置以下控制項:
File(開啟、循環、結束)、Playback、InfotoolBar1,4 個按鈕(開啟、播放/暫停、停止、循環)statusBar1,3 個面板(狀態、總時間、目前時間)panel1(影片渲染區域)timer1,Interval = 100(更新進度用)private void OpenMediaFile()
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "媒體檔案|*.mpg;*.avi;*.wma;*.mov;*.wav;*.mp2;*.mp3|所有檔案|*.*";
if (ofd.ShowDialog() != DialogResult.OK) return;
CleanUp(); // 釋放先前資源
// 建立 Filter Graph Manager
m_objFilterGraph = new FilgraphManager();
m_objFilterGraph.RenderFile(ofd.FileName); // 自動建構濾鏡圖
// 取得常用介面
m_objBasicAudio = m_objFilterGraph as IBasicAudio;
m_objVideoWindow = m_objFilterGraph as IVideoWindow;
m_objMediaEventEx = m_objFilterGraph as IMediaEventEx;
m_objMediaPosition = m_objFilterGraph as IMediaPosition;
m_objMediaControl = m_objFilterGraph as IMediaControl;
// 設定視訊視窗嵌入 panel1
if (m_objVideoWindow != null)
{
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);
}
// 註冊圖表事件通知視窗
m_objMediaEventEx.SetNotifyWindow((int)this.Handle, WM_GRAPHNOTIFY, 0);
// 開始播放
m_objMediaControl.Run();
m_CurrentStatus = MediaStatus.Running;
UpdateToolBar();
}
// 播放/暫停切換
private void TogglePlayPause()
{
if (m_objMediaControl == null) return;
if (m_CurrentStatus == MediaStatus.Running)
{
m_objMediaControl.Pause();
m_CurrentStatus = MediaStatus.Paused;
}
else
{
m_objMediaControl.Run();
m_CurrentStatus = MediaStatus.Running;
}
UpdateToolBar();
}
// 停止
private void StopPlayback()
{
if (m_objMediaControl == null) return;
m_objMediaControl.Stop();
m_objMediaPosition.CurrentPosition = 0;
m_CurrentStatus = MediaStatus.Stopped;
UpdateToolBar();
UpdateStatusBar();
}
private void timer1_Tick(object sender, EventArgs e)
{
if (m_CurrentStatus == MediaStatus.Running)
UpdateStatusBar();
}
private void UpdateStatusBar()
{
if (m_objMediaPosition == null) return;
// 總長度
int totalSec = (int)m_objMediaPosition.Duration;
statusBarPanel2.Text = FormatTime(totalSec);
// 目前位置
int currSec = (int)m_objMediaPosition.CurrentPosition;
statusBarPanel3.Text = FormatTime(currSec);
// 狀態文字
statusBarPanel1.Text = m_CurrentStatus.ToString();
}
private string FormatTime(int seconds)
{
int h = seconds / 3600;
int m = (seconds % 3600) / 60;
int s = seconds % 60;
return $"{h:D2}:{m:D2}:{s:D2}";
}
// 常數定義
private const int WM_GRAPHNOTIFY = 0x8001;
private const int EC_COMPLETE = 0x01;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_GRAPHNOTIFY && m_objMediaEventEx != null)
{
int evCode, param1, param2;
while (true)
{
try
{
m_objMediaEventEx.GetEvent(out evCode, out param1, out param2, 0);
m_objMediaEventEx.FreeEventParams(evCode, param1, param2);
if (evCode == EC_COMPLETE)
{
StopPlayback(); // 重置狀態
break;
}
}
catch { break; }
}
}
base.WndProc(ref m);
}
private void CleanUp()
{
if (m_objMediaControl != null)
{
m_objMediaControl.Stop();
m_objMediaControl = null;
}
if (m_objMediaEventEx != null)
{
m_objMediaEventEx.SetNotifyWindow(0, 0, 0);
m_objMediaEventEx = null;
}
m_objVideoWindow = null;
m_objFilterGraph = null;
m_CurrentStatus = MediaStatus.None;
}
| 問題 | 原因 | 解決 |
|---|---|---|
| 找不到 QuartzTypeLib | DirectX 未安裝或未註冊 | 安裝 DirectX 9.0c,或手動 regsvr32 quartz.dll |
| 影片無畫面只有聲音 | Video Window Owner 未設定 | 確認 m_objVideoWindow.Owner = panel.Handle |
| 播放結束不觸發事件 | 未正確 SetNotifyWindow | 檢查 WM_GRAPHNOTIFY 訊息迴圈與 FreeEventParams |
| 記憶體洩漏 | 未釋放 COM 介面 | 在 CleanUp/表單關閉時 Marshal.ReleaseComObject |
透過 DirectShow 強大的濾鏡架構,配合 C# 的 COM Interop 能力,我們可以用相較簡潔的程式碼建立功能完整的媒體播放器。實務上可再擴充:音量控制、全螢幕、播放清單、字幕支援、影像擷取等功能。