2024-06-23 17:36:53 +08:00

399 lines
9.4 KiB
C++

// CaptureVideo.cpp: implementation of the CCaptureVideo class.
//
//////////////////////////////////////////////////////////////////////
#include "CaptureVideo.h"
#include <atlbase.h>
#pragma comment(lib, "strmiids.lib")
#pragma comment(lib, "strmbase.lib")
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CCaptureVideo::CCaptureVideo()
{
if(FAILED(CoInitialize(NULL))) /*, COINIT_APARTMENTTHREADED)))*/
return;
m_pCapture = NULL;
m_pGB = NULL;
m_pMC = NULL;
m_pBF = NULL;
m_pGrabber = NULL;
m_pNullRenderer = NULL;
m_nWidth = 0;
m_nHeight = 0;
m_bIsStarted = FALSE;
m_pEnc = NULL;
}
CCaptureVideo::~CCaptureVideo()
{
if (m_pEnc)
{
if (m_bIsStarted)
{
m_pEnc->Close();
}
delete m_pEnc ;
m_pEnc = NULL;
Stop();
}
CoUninitialize();
}
LPBITMAPINFO CCaptureVideo::GetBmpInfo()
{
return mCB.GetBmpInfo();
}
void CCaptureVideo::SendEnd() //发送结束 设置可以再取数据
{
InterlockedExchange((LPLONG)&mCB.bStact,CMD_CAN_COPY); //原子自增可以发送 //原子自减 发送完毕 可以copy
}
LPBYTE CCaptureVideo::GetDIB(DWORD& nSize)
{
BYTE *buf = NULL;
if (mCB.bStact==CMD_CAN_SEND) //这里改变了一下发送的状态
{
buf = mCB.GetNextScreen(nSize);
}
if (buf == NULL)
return NULL;
return m_pEnc->Encode(buf,nSize);
}
HRESULT CCaptureVideo::InitCaptureGraphBuilder()
{
HRESULT hr;
// 创建IGraphBuilder接口
hr=CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&m_pGB);
// 创建ICaptureGraphBuilder2接口
hr = CoCreateInstance (CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC,
IID_ICaptureGraphBuilder2, (void **) &m_pCapture);
if (FAILED(hr))
return hr;
m_pCapture->SetFiltergraph(m_pGB);
hr = m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC);
if (FAILED(hr))
return hr;
return hr;
}
bool CCaptureVideo::BindFilter(int deviceId, IBaseFilter **pFilter)
{
if (deviceId < 0)
return false;
// 举所有视频捕捉设备
CComPtr<ICreateDevEnum> pCreateDevEnum;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,IID_ICreateDevEnum, (void**)&pCreateDevEnum);
if (hr != NOERROR)
return false;
CComPtr<IEnumMoniker> pEm;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEm, 0);
if (hr != NOERROR)
return false;
pEm->Reset();
ULONG cFetched;
IMoniker *pM;
int index = 0;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK, index <= deviceId)
{
IPropertyBag *pBag;
hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
if(SUCCEEDED(hr))
{
VARIANT var;
var.vt = VT_BSTR;
hr = pBag->Read(L"FriendlyName", &var, NULL);
if (hr == NOERROR)
{
if (index == deviceId)
{
pM->BindToObject(0, 0, IID_IBaseFilter, (void**)pFilter);
}
SysFreeString(var.bstrVal);
}
pBag->Release();
}
pM->Release();
index++;
}
return true;
}
void CCaptureVideo::FreeMediaType(AM_MEDIA_TYPE& mt)
{
if (mt.cbFormat != 0)
{
CoTaskMemFree((PVOID)mt.pbFormat);
// Strictly unnecessary but tidier
mt.cbFormat = 0;
mt.pbFormat = NULL;
}
if (mt.pUnk != NULL)
{
mt.pUnk->Release();
mt.pUnk = NULL;
}
}
HRESULT CCaptureVideo::Start(int iDeviceID, HWND hWnd , int width, int height)
{
HRESULT hr;
hr = InitCaptureGraphBuilder();
if (FAILED(hr))
return hr;
// 绑定设备过滤器。我们知道设备因为id
if(!BindFilter(iDeviceID, &m_pBF))
return S_FALSE;
hr = m_pGB->AddFilter(m_pBF, L"Capture Filter");
// create a sample grabber
hr = CoCreateInstance( CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_ISampleGrabber, (void**)&m_pGrabber );
if(FAILED(hr)) return hr;
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (LPVOID*) &m_pNullRenderer);
if(FAILED(hr)) return hr;
hr = m_pGB->AddFilter(m_pNullRenderer, L"NullRenderer");
CComQIPtr< IBaseFilter, &IID_IBaseFilter > pGrabBase( m_pGrabber );
//设置视频格式
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24; // MEDIASUBTYPE_RGB24 ;
hr = m_pGrabber->SetMediaType(&mt);
if( FAILED(hr) )
{
return hr;
}
hr = m_pGB->AddFilter( pGrabBase, L"Grabber" );
if( FAILED( hr ) )
{
return hr;
}
IAMStreamConfig *pConfig;
hr = m_pCapture->FindInterface(&PIN_CATEGORY_CAPTURE,&MEDIATYPE_Interleaved,m_pBF,IID_IAMStreamConfig,(void **)&pConfig);
if( hr != NOERROR )
{
hr = m_pCapture->FindInterface(&PIN_CATEGORY_CAPTURE,&MEDIATYPE_Video,m_pBF,IID_IAMStreamConfig,(void **)&pConfig);
if(hr != NOERROR)
return hr;
}
m_nWidth = width;
m_nHeight = height;
AM_MEDIA_TYPE *mmt = NULL;
hr = pConfig-> GetFormat(&mmt);
VIDEOINFOHEADER *vvih = (VIDEOINFOHEADER*)mmt->pbFormat;
if(vvih == NULL)
{
return S_FALSE;
}
AM_MEDIA_TYPE *pmt = mmt;
VIDEOINFOHEADER *pvih = (VIDEOINFOHEADER*)pmt->pbFormat;
if(pvih == NULL)
{
return S_FALSE;
}
pvih->bmiHeader.biHeight=height; //修改采集视频的高为240
pvih->bmiHeader.biWidth=width; //修改采集视频的宽为320
pvih->AvgTimePerFrame = 10000000/10;
pmt->pbFormat = (unsigned char*)pvih;
hr = pConfig->SetFormat(pmt); //重新设置参数
if(FAILED(hr))
{
return hr;
}
SAFE_RELEASE(pConfig);
// try to render preview/capture pin
hr = m_pCapture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,m_pBF,pGrabBase,m_pNullRenderer);
if( FAILED( hr ) )
{
hr = m_pCapture->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,m_pBF,pGrabBase,m_pNullRenderer);
if( FAILED( hr ) )
return hr;
}
mCB.lWidth = width;
mCB.lHeight = height;
FreeMediaType(mt);
hr = m_pGrabber->SetBufferSamples( FALSE );
hr = m_pGrabber->SetOneShot( FALSE );
hr = m_pGrabber->SetCallback( &mCB, 1 );//设置视频捕获回调函数 也就是如果有视频数据时就会调用这个类的BufferCB函数
m_pEnc = new CXvidEnc;
if (m_pEnc == NULL)
{
return S_FALSE;
}
m_pEnc->AttachCaller(mCB.lWidth, mCB.lHeight) ;
m_pEnc->XVID_GLOBAL_INIT() ;
m_pEnc->Open();
m_bIsStarted = TRUE;
hr = m_pMC->Run();//开始视频捕捉
if(FAILED(hr))
{
return hr; // 失败
}
return S_OK;
}
HRESULT CCaptureVideo::Stop()
{
if(m_pMC)
m_pMC->Stop();
// m_pMC->StopWhenReady();
SAFE_RELEASE(m_pMC);
SAFE_RELEASE(m_pGB);
SAFE_RELEASE(m_pBF);
SAFE_RELEASE(m_pNullRenderer);
SAFE_RELEASE(m_pGrabber);
SAFE_RELEASE(m_pCapture);
m_pMC = NULL;
m_pGB = NULL;
m_pBF = NULL;
m_pNullRenderer = NULL;
m_pGrabber = NULL;
m_pCapture = NULL;
return S_OK ;
}
int CCaptureVideo::EnumDevices(DeviceInfo *head)
{
if (!head)
return 0;
DeviceInfo *ptr = head;
int id = 0;//枚举视频扑捉设备
//枚举视频扑捉设备
ICreateDevEnum *pCreateDevEnum; //ICreateDevEnum接口建立指定类型的列表
ICaptureGraphBuilder2 *pBuilder = NULL;
IBaseFilter *pVCap = NULL;
IAMStreamConfig *pVSC = NULL;
HRESULT hr = CoCreateInstance((REFCLSID)CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder2,(void **)&pBuilder);
if (hr != NOERROR)
return -1;
//用指定的类标识符创建一个未初始化的对象,这里的参数为CLSID_SystemDeviceEnum即枚举设备的对象
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,IID_ICreateDevEnum, (void**)&pCreateDevEnum);
if (hr != NOERROR)
return -1;
//指出要枚举那个类型的设备这里参数CLSID_VideoInputDeviceCategory枚举视频设备
CComPtr<IEnumMoniker> pEm;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEm, 0);
if (hr != NOERROR)
return -1;
pEm->Reset();
ULONG cFetched;
IMoniker *pM;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
{
IPropertyBag *pBag;
hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
if(SUCCEEDED(hr))
{
VARIANT var;
var.vt = VT_BSTR;
hr = pBag->Read(L"FriendlyName", &var, NULL);
if (hr == NOERROR)
{
char str[2048];
if (ptr->deviceId != -1)
{
ptr->next = new DeviceInfo();
ptr = ptr->next;
}
ptr->deviceId = id++;
WideCharToMultiByte(CP_ACP,0,var.bstrVal, -1, str, 2048, NULL, NULL);
ptr->friendlyName = str;
SysFreeString(var.bstrVal);
}
pBag->Release();
}
hr = pM->BindToObject(0, 0, IID_IBaseFilter, (void**)&pVCap);
if(SUCCEEDED(hr))
{
hr = pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE,&MEDIATYPE_Video,pVCap, IID_IAMStreamConfig, (void **)&pVSC);
if(hr != NOERROR)
{
hr = pBuilder->FindInterface(&PIN_CATEGORY_PREVIEW,&MEDIATYPE_Video,
pVCap, IID_IAMStreamConfig, (void **)&pVSC);
}
if(SUCCEEDED(hr))
{
int iCount = 0, iSize = 0;
hr = pVSC->GetNumberOfCapabilities(&iCount, &iSize);
ResolutionInfo** pRes = &(ptr->resInfo);
if(iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS))
{
for (int iFormat = 0; iFormat < iCount; iFormat++)
{
VIDEO_STREAM_CONFIG_CAPS scc;
AM_MEDIA_TYPE *pmtConfig = NULL;
hr = pVSC->GetStreamCaps(iFormat, &pmtConfig, (BYTE*)&scc);
if (SUCCEEDED(hr))
{
//分辨率
if(HEADER(pmtConfig->pbFormat)->biWidth != 0 && HEADER(pmtConfig->pbFormat)->biHeight != 0)
{
char cResolution[32] = {0};
sprintf(cResolution,"%d * %d:",HEADER(pmtConfig->pbFormat)->biWidth,HEADER(pmtConfig->pbFormat)->biHeight);
*pRes = new ResolutionInfo;
(*pRes)->Resolution = cResolution;
pRes = &((*pRes)->next);
}
}
}
}
pM->AddRef();
}
}
pM->Release();
}
SAFE_RELEASE(pBuilder);
SAFE_RELEASE(pVCap);
SAFE_RELEASE(pVSC);
return id;
}