2016-11-14 106 views
1

我從網絡捕獲了一段代碼以捕獲視頻文件中的一幀,並對其進行修改以捕獲所有幀並將其存儲爲bmp圖像。使用C++中的directshow過濾器捕獲視頻中的幀

HRESULT GrabVideoBitmap(PCWSTR pszVideoFile) 
{ 
    IGraphBuilder *pGraph = NULL; 
    IMediaControl *pControl = NULL; 
    IMediaEventEx *pEvent = NULL; 
    IBaseFilter *pGrabberF = NULL; 
    ISampleGrabber *pGrabber = NULL; 
    IBaseFilter *pSourceF = NULL; 
    IEnumPins *pEnum = NULL; 
    IPin *pPin = NULL; 
    IBaseFilter *pNullF = NULL; 

    long evCode; 

    wchar_t temp[10]; 
    wchar_t framename[50] = IMAGE_FILE_PATH; // L"D:\\sampleframe"; 

    BYTE *pBuffer = NULL; 
    HRESULT hr = CoInitialize(NULL); 
    if (FAILED(hr)) 
     return 0; 

    hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, 
          IID_PPV_ARGS(&pGraph)); 

    hr = pGraph->QueryInterface(IID_PPV_ARGS(&pControl)); 

    hr = pGraph->QueryInterface(IID_PPV_ARGS(&pEvent)); 

    // Create the Sample Grabber filter. 
    hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, 
          IID_PPV_ARGS(&pGrabberF)); 

    hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber"); 

    hr = pGrabberF->QueryInterface(IID_PPV_ARGS(&pGrabber)); 


    // Displays the metadata of the file 
    DisplayFileInfo((wchar_t*)pszVideoFile); // to display video information 

    AM_MEDIA_TYPE mt; 
    ZeroMemory(&mt, sizeof(mt)); 
    mt.majortype = MEDIATYPE_Video; 
    mt.subtype = MEDIASUBTYPE_RGB24; 

    hr = pGrabber->SetMediaType(&mt); 

    hr = pGraph->AddSourceFilter(pszVideoFile, L"Source", &pSourceF); 

    hr = pSourceF->EnumPins(&pEnum); 

    while (S_OK == pEnum->Next(1, &pPin, NULL)) 
    { 
     hr = ConnectFilters(pGraph, pPin, pGrabberF); 
     SafeRelease(&pPin); 
     if (SUCCEEDED(hr)) 
     { 
      break; 
     } 
    } 

    hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, 
          IID_PPV_ARGS(&pNullF)); 

    hr = pGraph->AddFilter(pNullF, L"Null Filter"); 

    hr = ConnectFilters(pGraph, pGrabberF, pNullF); 

    hr = pGrabber->SetOneShot(TRUE); 

    hr = pGrabber->SetBufferSamples(TRUE); 

    hr = pControl->Run(); 


    hr = pEvent->WaitForCompletion(INFINITE, &evCode); 

    for (int i = 0; i < 10; i++) 
    { 
     // Find the required buffer size. 
     long cbBuffer; 
     hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL); 

     pBuffer = (BYTE*)CoTaskMemAlloc(cbBuffer); 

     hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*)pBuffer); 

     hr = pGrabber->GetConnectedMediaType(&mt); 

     // Examine the format block. 
     if ((mt.formattype == FORMAT_VideoInfo) && 
      (mt.cbFormat >= sizeof(VIDEOINFOHEADER)) && 
      (mt.pbFormat != NULL)) 
     { 

      swprintf(temp, 5, L"%d", i); 
      wcscat_s(framename, temp); 
      wcscat_s(framename, L".bmp"); 

      VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat; 
      hr = WriteBitmap((PCWSTR)framename, &pVih->bmiHeader, 
        mt.cbFormat - SIZE_PREHEADER, pBuffer, cbBuffer); 
      wcscpy_s(framename, IMAGE_FILE_PATH); 
     } 
     else 
     { 
      // Invalid format. 
      hr = VFW_E_INVALIDMEDIATYPE; 
     } 

     FreeMediaType(mt); 
    } 
done: 
    CoTaskMemFree(pBuffer); 
    SafeRelease(&pPin); 
    SafeRelease(&pEnum); 
    SafeRelease(&pNullF); 
    SafeRelease(&pSourceF); 
    SafeRelease(&pGrabber); 
    SafeRelease(&pGrabberF); 
    SafeRelease(&pControl); 
    SafeRelease(&pEvent); 
    SafeRelease(&pGraph); 
    return hr; 
} 

輸入視頻文件有132幀。 但僅生成68個圖像。 還爲最後的38張圖像捕獲視頻的最後一幀。

我認爲directshow圖表連續運行,WriteBitmap()缺少幀。

如何讓directX中的控件捕獲一幀並將其寫入bmp文件並捕獲下一幀,從而將所有幀捕獲爲bmp圖像。

感謝 阿倫

回答

2

你的做法是錯誤的。目前,您將採樣器設置爲一次,然後等待圖形完成。這種方式僅適用於捕獲單個幀。您需要捕獲pGrabber的ISampleGrabberCB回調中的幀。您需要實現ISampleGrabberCB接口並在您的pGrabber過濾器上使用ISampleGrabber :: SetCallback將其指向您的實現。之後,您可以捕獲SampleCB或BufferCB方法內的幀。 http://www.infognition.com/blog/2013/accessing_raw_video_in_directshow.html