DirectShow过滤器开发-读音频文件过滤器

下载由本文代码制作的DLL
本过滤器解析并读取音频文件,将音频解码为未压缩的PCM音频流由输出引脚输出。可以解析的音频文件格式有:aac,m4a,m4r,mp3,wav,wma。

过滤器的参数信息

过滤器名称:读音频文件
过滤器GUID:{9EA694F1-8B08-428D-9718-A353932B7995}
DLL注册函数名:DllRegisterServer
删除注册函数名:DllUnregisterServer
过滤器有1个输出引脚。
输出引脚标识:1
输出引脚媒体类型:
主要类型:MEDIATYPE_Audio
子类型:MEDIASUBTYPE_PCM
格式类型:FORMAT_WaveFormatEx

过滤器开发过程

过滤器从CSource派生,实现了IFileSourceFilter,IMediaSeeking接口。IFileSourceFilter接口用于指定要读取的音频文件。在IFileSourceFilter的Load方法中,创建了一个线程,在线程中创建了一个源读取器,用于获取指定媒体类型为PCM音频时,媒体类型的全部信息。IMediaSeeking接口用于调整播放的当前位置。

引脚从CSourceStream派生。
重写了ThreadProc函数,该函数是引脚的线程函数。在函数中创建了第2个源读取器,并指定源读取器音频流的媒体类型为在IFileSourceFilter的Load方法中获取到的媒体类型。当指定媒体类型成功时,源读取器将实例化解码器。在引脚类中声明了一个BOOL变量B_Pasue,在收到运行,停止,暂停请求时,设定B_Pasue的值。ThreadProc函数的其它代码,从CSourceStream::ThreadProc定义复制而来。
重写了DoBufferProcessingLoop函数,在函数中添加使用B_Pasue变量控制样本的发送代码。
重写了FillBuffer函数。在函数中,从源读取器读取样本,包括样本的显示时间,持续时间,有效数据长度,样本数据;使用这些参数填充引脚样本缓冲区,指定引脚样本各参数。
重写了GetMediaType函数,用于指定引脚的媒体类型。
重写了DecideBufferSize函数,用于确定引脚样本缓冲区的大小。

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

DLL模块定义文件:AudioReader.def

LIBRARY     AudioReader.dll
EXPORTS
            DllMain                 PRIVATE
            DllGetClassObject       PRIVATE
            DllCanUnloadNow         PRIVATE
            DllRegisterServer       PRIVATE
            DllUnregisterServer     PRIVATE

DLL头文件:AudioReader.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

#include "strsafe.h"
#include "mfapi.h"
#include "mfidl.h"
#include "mfreadwrite.h"
#include "mferror.h"
#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mfreadwrite.lib")
#pragma comment(lib, "mfuuid.lib")

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

// {9EA694F1-8B08-428D-9718-A353932B7995}
DEFINE_GUID(CLSID_AudioReader, 
0x9ea694f1, 0x8b08, 0x428d, 0x97, 0x18, 0xa3, 0x53, 0x93, 0x2b, 0x79, 0x95);

#endif // DLL_FILE

DLL源文件:AudioReader.cpp

#include "AudioReader.h"
#include "CFilter.h"


const AMOVIESETUP_MEDIATYPE PinTypes =
{
    &MEDIATYPE_Audio,            // 主要类型
    &MEDIASUBTYPE_PCM            // 子类型
};

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

const AMOVIESETUP_FILTER FilterInfo =  //过滤器的注册信息
{
    &CLSID_AudioReader,          //过滤器的类标识
    L"读音频文件",                //过滤器的名称
    MERIT_DO_NOT_USE,            //过滤器优先值
    1,                           //引脚数量
    &PinInfo                     //引脚信息
};

CFactoryTemplate g_Templates []  = {
    {
		L"读音频文件"
      , &CLSID_AudioReader
      , CFilter::CreateInstance
      , NULL
      , &FilterInfo
	}
};

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  "AudioReader.h"
#include  "CPin.h"

class CFilter : public CSource, public IFileSourceFilter, public IMediaSeeking
{
	friend class CPin;
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);
	CPin* pCAudioOut;//音频引脚指针
	LPWSTR m_pFileName;//要读取的音频文件路径
	HANDLE hInit;//“获取信息完成”事件句柄
	HANDLE hAReset;//“音频更改播放位置”事件句柄
	IMFMediaType* pUncompressedAudioType;
	LONGLONG CUR;//音频当前时间,100纳秒单位
	LONGLONG DUR;//音频持续时间,100纳秒单位
	LONGLONG AResetPos;//音频新位置,100纳秒单位
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 "CPin.h"

CFilter::CFilter(LPUNKNOWN lpunk, HRESULT *phr) : CSource(NAME("读音频文件"), lpunk, CLSID_AudioReader)
{
	m_paStreams = new CSourceStream*[1];
	pCAudioOut=new CPin(phr, this, L"Out");//创建音频引脚
	m_paStreams[0]=(CSourceStream*)pCAudioOut;
	pUncompressedAudioType = NULL;
	DUR=0;//音频持续时间,100纳秒单位
	AResetPos=0;
	hInit = CreateEvent( NULL, TRUE, FALSE, NULL); //创建事件,为手动重置,初始状态无信号 
	hAReset = CreateEvent( NULL, FALSE, FALSE, NULL); //创建事件,自动重置,初始状态无信号 
}

CFilter::~CFilter()
{
	CloseHandle(hInit);CloseHandle(hAReset);
	SafeRelease(&pUncompressedAudioType);
}

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);
	WCHAR* pWch=new WCHAR[len];
    m_pFileName = (LPWSTR)pWch;
    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".aac") != 0 && wcscmp(ExName,L".m4a") != 0 && wcscmp(ExName,L".m4r") != 0
		 && wcscmp(ExName,L".mp3") != 0 && wcscmp(ExName,L".wav") != 0 && wcscmp(ExName,L".wma") != 0)//如果不是指定的音频文件
	{
		delete[] pWch; m_pFileName=NULL;
		return VFW_E_INVALID_FILE_FORMAT;//设置文件名失败
	}
	ResetEvent(hInit);//设置“获取信息完成”无信号
	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;//参数为过滤器指针
	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; 
	}
	IMFSourceReader* pReader = NULL;
	hr = MFCreateSourceReaderFromURL(pCFilter->m_pFileName, NULL, &pReader);
	if (hr != S_OK)
	{
		MessageBox(NULL,L"创建源读取器失败",NULL,MB_OK); 
		MFShutdown();//关闭媒体基础
		CoUninitialize();//关闭COM库
		return 0; 
	}
	IMFMediaType* pPartialType = NULL;
	if (SUCCEEDED(hr))
	{
		hr = MFCreateMediaType(&pPartialType);//创建空的媒体类型
	}
	if (SUCCEEDED(hr))
	{
		hr = pPartialType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);//设置主要类型音频
	}
	if (SUCCEEDED(hr))
	{
		hr = pPartialType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);//设置子类型PCM
	}
	if (SUCCEEDED(hr))
	{
		hr = pReader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,NULL, pPartialType);//在源读取器上设置此媒体类型
	}
	SafeRelease(&pCFilter->pUncompressedAudioType);
	if (SUCCEEDED(hr))
	{
		hr = pReader->GetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,&pCFilter->pUncompressedAudioType);//获取完整的音频媒体类型
	}
	PROPVARIANT var;
	if (SUCCEEDED(hr))
	{
		PropVariantInit(&var);
		hr=pReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE ,MF_PD_DURATION,&var);//获取音频时间长度,100纳秒单位
	}
    if (SUCCEEDED(hr))
    {
		pCFilter->DUR=(LONGLONG)var.uhVal.QuadPart;
		PropVariantClear(&var);
    }

	SafeRelease(&pReader);SafeRelease(&pPartialType);
	MFShutdown();//关闭媒体基础
	CoUninitialize();//关闭COM库
	if (hr!=S_OK)
	{
		MessageBox(0,L"获取信息失败",0,MB_OK);return 0;
	}
	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))
	{
		AResetPos=*pCurrent;*pStop=DUR-*pCurrent;
		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;
}

引脚头文件:CPin.h

#ifndef  PIN_FILE
#define PIN_FILE

#include "AudioReader.h"
#include "CFilter.h"

class CPin : public CSourceStream
{
	friend class CFilter;
public:
    CPin(HRESULT *phr, CSource *pParent, LPCWSTR pPinName);
    ~CPin();
	HRESULT GetMediaType(CMediaType *pmt);
	HRESULT DecideBufferSize(IMemAllocator * pAlloc, ALLOCATOR_PROPERTIES * pRequest);//确定缓冲区大小
	STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
	HRESULT FillBuffer(IMediaSample *pms);
	HRESULT OnThreadDestroy(void);
	CFilter* pCFilter;//过滤器指针
	IMFSourceReader* pReader;//源读取器接口
	BOOL B_Pasue;//TRUE,暂停状态;FALSE,运行或停止状态
protected:
	DWORD ThreadProc(void);
	HRESULT DoBufferProcessingLoop(void);
};


#endif // PIN_FILE

引脚源文件:CPin.cpp

#include "CPin.h"

CPin::CPin(HRESULT *phr, CSource *pParent, LPCWSTR pPinName) : CSourceStream(NAME("Out"), phr, pParent, pPinName)
{
	pCFilter=(CFilter*)pParent;
	pReader = NULL;B_Pasue=FALSE;
}

CPin::~CPin()
{

}

HRESULT CPin::GetMediaType(CMediaType *pmt)
{
	DWORD dw=WaitForSingleObject(pCFilter->hInit,2000);//最多等待2秒
	if(dw!=WAIT_OBJECT_0) return E_UNEXPECTED;//如果没有“获取信息完成”信号,返回错误
	AM_MEDIA_TYPE* pMt=NULL;
	pCFilter->pUncompressedAudioType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION,(void**)&pMt);//将IMFMediaType表示的媒体类型,转换为AM_MEDIA_TYPE结构形式
	pmt->Set(*pMt);
	pCFilter->pUncompressedAudioType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION,pMt);//释放GetRepresentation分配的内存
	return S_OK;
}

HRESULT CPin::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;
}

STDMETHODIMP CPin::Notify(IBaseFilter * pSender, Quality q)
{
	 return NOERROR;
}

HRESULT CPin::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=pReader->SetCurrentPosition(GUID_NULL, pv);//更改源读取器读取位置
		PropVariantClear(&pv);
		pms->SetDiscontinuity(TRUE);//设置流中断
	}

	DWORD index,flags;LONGLONG stime;
	IMFSample* pIMFSample=NULL;IMFMediaBuffer* pIMFMediaBuffer=NULL;
AganRead:
	hr=pReader->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;//使用IMediaSeeking接口调整播放位置后,AResetPos可能不为0,需计算时间戳的值
	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;
} 

HRESULT CPin::OnThreadDestroy(void) 
{
	SafeRelease(&pReader);//释放源读取器接口
	MFShutdown();//关闭媒体基础
	return NOERROR;
}

DWORD CPin::ThreadProc()
{
	WaitForSingleObject(pCFilter->hInit,INFINITE);//等待“获取信息完成”信号
	HRESULT hr;
    hr = MFStartup(MF_VERSION);
    if (hr != S_OK)
	{
		MessageBox(NULL,L"初始化媒体基础失败",NULL,MB_OK); 
		return 1;
	}
	hr = MFCreateSourceReaderFromURL(pCFilter->m_pFileName, NULL, &pReader);
	if (hr != S_OK)
	{
		MessageBox(NULL,L"创建源读取器失败",NULL,MB_OK); 
		MFShutdown();//关闭媒体基础
		CoUninitialize();//关闭COM库
		return 1; 
	}
	if (SUCCEEDED(hr))
	{
		hr = pReader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,NULL, pCFilter->pUncompressedAudioType);//在源读取器上设置此媒体类型
	}
	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);

	B_Pasue=TRUE;

    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:
			if(B_Pasue)
			{
				B_Pasue=FALSE;
			}
			else
			{
				B_Pasue=TRUE;
			}
			Reply(NOERROR);
			DoBufferProcessingLoop();
			break;
		case CMD_STOP:
			B_Pasue=FALSE;
			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 CPin::DoBufferProcessingLoop(void) 
{
    Command com;
    OnThreadStartPlay();
    do 
	{
		while (!CheckRequest(&com)) 
		{
			if(B_Pasue)
			{
				continue;//如果是暂停状态,不获取样本缓冲区,不填充样本和发送样本
			}
			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;
}

下载由本文代码制作的DLL

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

h3974

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

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

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

打赏作者

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

抵扣说明:

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

余额充值