引入这些包
implementation "androidx.camera:camera-camera2:1.0.0-beta07"
implementation "androidx.camera:camera-view:1.0.0-alpha14"
implementation "androidx.camera:camera-extensions:1.0.0-alpha14"
implementation "androidx.camera:camera-lifecycle:1.0.0-beta07"
给权限
activity部分
package com.awesome.camerax_record_video
import android.Manifest
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.os.Environment
import android.util.Log
import android.widget.Toast
import androidx.annotation.NonNull
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import kotlinx.android.synthetic.main.activity_record_video.*
import java.io.File
import java.nio.ByteBuffer
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
/**
* Author: JzhangF
* Date: 2021/10/20
*/
typealias LumaListener = (luma: Double) -> Unit
class RecordVideoActivity : AppCompatActivity() {
companion object {
private const val TAG = "RecordVideoActivity"
private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
private const val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS = arrayOf(
Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO
)
}
private lateinit var cameraExecutor: ExecutorService
private var cameraProvider: ProcessCameraProvider? = null//相机信息
private var preview: Preview? = null//预览对象
private var cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA//后置相机
private var camera: Camera? = null//相机对象
private var imageCapture: ImageCapture? = null//拍照用例
private var videoCapture: VideoCapture? = null//录像用例
private var mRecording:Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_record_video)
if (allPermissionsGranted()) {
initCamera()
} else {
ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
}
btnCapture.setOnClickListener {
takePhoto()
}
btnVideo.setOnClickListener {
if (mRecording){
stopVideo()
}else{
startVideo()
}
mRecording = !mRecording
}
btnSwitch.setOnClickListener {
cameraSelector = if (cameraSelector == CameraSelector.DEFAULT_BACK_CAMERA) {
CameraSelector.DEFAULT_FRONT_CAMERA
} else {
CameraSelector.DEFAULT_BACK_CAMERA
}
initCamera()
}
}
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array,
grantResults: IntArray
) {
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {
initCamera()
} else {
Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT).show()
finish()
}
}
}
private fun initCamera() {
cameraExecutor = Executors.newSingleThreadExecutor()
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener(Runnable {
cameraProvider = cameraProviderFuture.get()//获取相机信息
//预览配置
preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(viewFinder.createSurfaceProvider())
}
imageCapture = ImageCapture.Builder().build()//拍照用例配置
val imageAnalyzer = ImageAnalysis.Builder()
.build()
.also {
it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
Log.i(TAG,"Average luminosity: $luma")
})
}
videoCapture = VideoCapture.Builder()//录像用例配置
// .setTargetAspectRatio(AspectRatio.RATIO_16_9) //设置高宽比
// .setTargetRotation(viewFinder.display.rotation)//设置旋转角度
// .setAudioRecordSource(AudioSource.MIC)//设置音频源麦克风
.build()
try {
cameraProvider?.unbindAll()//先解绑所有用例
camera = cameraProvider?.bindToLifecycle(
this,
cameraSelector,
preview,
imageCapture,
videoCapture
)//绑定用例
} catch (exc: Exception) {
Log.i(TAG,"Use case binding failed: $exc")
}
}, ContextCompat.getMainExecutor(this))
}
private class LuminosityAnalyzer(private val listener: LumaListener) : ImageAnalysis.Analyzer {
private fun ByteBuffer.toByteArray(): ByteArray {
rewind()
val data = ByteArray(remaining())
get(data)
return data
}
override fun analyze(image: ImageProxy) {
val buffer = image.planes[0].buffer
val data = buffer.toByteArray()
val pixels = data.map { it.toInt() and 0xFF }
val luma = pixels.average()
listener(luma)
image.close()
}
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor.shutdown()
}
private fun takePhoto() {
val imageCapture = imageCapture ?: return
val file = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).path +
"/CameraX" + SimpleDateFormat(
FILENAME_FORMAT,
Locale.CHINA
).format(System.currentTimeMillis()) + ".jpg"
)
val outputOptions = ImageCapture.OutputFileOptions.Builder(file).build()
imageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, "Photo capture failed: ${exc.message}", exc)
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri = Uri.fromFile(file)
val msg = "Photo capture succeeded: $savedUri"
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
})
}
private fun startVideo() {
btnVideo.text = "Stop Video"
//视频保存路径
val file = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).path + "/CameraX" + SimpleDateFormat(
FILENAME_FORMAT, Locale.CHINA
).format(System.currentTimeMillis()) + ".mp4"
)
//开始录像
videoCapture?.startRecording(file, Executors.newSingleThreadExecutor(), object :
VideoCapture.OnVideoSavedCallback {
override fun onVideoSaved(@NonNull file: File) {
//保存视频成功回调,会在停止录制时被调用
Log.i(TAG,"onVideoSaved: ${file.absoluteFile}")
}
override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) {
//保存失败的回调,可能在开始或结束录制时被调用
Log.i(TAG,"onError: $message")
}
})
}
private fun stopVideo() {
btnVideo.text = "Start Video"
//结束录像
videoCapture?.stopRecording()//停止录制
}
}
xml部分