DirectShow采集YUV数据

   说明:本周的主要工作就是从摄像头获取YUV数据用于后期处理,从来没有用过DirectShow,做个记录方便以后参考。DirectShow本身并不提供采集到的图像数据的输出,根据文档,视频CAPTURE后只能输出为AVI或ASF格式,所以原始的YUV数据只能通过例子中附带的ISampleGrabberCB类得到。

1.参考程序

安装WindowsSDK后在安装目录下有.\Samples\multimedia\directshow\capture\amcap,其中是官方提供的音、视频采集例程。

2.数据类型及定义

ICaptureGraphBuilder2 *pCGB2;
IGraphBuilder *pGB;
IMoniker *pVid;              // 视频采集设备指针
IBaseFilter *pVCap;          // 用于绑定pVid的filter指针
ISampleGrabber *pVGrab;      // 截取数据类指针
IBaseFilter *pVGrabFilter;   // pVGrab中的filter指针
GrabberCB *pVGrabCB;         // 截取数据后的callback类

其中ISampleGrabber是MS提供的用于截取数据的,与ISampleGrabberCB配合使用

class GrabberCB : public ISampleGrabberCB {
public:
    ULONG STDMETHODCALLTYPE AddRef();
    ULONG STDMETHODCALLTYPE Release();
    HRESULT STDMETHODCALLTYPE QueryInterface( 
            /* [in] */ REFIID riid,
            /* [iid_is][out] */ __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject);
    STDMETHODIMP SampleCB(double, IMediaSample *);
    STDMETHODIMP BufferCB(double, BYTE *, long);
    ...
};

GrabberCB子类继承自ISampleGrabberCB,类中必须定义父类中的几个虚函数,前三个可以直接return,SampleCB定义了收到IMediaSample数据后的动作,而BufferCB则定义了收到原始数据后的动作

3.采集流程

3.1

实例化pCGB2和pGB,设置pGB为pCGB2中的graph
pCGB2->SetFiltergraph(pGB)

3.2

获取采集设备指针pVid,具体获取方式可以用枚举设备的方式,或者枚举时记录设备名称然后调用MkParseDisplayName函数

3.3

pVid->BindToObject(NULL, NULL, IID_IBaseFilter, (void **)&pVCap)
绑定pVCap到pVid,graph中只能用IBaseFilter

3.4

将pVCap加入graph中
pGB->AddFilter(pVCap, L"Video cap")
——————————–此处开始与amcap不同——————————–

3.5

实例化pVGrab并获取pVGrab的IBaseFilter,调用函数
pVGrab->QueryInterface(IID_IBaseFilter, (void **)&pVGrabFilter)

3.6

把pVGrabFilter加入graph
pGB->AddFilter(pVGrabFilter, L"Grabber")
这样就完成了graph的创建工作,如果与graphedt中的操作对应,以上完成的操作就是把采集设备和截取数据设备加入到视图中

3.7

连接graph中的filter
pCGB2->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pVCap, NULL, pVGrabFilter)
函数的最后三个参数是三个filter,如果把pVGrabFilter放在第二个位置而把第三个赋值NULL,则DirectShow自动添加默认输出,运行时会弹出预览窗口

3.8

设置pVGrab
pVGrab->SetBufferSamples(FALSE)
pVGrab->SetOneShot(FALSE)
MSDN上专门有ISampleGrabber的使用说明,第一个函数置FALSE,表明直接利用源数据而不另外copy一份;第二个函数置FALSE,表明连续截取数据

3.9

设置pVGrabCB
pVGrabCB = new GrabberCB;
pVGrab->SetCallback(pVGrabCB, 1);
创建pVGrabCB并在pVGrab指定回调类,其中第二个参数为0时调用SampleCB函数,为1时调用BufferCB函数

3.10

至此一个能够截取视频原始数据的graph已经完成了

IMediaControl *pMC = NULL;
pGB->QueryInterface(IID_IMediaControl, (void **)&pMC);
pMC->Run();
pMC->Release();

如上得到pGB的控制后运行即可

4.Q&A

Q1 怎么用ISampleGrabber和ISampleGrabberCB?
这两个类定义在qedit.h中,该头文件是较早版本的directx SDK中的,搜索下载即可,但是注意使用方法

#define __IDxtCompositor_INTERFACE_DEFINED__  
#define __IDxtAlphaSetter_INTERFACE_DEFINED__  
#define __IDxtJpeg_INTERFACE_DEFINED__  
#define __IDxtKey_INTERFACE_DEFINED__ 
#include "qedit.h"

先定义前面的宏,否则编译时出错
Q2 用了DeleteMediaType()函数编译时出现错误”wcsrchr already defined in xxx”?
可以用以下方式释放内存

if (pMt->cbFormat)
{
    CoTaskMemFree(LPVOID(pMt->pbFormat));
    pMt->cbFormat = 0;
    pMt->pbFormat = NULL;
}
if (pMt->pUnk)
{
    pMt->pUnk->Release();
    pMt->pUnk = NULL;
}
CoTaskMemFree(pMt);
pMt = NULL;

编译时遇到此问题时可以尝试
Q3 怎么使用硬件自身的配置窗口?
视频采集设备配置窗口分为两种:亮度、白平衡等设置窗口和分辨率、采集格式及帧率设置窗口,前者调用代码为

ISpecifyPropertyPages *pSpec;
CAUUID cauuid;
pVCap->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
pSpec->GetPages(&cauuid);
OleCreatePropertyFrame(ghwndApp, 30, 30, NULL, 1, (IUnknown **)&pVCap,
    cauuid.cElems, (GUID *)cauuid.pElems, 0, 0, NULL);
CoTaskMemFree(cauuid.pElems);
pSpec->Release();

其中ghwndApp为程序窗口句柄,后面两个参数设置弹出窗口相对程序窗口的位置
后者调用代码

IAMStreamConfig *pSC;
pGB->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pVCap, IID_IAMStreamConfig, (void **)&pSC);
ISpecifyPropertyPages *pSpec;
CAUUID cauuid;
hr = pSC->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pSpec);
...(同上)

可以看出前者直接从IBaseFilter即采集设备获取设置窗口,后者查询采集设备上的流设置信息并从该信息获取设置窗口
Q4 怎么直接设置视频采集分辨率、格式等参数?

// 设置参数,p1=宽,p2=高,p3=帧率
AM_MEDIA_TYPE *p = NULL;
IAMStreamConfig *pSC = NULL;
pCGB2->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pVCap, 
    IID_IAMStreamConfig, (void **)&pSC);
pSC->GetFormat(&p);
VIDEOINFOHEADER *pHd = (VIDEOINFOHEADER *)p->pbFormat;
// 单位为100ns,所以每帧(10^7/p3)*100ns
pHd->AvgTimePerFrame = 10000000 / p3;
// I420格式
pHd->bmiHeader.biCompression = MAKEFOURCC('I', '4', '2', '0');
pHd->bmiHeader.biWidth = p1;
pHd->bmiHeader.biHeight = p2;
// 一个像素点12位
pHd->bmiHeader.biBitCount = 12;
// 图像大小
pHd->bmiHeader.biSizeImage = p1 * p2 * 3 / 2;
// 图像传输率,单位bps
pHd->dwBitRate = pHd->bmiHeader.biSizeImage * 8 * p3;
// defined in wmsdkidl.h
p->subtype = WMMEDIASUBTYPE_I420;
p->lSampleSize = pHd->bmiHeader.biSizeImage;
pSC->SetFormat(p);
// free p
FreeAM_MEDIA_TYPE(p);
pSC->Release();
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值