下载本过滤器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();
}