DirectShow 播放视频文件

发布于 2021-11-24


以 DirectShow 一个播放视频文件的例子,来介绍如何入门使用 DirectShow。代码如下:

#include <windows.h>

// 包含 CComPtr,自动管理 COM 指针
#include <atlbase.h>

// DirectShow 所需要的头文件
#include <dshow.h>

// DirectShow 所需要的lib库,定义实现了相关的 COM 接口
#pragma comment(lib, "strmiids.lib")

// 实现了 AMGetErrorText 函数。不是必须。
#pragma comment(lib, "strmbase.lib")

// 自动初始化、销毁 COM
class ScopedCOMInitializer {
public:
  ScopedCOMInitializer() { hr_ = CoInitialize(NULL); }
  ~ScopedCOMInitializer() {
    if (SUCCEEDED(hr_)) {
      CoUninitialize();
    }
  }
  bool IsSucceeded() { return SUCCEEDED(hr_); }

private:
  // 禁止拷贝
  ScopedCOMInitializer(const ScopedCOMInitializer &) = delete;

  // 禁止赋值
  ScopedCOMInitializer &operator=(const ScopedCOMInitializer &) = delete;

  HRESULT hr_;
};

int main() {

  // 初始化 COM
  ScopedCOMInitializer com_initializer;
  if (!com_initializer.IsSucceeded()) {
    printf("ERROR:init com failed.\n");
    return 0;
  }

  // 创建 Filter Graph Manager
  CComPtr<IGraphBuilder> graph_buiulder;
  HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
                                IID_IGraphBuilder, (void **)&graph_buiulder);
  if (FAILED(hr)) {
    printf("ERROR:Could not create the Filter Graph Manager.\n");
    return 0;
  }

  CComPtr<IMediaControl> media_control;
  hr = graph_buiulder->QueryInterface(IID_IMediaControl,
                                      (void **)&media_control);
  if (FAILED(hr)) {
    printf("ERROR:Could not create the IMediaControl.\n");
    return 0;
  }

  CComPtr<IMediaEvent> media_event;
  hr = graph_buiulder->QueryInterface(IID_IMediaEvent, (void **)&media_event);
  if (FAILED(hr)) {
    printf("ERROR:Could not create the IMediaEvent.\n");
    return 0;
  }

  hr = graph_buiulder->RenderFile(L"D:\\example.mp4", NULL);
  if (FAILED(hr)) {
    printf("ERROR:Could not RenderFile.\n");
    return 0;
  }

  hr = media_control->Run();
  if (FAILED(hr)) {
    printf("ERROR:IMediaControl run failed.\n");
    return 0;
  }

  // 等待视频播放完成
  long event_code = 0;
  media_event->WaitForCompletion(INFINITE, &event_code);

  return 0;
}

因为 DirectShow 是基于 COM 的。因此我们需要在使用 DirectShow 之前 初始化 COM。

一般的 DirectShow 框架都有 3 个步骤:

  1. 创建 Filter Graph Manager
  2. 使用 Filter Graph Manager 创建 Filter Graph
  3. 运行 Filter Graph,使其中的 Filter 按照我们设计好的工作起来

以上代码中 IGraphBuilder、IMediaControl、IMediaEvent 等等接口组成了 Filter Graph Manager。

  • IMediaControl 接口控制 Filter Graph 添加 Filter、运行、暂停、结束等等。
  • IMediaEvent 接口获取 Filter Graph 产生的事件。

这个例子中,我们没有显式往 Filter Graph 中添加 Filter。而是通过 IGraphBuilder 接口的 RenderFile 方法,传递视频文件的路径,隐式的添加 Filter 来创建 Filter Graph。

创建好 Filter Graph,通过 IMediaControl 的 Run 接口开始运行 Filter Graph,从而播放视频文件。

另外我们通过 IMediaEvent 的 WaitForCompletion 接口,等待接收视频文件播放完成的事件,从而结束程序。

最后程序运行的效果如下:

DirectShow Play File

可以看到,它会自动创建一个名为 ActiveMovide Window 的窗口,在那里显示播放的视频画面。

参考