利用ATL實現QuickTime多媒體檔案播放 (轉)

worldblog發表於2007-08-16
利用ATL實現QuickTime多媒體檔案播放 (轉)[@more@]

 :namespace prefix = o ns = "urn:schemas--com::office" />

利用ATL實現QuickTime

 

摘要  本文主要介紹了平臺上Visual C++ 6利用ATL庫和QuickTime SDK開發播放QuickTime多媒體。為從事多媒體播放開發工作者提供借鑑和參考。

關鍵詞  多媒體 播放 QuickTime ATL

 

1 前言

在當今多媒體播放軟體主要有dia 、er和QuickTime。 Apple公司的QuickTime於1991年登臺亮相,是Apple公司面向專業影片編輯、網站建立和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為基類,編寫自己的視窗類CQTVoWnd。並且定義宏來接收視窗訊息。

#define MY_QT_MSG_HANDLE(func) 

  { 

  BOOL bHandled = TRUE; 

    lResult;

    func(uMsg,wParam,lParam,bHandled); 

    if(bHandled) 

    return TRUE; 

  }

 

class CQTVideoWnd:

  public CWindowImpl

{

  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 的輔助類,後面將介紹。

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和Tenate分別為初始化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

//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();

  DeInfo("CQuickTime::Terminate this = %p,m_pQTEventSink = %p",this,m_pQTEventSink);

}

 

3.2.2得到電影的控制程式碼

如果是本地檔案呼叫OpenLocalMovie,得到控制程式碼後儲存在成員變數裡m_Movie。該函式開啟電影后建立MovController。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    File;

  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_bBegine = 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公司在多媒體播放上的努力而致敬。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-962084/,如需轉載,請註明出處,否則將追究法律責任。

相關文章