咱們知道 Android 中相機開發是有兩套 API 可使用的,一個是 Camera,這個適用於 Android 5.0 如下,另一個是 Camera2,這個適用於 Android 5.0 以上。可是這僅僅是系統的建議,其實開發中因爲國內廠商對 Camera2 的支持程度各不相同,即使是 5.0 以上的手機,也可能對 Camera2 支持很是差的狀況,咱們可能還得降級使用 Camera 來開發。android
使用 Camera2 開發會涉及到一些系統方法的調用,咱們須要大概瞭解一下他們的做用。ios
1.相機的管理主要由如下兩個類提供:session
CameraManager:相機管理類,能夠獲取相機個數,以及打開或關閉相機等操做。ide
CameraCharacteristics:獲取相機的配置參數,好比獲取相機支持的拍攝分辨率大小、ISO範圍、曝光時間等,系統提供了大概78個配置選項。性能
2.相機的預覽和拍攝主要由下面的類管理:動畫
CameraDevice:這個至關因而打開相機後當前攝像頭的表示,相機開發後會傳入一個CameraDevice,咱們可使用此類來建立與相機的鏈接。ui
CameraCaputreSession:由CameraDevice配置好後產生的session,用於處理相機預覽或者是拍照等處理,就至關因而已經創建鏈接了,而後如今經過這個CameraCaptureSession處理與相機進行對話。this
CaptureRequest:控制本次獲取圖像的配置,好比配置圖片的ISO,對焦方式和曝光時間等。google
CaptureResult:描述拍照完成後的結果。.net
ImageReader:能夠用這個類來作簡單的捕獲圖像處理。
3.展現預覽圖像可使用 SurfaceView 或 TextureView:
SurfaceView:界面渲染能夠放在單獨線程,自身不能支持使用動畫。
TextureView:只能在擁有硬件加速層層的Window繪製,性能不如SurfaceView,而且可能丟幀,可是能夠作一些動畫效果。
二者詳細差異分析能夠參考:https://groups.google.com/a/chromium.org/forum/#!topic/graphics-dev/Z0yE-PWQXc4
Camera2 開發的整個流程就上面介紹的那樣:
先請求相機權限
使用 CameraManager找到你想要的攝像頭,前置或是後置。
經過 CameraCharacteristics 獲取相機的配置信息,方便以後調整相機的各類參數。
經過 CameraManager 打開相機,獲得當前的 CameraDevice。
經過 CameraDevice 建立本次會話,獲得本次會話的 CameraCaptureSession。
使用 CaptureRequest 設置獲取圖片的參數信息,設置到 CameraCaptureSession 中。
在 ImageReader 或 CaptureResult 處理獲得的圖片。
關閉相機(不關閉有可能會致使相機資源佔用,致使別的相機沒法正常打開)
開發以前先來了解一些相機的配置介紹:
AE:Automatic Exposure(自動曝光)。
AF:Auto Focus(自動對焦)。
ISO:International Orization for Standardization,這個是國際標準化組織,因爲照相機的感光度最終由這個組織發佈,因此稱爲感光度ISO值。
EV:Exposure value(曝光值)
F:F-number(光圈)
咱們經過本身設置這些參數能夠拍出很是有意思的照片,下面說一下 Android 爲咱們提供的與攝像頭交互的一些類,以及如何配置本身的相機參數來開發相機。
下面將使用 TextureView 結合 Camera2 API 製做一個簡單的相機預覽
1. 在TextureView可用狀態時打開相機。
//監聽view的事件
mTextureView.surfaceTextureListener = this
override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) {
//可用的時候會回調當前方法,width是此view的寬,height是高。
openCamera(width,height)
}
2.首先在 openCamera 中檢查是否有相機權限
private fun openCamera(width : Int,height : Int) {
//判斷是否有相機權限
if (ContextCompat.checkSelfPermission(activity!!, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
requestCameraPermission()
return
}
//設置要使用的攝像頭以及獲取攝像頭的配置
setUpCameraOutputs(width, height)
//打開攝像頭
waitOpen()
}
//申請權限
private fun requestCameraPermission() {
requestPermissions(arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)
}
3.配置相關屬性
private fun setUpCameraOutputs(width: Int, height: Int) {
val activity = act
val manager = activity.getSystemService(Context.CAMERA_SERVICE) as CameraManager
manager.cameraIdList.forEach { cameraId ->
val characteristics = manager.getCameraCharacteristics(cameraId)
val facing = characteristics.get(CameraCharacteristics.LENS_FACING)
//這裏咱們不使用前置攝像頭
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
return@forEach
}
//獲得相機支持的流配置(包括支持的圖片分辨率等),不支持就返回
val map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
?: return@forEach
//獲取支持的iso範圍
val isoRange = characteristics.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE)
//獲得最高和最低值
if (isoRange != null) {
val isoMin = isoRange.lower
val isoMax = isoRange.upper
}
//獲取支持的圖像曝光時間範圍,單位納秒
val timeRange = characteristics.get(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE)
//獲得最大最小值
if (timeRange != null) {
val timeMin = timeRange.lower
val timeMax = timeRange.upper
}
//爲了方便,這裏咱們獲取支持攝像頭支持的最大尺寸來存儲
//咱們直接使用了 JPEG 的圖片格式,android 支持的圖片格式能夠查看ImageFormat這個類
val largest = Collections.max(
Arrays.asList(*map.getOutputSizes(ImageFormat.JPEG)),
CompareSizesByArea())
//建立一個用於獲取攝像頭圖片的 ImageReader,最多接受 2 個
mImageReader = ImageReader.newInstance(largest.width, largest.height, ImageFormat.JPEG, 2)
//監聽接受到圖片的事件
mImageReader?.setOnImageAvailableListener(mOnImageAvailableListener, mHandler)
//檢查是否支持 flash
mFlashSupported = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE) ?: false
//獲得當前的攝像頭id
mCameraId = cameraId
}
}
4.根據 id 打開相機
private fun waitOpen() {
val activity = act
val manager = activity.getSystemService(Context.CAMERA_SERVICE) as CameraManager
if (mCameraId == null){
//沒有找到符合的攝像頭
return
}
manager.openCamera(mCameraId,mStateCallback,mHandler)
}
//在這裏獲得打開相機的各類回調
private val mStateCallback : CameraDevice.StateCallback = object : CameraDevice.StateCallback(){
override fun onOpened(camera: CameraDevice) {
//獲得當前攝像頭
mCameraDevice = camera
//建立預覽請求
createCameraPreviewSession()
}
override fun onDisconnected(camera: CameraDevice) {
}
override fun onError(camera: CameraDevice, error: Int) {
}
}
5.建立預覽請求
private fun createCameraPreviewSession(){
//獲取view的surface,這裏的寬高應該是獲取適合攝像頭當前的寬高,這裏爲了方便就直接使用屏幕寬高了
val texture = mTextureView.surfaceTexture
texture.setDefaultBufferSize(act.getWidth(),act.getHeight())
val surface = Surface(texture)
//構建預覽請求
mPreviewRequestBuilder = mCameraDevice?.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
mPreviewRequestBuilder?.addTarget(mImageReader?.surface)
mPreviewRequestBuilder?.addTarget(surface)
//建立預覽會話
mCameraDevice?.createCaptureSession(listOf(surface, mImageReader?.surface),mSessionCallBack,null)
}
//會在mSessionCallBack獲得咱們本次的session
private val mSessionCallBack : CameraCaptureSession.StateCallback = object : CameraCaptureSession.StateCallback(){
override fun onConfigureFailed(session: CameraCaptureSession) {}
override fun onConfigured(session: CameraCaptureSession) {
if (mCameraDevice == null){
return
}
//獲得本次的會話類
mCaptureSession= session
//設置爲自動對焦的會話預覽
mPreviewRequestBuilder?.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
//將imageReader添加進來便可在此類中獲得預覽的圖片
mPreviewRequestBuilder?.addTarget(mImageReader?.surface)
mPreviewRequest = mPreviewRequestBuilder?.build()
//設置爲連續請求
mCaptureSession?.setRepeatingRequest(mPreviewRequest,mCaptureCallback,mHandler)
}
}
這裏還能夠設置別的模式,好比設置能夠本身調節ISO大小,值的大小能夠根據從相機配置讀取的參數範圍設置
/**
* 設置ios感光度以及曝光時間
* ae 自動曝光 Automatic Exposure
* af 自動對焦 Auto Focus
* @param iso 靈敏度
* @param exposure 曝光時間
* @param frame 幀持續時間
*/
private fun setIsoMode(iso: Int, exposure: Long, frame: Long) {
//禁用全部自動設置
// mPreviewRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF);
//只是禁用曝光,白平衡繼續開啓,本身設置iso等值,必須禁用曝光
mPreviewRequestBuilder?.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF)
mPreviewRequestBuilder?.set(CaptureRequest.SENSOR_EXPOSURE_TIME, exposure)
mPreviewRequestBuilder?.set(CaptureRequest.SENSOR_SENSITIVITY, iso)
mPreviewRequestBuilder?.set(CaptureRequest.SENSOR_FRAME_DURATION, frame)
mPreviewRequestBuilder?.addTarget(mImageReader?.surface)
//須要預覽的話改爲這個而且添加到下面的createCaptureSession裏
mPreviewRequest = mPreviewRequestBuilder?.build()
mCaptureSession?.setRepeatingRequest(mPreviewRequest,
mCaptureCallback, mHandler)
}
6.在 ImageReader 中處理圖片結果
//這個是剛剛爲mImageReader添加的回調接口
private val mOnImageAvailableListener = (ImageReader.OnImageAvailableListener { reader ->
//獲取下一張圖片
val image = reader.acquireNextImage()
//這裏的planes大小和所選的圖片格式有關,好比YUV_444就有三個通道
val buffer = image.planes[0].buffer
val bytes = ByteArray(buffer.remaining())
val bitmap = BitmapFactory.decodeByteArray(bytes,0,bytes.size)
//使用完記得回收
image.close()
})
7.操做完以後就記得釋放相機資源
private fun closeCamera(){ if (mCaptureSession != null) { mCaptureSession?.close() mCaptureSession = null } if (mCameraDevice != null) { mCameraDevice?.close() mCameraDevice = null } if (mImageReader != null) { mImageReader?.close() mImageReader = null } } -