DirectShow 是 Microsoft DirectX 媒體串流架構的核心,採用篩選器圖(Filter Graph)模型處理音視頻資料流。其基礎建立在 COM (Component Object Model) 之上,所有元件皆為 COM 物件。一個最小可行的 DirectShow 播放程式僅需三步驟:
CLSID_FilterGraph)IGraphBuilder::RenderFile() 自動構建完整過濾器鏈(來源 → 解碼器 → 渲染器)IMediaControl::Run() 開始串流,配合 IMediaEvent::WaitForCompletion() 等待播放結束HRESULT hr = CoInitialize(NULL); // 單執行緒單元 (STA) 模式
if (FAILED(hr)) {
printf("ERROR - 無法初始化 COM 程式庫");
return 1;
}
// 進階:可用 CoInitializeEx(NULL, COINIT_MULTITHREADED) 啟用自由執行緒模式
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 支援自由執行緒模式,適合高效能場景。
// 檔案路徑需轉為 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,應設定超時或用視窗訊息輪詢
}
}
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;
}
strmbase.lib(Debug 版為 strmbasd.lib)strmbase.lib$(DXSDK_DIR)\Samples\C++\DirectShow\BaseClasses
IMediaControl::Pause()/Stop()、IMediaSeeking 實現拖曳定位IVideoWindow 設定擁有者視窗、全螢幕、長寬比IMediaEventEx::GetEventHandle() 配合 MsgWaitForMultipleObjects 非阻塞輪詢| 現象 | 可能原因 | 解決方案 |
|---|---|---|
RenderFile 失敗 (0x80040266) | 缺少分離器或解碼器 | 安裝 LAV Filters、K-Lite Codec Pack 或對應編解碼器 |
| 播放無聲音/無畫面 | 渲染器未正確連接 | 檢查 IGraphBuilder::RenderFile 回傳值;嘗試手動 AddFilter/Connect |
| 主控台閃退 | WaitForCompletion(INFINITE) 阻塞 | 改用有限超時或視窗訊息迴圈處理 EC_COMPLETE 事件 |