當前市面上存在大量不同的媒體格式,且新格式層出不窮。為了儲存和傳輸這些多樣化的媒體數據,衍生出許多儲存設備(如 CD、VCD、DVD)與傳輸協定(有線的 UDP、HTTP,無線的 WAP 等)。要讓行動裝置存取這些異構媒體資料,必須設計一套規範化、強大且可擴充的應用程式介面。J2ME 提供的 Mobile Media API (MMAPI) 正是為此而生,提供一套標準化的音訊/視訊播放與錄製介面。
媒體處理流程可分解為兩大核心過程:
MMAPI 使用 javax.microedition.media.protocol.DataSource 封裝傳輸細節。DataSource 負責從原始位置(遠端伺服器、資源檔、RMS 資料庫)讀取資料,透過 HTTP、RTP 等協定傳送給 Player。下圖展示其運作流程:

圖一:DataSource 工作流程
javax.microedition.media.Player 介面實作類負責從 DataSource 讀取資料、解碼、識別輸出裝置並控制播放同步。Player 透過 Control 介面(javax.microedition.media.Control)提供進階功能,如音量控制、視訊顯示模式設定等。
採用工廠模式管理 DataSource 與 Player 的建立,類似 JDBC 的 DriverManager。Manager 提供三種建立 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 字串(含協定與位置)建立;第二種使用現有 DataSource;第三種從 InputStream 建立。下圖為 MMAPI 整體架構圖:

圖二:MMAPI 整體架構
Player 擁有四個狀態,反映其生命週期:
常用控制方法:start()、stop()、setMediaTime()、close()、getState()。狀態轉換圖如下:

圖三:Player 狀態轉換圖
單次播放單音:Manager.playTone(note, duration, volume)。序列音需使用 ToneControl:
Player p = Manager.createPlayer(Manager.TONE_DEVICE_LOCATOR);
p.realize();
ToneControl tc = (ToneControl) p.getControl("ToneControl");
tc.setSequence(new byte[] {ToneControl.C4, 8, ToneControl.C4+2, 8});
p.start();
HTTP 串流播放 WAV:
Player p = Manager.createPlayer("https://example.com/sound.wav");
p.start();
JAR 內嵌資源播放(需指定 MIME 類型):
InputStream is = getClass().getResourceAsStream("/sound.wav");
Player p = Manager.createPlayer(is, "audio/x-wav");
p.start();
RMS 儲存播放:
RecordStore rs = RecordStore.openRecordStore("name", true);
byte[] data = rs.getRecord(id);
ByteArrayInputStream is = new ByteArrayInputStream(data);
Player p = Manager.createPlayer(is, "audio/x-wav");
p.start();
視訊播放需取得 VideoControl 並指定顯示區域(Canvas 或 Form):
InputStream is = getClass().getResourceAsStream("/video.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, canvas);
vc.setVisible(true);
p.start();
}