前言
官方介绍 CameraX 是一个 Jetpack 库,旨在帮助您更轻松地开发相机应用。如果您要开发新应用,我们建议您从 CameraX 开始。它提供了一个一致且易于使用的 API,该 API 适用于绝大多数 Android 设备,并向后兼容 Android 5.0(API 级别 21)。
这章主要讲述 预览,拍照,录像,切换摄像头
1 环境
android studio4.1.1
cameraX1.2.0
2 工程说明
1> 布局
activity_main
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:id="@+id/btn3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="拍照录像"
android:onClick="recordimages"
/>
</LinearLayout>
activity_preview.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btnCapture"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginBottom="50dp"
android:elevation="2dp"
android:scaleType="fitCenter"
android:text="Take Photo"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/btnVideo"
android:onClick ="takePhoto"
/>
<Button
android:id="@+id/btnVideo"
android:layout_width="100dp"
android:layout_height="100dp"
android:elevation="2dp"
android:scaleType="fitCenter"
android:text="Start Video"
app:layout_constraintBottom_toBottomOf="@+id/btnCapture"
app:layout_constraintEnd_toStartOf="@id/btnCapture"
app:layout_constraintStart_toEndOf="@+id/btnSwitch"
android:onClick="videoRecord"/>
<Button
android:id="@+id/btnSwitch"
android:layout_width="100dp"
android:layout_height="100dp"
android:elevation="2dp"
android:scaleType="fitCenter"
android:text="Switch Camera"
app:layout_constraintBottom_toBottomOf="@+id/btnCapture"
app:layout_constraintEnd_toStartOf="@id/btnVideo"
app:layout_constraintStart_toStartOf="parent"
android:onClick="cameraSwitch"/>
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.testcamerax">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!--录制音频权限-->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Testcamerax">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".PreviewActivity"/>
</application>
</manifest>
app 下 build.gradle
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 33
buildToolsVersion "33.0.0"
defaultConfig {
applicationId "com.example.testcamerax"
minSdkVersion 23
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
def camerax_version = "1.2.0" //"1.1.0-beta03"
// CameraX core library
implementation "androidx.camera:camera-core:$camerax_version"
// CameraX Camera2 extensions[可选]拓展库可实现人像、HDR、夜间和美颜、滤镜但依赖于OEM
implementation "androidx.camera:camera-camera2:$camerax_version"
// CameraX Lifecycle library[可选]避免手动在生命周期释放和销毁数据
implementation "androidx.camera:camera-lifecycle:$camerax_version"
// CameraX View class[可选]最佳实践,最好用里面的PreviewView,它会自行判断用SurfaceView还是TextureView来实现
implementation "androidx.camera:camera-view:$camerax_version"
implementation "androidx.camera:camera-extensions:${camerax_version}"
// If you want to additionally use the CameraX VideoCapture library
implementation "androidx.camera:camera-video:${camerax_version}"
// CameraX core library using the camera2 implementation
// def camerax_version = "1.2.0-alpha02" //1.2.0-alpha02
// The following line is optional, as the core library is included indirectly by camera-camera2
/* implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
// If you want to additionally use the CameraX Lifecycle library
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
// If you want to additionally use the CameraX VideoCapture library
implementation "androidx.camera:camera-video:${camerax_version}"
// If you want to additionally use the CameraX View class
implementation "androidx.camera:camera-view:${camerax_version}"
// If you want to additionally add CameraX ML Kit Vision Integration
implementation "androidx.camera:camera-mlkit-vision:${camerax_version}"
// If you want to additionally use the CameraX Extensions library
implementation "androidx.camera:camera-extensions:${camerax_version}"*/
}
2 > 代码
MainActivity.java
package com.example.testcamerax;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.View;
import android.widget.Toast;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MainActivity extends AppCompatActivity {
private final String TAG = MainActivity.class.getName() ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//启动拍照,录像的页
public void recordimages(View v){
startActivity(new Intent(this, PreviewActivity.class));
// mCameraPRrovider.
}
// button2.setOnClickListener(new OnClickListener(){
//
// @Override
// public void onClick(View v) {
// // TODO Auto-generated method stub
// Toast.makeText(MainActivity.this, "haha 你点了button2",
// Toast.LENGTH_LONG).show();
// }
//
// });
}
PreviewActivity.java
package com.example.testcamerax;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.AspectRatio;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.Preview;
import androidx.camera.core.VideoCapture;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static androidx.camera.core.ImageCapture.*;
public class PreviewActivity extends AppCompatActivity {
public static String TAG = PreviewActivity.class.getName() ;
private ExecutorService cameraExecutor;
private ProcessCameraProvider mCameraPRrovider = null;
public static final int DMS_INPUT_IMG_W = 640;
public static final int DMS_INPUT_IMG_H = 480;
@RequiresApi(21)
private ImageCapture imageCapture;
@SuppressLint("RestrictedApi")
private VideoCapture videoCapture;
private CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;//后置相机
private final int REQUEST_CODE_CONTACT = 1;
private Boolean mRecording = false;
private Button bVideoRecord ;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_preview);
bVideoRecord = (Button)findViewById(R.id.btnVideo);
requestAllPermission();
}
private ImageAnalysis imageAnalysis(ExecutorService cameraExecutor) {
//
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
.setTargetResolution(new Size(DMS_INPUT_IMG_W, DMS_INPUT_IMG_H)) // 图片的建议尺寸
.setOutputImageRotationEnabled(true) // 是否旋转分析器中得到的图片
.setTargetRotation(Surface.ROTATION_0) // 允许旋转后 得到图片的旋转设置
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setImageQueueDepth(1)
.build();
// imageAnalysis.setAnalyzer(cameraExecutor, new MyAnalyzer());
imageAnalysis.setAnalyzer(cameraExecutor, imageProxy -> {
int picformat = imageProxy.getFormat();
if (imageProxy.getFormat() == ImageFormat.YUV_420_888) {
} else if (imageProxy.getFormat() == ImageFormat.FLEX_RGBA_8888 || picformat == PixelFormat.RGBA_8888) {
}
imageProxy.close(); // 最后要关闭这个
});
return imageAnalysis;
}
private Preview previewCreate() {
//
//要预览开启下面3句 + processCameraProvider.bindToLifecycle(MainActivity.this, cameraSelector,
// imageAnalysis,preview); preview 加上,不要预览就去跳
// 创建一个Preview 实例,并设置该实例的 surface 提供者(provider)。
PreviewView viewFinder = (PreviewView) findViewById(R.id.viewFinder);
Preview preview = new Preview.Builder()
.build();
preview.setSurfaceProvider(viewFinder.getSurfaceProvider());
return preview;
///
}
//绑定
private Camera carmerabindToLifecycle(ProcessCameraProvider cameraProvider, LifecycleOwner lifecycleOwner, CameraSelector cameraSelector, Preview preview, ImageAnalysis imageAnalysis, ImageCapture imageCapture) {
return cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview, imageAnalysis, imageCapture);
}
private ImageCapture imageCaptureCreate(PreviewView previewView) {
//设置宽高比
// ImageAnalysis.Builder().setTargetAspectRatio(AspectRatio.RATIO_16_9)
//设置实际的尺寸
// ImageAnalysis.Builder().setTargetResolution(Size(1920, 1080))
int screenAspectRatio = AspectRatio.RATIO_16_9; //Tools.aspectRatio(this);
int rotation = Surface.ROTATION_0;// previewView.getDisplay() == null ? Surface.ROTATION_0 : previewView.getDisplay().getRotation();
ImageCapture imageCapture = new Builder()
//优化捕获速度,可能降低图片质量
.setCaptureMode(CAPTURE_MODE_MINIMIZE_LATENCY)
.setTargetAspectRatio(screenAspectRatio)
.setTargetRotation(rotation)
.build();
return imageCapture;
}
private VideoCapture videoRecordCreate() {
int screenAspectRatio = AspectRatio.RATIO_16_9; //Tools.aspectRatio(this);
int rotation = Surface.ROTATION_0;
@SuppressLint("RestrictedApi") VideoCapture VideoCapture = new VideoCapture.Builder()
// .setTargetAspectRatio(screenAspectRatio) // 设置宽高比例
// .setVideoFrameRate(60) // 设置视频帧率
// .setBitRate(3 * 1024 * 1024) // 设置比特率
// .setTargetRotation(rotation) // 设置旋转角度
// .setAudioRecordSource(MediaRecorder.AudioSource.MIC)
.build();
return VideoCapture;
// if (mCameraMode == MODE_RECORD) { // 录像
// 构建一个视频捕捉器
// }
}
private void bindCaptureUsecase() {
// val cameraProvider = ProcessCameraProvider.getInstance(requireContext()).await()
// // 选择前置/后置摄像头
// val cameraSelector = getCameraSelector(cameraIndex)
// // 选择分辨率
// val quality = cameraCapabilities[cameraIndex].qualities[qualityIndex]
// val qualitySelector = QualitySelector.from(quality)
// // 连接preview到previewView
// val preview = Preview.Builder()
// .setTargetAspectRatio(quality.getAspectRatio(quality))
// .build()
// preview.setSurfaceProvider(captureViewBinding.previewView.surfaceProvider)
// 创建videoCapture对象
// val recorder = Recorder.Builder()
// .setQualitySelector(qualitySelector)
// .build()
// videoCapture = VideoCapture.withOutput(recorder)
// try {
// // 绑定生命周期
// cameraProvider.unbindAll()
// cameraProvider.bindToLifecycle(viewLifecycleOwner, cameraSelector, videoCapture, preview)
// } catch (e: Exception) {
// Log.e(TAG, "VideoCapture binding failed:$e")
// }
}
//
//init camera
private void initCamera() {
// 将Camera的生命周期和Activity绑定在一起(设定生命周期所有者),这样就不用手动控制相机的启动和关闭。
ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);
cameraExecutor = Executors.newSingleThreadExecutor();
cameraProviderFuture.addListener(() -> {
// 将你的相机和当前生命周期的所有者绑定所需的对象
try {
mCameraPRrovider = cameraProviderFuture.get();
//
//要预览开启下面3句 + processCameraProvider.bindToLifecycle(MainActivity.this, cameraSelector,
// imageAnalysis,preview); preview 加上,不要预览就去跳
// 创建一个Preview 实例,并设置该实例的 surface 提供者(provider)。
PreviewView viewFinder = (PreviewView) findViewById(R.id.viewFinder);
Preview preview = new Preview.Builder()
.build();
preview.setSurfaceProvider(viewFinder.getSurfaceProvider());
///
//选择摄像头
// 选择前置摄像头作为默认摄像头
// CameraSelector cameraSelector = isBackCamera?CameraSelector.DEFAULT_BACK_CAMERA:CameraSelector.DEFAULT_FRONT_CAMERA;
//选择其他ID 摄像头
// int mCameraId =1 ; //1 前置 0 后置
// CameraSelector cameraSelector = new CameraSelector.Builder().addCameraFilter(new MyCameraFilter(mCameraId)).build() ;
/
imageCapture = new ImageCapture.Builder().build();//拍照用例配置
//
// 设置预览帧分析
// ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
// .build();
ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
.setTargetResolution(new Size(DMS_INPUT_IMG_W, DMS_INPUT_IMG_H)) // 图片的建议尺寸
.setOutputImageRotationEnabled(true) // 是否旋转分析器中得到的图片
.setTargetRotation(Surface.ROTATION_0) // 允许旋转后 得到图片的旋转设置
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setImageQueueDepth(1)
.build();
imageAnalysis.setAnalyzer(cameraExecutor, imageProxy -> {
final int picformat = imageProxy.getFormat();
if (picformat == ImageFormat.YUV_420_888) {
} else if (picformat == ImageFormat.FLEX_RGBA_8888 || picformat == PixelFormat.RGBA_8888) {
}
imageProxy.close();
});
//
//录像用例配置
videoCapture = new VideoCapture.Builder()
// .setTargetAspectRatio(screenAspectRatio) // 设置宽高比例
// .setVideoFrameRate(60) // 设置视频帧率
// .setBitRate(3 * 1024 * 1024) // 设置比特率
// .setTargetRotation(rotation) // 设置旋转角度
// .setAudioRecordSource(MediaRecorder.AudioSource.MIC)
.build();
//
mCameraPRrovider.unbindAll();//先解绑所有用例
//因为camera 同时只能绑定3种use case,所以本节在拍照、摄像、预览、分析中,选择了前3种用途,代码如下:
mCameraPRrovider.bindToLifecycle(PreviewActivity.this, cameraSelector, preview, imageCapture,videoCapture); //imageAnalysis,
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
预览配置
}, ContextCompat.getMainExecutor(this));
}
private void requestAllPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.INTERNET,
Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO,
}; //Manifest.permission.RECORD_AUDIO,
for (String str : permissions) {
if (ContextCompat.checkSelfPermission(this, str) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE_CONTACT);
return;
}
}
initCamera();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_CONTACT) {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// doInitWork();
initCamera();
} else if (grantResults.length == 0) {
// 已知魅族手机会进这个结果
} else {
Toast.makeText(this, "您拒绝了相关权限,无法使用!", Toast.LENGTH_SHORT).show();
finish();
}
}
}
//拍照
public void takePhoto(View v) {
// File dir = new File(Constants.PATH);
// if (!dir.exists()) {
// dir.mkdirs();
// }
// //创建文件
// File file = new File(Constants.PATH,"testx.jpg");
// if (file.exists()) {
// file.delete();
// }
String photofilepath = getFilesPath(this);
///storage/emulated/0/Android/data/com.example.testcamerax/files
File file = new File(photofilepath + File.separator + "20230222.jpg");
if (file.exists()) {
file.delete();
}
ImageCapture.OutputFileOptions outputOptions = new ImageCapture.OutputFileOptions.Builder(file).build();//new File(photoFile)
// 设置图像捕获监听器,在拍照后触发
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(this),
new ImageCapture.OnImageSavedCallback() {
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
Toast.makeText(PreviewActivity.this, "保存成功: ", Toast.LENGTH_SHORT).show();
// rl_start.setVisibility(View.GONE);
// rl_result_picture.setVisibility(View.VISIBLE);
// ll_picture_parent.setVisibility(View.VISIBLE);
// Bitmap bitmap = Tools.bitmapClip(CameraActivity.this, photoFile, front);
// img_picture.setImageBitmap(bitmap);
}
@Override
public void onError(@NonNull ImageCaptureException exception) {
Log.e("wld_____", "Photo capture failed: ${exc.message}", exception);
}
});
}
@SuppressLint("RestrictedApi")
private void startVideo() {
String photofilepath = getFilesPath(this);
///storage/emulated/0/Android/data/com.example.testcamerax/files
File file = new File(photofilepath + File.separator + "20230222.mp4");
if (file.exists()) {
file.delete();
}
// ImageCapture.OutputFileOptions outputOptions = new ImageCapture.OutputFileOptions.Builder(file).build();
VideoCapture.OutputFileOptions build = new VideoCapture.OutputFileOptions.Builder(file).build();
// 开始录像动作
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
videoCapture.startRecording(build, Executors.newSingleThreadExecutor(), new VideoCapture.OnVideoSavedCallback() {
@Override
public void onVideoSaved(VideoCapture.OutputFileResults outputFileResults) {
//file:///storage/emulated/0/Android/data/com.example.testcamerax/files/20230222.mp4
Log.e(TAG, "onVideoSaved: " + outputFileResults.getSavedUri());
// Toast.makeText(PreviewActivity.this, "录制结束", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(int videoCaptureError, String message, Throwable cause) {
Log.e(TAG, "onVideoSaved: fail"+message );
}
});
// //开始录像
// videoCapture.startRecording(outputOptions,new Executors.newSingleThreadExecutor());
// // 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")
// }
// })
}
@SuppressLint("RestrictedApi")
private void stopVideo() {
// btnVideo.text = "Start Video"
//结束录像
videoCapture.stopRecording();//停止录制
}
//录像
public void videoRecord(View v){
if(!mRecording){ //没开始
bVideoRecord.setText("Stop Video");
startVideo();
}else{
bVideoRecord.setText("Start Video");
stopVideo();
}
mRecording = !mRecording ;
}
@SuppressLint("RestrictedApi")
public void cameraSwitch(View v){
if( cameraSelector.getLensFacing() == CameraSelector.DEFAULT_BACK_CAMERA.getLensFacing()) {//后置相机
cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA;
}else{
cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
}
cameraExecutor.shutdown();
initCamera();
}
// 函数返回路径/storage/emulated/0/Android/data/包名/files
// 用来存储一些长时间保留的数据,应用卸载会被删除
public String getFilesPath( Context context ){
String filePath ;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
//外部存储可用
filePath = context.getExternalFilesDir(null).getPath();
}else {
//外部存储不可用
filePath = context.getFilesDir().getPath() ;
}
return filePath ;
}
//函数返回路径/storage/emulated/0/Android/data/包名/cache
//用来存储一些临时缓存数据
public String getCachePath( Context context ){
String cachePath ;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
//外部存储可用
cachePath = context.getExternalCacheDir().getPath() ;
}else {
//外部存储不可用
cachePath = context.getCacheDir().getPath() ;
}
return cachePath ;
}
///
// 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()
// }
// }
/
}
3>代码说明
//选择其他ID 摄像头
// int mCameraId =1 ; //1 前置 0 后置
// CameraSelector cameraSelector = new CameraSelector.Builder().addCameraFilter(new MyCameraFilter(mCameraId)).build() ;
需要文件MyCameraFilter.java 放在 PreviewActivity.java 同目录下即可
import android.annotation.SuppressLint;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.camera.camera2.internal.Camera2CameraInfoImpl;
import androidx.camera.core.CameraFilter;
import androidx.camera.core.CameraInfo;
import androidx.camera.core.impl.CameraInfoInternal;
import androidx.camera.core.impl.Identifier;
//import androidx.core.util.Preconditions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class MyCameraFilter implements CameraFilter {
private int carmeraid = 0;
private String strcarmeraid = "";
public MyCameraFilter(int tcarmerid) {
carmeraid = tcarmerid ;
strcarmeraid = String.valueOf(tcarmerid);
}
@SuppressLint("RestrictedApi")
@NonNull
@Override
public List<CameraInfo> filter(@NonNull List<CameraInfo> cameraInfos) {
//return null;
List<CameraInfo> output = new ArrayList<>();
for(CameraInfo it : cameraInfos){
// Preconditions.checkArgument(it instanceof CameraInfoInternal,)
//instanceof, isInstance,isAssignableFrom
if(it instanceof Camera2CameraInfoImpl){
Camera2CameraInfoImpl it2 = (Camera2CameraInfoImpl)it ;
if(strcarmeraid.equalsIgnoreCase(it2.getCameraId())){
output.add(it);
break; //打开单个
}
}
}
return output;
// List<CameraInfo> output = new ArrayList<>(cameraInfos);
// for (Camcarmerx usb 摄像头 CameraInfoInternaleraFilter filter : mCameraFilterSet) {
// output = filter.filter(Collections.unmodifiableList(output));
// }
//
// output.retainAll(cameraInfos);
// return output;
//List<CameraInfo> output = new ArrayList<>();
}
}
3运行结果
4 后续如需可以上传DEMO工程