利用DirectShow实现视频文件播放
- 格式:doc
- 大小:133.00 KB
- 文档页数:28
基于Directshow的H.264网络视频监控客户端实现彭锋;林和志;黄联芬【摘要】In order to realize the real-time broadcasting of H. 264 at video monitoring client, a method that combines Directshow with MFC is adopted. Directshow taken as a drive of the video player receives data from the network, performs cache, decoding and display, and provides the API for MFC, which is used to design UI to control the process of playing. After verification on PC, the player can smoothly play the H. 264 video stream received from network, and the packet loss rate islow. The innovation of this paper is to use Directshow to play smoothly real-time H. 264 stream received from network.%为了实现视频监控客户端对H.264实时播放,采用Directshow与MFC相结合的方法,用Directshow作为视频播放的驱动,从网络接收数据,完成缓存,解码显示及封装成MFC可调用的API,MFC用来设计户界面,对播放过程进行相应的控制,达到了播放从网络接收到的H.264视频的目的.经过PC机上验证,可以流畅地从网络上接收播放H.264视频流,并且丢包率很小.在此用Directshow来实时流畅的播放从网络上接收到的H.264视频流.【期刊名称】《现代电子技术》【年(卷),期】2011(034)008【总页数】3页(P118-120)【关键词】H.264;Directshow;MFC;网络视频监控【作者】彭锋;林和志;黄联芬【作者单位】厦门大学,福建,厦门,361005;厦门大学,福建,厦门,361005;厦门大学,福建,厦门,361005【正文语种】中文【中图分类】TN919-340 引言视频监控以其直观方便信息内容丰富而广泛应用于安保,监控等场合,成为商业,交通,住宅等领域防范的重要手段。
wince下用DirectShow播放音频和视频wince下用DirectShow播放音频和视频虽然网上关于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 valueenum 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 propertytypedef 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 videoLONG lWidth;//Height of the videoLONG lHeight;//Approximate bit rateLONG 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 interfacesIGraphBuilder *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 videoHWND m_hWndNotify; //The window notifyBOOL 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//----------------------------------------------------------------------//InitializeCMedia *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 fileif(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 existingHANDLE hdFile =CreateFile(m_szFileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,NULL,NULL);if(hdFile == INVALID_HANDLE_VALUE)...{//The file doesn't existgoto END;}else...{CloseHandle(hdFile);}}// Initialize COMif(CoInitializeEx(NULL, COINIT_MULTITHREADED) != S_OK)...{goto END;}// Get the interface for DirectShow's GraphBuilderif(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&m_pGB) != S_OK)...{goto END;}// Have the graph construct its the appropriate graph automaticallyif(m_pGB->RenderFile(m_szFileName, NULL) != NOERROR)...{goto END;}// QueryInterface for DirectShow interfacesif(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 filesif(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 modeSetDisplayMode(m_DispMode);bResult = TRUE;END:if(bResult == FALSE)...{//Release the resourceClose();}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 hidingif(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 COMmemset(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 propertym_pBA->get_Volume(&prop.lVolume);m_pBA->get_Balance(&prop.lBalance);//Get the video propertyif(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 modem_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 eventsm_pME->FreeEventParams(evCode, evParam1, evParam2);}else...{return FALSE;}return TRUE;}本文来自CSDN博客,转载请标明出处:/norains/archive/2007/05/14/1609118.aspx。
关于DirectShow在Video视频处理方面的的一些基本应用作者:佚名文章来源:不详点击数:8017更新时间:2005-10-6关于DirectShow在Video视频处理方面的的一些基本应用DirectShow是微软提供的DirectX软件开发包中的一员,主要以处理流媒体数据为重点开发的一套windows下的编程接口,为了高效地处理音视频数据,它向应用提供了直接访问系统底层功能的接口,可以使应用直接控制从数据采集到数据演播的各个中间环节,如数据压缩和解压缩格式等。
DirectShow是以一种比较新的概念来操作媒体流运行的,他有点类似于计算机硬件的管脚Pin的概念,并且引进了滤波器Filter和流图Graph等原理。
媒体流通过流图中的各个滤波器,最后到达输出界面,期间经过各个滤波器的转化和控制使最终数据达到一定的要求。
通过DirectShow的基本接口,应用可以自行设计自己的各种滤波器完成数据在流动中的特殊处理需求,比如视频数据流的格式从MPEG到RGB的变换(解压缩)或象素格式从YUV到RGB的变换等。
DirectShow本身是一个比较复杂的系统,其中包含了许多的概念,对于一个初涉的编程人员,需要学习许多新的知识,特别是对于使用非VC进行应用开发的人员,想要实际使用DirectShow的功能其困难程度是可想而知的。
为此,我这里提供一个折中的解决方案,使得非VC的开发人员也能方便的使用DirectSh ow提供的各种功能,同时也为所有希望进入视频编程领域的开发人员提供一个D irectShow的入门级引导。
这篇文章和相关源码或许能对感兴趣的读者提供一些帮助。
一、DirectShow的graph原理Graph实际上是一个filters的容器,Dshow提供一个Graph控件(Com),应用可以用CoCreateInstance来建立一个graph对象。
在Dshow中还有一个用于创建和操作Graph,这就是Builder对象,一般来讲,应用应该首先创建Build er对象,它是Dshow流控制的关键,Builder接口提供的方法包含了智能创建G raph中filters的能力,当未知媒体流格式时,使用Builder连接Filter时,将智能添加系统中的格式转换Filter到Graph的Filters链中产生目标格式的最佳转换链。
DirectShow 媒体文件回放总结收藏作者:Inkick1.概述DirectShow中媒体文件回放的过程也就是一个为媒体文件选择相应所需的Filter、构建Filter Graph、并对Filter Graph的状态进行维持、控制的过程。
这里所说的媒体文件,不仅仅是指音频、视频文件,同时也包括bmp、jpeg、gif等图形图像格式以及midi等数字化音乐序列。
因此,使用DirectShow进行媒体文件的回放需要经过以下的步骤:2.构建Filter GraphFilter Graph为Filter提供了一个容器,一个构建完整的Filter Graph也就是一个完整的Filter 连路,这个连路对于程序是透明的,可控制的。
而对于每一个媒体文件来说,Filter Graph 与媒体文件存在着对应的关系。
也就是说,一个Filter Graph只能实现一个(种)文件的回放。
在DirectShow中,Filter Graph是由接口对象IGraphBuilder实现的,我们可以调用Win32 API 函数CoCreateInstance()建立一个实体。
Filter Graph实体建立之后并不具有任何的Filter,因此不具有任何实际用途。
因此我们需要连接需要的Filter来完成FilterGraph的构建。
智能连接这个术语覆盖了一系列Filter Graph Manager用于构建所有或部份filter graph的算法。
任何时候,当Filter Graph Manager需要添加filter来完成graph时,它大致做以下几件事情:如果有一个filter存在于graph中,而且这个filter有至少一个没有连接的input pin,Filter Graph Manager试着去试用这个filter。
否则,Filter Graph Manager在已注册的filter中寻找连接时可以接受合适的媒体类型的filter。
每一个filter都注册有一个Merit值,这个值用以标记哪个filter最容易被Filter Graph Manager 选中来完成graph。
Filter Graph Manager按Merit值的顺序来选择filter,Merit值越大,被选中的机会越大。
对于每种流类型(如音频、视频、MIDI),默认的renderer具有一个很高的Merit值,解码器同样是,专用filter具有低Merit值。
如果Filter Graph Manager选择的filter不合适,它会返回来尝试另外的filter组合。
我们有三种构建graph的途径:1.filter graph manager构建整个graph2.filter graph manager构建部分graph3.应用程序构建整个graph2.1 RenderFileIGraphBuilder提供了多种智能完成FilterGraph构建的方法。
最简单的是使用接口方法IGraphBuilder::RenderFile。
HRESULT RenderFile(LPCWSTR lpwstrFile, LPCWSTR lpwstrPlayList);第一个参数为文件的路径(祥见后文),第二个参数保留,必须为空。
这个方法需要一个表示媒体文件路径或者URL的Unicode字符串参数。
而我们通过界面获得的文件路径的字符串往往是ANSI字符串。
我们可以使用下面方法进行转换:包含头文件:#include <tchar.h>#include <atlbase.h>这两个头文件包含了ANSI字符串与Unicode字符串相互转化的函数与宏使用宏:USES_CONVERSION;定义一个WCHAR的数组:WCHAR FileName[MAX_PA TH];而MAX_PA TH是在windef.h中定义的:#define MAX_PA TH 260这与windows路径最大字符为260个相符。
这个数组保存转化后的Unicode形式表示的路径。
然后可以使用下面的函数进行转换:(假定以ANSI形式给出的字符串为szFile)wcsncpy (FileName, T2W(szFile), NUMELMS(wFile)-1);FileName[MAX_PA TH-1] = 0;wcsncpy的原型:wchar_t *wcsncpy( wchar_t *strDest, const wchar_t *strSource, size_t count );这个函数的作用类似于strcpy,是实现字符串之间的复制。
只不过,这是一个用在Unicode 上的版本。
第一个参数指定了字符串转化后的存放地址,也就是我们要得到的Unicode字符串,第二个参数指定了要转化的字符串的来源地址,也就是我们要转化的ANSI字符串。
在第二个参数使用了宏T2W(szFile),这个宏可以把一个ANSI字符串转为一个WCHAR类型的字符串。
第三个参数为转化字符串中字符的数量。
现在问题出来了,这个函数的第二个参数需要宽字符串的地址,如果我们有这样的一个地址,我们还转换什么?因此这个的关键在于T2W上面。
让我们来看一下T2W的定义。
这个定义在头文件A TLCONV.H里面(只保留我们比较感兴趣的部分)。
#ifdef _UNICODEinline LPWSTR T2W(LPTSTR lp) { return lp; }inline LPTSTR W2T(LPWSTR lp) { return lp; }#else#define T2W A2W#endif我们可以看到,如果定义了_UNICODE,则T2W直接返回将要转化的字符串,这是因为在Unicode环境下,ANSI字符拥有和Unicode字符同样的宽度。
也就是说,ANSI是Unicode 的一个子集。
但是在非Unicode环境下,就将T2W替换成A2W,这样我们返回来看A2W 的定义(这个定义在同样的头文件中):#define A2W(lpa) (\((LPCSTR)lpa == NULL) ? NULL : (\_convert = (lstrlenA(lpa)+1),\A TLA2WHELPER((LPWSTR) alloca(_convert*2), lpa, _convert)))宏定义比较晦涩,我们转换成比较好理解的函数形式:LPCSTR A2W(LPTSTR lpa){if(lpa == NULL){return NULL;}_convert = (lstrlen(A)(lpa)+1);AtlA2WHelper ((LPWSTR)alloca(_covert*2),lpa,_convert);return lpa;}这个函数的结构比较清晰,首先判断是不是空字符串,如果是空字符串就返回空,因为ANSI 和Unicode意义上的空字符串都是NULL,如果不为空,则开始转换。
重点是函数A TLA2WWHELPER(),这个函数的实现部分在A TLCONV.CPP中:LPWSTR WINAPI AtlA2WHelper(LPWSTR lpw, LPCSTR lpa, int nChars){_ASSERTE(lpa != NULL);_ASSERTE(lpw != NULL);lpw[0] = '\0';MultiByteToWideChar(CP_ACP, 0, lpa, -1, lpw, nChars);return lpw;}这个函数除了做一些必要的安全性判定以及前序准备以外,核心工作是调用了一个函数MultiByteToWideChar,因此我们还要继续深入。
这次我们发现我们找不到源代码了,但是在MSDN中我们可以得到明确的提示:int MultiByteToWideChar(UINT CodePage, // code pageDWORD dwFlags, // character-type optionsLPCSTR lpMultiByteStr, // address of string to mapint cchMultiByte, // number of bytes in stringLPWSTR lpWideCharStr, // address of wide-character bufferint cchWideChar // size of buffer);这个API函数接受六个参数(Win32 API的风格——参数超多),第一个参数指定了CodePage(解释见附录5.1),在这里我们可以指定CP_ACP,来表示我们选择ANSI Code Page 代表我们要转换的源字符串编码形式为ANSI,第二个参数是一组位标志,决定了如何处理原字符串中的控制字符或者无效字符。
一般指定MB_PRECOMPOSED,第三个参数是源字符串的地址(或者指针)。
第四个字参数是要转化的字符串里面包含了多少个字符,第五个参数指定了转换后的字符串的存放地址,第六个参数指定了转换后字符串的Buffer的大小,也就是转换后占用内存空间的多少。
现在我们逐层返回,(如果你已经忘记了我们的初衷是什么建议你听一下F.I.R的歌),根据最核心的函数MultiByteToWideChar的分析,我们可以得出AtlA2Whelper几个参数的含义:AtlA2WHelper( LPWSTR lpw, //转化的目标字符串的地址LPCSTR lpa, //转化的源字符串的地址int nChars //源字符串中包含的字符数)因此在判断lpw以及lpa不为空之后便直接调用:lpw[0] = '\0';MultiByteToWideChar(CP_ACP, 0, lpa, -1, lpw, nChars);我们已经返回到了那个宏,还是让我们来看我们改写的那个函数吧:_convert = (lstrlen(A)(lpa)+1);AtlA2WHelper ((LPWSTR)alloca(_covert*2),lpa,_convert);return lpa;lstrlen函数得到参数字符串的长度(对于ANSI字符串来说是字节数,对于Unicode字符串来说是字符数),后面的(A)表明参数字符串为ANSI字符串,经过这个参数的调用,_convert 的值变成了要转换的字符串长度加一。
为什么要加一呢?因为字符串的结尾要补上一个’\0’,我们总要为这个’\0’预留空间。
下一步就是要申请空间来保存转化完成的字符串了,使用(LPWSTR)alloca(_covert*2),分配字符串数的两倍空间(因为Unicode字符占用的空间是ANSI的两倍),然后将地址转为LPWSTR,当作参数传递给AtlA2Whelper,开始转换,任务完成!可是,真的完成了吗?那个该死的_convert是哪里来的?哈哈,还记得我们一开始说的吗?回忆一下,在使用T2W之前要做的工作是什么?使用宏USES_CONVERSION;!为什么呢?我们继续看这个宏的定义:#ifndef _DEBUG#else#define USES_CONVERSION int _convert = 0#endif看到了吗?这个宏其实就是在定义这个_convert变量。