/*-----------------------------------------
* Copyright (c) 2008 Eric Wong
* 本版紧供读者参考,不得用于任何商业行为
*
* 文件名称: EricMediaControl.h
* 文件标识:
* 摘要:用于封装DSHOW 播放视频文件
*
* 当前版本: 1.0
* 作者: 汪兵 Eric Wong
* 完成日期: 2008年2月2日
*
* 取代版本:
* 原作者:
* 完成日期:
----------------------------------------*/
#pragma once
/*
strmiids.lib
*/
#include <dshow.h>
//定义DSHOW 事件通知消息
#define WM_GRAPHNOTIFY WM_USER + 101
class CEricMediaControl
{
public:
CEricMediaControl(void);
~CEricMediaControl(void);
private:
//DSHOW 接口
IGraphBuilder *m_pGB ;
IMediaControl *m_pMC ;
IMediaEventEx *m_pME ;
IVideoWindow *m_pVW ;
IBasicAudio *m_pBA ;
IBasicVideo *m_pBV ;
IMediaSeeking *m_pMS ;
//显示视频的窗口句柄
HWND m_hOwnerWnd;
private:
//初始化DSHOW接口
BOOL InitDShow(LPCTSTR strFileName /*视频文件名*/
,HWND hOwnerWnd /*显示视频的窗口句柄*/
,HWND hNotifyWnd /*接收DSHOW事件消息的串口句柄*/
);
//释放DSHOW接口
BOOL UnInitDShow();
public:
//打开视频文件
BOOL OpenFile(LPCTSTR strFileName /*视频文件名*/
,HWND hOwnerWnd /*显示视频的窗口句柄*/
,HWND hNotifyWnd /*接收DSHOW事件消息的串口句柄*/
);
//播放视频
BOOL VideoRun();
//暂停视频
BOOL VideoPause();
//停止视频
BOOL VideoStop();
public:
//设置视频显示比例
BOOL FitVideoWindow(FLOAT fScale);
//全屏显示
BOOL FullScreen();
//得到是否是全屏显示
BOOL GetFullScreenStatus();
//得到DShow播放事件
BOOL GetMediaEvent(long *lEventCode);
//设置播放进度,单位: 秒
BOOL SetPositions(DWORD dwPos /*设置当前播放进度*/);
//得到视频播放当前的位置,单位:秒
BOOL GetCurrentPos(DWORD &dwPos /*out 当前播放进度*/);
//得到视频文件时间长度,单位:秒
BOOL GetDuration(DWORD &dwLength);
};
/*-----------------------------------------
* Copyright (c) 2008 Eric Wong
* 本版紧供读者参考,不得用于任何商业行为
*
* 文件名称: EricMediaControl.cpp
* 文件标识:
* 摘要:用于封装DSHOW 播放视频文件
*
* 当前版本: 1.0
* 作者: 汪兵 Eric Wong
* 完成日期: 2008年2月2日
*
* 取代版本:
* 原作者:
* 完成日期:
----------------------------------------*/
#include "StdAfx.h"
#include "EricMediaControl.h"
//构造函数
CEricMediaControl::CEricMediaControl(void)
{
//将DSHOW接口置空
m_pGB = NULL;
m_pMC = NULL;
m_pME = NULL;
m_pVW = NULL;
m_pBA = NULL;
m_pBV = NULL;
m_pMS = NULL;
//初始化 COM 环境
CoInitialize(NULL);
}
//析构函数
CEricMediaControl::~CEricMediaControl(void)
{
//释放DSHOW接口
UnInitDShow();
//释放COM 环境
CoUninitialize();
}
/*
*函数介绍: 初始化DShow接口,并渲染好视频文件
*入口参数: strFileName: 视频文件名
hOwnerWnd: 显示视频的窗口句柄
hNotifyWnd: 接收DSHOW事件消息的串口句柄
*出口参数:(无)
*返回值:TRUE:成功;FALSE:失败
*/
BOOL CEricMediaControl::InitDShow(LPCTSTR strFileName /*视频文件名*/
,HWND hOwnerWnd /*显示视频的窗口句柄*/
,HWND hNotifyWnd /*接收DSHOW事件消息的串口句柄*/
)
{
HRESULT hResult;
//第1步:创建IGraphBuilder接口
hResult = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&m_pGB);
if (hResult != S_OK)
{
return FALSE;
}
//第2步:利用IGraphBuilder渲染视频文件
hResult = m_pGB->RenderFile(strFileName,NULL);
if (hResult != S_OK )
{
//
if (hResult == VFW_S_PARTIAL_RENDER)
{
TRACE(L"Some of the streams in this movie are in an unsupported format./n");
}
else if (hResult == VFW_S_AUDIO_NOT_RENDERED)
{
TRACE(L"Partial success; the audio was not rendered./n");
}
else if (hResult == VFW_S_DUPLICATE_NAME)
{
TRACE(L"Success; the Filter Graph Manager modified the filter name to avoid duplication../n");
}
else if (hResult == VFW_S_VIDEO_NOT_RENDERED)
{
TRACE(L"Partial success; some of the streams in this movie are in an unsupported format./n");
}
else
{
//释放DSHOW接口
UnInitDShow();
return FALSE;
}
}
//第3步:得到媒体播放控制接口
hResult = m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC);
if (hResult != S_OK)
{
//释放DSHOW接口
UnInitDShow();
return FALSE;
}
//第4步:得到媒体播放位置搜索接口
hResult = m_pGB->QueryInterface(IID_IMediaSeeking,(void**)&m_pMS);
if (hResult != S_OK)
{
//释放DSHOW接口
UnInitDShow();
return FALSE;
}
//设置查找定位的时间单位,这里设置:100纳秒(十亿分之一秒)
GUID guid_timeFormat = TIME_FORMAT_MEDIA_TIME;
m_pMS->SetTimeFormat(&guid_timeFormat);
//第5步:得到Filter Graph媒体事件接口
hResult = m_pGB->QueryInterface(IID_IMediaEventEx,(void**)&m_pME);
if (hResult != S_OK)
{
//释放DSHOW接口
UnInitDShow();
return FALSE;
}
//设置媒体事件通知消息窗口
m_pME->SetNotifyWindow((OAHWND)hNotifyWnd, WM_GRAPHNOTIFY, 0);
//第6步:得到视频播放窗口接口
hResult = m_pGB->QueryInterface(IID_IVideoWindow, (void **)&m_pVW);
if (hResult != S_OK)
{
//释放DSHOW接口
UnInitDShow();
return FALSE;
}
//设置视频播放窗口句柄
m_pVW->put_Owner((OAHWND)hOwnerWnd);
//设置视频窗口格式
m_pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
//第7步:得到基础视频流接口
hResult = m_pGB->QueryInterface(IID_IBasicVideo, (void **)&m_pBV);
if (hResult != S_OK)
{
//释放DSHOW接口
UnInitDShow();
return FALSE;
}
//第8步:得到基础音频流接口
hResult = m_pGB->QueryInterface(IID_IBasicAudio, (void **)&m_pBA);
if (hResult != S_OK)
{
//释放DSHOW接口
UnInitDShow();
return FALSE;
}
return TRUE;
}
/*
*函数介绍: 卸载DShow系列接口
*入口参数: (无)
*出口参数:(无)
*返回值:TRUE:成功;FALSE:失败
*/
BOOL CEricMediaControl::UnInitDShow()
{
//1,释放媒体播放控制接口
if (m_pMC != NULL)
{
//停止视频播放
m_pMC->Stop();
m_pMC->Release();
m_pMC = NULL;
}
//2,释放媒体事件接口
if (m_pME != NULL)
{
//消息通知窗口置空
m_pME->SetNotifyWindow(NULL, 0, 0);
m_pME->Release();
m_pME = NULL;
}
//3,释放视频播放窗口接口
if (m_pVW != NULL)
{
//隐藏视频窗口
m_pVW->put_Visible(OAFALSE);
//设置视频播放窗口句柄为空
m_pVW->put_Owner(NULL);
m_pVW->Release();
m_pVW = NULL;
}
//4,释放基础音频流接口
if (m_pBA != NULL)
{
m_pBA->Release();
m_pBA = NULL;
}
//5,释放基础视频流接口
if (m_pBV != NULL)
{
m_pBV->Release();
m_pBV = NULL;
}
//6,释放媒体搜索接口
if (m_pMS != NULL)
{
m_pMS->Release();
m_pMS = NULL;
}
//7,最后释放FilterGpaph接口
if (m_pGB != NULL)
{
m_pGB->Release();
m_pGB = NULL;
}
return TRUE;
}
/*
*函数介绍:打开视频文件
*入口参数: strFileName: 视频文件名
hOwnerWnd: 显示视频的窗口句柄
hNotifyWnd: 接收DSHOW事件消息的串口句柄
*出口参数:(无)
*返回值:TRUE:成功打开视频文件;FALSE:打开视频文件失败
*/
BOOL CEricMediaControl::OpenFile(LPCTSTR strFileName /*视频文件名*/
,HWND hOwnerWnd /*显示视频的窗口句柄*/
,HWND hNotifyWnd /*接收DSHOW事件消息的串口句柄*/
)
{
//存储显示视频窗口句柄
m_hOwnerWnd = hOwnerWnd;
//重置DSHOW接口
UnInitDShow();
//打开视频文件,并对DSHOW接口做初始化工作
return InitDShow(strFileName,hOwnerWnd,hNotifyWnd);
}
/*
*函数介绍:播放视频
*入口参数:(无)
*出口参数:(无)
*返回值:TRUE:成功;FALSE:失败
*/
BOOL CEricMediaControl::VideoRun()
{
//检测IMediaControl控制接口有效性
if (m_pMC == NULL)
{
return FALSE;
}
//播放视频
HRESULT hResult = m_pMC->Run();
if (hResult != S_OK)
{
return FALSE;
}
return TRUE;
}
/*
*函数介绍:暂停视频
*入口参数:(无)
*出口参数:(无)
*返回值:TRUE:成功;FALSE:失败
*/
BOOL CEricMediaControl::VideoPause()
{
//检测IMediaControl控制接口有效性
if (m_pMC == NULL)
{
return FALSE;
}
//暂停视频
HRESULT hResult = m_pMC->Pause();
if (hResult != S_OK)
{
return FALSE;
}
return TRUE;
}
/*
*函数介绍:停止视频
*入口参数:(无)
*出口参数:(无)
*返回值:TRUE:成功;FALSE:失败
*/
BOOL CEricMediaControl::VideoStop()
{
HRESULT hResult ;
//检测IMediaControl控制接口有效性
if (m_pMC == NULL)
{
return FALSE;
}
//停止视频
hResult = m_pMC->Stop();
if (hResult != S_OK)
{
return FALSE;
}
//将当前播放位置置0
LONGLONG pos=0;
hResult = m_pMS->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
NULL, AM_SEEKING_NoPositioning);
return TRUE;
}
/*
*函数介绍: 设置视频显示比例
*入口参数: fScale : 显示比例, <= 1.0
*出口参数: (无)
*返回值:TRUE:成功;FALSE:失败,此处有点问题
*/
BOOL CEricMediaControl::FitVideoWindow(FLOAT fScale)
{
LONG lHeight, lWidth;
int iSeek = 0;
double dblScaleX,dblScaleY;
HRESULT hr = S_OK;
LONG lDeflateX = 0;
LONG lDeflateY = 0;
CRect clientRect;
CRect dstRect;
//
if (m_pBV == NULL)
{
return FALSE;
}
//放缩比例必须小于等于1
if (fScale > 1.0)
{
return FALSE;
}
//得到原始视频尺寸
hr = m_pBV->GetVideoSize(&lWidth, &lHeight);
if (hr != S_OK)
{
return FALSE;
}
//设置拉伸后的尺寸
lWidth = lWidth * fScale;
lHeight = lHeight * fScale;
//得到视频播放窗口的尺寸
GetClientRect(m_hOwnerWnd, &clientRect);
lDeflateX = (clientRect.Width() - clientRect.Width() * fScale) / 2;
lDeflateY = (clientRect.Height() - clientRect.Height() * fScale) / 2;
//重新设置客户区域
clientRect.DeflateRect(lDeflateX,lDeflateY);
if ( (lWidth <= clientRect.Width())
&& (lHeight <= clientRect.Height()))
{
dstRect.left = (clientRect.right - clientRect.left - lWidth) /2;
dstRect.right = dstRect.left + lWidth;
dstRect.top = (clientRect.bottom - clientRect.top - lHeight) /2;
dstRect.bottom = dstRect.top + lHeight;
}
else
{
dblScaleX =double(clientRect.Width()) / double(lWidth) ;
dblScaleY = double(clientRect.Height()) / double(lHeight) ;
if (dblScaleX <= dblScaleY)
{
dstRect.left = clientRect.left;
dstRect.right = clientRect.right;
iSeek =(clientRect.Height() - clientRect.Width()*(double(lHeight) / double(lWidth)))/2;
dstRect.top = clientRect.top + iSeek;
dstRect.bottom = dstRect.top + clientRect.Width()*(double(lHeight) / double(lWidth));
}
else
{
dstRect.top = clientRect.top;
dstRect.bottom = clientRect.bottom;
iSeek =(clientRect.Width() - clientRect.Height()*(double(lWidth) / double(lHeight)))/2;
dstRect.left = clientRect.left + iSeek;
dstRect.right = dstRect.left + clientRect.Height()*(double(lWidth) / double(lHeight));
}
}
//设置视频播放位置
m_pVW->SetWindowPosition(dstRect.left,dstRect.top,dstRect.Width(),dstRect.Height());
return TRUE;
}
/*
*函数介绍: 全屏显示
*入口参数: (无)
*出口参数: (无)
*返回值:TRUE:成功;FALSE:失败
*/
BOOL CEricMediaControl::FullScreen()
{
LONG lMode = 0;
static HWND hDrain=0;
if (m_pBV == NULL)
{
return FALSE;
}
//得到全屏状态
m_pVW->get_FullScreenMode(&lMode);
if (lMode == OAFALSE)
{
// Save current message drain
m_pVW->get_MessageDrain((OAHWND *) &hDrain);
// Set message drain to application main window
m_pVW->put_MessageDrain((OAHWND)m_hOwnerWnd );
//设置全屏幕
lMode = OATRUE;
m_pVW->put_FullScreenMode(lMode);
}
else
{
//切换到非全屏模式
lMode = OAFALSE;
m_pVW->put_FullScreenMode(lMode);
// Undo change of message drain
m_pVW->put_MessageDrain((OAHWND) hDrain);
// Reset video window
FitVideoWindow(1);
m_pVW->SetWindowForeground(-1);
}
return TRUE;
}
/*
*函数介绍: 得到是否全屏幕播放
*入口参数: (无)
*出口参数: (无)
*返回值:TRUE:成功;FALSE:失败
*/
BOOL CEricMediaControl::GetFullScreenStatus()
{
LONG lMode = 0;
if (m_pBV == NULL)
{
return FALSE;
}
m_pVW->get_FullScreenMode(&lMode);
if (lMode == OAFALSE)
{
return FALSE;
}
else
{
return TRUE;
}
}
/*
*函数介绍: 得到媒体事件
*入口参数: (无)
*出口参数: lEventCode
*返回值:TRUE:成功;FALSE:失败
*/
BOOL CEricMediaControl::GetMediaEvent(long *lEventCode)
{
LONG evCode, evParam1, evParam2;
HRESULT hr=S_OK;
if (m_pME == NULL)
{
return FALSE;
}
hr = m_pME->GetEvent(&evCode, &evParam1, &evParam2, 0);
if (SUCCEEDED(hr))
{
*lEventCode = evCode;
// Spin through the events
hr = m_pME->FreeEventParams(evCode, evParam1, evParam2);
return TRUE;
}
return FALSE;
}
/*
*函数介绍: 设置播放进度
*入口参数: dwPos :播放进度,单位秒
*出口参数: (无)
*返回值:TRUE:成功;FALSE:失败
*/
BOOL CEricMediaControl::SetPositions(DWORD dwPos /*设置当前播放进度*/)
{
//设置绝对位置,转化成纳秒为单位
LONGLONG llPos = dwPos * 10000 * 1000;
if (m_pMS == NULL)
{
return FALSE;
}
//设置视频播放当前位置
HRESULT hr = m_pMS->SetPositions(&llPos,AM_SEEKING_AbsolutePositioning ,
NULL, AM_SEEKING_NoPositioning);
if (hr == S_OK)
{
return TRUE;
}
else
{
return FALSE;
}
}
/*
*函数介绍: 得到视频文件播放长度,单位秒
*入口参数: dwLength :视频文件时间长度
*出口参数: dwLength :视频文件时间长度
*返回值:TRUE:成功;FALSE:失败
*/
BOOL CEricMediaControl::GetDuration(DWORD &dwLength)
{
dwLength = 0;
if (m_pMS == NULL)
{
return FALSE;
}
//得到视频总时间长度
LONGLONG llDuration;
HRESULT hResult = m_pMS->GetDuration(&llDuration);
if (hResult != S_OK)
{
return FALSE;
}
//转换成以秒为单位
llDuration = llDuration / 10000 ;
llDuration = llDuration / 1000;
dwLength = (DWORD)llDuration;
return TRUE;
}
/*
*函数介绍: 得到媒体当前播放进度位置,单位秒
*入口参数: dwPos :当前播放进度,单位秒
*出口参数: dwPos :当前播放进度,单位秒
*返回值:TRUE:成功;FALSE:失败
*/
BOOL CEricMediaControl::GetCurrentPos(DWORD &dwPos)
{
dwPos = 0;
LONGLONG llPos = 0;
if (m_pMS== NULL)
{
return FALSE;
}
//得到当前视频播放位置
HRESULT hResult = m_pMS->GetCurrentPosition(&llPos);
if (hResult != S_OK)
{
return FALSE;
}
//转换成以秒为单位
llPos = llPos / 10000 ;
llPos = llPos / 1000;
dwPos = (DWORD)llPos;
return TRUE;
}