⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mediaplay.cpp

📁 wince,用DrectShow实现的媒体播放的DLL封装类,支持mp3,wav,wmv,wma,avi等格式.
💻 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 + -