文章目录
前言
自己总结的camera2的简单使用
一、使用步骤及介绍
使用步骤
1.申请相机权限和存储读写权限
2.创建CameraManager对象,获取里面的摄像头信息
2.创建surfaceHolder并为其添加状态监听器,重写里面的surfaceCreated方法
3.在surfaceCreated方法中,创建一个UI线程HandlerThread,也可以不创建
4.调用CameraManager的openCamera()方法打开相机
openCamera()方法有三个参数:
1.相机id
2.摄像头状态监听器:CameraDevice.StateCallback
3.用于相机运行的Ui线程,没有创建UI线程的话也可以为null,为null时默认在主线程运行
5.上一步需要一个摄象头状态监听器,所以这里创建一个CameraDevice.StateCallback
在这个监听器中有一个onOpened方法,这个方法是摄像头打开成功后会调用的
代码步骤
1.创建图像捕获状态监听器:CameraCaptureSession.CaptureCallback
2.创建相机捕获会话状态监听器:CameraCaptureSession.StateCallback,在onConfigured方法里面设置摄像头参数
3.创建相机状态监听器:CameraDevice.StateCallback,在onOpened方法中创建相机捕获会话
4.创建CameraManager对象,获取设备的摄像头信息
5.创建预览组件状态监听器:android.view.SurfaceHolder.Callback,在surfaceCreated方法中创建相机线程并打开相机,线程也可以不创建
6.申请权限,权限申请成功后创建预览组件管理器并添加状态监听器:SurfaceHolder
实际步骤
先申请权限,权限到手后先创建SurfaceHolder,然后在SurfaceHolder中添加SurfaceHolder.Callback,
SurfaceHolder.Callback中需要用到CameraManager和CameraDevice.StateCallback,
CameraDevice.StateCallback中又需要用到CameraCaptureSession.StateCallback,
CameraCaptureSession.StateCallback中又需要用到CameraCaptureSession.CaptureCallback,
所以写代码的时候先创建最后一个,看起来有点绕
二、实例代码
使用SurfaceView实现预览
package com.eyetracking.camera2preview;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback {
private SurfaceView surfaceView;
private SurfaceHolder holder;
private CameraDevice cameraDevice;
private HandlerThread handlerThread;
private Handler myHandler;
private CameraManager manager;
private CameraCaptureSession previeSession;
private CaptureRequest.Builder previeBuilder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
holder = surfaceView.getHolder();
checkCameraPermission();
}
/*
SurfaceHolder.Callback ***********************************************************************************************
*/
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.d("SurfaceHolders", "SurfaceHolder创建成功!");
String cameraId = getCameras();
//创建一个UI线程
handlerThread = new HandlerThread("camera");
handlerThread.start();
myHandler = new Handler(handlerThread.getLooper());
try {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
return;
}
manager.openCamera(cameraId, new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
Log.d("DeviceCallback", "摄像头打开成功!");
cameraDevice = camera;
try {
cameraDevice.createCaptureSession(Arrays.asList(holder.getSurface()), new CameraCaptureSession.StateCallback() {
//摄像机设备完成自身配置后会调用此方法,并且会话可以开始处理捕获请求。
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
Log.d("CaptureSessionCallback", "摄像机设备已完成自身配置,并且会话可以开始处理捕获请求!");
previeSession = session;
try {
previeBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
//设置预览画面显示的surface
previeBuilder.addTarget(holder.getSurface());
// previeBuilder.set()方法参数解析:第一个参数表示设置的是什么东西,比如闪光灯,第二个参数表示把要设置的东西设置成什么样,比如关闭闪光灯,打开闪光灯
//可以设置的内容参考https://www.apiref.com/android-zh/android/hardware/camera2/CaptureRequest.html
// 设置对焦模式-自动对焦
previeBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// 设置闪光灯模式-自动打开闪光灯
previeBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
//setRepeatingRequest()方法参数说明:
// 第二个参数为CameraCaptureSession.StateCallback(),没什么作用,可以写null
// 第三个参数为预览的hndler线程,为null的时候表示在主线程预览
previeSession.setRepeatingRequest(previeBuilder.build(), null, myHandler);
} catch (CameraAccessException e) {
Log.d("CaptureSessionCallback", "cameraCaptureSession访问摄像头失败");
}
}
//如果会话无法按请求进行配置,则调用此方法。
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
Log.d("CaptureSessionCallback", "会话无法按请求进行配置!");
}
}, myHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
Log.d("DeviceCallback", "CaptureRequest.Builder访问摄像头失败");
}
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
closeCamera();
Toast.makeText(MainActivity.this, "摄像头打开错误", Toast.LENGTH_SHORT).show();
}
}, myHandler);
} catch (CameraAccessException e) {
Log.d("SurfaceHolders", "cameraManager访问摄像头失败");
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
public String getCameras() {
String[] cameras = new String[0];
String cameid = "1";
try {
manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
cameras = manager.getCameraIdList();
for (int i = 0; i < cameras.length; i++) {
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameras[i]);
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
//判断是否为后摄,LENS_FACING_BACK为后摄 LENS_FACING_FRONT为前摄 LENS_FACING_EXTERNAL为USB摄像头
if (facing == CameraCharacteristics.LENS_FACING_BACK) {
cameid = cameras[i];
Log.d("摄像头ID", cameras[i]);
}
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
return cameid;
}
//关闭摄像头
public void closeCamera() {
if (cameraDevice == null) {
return;
}
cameraDevice.close();
cameraDevice = null;
}
//关闭线程
private void destoryHandler() {
if (handlerThread == null) {
return;
}
handlerThread.quitSafely();
try {
handlerThread.join(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
closeCamera();
destoryHandler();
}
private void checkCameraPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
1);
} else {
//有权限
Log.d("MainActivity", "有权限");
holder.addCallback(this);
}
}
//权限请求回调
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults != null && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
holder.addCallback(this);
} else {
Toast.makeText(this, "拒绝权限,请去设置里面手动开启权限", Toast.LENGTH_SHORT).show();
}
break;
}
}
}
使用TextureView实现预览
这里说明以下TextureView和可以在view层对图片显示优化,镜像翻转等操作,SurfaceView不行
package com.eyetracking.camera2preview2;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;
import android.widget.Toast;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener {
private TextureView textureView;
private CameraDevice cameraDevice;
private HandlerThread handlerThread;
private Handler myHandler;
private CameraManager manager;
private CameraCaptureSession previeSession;
private CaptureRequest.Builder previeBuilder;
private Surface mSurface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textureView = (TextureView) findViewById(R.id.textureView);
checkCameraPermission();
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
Log.d("SurfaceHolders", "SurfaceHolder创建成功!");
//设置预览分辨率
surface.setDefaultBufferSize(1920, 1080);
mSurface = new Surface(surface);
String cameraId = getCameras();
//创建一个UI线程
handlerThread = new HandlerThread("camera");
handlerThread.start();
myHandler = new Handler(handlerThread.getLooper());
try {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
return;
}
manager.openCamera(cameraId, new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
Log.d("DeviceCallback", "摄像头打开成功!");
cameraDevice = camera;
try {
cameraDevice.createCaptureSession(Arrays.asList(mSurface), new CameraCaptureSession.StateCallback() {
//摄像机设备完成自身配置后会调用此方法,并且会话可以开始处理捕获请求。
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
Log.d("CaptureSessionCallback", "摄像机设备已完成自身配置,并且会话可以开始处理捕获请求!");
previeSession = session;
try {
previeBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
//设置预览画面显示的surface
previeBuilder.addTarget(mSurface);
// previeBuilder.set()方法参数解析:第一个参数表示设置的是什么东西,比如闪光灯,第二个参数表示把要设置的东西设置成什么样,比如关闭闪光灯,打开闪光灯
//可以设置的内容参考https://www.apiref.com/android-zh/android/hardware/camera2/CaptureRequest.html
// 设置对焦模式-自动对焦
previeBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// 设置闪光灯模式-自动打开闪光灯
previeBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
//setRepeatingRequest()方法参数说明:
// 第二个参数为CameraCaptureSession.StateCallback(),没什么作用,可以写null
// 第三个参数为预览的hndler线程,为null的时候表示在主线程预览
previeSession.setRepeatingRequest(previeBuilder.build(), null, myHandler);
} catch (CameraAccessException e) {
Log.d("CaptureSessionCallback", "cameraCaptureSession访问摄像头失败");
}
}
//如果会话无法按请求进行配置,则调用此方法。
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
Log.d("CaptureSessionCallback", "会话无法按请求进行配置!");
}
}, myHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
Log.d("DeviceCallback", "CaptureRequest.Builder访问摄像头失败");
}
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
closeCamera();
Toast.makeText(MainActivity.this, "摄像头打开错误", Toast.LENGTH_SHORT).show();
}
}, myHandler);
} catch (CameraAccessException e) {
Log.d("SurfaceHolders", "cameraManager访问摄像头失败");
}
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
public String getCameras() {
String[] cameras = new String[0];
String cameid = "1";
try {
manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
cameras = manager.getCameraIdList();
for (int i = 0; i < cameras.length; i++) {
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameras[i]);
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
//判断是否为后摄,LENS_FACING_BACK为后摄 LENS_FACING_FRONT为前摄 LENS_FACING_EXTERNAL为USB摄像头
if (facing == CameraCharacteristics.LENS_FACING_BACK) {
cameid = cameras[i];
Log.d("摄像头ID", cameras[i]);
}
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
return cameid;
}
//关闭摄像头
public void closeCamera() {
if (cameraDevice == null) {
return;
}
cameraDevice.close();
cameraDevice = null;
}
//关闭线程
private void destoryHandler() {
if (handlerThread == null) {
return;
}
handlerThread.quitSafely();
try {
handlerThread.join(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
closeCamera();
destoryHandler();
}
private void checkCameraPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
1);
} else {
//有权限
Log.d("MainActivity", "有权限");
textureView.setSurfaceTextureListener(this);
}
}
//权限请求回调
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults != null && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
textureView.setSurfaceTextureListener(this);
} else {
Toast.makeText(this, "拒绝权限,请去设置里面手动开启权限", Toast.LENGTH_SHORT).show();
}
break;
}
}
}
实现预览和拍照功能
package com.eyetracking.camera2picture;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.media.Image;
import android.media.ImageReader;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.ImageButton;
import android.widget.Toast;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback {
private ImageButton btPicture;
private SurfaceView surfaceView;
private SurfaceHolder holder;
private CameraDevice cameraDevice;
private HandlerThread handlerThread;
private Handler myHandler;
private CameraManager manager;
private CameraCaptureSession previeSession;
private CaptureRequest.Builder previeBuilder;
private CaptureRequest.Builder captureBuilder;
private ImageReader imageReader;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
btPicture = (ImageButton) findViewById(R.id.picture);
holder = surfaceView.getHolder();
checkCameraPermission();
}
public void btPicture(View v) {
Toast.makeText(getApplicationContext(), "点击拍照", Toast.LENGTH_SHORT).show();
openCapture();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
setImageReader();
Log.d("SurfaceHolders", "SurfaceHolder创建成功!");
String cameraId = getCameras();
//创建一个UI线程
handlerThread = new HandlerThread("camera");
handlerThread.start();
myHandler = new Handler(handlerThread.getLooper());
try {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
return;
}
manager.openCamera(cameraId, new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
Log.d("DeviceCallback", "摄像头打开成功!");
cameraDevice = camera;
try {
cameraDevice.createCaptureSession(Arrays.asList(holder.getSurface(), imageReader.getSurface()), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
Log.d("CaptureSessionCallback", "摄像机设备已完成自身配置,并且会话可以开始处理捕获请求!");
previeSession = session;
openPreview();
}
//如果会话无法按请求进行配置,则调用此方法。
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
Log.d("CaptureSessionCallback", "会话无法按请求进行配置!");
}
}, myHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
Log.d("DeviceCallback", "CaptureRequest.Builder访问摄像头失败");
}
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
previeSession.close();
closeCamera();
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
previeSession.close();
closeCamera();
Toast.makeText(MainActivity.this, "摄像头打开错误", Toast.LENGTH_SHORT).show();
}
}, myHandler);
} catch (CameraAccessException e) {
Log.d("SurfaceHolders", "cameraManager访问摄像头失败");
}
}
public void openCapture() {
if (previeSession != null) {
try {
//设置拍照参数
captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(imageReader.getSurface());
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
//停止预览状态
previeSession.stopRepeating();
previeSession.abortCaptures();
//执行拍照
previeSession.capture(captureBuilder.build(), new CameraCaptureSession.CaptureCallback() {
//当图像捕捉已完全完成并且所有结果元数据可用时调用此方法。
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
Log.d("CaptureCallbacks", "拍摄完成,重新打开预览");
openPreview();
}
}, myHandler);
} catch (CameraAccessException e) {
Log.d("CaptureSessionCallback", "cameraCaptureSession访问摄像头失败");
}
}
}
public void openPreview() {
try {
previeBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previeBuilder.addTarget(holder.getSurface());
previeBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
previeBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
previeSession.setRepeatingRequest(previeBuilder.build(), null, myHandler);
} catch (CameraAccessException e) {
Log.d("CaptureSessionCallback", "cameraCaptureSession访问摄像头失败");
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
closeCamera();
destoryHandler();
}
private void setImageReader() {
imageReader = ImageReader.newInstance(1920, 1080, ImageFormat.JPEG, 1);
imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Log.d("pictureListener", "取出图片数据");
Image image = reader.acquireNextImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);//由缓冲区存入字节数组
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
try {
String filePath = null;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
if (Build.VERSION.SDK_INT < 29) {
filePath = Environment.getExternalStorageDirectory().getCanonicalPath() + "/" + System.currentTimeMillis() + ".jpg";
} else {
filePath = getExternalFilesDir(null).getAbsolutePath() + "/" + System.currentTimeMillis() + ".jpg";
}
}
//将图片数据写入图片文件
FileOutputStream outStream = new FileOutputStream(filePath);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
outStream.flush();
outStream.close();
image.close();
Log.d("pictureListener", "图片保存成功");
} catch (IOException e) {
e.printStackTrace();
Log.d("pictureListener", "图片保存失败");
}
}
}, myHandler);
}
public String getCameras() {
String[] cameras = new String[0];
String cameid = "1";
try {
manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
cameras = manager.getCameraIdList();
for (int i = 0; i < cameras.length; i++) {
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameras[i]);
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
//判断是否为后摄,LENS_FACING_BACK为后摄 LENS_FACING_FRONT为前摄 LENS_FACING_EXTERNAL为USB摄像头
if (facing == CameraCharacteristics.LENS_FACING_BACK) {
cameid = cameras[i];
Log.d("摄像头ID", cameras[i]);
}
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
return cameid;
}
//关闭摄像头
public void closeCamera() {
if (previeSession != null) {
previeSession.close();
previeSession = null;
}
if (cameraDevice != null) {
cameraDevice.close();
cameraDevice = null;
}
}
//关闭线程
private void destoryHandler() {
if (handlerThread != null) {
handlerThread.quitSafely();
try {
handlerThread.join(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
closeCamera();
destoryHandler();
}
private void checkCameraPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
1);
} else {
//有权限
Log.d("MainActivity", "有权限");
holder.addCallback(this);
}
}
//权限请求回调
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults != null && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
holder.addCallback(this);
} else {
Toast.makeText(this, "拒绝权限,请去设置里面手动开启权限", Toast.LENGTH_SHORT).show();
}
break;
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<RelativeLayout
android:id="@+id/ls"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:background="#070707">
<ImageButton
android:id="@+id/picture"
android:layout_width="96px"
android:layout_height="96px"
android:layout_centerInParent="true"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:onClick="btPicture"
tools:src="@android:drawable/ic_menu_camera" />
</RelativeLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toLeftOf="@+id/ls">
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</RelativeLayout>
使用ImageReader实现不显示预览画面,仅获取预览数据(AI人脸识别可用)
package com.eyetracking.camera2preview3;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.media.Image;
import android.media.ImageReader;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.widget.Toast;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity {
private CameraDevice cameraDevice;
private HandlerThread handlerThread;
private Handler myHandler;
private CameraManager manager;
private CameraCaptureSession previeSession;
private CaptureRequest.Builder previeBuilder;
private ImageReader imageReader;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkCameraPermission();
}
private void setImageReader() {
imageReader = ImageReader.newInstance(1920, 1080, ImageFormat.JPEG, 2);
imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Log.d("pictureListener", "取出图片数据");
Image image = reader.acquireNextImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);//由缓冲区存入字节数组
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
try {
String filePath = null;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
if (Build.VERSION.SDK_INT < 29) {
filePath = Environment.getExternalStorageDirectory().getCanonicalPath() + "/" + System.currentTimeMillis() + ".jpg";
} else {
filePath = getExternalFilesDir(null).getAbsolutePath() + "/" + System.currentTimeMillis() + ".jpg";
}
}
//将图片数据写入图片文件
FileOutputStream outStream = new FileOutputStream(filePath);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
outStream.flush();
outStream.close();
image.close();
Log.d("pictureListener", "图片保存成功");
} catch (IOException e) {
e.printStackTrace();
Log.d("pictureListener", "图片保存失败");
}
}
}, myHandler);
previewData();
}
public void previewData() {
Log.d("SurfaceHolders", "SurfaceHolder创建成功!");
String cameraId = getCameras();
//创建一个UI线程
handlerThread = new HandlerThread("camera");
handlerThread.start();
myHandler = new Handler(handlerThread.getLooper());
try {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
return;
}
manager.openCamera(cameraId, new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
Log.d("DeviceCallback", "摄像头打开成功!");
cameraDevice = camera;
try {
cameraDevice.createCaptureSession(Arrays.asList(imageReader.getSurface()), new CameraCaptureSession.StateCallback() {
//摄像机设备完成自身配置后会调用此方法,并且会话可以开始处理捕获请求。
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
Log.d("CaptureSessionCallback", "摄像机设备已完成自身配置,并且会话可以开始处理捕获请求!");
previeSession = session;
try {
previeBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
//设置预览画面显示的surface
previeBuilder.addTarget(imageReader.getSurface());
// previeBuilder.set()方法参数解析:第一个参数表示设置的是什么东西,比如闪光灯,第二个参数表示把要设置的东西设置成什么样,比如关闭闪光灯,打开闪光灯
//可以设置的内容参考https://www.apiref.com/android-zh/android/hardware/camera2/CaptureRequest.html
// 设置对焦模式-自动对焦
previeBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// 设置闪光灯模式-自动打开闪光灯
previeBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
//setRepeatingRequest()方法参数说明:
// 第二个参数为CameraCaptureSession.StateCallback(),没什么作用,可以写null
// 第三个参数为预览的hndler线程,为null的时候表示在主线程预览
previeSession.setRepeatingRequest(previeBuilder.build(), null, myHandler);
} catch (CameraAccessException e) {
Log.d("CaptureSessionCallback", "cameraCaptureSession访问摄像头失败");
}
}
//如果会话无法按请求进行配置,则调用此方法。
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
Log.d("CaptureSessionCallback", "会话无法按请求进行配置!");
}
}, myHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
Log.d("DeviceCallback", "CaptureRequest.Builder访问摄像头失败");
}
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
closeCamera();
Toast.makeText(MainActivity.this, "摄像头打开错误", Toast.LENGTH_SHORT).show();
}
}, myHandler);
} catch (CameraAccessException e) {
Log.d("SurfaceHolders", "cameraManager访问摄像头失败");
}
}
public String getCameras() {
String[] cameras = new String[0];
String cameid = "1";
try {
manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
cameras = manager.getCameraIdList();
for (int i = 0; i < cameras.length; i++) {
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameras[i]);
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
//判断是否为后摄,LENS_FACING_BACK为后摄 LENS_FACING_FRONT为前摄 LENS_FACING_EXTERNAL为USB摄像头
if (facing == CameraCharacteristics.LENS_FACING_BACK) {
cameid = cameras[i];
Log.d("摄像头ID", cameras[i]);
}
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
return cameid;
}
//关闭摄像头
public void closeCamera() {
if (cameraDevice == null) {
return;
}
cameraDevice.close();
cameraDevice = null;
}
//关闭线程
private void destoryHandler() {
if (handlerThread == null) {
return;
}
handlerThread.quitSafely();
try {
handlerThread.join(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
closeCamera();
destoryHandler();
}
private void checkCameraPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE},
1);
} else {
//有权限
Log.d("MainActivity", "有权限");
setImageReader();
}
}
//权限请求回调
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults != null && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
setImageReader();
} else {
Toast.makeText(this, "拒绝权限,请去设置里面手动开启权限", Toast.LENGTH_SHORT).show();
}
break;
}
}
}
总结
camera代码相对简单,更容易理解,camera2代码更复杂,流程更精细,同样对数据的可操作性更高,以上代码均可直接复制粘贴使用