我有一个带有 SDK for Visual C++ 的视频采集卡。在 SDK 的回调中,我可以以 30 fps 的速度使用彩色帧 (640 x 480)。目前,我正在将整个图像序列作为单独的 bmp 文件一次写入一个单独的线程中——即一小时内 108,000 个文件,或每小时约 100 GB,这是无法管理的。我想将这些传入的帧推送到一个 AVI 文件,而不是可选压缩。我什至从哪里开始?到目前为止,翻阅 MSDN DirectShow 文档让我感到困惑。那里有更好的例子吗?OpenCV 是答案吗?我看过一些例子,但我不确定 OpenCV 是否会将卡识别为捕获设备,我也不明白它是如何识别捕获设备的。另外,我已经把框架放进去了,我只需要将它们放在一些不支持我的生产者线程的消费者线程中的 AVI 中。谢谢你的帮助。
3 回答
我以前用过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;
显然,您必须确保您的 ImageBuffer 包含正确格式的数据等。但是一旦我把这些东西都整理好,它就很好用了。
您可以使用 Video for Windows 或 DirectShow。每个都有自己的编解码器集。(并且可以扩展)
尽管微软认为 VfW 已被弃用,但它仍然完全可用,并且比 DirectShow 更容易设置。
好吧,您需要将 AVI Mux (CLSID_AviDest) 附加到您的采集卡。然后,您需要附加一个文件编写器 (CLSID_FileWriter),它会为您写出所有内容。
诚然,设置捕获图并不一定容易,因为 DirectShow 会让您跳过一百万又一圈。
使用 ICaptureGraphBuilder2 接口要容易得多。值得庆幸的是,微软已经给出了如何做到这一点的非常好的概要......
http://msdn.microsoft.com/en-us/library/dd318627.aspx
但是添加编码器并不容易,并且方便地在该链接中掩盖了。
这是我为我的 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;
}
祝你好运!:)