下载本过滤器
本过滤器播放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
#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 10000class 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_IUNKNOWNSTDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);static CUnknown * WINAPI CreateInstance(LPUNKNOWN, HRESULT *);int GetPinCount();CBasePin *GetPin(int n);CPin *pCPin; //输入引脚指针
};#endif //DLL_FILE
DLL.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
#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);}elsereturn CBaseFilter::NonDelegatingQueryInterface(iid, ppv);
}
CPin.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();//重置流,刷新所有挂起的数据,将音频时钟流位置重置为0return CBaseInputPin::Inactive();
}
下载本过滤器