views:

1065

answers:

4

I have a video capture card with SDK for Visual C++. Color frames (640 x 480) become available to me at 30 fps in a callback from the SDK. Currently, I am writing the entire image sequence out one at a time as individual bmp files in a separate thread -- that's 108,000 files in an hour, or about 100 GB per hour, which is not manageable. I would like to push these incoming frames to one AVI file instead, with optional compression. Where do I even start? Wading through the MSDN DirectShow documentation has confused me so far. Are there better examples out there? Is OpenCV the answer? I've looked at some examples, but I'm not sure OpenCV would even recognize the card as a capture device, nor do I understand how it even recognizes capture devices in the first place. Also, I'm already getting the frames in, I just need to put them out to AVI in some consumer thread that does not back up my producer thread. Thanks for any help.

A: 

You can either use Video for Windows or DirectShow. Each comes with its own set of codecs. (and can be extended)

Though Microsoft considers VfW deprecated it is still perfectly usable, and is easier to setup than DirectShow.

EFraim
+1  A: 

I've used CAviFile before. It works pretty well, I had to tweak it a bit to allow the user to pick the codec. I took that code from CAviGenerator. The interface for CAviFile is very simple, here's some sample code:

CAviFile *Avi = new CAviFile(fileName.c_str(), 0, 10);

HRESULT res = Avi->AppendNewFrame(Width, Height, ImageBuffer, BitsPerPixel);
if (FAILED(res))
{
    std::cout << "Error recording AVI: " << Avi->GetLastErrorMessage() << std::endl;
}

delete Avi;

Obviously you have to ensure your ImageBuffer contains data in the right format etc. But once I got that kind of stuff all sorted out it worked great.

Gabe
@JCoenia, I just used it to capture a video at 1600x1200, so if there are limits on VFW, then they're pretty high.
Gabe
A: 

Thanks Gabe and EFraim. I thought VFW had limits on AVI image size? GMan, it's an ImperX card, a capture card for notebooks, although you can always get a bus bridge and put one in a desktop. Their SDK programmers are Russian, so there are some language and time zone issues when getting support.

A: 

Well you need to attach an AVI Mux (CLSID_AviDest) to your capture card. You then need to attach a File Writer (CLSID_FileWriter) and it will write out everything for you.

Admittedly Setting up the capture graph is not necessarily easy as DirectShow makes you jump through a million and one hoops.

Its much easier using the ICaptureGraphBuilder2 interface. Thankfully Microsoft have given a really nice rundown of how to do this ...

http://msdn.microsoft.com/en-us/library/dd318627.aspx

Adding an encoder is not easy though and, conveniently, glossed over in that link.

Here is an example of how to enumerate all the video compressors in a system that I wrote for an MFC app of mine.

BOOL LiveInputDlg::EnumerateVideoCompression()
{
    CComboBox* pVideoCompression = (CComboBox*)GetDlgItem( IDC_COMBO_VIDEOCOMPRESSION );
    pVideoCompression->SetExtendedUI( TRUE );

    pVideoCompression->SetCurSel( pVideoCompression->AddString( _T( "<None>" ) ) );


    ICreateDevEnum* pDevEnum = NULL;
    IEnumMoniker* pEnum  = NULL;

    HRESULT hr = S_OK;
    hr = CoCreateInstance( CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pDevEnum );
    if ( FAILED( hr ) )
    {
     return FALSE;
    }

    hr = pDevEnum->CreateClassEnumerator( CLSID_VideoCompressorCategory, &pEnum, 0 );

    pDevEnum->Release();
    if ( FAILED( hr ) )
    {
     return FALSE;
    }

    if ( pEnum )
    {
     IMoniker* pMoniker = NULL;
     hr = pEnum->Next( 1, &pMoniker, NULL );
     while( hr == S_OK )
     {
      IPropertyBag* pPropertyBag = NULL;
      hr = pMoniker->BindToStorage( NULL, NULL, IID_IPropertyBag, (void**)&pPropertyBag );
      if ( FAILED( hr ) )
      {
       pMoniker->Release();
       pEnum->Release();
       return FALSE;
      }

      VARIANT varName;
      VariantInit( &varName );
      hr = pPropertyBag->Read( L"Description", &varName, NULL );
      if ( FAILED( hr ) )
      {
       hr = pPropertyBag->Read( L"FriendlyName", &varName, NULL );
       if ( FAILED( hr ) )
       {
        pPropertyBag->Release();
        pMoniker->Release();
        pEnum->Release();

        return FALSE;
       }
      }

      IBaseFilter* pBaseFilter = NULL;
      pMoniker->BindToObject( NULL, NULL, IID_IBaseFilter, (void**)&pBaseFilter );

      {
       USES_CONVERSION;
       TCHAR* pName = OLE2T( varName.bstrVal );
       int index  = pVideoCompression->AddString( pName );
       pVideoCompression->SetItemDataPtr( index, pMoniker );

       VariantClear( &varName );
       pPropertyBag->Release();
      }

      hr = pEnum->Next( 1, &pMoniker, NULL );
     }

     pEnum->Release();
    }
    return TRUE;
}

Good Luck! :)

Goz