|
XML:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 利用ATL实现QuickTime 多媒体文件播放 摘要 本文主要介绍了Windows平台上Visual C++ 6利用ATL库和QuickTime SDK开发播放QuickTime多媒体软件。为从事多媒体播放开发工作者提供借鉴和参考。 关键词 多媒体 播放 QuickTime ATL 1 前言 在当今多媒体播放软件主要有Windows media player、Real player和QuickTime。 Apple公司的QuickTime于1991年登台亮相,是Apple公司面向专业视频编辑、Web网站创建和CD-ROM内容制作领域开发的多媒体技术平台,QuickTime支持几乎所有主流的个人计算机平台,是数字媒体领域事实上的工业标准,是创建3D动画、实时效果、虚拟现实、A/V和其他数字流媒体的重要基础。 由于众多企业有对QuickTime player应用的需求,在国内外相关资料中有用Windows SDK或MFC的相关应用,本文试用小巧的ATL库和QuickTime SDK开发定制QuickTime 多媒体播放软件做了分析。 2 QuickTime Windows程序的开发概述 2.1开发前的准备 登陆Apple官方网站下载QuickTime SDK和了解有关技术资料。 由于QuickTime原先是为Mac OS设计,所以它里面的许多概念和函数的调用都是面向Mac。 表1 Windows and QTML 术语比较 Windows 术语 | QTML 对应术语 | Message ( MSG ) | Event ( EventRecord ) | GraphiCS Device Interface (GDI) | QuickDraw | Device context ( DC ) | Graphics port ( CGrafPort ) | Window handle ( HWND ) | Window pointer ( CWindowPtr ) | Common Dialog Box Library | Standard File Package |
对于一个原来是Windows程序员必须对于一些QuickTime概念有些最基本的了解才能比较快的掌握典型QuickTime Windows程序的开发。 2.2开发基本步骤 开发一个简单的QuickTime Windows程序必须采取下面基本步骤加入到Windows应用中。 2.2.1在程序的开头初始化QuickTime媒体层(InitializQTML)和QuickTime(EnterMovies)。 2.2.2和电影窗口建立图形端口的关联(CreatePortAssociation)。 2.2.3打开电影(OpenMovieFile)和得到电影的句柄(NewMovieFromFile)。 2.2.4创建在屏幕上显示电影图像的控件(NewMovieController)。 2.2.5在Windows处理函数中,将接收的Windows消息转换为QTML事件(WinEventToMacEvent)并将它们传到电影控件处理(MCIsPlayerEvent)。 2.2.6如果不在用到,释放电影句柄(DisposeMovie)和电影控件(DisPoseMovieController)。 2.2.7当销毁窗口时,破坏电影图形端口的关联(DestroyPortAssociation)。 3 在ATL上实现播放 3.1用ATL创建Windows窗口 以CWindowImpl为基类,编写自己的窗口类CQTVideoWnd。并且定义宏来接收窗口消息。 #define MY_QT_MSG_HANDLE(func) \ { \ BOOL bHandled = TRUE; \ lResult; \ func(uMsg,wParam,lParam,bHandled); \ if(bHandled) \ return TRUE; \ } class CQTVideoWnd: public CWindowImpl<CVideoPlayerQT> { public: CQTVideoWnd(HWND hParent, RECT& rc, IVideoPlayerNotifySink* pVPSink); virtual ~ CQTVideoWnd(); public: BEGIN_MSG_MAP(CVideoPlayerQT) MY_QT_MSG_HANDLE (NewProc) END_MSG_MAP() protected: LRESULT NewProc(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); …… CQuickTime m_QT; …… } CQuickTime为笔者调用QuickTime API的辅助类,后面将介绍。 NewProc成员函数根据接收不同的窗口消息分别调用不同的成员函数处理。 LRESULT CQTVideoWnd::NewProc(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if(uMsg == WM_ERASEBKGND) { bHandled = FALSE; LRESULT theResult = DefWindowProc(uMsg, wParam, lParam); m_QT.ProcessMovieEvent (m_hWnd, uMsg, wParam, lParam); return theResult; } else { m_QT.ProcessMovieEvent (m_hWnd, uMsg, wParam, lParam); switch(uMsg) { case WM_CREATE: OnCreate(uMsg, wParam, lParam,bHandled); break; case WM_PAINT: OnPaint(uMsg, wParam, lParam,bHandled); break; case WM_DESTROY: OnDestroy(uMsg, wParam, lParam,bHandled); break; default: bHandled = FALSE; break; } } return 0; } 其中OnCreate、OnPaint、OnDestroy等成员函数将根据2.2,分别调用辅助类CQuickTime处理。如 LRESULT CQTVideoWnd::OnCreate(UINT uMsg, WPARAM wParam , LPARAM lParam, BOOL& bHandled) { m_QT.OnMovieWindowCreate(m_hWnd,NULL); return 0; } 3.2 CQuickTime CQuickTime为调用QuickTime API函数的辅助类。 3.2.1初始化和退出应用 InitializeQT和Terminate分别为初始化QuickTime媒体层和退出QuickTime媒体层。它们可以在程序的开始和结束。我们在CQTVideoWnd的构造函数和析构函数中调用它们。 BOOL CQuickTime::InitializeQT(IQTEventSink* pQTEventSink /*= NULL*/) { …… OSErr Err = noErr; // Initialize QuickTime Media Layer Err = InitializeQTML(0); // Initialize QuickTime if(Err == noErr) { Err = EnterMovies(); } else { Err = QT_NOPLAYER; } if(Err == noErr) { //Because we can not distinguish the error from which object //So we marked SetMoviesErrorProc // SetMoviesErrorProc(MoviesErrorProc,(long)this); } else Err = QT_INITIAL_ERR; …… return Err == noErr ? TRUE : FALSE; } 如果要得到QuickTime的错误代码,我们可以在初始化完后调用SetMoviesErrorProc函数,但是假如一个应用中有多个QuickTime的电影对象。我们将不能区分错误来自哪个对象。 void CQuickTime::Terminate() { // Clean up ExitMovies(); TerminateQTML(); DebugInfo("CQuickTime::Terminate this = %p,m_pQTEventSink = %p",this,m_pQTEventSink); } 3.2.2得到电影的句柄 如果是本地文件调用OpenLocalMovie,得到句柄后保存在成员变量里m_Movie。该函数打开电影后创建Movie Controller。Apple公司推荐一般用Movie Controller来播放电影。 BOOL CQuickTime::OpenLocalMovie(LPCSTR fullPath) { _ASSERTE(fullPath && m_hViewWnd); if(!fullPath !m_hViewWnd) return FALSE; VIDEO_STATUE oldState = m_enState; if ( strlen ((char*)fullPath ) != 0) { OSErr err; short nTheFile = 0; long lControllerFlags = 0L; FSSpec sfFile; short nMovieResFile; short nMovieResId; char theFullPath[255]; // Close any previously opened movie CloseMovie(); // make a copy of our full path name strcpy ( (char *)theFullPath, (const char *) fullPath ); // convert theFullPath to pstring c2pstr( (char*)theFullPath ); // Make a FSSpec with a pascal string filename FSMakeFSSpec(0,0L,(unsigned char*)theFullPath, &sfFile); // Set the port SetGWorld((CGrafPtr)GetHWNDPort(m_hViewWnd), nil); // Open the movie file err = OpenMovieFile(&sfFile, &nMovieResFile, fsRdPerm); if (err == noErr) { // Get the Movie from the file nMovieResId = 0; err = NewMovieFromFile(&m_Movie,nMovieResFile, &nMovieResId, nil, newMovieActive, /* flags */ nil); // Close the movie file CloseMovieFile(nMovieResFile); if (err == noErr) { SetMovieTimeScale(m_Movie,1000); m_bBegineDownload = TRUE; // Create the movie controller CreateNewMovieController(m_hViewWnd,m_Movie,&m_MC); p2cstr((unsigned char*)theFullPath); if(m_MC) { return TRUE; } } } } CloseMovie(); return FALSE; } 如果是URL文件,调用OpenURLMovie,该函数跟OpenLocalMovie区别主要在于不用NewMovieFromFile而用NewMovieFromDataRef来得到句柄。 一般电影在创建完Movie Controller后最好调用PrePrerollMovie。 void CQuickTime::CreateNewMovieController(HWND hwnd, Movie theMovie, MovieController *theMC) { …… PrePrerollMovie(theMovie, GetMovieTime(theMovie, NULL), GetMoviePreferredRate(theMovie), NewMoviePrePrerollCompleteProc(QTPrePrerollCompleteProc), (void *)m_hViewWnd); } 3.2.3关闭电影 void CQuickTime::CloseMovie(void) { if (m_MC) { DisposeMovieController(m_MC); } if (m_Movie) { DisposeMovie(m_Movie); } m_Movie = NULL; m_MC = NULL; } 3.2.4建立和取消电影窗口关联 int CQuickTime::OnMovieWindowCreate(HWND hWnd, CREATESTRUCT *lpCreateStruct) { if ( hWnd != NULL) { m_hViewWnd = hWnd; // the view's hwnd // Create GrafPort <-> HWND association CreatePortAssociation(m_hViewWnd, NULL, kQTMLHandlePortEvents); } return 0; } void CQuickTime::OnMovieWindowDestroy() { if(m_Movie) AbortPrePrerollMovie(m_Movie,noErr); CGrafPtr windowPort = NULL; // close any movies before destroying PortAssocation CloseMovie(); // Destroy the view's GrafPort <-> HWND association if (m_hViewWnd) windowPort = (CGrafPtr)GetHWNDPort(m_hViewWnd); if (windowPort) DestroyPortAssociation(windowPort); } 3.2.5控制电影播放 笔者用控制MCDoAction来控制播放,这样可以得到播放的状态,当然也可以调用StartMovie、StopMovie等api函数。例如, void CQuickTime::Play() { if(m_Movie) { MCDoAction (m_MC, mcActionPlay, (void *)GetMoviePreferredRate(m_Movie)); long controllerFlags; MCGetControllerInfo(m_MC,&controllerFlags); if((controllerFlags&mcInfoIsPlaying)) //Now this is playing state. } } 4 综述 通过上面较为详细的讨论对于建立一个QuickTime的窗口,打开和控制QuickTime电影文件的播放的基本概念和基本过程,我认为QuickTime Player在Windows的平台的应用将更为宽广。同时为Apple公司在多媒体播放上的努力而致敬。
|