我将有关Media Foundation的函数调用封装到一个类CSpsPlayer中了,CSpsPlayer类的使用方法如下:
1)CSpsPlayer* pPlayer = new CSpsPlayer(hWndPlay);
2)pPlayer->OpenFile("c:\\ttt.asf");
3)获取视频时长pPlayer->GetTimeLen();单位是1/10^4毫秒;
4)pPlayer->Play();
5)暂定pPlayer->Pause();
6)暂停后继续pPlayer->Play();
7)获取播放位置pPlayer->GetCurrentPos();单位是1/10^4毫秒;
8)设置音量pPlayer->SetAudioVolumn(lVolumn);lVolumn取值是0~100
9)Seek操作pPlayer->Move(pos);pos单位是1/10^4毫秒;
10)播放窗口尺寸变化pPlayer->AdjustWindowPosition();
11)停止播放pPlayer->Stop();
12)关闭文件pPlayer->CloseFile();
SpsPlayer.h文件:
//
// Class: CSpsPlayer
//
// Compiler: Visual C++
// Tested on: Visual C++ 2010
//
// Created: 08/March/2014
//
// Author: Liu Yang [email protected]
//
//
#ifndef SPSPLAYER_H
#define SPSPLAYER_H
#include
#include
#include
#include
#include
#include
#include
#include
// Media Foundation headers
#include
#include
#include
#include
#define SAFE_RELEASE(x) { if (x) x->Release(); x = NULL; }
#define SAFE_CLOSE_HANDLE(x) { if (x)CloseHandle(x); x = NULL; }
enum PlayerState//播放器状态
{
Closed = 0, // No session.
Started, // Session is playing a file.
Paused, // Session is paused.
Stopped, // Session is stopped (ready to play).
};
class CMyCriticalSection
{
public:
CMyCriticalSection()
{
::InitializeCriticalSection(&m_cs);
}
virtual ~CMyCriticalSection()
{
::DeleteCriticalSection(&m_cs);
}
void lock()
{
::EnterCriticalSection(&m_cs);
}
void unlock()
{
::LeaveCriticalSection(&m_cs);
}
private:
CRITICAL_SECTION m_cs;
};
class CMyAutoLock
{
public:
CMyAutoLock(CMyCriticalSection * pCrit)
{
m_pCrit = pCrit;
m_pCrit->lock();
}
virtual ~CMyAutoLock()
{
m_pCrit->unlock();
}
private:
CMyCriticalSection * m_pCrit;
};
class CSpsPlayer : public IMFAsyncCallback
{
public:
CSpsPlayer(HWND hWndVideo);
virtual ~CSpsPlayer();
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID iid, void** ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IMFAsyncCallback methods
STDMETHODIMP GetParameters(DWORD*, DWORD*)
{
// Implementation of this method is optional.
return E_NOTIMPL;
}
STDMETHODIMP Invoke(IMFAsyncResult* pAsyncResult);
//播放控制函数
std::string GetSpsFilePathName();//获取文件名
BOOL OpenFile(const char * s);//打开文件
BOOL CloseFile();//关闭文件
long GetPlayStatus();//获取播放器状态,返回:0:停止;1:播放;2:暂停
BOOL Play();//播放
BOOL Pause();//暂停
BOOL Stop();//停止
LONGLONG GetTimeLen();//获取时长(单位是1/10^4毫秒)
LONGLONG GetCurrentPos();//获取当前播放位置(单位是1/10^4毫秒)
BOOL Move(LONGLONG pos);//跳到指定位置播放(单位是1/10^4毫秒)
BOOL GetAudioIsOpenedOrClosed();//获取声音是开启还是关闭状态
BOOL OpenOrCloseAudio(BOOL bOpen);//打开或关闭声音
long GetAudioVolumn();//获取音量
BOOL SetAudioVolumn(long lVolumn);//设置音量
void AdjustWindowPosition();//播放窗口尺寸发生变化时调用
void RepaitVideo();//暂停时,播放窗口重绘后调用
private:
BOOL AddBranchToTopology( IMFTopology *pTopology, IMFMediaSource *pSource, IMFPresentationDescriptor *pPD, DWORD iStream);//添加流的分支到topology
IMFGetService* GetMFGetService();
IMFSimpleAudioVolume* GetSimpleAudioVolume();
IMFPresentationClock* GetPresentationClock();
IMFVideoDisplayControl* GetVideoDisplayControl();
private:
CMyCriticalSection m_csCrit;//播放器锁
HWND m_hWndVideo;//播放窗口
std::string m_sSpsFileName;//视频文件
//
IMFMediaSession* m_pSession;//Media Session
PlayerState m_state;//播放器状态
LONGLONG m_llTimeLen;//1/10^4毫秒
long m_nRefCount; // Reference count.
};
#endif
SpsPlayer.cpp文件:
//
// Class: CSpsPlayer
//
// Compiler: Visual C++
// Tested on: Visual C++ 2010
//
// Created: 08/March/2014
//
// Author: Liu Yang [email protected]
//
//
#include "SpsPlayer.h"
#include
#pragma comment(lib, "shlwapi")
static const GUID MY_MR_VIDEO_RENDER_SERVICE ={0x1092a86c,0xab1a,0x459a, {0xa3, 0x36, 0x83, 0x1f, 0xbc, 0x4d, 0x11, 0xff} };
CSpsPlayer::CSpsPlayer(HWND hWndVideo)
{
m_hWndVideo = hWndVideo;
//
m_pSession = NULL;
m_state = Closed;
m_llTimeLen = 0;
}
CSpsPlayer::~CSpsPlayer()
{
CloseFile();
}
HRESULT CSpsPlayer::QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] =
{
QITABENT(CSpsPlayer, IMFAsyncCallback),
{ 0 }
};
return QISearch(this, qit, riid, ppv);
}
ULONG CSpsPlayer::AddRef()
{
return InterlockedIncrement(&m_nRefCount);
}
ULONG CSpsPlayer::Release()
{
ULONG uCount = InterlockedDecrement(&m_nRefCount);
if (uCount == 0)
{
delete this;
}
return uCount;
}
std::string CSpsPlayer::GetSpsFilePathName()
{
CMyAutoLock lock(&m_csCrit);
return m_sSpsFileName;
}
BOOL CSpsPlayer::OpenFile(const char * s)
{
CloseFile();
CMyAutoLock lock(&m_csCrit);
std::string sSpsFileName;
sSpsFileName = s;
//初始化Media Foundataion平台
HRESULT hr = MFStartup(MF_VERSION);
if (FAILED(hr))
{
return FALSE;
}
//创建Media Session对象
hr = MFCreateMediaSession(NULL, &m_pSession);
if (FAILED(hr))
{
MFShutdown();
return FALSE;
}
//开始从Media Session接收事件
hr = m_pSession->BeginGetEvent((IMFAsyncCallback*)this, NULL);
if (FAILED(hr))
{
SAFE_RELEASE(m_pSession);
MFShutdown();
return FALSE;
}
MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;
IMFSourceResolver* pSourceResolver = NULL;//Media Source选择器
IUnknown* pSource = NULL;
IMFMediaSource* pMFMediaSource = NULL;
//根据给定的文件路径,创建Media Source对象,并获取IMFMediaSource接口
hr = MFCreateSourceResolver(&pSourceResolver);
if (FAILED(hr))
{
SAFE_RELEASE(m_pSession);
MFShutdown();
return FALSE;
}
WCHAR szwFile[MAX_PATH] = {0};
MultiByteToWideChar(CP_ACP, 0, sSpsFileName.c_str(), -1, szwFile, MAX_PATH);
hr = pSourceResolver->CreateObjectFromURL( szwFile, MF_RESOLUTION_MEDIASOURCE, NULL, &ObjectType, &pSource );
if (FAILED(hr))
{
SAFE_RELEASE(pSourceResolver);
SAFE_RELEASE(m_pSession);
MFShutdown();
return FALSE;
}
hr = pSource->QueryInterface(IID_PPV_ARGS(&pMFMediaSource));
if (FAILED(hr))
{
SAFE_RELEASE(pSource);
SAFE_RELEASE(pSourceResolver);
SAFE_RELEASE(m_pSession);
MFShutdown();
return FALSE;
}
SAFE_RELEASE(pSource);
SAFE_RELEASE(pSourceResolver);
IMFPresentationDescriptor* pSourcePD = NULL;
//创建Presentation描述符
hr = pMFMediaSource->CreatePresentationDescriptor(&pSourcePD);
if (FAILED(hr))
{
SAFE_RELEASE(pMFMediaSource);
SAFE_RELEASE(m_pSession);
MFShutdown();
return FALSE;
}
MFTIME mDuration = 0;
hr = pSourcePD->GetUINT64(MF_PD_DURATION, (UINT64*)&mDuration);
if (FAILED(hr))
{
SAFE_RELEASE(pSourcePD);
SAFE_RELEASE(pMFMediaSource);
SAFE_RELEASE(m_pSession);
MFShutdown();
return FALSE;
}
m_llTimeLen = mDuration;
DWORD cSourceStreams = 0;
//获取Media Source对象的流的数目
hr = pSourcePD->GetStreamDescriptorCount(&cSourceStreams);
if (FAILED(hr))
{
SAFE_RELEASE(pSourcePD);
SAFE_RELEASE(pMFMediaSource);
SAFE_RELEASE(m_pSession);
MFShutdown();
return FALSE;
}
IMFTopology *pTopology = NULL;
//创建topology对象
hr = MFCreateTopology(&pTopology);
if (FAILED(hr))
{
SAFE_RELEASE(pSourcePD);
SAFE_RELEASE(pMFMediaSource);
SAFE_RELEASE(m_pSession);
MFShutdown();
return FALSE;
}
//为每一个流创建节点,并将节点添加到topology中
for (DWORD i = 0; i < cSourceStreams; i++)
{
BOOL bRet = AddBranchToTopology(pTopology, pMFMediaSource, pSourcePD, i);
if (!bRet)
{
SAFE_RELEASE(pTopology);
SAFE_RELEASE(pSourcePD);
SAFE_RELEASE(pMFMediaSource);
SAFE_RELEASE(m_pSession);
MFShutdown();
return FALSE;
}
}
//将topology对象设置到Media Session对象中
hr = m_pSession->SetTopology(0, pTopology);
if (FAILED(hr))
{
SAFE_RELEASE(pTopology);
SAFE_RELEASE(pSourcePD);
SAFE_RELEASE(pMFMediaSource);
SAFE_RELEASE(m_pSession);
MFShutdown();
return FALSE;
}
m_sSpsFileName = sSpsFileName;
m_state = Stopped;
return TRUE;
}
BOOL CSpsPlayer::CloseFile()
{
Stop();
CMyAutoLock lock(&m_csCrit);
SAFE_RELEASE(m_pSession);
MFShutdown();
m_sSpsFileName = "";
return TRUE;
}
long CSpsPlayer::GetPlayStatus()
{
CMyAutoLock lock(&m_csCrit);
switch(m_state)
{
case Closed://还没有打开文件
case Stopped://停止了--打开文件后就是此状态
return 0;
break;
case Started://开始播放了
return 1;
break;
case Paused://暂停了
return 2;
break;
default:
return -1;
break;
}
}
BOOL CSpsPlayer::Play()
{
CMyAutoLock lock(&m_csCrit);
if (m_state != Paused && m_state != Stopped)
{
return FALSE;
}
PROPVARIANT varStart;
PropVariantInit(&varStart);
HRESULT hr = m_pSession->Start(&GUID_NULL, &varStart);
PropVariantClear(&varStart);
if (SUCCEEDED(hr))
{
// Note: Start is an asynchronous operation. However, we
// can treat our state as being already started. If Start
// fails later, we'll get an MESessionStarted event with
// an error code, and we will update our state then.
m_state = Started;
return TRUE;
}
return FALSE;
}
BOOL CSpsPlayer::Pause()
{
CMyAutoLock lock(&m_csCrit);
if (m_state != Started)
{
return FALSE;
}
HRESULT hr = m_pSession->Pause();
if (SUCCEEDED(hr))
{
m_state = Paused;
return TRUE;
}
return FALSE;
}
BOOL CSpsPlayer::Stop()
{
CMyAutoLock lock(&m_csCrit);
if (m_state != Started && m_state != Paused)
{
return FALSE;
}
HRESULT hr = m_pSession->Stop();
if (SUCCEEDED(hr))
{
m_state = Stopped;
return TRUE;
}
return FALSE;
}
LONGLONG CSpsPlayer::GetTimeLen()
{
CMyAutoLock lock(&m_csCrit);
return m_llTimeLen;
}
LONGLONG CSpsPlayer::GetCurrentPos()
{
CMyAutoLock lock(&m_csCrit);
IMFPresentationClock* pPresentationClock = GetPresentationClock();
if (!pPresentationClock)
return 0;
MFTIME pos = 0;
HRESULT hr = pPresentationClock->GetTime(&pos);
SAFE_RELEASE(pPresentationClock);
if (FAILED(hr))
return 0;
return pos;
}
BOOL CSpsPlayer::Move(LONGLONG pos)
{
CMyAutoLock lock(&m_csCrit);
if (!m_pSession)
return FALSE;
PROPVARIANT varStart;
varStart.vt = VT_I8;
varStart.hVal.QuadPart = pos;
HRESULT hr = m_pSession->Start(NULL, &varStart);
if (FAILED(hr))
return FALSE;
return TRUE;
}
BOOL CSpsPlayer::GetAudioIsOpenedOrClosed()
{
CMyAutoLock lock(&m_csCrit);
if (!m_pSession)
{
return FALSE;
}
IMFSimpleAudioVolume* pa = GetSimpleAudioVolume();
if(pa)
{
BOOL bMute = FALSE;
HRESULT hr = pa->GetMute(&bMute);
if (FAILED(hr))
return FALSE;
return bMute;
}
return FALSE;
}
BOOL CSpsPlayer::OpenOrCloseAudio(BOOL bOpen)
{
CMyAutoLock lock(&m_csCrit);
if (!m_pSession)
{
return FALSE;
}
IMFSimpleAudioVolume* pa = GetSimpleAudioVolume();
if(pa)
{
HRESULT hr;
if (bOpen)
{
hr = pa->SetMute(FALSE);
if (FAILED(hr))
return FALSE;
}
else
{
hr = pa->SetMute(TRUE);
if (FAILED(hr))
return FALSE;
}
}
return TRUE;
}
long CSpsPlayer::GetAudioVolumn()
{
CMyAutoLock lock(&m_csCrit);
if (!m_pSession)
{
return 0;
}
IMFSimpleAudioVolume* pa = GetSimpleAudioVolume();
if(pa)
{
HRESULT hr;
float f = 0.0f;
hr = pa->GetMasterVolume(&f);
if (FAILED(hr))
return 0;
f = (f < 0.0f ? 0.0f : (f > 1.0f ? 1.0f : f));
return (long)(f * 100.0f);
}
return 0;
}
BOOL CSpsPlayer::SetAudioVolumn(long ll)
{
CMyAutoLock lock(&m_csCrit);
if (!m_pSession)
{
return FALSE;
}
IMFSimpleAudioVolume* pa = GetSimpleAudioVolume();
if(pa)
{
HRESULT hr;
ll = (ll < 0 ? 0 : (ll > 100 ? 100 : ll));
float f = ((float)ll / 100.0f);
f = (f < 0.0f ? 0.0f : (f > 1.0f ? 1.0f : f));
hr = pa->SetMasterVolume(f);
if (FAILED(hr))
return FALSE;
return TRUE;
}
return FALSE;
}
void CSpsPlayer::AdjustWindowPosition()
{
CMyAutoLock lock(&m_csCrit);
IMFVideoDisplayControl* pVideoDisplayControl = GetVideoDisplayControl();
if (pVideoDisplayControl)
{
RECT rcDest;
GetWindowRect(m_hWndVideo, &rcDest);
POINT pt;
pt.x = rcDest.left;
pt.y = rcDest.top;
ScreenToClient(m_hWndVideo, &pt);
rcDest.left = pt.x;
rcDest.top = pt.y;
pt.x = rcDest.right;
pt.y = rcDest.bottom;
ScreenToClient(m_hWndVideo, &pt);
rcDest.right = pt.x;
rcDest.bottom = pt.y;
pVideoDisplayControl->SetVideoPosition(0, &rcDest);
}
}
void CSpsPlayer::RepaitVideo()
{
CMyAutoLock lock(&m_csCrit);
IMFVideoDisplayControl* pVideoDisplayControl = GetVideoDisplayControl();
if (pVideoDisplayControl)
pVideoDisplayControl->RepaintVideo();
}
BOOL CSpsPlayer::AddBranchToTopology( IMFTopology *pTopology, IMFMediaSource *pSource, IMFPresentationDescriptor *pPD, DWORD iStream)
{
IMFStreamDescriptor *pSD = NULL;
IMFTopologyNode *pSourceNode = NULL;
IMFTopologyNode *pOutputNode = NULL;
BOOL fSelected = FALSE;
//获取流描述符
HRESULT hr = pPD->GetStreamDescriptorByIndex(iStream, &fSelected, &pSD);
if (FAILED(hr))
{
return FALSE;
}
if (fSelected)
{
// Create the media sink activation object.
IMFMediaTypeHandler *pHandler = NULL;
IMFActivate *pActivate = NULL;
//获取流的IMFMeidaTypeHandle
HRESULT hr = pSD->GetMediaTypeHandler(&pHandler);
if (FAILED(hr))
{
SAFE_RELEASE(pSD);
return FALSE;
}
//获取流的主类型
GUID guidMajorType;
hr = pHandler->GetMajorType(&guidMajorType);
if (FAILED(hr))
{
SAFE_RELEASE(pHandler);
SAFE_RELEASE(pSD);
return FALSE;
}
SAFE_RELEASE(pHandler);
// 根据流的主类型为Render创建IMFActivate对象
if (MFMediaType_Audio == guidMajorType)
{
hr = MFCreateAudioRendererActivate(&pActivate);
if (FAILED(hr))
{
SAFE_RELEASE(pSD);
return FALSE;
}
}
else if (MFMediaType_Video == guidMajorType)
{
hr = MFCreateVideoRendererActivate(m_hWndVideo, &pActivate);
if (FAILED(hr))
{
SAFE_RELEASE(pSD);
return FALSE;
}
}
//创建源节点
hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pSourceNode);
if (FAILED(hr))
{
SAFE_RELEASE(pActivate);
SAFE_RELEASE(pSD);
return FALSE;
}
//设置源节点的源属性
hr = pSourceNode->SetUnknown(MF_TOPONODE_SOURCE, pSource);
if (FAILED(hr))
{
SAFE_RELEASE(pSourceNode);
SAFE_RELEASE(pActivate);
SAFE_RELEASE(pSD);
return FALSE;
}
//设置源节点的Presentation描述符
hr = pSourceNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD);
if (FAILED(hr))
{
SAFE_RELEASE(pSourceNode);
SAFE_RELEASE(pActivate);
SAFE_RELEASE(pSD);
return FALSE;
}
//设置源节点的流的描述符
hr = pSourceNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD);
if (FAILED(hr))
{
SAFE_RELEASE(pSourceNode);
SAFE_RELEASE(pActivate);
SAFE_RELEASE(pSD);
return FALSE;
}
//添加源节点到topology.
hr = pTopology->AddNode(pSourceNode);
if (FAILED(hr))
{
SAFE_RELEASE(pSourceNode);
SAFE_RELEASE(pActivate);
SAFE_RELEASE(pSD);
return FALSE;
}
//创建输出节点
hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pOutputNode);
if (FAILED(hr))
{
SAFE_RELEASE(pSourceNode);
SAFE_RELEASE(pActivate);
SAFE_RELEASE(pSD);
return FALSE;
}
//设置输出节点的指针
hr = pOutputNode->SetObject(pActivate);
if (FAILED(hr))
{
SAFE_RELEASE(pOutputNode);
SAFE_RELEASE(pSourceNode);
SAFE_RELEASE(pActivate);
SAFE_RELEASE(pSD);
return FALSE;
}
SAFE_RELEASE(pActivate);
//设置输出节点所在流的id
hr = pOutputNode->SetUINT32(MF_TOPONODE_STREAMID, 0);
if (FAILED(hr))
{
SAFE_RELEASE(pOutputNode);
SAFE_RELEASE(pSourceNode);
SAFE_RELEASE(pSD);
return FALSE;
}
hr = pOutputNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);
if (FAILED(hr))
{
SAFE_RELEASE(pOutputNode);
SAFE_RELEASE(pSourceNode);
SAFE_RELEASE(pSD);
return FALSE;
}
//添加输出节点到topology.
hr = pTopology->AddNode(pOutputNode);
if (FAILED(hr))
{
SAFE_RELEASE(pOutputNode);
SAFE_RELEASE(pSourceNode);
SAFE_RELEASE(pSD);
return FALSE;
}
hr = pSourceNode->ConnectOutput(0, pOutputNode, 0);
if (FAILED(hr))
{
SAFE_RELEASE(pOutputNode);
SAFE_RELEASE(pSourceNode);
SAFE_RELEASE(pSD);
return FALSE;
}
}
SAFE_RELEASE(pOutputNode);
SAFE_RELEASE(pSourceNode);
SAFE_RELEASE(pSD);
return TRUE;
}
HRESULT CSpsPlayer::Invoke(IMFAsyncResult *pResult)
{
MediaEventType meType = MEUnknown; // Event type
IMFMediaEvent *pEvent = NULL;
m_csCrit.lock();
// Get the event from the event queue.
HRESULT hr = m_pSession->EndGetEvent(pResult, &pEvent);
if (FAILED(hr))
{
m_csCrit.unlock();
return S_OK;
}
// Get the event type.
hr = pEvent->GetType(&meType);
if (FAILED(hr))
{
SAFE_RELEASE(pEvent);
m_csCrit.unlock();
return S_OK;
}
if (meType == MEEndOfPresentation)
{
SAFE_RELEASE(pEvent);
m_csCrit.unlock();
Stop();
return S_OK;
}
else
{
SAFE_RELEASE(pEvent);
// For all other events, get the next event in the queue.
hr = m_pSession->BeginGetEvent(this, NULL);
if (FAILED(hr))
{
//error
}
}
m_csCrit.unlock();
return S_OK;
}
IMFGetService* CSpsPlayer::GetMFGetService()
{
if (!m_pSession)
return NULL;
IMFGetService* pGetService = NULL;
HRESULT hr = m_pSession->QueryInterface(IID_IMFGetService, (void **)&pGetService);
if(FAILED(hr))
return NULL;
return pGetService;
}
IMFSimpleAudioVolume* CSpsPlayer::GetSimpleAudioVolume()
{
if (!m_pSession)
return NULL;
IMFGetService* pGetService = GetMFGetService();
if(!pGetService)
return NULL;
IMFSimpleAudioVolume* pa = NULL;
HRESULT hr = pGetService->GetService(MR_POLICY_VOLUME_SERVICE, IID_PPV_ARGS(&pa));
SAFE_RELEASE(pGetService);
if(FAILED(hr))
return NULL;
return pa;
}
IMFPresentationClock* CSpsPlayer::GetPresentationClock()
{
if (!m_pSession)
return NULL;
IMFClock* pClock = NULL;
HRESULT hr = m_pSession->GetClock(&pClock);
if(FAILED(hr))
return NULL;
IMFPresentationClock* pPresentationClock = NULL;
hr = pClock->QueryInterface(IID_PPV_ARGS(&pPresentationClock));
SAFE_RELEASE(pClock);
if(FAILED(hr))
return NULL;
return pPresentationClock;
}
IMFVideoDisplayControl* CSpsPlayer::GetVideoDisplayControl()
{
if (!m_pSession)
return NULL;
IMFVideoDisplayControl* pVideoDisplay = NULL;
HRESULT hr = MFGetService(m_pSession, MY_MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&pVideoDisplay));
if(FAILED(hr))
return NULL;
return pVideoDisplay;
}