//========================================================================
//TITLE:
// wince下用DirectShow播放音频和视频
//AUTHOR:
// norains
//DATE:
// Monday 14-May-2007
//Environment:
// WinCE 5.0
//========================================================================
虽然网上关于wince下如何使用DirectShow播放多媒体文件的资料不多,但WinCE毕竟还属于windows,而桌面系统的DirectShow例子网上信手拈来,并且其中DirectShow的功能方法与之WinCE下差别不大,又本人实在没有信心比他们的轮子造得更为华丽,所以这篇文章就直接切入正题,不介绍DirectShow的结构功能,直接来看看怎么用吧.(其实还是自己懒惰的原因大一些,恩,不过这个和本文的主题没多大关系:-)).
为了方便代码的移植,所以我将DirectShow的操作封装成CMedia类,只要直接调用该类,就可以相当简便地调用DirectShow来播放多媒体文件了
好,闲话至此,我们以具体代码看看是如何:
//
获取CMedia的实例
CMedia
*
m_pMedia
=
CMedia::GetInstance();
//
设置播放的窗口
m_pMedia
->
SetVideoWindow(hWnd);
//
打开媒体文件
m_pMedia
->
Open(TEXT(
"
A.AVI
"
));
//
播放
m_pMedia
->
Play();
...
//
播放结束后,调用Close释放资源
m_pMedia
->
Open();
没错,就是六行代码,就这么简单,可以顺利播放媒体文件.在这里要说一下的是,因为我们播放的是视频,需要有一个窗口显示,所以需要调用SetVideoWindow()函数来设置播放窗口.这个播放视频的窗口,可以是普通的窗口,也可以是Picture控件.当然咯,如果是播放音频文件,那么则完全可以无视这个函数.
还有一个最值得注意的地方,当调用Open()成功之后,一定要调用Close()来释放资源,然后才能打开另一个媒体文件.否则,不释放的资源可能会导致很多莫名其妙的后果哦.
等等,代码似乎还不完美,比如说,我想在文件播放之后再接着播放另外一个文件,那么我如何知道什么时候文件已经播放完毕了呢?这时候我们就需要请出SetNotifyWindow().
该函数的作用是设置一个接受消息的窗口,当DirectShow有事件变更时,就会发送指定的消息到指定的窗口,原型如下:
SetNotifyWindow(HWND hWnd, UINT wMsg,long lInstanceData)
hWnd:接收消息的窗口句柄.
wMsg:指定的自定义消息
lInstanceData:消息的参数.
那么,现在以接收一个视频播放结束事件的代码片段为例子:
//
自定义一个消息
#define
WM_GRAPHNOTIFY (WM_USER + 13)
//
设置接收消息窗口和消息
m_pMedia
->
SetVideoWindow(hWnd,WM_GRAPHNOTIFY,NULL);
...
//
这个是消息循环函数
LRESULT CMainWnd::WndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
switch(wMsg)
{
...
case WM_GRAPHNOTIFY:
{
LONG evCode,evParam1,evParam2;
//获取此时的DirectShow事件
if(m_pMedia->GetEvent(&evCode,&evParam1,&evParam2) == TRUE)
{
if(evCode == EC_COMPLETE)
{
MessageBox(NULL,TEXT("播放完毕"),TEXT(""),MB_OK);
}
}
return 0;
}
}
...
}
好了,知道播放完毕,就这么简单.恩,还很复杂..?呵呵,我觉得已经很简单了.
文章的最后,让我们再来看看CMedia的其它几个有用的函数吧:
CheckVisibility()
描述:判断文件的种类
当返回值为TRUE时,为视频文件;反之为只是音频文件.
SetVolume(LONG lVolume, LONG lBalance)
描述:设置音量.
lVolume:设置音量的大小,范围为–10,000 到 0.
lBalance:设置左右音量的均衡,范围是–10,000 到 10,000,默认是0.
SetDisplayMode(DISPLAYMODE mode)
描述:设置播放模式.
DISP_FIT:按比例拉伸至视屏窗口.
DISP_STRETCH:不按比例拉伸至视屏窗口.
DISP_NATIVE:如果视频原本尺寸小于屏幕,则以原视频文件大小播放.否则,将和DISP_FIT相同
DISP_FULLSCREEN:全屏
/
//
Media.h: interface for the CMedia class.
//
//
Version:
//
1.2.0
//
Date:
//
2007.05.08
//
#ifndef MEDIA_H
#define
MEDIA_H


#include
<
mmsystem.h
>
#include
<
streams.h
>

//
--------------------------------------------------------------------
//
Macro define

//
The volume value
#define
MAX_VOLUME 0
#define
MIN_VOLUME -10000

//
The balance value
#define
MAX_BALANCE 10000
#define
MIN_BALANCE -10000

//
--------------------------------------------------------------------
//
Enum value
enum
DISPLAYMODE
{
//Fit to the play window size. How wide (height) the window is, how
//is the move. Keep aspect ratio.
DISP_FIT,

//Stretch to the play window size. Don't keep the aspect ratio.
DISP_STRETCH,

//Full screen play.
DISP_FULLSCREEN,

//When the size of video is smaller than the play window, it displayes
//as the video size. If it's bigger , it just like the DISP_FIT mode.
DISP_NATIVE
}
;
//
--------------------------------------------------------------------

//
The media file property
typedef
struct
{
//The volume range is –10,000 to 0.
//Divide by 100 to get equivalent decibel value (for example –10,000 = –100 dB).
LONG lVolume;

//The value from –10,000 to 10,000 indicating the stereo balance
//As with the Volume property, units correspond to .01 decibels (multiplied by –1 when plBalance is a positive value).
//For example, a value of 1000 indicates –10 dB on the right channel and –90 dB on the left channel.
LONG lBalance;

//Width of the video
LONG lWidth;

//Height of the video
LONG lHeight;

//Approximate bit rate
LONG lBitRate;

}
MEDIAPROPERTY,
*
PMEDIAPROPERTY;
//
--------------------------------------------------------------------
class
CMedia
{
public:
BOOL GetEvent(LONG *plEvCode, LONG *plParam1, LONG *plParam2);
BOOL SetNotifyWindow(HWND hWnd, UINT wMsg,long lInstanceData);
BOOL SetVolume(LONG lVolume, LONG lBalance = 0);
BOOL SetDisplayMode(DISPLAYMODE mode);
BOOL GetMediaProperty(PMEDIAPROPERTY pOutProperty);
static CMedia * GetInstance();
void Close();
BOOL CheckVisibility();
void SetVideoWindow(HWND hWndVideo);
BOOL Open(TCHAR * pszFileName);
BOOL Stop();
BOOL Pause();
BOOL Play();
virtual ~CMedia();

protected:


CMedia();
// Collection of interfaces
IGraphBuilder *m_pGB;
IMediaControl *m_pMC;
IMediaEventEx *m_pME;
IVideoWindow *m_pVW;
IBasicAudio *m_pBA;
IBasicVideo *m_pBV;
IMediaSeeking *m_pMS;

TCHAR m_szFileName[MAX_PATH];
HWND m_hWndVideo; //The window play video
HWND m_hWndNotify; //The window notify
BOOL m_bExitThrd;
BOOL m_bThrdRunning;
static CMedia * m_pInstance;
DISPLAYMODE m_DispMode;

}
;

#endif
//
#ifndef MEDIA_H



//
//
Media.cpp: implementation of the CMedia class.
//
//
#include
"
stdafx.h
"
#include
"
Media.h
"


//
----------------------------------------------------------------------------------------------
//
Macro define

//
Default play mode
#define
DEFAULT_DISPLAY_MODE DISP_NATIVE

//
----------------------------------------------------------------------
//
Initialize
CMedia
*
CMedia::m_pInstance
=
NULL;
//
------------------------------------------------------------------------

//
//
Construction/Destruction
//
CMedia::CMedia():
m_pGB(NULL),
m_pMC(NULL),
m_pME(NULL),
m_pVW(NULL),
m_pBA(NULL),
m_pBV(NULL),
m_pMS(NULL),
m_hWndVideo(NULL),
m_bExitThrd(TRUE),
m_bThrdRunning(FALSE),
m_DispMode(DEFAULT_DISPLAY_MODE),
m_hWndNotify(NULL)
{
memset(m_szFileName,0,sizeof(m_szFileName));
}

CMedia::
~
CMedia()
{
if(m_pInstance != NULL)
{
delete m_pInstance;
m_pInstance = NULL;
}

}




//
------------------------------------------------------------
//
Description:
//
Play the media file
//
When you call the function,you should call Open() before.
//
//
-------------------------------------------------------------
BOOL CMedia::Play()
{
// Run the graph to play the media file

if(m_pMC == NULL)
{
return FALSE;
}



m_pMC->Run();

return TRUE;
}




//
------------------------------------------------------------
//
Description:
//
Pause.
//
When you call the function,you should call Open() before.
//
//
-------------------------------------------------------------
BOOL CMedia::Pause()
{

if(m_pMC == NULL)
{
return FALSE;
}
m_pMC->Pause();

return TRUE;
}




//
------------------------------------------------------------
//
Description:
//
Stop.
//
When you call the function,you should call Open() before.
//
//
-------------------------------------------------------------
BOOL CMedia::Stop()
{

if(m_pMC == NULL || m_pMS == NULL)
{
return FALSE;
}

m_pMC->Stop();
m_pMS->SetPositions(0, AM_SEEKING_AbsolutePositioning,NULL,AM_SEEKING_NoPositioning);
return TRUE;
}




//
--------------------------------------------------------------------------
//
Description:
//
Open the media file. When succeed in calling the function ,
//
you should call the Close() to release the resource
//
//
-------------------------------------------------------------------------
BOOL CMedia::Open(TCHAR
*
pszFileName)
{
BOOL bResult = FALSE;

if(_tcslen(pszFileName) >= MAX_PATH)
{
goto END;
}
else
{
_tcscpy(m_szFileName,pszFileName);

//Check the file existing
HANDLE hdFile = CreateFile(m_szFileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,NULL,NULL);
if(hdFile == INVALID_HANDLE_VALUE)
{
//The file doesn't exist
goto END;
}
else
{
CloseHandle(hdFile);
}
}




// Initialize COM
if(CoInitializeEx(NULL, COINIT_MULTITHREADED) != S_OK)
{
goto END;
}

// Get the interface for DirectShow's GraphBuilder
if(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&m_pGB) != S_OK)
{
goto END;
}


// Have the graph construct its the appropriate graph automatically
if(m_pGB->RenderFile(m_szFileName, NULL) != NOERROR)
{
goto END;
}

// QueryInterface for DirectShow interfaces
if(m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC) != NOERROR)
{
goto END;
}
if(m_pGB->QueryInterface(IID_IMediaEventEx, (void **)&m_pME) != NOERROR)
{
goto END;
}
if(m_pGB->QueryInterface(IID_IMediaSeeking, (void **)&m_pMS) != NOERROR)
{
goto END;
}

// Query for video interfaces, which may not be relevant for audio files
if(m_pGB->QueryInterface(IID_IVideoWindow, (void **)&m_pVW) != NOERROR)
{
goto END;
}
if(m_pGB->QueryInterface(IID_IBasicVideo, (void **)&m_pBV) != NOERROR)
{
goto END;
}

// Query for audio interfaces, which may not be relevant for video-only files
if(m_pGB->QueryInterface(IID_IBasicAudio, (void **)&m_pBA) != NOERROR)
{
goto END;
}
// Is this an audio-only file (no video component)?
if (CheckVisibility() == TRUE)
{
if(m_pVW->put_Owner((OAHWND)m_hWndVideo) != NOERROR)
{
goto END;
}

if(m_pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN) != NOERROR)
{
goto END;
}
}


//Set play mode
SetDisplayMode(m_DispMode);
bResult = TRUE;

END:

if(bResult == FALSE)
{
//Release the resource
Close();
}

return bResult;
}




//
------------------------------------------------------------
//
Description:
//
This method sets an owning parent for the video window.
//
//
Parameters:
//
hWnd : [in] Handle of new owner window.
//
//
----------------------------------------------------------
void
CMedia::SetVideoWindow(HWND hWndVideo)
{
m_hWndVideo = hWndVideo;
}



//
------------------------------------------------------------
//
Description:
//
Check the file visibility
//
When you call the function,you should call Open() before.
//
//
Parameters:
//
TRUE: Video
//
FALSE: It's not the video
//
//
------------------------------------------------------------
BOOL CMedia::CheckVisibility()
{
if (!m_pVW)
{
//No VideoWindow interface. Assuming audio/MIDI file or unsupported video codec
return FALSE;
}
if (!m_pBV)
{
//No BasicVideo interface. Assuming audio/MIDI file or unsupported video codec.
return FALSE;
}
// If this is an audio-only clip, get_Visible() won't work.
//
// Also, if this video is encoded with an unsupported codec,
// we won't see any video, although the audio will work if it is
// of a supported format.
long lVisible;
if(m_pVW->get_Visible(&lVisible) != NOERROR)
{
return FALSE;
}
return TRUE;
}




//
------------------------------------------------------------
//
Description:
//
Release the resource which opened in the Open()
//
//
------------------------------------------------------------
void
CMedia::Close()
{

// Relinquish ownership (IMPORTANT!) after hiding
if(m_pVW)
{
m_pVW->put_Visible(OAFALSE);
m_pVW->put_Owner(NULL);
}

if(m_pMC != NULL)
{
m_pMC->Release();
m_pMC = NULL;
}

if(m_pME != NULL)
{
m_pME->SetNotifyWindow(NULL,NULL,NULL);

m_pME->Release();
m_pME = NULL;
}

if(m_pMS != NULL)
{
m_pMS->Release();
m_pMS = NULL;
}

if(m_pBV != NULL)
{
m_pBV->Release();
m_pBV = NULL;
}
if(m_pBA != NULL)
{
m_pBA->Release();
m_pBA = NULL;
}
if(m_pVW != NULL)
{
m_pVW->Release();
m_pVW = NULL;
}

if(m_pGB != NULL)
{
m_pGB->Release();
m_pGB = NULL;
}

// Finished with COM
memset(m_szFileName,0,sizeof(m_szFileName));


CoUninitialize();
}


//
------------------------------------------------------------
//
Description:
//
Get the instance of object
//
//
------------------------------------------------------------
CMedia
*
CMedia::GetInstance()
{
if(m_pInstance == NULL)
{
m_pInstance = new CMedia();
}

return m_pInstance;
}




//
------------------------------------------------------------
//
Description:
//
Get the media file property.
//
When you call the function,you should call Open() before.
//
//
------------------------------------------------------------
BOOL CMedia::GetMediaProperty(PMEDIAPROPERTY pOutProperty)
{

MEDIAPROPERTY prop = {0};

if(m_pBA == NULL || m_pBV == NULL)
{
return FALSE;
}


//Get the audio property
m_pBA->get_Volume(&prop.lVolume);
m_pBA->get_Balance(&prop.lBalance);


//Get the video property
if(CheckVisibility() == TRUE)
{
m_pBV->get_BitRate(&prop.lBitRate);
m_pBV->GetVideoSize(&prop.lWidth,&prop.lHeight);

}

*pOutProperty = prop;


return TRUE;
}


//
------------------------------------------------------------
//
Description:
//
Set the display mode.
//
When you call the function,you should call Open() before.
//
//
------------------------------------------------------------
BOOL CMedia::SetDisplayMode(DISPLAYMODE mode)
{
if(m_pVW == NULL)
{
return FALSE;
}

m_DispMode = mode;

if(mode == DISP_FULLSCREEN)
{
m_pVW->put_FullScreenMode(OATRUE);
}
else
{
//Restore to the normal mode
m_pVW->put_FullScreenMode(OAFALSE);
RECT rcWnd = {0};
GetClientRect(m_hWndVideo,&rcWnd);
LONG lWndWidth = rcWnd.right - rcWnd.left;
LONG lWndHeight = rcWnd.bottom - rcWnd.top;

MEDIAPROPERTY prop = {0};
GetMediaProperty(&prop);


if(mode == DISP_FIT || mode == DISP_NATIVE)
{
LONG lDispLeft,lDispTop,lDispWidth,lDispHeight;

if(mode == DISP_NATIVE && lWndWidth >= prop.lWidth && lWndHeight >= prop.lHeight)
{
lDispLeft = (lWndWidth - prop.lWidth) / 2;
lDispTop = (lWndHeight - prop.lHeight) / 2;
lDispWidth = prop.lWidth;
lDispHeight = prop.lHeight;
}
else
{
if(prop.lWidth * lWndHeight > lWndWidth * prop.lHeight)
{
lDispWidth = lWndWidth;
lDispHeight = (LONG)((float)lDispWidth / (float)prop.lWidth * prop.lHeight);
lDispLeft = 0;
lDispTop = (lWndHeight - lDispHeight) / 2;
}
else if(prop.lWidth * lWndHeight < lWndWidth * prop.lHeight)
{
lDispHeight = lWndHeight;
lDispWidth = (LONG)((float)lDispHeight / (float)prop.lHeight * prop.lWidth);
lDispLeft = (lWndWidth - lDispWidth) / 2;
lDispTop = 0;
}
else
{
lDispWidth = lWndWidth;
lDispHeight = lWndHeight;
lDispLeft = 0;
lDispTop = 0;
}
}




m_pVW->put_Left(lDispLeft);
m_pVW->put_Top(lDispTop);
m_pVW->put_Width(lDispWidth);
m_pVW->put_Height(lDispHeight);
}
else if(mode == DISP_STRETCH)
{

m_pVW->put_Left(0);
m_pVW->put_Top(0);
m_pVW->put_Width(lWndWidth);
m_pVW->put_Height(lWndHeight);
}
}



return TRUE;
}


//
------------------------------------------------------------
//
Description:
//
Set the volume.
//
When you call the function,you should call Open() before.
//
//
Parameters:
//
lVolume:[in] The volume (amplitude) of the audio signal.
//
Range is –10,000 to 0.
//
lBalance:[in] The balance for the audio signal. Default value is 0.
//
The value from –10,000 to 10,000 indicating the stereo balance.
//
//
------------------------------------------------------------
BOOL CMedia::SetVolume(LONG lVolume, LONG lBalance)
{
if(m_pBA == NULL)
{
return FALSE;
}

if(lVolume < MIN_VOLUME && lVolume > MAX_VOLUME && lBalance < MIN_BALANCE && lBalance > MAX_BALANCE)
{
return FALSE;
}

m_pBA->put_Volume(lVolume);
m_pBA->put_Balance(lBalance);

return TRUE;
}




//
----------------------------------------------------------------------
//
Description:
//
Registers a window that will handle messages when a specified event occurs.
//
//
Parameters:
//
hWnd:[in] Handle of window to notify. Pass NULL to stop notification.
//
wMsg:[in] Window message to be passed as the notification.
//
lInstanceData:[in] Value (instance data) to be passed as the lParam parameter for the lMsg message.
//
//
-----------------------------------------------------------------------------
BOOL CMedia::SetNotifyWindow(HWND hWnd, UINT wMsg,
long
lInstanceData)
{
if(m_pME == NULL)
{
return FALSE;
}

m_pME->SetNotifyWindow((OAHWND)hWnd,wMsg,lInstanceData);

return TRUE;
}


//
----------------------------------------------------------------------
//
Description:
//
This method retrieves the notification event.
//
//
-----------------------------------------------------------------------
BOOL CMedia::GetEvent(LONG
*
plEvCode, LONG
*
plParam1, LONG
*
plParam2)
{
if(m_pME == NULL)
{
return FALSE;
}

LONG evCode, evParam1, evParam2;

if(m_pME->GetEvent(&evCode, &evParam1, &evParam2, 0) == NOERROR)
{
*plEvCode = evCode;
*plParam1 = evParam1;
*plParam2 = evParam2;

// Spin through the events
m_pME->FreeEventParams(evCode, evParam1, evParam2);
}
else
{
return FALSE;
}

return TRUE;
}
//TITLE:
// wince下用DirectShow播放音频和视频
//AUTHOR:
// norains
//DATE:
// Monday 14-May-2007
//Environment:
// WinCE 5.0
//========================================================================
虽然网上关于wince下如何使用DirectShow播放多媒体文件的资料不多,但WinCE毕竟还属于windows,而桌面系统的DirectShow例子网上信手拈来,并且其中DirectShow的功能方法与之WinCE下差别不大,又本人实在没有信心比他们的轮子造得更为华丽,所以这篇文章就直接切入正题,不介绍DirectShow的结构功能,直接来看看怎么用吧.(其实还是自己懒惰的原因大一些,恩,不过这个和本文的主题没多大关系:-)).
为了方便代码的移植,所以我将DirectShow的操作封装成CMedia类,只要直接调用该类,就可以相当简便地调用DirectShow来播放多媒体文件了
好,闲话至此,我们以具体代码看看是如何:
















没错,就是六行代码,就这么简单,可以顺利播放媒体文件.在这里要说一下的是,因为我们播放的是视频,需要有一个窗口显示,所以需要调用SetVideoWindow()函数来设置播放窗口.这个播放视频的窗口,可以是普通的窗口,也可以是Picture控件.当然咯,如果是播放音频文件,那么则完全可以无视这个函数.
还有一个最值得注意的地方,当调用Open()成功之后,一定要调用Close()来释放资源,然后才能打开另一个媒体文件.否则,不释放的资源可能会导致很多莫名其妙的后果哦.
等等,代码似乎还不完美,比如说,我想在文件播放之后再接着播放另外一个文件,那么我如何知道什么时候文件已经播放完毕了呢?这时候我们就需要请出SetNotifyWindow().
该函数的作用是设置一个接受消息的窗口,当DirectShow有事件变更时,就会发送指定的消息到指定的窗口,原型如下:
SetNotifyWindow(HWND hWnd, UINT wMsg,long lInstanceData)
hWnd:接收消息的窗口句柄.
wMsg:指定的自定义消息
lInstanceData:消息的参数.
那么,现在以接收一个视频播放结束事件的代码片段为例子:



































好了,知道播放完毕,就这么简单.恩,还很复杂..?呵呵,我觉得已经很简单了.
文章的最后,让我们再来看看CMedia的其它几个有用的函数吧:
CheckVisibility()
描述:判断文件的种类
当返回值为TRUE时,为视频文件;反之为只是音频文件.
SetVolume(LONG lVolume, LONG lBalance)
描述:设置音量.
lVolume:设置音量的大小,范围为–10,000 到 0.
lBalance:设置左右音量的均衡,范围是–10,000 到 10,000,默认是0.
SetDisplayMode(DISPLAYMODE mode)
描述:设置播放模式.
DISP_FIT:按比例拉伸至视屏窗口.
DISP_STRETCH:不按比例拉伸至视屏窗口.
DISP_NATIVE:如果视频原本尺寸小于屏幕,则以原视频文件大小播放.否则,将和DISP_FIT相同
DISP_FULLSCREEN:全屏































































































































































































































































































































































































































































































































































































































































































































































