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

DirectShow 入門實戰:打造極簡命令列多媒體播放器

     編輯:關於C語言

概述:DirectShow 架構與核心流程

DirectShow 是 Microsoft DirectX 媒體串流架構的核心,採用篩選器圖(Filter Graph)模型處理音視頻資料流。其基礎建立在 COM (Component Object Model) 之上,所有元件皆為 COM 物件。一個最小可行的 DirectShow 播放程式僅需三步驟:

  1. 建立 Filter Graph Manager 實例(CLSID_FilterGraph)
  2. 透過 IGraphBuilder::RenderFile() 自動構建完整過濾器鏈(來源 → 解碼器 → 渲染器)
  3. 呼叫 IMediaControl::Run() 開始串流,配合 IMediaEvent::WaitForCompletion() 等待播放結束

詳細實作步驟與代碼解析

1. COM 程式庫初始化

HRESULT hr = CoInitialize(NULL);  // 單執行緒單元 (STA) 模式
if (FAILED(hr)) {
    printf("ERROR - 無法初始化 COM 程式庫");
    return 1;
}
// 進階:可用 CoInitializeEx(NULL, COINIT_MULTITHREADED) 啟用自由執行緒模式

2. 建立 Filter Graph Manager 並取得核心介面

IGraphBuilder *pGraph = NULL;
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
                      IID_IGraphBuilder, (void**)&pGraph);
if (FAILED(hr)) { /* 錯誤處理 */ }

// 取得媒體控制與事件介面
IMediaControl *pControl = NULL;
IMediaEvent   *pEvent   = NULL;
pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl);
pGraph->QueryInterface(IID_IMediaEvent,   (void**)&pEvent);

關鍵點: CLSCTX_INPROC_SERVER 表示元件在進程內 DLL 中實作;DirectShow 支援自由執行緒模式,適合高效能場景。

3. 建立過濾器圖並播放媒體檔案

// 檔案路徑需轉為 Unicode (LPCWSTR)
WCHAR wText[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, strAviName, -1, wText, MAX_PATH);

// 一鍵建圖:自動尋找分離器、解碼器、渲染器
hr = pGraph->RenderFile(wText, NULL);
if (SUCCEEDED(hr)) {
    hr = pControl->Run();  // 進入 Running 狀態,資料開始流動
    if (SUCCEEDED(hr)) {
        long evCode;
        pEvent->WaitForCompletion(INFINITE, &evCode);  // 阻塞至播放結束
        // 實際專案切勿使用 INFINITE,應設定超時或用視窗訊息輪詢
    }
}

4. 資源釋放與 COM 反初始化

pControl->Release();
pEvent->Release();
pGraph->Release();
CoUninitialize();  // 必須與 CoInitialize 配對呼叫

完整原始碼:極簡播放器

/*********************************************************************
 *  DirectShow 最簡播放器範例
 *  編譯前須先建置 DX SDK 基礎類別庫:
 *  $(DXSDK_DIR)\Samples\C++\DirectShow\BaseClasses\strmbase.lib
 *********************************************************************/
#include <dshow.h>
#include <iostream>
#include <cstring>

int main(int argc, char *argv[]) {
    if (argc != 2) {
        std::cout << "用法: Player.exe <媒體檔案路徑>" << std::endl;
        return 1;
    }

    char strAviName[256];
    strcpy(strAviName, argv[1]);

    IGraphBuilder *pGraph = NULL;
    IMediaControl *pControl = NULL;
    IMediaEvent   *pEvent = NULL;

    // 1. 初始化 COM
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr)) {
        printf("ERROR - 無法初始化 COM 程式庫");
        return 1;
    }

    // 2. 建立 Filter Graph Manager
    hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
                          IID_IGraphBuilder, (void**)&pGraph);
    if (FAILED(hr)) {
        printf("ERROR - 無法建立 Filter Graph Manager");
        CoUninitialize();
        return 1;
    }

    // 3. 取得控制與事件介面
    hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl);
    hr = pGraph->QueryInterface(IID_IMediaEvent, (void**)&pEvent);

    // 4. 路徑轉 Unicode
    int len = strlen(strAviName) + 1;
    WCHAR *wText = new WCHAR[len];
    memset(wText, 0, len * sizeof(WCHAR));
    MultiByteToWideChar(CP_ACP, 0, strAviName, -1, wText, len);

    // 5. 建圖並播放
    hr = pGraph->RenderFile(wText, NULL);
    delete[] wText;

    if (SUCCEEDED(hr)) {
        hr = pControl->Run();
        if (SUCCEEDED(hr)) {
            long evCode;
            pEvent->WaitForCompletion(INFINITE, &evCode);
            // 注意:實務上應避免 INFINITE 造成主執行緒永久阻塞
        }
    } else {
        std::cout << "ERROR - 檔案不存在或格式不支援" << std::endl;
    }

    // 6. 清理資源
    pControl->Release();
    pEvent->Release();
    pGraph->Release();
    CoUninitialize();
    return 0;
}

編譯環境配置指南

  1. 安裝 DirectX SDK(Windows 8+ 已整合至 Windows SDK)
  2. 編譯 BaseClasses 專案產出 strmbase.lib(Debug 版為 strmbasd.lib)
  3. 專案設定 → Linker → Input → Additional Dependencies 加入 strmbase.lib
  4. 包含目錄加入 $(DXSDK_DIR)\Samples\C++\DirectShow\BaseClasses

專案設定加入 strmbase.lib

進階延伸方向

  • 播放控制: IMediaControl::Pause()/Stop()、IMediaSeeking 實現拖曳定位
  • 視窗渲染: IVideoWindow 設定擁有者視窗、全螢幕、長寬比
  • 事件處理: 使用 IMediaEventEx::GetEventHandle() 配合 MsgWaitForMultipleObjects 非阻塞輪詢
  • 自訂過濾器: 開發原始碼/轉碼/特效篩選器擴展管線功能
  • Media Foundation: Windows Vista+ 推薦的下一代媒體平台,支援硬體加速、DRM、低延遲

常見問題排查

現象可能原因解決方案
RenderFile 失敗 (0x80040266)缺少分離器或解碼器安裝 LAV Filters、K-Lite Codec Pack 或對應編解碼器
播放無聲音/無畫面渲染器未正確連接檢查 IGraphBuilder::RenderFile 回傳值;嘗試手動 AddFilter/Connect
主控台閃退WaitForCompletion(INFINITE) 阻塞改用有限超時或視窗訊息迴圈處理 EC_COMPLETE 事件
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved