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


#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();
}

下载本过滤器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/55596.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 MongoDB 构建 AI:利用实时客户数据优化产品生命周期

在《使用 MongoDB 构建 AI》系列博文中&#xff0c;我们看到越来越多的企业正在利用 AI 技术优化产品研发和用户支持流程。例如&#xff0c;我们介绍了以下案例&#xff1a; Ventecon 的 AI 助手帮助产品经理生成和优化新产品规范 Cognigy 的对话式 AI 帮助企业使用任意语言&a…

约克VRF打造舒适绿色无污染的生活环境

在生活的各个方面&#xff0c;约克VRF都采取了多种措施助力碳中和。 采用国际领先的空气源热泵技术&#xff0c;只需少量电力就可将空气中的能量转化为室内热量&#xff0c;被称为“大自然的搬运工”&#xff01;COP能效值最高可达4.24&#xff08;每用一度电产生4.24度电热量&…

线性系统性能分析方法3——频率特性分析法(频域分析法)

一种图解的分析方法&#xff0c;不必直接求解系统输出的时域表达式&#xff0c;不需要求解系统的闭环特征根&#xff0c;具有较多的优点。如&#xff1a; ①根据系统的开环频率特性揭示闭环系统的动态性能和稳态性能&#xff0c;得到定性和定量的结论&#xff0c;可以简单迅速…

Qt界面开发(对象树概念、信号与槽机制)

&#x1f333;对象树 在Qt框架中&#xff0c;对象树&#xff08;Object Tree&#xff09;是针对QObject类以及其子类的结构化组织方式/每一个QObject实例都可以有一个父对象和多个子对象&#xff0c;形成一种层次化的树状关系。这种设计在Qt中具有多个用途和优势。 概念&…

Apache Seata快速入门

前置推荐阅读&#xff1a;Apache Seata 简介-CSDN博客 快速开始 让我们从一个微服务示例开始。 用例​ 用户购买商品的业务逻辑。整个业务逻辑由 3 个微服务提供支持&#xff1a; 仓储服务&#xff1a;对给定的商品扣除仓储数量。订单服务&#xff1a;根据采购需求创建订单…

【Linux】解答:为什么创建目录文件,硬链接数是2;创建普通文件时,硬链接数是1?(超详细图文)

前言 大家好吖&#xff0c;欢迎来到 YY 滴Linux系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《Lin…

gitlab配置ssh密钥

1.配置用户信息 git config --global user.name "你的名字" git config --global user.email "你的邮箱" 查看配置是否成功 git config --global --list 2.生成密钥 终端 或 右键文件夹open git bash here 输入命令 ssh-keygen -t rsa -C 随意(生…

接口测试(二)jmeter——实现http请求、察看结果树、请求默认值

一、实现http请求&#xff0c;察看结果树 1. 测试计划 --> 添加 --> 线程(用户) --> 线程组 2. 线程组配置 默认配置 线程数&#xff1a;虚拟用户数&#xff0c;一个虚拟用户占用一个进程或线程。 Ramp-Up 时间&#xff08;秒&#xff09;&#xff1a;全部线程执行完…

使用Jenkins部署项目

部署中的痛点 为什么要用Jenkins&#xff1f;我说下我以前开发的痛点&#xff0c;在一些中小型企业&#xff0c;每次开发一个项目完成后&#xff0c;需要打包部署&#xff0c;可能没有专门的运维人员&#xff0c;只能开发人员去把项目打成一个exe包&#xff0c;可能这个项目已…

Kettle基本使用

目录 一、安装Kelttle 1-1 安装java环境 1-2 Kettle安装 二、Kettle的基本使用 2-1 将txt文本数据转为excel数据 创建txt文件 创建kettle的转换任务 定义转换流程 配置输入文件 连接读取和写入任务 配置excel输出 保存转换任务 执行转换任务 2-2 将txt文件输出到M…

数据库管理-第252期 深入浅出多主多活数据库技术- Cantian存储引擎(二)(20241017)

数据库管理252期 2024-10-17 数据库管理-第252期 深入浅出多主多活数据库技术- Cantian存储引擎&#xff08;二&#xff09;&#xff08;20241017&#xff09;1 部署规划2 服务器基础配置2.1 配置HOSTS2.2 关闭防火墙2.3 关闭SELinux2.4 配置yum源 3 编译服务器配置3.1 安装git…

「Python精品教程」Python快速入门,基础数据结构:数字

​***奕澄羽邦精品教程系列*** 编程环境&#xff1a; 1、Python 3.12.5 2、Visual Studio Code 1.92.1 在现实世界中&#xff0c;我们经常要面对各式各样的数字&#xff0c;通过简单或者复杂的数学运算&#xff0c;来帮助我们计算出想要的结果。程序开发过程中&#xff0c;数字…

自动化测试工具在API测试中的优势是什么?

在设计API接口时&#xff0c;确保数据获取的效率和准确性是至关重要的。以下是一些最佳实践和代码示例&#xff0c;帮助你提高API的数据获取效率和准确性。 1. 使用高效的数据访问模式 选择合适的数据库访问模式对于提高数据获取效率至关重要。例如&#xff0c;使用索引可以显…

【启明智显分享】ZX7981PM WIFI6 5G-CPE:2.5G WAN口,2.4G/5G双频段自动调速

昨天&#xff0c;我们向大家展现了ZX7981PG WIFI6 5G-CPE&#xff0c;它强大的性能也引起了一波关注&#xff0c;与此同时&#xff0c;我们了解到部分用户对更高容量与更高速网口的需求。没关系&#xff01;启明智显早就预料到了&#xff01;ZX7981PM满足你的需求&#xff01; …

Vue3 集成Monaco Editor编辑器

Vue3 集成Monaco Editor编辑器 1. 安装依赖2. 使用3. 效果 Monaco Editor &#xff08;官方链接 https://microsoft.github.io/monaco-editor/&#xff09;是一个由微软开发的功能强大的在线代码编辑器&#xff0c;被广泛应用于各种 Web 开发场景中。以下是对 Monaco Editor 的…

HTML5教程(三)- 常用标签

1 文本标签-h 标题标签&#xff08;head&#xff09;&#xff1a; 自带加粗效果&#xff0c;从h1到h6字体大小逐级递减一个标题独占一行 语法 <h1>一级标题</h1><h2>二级标题</h2><h3>三级标题</h3><h4>四级标题</h4><h5…

关于md5强比较和弱比较绕过的实验

在ctf比赛题中我们的md5强弱比较的绕过题型很多&#xff0c;大部分都是结合了PHP来进行一个考核。这一篇文章我将讲解一下最基础的绕过知识。 MD5弱比较 比较的步骤 在进行弱比较时&#xff0c;PHP会按照以下步骤执行&#xff1a; 确定数据类型&#xff1a;检查参与比较的两…

jmeter响应断言放进csv文件遇到的问题

用Jmeter的json 断言去测试http请求响应结果&#xff0c;发现遇到中文时出现乱码&#xff0c;导致无法正常进行响应断言&#xff0c;很影响工作。于是&#xff0c;察看了其他测试人员的解决方案&#xff0c;发现是jmeter本身对编码格式的设置导致了这一问题。解决方案是在jmete…

【文化课学习笔记】【化学】选必三:同分异构体的书写

【化学】选必三&#xff1a;同分异构体的书写 如果你是从 B 站一化儿笔记区来的&#xff0c;请先阅读我在第一篇有机化学笔记中的「读前须知」(点开头的黑色小三角展开)&#xff1a;链接 链状烃的取代和插空法 取代法 一取代物 甲烷、乙烷、丙烷、丁烷的种类 甲烷&#xff1a;只…

游戏逆向基础-找释放技能CALL

思路&#xff1a;通过send断点然后对send的data参数下写入断点找到游戏里面的技能或者攻击call 进入游戏先选好一个怪物&#xff08;之所以要先选好是因为选怪也会断&#xff0c;如果直接左键打怪的话就会断几次&#xff09; 断下来后对参数下硬件写入断点 硬件断点断下来后先…