下载由本文代码制作的DLL
本过滤器解析MP4文件,读取视频流解码为RGB32,由视频输出引脚输出。读取音频流解码为16位PCM,由音频输出引脚输出。
本过滤器使用下面方式开发:
1.使用Win7系统。
2.安装Windows SDK 7.1软件开发工具包。
3.使用Visual Studio 2010,或Visual Studio 2008,或Visual Studio 2005开发环境。
过滤器从CSource类派生,实现了IFileSourceFilter,IMediaSeeking接口。IFileSourceFilter接口用于指定和获取要读取的MP4文件路径。IMediaSeeking接口用于调整播放的当前位置。在IFileSourceFilter接口的Load方法中,创建了1个源读取器,用于获取MP4文件的视频音频的一些参数。
视频引脚和音频引脚从CSourceStream类派生。重写了 GetMediaType,DecideBufferSize,FillBuffer,OnThreadDestroy,ThreadProc,DoBufferProcessingLoop函数。GetMediaType指定首选媒体类型;DecideBufferSize确定缓冲区大小;FillBuffer从源读取器读取音频或视频样本,填充引脚样本缓冲区;在OnThreadDestroy函数中,释放源读取器;在音频引脚ThreadProc函数中,创建了第2个源读取器,专门读取MP4中的音频样本;在视频引脚ThreadProc函数中,创建了第3个源读取器,专门读取MP4中的视频样本。从第3个源读取器读取的视频样本,格式类型实际为FORMAT_VideoInfo2,格式结构的图像高度值为负。
本过滤器的参数信息
过滤器GUID:{4EE72E8C-8E40-4AC2-8AB8-5A38AB9BC1A9}
DLL注册函数名:DllRegisterServer
删除注册函数名:DllUnregisterServer
视频输出引脚媒体类型:
主要类型:MEDIATYPE_Video
子类型:MEDIASUBTYPE_RGB32
格式类型:FORMAT_VideoInfo
样本为固定大小。
不使用时间压缩。
音频输出引脚媒体类型:
主要类型:MEDIATYPE_Audio
子类型:MEDIASUBTYPE_PCM
格式类型:FORMAT_WaveFormatEx
样本是固定大小。
不使用时间压缩。
样本为16位。
采样率为44100。
下面是本过滤器DLL的全部代码:
DLL头文件:MP4Reader.h
#ifndef DLL_FILE
#define DLL_FILE
#include <streams.h>
#include <initguid.h>
#include <wmcodecdsp.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
#include "dvdmedia.h"
#include <strsafe.h>
#include "strmif.h"
#include "mfapi.h"
#include "mfidl.h"
#include "mfreadwrite.h"
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfreadwrite")
#pragma comment(lib, "mfuuid")
template <class T> void SafeRelease(T** ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
// {4EE72E8C-8E40-4AC2-8AB8-5A38AB9BC1A9}
DEFINE_GUID(CLSID_MP4Reader,
0x4ee72e8c, 0x8e40, 0x4ac2, 0x8a, 0xb8, 0x5a, 0x38, 0xab, 0x9b, 0xc1, 0xa9);
#endif // DLL_FILE
DLL源文件:MP4Reader.cpp
#include "MP4Reader.h"
#include "CFilter.h"
const AMOVIESETUP_MEDIATYPE sudPin1Types = // 引脚1媒体类型
{
&MEDIATYPE_Video, // 主要类型
&MEDIASUBTYPE_RGB32 // 子类型
};
const AMOVIESETUP_MEDIATYPE sudPin2Types = // 引脚2媒体类型
{
&MEDIATYPE_Audio, // 主要类型
&MEDIASUBTYPE_PCM // 子类型
};
const AMOVIESETUP_PIN sudPins[] = // 引脚信息
{
{
L"VideoOut", //引脚名称
FALSE, //必须渲染输入引脚
TRUE, //输出引脚
FALSE, //具有该引脚的零个实例
FALSE, //可以创建一个以上引脚的实例
&CLSID_NULL, //该引脚连接的过滤器的类标识
NULL, //该引脚连接的引脚名称
1, //引脚支持的媒体类型数
&sudPin1Types //媒体类型信息
},
{
L"AudioOut", //引脚名称
FALSE, //必须渲染输入引脚
TRUE, //输出引脚
FALSE, //具有该引脚的零个实例
FALSE, //可以创建一个以上引脚的实例
&CLSID_NULL, //该引脚连接的过滤器的类标识
NULL, //该引脚连接的引脚名称
1, //引脚支持的媒体类型数
&sudPin2Types //媒体类型信息
}
} ;
const AMOVIESETUP_FILTER MP4Reader = //过滤器的注册信息
{
&CLSID_MP4Reader, //过滤器的类标识
L"读MP4", //过滤器的名称
MERIT_DO_NOT_USE, //过滤器优先值
2, //引脚数量
sudPins //引脚信息
};
CFactoryTemplate g_Templates [] = {
{
L"读MP4"
, &CLSID_MP4Reader
, CFilter::CreateInstance
, NULL
, &MP4Reader
}
};
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);
}
DLL模块定义文件:MP4Reader.def
LIBRARY MP4Reader.dll
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
过滤器头文件:CFilter.h
#ifndef FILTER_FILE
#define FILTER_FILE
#include "MP4Reader.h"
#include "CVideoPin.h"
#include "CAudioPin.h"
class CFilter : public CSource, public IFileSourceFilter, public IMediaSeeking
{
friend class CVideoPin;
friend class CAudioPin;
public:
CFilter(LPUNKNOWN pUnk,HRESULT *phr);
~CFilter();
static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
DECLARE_IUNKNOWN
STDMETHODIMP Load(LPCOLESTR lpwszFileName, const AM_MEDIA_TYPE *pmt);
STDMETHODIMP GetCurFile(LPOLESTR * ppszFileName, AM_MEDIA_TYPE *pmt);
CVideoPin* pCVideoOut;//视频引脚指针
CAudioPin* pCAudioOut;//音频引脚指针
LPWSTR m_pFileName;//要读取的媒体文件路径
HANDLE hInit;//“获取信息完成”事件句柄
HANDLE hAReset;//“音频更改播放位置”事件句柄
LONGLONG VResetPos;//视频新位置
LONGLONG AResetPos;//音频新位置
LONGLONG DUR;//媒体持续时间,100纳秒单位
LONGLONG CUR;//音频当前时间,100纳秒单位
IMFMediaType* pAudioMT;//音频媒体类型
IMFMediaType* pVideoMT;//视频媒体类型
private:
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
public:
HRESULT STDMETHODCALLTYPE CheckCapabilities(DWORD *pCapabilities);
HRESULT STDMETHODCALLTYPE ConvertTimeFormat(LONGLONG *pTarget, const GUID *pTargetFormat, LONGLONG Source, const GUID *pSourceFormat);
HRESULT STDMETHODCALLTYPE GetAvailable(LONGLONG *pEarliest, LONGLONG *pLatest);
HRESULT STDMETHODCALLTYPE GetCapabilities(DWORD *pCapabilities);
HRESULT STDMETHODCALLTYPE GetCurrentPosition(LONGLONG *pCurrent);
HRESULT STDMETHODCALLTYPE GetDuration(LONGLONG *pDuration);
HRESULT STDMETHODCALLTYPE GetPositions(LONGLONG *pCurrent, LONGLONG *pStop);
HRESULT STDMETHODCALLTYPE GetPreroll(LONGLONG *pllPreroll);
HRESULT STDMETHODCALLTYPE GetRate(double *pdRate);
HRESULT STDMETHODCALLTYPE GetStopPosition(LONGLONG *pStop);
HRESULT STDMETHODCALLTYPE GetTimeFormat(GUID *pFormat);
HRESULT STDMETHODCALLTYPE IsFormatSupported(const GUID *pFormat);
HRESULT STDMETHODCALLTYPE IsUsingTimeFormat(const GUID *pFormat);
HRESULT STDMETHODCALLTYPE QueryPreferredFormat(GUID *pFormat);
HRESULT STDMETHODCALLTYPE SetPositions(LONGLONG *pCurrent, DWORD dwCurrentFlags, LONGLONG *pStop, DWORD dwStopFlags);
HRESULT STDMETHODCALLTYPE SetRate(double dRate);
HRESULT STDMETHODCALLTYPE SetTimeFormat(const GUID *pFormat);
};
#endif // FILTER_FILE
过滤器源文件:CFilter.cpp
#include "CFilter.h"
#include "CVideoPin.h"
#include "CAudioPin.h"
CFilter::CFilter(LPUNKNOWN lpunk, HRESULT *phr) : CSource(NAME("读MP4"), lpunk, CLSID_MP4Reader)
{
pCAudioOut=new CAudioPin(phr, this, L"AudioOut");
pCVideoOut=new CVideoPin(phr, this, L"VideoOut");
hInit = CreateEvent( NULL, TRUE, FALSE, NULL); //创建事件,为手动重置,初始状态无信号
hAReset = CreateEvent( NULL, FALSE, FALSE, NULL); //创建事件,自动重置,初始状态无信号
m_pFileName=NULL;DUR=0;CUR=0; pVideoMT=NULL;pAudioMT=NULL;
VResetPos=0;AResetPos=0;
}
CFilter::~CFilter()
{
CloseHandle(hAReset);
CloseHandle(hInit);
SafeRelease(&pAudioMT);SafeRelease(&pVideoMT);
if(m_pFileName!=NULL)delete[] m_pFileName;
}
STDMETHODIMP CFilter::NonDelegatingQueryInterface(REFIID iid, void ** ppv)
{
if(iid==IID_IMediaSeeking)
{
return GetInterface(static_cast<IMediaSeeking*>(this), ppv);
}
else if(iid==IID_IFileSourceFilter)
{
return GetInterface(static_cast<IFileSourceFilter*>(this), ppv);
}
else
return CBaseFilter::NonDelegatingQueryInterface(iid, ppv);
}
CUnknown * WINAPI CFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr)
{
return new CFilter(pUnk, phr);//创建过滤器
}
DWORD WINAPI GetInfoThread(LPVOID pParam);//读取视频音频参数线程函数声明
STDMETHODIMP CFilter::Load(LPCOLESTR lpwszFileName, const AM_MEDIA_TYPE *pmt)
{
CheckPointer(lpwszFileName,E_POINTER);
if(wcslen(lpwszFileName) > MAX_PATH || wcslen(lpwszFileName)<4)
return ERROR_FILENAME_EXCED_RANGE;
size_t len = 1+lstrlenW(lpwszFileName);
m_pFileName = (LPWSTR)new WCHAR[len];
if (m_pFileName == NULL)return E_OUTOFMEMORY;
HRESULT hr = StringCchCopyW(m_pFileName, len, lpwszFileName);
wchar_t ExName[5]={m_pFileName[len-5],m_pFileName[len-4],m_pFileName[len-3],m_pFileName[len-2],0};
if(wcscmp(ExName,L".mp4") != 0)//如果不是MP4文件
{
delete[] m_pFileName; m_pFileName=NULL;
return VFW_E_INVALID_FILE_FORMAT;//设置文件名失败
}
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)GetInfoThread,this,0,NULL);//创建读取视频音频参数线程
return S_OK;
}
STDMETHODIMP CFilter::GetCurFile(LPOLESTR * ppszFileName, AM_MEDIA_TYPE *pmt)
{
CheckPointer(ppszFileName, E_POINTER);
*ppszFileName = NULL;
if (m_pFileName !=NULL)
{
DWORD n = sizeof(WCHAR)*(1+lstrlenW(m_pFileName));
*ppszFileName = (LPOLESTR)CoTaskMemAlloc(n);
if (*ppszFileName!=NULL)CopyMemory(*ppszFileName, m_pFileName, n);
}
return S_OK;
}
DWORD WINAPI GetInfoThread(LPVOID pParam)//读取视频音频参数线程
{
CFilter* pCFilter=(CFilter*)pParam;//过滤器指针
ResetEvent(pCFilter->hInit);//设置“获取信息完成”无信号
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (hr != S_OK)
{
MessageBox(NULL,L"初始化COM库失败",NULL,MB_OK);
return 0;
}
hr = MFStartup(MF_VERSION);
if (hr != S_OK)
{
MessageBox(NULL,L"初始化媒体基础失败",NULL,MB_OK);
CoUninitialize();//关闭COM库
return 0;
}
IMFAttributes* pIMFAttributes = NULL;
hr=MFCreateAttributes(&pIMFAttributes, 0);
if (SUCCEEDED(hr))
{
hr = pIMFAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, (UINT32)1);//使用基于硬件的媒体基础转换
}
if (SUCCEEDED(hr))
{
hr = pIMFAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, (UINT32)1);//允许源读取器进行有限的视频处理
}
IMFSourceReader* pIMFSourceReader = NULL;
if (SUCCEEDED(hr))
{
hr = MFCreateSourceReaderFromURL(pCFilter->m_pFileName, pIMFAttributes, &pIMFSourceReader);
}
if (hr != S_OK)
{
MessageBox(NULL,L"创建源读取器失败",NULL,MB_OK);
MFShutdown();//关闭媒体基础
CoUninitialize();//关闭COM库
return 0;
}
IMFMediaType* pAudioMTA = NULL;
if (SUCCEEDED(hr))
{
hr = MFCreateMediaType(&pAudioMTA);//创建音频媒体类型
}
if (SUCCEEDED(hr))
{
hr = pAudioMTA->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);//设置主要类型音频
}
if (SUCCEEDED(hr))
{
hr = pAudioMTA->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);//设置子类型PCM
}
if (SUCCEEDED(hr))
{
hr = pIMFSourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, pAudioMTA);//设置音频媒体类型
}
SafeRelease(&pAudioMTA);
IMFMediaType* pVideoMTV = NULL;
if (SUCCEEDED(hr))
{
hr = MFCreateMediaType(&pVideoMTV);//创建视频媒体类型
}
if (SUCCEEDED(hr))
{
hr = pVideoMTV->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);//设置主要类型为视频
}
if (SUCCEEDED(hr))
{
hr =pVideoMTV->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);//设置子类型RGB32
}
if (SUCCEEDED(hr))
{
hr = pIMFSourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, pVideoMTV);//设置视频媒体类型
}
SafeRelease(&pVideoMTV);
if (SUCCEEDED(hr))
{
hr = pIMFSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM,&pCFilter->pAudioMT);//获取当前音频媒体类型
}
if (SUCCEEDED(hr))
{
hr = pIMFSourceReader->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM,&pCFilter->pVideoMT);//获取当前视频媒体类型
}
PROPVARIANT var;
if (SUCCEEDED(hr))
{
PropVariantInit(&var);
hr=pIMFSourceReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE ,MF_PD_DURATION,&var);//获取媒体时间长度,100纳秒单位
}
if (SUCCEEDED(hr))
{
pCFilter->DUR=(LONGLONG)var.uhVal.QuadPart;
PropVariantClear(&var);
}
//释放所有接口
SafeRelease(&pIMFSourceReader);SafeRelease(&pIMFAttributes);
MFShutdown();//关闭媒体基础
CoUninitialize();//关闭COM库
SetEvent(pCFilter->hInit);//发出“获取信息完成”信号
return 1;
}
HRESULT STDMETHODCALLTYPE CFilter::CheckCapabilities(DWORD *pCapabilities)//检查是否具有指定的查找功能
{
if(pCapabilities==NULL)return E_POINTER;
if(*pCapabilities == (AM_SEEKING_CanSeekAbsolute | AM_SEEKING_CanGetDuration))
return S_OK;
else if(*pCapabilities & AM_SEEKING_CanSeekAbsolute) //可以查找到绝对位置
{
return S_FALSE;
}
else if(*pCapabilities & AM_SEEKING_CanGetDuration)//可以获取持续时间
{
return S_FALSE;
}
else return E_FAIL;
}
HRESULT STDMETHODCALLTYPE CFilter::ConvertTimeFormat(LONGLONG *pTarget, const GUID *pTargetFormat, LONGLONG Source, const GUID *pSourceFormat)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CFilter::GetAvailable(LONGLONG *pEarliest, LONGLONG *pLatest)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CFilter::GetCapabilities(DWORD *pCapabilities)
{
*pCapabilities=AM_SEEKING_CanSeekAbsolute | AM_SEEKING_CanGetDuration;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::GetCurrentPosition(LONGLONG *pCurrent)
{
*pCurrent=CUR;return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::GetDuration(LONGLONG *pDuration)
{
DWORD dw=WaitForSingleObject(hInit,0);
if(dw!=WAIT_OBJECT_0)return E_NOTIMPL;
*pDuration=DUR;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::GetPositions(LONGLONG *pCurrent, LONGLONG *pStop)
{
DWORD dw=WaitForSingleObject(hInit,0);
if(dw==WAIT_OBJECT_0)
{
*pCurrent=CUR;return S_OK;
}
else return S_FALSE;
}
HRESULT STDMETHODCALLTYPE CFilter::GetPreroll(LONGLONG *pllPreroll)
{
*pllPreroll=0;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::GetRate(double *pdRate)
{
*pdRate=1.0;return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::GetStopPosition(LONGLONG *pStop)
{
*pStop=DUR-AResetPos;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::GetTimeFormat(GUID *pFormat)
{
if(pFormat==NULL)return E_POINTER;
*pFormat=TIME_FORMAT_MEDIA_TIME;return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::IsFormatSupported(const GUID *pFormat)
{
if(TIME_FORMAT_MEDIA_TIME==*pFormat)return S_OK;
else return S_FALSE;
}
HRESULT STDMETHODCALLTYPE CFilter::IsUsingTimeFormat(const GUID *pFormat)
{
if(*pFormat==TIME_FORMAT_MEDIA_TIME)return S_OK;
else return S_FALSE;
}
HRESULT STDMETHODCALLTYPE CFilter::QueryPreferredFormat(GUID *pFormat)
{
if(pFormat==NULL)return E_POINTER;
*pFormat=TIME_FORMAT_MEDIA_TIME;return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::SetPositions(LONGLONG *pCurrent, DWORD dwCurrentFlags, LONGLONG *pStop, DWORD dwStopFlags)
{
DWORD dw=WaitForSingleObject(hInit,0);
if((dw==WAIT_OBJECT_0) && (dwCurrentFlags & AM_SEEKING_AbsolutePositioning))
{
HRESULT hr;
VResetPos=*pCurrent;AResetPos=*pCurrent;*pStop=DUR-*pCurrent;
hr=pCVideoOut->DeliverBeginFlush();
hr=pCVideoOut->Stop();
hr=pCVideoOut->DeliverEndFlush();
hr=pCVideoOut->Run();
SetEvent(hAReset);//发出“音频更改播放位置”信号
return S_OK;
}
else
{
return S_FALSE;
}
}
HRESULT STDMETHODCALLTYPE CFilter::SetRate(double dRate)
{
if(dRate==1.0)return S_OK;
else return S_FALSE;
}
HRESULT STDMETHODCALLTYPE CFilter::SetTimeFormat(const GUID *pFormat)
{
if(*pFormat==TIME_FORMAT_MEDIA_TIME)return S_OK;
else return S_FALSE;
}
音频输出引脚头文件:CAudioPin.h
#ifndef PIN_FILE2
#define PIN_FILE2
#include "MP4Reader.h"
#include "CFilter.h"
class CAudioPin : public CSourceStream
{
friend class CFilter;
public:
CAudioPin(HRESULT *phr, CSource *pParent, LPCWSTR pPinName);
~CAudioPin();
HRESULT GetMediaType(CMediaType *pmt);
HRESULT DecideBufferSize(IMemAllocator * pAlloc, ALLOCATOR_PROPERTIES * pRequest);//确定缓冲区大小
HRESULT FillBuffer(IMediaSample *pms);
STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
HRESULT OnThreadDestroy(void);
CFilter* pCFilter;//过滤器指针
IMFSourceReader* pIMFSourceReader;
protected:
DWORD ThreadProc(void);
HRESULT DoBufferProcessingLoop(void);
};
#endif // PIN_FILE2
音频输出引脚源文件:CAudioPin.cpp
#include "CAudioPin.h"
CAudioPin::CAudioPin(HRESULT *phr, CSource *pParent, LPCWSTR pPinName) : CSourceStream(NAME("AudioOut"), phr, pParent, pPinName)
{
pCFilter=(CFilter*)pParent;
pIMFSourceReader=NULL;
}
CAudioPin::~CAudioPin()
{
}
HRESULT CAudioPin::GetMediaType(CMediaType *pmt)
{
DWORD dw=WaitForSingleObject(pCFilter->hInit,2000);//最多等待2秒
if(dw!=WAIT_OBJECT_0) return E_UNEXPECTED;//如果没有“获取信息完成”信号,返回错误
AM_MEDIA_TYPE* pMt=NULL;
pCFilter->pAudioMT->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION,(void**)&pMt);//将IMFMediaType表示的媒体类型,转换为AM_MEDIA_TYPE结构形式
pmt->Set(*pMt);
pCFilter->pAudioMT->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION,pMt);//释放GetRepresentation分配的内存
return S_OK;
}
HRESULT CAudioPin::DecideBufferSize(IMemAllocator * pAlloc, ALLOCATOR_PROPERTIES * pRequest)
{
DWORD dw=WaitForSingleObject(pCFilter->hInit,2000);//最多等待2秒
if(dw!=WAIT_OBJECT_0) return E_FAIL;//如果没有“获取信息完成”信号
HRESULT hr;
pRequest->cBuffers = 1;//1个缓冲区
pRequest->cbBuffer = 1000000;//缓冲区的大小1M
ALLOCATOR_PROPERTIES Actual;
hr = pAlloc->SetProperties(pRequest,&Actual);
if(FAILED(hr))return hr;
if(Actual.cbBuffer < pRequest->cbBuffer)// 这个分配器是否不合适
{
return E_FAIL;
}
return NOERROR;
}
HRESULT CAudioPin::FillBuffer(IMediaSample *pms)
{
HRESULT hr;
BYTE *pBuffer;
pms->GetPointer(&pBuffer);//获取样本缓冲区指针
long BufferLen = pms->GetSize();//获取样本缓冲区大小
DWORD dw=WaitForSingleObject(pCFilter->hAReset,0);
if(dw==WAIT_OBJECT_0)//如果有“音频更改播放位置”信号
{
hr=DeliverBeginFlush();
Sleep(1);
hr=DeliverEndFlush();
hr=DeliverNewSegment(0,pCFilter->DUR-pCFilter->AResetPos,1.0);
PROPVARIANT pv;
PropVariantInit(&pv);
pv.vt=20;
pv.hVal.QuadPart=pCFilter->AResetPos;
hr=pIMFSourceReader->SetCurrentPosition(GUID_NULL, pv);//更改源读取器位置
PropVariantClear(&pv);
pms->SetDiscontinuity(TRUE);//设置流中断
}
DWORD index,flags;LONGLONG stime;
IMFSample* pIMFSample=NULL;IMFMediaBuffer* pIMFMediaBuffer=NULL;
AganRead:
hr=pIMFSourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM,0,&index,&flags,&stime,&pIMFSample);//读取音频样本
if(MF_SOURCE_READERF_ENDOFSTREAM == flags)return S_FALSE;//如果到达流的末尾,返回S_FALSE
if(pIMFSample==NULL)
{
Sleep(1);goto AganRead;
}
LONGLONG Cur, Dur, End;
hr=pIMFSample->GetSampleTime(&Cur);//获取显示时间
pCFilter->CUR=Cur;
hr=pIMFSample->GetSampleDuration(&Dur);//获取持续时间
End=Cur+Dur;//计算样本结束时间
Cur-=pCFilter->AResetPos;End-=pCFilter->AResetPos;
DWORD Lt;
hr=pIMFSample->GetTotalLength(&Lt);//获取有效长度
hr=pIMFSample->GetBufferByIndex(0,&pIMFMediaBuffer);//获取缓冲区接口
BYTE* pSB=NULL;
hr=pIMFMediaBuffer->Lock(&pSB,NULL,NULL);//锁定缓冲区
if(Lt<=(DWORD)BufferLen)//防止数据长度大于样本缓冲区大小时,进行数据复制
CopyMemory(pBuffer, pSB, Lt);//复制数据
pms->SetTime(&Cur,&End);//设置时间戳
pms->SetActualDataLength(Lt);//设置有效数据长度
pms->SetSyncPoint(TRUE);//设置样本为同步点
hr=pIMFMediaBuffer->Unlock();//解锁缓冲区
SafeRelease(&pIMFMediaBuffer);SafeRelease(&pIMFSample);//释放接口
return S_OK;
}
STDMETHODIMP CAudioPin::Notify(IBaseFilter * pSender, Quality q)
{
return NOERROR;
}
DWORD CAudioPin::ThreadProc()
{
HRESULT hr;
hr = MFStartup(MF_VERSION);
if (hr != S_OK)
{
MessageBox(NULL,L"初始化媒体基础失败",NULL,MB_OK);
return 1;
}
IMFAttributes* pIMFAttributesAudio = NULL;
hr=MFCreateAttributes(&pIMFAttributesAudio, 0);
if (SUCCEEDED(hr))
{
hr = pIMFAttributesAudio->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, (UINT32)1);//使用基于硬件的媒体基础转换
}
if (SUCCEEDED(hr))
{
hr = pIMFAttributesAudio->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, (UINT32)1);//允许源读取器进行有限的视频处理
}
if (SUCCEEDED(hr))
{
hr = MFCreateSourceReaderFromURL(pCFilter->m_pFileName, pIMFAttributesAudio, &pIMFSourceReader);//创建源读取器
}
SafeRelease(&pIMFAttributesAudio);
if (hr != S_OK)
{
MessageBox(NULL,L"创建源读取器失败",NULL,MB_OK);
return 1;
}
WaitForSingleObject(pCFilter->hInit,INFINITE);//等待“获取信息完成”信号
IMFMediaType* pAudioMTA = NULL;
if (SUCCEEDED(hr))
{
hr = MFCreateMediaType(&pAudioMTA);//创建音频媒体类型
}
if (SUCCEEDED(hr))
{
hr = pAudioMTA->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);//设置主要类型音频
}
if (SUCCEEDED(hr))
{
hr = pAudioMTA->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);//设置子类型PCM
}
if (SUCCEEDED(hr))
{
hr = pIMFSourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, pAudioMTA);//设置音频媒体类型
}
SafeRelease(&pAudioMTA);
if(FAILED(hr))
{
MessageBox(NULL,L"初始化音频媒体类型失败",NULL,MB_OK);return 1;
}
Command com;
do
{
com = GetRequest();
if (com != CMD_INIT)
{
DbgLog((LOG_ERROR, 1, TEXT("Thread expected init command")));
Reply((DWORD) E_UNEXPECTED);
}
} while (com != CMD_INIT);
DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread initializing")));
hr = OnThreadCreate();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadCreate failed. Aborting thread.")));
OnThreadDestroy();
Reply(hr);
return 1;
}
Reply(NOERROR);
Command cmd;
do
{
cmd = GetRequest();
switch (cmd)
{
case CMD_EXIT:
Reply(NOERROR);
break;
case CMD_RUN:
DbgLog((LOG_ERROR, 1, TEXT("CMD_RUN received before a CMD_PAUSE???")));
case CMD_PAUSE:
Reply(NOERROR);
DoBufferProcessingLoop();
break;
case CMD_STOP:
Reply(NOERROR);
break;
default:
DbgLog((LOG_ERROR, 1, TEXT("Unknown command %d received!"), cmd));
Reply((DWORD) E_NOTIMPL);
break;
}
} while (cmd != CMD_EXIT);
hr = OnThreadDestroy();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadDestroy failed. Exiting thread.")));
return 1;
}
DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread exiting")));
return 0;
}
HRESULT CAudioPin::OnThreadDestroy(void)
{
SafeRelease(&pIMFSourceReader);
MFShutdown();//关闭媒体基础
return NOERROR;
}
HRESULT CAudioPin::DoBufferProcessingLoop(void)
{
Command com;
OnThreadStartPlay();
do
{
while (!CheckRequest(&com))
{
IMediaSample *pSample;
HRESULT hr = GetDeliveryBuffer(&pSample,NULL,NULL,0);
if (FAILED(hr))
{
Sleep(1);
continue;
}
hr = FillBuffer(pSample);
if (hr == S_OK)
{
hr = Deliver(pSample);
pSample->Release();
if(hr != S_OK)
{
DbgLog((LOG_TRACE, 2, TEXT("Deliver() returned %08x; stopping"), hr));
return S_OK;
}
}
else if (hr == S_FALSE)
{
pSample->Release();
DeliverEndOfStream();
return S_OK;
}
else
{
pSample->Release();
DbgLog((LOG_ERROR, 1, TEXT("Error %08lX from FillBuffer!!!"), hr));
DeliverEndOfStream();
m_pFilter->NotifyEvent(EC_ERRORABORT, hr, 0);
return hr;
}
}//while
if (com == CMD_RUN || com == CMD_PAUSE)
{
Reply(NOERROR);
}
else if (com != CMD_STOP)
{
Reply((DWORD) E_UNEXPECTED);
DbgLog((LOG_ERROR, 1, TEXT("Unexpected command!!!")));
}
} while (com != CMD_STOP);
return S_FALSE;
}
视频输出引脚头文件:CVideoPin.h
#ifndef PIN_FILE1
#define PIN_FILE1
#include "MP4Reader.h"
#include "CFilter.h"
class CVideoPin : public CSourceStream
{
friend class CFilter;
public:
CVideoPin(HRESULT *phr, CSource *pParent, LPCWSTR pPinName);
~CVideoPin();
HRESULT GetMediaType(CMediaType *pmt);
HRESULT DecideBufferSize(IMemAllocator * pAlloc, ALLOCATOR_PROPERTIES * pRequest);//确定缓冲区大小
HRESULT FillBuffer(IMediaSample *pms);
STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
HRESULT OnThreadStartPlay(void);
HRESULT OnThreadDestroy(void);
CFilter* pCFilter;
IMFSourceReader* pIMFSourceReader;
AM_MEDIA_TYPE* pMt;
LONG BufferSize;
protected:
DWORD ThreadProc(void);
HRESULT DoBufferProcessingLoop(void);
};
#endif // PIN_FILE1
视频输出引脚源文件:CVideoPin.cpp
#include "CVideoPin.h"
CVideoPin::CVideoPin(HRESULT *phr, CSource *pParent, LPCWSTR pPinName) : CSourceStream(NAME("VideoOut"),phr, pParent, pPinName)
{
pCFilter=(CFilter*)pParent;
pIMFSourceReader=NULL;
pMt=NULL;
}
CVideoPin::~CVideoPin()
{
}
HRESULT CVideoPin::GetMediaType(CMediaType *pmt)
{
DWORD dw=WaitForSingleObject(pCFilter->hInit,2000);//最多等待2秒
if(dw!=WAIT_OBJECT_0) return E_UNEXPECTED;//如果没有“获取信息完成”信号,返回错误
pCFilter-> pVideoMT->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION,(void**)&pMt);//将IMFMediaType表示的媒体类型,转换为AM_MEDIA_TYPE结构形式
VIDEOINFOHEADER2* pH2=(VIDEOINFOHEADER2*)(pMt->pbFormat);
pmt->SetType(&MEDIATYPE_Video);//设置主要类型
pmt->SetSubtype(&MEDIASUBTYPE_RGB32);//设置子类型
pmt->SetFormatType(&FORMAT_VideoInfo);//设置格式类型
pmt->SetTemporalCompression(FALSE);//不使用时间压缩
VIDEOINFOHEADER* pH=(VIDEOINFOHEADER*)pmt->AllocFormatBuffer(sizeof(VIDEOINFOHEADER));//分配格式块内存
pH->rcSource=pH2->rcSource;
pH->rcTarget=pH2->rcTarget;
pH->dwBitRate=pH2->dwBitRate;
pH->dwBitErrorRate=pH2->dwBitErrorRate;
pH->AvgTimePerFrame=pH2->AvgTimePerFrame;
pH->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
pH->bmiHeader.biWidth=pH2->bmiHeader.biWidth;
pH->bmiHeader.biHeight=pH2->bmiHeader.biHeight;
pH->bmiHeader.biPlanes=1;
pH->bmiHeader.biBitCount=32;
pH->bmiHeader.biCompression=BI_RGB;
BufferSize=pH->bmiHeader.biSizeImage=pH2->bmiHeader.biWidth*abs(pH2->bmiHeader.biHeight)*4;
pH->bmiHeader.biXPelsPerMeter=0;
pH->bmiHeader.biYPelsPerMeter=0;
pH->bmiHeader.biClrUsed=0;
pH->bmiHeader.biClrImportant=0;
pmt->SetSampleSize(BufferSize);//设置样本大小
pCFilter-> pVideoMT->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION,pMt);//释放GetRepresentation分配的内存
return S_OK;
}
HRESULT CVideoPin::DecideBufferSize(IMemAllocator * pAlloc, ALLOCATOR_PROPERTIES * pRequest)
{
DWORD dw=WaitForSingleObject(pCFilter->hInit,2000);//最多等待2秒
if(dw!=WAIT_OBJECT_0) return E_FAIL;//如果没有“获取信息完成”信号
HRESULT hr;
pRequest->cBuffers = 1;//1个缓冲区
pRequest->cbBuffer =BufferSize;//缓冲区的大小
ALLOCATOR_PROPERTIES Actual;
hr = pAlloc->SetProperties(pRequest,&Actual);
if(FAILED(hr))return hr;
if(Actual.cbBuffer < pRequest->cbBuffer)// 这个分配器是否不合适
{
return E_FAIL;
}
return NOERROR;
}
HRESULT CVideoPin::FillBuffer(IMediaSample *pms)
{
HRESULT hr;
BYTE *pBuffer;
pms->GetPointer(&pBuffer);//获取样本缓冲区指针
long BufferLen = pms->GetSize();//获取样本缓冲区大小
pms->SetSyncPoint(TRUE);//设置样本为同步点
hr=pms->SetDiscontinuity(FALSE);
DWORD index,flags;LONGLONG stime;
IMFSample* pIMFSample=NULL;IMFMediaBuffer* pIMFMediaBuffer=NULL;
AganRead:
hr=pIMFSourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM,0,&index,&flags,&stime,&pIMFSample);//读取视频样本
if(MF_SOURCE_READERF_ENDOFSTREAM == flags)return S_FALSE;//如果到达流的末尾,返回S_FALSE
if(pIMFSample==NULL)
{
Sleep(1);goto AganRead;
}
LONGLONG Cur, Dur, End;
hr=pIMFSample->GetSampleTime(&Cur);//获取显示时间
hr=pIMFSample->GetSampleDuration(&Dur);//获取持续时间
End=Cur+Dur;//计算样本结束时间
Cur-=pCFilter->VResetPos;
End-=pCFilter->VResetPos;
DWORD Lt;
hr=pIMFSample->GetTotalLength(&Lt);//获取有效长度
hr=pIMFSample->GetBufferByIndex(0,&pIMFMediaBuffer);//获取缓冲区接口
BYTE* pSB=NULL;
hr=pIMFMediaBuffer->Lock(&pSB,NULL,NULL);//锁定缓冲区
CopyMemory(pBuffer,pSB,BufferLen);
pms->SetTime(&Cur,&End);//设置时间戳
pms->SetActualDataLength(BufferLen);//设置有效数据长度
hr=pIMFMediaBuffer->Unlock();//解锁缓冲区
SafeRelease(&pIMFMediaBuffer);SafeRelease(&pIMFSample);//释放接口
return S_OK;
}
STDMETHODIMP CVideoPin::Notify(IBaseFilter * pSender, Quality q)
{
return NOERROR;
}
DWORD CVideoPin::ThreadProc()
{
HRESULT hr;
hr = MFStartup(MF_VERSION);
if (hr != S_OK)
{
MessageBox(NULL,L"初始化媒体基础失败",NULL,MB_OK);
return 1;
}
IMFAttributes* pIMFAttributesVideo= NULL;
hr=MFCreateAttributes(&pIMFAttributesVideo, 0);
if (SUCCEEDED(hr))
{
hr = pIMFAttributesVideo->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, (UINT32)1);//使用基于硬件的媒体基础转换
}
if (SUCCEEDED(hr))
{
hr = pIMFAttributesVideo->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, (UINT32)1);//允许源读取器进行有限的视频处理
}
if (SUCCEEDED(hr))
{
hr = MFCreateSourceReaderFromURL(pCFilter->m_pFileName, pIMFAttributesVideo, &pIMFSourceReader);//创建源读取器
}
SafeRelease(&pIMFAttributesVideo);
if (hr != S_OK)
{
MessageBox(NULL,L"创建源读取器失败",NULL,MB_OK);
return 1;
}
WaitForSingleObject(pCFilter->hInit,INFINITE);//等待“获取信息完成”信号
IMFMediaType* pVideoMTV = NULL;
if (SUCCEEDED(hr))
{
hr = MFCreateMediaType(&pVideoMTV);//创建视频媒体类型
}
if (SUCCEEDED(hr))
{
hr = pVideoMTV->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);//设置主要类型为视频
}
if (SUCCEEDED(hr))
{
hr =pVideoMTV->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);//设置子类型RGB32
}
if (SUCCEEDED(hr))
{
hr = pIMFSourceReader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, pVideoMTV);//设置视频媒体类型
}
SafeRelease(&pVideoMTV);
if(FAILED(hr))
{
MessageBox(NULL,L"初始化媒体类型失败",NULL,MB_OK);return 1;
}
Command com;
do
{
com = GetRequest();
if (com != CMD_INIT)
{
DbgLog((LOG_ERROR, 1, TEXT("Thread expected init command")));
Reply((DWORD) E_UNEXPECTED);
}
} while (com != CMD_INIT);
DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread initializing")));
hr = OnThreadCreate();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadCreate failed. Aborting thread.")));
OnThreadDestroy();
Reply(hr);
return 1;
}
Reply(NOERROR);
Command cmd;
do
{
cmd = GetRequest();
switch (cmd)
{
case CMD_EXIT:
Reply(NOERROR);
break;
case CMD_RUN:
DbgLog((LOG_ERROR, 1, TEXT("CMD_RUN received before a CMD_PAUSE???")));
case CMD_PAUSE:
Reply(NOERROR);
DoBufferProcessingLoop();
break;
case CMD_STOP:
Reply(NOERROR);
break;
default:
DbgLog((LOG_ERROR, 1, TEXT("Unknown command %d received!"), cmd));
Reply((DWORD) E_NOTIMPL);
break;
}
} while (cmd != CMD_EXIT);
hr = OnThreadDestroy();
if (FAILED(hr))
{
DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadDestroy failed. Exiting thread.")));
return 1;
}
DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread exiting")));
return 0;
}
HRESULT CVideoPin::OnThreadStartPlay(void)
{
HRESULT hr;
PROPVARIANT var;
PropVariantInit(&var);
var.vt=20;var.hVal.QuadPart=pCFilter->VResetPos;
hr=pIMFSourceReader->SetCurrentPosition(GUID_NULL, var);//更改源读取器位置
PropVariantClear(&var);
return DeliverNewSegment(0,pCFilter->DUR-pCFilter->VResetPos,1.0);
}
HRESULT CVideoPin::OnThreadDestroy(void)
{
SafeRelease(&pIMFSourceReader);
MFShutdown();//关闭媒体基础
return NOERROR;
}
HRESULT CVideoPin::DoBufferProcessingLoop(void)
{
Command com;
OnThreadStartPlay();
do
{
while (!CheckRequest(&com))
{
IMediaSample *pSample;
HRESULT hr = GetDeliveryBuffer(&pSample,NULL,NULL,0);
if (FAILED(hr))
{
Sleep(1);
continue;
}
hr = FillBuffer(pSample);
if (hr == S_OK)
{
hr = Deliver(pSample);
pSample->Release();
if(hr != S_OK)
{
DbgLog((LOG_TRACE, 2, TEXT("Deliver() returned %08x; stopping"), hr));
return S_OK;
}
}
else if (hr == S_FALSE)
{
pSample->Release();
DeliverEndOfStream();
return S_OK;
}
else
{
pSample->Release();
DbgLog((LOG_ERROR, 1, TEXT("Error %08lX from FillBuffer!!!"), hr));
DeliverEndOfStream();
m_pFilter->NotifyEvent(EC_ERRORABORT, hr, 0);
return hr;
}
}//while
if (com == CMD_RUN || com == CMD_PAUSE)
{
Reply(NOERROR);
}
else if (com != CMD_STOP)
{
Reply((DWORD) E_UNEXPECTED);
DbgLog((LOG_ERROR, 1, TEXT("Unexpected command!!!")));
}
} while (com != CMD_STOP);
return S_FALSE;
}