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函数。在过滤器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;
}

下载本过滤器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、付费专栏及课程。

余额充值