下载本过滤器DLL
本过滤器渲染未压缩的视频流,将视频呈现在过滤器窗口中。
过滤器信息
过滤器名称:视频渲染
过滤器GUID:{F783F06D-3E50-4E3F-B5FD-8F279619C836}
DLL注册函数名:DllRegisterServer
删除注册函数名:DllUnregisterServer
过滤器接口:IBaseFilter,IBasicVideo,IMediaSeeking,IQualProp,IQualityControl,ISpecifyPropertyPages,IVideoWindow
过滤器有1个输入引脚。
输入引脚标识:In
输入引脚接口:IOverlay,IPin,IQualityControl
输入引脚媒体类型:
主要类型:MEDIATYPE_Video
子类型:
MEDIASUBTYPE_ARGB32
MEDIASUBTYPE_RGB32
MEDIASUBTYPE_RGB24
MEDIASUBTYPE_RGB555
MEDIASUBTYPE_RGB565
MEDIASUBTYPE_RGB8
过滤器的使用
1.可以使用CoCreateInstance函数创建过滤器:
IGraphBuilder* pGraph=NULL;
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pGraph));//创建过滤器图管理器
IBaseFilter pFilter = NULL;
GUID guid = {0xf783f06d, 0x3e50, 0x4e3f, 0xb5, 0xfd, 0x8f, 0x27, 0x96, 0x19, 0xc8, 0x36};
hr = CoCreateInstance(guid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFilter));//创建本视频渲染过滤器
hr = pGraph->AddFilter(pFilter, L"视频渲染");//加入过滤器图
IPin pPin = NULL;
hr = pFilter->FindPin(L"In", &pPin);//获取视频渲染过滤器引脚
2.由于过滤器视频窗口是弹出窗口,如果在应用程序中使用本过滤器,须将视频窗口设置为软件界面的子窗口,并指定大小和位置:
IVideoWindow* pIVideoWindow = NULL;
hr = pFilter->QueryInterface(IID_IVideoWindow, (void**)&pIVideoWindow);//获取IVideoWindow接口
hr = pIVideoWindow->put_WindowStyle(WS_CHILD);//指定窗口样式为子窗口
hr = pIVideoWindow->put_Owner((OAHWND)GetSafeHwnd());//指定父窗口
hr = pIVideoWindow->SetWindowPosition(0, 0, 600, 400);//设置视频窗口的大小和位置
//pIVideoWindow->Release();//接口都有引用计数,接口使用完成后须释放
3.此方法会改变视频画面的大小和宽高比,如果不希望改变宽高比,根据视频的原始宽高比,计算出目标矩形的大小和位置,重新设置目标矩形:
IBasicVideo* pIBasicVideo=NULL;
hr=pFilter->QueryInterface(IID_IBasicVideo, (void**)&pIBasicVideo);//获取IBasicVideo接口
LONG w,h;
hr=pIBasicVideo->get_VideoWidth(&w);//获取视频画面宽度
hr=pIBasicVideo->get_VideoHeight(&h);//获取视频画面高度
double f=(double)w/(double)h;//获取视频图像宽高比
LONG mW=600,mH=400;
double p=(double)mW/(double)mH;//获取视频窗口宽高比
if(f>p)//如果视频图像宽高比大于窗口
{
w=mW;h=(LONG)(w/f);
}
else//如果小于或等于
{
h=mH;w=(LONG)(f*h);
}
hr=pIBasicVideo->SetDestinationPosition((mW-w)/2,(mH-h)/2,w,h);//设置目标矩形
pIBasicVideo->Release();
4.可能还会使用到全屏播放,视频窗口将占满全部屏幕:(取消上面的代码)
hr=pIVideoWindow->put_FullScreenMode(1);//全屏播放
hr=pIVideoWindow->put_FullScreenMode(0);//退出全屏播放
5.在过滤器图编辑器中使用全屏播放,按F2键;退出全屏按Esc键。
6.要获取视频的时长,设置播放的当前位置,使用下面方法:
IMediaSeeking* m_pSeek = NULL;
hr = pFilter->QueryInterface(IID_IMediaSeeking, (void**)&m_pSeek);//获取IMediaSeeking接口
LONGLONG LL; //保存视频时长,单位100纳秒
hr = m_pSeek->GetDuration(&LL);//获取流播放时间的总长度,单位100纳秒
int Len = (int)(LL / 10000000);//转换为秒单位
int ho = Len / 60 / 60;//时
Len %= 60 * 60;
int m = Len / 60;//分
Len %= 60;
int s = Len;//秒
CString Len_str; Len_str.Format(_T(“%0d : %0d : %0*d”), 2, ho, 2, m, 2, s);
SetDlgItemText(IDC_EDIT1, Len_str);//显示在编辑框中
LONGLONG CUR = 20 * 10000000;//指定从20秒位置播放
hr= m_pSeek->SetPositions(&CUR, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);//指定播放位置
过滤器开发过程
本过滤器DLL代码是从Windows SDK 7.1示例“sampvid”修改而来。
CFilter为过滤器类。从CBaseVideoRenderer基础类和ISpecifyPropertyPages接口派生。ISpecifyPropertyPages用于实现过滤器属性页。CBaseVideoRenderer实现了IQualProp,IQualityControl,IBaseFilter接口;IBaseFilter为过滤器接口,IQualProp接口用于从视频渲染器中检索性能信息,IQualityControl接口用于质量控制。
CPin为视频输入引脚类。从CRendererInputPin基础类和IOverlay接口派生。CRendererInputPin实现了IPin,IQualityControl接口。IOverlay接口只实现了一个方法,获取视频窗口句柄。视频窗口类的IVideoWindow的方法已经可以满足对视频窗口的所有操作要求,但考虑到可能某些情况下,可能还需使用视频窗口句柄,因此添加了此方法。
CVideoWindow为视频窗口类。从CBaseControlWindow和CBaseControlVideo基础类派生。CBaseControlWindow实现了IVideoWindow接口,用于对视频窗口的控制。CBaseControlVideo实现了IBasicVideo接口,用于对视频画面源矩形,目标矩形的控制。
CQualityProperties为属性页类。从CBasePropertyPage基础类派生。用于实现属性页。
视频绘制使用CDrawImage类的DrawImage方法在视频窗口上绘制视频帧。当前时间未到样本的渲染时间,会等待;当前时间等于渲染时间时,绘制视频帧到窗口。如果当前时间已大于渲染时间,丢弃1个帧,直接绘制下一帧。此种情况只在视频画面较大和计算机性能较差的情况下出现。
下面是本过滤器DLL的全部代码
DLL模块定义文件:VideoRenderer.def
LIBRARY VideoRenderer.dll
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
DLL头文件:VideoRenderer.h
#include "streams.h"
#include "initguid.h"
#if _DEBUG
#pragma comment(lib, "Strmbasd.lib")//C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\multimedia\directshow\baseclasses\Debug\strmbasd.lib
#else
#pragma comment(lib, "Strmbase.lib")//C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\multimedia\directshow\baseclasses\Release\strmbase.lib
#endif
#pragma comment(lib, "Winmm.lib")//C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\Winmm.lib
#pragma comment(lib, "msvcrt.lib")//C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\lib\msvcrt.lib
// {F783F06D-3E50-4E3F-B5FD-8F279619C836}
DEFINE_GUID(CLSID_Renderer,
0xf783f06d, 0x3e50, 0x4e3f, 0xb5, 0xfd, 0x8f, 0x27, 0x96, 0x19, 0xc8, 0x36);
// {4311F831-CF04-4E00-984C-75A471F8E64F}
DEFINE_GUID(CLSID_SampleQuality,
0x4311f831, 0xcf04, 0x4e00, 0x98, 0x4c, 0x75, 0xa4, 0x71, 0xf8, 0xe6, 0x4f);
#define IDD_QUALITY 150 // Dialog resource
#define IDD_QDRAWN 171 // Frames played
#define IDD_QDROPPED 172 // Frames dropped
#define IDD_QAVGFRM 174 // Average frame rate achieved
#define IDD_QJITTER 175 // Average frame jitter
#define IDD_QSYNCAVG 176 // Average sync offset
#define IDD_QSYNCDEV 177 // Std dev sync offset
#define IDS_NAME 178 // Quality dialog name
class CQualityProperties : public CBasePropertyPage//属性页类定义
{
IQualProp *m_pQualProp; //IQualProp接口,从视频渲染器中检索性能信息
int m_iDropped; //丢帧数量
int m_iDrawn; //实际绘制的帧数
int m_iSyncAvg; //视频帧的时间与实际显示时间之间的平均时间差
int m_iSyncDev; //视频帧的时间与实际显示时间之间的标准偏差
int m_iFrameRate; //平均帧速率
int m_iFrameJitter; //抖动
void SetEditFieldData();
public:
CQualityProperties(LPUNKNOWN lpUnk, HRESULT *phr);
static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
HRESULT OnConnect(IUnknown *pUnknown);
HRESULT OnDisconnect();
HRESULT OnActivate();
};
class CFilter;
class CPin;
class CVideoWindow;
class CVideoWindow : public CBaseControlWindow, public CBaseControlVideo//视频窗口类定义
{
protected:
CFilter *m_pRenderer;
SIZE m_Size;
BOOL m_FullScreen;
public:
CVideoWindow(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr, CCritSec *pInterfaceLock, CFilter *pRenderer);
virtual ~CVideoWindow();
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void **ppv);
BOOL WindowSetPos(SIZE ClientSize);//自定义函数,根据客户区大小计算窗口大小,并按此设置窗口大小
STDMETHODIMP get_FullScreenMode(long *FullScreenMode);
STDMETHODIMP put_FullScreenMode(long FullScreenMode);
HRESULT IsDefaultTargetRect();
HRESULT SetDefaultTargetRect();
HRESULT SetTargetRect(RECT *pTargetRect);
HRESULT GetTargetRect(RECT *pTargetRect);
HRESULT IsDefaultSourceRect();
HRESULT SetDefaultSourceRect();
HRESULT SetSourceRect(RECT *pSourceRect);
HRESULT GetSourceRect(RECT *pSourceRect);
HRESULT GetStaticImage(long *pBufferSize,long *pDIBImage);
void InitRenderer(TCHAR *pStringName);
RECT GetDefaultRect();
VIDEOINFOHEADER *GetVideoFormat();
LPTSTR GetClassWindowStyles(DWORD *pClassStyles, DWORD *pWindowStyles, DWORD *pWindowStylesEx);
LRESULT OnReceiveMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};
class CPin : public CRendererInputPin, public IOverlay//视频输入引脚类定义
{
CFilter *m_pRenderer;
CCritSec *m_pInterfaceLock;
public:
CPin(TCHAR *pObjectName, CFilter *pRenderer, CCritSec *pInterfaceLock, HRESULT *phr, LPCWSTR pPinName);
STDMETHODIMP GetAllocator(IMemAllocator **ppAllocator);
STDMETHODIMP NotifyAllocator(IMemAllocator *pAllocator,BOOL bReadOnly);
DECLARE_IUNKNOWN
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
virtual HRESULT STDMETHODCALLTYPE GetPalette(DWORD *pdwColors, PALETTEENTRY **ppPalette);
virtual HRESULT STDMETHODCALLTYPE SetPalette(DWORD dwColors, PALETTEENTRY *pPalette);
virtual HRESULT STDMETHODCALLTYPE GetDefaultColorKey(COLORKEY *pColorKey);
virtual HRESULT STDMETHODCALLTYPE GetColorKey(COLORKEY *pColorKey);
virtual HRESULT STDMETHODCALLTYPE SetColorKey(COLORKEY *pColorKey);
virtual HRESULT STDMETHODCALLTYPE GetWindowHandle(HWND *pHwnd);
virtual HRESULT STDMETHODCALLTYPE GetClipList(RECT *pSourceRect, RECT *pDestinationRect, RGNDATA **ppRgnData);
virtual HRESULT STDMETHODCALLTYPE GetVideoPosition(RECT *pSourceRect, RECT *pDestinationRect);
virtual HRESULT STDMETHODCALLTYPE Advise(IOverlayNotify *pOverlayNotify, DWORD dwInterests);
virtual HRESULT STDMETHODCALLTYPE Unadvise( void);
};
class CFilter : public ISpecifyPropertyPages, public CBaseVideoRenderer//视频渲染过滤器类定义
{
public:
static CUnknown * WINAPI CreateInstance(LPUNKNOWN, HRESULT *);
CFilter(TCHAR *pName,LPUNKNOWN pUnk,HRESULT *phr);
~CFilter();
DECLARE_IUNKNOWN
STDMETHODIMP NonDelegatingQueryInterface(REFIID, void **);
STDMETHODIMP GetPages(CAUUID *pPages);//ISpecifyPropertyPages方法
CBasePin *GetPin(int n);
void PrepareRender();
HRESULT Active();
HRESULT BreakConnect();
HRESULT CompleteConnect(IPin *pReceivePin);
HRESULT SetMediaType(const CMediaType *pmt);
HRESULT CheckMediaType(const CMediaType *pmtIn);
HRESULT CheckVideoType(const VIDEOINFO *pDisplay,const VIDEOINFO *pInput);
HRESULT UpdateFormat(VIDEOINFO *pVideoInfo);
HRESULT DoRenderSample(IMediaSample *pMediaSample);
void OnReceiveFirstSample(IMediaSample *pMediaSample);
public:
CImageAllocator m_ImageAllocator; //分配器
CPin m_InputPin; //视频输入引脚
CImageDisplay m_Display; //视频渲染器的助手类,用于管理显示格式
CMediaType m_mtIn; //引脚连接使用的媒体类型
CVideoWindow m_Window; //视频窗口类对象
CImagePalette m_ImagePalette; //管理视频渲染器的调色板
CDrawImage m_DrawImage; //视频渲染器的助手类,用于管理视频渲染器绘制
SIZE m_VideoSize; //视频图像大小
};
DLL源文件:VideoRenderer.cpp
#include "VideoRenderer.h"
const AMOVIESETUP_MEDIATYPE sudPinTypes[] = // 引脚媒体类型
{
{
&MEDIATYPE_Video, // 主要类型
&MEDIASUBTYPE_RGB32 // 子类型
},
{
&MEDIATYPE_Video, // 主要类型
&MEDIASUBTYPE_ARGB32 // 子类型
},
{
&MEDIATYPE_Video, // 主要类型
&MEDIASUBTYPE_RGB24 // 子类型
},
{
&MEDIATYPE_Video, // 主要类型
&MEDIASUBTYPE_RGB555 // 子类型
},
{
&MEDIATYPE_Video, // 主要类型
&MEDIASUBTYPE_RGB565 // 子类型
},
{
&MEDIATYPE_Video, // 主要类型
&MEDIASUBTYPE_RGB8 // 子类型
}
};
const AMOVIESETUP_PIN sudPins =
{
L"Input", //引脚名称
FALSE, //渲染引脚(仅适用于输入引脚。对于输出引脚,该值始终为FALSE)
FALSE, //输出引脚
FALSE, //可以具有该引脚的零个实例
FALSE, //可以创建一个以上引脚的实例
&CLSID_NULL, //该引脚连接的过滤器的类标识
NULL, //该引脚连接的引脚名称
6, //引脚支持的媒体类型数
sudPinTypes //媒体类型信息
};
const AMOVIESETUP_FILTER VideoReader = //过滤器的注册信息
{
&CLSID_Renderer, //过滤器的类标识
L"视频渲染", //过滤器的名称
MERIT_DO_NOT_USE, //过滤器优先值
1, //引脚数量
&sudPins //引脚信息
};
CFactoryTemplate g_Templates [] = {//类工厂模板数组
{
L"视频渲染" //过滤器的名称
, &CLSID_Renderer //过滤器的类标识
, CFilter::CreateInstance //过滤器创建函数
, NULL //DLL入口点调用的函数
, &VideoReader //过滤器注册信息
}
,
{
L"Quality Property Page"
, &CLSID_SampleQuality
, CQualityProperties::CreateInstance
}
};
int g_cTemplates = 2;
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 "VideoRenderer.h"
#pragma warning(disable:4355)
CFilter::CFilter(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr) : CBaseVideoRenderer(CLSID_Renderer,pName,pUnk,phr),
m_InputPin(NAME("Video Pin"),this,&m_InterfaceLock,phr,L"Input"),//创建输入引脚
m_ImageAllocator(this,NAME("Video allocator"),phr),//创建分配器
m_Window(NAME("Video properties"),GetOwner(),phr,&m_InterfaceLock,this),//创建视频窗口
m_ImagePalette(this,&m_Window,&m_DrawImage),// CImagePalette对象
m_DrawImage(&m_Window)//CDrawImage对象
{
m_pInputPin = &m_InputPin;
m_VideoSize.cx = 0;
m_VideoSize.cy = 0;
m_Window.SetControlVideoPin(&m_InputPin);//CBaseControlVideo函数
m_Window.SetControlWindowPin(&m_InputPin);//CBaseControlWindow函数
}
CFilter::~CFilter()
{
m_pInputPin = NULL;
}
CUnknown * WINAPI CFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *phr)
{
return new CFilter(NAME("视频渲染"),pUnk,phr);
}
STDMETHODIMP CFilter::NonDelegatingQueryInterface(REFIID riid,void **ppv)
{
CheckPointer(ppv,E_POINTER);
if (riid == IID_ISpecifyPropertyPages)
return GetInterface((ISpecifyPropertyPages *)this, ppv);
else if (riid == IID_IVideoWindow)
return m_Window.NonDelegatingQueryInterface(riid,ppv);
else if (riid == IID_IBasicVideo)
return m_Window.NonDelegatingQueryInterface(riid,ppv);
return CBaseVideoRenderer::NonDelegatingQueryInterface(riid,ppv);
}
STDMETHODIMP CFilter::GetPages(CAUUID *pPages)//ISpecifyPropertyPages方法,检索属性页列表
{
CheckPointer(pPages,E_POINTER);
pPages->cElems = 1;
pPages->pElems = (GUID *) CoTaskMemAlloc(sizeof(GUID));
if (pPages->pElems == NULL)return E_OUTOFMEMORY;
pPages->pElems[0] = CLSID_SampleQuality;
return NOERROR;
}
CBasePin *CFilter::GetPin(int n)//CBaseFilter重写函数,指定渲染器的引脚为m_InputPin
{
ASSERT(n == 0);
if (n != 0) return NULL;
if (m_pInputPin == NULL)
{
m_pInputPin = &m_InputPin;
}
return m_pInputPin;
}
void CFilter::PrepareRender()//CBaseRenderer重写函数,
{
m_Window.DoRealisePalette();//CBaseWindow函数,在窗口和设备上下文中实现调色板
}
HRESULT CFilter::Active()//CBaseRenderer重写函数,激活视频窗口
{
HWND hwnd = m_Window.GetWindowHWND();
NOTE("AutoShowWindow");
if(m_Window.IsAutoShowEnabled() == TRUE)
{
if(m_bAbort == FALSE)
{
if(IsWindowVisible(hwnd) == FALSE)
{
NOTE("Executing AutoShowWindow");
SetRepaintStatus(FALSE);//CBaseRenderer函数,设置重绘标志为FALSE
m_Window.PerformanceAlignWindow();//CBaseWindow函数,在DWORD边界上对齐窗口以获得最高性能
m_Window.DoShowWindow(SW_SHOWNORMAL);//CBaseWindow函数,以SW_SHOWNORMAL方式显示窗口
m_Window.DoSetWindowForeground(TRUE);CBaseWindow函数,将窗口置于Z秩序顶端;参数TRUE,指定窗口具有焦点
}
}
}
return CBaseVideoRenderer::Active();
}
HRESULT CFilter::BreakConnect()//CBaseRenderer重写函数,当断开渲染器引脚时调用
{
CAutoLock cInterfaceLock(&m_InterfaceLock);
HRESULT hr = CBaseVideoRenderer::BreakConnect();
if (FAILED(hr)) return hr;
IPin *pPin = m_InputPin.GetConnected();//获取与视频渲染器输入引脚连接的引脚
if (pPin)
SendNotifyWindow(pPin,NULL);//CBaseRenderer函数,将窗口句柄传递给上游过滤器
m_ImagePalette.RemovePalette();//CImagePalette函数,删除现有的调色板
m_mtIn.ResetFormatBuffer();//CMediaType函数,删除格式块
return NOERROR;
}
HRESULT CFilter::CompleteConnect(IPin *pReceivePin)//CBaseRenderer重写函数,成功连接渲染器引脚时调用
{
CAutoLock cInterfaceLock(&m_InterfaceLock);
CBaseVideoRenderer::CompleteConnect(pReceivePin);
m_DrawImage.ResetPaletteVersion();
VIDEOINFOHEADER *pVideoInfo = (VIDEOINFOHEADER *) m_mtIn.Format();
if (pVideoInfo->bmiHeader.biWidth == m_VideoSize.cx)
{
if (pVideoInfo->bmiHeader.biHeight == m_VideoSize.cy)
{
return NOERROR;
}
}
HWND hwnd = m_Window.GetWindowHWND();
NOTE1("Sending EC_NOTIFY_WINDOW %x",hwnd);
SendNotifyWindow(pReceivePin,hwnd);
m_DrawImage.SetDrawContext();
m_VideoSize.cx = pVideoInfo->bmiHeader.biWidth;//获取视频图像宽度
m_VideoSize.cy = pVideoInfo->bmiHeader.biHeight;//获取视频图像高度
m_Window.WindowSetPos(m_VideoSize);//CVideoWindow类自定义函数,根据客户区大小计算窗口大小,并按此设置窗口大小
m_Window.SetDefaultSourceRect();//CBaseControlVideo重写函数,设置默认的源视频矩形
m_Window.SetDefaultTargetRect();//CBaseControlVideo重写函数,设置默认的目标视频矩形
m_Window.OnVideoSizeChange();//CBaseControlVideo函数,从视频格式结构获取图像宽高,发送EC_VIDEO_SIZE_CHANGED通知
m_Window.ActivateWindow();//CBaseWindow函数,此时激活标志(m_bActivated)为FALSE,以默认矩形作为客户区大小,计算并设置窗口大小,不显示窗口
return NOERROR;
}
HRESULT CFilter::SetMediaType(const CMediaType *pmt)//CBaseRenderer重写函数,设置引脚媒体类型
{
CheckPointer(pmt,E_POINTER);
HRESULT hr = NOERROR;
CAutoLock cInterfaceLock(&m_InterfaceLock);
CMediaType StoreFormat(m_mtIn);
m_mtIn = *pmt;
VIDEOINFO *pVideoInfo = (VIDEOINFO *) m_mtIn.Format();
m_Display.UpdateFormat(pVideoInfo);//将biClrUsed,biClrImportant和biSizeImage成员设置为正确的值,并清除源矩形和目标矩形
hr = m_ImagePalette.PreparePalette(&m_mtIn,&StoreFormat,NULL);//根据媒体类型设置调色板
if (FAILED(hr))return hr;
m_DrawImage.NotifyMediaType(&m_mtIn);//CImageDisplay函数,将当前媒体类型通知CDrawImage对象
m_ImageAllocator.NotifyMediaType(&m_mtIn);//将当前媒体类型通知给CImageAllocator对象
return NOERROR;
}
HRESULT CFilter::CheckMediaType(const CMediaType *pmtIn)//CBaseRenderer重写函数,确定引脚媒体类型
{
return m_Display.CheckMediaType(pmtIn);//CImageDisplay函数,确定建议的媒体类型是否与显示格式兼容
}
HRESULT CFilter::DoRenderSample(IMediaSample *pMediaSample)//CBaseRenderer重写函数,渲染样本
{
return m_DrawImage.DrawImage(pMediaSample);//CDrawImage函数,在视频窗口上绘制视频帧
}
void CFilter::OnReceiveFirstSample(IMediaSample *pMediaSample)//CBaseRenderer重写函数,接收第1个样本时调用
{
ASSERT(pMediaSample);
DoRenderSample(pMediaSample);//渲染样本
}
引脚实现文件:CPin.cpp
#include "VideoRenderer.h"
CPin::CPin(TCHAR *pObjectName, CFilter *pRenderer, CCritSec *pInterfaceLock, HRESULT *phr, LPCWSTR pPinName) : CRendererInputPin(pRenderer,phr,pPinName),
m_pRenderer(pRenderer), m_pInterfaceLock(pInterfaceLock)
{
ASSERT(m_pRenderer);
ASSERT(pInterfaceLock);
}
STDMETHODIMP CPin::GetAllocator(IMemAllocator **ppAllocator)//CBaseInputPin重写函数,获取内存分配器
{
CheckPointer(ppAllocator,E_POINTER);
CAutoLock cInterfaceLock(m_pInterfaceLock);
if (m_pAllocator == NULL)
{
m_pAllocator = &m_pRenderer->m_ImageAllocator;//指定分配器为m_ImageAllocator
m_pAllocator->AddRef();
}
m_pAllocator->AddRef();
*ppAllocator = m_pAllocator;
return NOERROR;
}
STDMETHODIMP CPin::NotifyAllocator(IMemAllocator *pAllocator,BOOL bReadOnly)//CBaseInputPin重写函数,为连接指定分配器
{
CAutoLock cInterfaceLock(m_pInterfaceLock);
HRESULT hr = CBaseInputPin::NotifyAllocator(pAllocator,bReadOnly);
if (FAILED(hr))
return hr;
m_pRenderer->m_DrawImage.NotifyAllocator(FALSE);
if (pAllocator == &m_pRenderer->m_ImageAllocator)
m_pRenderer->m_DrawImage.NotifyAllocator(TRUE);
return NOERROR;
}
STDMETHODIMP CPin::NonDelegatingQueryInterface(REFIID riid,void **ppv)
{
CheckPointer(ppv,E_POINTER);
if (riid == IID_IOverlay)
return GetInterface((IOverlay*)this, ppv);
return CRendererInputPin::NonDelegatingQueryInterface(riid,ppv);
}
HRESULT STDMETHODCALLTYPE CPin::GetPalette(DWORD *pdwColors, PALETTEENTRY **ppPalette)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CPin::SetPalette(DWORD dwColors, PALETTEENTRY *pPalette)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CPin::GetDefaultColorKey(COLORKEY *pColorKey)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CPin::GetColorKey(COLORKEY *pColorKey)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CPin::SetColorKey(COLORKEY *pColorKey)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CPin::GetWindowHandle(HWND *pHwnd)
{
*pHwnd=m_pRenderer->m_Window.GetWindowHWND();
return S_OK;
}
HRESULT STDMETHODCALLTYPE CPin::GetClipList(RECT *pSourceRect, RECT *pDestinationRect, RGNDATA **ppRgnData)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CPin::GetVideoPosition(RECT *pSourceRect, RECT *pDestinationRect)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CPin::Advise(IOverlayNotify *pOverlayNotify, DWORD dwInterests)
{
return E_NOTIMPL;
}
HRESULT STDMETHODCALLTYPE CPin::Unadvise( void)
{
return E_NOTIMPL;
}
视频窗口实现文件:CVideoWindow.cpp
#include "VideoRenderer.h"
CVideoWindow::CVideoWindow(TCHAR *pName, LPUNKNOWN pUnk, HRESULT *phr, CCritSec *pInterfaceLock, CFilter *pRenderer) : CBaseControlVideo(pRenderer,pInterfaceLock,pName,pUnk,phr), CBaseControlWindow(pRenderer,pInterfaceLock,pName,pUnk,phr), m_pRenderer(pRenderer)
{
PrepareWindow();//CBaseWindow函数,注册视频窗口类并创建窗口,此时窗口不可见
m_FullScreen=FALSE;
}
CVideoWindow::~CVideoWindow()
{
InactivateWindow();//CBaseWindow函数,禁用视频窗口
DoneWithWindow();//CBaseWindow函数,销毁视频窗口
}
STDMETHODIMP CVideoWindow::NonDelegatingQueryInterface(REFIID riid,void **ppv)
{
CheckPointer(ppv,E_POINTER);
if (riid == IID_IVideoWindow)
{
return CBaseVideoWindow::NonDelegatingQueryInterface(riid,ppv);
}
else
{
ASSERT(riid == IID_IBasicVideo);
return CBaseBasicVideo::NonDelegatingQueryInterface(riid,ppv);
}
}
BOOL CVideoWindow::WindowSetPos(SIZE ClientSize)//自定义函数,以视频画面大小作为窗口客户区大小,由客户区大小计算并设置窗口大小
{
RECT rect;rect.left=0;rect.top=0;rect.right=ClientSize.cx;rect.bottom=ClientSize.cy;
DWORD Styles=WS_BORDER | WS_CAPTION | WS_POPUP | WS_SYSMENU | WS_SIZEBOX | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN;
BOOL B=AdjustWindowRectEx(&rect,Styles,FALSE,(DWORD)0);//根据客户区大小计算窗口大小
m_Size.cx=rect.right-rect.left;m_Size.cy=rect.bottom-rect.top;
int xRes = GetSystemMetrics(SM_CXSCREEN);//屏幕水平分辨率
int yRes = GetSystemMetrics(SM_CYSCREEN);//屏幕垂直分辨率
MoveWindow(GetWindowHWND(),(xRes-m_Size.cx)/2,(yRes-m_Size.cy)/2,m_Size.cx ,m_Size.cy,FALSE);//设置窗口大小,居中窗口
return B;
}
STDMETHODIMP CVideoWindow::get_FullScreenMode(long *FullScreenMode)//CBaseControlWindow重写函数,当前是否为全屏模式
{
*FullScreenMode=m_FullScreen;
return S_OK;
}
STDMETHODIMP CVideoWindow::put_FullScreenMode(long FullScreenMode)//CBaseControlWindow重写函数,参数为TRUE,设置全屏模式;否则,设置非全屏
{
LONG B_Visible;
get_Visible(&B_Visible);
int xRes = GetSystemMetrics(SM_CXSCREEN);//屏幕水平分辨率
int yRes = GetSystemMetrics(SM_CYSCREEN);//屏幕垂直分辨率
LONG w,h;
get_VideoWidth(&w);get_VideoHeight(&h);
double f=(double)w/(double)h;//获取视频图像宽高比
double p=(double)xRes/(double)yRes;//获取屏幕宽高比
if(FullScreenMode)//全屏显示
{
m_FullScreen=TRUE;
put_WindowStyle(WS_POPUP);
put_WindowStyleEx(WS_EX_TOPMOST);
MoveWindow(m_hwnd,0,0,xRes,yRes,FALSE);
if(f>p)//如果视频图像宽高比大于屏幕
{
w=xRes;h=(LONG)(w/f);
}
else//如果小于或等于
{
h=yRes;w=(LONG)(f*h);
}
RECT TargetRect = {(xRes-w)/2,(yRes-h)/2,(xRes-w)/2+w,(yRes-h)/2+h};
SendMessage(m_hwnd,WM_ERASEBKGND,(WPARAM)m_hdc,(LPARAM)0);//重绘背景
SetTargetRect(&TargetRect);//设置目标矩形
}
else//非全屏,回复窗口默认大小
{
m_FullScreen=FALSE;
put_WindowStyle(WS_BORDER | WS_CAPTION | WS_POPUP | WS_SYSMENU | WS_SIZEBOX | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN);
put_WindowStyleEx((DWORD) 0);
MoveWindow(m_hwnd,(xRes-m_Size.cx)/2,(yRes-m_Size.cy)/2,m_Size.cx,m_Size.cy,FALSE);
}
put_Visible(B_Visible);//保持窗口原来的可见状态
return S_OK;
}
LPTSTR CVideoWindow::GetClassWindowStyles(DWORD *pClassStyles, DWORD *pWindowStyles, DWORD *pWindowStylesEx)//CBaseWindow重写函数,获取窗口及类样式;在创建窗口时调用
{
CheckPointer(pClassStyles,NULL);
CheckPointer(pWindowStyles,NULL);
CheckPointer(pWindowStylesEx,NULL);
*pClassStyles = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNCLIENT;//窗口类样式
*pWindowStyles = WS_BORDER | WS_CAPTION | WS_POPUP | WS_SYSMENU | WS_SIZEBOX | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CLIPCHILDREN;//窗口样式
*pWindowStylesEx = (DWORD) 0;//窗口扩展样式
return TEXT("VideoRenderer\0");
}
RECT CVideoWindow::GetDefaultRect()//CBaseWindow重写函数,获取工作区的默认大小
{
RECT DefaultRect = {0,0,m_Size.cx,m_Size.cy};
ASSERT(m_hwnd);
ASSERT(m_hdc);
return DefaultRect;
}
LRESULT CVideoWindow::OnReceiveMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) //CBaseWindow重写函数,处理视频窗口消息
{
IBaseFilter *pFilter = NULL;
RECT ClientRect;
if (uMsg == WM_SIZE)
{
CBaseWindow::OnReceiveMessage(hwnd,uMsg,wParam,lParam);
RECT TargetRect = {0,0,m_Width,m_Height};
return SetTargetRect(&TargetRect);
}
if (uMsg == WM_ERASEBKGND)
{
EXECUTE_ASSERT(GetClientRect(m_hwnd,&ClientRect));
HBRUSH hBrush = CreateSolidBrush(RGB(0,0,0));
EXECUTE_ASSERT(FillRect(m_hdc,&ClientRect,hBrush));
EXECUTE_ASSERT(DeleteObject(hBrush));
return (LRESULT) 0;
}
if (uMsg == WM_CLOSE)
{
m_pRenderer->NotifyEvent(EC_USERABORT,0,0);
DoShowWindow(SW_HIDE);
return CBaseWindow::OnClose();
}
if (uMsg == WM_ACTIVATEAPP)
{
NOTE1("Notification of EC_ACTIVATE (%d)",(BOOL) wParam);
m_pRenderer->QueryInterface(IID_IBaseFilter,(void **) &pFilter);
if (pFilter)
{
m_pRenderer->NotifyEvent(EC_ACTIVATE, wParam, (LPARAM) pFilter);
pFilter->Release();
}
return (LRESULT) 0;
}
if (uMsg == WM_KEYDOWN && wParam==VK_F2) //按F2全屏显示视频
{
put_FullScreenMode(TRUE);
return (LRESULT) 0;
}
if (uMsg == WM_KEYDOWN && wParam==VK_ESCAPE) //按ESC退出全屏
{
put_FullScreenMode(FALSE);
return (LRESULT) 0;
}
if (uMsg == WM_SETCURSOR)
{
if (IsCursorHidden() == TRUE)
{
SetCursor(NULL);
return (LRESULT) 1;
}
}
if (uMsg == WM_DISPLAYCHANGE)
{
m_pRenderer->m_Display.RefreshDisplayType(NULL);
m_pRenderer->OnDisplayChange();
NOTE("Sent EC_DISPLAY_CHANGED event");
return (LRESULT) 0;
}
return CBaseWindow::OnReceiveMessage(hwnd,uMsg,wParam,lParam);
}
HRESULT CVideoWindow::SetDefaultTargetRect()//CBaseControlVideo重写函数,设置默认的目标视频矩形
{
RECT TargetRect = {0,0,m_Size.cx,m_Size.cy};
m_pRenderer->m_DrawImage.SetTargetRect(&TargetRect);
return NOERROR;
}
HRESULT CVideoWindow::IsDefaultTargetRect()//CBaseControlVideo重写函数,确定渲染器是否正在使用默认目标矩形
{
RECT TargetRect;
m_pRenderer->m_DrawImage.GetTargetRect(&TargetRect);
if (TargetRect.left != 0 ||
TargetRect.top != 0 ||
TargetRect.right != m_Size.cx ||
TargetRect.bottom != m_Size.cy)
{
return S_FALSE;
}
return S_OK;
}
HRESULT CVideoWindow::SetTargetRect(RECT *pTargetRect)//CBaseControlVideo重写函数,设置当前目标矩形
{
m_pRenderer->m_DrawImage.SetTargetRect(pTargetRect);
return NOERROR;
}
HRESULT CVideoWindow::GetTargetRect(RECT *pTargetRect)//CBaseControlVideo重写函数,获取当前目标矩形
{
ASSERT(pTargetRect);
m_pRenderer->m_DrawImage.GetTargetRect(pTargetRect);
return NOERROR;
}
HRESULT CVideoWindow::SetDefaultSourceRect()//CBaseControlVideo重写函数,设置默认的源视频矩形
{
SIZE VideoSize = m_pRenderer->m_VideoSize;
RECT SourceRect = {0,0,VideoSize.cx,VideoSize.cy};
m_pRenderer->m_DrawImage.SetSourceRect(&SourceRect);
return NOERROR;
}
HRESULT CVideoWindow::IsDefaultSourceRect()//CBaseControlVideo重写函数,确定渲染器是否使用默认的源矩形
{
RECT SourceRect;
SIZE VideoSize = m_pRenderer->m_VideoSize;
CAutoLock cWindowLock(&m_WindowLock);
m_pRenderer->m_DrawImage.GetSourceRect(&SourceRect);
if (SourceRect.right == VideoSize.cx)
{
if (SourceRect.bottom == VideoSize.cy)
{
if (SourceRect.left == 0)
{
if (SourceRect.top == 0)
{
return S_OK;
}
}
}
}
return S_FALSE;
}
HRESULT CVideoWindow::SetSourceRect(RECT *pSourceRect)//CBaseControlVideo重写函数,设置当前源矩形
{
m_pRenderer->m_DrawImage.SetSourceRect(pSourceRect);
return NOERROR;
}
HRESULT CVideoWindow::GetSourceRect(RECT *pSourceRect)//CBaseControlVideo重写函数,获取当前源矩形
{
ASSERT(pSourceRect);
m_pRenderer->m_DrawImage.GetSourceRect(pSourceRect);
return NOERROR;
}
HRESULT CVideoWindow::GetStaticImage(long *pBufferSize,long *pDIBImage)//CBaseControlVideo重写函数,获取当前图像
{
NOTE("Entering GetStaticImage");
IMediaSample *pMediaSample;
pMediaSample = m_pRenderer->GetCurrentSample();
RECT SourceRect;
if (pMediaSample == NULL)
return E_UNEXPECTED;
m_pRenderer->m_DrawImage.GetSourceRect(&SourceRect);
SourceRect = m_pRenderer->m_DrawImage.ScaleSourceRect(&SourceRect);
VIDEOINFOHEADER *pVideoInfo = (VIDEOINFOHEADER *) m_pRenderer->m_mtIn.Format();
HRESULT hr = CopyImage(pMediaSample, pVideoInfo, pBufferSize, (BYTE*) pDIBImage, &SourceRect);
pMediaSample->Release();
return hr;
}
VIDEOINFOHEADER *CVideoWindow::GetVideoFormat()//CBaseControlVideo重写函数,获取包含视频格式的VIDEOINFOHEADER结构
{
return (VIDEOINFOHEADER *)m_pRenderer->m_mtIn.Format();
}
属性页实现文件:CQualityProperties.cpp
#include "VideoRenderer.h"
#include "strsafe.h"
CQualityProperties::CQualityProperties(LPUNKNOWN pUnk,HRESULT *phr) : CBasePropertyPage(NAME("Quality Page"),pUnk,IDD_QUALITY,IDS_NAME), m_pQualProp(NULL)//IDD_QUALITY为属性页对话框ID
{
ASSERT(phr);
}
CUnknown * WINAPI CQualityProperties::CreateInstance(LPUNKNOWN lpUnk, HRESULT *phr)
{
ASSERT(phr);
return new CQualityProperties(lpUnk, phr);
}
HRESULT CQualityProperties::OnConnect(IUnknown *pUnknown)//CBasePropertyPage重写函数,当属性页连接关联的对象时调用
{
ASSERT(m_pQualProp == NULL);
CheckPointer(pUnknown,E_POINTER);
HRESULT hr = pUnknown->QueryInterface(IID_IQualProp,(void **)&m_pQualProp);
if (FAILED(hr)) return E_NOINTERFACE;
ASSERT(m_pQualProp);
m_pQualProp->get_FramesDroppedInRenderer(&m_iDropped);//检索丢帧数量
m_pQualProp->get_FramesDrawn(&m_iDrawn);//检索自流开始以来实际绘制的帧数
m_pQualProp->get_AvgFrameRate(&m_iFrameRate);//检索自流开始以来的实际平均帧速率
m_pQualProp->get_Jitter(&m_iFrameJitter);//获取连续帧之间的抖动
m_pQualProp->get_AvgSyncOffset(&m_iSyncAvg);//视频帧的时间与实际显示时间之间的平均时间差
m_pQualProp->get_DevSyncOffset(&m_iSyncDev);//视频帧的时间与实际显示时间之间的标准偏差
return NOERROR;
}
HRESULT CQualityProperties::OnDisconnect()//CBasePropertyPage重写函数,当属性页释放关联的对象时调用
{
if (m_pQualProp == NULL) return E_UNEXPECTED;
m_pQualProp->Release();
m_pQualProp = NULL;
return NOERROR;
}
HRESULT CQualityProperties::OnActivate()//CBasePropertyPage重写函数,激活属性页时调用
{
SetEditFieldData();
return NOERROR;
}
void CQualityProperties::SetEditFieldData()//自定义函数,设置所有编辑控件文本
{
ASSERT(m_pQualProp);
TCHAR buffer[50];
(void)StringCchPrintf(buffer,NUMELMS(buffer), TEXT("%d\0"), m_iDropped);
SendDlgItemMessage(m_Dlg, IDD_QDROPPED, WM_SETTEXT, 0, (LPARAM) buffer);
(void)StringCchPrintf(buffer,NUMELMS(buffer), TEXT("%d\0"), m_iDrawn);
SendDlgItemMessage(m_Dlg, IDD_QDRAWN, WM_SETTEXT, 0, (LPARAM) buffer);
(void)StringCchPrintf(buffer,NUMELMS(buffer), TEXT("%d.%02d\0"), m_iFrameRate/100, m_iFrameRate%100);
SendDlgItemMessage(m_Dlg, IDD_QAVGFRM, WM_SETTEXT, 0, (LPARAM) buffer);
(void)StringCchPrintf(buffer,NUMELMS(buffer), TEXT("%d\0"), m_iFrameJitter);
SendDlgItemMessage(m_Dlg, IDD_QJITTER, WM_SETTEXT, 0, (LPARAM) buffer);
(void)StringCchPrintf(buffer,NUMELMS(buffer), TEXT("%d\0"), m_iSyncAvg);
SendDlgItemMessage(m_Dlg, IDD_QSYNCAVG, WM_SETTEXT, 0, (LPARAM) buffer);
(void)StringCchPrintf(buffer,NUMELMS(buffer), TEXT("%d\0"), m_iSyncDev);
SendDlgItemMessage(m_Dlg, IDD_QSYNCDEV, WM_SETTEXT, 0, (LPARAM) buffer);
}