DirectShow过滤器开发-写MP3音频文件过滤器

下载本过滤器DLL
本过滤器将已压缩的MP3音频流写入MP3文件。

过滤器信息

过滤器名称:写MP3
过滤器GUID: {3A3782BD-D5D6-4C52-80ED-0AA44985DB58}
DLL注册函数名:DllRegisterServer
删除注册函数名:DllUnregisterServer
过滤器有1个输入引脚。

引脚标识:In
引脚媒体类型:
主要类型:MEDIATYPE_Audio
子类型:{0x00000055, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}
格式类型:FORMAT_WaveFormatEx
采样率:48000,44100,32000。

过滤器开发信息

过滤器类从CBaseFilter,CCritSec,IFileSinkFilter派生。实现了IFileSinkFilter接口,用于指定或获取输出文件路径。实现了IMFAsyncCallback回调接口。引脚类从CBaseInputPin派生。重写了CheckMediaType,SetMediaType,Receive函数。在过滤器类的InitSink方法中,创建输出文件,获取字节流接口,创建MP3媒体接收器,为MP3媒体接收器设置演示时钟,获取MP3媒体接收器的IMFStreamSink接口。在过滤器运行时,启动时钟,开始接收流接收器事件。每当流接收器产生事件,将调用CAsyncCallback::Invoke函数,如果是“请求样本”事件,发出“请求样本”信号;在引脚的接收样本的Receive函数中,获取引脚样本,转换为媒体基础样本,检测“请求样本”信号,没有“请求样本”信号时等待,有“请求样本”信号时,向MP3媒体接收器发送样本。

下面是本过滤器DLL的全部代码

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

// {3A3782BD-D5D6-4C52-80ED-0AA44985DB58}
DEFINE_GUID(CLSID_WriteMP3,
	0x3a3782bd, 0xd5d6, 0x4c52, 0x80, 0xed, 0xa, 0xa4, 0x49, 0x85, 0xdb, 0x58);

DEFINE_GUID(MEDIASUBTYPE_MP3,
	0x00000055, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);

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


#include "shlwapi.h"
#include "strsafe.h"
#include "mfapi.h"
#include "Mfobjects.h"
#include "Mfidl.h"
#pragma comment(lib, "Mf.lib")
#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "Mfuuid.lib")
#pragma comment(lib, "Shlwapi.lib")

class CFilter;

class CInputPin : public CBaseInputPin
{
	friend class CFilter;
	CFilter *pCFilter;
public:
	CInputPin(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName);
	~CInputPin();
	HRESULT CheckMediaType(const CMediaType *pmt);
	HRESULT SetMediaType(const CMediaType *pmt);
	STDMETHODIMP Receive(IMediaSample *pSample);
	HRESULT Active();
	HRESULT Inactive();
	HANDLE hRequestSample;//“请求样本”事件句柄
	HANDLE hStopped;//“停止”事件句柄
};

class CFilter : public CBaseFilter, public CCritSec, public IFileSinkFilter
{
	friend class CInputPin;
public:
	CFilter(LPWSTR lpName, LPUNKNOWN pUnk, HRESULT *phr);
	virtual ~CFilter();
	DECLARE_IUNKNOWN
	STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
	virtual HRESULT STDMETHODCALLTYPE  SetFileName(LPCOLESTR pszFileName, const AM_MEDIA_TYPE *pmt);
	virtual HRESULT STDMETHODCALLTYPE  GetCurFile(LPOLESTR * ppszFileName, AM_MEDIA_TYPE *pmt);
	static CUnknown * WINAPI CreateInstance(LPUNKNOWN, HRESULT *);
	int GetPinCount();
	CBasePin *GetPin(int n);
	HRESULT InitSink();//初始化MP3媒体接收器
	STDMETHODIMP Run(REFERENCE_TIME tStart);
	CInputPin *m_pInputPin;//输入引脚指针
	LPOLESTR m_pFileName;//要创作的MP3文件路径
	IMFByteStream *pStream;//字节流接口
	IMFMediaSink *pSink;//MP3媒体接收器接口
	IMFStreamSink* pIMFStreamSink;//媒体接收器流接口
	IMFMediaTypeHandler* pHandler;//获取和设置流媒体类型接口
	class CAsyncCallback : public IMFAsyncCallback //实现回调接口
	{
	public:
		CAsyncCallback() : m_cRef(1) { }
		virtual ~CAsyncCallback() { }
		STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
		{
			static const QITAB qit[] =
			{
				QITABENT(CAsyncCallback, IMFAsyncCallback),{ 0 }
			};
			return QISearch(this, qit, riid, ppv);
		}
		STDMETHODIMP_(ULONG) AddRef()
		{
			return InterlockedIncrement(&m_cRef);
		}
		STDMETHODIMP_(ULONG) Release()
		{
			long cRef = InterlockedDecrement(&m_cRef);
			if (cRef == 0)
			{
				delete this;
			}
			return cRef;
		}
		STDMETHODIMP GetParameters(DWORD* pdwFlags, DWORD* pdwQueue)//此方法的实现是可选的
		{
			return E_NOTIMPL;
		}
		STDMETHODIMP Invoke(IMFAsyncResult* pAsyncResult);//回调函数
		CFilter* pCFilter;
		long    m_cRef;
	};
	CAsyncCallback* pCAsyncCallback = NULL;
	IMFPresentationTimeSource* pTimeSource;//时间源接口
	IMFPresentationClock* pPClock;//演示时钟接口
	IMFAsyncCallback* pIMFAsyncCallback;//回调接口
};

#endif //DLL_FILE

DLL源文件:DLL.cpp

#include "DLL.h"


const AMOVIESETUP_MEDIATYPE sudPinTypes =
{
	&MEDIATYPE_Audio,            //主要类型
	&MEDIASUBTYPE_MP3            //子类型
};

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

const AMOVIESETUP_FILTER WriteMP3 =  //过滤器的注册信息
{
	&CLSID_WriteMP3,            //过滤器的类标识
	L"写MP3",                   //过滤器的名称
	MERIT_DO_NOT_USE,           //过滤器优先值
	1,                          //引脚数量
	&sudPin                     //引脚信息
};

CFactoryTemplate g_Templates[] = {
	{
		L"写MP3"                       //对象(这里为过滤器)名称
		, &CLSID_WriteMP3              //对象CLSID的指针
	, CFilter::CreateInstance          //创建对象实例的函数的指针
	, NULL                             //指向从DLL入口点调用的函数的指针
	, &WriteMP3                        //指向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"

#pragma warning(disable:4355 )//禁用警告C4355

CFilter::CFilter(LPWSTR lpName, LPUNKNOWN pUnk, HRESULT *phr) : CBaseFilter(lpName, pUnk, (CCritSec *) this, CLSID_WriteMP3)
{
	HRESULT hr = MFStartup(MF_VERSION);//初始化媒体基础
	if (hr != S_OK)
	{
		MessageBox(NULL, L"初始化媒体基础失败", NULL, MB_OK); return;
	}
	pSink = NULL; pStream = NULL; pIMFStreamSink = NULL; pHandler = NULL; pTimeSource = NULL; pPClock = NULL;
	m_pInputPin = new CInputPin(this, phr, L"In");//创建输入引脚
	if (m_pInputPin == NULL)
	{
		if (phr) *phr = E_OUTOFMEMORY;
	}
	pCAsyncCallback = new CAsyncCallback();
	pIMFAsyncCallback = pCAsyncCallback;
	pCAsyncCallback->pCFilter = this;
	hr = MFCreateSystemTimeSource(&pTimeSource);//创建基于系统时间的时间源
	hr = MFCreatePresentationClock(&pPClock);//创建演示时钟
	hr = pPClock->SetTimeSource(pTimeSource);//设置演示时钟的时间源
}

CFilter::~CFilter()
{
	SafeRelease(&pHandler);//释放所有接口
	SafeRelease(&pIMFStreamSink);
	SafeRelease(&pSink);
	SafeRelease(&pTimeSource);
	SafeRelease(&pPClock);
	SafeRelease(&pIMFAsyncCallback);
	MFShutdown();//关闭媒体基础
}

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

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

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

STDMETHODIMP CFilter::NonDelegatingQueryInterface(REFIID iid, void ** ppv)
{
	if (iid == IID_IFileSinkFilter)
	{
		return GetInterface(static_cast<IFileSinkFilter*>(this), ppv);
	}
	else
		return CBaseFilter::NonDelegatingQueryInterface(iid, ppv);
}

HRESULT CFilter::GetCurFile(LPOLESTR *ppszFileName, AM_MEDIA_TYPE *pmt)
{
	CheckPointer(ppszFileName, E_POINTER);
	*ppszFileName = NULL;
	if (m_pFileName != NULL)
	{
		size_t len = 1 + lstrlenW(m_pFileName);
		*ppszFileName = (LPOLESTR)QzTaskMemAlloc(sizeof(WCHAR) * (len));
		if (*ppszFileName != NULL)
		{
			HRESULT hr = StringCchCopyW(*ppszFileName, len, m_pFileName);
		}
	}
	if (pmt)
	{
		ZeroMemory(pmt, sizeof(*pmt));
		pmt->majortype = MEDIATYPE_NULL;
		pmt->subtype = MEDIASUBTYPE_NULL;
	}
	return S_OK;
}

HRESULT CFilter::SetFileName(LPCOLESTR pszFileName, const AM_MEDIA_TYPE *pmt)
{
	CheckPointer(pszFileName, E_POINTER);
	if (wcslen(pszFileName) > MAX_PATH || wcslen(pszFileName)<4)
		return ERROR_FILENAME_EXCED_RANGE;
	size_t len = 1 + lstrlenW(pszFileName);
	m_pFileName = new WCHAR[len];
	if (m_pFileName == 0)
		return E_OUTOFMEMORY;
	HRESULT hr = StringCchCopyW(m_pFileName, len, pszFileName);
	if (m_pFileName[len - 2] != '3' || m_pFileName[len - 3] != 'p' || m_pFileName[len - 4] != 'm' || m_pFileName[len - 5] != '.')//如果不是MP3文件
	{
		delete m_pFileName; m_pFileName = NULL;
		return VFW_E_INVALID_FILE_FORMAT;//设置文件名失败
	}
	InitSink();//初始化MP3媒体接收器,此次调用仅为了获取IMFMediaTypeHandler接口
	pSink->Shutdown();//关闭媒体接收器并释放它正在使用的资源
	SafeRelease(&pStream);//释放IMFByteStream接口,关闭输出文件
	return S_OK;
}

HRESULT CFilter::InitSink()//初始化MP3媒体接收器
{
	SafeRelease(&pHandler); SafeRelease(&pIMFStreamSink); SafeRelease(&pSink); 
	HRESULT hr = MFCreateFile(//从文件创建字节流
		MF_ACCESSMODE_WRITE, //写入模式
		MF_OPENMODE_DELETE_IF_EXIST, //创建一个新文件。如果该文件存在,请覆盖该文件
		MF_FILEFLAGS_NONE, //默认行为
		m_pFileName, //输出的MP3文件路径
		&pStream
		);
	if (hr == S_OK)
	{
		hr = MFCreateMP3MediaSink(pStream, &pSink);//创建MP3媒体接收器
	}
	if (hr == S_OK)
	{
		hr = pSink->GetStreamSinkByIndex(0, &pIMFStreamSink);//获取IMFStreamSink接口
	}
	if (hr == S_OK)
	{
		hr = pIMFStreamSink->GetMediaTypeHandler(&pHandler);//获取IMFMediaTypeHandler接口
	}
	if (hr == S_OK)
	{
		hr = pSink->SetPresentationClock(pPClock);//设置媒体接收器上的演示时钟
	}
	return hr;
}

STDMETHODIMP CFilter::Run(REFERENCE_TIME tStart)
{
	if (m_pFileName == NULL)
	{
		MessageBox(NULL, L"未指定输出文件", NULL, MB_OK); return S_FALSE;
	}
	return CBaseFilter::Run(tStart);
}

STDMETHODIMP CFilter::CAsyncCallback::Invoke(IMFAsyncResult* pAsyncResult)//当流接收器产生事件,调用此函数
{
	HRESULT hr = S_OK;
	IMFMediaEvent* pEvent = NULL;
	MediaEventType meType = MEUnknown;
	HRESULT hrStatus = S_OK;
	int Count;
	hr = pCFilter->pIMFStreamSink->EndGetEvent(pAsyncResult, &pEvent);
	if (SUCCEEDED(hr))
	{
		hr = pEvent->GetType(&meType);//获取事件类型
	}
	if (SUCCEEDED(hr))//如果成功
	{
		switch (meType)
		{
		case MEStreamSinkStarted:
			break;
		case MEStreamSinkStopped:
			break;
		case MEStreamSinkRequestSample://如果是“请求样本”事件
			SetEvent(pCFilter->m_pInputPin->hRequestSample);//设置“请求样本”有信号
			break;
		}
	}
	SafeRelease(&pEvent);//释放事件
	hr = pCFilter->pIMFStreamSink->BeginGetEvent(this, NULL);
	return hr;
}

引脚源文件:CInputPin.cpp

#include "DLL.h"

CInputPin::CInputPin(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName) : CBaseInputPin(NAME("In"), pFilter, pFilter, phr, pPinName)
{
	pCFilter = pFilter;
	hRequestSample = CreateEvent(NULL, TRUE, FALSE, NULL);  //创建事件,手动重置,初始状态无信号
	hStopped = CreateEvent(NULL, TRUE, FALSE, NULL);  //创建事件,手动重置,初始状态无信号
}

CInputPin::~CInputPin()//输入引脚析构函数
{

}

HRESULT CInputPin::CheckMediaType(const CMediaType *pmt)
{
	if (pmt->majortype == MEDIATYPE_Audio && pmt->subtype == MEDIASUBTYPE_MP3 && pmt->formattype == FORMAT_WaveFormatEx)
	{
		WAVEFORMATEX* p = (WAVEFORMATEX*)pmt->pbFormat;
		if (p->wFormatTag != 85 || (p->nSamplesPerSec != 48000 && p->nSamplesPerSec != 44100 && p->nSamplesPerSec != 32000))return S_FALSE;
		return S_OK;
	}
	else
		return S_FALSE;
}

HRESULT CInputPin::SetMediaType(const CMediaType *pmt)
{
	HRESULT hr;
	AM_MEDIA_TYPE* pMt = (AM_MEDIA_TYPE*)pmt;
	IMFMediaType* pIMFMediaType = NULL;
	hr = MFCreateMediaTypeFromRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pMt, &pIMFMediaType);//从AM_MEDIA_TYPE结构创建媒体基础媒体类型
	if (hr != S_OK)
	{
		MessageBox(0, L"MFCreateMediaTypeFromRepresentation失败", 0, MB_OK); return S_FALSE;
	}
	hr = pCFilter->pHandler->SetCurrentMediaType(pIMFMediaType);//设置接收器流当前媒体类型
	SafeRelease(&pIMFMediaType);
	if (hr != S_OK)
	{
		MessageBox(0, L"SetCurrentMediaType失败", 0, MB_OK); return S_FALSE;
	}
	return CBaseInputPin::SetMediaType(pmt);
}

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

CreateBuffer:
	IMFMediaBuffer* pMFBuffer = NULL;
	hr = MFCreateMemoryBuffer(len, &pMFBuffer);//创建媒体基础缓冲区
	if (hr != S_OK || pMFBuffer == NULL)//如果创建失败
	{
		Sleep(1); goto CreateBuffer;//再次创建
	}
	BYTE* pData = NULL;
	hr = pMFBuffer->Lock(&pData, NULL, NULL);//锁定媒体基础缓冲区
	CopyMemory(pData, pBy, len);//从引脚样本缓冲区,复制数据到媒体基础样本缓冲区
	hr = pMFBuffer->Unlock();//解锁媒体基础缓冲区
	hr = pMFBuffer->SetCurrentLength((DWORD)len);//设置媒体基础缓冲区的数据长度

CreateSample:
	IMFSample* pMFSample = NULL;
	if (SUCCEEDED(hr))
	{
		hr = MFCreateSample(&pMFSample);//创建媒体基础样本
		if (hr != S_OK || pMFSample == NULL)//如果创建失败
		{
			Sleep(1); goto CreateSample;//再次创建
		}
	}
	hr = pMFSample->AddBuffer(pMFBuffer);//添加缓冲区到媒体基础样本

	REFERENCE_TIME star, end;
	hr = pSample->GetTime(&star, &end);
	REFERENCE_TIME dur = end - star;
	if (hr == S_OK)//如果引脚样本有时间戳
	{
		hr = pMFSample->SetSampleTime(star);//设置媒体基础样本显示时间
		hr = pMFSample->SetSampleDuration(dur);//设置媒体基础样本持续时间
	}
	hr = pSample->IsDiscontinuity();
	if (hr == S_OK)//如果引脚样本有中断标志
	{
		hr = pMFSample->SetUINT32(MFSampleExtension_Discontinuity, 1);//设置媒体基础样本中断标志
	}
	hr = pSample->IsSyncPoint();
	if (hr == S_OK)//如果引脚样本有同步点标志
	{
		hr = pMFSample->SetUINT32(MFSampleExtension_CleanPoint, 1);//设置媒体基础样本同步点标志
	}

Agan:
	DWORD sn = WaitForSingleObject(hRequestSample, 0);//检测“请求样本”信号
	DWORD ov = WaitForSingleObject(hStopped, 0);//检测“停止”信号
	if (ov == WAIT_OBJECT_0)//有“停止”信号
	{
		return S_FALSE;//如果返回值不是S_OK,流将终止
	}
	if (sn != WAIT_OBJECT_0)//没有“请求样本”信号,再次检测(阻塞)
	{
		goto Agan;
	}
	hr = pCFilter->pIMFStreamSink->ProcessSample(pMFSample);//向MP3媒体接收器传送样本
	ResetEvent(hRequestSample);//设置“请求样本”无信号
	SafeRelease(&pMFBuffer); SafeRelease(&pMFSample);//释放媒体基础缓冲区,媒体基础样本
	return S_OK;
}

HRESULT CInputPin::Active()
{
	ResetEvent(hRequestSample);//设置“请求样本”无信号
	ResetEvent(hStopped);//设置“停止”无信号
	HRESULT hr;
	hr = pCFilter->InitSink();//初始化MP3媒体接收器
	hr = pCFilter->pPClock->Start(0);//启动时钟
	hr = pCFilter->pIMFStreamSink->BeginGetEvent(pCFilter->pIMFAsyncCallback, NULL);//开始异步请求下一个事件。参数1指定回调对象
	return CBaseInputPin::Active();
}

HRESULT CInputPin::Inactive()
{
	SetEvent(hStopped);//设置“停止”有信号
	HRESULT hr;
	hr = pCFilter->pIMFStreamSink->EndGetEvent(NULL, NULL);
	hr = pCFilter->pPClock->Stop();
	hr = pCFilter->pSink->Shutdown();//关闭媒体接收器
	SafeRelease(&pCFilter->pStream);//释放IMFByteStream接口,关闭输出文件
	return CBaseInputPin::Inactive();
}

下载本过滤器DLL

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

h3974

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值