如何使用 DirectShow 以 RGB 格式捕获实时相机帧

发布于 2024-08-13 06:39:32 字数 6569 浏览 5 评论 0原文

我正在通过 DirectShow 实现实时视频捕获,以进行实时处理和显示。 (增强现实应用程序)。

我可以很容易地访问像素,但似乎我无法让 SampleGrabber 提供 RGB 数据。设备(iSight - 在 VMWare 中运行 VC++ Express)仅报告 MEDIASUBTYPE_YUY2。

经过广泛的谷歌搜索后,我仍然无法弄清楚 DirectShow 是否应该为此类事情提供内置的色彩空间转换。一些网站报告没有内置 YUV<->RGB 转换,其他报告说您只需使用 RGB 子类型在 ISampleGrabber 上调用 SetMediaType 即可。

非常感谢任何建议,我对此非常着迷。下面提供了代码。请注意,

  • 该代码可以工作,只是它不提供 RGB 数据
  • 我知道我可以实现自己的转换过滤器,但这不可行,因为我必须预测每种可能的设备格式,并且这是一个相对较小的项目

    // 播放
    IGraphBuilder *pGraphBuilder = NULL;
    ICaptureGraphBuilder2 *pCaptureGraphBuilder2 = NULL;
    IMediaControl *pMediaControl = NULL;
    IBaseFilter *pDeviceFilter = NULL;
    IAMStreamConfig *pStreamConfig = NULL;
    字节 *videoCaps = NULL;
    AM_MEDIA_TYPE **mediaTypeArray = NULL;
    
    // 设备选择
    ICreateDevEnum *pCreateDevEnum = NULL;
    IEnumMoniker *pEnumMoniker = NULL;
    IMoniker *pMoniker = NULL;
    ULONG nFetched = 0;
    
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    
    // 创建 CreateDevEnum 来列出设备
    hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (PVOID *)&pCreateDevEnum);
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    // 创建 EnumMoniker 来列出设备 
    hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumMoniker, 0);
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    pEnumMoniker->Reset();
    
    // 找到想要的设备
    while (pEnumMoniker->Next(1, &pMoniker, &nFetched) == S_OK) 
    {
      IPropertyBag *pPropertyBag;
      TCHAR 设备名称[256];
    
      // 绑定到 IPropertyBag
      hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropertyBag);
    
      如果(失败(小时))
      {
        pMoniker->Release();
        继续;
      }
    
      VARIANT 变量名称;
      VariantInit(&varName);
      HRESULT hr = pPropertyBag->Read(L"DevicePath", &varName, 0);
    
      如果(失败(小时))
      {
        pMoniker->Release();
        pPropertyBag->Release();
        继续;
      }
    
      char devicePath[DeviceInfo::STRING_LENGTH_MAX] = "";
    
      wcstombs(devicePath, varName.bstrVal, DeviceInfo::STRING_LENGTH_MAX);
    
      if (strcmp(devicePath, deviceId) == 0)
      {
        // 将名字对象绑定到过滤器
        pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pDeviceFilter);
    
        休息;
      }
    
      pMoniker->Release();
      pPropertyBag->Release();
    }
    
    if (pDeviceFilter == NULL) 转到 ReleaseDataAndFail;
    
    // 创建样本采集器
    IBaseFilter *pGrabberF = NULL;
    hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pGrabberF);
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    hr = pGrabberF->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber);
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    // 创建过滤图
    hr = CoCreateInstance(CLSID_FilterGraph,
    无效的,
    CLSCTX_INPROC,
    IID_IGraphBuilder,
    (LPVOID *)&pGraphBuilder);
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    // 创建CaptureGraphBuilder2
    hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC, IID_ICaptureGraphBuilder2, (LPVOID *)&pCaptureGraphBuilder2);
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    // 设置过滤图
    hr = pCaptureGraphBuilder2->SetFiltergraph(pGraphBuilder);
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    // 获取MediaControl接口
    hr = pGraphBuilder->QueryInterface(IID_IMediaControl, (LPVOID *)&pMediaControl);
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    // 添加过滤器
    hr = pGraphBuilder->AddFilter(pDeviceFilter, L"设备过滤器");
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    hr = pGraphBuilder->AddFilter(pGrabberF, L"样本采集器");
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    // 设置样本采集器选项
    AM_MEDIA_TYPE mt;
    ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
    mt.majortype = MEDIATYPE_Video;
    mt.subtype = MEDIASUBTYPE_RGB32;
    hr = pGrabber->SetMediaType(&mt);
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    hr = pGrabber->SetOneShot(FALSE);
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    hr = pGrabber->SetBufferSamples(TRUE);
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    // 获取流配置接口
    hr = pCaptureGraphBuilder2->FindInterface(NULL, &MEDIATYPE_Video, pDeviceFilter, IID_IAMStreamConfig, (void **)&pStreamConfig);
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    intstreamCapsCount = 0,capsSize,bestFit = -1,bestFitPixelDiff = 1000000000,desiredPixelCount = _width * _height,
    最佳适配宽度 = 0,最佳适配高度 = 0;
    
    浮点期望宽高比 = (浮点)_宽度 / (浮点)_高度;
    
    hr = pStreamConfig->GetNumberOfCapability(&streamCapsCount, &capsSize);
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    videoCaps = (BYTE *)malloc(capsSize * streamCapsCount);
    mediaTypeArray = (AM_MEDIA_TYPE **)malloc(sizeof(AM_MEDIA_TYPE *) * streamCapsCount);
    
    for (int i = 0; i GetStreamCaps(i, &mediaTypeArray[i], videoCaps + capsSize * i);
      如果(失败(小时))继续;
    
      VIDEO_STREAM_CONFIG_CAPS *currentVideoCaps = (VIDEO_STREAM_CONFIG_CAPS *)(videoCaps + capsSize * i);
    
      int closeWidth = MAX(currentVideoCaps->MinOutputSize.cx, MIN(currentVideoCaps->MaxOutputSize.cx, width));
      int closeHeight = MAX(currentVideoCaps->MinOutputSize.cy, MIN(currentVideoCaps->MaxOutputSize.cy, height));
    
      int PixelDiff = ABS(desiredPixelCount - 最接近的宽度 * 最接近的高度);
    
      if (pixelDiff < bestFitPixelDiff && ABS(desiredAspectRatio - (float)closestWidth / (float)closestHeight) < 0.1f)
      {
        最佳拟合=我;
        bestFitPixelDiff = PixelDiff;
        最佳适配宽度 = 最接近宽度;
        最佳拟合高度 = 最接近高度;
      }
    }
    
    if (bestFit == -1) 转到 ReleaseDataAndFail;
    
    AM_MEDIA_TYPE *媒体类型;
    hr = pStreamConfig->GetFormat(&mediaType);
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    VIDEOINFOHEADER *videoInfoHeader = (VIDEOINFOHEADER *)mediaType->pbFormat;
    videoInfoHeader->bmiHeader.biWidth = bestFitWidth;
    videoInfoHeader->bmiHeader.biHeight = bestFitHeight;
    //媒体类型->子类型 = MEDIASUBTYPE_RGB32;
    hr = pStreamConfig->SetFormat(mediaType);
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    pStreamConfig->Release();
    pStreamConfig = NULL;
    
    免费(videoCaps);
    视频大小 = NULL;
    免费(媒体类型数组);
    媒体类型数组 = NULL;
    
    // 连接引脚
    IPin *pDeviceOut = NULL, *pGrabberIn = NULL;
    
    if (FindPin(pDeviceFilter, PINDIR_OUTPUT, 0, &pDeviceOut) && FindPin(pGrabberF, PINDIR_INPUT, 0, &pGrabberIn))
    {
      hr = pGraphBuilder->Connect(pDeviceOut, pGrabberIn);
      if (FAILED(hr)) 转到 ReleaseDataAndFail;
    }
    别的
    {
      转到发布数据并失败;
    }
    
    //开始播放
    hr = pMediaControl->Run();
    if (FAILED(hr)) 转到 ReleaseDataAndFail;
    
    hr = pGrabber->GetConnectedMediaType(&mt);
    
    // 设置尺寸
    宽度=最佳适配宽度;
    高度=最佳适合高度;
    _width = 最佳适配宽度;
    _高度=最佳适合高度;
    
    // 分配像素缓冲区
    pPixelBuffer = (无符号*)malloc(宽度*高度*4);
    
    // 释放对象
    pGraphBuilder->Release();
    pGraphBuilder = NULL;
    pEnumMoniker->Release();
    pEnumMoniker = NULL;
    pCreateDevEnum->Release();
    pCreateDevEnum = NULL;
    
    返回真;
    

I'm implementing live video capture through DirectShow for live processing and display. (Augmented Reality app).

I can access the pixels easily enough, but it seems I can't get the SampleGrabber to provide RGB data. The device (an iSight -- running VC++ Express in VMWare) only reports MEDIASUBTYPE_YUY2.

After extensive Googling, I still can't figure out whether DirectShow is supposed to provide built-in color space conversion for this sort of thing. Some sites report that there is no YUV<->RGB conversion built in, others report that you just have to call SetMediaType on your ISampleGrabber with an RGB subtype.

Any advice is greatly appreciated, I'm going nuts on this one. Code provided below. Please note that

  • The code works, except that it doesn't provide RGB data
  • I'm aware that I can implement my own conversion filter, but this is not feasible because I'd have to anticipate every possible device format, and this is a relatively small project

    // Playback
    IGraphBuilder *pGraphBuilder = NULL;
    ICaptureGraphBuilder2 *pCaptureGraphBuilder2 = NULL;
    IMediaControl *pMediaControl = NULL;
    IBaseFilter *pDeviceFilter = NULL;
    IAMStreamConfig *pStreamConfig = NULL;
    BYTE *videoCaps = NULL;
    AM_MEDIA_TYPE **mediaTypeArray = NULL;
    
    // Device selection
    ICreateDevEnum *pCreateDevEnum = NULL;
    IEnumMoniker *pEnumMoniker = NULL;
    IMoniker *pMoniker = NULL;
    ULONG nFetched = 0;
    
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    
    // Create CreateDevEnum to list device
    hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (PVOID *)&pCreateDevEnum);
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    // Create EnumMoniker to list devices 
    hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumMoniker, 0);
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    pEnumMoniker->Reset();
    
    // Find desired device
    while (pEnumMoniker->Next(1, &pMoniker, &nFetched) == S_OK) 
    {
      IPropertyBag *pPropertyBag;
      TCHAR devname[256];
    
      // bind to IPropertyBag
      hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropertyBag);
    
      if (FAILED(hr))
      {
        pMoniker->Release();
        continue;
      }
    
      VARIANT varName;
      VariantInit(&varName);
      HRESULT hr = pPropertyBag->Read(L"DevicePath", &varName, 0);
    
      if (FAILED(hr))
      {
        pMoniker->Release();
        pPropertyBag->Release();
        continue;
      }
    
      char devicePath[DeviceInfo::STRING_LENGTH_MAX] = "";
    
      wcstombs(devicePath, varName.bstrVal, DeviceInfo::STRING_LENGTH_MAX);
    
      if (strcmp(devicePath, deviceId) == 0)
      {
        // Bind Moniker to Filter
        pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pDeviceFilter);
    
        break;
      }
    
      pMoniker->Release();
      pPropertyBag->Release();
    }
    
    if (pDeviceFilter == NULL) goto ReleaseDataAndFail;
    
    // Create sample grabber
    IBaseFilter *pGrabberF = NULL;
    hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pGrabberF);
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    hr = pGrabberF->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber);
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    // Create FilterGraph
    hr = CoCreateInstance(CLSID_FilterGraph,
    NULL,
    CLSCTX_INPROC,
    IID_IGraphBuilder,
    (LPVOID *)&pGraphBuilder);
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    // create CaptureGraphBuilder2
    hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC, IID_ICaptureGraphBuilder2, (LPVOID *)&pCaptureGraphBuilder2);
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    // set FilterGraph
    hr = pCaptureGraphBuilder2->SetFiltergraph(pGraphBuilder);
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    // get MediaControl interface
    hr = pGraphBuilder->QueryInterface(IID_IMediaControl, (LPVOID *)&pMediaControl);
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    // Add filters
    hr = pGraphBuilder->AddFilter(pDeviceFilter, L"Device Filter");
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    hr = pGraphBuilder->AddFilter(pGrabberF, L"Sample Grabber");
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    // Set sampe grabber options
    AM_MEDIA_TYPE mt;
    ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
    mt.majortype = MEDIATYPE_Video;
    mt.subtype = MEDIASUBTYPE_RGB32;
    hr = pGrabber->SetMediaType(&mt);
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    hr = pGrabber->SetOneShot(FALSE);
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    hr = pGrabber->SetBufferSamples(TRUE);
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    // Get stream config interface
    hr = pCaptureGraphBuilder2->FindInterface(NULL, &MEDIATYPE_Video, pDeviceFilter, IID_IAMStreamConfig, (void **)&pStreamConfig);
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    int streamCapsCount = 0, capsSize, bestFit = -1, bestFitPixelDiff = 1000000000, desiredPixelCount = _width * _height,
    bestFitWidth = 0, bestFitHeight = 0;
    
    float desiredAspectRatio = (float)_width / (float)_height;
    
    hr = pStreamConfig->GetNumberOfCapabilities(&streamCapsCount, &capsSize);
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    videoCaps = (BYTE *)malloc(capsSize * streamCapsCount);
    mediaTypeArray = (AM_MEDIA_TYPE **)malloc(sizeof(AM_MEDIA_TYPE *) * streamCapsCount);
    
    for (int i = 0; i < streamCapsCount; i++)
    {
      hr = pStreamConfig->GetStreamCaps(i, &mediaTypeArray[i], videoCaps + capsSize * i);
      if (FAILED(hr)) continue;
    
      VIDEO_STREAM_CONFIG_CAPS *currentVideoCaps = (VIDEO_STREAM_CONFIG_CAPS *)(videoCaps + capsSize * i);
    
      int closestWidth = MAX(currentVideoCaps->MinOutputSize.cx, MIN(currentVideoCaps->MaxOutputSize.cx, width));
      int closestHeight = MAX(currentVideoCaps->MinOutputSize.cy, MIN(currentVideoCaps->MaxOutputSize.cy, height));
    
      int pixelDiff = ABS(desiredPixelCount - closestWidth * closestHeight);
    
      if (pixelDiff < bestFitPixelDiff && ABS(desiredAspectRatio - (float)closestWidth / (float)closestHeight) < 0.1f)
      {
        bestFit = i;
        bestFitPixelDiff = pixelDiff;
        bestFitWidth = closestWidth;
        bestFitHeight = closestHeight;
      }
    }
    
    if (bestFit == -1) goto ReleaseDataAndFail;
    
    AM_MEDIA_TYPE *mediaType;
    hr = pStreamConfig->GetFormat(&mediaType);
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    VIDEOINFOHEADER *videoInfoHeader = (VIDEOINFOHEADER *)mediaType->pbFormat;
    videoInfoHeader->bmiHeader.biWidth = bestFitWidth;
    videoInfoHeader->bmiHeader.biHeight = bestFitHeight;
    //mediaType->subtype = MEDIASUBTYPE_RGB32;
    hr = pStreamConfig->SetFormat(mediaType);
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    pStreamConfig->Release();
    pStreamConfig = NULL;
    
    free(videoCaps);
    videoCaps = NULL;
    free(mediaTypeArray);
    mediaTypeArray = NULL;
    
    // Connect pins
    IPin *pDeviceOut = NULL, *pGrabberIn = NULL;
    
    if (FindPin(pDeviceFilter, PINDIR_OUTPUT, 0, &pDeviceOut) && FindPin(pGrabberF, PINDIR_INPUT, 0, &pGrabberIn))
    {
      hr = pGraphBuilder->Connect(pDeviceOut, pGrabberIn);
      if (FAILED(hr)) goto ReleaseDataAndFail;
    }
    else
    {
      goto ReleaseDataAndFail;
    }
    
    // start playing
    hr = pMediaControl->Run();
    if (FAILED(hr)) goto ReleaseDataAndFail;
    
    hr = pGrabber->GetConnectedMediaType(&mt);
    
    // Set dimensions
    width = bestFitWidth;
    height = bestFitHeight;
    _width = bestFitWidth;
    _height = bestFitHeight;
    
    // Allocate pixel buffer
    pPixelBuffer = (unsigned *)malloc(width * height * 4);
    
    // Release objects
    pGraphBuilder->Release();
    pGraphBuilder = NULL;
    pEnumMoniker->Release();
    pEnumMoniker = NULL;
    pCreateDevEnum->Release();
    pCreateDevEnum = NULL;
    
    return true;
    

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

ι不睡觉的鱼゛ 2024-08-20 06:39:32

库存色彩空间转换器不支持 YUY2 到 RGB 转换。但是,有许多应用程序和设备安装了某种转换器,如果正确注册,dshow 将自动使用它。这就是为什么有些人报告说它确实有效。 (当然,有些设备提供 RGB,因此在这些情况下不需要转换)。

您可以从 YUV Transform(位于页面)。在您的系统上注册它,它应该允许以任何合理的 RGB 或 YUV 格式捕获。

G

The stock colour space converter does not support YUY2 to RGB conversion. However, there are a number of apps and devices that install a converter of some sort, and if this is properly registered, dshow will use it automatically. That's why some people report that it just works. (of course some devices offer RGB, so no conversion is needed in those cases).

You can download a freely-available YUV conversion filter, "yuvxfm" from YUV Transform (at the bottom of the page). Register this on your system and it should allow capture in any reasonable RGB or YUV format.

G

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文