cameraX视频录制 拷贝直接用
Posted 六道对穿肠
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cameraX视频录制 拷贝直接用相关的知识,希望对你有一定的参考价值。
效果图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3i0EaImv-1637722081187)(https://liudao01.github.io/picture/img/视频录制.gif)]
https://liudao01.github.io/picture/img/视频录制.gif
activity代码
package com.sinochem.www.station.activity
import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.app.AlertDialog
import android.app.ProgressDialog
import android.content.pm.PackageManager
import android.graphics.Point
import android.net.Uri
import android.os.Bundle
import android.os.Environment
import android.os.SystemClock
import android.text.TextUtils
import android.util.Log
import android.util.Size
import android.view.View
import android.widget.*
import androidx.annotation.NonNull
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.google.common.util.concurrent.ListenableFuture
import com.sinochem.www.station.R
import com.sinochem.www.station.base.BaseActivity
import com.sinochem.www.station.utils.*
import com.sinochem.www.station.utils.camerax.CameraXCustomPreviewView
import com.sinochem.www.station.utils.camerax.FocusImageView
import com.sinochem.www.station.view.LoadingFragment
import java.io.File
import java.lang.ref.WeakReference
import java.nio.ByteBuffer
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
// Typealias (本文下称 "类型别名")。类型别名可以使您在不增加新类型的情况下,为现有类或函数类型提供替代名称。
typealias LumaListener = (luma: Double) -> Unit
class CameraXActivity : BaseActivity(), View.OnClickListener
private val title: RelativeLayout by lazy findViewById<RelativeLayout>(R.id.title)
private val viewFinder: CameraXCustomPreviewView by lazy
findViewById<CameraXCustomPreviewView>(
R.id.viewFinder
)
private val back: ImageView by lazy findViewById<ImageView>(R.id.back)
private val switchBtn: Button by lazy findViewById<Button>(R.id.switch_btn)
private val chronometer: Chronometer by lazy findViewById<Chronometer>(R.id.chronometer)
private val recorderStart: ImageView by lazy findViewById<ImageView>(R.id.recorder_start)
private val recorderStop: ImageView by lazy findViewById<ImageView>(R.id.recorder_stop)
private val focusView: FocusImageView by lazy findViewById<FocusImageView>(R.id.focusView)
private lateinit var cameraExecutor: ExecutorService
var cameraProvider: ProcessCameraProvider? = null//相机信息
var preview: Preview? = null//预览对象
var cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA//当前相机
var camera: Camera? = null//相机对象
private var imageCapture: ImageCapture? = null//拍照用例
var videoCapture: VideoCapture? = null//录像用例
var maxMinuie = 180 //最大时间
val minMinuie = 5 //最小时间
private var progressDialog: ProgressDialog? = null
var localPath: String? = null;
var isTimeOver: Boolean = false //是否时间结束
var alertDialogBuilder: AlertDialog.Builder? = null
override fun setLayoutId(): Int
return R.layout.activity_camerax_record;
override fun initVariables()
if (intent != null)
maxMinuie = intent.getIntExtra("maxMinuie", 180)
LogUtil.d("最大录制时长 : $maxMinuie")
// 设置视频文件输出的路径
// 设置视频文件输出的路径
localPath = (PathUtil.getInstance().videoPath
+ System.currentTimeMillis() + ".mp4")
override fun initViews(savedInstanceState: Bundle?)
override fun doBusiness()
recorderStop.setOnClickListener(this)
recorderStart.setOnClickListener(this)
back.setOnClickListener(this)
chronometer.onChronometerTickListener =
Chronometer.OnChronometerTickListener chronometer ->
val recordingTime = SystemClock.elapsedRealtime() - chronometer.base // 保存这次记录了的时间
val contentDescription = chronometer.contentDescription
val second = Math.round(recordingTime.toFloat() / 1000).toFloat()
LogUtil.d("打印回调计时器 = " + second)
if (maxMinuie <= second)
isTimeOver = true
recorderStopAction();
@SuppressLint("ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
// setContentView()
if (allPermissionsGranted())
startCamera()
else
ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS)
// camera_capture_button.setOnClickListener takePhoto()
// recorderStart.setOnClickListener
btnStartVideo.text = "Stop Video"
// takeVideo()
//
switchBtn.setOnClickListener
if (cameraSelector == CameraSelector.DEFAULT_BACK_CAMERA)
cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA
else
cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
startCamera()
/**
* 初始化手势动作
*/
private fun initCameraListener()
val zoomState = camera!!.cameraInfo.zoomState
viewFinder.setCustomTouchListener(object : CameraXCustomPreviewView.CustomTouchListener
override fun zoom(delta: Float)
//双指缩放
// zoomState.value?.let
// val currentZoomRatio = it.zoomRatio
// camera!!.cameraControl.setZoomRatio(currentZoomRatio * delta)
//
override fun click(x: Float, y: Float)
//点击对焦
if (cameraSelector == CameraSelector.DEFAULT_BACK_CAMERA)
val factory = viewFinder.createMeteringPointFactory(cameraSelector)
val point = factory.createPoint(x, y)
val action = FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF)
.setAutoCancelDuration(3, TimeUnit.SECONDS)
.build()
focusView.startFocus(Point(x.toInt(), y.toInt()))
val future: ListenableFuture<*> =
camera!!.cameraControl.startFocusAndMetering(action)
future.addListener(Runnable
try
val result = future.get() as FocusMeteringResult
if (result.isFocusSuccessful)
focusView.onFocusSuccess()
else
focusView.onFocusFailed()
catch (e: Exception)
Log.e("", "", e)
, cameraExecutor)
override fun doubleClick(x: Float, y: Float)
//双击放大缩小
// zoomState.value?.let
// val currentZoomRatio = it.zoomRatio
// if (currentZoomRatio > it.minZoomRatio)
// camera!!.cameraControl.setLinearZoom(0f)
// else
// camera!!.cameraControl.setLinearZoom(0.5f)
//
//
override fun longClick(x: Float, y: Float)
)
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
)
if (requestCode == REQUEST_CODE_PERMISSIONS)
if (allPermissionsGranted())
startCamera()
else
Toast.makeText(this, "Permissions not granted by the user.", Toast.LENGTH_SHORT)
.show()
// finish()
sendVideo()
@SuppressLint("RestrictedApi")
private fun startCamera()
// if (cameraExecutor != null)
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.d(TAG, "Average luminosity: $luma")
)
//比特率
videoCapture = VideoCapture.Builder()//录像用例配置
// .setBitRate(3 * 1024 * 1024)较为清晰,且文件大小
.setBitRate(900 * 1024)较为清晰,且文件大小为3.26M(30秒)
.setVideoFrameRate(20)//帧率 视频帧率 越高视频体积越大
// .setAudioBitRate(1024)//设置音频的码率
.setTargetResolution(Size(720, 1080))//setTargetResolution设置生成的视频的分辨率
// .setTargetResolution(Size(720,1080))//setTargetResolution设置生成的视频的分辨率
// .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.e(TAG, "Use case binding failed", exc)
initCameraListener()
, ContextCompat.getMainExecutor(this))
//拍照
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)
)
//录制视频
@SuppressLint("RestrictedApi", "ClickableViewAccessibility")
private fun takeVideo()
recorderStop.visibility = View.VISIBLE
recorderStart.visibility = View.GONE
//视频保存路径
val file = File(localPath)
//开始录像
videoCapture?.startRecording(
file,
Executors.newSingleThreadExecutor(),
object : VideoCapture.OnVideoSavedCallback
override fun onVideoSaved(@NonNull file: File)
//保存视频成功回调,会在停止录制时被调用
LogUtil.d("onVideoSaved: file.absolutePath = " + file.absolutePath)
// finish()
// recorderStopAction(false)
// showSelectDialog(isTimeOver)
runOnUiThread
LoadingFragment.dismiss()
showSelectDialog(isTimeOver)
// Toast.makeText(this@MainActivity, file.absolutePath, Toast.LENGTH_SHORT).show()
override fun onError(videoCaptureError: Int, message: String, cause: Throwable?)
//保存失败的回调,可能在开始或结束录制时被调用
Log.e("", "onError: $message")
runOnUiThread
LoadingFragment.dismiss()
ToastUtils.showCenter("视频处理失败")
finish()
)
@SuppressLint("RestrictedApi")
private fun stopRecording()
videoCapture?.stopRecording()//停止录制
preview?.clear()//清除预览
recorderStop.visibility = View.GONE
recorderStart.visibility = View.VISIBLE
chronometer.stop()
chronometer.base = SystemClock.elapsedRealtime()
private fun recorderStopAction()
LogUtil.d("视频停止看看是否重复调用")
// videoCapture.
// recorderStop.setEnabled(false)
// 停止拍摄
var weakReference: WeakReference<Activity> = WeakReference(this)
LoadingFragment.showLodingDialog(weakReference.get())
stopRecording()
// btn_switch.setVisibility(View.VISIBLE);
private fun showSelectDialog(isTime: Boolean)
var msg = "是否发送视频"
if (isTime)
msg = "录制时间已到" + maxMinuie / 60 + "分钟,是否发送视频"
if (alertDialogBuilder == null)
alertDialogBuilder = AlertDialog.Builder(this)
.setMessage(msg)
.setPositiveButton("确定") dialog, which ->
dialog.dismiss()
sendVideo()
.setNegativeButton("取消") dialog, which ->
if (localPath != null)
val file: File = File(localPath)
if (file.exists()) file.delete()
finish()
.setCancelable(false)
alertDialogBuilder?.show()
fun sendVideo()
if (TextUtils.isEmpty(localPath))
LogUtil.e( "recorder fail please try again!")
return
setResult(Activity.RESULT_OK, intent.putExtra("uri", localPath))
finish()
private fun stopOrSave()
val recordingTime = SystemClock.elapsedRealtime() - chronometer.base // 保存这次记录了的时间
val second = Math.round(recordingTime.toFloat() / 1000).toFloat()
LogUtil.d("second $second")
try
if (second < minMinuie)
ToastUtils.showCenter("视频录制时间最少5秒")
return
recorderStop.isEnabled = false
recorderStart.isEnabled = false
recorderStopAction()
catch (e: Exception)
ToastUtils.showCenter("操作异常,请返回后重试")
e.printStackTrace()
CrashReportUtil.getInstance().postException(e)
override fun onPause()
super.onPause()
if (progressDialog != null && progressDialog?.isShowing == true)
progressDialog?.dismiss()
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all
ContextCompat.checkSelfPermission(baseContext, it) == PackageManager.PERMISSION_GRANTED
@SuppressLint("RestrictedApi")
override fun onDestroy()
super.onDestroy()
alertDialogBuilder = null
cameraExecutor.shutdown()
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()
companion object
private const val TAG = "CameraXBasic"
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
)
override fun onBackPressed()
// super.onBackPressed()
stopOrSave()
override fun onClick(v: View?)
when (v?.id)
R.id.recorder_start ->
// 重置其他
chronometer.base = SystemClock.elapsedRealtime()
chronometer.start()
takeVideo()
R.id.back,
R.id.recorder_stop ->
isTimeOver = false;
stopOrSave()
项目地址.
https://gitee.com/liudao/camera-xvideo/tree/master
以上是关于cameraX视频录制 拷贝直接用的主要内容,如果未能解决你的问题,请参考以下文章
Android 使用CameraX实现预览/拍照/录制视频/图片分析/对焦/缩放/切换摄像头等操作