而J2ME技術發展很快,文中提到的
開發工具和運行測試環境現在都有較大變動,請讀者注意。
譯文
概述
Mobile Media API(MMAPI)讓MIDlet能夠播放和錄制音頻和視頻數據.但是設備的真實能力還是依賴於其具體實現。目前,支持MMAPI的唯一設備是Nokia 3650.本文描述了如何利用MMAPI來構建一個簡單的拍照程序。
開發MMAPI應用程序所用工具
構建MMAPI應用程序的第一個難題是找到正確的
開發工具.J2ME Wireless Toolkit 2.0支持MIDP2.0,MMAPI 1.0,WMA1.1.乍一看,它包括了你所要的一切.但是Nokia 3650只能運行MIDP1.0,不能運行MIDP2.0.為了開發MIDP1.0的應用程序,你需要J2ME Wireless Toolkit的1.0.4版,外帶一個支持 MMAPI的模擬器。
Sun提供這樣一個模擬器,你可以在MMAPI的主頁上找到(
http://Java.sun.com/products/mmapi/index.JSP).盡管你可以利用該模擬器構建MMAPI應用程序,但是它不支持拍照,因此你不能用它來測試拍照的應用程序。
Nokia有一個支持拍照的模擬器,它是Nokia SerIEs 60 MIDP Concept SDK Beta 0.2的一個組成部分.你可以在Nokia的開發者論壇(
http://www.forum.nokia.com)上找到.文件的名字是nS60_jme_concept_sdk_b0_2_014.zip,Linux開發者應該是用2.0版的Nokia Developer's Suite for J2ME(
http://www.forum.nokia.com/nds_for_J2ME.Html),下面的安裝指南只適用於Windows環境。
首先,在<J2ME Wireless Toolkit>\wtklib\devices目錄下安裝Nokia SDK(把Nokia_SerIEs_60_MIDP_Concept_SDK_Beta_0_2的安裝目錄拷貝到這個目錄亦可).接下來,運行J2ME Wireless Toolkit,你就有一個額外的模擬器--Nokia_SerIEs_60_MIDP_Concept_SDK_Beta_0_2可用了。選擇該模擬器後,你就可以構建和測試你的MMAPI應用程序了。該模擬器和真實的Nokia 3650看起來非常相似。
MMAPI的測試設備
沒有什麼能替代真機測試。從長遠觀點來看,將會有很多設備支持MMAPI,但是Nokia 3650是目前唯一支持MMAPI的設備.有好幾種方式可以在Nokia 3650真機上發布MIDlet.我使用紅外接口在我的筆記本和手機上傳輸文件。你也可以使用藍牙和OTA.
獲取照相機
MIDlet中拍照(正式的說法叫視頻捕捉)的第一步是從Manager獲取一個Player。一個特殊的定位符:capture://video,指示照相機使用缺省的圖片大小來拍照。
mPlayer = Manager.createPlayer("capture://video");
如果設備不支持視頻捕捉,這將會拋出一個MediaException。你可以預先檢查一下設備是否支持拍照,如果系統屬性supports.video.capture的值為真,那麼該設備就支持照相功能。
為了獲取拍照所需資源,Player需要被realize.如果你沒有學過或者忘記了realized是什麼意思,請參考<Mobile Media API OverIEw>(
http://developers.sun.com/techtopics/mobility/apis/articles/mmapi_overvIEw/)或者J2ME Mobile Media API.
mPlayer.realize();
顯示相機視頻
來源於照相機的視頻可以作為一個Item添加到Form上,也可以作為Canvas的一部分。一個VideoControl可以讓你實現這個功能,你可以采用如下語句獲取VideoControl.
mVideoControl = (VideoControl)
mPlayer.getControl("VideoControl");
如果你希望在Canvas裡面顯示來源於相機的視頻,初始化VideoControl,然後在Canvas裡面設置視頻的大小和位置,最後使視頻可見。下面的例子(一個Canvas字類的構造函數)顯示了如何把視頻擺放在離Canvas上下邊框2個像素寬的位置。如果不能這樣放置,構造函數試著使用整個屏幕,最後,它調用setVisible()使視頻可見.
public CameraCanvas(SnapperMIDlet midlet,
VideoControl videoControl) {
int width = getWidth();
int height = getHeight();
mSnapperMIDlet = midlet;
videoControl.initDisplayMode(
VideoControl.USE_DIRECT_VIDEO, this);
try {
videoControl.setDisplayLocation(2, 2);
videoControl.setDisplaySize(width - 4, height - 4);
}
catch (MediaException me) {
try { videoControl.setDisplayFullScreen(true); }
catch (MediaException me2) {}
}
videoControl.setVisible(true);
}
在Form裡面顯示視頻稍微有所不同.我們不再用USE_DIRECT_VIDEO參數調用VideoControl的initDisplayMode(),而是代之以USE_GUI_PRIMITIVE參數。在MIDP設備上,你可以獲得一個Item顯示在Form上
Form form = new Form("Camera form");
Item item = (Item)mVideoControl.initDisplayMode(
GUIControl.USE_GUI_PRIMITIVE, null);
form.append(item);
拍照
一旦相機視頻顯示在設備上,拍照就很容易了.你所需要的只是調用一下VideoControl的getSnapshot()方法。為了調用該方法,你需要傳遞一個圖片類型參數,如果你使用null作參數,那麼相機就會適用缺省的圖片類型?PNG.通過獲取用空格隔開的系統屬性video.snapshot.encodings的值,你可以預先獲取相機支持的圖片類型。
The getSnapshot()方法返回一個字節數組,裡面是按你所指定的圖片格式的圖片數據。接下來,你就可以隨意行事了,你可以把這個字節數組存儲在數據庫裡,也可以把它發到服務器上去,或者依據這個字節數組構造一個Image對象,這樣你就可以把剛才所拍照片顯示在用戶面前,如下例所示:
byte[] raw = mVideoControl.getSnapshot(null);
Image image = Image.createImage(raw, 0, raw.length);
創建微縮圖
在這篇文章中,我想要談談如何創建微縮圖片,也就是使所拍的相片縮小化。這事看起來簡單,其實卻並不輕松。MIDP2.0中包含一些方法,這些方法可以從Image對象中獲取原始的像素數據,有可能實現圖像的伸縮變換。不幸的是,MIDP1.0並不提供對像素數據的直接訪問。
從圖像處理角度來看,我這裡采用的解決辦法既不優雅,也不嚴格正確。但是這種辦法接近我所需要的效果,並且不涉及到手工解析PNG格式。我創建了一個新的空白的圖片作為微縮圖,對於圖片的每個像素,我設置一個圍繞該像素的極小的剪切區。然後把原始圖片繪制在一個適當的位置上,這樣就設置了微縮圖的每一個像素.
private Image createThumbnail(Image image) {
int sourceWidth = image.getWidth();
int sourceHeight = image.getHeight();
int thumbWidth = 64;
int thumbHeight = -1;
if (thumbHeight == -1)
thumbHeight = thumbWidth * sourceHeight / sourceWidth;
Image thumb = Image.createImage(thumbWidth, thumbHeight);
Graphics g = thumb.getGraphics();
for (int y = 0; y < thumbHeight; y++) {
for (int x = 0; x < thumbWidth; x++) {
g.setClip(x, y, 1, 1);
int dx = x * sourceWidth / thumbWidth;
int dy = y * sourceHeight / thumbHeight;
g.drawImage(image, x - dx, y - dy,
Graphics.LEFT | Graphics.TOP);
}
}
Image immutableThumb = Image.createImage(thumb);
return immutableThumb;
}
Snapper例子
從本文下載的源代碼是一個叫做Snapper的程序。Snapper讓你可以把所拍照片的所有微縮圖都顯示在一個Form裡.本文所引用的代碼來源於Snapper.Snapper程序僅包含2個類。SnapperMIDlet提供應用邏輯和視圖流程控制,與絕大部分的MMAPI代碼相似,需要創建Player,獲取snapshot.CameraCanvas是一個Canvas的子類,它包含一個VideoControl來顯示相機視頻。
Snapper能聰明的發現目標設備是否支持視頻捕捉,如果你在Sun的MME模擬器上運行該程序,你會收到一個優雅的反饋信息:
在不支持視頻捕捉的設備上運行Snapper
如果你使用Nokia的Concept模擬器,你將看到如下效果:
Snapper的主屏
選擇Camera命令(Nokia機型把該命令放在Options菜單裡),在一台真機上,你可以看到來源於攝像頭的視頻,模擬器只能顯示單幅的靜態圖像。但是在這2種情況下,CameraCanvas都會在視頻周圍畫上一個綠框。
Snapper的攝像機屏幕
要拍照的話,你只需選擇Capture命令或者按開火鍵(位於方向鍵中心),Snapper將創建一個微縮圖片並顯示在MIDlet的主屏上。
放置了微縮圖的Snapper主屏
未來的改進
Snapper是一個非常簡單的例子,但是有很多有趣的地方你可以改進。
1.你可以把整個相片保存在數據庫裡,這樣你可以在今後浏覽。
2.你可以允許玩家刪除相片,或者改變其次序
3.你可以把整個相片發送到一個服務器上存儲起來,並且提供一個Web視圖進行浏覽。
4.使用MIDP2.0,你可以對圖片做標准的變換處理:模糊,銳化,顏色反轉,幾何變換等等。
總結
MMAPI是一個靈活而強大的API,它讓開發者能表現和捕獲音頻和視頻數據。本文描述了MMAPI的拍照功能,所需工具,以及如何編寫拍照的MIDlet. Snapper例子實現了拍照功能和微縮圖功能。你只要稍作加工,該例子就可以成為一個網絡相片共享程序的基礎.
關於作者
Jonathan Knudsen出了好幾本書,包括《Wireless Java (second edition)》,《The Unofficial Guide to LEGO MINDSTORMS Robots》,《Learning Java (second edition)》,《Java 2D Graphics》。Jonathan在Java 和 Lego robots方面廣泛寫作,他還為JavaWorld, EXE, NZZ Folio, and the O'Reilly Network寫文章. Jonathan在普林斯頓大學獲得了機械工程學位.