首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

使用 Visual C# 與 DirectShow 開發媒體播放器完整教學

     編輯:關於C語言

前言

DirectShow 是 Microsoft 提供的多媒體串流架構,透過 COM 介面支援音訊/視訊播放、DVD、串流媒體、播放清單等功能。本文示範如何在 Visual C# Windows Forms 專案中,透過 QuartzTypeLib (DirectShow 的 Interop 組件) 建立一個具備開啟檔案、播放/暫停/停止、進度顯示、播放完成事件處理的媒體播放器。

開發環境準備

  • Visual Studio .NET / Visual Studio 2005+
  • DirectX 9.0c 執行階段(含 DirectShow)
  • 註冊 QuartzTypeLib.dll(通常隨 DirectX 安裝自動註冊)

專案建立步驟

  1. 新建「Windows 應用程式」專案,命名為 DirectShowPlayer。
  2. 表單命名:frmPlayMedia,Text = 「媒體播放器」。
  3. 工具箱 → 右鍵「自訂工具箱」→ COM 元件 → 勾選 Interop.QuartzTypeLib。
  4. 程式碼頂端加入:using QuartzTypeLib;

介面配置

在表單上配置以下控制項:

  • 選單:File(開啟、循環、結束)、Playback、Info
  • 工具列:toolBar1,4 個按鈕(開啟、播放/暫停、停止、循環)
  • 狀態列:statusBar1,3 個面板(狀態、總時間、目前時間)
  • Panel:panel1(影片渲染區域)
  • Timer:timer1,Interval = 100(更新進度用)

核心程式碼實作

1. 開啟媒體檔案

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();
}

2. 播放控制

// 播放/暫停切換
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();
}

3. 進度更新(Timer Tick)

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}";
}

4. 處理播放完成事件 (EC_COMPLETE)

// 常數定義
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);
}

5. 資源清理

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;
}

DirectShow 核心概念補充

  • Filter Graph:由多個 Filter(來源、分割器、解碼器、渲染器)連接而成的管線。
  • Filter Graph Manager (FGM):管理 Filter 連接、資料流控制、事件通知。
  • RenderFile:自動根據副檔名建構完整濾鏡圖,極大簡化開發。
  • IVideoWindow:控制視訊渲染視窗的位置、大小、擁有者。
  • IMediaEventEx:非同步事件通知(完成、錯誤、使用者中止等)。

常見問題與解決

問題原因解決
找不到 QuartzTypeLibDirectX 未安裝或未註冊安裝 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 能力,我們可以用相較簡潔的程式碼建立功能完整的媒體播放器。實務上可再擴充:音量控制、全螢幕、播放清單、字幕支援、影像擷取等功能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved