一、引言
在當前世面上存在著大量的不同媒體格式,並且還有許多新的媒體格式即將被建立。為了存儲和傳輸這些不同的媒體格式,存在著了許多不同格式的存儲設備和傳輸協議,例如大家常使用的媒體存儲設備(如CD、VCD以及DVD),有線傳輸協議(如UDP、HTTP),無線傳輸協議(如WAP)。
為了使移動裝置能夠訪問這些不同格式的媒體數據,必需為其設計一個規范化的、強大的和可擴充的應用接口。J2ME中就提供了一套為規范的播放和錄制音頻或視頻接口,即Mobile Media API (MMAPI)。
二、MMAPI體系結構
一般情況下,可以將媒體處理過程分解為兩個過程:
● 處理媒體數據的傳輸協議的過程。
● 處理媒體數據內容的過程。
1、處理媒體數據的傳輸協議的過程
處理傳輸協議的過程是指從數據源(如一個文件、一個捕獲裝置或一個流服務)上讀取媒體數據內容後,將其傳送到媒體數據內容處理的過程。
MMAPI使用Data Source來處理媒體數據的傳輸協議的過程。一個Data Source知道如何從它的原始位置讀取媒體數據並傳送給媒體數據處理(Player)。媒體數據可以被保存在不同的位置,從遠程服務器到資源文件或者RMS數據庫。媒體數據可以從原始位置通過HTTP,像RTP一樣的流式傳輸協議,或者其他機制傳輸到媒體數據處理(Player)。圖一展示了Data Source的工作過程。
圖一:Data Source工作過程
javax.microedition.media.protocol.DataSource提供了MMAPI的Data Source的支持。
2、處理媒體數據內容的過程
處理媒體數據內容的過程通常需要對媒體數據進行解釋和解碼,並且還需要認別該媒體的輸出設置的類型,如是音頻設置或者視頻設置。舉個例子,當DataSource從某媒體數據源上獲取一MP3媒體數據,並將其傳送給處理媒體數據內容過程,此過程首先對這個MP3媒體數據進行解析和解碼,同時檢測該MP3媒體為音頻數據,打開裝置的音頻設備,將解析和解碼後的媒體數據直接傳送到裝置的音頻設備緩沖中,由該音頻設備根據緩沖區中的數據內容產生音頻信號。
MMAPI使用Player來處理媒體數據內容。一個Player是javax.microedition.media.Player接口的一個實現實例,它從Data Source中讀取媒體數據、解析和解碼數據以及識別媒體輸出設備和傳送媒體數據到輸出設備等。Player提供了一套方法去控制媒體的重放和同步。
MMAPI還提供了一個或多個Controls來調整player的行為,可以在player從媒體轉換數據的時候從一個player實例取得並且使用Controls。我們可以通過Player中提供的一些特殊的Controls訪問一些特殊的媒體類型。Controls由javax.microedition.media.Control接口實現。
3、Manager
J2ME為了實現對DataSource和Player有效管理,使用了工廠機制,由Manager來負責創建Player和DataSource。這樣的機制在JAVA中到處可見,如JDBC中的DriverManager,這裡我就不對這種機制進行介紹了。Manager不僅可以從DataSource中創建Player,而且還可以從本地或InputStream中創建Player。圖二展示了MMAPI整體結構圖。
圖二:MMAPI整體結構圖
三、使用MMAPI
MMAPI提供的類和接口都在javax.microedition.media中,在程序中使用MMAPI時,首先應該引用這些包,否則程序無法編譯。
每個MMAPI程序都需要創建一個Player對象,前面我們已經介紹過,MMAPI使用Manager的CreatePlayer函數來創建Player對象,該函數有三個版本,其格式如下:
public static Player createPlayer(String locator)
throws IOException, MediaException
public static Player createPlayer(DataSource source)
throws IOException, MediaException
public static Player createPlayer(InputStream stream, String type)
throws IOException, MediaException
第一個版本實現通過URL字符串指定的協議和數據位置的信息創建一個Player對象,其中locator的格式如下:
Manager將對createPlayer函數中提供的URL字符串參數進行分析,創建一個Data Source對象,由該對象完成對媒體數據的傳輸工作,並從數據中獲取該媒體的數據內容類型,Manager將根據這個媒體數據類型創建相應的Player對象,如果Manager無法確定DataSource的內容類型,它將拋出一個MediaException異常。
例:創建一個控制某網站MP3音頻的Player對象。
Player pMP3 = Manager.createPlayer( "http://www.XXX.com/111.mp3");
第二版本實現通過已知的DataSource對象創建Player對象。
第三版本實現通過InputStream流創建Player對象。
我們根據應用實際的情況選擇使用那種版本來創建Player對象。有了一個Player對象以後,接下來要通過這個Player對象提供的方法去控制媒體流。下面我們列出常用的方法:
Player.start():重放媒體流。
Player.stop():停止媒體流。
Player.setMediaTime(long now):設置媒體時間。
Player.close():關閉媒體流並釋放資源。
Player.getState():獲取Player的當前狀態。
每個Player對象中都存在著一個狀態變量,用於表示該Player對象的生命周期。當Player第一次被建立時處於UNREALIZED狀態;當為該Player設置了媒體數據的位置後,它處於REALIZED狀態(如Player正在從一個服務器的HTTP連接下載並解釋數據的時或Player在Http請求已經發送到服務器,收到HTTP響應後,而且DataSource准備好接收媒體數據的時);當該Player已經讀到足夠的數據而開始解釋和運算時,處於PREFETCHED狀態;當數據在運算完畢後,該Player的狀態變成了STARTED。我們在使用Player對象的方法對媒體流進行控制時,應注意它們可能影響到Player狀態的改變,可以使用getState函數獲取當前Player的狀態。圖三展示了Player對象的狀態轉換圖。
圖三:狀態轉換圖
四、播放常見媒體的方法
1.播放單音與序列音
如果要播放一個單音一次,可使用:
Manager.playTone(note,duration,volume);
如果要播放序列音,則必須使用ToneControl。使用一個特別的定位器來創建一個 Player對象,獲得ToneControl,設置其命令序列,然後啟動播放器,如:
Player p = Manager.createPlayer(Manger.TONE_DEVICE_LOCATOR);
p.realize();
ToneControl tc = (ToneControl)(player.getControl("ToneControl"));
tc.setSequence(new byte[] {ToneControl.C4,8
ToneControl.C4+2,8});
p.start();
2.播放聲訊和MIDI
聲訊是指如WAV這樣的聲音格式,在這種格式中,數據是各種聲訊樣本的一個流,它代表著該聲訊每一秒鐘的片斷。MIDI則是一系列的命令,用作多樂器的某種"虛擬合成器"。
如要播放一段能夠通過HTTP訪問的聲音文件,請使用:
Player p = Manager.createPlayer("http://something.com/somefile.wav");
p.start();
如要播放一段已經被置於MIDlet的JAR文件中的聲音文件,需要先了解其MIME類型(如,"audio/x-wav"),然後,使用:
InputStream is = getClass().getResourceAsStream("/somefile.wav");
Player p = Manager.createPlayer(is,"audio/x-wav");
p.start();
如要播放一段置於RMS中的聲音文件,請使用:
RecordStore rs = RecordStore.open("name");
byte[] data = rs.getRecord(id);
ByteArrayInputStream is = new ByteArrayInputStream(data);
Player p = Manager.createPlayer(is,"audio/x-wav");
p.start();
3.播放視頻
播放視頻類似於音頻播放。然而,需要告訴視頻播放器在哪裡顯示視頻信號,因此,需要從視頻播放器處得到一個"視頻控件",然後在Form或Canvas中顯示視頻內容。下面展示一個從Canvas中顯示視頻的例子:
InputStream is = getClass().getResourceAsStream("/somefile.avi");
Player p = Manager.createPlayer(is,"video/avi");
p.realize();
VideoControl vc = (VideoControl)p.getControl("VideoControl);
if( vc != null )
{
vc.initDisplayMode(VideoControl.USE_DIRECT_VIDEO,cav); //cav為Canvas對象。
vc.setVisible(true);
p.start();
}