Camera
版本:Android 4.0 r1
在本文中
關鍵類
參閱
Android框架包含了對多種攝像頭和攝像特性的支持,應用程序可以進行圖片和視頻的捕獲。本文討論了一種快速、簡便的捕獲圖像和視頻的方法,並簡述了一種更高級的可爲用戶創建自定義攝像功能的方法。
在讓應用程序使用Android設備的攝像頭之前,應該考慮一些期望如何使用此硬件的問題。
- 攝像頭需求—— 攝像頭的使用對於應用程序是否確實如此重要,以至於在沒有攝像頭的設備上就不期望安裝此應用了?如果確實如此,應該 在manifest中聲明攝像頭需求。
- 快速拍照還是自定義攝像—— 應用程序如何使用攝像頭?僅僅是對快速拍照和視頻片段感興趣,還是要提供一種使用攝像頭的新方式?對於快速拍照和攝像而言,可以考慮 使用內置的攝像頭應用 。爲了開發一種定製的攝像頭功能,請查看 創建攝像頭應用 一節。
- 存儲—— 應用程序產生的圖像和視頻是否期望僅對自身可見,還是可以共享——以便相冊或其它媒體應用也能夠使用?當應用程序被卸載後,還期望圖像和視頻可用麼?請查看 保存媒體文件 一節來了解如何實現這些選項。
-
Camera
- 此類是控制攝像頭的主要API。在創建攝像頭應用程序時,此類用於拍攝照片或視頻。
SurfaceView
- 此類用於向用戶提供攝像頭實時預覽功能。
MediaRecorder
- 此類用於從攝像頭錄製視頻。
Intent
- 動作類型爲
MediaStore.ACTION_IMAGE_CAPTURE
或MediaStore.ACTION_VIDEO_CAPTURE
的意圖, 可在不直接使用Camera
對象的情況下捕獲圖像和視頻。
開始開發攝像頭API的應用之前,應該確保已經在manifest中正確聲明瞭對攝像頭的使用及其它相關的feature。
- Camera權限——應用程序必須對請求攝像頭的使用權限。
<uses-permission android:name = "android.permission.CAMERA" />
注意:如果是 通過意圖 來使用攝像頭的,應用程序就不必請求本權限。
- Camera Feature——應用程序必須同時聲明對camera feature的使用,例如:
<uses-feature android:name = "android.hardware.camera" />
關於攝像頭feature的清單,參閱manifest Feature參考。
在manifest中加入camera feature,將會使得Android Market在沒有攝像頭或不支持指定feature的設備上禁止安裝該應用程序。關於Android Market基於feature過濾的使用詳情,請參閱 Android Market和基於Feature的過濾。
如果應用程序可能用到攝像頭或攝像頭feature,但卻不是必需的,則應在manifest中指定包含android:required 屬性的feature,並將該屬性設爲false:
<uses-feature android:name = "android.hardware.camera" android:required = "false" /> -
存儲權限——如果應用程序要把圖像或視頻保存到設備的外部存儲上(SD卡),則還必須在>manifest中指定如下權限。
<uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" /> -
錄音權限——要用音頻捕獲來錄音,應用程序必須請求音頻捕獲權限。
<uses-permission android:name = "android.permission.RECORD_AUDIO" />
有一種快捷的方法可以讓應用程序不用額外編寫很多代碼就能實現拍照或攝像,這就是用意圖 Intent 來調用內置的Android攝像頭應用程序。攝像頭intent會請求通過內置攝像應用來捕獲圖像或視頻,並把控制權返回給應用程序。本節展示瞭如何用這種方法來捕獲圖像。
通常按以下步驟來提交一個攝像頭intent:
-
構建一個攝像頭Intent——用以下意圖類型之一,創建一個請求圖像或視頻的 Intent:
- MediaStore.ACTION_IMAGE_CAPTURE ——向內置攝像頭程序請求圖像拍攝的intent action類型。
- MediaStore.ACTION_VIDEO_CAPTURE ——向內置攝像頭程序請求視頻錄製的intent action類型。
- 啓動攝像頭Intent——用 startActivityForResult() 方法執行攝像頭intent。啓動完畢後攝像頭應用的用戶界面就會顯示在屏幕上,用戶就可以拍照或攝像了。
- 接收Intent結果——在應用程序中設置 onActivityResult() 方法,用於接收從攝像頭intent返回的數據。當用戶拍攝完畢後(或者取消操作),系統會調用此方法。
如果希望程序以最少的代碼實現拍照功能,利用攝像頭intent捕獲圖像是一條捷徑。圖像捕捉intent還可以包含以下附加信息:
-
MediaStore.EXTRA_OUTPUT——本設置需要一個 Uri對象,用於指定存放圖片的路徑和文件名。本設置是可選項,但強烈建議使用。如果未指定本設置值,那麼攝像應用將會把所請求的圖片以默認文件名和路徑進行保存,並將數據置入intent的 Intent.getData()部分返回。
以下例子演示瞭如何構建並執行一個圖像捕獲intent。此例中的getOutputMediaFileUri() 方法引自保存媒體文件中的例程代碼。
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE=100;
private Uri fileUri;
@Override
public void onCreate (Bundle
}
startActivityForResult() 方法執行完畢後,用戶將看到內置攝像頭應用程序的界面。用戶拍照完畢(或取消操作)後,用戶界面返回應用程序,這時必須截獲 onActivityResult() 方法來接收intent的返回結果並執行後續操作。關於如何接收完整的intent,請參閱 接收攝像頭Intent的結果。
如果希望程序以最少的代碼實現攝像功能,利用攝像頭intent捕獲視頻是一條捷徑。視頻捕捉intent可以包含以下附帶信息:
- MediaStore.EXTRA_OUTPUT——本設置需要一個 Uri ,用於指定保存視頻的路徑和文件名。本設置是可選項,但強烈建議使用。如果未指定本設置值,那麼攝像應用將會把所請求的視頻以默認文件名和路徑進行保存,並將數據置入intent的 Intent.getData() 部分返回。
- MediaStore.EXTRA_VIDEO_QUALITY——本值用0表示最低品質及最小的文件尺寸,用1表示最高品質和較大的文件尺寸。
- MediaStore.EXTRA_DURATION_LIMIT——本值用於限制所捕獲視頻的長度,以秒爲單位。
- MediaStore.EXTRA_SIZE_LIMIT——本值用於限制所捕獲視頻的文件尺寸,以字節爲單位。
以下例子演示瞭如何構建並執行一個視頻捕獲intent。本例中的getOutputMediaFileUri()方法引自 保存媒體文件中的例程代碼。
private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE =200;
private Uri fileUri;
@Override
public void onCreate(Bundle savedInstanceState){
}
startActivityForResult() 方法執行完畢後,用戶將看到一個改動過的攝像程序界面。用戶攝像完畢(或取消操作)後,用戶界面返回應用程序,這時必須截獲 onActivityResult() 方法來接收intent的返回結果並執行後續操作。關於如何接收完整的intent,請參閱下一節。
一旦已構建並運行了圖像或視頻的攝像頭intent,應用程序就必須進行設置,以接收intent返回的結果。本節展示瞭如何 截獲攝像頭intent的回調方法,以便應用程序對捕獲到的圖片及視頻進行進一步的處理。
要接收intent的返回結果,必須覆蓋啓動intent的activity中的 onActivityResult()方法。以下例子演示瞭如何覆蓋 onActivityResult()來獲取上述章節例程中的 圖像捕獲intent或 視頻捕獲intent的結果。
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
private static final int CAPTURE_VIDEO_ACTIVITY_REQUEST_CODE = 200;
@Override protected void onActivityResult(int requestCode, int resultCode,Intent data){
}
一旦activity接收到成功的結果,就說明捕獲到的圖像或視頻已保存到指定位置了,應用程序就可對其進行訪問。
有些開發人員可能需要自定義外觀的攝像頭用戶界面,或者需要提供特殊的功能。相比 使用intent 而言,創建定製的攝像activity需要編寫更多的代碼,不過也能向用戶提供更吸引人的使用感受。
通常按照以下步驟創建一個定製的攝像界面:
- 檢測並訪問攝像頭——創建代碼以檢查攝像頭存在與否並請求訪問。
- 創建預覽類——創建繼承自 SurfaceView 並實現 SurfaceHolder 接口的攝像預覽類。此類能預覽攝像的實時圖像。
- 建立預覽Preview Layout——一旦有了攝像預覽類,即可創建一個view layout,用於把預覽畫面與設計好的用戶界面控件融合在一起。
- 爲捕獲設置偵聽器Listener——將用戶界面控件連接到listener,使其能響應用戶操作開始捕獲圖像或視頻,比如按下按鈕。
- 捕獲並保存文件——建立捕獲圖片或視頻並保存到輸出文件的代碼。
- 釋放攝像頭——攝像頭使用完畢後,應用程序必須正確地將其釋放,便於其它程序的使用。
攝像頭硬件是一個共享資源,必須對其進行精心管理,因此需要使用它的應用程序之間不能發生衝突。下一節將會討論如何檢測攝像頭硬件、如何請求訪問攝像頭、使用完畢如何釋放。
如果應用程序未利用manifest聲明對攝像頭需求進行特別指明,則應該在運行時檢查一下攝像頭是否可用。可用PackageManager.hasSystemFeature() 方法來進行這種檢查,代碼示例如下:
private
}
Android設備可能擁有多個攝像頭,比如後置攝像頭用於拍照、前置攝像頭用於攝像。Android 2.3 (API Level 9)以上版本允許利用 Camera.getNumberOfCameras()方法來檢查設備可用攝像頭的數量。
如果在運行程序的設備上已經檢測到了攝像頭,則必須通過獲取一個 Camera的實例來請求對其訪問(除非使用了 用於訪問攝像頭的intent)。
可用Camera.open()方法來訪問主攝像頭,並確保捕獲全部的異常,示例代碼如下:
public
Camera
}
在運行Android
一旦獲得了攝像頭的訪問權,就可以通過 Camera.getParameters()方法來獲取更多信息,檢查返回的Camera.Parameters對象可查看攝像頭所支持的feature。如果正在使用API
爲了方便拍照或攝像,用戶必須能看到攝像頭所拍攝的畫面。攝像頭預覽類就是一種能夠顯示攝像頭實時數據的SurfaceView,用戶可以調整並捕獲圖片和視頻。
以下示例代碼演示瞭如何創建一個基本的攝像頭預覽類,它可被嵌入一個 View佈局中。爲了截獲view創建和銷燬時的回調事件,此類實現了 SurfaceHolder.Callback,這在指定攝像頭預覽的輸入時需要用到。
public
}
將預覽畫面置入layout
上節例程所述的攝像預覽類必須被放入一個activity的layout中,連同其它用戶界面控件一起,實現拍照或攝像功能。本節展示瞭如何爲預覽創建一個簡單的layout和activity。
以下layout代碼提供了一個非常簡單的view,用於顯示一個攝像預覽畫面。在此例中,FrameLayout元素用於容納攝像預覽類。利用此類layout,可以把附加的圖片信息或控件疊加到實時預覽畫面上。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
</LinearLayout>
在大多數設備上,缺省的攝像預覽方向是橫向的。此例中的layout指定了橫向(landscape)佈局,下面的代碼還把應用程序的方向也改爲了橫向。爲了簡化攝像預覽畫面的刷新,應該在manifest中增加如下內容,把應用程序的預覽activity也改爲橫向顯示。
<activityandroid:name=".CameraActivity"
</activity>
注意:攝像預覽畫面並不是一定要橫向顯示。自Android 2.2 (API Level 8) 開始,可以利用setDisplayOrientation()方法來旋轉預覽畫面。爲了讓預覽方向跟隨手機方向的變化而改變,可以在預覽類的surfaceChanged()方法中實現,先用Camera.stopPreview()停止預覽,改變方向後再用Camera.startPreview()開啓預覽。
在攝像view 的activity中,請把預覽類添加到上述的FrameLayout元素中。當攝像頭暫停使用或者關閉時,攝像activity還必須確保將其釋放。以下例子展示瞭如何修改攝像activity,加入創建預覽類所述的預覽類。
publicclassCameraActivityextendsActivity{
}
注意:上例中的getCameraInstance()方法引用了訪問攝像頭中的方法示例。
一旦創建了預覽類和顯示它的view layout,就可以開始在程序中捕獲圖片了。必須在程序代碼中爲用戶界面控件設置listener,使其可響應用戶操作進行拍照。
可以通過Camera.takePicture()方法來獲取圖片,此方法用到三個參數並從攝像頭接收數據。如果要以JPEG的格式接收數據,必須實現Camera.PictureCallback接口,以接收圖片數據並寫入文件。以下代碼展示了Camera.PictureCallback接口的簡單例子,實現了從攝像頭接收圖片並保存。
privatePictureCallback mPicture =newPictureCallback(){
};
通過調用Camera.takePicture()方法,觸發器捕獲了一張圖片。以下例程展示瞭如何在按鈕View.OnClickListener的中調用此方法。
// 在Capture按鈕中加入listener
Button captureButton =(Button) findViewById(id.button_capture);
);
注意:下文例程中的mPicture成員將會引用上述代碼。
警告:當應用程序使用完攝像頭之後,請記得調用Camera.release()釋放Camera對象!關於如何釋放攝像頭的詳情,請參閱釋放攝像頭。
Android框架的視頻捕捉需要對Camera對象進行仔細的管理,還要與MediaRecorder類一起協同工作。使用Camera錄製視頻時,必須管理好Camera.lock()與Camera.unlock()的調用,使得MediaRecorder能夠順利訪問攝像頭硬件,並且還要進行Camera.open()和Camera.release()調用。
注意:自Android 4.0 (API level 14) 開始,Camera.lock()和Camera.unlock()調用由系統自動管理。
與用攝像頭拍照不同,視頻捕獲必需十分精確地按順序進行調用。必須按照特定的順序來執行,應用程序才能成功地準備並捕獲視頻,詳細步驟如下。
- 打開攝像頭——用 Camera.open() 來獲得一個camera對象的實例。
- 連接預覽——用 Camera.setPreviewDisplay() 將camera連接到一個 SurfaceView ,準備實時預覽。
- 開始預覽——調用 開始顯示實時攝像畫面。
-
開始錄製視頻 嚴格按照以下順序執行才能成功錄製視頻:
- 解鎖——調用 Camera.unlock() 解鎖,便於 MediaRecorder 使用攝像頭。
-
配置——按照如下順序調用 MediaRecorder 中的方法。詳情請參閱 MediaRecorder 參考文檔。
- setCamera()——用當前 Camera 實例將攝像頭用途設置爲視頻捕捉。
- setAudioSource()——用 MediaRecorder.AudioSource.CAMCORDER設置音頻源。
- setVideoSource()——用 MediaRecorder.VideoSource.CAMERA設置視頻源。
-
設置視頻輸出格式和編碼格式。對於Android 2.2 (API Level 8) 以上版本,使用MediaRecorder.setProfile來獲取一個profile實例。對於Android
prior to 2.2以上版本,必須設置視頻輸出格式和編碼參數:
- setOutputFormat()——設置輸出格式,指定缺省設置或MediaRecorder.OutputFormat.MPEG_4。
- setAudioEncoder()——設置聲音編碼類型。指定缺省設置或MediaRecorder.AudioEncoder.AMR_NB。
- setVideoEncoder()—— 設置視頻編碼類型,指定缺省設置或者MediaRecorder.VideoEncoder.MPEG_4_SP。
- setOutputFile()——用getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()設置輸出文件,見保存媒體文件一節中的方法示例。
- setPreviewDisplay()——用上面連接預覽中設置的對象來指定應用程序的 SurfaceView預覽layout元素。
-
警告:必須按照如下順序調用 MediaRecorder 的下列配置方法。否則應用程序將會引發錯誤,錄像也將失敗。
- 準備MediaRecorder——調用 MediaRecorder.prepare()設置配置,準備好 MediaRecorder。
- 啓動MediaRecorder——調用 MediaRecorder.start()開始錄製視頻。
-
停止錄製視頻——按照順序調用以下方法,才能成功完成視頻錄製:
- 停止MediaRecorder ——調用 MediaRecorder.stop()停止錄製視頻。
- 重置MediaRecorder——這是可選步驟,調用 MediaRecorder.reset()刪除recorder中的配置信息。
- 釋放MediaRecorder——調用 MediaRecorder.release()釋放 MediaRecorder。
- 鎖定攝像頭—— 用 Camera.lock()鎖定攝像頭,使得以後 MediaRecordersession能夠再次使用它。自Android 4.0 (API level 14)開始,不再需要本調用了,除非 MediaRecorder.prepare()調用失敗。
- 停止預覽——activity使用完攝像頭後,應該用 Camera.stopPreview()停止預覽。
-
釋放攝像頭—— 使用 Camera.release()釋放攝像頭,使其它應用程序可以使用它。
注意:也可以不必先創建攝像頭預覽就使用 MediaRecorder,並跳過本節開始的幾步。不過,因爲用戶一般都希望在開始錄像前看到預覽畫面,這裏就不討論那類過程了。
配置MediaRecorder
在使用MediaRecorder類進行錄像時,必須先按照特定順序進行配置,然後調用MediaRecorder.prepare()方法檢查並執行這些配置。以下例程演示瞭如何爲錄像正確配置並準備MediaRecorder類。
privateboolean prepareVideoRecorder(){
}
如果是Android 2.2 (API Level 8) 之前的版本,則必須直接指定輸出格式和編碼格式,而不是使用CamcorderProfile。以下代碼演示了這種方式:
MediaRecorder中以下有關視頻錄製的參數都給出了缺省值,當然也可以在應用程序中修改這些設置:
- setVideoEncodingBitRate()
- setVideoSize()
- setVideoFrameRate()
- setAudioEncodingBitRate()
- setAudioChannels()
- setAudioSamplingRate()
開始和停止MediaRecorder
使用MediaRecorder類開始和停止視頻錄製時,必須遵循以下特定順序。
- 用 Camera.unlock()解鎖攝像頭
- 如上代碼所示配置 MediaRecorder
- 用 MediaRecorder.start()開始錄製
- 記錄視頻
- 用 MediaRecorder.stop()停止錄製
- 用 MediaRecorder.release()釋放media recorder
- 用Camera.lock()鎖定攝像頭
以下例程演示瞭如何觸發按鈕並用camera和MediaRecorder類正確地開始和停止視頻錄製。
注意:視頻錄製完畢後請不要釋放camera,否則預覽將會停止。
privateboolean isRecording =false;
// 爲Capture按鈕加入listener
Button captureButton =(Button) findViewById(id.button_capture);
captureButton.setOnClickListener(
);
注意:在上例中,prepareVideoRecorder()方法引用了配置MediaRecorder.中的示例代碼。此方法實現了鎖定camera、配置和準備MediaRecorder實例。
攝像頭是設備上所有應用程序共享使用的資源。應用程序可以在獲得Camera實例後使用攝像頭,停止使用後請務必注意釋放攝像頭對象,應用程序暫停時(Activity.onPause())也是如此。如果某應用程序未能正確地釋放攝像頭,則所有後續訪問攝像頭的嘗試(包括該應用程序自身)都將會失敗,並可能導致應用程序被強行關閉。
用Camera.release()方法可以釋放Camera對象的實例,代碼示例如下。
publicclassCameraActivityextendsActivity{
@Override
}
警告:如果某應用程序未能正確釋放攝像頭,所有後續訪問攝像頭的嘗試(包括該應用程序自身)都將會失敗,並可能會導致應用程序被強行關閉。
諸如圖片和視頻這些由用戶創建的媒體文件,應該保存到設備外部存儲的目錄中(SD卡)去,以節省系統空間,並使用戶離開設備時也能訪問這些文件。設備上有很多可用於存儲媒體文件的目錄,但作爲開發人員只應考慮兩個標準的位置:
·
·
以下例程演示瞭如何爲媒體文件創建一個File或Uri存放位置,通過Intent調用攝像頭時可以使用該文件,創建攝像應用時也可以使用它。
publicstaticfinalint MEDIA_TYPE_IMAGE =1;
publicstaticfinalint MEDIA_TYPE_VIDEO =2;
privatestaticUri getOutputMediaFileUri(int type){
}
privatestaticUri getOutputMediaFile(int type){
// 如果期望圖片在應用程序卸載後還存在、且能被其它應用程序共享,
// 則此保存位置最合適
}
注意:Environment.getExternalStoragePublic
關於在Android設備上保存文件的詳細信息,請參閱數據存儲。