📄 mediaplay.cpp
字号:
#define MEDIAPLAYDLL_API _declspec(dllexport)
#include <streams.h>
#include <commdlg.h>
#include <assert.h>
#include "stdafx.h"
#include "MediaPlay.h"
#include "macdefine.h"
enum PLAYSTATE { psRUNNING, psPAUSED, psSTOPPED};
IGraphBuilder* ipGraBuilder = NULL;
IMediaControl* ipMedControl = NULL;
IMediaEventEx* ipMedEventEx = NULL;
IVideoWindow* ipVidWindow = NULL;
IBasicVideo* ipBasVideo = NULL;
IBasicAudio* ipBasAudio = NULL;
IMediaSeeking* ipMedSeeking = NULL;
bool g_bAudioOnly = false;
PLAYSTATE g_psCurrent=psSTOPPED;
int g_iBenchSound = MAX_SOUND;
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
// 初始化COM库
if(FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
{
RETAILMSG(1, (TEXT("CoInitialize Failed!\r\n")));
return false;
}
}
if (ul_reason_for_call == DLL_PROCESS_DETACH)
{
// 卸载COM库
CoUninitialize();
}
return true;
}
HRESULT InitInterface(LPTSTR lpFile)
{
int iFileLen = _tcslen(lpFile);
// 媒体文件路径不能为空
assert(iFileLen!=0);
TCHAR wFile[MAX_PATH];
HRESULT hr;
#ifndef UNICODE
MultiByteToWideChar(CP_ACP, 0, lpFile, -1, wFile, MAX_PATH);
#else
if (iFileLen < MAX_PATH)
{
lstrcpy(wFile, lpFile);
}
else
{
return E_FAIL;
}
#endif
// 创建一个Filter Graph组件
JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,IID_IGraphBuilder, (void **)&ipGraBuilder));
if (SUCCEEDED(hr))
{
// 使用智能链接构建Filter Graph
JIF(ipGraBuilder->RenderFile(wFile, NULL));
// 得到播放控制接口和随机定位接口
JIF(ipGraBuilder->QueryInterface(IID_IMediaControl, (void **)&ipMedControl));
JIF(ipGraBuilder->QueryInterface(IID_IMediaEventEx, (void **)&ipMedEventEx));
// 得到IMediaEventEx接口,以便让应用程序及时处理Filter Graph Manager发出的事件
JIF(ipGraBuilder->QueryInterface(IID_IMediaSeeking, (void **)&ipMedSeeking));
// 得到视频相关接口
JIF(ipGraBuilder->QueryInterface(IID_IVideoWindow, (void **)&ipVidWindow));
JIF(ipGraBuilder->QueryInterface(IID_IBasicVideo, (void **)&ipBasVideo));
// 得到音频相关接口
JIF(ipGraBuilder->QueryInterface(IID_IBasicAudio, (void **)&ipBasAudio));
}
return hr;
}
HRESULT SetPlayAgain(HWND hWnd,UINT Msg)
{
// 断言 窗口句柄和消息不能为null
assert(hWnd!=NULL && Msg!=0);
// 如果是无效窗口,返回S_FALSE
if(!IsWindow(hWnd))
{
return S_FALSE;
}
HRESULT hr;
// 注册接收消息响应的窗口
JIF(ipMedEventEx->SetNotifyWindow((OAHWND)hWnd, (long)Msg, 0));
return hr;
}
HRESULT PlayAgain(void)
{
LONG evCode, evParam1, evParam2;
HRESULT hr;
// 当IMediaEventEx接口释放后,就不需要访问IMediaEventEx接口了
if(!ipMedEventEx)
{
return S_FALSE;
}
// 一次while循环处理事件队列中的所有事件
while(SUCCEEDED(ipMedEventEx->GetEvent(&evCode, &evParam1, &evParam2, 0)))
{
// 当媒体播放完成,重新开始播放
if(EC_COMPLETE == evCode)
{
LONGLONG pos=0;
// 定位到文件头
hr = ipMedSeeking->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
NULL, AM_SEEKING_NoPositioning);
if (FAILED(hr))
{
// 有少数的filter不支持IMediaSeeking接口 ,则通过先停止后播放来实现重新播放
if(StopMedia()!=S_OK)
{
hr = S_FALSE;
break;
}
if(PlayMedia()!=S_OK)
{
hr = S_FALSE;
break;
}
}
}
// Filter Graph 运行过程中发生了错误
else if(EC_ERRORABORT == evCode)
{
if(StopMedia()!=S_OK)
{
hr = S_FALSE;
break;
}
hr = (HRESULT)evParam1;
break;
}
}
// 释放回调期间相关的内存
if (FAILED(hr = ipMedEventEx->FreeEventParams(evCode, evParam1, evParam2)))
{
CloseMedia();
}
return hr;
}
int CheckMediaType(void)
{
long lVisible;
HRESULT hr;
g_bAudioOnly = false;
// 播放纯音频文件的Filter Graph不会实现IVideoWindow和IBasicVideo接口
if (!ipVidWindow || !ipBasVideo)
{
g_bAudioOnly = true;
return 0;
}
// 另一种情况,媒体文件包含视频内容,系统没有相应的解码Filter,则下面这句话将调用失败
hr = ipVidWindow->get_Visible(&lVisible);
if (FAILED(hr))
{
if (hr == E_NOINTERFACE)
{
g_bAudioOnly = true;
return 0;
}
else
{
return -1;
}
}
return 1;
}
HRESULT GetMediaLength(LONGLONG& llMediaLength)
{
// 如果没有实现IMediaSeeking接口,或IMediaSeeking接口已经释放,则llMediaLength = -1
if(ipMedSeeking)
{
ipMedSeeking->GetDuration(&llMediaLength);
}
else
{
llMediaLength = -1;
}
return S_OK;
}
HRESULT GetMediaCurrPos(LONGLONG& llMediaLength)
{
// 如果没有实现IMediaSeeking接口,或IMediaSeeking接口已经释放,则llMediaLength = -1
if(ipMedSeeking)
{
ipMedSeeking->GetCurrentPosition(&llMediaLength);
}
else
{
llMediaLength = -1;
}
return S_OK;
}
HRESULT PlayMedia(void)
{
HRESULT hr;
OAFilterState fs;
// 开始播放
hr = ipMedControl->Run();
// 已开始播放
if(hr == S_OK)
{
g_psCurrent = psRUNNING;
return S_OK;
}
// 准备开始播放
else if(hr == S_FALSE)
{
// 等待其为:已开始播放
do
{
ipMedControl->GetState(50,&fs);
} while(fs!=State_Running);
g_psCurrent = psRUNNING;
return S_OK;
}
// 无法开始播放
else
{
return S_FALSE;
}
}
HRESULT PauseMedia(void)
{
HRESULT hr=S_OK;
OAFilterState fs;
// 暂停
hr = ipMedControl->Pause();
// 已实际暂停
if(hr == S_OK)
{
g_psCurrent = psPAUSED;
return S_OK;
}
// 准备暂停
else if(hr == S_FALSE)
{
// 等待其为:实际暂停
do
{
ipMedControl->GetState(50,&fs);
} while(fs!=State_Paused);
g_psCurrent = psPAUSED;
return S_OK;
}
// 无法暂停
else
{
return S_FALSE;
}
}
HRESULT StopMedia(void)
{
HRESULT hr=S_OK;
LONGLONG pos = 0;
OAFilterState fs;
// 停止播放
hr = ipMedControl->Stop();
// 实际已停止
if(hr == S_OK)
{
g_psCurrent = psSTOPPED;
}
// 准备停止
else if(hr == S_FALSE)
{
// 等待其为 :实际停止
do
{
ipMedControl->GetState(50,&fs);
} while(fs!=State_Stopped);
g_psCurrent = psSTOPPED;
hr = S_OK;
}
// 无法停止 返回
else
{
return S_FALSE;
}
// 定位到文件头
ipMedSeeking->SetPositions(&pos, AM_SEEKING_AbsolutePositioning ,
NULL, AM_SEEKING_NoPositioning);
return hr;
}
HRESULT SpeedMedia(LONGLONG llMediaPos)
{
HRESULT hr;
LONGLONG llMediaLen;
LONGLONG llCurrLen;
// 获取媒体总长度和当前媒体播放位置
GetMediaLength(llMediaLen);
GetMediaCurrPos(llCurrLen);
// 媒体总长度和当前媒体播放位置都不能为-1
assert(llMediaLen!=-1 && llCurrLen!=-1);
// 如果快进量与当前播放位置的和大于总长度,则从头开始播放
if((llCurrLen+llMediaPos)>llMediaLen)
{
hr = ReSetMediaPos(NULL);
}
else
{
hr = ReSetMediaPos(llCurrLen+llMediaPos);
}
return hr;
}
HRESULT BlackMedia(LONGLONG llMediaPos)
{
HRESULT hr;
LONGLONG llMediaLen;
LONGLONG llCurrLen;
// 获取媒体总长度和当前媒体播放位置
GetMediaLength(llMediaLen);
GetMediaCurrPos(llCurrLen);
// 媒体总长度和当前媒体播放位置都不能为-1
assert(llMediaLen!=-1 && llCurrLen!=-1);
// 如果快进量与当前播放位置的差小于0,则从头开始播放
if((llCurrLen-llMediaPos)<0)
{
hr = ReSetMediaPos(NULL);
}
else
{
hr = ReSetMediaPos(llCurrLen-llMediaPos);
}
return hr;
}
HRESULT ReSetMediaPos(LONGLONG llMediaPos)
{
HRESULT hr;
// 先暂停播放,然后重置播放位置
hr = PauseMedia();
if(hr == S_OK)
{
g_psCurrent = psPAUSED;
hr = ipMedSeeking->SetPositions(&llMediaPos,AM_SEEKING_AbsolutePositioning,
NULL,AM_SEEKING_NoFlush);
if(hr!=NOERROR)
{
return S_FALSE;
}
}
else
{
return S_FALSE;
}
// 重置播放位置后,开始播放
hr=PlayMedia();
if(hr!=S_OK)
{
return S_FALSE;
}
else
{
hr = NOERROR;
g_psCurrent = psRUNNING;
}
return hr;
}
HRESULT MinSound(void)
{
// 将音量设置为最小值MIN_SOUND
HRESULT hr;
JIF(ipBasAudio->put_Volume(MIN_SOUND));
return hr;
}
HRESULT MaxSound(void)
{
// 将音量设置为最大值MAX_SOUND
HRESULT hr;
JIF(ipBasAudio->put_Volume(MAX_SOUND));
return hr;
}
HRESULT IncreaseSound(int iIncrease)
{
HRESULT hr;
// 保证输入的增大音量值在0-10000之间
if(iIncrease>=0 && iIncrease<=(MIN_SOUND*(-1)))
{
// 基准音量变为其与增大的音量量的和
g_iBenchSound += iIncrease;
// 当前的基准音量大于最大音量
if(g_iBenchSound>MAX_SOUND)
{
// 将音量设置为最大
JIF(ipBasAudio->put_Volume(MAX_SOUND));
// 将基准音量设置为最大值
g_iBenchSound = MAX_SOUND;
return E_INVALIDARG;
}
// 音量设置为当前基准音量
else
{
JIF(ipBasAudio->put_Volume(g_iBenchSound));
}
}
else
{
return E_INVALIDARG;
}
return hr;
}
HRESULT ReduceSound(int iReduce)
{
HRESULT hr;
// 保证输入的减少音量值在0-10000之间
if(iReduce>=0 && iReduce<=(MIN_SOUND*(-1)))
{
// 基准音量变为其与减少的音量量的差
g_iBenchSound -= iReduce;
// 如果当前基准音量小于最小音量值
if (g_iBenchSound<MIN_SOUND)
{
// 设置音量为最小
JIF(ipBasAudio->put_Volume(MIN_SOUND));
// 基准音量设置为最小值
g_iBenchSound = MIN_SOUND;
return E_INVALIDARG;
}
// 音量设置为当前基准音量
else
{
JIF(ipBasAudio->put_Volume(g_iBenchSound));
}
}
else
{
return E_INVALIDARG;
}
return hr;
}
HRESULT SetSound(double dRate)
{
HRESULT hr;
// 可调的精准度1/1000
if(dRate>0.001 && dRate<=1.0)
{
int iBenchSound = (int)(MIN_SOUND*(1.0-dRate));
JIF(ipBasAudio->put_Volume((long)iBenchSound));
g_iBenchSound = iBenchSound;
}
else
{
return E_INVALIDARG; // 超出当前可设置的音量范围
}
return hr;
}
HRESULT FullScreen(HWND hWnd)
{
HRESULT hr;
LONG lMode;
// 如果窗口句柄无效,直接返回
if(!IsWindow(hWnd))
{
return S_FALSE;
}
// 如果媒体文件仅是音频或视频接口不存在,则无法全屏播放
if (g_bAudioOnly || !ipVidWindow)
{
return S_FALSE;
}
// 如果媒体当前是播放状态则先暂停播放
if (g_psCurrent == psRUNNING)
{
if(S_FALSE == PauseMedia())
{
return S_FALSE;
}
}
JIF(ipVidWindow->get_FullScreenMode(&lMode));
// 如果当前状态不是全屏,则设置为全屏
if (lMode == OAFALSE)
{
// 设置视频窗口将要发送消息的窗口
LIF(ipVidWindow->put_MessageDrain((OAHWND) hWnd));
JIF(ipVidWindow->put_FullScreenMode(OATRUE));
}
// 当前状态已经为全屏了
else
{
hr = S_FALSE;
}
// 如果当前播放状态为暂停,则改变播放状态为开始播放
if (g_psCurrent == psPAUSED)
{
if(S_FALSE == PlayMedia())
{
return S_FALSE;
}
}
return hr;
}
HRESULT OriginalScreen(void)
{
HRESULT hr;
// 恢复原始屏幕
JIF(ipVidWindow->put_FullScreenMode(OAFALSE));
return hr;
}
HRESULT InitVideoWnd(HWND hWnd,RECT rc)
{
HRESULT hr;
// 视频状态才真正调用
if (!g_bAudioOnly)
{
// 设置视频窗口依赖的窗口句柄
JIF(ipVidWindow->put_Owner((OAHWND)hWnd));
// 设置视频窗口样式
JIF(ipVidWindow->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN));
// 设置视频窗口的位置
JIF(ipVidWindow->SetWindowPosition(rc.left, rc.top, rc.right-rc.left,rc.bottom-rc.top));
// 设置视频窗口将要发送消息的窗口
LIF(ipVidWindow->put_MessageDrain((OAHWND) hWnd));
}
else
{
return S_FALSE;
}
return hr;
}
HRESULT SetPlayRate(double dRate)
{
HRESULT hr;
if (S_OK != PauseMedia())
{
return S_FALSE;
}
JIF(ipMedSeeking->SetRate(dRate));
if (S_OK != PlayMedia())
{
return S_FALSE;
}
return hr;
}
void CloseMedia(void)
{
// 隐藏视频窗口后放弃所有权
if(ipVidWindow)
{
ipVidWindow->put_Visible(OAFALSE);
ipVidWindow->put_Owner(NULL);
}
// 注销事件回调
if (ipMedEventEx)
{
ipMedEventEx->SetNotifyWindow((OAHWND)NULL, 0, 0);
}
// 安全释放各接口资源
SAFE_RELEASE(ipMedControl);
SAFE_RELEASE(ipMedEventEx);
SAFE_RELEASE(ipMedSeeking);
SAFE_RELEASE(ipBasVideo);
SAFE_RELEASE(ipBasAudio);
SAFE_RELEASE(ipVidWindow);
SAFE_RELEASE(ipGraBuilder);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -