下载本过滤器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函数。在过滤器IFileSinkFilter的SetFileName方法中,创建输出文件,获取字节流接口,创建MP3媒体接收器,为MP3媒体接收器设置演示时钟,获取MP3媒体接收器的IMFStreamSink接口。在过滤器运行时,启动时钟,开始接收流接收器事件。每当流接收器产生事件,将调用CAsyncCallback::Invoke函数,如果是“请求样本”事件,发出“发送样本”信号;在引脚的接收样本的Receive函数中,获取引脚样本,转换为媒体基础样本,检测“发送样本”信号,没有“发送样本”信号时等待,有“发送样本”信号时,向MP3媒体接收器发送样本。
下面是本过滤器DLL的全部代码
DLL头文件:WriteMP3.h
#ifndef DLL_FILE
#define DLL_FILE
#include "streams.h"
#include "initguid.h"
#if _DEBUG
#pragma comment(lib, "Strmbasd.lib")//C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\multimedia\directshow\baseclasses\Debug\strmbasd.lib
#else
#pragma comment(lib, "Strmbase.lib")//C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\multimedia\directshow\baseclasses\Release\strmbase.lib
#endif
#pragma comment(lib, "Winmm.lib")//C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\Winmm.lib
#pragma comment(lib, "msvcrt.lib")//C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\lib\msvcrt.lib
// {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;
}
}
#endif // DLL_FILE
DLL源文件:WriteMP3.cpp
#include "WriteMP3.h"
#include "CFilter.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
, CFilter::CreateInstance
, NULL
, &WriteMP3
}
};
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.h
#ifndef FILTER_FILE
#define FILTER_FILE
#include "WriteMP3.h"
#include "CInputPin.h"
#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 : 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);
STDMETHODIMP Run(REFERENCE_TIME tStart);
STDMETHODIMP Stop();
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;
};
IMFPresentationTimeSource* pTimeSource;//时间源接口
IMFPresentationClock* pPClock;//演示时钟接口
IMFAsyncCallback* pIMFAsyncCallback;//回调接口
HANDLE hSendSample;//“发送样本”事件句柄
HANDLE hOver;//“结束写MP3”事件句柄
};
#endif // FILTER_FILE
过滤器源文件:CFilter.cpp
#include "CFilter.h"
#pragma warning(disable:4355 4127)//禁用警告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;
}
CAsyncCallback* pCAsyncCallback=new CAsyncCallback();
pIMFAsyncCallback=pCAsyncCallback;
pCAsyncCallback->pCFilter=this;
hSendSample=CreateEvent( NULL, TRUE, FALSE, NULL); //创建事件,手动重置,初始状态无信号
hOver=CreateEvent( NULL, TRUE, FALSE, NULL); //创建事件,手动重置,初始状态无信号
}
CFilter::~CFilter()
{
CloseHandle(hSendSample);CloseHandle(hOver);
SafeRelease(&pHandler);//释放所有接口
SafeRelease(&pIMFStreamSink);
SafeRelease(&pStream);
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::Run(REFERENCE_TIME tStart)
{
if(m_pFileName==NULL)
{
MessageBox(NULL,L"未指定输出文件",NULL,MB_OK);return VFW_E_INVALID_FILE_FORMAT;
}
if(!m_pInputPin->IsConnected())return S_FALSE;//如果输入引脚没有连接,不运行过滤器
ResetEvent(hSendSample);//设置“发送样本”无信号
ResetEvent(hOver);//设置“结束发送样本”无信号
HRESULT hr;
hr=pPClock->Start(tStart);//启动时钟
hr=pIMFStreamSink->BeginGetEvent(pIMFAsyncCallback,NULL);//开始异步请求下一个事件。参数1指定回调对象
return CBaseFilter::Run(tStart);
}
STDMETHODIMP CFilter::Stop()
{
SetEvent(hOver);//发出“结束写MP3”信号,结束音频流,停止回调
return CBaseFilter::Stop();
}
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;//设置文件名失败
}
SafeRelease(&pStream);SafeRelease(&pSink);SafeRelease(&pIMFStreamSink);SafeRelease(&pHandler); SafeRelease(&pTimeSource);SafeRelease(&pPClock);
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=MFCreateSystemTimeSource(&pTimeSource);//创建基于系统时间的时间源
}
if(hr==S_OK)
{
hr=MFCreatePresentationClock(&pPClock);//创建演示时钟
}
if(hr==S_OK)
{
hr = pPClock->SetTimeSource(pTimeSource);//设置演示时钟的时间源
}
if(hr==S_OK)
{
hr = pSink->SetPresentationClock(pPClock);//设置媒体接收器上的演示时钟
}
return hr;
}
DWORD WINAPI ThreadProc(LPVOID lpParameter);
STDMETHODIMP CFilter::CAsyncCallback::Invoke(IMFAsyncResult* pAsyncResult)//当流接收器产生事件,调用此函数
{
HRESULT hr = S_OK;
IMFMediaEvent* pEvent = NULL;
MediaEventType meType = MEUnknown;
HRESULT hrStatus = S_OK;
hr = pCFilter->pIMFStreamSink->EndGetEvent(pAsyncResult, &pEvent);
if (SUCCEEDED(hr))
{
hr = pEvent->GetType(&meType);//获取事件类型
}
if (SUCCEEDED(hr))//如果成功
{
if(MEStreamSinkRequestSample==meType)//如果是“请求样本”事件
{
SetEvent(pCFilter->hSendSample);//发出“发送样本”信号
}
DWORD dw=WaitForSingleObject(pCFilter->hOver,0);//检测“结束写MP3”信号
if(dw==WAIT_OBJECT_0)//有“结束写MP3”信号
{
SafeRelease(&pEvent);//释放事件
hr=pCFilter->pIMFStreamSink->PlaceMarker(MFSTREAMSINK_MARKER_ENDOFSEGMENT,NULL,NULL);//放置流结束标记
hr=pCFilter->pPClock->Stop();
CreateThread(NULL,0,ThreadProc,pCFilter,0,NULL);
return S_OK;//结束写MP3文件,不再调用BeginGetEvent
}
hr = pCFilter->pIMFStreamSink->BeginGetEvent(this, NULL);
}
SafeRelease(&pEvent);//释放事件
return hr;
}
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
CFilter* pCFilter=(CFilter*)lpParameter;
Sleep(500);
HRESULT hr = pCFilter->pSink->Shutdown();//关闭媒体接收器
return 1;
}
引脚头文件:CInputPin.h
#ifndef PIN_FILE
#define PIN_FILE
#include "WriteMP3.h"
#include "CFilter.h"
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);
};
#endif //PIN_FILE
引脚源文件:CInputPin.cpp
#include "CInputPin.h"
CInputPin::CInputPin(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName) : CBaseInputPin(NAME("In"), pFilter, pFilter, phr, pPinName)//输入引脚构造函数
{
pCFilter = pFilter;
}
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* pBuffer = NULL;
hr = MFCreateMemoryBuffer(len, &pBuffer);//创建媒体基础缓冲区
if(hr!=S_OK || pBuffer==NULL)//如果创建失败
{
Sleep(1);goto CreateBuffer;//再次创建
}
BYTE* pData = NULL;
hr = pBuffer->Lock(&pData, NULL, NULL);//锁定媒体基础缓冲区
CopyMemory(pData, pBy,len);//从引脚样本缓冲区,复制数据到媒体基础样本缓冲区
hr = pBuffer->Unlock();//解锁媒体基础缓冲区
hr = pBuffer->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(pBuffer);//添加缓冲区到媒体基础样本
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(pCFilter->hSendSample,0);//检测“发送样本”信号
DWORD ov=WaitForSingleObject(pCFilter->hOver,0);//检测“结束写MP3”信号
if(ov==WAIT_OBJECT_0)//有“结束写MP3”信号
{
return S_FALSE;//如果返回值不是S_OK,流将终止
}
if(sn!=WAIT_OBJECT_0)//没有“发送样本”信号,再次检测(阻塞)
{
goto Agan;
}
hr=pCFilter->pIMFStreamSink->ProcessSample(pMFSample);//向MP3媒体接收器传送样本
ResetEvent(pCFilter->hSendSample);//清除“发送样本”信号
SafeRelease(&pBuffer);SafeRelease(&pMFSample);//释放媒体基础缓冲区,媒体基础样本
return S_OK;
}