//摄像头id列表
private lateinit var idList:Array
//当前摄像头id
private lateinit var cameraId:String
//当前摄像头属性类
private lateinit var cameraCharacteristics:CameraCharacteristics
//预览Surface
private lateinit var surface:Surface
//当前摄像头
private lateinit var cameraDevice: CameraDevice
//拍照输出Surface
private lateinit var imageReader:ImageReader
//预览CaptureRequest.Builder
private lateinit var captureRequestBuilder: CaptureRequest.Builder
//拍照CaptureRequest.Builder
private lateinit var photographCaptureRequest: CaptureRequest.Builder
//显示拍照图片的协程
private var mainJob:Job? = null
//Session
private lateinit var cameraCaptureSession: CameraCaptureSession
//相机分辨率
private lateinit var size:Size
override fun init() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//初始化
CameraUtil.init(application)
//获取摄像头id列表
idList = CameraUtil.getCameraIdList()
cameraId = idList[CameraUtil.index]
//开启surfaceTextureListener监听,获取Surface
textureView.surfaceTextureListener = surfaceTextureListener
}
//拍照
button.setOnClickListener {
photograph()
}
//切换相机
switchCamera.setOnClickListener {
switchCamera()
}
}
/**
* 拍照
*/
private fun photograph(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// 设置获取的照片方向
if (CameraUtil.index == 0){
CameraUtil.photoDirection(photographCaptureRequest,90)
}else {
CameraUtil.photoDirection(photographCaptureRequest,90*3)
}
//停止连续取景
cameraCaptureSession.stopRepeating()
//捕获单张图片
cameraCaptureSession.capture(photographCaptureRequest.build(), captureCallback, CameraUtil.getBackgroundHandler())
}
}
/**
* 切换相机
*/
private fun switchCamera(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
cameraDevice.close()
imageReader.close()
//切换相机
cameraId = CameraUtil.switchCamera(this,mStateCallback)
}
}
/**
* 获取Surface的回调
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private val surfaceTextureListener = object :TextureView.SurfaceTextureListener{
//SurfaceTexture大小发生变化时调用
override fun onSurfaceTextureSizeChanged(surfaceTexture: SurfaceTexture, width: Int, height: Int) {
//获取相机属性类
cameraCharacteristics = CameraUtil.getCameraCharacteristics(cameraId)
//设置预设的预览尺寸
size = CameraUtil.setDefaultBufferSize(surfaceTexture,cameraCharacteristics)
surface = Surface(surfaceTexture)
}
override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {
}
override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture?): Boolean {
return true
}
override fun onSurfaceTextureAvailable(surfaceTexture: SurfaceTexture, width: Int, height: Int) {
//获取相机属性类
cameraCharacteristics = CameraUtil.getCameraCharacteristics(cameraId)
//设置预设的预览尺寸
size = CameraUtil.setDefaultBufferSize(surfaceTexture,cameraCharacteristics)
surface = Surface(surfaceTexture)
//开启摄像头
CameraUtil.openCamera(this@VideoActivity,cameraId,mStateCallback)
}
}
/**
* 自动修改textureView宽高以适应不同预览比例
*/
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val width = textureView.width
val height = textureView.height
val proportion1 = size.width.toFloat() / size.height
val proportion2 = height.toFloat() / width
if (proportion1 > proportion2){
val layoutParams = textureView.layoutParams
layoutParams.width = (height * proportion1 + .5).toInt()
textureView.layoutParams = layoutParams
}else if (proportion1 < proportion2){
val layoutParams = textureView.layoutParams
layoutParams.height = (width * proportion1 + .5).toInt()
textureView.layoutParams = layoutParams
}
}
}
/**
* 打开摄像头的回调
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private val mStateCallback = object : CameraDevice.StateCallback() {
//成功打开时的回调,可以得到一个 CameraDevice 实例
override fun onOpened(camera: CameraDevice) {
cameraDevice = camera
//拍照输出图像
imageReader = ImageReader.newInstance(size.width, size.height, ImageFormat.JPEG,1)
imageReader.setOnImageAvailableListener(imageAvailableListener,CameraUtil.getBackgroundHandler())
//创建一个预览的CaptureRequest
captureRequestBuilder = CameraUtil.createPreviewCaptureRequest(cameraDevice,surface)
//设置相机的自动模式
CameraUtil.automaticMode(captureRequestBuilder)
//创建一个拍照的CaptureRequest
photographCaptureRequest = CameraUtil.createPhotographCaptureRequest(cameraDevice,imageReader.surface)
//设置相机的自动模式
CameraUtil.automaticMode(photographCaptureRequest)
//设置照片分辨率
photographCaptureRequest.set(CaptureRequest.JPEG_THUMBNAIL_SIZE,size)
//创建一个Session
cameraDevice.createCaptureSession(arrayListOf(surface,imageReader.surface),mSessionCallback,CameraUtil.getBackgroundHandler())
}
//当 camera 不再可用时的回调,通常在该方法中进行资源释放的操作
override fun onDisconnected(camera: CameraDevice) {
showToast("camera不再可用")
}
// 当 camera 打开失败时的回调,error 为具体错误原因,通常在该方法中也要进行资源释放的操作
override fun onError(camera: CameraDevice, error: Int) {
CameraUtil.showError(error)
CameraUtil.releaseBackgroundThread()
}
//相机关闭时回调
override fun onClosed(camera: CameraDevice) {
super.onClosed(camera)
cameraCaptureSession.close()
}
}
/**
* imageReader的回调
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private val imageAvailableListener = ImageReader.OnImageAvailableListener {
//获取接受到的图片并展示到ImageView上
val temp = CameraUtil.getImageReaderBitmap(it)
mainJob = GlobalScope.launch (Main){
imageView.setImageBitmap(temp)
//保存到系统相册
CameraUtil.saveAlbum(temp)
}
// 重新开始预览
cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), captureCallback, CameraUtil.getBackgroundHandler())
}
/**
* 创建Session的回调
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private val mSessionCallback = object : CameraCaptureSession.StateCallback(){
//
override fun onConfigured(session: CameraCaptureSession) {
cameraCaptureSession = session
// 开始预览,即设置反复请求
cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), captureCallback, CameraUtil.getBackgroundHandler())
}
//创建失败
override fun onConfigureFailed(session: CameraCaptureSession) {
}
}
/**
* Session进度的回调
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private val captureCallback = object : CameraCaptureSession.CaptureCallback() {
override fun onCaptureCompleted(
session: CameraCaptureSession,
request: CaptureRequest,
result: TotalCaptureResult
) {
super.onCaptureCompleted(session, request, result)
}
override fun onCaptureFailed(
session: CameraCaptureSession,
request: CaptureRequest,
failure: CaptureFailure
) {
super.onCaptureFailed(session, request, failure)
}
}
override fun onDestroy() {
super.onDestroy()
cameraDevice.close()
mainJob?.cancel()
imageReader.close()
surface.release()
CameraUtil.releaseBackgroundThread()
}
import android.Manifest
import android.app.Activity
import android.app.AlertDialog
import android.app.Application
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.SurfaceTexture
import android.hardware.camera2.*
import android.media.ImageReader
import android.net.Uri
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import android.provider.MediaStore
import android.provider.Settings
import android.support.annotation.RequiresApi
import android.support.v4.content.ContextCompat
import android.widget.Toast
import org.jetbrains.annotations.NotNull
import android.util.Size
import android.view.Surface
object CameraUtil {
private lateinit var mBackgroundThread: HandlerThread
private var mBackgroundHandler: Handler? = null
//摄像头id列表
private lateinit var cameraIdList: Array
//第几个相机
var index = 0
private lateinit var application: Application
private lateinit var cameraManager: CameraManager
/**
* 获取cameraManager
*/
fun getCameraManager(): CameraManager {
return cameraManager
}
/**
* 初始化
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun init(application: Application) {
this.application = application;
cameraManager = application.getSystemService(Context.CAMERA_SERVICE) as CameraManager
cameraIdList = cameraManager.cameraIdList
}
/**
* 获取摄像头id列表
*/
fun getCameraIdList(): Array {
return cameraIdList
}
/**
* 获取CameraCharacteristics相机属性类
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun getCameraCharacteristics(cameraId: String): CameraCharacteristics {
return cameraManager.getCameraCharacteristics(cameraId)
}
/**
* 获取预览尺寸
* 参数2:预览尺寸比例,如4:3,16:9
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun getPreviewSize(@NotNull cameraCharacteristics:CameraCharacteristics,aspectRatio:Float):Size?{
val streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
val supportedSizes = streamConfigurationMap.getOutputSizes(SurfaceTexture::class.java)
for (size in supportedSizes){
if (size.width.toFloat() / size.height == aspectRatio) {
return size
}
}
return null
}
/**
* 获取预览尺寸
* 参数2:预览尺寸比例的集合,按加入顺序寻找预览尺寸并返回
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun getPreviewSize(@NotNull cameraCharacteristics:CameraCharacteristics,aspectRatios:ArrayList):Size{
for (aspectRatio in aspectRatios){
val size = getPreviewSize(cameraCharacteristics,aspectRatio)
if (size != null){
return size
}
}
return Size(1280,720)
}
/**
* 获取预设的预览尺寸
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun getPreviewSize(@NotNull cameraCharacteristics:CameraCharacteristics):Size{
val aspectRatios = ArrayList()
aspectRatios.add(16.toFloat() / 9)
aspectRatios.add(4.toFloat() / 3)
aspectRatios.add(18.toFloat() / 9)
return getPreviewSize(cameraCharacteristics,aspectRatios)
}
/**
* 设置预设的预览尺寸
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun setDefaultBufferSize(@NotNull surfaceTexture: SurfaceTexture, cameraCharacteristics:CameraCharacteristics):Size{
val size = getPreviewSize(cameraCharacteristics)
surfaceTexture.setDefaultBufferSize(size.width,size.height)
return size
}
/**
* 打开摄像头
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun openCamera(activity: Activity, cameraId: String ,callback:CameraDevice.StateCallback ) {
if (ContextCompat.checkSelfPermission(
activity,
Manifest.permission.CAMERA
) == PackageManager.PERMISSION_GRANTED
) {
cameraManager.openCamera(cameraId, callback, getBackgroundHandler())
} else {
val dialog = AlertDialog.Builder(activity)
dialog.setTitle("开启相机失败").setMessage("缺少开启相机的权限").setCancelable(false)
dialog.setNegativeButton("取消") { _, _ ->
}
dialog.setPositiveButton("授权") { _, _ ->
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data = Uri.parse("package:" + activity.packageName)
activity.startActivity(intent)
}
dialog.show()
}
}
/**
* 获取拍照产生的Bitmap
*/
@RequiresApi(Build.VERSION_CODES.KITKAT)
fun getImageReaderBitmap(it:ImageReader):Bitmap{
val image = it.acquireLatestImage()
val byteBuffer = image.planes[0].buffer
val bytes = ByteArray(byteBuffer.remaining())
byteBuffer.get(bytes)
//清空imageReader
image.close()
return BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
}
/**
* 保存到相册
*/
fun saveAlbum(temp:Bitmap){
//保存到系统相册
MediaStore.Images.Media.insertImage(
application.contentResolver,
temp,
"image_file",
"file")
}
/**
* 创建一个预览的CaptureRequest
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun createPreviewCaptureRequest(cameraDevice:CameraDevice, surface:Surface):CaptureRequest.Builder{
//创建一个预览的CaptureRequest
val captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
// 设置预览输出的 Surface
captureRequestBuilder.addTarget(surface)
return captureRequestBuilder
}
/**
* 创建一个拍照的CaptureRequest
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun createPhotographCaptureRequest(cameraDevice:CameraDevice, surface:Surface):CaptureRequest.Builder{
//创建一个拍照的CaptureRequest
val captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
// 设置拍照输出的 Surface
captureRequestBuilder.addTarget(surface)
return captureRequestBuilder
}
/**
* 设置相机的自动模式
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun automaticMode(captureRequestBuilder:CaptureRequest.Builder){
// 自动对焦
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
//自动闪光
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON)
//自动白平衡
captureRequestBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO)
}
/**
* 设置获取的照片方向
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun photoDirection(captureRequestBuilder:CaptureRequest.Builder,angle:Int){
// 根据设备方向计算设置照片的方向
captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION,angle)
}
/**
* 切换相机
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun switchCamera(activity: Activity, callback:CameraDevice.StateCallback):String{
if (index < cameraIdList.size -1 ){
index++
}else{
index = 0
}
val cameraId = cameraIdList[index]
openCamera(activity,cameraId,callback)
return cameraId
}
/**
* 获取BackgroundHandler
*/
fun getBackgroundHandler(): Handler {
if (mBackgroundHandler == null) {
//设置摄像头线程
mBackgroundThread = HandlerThread("CameraBackground")
mBackgroundThread.start()
mBackgroundHandler = Handler(mBackgroundThread.looper)
}
return mBackgroundHandler as Handler
}
/**
* 释放线程资源
*/
fun releaseBackgroundThread() {
mBackgroundHandler?.removeCallbacksAndMessages(null)
mBackgroundHandler = null
mBackgroundThread.quitSafely()
mBackgroundThread.join()
}
/**
* 开启摄像头错误提示
*/
fun showError(error: Int) {
when (error) {
CameraDevice.StateCallback.ERROR_CAMERA_IN_USE -> {
showToast("当前相机设备已经在一个更高优先级的地方打开了")
}
CameraDevice.StateCallback.ERROR_MAX_CAMERAS_IN_USE -> {
showToast("已打开相机数量到上限了,无法再打开新的相机了")
}
CameraDevice.StateCallback.ERROR_CAMERA_DISABLED -> {
showToast("由于相关设备策略该相机设备无法打开")
}
CameraDevice.StateCallback.ERROR_CAMERA_DEVICE -> {
showToast("相机设备发生了一个致命错误")
}
CameraDevice.StateCallback.ERROR_CAMERA_SERVICE -> {
showToast("相机服务发生了一个致命错误")
}
}
}
private fun showToast(msg:String){
Toast.makeText(application,msg,Toast.LENGTH_LONG).show()
}
}