4

我正在通過DirectShow實現實時視頻捕捉以進行實時處理和顯示。 (增強現實應用程序)。如何使用DirectShow捕捉RGB中的實時相機幀

我可以很容易地訪問像素,但似乎我無法讓SampleGrabber提供RGB數據。該設備(在VMware中運行iSight的VC++ Express)僅報告MEDIASUBTYPE_YUY2。

經過廣泛的谷歌搜索之後,我仍然無法弄清楚DirectShow是否應該爲這類事情提供內置的色彩空間轉換。有些網站報告說沒有YUV < - > RGB轉換內置,others報告,您只需調用ISampleGrabber上的SetMediaType和RGB子類型。

任何意見,非常感謝,我瘋了這一個。代碼如下。請注意,

  • 代碼工作,但它不提供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; 
    

回答

4

股票色彩空間轉換器不支持YUY2到RGB轉換。但是,有許多應用程序和設備可以安裝某種類型的轉換器,如果這種轉換器已正確註冊,則dshow會自動使用它。這就是爲什麼有些人報告說它是正常的。 (當然有些設備提供RGB,所以在這些情況下不需要轉換)。

您可以從YUV Transform (at the bottom of the page)下載免費的YUV轉換濾鏡「yuvxfm」。將它註冊到您的系統上,它應該允許以任何合理的RGB或YUV格式進行捕獲。

G

+0

非常感謝您的豐富迴應。我嘗試在設備和採樣器之間添加一個yuvxfm濾波器,該濾波器可以工作。但是,我也發現,如果它也沒有工作。我試着取消註冊yuvxfm並直接從設備到rgb採樣卡。我不知道爲什麼它以前沒有工作。我正在運行幾乎全新安裝的XP SP3。在Windows 7上也嘗試過,這也適用。 那麼YUY2到RGB的轉換髮生在哪裏?我可以依靠這個嗎? – Simplex 2009-12-02 19:02:08

+0

是的。它的AviDecompressor支持YUY2 FourCC。有關提供的過濾器列表,請參閱http://msdn.microsoft.com/en-us/library/dd375464%28v=VS.85%29.aspx。 – Christopher 2010-06-08 07:34:18

+0

AviDecompressor是Video for Windows編解碼器的封裝器。您可能安裝了可以進行YUY2轉換的VfW編解碼器,但這將取決於系統。在某些系統上,Avi Decompressor包裝過濾器可能沒有YUY2支持。 – 2010-06-08 20:47:36

相關問題