目录
1 DirectShow 简介
DirectShow(简称 DShow) 是一个 Windows 平台上的流媒体框架,提供了高质量的多媒体流采集和回放功能。它支持多种多样的媒体文件格式,包括 ASF、MPEG、AVI、MP3和WAV 文件,同时支持使用 WDM 驱动或早期的 VFW 驱动来进行多媒体流的采集。
-
DirectShow 大大简化了媒体回放、格式转换和采集工作。但与此同时,它也为用户自定义的解决方案提供了底层流控制框架,从而使用户可以自行创建支持新的文件格式或其他用户的 DirectShow 组件。
-
DirectShow 专为 C ++ 而设计。 Microsoft 不提供用于 DirectShow 的托管 API。
-
DirectShow 是基于组件对象模型(COM)的,因此当你编写 DirectShow 应用程序时,你必须具备 COM 客户端程序编写的知识。对于大部分的应用程序,你不需要实现自己的 COM 对象,DirectShow 提供了大部分你需要的 DirectShow 组件,但是假如你需要编写自己的 DirectShow 组件来进行扩充,那么你必须编写实现 COM 对象。
-
使用 DirectShow 编写的典型应用程序包括:DVD 播放器、视频编辑程序、AVI 到 ASF 转换器、 MP3 播放器和数字视频采集应用。
2 DirectShow系统设备的枚举步骤
-
使用CoCreateInstance函数创建系统枚举器组件对象,并获得ICreateDevEnum接口;
-
使用接口方法ICreateDevEnum::CreateClassEnumerator为指定的Filter注册类型目录创建一个枚举器,并获得
-
IEnumMoniker接口;
-
使用IEnumMoniker接口方法枚举指定类型目录下所有设备标识
-
调用IMoniker::BindToStorage之后,可以访问设备标识的属性集,比如得到Display Name,Friendly Name等;
-
调用IMoniker::BindToOject可以将设备标识生成一个DirectShow Filter,随后调用IFilterGraph::AddFilter,并将之加入到FilterGraph中就可以参与工作了.
/***********************************************************************
功能: 获取摄像头个数
参数:
返回: 摄像头个数
************************************************************************/
int CCameraDS::CameraCount()
{
int count = 0;
CoInitialize(NULL);
// enumerate all video capture devices
CComPtr<ICreateDevEnum> pCreateDevEnum;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,IID_ICreateDevEnum, (void**)&pCreateDevEnum);
CComPtr<IEnumMoniker> pEm;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEm, 0);
if (hr != NOERROR)
{
return count;
}
pEm->Reset();
ULONG cFetched;
IMoniker *pM;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
{
count++;
}
return count;
}
/*******************************************************************
功能: PID和VID获取设备序号
参数: vid - 输入,vid,全小写,比如046d
pid - 输入,pid,全小写,比如08ff
返回: 当前PID和VID的摄像头个数
*******************************************************************/
int CCameraDS::EnumCamIndex(CString vid, CString pid,unsigned char CamIndex[32])
{
int count = 0;
CoInitialize(NULL);
CString strVid="";
CString strPid="";
int id = 0; //记录实际摄像头个数
int camnum=0; //记录当前符合PID VID的设备数
// enumerate all video capture devices
CComPtr<ICreateDevEnum> pCreateDevEnum;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,IID_ICreateDevEnum, (void**)&pCreateDevEnum);
if (hr != NOERROR)
return -1;
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 varname;
VARIANT varpath;
CString str="";
varname.vt = VT_BSTR;
varpath.vt = VT_BSTR;
//读取摄像头名称
hr = pBag->Read(L"FriendlyName", &varname, NULL);
if (hr != NOERROR)
{
continue;
}
//读取摄像头PID,VID
hr = pBag->Read(L"DevicePath", &varpath, NULL);
if (hr == NOERROR)
{
int Count =0;
while(varpath.bstrVal[Count] != 0x00 )
{
str += varpath.bstrVal[Count];
Count++;
}
strVid = str.Mid(str.Find("vid_")+4, 4);
strPid = str.Mid(str.Find("pid_")+4, 4);
if( 0 == strcmp(strVid, vid) && 0 == strcmp(strPid, pid))
{
if (camnum<32)
{
CamIndex[camnum]=id;
camnum++;
}
}
id++;
}
pBag->Release();
}
pM->Release();
}
return camnum;
}
3 DirectShow获取摄像头视频流
/************************************************************************************
功能: 打开USB摄像头
参数: index - 输入,摄像头序号
bDisplayProperties - 输入,指示是否自动弹出摄像头属性页
nWidth - 输入,宽度,输入不为0,并且在bDisplayProperties为false时有效
nHeight - 输入,高度,输入不为0,并且在bDisplayProperties为false时有效
返回: true-成功,false-失败
*************************************************************************************/
bool CCameraDS::OpenCamera(int nCamID, bool bDisplayProperties,int nWidth,int nHeight)
{
HRESULT hr = S_OK;
CoInitialize(NULL);
// Create the Filter Graph Manager.
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,
IID_IGraphBuilder, (void **)&m_pGraph);
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_IBaseFilter, (LPVOID *)&m_pSampleGrabberFilter);
hr = m_pGraph->QueryInterface(IID_IMediaControl, (void **) &m_pMediaControl);
hr = m_pGraph->QueryInterface(IID_IMediaEvent, (void **) &m_pMediaEvent);
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
IID_IBaseFilter, (LPVOID*) &m_pNullFilter);
hr = m_pGraph->AddFilter(m_pNullFilter, L"NullRenderer");
hr = m_pSampleGrabberFilter->QueryInterface(IID_ISampleGrabber, (void**)&m_pSampleGrabber);
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
mt.formattype = FORMAT_VideoInfo;
hr = m_pSampleGrabber->SetMediaType(&mt);
MYFREEMEDIATYPE(mt);
m_pGraph->AddFilter(m_pSampleGrabberFilter, L"Grabber");
// Bind Device Filter. We know the device because the id was passed in
BindFilter(nCamID, &m_pDeviceFilter);
m_pGraph->AddFilter(m_pDeviceFilter, NULL);
CComPtr<IEnumPins> pEnum;
m_pDeviceFilter->EnumPins(&pEnum);
hr = pEnum->Reset();
hr = pEnum->Next(1, &m_pCameraOutput, NULL);
pEnum = NULL;
m_pSampleGrabberFilter->EnumPins(&pEnum);
pEnum->Reset();
hr = pEnum->Next(1, &m_pGrabberInput, NULL);
pEnum = NULL;
m_pSampleGrabberFilter->EnumPins(&pEnum);
pEnum->Reset();
pEnum->Skip(1);
hr = pEnum->Next(1, &m_pGrabberOutput, NULL);
pEnum = NULL;
m_pNullFilter->EnumPins(&pEnum);
pEnum->Reset();
hr = pEnum->Next(1, &m_pNullInputPin, NULL);
//SetCrossBar();
//add by chengs 20160617
if (nWidth>0 && nHeight>0)
{
SetCaps(nWidth,nHeight);
}
if (bDisplayProperties)
{
CComPtr<ISpecifyPropertyPages> pPages;
HRESULT hr = m_pCameraOutput->QueryInterface(IID_ISpecifyPropertyPages, (void**)&pPages);
if (SUCCEEDED(hr))
{
PIN_INFO PinInfo;
m_pCameraOutput->QueryPinInfo(&PinInfo);
CAUUID caGUID;
pPages->GetPages(&caGUID);
OleCreatePropertyFrame(NULL, 0, 0,
L"Property Sheet", 1,
(IUnknown **)&(m_pCameraOutput.p),
caGUID.cElems,
caGUID.pElems,
0, 0, NULL);
CoTaskMemFree(caGUID.pElems);
PinInfo.pFilter->Release();
}
}
hr = m_pGraph->Connect(m_pCameraOutput, m_pGrabberInput);
hr = m_pGraph->Connect(m_pGrabberOutput, m_pNullInputPin);
if (FAILED(hr))
{
switch(hr)
{
case VFW_S_NOPREVIEWPIN :
break;
case E_FAIL :
break;
case E_INVALIDARG :
break;
case E_POINTER :
break;
}
}
m_pSampleGrabber->SetBufferSamples(TRUE);
m_pSampleGrabber->SetOneShot(TRUE);
hr = m_pSampleGrabber->GetConnectedMediaType(&mt);
if(FAILED(hr))
return false;
VIDEOINFOHEADER *videoHeader;
videoHeader = reinterpret_cast<VIDEOINFOHEADER*>(mt.pbFormat);
m_nWidth = videoHeader->bmiHeader.biWidth;
m_nHeight = videoHeader->bmiHeader.biHeight;
m_bConnected = true;
return true;
}
/************************************************************************
功能: 获取一帧视频流数据
参数:
返回: 一帧视频流数据
*************************************************************************/
unsigned char* CCameraDS::QueryFrame()
{
long evCode;
long size = 0;
m_pMediaControl->Run();
m_pMediaEvent->WaitForCompletion(5000, &evCode); //INFINITE
m_pSampleGrabber->GetCurrentBuffer(&size, NULL);
if (size == 0)
{
return NULL;
}
//if the buffer size changed
if (size != m_nBufferSize)
{
if (m_pFrame)
free(m_pFrame);
m_nBufferSize = size;
m_pFrame = (unsigned char*)malloc(m_nWidth*m_nHeight*3);
}
if (m_pFrame == NULL)
{
return NULL;
}
m_pSampleGrabber->GetCurrentBuffer(&m_nBufferSize, (long*)m_pFrame);
ImrotateVertical_RGB(m_pFrame, m_nWidth, m_nHeight);
return m_pFrame;
}
4 演示Demo
4.1 开发环境
-
Visual Studio 2013
-
DirectShow
-
Windows 10 Pro x64
4.2 功能介绍
演示VS2013 封装DirectShow,用于打开摄像头、获取摄像头视频流数据等操作。
4.3 下载地址
开发环境:
-
Visual Studio 2013
-
DirectShow
-
Windows 10 Pro x64