本过滤器读取MP4视频文件,输出视频流和音频流。已验证可读取的文件编码方式,视频有:H264,MP4V,M4S2;音频:AAC。
过滤器信息
过滤器名称:读MP4_3
过滤器GUID:{4C42ABCE-6459-4997-863D-BD969D07A862}
DLL注册函数名:DllRegisterServer
删除注册函数名:DllUnregisterServer
过滤器有2个输出引脚。
输出引脚1标识:Video
输出引脚1媒体类型:
主要类型:MEDIATYPE_Video
子类型:MEDIASUBTYPE_NULL
输出引脚2标识:Audio
输出引脚2媒体类型:
主要类型:MEDIATYPE_Audio
子类型:MEDIASUBTYPE_NULL
过滤器开发信息
过滤器从CBaseFilter派生,实现了IFileSourceFilter接口,其Load方法用于指定要读取的MP4文件,并解析MP4文件(在GetParam函数中)。定义了Sample结构,其中的变量存储样本大小,样本偏移量,关键帧标记,样本解码时间和合成时间之间的差值。根据解析结果,分别创建视频音频DWORD数组,数组大小就是样本的总数量,为每个样本创建一个Sample结构,根据样本大小,偏移量,关键帧标记,样本解码时间和合成时间之间的差值,赋值结构变量,将Sample结构指针按样本顺序存储在DWORD数组中,在本程序中称其为视频样本列表和音频样本列表。视频音频引脚样本媒体类型的判断,使用媒体基础的方法,创建源解析器,创建媒体源,获取演示文稿描述符,获取流描述符,获取流媒体类型处理接口,获取视频和音频流媒体类型,转换为AM_MEDIA_TYPE结构表示的媒体类型,设置给视频音频引脚。在视频引脚,音频引脚Active函数中分别创建视频音频工作线程,在工作线程中建立for循环,从样本列表中读取每个样本的偏移量,大小等参数,根据参数从MP4文件读取样本数据,从输出引脚获取空的样本,使用样本数据填充输出引脚样本,指定输出引脚样本的时间戳和同步点标记,向下游发送。
过滤器实现了IMediaSeeking接口,用于调整播放位置。在调整播放位置时,IMediaSeeking接口的SetPositions方法被调用,方法提供新的开始位置值,根据该值获取与其接近的关键帧序号;"定位"后,最好从关键帧开始,如果从非关键帧开始,解码器将会输出几副"错误"图像,这对于对视频要求较高的场合是不合适的。向视频音频工作线程发送"定位"信号,线程收到信号后,跳出for循环,重新进入新的for循环,新的for循环从新开始位置值开始。
过滤器DLL的全部代码
DLL.h
cpp
#ifndef DLL_FILE
#define DLL_FILE
#include "strmbase10.h"//过滤器基类定义文件
#if _DEBUG
#pragma comment(lib, "strmbasd10.lib")//过滤器基类实现文件调试版本
#else
#pragma comment(lib, "strmbase10.lib")//过滤器基类实现文件发布版本
#endif
// {4C42ABCE-6459-4997-863D-BD969D07A862}
DEFINE_GUID(CLSID_MP4Reader,//过滤器GUID
0x4c42abce, 0x6459, 0x4997, 0x86, 0x3d, 0xbd, 0x96, 0x9d, 0x7, 0xa8, 0x62);
#include "wmcodecdsp.h"
#include "mfapi.h"
#include "mfidl.h"
#pragma comment(lib, "mfplat.lib")
struct Sample//样本参数结构
{
UINT Size;//样本大小
ULONGLONG Offset;//样本偏移量
BOOL KeyFrame = FALSE;//为TRUE时,为关键帧
LONGLONG ctts = 0; //样本解码时间和合成时间之间的差值
};
class CAudioPin;
class CFilter;
class CVideoPin : public CBaseOutputPin
{
friend class CAudioPin;
friend class CFilter;
public:
CVideoPin(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName);
~CVideoPin();
HRESULT CheckMediaType(const CMediaType *pmt);
BOOL HasSet = FALSE;
HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
HRESULT SetMediaType(const CMediaType *pmt);
HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES * pProperties);
HRESULT BreakConnect();
HRESULT Active(void);
HRESULT Inactive(void);
CFilter *pCFilter = NULL;
BYTE* pSHeader = NULL;
UINT cbSHeader = 0;
GUID VideoSubType;//视频子类型GUID
};
class CAudioPin : public CBaseOutputPin
{
friend class CVideoPin;
friend class CFilter;
public:
CAudioPin(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName);
~CAudioPin();
HRESULT CheckMediaType(const CMediaType *pmt);
BOOL HasSet = FALSE;
HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
HRESULT SetMediaType(const CMediaType *pmt);
HRESULT DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES * pProperties);
HRESULT BreakConnect();
HRESULT Active(void);
CFilter *pCFilter = NULL;
};
class CFilter : public CCritSec, public CBaseFilter, public IFileSourceFilter, public IMediaSeeking
{
friend class CVideoPin;
friend class CAudioPin;
public:
CFilter(TCHAR* pName, LPUNKNOWN pUnk, HRESULT* hr);
~CFilter();
CBasePin* GetPin(int n);
int GetPinCount();
static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
DECLARE_IUNKNOWN
STDMETHODIMP NonDelegatingQueryInterface(REFIID iid, void ** ppv);
STDMETHODIMP Load(LPCOLESTR lpwszFileName, const AM_MEDIA_TYPE *pmt);
STDMETHODIMP GetCurFile(LPOLESTR * ppszFileName, AM_MEDIA_TYPE *pmt);
HANDLE hSeek;//"视频定位"事件句柄
HANDLE hSeekA;//"音频定位"事件句柄
UINT ListCount = 0;
DWORD* pSampleList = NULL;//视频样本参数列表
UINT SeekFrame;//视频Seek帧索引
UINT SeekANu;//音频Seek起始样本索引
UINT FindKeyFrame(UINT Frame);//查找关键帧
UINT* pTimeList = NULL;
UINT GetCtts(LONGLONG pos, UINT uS);
IMFMediaType* pVideoType = NULL;//视频媒体类型
IMFMediaType* pAudioType = NULL;//音频媒体类型
HRESULT GetMediaType();//确定视频音频输出媒体类型
void GetParam();//从MP4文件获取参数
UINT TimeScale;//分子为1的视频时间刻度分母,单位秒。即视频使用 1/TimeScale 秒作为时间单位
LONGLONG DUR;//100纳秒单位的视频时长
LONGLONG CUR;//100纳秒单位的视频当前时间
LONGLONG AvgTimePerFrame;//每帧持续时间,单位100纳秒
WORD width;//视频宽,单位像素
WORD height;//视频高,单位像素
WORD bit_depth;//位深度
void GetVideoParam();//获取视频参数
BYTE ProFile;//配置文件
BYTE AVCLevel;//编码级别
WORD SpsNu;//sps大小
BYTE* SPS = NULL;
WORD PpsNu; //pps大小
BYTE* PPS = NULL;
void GetAvcCParam();//获取avcC参数
LONGLONG StblStar;//stbl起始位置
LONGLONG SttsStar;//stts起始位置
LONGLONG StssStar;//stss起始位置
LONGLONG CttsStar;//ctts起始位置
LONGLONG StscStar;//stsc起始位置
LONGLONG StszStar;//stsz起始位置
LONGLONG StcoStar;//stco起始位置
LONGLONG Co64Star;//co64起始位置
UINT StscNu;//stsc条目数
UINT nFrame;//视频帧数量
HANDLE hFile = NULL;
HANDLE hFileA = NULL;
UINT TimeScaleA;//分子为1的音频时间刻度分母,单位秒。即音频使用 1/TimeScaleA 秒作为时间单位
LONGLONG DUR_A;//100纳秒单位的音频时长
LONGLONG CUR_A;//100纳秒单位的音频当前时间
WORD nChannels;//声道数
WORD wBitsPerSample;//音频样本位数
UINT nSamplesPerSec;//音频采样率
UINT nAvgBytesPerSec;//音频传输率
UINT nFrameA;//音频样本数量
LONGLONG AvgTimeA;//单个样本的时长,单位100纳秒
void GetAudioParam();//获取音频参数
LONGLONG StblStarA;//音频stbl起始位置
LONGLONG SttsStarA;//音频stts起始位置
LONGLONG StscStarA;//音频stsc起始位置
LONGLONG StszStarA;//音频stsz起始位置
LONGLONG StcoStarA;//音频stco起始位置
LONGLONG Co64StarA;//音频co64起始位置
UINT ListCountA = 0;
DWORD* pSampleListA = NULL;
CVideoPin* pCVideoPin = NULL;//视频引脚指针
CAudioPin* pCAudioPin = NULL;//音频引脚指针
WCHAR* m_pFileName = NULL;//要读取的MP4视频文件路径
HANDLE hRun;//"运行"事件句柄
DWORD m_dwSeekingCaps = AM_SEEKING_CanSeekForwards | AM_SEEKING_CanSeekBackwards | AM_SEEKING_CanSeekAbsolute | AM_SEEKING_CanGetStopPos | AM_SEEKING_CanGetDuration;
HRESULT STDMETHODCALLTYPE CheckCapabilities(DWORD *pCapabilities);
HRESULT STDMETHODCALLTYPE ConvertTimeFormat(LONGLONG *pTarget, const GUID *pTargetFormat, LONGLONG Source, const GUID *pSourceFormat);
HRESULT STDMETHODCALLTYPE GetAvailable(LONGLONG *pEarliest, LONGLONG *pLatest);
HRESULT STDMETHODCALLTYPE GetCapabilities(DWORD *pCapabilities);
HRESULT STDMETHODCALLTYPE GetCurrentPosition(LONGLONG *pCurrent);
HRESULT STDMETHODCALLTYPE GetDuration(LONGLONG *pDuration);
HRESULT STDMETHODCALLTYPE GetPositions(LONGLONG *pCurrent, LONGLONG *pStop);
HRESULT STDMETHODCALLTYPE GetPreroll(LONGLONG *pllPreroll);
HRESULT STDMETHODCALLTYPE GetRate(double *pdRate);
HRESULT STDMETHODCALLTYPE GetStopPosition(LONGLONG *pStop);
HRESULT STDMETHODCALLTYPE GetTimeFormat(GUID *pFormat);
HRESULT STDMETHODCALLTYPE IsFormatSupported(const GUID *pFormat);
HRESULT STDMETHODCALLTYPE IsUsingTimeFormat(const GUID *pFormat);
HRESULT STDMETHODCALLTYPE QueryPreferredFormat(GUID *pFormat);
HRESULT STDMETHODCALLTYPE SetPositions(LONGLONG *pCurrent, DWORD dwCurrentFlags, LONGLONG *pStop, DWORD dwStopFlags);
HRESULT STDMETHODCALLTYPE SetRate(double dRate);
HRESULT STDMETHODCALLTYPE SetTimeFormat(const GUID *pFormat);
};
template <class T> void SafeRelease(T** ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
BYTE ReadOne(HANDLE hFile);//读1个字节
WORD ReadTwo(HANDLE hFile);//读2个字节
UINT ReadThree(HANDLE hFile);//读3个字节
UINT ReadFour(HANDLE hFile);//读4个字节
UINT Read4Return(HANDLE hFile, LONGLONG& pos);//在指定位置读4个字节,并返回到文件原来位置。参数2加4字节
ULONGLONG Read8Return(HANDLE hFile, LONGLONG& pos);//在指定位置读8个字节,并返回到文件原来位置。参数2加8字节
BOOL ReadAtPos(HANDLE hFile, LONGLONG pos, UINT size, BYTE* pBuffer);//在参数2指定位置,读参数3指定字节
HRESULT MoveFilePointer(HANDLE hFile, LONGLONG size, int flag);//移动文件指针
UINT FindBox(HANDLE hFile, char* pBoxName);//读取BOX大小和名称
BOOL IsEnd(HANDLE hFile);//如果到文件末尾,返回TRUE
LONGLONG GetFilePos(HANDLE hFile);//获取文件当前位置
void GetHdlrType(HANDLE hFile, char* pch);//获取类型,vide或soun
LONGLONG FindTrak(HANDLE hFile, char* pCh);//移动文件指针到符合要求的trak开始位置,返回trak开始位置值。参数2,指定trak类型,视频或音频
LONGLONG FindMdia(HANDLE hFile);//文件指针移动到mdia开始位置,返回mdia开始位置值。查找的起始位置为trak的开始位置
LONGLONG FindMdhd(HANDLE hFile);//文件指针移动到mdhd开始位置,返回mdhd开始位置值。查找的起始位置为mdia的开始位置
LONGLONG FindHdlr(HANDLE hFile);//文件指针移动到hdlr开始位置,返回hdlr开始位置值。查找的起始位置为mdia的开始位置
LONGLONG FindMinf(HANDLE hFile);//文件指针移动到minf开始位置,返回minf开始位置值。查找的起始位置为mdia的开始位置
LONGLONG FindStbl(HANDLE hFile);//文件指针移动到stbl开始位置,返回stbl开始位置值。查找的起始位置为minf的开始位置
LONGLONG FindStblSub(HANDLE hFile, char* pch);//查找stbl的子box。文件指针移动到子box开始位置,返回子box开始位置值。查找的起始位置为stbl的开始位置
#endif //DLL_FILE
DLL.cpp
cpp
#include "DLL.h"
const REGPINTYPES Pin1Type =
{
&MEDIATYPE_Video, //主要类型
&MEDIASUBTYPE_NULL //子类型
};
const REGPINTYPES Pin2Type =
{
&MEDIATYPE_Audio, //主要类型
&MEDIASUBTYPE_NULL //子类型
};
const AMOVIESETUP_PIN sudPins[] = //引脚信息
{
{
L"Video", //引脚名称
FALSE, //渲染引脚
TRUE, //输出引脚
FALSE, //具有该引脚的零个实例
FALSE, //可以创建一个以上引脚的实例
&CLSID_NULL, //该引脚连接的过滤器的类标识
NULL, //该引脚连接的引脚名称
1, //引脚支持的媒体类型数
&Pin1Type //媒体类型信息
},
{
L"Audio", //引脚名称
FALSE, //渲染引脚
TRUE, //输出引脚
FALSE, //具有该引脚的零个实例
FALSE, //可以创建一个以上引脚的实例
&CLSID_NULL, //该引脚连接的过滤器的类标识
NULL, //该引脚连接的引脚名称
1, //引脚支持的媒体类型数
&Pin2Type //媒体类型信息
}
};
const AMOVIESETUP_FILTER MP4Reader = //过滤器的注册信息
{
&CLSID_MP4Reader, //过滤器的类标识
L"读MP4_3", //过滤器的名称
MERIT_DO_NOT_USE, //过滤器优先值
2, //引脚数量
sudPins //引脚信息
};
CFactoryTemplate g_Templates[] = //类工厂模板数组
{
{
L"读MP4_3", //对象(这里为过滤器)名称
&CLSID_MP4Reader, //对象CLSID的指针
CFilter::CreateInstance, //创建对象实例的函数的指针
NULL, //指向从DLL入口点调用的函数的指针
&MP4Reader //指向AMOVIESETUP_FILTER结构的指针
}
};
int g_cTemplates = 1;//模板数组大小
STDAPI DllRegisterServer()//注册DLL
{
return AMovieDllRegisterServer2(TRUE);
}
STDAPI DllUnregisterServer()//删除DLL注册
{
return AMovieDllRegisterServer2(FALSE);
}
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved);
}
CFilter.cpp
cpp
#include "DLL.h"
#include "strsafe.h"
CFilter::CFilter(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr) : CBaseFilter(NAME("读MP4_3"), pUnk, this, CLSID_MP4Reader)
{
HRESULT hr = MFStartup(MF_VERSION);//初始化媒体基础
if (hr != S_OK)
{
MessageBox(NULL, L"初始化媒体基础失败", L"读MP4_3", MB_OK); return;
}
pCVideoPin = new CVideoPin(this, phr, L"Video");//创建视频引脚
pCAudioPin = new CAudioPin(this, phr, L"Audio");//创建音频引脚
hRun = CreateEvent(NULL, TRUE, FALSE, NULL);//创建"运行"事件,手动重置,初始无信号
hSeek= CreateEvent(NULL, FALSE, FALSE, NULL);//创建"视频定位"事件,自动重置,初始无信号
hSeekA = CreateEvent(NULL, FALSE, FALSE, NULL);//创建"音频定位"事件,自动重置,初始无信号
}
CFilter::~CFilter()
{
SafeRelease(&pVideoType); SafeRelease(&pAudioType);//释放媒体类型
MFShutdown();//关闭媒体基础
if (m_pFileName)delete[] m_pFileName;
if (SPS)delete[] SPS;
if (PPS)delete[] PPS;
CloseHandle(hFile); CloseHandle(hFileA); CloseHandle(hRun); CloseHandle(hSeek); CloseHandle(hSeekA);
for (UINT i = 0; i < ListCount; i++)//删除视频样本列表中所有对象
{
Sample* pS = (Sample*)pSampleList[i]; delete pS;
}
if (pSampleList)delete[] pSampleList;//删除视频样本列表
for (UINT i = 0; i < ListCountA; i++)//删除音频样本列表中所有对象
{
Sample* pS = (Sample*)pSampleListA[i]; delete pS;
}
if (pSampleListA)delete[] pSampleListA;//删除音频样本列表
}
CBasePin *CFilter::GetPin(int n)
{
if (n == 0)return pCVideoPin;
if (n == 1)return pCAudioPin;
return NULL;
}
int CFilter::GetPinCount()
{
return 2;
}
CUnknown * WINAPI CFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr)
{
return new CFilter(NAME("读MP4_3"), pUnk, phr);
}
STDMETHODIMP CFilter::NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
if (riid == IID_IFileSourceFilter)
{
return GetInterface(static_cast<IFileSourceFilter*>(this), ppv);
}
else if (riid == IID_IMediaSeeking)
{
return GetInterface(static_cast<IMediaSeeking*>(this), ppv);
}
else
return CBaseFilter::NonDelegatingQueryInterface(riid, ppv);
}
STDMETHODIMP CFilter::Load(LPCOLESTR lpwszFileName, const AM_MEDIA_TYPE *pmt)
{
CheckPointer(lpwszFileName, E_POINTER);
int len = lstrlenW(lpwszFileName) + 1;
m_pFileName = new WCHAR[len];
if (m_pFileName != NULL)
CopyMemory(m_pFileName, lpwszFileName, len*sizeof(WCHAR));
wchar_t ExName[5] = { m_pFileName[len - 5],m_pFileName[len - 4],m_pFileName[len - 3],m_pFileName[len - 2],0 };
if (wcscmp(ExName, L".mp4") != 0)//如果不是MP4文件
{
delete[] m_pFileName; m_pFileName = NULL;
return VFW_E_INVALID_FILE_FORMAT;//设置文件名失败
}
GetParam();//从MP4文件获取参数
return S_OK;
}
STDMETHODIMP CFilter::GetCurFile(LPOLESTR * ppszFileName, AM_MEDIA_TYPE *pmt)
{
CheckPointer(ppszFileName, E_POINTER);
*ppszFileName = NULL;
if (m_pFileName != NULL)
{
DWORD n = sizeof(WCHAR)*(1 + lstrlenW(m_pFileName));
*ppszFileName = (LPOLESTR)CoTaskMemAlloc(n);
if (*ppszFileName != NULL)CopyMemory(*ppszFileName, m_pFileName, n);
}
return S_OK;
}
UINT CFilter::GetCtts(LONGLONG pos, UINT uS)//从ctts条目,初始化时间列表
{
MoveFilePointer(hFile, pos, FILE_BEGIN);//移动文件指针到ctts条目数位置
UINT CttsNu = ReadFour(hFile);//获取ctts条目数
pTimeList = new UINT[uS];//创建时间列表,类型UINT数组
int index = 0;
for (UINT i = 0; i < CttsNu; i++)
{
UINT SampleNu = ReadFour(hFile); UINT Time = ReadFour(hFile);
for (UINT j = 0; j < SampleNu; j++)
{
pTimeList[index] = Time;
index++;
}
}
return CttsNu;
}
HRESULT CFilter::GetMediaType()//确定视频音频输出媒体类型
{
SafeRelease(&pVideoType); SafeRelease(&pAudioType);//释放媒体类型。如果多次指定MP4文件,必须释放上一次获取的媒体类型
IMFSourceResolver* pSourceResolver = NULL;//源解析器接口
HRESULT hr = MFCreateSourceResolver(&pSourceResolver);//创建源解析器
MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
IUnknown* pSource = NULL;//媒体源IUnknown接口
if (SUCCEEDED(hr))
{
hr = pSourceResolver->CreateObjectFromURL(//从URL创建媒体源
m_pFileName, //源的URL
MF_RESOLUTION_MEDIASOURCE, //创建源对象。
NULL, //可选属性存储。
&ObjectType, //接收创建的对象类型。
&pSource //接收指向媒体源的指针
);
}
SafeRelease(&pSourceResolver);//释放源解析器
IMFMediaSource* pMFMediaSource = NULL;//媒体源接口
if (SUCCEEDED(hr))
{
hr = pSource->QueryInterface(IID_PPV_ARGS(&pMFMediaSource));//获取媒体源接口
}
SafeRelease(&pSource);//释放媒体源IUnknown接口
IMFPresentationDescriptor* pSourceD = NULL;//演示文稿描述符
if (SUCCEEDED(hr))
{
hr = pMFMediaSource->CreatePresentationDescriptor(&pSourceD);//获取演示文稿描述符
}
SafeRelease(&pMFMediaSource);//释放媒体源
IMFStreamDescriptor* pStreamD1 = NULL;//流1描述符
if (SUCCEEDED(hr))
{
BOOL Selected;
hr = pSourceD->GetStreamDescriptorByIndex(0, &Selected, &pStreamD1);//获取流1描述符
}
IMFStreamDescriptor* pStreamD2 = NULL;//流2描述符
if (SUCCEEDED(hr))
{
BOOL Selected;
hr = pSourceD->GetStreamDescriptorByIndex(1, &Selected, &pStreamD2);//获取流2描述符
}
SafeRelease(&pSourceD);//释放演示文稿描述符
IMFMediaTypeHandler* pHandle1 = NULL;//流1媒体类型处理器
if (SUCCEEDED(hr))
{
hr = pStreamD1->GetMediaTypeHandler(&pHandle1);//获取流1媒体类型处理器
}
SafeRelease(&pStreamD1);//释放流1描述符
IMFMediaTypeHandler* pHandle2 = NULL;//流2媒体类型处理器
if (SUCCEEDED(hr))
{
hr = pStreamD2->GetMediaTypeHandler(&pHandle2);//获取流2媒体类型处理器
}
SafeRelease(&pStreamD2);//释放流2描述符
if (SUCCEEDED(hr))
{
GUID guid;
hr = pHandle1->GetMajorType(&guid);//获取流1主要类型
if (SUCCEEDED(hr))
{
if (guid == MEDIATYPE_Video)//如果是视频
{
hr = pHandle1->GetCurrentMediaType(&pVideoType);//获取视频流的媒体类型
}
else if (guid == MEDIATYPE_Audio)//如果是音频
{
hr = pHandle1->GetCurrentMediaType(&pAudioType);//获取音频流的媒体类型
}
}
}
SafeRelease(&pHandle1);//释放流1媒体类型处理器
if (SUCCEEDED(hr))
{
GUID guid;
hr = pHandle2->GetMajorType(&guid);//获取流2主要类型
if (SUCCEEDED(hr))
{
if (guid == MEDIATYPE_Video)//如果是视频
{
hr = pHandle2->GetCurrentMediaType(&pVideoType);//获取视频流的媒体类型
}
else if (guid == MEDIATYPE_Audio)//如果是音频
{
hr = pHandle2->GetCurrentMediaType(&pAudioType);//获取音频流的媒体类型
}
}
}
SafeRelease(&pHandle2);//释放流2媒体类型处理器
return hr;
}
void CFilter::GetParam()//从MP4文件获取参数
{
CloseHandle(hFile);//如果多次指定MP4文件,需关闭上次文件
hFile = CreateFile(m_pFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);//打开MP4文件
if (hFile == INVALID_HANDLE_VALUE)
{
MessageBox(0, L"打开文件失败", L"读MP4_3", MB_OK);return;
}
GetMediaType();//确定视频音频输出媒体类型
FindTrak(hFile, "vide");//查找视频trak
LONGLONG MdiaStar = FindMdia(hFile);//查找视频mdia,记录mdia开始位置
FindMdhd(hFile);//查找视频mdhd
MoveFilePointer(hFile, 20, FILE_CURRENT);//移动文件指针20字节
TimeScale = ReadFour(hFile);//获视频取时间刻度分母,分子为1
MoveFilePointer(hFile, MdiaStar, FILE_BEGIN);//回到mdia开始位置
FindMinf(hFile);//查找视频minf
StblStar = FindStbl(hFile);//查找视频stbl,记录stbl开始位置
FindStblSub(hFile, "stsd");//查找视频stsd
GetVideoParam();//获取视频参数
MoveFilePointer(hFile, StblStar, FILE_BEGIN);//回到视频stbl开始位置
SttsStar = FindStblSub(hFile, "stts");//stts起始位置
MoveFilePointer(hFile, SttsStar + 12, FILE_BEGIN);//移动到stts条目数位置
UINT SttsEnter = ReadFour(hFile);//获取条目数量
UINT uL;//单个样本的时长,单位100纳秒
nFrame = 0;//视频样本总数量
for (UINT i = 0; i < SttsEnter; i++)//读取每个条目
{
UINT uS = ReadFour(hFile); uL = ReadFour(hFile); nFrame += uS;
}
AvgTimePerFrame = (LONGLONG)((float)uL / (float)TimeScale * (float)10000000);//计算单个样本的时长,单位100纳秒
DUR = AvgTimePerFrame*nFrame;//100纳秒单位的视频时长
MoveFilePointer(hFile, StblStar, FILE_BEGIN);//回到stbl开始位置
StssStar = FindStblSub(hFile, "stss");//stss起始位置
MoveFilePointer(hFile, StblStar, FILE_BEGIN);//回到stbl开始位置
CttsStar = 0;
CttsStar = FindStblSub(hFile, "ctts");//ctts起始位置
UINT CttsNu;
if (CttsStar)CttsNu = GetCtts(CttsStar + 12, nFrame);//如果有ctts box,获取ctts
MoveFilePointer(hFile, StblStar, FILE_BEGIN);//回到stbl开始位置
StscStar = FindStblSub(hFile, "stsc");//stsc起始位置
MoveFilePointer(hFile, StblStar, FILE_BEGIN);//回到stbl开始位置
StszStar = FindStblSub(hFile, "stsz");//stsz起始位置
MoveFilePointer(hFile, StblStar, FILE_BEGIN);//回到stbl开始位置
StcoStar = FindStblSub(hFile, "stco");//stco起始位置。如果stco box存在,返回非0值;如果stco box不存在,返回0
MoveFilePointer(hFile, StblStar, FILE_BEGIN);//回到stbl开始位置
Co64Star= FindStblSub(hFile, "co64");//co64起始位置。如果co64 box存在,返回非0值;如果co64 box不存在,返回0
for (UINT i = 0; i < ListCount; i++)//删除列表中所有对象。为过滤器指定读取文件可能多次,须删除原来的列表中的元素
{
Sample* pS = (Sample*)pSampleList[i]; delete pS;
}
if(pSampleList)delete[] pSampleList;//删除视频样本列表
pSampleList = new DWORD[nFrame]; ListCount = nFrame;//创建视频样本列表
{
UINT index = 0;//样本索引
LONGLONG stss_enter_star = StssStar + 16;//stss条目位置
LONGLONG stco_enter_star = StcoStar + 16;//stco条目位置
LONGLONG co64_enter_star = Co64Star + 16;//co64条目位置
LONGLONG stsz_enter_star = StszStar + 20;//stsz条目位置
UINT KeyFrame = Read4Return(hFile, stss_enter_star);//从stss条目中读取第一个关键帧序号,并将文件指针返回到原来的位置
MoveFilePointer(hFile, StscStar + 12, FILE_BEGIN);//移动文件指针到,stsc条目数位置
UINT StscNu = ReadFour(hFile);//获取stsc条目数
UINT First = ReadFour(hFile);//起始块序号
UINT NextFirst;//下一个条目起始块序号
UINT Nu;
for (UINT i = 0; i < StscNu; i++)//读取stsc每个条目
{
UINT nSample = ReadFour(hFile); //块中样本数量
UINT id = ReadFour(hFile); //stsd序号
if (i != StscNu - 1)//如果不是stsc最后一个条目
{
NextFirst = ReadFour(hFile);//读取下一个条目的起始块序号
Nu = NextFirst - First;
First = NextFirst;
}
else//如果是stsc最后一个条目
{
Nu = 1;
}
if (StscNu == 1)nSample = ListCount;//如果stsc只有1个条目,样本数量为全部样本数量
for (UINT j = 0; j < Nu; j++)//读取stco每个条目
{
ULONGLONG Offset;
if (StcoStar)//有stco
{
Offset = Read4Return(hFile, stco_enter_star);//从stco条目中读取块偏移量,并将文件指针返回到原来的位置
}
else//有co64
{
Offset = Read8Return(hFile, co64_enter_star);//从co64条目中读取块偏移量,并将文件指针返回到原来的位置
}
ULONGLONG NextOffset;//下一个偏移量
for (UINT k = 0; k < nSample; k++)//读取stsz每个条目
{
UINT size = Read4Return(hFile, stsz_enter_star);//从stsz条目中读取样本大小,并将文件指针返回到原来的位置
ULONGLONG offset;
if (k == 0)offset = Offset;
else offset = NextOffset;
NextOffset = offset + size;
BOOL KEY = FALSE;
if (KeyFrame == index+1)//如果是关键帧
{
KEY = TRUE;
KeyFrame = Read4Return(hFile, stss_enter_star);//获取下一个关键帧序号
}
Sample* pSample = new Sample();//创建Sample结构对象
pSample->Size = size;//样本大小
pSample->Offset = offset;//样本在文件中的偏移量
if (KEY)pSample->KeyFrame = TRUE;//值TRUE,标记关键帧
if (CttsStar)pSample->ctts = (LONGLONG)((float)pTimeList[index]*(float)10000000/(float)TimeScale);//如果存在ctts box,赋值解码时间和合成时间之间的差值
pSampleList[index] = (DWORD)pSample;//将Sample结构对象地址,存储在视频样本列表中
index++;
}
}
}
if (CttsStar)delete[] pTimeList;//如果存在ctts box,删除时间列表
}
CloseHandle(hFileA);
hFileA = CreateFile(m_pFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);//打开MP4文件
if (hFile == INVALID_HANDLE_VALUE)
{
MessageBox(0, L"打开文件失败", L"读MP4_3", MB_OK); return;
}
FindTrak(hFileA, "soun");//查找音频trak
LONGLONG MdiaStarA = FindMdia(hFileA);//查找音频mdia
FindMdhd(hFileA);//查找音频mdhd
MoveFilePointer(hFileA, 20, FILE_CURRENT);//移动文件指针20字节
TimeScaleA = ReadFour(hFileA);//获取音频时间刻度
MoveFilePointer(hFileA, MdiaStarA, FILE_BEGIN);//回到mdia开始位置
FindMinf(hFileA);//查找音频minf
StblStarA = FindStbl(hFileA);//查找音频stbl,记录stbl开始位置
FindStblSub(hFileA, "stsd");//查找音频stsd
GetAudioParam();//获取音频参数
MoveFilePointer(hFileA, StblStarA, FILE_BEGIN);//回到音频stbl开始位置
SttsStarA = FindStblSub(hFileA, "stts");//stts起始位置
MoveFilePointer(hFile, SttsStarA + 12, FILE_BEGIN);//移动到stts条目数位置
UINT SttsEnterA = ReadFour(hFile);//获取条目数量
UINT uLA;//单个样本的时长,单位100纳秒
nFrameA = 0;//音频样本数量
for (UINT i = 0; i < SttsEnterA; i++)//读取每个条目
{
UINT uS = ReadFour(hFile); uLA = ReadFour(hFile); nFrameA += uS;
}
AvgTimeA = (LONGLONG)((float)uLA * (float)10000000 / (float)TimeScaleA);//获取单个样本的时长,单位100纳秒
DUR_A = AvgTimeA*nFrameA;//100纳秒单位的音频时长
MoveFilePointer(hFileA, StblStarA, FILE_BEGIN);//回到stbl开始位置
StscStarA = FindStblSub(hFileA, "stsc");//stsc起始位置
MoveFilePointer(hFileA, StblStarA, FILE_BEGIN);//回到stbl开始位置
StszStarA = FindStblSub(hFileA, "stsz");//stsz起始位置
MoveFilePointer(hFileA, StblStarA, FILE_BEGIN);//回到stbl开始位置
StcoStarA = FindStblSub(hFileA, "stco");//stco起始位置
MoveFilePointer(hFileA, StblStarA, FILE_BEGIN);//回到stbl开始位置
Co64StarA = FindStblSub(hFileA, "co64");//co64起始位置
for (UINT i = 0; i < ListCountA; i++)//删除音频样本列表中所有对象。为过滤器指定读取文件可能多次,须删除原来的列表中的元素
{
Sample* pS = (Sample*)pSampleListA[i]; delete pS;
}
if (pSampleListA)delete[] pSampleListA;//删除音频样本列表
pSampleListA = new DWORD[nFrameA]; ListCountA = nFrameA;
{
ULONGLONG AudioDataSize = 0;//记录音频数据大小
UINT indexA = 0;//样本索引
LONGLONG stco_enter_starA = StcoStarA + 16;//stco条目位置
LONGLONG co64_enter_starA = Co64StarA + 16;//co64条目位置
LONGLONG stsz_enter_starA = StszStarA + 20;//stsz条目位置
MoveFilePointer(hFileA, StscStarA + 12, FILE_BEGIN);//移动文件指针到,stsc条目数位置
UINT StscNu = ReadFour(hFileA);//获取stsc条目数
UINT First = ReadFour(hFileA);//起始块序号
UINT NextFirst;//下一个条目起始块序号
UINT Nu;
for (UINT i = 0; i < StscNu; i++)//读取stsc每个条目
{
UINT nSample = ReadFour(hFileA); //块中样本数量
UINT id = ReadFour(hFileA); //stsd序号
if (i != StscNu - 1)//如果不是stsc最后一个条目
{
NextFirst = ReadFour(hFileA);//读取下一个条目的起始块序号
Nu = NextFirst - First;
First = NextFirst;
}
else//如果是stsc最后一个条目
{
Nu = 1;
}
if (StscNu == 1)nSample = ListCountA;//如果stsc只有1个条目,样本数量为全部样本数量
for (UINT j = 0; j < Nu; j++)//读取stco每个条目
{
ULONGLONG Offset;
if (StcoStarA)//有stco
{
Offset = Read4Return(hFileA, stco_enter_starA);//从stco条目中读取块偏移量,并将文件指针返回到原来的位置
}
else//有co64
{
Offset = Read8Return(hFileA, co64_enter_starA);//从co64条目中读取块偏移量,并将文件指针返回到原来的位置
}
ULONGLONG NextOffset;//下一个偏移量
for (UINT k = 0; k < nSample; k++)//读取stsz每个条目
{
UINT size = Read4Return(hFileA, stsz_enter_starA);//从stsz条目中读取样本大小,并将文件指针返回到原来的位置
ULONGLONG offset;
if (k == 0)offset = Offset;
else offset = NextOffset;
NextOffset = offset + size;
Sample* pSample = new Sample();
pSample->Size = size;
pSample->Offset = offset;
pSampleListA[indexA] = (DWORD)pSample;
AudioDataSize += size;
indexA++;
}
}
}
nAvgBytesPerSec = (UINT)(AudioDataSize / (DUR_A / 10000000));//计算音频平均传输率
}
}
ULONGLONG FindExBox(HANDLE hFile, char* pBoxName)//读取扩展box大小和名称
{
UINT box_size = ReadFour(hFile);
ReadFile(hFile, pBoxName, 4, NULL, NULL); pBoxName[4] = 0;
ULONGLONG Hi = ReadFour(hFile); UINT Lo = ReadFour(hFile);
ULONGLONG Exbox_size = (Hi << 32) + Lo;
MoveFilePointer(hFile, -16, FILE_CURRENT);//移动文件指针
return Exbox_size;
}
LONGLONG FindTrak(HANDLE hFile, char* pCh)//移动文件指针到符合参数2要求的trak开始位置,返回trak开始位置值。参数2,指定trak类型,视频或音频
{
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);//移动文件指针到文件开头
char ch[5];
Agan:
ULONGLONG box_size = FindBox(hFile, ch);
if (box_size == 1)box_size = FindExBox(hFile, ch);
if (strcmp(ch, "moov") == 0)
{
ULONGLONG moov_size = box_size - 8;
MoveFilePointer(hFile, 8, FILE_CURRENT);//移动文件指针8字节
moov_Agan:
box_size = FindBox(hFile, ch);
if (strcmp(ch, "trak") == 0)
{
LONGLONG TrakPos = GetFilePos(hFile);
FindMdia(hFile); FindHdlr(hFile);
GetHdlrType(hFile, ch);
MoveFilePointer(hFile, TrakPos, FILE_BEGIN);//移动文件指针到trak开始位置
if (strcmp(ch, pCh) == 0)return TrakPos;
else
{
MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
moov_size -= box_size;
if (moov_size>0)goto moov_Agan;
}
}
else
{
MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
moov_size -= box_size;
if (moov_size>0)goto moov_Agan;
}
}
else
{
MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
if (IsEnd(hFile))return 0;//如果到文件末尾,跳出循环
goto Agan;
}
MessageBox(0, L"查找trak失败", L"读MP4_3", 0);
return 0;
}
LONGLONG FindMdia(HANDLE hFile)//文件指针移动到mdia开始位置,返回mdia开始位置值。查找的起始位置为trak的开始位置
{
char ch[5];
UINT box_size = FindBox(hFile, ch);
UINT trak_size = box_size - 8;
MoveFilePointer(hFile, 8, FILE_CURRENT);//移动文件指针8字节
trak_Agan:
box_size = FindBox(hFile, ch);
if (strcmp(ch, "mdia") == 0)
{
return GetFilePos(hFile);
}
else
{
MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
trak_size -= box_size;
if (trak_size > 0)goto trak_Agan;
}
MessageBox(0, L"查找mdia失败", L"读MP4_3", 0);
return 0;
}
LONGLONG FindMdhd(HANDLE hFile)//文件指针移动到mdhd开始位置,返回mdhd开始位置值。查找的起始位置为mdia的开始位置
{
char ch[5];
UINT box_size = FindBox(hFile, ch);
UINT mdia_size = box_size - 8;
MoveFilePointer(hFile, 8, FILE_CURRENT);//移动文件指针8字节
mdia_Agan:
box_size = FindBox(hFile, ch);
if (strcmp(ch, "mdhd") == 0)
{
return GetFilePos(hFile);
}
else
{
MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
mdia_size -= box_size;
if (mdia_size > 0)goto mdia_Agan;
}
MessageBox(0, L"查找mdhd失败", L"读MP4_3", 0);
return 0;
}
LONGLONG FindHdlr(HANDLE hFile)//文件指针移动到hdlr开始位置,返回hdlr开始位置值。查找的起始位置为mdia的开始位置
{
char ch[5];
UINT box_size = FindBox(hFile, ch);
UINT mdia_size = box_size - 8;
MoveFilePointer(hFile, 8, FILE_CURRENT);//移动文件指针8字节
mdia_Agan:
box_size = FindBox(hFile, ch);
if (strcmp(ch, "hdlr") == 0)
{
return GetFilePos(hFile);
}
else
{
MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
mdia_size -= box_size;
if (mdia_size > 0)goto mdia_Agan;
}
MessageBox(0, L"查找hdlr失败", L"读MP4_3", 0);
return 0;
}
LONGLONG FindMinf(HANDLE hFile)//文件指针移动到minf开始位置,返回minf开始位置值。查找的起始位置为mdia的开始位置
{
char ch[5];
UINT box_size = FindBox(hFile, ch);
UINT mdia_size = box_size - 8;
MoveFilePointer(hFile, 8, FILE_CURRENT);//移动文件指针8字节
mdia_Agan:
box_size = FindBox(hFile, ch);
if (strcmp(ch, "minf") == 0)
{
return GetFilePos(hFile);
}
else
{
MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
mdia_size -= box_size;
if (mdia_size > 0)goto mdia_Agan;
}
MessageBox(0, L"查找minf失败", L"读MP4_3", 0);
return 0;
}
LONGLONG FindStbl(HANDLE hFile)//文件指针移动到stbl开始位置,返回stbl开始位置值。查找的起始位置为minf的开始位置
{
char ch[5];
UINT box_size = FindBox(hFile, ch);
UINT minf_size = box_size - 8;
MoveFilePointer(hFile, 8, FILE_CURRENT);//移动文件指针8字节
minf_Agan:
box_size = FindBox(hFile, ch);
if (strcmp(ch, "stbl") == 0)
{
return GetFilePos(hFile);
}
else
{
MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
minf_size -= box_size;
if (minf_size > 0)goto minf_Agan;
}
MessageBox(0, L"查找stbl失败", L"读MP4_3", 0);
return 0;
}
LONGLONG FindStblSub(HANDLE hFile, char* pch)//查找stbl的子box。文件指针移动到子box开始位置,返回子box开始位置值。查找的起始位置为stbl的开始位置
{
LONGLONG Star = GetFilePos(hFile);//记录stbl开始位置
char ch[5];
UINT box_size = FindBox(hFile, ch);
UINT stbl_size = box_size - 8;
MoveFilePointer(hFile, 8, FILE_CURRENT);//移动文件指针8字节
stbl_Agan:
box_size = FindBox(hFile, ch);
if (strcmp(ch, pch) == 0)
{
return GetFilePos(hFile);
}
else
{
MoveFilePointer(hFile, box_size, FILE_CURRENT);//移动文件指针box_size大小
stbl_size -= box_size;
if (stbl_size > 0)goto stbl_Agan;
}
MoveFilePointer(hFile, Star, FILE_BEGIN);//查找失败,回到stbl开始位置
return 0;
}
void CFilter::GetVideoParam()//获取视频参数
{
MoveFilePointer(hFile, 16, FILE_CURRENT);//移动文件指针16字节
char ch[5];
UINT box_size = FindBox(hFile, ch);
if (strcmp(ch, "avc1") == 0)
{
MoveFilePointer(hFile, 32, FILE_CURRENT);//移动文件指针
width = ReadTwo(hFile);//读取视频宽
height = ReadTwo(hFile);//读取视频高
MoveFilePointer(hFile, 46, FILE_CURRENT);//移动文件指针
bit_depth = ReadTwo(hFile);//位深度
MoveFilePointer(hFile, 2, FILE_CURRENT);//移动文件指针
GetAvcCParam();//获取avcC参数
}
else if (strcmp(ch, "mp4v") == 0)
{
MoveFilePointer(hFile, 32, FILE_CURRENT);//移动文件指针
width = ReadTwo(hFile);//读取视频宽
height = ReadTwo(hFile);//读取视频高
MoveFilePointer(hFile, 46, FILE_CURRENT);//移动文件指针
bit_depth = ReadTwo(hFile);//位深度
MoveFilePointer(hFile, 2, FILE_CURRENT);//移动文件指针
//GetEsds(box_size-84);
//GetDecSpecificInfotag();
}
else
MessageBox(0, L"无法解析", L"写MP4_3", MB_OK);
}
void CFilter::GetAudioParam()//获取音频参数
{
MoveFilePointer(hFileA, 16, FILE_CURRENT);//移动文件指针16字节
char ch[5];
UINT box_size = FindBox(hFileA, ch);
if (strcmp(ch, "mp4a") == 0)
{
MoveFilePointer(hFileA, 24, FILE_CURRENT);//移动文件指针
nChannels = ReadTwo(hFileA);//读取声道数
wBitsPerSample = ReadTwo(hFileA);//读取音频样本位数
MoveFilePointer(hFileA, 4, FILE_CURRENT);//移动文件指针
nSamplesPerSec = ReadTwo(hFileA);//采样率
}
}
void CFilter::GetAvcCParam()//获取avcC参数
{
MoveFilePointer(hFile, 9, FILE_CURRENT);//移动文件指针
ProFile = ReadOne(hFile);//配置文件
MoveFilePointer(hFile, 1, FILE_CURRENT);//移动文件指针
AVCLevel = ReadOne(hFile);//编码级别
MoveFilePointer(hFile, 1, FILE_CURRENT);//移动文件指针
BYTE mby = ReadOne(hFile);
BYTE N1 = 0x1F & mby;//条目数
SpsNu = ReadTwo(hFile);//sps大小
if (SPS)delete[] SPS;
SPS = new BYTE[SpsNu];
ReadFile(hFile, SPS, SpsNu, NULL, NULL);//读取sps
BYTE N2 = ReadOne(hFile); //条目数
PpsNu = ReadTwo(hFile);//pps大小
if (PPS)delete[] PPS;
PPS = new BYTE[PpsNu];
ReadFile(hFile, PPS, PpsNu, NULL, NULL);//读取pps
}
void GetHdlrType(HANDLE hFile, char* pch)
{
MoveFilePointer(hFile, 16, FILE_CURRENT);//移动文件指针16字节
ReadFile(hFile, pch, 4, NULL, NULL); pch[4] = 0;
}
BYTE ReadOne(HANDLE hFile)
{
BYTE mby1;
ReadFile(hFile, &mby1, 1, NULL, NULL);
return mby1;
}
WORD ReadTwo(HANDLE hFile)
{
BYTE mby1, mby2;
ReadFile(hFile, &mby1, 1, NULL, NULL); ReadFile(hFile, &mby2, 1, NULL, NULL);
WORD size = mby1 * 256 + mby2;
return size;
}
UINT ReadThree(HANDLE hFile)
{
BYTE mby1, mby2, mby3;
ReadFile(hFile, &mby1, 1, NULL, NULL); ReadFile(hFile, &mby2, 1, NULL, NULL); ReadFile(hFile, &mby3, 1, NULL, NULL);
UINT size = mby1 * 256 * 256 + mby2 * 256 + mby3;
return size;
}
UINT ReadFour(HANDLE hFile)
{
BYTE mby1, mby2, mby3, mby4;
ReadFile(hFile, &mby1, 1, NULL, NULL); ReadFile(hFile, &mby2, 1, NULL, NULL); ReadFile(hFile, &mby3, 1, NULL, NULL); ReadFile(hFile, &mby4, 1, NULL, NULL);
UINT size = mby1 * 256 * 256 * 256 + mby2 * 256 * 256 + mby3 * 256 + mby4;
return size;
}
UINT Read4Return(HANDLE hFile, LONGLONG& pos)//在指定位置读4个字节,并返回到文件原来位置。参数2加4字节
{
LONGLONG Cur = GetFilePos(hFile);
MoveFilePointer(hFile, pos, FILE_BEGIN);
UINT size = ReadFour(hFile); pos += 4;
MoveFilePointer(hFile, Cur, FILE_BEGIN);
return size;
}
ULONGLONG Read8Return(HANDLE hFile, LONGLONG& pos)//在指定位置读8个字节,并返回到文件原来位置。参数2加8字节
{
LONGLONG Cur = GetFilePos(hFile);
MoveFilePointer(hFile, pos, FILE_BEGIN);
ULONGLONG size1 = ReadFour(hFile); UINT size2 = ReadFour(hFile);
ULONGLONG UL = (size1 << 32) + size2;
pos += 8;
MoveFilePointer(hFile, Cur, FILE_BEGIN);
return UL;
}
BOOL ReadAtPos(HANDLE hFile, LONGLONG pos, UINT size, BYTE* pBuffer)//在参数2指定位置,读参数3指定字节
{
MoveFilePointer(hFile, pos, FILE_BEGIN);
return ReadFile(hFile, pBuffer, size, NULL, NULL);
}
HRESULT MoveFilePointer(HANDLE hFile, LONGLONG size, int flag)//移动文件指针
{
LARGE_INTEGER move;
move.QuadPart = size;
SetFilePointerEx(hFile, move, NULL, flag);
return S_OK;
}
UINT FindBox(HANDLE hFile, char* pBoxName)//读取BOX大小和名称
{
UINT box_size = ReadFour(hFile);
ReadFile(hFile, pBoxName, 4, NULL, NULL); pBoxName[4] = 0;
MoveFilePointer(hFile, -8, FILE_CURRENT);//移动文件指针
return box_size;
}
BOOL IsEnd(HANDLE hFile)//如果到文件末尾,返回TRUE
{
LARGE_INTEGER SIZE;
GetFileSizeEx(hFile, &SIZE);
LONGLONG Pos = GetFilePos(hFile);
if (Pos == SIZE.QuadPart)return TRUE;
else return FALSE;
}
LONGLONG GetFilePos(HANDLE hFile)//获取文件当前位置
{
LARGE_INTEGER move;
move.QuadPart = 0;
LARGE_INTEGER CUR;
SetFilePointerEx(hFile, move, &CUR, FILE_CURRENT);
return CUR.QuadPart;
}
HRESULT STDMETHODCALLTYPE CFilter::CheckCapabilities(DWORD *pCapabilities)//查询流是否具有指定的Seek功能
{
CheckPointer(pCapabilities, E_POINTER);
return (~m_dwSeekingCaps & *pCapabilities) ? S_FALSE : S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::ConvertTimeFormat(LONGLONG *pTarget, const GUID *pTargetFormat, LONGLONG Source, const GUID *pSourceFormat)//从一种时间格式转换为另一种时间格式
{
CheckPointer(pTarget, E_POINTER);
if (pTargetFormat == 0 || *pTargetFormat == TIME_FORMAT_MEDIA_TIME)
{
if (pSourceFormat == 0 || *pSourceFormat == TIME_FORMAT_MEDIA_TIME)
{
*pTarget = Source;
return S_OK;
}
}
return E_INVALIDARG;
}
HRESULT STDMETHODCALLTYPE CFilter::GetAvailable(LONGLONG *pEarliest, LONGLONG *pLatest)//获取有效Seek的时间范围
{
if (pEarliest)
{
*pEarliest = 0;
}
if (pLatest)
{
CAutoLock lock(m_pLock);
*pLatest = DUR;
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::GetCapabilities(DWORD *pCapabilities)//检索流的所有Seek功能
{
CheckPointer(pCapabilities, E_POINTER);
*pCapabilities = m_dwSeekingCaps;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::GetCurrentPosition(LONGLONG *pCurrent)//获取相对于流总持续时间的当前位置
{
*pCurrent = CUR;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::GetDuration(LONGLONG *pDuration)//获取流的持续时间
{
CheckPointer(pDuration, E_POINTER);
CAutoLock lock(m_pLock);
*pDuration = DUR;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::GetPositions(LONGLONG *pCurrent, LONGLONG *pStop)//获取相对于流总持续时间的当前位置和停止位置
{
CheckPointer(pCurrent, E_POINTER); CheckPointer(pStop, E_POINTER);
*pCurrent = CUR; *pStop = DUR;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::GetPreroll(LONGLONG *pllPreroll)//获取将在开始位置之前排队的数据量
{
CheckPointer(pllPreroll, E_POINTER);
*pllPreroll = 0;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::GetRate(double *pdRate)//获取播放速率
{
CheckPointer(pdRate, E_POINTER);
CAutoLock lock(m_pLock);
*pdRate = 1.0;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::GetStopPosition(LONGLONG *pStop)//获取相对于流的持续时间的停止播放时间
{
CheckPointer(pStop, E_POINTER);
CAutoLock lock(m_pLock);
*pStop = DUR;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::GetTimeFormat(GUID *pFormat)//获取当前用于Seek操作的时间格式
{
CheckPointer(pFormat, E_POINTER);
*pFormat = TIME_FORMAT_MEDIA_TIME;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::IsFormatSupported(const GUID *pFormat)//确定Seek操作是否支持指定的时间格式
{
CheckPointer(pFormat, E_POINTER);
return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;
}
HRESULT STDMETHODCALLTYPE CFilter::IsUsingTimeFormat(const GUID *pFormat)//确定Seek操作当前是否使用指定的时间格式
{
CheckPointer(pFormat, E_POINTER);
return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : S_FALSE;
}
HRESULT STDMETHODCALLTYPE CFilter::QueryPreferredFormat(GUID *pFormat)//获取首选的Seek时间格式
{
CheckPointer(pFormat, E_POINTER);
*pFormat = TIME_FORMAT_MEDIA_TIME;
return S_OK;
}
HRESULT STDMETHODCALLTYPE CFilter::SetPositions(LONGLONG *pCurrent, DWORD dwCurrentFlags, LONGLONG *pStop, DWORD dwStopFlags)//设置当前位置和停止位置
{
CheckPointer(pCurrent, E_POINTER);
DWORD dwCurrentPos = dwCurrentFlags & AM_SEEKING_PositioningBitsMask;
if (dwCurrentPos == AM_SEEKING_AbsolutePositioning && *pCurrent >= 0 && *pCurrent <= DUR)
{
LONGLONG cur = *pCurrent;
UINT Frame = (UINT)(cur / AvgTimePerFrame);
if (Frame > nFrame - 1)Frame = nFrame - 1;
SeekFrame = FindKeyFrame(Frame);//查找关键帧。定位后,第1帧必须是关键帧
SetEvent(hSeek);//设置"视频定位"有信号
SeekANu = (UINT)(AvgTimePerFrame * SeekFrame / AvgTimeA);//查找关键帧对应的音频样本索引
SetEvent(hSeekA);//设置"音频定位"有信号
return S_OK;
}
return E_INVALIDARG;
}
HRESULT STDMETHODCALLTYPE CFilter::SetRate(double dRate)//设置播放速率
{
if (dRate == 1.0)return S_OK;
else return S_FALSE;
}
HRESULT STDMETHODCALLTYPE CFilter::SetTimeFormat(const GUID *pFormat)//设置后续Seek操作的时间格式
{
CheckPointer(pFormat, E_POINTER);
return *pFormat == TIME_FORMAT_MEDIA_TIME ? S_OK : E_INVALIDARG;
}
UINT CFilter::FindKeyFrame(UINT Frame)//查找关键帧
{
UINT LastKeyFrame;
for (UINT i = ListCount - 1; i >= 0; i--) //获取最后一个关键帧
{
Sample* pSample = (Sample*)pSampleList[i];
if (pSample->KeyFrame)
{
LastKeyFrame = i; break;
}
}
Sample* pS = (Sample*)pSampleList[Frame];
while (pS->KeyFrame == FALSE)
{
Frame++;
if (Frame > LastKeyFrame)return LastKeyFrame;
pS = (Sample*)pSampleList[Frame];
}
return Frame;
}
CVideoPin.cpp
cpp
#include "DLL.h"
#include "dvdmedia.h"
CVideoPin::CVideoPin(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName) : CBaseOutputPin(NAME("Out"), pFilter, pFilter, phr, pPinName)
{
pCFilter = pFilter;
}
CVideoPin::~CVideoPin()
{
if (pSHeader)delete[] pSHeader;
}
HRESULT CVideoPin::CheckMediaType(const CMediaType *pmt)
{
AM_MEDIA_TYPE* pMt = NULL;
pCFilter->pVideoType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, (void**)&pMt);//将IMFMediaType表示的媒体类型,转换为AM_MEDIA_TYPE结构形式
CMediaType MT(*pMt);
if (pmt->MatchesPartial(&MT))
{
pCFilter->pVideoType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pMt);//释放GetRepresentation分配的内存
return S_OK;
}
pCFilter->pVideoType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pMt);//释放GetRepresentation分配的内存
return S_FALSE;
}
HRESULT CVideoPin::GetMediaType(int iPosition, CMediaType *pmt)
{
if (pCFilter->m_pFileName == NULL)return S_FALSE;
if (iPosition == 0)
{
HRESULT hr = pCFilter->pVideoType->GetGUID(MF_MT_SUBTYPE, &VideoSubType);//获取媒体基础媒体类型子类型
if (VideoSubType == MEDIASUBTYPE_H264)
{
hr = pCFilter->pVideoType->GetBlobSize(MF_MT_MPEG_SEQUENCE_HEADER, &(UINT32)cbSHeader);//获取序列头大小
if (pSHeader)delete[] pSHeader;//如果引脚多次连接,需要删除上一次的序列头
pSHeader = new BYTE[cbSHeader];
hr = pCFilter->pVideoType->GetBlob(MF_MT_MPEG_SEQUENCE_HEADER, pSHeader, cbSHeader, NULL);//获取序列头
}
AM_MEDIA_TYPE* pMt = NULL;
pCFilter->pVideoType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, (void**)&pMt);//将IMFMediaType表示的媒体类型,转换为AM_MEDIA_TYPE结构形式
pmt->Set(*pMt);
pCFilter->pVideoType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pMt);//释放GetRepresentation分配的内存
return S_OK;
}
return S_FALSE;
}
HRESULT CVideoPin::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES * pProperties)//确定输出引脚样本缓冲区大小
{
HRESULT hr = NOERROR;
pProperties->cBuffers = 1;//1个缓冲区
pProperties->cbBuffer = 10000000;//缓冲区的大小,10M
ALLOCATOR_PROPERTIES Actual;
hr = pAlloc->SetProperties(pProperties, &Actual);
if (FAILED(hr))return hr;
if (Actual.cbBuffer < pProperties->cbBuffer)// 这个分配器是否不合适
{
return E_FAIL;
}
return NOERROR;
}
HRESULT CVideoPin::SetMediaType(const CMediaType *pmt)
{
if (HasSet == FALSE)//如果GetMediaType函数没有调用
{
GetMediaType(0, &m_mt);//设置引脚媒体类型
HasSet = TRUE;
return S_OK;
}
return CBasePin::SetMediaType(pmt);
}
HRESULT CVideoPin::BreakConnect()
{
HasSet = FALSE;
return CBasePin::BreakConnect();
}
DWORD WINAPI VideoThread(LPVOID pParam);//视频引脚工作线程
HRESULT CVideoPin::Active(void)
{
CreateThread(NULL, 0, VideoThread, pCFilter, 0, NULL);//创建视频引脚工作线程
return CBaseOutputPin::Active();
}
HRESULT CVideoPin::Inactive(void)
{
ResetEvent(pCFilter->hRun);//设置"运行"无信号
return CBaseOutputPin::Inactive();
}
void SetStartCode(BYTE* pB, UINT len)//将所有单元大小替换为0x00000001
{
int index = 0;
while (index <(int)len)
{
UINT size = (pB[index] << 24) + (pB[index + 1] << 16) + (pB[index + 2] << 8) + pB[index + 3];
pB[index] = 0; pB[index + 1] = 0; pB[index + 2] = 0; pB[index + 3] = 1;
index += 4 + size;
}
}
DWORD WINAPI VideoThread(LPVOID pParam)//视频引脚工作线程
{
CFilter* pCFilter = (CFilter*)pParam;
HRESULT hr;
SetEvent(pCFilter->hRun);//设置"运行"有信号
ResetEvent(pCFilter->hSeek);//设置"定位"无信号
pCFilter->SeekFrame = 0;
SEEK:
pCFilter->pCVideoPin->DeliverNewSegment(0, pCFilter->DUR - pCFilter->SeekFrame*pCFilter->AvgTimePerFrame, 1.0);//向下游引脚发送新段通知
IMediaSample *pOutSample = NULL;
hr = pCFilter->pCVideoPin->GetDeliveryBuffer(&pOutSample, NULL, NULL, 0);//获取一个空的输出引脚样本
if (hr == S_OK)//首先发送解码器需要的初始化数据
{
BYTE* pBuffer = NULL;
hr = pOutSample->GetPointer(&pBuffer);//获取输出引脚样本缓冲区指针
if (pCFilter->pCVideoPin->VideoSubType == MEDIASUBTYPE_H264)//如果是H264视频
{
CopyMemory(pBuffer, pCFilter->pCVideoPin->pSHeader, pCFilter->pCVideoPin->cbSHeader);//首先发送sps, pps单元
hr = pOutSample->SetActualDataLength(pCFilter->pCVideoPin->cbSHeader);//设置输出引脚样本有效数据长度
}
LONGLONG Star = 0, End = 0;
hr = pOutSample->SetTime(&Star, &End);//设置时间戳
hr = pOutSample->SetDiscontinuity(TRUE);//设置中断标志
hr = pOutSample->SetPreroll(TRUE);//设置预卷标志
hr = pCFilter->pCVideoPin->Deliver(pOutSample);//输出引脚向下游发送样本
pOutSample->Release();//释放输出引脚样本
}
LONGLONG star = 0;
UINT S = pCFilter->SeekFrame;
for (UINT i = S; i < pCFilter->ListCount; i++)
{
DWORD dwRun = WaitForSingleObject(pCFilter->hRun, 0);
if (dwRun != WAIT_OBJECT_0)return 0;//如果"运行"无信号,终止线程
DWORD dwSeek = WaitForSingleObject(pCFilter->hSeek, 0);
if (dwSeek == WAIT_OBJECT_0)//如果"视频定位"有信号,转到SEEK
{
pCFilter->pCVideoPin->DeliverBeginFlush();//通知下游(解码器)开始刷新
Sleep(200);//确保下游解码器已丢弃所有样本
pCFilter->pCVideoPin->DeliverEndFlush();//结束刷新
goto SEEK;
}
Sample* pS = (Sample*)pCFilter->pSampleList[i];
IMediaSample *pOutSample = NULL;
hr = pCFilter->pCVideoPin->GetDeliveryBuffer(&pOutSample, NULL, NULL, 0);//获取一个空的输出引脚样本
if (hr == S_OK)
{
BYTE* pBuffer = NULL;
hr = pOutSample->GetPointer(&pBuffer);//获取输出引脚样本缓冲区指针
if (pCFilter->pCVideoPin->VideoSubType == MEDIASUBTYPE_H264)//如果是H264视频
{
ReadAtPos(pCFilter->hFile, pS->Offset, pS->Size, pBuffer);//读取样本
SetStartCode(pBuffer, pS->Size);//将所有单元大小替换为0x00000001
hr = pOutSample->SetActualDataLength(pS->Size);//设置输出引脚样本有效数据长度
LONGLONG Star = star + pS->ctts;
LONGLONG End = Star + pCFilter->AvgTimePerFrame;
hr = pOutSample->SetTime(&Star, &End);//设置输出引脚样本时间戳
pCFilter->CUR = Star;
}
else
{
ReadAtPos(pCFilter->hFile, pS->Offset, pS->Size, pBuffer);//读取样本
hr = pOutSample->SetActualDataLength(pS->Size);//设置输出引脚样本有效数据长度
LONGLONG end = star + pCFilter->AvgTimePerFrame;
hr = pOutSample->SetTime(&star, &end);//设置输出引脚样本时间戳
pCFilter->CUR = star;
}
if (pS->KeyFrame)//如果是关键帧
hr = pOutSample->SetSyncPoint(TRUE);//设置同步点标志
else
hr = pOutSample->SetSyncPoint(FALSE);
hr = pCFilter->pCVideoPin->Deliver(pOutSample);//输出引脚向下游发送样本
pOutSample->Release();//释放输出引脚样本
star += pCFilter->AvgTimePerFrame;//计算下一个样本的起始时间
}
}
hr = pCFilter->pCVideoPin->DeliverEndOfStream();
return 1;
}
CAudioPin.cpp
cpp
#include "DLL.h"
CAudioPin::CAudioPin(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName) : CBaseOutputPin(NAME("Out"), pFilter, pFilter, phr, pPinName)
{
pCFilter = pFilter;
}
CAudioPin::~CAudioPin()
{
}
HRESULT CAudioPin::CheckMediaType(const CMediaType *pmt)
{
AM_MEDIA_TYPE* pMt = NULL;
pCFilter->pAudioType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, (void**)&pMt);//将IMFMediaType表示的媒体类型,转换为AM_MEDIA_TYPE结构形式
CMediaType MT(*pMt);
if (pmt->MatchesPartial(&MT))
{
pCFilter->pAudioType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pMt);//释放GetRepresentation分配的内存
return S_OK;
}
pCFilter->pAudioType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pMt);//释放GetRepresentation分配的内存
return S_FALSE;
}
HRESULT CAudioPin::GetMediaType(int iPosition, CMediaType *pmt)
{
if (pCFilter->m_pFileName == NULL)return S_FALSE;
if (iPosition == 0)
{
AM_MEDIA_TYPE* pMt = NULL;
pCFilter->pAudioType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, (void**)&pMt);//将IMFMediaType表示的媒体类型,转换为AM_MEDIA_TYPE结构形式
pmt->Set(*pMt);
pCFilter->pAudioType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pMt);//释放GetRepresentation分配的内存
return S_OK;
}
return S_FALSE;
}
HRESULT CAudioPin::SetMediaType(const CMediaType *pmt)
{
if (HasSet == FALSE)//如果GetMediaType函数没有调用
{
GetMediaType(0, &m_mt);//设置引脚媒体类型
HasSet = TRUE;
return S_OK;
}
return CBasePin::SetMediaType(pmt);
}
HRESULT CAudioPin::BreakConnect()
{
HasSet = FALSE;
return CBasePin::BreakConnect();
}
HRESULT CAudioPin::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES * pProperties)//确定输出引脚样本缓冲区大小
{
HRESULT hr = NOERROR;
pProperties->cBuffers = 1;//1个缓冲区
pProperties->cbBuffer = 1000000;//以1个音频包的大小,指定引脚缓冲区的大小
ALLOCATOR_PROPERTIES Actual;
hr = pAlloc->SetProperties(pProperties, &Actual);
if (FAILED(hr))return hr;
if (Actual.cbBuffer < pProperties->cbBuffer)// 这个分配器是否不合适
{
return E_FAIL;
}
return NOERROR;
}
DWORD WINAPI AudioThread(LPVOID pParam);//音频引脚工作线程
HRESULT CAudioPin::Active(void)
{
CreateThread(NULL, 0, AudioThread, pCFilter, 0, NULL);//创建音频引脚工作线程
return CBaseOutputPin::Active();
}
DWORD WINAPI AudioThread(LPVOID pParam)//音频引脚工作线程
{
CFilter* pCFilter = (CFilter*)pParam;
HRESULT hr;
ResetEvent(pCFilter->hSeekA);//设置"定位"无信号
pCFilter->SeekANu = 0;
WaitForSingleObject(pCFilter->hRun, INFINITE);//等待"运行"信号
SEEK:
pCFilter->pCAudioPin->DeliverNewSegment(0, pCFilter->DUR_A - pCFilter->SeekANu * pCFilter->AvgTimeA, 1.0);//向下游引脚发送新段通知
UINT S = pCFilter->SeekANu;
LONGLONG star = 0;
for (UINT i = S; i < pCFilter->ListCountA; i++)
{
DWORD dwRun = WaitForSingleObject(pCFilter->hRun, 0);
if (dwRun != WAIT_OBJECT_0)return 0;//如果"运行"无信号,终止线程
DWORD dwSeek = WaitForSingleObject(pCFilter->hSeekA, 0);
if (dwSeek == WAIT_OBJECT_0)//如果"音频定位"有信号,转到SEEK
{
pCFilter->pCAudioPin->DeliverBeginFlush();//通知下游(解码器)开始刷新
Sleep(100);//确保下游解码器已丢弃所有样本
pCFilter->pCAudioPin->DeliverEndFlush();//结束刷新
goto SEEK;
}
Sample* pS = (Sample*)pCFilter->pSampleListA[i];
IMediaSample *pOutSample = NULL;
hr = pCFilter->pCAudioPin->GetDeliveryBuffer(&pOutSample, NULL, NULL, 0);//获取一个空的输出引脚样本
if (hr == S_OK)
{
BYTE* pBuffer = NULL;
hr = pOutSample->GetPointer(&pBuffer);//获取输出引脚样本缓冲区指针
ReadAtPos(pCFilter->hFileA, pS->Offset, pS->Size, pBuffer);
hr = pOutSample->SetActualDataLength(pS->Size);//设置输出引脚样本有效数据长度
LONGLONG end = star + pCFilter->AvgTimeA;
hr = pOutSample->SetTime(&star, &end);//设置输出引脚样本时间戳
pCFilter->CUR_A = star;
hr = pCFilter->pCAudioPin->Deliver(pOutSample);//输出引脚向下游发送样本
pOutSample->Release();//释放输出引脚样本
star += pCFilter->AvgTimeA;//计算下一个样本的起始时间
}
}
hr = pCFilter->pCAudioPin->DeliverEndOfStream();
return 1;
}