Visual C ++ AVI编写器功能将位图(640x480)推送到AVI文件?

时间:2022-11-07 20:06:40

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.

我有一个带有Visual C ++ SDK的视频采集卡。在SDK的回调中,我可以以30 fps的速度使用彩色帧(640 x 480)。目前,我正在将整个图像序列作为单独的bmp文件一次写出一个单独的线程 - 一小时内是108,000个文件,或者每小时大约100 GB,这是不可管理的。我想将这些传入的帧推送到一个AVI文件,而不是可选的压缩。我从哪里开始?到目前为止,浏览MSDN DirectShow文档让我很困惑。那里有更好的例子吗? OpenCV是答案吗?我看了一些例子,但我不确定OpenCV是否会将该卡识别为捕获设备,我也不了解它是如何识别捕获设备的。此外,我已经获得了帧,我只需要在一些不支持我的生产者线程的消费者线程中将它们放到AVI中。谢谢你的帮助。

3 个解决方案

#1


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。它工作得很好,我不得不稍微调整它以允许用户选择编解码器。我从CAviGenerator中获取了该代码。 CAviFile的界面非常简单,下面是一些示例代码:

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.

显然你必须确保你的ImageBuffer包含正确格式的数据等。但是一旦我得到那些东西,所有整理出来的效果都很好。

#2


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

您可以使用Video for Windows或DirectShow。每个都有自己的编解码器。 (可以延长)

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

虽然微软认为VfW已被弃用,但它仍然完全可用,并且比DirectShow更容易设置。

#3


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.

那么你需要将AVI Mux(CLSID_AviDest)连接到你的捕获卡。然后,您需要附加文件编写器(CLSID_FileWriter),它将为您写出所有内容。

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

不可否认,设置捕获图并不一定容易,因为DirectShow会让你跳过一百万个箍。

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

使用ICaptureGraphBuilder2界面更容易。值得庆幸的是,微软已经给出了如何做到这一点的非常好的概述......

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.

这是一个如何枚举我为我的MFC应用程序编写的系统中的所有视频压缩器的示例。

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! :)

祝好运! :)

#1


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。它工作得很好,我不得不稍微调整它以允许用户选择编解码器。我从CAviGenerator中获取了该代码。 CAviFile的界面非常简单,下面是一些示例代码:

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.

显然你必须确保你的ImageBuffer包含正确格式的数据等。但是一旦我得到那些东西,所有整理出来的效果都很好。

#2


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

您可以使用Video for Windows或DirectShow。每个都有自己的编解码器。 (可以延长)

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

虽然微软认为VfW已被弃用,但它仍然完全可用,并且比DirectShow更容易设置。

#3


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.

那么你需要将AVI Mux(CLSID_AviDest)连接到你的捕获卡。然后,您需要附加文件编写器(CLSID_FileWriter),它将为您写出所有内容。

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

不可否认,设置捕获图并不一定容易,因为DirectShow会让你跳过一百万个箍。

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

使用ICaptureGraphBuilder2界面更容易。值得庆幸的是,微软已经给出了如何做到这一点的非常好的概述......

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.

这是一个如何枚举我为我的MFC应用程序编写的系统中的所有视频压缩器的示例。

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! :)

祝好运! :)