DirectShow过滤器开发-H264解码器

本解码器将H264视频流,解码为YUV视频流。

H264解码过滤器信息

过滤器名称:H264解码器
过滤器GUID:{54588DC2-9BE5-42C8-90FB-D803FCF28828}
DLL注册函数名:DllRegisterServer
删除注册函数名:DllUnregisterServer
过滤器有1个输入引脚和1个输出引脚。

输入引脚标识:In
输入引脚媒体类型:
主要类型:MEDIATYPE_Video
子类型:MEDIASUBTYPE_H264
格式类型:FORMAT_MPEG2Video
使用时间压缩。

输出引脚标识:Out
输出引脚媒体类型:
主要类型:MEDIATYPE_Video
子类型:
MEDIASUBTYPE_I420
MEDIASUBTYPE_IYUV
MEDIASUBTYPE_NV12
MEDIASUBTYPE_YUY2
MEDIASUBTYPE_YV12
格式类型:FORMAT_VideoInfo2
样本为固定大小。
样本为12位。

H264解码过滤器开发信息

使用CoCreateInstance函数创建H264解码器对象,其为媒体基础转换,接口为IMFTransform。在连接输入引脚时,指定H264解码器的输入媒体类型;在连接输出引脚时,指定H264解码器的输出媒体类型。H264解码器要求先指定输入媒体类型。在过滤器运行时,将输入引脚样本转换为媒体基础样本,使用IMFTransform接口的ProcessInput方法向解码器传递输入样本;使用IMFTransform接口的ProcessOutput方法获取解码器的输出样本,并将输出样本转换为引脚样本,由输出引脚发送到下游。该解码器具有ICodecAPI接口,用于设置解码器。设置解码器须在输入输出引脚连接之前。实现了两个属性页,属性页1显示解码器允许的媒体类型,如果引脚已经连接,还可以显示引脚的当前媒体类型。属性页2用于对编码器进行设置。

H264解码过滤器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

#include "mfapi.h"
#include "mftransform.h"
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfuuid")

#include "wmcodecdsp.h"
#include "codecapi.h"


// {54588DC2-9BE5-42C8-90FB-D803FCF28828}
DEFINE_GUID(CLSID_H264Decoder,//过滤器GUID
	0x54588dc2, 0x9be5, 0x42c8, 0x90, 0xfb, 0xd8, 0x3, 0xfc, 0xf2, 0x88, 0x28);

// {938A838E-B496-43C9-A08F-3393986142DC}
DEFINE_GUID(CLSID_PropertyPage1,//属性页1GUID
	0x938a838e, 0xb496, 0x43c9, 0xa0, 0x8f, 0x33, 0x93, 0x98, 0x61, 0x42, 0xdc);

// {C64BEEC9-0D53-41DC-9606-F8AC0ECA42CD}
DEFINE_GUID(CLSID_PropertyPage2,//属性页2GUID
	0xc64beec9, 0xd53, 0x41dc, 0x96, 0x6, 0xf8, 0xac, 0xe, 0xca, 0x42, 0xcd);


class COutPin;
class CFilter;

class CInPin : public CBaseInputPin
{
	friend class COutPin;
	friend class CFilter;
public:
	CInPin(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName);
	~CInPin();
	HRESULT CheckMediaType(const CMediaType *pmt);
	HRESULT SetMediaType(const CMediaType *pmt);
	STDMETHODIMP Receive(IMediaSample *pSample);
	STDMETHODIMP EndOfStream();
	STDMETHODIMP BeginFlush();
	STDMETHODIMP EndFlush();
	STDMETHODIMP NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate);
	HRESULT GetOut();
	CFilter *pCFilter;
};

class COutPin : public CBaseOutputPin
{
	friend class CInPin;
	friend class CFilter;
	IUnknown   *m_pPosition = NULL;
	COutputQueue *m_pOutputQueue = NULL;//样本队列
public:
	COutPin(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName);
	~COutPin();
	STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppvoid);
	HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
	HRESULT CheckMediaType(const CMediaType *pmt);
	HRESULT SetMediaType(const CMediaType *pmt);
	HRESULT DecideBufferSize(IMemAllocator *pMemAllocator, ALLOCATOR_PROPERTIES * ppropInputRequest);
	HRESULT Active();
	HRESULT Inactive();
	STDMETHODIMP Receive(IMediaSample *pSample);
	HRESULT Deliver(IMediaSample *pMediaSample);
	HRESULT DeliverEndOfStream();
	HRESULT DeliverBeginFlush();
	HRESULT DeliverEndFlush();
	HRESULT DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate);
	STDMETHODIMP Notify(IBaseFilter *pSender, Quality q);
	CFilter *pCFilter = NULL;
};

class CFilter : public CCritSec, public CBaseFilter, public ISpecifyPropertyPages
{
	friend class CInPin;
	friend class COutPin;
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 riid, void **ppv);
	virtual HRESULT Get(CBaseFilter** ppF);
	STDMETHODIMP GetPages(CAUUID *pPages)
	{
		if (pPages == NULL) return E_POINTER;
		pPages->cElems = 2;
		pPages->pElems = (GUID*)CoTaskMemAlloc(sizeof(GUID) * 2);
		if (pPages->pElems == NULL)
		{
			return E_OUTOFMEMORY;
		}
		pPages->pElems[0] = CLSID_PropertyPage1;
		pPages->pElems[1] = CLSID_PropertyPage2;
		return S_OK;
	}
	CInPin* pCInPin = NULL;
	COutPin* pCOutPin = NULL;
	IMFTransform *pH264Decoder = NULL;
	ICodecAPI* pAPI = NULL;
	DWORD Param1 = UINT_MAX, Default1 = UINT_MAX;//Param1实际值,Default1默认值
	DWORD Param2 = UINT_MAX, Default2 = UINT_MAX;
	INT Param3 = INT_MAX, Default3 = INT_MAX;
};

class CPropertyPage1 : public CBasePropertyPage
{
	friend class CFilter;
	friend class CInPin;
public:
	CPropertyPage1(IUnknown *pUnk);
	~CPropertyPage1();
	static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr);
	virtual HRESULT OnConnect(IUnknown *pUnknown);
	virtual HRESULT OnDeactivate();
	virtual HRESULT OnActivate();//用于初始化属性页对话框
	virtual INT_PTR OnReceiveMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);//接收属性页对话框的消息
	WCHAR* DefineFromGUID(GUID guid);//获取GUID定义字符串
	static WCHAR* GetFormatString(void* p, GUID guid);//获取格式块描述字符串
	CFilter *pCFilter = NULL;
	HWND hList, hEdit;
	HFONT hFont1, hFont2;
	HWND hE1;//“输入输出允许媒体类型“”编辑框窗口句柄
};

class CPropertyPage2 : public CBasePropertyPage
{
	friend class CFilter;
	friend class CInPin;
	CFilter* pCFilter = NULL;
public:
	CPropertyPage2(IUnknown *pUnk);
	~CPropertyPage2();
	static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr);
	virtual HRESULT OnConnect(IUnknown *pUnknown);
	virtual HRESULT OnActivate();//用于初始化属性页对话框
	virtual INT_PTR OnReceiveMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);//接收属性页对话框的消息
	void SetDirty()
	{
		m_bDirty = TRUE;
		if (m_pPageSite)
		{
			m_pPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY);
		}
	}
	HRESULT OnApplyChanges(void);
	HRESULT SetDefault();
	HRESULT SetValue();
	HRESULT GetValue();
	HFONT hFont1;
	HWND hText1, hText2, hText3;
	HWND hCBox1, hCBox2, hE3;
	BOOL Init = FALSE;
	HWND hBtn;
};


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

#endif //DLL_FILE

DLL.cpp

#include "DLL.h"

const AMOVIESETUP_MEDIATYPE InPinType[] =   // 输入引脚媒体类型
{
	{
		&MEDIATYPE_Video,              //主要类型
		&MEDIASUBTYPE_H264             //子类型
	}
};

const AMOVIESETUP_MEDIATYPE OutPinType[] =   // 输出引脚媒体类型
{
	{
		&MEDIATYPE_Video,             //主要类型
		&MEDIASUBTYPE_I420            //子类型
	},
	{
		&MEDIATYPE_Video,             //主要类型
		&MEDIASUBTYPE_IYUV            //子类型
	},
	{
		&MEDIATYPE_Video,             //主要类型
		&MEDIASUBTYPE_NV12            //子类型-
	},
	{
		&MEDIATYPE_Video,             //主要类型
		&MEDIASUBTYPE_YUY2            //子类型
	},
	{
		&MEDIATYPE_Video,             //主要类型
		&MEDIASUBTYPE_YV12            //子类型
	}
};

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

const AMOVIESETUP_FILTER H264Decoder =  //过滤器的注册信息
{
	&CLSID_H264Decoder,                 //过滤器的类标识
	L"H264解码器",                      //过滤器的名称
	MERIT_DO_NOT_USE,                  //过滤器优先值
	2,                                 //引脚数量
	sudPins                            //引脚信息
};

CFactoryTemplate g_Templates[] =   //类工厂模板数组
{
	{
		L"H264解码器",              //过滤器名称
		&CLSID_H264Decoder,         //过滤器CLSID的指针
	    CFilter::CreateInstance,    //创建过滤器实例的函数的指针
	    NULL,                       //指向从DLL入口点调用的函数的指针
	    &H264Decoder                //指向AMOVIESETUP_FILTER结构的指针
	},
	{
		L"属性页1",                              //对象名称
		&CLSID_PropertyPage1,                   //对象CLSID的指针
	    CPropertyPage1::CreateInstance,         //对象创建函数
	    NULL,                                   //DLL入口点调用的函数
	    NULL                                    //包含注册信息的AMOVIESETUP_FILTER结构
	},
	{
		L"属性页2",                              //对象名称
		&CLSID_PropertyPage2,                   //对象CLSID的指针
	    CPropertyPage2::CreateInstance,         //对象创建函数
	    NULL,                                   //DLL入口点调用的函数
	    NULL                                    //包含注册信息的AMOVIESETUP_FILTER结构
	}
};

int g_cTemplates = 3;//模板数组大小

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 "Mfidl.h"
#pragma comment(lib, "Mfuuid.lib")
#include "mftransform.h"

CFilter::CFilter(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr) : CBaseFilter(NAME("H264解码器"), pUnk, this, CLSID_H264Decoder)
{
	HRESULT hr = MFStartup(MF_VERSION);//初始化媒体基础
	if (hr != S_OK)
	{
		MessageBox(NULL, L"初始化媒体基础失败", L"H264解码器", MB_OK); return;
	}
	GUID CLSID_H264DecoderMft = { 0x62ce7e72, 0x4c71, 0x4d20, 0xb1, 0x5d, 0x45, 0x28, 0x31, 0xa8, 0x7d, 0x9d };//H264视频解码器的类标识符
	hr = CoCreateInstance(CLSID_H264DecoderMft, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pH264Decoder));//创建H264视频解码器(媒体基础转换)
	if (hr != S_OK)
	{
		MessageBox(NULL, L"H264视频解码器创建失败", L"H264解码器", MB_OK); return;
	}
	hr = pH264Decoder->QueryInterface(IID_ICodecAPI, (void**)&pAPI);
	pCInPin = new CInPin(this, phr, L"In");//创建输入引脚
	pCOutPin = new COutPin(this, phr, L"Out");//创建输出引脚
}

CFilter::~CFilter()
{
	SafeRelease(&pH264Decoder); SafeRelease(&pAPI);
	MFShutdown();//关闭媒体基础
}

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

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

CUnknown * WINAPI CFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr)
{
	return new CFilter(NAME("H264解码器"), pUnk, phr);
}

HRESULT CFilter::Get(CBaseFilter** ppF)
{
	*ppF = this;
	return S_OK;
}

STDMETHODIMP CFilter::NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
	CheckPointer(ppv, E_POINTER);
	if (riid == IID_ISpecifyPropertyPages)
	{
		return GetInterface((ISpecifyPropertyPages*) this, ppv);
	}
	if (riid == IID_ICodecAPI)
	{
		return GetInterface(pAPI, ppv);
	}
	return CBaseFilter::NonDelegatingQueryInterface(riid, ppv);
}

CInPin.cpp

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

CInPin::CInPin(CFilter *pFilter, HRESULT *phr, LPCWSTR pPinName) : CBaseInputPin(NAME("In"), pFilter, pFilter, phr, pPinName)
{
	pCFilter = pFilter;
}

CInPin::~CInPin()
{

}

HRESULT CInPin::CheckMediaType(const CMediaType *pmt)
{
	if (pmt->majortype == MEDIATYPE_Video)
	{
		if (pmt->subtype == MEDIASUBTYPE_H264)return S_OK;
	}
	return S_FALSE;
}

HRESULT CInPin::SetMediaType(const CMediaType *pmt)
{
	IMFMediaType* pInType = NULL;
	HRESULT hr = MFCreateMediaTypeFromRepresentation(AM_MEDIA_TYPE_REPRESENTATION, (CMediaType*)pmt, &pInType);//从AM_MEDIA_TYPE结构创建媒体基础媒体类型
	hr = pCFilter->pH264Decoder->SetInputType(NULL, pInType, 0);//设置H264视频解码器输入媒体类型
	SafeRelease(&pInType);
	if (hr == S_OK)return CBasePin::SetMediaType(pmt);
	return S_FALSE;
}

STDMETHODIMP CInPin::Receive(IMediaSample *pSample)
{
	HRESULT hr;
	BYTE* pBy = NULL;
	hr = pSample->GetPointer(&pBy);//获取引脚样本缓冲区指针
	long len = pSample->GetActualDataLength();//获取有效数据长度
	REFERENCE_TIME star, end;
	hr = pSample->GetTime(&star, &end);//获取样本时间戳
	HRESULT Dhr = pSample->IsDiscontinuity();//是否有中断标志
	HRESULT Shr = pSample->IsSyncPoint();//是否有同步点标志

CreateBuffer:
	IMFMediaBuffer* pMFBuffer = NULL;
	hr = MFCreateMemoryBuffer(len, &pMFBuffer);//创建媒体基础缓冲区
	if (hr != S_OK || pMFBuffer == NULL)//如果创建失败
	{
		Sleep(1); goto CreateBuffer;//再次创建
	}
	BYTE* pData = NULL;
	hr = pMFBuffer->Lock(&pData, NULL, NULL);//锁定媒体基础缓冲区
	CopyMemory(pData, pBy, len);//从引脚样本缓冲区,复制数据到媒体基础样本缓冲区
	hr = pMFBuffer->Unlock();//解锁媒体基础缓冲区
	hr = pMFBuffer->SetCurrentLength((DWORD)len);//设置媒体基础缓冲区的数据长度

CreateSample:
	IMFSample* pMFSample = NULL;
	if (SUCCEEDED(hr))
	{
		hr = MFCreateSample(&pMFSample);//创建媒体基础样本
		if (hr != S_OK || pMFSample == NULL)//如果创建失败
		{
			Sleep(1); goto CreateSample;//再次创建
		}
	}
	hr = pMFSample->AddBuffer(pMFBuffer);//添加缓冲区到媒体基础样本
	REFERENCE_TIME dur = end - star;
	if (hr == S_OK)//
	{
		hr = pMFSample->SetSampleTime(star);//设置媒体基础样本显示时间
		hr = pMFSample->SetSampleDuration(dur);//设置媒体基础样本持续时间
		if (Dhr == S_OK)//如果有中断标志
		{
			hr = pMFSample->SetUINT32(MFSampleExtension_Discontinuity, 1);//设置中断标志
		}
		else
		{
			hr = pMFSample->SetUINT32(MFSampleExtension_Discontinuity, 0);
		}
		if (Shr == S_OK)//如果有同步点标志
		{
			hr = pMFSample->SetUINT32(MFSampleExtension_CleanPoint, 1);//指定关键帧
		}
		else
		{
			hr = pMFSample->SetUINT32(MFSampleExtension_CleanPoint, 0);
		}
	}
RePut:
	hr = pCFilter->pH264Decoder->ProcessInput(NULL, pMFSample, 0);//向解码器传递输入数据
	if (hr == S_OK)//如果传递输入成功
	{
		SafeRelease(&pMFBuffer); SafeRelease(&pMFSample);//释放媒体基础缓冲区,媒体基础样本
		return S_OK;//继续下一次传递输入
	}
	if (MF_E_NOTACCEPTING == hr)//如果不可以传递输入。MF_E_NOTACCEPTING表示已不能接收更多输入
	{
		GetOut();//获取解码器输出
		goto RePut;//传递输入失败时,需将此次的样本再次传递到输入
	}
	return S_OK;
}

HRESULT CInPin::GetOut()//获取解码器输出
{
CreateOutBuffer:
	IMFMediaBuffer* pMFOutBuffer = NULL;
	HRESULT hr = MFCreateMemoryBuffer(10000000, &pMFOutBuffer);//创建输出媒体基础缓冲区
	if (hr != S_OK || pMFOutBuffer == NULL)//如果创建失败
	{
		Sleep(1); goto CreateOutBuffer;//再次创建
	}
	BYTE* pD = NULL;
	hr = pMFOutBuffer->Lock(&pD, NULL, NULL);
CreateOutSample:
	IMFSample* pMFOutSample = NULL;
	if (SUCCEEDED(hr))
	{
		hr = MFCreateSample(&pMFOutSample);//创建输出媒体基础样本
		if (hr != S_OK || pMFOutSample == NULL)//如果创建失败
		{
			Sleep(1); goto CreateOutSample;//再次创建
		}
	}
	hr = pMFOutSample->AddBuffer(pMFOutBuffer);//添加缓冲区到媒体基础样本

	MFT_OUTPUT_DATA_BUFFER OD;
	OD.dwStreamID = NULL;
	OD.pSample = pMFOutSample;//须为解码器指定输出样本
	OD.dwStatus = 0;
	OD.pEvents = NULL;
	DWORD status = 0;
	hr = pCFilter->pH264Decoder->ProcessOutput(MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER, 1, &OD, &status);//获取解码器输出数据。数据将输出到刚才创建的输出样本的缓冲区内
	if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)//如果MFT需要更多的输入数据,此时已不可获取输出
	{
		SafeRelease(&pMFOutBuffer); SafeRelease(&pMFOutSample); SafeRelease(&OD.pEvents);//释放媒体基础缓冲区,媒体基础样
		return S_OK;
	}
	if (hr == S_OK)//如果成功获得输出数据
	{
		HRESULT hrA, hrB;
		UINT32 CleanPoint;
		hrA = pMFOutSample->GetUINT32(MFSampleExtension_CleanPoint, &CleanPoint);//是否为关键帧
		UINT32 Discontinuity;
		hrB = pMFOutSample->GetUINT32(MFSampleExtension_Discontinuity, &Discontinuity);//是否包含中断标志
		LONGLONG s, d;
		hr = pMFOutSample->GetSampleTime(&s);//获取编码器输出样本开始时间
		hr = pMFOutSample->GetSampleDuration(&d);//获取编码器输出样本持续时间
		DWORD L;
		hr = pMFOutSample->GetTotalLength(&L);//获取编码器输出样本有效数据长度
		IMediaSample *pOutSample = NULL;
		hr = pCFilter->pCOutPin->GetDeliveryBuffer(&pOutSample, NULL, NULL, 0);//获取一个空的输出引脚样本
		if (hr == S_OK)
		{
			BYTE* pOutBuffer = NULL;
			hr = pOutSample->GetPointer(&pOutBuffer);//获取输出引脚样本缓冲区指针
			LONG LEN = pOutSample->GetActualDataLength();
			LONGLONG Size = pOutSample->GetSize();
			if (pOutBuffer && pD && L <= 10000000)
				CopyMemory(pOutBuffer, pD, L);//从编码器输出样本缓冲区复制数据,到输出引脚样本缓冲区
			hr = pMFOutBuffer->Unlock();
			REFERENCE_TIME sOut = s, eOut = s + d;
			hr = pOutSample->SetTime(&sOut, &eOut);//设置输出引脚样本时间戳
			hr = pOutSample->SetActualDataLength(L);//设置输出引脚样本有效数据长度
			if (hrA == S_OK && CleanPoint)//如果是关键帧
			{
				hr = pOutSample->SetSyncPoint(TRUE);
			}
			else
			{
				hr = pOutSample->SetSyncPoint(FALSE);
			}
			if (hrB == S_OK && Discontinuity)//如果包含中断标志
			{
				hr = pOutSample->SetDiscontinuity(TRUE);
			}
			else
			{
				hr = pOutSample->SetDiscontinuity(FALSE);
			}
			hr = pCFilter->pCOutPin->Deliver(pOutSample);//输出引脚向下游发送样本
			pOutSample->Release();//释放输出引脚样本
		}
	}
	SafeRelease(&pMFOutBuffer); SafeRelease(&pMFOutSample); SafeRelease(&OD.pEvents);//释放媒体基础缓冲区,媒体基础样本
	goto CreateOutBuffer;//再次获取输出,直到不可获取为止
	return S_OK;
}

STDMETHODIMP CInPin::EndOfStream()
{
	return pCFilter->pCOutPin->DeliverEndOfStream();
}

STDMETHODIMP CInPin::BeginFlush()
{
	HRESULT hr = S_OK;
	hr = pCFilter->pH264Decoder->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, NULL);//通知解码器丢弃所有存储的数据
	hr = pCFilter->pCOutPin->DeliverBeginFlush();
	if (FAILED(hr))
		return hr;
	return CBaseInputPin::BeginFlush();
}

STDMETHODIMP CInPin::EndFlush()
{
	HRESULT hr = S_OK;
	hr = pCFilter->pCOutPin->DeliverEndFlush();
	if (FAILED(hr))
		return hr;
	return CBaseInputPin::EndFlush();
}

STDMETHODIMP CInPin::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
{
	HRESULT hr = S_OK;
	hr = pCFilter->pH264Decoder->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, NULL);//通知解码器丢弃所有存储的数据
	hr = pCFilter->pCOutPin->DeliverNewSegment(tStart, tStop, dRate);
	if (FAILED(hr))
		return hr;
	return CBaseInputPin::NewSegment(tStart, tStop, dRate);
}

COutPin.cpp

#include "DLL.h"

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

COutPin::~COutPin()
{
	SafeRelease(&m_pPosition);
}

STDMETHODIMP COutPin::NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
	CheckPointer(ppv, E_POINTER);
	ASSERT(ppv);
	*ppv = NULL;
	HRESULT hr = NOERROR;
	if (riid == IID_IMediaPosition || riid == IID_IMediaSeeking)
	{
		if (m_pPosition == NULL)
		{
			hr = CreatePosPassThru(GetOwner(), FALSE, (IPin *)pCFilter->pCInPin, &m_pPosition);
			if (FAILED(hr)) return hr;
		}
		return m_pPosition->QueryInterface(riid, ppv);
	}
	return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);
}

HRESULT COutPin::GetMediaType(int iPosition, CMediaType *pMediaType)
{
	if (iPosition < 0)return E_INVALIDARG;
	HRESULT hr;
	int index = 0;
	IMFMediaType* pMT = NULL;
	while (S_OK == pCFilter->pH264Decoder->GetOutputAvailableType(NULL, index, &pMT))//获取解码器所有允许的媒体类型
	{
		if (iPosition == index)
		{
			AM_MEDIA_TYPE* pmt;
			hr = pMT->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, (void**)&pmt);//从IMFMediaType媒体类型,转换为AM_MEDID_TYPE媒体类型结构
			*pMediaType = (CMediaType)*pmt;
			hr = pMT->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pmt);//调用方必须释放GetRepresentation获得的内存
			SafeRelease(&pMT);
			return S_OK;
		}
		index++;
	}
	if (iPosition > index)return VFW_S_NO_MORE_ITEMS;
	return S_FALSE;
}

HRESULT COutPin::CheckMediaType(const CMediaType *pmt)
{
	if (!pCFilter->pCInPin->IsConnected())
	{
		MessageBox(0, L"须先连接输入引脚", L"H264解码器", MB_OK); return S_FALSE;
	}
	if (pmt->majortype == MEDIATYPE_Video)
	{
		if (pmt->subtype == MEDIASUBTYPE_I420)return S_OK;
		if (pmt->subtype == MEDIASUBTYPE_IYUV)return S_OK;
		if (pmt->subtype == MEDIASUBTYPE_NV12)return S_OK;
		if (pmt->subtype == MEDIASUBTYPE_YUY2)return S_OK;
		if (pmt->subtype == MEDIASUBTYPE_YV12)return S_OK;
	}
	return S_FALSE;
}

HRESULT COutPin::SetMediaType(const CMediaType *pmt)
{
	HRESULT hr;
	AM_MEDIA_TYPE* pMt = (AM_MEDIA_TYPE*)pmt;
	IMFMediaType* pOut = NULL;
	hr = MFCreateMediaTypeFromRepresentation(AM_MEDIA_TYPE_REPRESENTATION, pMt, &pOut);//从AM_MEDIA_TYPE结构创建媒体基础媒体类型
	hr = pCFilter->pH264Decoder->SetOutputType(NULL, pOut, 0);//为解码器指定输出媒体类型
	if (hr == S_OK)
	{
		SafeRelease(&pOut);
		return CBasePin::SetMediaType(pmt);
	}
	else
		return S_FALSE;
}

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

HRESULT COutPin::Active()
{
	HRESULT hr = NOERROR;
	if (m_Connected == NULL)
		return NOERROR;
	if (m_pOutputQueue == NULL)
	{
		m_pOutputQueue = new COutputQueue(m_Connected, &hr, TRUE, FALSE);
		if (m_pOutputQueue == NULL)
			return E_OUTOFMEMORY;
		if (FAILED(hr))
		{
			delete m_pOutputQueue;
			m_pOutputQueue = NULL;
			return hr;
		}
	}
	CBaseOutputPin::Active();
	return NOERROR;
}

HRESULT COutPin::Inactive()
{
	if (m_pOutputQueue)
	{
		delete m_pOutputQueue;
		m_pOutputQueue = NULL;
	}
	CBaseOutputPin::Inactive();
	return NOERROR;
}

HRESULT COutPin::Deliver(IMediaSample *pMediaSample)
{
	if (m_pOutputQueue == NULL)
		return NOERROR;
	pMediaSample->AddRef();
	return m_pOutputQueue->Receive(pMediaSample);
}

HRESULT COutPin::DeliverEndOfStream()
{
	if (m_pOutputQueue == NULL)
		return NOERROR;
	m_pOutputQueue->EOS();
	return NOERROR;
}

HRESULT COutPin::DeliverBeginFlush()
{
	if (m_pOutputQueue == NULL)
		return NOERROR;
	m_pOutputQueue->BeginFlush();
	return NOERROR;
}

HRESULT COutPin::DeliverEndFlush()
{
	if (m_pOutputQueue == NULL)
		return NOERROR;
	m_pOutputQueue->EndFlush();
	return NOERROR;
}

HRESULT COutPin::DeliverNewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
{
	if (m_pOutputQueue == NULL)
		return NOERROR;
	m_pOutputQueue->NewSegment(tStart, tStop, dRate);
	return NOERROR;
}

STDMETHODIMP COutPin::Notify(IBaseFilter *pSender, Quality q)
{
	if (pCFilter->pCInPin->m_pQSink != NULL)
	{
		return pCFilter->pCInPin->m_pQSink->Notify(pCFilter, q);
	}
	else
	{
		HRESULT hr;
		IQualityControl * pIQC;
		hr = VFW_E_NOT_FOUND;
		if (pCFilter->pCInPin->m_Connected)
		{
			pCFilter->pCInPin->m_Connected->QueryInterface(IID_IQualityControl, (void**)&pIQC);
			if (pIQC != NULL)
			{
				hr = pIQC->Notify(pCFilter, q);
				pIQC->Release();
			}
		}
		return hr;
	}
	return NOERROR;
}

CPropertyPage1.cpp

#include "DLL.h"
#include "wchar.h"
#include "resource.h"
#include "stdio.h"
#include "wchar.h"
#include "commctrl.h "//列表视图控件使用
#include "dvdmedia.h"

CPropertyPage1::CPropertyPage1(IUnknown *pUnk) : CBasePropertyPage(NAME("属性页1"), pUnk, IDD_DIALOG1, IDS_STRING103)
{
	hFont1 = CreateFont(20, 0, 0, 0, FW_NORMAL, 0, 0, 0, CHINESEBIG5_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"微软雅黑");
	hFont2 = CreateFont(15, 0, 0, 0, FW_NORMAL, 0, 0, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"Cambria");
}

CPropertyPage1::~CPropertyPage1()
{

}

CUnknown * WINAPI CPropertyPage1::CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr)
{
	CPropertyPage1 *pNewObject = new CPropertyPage1(pUnk);
	if (pNewObject == NULL)
	{
		*pHr = E_OUTOFMEMORY;
	}
	return pNewObject;
}

HRESULT CPropertyPage1::OnConnect(IUnknown *pUnknown)
{
	if (pUnknown == NULL)
	{
		return E_POINTER;
	}
	ISpecifyPropertyPages* pPages = NULL;
	HRESULT hr = pUnknown->QueryInterface(IID_ISpecifyPropertyPages, (void**)&pPages);
	if (hr == S_OK)
	{
		pCFilter = (CFilter*)pPages; pPages->Release();
	}
	return hr;
}

HRESULT CPropertyPage1::OnDeactivate()
{
	return S_OK;
}

HRESULT CPropertyPage1::OnActivate(void)
{
	ListView_SetExtendedListViewStyleEx(hList, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);//整行选择,显示网格
	LVCOLUMN lvc;
	lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
	lvc.iSubItem = 0;
	lvc.pszText = L"输入或输出";
	lvc.cx = 140;
	lvc.fmt = LVCFMT_LEFT;
	ListView_InsertColumn(hList, 0, &lvc);//插入第1列
	lvc.iSubItem = 1;
	lvc.pszText = L"主要类型";
	lvc.cx = 120;
	ListView_InsertColumn(hList, 1, &lvc);//插入第2列
	lvc.iSubItem = 2;
	lvc.pszText = L"子类型";
	lvc.cx = 140;
	ListView_InsertColumn(hList, 2, &lvc);//插入第3列
	lvc.iSubItem = 3;
	lvc.pszText = L"格式类型";
	lvc.cx = 150;
	ListView_InsertColumn(hList, 3, &lvc);//插入第4列
	lvc.iSubItem = 4;
	lvc.pszText = L"";
	lvc.cx = 0;
	ListView_InsertColumn(hList, 4, &lvc);//插入第5列,此列不显示文本,包含格式块描述文本
	SetWindowTextW(hE1, L"输入允许的媒体类型:\r\nMEDIATYPE_Video::MEDIASUBTYPE_H264::FORMAT_MPEG2Video\r\n输出允许的媒体类型:\r\nMEDIATYPE_Video::MEDIASUBTYPE_NV12::FORMAT_VideoInfo2\r\nMEDIATYPE_Video::MEDIASUBTYPE_YV12::FORMAT_VideoInfo2\r\nMEDIATYPE_Video::MEDIASUBTYPE_IYUV::FORMAT_VideoInfo2\r\nMEDIATYPE_Video::MEDIASUBTYPE_I420::FORMAT_VideoInfo2\r\nMEDIATYPE_Video::MEDIASUBTYPE_YUY2::FORMAT_VideoInfo2");
	IMFMediaType *pType = NULL;//媒体类型
	if (pCFilter->pCInPin->IsConnected())//如果输入引脚已经连接
	{
		HRESULT hr = pCFilter->pH264Decoder->GetInputCurrentType(NULL, &pType);//获取输入引脚当前媒体类型
		AM_MEDIA_TYPE* PMT;
		hr = pType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, (void**)&PMT);//将IMFMediaType媒体类型转换为AM_MEDIA_TYPE结构表示的媒体类型
		if (hr == S_OK)
		{
			LVITEM lvI;
			lvI.mask = LVIF_TEXT;
			lvI.iItem = 0;
			lvI.iSubItem = 0;
			lvI.pszText = L"输入引脚媒体类型";
			ListView_InsertItem(hList, &lvI);//插入项,项文本“序号”
			LPWSTR lp1 = DefineFromGUID(PMT->majortype);
			ListView_SetItemText(hList, 0, 1, lp1);//设置子项1文本“主要类型”
			delete[] lp1;
			LPWSTR lp2 = DefineFromGUID(PMT->subtype);
			ListView_SetItemText(hList, 0, 2, lp2);//设置子项2文本“子类型”
			delete[] lp2;
			LPWSTR lp3 = DefineFromGUID(PMT->formattype);
			ListView_SetItemText(hList, 0, 3, lp3);//设置子项2文本“格式类型”
			delete[] lp3;
			LPWSTR lp4 = GetFormatString(PMT->pbFormat, PMT->formattype);//获取格式块描述字符串
			ListView_SetItemText(hList, 0, 4, lp4);//设置子项3文本“格式类型”
			delete[] lp4;
			hr = pType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, PMT);//释放GetRepresentation获得的内存
		}
		SafeRelease(&pType);//释放媒体类型
	}
	if (pCFilter->pCOutPin->IsConnected())//如果输出引脚已经连接
	{
		HRESULT hr = pCFilter->pH264Decoder->GetOutputCurrentType(NULL, &pType);//获取输入引脚媒体类型
		AM_MEDIA_TYPE* PMT;
		hr = pType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, (void**)&PMT);//将IMFMediaType媒体类型转换为AM_MEDIA_TYPE结构表示的媒体类型
		if (hr == S_OK)
		{
			LVITEM lvI;
			lvI.mask = LVIF_TEXT;
			lvI.iItem = 1;
			lvI.iSubItem = 0;
			lvI.pszText = L"输出引脚媒体类型";
			ListView_InsertItem(hList, &lvI);//插入项,项文本“序号”
			LPWSTR lp1 = DefineFromGUID(PMT->majortype);
			ListView_SetItemText(hList, 1, 1, lp1);//设置子项1文本“主要类型”
			delete[] lp1;
			LPWSTR lp2 = DefineFromGUID(PMT->subtype);
			ListView_SetItemText(hList, 1, 2, lp2);//设置子项2文本“子类型”
			delete[] lp2;
			LPWSTR lp3 = DefineFromGUID(PMT->formattype);
			ListView_SetItemText(hList, 1, 3, lp3);//设置子项2文本“格式类型”
			delete[] lp3;
			LPWSTR lp4 = GetFormatString(PMT->pbFormat, PMT->formattype);//获取格式块描述字符串
			ListView_SetItemText(hList, 1, 4, lp4);//设置子项3文本“格式类型”
			delete[] lp4;
			hr = pType->FreeRepresentation(AM_MEDIA_TYPE_REPRESENTATION, PMT);//释放GetRepresentation获得的内存
		}
		SafeRelease(&pType);//释放媒体类型
	}
	return 0;
}

INT_PTR CPropertyPage1::OnReceiveMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	hE1 = GetDlgItem(m_Dlg, IDC_EDIT3);
	hList = GetDlgItem(m_Dlg, IDC_LIST2);//获取列表视图控件窗口句柄
	hEdit = GetDlgItem(m_Dlg, IDC_EDIT2);
	RECT rect; int w; LPNMITEMACTIVATE lpnmitem;
	switch (uMsg)
	{
	case WM_INITDIALOG://初始化属性页对话框
		GetClientRect(m_Dlg, &rect);
		w = rect.right - rect.left;
		MoveWindow(hE1, 10, 10, w - 20, 170, 1); SendMessage(hE1, WM_SETFONT, (WPARAM)hFont1, (LPARAM)1);
		MoveWindow(hList, 10, 190, w - 20, 50, 1); SendMessage(hList, WM_SETFONT, (WPARAM)hFont2, (LPARAM)1);
		MoveWindow(hEdit, 10, 250, w - 20, 286, 1); SendMessage(hEdit, WM_SETFONT, (WPARAM)hFont1, (LPARAM)1);
		return TRUE;
	case WM_NOTIFY:
		lpnmitem = (LPNMITEMACTIVATE)lParam;
		if (lpnmitem->hdr.code == NM_CLICK && lpnmitem->hdr.idFrom == IDC_LIST2)//如果鼠标左键单击了列表某项
		{
			wchar_t wch[2000]; wmemset(wch, 0, 2000);
			ListView_GetItemText(lpnmitem->hdr.hwndFrom, lpnmitem->iItem, 4, wch, 2000);
			SetWindowText(hEdit, wch);
			return (INT_PTR)TRUE;
		}
		break;
	}
	return CBasePropertyPage::OnReceiveMessage(hwnd, uMsg, wParam, lParam);//让父类处理消息
}

WCHAR* CPropertyPage1::DefineFromGUID(GUID guid)//获取GUID定义字符串
{
	WCHAR* pW = new WCHAR[200];
	if (guid == MEDIATYPE_Video)
	{
		wcscpy_s(pW, 200, L"MEDIATYPE_Video");
		return pW;
	}
	if (guid == MEDIASUBTYPE_I420)
	{
		wcscpy_s(pW, 200, L"MEDIASUBTYPE_I420");
		return pW;
	}
	if (guid == MEDIASUBTYPE_IYUV)
	{
		wcscpy_s(pW, 200, L"MEDIASUBTYPE_IYUV");
		return pW;
	}
	if (guid == MEDIASUBTYPE_NV12)
	{
		wcscpy_s(pW, 200, L"MEDIASUBTYPE_NV12");
		return pW;
	}
	if (guid == MEDIASUBTYPE_YUY2)
	{
		wcscpy_s(pW, 200, L"MEDIASUBTYPE_YUY2");
		return pW;
	}
	if (guid == MEDIASUBTYPE_YV12)
	{
		wcscpy_s(pW, 200, L"MEDIASUBTYPE_YV12");
		return pW;
	}
	if (guid == MEDIASUBTYPE_H264)
	{
		wcscpy_s(pW, 200, L"MEDIASUBTYPE_H264");
		return pW;
	}
	if (guid == FORMAT_VideoInfo)
	{
		wcscpy_s(pW, 200, L"FORMAT_VideoInfo");
		return pW;
	}
	if (guid == FORMAT_VideoInfo2)
	{
		wcscpy_s(pW, 200, L"FORMAT_VideoInfo2");
		return pW;
	}
	if (guid == FORMAT_MPEG2Video)
	{
		wcscpy_s(pW, 200, L"FORMAT_MPEG2Video");
		return pW;
	}
	delete[] pW;
	return NULL;
}

WCHAR* CPropertyPage1::GetFormatString(void* p, GUID guid)//获取格式块描述字符串
{
	char ch[5]; ch[4] = 0;
	WCHAR* lp = new wchar_t[2000];
	if (guid == FORMAT_VideoInfo2)
	{
		VIDEOINFOHEADER2* pVH = (VIDEOINFOHEADER2*)p;
		CopyMemory(ch, &pVH->bmiHeader.biCompression, 4);
		wchar_t wch[5];
		MultiByteToWideChar(CP_ACP, MB_COMPOSITE, ch, -1, wch, 5);
		swprintf_s(lp, 2000, L"VIDEOINFOHEADER2结构\r\nrcSource={left=%d, top=%d, right=%d, bottom=%d};//源矩形\r\nrcTarget={left=%d, top=%d, right=%d, bottom=%d};//目标矩形\r\ndwBitRate=%u;//传输率\r\ndwBitErrorRate=%u;//错误率\r\nAvgTimePerFrame=%I64d;//帧持续时间,单位100纳秒\r\ndwInterlaceFlags=%u;//扫描方式\r\ndwCopyProtectFlags=%u;//流复制限制标志\r\ndwPictAspectRatioX=%u;//长宽比的X维度\r\ndwPictAspectRatioY=%u;//长宽比的Y维度\r\ndwControlFlags=%u;//控制标志\r\ndwReserved1=%u;//保留\r\ndwReserved2=%u;//保留\r\nbmiHeader=VIDEOINFOHEADER2;//BITMAPINFOHEADER2结构\r\nbmiHeader.biSize=%u;//BITMAPINFOHEADER结构的大小,单位字节\r\nbmiHeader.biWidth=%d;//图像宽度,单位像素\r\nbmiHeader.biHeight=%d;//图像的高度,单位像素\r\nbmiHeader.biPlanes=%d;//目标设备的平面数\r\nbmiHeader.biBitCount=%d;//每个像素的位数\r\nbmiHeader.biCompression=%u;//编码方式,FOURCC=%s\r\nbmiHeader.biSizeImage=%u;//图像的大小,单位字节\r\nbmiHeader.biXPelsPerMeter=%d;//图像目标设备的水平分辨率,以每米像素为单位\r\nbmiHeader.biYPelsPerMeter=%d;//图像目标设备的垂直分辨率,以每米像素为单位\r\nbmiHeader.biClrUsed=%u;//颜色表中颜色的数量\r\nbmiHeader.biClrImportant=%u;//重要颜色的数量",
			pVH->rcSource.left, pVH->rcSource.top, pVH->rcSource.right, pVH->rcSource.bottom,
			pVH->rcTarget.left, pVH->rcTarget.top, pVH->rcTarget.right, pVH->rcTarget.bottom,
			pVH->dwBitRate,
			pVH->dwBitErrorRate,
			pVH->AvgTimePerFrame,
			pVH->dwInterlaceFlags,
			pVH->dwCopyProtectFlags,
			pVH->dwPictAspectRatioX,
			pVH->dwPictAspectRatioY,
			pVH->dwControlFlags,
			pVH->dwReserved1,
			pVH->dwReserved2,
			pVH->bmiHeader.biSize,
			pVH->bmiHeader.biWidth,
			pVH->bmiHeader.biHeight,
			pVH->bmiHeader.biPlanes,
			pVH->bmiHeader.biBitCount,
			pVH->bmiHeader.biCompression,
			wch,
			pVH->bmiHeader.biSizeImage,
			pVH->bmiHeader.biXPelsPerMeter,
			pVH->bmiHeader.biYPelsPerMeter,
			pVH->bmiHeader.biClrUsed,
			pVH->bmiHeader.biClrImportant
			);
		return lp;
	}
	else 	if (guid == FORMAT_VideoInfo)
	{
		VIDEOINFOHEADER* pVH = (VIDEOINFOHEADER*)p;
		CopyMemory(ch, &pVH->bmiHeader.biCompression, 4);
		wchar_t wch[5];
		MultiByteToWideChar(CP_ACP, MB_COMPOSITE, ch, -1, wch, 5);
		swprintf_s(lp, 2000, L"VIDEOINFOHEADER结构\r\nrcSource={left=%d, top=%d, right=%d, bottom=%d};//源矩形\r\nrcTarget={left=%d, top=%d, right=%d, bottom=%d};//目标矩形\r\ndwBitRate=%u;//传输率\r\ndwBitErrorRate=%u;//错误率\r\nAvgTimePerFrame=%I64d;//帧持续时间。单位100纳秒\r\nbmiHeader=VIDEOINFOHEADER;//BITMAPINFOHEADER结构\r\nbmiHeader.biSize=%u;//BITMAPINFOHEADER结构的大小,单位字节\r\nbmiHeader.biWidth=%d;//图像宽度,单位像素\r\nbmiHeader.biHeight=%d;//图像的高度,单位像素\r\nbmiHeader.biPlanes=%d;//目标设备的平面数\r\nbmiHeader.biBitCount=%d;//每个像素的位数\r\nbmiHeader.biCompression=%u;//编码方式,FOURCC=%s\r\nbmiHeader.biSizeImage=%u;//图像的大小,单位字节\r\nbmiHeader.biXPelsPerMeter=%d;//图像目标设备的水平分辨率,以每米像素为单位\r\nbmiHeader.biYPelsPerMeter=%d;//图像目标设备的垂直分辨率,以每米像素为单位\r\nbmiHeader.biClrUsed=%u;//颜色表中颜色的数量\r\nbmiHeader.biClrImportant=%u;//重要颜色的数量",
			pVH->rcSource.left, pVH->rcSource.top, pVH->rcSource.right, pVH->rcSource.bottom,
			pVH->rcTarget.left, pVH->rcTarget.top, pVH->rcTarget.right, pVH->rcTarget.bottom,
			pVH->dwBitRate,
			pVH->dwBitErrorRate,
			pVH->AvgTimePerFrame,
			pVH->bmiHeader.biSize,
			pVH->bmiHeader.biWidth,
			pVH->bmiHeader.biHeight,
			pVH->bmiHeader.biPlanes,
			pVH->bmiHeader.biBitCount,
			pVH->bmiHeader.biCompression,
			wch,
			pVH->bmiHeader.biSizeImage,
			pVH->bmiHeader.biXPelsPerMeter,
			pVH->bmiHeader.biYPelsPerMeter,
			pVH->bmiHeader.biClrUsed,
			pVH->bmiHeader.biClrImportant
			);
		return lp;
	}
	else if (guid == FORMAT_MPEG2Video)
	{
		MPEG2VIDEOINFO* pMV = (MPEG2VIDEOINFO*)p;
		CopyMemory(ch, &pMV->hdr.bmiHeader.biCompression, 4);
		wchar_t wch[5];
		MultiByteToWideChar(CP_ACP, MB_COMPOSITE, ch, -1, wch, 5);
		swprintf_s(lp, 2000, L"MPEG2VIDEOINFO结构\r\nhdr=VIDEOINFOHEADER2;//VIDEOINFOHEADER2结构\r\nhdr.rcSource={left=%d, top=%d, right=%d, bottom=%d};//源矩形\r\nhdr.rcTarget={left=%d, top=%d, right=%d, bottom=%d};//目标矩形\r\nhdr.dwBitRatee=%u;//传输率\r\nhdr.dwBitErrorRate=%u;//错误率\r\nhdr.AvgTimePerFrame=%I64d;//帧持续时间,单位100纳秒\r\nhdr.bmiHeader=VIDEOINFOHEADER;//BITMAPINFOHEADER结构\r\nhdr.bmiHeader.biSize=%u;//BITMAPINFOHEADER结构的大小,单位字节\r\nhdr.bmiHeader.biWidth=%d;//图像宽度,单位像素\r\nhdr.bmiHeader.biHeight=%d;//图像的高度,单位像素\r\nhdr.bmiHeader.biPlanes=%d;//目标设备的平面数\r\nhdr.bmiHeader.biBitCount=%d;//每个像素的位数\r\nhdr.bmiHeader.biCompression=%u;//编码方式,FOURCC=%s\r\nhdr.bmiHeader.biSizeImage=%u;//图像的大小,单位字节\r\nhdr.bmiHeader.biXPelsPerMeter=%d;//图像目标设备的水平分辨率,以每米像素为单位\r\nhdr.bmiHeader.biYPelsPerMeter=%d;//图像目标设备的垂直分辨率,以每米像素为单位\r\nhdr.bmiHeader.biClrUsed=%u;//颜色表中颜色的数量\r\nhdr.bmiHeader.biClrImportant=%u;//重要颜色的数量\r\ndwStartTimeCode=%u;//开始时间码\r\ncbSequenceHeader=%u;//序列头的长度,以字节为单位\r\ndwProfile=%u;//MPEG-2配置文件\r\ndwLevel=%u;//MPEG-2级别\r\ndwFlags=%u;//标志位\r\ndwSequenceHeader=0x%0*x;//序列头数组地址",
			pMV->hdr.rcSource.left, pMV->hdr.rcSource.top, pMV->hdr.rcSource.right, pMV->hdr.rcSource.bottom,
			pMV->hdr.rcTarget.left, pMV->hdr.rcTarget.top, pMV->hdr.rcTarget.right, pMV->hdr.rcTarget.bottom,
			pMV->hdr.dwBitRate,
			pMV->hdr.dwBitErrorRate,
			pMV->hdr.AvgTimePerFrame,
			pMV->hdr.bmiHeader.biSize,
			pMV->hdr.bmiHeader.biWidth,
			pMV->hdr.bmiHeader.biHeight,
			pMV->hdr.bmiHeader.biPlanes,
			pMV->hdr.bmiHeader.biBitCount,
			pMV->hdr.bmiHeader.biCompression,
			wch,
			pMV->hdr.bmiHeader.biSizeImage,
			pMV->hdr.bmiHeader.biXPelsPerMeter,
			pMV->hdr.bmiHeader.biYPelsPerMeter,
			pMV->hdr.bmiHeader.biClrUsed,
			pMV->hdr.bmiHeader.biClrImportant,
			pMV->dwStartTimeCode,
			pMV->cbSequenceHeader,
			pMV->dwProfile,
			pMV->dwLevel,
			pMV->dwFlags,
			8,
			(DWORD)pMV->dwSequenceHeader
			);
		return lp;
	}
	delete[] lp;
	return NULL;
}

CPropertyPage2.cpp

#include "DLL.h"
#include "resource.h"
#include "wchar.h"
#include "commctrl.h "//列表视图控件使用
#include "stdio.h"
#include "dvdmedia.h"

CPropertyPage2::CPropertyPage2(IUnknown *pUnk) : CBasePropertyPage(NAME("属性页2"), pUnk, IDD_DIALOG2, IDS_STRING102)
{
	hFont1 = CreateFont(18, 0, 0, 0, FW_NORMAL, 0, 0, 0, CHINESEBIG5_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, L"微软雅黑");
}

CPropertyPage2::~CPropertyPage2()
{

}

CUnknown * WINAPI CPropertyPage2::CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr)
{
	CPropertyPage2 *pNewObject = new CPropertyPage2(pUnk);
	if (pNewObject == NULL)
	{
		*pHr = E_OUTOFMEMORY;
	}
	return pNewObject;
}

HRESULT CPropertyPage2::OnConnect(IUnknown *pUnknown)
{
	if (pUnknown == NULL)
	{
		return E_POINTER;
	}
	ISpecifyPropertyPages* pPages = NULL;
	HRESULT hr = pUnknown->QueryInterface(IID_ISpecifyPropertyPages, (void**)&pPages);
	if (hr == S_OK)
	{
		pCFilter = (CFilter*)pPages; pPages->Release();
	}
	return hr;
}

HRESULT CPropertyPage2::OnActivate(void)
{
	hText1 = GetDlgItem(m_Dlg, IDC_STATIC1);//获取控件句柄
	hText2 = GetDlgItem(m_Dlg, IDC_STATIC2);
	hText3 = GetDlgItem(m_Dlg, IDC_STATIC3);
	hCBox1 = GetDlgItem(m_Dlg, IDC_COMBO1);
	hCBox2 = GetDlgItem(m_Dlg, IDC_COMBO3);
	hE3 = GetDlgItem(m_Dlg, IDC_EDIT5);
	hBtn = GetDlgItem(m_Dlg, IDC_BUTTON1);
	int w, w1; RECT rect;
	GetClientRect(m_Dlg, &rect);
	w = rect.right - rect.left; w1 = (w - 30) / 2;
	MoveWindow(hText1, 10, 10, w1, 20, 0); MoveWindow(hCBox1, w1 + 20, 10, w1, 62, 0);//定位控件
	MoveWindow(hText2, 10, 10 + 30, w1, 20, 0); MoveWindow(hCBox2, w1 + 20, 10 + 30, w1, 62, 0);
	MoveWindow(hText3, 10, 10 + 30 * 2, w1, 20, 0); MoveWindow(hE3, w1 + 20, 10 + 30 * 2, w1, 20, 0);
	MoveWindow(hBtn, w1 + 20, 100, 80, 20, 0);
	SendMessage(hText1, WM_SETFONT, (WPARAM)hFont1, (LPARAM)1); SendMessage(hCBox1, WM_SETFONT, (WPARAM)hFont1, (LPARAM)1);//设置控件字体
	SendMessage(hText2, WM_SETFONT, (WPARAM)hFont1, (LPARAM)1); SendMessage(hCBox2, WM_SETFONT, (WPARAM)hFont1, (LPARAM)1);
	SendMessage(hText3, WM_SETFONT, (WPARAM)hFont1, (LPARAM)1); SendMessage(hE3, WM_SETFONT, (WPARAM)hFont1, (LPARAM)1);
	SendMessage(hBtn, WM_SETFONT, (WPARAM)hFont1, (LPARAM)1);
	//添加组合框项
	ComboBox_AddString(hCBox1, L"禁用"); ComboBox_AddString(hCBox1, L"启用");
	ComboBox_AddString(hCBox2, L"禁用"); ComboBox_AddString(hCBox2, L"启用");

	Init = TRUE;//标记正在初始化控件,此时不希望响应控件通知
	if (pCFilter->Default1 == UINT_MAX)//如果没有获取默认值,获取默认值
	{
		GetValue();//获取实际值
		pCFilter->Default1 = pCFilter->Param1; pCFilter->Default2 = pCFilter->Param2; pCFilter->Default3 = pCFilter->Param3; //给默认值赋值
	}
	else//如果已获取默认值
	{
		SetValue();//根据实际值初始化控件
	}
	Init = FALSE;
	return S_OK;
}

INT_PTR CPropertyPage2::OnReceiveMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg)
	{
	case WM_COMMAND:
		if (LOWORD(wParam) == IDC_BUTTON1 && HIWORD(wParam) == BN_CLICKED)//单击“使用默认”按钮
		{
			SetDefault();//将所有设置置为默认值
			return (INT_PTR)TRUE;
		}
		if (Init)return (INT_PTR)TRUE;//初始化时,不响应控件通知
		if (LOWORD(wParam) == IDC_COMBO1 && HIWORD(wParam) == CBN_SELCHANGE)//1.“启用或禁用硬件加速”组合框选择改变
		{
			int Sel = ComboBox_GetCurSel((HWND)lParam);//获取组合框当前选择
			if (Sel != -1)
			{
				pCFilter->Param1 = Sel;
				SetDirty();
			}
			return (INT_PTR)TRUE;
		}
		if (LOWORD(wParam) == IDC_COMBO3 && HIWORD(wParam) == CBN_SELCHANGE)//2.“启用或禁用缩略图生成模式”组合框选择改变
		{
			int Sel = ComboBox_GetCurSel((HWND)lParam);//获取组合框当前选择
			if (Sel != -1)
			{
				pCFilter->Param2 = Sel;
				SetDirty();
			}
			return (INT_PTR)TRUE;
		}
		if (LOWORD(wParam) == IDC_EDIT5 && HIWORD(wParam) == EN_CHANGE)//3.“解码器使用的工作线程数”编辑框文本改变
		{
			BOOL T;
			INT nt = GetDlgItemInt(m_Dlg, IDC_EDIT5, &T, 1);
			pCFilter->Param3 = nt;
			SetDirty();
			return (INT_PTR)TRUE;
		}
		break;
	}
	return CBasePropertyPage::OnReceiveMessage(hwnd, uMsg, wParam, lParam);//让父类处理消息
}

HRESULT CPropertyPage2::OnApplyChanges(void)//应用更改
{
	HRESULT hr; BOOL B[3] = { 0,0,0 };
	VARIANT var;
	if (IsWindowEnabled(hCBox1))
	{
		var.vt = 19; var.uintVal = pCFilter->Param1;
		hr = pCFilter->pAPI->SetValue(&CODECAPI_AVDecVideoAcceleration_H264, &var); //1.启用或禁用硬件加速
		VariantClear(&var);
		if (hr != S_OK)B[0] = TRUE;
	}
	if (IsWindowEnabled(hCBox2))
	{
		var.vt = 19; var.uintVal = pCFilter->Param2;
		hr = pCFilter->pAPI->SetValue(&CODECAPI_AVDecVideoThumbnailGenerationMode, &var); //2.启用或禁用缩略图生成模式
		VariantClear(&var);
		if (hr != S_OK)B[1] = TRUE;
	}
	if (IsWindowEnabled(hE3))
	{
		var.vt = 3; var.iVal = pCFilter->Param3;
		hr = pCFilter->pAPI->SetValue(&CODECAPI_AVDecNumWorkerThreads, &var); //3.解码器使用的工作线程数
		VariantClear(&var);
		if (hr != S_OK)B[2] = TRUE;
	}
	BOOL Error = FALSE;
	char ch[2000]; memset(ch, 0, 2000);
	for (int i = 0; i < 3; i++)
	{
		if (B[i])
		{
			char ch1[50];
			sprintf_s(ch1, 50, "第%d项设置失败\r\n", i + 1);
			strcat_s(ch, 2000, ch1);
			Error = TRUE;
		}
	}
	if (Error)
		MessageBoxA(0, ch, "H264视频解码器", MB_OK);
	return S_OK;
}

HRESULT CPropertyPage2::SetDefault()//将所有项置为默认值
{
	pCFilter->Param1 = pCFilter->Default1; pCFilter->Param2 = pCFilter->Default2; pCFilter->Param3 = pCFilter->Default3;
	SetValue();
	SetDirty();
	return S_OK;
}

HRESULT CPropertyPage2::SetValue()//根据实际值初始化控件
{
	if (pCFilter->Param1 != UINT_MAX)//1.启用或禁用硬件加速
	{
		ComboBox_SetCurSel(hCBox1, pCFilter->Param1);
	}
	if (pCFilter->Param2 != UINT_MAX)//2.启用或禁用缩略图生成模式
	{
		ComboBox_SetCurSel(hCBox2, pCFilter->Param2);
	}
	if (pCFilter->Param3 != INT_MAX)//3.解码器使用的工作线程数
	{
		SetDlgItemInt(m_Dlg, IDC_EDIT5, pCFilter->Param3, 1);
	}
	if (pCFilter->pCInPin->IsConnected())//如果输入引脚已经连接,禁用所有控件
	{
		EnableWindow(hCBox1, FALSE); EnableWindow(hCBox2, FALSE); EnableWindow(hE3, FALSE); EnableWindow(hBtn, FALSE);
	}
	return S_OK;
}

HRESULT CPropertyPage2::GetValue()//获取实际值
{
	VARIANT var;
	HRESULT hr = pCFilter->pAPI->GetValue(&CODECAPI_AVDecVideoAcceleration_H264, &var); //启用或禁用硬件加速
	if (hr == S_OK)
	{
		pCFilter->Param1 = var.uintVal; VariantClear(&var);
		ComboBox_SetCurSel(hCBox1, pCFilter->Param1);
	}
	VariantClear(&var);
	hr = pCFilter->pAPI->GetValue(&CODECAPI_AVDecVideoThumbnailGenerationMode, &var); //启用或禁用缩略图生成模式
	if (hr == S_OK)
	{
		pCFilter->Param2 = var.uintVal; VariantClear(&var);
		ComboBox_SetCurSel(hCBox2, pCFilter->Param2);
	}
	VariantClear(&var);
	hr = pCFilter->pAPI->GetValue(&CODECAPI_AVDecNumWorkerThreads, &var); //解码器使用的工作线程数
	if (hr == S_OK)
	{
		pCFilter->Param3 = var.uintVal; VariantClear(&var);
		SetDlgItemInt(m_Dlg, IDC_EDIT5, pCFilter->Param3, 1);
	}
	VariantClear(&var);
	if (pCFilter->pCInPin->IsConnected())//如果输入引脚已经连接,禁用所有控件
	{
		EnableWindow(hCBox1, FALSE); EnableWindow(hCBox2, FALSE); EnableWindow(hE3, FALSE); EnableWindow(hBtn, FALSE);
	}
	return S_OK;
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是爬取某音评论区的 Python 代码: ```python import requests import json # 设置请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'} # 模拟登录并获取cookies session = requests.session() login_url = 'https://passport.xiaomi.com/oauth2/login' session.get(login_url, headers=headers) login_api = 'https://account.xiaomi.com/pass/serviceLoginAuth2?_json=true' post_data = { 'sid': 'passport.xiaomi.com', 'callback': 'https://www.xiaomiyoupin.com/callback', 'qs': '%3FredirectUrl%3Dhttps%253A%252F%252Fm.xiaomiyoupin.com%252Fcomment%252Flist%253FgoodsId%253D118280%2526pageIndex%253D1%2526pageSize%253D10', '_sign': 'yR4M%2Fq%2BMFbqzFYjxZyJ3n9yMx7M%3D', 'serviceParam': '{"checkSafePhone":false}', 'service': 'mipay', 'loginTime': '1629805765', 'showExtra': 'true', 'callbackUrl': 'https://www.xiaomiyoupin.com/callback', 'qsStamp': 'eyJpdiI6InRnWU9pV1B0NzJQcHRoV3h1Zll1TlE9PSIsInZhbHVlIjoiWnZPRjN0bGY1QWpCQng5UWtWc1p4Zz09IiwibWFjIjoiYTJhMzg1ZjdhZjIwNmYzYzk0ZGJmMDY1ZjhhODM3YjQ0MjBhNjQ3YzhlZjM2NzczZjhiZmE5OGJjYjczMjBjZSJ9', 'sidTimeout': '1h', '_json': 'true', 'qsToken': 'eyJpdiI6IjJiNTdUZ21haE1aZzZmQ0xlQXh4dEE9PSIsInZhbHVlIjoiYStGXC9WQzBtRmZ1NU1Wb0tqT1ZUZz09IiwibWFjIjoiYzY2NTk0MjRlNDg0ZmY0ODRmNzU1ZGEyNTUzNzAzNzIzOWUyMzgyMjYzYjlkNjQ2ZDQ1MmRjZjNiNTQ2ZjEyZCJ9', 'deviceId': 'c7b4f0e9-b9da-31da-9c77-9b15a6b64a14', 'pwdMethod': 'pwd', 'user': 'your_account', # 替换成你的账号 'hash': 'your_password' # 替换成你的密码 } session.post(login_api, headers=headers, data=post_data) # 获取评论列表 comment_url = 'https://m.xiaomiyoupin.com/comment/list?goodsId=118280&pageIndex=1&pageSize=10' response = session.get(comment_url, headers=headers) if response.status_code == 200: result = json.loads(response.text) comments = result['data']['list'] for comment in comments: print(comment['content']) else: print('获取评论失败') ``` 需要注意的是,这个代码中的 `your_account` 和 `your_password` 需要替换成你自己的某音账号和密码。同时,由于某音评论区需要登录才能访问,所以在代码中我们先模拟登录并获取 cookies,然后再通过 cookies 访问评论列表。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

h3974

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值