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

下载本过滤器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,//过滤器GUID0x2ef49957, 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_LISTDWORD* 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_LISTDWORD* strl_LIST_Size;//char* strl_ch;//strlchar* strh_ch;//strhDWORD* 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;//strfDWORD* 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;//流的FOURCCDWORD* Reserved1, *Reserved2, *Reserved3;//保留struct ENTRY {LONGLONG Offset;//偏移量DWORD Size;//大小DWORD Dur;//长度};ENTRY* Entry;//indx条目数组char* strl_LIST_A;//音频strl_LISTDWORD* strl_LIST_A_Size;//char* strl_A_ch;//strlchar* strh_A_ch;//strhDWORD* 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;//strfDWORD* 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;//大小64char* 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;//流的FOURCCDWORD* 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_LISThdrl_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_LISTstrl_LIST_Size = (DWORD*)&p[92]; *strl_LIST_Size = 2300;// strl_ch = (char*)&p[96]; CopyMemory(strl_ch, "strl", 4);//strlstrh_ch = (char*)&p[100]; CopyMemory(strh_ch, "strh", 4);//strhstrh_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);//strfstrf_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);//附加信息总大小104ApendSize = (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);//流的FOURCCReserved1 = (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_LISTstrl_LIST_A_Size = (DWORD*)&p[2400]; *strl_LIST_A_Size = 2238;//strl_A_ch = (char*)&p[2404]; CopyMemory(strl_A_ch, "strl", 4);//strlstrh_A_ch = (char*)&p[2408]; CopyMemory(strh_A_ch, "strh", 4);//strhstrh_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);//strfstrf_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);//流的FOURCCReserved_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_IUNKNOWNSTDMETHODIMP 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);}elsereturn 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;//必须为0CopyMemory(&pV[pVIndex], &bIndexSubType, 1); pVIndex += 1;//写索引子类型BYTE bIndexType = AVI_INDEX_OF_CHUNKS;//必须为AVI_INDEX_OF_CHUNKSCopyMemory(&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;//必须为0CopyMemory(&pA[pAIndex], &bIndexSubTypeA, 1); pAIndex += 1;//写索引子类型BYTE bIndexTypeA = AVI_INDEX_OF_CHUNKS;//必须为AVI_INDEX_OF_CHUNKSCopyMemory(&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);//RIFFRIFF_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);//AVIXchar ch3[4] = { 'L','I','S','T' };WriteFile(hFile, ch3, 4, NULL, NULL);//LISTmovi_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);//LISTmovi_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);//moviResetEvent(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);//获取FOURCCheader.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.rhkb.cn/news/467962.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniapp实现H5和微信小程序获取当前位置(腾讯地图)

之前的一个老项目&#xff0c;使用 uniapp 的 uni.getLocation 发现H5端定位不准确&#xff0c;比如余杭区会定位到临平区&#xff0c;根据官方文档初步判断是项目的uniapp的版本太低。 我选择的方式不是区更新uniapp的版本&#xff0c;是直接使用高德地图的api获取定位。 1.首…

【WRF理论第七期】WPS预处理

【WRF理论第七期】WPS预处理 运行WPS&#xff08;Running the WPS&#xff09;步骤1&#xff1a;Define model domains with geogrid步骤2&#xff1a;Extracting meteorological fields from GRIB files with ungrib步骤3&#xff1a;Horizontally interpolating meteorologic…

机器学习(一)——基本概念、模型的评估与选择

目录 1 关于2 概念2.1 基础概念2.2 学习过程2.3 预测与评估2.4 标记与分类2.4.1 标记2.4.2 分类 2.5 回归分析2.6 聚类分析2.7 学习类型2.8 泛化能力2.9 统计学概念 3 模型评估与选择3.1 经验误差与过拟合3.2 评估方法3.2.1 留出法3.2.2 交叉验证法3.2.3 自助法3.2.4 调参与最终…

小白docker入门简介

Dockerfile入门使用分享 一、docker是啥二、镜像仓库三、自定义镜像四、动手做机甲玩偶五、帮我做数学题六、计算功能的写法七、咒语翻译器八、放屁九、解决问题 一、docker是啥 最开始我和你一样&#xff0c;围着镜像、容器、docker的名词团团转&#xff0c;其实没那么复杂。…

gitlab无法创建合并请求是所有分支都不显示

点击Merge Requests ------> New merge request 创建新的合并请求时&#xff0c;在Source branch和Target branch中一个分支都不显示 排查思路&#xff1a; 1.怀疑是权限问题。 发现只有我的一个账号出现&#xff0c;检查了账号的权限&#xff0c;尝试了master、develop角色…

macOS15.1及以上系统bug:开发者证书无法打开,钥匙串访问无法打开一直出现图标后立马闪退

团队紧跟苹果最新系统发现bug:今日设备信息如下,希望能带给遇到这个问题的开发者一点帮助。 错误图如下: 点击证书文件后,先出现钥匙串访问图标,后立马闪退消失 中间试过很多方法,都是一样的表现,最后好在解决了,看网上也没有相关的帖子,这里直接写解决办法和导致原因…

python实战案例——爬取A站视频,m3u8格式视频抓取(内含完整代码!)

1、任务目标 目标网站&#xff1a;A站视频&#xff08;https://www.acfun.cn/v/ac40795151&#xff09; 要求&#xff1a;抓取该网址下的视频&#xff0c;将其存入本地&#xff0c;视频如下&#xff1a; 2、网页分析 进入目标网站&#xff0c;打开开发者模式&#xff0c;我们发…

【基于轻量型架构的WEB开发】课程 12.4 页面跳转 Java EE企业级应用开发教程 Spring+SpringMVC+MyBatis

12.4 页面跳转 12.4.1 返回值为void类型的页面跳转 返回值为void类型的页面跳转到默认页面 当Spring MVC方法的返回值为void类型&#xff0c;方法执行后会跳转到默认的页面。默认页面的路径由方法映射路径和视图解析器中的前缀、后缀拼接成&#xff0c;拼接格式为“前缀方法…

濮良贵《机械设计》第十版课后习题答案全解PDF电子版

《机械设计》(第十版)是“十二五”普通高等教育本科国家级规划教材&#xff0c; 是在《机械设计》(第九版)的基础上修订而成的。本次修订主要做了以下几项工作&#xff1a; 1. 内容的适当更新——自本书第九版出版以来&#xff0c; 机械工程及相关领域的新理论、新技术和新标准…

【Unity基础】Unity中如何导入字体?

在Unity中&#xff0c;不能像其他软件一样直接使用字体文件&#xff0c;需要通过FontAssetCreator将其转换成Texture的Asset文件&#xff0c;然后才能使用。 本文介绍了使用FontAssetCreator导入字体的过程&#xff0c;并对其参数设置进行了说明。 Font Asset Creator 是 Uni…

2024年11月8日上海帆软用户大会

2024年11月8日上海帆软用户大会 2024年11月8日&#xff0c;上海成功举办了帆软用户大会&#xff0c;主题为“数字聚力&#xff0c;绽放新机”。大会汇聚了众多行业专家和企业代表&#xff0c;共同探讨数字化转型和商业智能领域的最新趋势和实践。 大会亮点&#xff1a; 专家…

注意力机制的目的:理解语义;编码器嵌入高纬空间计算;注意力得分“得到S*V”;解码器掩码和交叉注意力层用于训练;最终的编码器和输出实现大模型

目录 注意力机制的目的:理解语义中的它是小白兔 词编码器嵌入高纬空间 计算注意力得分“得到S*V” 权重QKV:连接权重 训练阶段使用解码器:翻译后的语句 解码器掩码和交叉注意力层用于训练 最终的编码器和输出实现大模型 Transformer模型中,QKV QKV的作用 举例说明…

纯前端实现在线预览excel文件(插件: LuckyExcel、Luckysheet)

概述 在实际开发中&#xff0c;遇到需要在线预览各种文件的需求&#xff0c;最近遇到在线预览excel文件的需求&#xff0c;在此记录一下&#xff01;本文主要功能实现&#xff0c;用于插件 LuckyExcel &#xff0c;Luckysheet&#xff01;废话不多说&#xff0c;上代码&#xf…

WPF自定义翻页控件

XAML文件如下&#xff1a; <UserControlx:Class"CTMVVMDemo.View.UserControls.DataPager"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://s…

Linux中.NET读取excel组件,不会出现The type initializer for ‘Gdip‘ threw an exception异常

组件&#xff0c;可通过nuget安装&#xff0c;直接搜名字&#xff1a; ExcelDataReader using ConsoleAppReadFileData.Model; using ExcelDataReader; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Task…

qt QColorDialog详解

1、概述 QColorDialog是Qt框架中的一个对话框类&#xff0c;专门用于让用户选择颜色。它提供了一个标准的颜色选择界面&#xff0c;其中包括基本的颜色选择器&#xff08;如调色板和颜色轮&#xff09;、自定义颜色输入区域以及预定义颜色列表。QColorDialog支持RGB、HSV和十六…

使用Python实现音频降噪

在音频处理领域&#xff0c;背景噪声是一个常见的问题。为了提高音频的质量&#xff0c;我们需要对音频进行降噪处理。本文将介绍如何使用 Python 实现音频降噪。 依赖库安装 在开始之前&#xff0c;我们需要安装以下依赖库&#xff1a; pydub&#xff1a;用于音频文件的读取…

【WRF模拟】全过程总结:WPS预处理及WRF运行

【WRF模拟】全过程总结:WPS预处理及WRF运行 1 数据准备1.1 嵌套域设置(Customize domain)-基于QGis中gis4wrf插件1.2 静态地理数据1.2.1 叶面积指数LAI和植被覆盖度Fpar(月尺度)1.2.2 地面反照率(月尺度)1.2.3 土地利用类型+不透水面积1.2.4 数据处理:geotiff→tiff(W…

【react】Redux基础用法

1. Redux基础用法 Redux 是一个用于 JavaScript 应用的状态管理库&#xff0c;它不依赖于任何 UI库&#xff0c;但常用于与 React 框架配合使用。它提供了一种集中式的状态管理方式&#xff0c;将应用的所有状态保存在一个单一的全局 Store&#xff08;存储&#xff09;中&…

DevCheck Pro手机硬件检测工具v5.33

前言 DevCheck Pro是一款手机硬件和操作系统信息检测查看工具&#xff0c;该软件的功能非常强大&#xff0c;为用户提供了系统、硬件、应用程序、相机、网络、电池等一系列信息查看功能 安装环境 [名称]&#xff1a;DevCheckPro [版本]&#xff1a;5.33 [大小]&a…