DirectShow过滤器开发-视频渲染过滤器

下载本过滤器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);
} 

下载本过滤器DLL

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

h3974

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

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

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

打赏作者

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

抵扣说明:

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

余额充值