如何使用DirectShow捕获RGB中的实时相机帧

时间:2009-12-01 17:19:09

标签: visual-c++ camera directshow video-capture augmented-reality

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

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

经过广泛的谷歌搜索后,我仍然无法弄清楚DirectShow是否应该为这类东西提供内置的色彩空间转换。有些网站报告内置了YUV< - > RGB转换,others报告您只需要在ISampleGrabber上使用RGB子类型调用SetMediaType。

非常感谢任何建议,我对这一点感到疯狂。代码如下。请注意

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

    // 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;
    

1 个答案:

答案 0 :(得分:5)

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

您可以从YUV Transform (at the bottom of the page)下载免费提供的YUV转换过滤器“yuvxfm”。在您的系统上注册它,它应该允许以任何合理的RGB或YUV格式捕获。