wince下用directx播放音頻和視頻

//========================================================================
//TITLE:
//    wince下用directx播放音頻和視頻
//AUTHOR:
//    norains
//DATE:
//    Monday  14-May-2007
//Environment:
//        WinCE 5.0
//========================================================================

        雖然網上關於wince下如何使用directx播放多媒體文件的資料不多,但WinCE畢竟還屬於windows,而桌面系統的directx例子網上信手拈來,並且其中directx的功能方法與之WinCE下差別不大,又本人實在沒有信心比他們的輪子造得更爲華麗,所以這篇文章就直接切入正題,不介紹directx的結構功能,直接來看看怎麼用吧.(其實還是自己懶惰的原因大一些,恩,不過這個和本文的主題沒多大關係:-)).
       
        爲了方便代碼的移植,所以我將directx的操作封裝成CMedia類,只要直接調用該類,就可以相當簡便地調用directx來播放多媒體文件了.
       
        好,閒話至此,我們以具體代碼看看是如何:
        //獲取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().
       
        該函數的作用是設置一個接受消息的窗口,當directx有事件變更時,就會發送指定的消息到指定的窗口,原型如下:
       
        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;
                    
                    
//獲取此時的directx事件
                    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;
}

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章