DirectShow过滤器开发-读MP4视频文件过滤器(再写)

下载本过滤器DLL

本过滤器读取MP4视频文件,输出视频流和音频流。已验证可读取的文件编码方式,视频有:H264,MP4V,M4S2;音频:AAC。

过滤器信息

过滤器名称:读MP4_3

过滤器GUID:{4C42ABCE-6459-4997-863D-BD969D07A862}

DLL注册函数名:DllRegisterServer

删除注册函数名:DllUnregisterServer

过滤器有2个输出引脚。

输出引脚1标识:Video

输出引脚1媒体类型:

主要类型:MEDIATYPE_Video

子类型:MEDIASUBTYPE_NULL

输出引脚2标识:Audio

输出引脚2媒体类型:

主要类型:MEDIATYPE_Audio

子类型:MEDIASUBTYPE_NULL

过滤器开发信息

过滤器从CBaseFilter派生,实现了IFileSourceFilter接口,其Load方法用于指定要读取的MP4文件,并解析MP4文件(在GetParam函数中)。定义了Sample结构,其中的变量存储样本大小,样本偏移量,关键帧标记,样本解码时间和合成时间之间的差值。根据解析结果,分别创建视频音频DWORD数组,数组大小就是样本的总数量,为每个样本创建一个Sample结构,根据样本大小,偏移量,关键帧标记,样本解码时间和合成时间之间的差值,赋值结构变量,将Sample结构指针按样本顺序存储在DWORD数组中,在本程序中称其为视频样本列表和音频样本列表。视频音频引脚样本媒体类型的判断,使用媒体基础的方法,创建源解析器,创建媒体源,获取演示文稿描述符,获取流描述符,获取流媒体类型处理接口,获取视频和音频流媒体类型,转换为AM_MEDIA_TYPE结构表示的媒体类型,设置给视频音频引脚。在视频引脚,音频引脚Active函数中分别创建视频音频工作线程,在工作线程中建立for循环,从样本列表中读取每个样本的偏移量,大小等参数,根据参数从MP4文件读取样本数据,从输出引脚获取空的样本,使用样本数据填充输出引脚样本,指定输出引脚样本的时间戳和同步点标记,向下游发送。

过滤器实现了IMediaSeeking接口,用于调整播放位置。在调整播放位置时,IMediaSeeking接口的SetPositions方法被调用,方法提供新的开始位置值,根据该值获取与其接近的关键帧序号;"定位"后,最好从关键帧开始,如果从非关键帧开始,解码器将会输出几副"错误"图像,这对于对视频要求较高的场合是不合适的。向视频音频工作线程发送"定位"信号,线程收到信号后,跳出for循环,重新进入新的for循环,新的for循环从新开始位置值开始。

过滤器DLL的全部代码

DLL.h

cpp 复制代码
#ifndef  DLL_FILE
#define DLL_FILE

#include "strmbase10.h"//过滤器基类定义文件

#if _DEBUG
#pragma comment(lib, "strmbasd10.lib")//过滤器基类实现文件调试版本
#else
#pragma comment(lib, "strmbase10.lib")//过滤器基类实现文件发布版本
#endif

// {4C42ABCE-6459-4997-863D-BD969D07A862}
DEFINE_GUID(CLSID_MP4Reader,//过滤器GUID
	0x4c42abce, 0x6459, 0x4997, 0x86, 0x3d, 0xbd, 0x96, 0x9d, 0x7, 0xa8, 0x62);

#include "wmcodecdsp.h"
#include "mfapi.h"
#include "mfidl.h"
#pragma comment(lib, "mfplat.lib")


struct Sample//样本参数结构
{
	UINT Size;//样本大小
	ULONGLONG Offset;//样本偏移量
	BOOL KeyFrame = FALSE;//为TRUE时,为关键帧
	LONGLONG ctts = 0; //样本解码时间和合成时间之间的差值
};

class CAudioPin;
class CFilter;

class CVideoPin : public CBaseOutputPin
{
	friend class CAudioPin;
	friend class CFilter;
public:
	CVideoPin(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName);
	~CVideoPin();
	HRESULT CheckMediaType(const CMediaType *pmt);
	BOOL HasSet = FALSE;
	HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
	HRESULT SetMediaType(const CMediaType *pmt);
	HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES * pProperties);
	HRESULT BreakConnect();
	HRESULT Active(void);
	HRESULT Inactive(void);
	CFilter *pCFilter = NULL;
	BYTE* pSHeader = NULL;
	UINT cbSHeader = 0;
	GUID VideoSubType;//视频子类型GUID
};

class CAudioPin : public CBaseOutputPin
{
	friend class CVideoPin;
	friend class CFilter;
public:
	CAudioPin(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName);
	~CAudioPin();
	HRESULT CheckMediaType(const CMediaType *pmt);
	BOOL HasSet = FALSE;
	HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
	HRESULT SetMediaType(const CMediaType *pmt);
	HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES * pProperties);
	HRESULT BreakConnect();
	HRESULT Active(void);
	CFilter *pCFilter = NULL;
};

class CFilter : public CCritSec, public CBaseFilter, public IFileSourceFilter, public IMediaSeeking
{
	friend class CVideoPin;
	friend class CAudioPin;
public:
	CFilter(TCHAR* pName, LPUNKNOWN pUnk, HRESULT* hr);
	~CFilter();
	CBasePin* GetPin(int n);
	int GetPinCount();
	static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
	DECLARE_IUNKNOWN
	STDMETHODIMP NonDelegatingQueryInterface(REFIID iid, void ** ppv);
	STDMETHODIMP Load(LPCOLESTR lpwszFileName, const AM_MEDIA_TYPE *pmt);
	STDMETHODIMP GetCurFile(LPOLESTR * ppszFileName, AM_MEDIA_TYPE *pmt);
	HANDLE hSeek;//"视频定位"事件句柄
	HANDLE hSeekA;//"音频定位"事件句柄
	UINT ListCount = 0;
	DWORD* pSampleList = NULL;//视频样本参数列表
	UINT SeekFrame;//视频Seek帧索引
	UINT SeekANu;//音频Seek起始样本索引
	UINT FindKeyFrame(UINT Frame);//查找关键帧
	UINT* pTimeList = NULL;
	UINT GetCtts(LONGLONG pos, UINT uS);
	IMFMediaType* pVideoType = NULL;//视频媒体类型
	IMFMediaType* pAudioType = NULL;//音频媒体类型
	HRESULT GetMediaType();//确定视频音频输出媒体类型
	void GetParam();//从MP4文件获取参数
	UINT TimeScale;//分子为1的视频时间刻度分母,单位秒。即视频使用 1/TimeScale 秒作为时间单位
	LONGLONG DUR;//100纳秒单位的视频时长
	LONGLONG CUR;//100纳秒单位的视频当前时间
	LONGLONG AvgTimePerFrame;//每帧持续时间,单位100纳秒
	WORD width;//视频宽,单位像素
	WORD height;//视频高,单位像素
	WORD bit_depth;//位深度
	void GetVideoParam();//获取视频参数
	BYTE ProFile;//配置文件
	BYTE AVCLevel;//编码级别
	WORD SpsNu;//sps大小
	BYTE* SPS = NULL;
	WORD PpsNu; //pps大小
	BYTE* PPS = NULL;
	void GetAvcCParam();//获取avcC参数
	LONGLONG StblStar;//stbl起始位置
	LONGLONG SttsStar;//stts起始位置
	LONGLONG StssStar;//stss起始位置
	LONGLONG CttsStar;//ctts起始位置
	LONGLONG StscStar;//stsc起始位置
	LONGLONG StszStar;//stsz起始位置
	LONGLONG StcoStar;//stco起始位置
	LONGLONG Co64Star;//co64起始位置
	UINT StscNu;//stsc条目数
	UINT nFrame;//视频帧数量
	HANDLE hFile = NULL;
	HANDLE hFileA = NULL;
	UINT TimeScaleA;//分子为1的音频时间刻度分母,单位秒。即音频使用 1/TimeScaleA 秒作为时间单位
	LONGLONG DUR_A;//100纳秒单位的音频时长
	LONGLONG CUR_A;//100纳秒单位的音频当前时间
	WORD nChannels;//声道数
	WORD  wBitsPerSample;//音频样本位数
	UINT nSamplesPerSec;//音频采样率
	UINT nAvgBytesPerSec;//音频传输率
	UINT nFrameA;//音频样本数量
	LONGLONG AvgTimeA;//单个样本的时长,单位100纳秒
	void GetAudioParam();//获取音频参数
	LONGLONG StblStarA;//音频stbl起始位置
	LONGLONG SttsStarA;//音频stts起始位置
	LONGLONG StscStarA;//音频stsc起始位置
	LONGLONG StszStarA;//音频stsz起始位置
	LONGLONG StcoStarA;//音频stco起始位置
	LONGLONG Co64StarA;//音频co64起始位置
	UINT ListCountA = 0;
	DWORD* pSampleListA = NULL;
	CVideoPin* pCVideoPin = NULL;//视频引脚指针
	CAudioPin* pCAudioPin = NULL;//音频引脚指针
	WCHAR* m_pFileName = NULL;//要读取的MP4视频文件路径
	HANDLE hRun;//"运行"事件句柄

	DWORD m_dwSeekingCaps = AM_SEEKING_CanSeekForwards | AM_SEEKING_CanSeekBackwards | AM_SEEKING_CanSeekAbsolute | AM_SEEKING_CanGetStopPos | AM_SEEKING_CanGetDuration;
	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);
};


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

BYTE ReadOne(HANDLE hFile);//读1个字节
WORD ReadTwo(HANDLE hFile);//读2个字节
UINT ReadThree(HANDLE hFile);//读3个字节
UINT ReadFour(HANDLE hFile);//读4个字节
UINT Read4Return(HANDLE hFile, LONGLONG& pos);//在指定位置读4个字节,并返回到文件原来位置。参数2加4字节
ULONGLONG Read8Return(HANDLE hFile, LONGLONG& pos);//在指定位置读8个字节,并返回到文件原来位置。参数2加8字节
BOOL ReadAtPos(HANDLE hFile, LONGLONG pos, UINT size, BYTE* pBuffer);//在参数2指定位置,读参数3指定字节
HRESULT MoveFilePointer(HANDLE hFile, LONGLONG size, int flag);//移动文件指针
UINT FindBox(HANDLE hFile, char* pBoxName);//读取BOX大小和名称
BOOL IsEnd(HANDLE hFile);//如果到文件末尾,返回TRUE
LONGLONG GetFilePos(HANDLE hFile);//获取文件当前位置
void GetHdlrType(HANDLE hFile, char* pch);//获取类型,vide或soun
LONGLONG FindTrak(HANDLE hFile, char* pCh);//移动文件指针到符合要求的trak开始位置,返回trak开始位置值。参数2,指定trak类型,视频或音频
LONGLONG FindMdia(HANDLE hFile);//文件指针移动到mdia开始位置,返回mdia开始位置值。查找的起始位置为trak的开始位置
LONGLONG FindMdhd(HANDLE hFile);//文件指针移动到mdhd开始位置,返回mdhd开始位置值。查找的起始位置为mdia的开始位置
LONGLONG FindHdlr(HANDLE hFile);//文件指针移动到hdlr开始位置,返回hdlr开始位置值。查找的起始位置为mdia的开始位置
LONGLONG FindMinf(HANDLE hFile);//文件指针移动到minf开始位置,返回minf开始位置值。查找的起始位置为mdia的开始位置
LONGLONG FindStbl(HANDLE hFile);//文件指针移动到stbl开始位置,返回stbl开始位置值。查找的起始位置为minf的开始位置
LONGLONG FindStblSub(HANDLE hFile, char* pch);//查找stbl的子box。文件指针移动到子box开始位置,返回子box开始位置值。查找的起始位置为stbl的开始位置

#endif //DLL_FILE

DLL.cpp

cpp 复制代码
#include "DLL.h"

const REGPINTYPES Pin1Type =
{
	&MEDIATYPE_Video,               //主要类型
	&MEDIASUBTYPE_NULL              //子类型
};

const REGPINTYPES Pin2Type =
{
	&MEDIATYPE_Audio,               //主要类型
	&MEDIASUBTYPE_NULL              //子类型
};

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

const AMOVIESETUP_FILTER MP4Reader =  //过滤器的注册信息
{
	&CLSID_MP4Reader,                 //过滤器的类标识
	L"读MP4_3",                       //过滤器的名称
	MERIT_DO_NOT_USE,                 //过滤器优先值
	2,                                //引脚数量
	sudPins                           //引脚信息
};

CFactoryTemplate g_Templates[] =      //类工厂模板数组
{
	{
		L"读MP4_3",                  //对象(这里为过滤器)名称
		&CLSID_MP4Reader,            //对象CLSID的指针
	    CFilter::CreateInstance,     //创建对象实例的函数的指针
	    NULL,                        //指向从DLL入口点调用的函数的指针
	    &MP4Reader                   //指向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

cpp 复制代码
#include "DLL.h"
#include "strsafe.h"

CFilter::CFilter(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr) : CBaseFilter(NAME("读MP4_3"), pUnk, this, CLSID_MP4Reader)
{
	HRESULT hr = MFStartup(MF_VERSION);//初始化媒体基础
	if (hr != S_OK)
	{
		MessageBox(NULL, L"初始化媒体基础失败", L"读MP4_3", MB_OK); return;
	}
	pCVideoPin = new CVideoPin(this, phr, L"Video");//创建视频引脚
	pCAudioPin = new CAudioPin(this, phr, L"Audio");//创建音频引脚
	hRun = CreateEvent(NULL, TRUE, FALSE, NULL);//创建"运行"事件,手动重置,初始无信号
	hSeek= CreateEvent(NULL, FALSE, FALSE, NULL);//创建"视频定位"事件,自动重置,初始无信号
	hSeekA = CreateEvent(NULL, FALSE, FALSE, NULL);//创建"音频定位"事件,自动重置,初始无信号
}

CFilter::~CFilter()
{
	SafeRelease(&pVideoType); SafeRelease(&pAudioType);//释放媒体类型
	MFShutdown();//关闭媒体基础
	if (m_pFileName)delete[] m_pFileName;
	if (SPS)delete[] SPS;
	if (PPS)delete[] PPS;
	CloseHandle(hFile); CloseHandle(hFileA); CloseHandle(hRun); CloseHandle(hSeek); CloseHandle(hSeekA);
	for (UINT i = 0; i < ListCount; i++)//删除视频样本列表中所有对象
	{
		Sample* pS = (Sample*)pSampleList[i]; delete pS;
	}
	if (pSampleList)delete[] pSampleList;//删除视频样本列表
	for (UINT i = 0; i < ListCountA; i++)//删除音频样本列表中所有对象
	{
		Sample* pS = (Sample*)pSampleListA[i]; delete pS;
	}
	if (pSampleListA)delete[] pSampleListA;//删除音频样本列表
}

CBasePin *CFilter::GetPin(int n)
{
	if (n == 0)return pCVideoPin;
	if (n == 1)return pCAudioPin;
	return NULL;
}

int CFilter::GetPinCount()
{
	return 2;
}

CUnknown * WINAPI CFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr)
{
	return new CFilter(NAME("读MP4_3"), pUnk, phr);
}

STDMETHODIMP CFilter::NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
	if (riid == IID_IFileSourceFilter)
	{
		return GetInterface(static_cast<IFileSourceFilter*>(this), ppv);
	}
	else if (riid == IID_IMediaSeeking)
	{
		return GetInterface(static_cast<IMediaSeeking*>(this), ppv);
	}
	else
		return CBaseFilter::NonDelegatingQueryInterface(riid, ppv);
}

STDMETHODIMP CFilter::Load(LPCOLESTR lpwszFileName, const AM_MEDIA_TYPE *pmt)
{
	CheckPointer(lpwszFileName, E_POINTER);
	int len = lstrlenW(lpwszFileName) + 1;
	m_pFileName = new WCHAR[len];
	if (m_pFileName != NULL)
		CopyMemory(m_pFileName, lpwszFileName, len*sizeof(WCHAR));
	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;//设置文件名失败
	}
	GetParam();//从MP4文件获取参数
	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;
}

UINT CFilter::GetCtts(LONGLONG pos, UINT uS)//从ctts条目,初始化时间列表
{
	MoveFilePointer(hFile, pos, FILE_BEGIN);//移动文件指针到ctts条目数位置
	UINT CttsNu = ReadFour(hFile);//获取ctts条目数
	pTimeList = new  UINT[uS];//创建时间列表,类型UINT数组
	int index = 0;
	for (UINT i = 0; i < CttsNu; i++)
	{
		UINT SampleNu = ReadFour(hFile); UINT Time = ReadFour(hFile);
		for (UINT j = 0; j < SampleNu; j++)
		{
			pTimeList[index] = Time;
			index++;
		}
	}
	return CttsNu;
}

HRESULT CFilter::GetMediaType()//确定视频音频输出媒体类型
{
	SafeRelease(&pVideoType); SafeRelease(&pAudioType);//释放媒体类型。如果多次指定MP4文件,必须释放上一次获取的媒体类型
	IMFSourceResolver* pSourceResolver = NULL;//源解析器接口
	HRESULT hr = MFCreateSourceResolver(&pSourceResolver);//创建源解析器
	MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
	IUnknown* pSource = NULL;//媒体源IUnknown接口
	if (SUCCEEDED(hr))
	{
		hr = pSourceResolver->CreateObjectFromURL(//从URL创建媒体源
			m_pFileName,                       //源的URL
			MF_RESOLUTION_MEDIASOURCE,  //创建源对象。
			NULL,                      //可选属性存储。
			&ObjectType,        //接收创建的对象类型。
			&pSource            //接收指向媒体源的指针
			);
	}
	SafeRelease(&pSourceResolver);//释放源解析器
	IMFMediaSource* pMFMediaSource = NULL;//媒体源接口
	if (SUCCEEDED(hr))
	{
		hr = pSource->QueryInterface(IID_PPV_ARGS(&pMFMediaSource));//获取媒体源接口
	}
	SafeRelease(&pSource);//释放媒体源IUnknown接口
	IMFPresentationDescriptor* pSourceD = NULL;//演示文稿描述符
	if (SUCCEEDED(hr))
	{
		hr = pMFMediaSource->CreatePresentationDescriptor(&pSourceD);//获取演示文稿描述符
	}
	SafeRelease(&pMFMediaSource);//释放媒体源
	IMFStreamDescriptor* pStreamD1 = NULL;//流1描述符
	if (SUCCEEDED(hr))
	{
		BOOL  Selected;
		hr = pSourceD->GetStreamDescriptorByIndex(0, &Selected, &pStreamD1);//获取流1描述符
	}
	IMFStreamDescriptor* pStreamD2 = NULL;//流2描述符
	if (SUCCEEDED(hr))
	{
		BOOL  Selected;
		hr = pSourceD->GetStreamDescriptorByIndex(1, &Selected, &pStreamD2);//获取流2描述符
	}
	SafeRelease(&pSourceD);//释放演示文稿描述符
	IMFMediaTypeHandler* pHandle1 = NULL;//流1媒体类型处理器
	if (SUCCEEDED(hr))
	{
		hr = pStreamD1->GetMediaTypeHandler(&pHandle1);//获取流1媒体类型处理器
	}
	SafeRelease(&pStreamD1);//释放流1描述符
	IMFMediaTypeHandler* pHandle2 = NULL;//流2媒体类型处理器
	if (SUCCEEDED(hr))
	{
		hr = pStreamD2->GetMediaTypeHandler(&pHandle2);//获取流2媒体类型处理器
	}
	SafeRelease(&pStreamD2);//释放流2描述符
	if (SUCCEEDED(hr))
	{
		GUID guid;
		hr = pHandle1->GetMajorType(&guid);//获取流1主要类型
		if (SUCCEEDED(hr))
		{
			if (guid == MEDIATYPE_Video)//如果是视频
			{
				hr = pHandle1->GetCurrentMediaType(&pVideoType);//获取视频流的媒体类型
			}
			else if (guid == MEDIATYPE_Audio)//如果是音频
			{
				hr = pHandle1->GetCurrentMediaType(&pAudioType);//获取音频流的媒体类型
			}
		}
	}
	SafeRelease(&pHandle1);//释放流1媒体类型处理器
	if (SUCCEEDED(hr))
	{
		GUID guid;
		hr = pHandle2->GetMajorType(&guid);//获取流2主要类型
		if (SUCCEEDED(hr))
		{
			if (guid == MEDIATYPE_Video)//如果是视频
			{
				hr = pHandle2->GetCurrentMediaType(&pVideoType);//获取视频流的媒体类型
			}
			else if (guid == MEDIATYPE_Audio)//如果是音频
			{
				hr = pHandle2->GetCurrentMediaType(&pAudioType);//获取音频流的媒体类型
			}
		}
	}
	SafeRelease(&pHandle2);//释放流2媒体类型处理器
	return hr;
}

void CFilter::GetParam()//从MP4文件获取参数
{
	CloseHandle(hFile);//如果多次指定MP4文件,需关闭上次文件
	hFile = CreateFile(m_pFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);//打开MP4文件
	if (hFile == INVALID_HANDLE_VALUE)
	{
		MessageBox(0, L"打开文件失败", L"读MP4_3", MB_OK);return;
	}
	GetMediaType();//确定视频音频输出媒体类型
	FindTrak(hFile, "vide");//查找视频trak
	LONGLONG MdiaStar = FindMdia(hFile);//查找视频mdia,记录mdia开始位置
	FindMdhd(hFile);//查找视频mdhd
	MoveFilePointer(hFile, 20, FILE_CURRENT);//移动文件指针20字节
	TimeScale = ReadFour(hFile);//获视频取时间刻度分母,分子为1
	MoveFilePointer(hFile, MdiaStar, FILE_BEGIN);//回到mdia开始位置
	FindMinf(hFile);//查找视频minf
	StblStar = FindStbl(hFile);//查找视频stbl,记录stbl开始位置

	FindStblSub(hFile, "stsd");//查找视频stsd
	GetVideoParam();//获取视频参数
	MoveFilePointer(hFile, StblStar, FILE_BEGIN);//回到视频stbl开始位置
	SttsStar = FindStblSub(hFile, "stts");//stts起始位置
	MoveFilePointer(hFile, SttsStar + 12, FILE_BEGIN);//移动到stts条目数位置
	UINT SttsEnter = ReadFour(hFile);//获取条目数量
	UINT uL;//单个样本的时长,单位100纳秒
	nFrame = 0;//视频样本总数量
	for (UINT i = 0; i < SttsEnter; i++)//读取每个条目
	{
		UINT uS = ReadFour(hFile); uL = ReadFour(hFile); nFrame += uS;
	}
	AvgTimePerFrame = (LONGLONG)((float)uL / (float)TimeScale * (float)10000000);//计算单个样本的时长,单位100纳秒
	DUR = AvgTimePerFrame*nFrame;//100纳秒单位的视频时长
	MoveFilePointer(hFile, StblStar, FILE_BEGIN);//回到stbl开始位置
	StssStar = FindStblSub(hFile, "stss");//stss起始位置
	MoveFilePointer(hFile, StblStar, FILE_BEGIN);//回到stbl开始位置
	CttsStar = 0;
	CttsStar = FindStblSub(hFile, "ctts");//ctts起始位置
	UINT CttsNu;
	if (CttsStar)CttsNu = GetCtts(CttsStar + 12, nFrame);//如果有ctts box,获取ctts
	MoveFilePointer(hFile, StblStar, FILE_BEGIN);//回到stbl开始位置
	StscStar = FindStblSub(hFile, "stsc");//stsc起始位置
	MoveFilePointer(hFile, StblStar, FILE_BEGIN);//回到stbl开始位置
	StszStar = FindStblSub(hFile, "stsz");//stsz起始位置
	MoveFilePointer(hFile, StblStar, FILE_BEGIN);//回到stbl开始位置
	StcoStar = FindStblSub(hFile, "stco");//stco起始位置。如果stco box存在,返回非0值;如果stco box不存在,返回0
	MoveFilePointer(hFile, StblStar, FILE_BEGIN);//回到stbl开始位置
	Co64Star= FindStblSub(hFile, "co64");//co64起始位置。如果co64 box存在,返回非0值;如果co64 box不存在,返回0

	for (UINT i = 0; i < ListCount; i++)//删除列表中所有对象。为过滤器指定读取文件可能多次,须删除原来的列表中的元素
	{
		Sample* pS = (Sample*)pSampleList[i]; delete pS;
	}
	if(pSampleList)delete[] pSampleList;//删除视频样本列表

	pSampleList = new DWORD[nFrame]; ListCount = nFrame;//创建视频样本列表
	
	{
		UINT index = 0;//样本索引
		LONGLONG stss_enter_star = StssStar + 16;//stss条目位置
		LONGLONG stco_enter_star = StcoStar + 16;//stco条目位置
		LONGLONG co64_enter_star = Co64Star + 16;//co64条目位置
		LONGLONG stsz_enter_star = StszStar + 20;//stsz条目位置

		UINT KeyFrame = Read4Return(hFile, stss_enter_star);//从stss条目中读取第一个关键帧序号,并将文件指针返回到原来的位置
		MoveFilePointer(hFile, StscStar + 12, FILE_BEGIN);//移动文件指针到,stsc条目数位置
		UINT StscNu = ReadFour(hFile);//获取stsc条目数
		UINT First = ReadFour(hFile);//起始块序号
		UINT NextFirst;//下一个条目起始块序号
		UINT Nu;
		for (UINT i = 0; i < StscNu; i++)//读取stsc每个条目
		{
			UINT nSample = ReadFour(hFile); //块中样本数量
			UINT id = ReadFour(hFile); //stsd序号
			if (i != StscNu - 1)//如果不是stsc最后一个条目
			{
				NextFirst = ReadFour(hFile);//读取下一个条目的起始块序号
				Nu = NextFirst - First;
				First = NextFirst;
			}
			else//如果是stsc最后一个条目
			{
				Nu = 1;
			}
			if (StscNu == 1)nSample = ListCount;//如果stsc只有1个条目,样本数量为全部样本数量
			for (UINT j = 0; j < Nu; j++)//读取stco每个条目
			{
				ULONGLONG Offset;
				if (StcoStar)//有stco
				{
					Offset = Read4Return(hFile, stco_enter_star);//从stco条目中读取块偏移量,并将文件指针返回到原来的位置
				}
				else//有co64
				{
					Offset = Read8Return(hFile, co64_enter_star);//从co64条目中读取块偏移量,并将文件指针返回到原来的位置
				}
				ULONGLONG NextOffset;//下一个偏移量
				for (UINT k = 0; k < nSample; k++)//读取stsz每个条目
				{
					UINT size = Read4Return(hFile, stsz_enter_star);//从stsz条目中读取样本大小,并将文件指针返回到原来的位置
					ULONGLONG offset;
					if (k == 0)offset = Offset;
					else offset = NextOffset;
					NextOffset = offset + size;
					BOOL KEY = FALSE;
					if (KeyFrame == index+1)//如果是关键帧
					{
						KEY = TRUE;
						KeyFrame = Read4Return(hFile, stss_enter_star);//获取下一个关键帧序号
					}
					Sample* pSample = new Sample();//创建Sample结构对象
					pSample->Size = size;//样本大小
					pSample->Offset = offset;//样本在文件中的偏移量
					if (KEY)pSample->KeyFrame = TRUE;//值TRUE,标记关键帧
					if (CttsStar)pSample->ctts = (LONGLONG)((float)pTimeList[index]*(float)10000000/(float)TimeScale);//如果存在ctts box,赋值解码时间和合成时间之间的差值
					pSampleList[index] = (DWORD)pSample;//将Sample结构对象地址,存储在视频样本列表中
					index++;
				}
			}
		}
		if (CttsStar)delete[] pTimeList;//如果存在ctts box,删除时间列表
	}

	CloseHandle(hFileA);
	hFileA = CreateFile(m_pFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);//打开MP4文件
	if (hFile == INVALID_HANDLE_VALUE)
	{
		MessageBox(0, L"打开文件失败", L"读MP4_3", MB_OK); return;
	}
	FindTrak(hFileA, "soun");//查找音频trak
	LONGLONG MdiaStarA = FindMdia(hFileA);//查找音频mdia
	FindMdhd(hFileA);//查找音频mdhd
	MoveFilePointer(hFileA, 20, FILE_CURRENT);//移动文件指针20字节
	TimeScaleA = ReadFour(hFileA);//获取音频时间刻度
	MoveFilePointer(hFileA, MdiaStarA, FILE_BEGIN);//回到mdia开始位置
	FindMinf(hFileA);//查找音频minf
	StblStarA = FindStbl(hFileA);//查找音频stbl,记录stbl开始位置

	FindStblSub(hFileA, "stsd");//查找音频stsd
	GetAudioParam();//获取音频参数
	MoveFilePointer(hFileA, StblStarA, FILE_BEGIN);//回到音频stbl开始位置
	SttsStarA = FindStblSub(hFileA, "stts");//stts起始位置
	MoveFilePointer(hFile, SttsStarA + 12, FILE_BEGIN);//移动到stts条目数位置
	UINT SttsEnterA = ReadFour(hFile);//获取条目数量
	UINT uLA;//单个样本的时长,单位100纳秒
	nFrameA = 0;//音频样本数量
	for (UINT i = 0; i < SttsEnterA; i++)//读取每个条目
	{
		UINT uS = ReadFour(hFile); uLA = ReadFour(hFile); nFrameA += uS;
	}
	AvgTimeA = (LONGLONG)((float)uLA  * (float)10000000 / (float)TimeScaleA);//获取单个样本的时长,单位100纳秒
	DUR_A = AvgTimeA*nFrameA;//100纳秒单位的音频时长
	MoveFilePointer(hFileA, StblStarA, FILE_BEGIN);//回到stbl开始位置
	StscStarA = FindStblSub(hFileA, "stsc");//stsc起始位置
	MoveFilePointer(hFileA, StblStarA, FILE_BEGIN);//回到stbl开始位置
	StszStarA = FindStblSub(hFileA, "stsz");//stsz起始位置
	MoveFilePointer(hFileA, StblStarA, FILE_BEGIN);//回到stbl开始位置
	StcoStarA = FindStblSub(hFileA, "stco");//stco起始位置
	MoveFilePointer(hFileA, StblStarA, FILE_BEGIN);//回到stbl开始位置
	Co64StarA = FindStblSub(hFileA, "co64");//co64起始位置
	
	for (UINT i = 0; i < ListCountA; i++)//删除音频样本列表中所有对象。为过滤器指定读取文件可能多次,须删除原来的列表中的元素
	{
		Sample* pS = (Sample*)pSampleListA[i]; delete pS;
	}
	if (pSampleListA)delete[] pSampleListA;//删除音频样本列表

	pSampleListA = new DWORD[nFrameA]; ListCountA = nFrameA;

	{
		ULONGLONG AudioDataSize = 0;//记录音频数据大小
		UINT indexA = 0;//样本索引
		LONGLONG stco_enter_starA = StcoStarA + 16;//stco条目位置
		LONGLONG co64_enter_starA = Co64StarA + 16;//co64条目位置
		LONGLONG stsz_enter_starA = StszStarA + 20;//stsz条目位置
		MoveFilePointer(hFileA, StscStarA + 12, FILE_BEGIN);//移动文件指针到,stsc条目数位置
		UINT StscNu = ReadFour(hFileA);//获取stsc条目数
		UINT First = ReadFour(hFileA);//起始块序号
		UINT NextFirst;//下一个条目起始块序号
		UINT Nu;
		for (UINT i = 0; i < StscNu; i++)//读取stsc每个条目
		{
			UINT nSample = ReadFour(hFileA); //块中样本数量
			UINT id = ReadFour(hFileA); //stsd序号
			if (i != StscNu - 1)//如果不是stsc最后一个条目
			{
				NextFirst = ReadFour(hFileA);//读取下一个条目的起始块序号
				Nu = NextFirst - First;
				First = NextFirst;
			}
			else//如果是stsc最后一个条目
			{
				Nu = 1;
			}
			if (StscNu == 1)nSample = ListCountA;//如果stsc只有1个条目,样本数量为全部样本数量
			for (UINT j = 0; j < Nu; j++)//读取stco每个条目
			{
				ULONGLONG Offset;
				if (StcoStarA)//有stco
				{
					Offset = Read4Return(hFileA, stco_enter_starA);//从stco条目中读取块偏移量,并将文件指针返回到原来的位置
				}
				else//有co64
				{
					Offset = Read8Return(hFileA, co64_enter_starA);//从co64条目中读取块偏移量,并将文件指针返回到原来的位置
				}
				ULONGLONG NextOffset;//下一个偏移量
				for (UINT k = 0; k < nSample; k++)//读取stsz每个条目
				{
					UINT size = Read4Return(hFileA, stsz_enter_starA);//从stsz条目中读取样本大小,并将文件指针返回到原来的位置
					ULONGLONG offset;
					if (k == 0)offset = Offset;
					else offset = NextOffset;
					NextOffset = offset + size;
					Sample* pSample = new Sample();
					pSample->Size = size;
					pSample->Offset = offset;
					pSampleListA[indexA] = (DWORD)pSample;
					AudioDataSize += size;
					indexA++;
				}
			}
		}
		nAvgBytesPerSec = (UINT)(AudioDataSize / (DUR_A / 10000000));//计算音频平均传输率
	}
}

ULONGLONG FindExBox(HANDLE hFile, char* pBoxName)//读取扩展box大小和名称
{
	UINT box_size = ReadFour(hFile);
	ReadFile(hFile, pBoxName, 4, NULL, NULL); pBoxName[4] = 0;
	ULONGLONG Hi = ReadFour(hFile); UINT Lo = ReadFour(hFile);
	ULONGLONG Exbox_size = (Hi << 32) + Lo;
	MoveFilePointer(hFile, -16, FILE_CURRENT);//移动文件指针
	return Exbox_size;
}

LONGLONG FindTrak(HANDLE hFile, char* pCh)//移动文件指针到符合参数2要求的trak开始位置,返回trak开始位置值。参数2,指定trak类型,视频或音频
{
	SetFilePointer(hFile, 0, NULL, FILE_BEGIN);//移动文件指针到文件开头
	char ch[5];
Agan:
	ULONGLONG box_size = FindBox(hFile, ch);
	if (box_size == 1)box_size = FindExBox(hFile, ch);
	if (strcmp(ch, "moov") == 0)
	{
		ULONGLONG moov_size = box_size - 8;
		MoveFilePointer(hFile, 8, FILE_CURRENT);//移动文件指针8字节
	moov_Agan:
		box_size = FindBox(hFile, ch);
		if (strcmp(ch, "trak") == 0)
		{
			LONGLONG TrakPos = GetFilePos(hFile);
			FindMdia(hFile); FindHdlr(hFile);
			GetHdlrType(hFile, ch);
			MoveFilePointer(hFile, TrakPos, FILE_BEGIN);//移动文件指针到trak开始位置
			if (strcmp(ch, pCh) == 0)return TrakPos;
			else
			{
				MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
				moov_size -= box_size;
				if (moov_size>0)goto moov_Agan;
			}
		}
		else
		{
			MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
			moov_size -= box_size;
			if (moov_size>0)goto moov_Agan;
		}
	}
	else
	{
		MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
		if (IsEnd(hFile))return 0;//如果到文件末尾,跳出循环
		goto Agan;
	}
	MessageBox(0, L"查找trak失败", L"读MP4_3", 0);
	return 0;
}

LONGLONG FindMdia(HANDLE hFile)//文件指针移动到mdia开始位置,返回mdia开始位置值。查找的起始位置为trak的开始位置
{
	char ch[5];
	UINT box_size = FindBox(hFile, ch);
	UINT trak_size = box_size - 8;
	MoveFilePointer(hFile, 8, FILE_CURRENT);//移动文件指针8字节
trak_Agan:
	box_size = FindBox(hFile, ch);
	if (strcmp(ch, "mdia") == 0)
	{
		return GetFilePos(hFile);
	}
	else
	{
		MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
		trak_size -= box_size;
		if (trak_size > 0)goto trak_Agan;
	}
	MessageBox(0, L"查找mdia失败", L"读MP4_3", 0);
	return 0;
}

LONGLONG FindMdhd(HANDLE hFile)//文件指针移动到mdhd开始位置,返回mdhd开始位置值。查找的起始位置为mdia的开始位置
{
	char ch[5];
	UINT box_size = FindBox(hFile, ch);
	UINT mdia_size = box_size - 8;
	MoveFilePointer(hFile, 8, FILE_CURRENT);//移动文件指针8字节
mdia_Agan:
	box_size = FindBox(hFile, ch);
	if (strcmp(ch, "mdhd") == 0)
	{
		return GetFilePos(hFile);
	}
	else
	{
		MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
		mdia_size -= box_size;
		if (mdia_size > 0)goto mdia_Agan;
	}
	MessageBox(0, L"查找mdhd失败", L"读MP4_3", 0);
	return 0;
}

LONGLONG FindHdlr(HANDLE hFile)//文件指针移动到hdlr开始位置,返回hdlr开始位置值。查找的起始位置为mdia的开始位置
{
	char ch[5];
	UINT box_size = FindBox(hFile, ch);
	UINT mdia_size = box_size - 8;
	MoveFilePointer(hFile, 8, FILE_CURRENT);//移动文件指针8字节
mdia_Agan:
	box_size = FindBox(hFile, ch);
	if (strcmp(ch, "hdlr") == 0)
	{
		return GetFilePos(hFile);
	}
	else
	{
		MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
		mdia_size -= box_size;
		if (mdia_size > 0)goto mdia_Agan;
	}
	MessageBox(0, L"查找hdlr失败", L"读MP4_3", 0);
	return 0;
}

LONGLONG FindMinf(HANDLE hFile)//文件指针移动到minf开始位置,返回minf开始位置值。查找的起始位置为mdia的开始位置
{
	char ch[5];
	UINT box_size = FindBox(hFile, ch);
	UINT mdia_size = box_size - 8;
	MoveFilePointer(hFile, 8, FILE_CURRENT);//移动文件指针8字节
mdia_Agan:
	box_size = FindBox(hFile, ch);
	if (strcmp(ch, "minf") == 0)
	{
		return GetFilePos(hFile);
	}
	else
	{
		MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
		mdia_size -= box_size;
		if (mdia_size > 0)goto mdia_Agan;
	}
	MessageBox(0, L"查找minf失败", L"读MP4_3", 0);
	return 0;
}

LONGLONG FindStbl(HANDLE hFile)//文件指针移动到stbl开始位置,返回stbl开始位置值。查找的起始位置为minf的开始位置
{
	char ch[5];
	UINT box_size = FindBox(hFile, ch);
	UINT minf_size = box_size - 8;
	MoveFilePointer(hFile, 8, FILE_CURRENT);//移动文件指针8字节
minf_Agan:
	box_size = FindBox(hFile, ch);
	if (strcmp(ch, "stbl") == 0)
	{
		return GetFilePos(hFile);
	}
	else
	{
		MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
		minf_size -= box_size;
		if (minf_size > 0)goto minf_Agan;
	}
	MessageBox(0, L"查找stbl失败", L"读MP4_3", 0);
	return 0;
}

LONGLONG FindStblSub(HANDLE hFile, char* pch)//查找stbl的子box。文件指针移动到子box开始位置,返回子box开始位置值。查找的起始位置为stbl的开始位置
{
	LONGLONG Star = GetFilePos(hFile);//记录stbl开始位置
	char ch[5];
	UINT box_size = FindBox(hFile, ch);
	UINT stbl_size = box_size - 8;
	MoveFilePointer(hFile, 8, FILE_CURRENT);//移动文件指针8字节
stbl_Agan:
	box_size = FindBox(hFile, ch);
	if (strcmp(ch, pch) == 0)
	{
		return GetFilePos(hFile);
	}
	else
	{
		MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
		stbl_size -= box_size;
		if (stbl_size > 0)goto stbl_Agan;
	}
	MoveFilePointer(hFile, Star, FILE_BEGIN);//查找失败,回到stbl开始位置
	return 0;
}

void CFilter::GetVideoParam()//获取视频参数
{
	MoveFilePointer(hFile, 16, FILE_CURRENT);//移动文件指针16字节
	char ch[5];
	UINT box_size = FindBox(hFile, ch);
	if (strcmp(ch, "avc1") == 0)
	{
		MoveFilePointer(hFile, 32, FILE_CURRENT);//移动文件指针
		width = ReadTwo(hFile);//读取视频宽
		height = ReadTwo(hFile);//读取视频高
		MoveFilePointer(hFile, 46, FILE_CURRENT);//移动文件指针
		bit_depth = ReadTwo(hFile);//位深度
		MoveFilePointer(hFile, 2, FILE_CURRENT);//移动文件指针
		GetAvcCParam();//获取avcC参数
	}
	else if (strcmp(ch, "mp4v") == 0)
	{
		MoveFilePointer(hFile, 32, FILE_CURRENT);//移动文件指针
		width = ReadTwo(hFile);//读取视频宽
		height = ReadTwo(hFile);//读取视频高
		MoveFilePointer(hFile, 46, FILE_CURRENT);//移动文件指针
		bit_depth = ReadTwo(hFile);//位深度
		MoveFilePointer(hFile, 2, FILE_CURRENT);//移动文件指针
		//GetEsds(box_size-84);
		//GetDecSpecificInfotag();
	}
	else
		MessageBox(0, L"无法解析", L"写MP4_3", MB_OK);
}

void CFilter::GetAudioParam()//获取音频参数
{
	MoveFilePointer(hFileA, 16, FILE_CURRENT);//移动文件指针16字节
	char ch[5];
	UINT box_size = FindBox(hFileA, ch);
	if (strcmp(ch, "mp4a") == 0)
	{
		MoveFilePointer(hFileA, 24, FILE_CURRENT);//移动文件指针
		nChannels = ReadTwo(hFileA);//读取声道数
		wBitsPerSample = ReadTwo(hFileA);//读取音频样本位数
		MoveFilePointer(hFileA, 4, FILE_CURRENT);//移动文件指针
		nSamplesPerSec = ReadTwo(hFileA);//采样率
	}
}

void CFilter::GetAvcCParam()//获取avcC参数
{
	MoveFilePointer(hFile, 9, FILE_CURRENT);//移动文件指针
	ProFile = ReadOne(hFile);//配置文件
	MoveFilePointer(hFile, 1, FILE_CURRENT);//移动文件指针
	AVCLevel = ReadOne(hFile);//编码级别
	MoveFilePointer(hFile, 1, FILE_CURRENT);//移动文件指针
	BYTE mby = ReadOne(hFile);
	BYTE N1 = 0x1F & mby;//条目数
	SpsNu = ReadTwo(hFile);//sps大小
	if (SPS)delete[] SPS;
	SPS = new BYTE[SpsNu];
	ReadFile(hFile, SPS, SpsNu, NULL, NULL);//读取sps
	BYTE N2 = ReadOne(hFile); //条目数
	PpsNu = ReadTwo(hFile);//pps大小
	if (PPS)delete[] PPS;
	PPS = new BYTE[PpsNu];
	ReadFile(hFile, PPS, PpsNu, NULL, NULL);//读取pps
}

void GetHdlrType(HANDLE hFile, char* pch)
{
	MoveFilePointer(hFile, 16, FILE_CURRENT);//移动文件指针16字节
	ReadFile(hFile, pch, 4, NULL, NULL); pch[4] = 0;
}

BYTE ReadOne(HANDLE hFile)
{
	BYTE mby1;
	ReadFile(hFile, &mby1, 1, NULL, NULL);
	return mby1;
}

WORD ReadTwo(HANDLE hFile)
{
	BYTE mby1, mby2;
	ReadFile(hFile, &mby1, 1, NULL, NULL); ReadFile(hFile, &mby2, 1, NULL, NULL);
	WORD size = mby1 * 256 + mby2;
	return size;
}

UINT ReadThree(HANDLE hFile)
{
	BYTE mby1, mby2, mby3;
	ReadFile(hFile, &mby1, 1, NULL, NULL); ReadFile(hFile, &mby2, 1, NULL, NULL); ReadFile(hFile, &mby3, 1, NULL, NULL);
	UINT size = mby1 * 256 * 256 + mby2 * 256 + mby3;
	return size;
}

UINT ReadFour(HANDLE hFile)
{
	BYTE mby1, mby2, mby3, mby4;
	ReadFile(hFile, &mby1, 1, NULL, NULL); ReadFile(hFile, &mby2, 1, NULL, NULL); ReadFile(hFile, &mby3, 1, NULL, NULL); ReadFile(hFile, &mby4, 1, NULL, NULL);
	UINT size = mby1 * 256 * 256 * 256 + mby2 * 256 * 256 + mby3 * 256 + mby4;
	return size;
}

UINT Read4Return(HANDLE hFile, LONGLONG& pos)//在指定位置读4个字节,并返回到文件原来位置。参数2加4字节
{
	LONGLONG Cur = GetFilePos(hFile);
	MoveFilePointer(hFile, pos, FILE_BEGIN);
	UINT size = ReadFour(hFile); pos += 4;
	MoveFilePointer(hFile, Cur, FILE_BEGIN);
	return size;
}

ULONGLONG Read8Return(HANDLE hFile, LONGLONG& pos)//在指定位置读8个字节,并返回到文件原来位置。参数2加8字节
{
	LONGLONG Cur = GetFilePos(hFile);
	MoveFilePointer(hFile, pos, FILE_BEGIN);
	ULONGLONG size1 = ReadFour(hFile); UINT size2 = ReadFour(hFile);
	ULONGLONG UL = (size1 << 32) + size2;
	pos += 8;
	MoveFilePointer(hFile, Cur, FILE_BEGIN);
	return UL;
}

BOOL ReadAtPos(HANDLE hFile, LONGLONG pos, UINT size, BYTE* pBuffer)//在参数2指定位置,读参数3指定字节
{
	MoveFilePointer(hFile, pos, FILE_BEGIN);
	return ReadFile(hFile, pBuffer, size, NULL, NULL);
}

HRESULT MoveFilePointer(HANDLE hFile, LONGLONG size, int flag)//移动文件指针
{
	LARGE_INTEGER move;
	move.QuadPart = size;
	SetFilePointerEx(hFile, move, NULL, flag);
	return S_OK;
}

UINT FindBox(HANDLE hFile, char* pBoxName)//读取BOX大小和名称
{
	UINT box_size = ReadFour(hFile);
	ReadFile(hFile, pBoxName, 4, NULL, NULL); pBoxName[4] = 0;
	MoveFilePointer(hFile, -8, FILE_CURRENT);//移动文件指针
	return box_size;
}

BOOL IsEnd(HANDLE hFile)//如果到文件末尾,返回TRUE
{
	LARGE_INTEGER SIZE;
	GetFileSizeEx(hFile, &SIZE);
	LONGLONG Pos = GetFilePos(hFile);
	if (Pos == SIZE.QuadPart)return TRUE;
	else return FALSE;
}

LONGLONG GetFilePos(HANDLE hFile)//获取文件当前位置
{
	LARGE_INTEGER move;
	move.QuadPart = 0;
	LARGE_INTEGER CUR;
	SetFilePointerEx(hFile, move, &CUR, FILE_CURRENT);
	return CUR.QuadPart;
}

HRESULT STDMETHODCALLTYPE CFilter::CheckCapabilities(DWORD *pCapabilities)//查询流是否具有指定的Seek功能
{
	CheckPointer(pCapabilities, E_POINTER);
	return (~m_dwSeekingCaps & *pCapabilities) ? S_FALSE : S_OK;
}

HRESULT STDMETHODCALLTYPE CFilter::ConvertTimeFormat(LONGLONG *pTarget, const GUID *pTargetFormat, LONGLONG Source, const GUID *pSourceFormat)//从一种时间格式转换为另一种时间格式
{
	CheckPointer(pTarget, E_POINTER);
	if (pTargetFormat == 0 || *pTargetFormat == TIME_FORMAT_MEDIA_TIME)
	{
		if (pSourceFormat == 0 || *pSourceFormat == TIME_FORMAT_MEDIA_TIME)
		{
			*pTarget = Source;
			return S_OK;
		}
	}
	return E_INVALIDARG;
}

HRESULT STDMETHODCALLTYPE CFilter::GetAvailable(LONGLONG *pEarliest, LONGLONG *pLatest)//获取有效Seek的时间范围
{
	if (pEarliest)
	{
		*pEarliest = 0;
	}
	if (pLatest)
	{
		CAutoLock lock(m_pLock);
		*pLatest = DUR;
	}
	return S_OK;
}

HRESULT STDMETHODCALLTYPE CFilter::GetCapabilities(DWORD *pCapabilities)//检索流的所有Seek功能
{
	CheckPointer(pCapabilities, E_POINTER);
	*pCapabilities = m_dwSeekingCaps;
	return S_OK;
}

HRESULT STDMETHODCALLTYPE CFilter::GetCurrentPosition(LONGLONG *pCurrent)//获取相对于流总持续时间的当前位置
{
	*pCurrent = CUR;
	return S_OK;
}

HRESULT STDMETHODCALLTYPE CFilter::GetDuration(LONGLONG *pDuration)//获取流的持续时间
{
	CheckPointer(pDuration, E_POINTER);
	CAutoLock lock(m_pLock);
	*pDuration = DUR;
	return S_OK;
}

HRESULT STDMETHODCALLTYPE CFilter::GetPositions(LONGLONG *pCurrent, LONGLONG *pStop)//获取相对于流总持续时间的当前位置和停止位置
{
	CheckPointer(pCurrent, E_POINTER); CheckPointer(pStop, E_POINTER);
	*pCurrent = CUR; *pStop = DUR;
	return S_OK;
}

HRESULT STDMETHODCALLTYPE CFilter::GetPreroll(LONGLONG *pllPreroll)//获取将在开始位置之前排队的数据量
{
	CheckPointer(pllPreroll, E_POINTER);
	*pllPreroll = 0;
	return S_OK;
}

HRESULT STDMETHODCALLTYPE CFilter::GetRate(double *pdRate)//获取播放速率
{
	CheckPointer(pdRate, E_POINTER);
	CAutoLock lock(m_pLock);
	*pdRate = 1.0;
	return S_OK;
}

HRESULT STDMETHODCALLTYPE CFilter::GetStopPosition(LONGLONG *pStop)//获取相对于流的持续时间的停止播放时间
{
	CheckPointer(pStop, E_POINTER);
	CAutoLock lock(m_pLock);
	*pStop = DUR;
	return S_OK;
}

HRESULT STDMETHODCALLTYPE CFilter::GetTimeFormat(GUID *pFormat)//获取当前用于Seek操作的时间格式
{
	CheckPointer(pFormat, E_POINTER);
	*pFormat = TIME_FORMAT_MEDIA_TIME;
	return S_OK;
}

HRESULT STDMETHODCALLTYPE CFilter::IsFormatSupported(const GUID *pFormat)//确定Seek操作是否支持指定的时间格式
{
	CheckPointer(pFormat, E_POINTER);
	return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;
}

HRESULT STDMETHODCALLTYPE CFilter::IsUsingTimeFormat(const GUID *pFormat)//确定Seek操作当前是否使用指定的时间格式
{
	CheckPointer(pFormat, E_POINTER);
	return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;
}

HRESULT STDMETHODCALLTYPE CFilter::QueryPreferredFormat(GUID *pFormat)//获取首选的Seek时间格式
{
	CheckPointer(pFormat, E_POINTER);
	*pFormat = TIME_FORMAT_MEDIA_TIME;
	return S_OK;
}

HRESULT STDMETHODCALLTYPE CFilter::SetPositions(LONGLONG *pCurrent, DWORD dwCurrentFlags, LONGLONG *pStop, DWORD dwStopFlags)//设置当前位置和停止位置
{
	CheckPointer(pCurrent, E_POINTER);
	DWORD dwCurrentPos = dwCurrentFlags & AM_SEEKING_PositioningBitsMask;
	if (dwCurrentPos == AM_SEEKING_AbsolutePositioning && *pCurrent >= 0 && *pCurrent <= DUR)
	{
		LONGLONG cur = *pCurrent;
		UINT Frame = (UINT)(cur / AvgTimePerFrame);
		if (Frame > nFrame - 1)Frame = nFrame - 1;
		SeekFrame = FindKeyFrame(Frame);//查找关键帧。定位后,第1帧必须是关键帧
		SetEvent(hSeek);//设置"视频定位"有信号
		SeekANu = (UINT)(AvgTimePerFrame * SeekFrame / AvgTimeA);//查找关键帧对应的音频样本索引
		SetEvent(hSeekA);//设置"音频定位"有信号
		return S_OK;
	}
	return E_INVALIDARG;
}

HRESULT STDMETHODCALLTYPE CFilter::SetRate(double dRate)//设置播放速率
{
	if (dRate == 1.0)return S_OK;
	else return S_FALSE;
}

HRESULT STDMETHODCALLTYPE CFilter::SetTimeFormat(const GUID *pFormat)//设置后续Seek操作的时间格式
{
	CheckPointer(pFormat, E_POINTER);
	return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : E_INVALIDARG;
}

UINT CFilter::FindKeyFrame(UINT Frame)//查找关键帧
{
	UINT LastKeyFrame;
	for (UINT i = ListCount - 1; i >= 0; i--) //获取最后一个关键帧
	{
		Sample* pSample = (Sample*)pSampleList[i];
		if (pSample->KeyFrame)
		{
			LastKeyFrame = i; break;
		}
	}
	Sample* pS = (Sample*)pSampleList[Frame];
	while (pS->KeyFrame == FALSE)
	{
		Frame++;
		if (Frame > LastKeyFrame)return LastKeyFrame;
		pS = (Sample*)pSampleList[Frame];
	}
	return Frame;
}

CVideoPin.cpp

cpp 复制代码
#include "DLL.h"
#include "dvdmedia.h"

CVideoPin::CVideoPin(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName) : CBaseOutputPin(NAME("Out"), pFilter, pFilter, phr, pPinName)
{
	pCFilter = pFilter;
}

CVideoPin::~CVideoPin()
{
	if (pSHeader)delete[] pSHeader;
}

HRESULT CVideoPin::CheckMediaType(const CMediaType *pmt)
{
	AM_MEDIA_TYPE* pMt = NULL;
	pCFilter->pVideoType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, (void**)&pMt);//将IMFMediaType表示的媒体类型,转换为AM_MEDIA_TYPE结构形式
	CMediaType MT(*pMt);
	if (pmt->MatchesPartial(&MT))
	{
		pCFilter->pVideoType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pMt);//释放GetRepresentation分配的内存
		return S_OK;
	}
	pCFilter->pVideoType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pMt);//释放GetRepresentation分配的内存
	return S_FALSE;
}

HRESULT CVideoPin::GetMediaType(int iPosition, CMediaType *pmt)
{
	if (pCFilter->m_pFileName == NULL)return S_FALSE;
	if (iPosition == 0)
	{
		HRESULT hr = pCFilter->pVideoType->GetGUID(MF_MT_SUBTYPE, &VideoSubType);//获取媒体基础媒体类型子类型
		if (VideoSubType == MEDIASUBTYPE_H264)
		{
			hr = pCFilter->pVideoType->GetBlobSize(MF_MT_MPEG_SEQUENCE_HEADER, &(UINT32)cbSHeader);//获取序列头大小
			if (pSHeader)delete[] pSHeader;//如果引脚多次连接,需要删除上一次的序列头
			pSHeader = new BYTE[cbSHeader];
			hr = pCFilter->pVideoType->GetBlob(MF_MT_MPEG_SEQUENCE_HEADER, pSHeader, cbSHeader, NULL);//获取序列头
		}
		AM_MEDIA_TYPE* pMt = NULL;
		pCFilter->pVideoType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, (void**)&pMt);//将IMFMediaType表示的媒体类型,转换为AM_MEDIA_TYPE结构形式
		pmt->Set(*pMt);
		pCFilter->pVideoType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pMt);//释放GetRepresentation分配的内存
		return S_OK;
	}
	return S_FALSE;
}

HRESULT CVideoPin::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES * pProperties)//确定输出引脚样本缓冲区大小
{
	HRESULT hr = NOERROR;
	pProperties->cBuffers = 1;//1个缓冲区
	pProperties->cbBuffer = 10000000;//缓冲区的大小,10M
	ALLOCATOR_PROPERTIES Actual;
	hr = pAlloc->SetProperties(pProperties, &Actual);
	if (FAILED(hr))return hr;
	if (Actual.cbBuffer < pProperties->cbBuffer)// 这个分配器是否不合适
	{
		return E_FAIL;
	}
	return NOERROR;
}

HRESULT CVideoPin::SetMediaType(const CMediaType *pmt)
{
	if (HasSet == FALSE)//如果GetMediaType函数没有调用
	{
		GetMediaType(0, &m_mt);//设置引脚媒体类型
		HasSet = TRUE;
		return S_OK;
	}
	return CBasePin::SetMediaType(pmt);
}

HRESULT CVideoPin::BreakConnect()
{
	HasSet = FALSE;
	return CBasePin::BreakConnect();
}

DWORD WINAPI  VideoThread(LPVOID pParam);//视频引脚工作线程

HRESULT CVideoPin::Active(void)
{
	CreateThread(NULL, 0, VideoThread, pCFilter, 0, NULL);//创建视频引脚工作线程
	return CBaseOutputPin::Active();
}

HRESULT CVideoPin::Inactive(void)
{
	ResetEvent(pCFilter->hRun);//设置"运行"无信号
	return CBaseOutputPin::Inactive();
}

void SetStartCode(BYTE* pB, UINT len)//将所有单元大小替换为0x00000001
{
	int index = 0;
	while (index <(int)len)
	{
		UINT size = (pB[index] << 24) + (pB[index + 1] << 16) + (pB[index + 2] << 8) + pB[index + 3];
		pB[index] = 0; pB[index + 1] = 0; pB[index + 2] = 0; pB[index + 3] = 1;
		index += 4 + size;
	}
}

DWORD WINAPI  VideoThread(LPVOID pParam)//视频引脚工作线程
{
	CFilter* pCFilter = (CFilter*)pParam;
	HRESULT hr;
	SetEvent(pCFilter->hRun);//设置"运行"有信号
	ResetEvent(pCFilter->hSeek);//设置"定位"无信号
	pCFilter->SeekFrame = 0;

SEEK:
	pCFilter->pCVideoPin->DeliverNewSegment(0, pCFilter->DUR - pCFilter->SeekFrame*pCFilter->AvgTimePerFrame, 1.0);//向下游引脚发送新段通知
	IMediaSample *pOutSample = NULL;
	hr = pCFilter->pCVideoPin->GetDeliveryBuffer(&pOutSample, NULL, NULL, 0);//获取一个空的输出引脚样本
	if (hr == S_OK)//首先发送解码器需要的初始化数据
	{
		BYTE* pBuffer = NULL;
		hr = pOutSample->GetPointer(&pBuffer);//获取输出引脚样本缓冲区指针
		if (pCFilter->pCVideoPin->VideoSubType == MEDIASUBTYPE_H264)//如果是H264视频
		{
			CopyMemory(pBuffer, pCFilter->pCVideoPin->pSHeader, pCFilter->pCVideoPin->cbSHeader);//首先发送sps, pps单元
			hr = pOutSample->SetActualDataLength(pCFilter->pCVideoPin->cbSHeader);//设置输出引脚样本有效数据长度
		}
		LONGLONG Star = 0, End = 0;
		hr = pOutSample->SetTime(&Star, &End);//设置时间戳
		hr = pOutSample->SetDiscontinuity(TRUE);//设置中断标志
		hr = pOutSample->SetPreroll(TRUE);//设置预卷标志
		hr = pCFilter->pCVideoPin->Deliver(pOutSample);//输出引脚向下游发送样本
		pOutSample->Release();//释放输出引脚样本
	}
	LONGLONG star = 0;
	UINT S = pCFilter->SeekFrame;
	for (UINT i = S; i < pCFilter->ListCount; i++)
	{
		DWORD dwRun = WaitForSingleObject(pCFilter->hRun, 0);
		if (dwRun != WAIT_OBJECT_0)return 0;//如果"运行"无信号,终止线程
		DWORD dwSeek = WaitForSingleObject(pCFilter->hSeek, 0);
		if (dwSeek == WAIT_OBJECT_0)//如果"视频定位"有信号,转到SEEK
		{
			pCFilter->pCVideoPin->DeliverBeginFlush();//通知下游(解码器)开始刷新
			Sleep(200);//确保下游解码器已丢弃所有样本
			pCFilter->pCVideoPin->DeliverEndFlush();//结束刷新
			goto SEEK;
		}
		Sample* pS = (Sample*)pCFilter->pSampleList[i];
		IMediaSample *pOutSample = NULL;
		hr = pCFilter->pCVideoPin->GetDeliveryBuffer(&pOutSample, NULL, NULL, 0);//获取一个空的输出引脚样本
		if (hr == S_OK)
		{
			BYTE* pBuffer = NULL;
			hr = pOutSample->GetPointer(&pBuffer);//获取输出引脚样本缓冲区指针
			if (pCFilter->pCVideoPin->VideoSubType == MEDIASUBTYPE_H264)//如果是H264视频
			{
				ReadAtPos(pCFilter->hFile, pS->Offset, pS->Size, pBuffer);//读取样本
				SetStartCode(pBuffer, pS->Size);//将所有单元大小替换为0x00000001
				hr = pOutSample->SetActualDataLength(pS->Size);//设置输出引脚样本有效数据长度
				LONGLONG Star = star + pS->ctts;
				LONGLONG End = Star + pCFilter->AvgTimePerFrame;
				hr = pOutSample->SetTime(&Star, &End);//设置输出引脚样本时间戳
				pCFilter->CUR = Star;
			}
			else
			{
				ReadAtPos(pCFilter->hFile, pS->Offset, pS->Size, pBuffer);//读取样本
				hr = pOutSample->SetActualDataLength(pS->Size);//设置输出引脚样本有效数据长度
				LONGLONG end = star + pCFilter->AvgTimePerFrame;
				hr = pOutSample->SetTime(&star, &end);//设置输出引脚样本时间戳
				pCFilter->CUR = star;
			}
			if (pS->KeyFrame)//如果是关键帧
				hr = pOutSample->SetSyncPoint(TRUE);//设置同步点标志
			else
				hr = pOutSample->SetSyncPoint(FALSE);
			hr = pCFilter->pCVideoPin->Deliver(pOutSample);//输出引脚向下游发送样本
			pOutSample->Release();//释放输出引脚样本
			star += pCFilter->AvgTimePerFrame;//计算下一个样本的起始时间
		}
	}
	hr = pCFilter->pCVideoPin->DeliverEndOfStream();
	return 1;
}

CAudioPin.cpp

cpp 复制代码
#include "DLL.h"

CAudioPin::CAudioPin(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName) : CBaseOutputPin(NAME("Out"), pFilter, pFilter, phr, pPinName)
{
	pCFilter = pFilter;
}

CAudioPin::~CAudioPin()
{

}

HRESULT CAudioPin::CheckMediaType(const CMediaType *pmt)
{
	AM_MEDIA_TYPE* pMt = NULL;
	pCFilter->pAudioType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, (void**)&pMt);//将IMFMediaType表示的媒体类型,转换为AM_MEDIA_TYPE结构形式
	CMediaType MT(*pMt);
	if (pmt->MatchesPartial(&MT))
	{
		pCFilter->pAudioType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pMt);//释放GetRepresentation分配的内存
		return S_OK;
	}
	pCFilter->pAudioType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pMt);//释放GetRepresentation分配的内存
	return S_FALSE;
}

HRESULT CAudioPin::GetMediaType(int iPosition, CMediaType *pmt)
{
	if (pCFilter->m_pFileName == NULL)return S_FALSE;
	if (iPosition == 0)
	{
		AM_MEDIA_TYPE* pMt = NULL;
		pCFilter->pAudioType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, (void**)&pMt);//将IMFMediaType表示的媒体类型,转换为AM_MEDIA_TYPE结构形式
		pmt->Set(*pMt);
		pCFilter->pAudioType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pMt);//释放GetRepresentation分配的内存
		return S_OK;
	}
	return S_FALSE;
}

HRESULT CAudioPin::SetMediaType(const CMediaType *pmt)
{
	if (HasSet == FALSE)//如果GetMediaType函数没有调用
	{
		GetMediaType(0, &m_mt);//设置引脚媒体类型
		HasSet = TRUE;
		return S_OK;
	}
	return CBasePin::SetMediaType(pmt);
}

HRESULT CAudioPin::BreakConnect()
{
	HasSet = FALSE;
	return CBasePin::BreakConnect();
}

HRESULT CAudioPin::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES * pProperties)//确定输出引脚样本缓冲区大小
{
	HRESULT hr = NOERROR;
	pProperties->cBuffers = 1;//1个缓冲区
	pProperties->cbBuffer = 1000000;//以1个音频包的大小,指定引脚缓冲区的大小
	ALLOCATOR_PROPERTIES Actual;
	hr = pAlloc->SetProperties(pProperties, &Actual);
	if (FAILED(hr))return hr;
	if (Actual.cbBuffer < pProperties->cbBuffer)// 这个分配器是否不合适
	{
		return E_FAIL;
	}
	return NOERROR;
}

DWORD WINAPI  AudioThread(LPVOID pParam);//音频引脚工作线程

HRESULT CAudioPin::Active(void)
{
	CreateThread(NULL, 0, AudioThread, pCFilter, 0, NULL);//创建音频引脚工作线程
	return CBaseOutputPin::Active();
}

DWORD WINAPI  AudioThread(LPVOID pParam)//音频引脚工作线程
{
	CFilter* pCFilter = (CFilter*)pParam;
	HRESULT hr;
	ResetEvent(pCFilter->hSeekA);//设置"定位"无信号
	pCFilter->SeekANu = 0;
	WaitForSingleObject(pCFilter->hRun, INFINITE);//等待"运行"信号

SEEK:
	pCFilter->pCAudioPin->DeliverNewSegment(0, pCFilter->DUR_A - pCFilter->SeekANu * pCFilter->AvgTimeA, 1.0);//向下游引脚发送新段通知
	UINT S = pCFilter->SeekANu;
	LONGLONG star = 0;
	for (UINT i = S; i < pCFilter->ListCountA; i++)
	{
		DWORD dwRun = WaitForSingleObject(pCFilter->hRun, 0);
		if (dwRun != WAIT_OBJECT_0)return 0;//如果"运行"无信号,终止线程
		DWORD dwSeek = WaitForSingleObject(pCFilter->hSeekA, 0);
		if (dwSeek == WAIT_OBJECT_0)//如果"音频定位"有信号,转到SEEK
		{
			pCFilter->pCAudioPin->DeliverBeginFlush();//通知下游(解码器)开始刷新
			Sleep(100);//确保下游解码器已丢弃所有样本
			pCFilter->pCAudioPin->DeliverEndFlush();//结束刷新
			goto SEEK;
		}
		Sample* pS = (Sample*)pCFilter->pSampleListA[i];
		IMediaSample *pOutSample = NULL;
		hr = pCFilter->pCAudioPin->GetDeliveryBuffer(&pOutSample, NULL, NULL, 0);//获取一个空的输出引脚样本
		if (hr == S_OK)
		{
			BYTE* pBuffer = NULL;
			hr = pOutSample->GetPointer(&pBuffer);//获取输出引脚样本缓冲区指针
			ReadAtPos(pCFilter->hFileA, pS->Offset, pS->Size, pBuffer);
			hr = pOutSample->SetActualDataLength(pS->Size);//设置输出引脚样本有效数据长度
			LONGLONG end = star + pCFilter->AvgTimeA;
			hr = pOutSample->SetTime(&star, &end);//设置输出引脚样本时间戳
			pCFilter->CUR_A = star;
			hr = pCFilter->pCAudioPin->Deliver(pOutSample);//输出引脚向下游发送样本
			pOutSample->Release();//释放输出引脚样本
			star += pCFilter->AvgTimeA;//计算下一个样本的起始时间
		}
	}
	hr = pCFilter->pCAudioPin->DeliverEndOfStream();
	return 1;
}

下载本过滤器DLL

相关推荐
18538162800余。几秒前
矩阵碰一碰发视频源码搭建技术解析
音视频
sukalot30 分钟前
Windows 图形显示驱动开发-WDDM 2.4功能-GPU 半虚拟化(十一)
windows·驱动开发
车载小杜1 小时前
基于指针的线程池
开发语言·c++
西北丰1 小时前
构造超小程序
windows
『六哥』2 小时前
Windows 10 如何设置右击鼠标快速进行“关机”
windows·经验分享·编辑器
weixin_420947643 小时前
windows golang,consul,grpc学习
windows·golang·consul
云 无 心 以 出 岫3 小时前
贪心算法QwQ
数据结构·c++·算法·贪心算法
换一颗红豆4 小时前
【C++ 多态】—— 礼器九鼎,釉下乾坤,多态中的 “风水寻龙诀“
c++
随便昵称4 小时前
蓝桥杯专项复习——前缀和和差分
c++·算法·前缀和·蓝桥杯
commonbelive4 小时前
团体程序设计天梯赛——L1-100 四项全能
c++