DirectShow采集参数如何正确设置?

99ANYc3cd6
预计阅读时长 30 分钟
位置: 首页 参数 正文

DirectShow 本身是一个基于 Filter Graph 的框架,它不直接提供像 AVFoundationMediaFoundation 那样高级的 API 来设置参数,设置参数的过程实际上是在 Filter Graph 中找到合适的 Filter,然后通过其接口(如 IAMStreamConfig)来调整参数

directshow设置 采集参数
(图片来源网络,侵删)

这个过程的核心是枚举和配置 Pin


核心概念

  1. Filter Graph (滤镜图): 数据流处理的管道图,采集设备(如摄像头)是一个 Filter,它有一个或多个输出 Pin。
  2. Pin (引脚): Filter 的连接点,输出 Pin 产出数据,输入 Pin 接收数据,一个摄像头 Filter 通常有一个或多个输出 Pin,例如一个用于视频,一个用于音频。
  3. IAMStreamConfig 接口: 这是设置采集参数的关键接口,它存在于许多 Filter(如摄像头 Filter、视频渲染器 Filter)上,用于配置流的格式、分辨率、帧率等。
  4. IPin 接口: 代表 Pin 的基本接口,用于连接、枚举媒体类型等。
  5. AM_MEDIA_TYPE 结构: 描述媒体流的格式信息,包括主要类型(如 MEDIATYPE_Video)、子类型(如 MEDIASUBTYPE_RGB24)和格式块(formattype,如 FORMAT_VideoInfo)。

设置采集参数的完整步骤

假设我们要从一个视频采集设备(摄像头)中采集视频,并设置其分辨率和帧率。

第 1 步:枚举系统设备

你需要找到系统中可用的视频采集设备,这通常通过 ICreateDevEnum (设备枚举器) 来完成。

#include <dshow.h>
#include <dshowasf.h>
#pragma comment(lib, "strmiids.lib")
// 初始化 COM 库
CoInitialize(NULL);
// 创建设备枚举器
ICreateDevEnum* pDevEnum = NULL;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pDevEnum);
if (FAILED(hr)) { /* 错误处理 */ }
// 创建视频输入设备的类别枚举器
IEnumMoniker* pEnum = NULL;
hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDevice, &pEnum, 0);
if (FAILED(hr) || pEnum == NULL) { /* 没有找到视频设备 */ }
// 枚举所有视频设备
IMoniker* pMoniker = NULL;
while (pEnum->Next(1, &pMoniker, NULL) == S_OK) {
    // 从 Moniker 获取设备名称
    IPropertyBag* pPropBag = NULL;
    hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pPropBag);
    if (SUCCEEDED(hr)) {
        VARIANT var;
        var.vt = VT_BSTR;
        hr = pPropBag->Read(L"FriendlyName", &var, NULL);
        if (SUCCEEDED(hr)) {
            wprintf(L"Found video device: %s\n", var.bstrVal);
            SysFreeString(var.bstrVal);
        }
        pPropBag->Release();
    }
    pMoniker->Release();
}
pEnum->Release();
pDevEnum->Release();

第 2 步:构建 Filter Graph

选择一个设备后,用它来创建 Filter Graph。

directshow设置 采集参数
(图片来源网络,侵删)
IGraphBuilder* pGraph = NULL;
ICaptureGraphBuilder2* pBuilder = NULL;
IBaseFilter* pVideoInputFilter = NULL;
// 创建 Filter Graph Builder
hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void**)&pBuilder);
if (FAILED(hr)) { /* 错误处理 */ }
// 创建 Graph Builder
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGraph);
if (FAILED(hr)) { /* 错误处理 */ }
pBuilder->SetFiltergraph(pGraph);
// 使用上一步找到的 Moniker 创建 Filter
// (这里简化了,实际应用中应保存选中的 pMoniker)
// hr = pMoniker->BindToObject(..., IID_IBaseFilter, (void**)&pVideoInputFilter);
// 假设我们已经有了 pVideoInputFilter
// hr = pGraph->AddFilter(pVideoInputFilter, L"Video Capture Filter");

第 3 步:查找并配置 Pin(核心步骤)

这是设置参数的关键,我们需要找到视频输入 Filter 的输出 Pin,然后通过 IAMStreamConfig 接口来配置它。

// 1. 在视频输入 Filter 上查找输出 Pin
IPin* pInputPin = NULL;
hr = pVideoInputFilter->FindPin(L"Capture", &pInputPin); // "Capture" 是常见的输出 Pin 名称
if (FAILED(hr)) {
    // "Capture" 不行,可以尝试枚举所有 Pin 来找到视频输出 Pin
    IEnumPins* pEnumPins = NULL;
    pVideoInputFilter->EnumPins(&pEnumPins);
    IPin* pPin = NULL;
    while (pEnumPins->Next(1, &pPin, NULL) == S_OK) {
        PIN_DIRECTION dir;
        pPin->QueryDirection(&dir);
        if (dir == PINDIR_OUTPUT) {
            // 检查这个 Pin 是否输出视频
            AM_MEDIA_TYPE mt;
            pPin->ConnectionMediaType(&mt);
            if (mt.majortype == MEDIATYPE_Video) {
                pInputPin = pPin;
                break;
            }
        }
        pPin->Release();
    }
    pEnumPins->Release();
}
if (!pInputPin) { /* 错误:找不到视频输出 Pin */ }
// 2. 查询 IAMStreamConfig 接口
IAMStreamConfig* pStreamConfig = NULL;
hr = pInputPin->QueryInterface(IID_IAMStreamConfig, (void**)&pStreamConfig);
if (FAILED(hr)) { /* 错误:Pin 不支持 IAMStreamConfig */ }
// 3. 获取 Pin 支持的所有媒体类型
int iCount = 0;
int iSize = 0;
hr = pStreamConfig->GetNumberOfCapabilities(&iCount, &iSize);
if (FAILED(hr)) { /* 错误处理 */ }
// 4. 遍历所有媒体类型,找到我们想要的分辨率和帧率
for (int i = 0; i < iCount; i++) {
    AM_MEDIA_TYPE* pmt = NULL;
    VIDEO_STREAM_CONFIG_CAPS caps;
    hr = pStreamConfig->GetStreamCaps(i, &pmt, (BYTE*)&caps);
    if (SUCCEEDED(hr)) {
        // 检查这个媒体类型是否符合我们的要求
        if (pmt->formattype == FORMAT_VideoInfo) {
            VIDEOINFOHEADER* pvi = (VIDEOINFOHEADER*)pmt->pbFormat;
            // 假设我们要 1920x1080 分辨率,30fps
            if (pvi->bmiHeader.biWidth == 1920 && pvi->bmiHeader.biHeight == 1080) {
                // 检查帧率
                double AvgTimePerFrame = (double)pvi->AvgTimePerFrame / 10000000.0; // 转换为秒
                if (fabs(AvgTimePerFrame - (1.0/30.0)) < 0.001) { // 允许小的浮点误差
                    // 找到了!设置这个媒体类型
                    hr = pStreamConfig->SetFormat(pmt);
                    if (SUCCEEDED(hr)) {
                        wprintf(L"Successfully set format to 1920x1080@30fps.\n");
                    }
                    // 退出循环
                    break;
                }
            }
        }
        DeleteMediaType(pmt); // 释放 GetStreamCaps 分配的内存
    }
}
// 5. 释放接口
pStreamConfig->Release();
pInputPin->Release();

第 4 步:连接并运行 Graph

设置好参数后,就可以将 Pin 连接到渲染器(如 Video Renderer Filter)并运行 Graph 了。

// ... (添加渲染器 Filter)
IBaseFilter* pVideoRenderer = NULL;
// CoCreateInstance(CLSID_VideoRenderer, ..., &pVideoRenderer);
// pGraph->AddFilter(pVideoRenderer, L"Video Renderer");
// 连接 Pin
IPin* pOutPin = NULL; // 从 pVideoInputFilter 的 Capture Pin 获取
IPin* pInPin = NULL;  // 从 pVideoRenderer 获取 In Pin
// pGraph->Connect(pOutPin, pInPin);
// 运行 Graph
IMediaControl* pControl = NULL;
pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl);
hr = pControl->Run();
if (SUCCEEDED(hr)) {
    // 等待或处理消息
    Sleep(10000); // 运行10秒
}
pControl->Stop();
pControl->Release();
// ... (释放所有 Filter 和 Graph 接口)

常见采集参数及其设置

分辨率

如上例所示,通过遍历 VIDEOINFOHEADER 结构中的 bmiHeader.biWidthbmiHeader.biHeight 来找到并设置。

帧率

帧率存储在 VIDEOINFOHEADERAvgTimePerFrame 字段中,单位是 100-nanosecond units(即 1/10,000,000 秒)。

  • 30fps 的 AvgTimePerFrame 值是 333333 (即 1/30 * 10,000,000)。
  • 25fps 的值是 400000
  • 计算公式:AvgTimePerFrame = (1 / fps) * 10000000

视频格式(颜色空间)

这由 AM_MEDIA_TYPEsubtype 字段决定。

  • MEDIASUBTYPE_RGB24: 24位真彩色
  • MEDIASUBTYPE_RGB32: 32位真彩色
  • MEDIASUBTYPE_YUY2: YUV 4:2:2 格式(常用于视频处理)
  • MEDIASUBTYPE_NV12: YUV 4:2:0 格式(现代摄像头常用)
  • MEDIASUBTYPE_UYVY: 另一种 YUV 4:2:2 格式

在遍历 GetStreamCaps 时,检查 pmt->subtype 即可。

其他高级参数(如亮度、对比度、对焦)

这些参数通常通过 IAMVideoProcAmp 接口来控制,它和 IAMStreamConfig 一样,也是在 Pin 上查询。

// 在找到的视频输出 Pin (pInputPin) 上查询
IAMVideoProcAmp* pProcAmp = NULL;
hr = pInputPin->QueryInterface(IID_IAMVideoProcAmp, (void**)&pProcAmp);
if (SUCCEEDED(hr)) {
    long lValue;
    long Flags;
    // 获取当前亮度
    pProcAmp->Get(VideoProcAmp_Brightness, &lValue, &Flags);
    // 设置亮度 (范围通常是 0-100)
    lValue = 80; // 设置亮度为80
    pProcAmp->Set(VideoProcAmp_Brightness, lValue, VideoProcAmp_Flags_Manual);
    pProcAmp->Release();
}

IAMTuner 接口用于控制电视调谐器相关的参数(如频道、制式)。


重要注意事项

  1. 线程安全: DirectShow 操作不是线程安全的,最好在同一个线程(通常是主线程)中创建、控制和销毁 Filter Graph。
  2. 内存管理: GetStreamCaps 分配的 AM_MEDIA_TYPE 结构必须用 DeleteMediaType 函数释放。CoTaskMemFree 适用于 COM 分配的内存,不适用于此。
  3. 接口释放: 所有通过 QueryInterface 获取的接口,以及 CoCreateInstance 创建的对象,都必须在使用完毕后调用 Release() 释放引用计数。
  4. 错误处理: DirectShow 操作返回 HRESULT,必须检查每个 HRESULT 的值,确保操作成功,可以使用 SUCCEEDED(hr)FAILED(hr) 宏。
  5. 现代替代方案: DirectShow 是一项较老的技术(Windows XP 时代),在 Windows 7 及更高版本,推荐使用 Media Foundation (MF),它提供了更现代、更强大的 API 和更清晰的编程模型,对于新的项目,除非有特殊兼容性要求,否则应优先考虑 Media Foundation。

在 DirectShow 中设置采集参数的流程可以概括为: 枚举设备 -> 构建Graph -> 在Filter的Output Pin上查询IAMStreamConfig接口 -> 遍历支持的媒体类型 -> 找到匹配的参数 -> 调用SetFormat应用参数。

这个过程比较繁琐,需要手动管理 Filter Graph 和 Pin 连接,但它提供了底层的控制能力。

-- 展开阅读全文 --
头像
甘肃移动智能掌上CRM系统有何优势?
« 上一篇 今天
parrot智能车怎么用?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

最近发表

标签列表

目录[+]