DirectShow过滤器开发-音频渲染过滤器

下载本过滤器

本过滤器播放PCM和FLOAT音频流。

过滤器信息

过滤器名称:音频渲染

过滤器GUID:{4A910FA8-08DC-4832-85B2-4B7A3FF87F88}

DLL注册函数名:DllRegisterServer

删除注册函数名:DllUnregisterServer

过滤器有1个输入引脚。

输入引脚标识:In

输入引脚媒体类型:

主要类型:MEDIATYPE_Audio

子类型:MEDIASUBTYPE_PCM或MEDIASUBTYPE_IEEE_FLOAT

格式类型:FORMAT_WaveFormatEx

在子类型为PCM时,样本为16位。为FLOAT时,样本为32位。

过滤器开发信息

使用CoCreateInstance函数创建设备枚举器,接口为IMMDeviceEnumerator,使用该接口获取音频渲染默认端点,接口为IMMDevice,使用该接口的Activate方法获取音频端点客户端,接口为IAudioClient,使用该接口的Initialize方法以指定格式初始化音频流,本示例请求了1秒钟的缓冲区大小,由Initialize方法的参数3指定;使用IAudioClient接口的GetService方法获取IAudioRenderClient接口,该接口可以将音频数据写入渲染端点缓冲区。即,使用接口的GetBuffer方法获取指定大小的可用渲染缓冲区的指针,将音频数据复制到缓冲区,然后使用接口的ReleaseBuffer方法释放GetBuffer获取的缓冲区。调用IAudioClient接口的Start方法后,填充的音频数据将被播放。

在调用Start方法前,需预先填充一些音频数据;Start方法调用后,使用IAudioClient的GetCurrentPadding方法获取等待播放的音频帧数量,计算出渲染缓冲区可用的大小,反复的向渲染缓冲区填充音频数据,音频数据将被连续播放。

过滤器DLL的全部代码

DLL.h

cpp 复制代码
#ifndef  DLL_FILE
#define DLL_FILE

#include "strmbase10.h"//过滤器基础类定义文件

#if _DEBUG
#pragma comment(lib, "strmbasd10.lib")//过滤器基础类实现文件调试版本
#else
#pragma comment(lib, "strmbase10.lib")//过滤器基础类实现文件发布版本
#endif

// {4A910FA8-08DC-4832-85B2-4B7A3FF87F88}
DEFINE_GUID(CLSID_MyAudioRender,
	0x4a910fa8, 0x8dc, 0x4832, 0x85, 0xb2, 0x4b, 0x7a, 0x3f, 0xf8, 0x7f, 0x88);


template <class T> void SafeRelease(T** ppT)
{
	if (*ppT)
	{
		(*ppT)->Release();
		*ppT = NULL;
	}
}

#define REFTIMES_PER_SEC  10000000
#define REFTIMES_PER_MILLISEC  10000

class CFilter;

class CPin : public CBaseInputPin
{
	friend class CFilter;
	CFilter *pCFilter;
public:
	CPin(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName);
	~CPin();
	HRESULT CheckMediaType(const CMediaType *pmt);
	HRESULT SetMediaType(const CMediaType *pmt);
	STDMETHODIMP Receive(IMediaSample *pSample);
	HRESULT Active();
	HRESULT Inactive();
	BOOL First = FALSE;
	WORD nBlockAlign;//块对齐
	REFERENCE_TIME hnsRequestedDuration;
	REFERENCE_TIME hnsActualDuration;
	void *pEnumerator = NULL;
	void *pDevice = NULL;
	void *pAudioClient = NULL;
	void *pRenderClient = NULL;
	WAVEFORMATEX *pwfx = NULL;
	UINT32 bufferFrameCount;
	UINT32 numFramesAvailable;
	UINT32 numFramesPadding;
	BYTE *pData;
	DWORD flags = 0;
};

class CFilter : public CBaseFilter, public CCritSec
{
	friend class CPin;
	IUnknown   *m_pPosition = NULL;
public:
	CFilter(LPWSTR lpName, LPUNKNOWN pUnk, HRESULT *phr);
	virtual ~CFilter();
	DECLARE_IUNKNOWN
	STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
	static CUnknown * WINAPI CreateInstance(LPUNKNOWN, HRESULT *);
	int GetPinCount();
	CBasePin *GetPin(int n);
	CPin *pCPin;   //输入引脚指针
};

#endif //DLL_FILE

DLL.cpp

cpp 复制代码
#include "DLL.h"


const AMOVIESETUP_MEDIATYPE sudPinTypes[] =
{
	{
		&MEDIATYPE_Audio,            //主要类型
		&MEDIASUBTYPE_PCM            //子类型
	},
	{
		&MEDIATYPE_Audio,            //主要类型
		&MEDIASUBTYPE_IEEE_FLOAT     //子类型
	}
};

const AMOVIESETUP_PIN sudPin =    //引脚信息
{
	L"In",                        //引脚名称
	TRUE,                         //渲染引脚
	FALSE,                        //输出引脚
	FALSE,                        //具有该引脚的零个实例
	FALSE,                        //可以创建一个以上引脚的实例
	&CLSID_NULL,                  //该引脚连接的过滤器的类标识
	NULL,                         //该引脚连接的引脚名称
	2,                            //引脚支持的媒体类型数
	sudPinTypes                   //媒体类型信息
};

const AMOVIESETUP_FILTER AudioRender =  //过滤器的注册信息
{
	&CLSID_MyAudioRender,           //过滤器的类标识
	L"音频渲染",                    //过滤器的名称
	MERIT_DO_NOT_USE,              //过滤器优先值
	1,                             //引脚数量
	&sudPin                        //引脚信息
};

CFactoryTemplate g_Templates[] = 
{
	{
		L"音频渲染"                               //对象(这里为过滤器)名称
		, &CLSID_MyAudioRender                   //对象CLSID的指针
	    , CFilter::CreateInstance                //创建对象实例的函数的指针
	    , NULL                                   //指向从DLL入口点调用的函数的指针
	    , &AudioRender                           //指向AMOVIESETUP_FILTER结构的指针
	}
};

int g_cTemplates = 1;

STDAPI DllRegisterServer()//注册DLL
{
	return AMovieDllRegisterServer2(TRUE);
}

STDAPI DllUnregisterServer()//删除DLL注册
{
	return AMovieDllRegisterServer2(FALSE);
}

extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);

BOOL APIENTRY DllMain(HANDLE hModule, DWORD  dwReason, LPVOID lpReserved)
{
	return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved);
}

CFilter.cpp

cpp 复制代码
#include "DLL.h"

CFilter::CFilter(LPWSTR lpName, LPUNKNOWN pUnk, HRESULT *phr) : CBaseFilter(lpName, pUnk, (CCritSec *) this, CLSID_MyAudioRender)
{
	pCPin = new CPin(this, phr, L"In");//创建输入引脚
}

CFilter::~CFilter()
{
	if (m_pPosition != NULL)delete m_pPosition;
}

CUnknown * WINAPI CFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr)
{
	return new CFilter(L"音频渲染", pUnk, phr);//创建过滤器
}

int CFilter::GetPinCount()
{
	return 1;
}

CBasePin *CFilter::GetPin(int n)
{
	if (n != 0)
	{
		return NULL;
	}
	return pCPin;
}

STDMETHODIMP CFilter::NonDelegatingQueryInterface(REFIID iid, void ** ppv)
{
	HRESULT hr = NOERROR;
	if( iid == IID_IMediaPosition || iid == IID_IMediaSeeking)
	{
		if (m_pPosition == NULL)
		{
			hr = CreatePosPassThru(CBaseFilter::GetOwner(), FALSE, (IPin *)pCPin, &m_pPosition);
			if (FAILED(hr)) return hr;
		}
		return m_pPosition->QueryInterface(iid, ppv);
	}
	else
		return CBaseFilter::NonDelegatingQueryInterface(iid, ppv);
}

CPin.cpp

cpp 复制代码
#include "DLL.h"
#include "mmdeviceapi.h"
#include "audioclient.h"


CPin::CPin(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName) : CBaseInputPin(NAME("In"), pFilter, pFilter, phr, pPinName)
{
	pCFilter = pFilter;
	hnsRequestedDuration = REFTIMES_PER_SEC;
	HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);
	if (hr != S_OK)
	{
		MessageBox(NULL, L"创建设备枚举器失败", L"音频渲染", MB_OK); return;
	}
	IMMDevice* pMD = NULL;
	hr = ((IMMDeviceEnumerator*)pEnumerator)->GetDefaultAudioEndpoint(eRender, eConsole, &pMD);
	if (hr != S_OK)
	{
		MessageBox(NULL, L"获取默认音频端点失败", L"音频渲染", MB_OK); return;
	}
	pDevice = (void*)pMD;
	hr = ((IMMDevice*)pDevice)->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient);
	if (hr != S_OK)
	{
		MessageBox(NULL, L"获取音频端点客户端失败", L"音频渲染", MB_OK); return;
	}
	hr = ((IAudioClient*)pAudioClient)->GetMixFormat(&pwfx);
	if (hr != S_OK)
	{
		MessageBox(NULL, L"获取音频端点流格式失败", L"音频渲染", MB_OK); return;
	}
}

CPin::~CPin()
{
	CoTaskMemFree(pwfx);
	IMMDeviceEnumerator* pE = (IMMDeviceEnumerator*)pEnumerator;
	SafeRelease(&pE);
	IMMDevice *pD = (IMMDevice*)pDevice;
	SafeRelease(&pD);
	IAudioClient *pA = (IAudioClient*)pAudioClient;
	SafeRelease(&pA);
	IAudioRenderClient *pR = (IAudioRenderClient*)pRenderClient;
	SafeRelease(&pR);
}

HRESULT CPin::CheckMediaType(const CMediaType *pmt)
{
	if (pmt->majortype == MEDIATYPE_Audio && (pmt->subtype == MEDIASUBTYPE_PCM || pmt->subtype == MEDIASUBTYPE_IEEE_FLOAT) && pmt->formattype == FORMAT_WaveFormatEx)
	{
		return S_OK;
	}
	return S_FALSE;
}

HRESULT CPin::SetMediaType(const CMediaType *pmt)
{
	WAVEFORMATEX* p = (WAVEFORMATEX*)pmt->pbFormat;
	HRESULT hr= ((IAudioClient*)pAudioClient)->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, hnsRequestedDuration, 0, p, NULL);//以共享模式初始化音频流
	if (hr == S_OK)
	{
		hr = ((IAudioClient*)pAudioClient)->GetBufferSize(&bufferFrameCount);//获取分配的缓冲区的实际大小
	}
	IAudioRenderClient *pRender = NULL;
	if (hr == S_OK)
	{
		hr = ((IAudioClient*)pAudioClient)->GetService(__uuidof(IAudioRenderClient), (void**)&pRender);
	}
	if (hr == S_OK)
	{
		pRenderClient = (void*)pRender;
		nBlockAlign = p->nBlockAlign;
	}
	else return hr;
	return CBaseInputPin::SetMediaType(pmt);
}

HRESULT CPin::Receive(IMediaSample * pSample)//接收函数
{
	HRESULT hr;
	BYTE* pBy = NULL;
	hr = pSample->GetPointer(&pBy);//获取引脚样本缓冲区指针
	long len = pSample->GetActualDataLength();//获取有效数据长度

	if (First)
	{
		First = FALSE;
		hr = ((IAudioRenderClient*)pRenderClient)->GetBuffer(len / nBlockAlign, &pData);//参数1,请求的缓冲区的音频帧大小;参数2,获取缓冲区的指针
		CopyMemory(pData, pBy, len);
		hr = ((IAudioRenderClient*)pRenderClient)->ReleaseBuffer(len / nBlockAlign, flags);
		hnsActualDuration = (double)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec;
		hr = ((IAudioClient*)pAudioClient)->Start();//启动音频流
		return S_OK;
	}
Agan:
	hr = ((IAudioClient*)pAudioClient)->GetCurrentPadding(&numFramesPadding);//获取未播放的音频帧数
	numFramesAvailable = bufferFrameCount - numFramesPadding;//计算缓冲区中可填充的帧数
	if ((long)(numFramesAvailable * nBlockAlign) < len)//如果可填充的大小,小于引脚样本有效数据长度
	{
		Sleep(10); goto Agan;//等待并阻塞
	}
	hr = ((IAudioRenderClient*)pRenderClient)->GetBuffer(len / nBlockAlign, &pData);//获取渲染缓冲区可用内存的指针,由参数2输出;参数1为输入参数,请求的可用内存的大小,单位音频帧
	CopyMemory(pData, pBy, len);
	hr = ((IAudioRenderClient*)pRenderClient)->ReleaseBuffer(len / nBlockAlign, flags);//释放GetBuffer获取的缓冲区
	return S_OK;
}

HRESULT CPin::Active()
{
	First = TRUE;
	return CBaseInputPin::Active();
}

HRESULT CPin::Inactive()
{
	Sleep((DWORD)(hnsActualDuration / REFTIMES_PER_MILLISEC / 2));
	HRESULT hr;
	hr = ((IAudioClient*)pAudioClient)->Stop();//停止音频流
	hr = ((IAudioClient*)pAudioClient)->Reset();//重置流,刷新所有挂起的数据,将音频时钟流位置重置为0
	return CBaseInputPin::Inactive();
}

下载本过滤器

相关推荐
捕鲸叉5 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer5 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq5 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
hairenjing11236 小时前
使用 Mac 数据恢复从 iPhoto 图库中恢复照片
windows·stm32·嵌入式硬件·macos·word
青花瓷6 小时前
C++__XCode工程中Debug版本库向Release版本库的切换
c++·xcode
九鼎科技-Leo8 小时前
了解 .NET 运行时与 .NET 框架:基础概念与相互关系
windows·c#·.net
幺零九零零8 小时前
【C++】socket套接字编程
linux·服务器·网络·c++
捕鲸叉8 小时前
MVC(Model-View-Controller)模式概述
开发语言·c++·设计模式
Dola_Pan9 小时前
C++算法和竞赛:哈希算法、动态规划DP算法、贪心算法、博弈算法
c++·算法·哈希算法
yanlou2339 小时前
KMP算法,next数组详解(c++)
开发语言·c++·kmp算法