前言
DirectX 是微軟提供的多媒體 API 函式庫,抽象化硬體差異,提供統一介面操作顯示卡、聲音卡、輸入裝置。Managed DirectX (MDX) 讓 .NET 開發者能直接使用 DirectX 功能。本文示範使用 `Microsoft.DirectX.AudioVideoPlayback` 命名空間實作簡易視訊播放器,涵蓋開檔、播放、暫停、停止、全螢幕、視訊畫面宿主等核心功能。
環境準備
- 安裝 DirectX SDK:下載 `dxsdk_apr2005.exe` (或更新版本) 並安裝
- 參考組件:專案右鍵 → Add Reference → Browse 找到 `Microsoft.DirectX.AudioVideoPlayback.dll` (通常在 `C:\Windows\Microsoft.NET\Managed DirectX\v9.02.2904\`)
- 引入命名空間:`using Microsoft.DirectX.AudioVideoPlayback;`
核心類別架構
| 類別 | 用途 | 關鍵成員 |
| Video | 視訊播放核心類別 | Owner, Play(), Pause(), Stop(), CurrentPosition, Duration, State, Fullscreen |
| Audio | 音訊播放核心類別 | 同上,無視訊畫面 |
| TextureRenderEventArgs | 自訂繪製事件參數 | 進階自訂渲染用 |
Video 類別關鍵屬性/方法
| 成員 | 型別 | 說明 |
| Owner | Control | 視訊畫面宿主控制項 (通常是 Panel) |
| CurrentPosition | double | 目前播放位置 (秒),可讀寫 (Seek) |
| Duration | double | 視訊總長度 (秒),唯讀 |
| State | StateFlags | 播放狀態:Playing, Paused, Stopped, Buffering |
| Fullscreen | bool | 全螢幕模式切換 |
| Play() | void | 開始/繼續播放 |
| Pause() | void | 暫停播放 |
| Stop() | void | 停止並重置位置 |
| Dispose() | void | 釋放非受控資源 (重要!) |
完整實作:WinForms 視訊播放器
1. 表單設計
- Panel `panelVideo` (Dock=Top, Size=540x346) - 視訊畫面宿主
- Button `btnOpen` (Text="開啟") - 選擇檔案
- Button `btnPlay` (Text="播放") - 播放
- Button `btnPause` (Text="暫停") - 暫停
- Button `btnStop` (Text="停止") - 停止
- OpenFileDialog `ofdVideo` (Filter="視訊檔案|*.avi;*.mp4;*.wmv;*.mpg;*.mov|所有檔案|*.*")
- TrackBar `trkPosition` - 進度條 (可選)
- Timer `tmrUpdate` (Interval=500) - 更新進度條
2. 完整程式碼
using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX.AudioVideoPlayback;
namespace VideoPlayerDemo
{
public partial class MainForm : Form
{
private Video _video = null;
private bool _isSeeking = false;
public MainForm()
{
InitializeComponent();
InitializePlayer();
}
private void InitializePlayer()
{
// 初始化按鈕狀態
UpdateButtonStates(false);
// 進度條事件
trkPosition.MouseDown += (s, e) => _isSeeking = true;
trkPosition.MouseUp += (s, e) =>
{
_isSeeking = false;
if (_video != null)
_video.CurrentPosition = trkPosition.Value;
};
// 定時器更新進度
tmrUpdate.Tick += (s, e) => UpdatePosition();
}
// 開啟檔案
private void btnOpen_Click(object sender, EventArgs e)
{
ofdVideo.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyVideos);
if (ofdVideo.ShowDialog() == DialogResult.OK)
{
OpenVideoFile(ofdVideo.FileName);
}
}
private void OpenVideoFile(string filePath)
{
try
{
// 釋放舊視訊物件
if (_video != null)
{
_video.Stop();
_video.Dispose();
_video = null;
}
// 建立新視訊物件
_video = new Video(filePath);
// 設定宿主控制項 (關鍵步驟!)
_video.Owner = panelVideo;
// 重設 Panel 大小觸發重繪
int w = panelVideo.Width, h = panelVideo.Height;
panelVideo.Width = w; panelVideo.Height = h;
// 播放第一幀顯示靜態畫面
_video.Play();
_video.Pause();
// 更新 UI
UpdateButtonStates(true);
trkPosition.Maximum = (int)Math.Ceiling(_video.Duration);
trkPosition.Value = 0;
lblDuration.Text = FormatTime(_video.Duration);
// 啟動更新定時器
tmrUpdate.Start();
}
catch (Exception ex)
{
MessageBox.Show($"開啟視訊失敗:{ex.Message}", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
UpdateButtonStates(false);
}
}
// 播放
private void btnPlay_Click(object sender, EventArgs e)
{
_video?.Play();
}
// 暫停
private void btnPause_Click(object sender, EventArgs e)
{
_video?.Pause();
}
// 停止
private void btnStop_Click(object sender, EventArgs e)
{
if (_video != null)
{
_video.Stop();
trkPosition.Value = 0;
lblPosition.Text = "00:00";
}
}
// 更新播放位置
private void UpdatePosition()
{
if (_video == null || _isSeeking) return;
try
{
double pos = _video.CurrentPosition;
trkPosition.Value = (int)Math.Min(pos, trkPosition.Maximum);
lblPosition.Text = FormatTime(pos);
// 自動停止更新 (視訊結束)
if (_video.State == StateFlags.Stopped)
{
tmrUpdate.Stop();
UpdateButtonStates(false);
}
}
catch { /* 忽略存取已釋放物件的例外 */ }
}
private void UpdateButtonStates(bool hasVideo)
{
btnPlay.Enabled = hasVideo;
btnPause.Enabled = hasVideo;
btnStop.Enabled = hasVideo;
trkPosition.Enabled = hasVideo;
}
private string FormatTime(double seconds)
{
TimeSpan t = TimeSpan.FromSeconds(seconds);
return t.ToString(t.Hours > 0 ? @"hh\:mm\:ss" : @"mm\:ss");
}
// 表單關閉時釋放資源
protected override void OnFormClosing(FormClosingEventArgs e)
{
tmrUpdate.Stop();
_video?.Dispose();
base.OnFormClosing(e);
}
}
}
進階功能實作
音量控制
// Video/Audio 類別有 Audio 屬性可存取音訊控制
private void trkVolume_Scroll(object sender, EventArgs e)
{
if (_video?.Audio != null)
{
// 音量範圍:-10000 (靜音) 到 0 (最大)
_video.Audio.Volume = -10000 + (trkVolume.Value * 100); // 0-100 對應
}
}
// 靜音切換
private void btnMute_Click(object sender, EventArgs e)
{
if (_video?.Audio != null)
{
_video.Audio.Volume = btnMute.Text == "靜音" ? -10000 : 0;
btnMute.Text = btnMute.Text == "靜音" ? "取消靜音" : "靜音";
}
}
全螢幕切換
private void btnFullscreen_Click(object sender, EventArgs e)
{
if (_video != null)
{
_video.Fullscreen = !_video.Fullscreen;
btnFullscreen.Text = _video.Fullscreen ? "離開全螢幕" : "全螢幕";
}
}
// 雙擊畫面切換全螢幕
private void panelVideo_DoubleClick(object sender, EventArgs e)
{
btnFullscreen_Click(sender, e);
}
播放速度控制 (需 DirectShow 介面,MDX 不直接支援)
// 進階:透過 IMediaSeeking / IMediaControl 介面 (需 COM 互操作)
// 此處略過,建議改用 MediaFoundation 或 LibVLCSharp 獲得更好控制
支援更多格式:安裝編碼器
- DirectX 依賴系統安裝的 DirectShow 解碼器
- 常見格式:AVI (內建)、WMV (內建)、MPEG (需編碼器)、MP4/H.264 (需 LAV Filters / K-Lite Codec Pack)
- 建議:專案部署時檢查必要編碼器,或改用內建編碼器的 MediaFoundation / LibVLCSharp
常見問題與解決方案
| 問題 | 原因 | 解決 |
| 找不到 Microsoft.DirectX.AudioVideoPlayback | 未安裝 Managed DirectX / 參考路徑錯誤 | 安裝 DirectX SDK,手動加入參考 DLL |
| 視訊畫面不顯示 / 黑畫面 | Owner 未正確設定 / Panel 可見度 / 格式不支援 | 確認 _video.Owner = panelVideo;Panel.Visible=true;安裝編碼器 |
| 播放無聲音 | 音訊裝置被佔用 / 格式不支援 / 靜音 | 檢查系統音量、_video.Audio.Volume、安裝音訊解碼器 |
| 記憶體洩漏 / 重複開啟崩潰 | 未正確 Dispose 舊 Video 物件 | 開啟新檔前必須 _video?.Dispose() |
| 拖曳進度條閃爍 | Timer 更新與手動 Seek 衝突 | 使用 _isSeeking 旗標同步 |
| MP4/H.264 無法播放 | 系統缺少 H.264 DirectShow 解碼器 | 安裝 LAV Filters 或 K-Lite Codec Pack,或改用 MediaFoundation |
現代替代方案比較
| 方案 | 優點 | 缺點 | 適用場景 |
| Managed DirectX (本文) | 原生 .NET、API 簡單、輕量 | 已停止更新、格式支援有賴系統編碼器、無硬體加速控制 | 簡易播放器、Legacy 專案維護 |
| MediaFoundation (MF.NET) | Windows 內建、硬體加速、支援 H.264/HEVC、低延遲 | API 複雜、需 COM 互操作、文件較少 | 高效能播放、UWP/WinUI 應用 |
| LibVLCSharp | 跨平台、格式支援最全、功能豐富、活躍維護 | 需部署 libvlc 原生庫、體積較大 | 專業播放器、跨平台應用 |
| FFmpeg + SharpFFmpeg | 完全控制、最強編解碼 | 極高複雜度、需深度音視訊知識 | 轉碼、串流、特殊處理 |
| WPF MediaElement | XAML 整合、支援硬體加速 | 格式受限於 WPF/系統、Seek 不精確、記憶體洩漏歷史問題 | 簡易 WPF 應用 |
總結
`Microsoft.DirectX.AudioVideoPlayback` 提供了極簡單的 API 讓 C# 開發者快速加入視訊播放功能。核心要點:
- Owner 屬性:必須指定宿主控制項 (Panel) 才能顯示畫面
- 生命週期管理:開啟新檔前 `Dispose()` 舊物件,表單關閉時 `Dispose()`
- 狀態機:Play/Pause/Stop 切換 State,配合 Timer 更新 UI
- 格式相容性:依賴系統 DirectShow 解碼器,生產環境建議安裝 LAV Filters
對於新專案,建議評估 **LibVLCSharp** (跨平台、功能完整) 或 **MediaFoundation** (Windows 原生、高效能)。但理解 MDX 基礎架構,有助於掌握音視訊播放的核心概念:解碼器、渲染器、時鐘、緩衝、同步。