最简单的source filter

原文地址:http://blog.csdn.net/bwmwm/article/details/5463852/.

创建一个空的Dll工程,添加5个空文件分别名为:MyOutputPin.h、MySourceFilter.h、MyOutputPin.cpp、MySourceFilter.cpp和MySourceFilter.def。

 

2.声明两个类,一个是Filter的实现类,一个是输出Pin的实现类,由于是最简单的源Filter,因此Filter只有一个输出Pin。实现的功能是从本地磁盘读取三个图片文件,轮流显示这三张图片,效果是模拟一个视频流。这两个类的声明代码:

 //MySourceFilter.h
class CMySourceFilter 
	//从SDK库中的CSource类派生
	:	public CSource			
{
public:
	//实例化接口
	static CUnknown * WINAPI CreateInstance(LPUNKNOWN lpunk, HRESULT *phr);
private:
	//构造函数
	CMySourceFilter(LPUNKNOWN lpunk, HRESULT *phr);	
};

//MyOutputPin.h
class CMyOutputPin 
	//CSource的派生类都使用CSourceStream的派生类做为pin
	:public CSourceStream
{
public:
	CMyOutputPin(HRESULT *phr, CSource *pFilter);
	~CMyOutputPin(void);

	//填充样本函数
	//参数pMediaSample就是要传递到下一个Filter输入pin的样本
	//把数据填充到pMediaSample中就是这个函数的功能
	HRESULT FillBuffer(IMediaSample *pMediaSample);

	//协商每个CMediaSample数据块的大小
	HRESULT DecideBufferSize(IMemAllocator *pIMemAlloc,
		ALLOCATOR_PROPERTIES *pProperties);

	//获得媒体类型
	//在枚举器中枚举支持的媒体类型时调用此函数得到PIN支持的媒体类型
	//此函数设置pmt的各个成员,因此,由此函数的内容觉得PIN支持什么媒体类型
	HRESULT GetMediaType(int iPosition, CMediaType *pmt);

	//检测是否支持参数传入的媒体类型
	HRESULT CheckMediaType(const CMediaType *pMediaType);

	//这是质量控制接口,最简单的源Filter不需要质量控制
	STDMETHODIMP Notify(IBaseFilter *pSelf, Quality q)
	{
		return E_FAIL;
	}

private:
	BYTE* m_pData[3];//存储图片数据
	int m_nWidth;//图片的宽
	int m_nHeight;//图片的高
	int m_nImageSize;//图片数据的大小
	int m_nCount;//计数器,用来切换图片数据的缓冲区
};
3.实现CMySourceFilter类。这个类只有两个函数需要编写,很简单。

//CMySourceFilter.cpp
CUnknown* CMySourceFilter::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr)
{
	//实例化函数的工作就是实例化一个源Filter的对象
	 CUnknown *punk = new CMySourceFilter(lpunk,phr);
	 if (punk == NULL)
	 {
		 *phr = E_OUTOFMEMORY;
	 }
	 return punk;
}

CMySourceFilter::CMySourceFilter(LPUNKNOWN lpunk, HRESULT *phr)
 : CSource(L"MyFilter",lpunk,CLSID_MyFilter,phr)
{
	//创建一个pin的对象实例
	//在CSourceStream的构造函数中,会把pin添加到Filter中
	CMyOutputPin* pOutPin = new CMyOutputPin(phr,this);
	if (FAILED(*phr))
	{
		//因此,在创建失败的时候,要将这个pin从Filter中移除
		RemovePin(pOutPin);
		pOutPin->Release();
	}
}

4.实现CMyOutputPin类,编写Filter主要就是写pin。

//MyOutputPin.cpp


//构造函数
CMyOutputPin::CMyOutputPin(HRESULT *phr, CSource *pFilter)
: CSourceStream(L"MyFilter",phr,pFilter,L"Out")
, m_nWidth(0)
, m_nHeight(0)
, m_nImageSize(0)
, m_nCount(0)
{
	//把图片读到内存中,准备好数据
	m_pData[0] = LoadBitmapFileToMemory(L"E://DirectShow//MySourceFilter//1.bmp",
		m_nWidth,m_nHeight,m_nImageSize);
	m_pData[1] = LoadBitmapFileToMemory(L"E://DirectShow//MySourceFilter//2.bmp",
		m_nWidth,m_nHeight,m_nImageSize);
	m_pData[2] = LoadBitmapFileToMemory(L"E://DirectShow//MySourceFilter//3.bmp",
		m_nWidth,m_nHeight,m_nImageSize);
}


//析构函数
CMyOutputPin::~CMyOutputPin(void)
{
	//释放内存
	delete []m_pData[0];
	delete []m_pData[1];
	delete []m_pData[2];
}


//获取媒体类型
//填充pmt
//最简单的源Filter,因此只支持一种类型,所以iPosition为0
HRESULT CMyOutputPin::GetMediaType(int iPosition, CMediaType *pmt)
{
	CheckPointer(pmt,E_POINTER);


	CAutoLock cAutoLock(m_pFilter->pStateLock());
	if(iPosition < 0)
	{
		return E_INVALIDARG;
	}
	// Have we run off the end of types?
	if(iPosition > 0)
	{
		return VFW_S_NO_MORE_ITEMS;
	}


	//给媒体类型申请Format的空间
	//填充每一个对象,主要是BITMAPINFOHEADER结构
	VIDEOINFO *pvi = (VIDEOINFO *) pmt->AllocFormatBuffer(sizeof(VIDEOINFO));
	if(NULL == pvi)
		return(E_OUTOFMEMORY);


	ZeroMemory(pvi, sizeof(VIDEOINFO));
	pvi->bmiHeader.biBitCount = 24;
	pvi->bmiHeader.biHeight = m_nHeight;
	pvi->bmiHeader.biWidth = m_nWidth;
	pvi->bmiHeader.biSizeImage = m_nImageSize;
	pvi->bmiHeader.biPlanes = 1;
	pvi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	pvi->bmiHeader.biCompression = BI_RGB;
	pvi->bmiHeader.biClrImportant = 0;


	SetRectEmpty(&pvi->rcSource);
	SetRectEmpty(&pvi->rcTarget);


	pmt->SetType(&MEDIATYPE_Video);//设置主媒体类型
	pmt->SetSubtype(&MEDIASUBTYPE_RGB24);//设置子媒体类型
	pmt->SetFormatType(&FORMAT_VideoInfo);//设置详细格式类型
	pmt->SetSampleSize(m_nImageSize);//设置Sample的大小
	pmt->SetTemporalCompression(FALSE);


	return NOERROR;
}


//检查媒体类型
//主要是对GetMediaType中设置的各个参数进行比较
HRESULT CMyOutputPin::CheckMediaType(const CMediaType *pMediaType)
{
	CheckPointer(pMediaType,E_POINTER);


	if (*(pMediaType->Type()) != MEDIATYPE_Video
		|| !(pMediaType->IsFixedSize()))
	{
		return E_INVALIDARG;
	}


	const GUID *SubType = pMediaType->Subtype();
	if (SubType == NULL)
	{
		return E_INVALIDARG;
	}
	if (*SubType != MEDIASUBTYPE_RGB24)
	{
		return E_INVALIDARG;
	}
	const GUID* FormatType = pMediaType->FormatType();
	if (FormatType == NULL)
	{
		return E_INVALIDARG;
	}
	if (*FormatType != FORMAT_VideoInfo)
	{
		return E_INVALIDARG;
	}


	VIDEOINFO* pvi = (VIDEOINFO*)pMediaType->Format();
	if (pvi == NULL)
	{
		return E_INVALIDARG;
	}
	if (pvi->bmiHeader.biBitCount != 24 || 
		pvi->bmiHeader.biWidth != m_nWidth ||
		pvi->bmiHeader.biHeight != m_nHeight)
	{
		return E_INVALIDARG;
	}


	return S_OK;
}


//协商Sample的大小
HRESULT CMyOutputPin::DecideBufferSize(IMemAllocator *pIMemAlloc, ALLOCATOR_PROPERTIES *pProperties)
{
	CheckPointer(pIMemAlloc,E_POINTER);
	CheckPointer(pProperties,E_POINTER);


	CAutoLock cAutoLock(m_pFilter->pStateLock());
	HRESULT hr = NOERROR;


	VIDEOINFO *pvi = (VIDEOINFO *) m_mt.Format();
	//确定只有一个buffer
	pProperties->cBuffers = 1;
	//设置buffer的大小
	pProperties->cbBuffer = m_nImageSize;


	ASSERT(pProperties->cbBuffer);


	//设置属性页
	ALLOCATOR_PROPERTIES Actual;
	hr = pIMemAlloc->SetProperties(pProperties,&Actual);
	if(FAILED(hr))
	{
		return hr;
	}


	if(Actual.cbBuffer < pProperties->cbBuffer)
	{
		return E_FAIL;
	}


	ASSERT(Actual.cBuffers == 1);
	return NOERROR;
}


//填充Sample
HRESULT CMyOutputPin::FillBuffer(IMediaSample *pMediaSample)
{
	CheckPointer(pMediaSample,E_POINTER);
	BYTE* pData = NULL;
	long lDataSize = 0;


	//获得Sample中存放数据的地址
	pMediaSample->GetPointer(&pData);
	//取得Sample分配的内存大小
	lDataSize = pMediaSample->GetSize();


	ZeroMemory(pData,lDataSize);
	//把当前需要显示的数据拷贝到内存中
	CopyMemory(pData,m_pData[m_nCount%3],m_nImageSize);


	//设置时间戳
	REFERENCE_TIME start = TS_ONE * m_nCount;
	REFERENCE_TIME stop = TS_ONE + start;
	pMediaSample->SetTime(&start,&stop);


	//准备下一帧数据
	m_nCount++;


	pMediaSample->SetSyncPoint(TRUE);	


	return NOERROR;
}
LoadBitmapFileToMemory函数的实现
BYTE* LoadBitmapFileToMemory(TCHAR* pFileName, int& nWidth, int& nHeight, int& nImageDataSize)
{
	HBITMAP hBitmap = (HBITMAP)LoadImage( NULL, pFileName, IMAGE_BITMAP, 0, 0,
		LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE );

	if(hBitmap == NULL)
		return NULL;

	HDC hDC = CreateCompatibleDC(NULL);
	HBITMAP hOldBitmap = (HBITMAP)SelectObject(hDC, hBitmap);

	BITMAP bmp;
	GetObject(hBitmap, sizeof(bmp), &bmp);

	BITMAPINFOHEADER bih = {0};//位图信息头
	bih.biBitCount = bmp.bmBitsPixel;//每个像素字节大小
	bih.biCompression = BI_RGB;
	bih.biHeight = bmp.bmHeight;//高度
	bih.biPlanes = 1;
	bih.biSize = sizeof(BITMAPINFOHEADER);
	bih.biSizeImage = bmp.bmWidthBytes * bmp.bmHeight;//图像数据大小
	bih.biWidth = bmp.bmWidth;//宽度

	nImageDataSize = bmp.bmWidthBytes * bmp.bmHeight;
	byte * p = new byte[nImageDataSize];//申请内存保存位图数据
	GetDIBits(hDC, hBitmap, 0, bmp.bmHeight, p,
		(LPBITMAPINFO) &bih, DIB_RGB_COLORS);//获取位图数据

	SelectObject(hDC, hOldBitmap);
	DeleteObject(hBitmap);
	DeleteDC(hDC);

	nWidth = bmp.bmWidth;
	nHeight = bmp.bmHeight;

	return p;
}
5.主要的工作已经做完了,功能已经实现,接下来就是生成Filter。

//MySourceFilter.h
//动态库工程自然也要有入口函数(固定格式)
	extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);

	BOOL APIENTRY DllMain(HANDLE hModule, 
		DWORD  dwReason, 
		LPVOID lpReserved)
	{
		return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved);
	}
//组件就少不了注册与注销函数(固定格式)
STDAPI DllRegisterServer()
	{
		return AMovieDllRegisterServer2(TRUE);

	}
STDAPI DllUnregisterServer()
	{
		return AMovieDllRegisterServer2(FALSE);

	}

//组件,就要有GUID(通过工具创建)
DEFINE_GUID(CLSID_MyFilter, 
			0x159386e0, 0x5193, 0x48ac, 0x8a, 0x57, 0x17, 0x88, 0xc7, 0x33, 0x40, 0xc1);

//以下是注册信息的模版,写了注释的地方是我们需要填写的,其他的采用默认
const AMOVIESETUP_MEDIATYPE sudOpPinTypes =
{
	&MEDIATYPE_Video,       // Major type
	&MEDIASUBTYPE_NULL      // Minor type
};

const AMOVIESETUP_PIN sudOpPin =
{
	L"Output",             
	FALSE,                 
	TRUE,                   
	FALSE,                  
	FALSE,                  
	&CLSID_NULL,           
	NULL,                  
	1,                      
	&sudOpPinTypes };     

	const AMOVIESETUP_FILTER sudBallax =
	{
		&CLSID_MyFilter,    // 自定义的GUID
		L"MyFilter",       // Filter的名字
		MERIT_DO_NOT_USE,       
		1,                      
		&sudOpPin         
	};


	// COM global table of objects in this dll

	CFactoryTemplate g_Templates[] = {
		{ L"MyFilter"//Filter的名字
		, &CLSID_MyFilter//自定义的GUID
		, CMySourceFilter::CreateInstance//Filter的实例化接口
		, NULL
		, &sudBallax }
	};
	int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);

6.MySourceFilter.def文件的内容
LIBRARY	"MySourceFilter.ax"

EXPORTS
            DllMain                 PRIVATE
            DllGetClassObject       PRIVATE
            DllCanUnloadNow         PRIVATE
            DllRegisterServer       PRIVATE
            DllUnregisterServer     PRIVATE

7.注意

1)包含头文件 #include <initguid.h>,否则有可能提示 error LNK2001: 无法解析的外部符号 _CLSID_MyFilter

2)包含导出库#pragma comment(lib, "winmm")

3)包含导入库#pragma comment(lib, "strmbase.lib"),Debug下包含#pragma comment(lib, "strmbasd.lib")

 

8.大功告成。调用regsvr32注册Filter。使用GraphEdit调试Filter。(VS2005)

在工程的属性中选择调试,在命令中填入GraphEdit的完整路径,把Filter的工程作为启动项。按下F5,在运行的GraphEdit中选择我们的Filter,Render pin,就可以看到一条完整的链路,然后run,效果出来了,三幅图片轮流显示在窗口中。


源码工程:http://d.download.csdn.net/download/bwmwm/2218035

感谢原作者
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值