您的位置:首页 > 编程语言 > C语言/C++

浅谈视频采集方案

2016-03-11 10:43 375 查看
现在手头上有一个项目就是需要优化采集方案。

我们这边之前使用的是作者Shiqi Yu写的开源代码,底层是基于DShow做的封装。但使用后发现采集到的视频流在进行回显时有点模糊,特别是文字部分。

现在通过万能的网络找到了三种替换方案:WebRTC 接、MediaFoundation、VideoInput。

1、WebRTC:现被Google整编,但由于需要翻墙才能下载和更新,所以没有使用。

2、MediaFoundation:微软基于DShow上做的优化框架,回显也明显清晰流畅了许多,但是继承了微软的封装特性,所以想嵌入自己的逻辑,相对于比较麻烦,所以放弃。

获取视频采集设备。

HRESULT hr = S_OK;
IMFAttributes *pAttributes;
if (SUCCEEDED(hr))
{
hr = MFStartup(MF_VERSION); //这是必须的,不然无法绑定回调资源
}

if (SUCCEEDED(hr))
{
hr = MFCreateAttributes(&pAttributes, 1);
}

if (SUCCEEDED(hr))
{
hr = m_pAttributes->SetGUID(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID
);
}

if (SUCCEEDED(hr))
{
hr = MFEnumDeviceSources(m_pAttributes, &m_param.ppDevices, &m_param.count);
}

SafeRelease(&pAttributes);


打开一个采集设备

HRESULT hr = S_OK;
IMFMediaSource *pSource = NULL;
EnterCriticalSection(&m_critsec);

IMFActivate* pActivate  = m_param.ppDevices[nCamID];
pActivate->AddRef();

// Create the media source for the device.
if (SUCCEEDED(hr))
{
hr = pActivate->ActivateObject(__uuidof(IMFMediaSource),
(void**)&pSource
);
}

// Get the symbolic link. This is needed to handle device-
// loss notifications. (See CheckDeviceLost.)

if (SUCCEEDED(hr))
{
hr = pActivate->GetAllocatedString(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK,
&m_pwszSymbolicLink,
&m_cchSymbolicLink
);
}

WCHAR *szFriendlyName = NULL;
if (SUCCEEDED(hr))
{
hr = pActivate->GetAllocatedString(
MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME,
&szFriendlyName,
NULL
);
}

UINT32 unMaxSize = 0;
if (SUCCEEDED(hr))
{
hr = pActivate->GetUINT32(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_MAX_BUFFERS,
&unMaxSize
);
}

// When the method completes, MFPlay will call OnMediaPlayerEvent
// with the MFP_EVENT_TYPE_MEDIAITEM_CREATED event.
if (SUCCEEDED(hr))
{
hr = CreateSourceReaderAsync(pSource);
}

if (SUCCEEDED(hr))
{
m_pSource = pSource;
m_pSource->AddRef();
}

if (FAILED(hr))
{
CloseCamera();
}
else
{
m_ob = ob;
Start();
}

SafeRelease(&pSource);
SafeRelease(&pActivate);
LeaveCriticalSection(&m_critsec);

视频采集器绑定视频资源与回调
HRESULT hr = S_OK;
IMFAttributes *pAttributes = NULL;
IMFMediaType * nativeType = NULL;

hr = MFCreateAttributes(&pAttributes, 2);

if (SUCCEEDED(hr))
{
hr = pAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, this);
}

if (SUCCEEDED(hr))
{
hr = MFCreateSourceReaderFromMediaSource(pSource,pAttributes,&m_pSourceReader);
}

if (SUCCEEDED(hr))
{
hr = m_pSourceReader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,NULL,nativeType);
}
SafeRelease(&nativeType);
SafeRelease(&pAttributes);

视频流读取

if (FAILED(hrStatus))
{
hr = hrStatus;
LeaveCriticalSection(&m_critsec);
return hr;
}

if(pSample )
{
DWORD maxLength,currentLength;
BYTE* pBuffer = NULL;
BYTE * pBitmapData = NULL;
IMFMediaBuffer* pMBuffer = NULL;
hr = pSample->GetBufferByIndex(0, &pMBuffer);

//hr = pSample->ConvertToContiguousBuffer(&pMBuffer);
if(SUCCEEDED(hr))
{
hr = pMBuffer->Lock(&pBuffer,&maxLength,¤tLength);
}

if(SUCCEEDED(hr))
{
hr = pMBuffer->Unlock();

if(currentLength > 0)
m_ob->onRGB((const char*)pBuffer,currentLength);

SafeRelease(&pMBuffer);
}
}

3、VideoInput 被OpenCV收入的一个非常实用的开源视频采集库,现在我就使用的这个库。

打开一个采集设备

if(pCamara)
{
delete pCamara;
pCamara = NULL;
}

//videoInput::setComMultiThreaded(true); //如果项目中使用到不支持多线程的COM库,请不要执行此段代码
pCamara = (void*)(new 	videoInput());
((videoInput*)pCamara)->setIdealFramerate(nCamID,25);  //设置当前采集帧率默认为25帧,实际可能大于25帧。
bool bRs = ((videoInput*)pCamara)->setupDevice(nCamID,nWidth,nHeight);
if(bRs)
{
m_ob = ob;
nDeviceId = nCamID;
((videoInput*)pCamara)->setUseCallback(true); //设置当前采集使用回调的方式获取数据
hThread = (void*)CreateThread(NULL,0,OnCaptureFrame,this,0,0);
m_bIsRun = true;
}

通过推的方式采集RGB数据

CCameraVI* pThis = (CCameraVI*)lpParam;
while(pThis->isRun())
{
if(pThis->isFrameNew())
{
const char* pData = (const char*)pThis->GetFrame();
pThis->getObserver()->onRGB(pData,pThis->GetWidth()*pThis->GetHeight()*3);
}
else
{
Sleep(1);
}
}
return 0;


整个采集逻辑就是这么简单的两段代码实现。

获取设备数量

videoInput::listDevices();

获取设备名称

videoInput::listDevices();
strncpy(sName,videoInput::getDeviceName(nCamID),nBufferSize);

通过拉的方式采集RGB数据

if (pCamara )
{
return ((videoInput*)pCamara)->getPixels(nDeviceId,false);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息