DirectShow过滤器开发-写AVI视频文件过滤器

news2024/11/14 12:01:34

下载本过滤器DLL
本过滤器将视频流和音频流写入AVI视频文件。

过滤器信息

过滤器名称:写AVI
过滤器GUID:{2EF49957-37DF-4356-A2A0-ECBC52D1984B}
DLL注册函数名:DllRegisterServer
删除注册函数名:DllUnregisterServer
过滤器有2个输入引脚。

输入引脚1标识:Video
输入引脚1媒体类型:
主要类型:MEDIATYPE_Video
子类型:MEDIASUBTYPE_NULL
格式类型:FORMAT_VideoInfo

输入引脚2标识:Audio
输入引脚2媒体类型:
主要类型:MEDIATYPE_Audio
子类型:MEDIASUBTYPE_MP3,MEDIASUBTYPE_PCM,MEDIASUBTYPE_ALAW
格式类型:FORMAT_WaveFormatEx

过滤器开发信息

本文介绍的方法完全从AVI文件结构入手,写RIFF文件头(12字节),创建hdrl_LIST,包括avih和1个视频strl_LIST,1个音频strl_LIST;在视频音频strl_LIST中各创建1个indx索引,indx索引中每个条目指向一个索引块,视频索引块条目指向1个视频帧的存储位置,音频索引块条目指向1个音频数据块的存储位置。没有创建idx1索引。视频音频数据都存储在RIFF_movi_LIST和AVIX_movi_LIST中,每写入一次,创建一个索引块条目,条目报告数据的位置和大小。包括索引块也存储在RIFF_movi_LIST和AVIX_movi_LIST中;每创建一个索引块,就增加一个indx索引条目,条目报告索引块的位置,大小和音频或视频的长度;视频长度就是帧数量,PCM音频长度是音频帧的数量,MP3长度直接使用数据的字节大小。
AVI文件没有限制视频音频的编码方式。本过滤器没有限制视频编码方式,但音频编码方式限制为MP3,PCM,ALAW。
下面是使用本过滤器创建的1个AVI文件的结构图片:
在这里插入图片描述

过滤器DLL的全部代码

DLL.h


#ifndef  DLL_FILE
#define DLL_FILE

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

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

// {2EF49957-37DF-4356-A2A0-ECBC52D1984B}
DEFINE_GUID(CLSID_AviWriter,//过滤器GUID
	0x2ef49957, 0x37df, 0x4356, 0xa2, 0xa0, 0xec, 0xbc, 0x52, 0xd1, 0x98, 0x4b);

DEFINE_GUID(MEDIASUBTYPE_ALAW,//
	0x00000006, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);

DEFINE_GUID(MEDIASUBTYPE_MP3,
	0x00000055, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);

#include "strsafe.h"
#include "aviriff.h"

class AVI_HEADER
{
public:
	BYTE p[4642];

	char* RIFF_ch;//文件规范标识
	DWORD* FileSize;//文件大小
	char* AVI_ch;//文件类型

	char* hdrl_LIST_ch;//hdrl_LIST
	DWORD* hdrl_LIST_Size;//
	char* hdrl_ch;

	char* fcc; //avih块               
	DWORD*  cb;//avih块大小
	DWORD*  dwMicroSecPerFrame;//帧持续时间,单位微秒
	DWORD*  dwMaxBytesPerSec;//传输率
	DWORD*  dwPaddingGranularity;//块对齐
	DWORD*  dwFlags;//文件属性
	DWORD*  dwTotalFrames;//帧总数
	DWORD*  dwInitialFrames;//初始帧
	DWORD*  dwStreams;//流数量
	DWORD*  dwSuggestedBufferSize;//建议的缓冲区的大小
	DWORD*  dwWidth;//图像的宽度
	DWORD*  dwHeight;//图像的高度
	BYTE*  dwReserved;//保留


	char* strl_LIST;//视频strl_LIST
	DWORD* strl_LIST_Size;//
	char* strl_ch;//strl

	char* strh_ch;//strh
	DWORD* strh_Size;//
	char* fccType;//
	char* fccHandler;//首选处理器
	DWORD*  strhFlags;//标志
	WORD*   wPriority;//流的优先级
	WORD*   wLanguage;//语言
	DWORD*  strh_dwInitialFrames;//初始帧
	DWORD*  dwScale;//采样率分母
	DWORD*  dwRate;//采样率分子
	DWORD*  dwStart;//流的开始时间
	DWORD*  dwLength;//流的长度,以视频帧为单位
	DWORD*  strh_dwSuggestedBufferSize;//建议的缓冲区大小 
	DWORD*  dwQuality;//质量参数
	DWORD*  dwSampleSize;//单个数据样本的大小
	struct Frame {
		short int left;
		short int top;
		short int right;
		short int bottom;
	};
	Frame* rcFrame;//目标矩形

	char* strf_ch;//strf
	DWORD* strf_Size;//strf块大小
	DWORD* biSize;//位图头大小
	LONG*  biWidth;//位图的宽度
	LONG*  biHeight;//位图的高度
	WORD*  biPlanes;//平面数
	WORD*  biBitCount;//每个像素的位数
	DWORD* biCompression;//编码方式
	DWORD* biSizeImage;//图像大小
	LONG*  biXPelsPerMeter;//目标设备的水平分辨率
	LONG*  biYPelsPerMeter;//目标设备的垂直分辨率
	DWORD* biClrUsed;//颜色数量
	DWORD* biClrImportant;//重要颜色数量

	char* ApendID;//
	DWORD* ApendSize;//
	BYTE* ApendData;//

	char* indx_ch; //视频indx块
	DWORD* index_Size;
	WORD* wLongsPerEntry;//每个条目所占DWORD个数
	BYTE* bIndexSubType;//索引子类型,0指向索引块,1指向数据块,8条目是真实数据
	BYTE* bIndexType;//索引类型,0标准索引,1场索引
	DWORD* nEntriesInUse;//条目数量
	char* dwChunkId;//流的FOURCC
	DWORD* Reserved1, *Reserved2, *Reserved3;//保留
	struct ENTRY {
		LONGLONG Offset;//偏移量
		DWORD Size;//大小
		DWORD Dur;//长度
	};
	ENTRY* Entry;//indx条目数组

	char* strl_LIST_A;//音频strl_LIST
	DWORD* strl_LIST_A_Size;//
	char* strl_A_ch;//strl

	char* strh_A_ch;//strh
	DWORD* strh_A_Size;//
	char* fccType_A;//
	char* fccHandler_A;//首选处理器
	DWORD*  strhFlags_A;//标志
	WORD*   wPriority_A;//流的优先级
	WORD*   wLanguage_A;//语言
	DWORD*  strh_A_dwInitialFrames;//初始帧
	DWORD*  dwScale_A;//采样率分母
	DWORD*  dwRate_A;//采样率分子
	DWORD*  dwStart_A;//流的开始时间
	DWORD*  dwLength_A;//流的长度,以音频帧为单位
	DWORD*  strh_A_dwSuggestedBufferSize;//建议的缓冲区大小
	DWORD*  dwQuality_A;//质量参数
	DWORD*  dwSampleSize_A;//单个数据样本的大小

	Frame* rcFrame_A;//目标矩形

	char* strf_A_ch;//strf
	DWORD* strf_A_Size;//strf块大小
	WORD* wFormatTag_A;//音频格式
	WORD* nChannels_A;//声道数
	DWORD* nSamplesPerSec_A;//采样率
	DWORD* nAvgBytesPerSec_A;//传输率
	WORD* nBlockAlign_A;//块对齐
	WORD* wBitsPerSample_A;//样本位数
	WORD* cbSize_A;//附加信息大小
	BYTE* pApend;//大小64

	char* indx_ch_A; //音频indx块
	DWORD* index_Size_A;
	WORD* wLongsPerEntry_A;//每个条目所占DWORD个数
	BYTE* bIndexSubType_A;//索引子类型,0指向索引块,1指向数据块,8条目是真实数据
	BYTE* bIndexType_A;//索引类型,0标准索引,1场索引
	DWORD* nEntriesInUse_A;//条目数量
	char* dwChunkId_A;//流的FOURCC
	DWORD* Reserved_A1, *Reserved_A2, *Reserved_A3;//保留
	ENTRY* EntryA;//indx条目数组

	AVI_HEADER()
	{
		RIFF_ch = (char*)&p[0]; CopyMemory(RIFF_ch, "RIFF", 4);//文件规范标识
		FileSize = (DWORD*)&p[4]; *FileSize = 0;//文件大小
		AVI_ch = (char*)&p[8]; CopyMemory(AVI_ch, "AVI ", 4);//文件类型

		hdrl_LIST_ch = (char*)&p[12]; CopyMemory(hdrl_LIST_ch, "LIST", 4);//hdrl_LIST
		hdrl_LIST_Size = (DWORD*)&p[16]; *hdrl_LIST_Size = 4622;//
		hdrl_ch = (char*)&p[20]; CopyMemory(hdrl_ch, "hdrl", 4);

		fcc = (char*)&p[24]; CopyMemory(fcc, "avih", 4); //avih块     
		cb = (DWORD*)&p[28]; *cb = 56;//avih块大小
		dwMicroSecPerFrame = (DWORD*)&p[32];//帧持续时间,单位微秒
		dwMaxBytesPerSec = (DWORD*)&p[36]; *dwMaxBytesPerSec = 0; //传输率
		dwPaddingGranularity = (DWORD*)&p[40]; *dwPaddingGranularity = 1;//块对齐 
		dwFlags = (DWORD*)&p[44]; *dwFlags = AVIF_ISINTERLEAVED | AVIF_TRUSTCKTYPE;//文件属性
																/*AVIF_ISINTERLEAVED  表示AVI文件已交错
																AVIF_TRUSTCKTYPE  使用CKType确定关键帧*/
		dwTotalFrames = (DWORD*)&p[48];//帧总数 
		dwInitialFrames = (DWORD*)&p[52]; *dwInitialFrames = 0;//初始帧
		dwStreams = (DWORD*)&p[56]; *dwStreams = 2;//流数量
		dwSuggestedBufferSize = (DWORD*)&p[60]; *dwSuggestedBufferSize = 0;//建议的缓冲区的大小
		dwWidth = (DWORD*)&p[64];//图像的宽度 
		dwHeight = (DWORD*)&p[68];//图像的高度
		dwReserved = &p[72]; memset(dwReserved, 0, 16);//保留


		strl_LIST = (char*)&p[88]; CopyMemory(strl_LIST, "LIST", 4);//视频strl_LIST
		strl_LIST_Size = (DWORD*)&p[92]; *strl_LIST_Size = 2300;// 
		strl_ch = (char*)&p[96]; CopyMemory(strl_ch, "strl", 4);//strl

		strh_ch = (char*)&p[100]; CopyMemory(strh_ch, "strh", 4);//strh
		strh_Size = (DWORD*)&p[104]; *strh_Size = 56;//
		fccType = (char*)&p[108]; CopyMemory(fccType, "vids", 4);
		fccHandler = (char*)&p[112];//首选处理器
		strhFlags = (DWORD*)&p[116]; *strhFlags = 0;//标志
		wPriority = (WORD*)&p[120]; *wPriority = 0;//流的优先级
		wLanguage = (WORD*)&p[122]; *wLanguage = 0;//语言
		strh_dwInitialFrames = (DWORD*)&p[124]; *strh_dwInitialFrames = 0;//初始帧
		dwScale = (DWORD*)&p[128]; //采样率分母
		dwRate = (DWORD*)&p[132]; *dwRate = 10000000;//采样率分子
		dwStart = (DWORD*)&p[136]; *dwStart = 0;//流的开始时间
		dwLength = (DWORD*)&p[140];//流的长度,以视频帧为单位 
		strh_dwSuggestedBufferSize = (DWORD*)&p[144];//建议的缓冲区大小
		dwQuality = (DWORD*)&p[148]; *dwQuality = 0;//质量参数
		dwSampleSize = (DWORD*)&p[152]; *dwSampleSize = 0;//单个数据样本的大小
		rcFrame = (Frame*)&p[156];//目标矩形

		strf_ch = (char*)&p[164]; CopyMemory(strf_ch, "strf", 4);//strf
		strf_Size = (DWORD*)&p[168]; *strf_Size = 40;//strf块大小
		biSize = (DWORD*)&p[172]; *biSize = 40;//位图头大小
		biWidth = (LONG*)&p[176];//位图的宽度
		biHeight = (LONG*)&p[180];//位图的高度
		biPlanes = (WORD*)&p[184]; *biPlanes = 1;//平面数
		biBitCount = (WORD*)&p[186];//每个像素的位数
		biCompression = (DWORD*)&p[188];//编码方式 
		biSizeImage = (DWORD*)&p[192];//图像大小 
		biXPelsPerMeter = (LONG*)&p[196];//目标设备的水平分辨率 
		biYPelsPerMeter = (LONG*)&p[200];//目标设备的垂直分辨率
		biClrUsed = (DWORD*)&p[204];//颜色数量
		biClrImportant = (DWORD*)&p[208];//重要颜色数量

		ApendID = (char*)&p[212]; CopyMemory(ApendID, "JUNK", 4);//附加信息总大小104
		ApendSize = (DWORD*)&p[216]; *ApendSize = 96;
		ApendData = &p[220];

		indx_ch = (char*)&p[316]; CopyMemory(indx_ch, "indx", 4);//视频indx块
		index_Size = (DWORD*)&p[320]; *index_Size = 2072;
		wLongsPerEntry = (WORD*)&p[324]; *wLongsPerEntry = 4;//每个条目所占DWORD个数
		bIndexSubType = &p[326]; *bIndexSubType = 0;//索引子类型,0指向索引块,1指向数据块,8条目是真实数据
		bIndexType = &p[327]; *bIndexType = 0;//索引类型,0标准索引,1场索引
		nEntriesInUse = (DWORD*)&p[328]; *nEntriesInUse = 0;//条目数量
		dwChunkId = (char*)&p[332]; CopyMemory(dwChunkId, "00db", 4);//流的FOURCC
		Reserved1 = (DWORD*)&p[336]; *Reserved1 = 0;//保留
		Reserved2 = (DWORD*)&p[340]; *Reserved2 = 0;
		Reserved3 = (DWORD*)&p[344]; *Reserved3 = 0;
		Entry = (ENTRY*)&p[348];


		strl_LIST_A = (char*)&p[2396]; CopyMemory(strl_LIST_A, "LIST", 4);//音频strl_LIST
		strl_LIST_A_Size = (DWORD*)&p[2400]; *strl_LIST_A_Size = 2238;//
		strl_A_ch = (char*)&p[2404]; CopyMemory(strl_A_ch, "strl", 4);//strl

		strh_A_ch = (char*)&p[2408]; CopyMemory(strh_A_ch, "strh", 4);//strh
		strh_A_Size = (DWORD*)&p[2412]; *strh_A_Size = 56;// 
		fccType_A = (char*)&p[2416]; CopyMemory(fccType_A, "auds", 4);
		fccHandler_A = (char*)&p[2420]; //首选处理器
		strhFlags_A = (DWORD*)&p[2424]; *strhFlags_A = 0;//标志
		wPriority_A = (WORD*)&p[2428]; *wPriority_A = 0;//流的优先级
		wLanguage_A = (WORD*)&p[2430]; *wLanguage_A = 0;//语言
		strh_A_dwInitialFrames = (DWORD*)&p[2432]; *strh_A_dwInitialFrames = 0;//初始帧
		dwScale_A = (DWORD*)&p[2436]; *dwScale_A = 1;//采样率分母
		dwRate_A = (DWORD*)&p[2440]; *dwRate_A;//采样率分子
		dwStart_A = (DWORD*)&p[2444]; *dwStart_A = 0;//流的开始时间
		dwLength_A = (DWORD*)&p[2448];//流的长度,以音频帧为单位
		strh_A_dwSuggestedBufferSize = (DWORD*)&p[2452]; //建议的缓冲区大小
		dwQuality_A = (DWORD*)&p[2456]; *dwQuality_A = 0;//质量参数
		dwSampleSize_A = (DWORD*)&p[2460];//单个数据样本的大小
		rcFrame_A = (Frame*)&p[2464];//目标矩形
		rcFrame_A->left = 0; rcFrame_A->top = 0; rcFrame_A->right = 0; rcFrame_A->bottom = 0;

		strf_A_ch = (char*)&p[2472]; CopyMemory(strf_A_ch, "strf", 4);//strf
		strf_A_Size = (DWORD*)&p[2476]; *strf_A_Size = 18;//strf块大小 
		wFormatTag_A = (WORD*)&p[2480];//音频格式
		nChannels_A = (WORD*)&p[2482];//声道数
		nSamplesPerSec_A = (DWORD*)&p[2484];//采样率
		nAvgBytesPerSec_A = (DWORD*)&p[2488];//传输率
		nBlockAlign_A = (WORD*)&p[2492];//块对齐
		wBitsPerSample_A = (WORD*)&p[2494];//样本位数
		cbSize_A = (WORD*)&p[2496]; *cbSize_A = 0;//附加信息大小
		pApend = &p[2498]; CopyMemory(pApend, "JUNK", 4);
		DWORD* ApendSize_A = (DWORD*)&p[2502]; *ApendSize_A = 56;

		indx_ch_A = (char*)&p[2562]; CopyMemory(indx_ch_A, "indx", 4); //音频indx块
		index_Size_A = (DWORD*)&p[2566]; *index_Size_A = 2072;
		wLongsPerEntry_A = (WORD*)&p[2570]; *wLongsPerEntry_A = 4;//每个条目所占DWORD个数
		bIndexSubType_A = &p[2572]; *bIndexSubType_A = 0;//索引子类型,0指向索引块,1指向数据块,8条目是真实数据
		bIndexType_A = &p[2573]; *bIndexType_A = 0;//索引类型,0标准索引,1场索引
		nEntriesInUse_A = (DWORD*)&p[2574]; *nEntriesInUse_A = 0;//条目数量
		dwChunkId_A = (char*)&p[2578]; CopyMemory(dwChunkId_A, "01wb", 4);//流的FOURCC
		Reserved_A1 = (DWORD*)&p[2582]; *Reserved_A1 = 0;//保留
		Reserved_A2 = (DWORD*)&p[2586]; *Reserved_A2 = 0;
		Reserved_A3 = (DWORD*)&p[2590]; *Reserved_A3 = 0;
		EntryA = (ENTRY*)&p[2594];
		//以上总大小4642字节
	}
};

class CFilter;
class CPin2;

class CPin1 : public CBaseInputPin
{
	friend class CFilter;
	friend class CPin2;
public:
	CPin1(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName);
	~CPin1();
	HRESULT CheckMediaType(const CMediaType *pmt);
	HRESULT SetMediaType(const CMediaType *pmt);
	STDMETHODIMP Receive(IMediaSample *pSample);
	HRESULT Active();
	HRESULT Inactive();
	HRESULT InitFile();//初始化AVI输出文件
	HRESULT WriteEnd();//完善AVI输出文件
	STDMETHODIMP EndOfStream();
	CFilter *pCFilter;
	HANDLE hFile = NULL;//输出文件句柄
	AVI_HEADER header;//AVI文件头

	LONGLONG movi_LIST_Size_Pos;//movi_LIST块大小的文件位置

	int mIndex = 0;//视频主索引条目索引
	BYTE pV[80000000]; //视频索引块内存数组
	int pVIndex;//视频索引内存数组索引
	DWORD* VideoSize = NULL;//记录视频“块大小”地址
	DWORD* Entries = NULL;//视频“条目数”地址
	LONGLONG* pOffset = NULL;//基本偏移量
	DWORD 	nEntriesInUse = 0;//视频主索引条目数
	void WriteVideo(IMediaSample *pSample);//写视频样本
	void WriteVideoIndexEnd();//完善上一个视频索引块,写视频主索引条目,递增视频主索引条目索引

	int mIndexA = 0;//音频主索引条目索引
	BYTE pA[80000000]; //音频索引块内存数组
	int pAIndex;//音频索引内存数组索引
	DWORD* AudioSize = NULL;//记录音频“块大小”地址
	DWORD* EntriesA = NULL;//音频“条目数”地址
	LONGLONG* pOffsetA = NULL;//基本偏移量
	DWORD 	nEntriesInUseA = 0;//音频索引条目数
	void WriteAudio(IMediaSample *pSample);//写音频样本
	LONGLONG RIFF_Size_Pos;//RIFF_AVIX,RIFF大小位置
	LONGLONG movi_Size_Pos;//RIFF_AVIX,movi_LIST大小位置
	void WriteAudioIndexEnd();//完善上一个音频索引块,写音频主索引条目,递增音频主索引条目索引

	LONGLONG BaseOffset[128];//基本偏移量
	LONGLONG BaseOffsetA[128];//基本偏移量

	CMediaType VideoMediaType;//视频媒体类型
	DWORD VideoCount = 0;//视频帧总数
	DWORD AudioCount = 0;//音频长度
	DWORD ChuckCount = 0;//音频块长度
	long VideoBufferSize = 0, AudioBufferSize = 0;
	HANDLE hStop;//“停止”事件句柄
	LONGLONG CurTime = 0;//视频流当前时间
	HANDLE WriteCompleted = NULL;//“写样本完成”事件句柄
	BOOL END = FALSE;//为TRUE时,标记视频流结束
};

class CPin2 : public CBaseInputPin
{
	friend class CFilter;
	friend class CPin1;
public:
	CPin2(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName);
	~CPin2();
	HRESULT CheckMediaType(const CMediaType *pmt);
	HRESULT SetMediaType(const CMediaType *pmt);
	STDMETHODIMP Receive(IMediaSample *pSample);
	STDMETHODIMP EndOfStream();
	CFilter *pCFilter;//过滤器指针
	CMediaType AudioMediaType;//音频媒体类型
	LONGLONG CurTime = 0;//音频流当前时间
	HANDLE WriteCompleted = NULL;//“写样本完成”事件句柄
	BOOL END = FALSE;//为TRUE时,标记音频流结束
};

class CFilter : public CBaseFilter, public CCritSec, public IFileSinkFilter
{
	friend class CPin1;
	friend class CPin2;
public:
	CFilter(LPWSTR lpName, LPUNKNOWN pUnk, HRESULT *phr);
	virtual ~CFilter();
	DECLARE_IUNKNOWN
	STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
	virtual HRESULT STDMETHODCALLTYPE  SetFileName(LPCOLESTR pszFileName, const AM_MEDIA_TYPE *pmt);
	virtual HRESULT STDMETHODCALLTYPE  GetCurFile(LPOLESTR * ppszFileName, AM_MEDIA_TYPE *pmt);
	static CUnknown * WINAPI CreateInstance(LPUNKNOWN, HRESULT *);
	int GetPinCount();
	CBasePin *GetPin(int n);
	CPin1 *pCPin1 = NULL;   //视频引脚指针
	CPin2 *pCPin2 = NULL;   //音频引脚指针
	WCHAR* m_pFileName = NULL;//要创作的AVI文件路径
};

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

#endif //DLL_FILE

DLL.cpp

#include "DLL.h"


const AMOVIESETUP_MEDIATYPE PinType1[] =   //视频引脚媒体类型
{
	{
		&MEDIATYPE_Video,                 //主要类型
		&MEDIASUBTYPE_NULL                //子类型
	}
};

const AMOVIESETUP_MEDIATYPE PinType2[] =   //音频引脚媒体类型
{
	{
		&MEDIATYPE_Audio,                 //主要类型
		&MEDIASUBTYPE_MP3                 //子类型
	},
	{
		&MEDIATYPE_Audio,                 //主要类型
		&MEDIASUBTYPE_ALAW                //子类型
	},
	{
		&MEDIATYPE_Audio,                 //主要类型
		&MEDIASUBTYPE_PCM                 //子类型
	}
};

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

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

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

#include "DLL.h"

CFilter::CFilter(LPWSTR lpName, LPUNKNOWN pUnk, HRESULT *phr) : CBaseFilter(lpName, pUnk, (CCritSec *) this, CLSID_AviWriter)
{
	pCPin1 = new CPin1(this, phr, L"Video");//创建视频输入引脚
	pCPin2 = new CPin2(this, phr, L"Audio");//创建音频输入引脚
}

CFilter::~CFilter()
{
	if (m_pFileName)delete[] m_pFileName;
}

CUnknown * WINAPI CFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr)
{
	return new CFilter(L"写AVI", pUnk, phr);//创建过滤器
}

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

CBasePin *CFilter::GetPin(int n)
{
	if (n == 0)
	{
		return pCPin1;
	}
	else if (n == 1)
	{
		return pCPin2;
	}
	return NULL;
}

STDMETHODIMP CFilter::NonDelegatingQueryInterface(REFIID iid, void ** ppv)
{
	if (iid == IID_IFileSinkFilter)
	{
		return GetInterface(static_cast<IFileSinkFilter*>(this), ppv);
	}
	else
		return CBaseFilter::NonDelegatingQueryInterface(iid, ppv);
}

HRESULT CFilter::GetCurFile(LPOLESTR *ppszFileName, AM_MEDIA_TYPE *pmt)
{
	CheckPointer(ppszFileName, E_POINTER);
	*ppszFileName = NULL;
	if (m_pFileName != NULL)
	{
		size_t len = 1 + lstrlenW(m_pFileName);
		*ppszFileName = (LPOLESTR)QzTaskMemAlloc(sizeof(WCHAR) * (len));
		if (*ppszFileName != NULL)
		{
			HRESULT hr = StringCchCopyW(*ppszFileName, len, m_pFileName);
		}
	}
	if (pmt)
	{
		ZeroMemory(pmt, sizeof(*pmt));
		pmt->majortype = MEDIATYPE_NULL;
		pmt->subtype = MEDIASUBTYPE_NULL;
	}
	return S_OK;
}

HRESULT CFilter::SetFileName(LPCOLESTR pszFileName, const AM_MEDIA_TYPE *pmt)
{
	CheckPointer(pszFileName, E_POINTER);
	if (wcslen(pszFileName) > MAX_PATH || wcslen(pszFileName)<4)
		return ERROR_FILENAME_EXCED_RANGE;
	size_t len = 1 + lstrlenW(pszFileName);
	m_pFileName = new WCHAR[len];
	if (m_pFileName == 0)
		return E_OUTOFMEMORY;
	HRESULT hr = StringCchCopyW(m_pFileName, len, pszFileName);
	if (m_pFileName[len - 2] != 'i' || m_pFileName[len - 3] != 'v' || m_pFileName[len - 4] != 'a' || m_pFileName[len - 5] != '.')//如果不是AVI文件
	{
		delete[] m_pFileName; m_pFileName = NULL;
		return VFW_E_INVALID_FILE_FORMAT;//设置文件名失败
	}
	return S_OK;
}


CPin1.cpp

#include "DLL.h"
#include "dvdmedia.h"


CPin1::CPin1(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName) : CBaseInputPin(NAME("Video"), pFilter, pFilter, phr, pPinName)
{
	pCFilter = pFilter;

	WriteCompleted = CreateEvent(NULL, TRUE, FALSE, NULL);//手动重置,初始无信号
	hStop = CreateEvent(NULL, TRUE, FALSE, NULL);//手动重置,初始无信号

	//写视频索引头
	pVIndex = 0;
	char chfcc[4] = { 'i', 'x','0','0' };//视频索引块标识
	CopyMemory(&pV[pVIndex], chfcc, 4); pVIndex += 4;//写索引块标识
	VideoSize = (DWORD*)&pV[pVIndex];//获取视频“块大小”地址
	DWORD 	ixSize = 0;//块大小
	CopyMemory(&pV[pVIndex], &ixSize, 4); pVIndex += 4;//写索引块大小
	WORD 	wLongsPerEntry = 2;//条目的DWORD大小
	CopyMemory(&pV[pVIndex], &wLongsPerEntry, 2); pVIndex += 2;//写条目大小
	BYTE 		bIndexSubType = 0;//必须为0
	CopyMemory(&pV[pVIndex], &bIndexSubType, 1); pVIndex += 1;//写索引子类型
	BYTE bIndexType = AVI_INDEX_OF_CHUNKS;//必须为AVI_INDEX_OF_CHUNKS
	CopyMemory(&pV[pVIndex], &bIndexType, 1); pVIndex += 1;//写索引类型
	Entries = (DWORD*)&pV[pVIndex];//记录视频“条目数”地址
	CopyMemory(&pV[pVIndex], &nEntriesInUse, 4); pVIndex += 4;//写条目数
	char ChunkId[4] = { '0','0','d','b' };//流标识
	CopyMemory(&pV[pVIndex], ChunkId, 4); pVIndex += 4;//写流标识
	pOffset = (LONGLONG*)&pV[pVIndex];//获取基本偏移量地址
	BaseOffset[0] = 0;
	CopyMemory(&pV[pVIndex], &BaseOffset[0], 8); pVIndex += 8;//写基本偏移量
	DWORD dwReserved = 0;//保留
	CopyMemory(&pV[pVIndex], &dwReserved, 4); pVIndex += 4;//写保留

	 //写音频索引头
	pAIndex = 0;
	char chfccA[4] = { 'i', 'x','0','1' };//音频索引块标识
	CopyMemory(&pA[pAIndex], chfccA, 4); pAIndex += 4;//写索引块标识
	AudioSize = (DWORD*)&pA[pAIndex];//记录音频“块大小”地址
	DWORD 	ixSizeA = 0;//块大小
	CopyMemory(&pA[pAIndex], &ixSizeA, 4); pAIndex += 4;//写索引块大小
	WORD 	wLongsPerEntryA = 2;//条目的DWORD大小
	CopyMemory(&pA[pAIndex], &wLongsPerEntryA, 2); pAIndex += 2;//写条目大小
	BYTE 		bIndexSubTypeA = 0;//必须为0
	CopyMemory(&pA[pAIndex], &bIndexSubTypeA, 1); pAIndex += 1;//写索引子类型
	BYTE bIndexTypeA = AVI_INDEX_OF_CHUNKS;//必须为AVI_INDEX_OF_CHUNKS
	CopyMemory(&pA[pAIndex], &bIndexTypeA, 1); pAIndex += 1;//写索引类型
	EntriesA = (DWORD*)&pA[pAIndex];//记录音频“条目数”地址
	CopyMemory(&pA[pAIndex], &nEntriesInUseA, 4); pAIndex += 4;//写条目数
	char ChunkIdA[4] = { '0','1','w','b' };//流标识
	CopyMemory(&pA[pAIndex], ChunkIdA, 4); pAIndex += 4;//写流标识
	pOffsetA = (LONGLONG*)&pA[pAIndex];//获取基本偏移量地址
	BaseOffsetA[0] = 0;
	CopyMemory(&pA[pAIndex], &BaseOffsetA[0], 8); pAIndex += 8;//写基本偏移量
	DWORD dwReservedA = 0;//保留
	CopyMemory(&pA[pAIndex], &dwReservedA, 4); pAIndex += 4;//写保留
}

CPin1::~CPin1()
{
}

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

HRESULT WriteFoure(HANDLE hFile, LONGLONG Pos, void* p)//在指定位置写入4字节,并将文件指针返回到原来的位置
{
	LONGLONG Cur = GetFilePos(hFile);//获取当前位置
	LARGE_INTEGER Move;
	Move.QuadPart = Pos;
	SetFilePointerEx(hFile, Move, NULL, FILE_BEGIN);//移动到指定位置
	WriteFile(hFile, p, 4, NULL, NULL);//写入4字节
	Move.QuadPart = Cur;
	SetFilePointerEx(hFile, Move, NULL, FILE_BEGIN);//返回到返回原来的位置
	return S_OK;
}

HRESULT CPin1::CheckMediaType(const CMediaType *pmt)
{
	if (pmt->majortype == MEDIATYPE_Video && pmt->formattype == FORMAT_VideoInfo)
	{
		return S_OK;
	}
	return S_FALSE;
}

HRESULT CPin1::SetMediaType(const CMediaType *pmt)
{
	VideoMediaType = *pmt;
	return CBaseInputPin::SetMediaType(pmt);
}

HRESULT CPin1::Receive(IMediaSample * pSample)//接收函数
{
	if (pCFilter->pCPin2->END)//如果音频流已结束
	{
		WriteVideo(pSample);
		return S_OK;
	}
Agan:
	DWORD dw1 = WaitForSingleObject(WriteCompleted, 0);//检测“写样本完成”信号
	DWORD dw2 = WaitForSingleObject(hStop, 0);//检测“停止”信号
	if (dw2 == WAIT_OBJECT_0)
	{
		return S_FALSE;//如果有“停止”信号,返回S_FALSE以终止流
	}
	if (dw1 != WAIT_OBJECT_0)//没有“写样本完成”信号
	{
		goto Agan;//重新检测(阻塞)
	}
	WriteVideo(pSample);
	if (CurTime <= pCFilter->pCPin2->CurTime)//如果视频当前时间小于或等于音频当前时间
	{
		ResetEvent(pCFilter->pCPin2->WriteCompleted);//设置音频“写样本完成”无信号
		SetEvent(WriteCompleted);//设置视频“写样本完成”有信号
	}
	else//如果音频当前时间小
	{
		ResetEvent(WriteCompleted);//设置视频“写样本完成”无信号
		SetEvent(pCFilter->pCPin2->WriteCompleted);//设置音频“写样本完成”有信号
	}
	return S_OK;
}

void CPin1::WriteVideo(IMediaSample *pSample)//写视频样本
{
	BYTE* pBy = NULL;
	HRESULT  hr = pSample->GetPointer(&pBy);//获取视频引脚样本缓冲区指针
	long len = pSample->GetActualDataLength();//获取有效数据长度
	HRESULT SyncPoint = pSample->IsSyncPoint();//获取同步点标志
	LONGLONG star, end;
	hr = pSample->GetTime(&star, &end);
	if (GetFilePos(hFile) - BaseOffset[mIndex]+len > 0x7FFFFFFF)//如果块大小大于 0x7FFFFFFF
	{
		WriteVideoIndexEnd();//完善上一个视频索引块,写视频主索引条目,递增视频主索引条目索引
		WriteAudioIndexEnd();//完善上一个音频索引块,写音频主索引条目,递增音频主索引条目索引
		if (mIndex == 1)
		{
			DWORD movi_LIST_Size = (DWORD)(GetFilePos(hFile) - movi_LIST_Size_Pos - 4);
			WriteFoure(hFile, movi_LIST_Size_Pos, &movi_LIST_Size);//写movi_LIST大小
			*header.FileSize = (DWORD)(GetFilePos(hFile) - 8);//赋值RIFF文件大小
		}
		else
		{
			DWORD RIFF_Size = (DWORD)(GetFilePos(hFile) - RIFF_Size_Pos - 4);
			WriteFoure(hFile, RIFF_Size_Pos, &RIFF_Size);//写RIFF_AVIX,RIFF大小
			DWORD movi_Size = (DWORD)(GetFilePos(hFile) - movi_Size_Pos - 4);
			WriteFoure(hFile, movi_Size_Pos, &movi_Size);//写RIFF_AVIX,movi_LIST大小
		}

		char ch1[4] = { 'R','I','F','F' };//写RIFF_AVIX头
		WriteFile(hFile, ch1, 4, NULL, NULL);//RIFF
		RIFF_Size_Pos = GetFilePos(hFile);
		DWORD RIFF_Size = 0;
		WriteFile(hFile, &RIFF_Size, 4, NULL, NULL);//RIFF大小
		char ch2[4] = { 'A','V','I','X' };
		WriteFile(hFile, ch2, 4, NULL, NULL);//AVIX
		char ch3[4] = { 'L','I','S','T' };
		WriteFile(hFile, ch3, 4, NULL, NULL);//LIST
		movi_Size_Pos = GetFilePos(hFile);
		DWORD movi_Size = 0;
		WriteFile(hFile, &movi_Size, 4, NULL, NULL);//movi_LIST大小
		char ch4[4] = { 'm','o','v','i' };
		WriteFile(hFile, ch4, 4, NULL, NULL);//movi

	}
	CurTime = end;
	if (SyncPoint == S_OK)//如果有同步点标志
	{
		char chV[4] = { '0','0','d','b' };
		WriteFile(hFile, chV, 4, NULL, NULL);//写视频数据标识
	}
	else
	{
		char chV[4] = { '0','0','d','c' };
		WriteFile(hFile, chV, 4, NULL, NULL);//写视频数据标识
	}
	DWORD size = len;
	WriteFile(hFile, &size, 4, NULL, NULL);//写数据大小
	DWORD offset = (DWORD)(GetFilePos(hFile) - BaseOffset[mIndex]);
	CopyMemory(&pV[pVIndex], &offset, 4); pVIndex += 4;//写视频索引块偏移量
	CopyMemory(&pV[pVIndex], &size, 4); pVIndex += 4;//写视频索引块大小
	WriteFile(hFile, pBy, len, NULL, NULL);//写数据
	VideoBufferSize = max(VideoBufferSize, len);
	nEntriesInUse++;//递增视频主索引条目数
	VideoCount++;//递增帧数量
}

void CPin1::WriteAudio(IMediaSample *pSample)//写音频样本
{
	BYTE* pBy = NULL;
	HRESULT  hr = pSample->GetPointer(&pBy);//获取视频引脚样本缓冲区指针
	long len = pSample->GetActualDataLength();//获取有效数据长度
	LONGLONG star, end;
	if (pCFilter->pCPin2->AudioMediaType.subtype== MEDIASUBTYPE_MP3)//如果是MP3流
	{
		WAVEFORMATEX* p = (WAVEFORMATEX*)pCFilter->pCPin2->AudioMediaType.pbFormat;
		DWORD TimePerFrame = (DWORD)((float)1152 / (float)p->nSamplesPerSec * (float)10000000);//计算每帧持续时间,单位100纳秒
		pCFilter->pCPin2->CurTime += TimePerFrame;//计算当前时间
		ChuckCount += len;
		AudioCount += len;
	}
	else
	{
		hr = pSample->GetTime(&star, &end);
		pCFilter->pCPin2->CurTime = end;
		WAVEFORMATEX* p = (WAVEFORMATEX*)pCFilter->pCPin2->AudioMediaType.pbFormat;
		ChuckCount += len / p->nBlockAlign;
		AudioCount += len / p->nBlockAlign;
	}
	DWORD size = len;
	char chA[4] = { '0', '1', 'w', 'b' };
	WriteFile(hFile, chA, 4, NULL, NULL);//写音频数据标识
	WriteFile(hFile, &size, 4, NULL, NULL);//写数据大小
	DWORD offset = (DWORD)(GetFilePos(hFile) - BaseOffsetA[mIndexA]);
	CopyMemory(&pA[pAIndex], &offset, 4); pAIndex += 4;//写音频索引偏移量
	CopyMemory(&pA[pAIndex], &size, 4); pAIndex += 4;//写音频索引大小
	WriteFile(hFile, pBy, len, NULL, NULL);//写数据
	AudioBufferSize = max(AudioBufferSize, len);
	nEntriesInUseA++;//递增音频索引条目数
}

HRESULT CPin1::Active()
{
	if (InitFile() != S_OK)return S_FALSE;
	return CBaseInputPin::Active();
}

HRESULT CPin1::Inactive()
{
	SetEvent(hStop);
	WriteVideoIndexEnd();//完善上一个视频索引块,写视频主索引条目,递增视频主索引条目索引
	WriteAudioIndexEnd();//完善上一个音频索引块,写音频主索引条目,递增音频主索引条目索引
	if (mIndex == 1)
	{
		DWORD movi_LIST_Size = (DWORD)(GetFilePos(hFile) - movi_LIST_Size_Pos - 4);
		WriteFoure(hFile, movi_LIST_Size_Pos, &movi_LIST_Size);//写movi_LIST大小
		*header.FileSize= (DWORD)(GetFilePos(hFile) - 8);//赋值RIFF文件大小
	}
	else
	{
		DWORD RIFF_Size = (DWORD)(GetFilePos(hFile) - RIFF_Size_Pos - 4);
		WriteFoure(hFile, RIFF_Size_Pos, &RIFF_Size);//写RIFF_AVIX,RIFF大小
		DWORD movi_Size = (DWORD)(GetFilePos(hFile) - movi_Size_Pos - 4);
		WriteFoure(hFile, movi_Size_Pos, &movi_Size);//写RIFF_AVIX,movi_LIST大小
	}
	*header.nEntriesInUse = mIndex;
	*header.nEntriesInUse_A = mIndexA;
	*header.dwTotalFrames = VideoCount;//赋值帧总数,avih参数
	WriteEnd();//完善输出文件
	return CBaseInputPin::Inactive();
}

HRESULT CPin1::InitFile()// 初始化AVI输出文件
{
	if (pCFilter->m_pFileName == NULL)
	{
		MessageBox(0, L"没有指定输出文件", L"写AVI", MB_OK); return S_FALSE;
	}
	hFile = CreateFile(pCFilter->m_pFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);//创建输出文件
	if (INVALID_HANDLE_VALUE == hFile)
	{
		MessageBox(0, L"创建输出文件失败", L"写AVI", MB_OK);
		hFile = NULL;
		return S_FALSE;
	}
	END = FALSE; pCFilter->pCPin2->END = FALSE; pCFilter->pCPin2->CurTime = 0;
	VideoBufferSize = 0; AudioBufferSize = 0;
	AudioCount = 0;//音频长度
	ChuckCount = 0;//音频块长度
	nEntriesInUse = 0;//视频主索引条目数
	mIndex = 0;
	header.Entry[0].Offset = BaseOffset[0] = 0;
	nEntriesInUseA = 0;//音频主索引条目数
	mIndexA = 0;
	header.EntryA[0].Offset = BaseOffsetA[0] = 0;
	WriteFile(hFile, &header, 4642, NULL, NULL);//写AVI文件头
	char chLIST[4] = { 'L','I','S','T' };//写movi_LIST头
	WriteFile(hFile, chLIST, 4, NULL, NULL);//LIST
	movi_LIST_Size_Pos = GetFilePos(hFile);//获取movi_LIST大小地址
	DWORD movi_LIST_Size = 0;
	WriteFile(hFile, &movi_LIST_Size, 4, NULL, NULL);//movi_LIST大小
	char chmovi[4] = { 'm','o','v','i' };
	WriteFile(hFile, chmovi, 4, NULL, NULL);//movi

	ResetEvent(hStop);//设置“停止”无信号
	SetEvent(WriteCompleted);//设置视频“写样本完成”有信号
	ResetEvent(pCFilter->pCPin2->WriteCompleted);//设置音频“写样本完成”无信号
	return S_OK;
}

void CPin1::WriteVideoIndexEnd()//完善上一个视频索引块,写视频主索引条目,递增视频主索引条目索引
{
	DWORD VideoIndexSize = pVIndex;//视频索引块大小
	*VideoSize = VideoIndexSize - 8;//写视频索引块大小
	*Entries = nEntriesInUse;//写条目数量
	*pOffset = BaseOffset[mIndex];//重写基本偏移量
	header.Entry[mIndex].Offset = GetFilePos(hFile);//视频主索引条目偏移量
	header.Entry[mIndex].Size = VideoIndexSize;//视频主索引条目大小
	header.Entry[mIndex].Dur = nEntriesInUse;//视频主索引条目长度
	WriteFile(hFile, &pV[0], VideoIndexSize, NULL, NULL);//写视频索引块
	mIndex++;//递增视频主索引条目索引
	nEntriesInUse = 0;
	BaseOffset[mIndex] = GetFilePos(hFile);//赋值下一个基本偏移量
	pVIndex = 32;//移动指针到,第1个条目位置
}

void CPin1::WriteAudioIndexEnd()//完善上一个音频索引块,写音频主索引条目,递增音频主索引条目索引
{
	DWORD AudioIndexSize = pAIndex;//音频索引块大小
	*AudioSize = AudioIndexSize - 8;//写音频索引块大小
	*EntriesA = nEntriesInUseA;//写条目数量
	*pOffsetA = BaseOffsetA[mIndexA];//重写基本偏移量
	header.EntryA[mIndexA].Offset = GetFilePos(hFile);//音频主索引条目偏移量
	header.EntryA[mIndexA].Size = AudioIndexSize;//音频主索项条目大小
	header.EntryA[mIndexA].Dur = ChuckCount;//音频主索条目长度
	ChuckCount = 0;
	WriteFile(hFile, &pA[0], AudioIndexSize, NULL, NULL);//写音频索引块
	mIndexA++;//递增音频主索引条目索引
	nEntriesInUseA = 0;
	BaseOffsetA[mIndexA] = GetFilePos(hFile);//赋值下一个基本偏移量
	pAIndex = 32;//移动指针到,第1个条目位置
}

HRESULT CPin1::WriteEnd()//完善AVI输出文件
{
	VIDEOINFOHEADER* pH = (VIDEOINFOHEADER*)VideoMediaType.pbFormat;
	*header.dwScale = (DWORD)pH->AvgTimePerFrame;//采样率分母,视频strh参数
	*header.dwMicroSecPerFrame = (DWORD)(pH->AvgTimePerFrame / 10);//是视频帧持续时间,单位微秒,avih参数
	*header.dwWidth = pH->bmiHeader.biWidth;//图像的宽度
	*header.dwHeight = pH->bmiHeader.biHeight;//图像的高度
	CopyMemory(header.fccHandler, &VideoMediaType.subtype.Data1, 4);//获取FOURCC
	header.rcFrame->left = 0; header.rcFrame->top = 0;
	header.rcFrame->right = (short)pH->bmiHeader.biWidth; header.rcFrame->bottom = (short)pH->bmiHeader.biHeight;//目标矩形
	*header.biWidth = pH->bmiHeader.biWidth;//图像的宽度
	*header.biHeight = pH->bmiHeader.biHeight;//图像的高度
	*header.biBitCount = pH->bmiHeader.biBitCount;//每个像素的位数
	*header.biCompression = pH->bmiHeader.biCompression;//编码方式
	*header.biSizeImage = pH->bmiHeader.biSizeImage;//图像大小
	*header.biXPelsPerMeter = pH->bmiHeader.biXPelsPerMeter;//目标设备的水平分辨率
	*header.biYPelsPerMeter = pH->bmiHeader.biYPelsPerMeter;//目标设备的垂直分辨率
	*header.biClrUsed = pH->bmiHeader.biClrUsed;//颜色数量
	*header.biClrImportant = pH->bmiHeader.biClrImportant;//重要颜色数量

	*header.dwLength = VideoCount;//赋值视频流的长度,单位视频帧,视频strh参数

	WAVEFORMATEX* p = (WAVEFORMATEX*)pCFilter->pCPin2->AudioMediaType.pbFormat;
	CopyMemory(header.fccHandler_A, &pCFilter->pCPin2->AudioMediaType.subtype.Data1, 4);//获取FOURCC,音频strh参数
	if (pCFilter->pCPin2->AudioMediaType.subtype == MEDIASUBTYPE_MP3)//如果是MP3流
	{
		*pCFilter->pCPin1->header.dwRate_A = p->nAvgBytesPerSec;
		*pCFilter->pCPin1->header.cbSize_A = p->cbSize;//附加信息大小
		CopyMemory(pCFilter->pCPin1->header.pApend, (BYTE*)p + sizeof(WAVEFORMATEX), p->cbSize);//复制附加信息
		BYTE* pP = pCFilter->pCPin1->header.pApend;
		pP += p->cbSize;
		CopyMemory(pP, "JUNK", 4); pP += 4;
		DWORD JUNKSize = 56 - p->cbSize;
		CopyMemory(pP, &JUNKSize, 4);
		*pCFilter->pCPin1->header.strf_A_Size += p->cbSize;
		*pCFilter->pCPin1->header.dwStart_A = 0;//流的开始时间
	}
	else//如果是PCM或ALAW流
	{
		*pCFilter->pCPin1->header.dwRate_A = p->nSamplesPerSec;//采样率分子,音频strh参数(采样率分母使用值1,故采样率分子直接等于采样率)
	}

	*pCFilter->pCPin1->header.dwSampleSize_A = p->nBlockAlign;//单个数据样本的大小,音频strh参数
	*header.dwLength_A = AudioCount;//赋值音频流的长度,单位音频帧,音频strh参数

	*pCFilter->pCPin1->header.wFormatTag_A = (WORD)pCFilter->pCPin2->AudioMediaType.subtype.Data1;//音频格式,音频strf参数
	*pCFilter->pCPin1->header.nChannels_A = p->nChannels;//声道数,音频strf参数
	*pCFilter->pCPin1->header.nSamplesPerSec_A = p->nSamplesPerSec;//采样率,音频strf参数
	*pCFilter->pCPin1->header.nAvgBytesPerSec_A = p->nAvgBytesPerSec;//传输率,音频strf参数
	*pCFilter->pCPin1->header.nBlockAlign_A = p->nBlockAlign;//块对齐,音频strf参数
	*pCFilter->pCPin1->header.wBitsPerSample_A = p->wBitsPerSample;//样本位数,音频strf参数

	if (VideoBufferSize % 4)VideoBufferSize = (VideoBufferSize / 4) * 4 + 4;//将缓冲区大小4倍取整
	if (AudioBufferSize % 4)AudioBufferSize = (AudioBufferSize / 4) * 4 + 4;
	*header.strh_dwSuggestedBufferSize = VideoBufferSize;//赋值视频建议的缓冲区大小,视频strh参数
	*header.strh_A_dwSuggestedBufferSize = AudioBufferSize;//赋值音频建议的缓冲区大小,音频strh参数

	LARGE_INTEGER Move;
	Move.QuadPart = 0;
	SetFilePointerEx(hFile, Move, NULL, FILE_BEGIN);//移动文件指针,到开始位置
	WriteFile(hFile, &header, 4642, NULL, NULL);//重写AVI文件头,使赋值参数应用于文件

	CloseHandle(hFile);//关闭输出文件
	hFile = NULL;
	return S_OK;
}

#define EC_VIDEOSTREAMEND EC_USER+1122//自定义”视频流结束“事件通知

STDMETHODIMP CPin1::EndOfStream()
{
	pCFilter->NotifyEvent(EC_VIDEOSTREAMEND, NULL, NULL);//发送自定义“视频流结束”事件通知
	END = TRUE;
	return CBaseInputPin::EndOfStream();
}

CPin2.cpp

#include "DLL.h"

CPin2::CPin2(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName) : CBaseInputPin(NAME("Audio"), pFilter, pFilter, phr, pPinName)
{
	pCFilter = pFilter;
	WriteCompleted = CreateEvent(NULL, TRUE, FALSE, NULL);//手动重置,初始无信号
}

CPin2::~CPin2()//输入引脚析构函数
{

}

HRESULT CPin2::CheckMediaType(const CMediaType *pmt)
{
	if (pmt->majortype == MEDIATYPE_Audio && (pmt->subtype == MEDIASUBTYPE_MP3 || pmt->subtype == MEDIASUBTYPE_PCM || pmt->subtype == MEDIASUBTYPE_ALAW)
		&& pmt->formattype == FORMAT_WaveFormatEx)return S_OK;
	return S_FALSE;
}

HRESULT CPin2::SetMediaType(const CMediaType *pmt)
{
	AudioMediaType = *pmt;
	return CBaseInputPin::SetMediaType(pmt);
}

HRESULT CPin2::Receive(IMediaSample * pSample)//接收函数
{
	if (pCFilter->pCPin1->END)//如果视频流已结束
	{
		pCFilter->pCPin1->WriteAudio(pSample);//写音频样本
		return S_OK;
	}
Agan:
	DWORD dw1 = WaitForSingleObject(WriteCompleted, 0);//检测“写样本完成”信号
	DWORD dw2 = WaitForSingleObject(pCFilter->pCPin1->hStop, 0);//检测“停止”信号
	if (dw2 == WAIT_OBJECT_0)
	{
		return S_FALSE;//如果有“停止”信号,返回S_FALSE以终止流
	}
	if (dw1 != WAIT_OBJECT_0)//没有“写样本完成”信号
	{
		goto Agan;//重新检测(阻塞)
	}
	pCFilter->pCPin1->WriteAudio(pSample);//写音频样本
	if (CurTime < pCFilter->pCPin1->CurTime)//如果音频当前时间小
	{
		ResetEvent(pCFilter->pCPin1->WriteCompleted);//设置视频“写样本完成”无信号
		SetEvent(WriteCompleted);//设置音频“写样本完成”有信号
	}
	else//如果视频当前时间小于或等于音频当前时间
	{
		ResetEvent(WriteCompleted);//设置音频“写样本完成”无信号
		SetEvent(pCFilter->pCPin1->WriteCompleted);//设置视频“写样本完成”有信号
	}
	return S_OK;
}

#define EC_AUDIOSTREAMEND EC_USER+1121//自定义”音频流结束“事件通知

STDMETHODIMP CPin2::EndOfStream()
{
	pCFilter->NotifyEvent(EC_AUDIOSTREAMEND, NULL, NULL);//发送自定义“音频流结束”事件通知
	END = TRUE;
	return CBaseInputPin::EndOfStream();
}

下载本过滤器DLL

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2236910.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用 API 和离线库查询 IP 地址方法详解

目录 一、IP 地址查询能获取哪些信息1.地理位置信息2.网络信息3.网络类型 二、IP 地址查询方法&#xff0c;附代码1.在线查询 IP 地址方法2.使用 API 进行 IP 地址查询3.使用离线库进行 IP 地址查询 互联网监管部门要求公开 IP 归属地&#xff0c;引起了很大热度&#xff0c;但…

机器学习在时间序列预测中的应用与实现——以电力负荷预测为例(附代码)

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 1. 引言 随着数据采集技术的发展&#xff0c;时间序列数据在各个领域中的应用越来越广泛。时间序列预测旨在基于过去的时间数据来…

语音识别ic赋能烤箱,离线对话操控,引领智能厨房新体验

一、智能烤箱产品的行业背景 随着科技的飞速发展&#xff0c;智能家居已经成为现代家庭的新宠。智能烤箱作为智能家居的重要组成部分&#xff0c;正逐渐从高端市场走向普通家庭。消费者对于烤箱的需求不再仅仅局限于基本的烘焙功能&#xff0c;而是更加注重其智能化、便捷化和…

【C++】异常处理机制(对运行时错误的处理)

&#x1f308; 个人主页&#xff1a;谁在夜里看海. &#x1f525; 个人专栏&#xff1a;《C系列》《Linux系列》 ⛰️ 天高地阔&#xff0c;欲往观之。 目录 引言 1.编译器可以处理的错误 2.编译器不能处理的错误 3.传统的错误处理机制 assert终止程序 返回错误码 一、…

Python基础学习-03逻辑分支语句、循环

目录 1、记住逻辑关系 2、逻辑分支语句 3、for-loop循环 4、while-loop 5、break 和 continue 6、本节总结 1、记住逻辑关系 • 逻辑关系 1&#xff09; True&#xff08;真&#xff09; 和 False&#xff08;假&#xff09; 2&#xff09;逻辑关系有 and&#xff08;与…

【Windows】Android Studio 上cmd 换为Powershell 终端

最近在Windows 环境下Android Studio 的Terminal 终端&#xff0c;低版本默认用的是cmd.exe&#xff0c;好多linux 命令不支持&#xff0c;有时候一不小心就记忆错了&#xff1b;干脆直接换成Windows PowerShell 得了。 下载Powershell&#xff1a;https://aka.ms/PSWindows 选…

javascript实现sha512和sha384算法(支持微信小程序),可分多次计算

概述&#xff1a; 本人前端需要实现sha512和sha384计算的功能&#xff0c;最好是能做到分多次计算。 本文所写的代码在现有sha512和sha384的C代码&#xff0c;反复测试对比计算过程参数&#xff0c;成功改造成sha512和sha384的javascript代码&#xff0c;并成功验证好分多次计算…

Pr 视频过渡:沉浸式视频

效果面板/视频过渡/沉浸式视频 Video Transitions/Immersive Video Adobe Premiere Pro 的视频过渡效果中&#xff0c;沉浸式视频 Immersive Video效果组主要用于 VR 视频剪辑之间的过渡。 自动 VR 属性 Auto VR Properties是所有 VR 视频过渡效果的通用选项。 默认勾选&#x…

Ascend C的编程模型

1 并发执行 Ascend C和cudnn相似&#xff0c;都是一种多核心编程的范式。想要了解Ascend C&#xff0c;必须得先掌握这种“多核”是怎么实现得。 多核执行&#xff0c;说白了就是使用CPU/GPU/Ascend的物理多核并发去执行一段流程&#xff0c;一般情况下&#xff0c;可以通过以…

商品,订单风控业务梳理二

订单风控流程 业务风控系统

苍穹外卖05-Redis相关知识点

目录 什么是Redis&#xff1f; redis中的一些常用指令 value的5种常用数据类型 各种数据类型的特点 Redis中数据操作的常用命令 字符串类型常用命令&#xff1a; 哈希类型常用命令 列表操作命令 集合操作命令 有序集合操作命令 通用命令 在java中操作Redis 环境…

一些面试题总结(一)

1、string为什么是不可变的&#xff0c;有什么好处 原因&#xff1a; 1、因为String类下的value数组是用final修饰的&#xff0c;final保证了value一旦被初始化&#xff0c;就不可改变其引用。 2、此外&#xff0c;value数组的访问权限为 private&#xff0c;同时没有提供方…

3.3 软件需求:面对对象分析模型

面对对象分析模型 1、对象2、面对对象的软件开发模型3、用例图建模基础3.1 用例图基本符号参与者用例系统执行关联 3.2 用例建模过程3.3 用例图初步3.4 用例图进阶关联Association泛化Inheritance包含Include扩展Extend示例 1、对象 在现实世界中有意义的&#xff0c;与所要解…

「C/C++」C++标准库 之 #include<exception> 异常处理库

✨博客主页何曾参静谧的博客&#x1f4cc;文章专栏「C/C」C/C程序设计&#x1f4da;全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

嵌入式linux中gpio子系统的开发与实现

大家好,今天主要给大家分享一下,如何使用gpio子系统,来控制对应的引脚电平状态与实现。 第一:linux中gpio子系统描述 gpio0:gpio@fdd60000{compatible = "rockchip,gpio-bank";reg = <0x0 0xfdd60000 0x0 0x100>; interrupts = <GIC_SPI 33 IRQ_TYP…

【主机游戏】艾尔登法环游戏攻略

艾尔登法环&#xff0c;作为一款备受好评但优化问题频发的游戏&#xff0c;就连马斯克都夸过 今天介绍一下这款游戏 https://pan.quark.cn/s/24760186ac0b 角色升级 在《艾尔登法环》中&#xff0c;角色升级需要找到梅琳娜。你可以在关卡前废墟的营地附近&#xff0c;风暴关…

大数据面试题--kafka夺命连环问

1、kafka消息发送的流程&#xff1f; 在消息发送过程中涉及到两个线程&#xff1a;一个是 main 线程和一个 sender 线程。在 main 线程中创建了一个双端队列 RecordAccumulator。main 线程将消息发送给双端队列&#xff0c;sender 线程不断从双端队列 RecordAccumulator 中拉取…

出海企业如何借助云计算平台实现多区域部署?

云计算de小白 如需进一步了解&#xff0c;请单击链接了解有关 Akamai 云计算的更多信息 在本文中我们将告诉大家如何在Linode云计算平台上借助VLAN快速实现多地域部署。 首先我们需要明确一些基本概念和思想&#xff1a; 部署多区域 VLAN 为了在多区域部署中在不同的 VLAN …

W55RP20-EVB-Pico评估板介绍

目录 1 简介 2 硬件资源 2.1 硬件规格 2.2 引脚定义 2.3 工作条件 3 参考资料 3.1 RP2040 数据手册 3.2 原理图 ​编辑 原理图 & 物料清单 & Gerber 文件 3.3 尺寸图&#xff08;单位&#xff1a;mm&#xff09; ​编辑 3.4 认证 3.5 参考例程 4 硬件协…

【机器学习】均方误差根(RMSE:Root Mean Squared Error)

均方误差根&#xff08;Root Mean Squared Error&#xff0c;RMSE&#xff09;是机器学习和统计学中常用的误差度量指标&#xff0c;用于评估预测值与真实值之间的差异。它通常用于回归模型的评价&#xff0c;以衡量模型的预测精度。 RMSE的定义与公式 给定预测值 和实际值 …