Android开发 CameraX开发
<h1><span style="color: rgba(0, 128, 128, 1)">前言</span></h1><p><span style="color: rgba(0, 0, 0, 1)"> google推出Camera后,发现Camera功能简单,难以满足需求调用Camera各种效果,所以又推出了Camera2. Camera2功能强大但是使用十分麻烦,回调与冗余代码太多,而且特别容易在释放Camera上犯错导致activty的内存泄露. 所以google推出了更简单易用,但是功能也强大的CameraX.</span></p>
<p><span style="color: rgba(0, 0, 0, 1)"> 因为CameraX的简单可以帮助我们高效率开发,所以也是有学习的必要性.(Camera2了解就行,没必要死磕浪费太多时间),CameraX有以下优势:</span></p>
<ol>
<li><span style="color: rgba(0, 0, 0, 1)">CameraX与Liftcycle结合,与Activity或者Fragment的生命周期捆绑,不要考虑摄像头的释放问题,减少了代码的复杂度.</span></li>
<li><span style="color: rgba(0, 0, 0, 1)">CameraX兼容至 Android L (API 21)</span></li>
<li><span style="color: rgba(0, 0, 0, 1)">依然支持Camera2的丰富摄像头功能</span></li>
</ol>
<h1><span style="color: rgba(0, 128, 128, 1)">添加依赖</span></h1>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> CameraX 核心库使用 camera2 实现</span>
implementation "androidx.camera:camera-camera2:1.0.0-beta03"
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 可以使用CameraView</span>
implementation "androidx.camera:camera-view:1.0.0-alpha10"
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 可以使用供应商扩展</span>
implementation "androidx.camera:camera-extensions:1.0.0-alpha10"
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">camerax的生命周期库</span>
implementation "androidx.camera:camera-lifecycle:1.0.0-beta03"</pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">获取权限</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">跟以前一样,需要动态授权一些必要权限</span></p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 128, 0, 1)"><!--</span><span style="color: rgba(0, 128, 0, 1)"> 相机相关 </span><span style="color: rgba(0, 128, 0, 1)">--></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">uses-permission </span><span style="color: rgba(255, 0, 0, 1)">android:name</span><span style="color: rgba(0, 0, 255, 1)">="android.permission.WRITE_EXTERNAL_STORAGE"</span> <span style="color: rgba(0, 0, 255, 1)">/></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">uses-permission </span><span style="color: rgba(255, 0, 0, 1)">android:name</span><span style="color: rgba(0, 0, 255, 1)">="android.permission.CAMERA"</span> <span style="color: rgba(0, 0, 255, 1)">/></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">uses-permission </span><span style="color: rgba(255, 0, 0, 1)">android:name</span><span style="color: rgba(0, 0, 255, 1)">="android.permission.WRITE_EXTERNAL_STORAGE"</span> <span style="color: rgba(0, 0, 255, 1)">/></span>
<span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">uses-permission </span><span style="color: rgba(255, 0, 0, 1)">android:name</span><span style="color: rgba(0, 0, 255, 1)">="android.permission.READ_EXTERNAL_STORAGE"</span> <span style="color: rgba(0, 0, 255, 1)">/></span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">预览摄像头画面</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">从最简单的预览摄像头图像开始,我们逐步了解使用方式,代码如下:</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">布局要求使用</span>PreviewView,作为SurfaceProvider</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)"><</span><span style="color: rgba(128, 0, 0, 1)">androidx.camera.view.PreviewView
</span><span style="color: rgba(255, 0, 0, 1)">android:id</span><span style="color: rgba(0, 0, 255, 1)">="@+id/previewView"</span><span style="color: rgba(255, 0, 0, 1)">
android:layout_width</span><span style="color: rgba(0, 0, 255, 1)">="match_parent"</span><span style="color: rgba(255, 0, 0, 1)">
android:layout_height</span><span style="color: rgba(0, 0, 255, 1)">="match_parent"</span><span style="color: rgba(0, 0, 255, 1)">/></span></pre>
</div>
<p><span style="color: rgba(0, 0, 0, 1)">代码:</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> CameraXActivity : AppCompatActivity() {
</span><span style="color: rgba(0, 0, 255, 1)">private</span> val TAG = CameraXActivity::<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">.java.simpleName
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> lateinit var mPreviewView: PreviewView<br>
override fun onCreate(savedInstanceState: Bundle</span>?<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera_x2)
mPreviewView </span>=<span style="color: rgba(0, 0, 0, 1)"> findViewById(R.id.previewView)
startCameraPreview()
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 开始相机预览
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun startCameraPreview() {
val cameraProviderFuture </span>= ProcessCameraProvider.getInstance(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">)
cameraProviderFuture.addListener(Runnable {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">用于将相机的生命周期绑定到生命周期所有者</span>
val cameraProvider: ProcessCameraProvider =<span style="color: rgba(0, 0, 0, 1)"> cameraProviderFuture.get()
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建预览</span>
val preview =<span style="color: rgba(0, 0, 0, 1)"> Preview.Builder().build()
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">选择后置摄像头</span>
val cameraSelector =<span style="color: rgba(0, 0, 0, 1)"> CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">在重新绑定之前取消绑定</span>
<span style="color: rgba(0, 0, 0, 1)"> cameraProvider.unbindAll()
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">将生命周期,选择摄像头,预览,绑定到相机</span>
val camera = cameraProvider.bindToLifecycle(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">, cameraSelector, preview)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置预览的View</span>
<span style="color: rgba(0, 0, 0, 1)"> preview.setSurfaceProvider(mPreviewView.createSurfaceProvider(camera.cameraInfo))
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (exc: Exception) {
Log.e(TAG, </span>"Use case binding failed"<span style="color: rgba(0, 0, 0, 1)">, exc)
}
}, ContextCompat.getMainExecutor(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">))
}
}</span></pre>
</div>
<p>特别简单就完成了,而且无需考虑摄像头的释放</p>
<h1><span style="color: rgba(0, 128, 128, 1)">实现拍照</span></h1>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> CameraXActivity : AppCompatActivity() {
</span><span style="color: rgba(0, 0, 255, 1)">private</span> val TAG = CameraXActivity::<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">.java.simpleName
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> lateinit var mImageCapture: ImageCapture
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> lateinit var mImageAnalysis: ImageAnalysis
</span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> lateinit var mPreviewView: PreviewView
override fun onCreate(savedInstanceState: Bundle</span>?<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera_x2)
mPreviewView </span>=<span style="color: rgba(0, 0, 0, 1)"> findViewById(R.id.previewView)
startCameraPreview()
takePhoto.setOnClickListener {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">点击后拍照</span>
<span style="color: rgba(0, 0, 0, 1)"> takePhoto()
}
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 开始相机预览
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun startCameraPreview() {
val cameraProviderFuture </span>= ProcessCameraProvider.getInstance(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">)
cameraProviderFuture.addListener(Runnable {
val cameraProvider: ProcessCameraProvider </span>=<span style="color: rgba(0, 0, 0, 1)"> cameraProviderFuture.get()
val preview </span>=<span style="color: rgba(0, 0, 0, 1)"> Preview.Builder().build()
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建图像捕捉</span>
mImageCapture =<span style="color: rgba(0, 0, 0, 1)"> ImageCapture.Builder().build()
val cameraSelector </span>=<span style="color: rgba(0, 0, 0, 1)"> CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
cameraProvider.unbindAll()
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">请注意,这里新增了一个ImageCapture</span>
val camera = cameraProvider.bindToLifecycle(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">, cameraSelector, preview, mImageCapture)
preview.setSurfaceProvider(mPreviewView.createSurfaceProvider(camera.cameraInfo))
} </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (exc: Exception) {
Log.e(TAG, </span>"Use case binding failed"<span style="color: rgba(0, 0, 0, 1)">, exc)
}
}, ContextCompat.getMainExecutor(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">))
}
</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* 拍照
</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun takePhoto() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">图像的保存路径与名称</span>
val photoFile = File(applicationContext.externalCacheDir?<span style="color: rgba(0, 0, 0, 1)">.path
, SimpleDateFormat(</span>"yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(System.currentTimeMillis()) + ".jpg"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建图像文件输出选项</span>
val outputOptions =<span style="color: rgba(0, 0, 0, 1)"> ImageCapture.OutputFileOptions.Builder(photoFile).build()
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">拍照,并且注册拍照后的结果监听</span>
mImageCapture.takePicture(outputOptions, ContextCompat.getMainExecutor(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">), object : ImageCapture.OnImageSavedCallback {
override fun onError(exc: ImageCaptureException) {
Log.e(TAG, </span>"Photo capture failed: ${exc.message}"<span style="color: rgba(0, 0, 0, 1)">, exc)
}
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri </span>=<span style="color: rgba(0, 0, 0, 1)"> Uri.fromFile(photoFile)
val msg </span>= "Photo capture succeeded: $savedUri"<span style="color: rgba(0, 0, 0, 1)">
Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show()
Log.d(TAG, msg)
}
})
}
}</span></pre>
</div>
<h2>拍照图像旋转</h2>
<p>代码的其他部分与上面的示例代码一致, 我们只需要关注ImageCapture的创建与添加setTargetRotation</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun createImageCapture():ImageCapture{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建图像捕捉</span>
mImageCapture =<span style="color: rgba(0, 0, 0, 1)"> ImageCapture.Builder()
.setTargetRotation(Surface.ROTATION_90)</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置旋转角度,并且只能有4个旋转方向属性ROTATION_0/ROTATION_90/ROTATION_180/ROTATION_270</span>
<span style="color: rgba(0, 0, 0, 1)"> .build()
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mImageCapture
}</span></pre>
</div>
<h2>设置执行IO线程</h2>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun createImageCapture():ImageCapture{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建图像捕捉</span>
mImageCapture =<span style="color: rgba(0, 0, 0, 1)"> ImageCapture.Builder()
.setIoExecutor(Executors.newSingleThreadExecutor())</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置执行IO线程</span>
<span style="color: rgba(0, 0, 0, 1)"> .build()
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mImageCapture
}</span></pre>
</div>
<h2>设置捕捉模式</h2>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun createImageCapture():ImageCapture{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建图像捕捉</span>
mImageCapture =<span style="color: rgba(0, 0, 0, 1)"> ImageCapture.Builder()
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">CAPTURE_MODE_MAXIMIZE_QUALITY 高画质
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">CAPTURE_MODE_MINIMIZE_LATENCY 低延迟</span>
<span style="color: rgba(0, 0, 0, 1)"> .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
.build()
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mImageCapture
}</span></pre>
</div>
<h2>设置闪光灯</h2>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun createImageCapture():ImageCapture{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建图像捕捉</span>
mImageCapture =<span style="color: rgba(0, 0, 0, 1)"> ImageCapture.Builder()
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">FLASH_MODE_ON 闪光灯开启
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">FLASH_MODE_OFF 闪光灯关闭
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">FLASH_MODE_AUTO 闪光灯自动</span>
<span style="color: rgba(0, 0, 0, 1)"> .setFlashMode(ImageCapture.FLASH_MODE_ON)
.build()
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mImageCapture
}</span></pre>
</div>
<h2>设置宽高比</h2>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun createImageCapture():ImageCapture{
mImageCapture </span>=<span style="color: rgba(0, 0, 0, 1)"> ImageCapture.Builder()
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">RATIO_4_3 4比3
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">RATIO_16_916比9</span>
<span style="color: rgba(0, 0, 0, 1)"> .setTargetAspectRatio(AspectRatio.RATIO_16_9)
.build()
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mImageCapture
}</span></pre>
</div>
<h2>设置分辨率</h2>
<p>下面还帖了一些注释,这注释的意思是,你可以随便设置分辨率大小,但是真正的分辨率并不一定是你设置的数值,而是在摄像头里获取的分辨率列表中去取最近似值.</p>
<p>为什么会有这种说明? 我这里给没有接触过摄像头开发的朋友说明一下:</p>
<p>手机的摄像头的分辨率并不是可以随便设置的,这需要取决于你开发的设备的摄像头驱动的分辨率列表. 在以往开发Camera1和Camera2的时候我们需要自己获取这份列表,从中选择我们需要的分辨率. 在使用CameraX的时候他们帮我们简化了这个筛选过程,你只需要设置目标分辨率,代码会自动选择近似分辨率</p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun createImageCapture(): ImageCapture {
mImageCapture </span>=<span style="color: rgba(0, 0, 0, 1)"> ImageCapture.Builder()
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
目标分辨率尝试建立图像分辨率的最小界限。实际图像分辨率将是尺寸上最接近的可用分辨率,该分辨率不小于由相机实现确定的目标分辨率。
但是,如果不存在等于或大于目标分辨率的分辨率,则将选择最接近的小于目标分辨率的可用分辨率。
与提供的 {@link Size} 具有相同纵横比的分辨率将在不同纵横比的分辨率之前优先考虑。
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
.setTargetResolution(Size(</span>1280, 720<span style="color: rgba(0, 0, 0, 1)">))
.build()
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mImageCapture
}</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">控制对焦</span></h1>
<div class="cnblogs_code">
<pre>val factory =<span style="color: rgba(0, 0, 0, 1)"> SurfaceOrientedMeteringPointFactory(width, height)
val point </span>=<span style="color: rgba(0, 0, 0, 1)"> factory.createPoint(x, y)
val action </span>=<span style="color: rgba(0, 0, 0, 1)"> FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF)
.addPoint(point2, FocusMeteringAction.FLAG_AE) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> could have many
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> auto calling cancelFocusAndMetering in 5 seconds</span>
.setAutoCancelDuration(5<span style="color: rgba(0, 0, 0, 1)">, TimeUnit.SECONDS)
.build()
val future </span>=<span style="color: rgba(0, 0, 0, 1)"> cameraControl.startFocusAndMetering(action)
future.addListener( Runnable {
val result </span>=<span style="color: rgba(0, 0, 0, 1)"> future.get()
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> process the result</span>
} , executor)</pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">实现录像</span></h1>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> takeVideo() {
VideoCapture videoCapture </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> VideoCaptureConfig.Builder()
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置宽高</span>
<span style="color: rgba(0, 0, 0, 1)"> .setTargetAspectRatio(aspectRatio(width, height))
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">设置旋转角度</span>
<span style="color: rgba(0, 0, 0, 1)"> .setTargetRotation(previewView.getDisplay().getRotation())
.build();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">录像前必须解绑</span>
<span style="color: rgba(0, 0, 0, 1)"> cameraProvider.unbindAll();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">开启相机预览</span>
<span style="color: rgba(0, 0, 0, 1)"> preview.setSurfaceProvider(previewView.createSurfaceProvider());
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">绑定生命周期,这里如果没有参数preview,则只录像,不显示画面</span>
cameraProvider.bindToLifecycle(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">, cameraSelector,preview, videoCapture);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">视频路径</span>
File file = getFile(".mp4"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">开始录像</span>
videoCapture.startRecording(file, ContextCompat.getMainExecutor(MainActivity.<span style="color: rgba(0, 0, 255, 1)">this</span>), <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> VideoCapture.OnVideoSavedCallback() {
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onVideoSaved(@NonNull File file) {
Toast.makeText(MainActivity.</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">, file.getAbsolutePath(), Toast.LENGTH_SHORT).show();
}
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> onError(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> videoCaptureError, @NonNull String message, @Nullable Throwable cause) {
Log.d(TAG, </span>"onError: " +<span style="color: rgba(0, 0, 0, 1)"> message);
}
});
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">停止录像,并且回调OnVideoSavedCallback</span>
btn4.setOnClickListener(<span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> View.OnClickListener() {
@Override
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> onClick(View v) {
videoCapture.stopRecording();
preview.clear();
}
});
</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">分析图像</span></h1>
<p> </p>
<div class="cnblogs_code">
<pre>val imageAnalysis =<span style="color: rgba(0, 0, 0, 1)"> ImageAnalysis.Builder()
.setTargetResolution(Size(</span>1280, 720<span style="color: rgba(0, 0, 0, 1)">))
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
imageAnalysis.setAnalyzer(Executors.newSingleThreadExecutor(), ImageAnalysis.Analyzer { image </span>-><span style="color: rgba(0, 0, 0, 1)">
val rotationDegrees </span>= image.imageInfo.rotationDegrees <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">旋转角度</span>
val format = image.format <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">格式</span>
val width = image.width <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">宽</span>
val height = image.height <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">高</span>
val plane = image.planes <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">PlaneProxy数据</span>
val buffer =<span style="color: rgba(0, 0, 0, 1)"> plane.buffer
Log.e(</span>"ytzn", "rotationDegrees = $rotationDegrees"<span style="color: rgba(0, 0, 0, 1)">)
Log.e(</span>"ytzn", "format = $format"<span style="color: rgba(0, 0, 0, 1)">)
Log.e(</span>"ytzn", "width = $width"<span style="color: rgba(0, 0, 0, 1)">)
Log.e(</span>"ytzn", "height = $height"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> insert your code here.</span>
<span style="color: rgba(0, 0, 0, 1)">})
cameraProvider.bindToLifecycle(</span><span style="color: rgba(0, 0, 255, 1)">this</span> as LifecycleOwner, cameraSelector, imageAnalysis, preview)</pre>
</div>
<p>CameraX 会生成 <code dir="ltr">YUV_420_888</code> 格式的图片 在ImageFormat类里可以看到此格式</p>
<h1><span style="color: rgba(0, 128, 128, 1)">拓展功能</span></h1>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun setPreviewExtender(builder: Preview.Builder, cameraSelector: CameraSelector) {
val extender </span>=<span style="color: rgba(0, 0, 0, 1)"> AutoPreviewExtender.create(builder)
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">自动模式</span>
<span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (extender.isExtensionAvailable(cameraSelector)) {
extender.enableExtension(cameraSelector)
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">散景扩展</span>
val bokehPreviewExtender =<span style="color: rgba(0, 0, 0, 1)"> BokehPreviewExtender.create(builder)
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (bokehPreviewExtender.isExtensionAvailable(cameraSelector)) {
bokehPreviewExtender.enableExtension(cameraSelector)
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">hdr扩展</span>
val hdrPreviewExtender =<span style="color: rgba(0, 0, 0, 1)"> HdrPreviewExtender.create(builder)
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (hdrPreviewExtender.isExtensionAvailable(cameraSelector)) {
hdrPreviewExtender.enableExtension(cameraSelector)
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">美颜模式</span>
val beautyPreviewExtender =<span style="color: rgba(0, 0, 0, 1)"> BeautyPreviewExtender.create(builder)
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (beautyPreviewExtender.isExtensionAvailable(cameraSelector)) {
beautyPreviewExtender.enableExtension(cameraSelector)
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">夜晚模式</span>
val nightPreviewExtender =<span style="color: rgba(0, 0, 0, 1)"> NightPreviewExtender.create(builder)
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (nightPreviewExtender.isExtensionAvailable(cameraSelector)) {
nightPreviewExtender.enableExtension(cameraSelector)
}
}</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">CameraX的一些问题</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">个人在开发过程中发现了一些问题,如下:</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">1.CameraX不支持外置摄像头</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">2.一直没找到跟Camera2一样配置摄像头拍照参数的方式</span></p>
<p> </p>
<p><span style="color: rgba(0, 128, 128, 1)">End</span></p>
</div>
<div id="MySignature" role="contentinfo">
<div style="text-align: center">
<p style="color:orange;font-size:16px;" >本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/15132909.html </p>
<div style="color:orange;font-size:16px;">本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。 </div>
</div><br><br>
来源:https://www.cnblogs.com/guanxinjing/p/15132909.html
頁:
[1]