基于Camera2 API实现的摄像机预览功能封装,如果想要对实现进一步了解的话,可以观看Android 基于Camera2 API进行摄像机图像预览进行了解。
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.Point
import android.graphics.Rect
import android.graphics.SurfaceTexture
import android.hardware.camera2.CameraAccessException
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraDevice
import android.hardware.camera2.CameraManager
import android.hardware.camera2.CaptureRequest
import android.hardware.camera2.params.StreamConfigurationMap
import android.os.Handler
import android.os.HandlerThread
import android.util.Log
import android.util.Size
import android.view.Surface
import android.view.SurfaceHolder
import android.view.SurfaceView
import androidx.core.app.ActivityCompat
import java.util.Arrays
import kotlin.math.abs
/**
* 需要权限<uses-permission android:name="android.permission.CAMERA" />
*/
class CameraWrapper(private var context:Context) {
private val TAG = "CameraWrapper"
private var cameraManager: CameraManager
private var cameraDevice: CameraDevice? = null
private var characteristics: CameraCharacteristics? = null
private var session: CameraCaptureSession? = null
//当前设备的相机列表
private var cameraIds:Array<String>
//默认启用的cameraId
private var cameraId:String = "0"//默认前置
private var previewView:SurfaceView? = null
private var encoderSurface: Surface? = null
private var cameraThread:HandlerThread = HandlerThread("CameraThread")
private var cameraHandler: Handler
private var previewSize:Size? = Size(1280,720)
init {
cameraThread.start()
cameraHandler = Handler(cameraThread.looper)
cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
cameraIds = getCameraIds()
//看下id和前后置的对应关系
for(id in cameraIds){
Log.d(TAG,"id : "+id+"---"+getCameraOrientationString(id))
}
useCamera(cameraId)
}
//添加权限请求和检查接口,方便使用
companion object{
var PERMISSION_REQUEST_CODE = 998
fun requestCameraPermissions(activity: Activity){
ActivityCompat.requestPermissions(
activity,
arrayOf(
Manifest.permission.CAMERA,
),
PERMISSION_REQUEST_CODE
)
}
fun checkCameraPermission(context: Context):Boolean{
return ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
}
}
fun setPreviewSize(width:Int,height:Int){
previewSize = Size(width,height)
}
fun useCamera(id:String){
cameraId = id
try {
characteristics = cameraManager.getCameraCharacteristics(cameraId)
} catch (e: CameraAccessException) {
throw RuntimeException(e)
}
}
fun useBackCamera(){
cameraId = getSwitchCameraId(CameraCharacteristics.LENS_FACING_BACK)
useCamera(cameraId)
}
fun useFrontCamera(){
cameraId = getSwitchCameraId(CameraCharacteristics.LENS_FACING_FRONT)
useCamera(cameraId)
}
fun useExternalCamera(){
cameraId = getSwitchCameraId(CameraCharacteristics.LENS_FACING_EXTERNAL)
useCamera(cameraId)
}
fun setEncoderSurface(surface:Surface?){
encoderSurface = surface
}
fun startPreview(surfaceView: SurfaceView){
if(previewView!=null){
stopPreview()
}
previewView = surfaceView
previewView?.let {
it.holder.addCallback(previewCallback)
if(it.holder.surface !=null&&it.holder.surface.isValid){
openCamera(cameraId)
}
}
}
fun stopPreview(){
try {
session?.stopRepeating()
cameraDevice?.close()
session = null
cameraDevice = null
} catch (e:CameraAccessException) {
throw RuntimeException(e)
}
}
fun release(){
stopPreview()
cameraThread.quit()
}
/**
* 获取摄像头方向
* 一般情况下cameraId为0是后摄,cameraId为1是前摄。
*/
private fun getCameraOrientationString(cameraId:String):String{
var characteristics:CameraCharacteristics? = null
try {
characteristics = cameraManager.getCameraCharacteristics(cameraId);
} catch (e:CameraAccessException) {
throw RuntimeException(e)
}
var lensFacing = characteristics.get(CameraCharacteristics.LENS_FACING);
return when(lensFacing){
CameraCharacteristics.LENS_FACING_BACK -> "后摄(Back)"
CameraCharacteristics.LENS_FACING_FRONT -> "前摄(Front)"
CameraCharacteristics.LENS_FACING_EXTERNAL -> "外置(External)"
else -> "Unknown"
}
}
private fun getSwitchCameraId(switch:Int):String{
if(cameraIds==null|| cameraIds.isEmpty()) return "-1"
cameraIds.forEach {
var characteristics:CameraCharacteristics? = null
try {
characteristics = cameraManager.getCameraCharacteristics(cameraId);
} catch (e:CameraAccessException) {
throw RuntimeException(e)
}
var lensFacing = characteristics.get(CameraCharacteristics.LENS_FACING);
if(lensFacing == switch){
return it
}
}
return cameraIds[0]
}
@SuppressLint("MissingPermission")
private fun openCamera(cameraId:String) {
try {
var map:StreamConfigurationMap? = characteristics?.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
previewSize = getBestSupportedSize(map!!.getOutputSizes(SurfaceTexture::class.java).toMutableList())
cameraManager.openCamera(cameraId,object:CameraDevice.StateCallback(){
override fun onOpened(p0: CameraDevice) {
cameraDevice = p0
try {
startPreview()
} catch (e:CameraAccessException ) {
throw RuntimeException(e)
}
}
override fun onDisconnected(p0: CameraDevice) {
cameraDevice?.close()
}
override fun onError(p0: CameraDevice, p1: Int) {
Log.e(TAG, "openCamera Failed:$p1");
cameraDevice?.close();
cameraDevice = null;
}
},cameraHandler)
} catch (e:CameraAccessException) {
throw RuntimeException(e)
}
}
private fun startPreview() {
//因为摄像头设备可以同时输出多个流,所以可以传入多个surface
var targets = ArrayList<Surface>()
/*,这里可以传入多个surface*/
targets.add(previewView!!.holder.surface)
if(encoderSurface!=null){
targets.add(encoderSurface!!)
}
cameraDevice?.createCaptureSession(targets, object:CameraCaptureSession.StateCallback(){
override fun onConfigured(p0: CameraCaptureSession) {
session = p0
try {
var captureRequest =
cameraDevice?.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequest?.addTarget(previewView!!.holder.surface)
if (encoderSurface != null) {
captureRequest?.addTarget(encoderSurface!!)
}
captureRequest?.set(CaptureRequest.SCALER_CROP_REGION, Rect(0,0,previewSize!!.width,previewSize!!.height))
//这将不断地实时发送视频流,直到会话断开或调用session.stoprepeat()
session?.setRepeatingRequest(captureRequest?.build()!!, null, cameraHandler);
} catch (e:CameraAccessException) {
throw RuntimeException(e)
}
}
override fun onConfigureFailed(p0: CameraCaptureSession) {
Log.e(TAG,"session configuration failed")
}
},cameraHandler)
}
// 获取所有摄像头的CameraID
private fun getCameraIds():Array<String>{
try {
return cameraManager.cameraIdList
} catch (e:CameraAccessException) {
throw RuntimeException(e)
}
}
private fun getBestSupportedSize(sizs:List<Size>):Size {
var maxPreviewSize: Point = Point(previewSize!!.width, previewSize!!.height)
var minPreviewSize:Point = Point(1280, 720)
var defaultSize:Size = sizs[0]
var tempSizes:Array<Size> = sizs.toTypedArray()
Arrays.sort(tempSizes, object:Comparator<Size> {
override fun compare(o1:Size , o2:Size):Int {
return if (o1.width > o2.width) {
-1
} else if (o1.width == o2.width) {
if(o1.height > o2.height){
-1
}else{
1
}
} else {
1
}
}
})
var sizes:MutableList<Size> = tempSizes.toMutableList()
for(s in tempSizes){
if (maxPreviewSize != null) {
if (s.width > maxPreviewSize.x || s.height > maxPreviewSize.y) {
sizes.remove(s)
continue
}
}
if (minPreviewSize != null) {
if (s.width < minPreviewSize.x || s.height < minPreviewSize.y) {
sizes.remove(s)
}
}
}
if (sizes.size == 0) {
return defaultSize
}
var bestSize = sizes[0]
var previewViewRatio:Float = if (previewSize != null) {
previewSize!!.width.toFloat() / previewSize!!.height.toFloat()
} else {
bestSize.width.toFloat() / bestSize.height.toFloat()
}
if (previewViewRatio > 1) {
previewViewRatio = 1 / previewViewRatio;
}
for ( s in sizes) {
if (abs((s.height.toFloat() / s.width.toFloat()) - previewViewRatio) < abs(bestSize.height.toFloat() / bestSize.width.toFloat() - previewViewRatio)) {
bestSize = s
}
}
return bestSize
}
private var previewCallback:SurfaceHolder.Callback = object:SurfaceHolder.Callback{
@Override
override fun surfaceCreated(surfaceHolder:SurfaceHolder) {
// setAspectRatio()
openCamera(cameraId)
}
@Override
override fun surfaceChanged(surfaceHolder:SurfaceHolder , i:Int , i1:Int , i2:Int) {
}
@Override
override fun surfaceDestroyed(surfaceHolder:SurfaceHolder) {
stopPreview()
}
}
}