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


#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,//过滤器GUID0x4c42abce, 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_IUNKNOWNSTDMETHODIMP 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

#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

#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);}elsereturn 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,                       //源的URLMF_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");//查找视频trakLONGLONG MdiaStar = FindMdia(hFile);//查找视频mdia,记录mdia开始位置FindMdhd(hFile);//查找视频mdhdMoveFilePointer(hFile, 20, FILE_CURRENT);//移动文件指针20字节TimeScale = ReadFour(hFile);//获视频取时间刻度分母,分子为1MoveFilePointer(hFile, MdiaStar, FILE_BEGIN);//回到mdia开始位置FindMinf(hFile);//查找视频minfStblStar = FindStbl(hFile);//查找视频stbl,记录stbl开始位置FindStblSub(hFile, "stsd");//查找视频stsdGetVideoParam();//获取视频参数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,获取cttsMoveFilePointer(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不存在,返回0MoveFilePointer(hFile, StblStar, FILE_BEGIN);//回到stbl开始位置Co64Star= FindStblSub(hFile, "co64");//co64起始位置。如果co64 box存在,返回非0值;如果co64 box不存在,返回0for (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");//查找音频trakLONGLONG MdiaStarA = FindMdia(hFileA);//查找音频mdiaFindMdhd(hFileA);//查找音频mdhdMoveFilePointer(hFileA, 20, FILE_CURRENT);//移动文件指针20字节TimeScaleA = ReadFour(hFileA);//获取音频时间刻度MoveFilePointer(hFileA, MdiaStarA, FILE_BEGIN);//回到mdia开始位置FindMinf(hFileA);//查找音频minfStblStarA = FindStbl(hFileA);//查找音频stbl,记录stbl开始位置FindStblSub(hFileA, "stsd");//查找音频stsdGetAudioParam();//获取音频参数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();}elseMessageBox(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);//读取spsBYTE 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

#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;//缓冲区的大小,10MALLOCATOR_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);//将所有单元大小替换为0x00000001hr = 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);//设置同步点标志elsehr = pOutSample->SetSyncPoint(FALSE);hr = pCFilter->pCVideoPin->Deliver(pOutSample);//输出引脚向下游发送样本pOutSample->Release();//释放输出引脚样本star += pCFilter->AvgTimePerFrame;//计算下一个样本的起始时间}}hr = pCFilter->pCVideoPin->DeliverEndOfStream();return 1;
}

CAudioPin.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

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

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

相关文章

MySQL误删数据怎么办?

文章目录 1. 从备份恢复数据2. 通过二进制日志恢复数据3. 使用数据恢复工具4. 利用事务回滚恢复数据5. 预防误删数据的策略总结 在使用MySQL进行数据管理时&#xff0c;误删数据是一个常见且具有高风险的操作。无论是因为操作失误、系统故障&#xff0c;还是不小心执行了删除命…

AAAI2024论文合集解读|Multi-granularity Causal Structure Learning-water-merged

论文标题 Multi-granularity Causal Structure Learning 多粒度因果结构学习 论文链接 Multi-granularity Causal Structure Learning 论文下载 论文作者 Jiaxuan Liang, Jun Wang, Guoxian Yu, Shuyin Xia, Guoyin Wang 内容简介 本文提出了一种新颖的方法&#xff0c;…

python3+TensorFlow 2.x(二) 回归模型

目录 回归算法 1、线性回归 (Linear Regression) 一元线性回归举例 2、非线性回归 3、回归分类 回归算法 回归算法用于预测连续的数值输出。回归分析的目标是建立一个模型&#xff0c;以便根据输入特征预测目标变量&#xff0c;在使用 TensorFlow 2.x 实现线性回归模型时&…

【景区导游——LCA】

题目 代码 #include <bits/stdc.h> using namespace std; using ll long long; const int N 1e5 10; const int M 2 * N; int p[N][18], d[N], a[N]; ll dis[N][18]; //注意这里要开long long int h[N], e[M], ne[M], idx, w[M]; int n, k; void add(int a, int b, …

家政预约小程序11分类展示

目录 1 创建页面2 配置导航菜单3 配置侧边栏选项卡4 配置数据列表5 首页和分类页联动总结 我们现在在首页开发了列表显示服务信息的功能&#xff0c;在点击导航菜单的时候&#xff0c;需要自动跳转到对应的分类&#xff0c;本篇我们介绍一下使用侧边栏选项卡实现分类显示的功能…

CVE-2023-38831 漏洞复现:win10 压缩包挂马攻击剖析

目录 前言 漏洞介绍 漏洞原理 产生条件 影响范围 防御措施 复现步骤 环境准备 具体操作 前言 在网络安全这片没有硝烟的战场上&#xff0c;新型漏洞如同隐匿的暗箭&#xff0c;时刻威胁着我们的数字生活。其中&#xff0c;CVE - 2023 - 38831 这个关联 Win10 压缩包挂…

WPF进阶 | WPF 数据绑定进阶:绑定模式、转换器与验证

WPF进阶 | WPF 数据绑定进阶&#xff1a;绑定模式、转换器与验证 一、前言二、WPF 数据绑定基础回顾2.1 数据绑定的基本概念2.2 数据绑定的基本语法 三、绑定模式3.1 单向绑定&#xff08;One - Way Binding&#xff09;3.2 双向绑定&#xff08;Two - Way Binding&#xff09;…

Java Swing 基础组件详解 [论文投稿-第四届智能系统、通信与计算机网络]

大会官网&#xff1a;www.icisccn.net Java Swing 是一个功能强大的 GUI 工具包&#xff0c;提供了丰富的组件库用于构建跨平台的桌面应用程序。本文将详细讲解 Swing 的基础组件&#xff0c;包括其作用、使用方法以及示例代码&#xff0c;帮助你快速掌握 Swing 的核心知识。 一…

题解 信息学奥赛一本通/AcWing 1118 分成互质组 DFS C++

题目 传送门 AcWing 1118. 分成互质组 - AcWing题库https://www.acwing.com/problem/content/1120/https://www.acwing.com/problem/content/1120/https://www.acwing.com/problem/content/1120/https://www.acwing.com/problem/content/1120/https://www.acwing.com/proble…

论文阅读笔记:VMamba: Visual State Space Model

论文阅读笔记&#xff1a;VMamba: Visual State Space Model 1 背景2 创新点3 方法4 模块4.1 2D选择性扫描模块&#xff08;SS2D&#xff09;4.2 加速VMamba 5 效果5.1 和SOTA方法对比5.2 SS2D和自注意力5.3 有效感受野5.4 扫描模式 论文&#xff1a;https://arxiv.org/pdf/240…

技术总结:FPGA基于GTX+RIFFA架构实现多功能SDI视频转PCIE采集卡设计方案

目录 1、前言工程概述免责声明 3、详细设计方案设计框图SDI 输入设备Gv8601a 均衡器GTX 解串与串化SMPTE SD/HD/3G SDI IP核BT1120转RGBFDMA图像缓存RIFFA用户数据控制RIFFA架构详解Xilinx 7 Series Integrated Block for PCI ExpressRIFFA驱动及其安装QT上位机HDMI输出RGB转BT…

03:Heap代码的分析

Heap代码的分析 1、内存对齐2、Heap_1.c文件代码分析3、Heap_2.c文件代码分析4、Heap_4.c文件代码分析5、Heap_5.c文件代码分析 1、内存对齐 内存对齐的作用是为了CPU更快的读取数据。对齐存储与不对齐存储的情况如下&#xff1a; 计算机读取内存中的数据时是一组一组的读取的…

自动驾驶---苏箐对智驾产品的思考

1 前言 对于更高级别的自动驾驶&#xff0c;很多人都有不同的思考&#xff0c;方案也好&#xff0c;产品也罢。最近在圈内一位知名的自动驾驶专家苏箐发表了他自己对于自动驾驶未来的思考。 苏箐是地平线的副总裁兼首席架构师&#xff0c;同时也是高阶智能驾驶解决方案SuperDri…

Android BitmapShader简洁实现马赛克/高斯模糊(毛玻璃),Kotlin(三)

Android BitmapShader简洁实现马赛克/高斯模糊&#xff08;毛玻璃&#xff09;&#xff0c;Kotlin&#xff08;三&#xff09; 发现&#xff0c;如果把&#xff08;二&#xff09; Android BitmapShader简洁实现马赛克&#xff0c;Kotlin&#xff08;二&#xff09;-CSDN博客 …

【数据结构】 并查集 + 路径压缩与按秩合并 python

目录 前言模板朴素实现路径压缩按秩合并按树高为秩按节点数为秩 总结 前言 并查集的基本实现通常使用森林来表示不同的集合&#xff0c;每个集合用一棵树表示&#xff0c;树的每个节点有一个指向其父节点的指针。 如果一个节点是它自己的父节点&#xff0c;那么它就是该集合的代…

【深度学习入门_机器学习理论】K近邻法(KNN)

本部分主要为机器学习理论入门_K近邻法(KNN)&#xff0c;书籍参考 “ 统计学习方法&#xff08;第二版&#xff09;”。 学习目标&#xff1a; 了解k近邻算法的基本概念、原理、应用&#xff1b;熟悉k近邻算法重要影响要素&#xff1b;熟悉kd树原理与优化应用。 开始本算法之…

深入理解 SQL 中的子查询

文章目录 一、什么是子查询二、子查询的基本语法三、数据准备四、子查询的分类4.1 标量子查询4.2 单行子查询4.3 多行子查询4.4 关联子查询 五、子查询的应用场景5.1 子查询与 WHERE 子句5.2 子查询与 SELECT 子句5.3 子查询与 FROM 子句 六、性能优化与注意事项 本文将深入探讨…

Zookeeper入门部署(单点与集群)

本篇文章基于docker方式部署zookeeper集群&#xff0c;请先安装docker 目录 1. docker初期准备 2.启动zookeeper 2.1 单点部署 2.2 集群部署 3. Linux脚本实现快速切换启动关闭 1. docker初期准备 拉取zookeeper镜像 docker pull zookeeper:3.5.6 如果拉取时间过长&#xf…

【SpringBoot教程】Spring Boot + MySQL + HikariCP 连接池整合教程

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 在前面一篇文章中毛毛张介绍了SpringBoot中数据源与数据库连接池相关概念&#xff0c;今天毛毛张要分享的是关于SpringBoot整合HicariCP连接池相关知识点以及底层源码…