创建预览组件surfaceView
surfaceView创建好后打开摄像头
摄像头状态回调,创建预览至surfaceView
拍照
SurfaceHolder.Callback()中openCamera() -> CameraDevice.StateCallback 中创建 CameraCaptureSession -> CameraCaptureSession.StateCallback 中发起请求
package com.haoyang.camera;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
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.Camera;
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.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class MainActivity extends AppCompatActivity {
private SurfaceView surfaceView;
private Button btSn;
private ImageView ivBitmap;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceView = findViewById(R.id.sv_surfaceView);
btSn = findViewById(R.id.bt_sn);
ivBitmap = findViewById(R.id.iv_bitmap);
surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void surfaceCreated(SurfaceHolder holder) {
//打开摄像头
try {
openCamera();
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
});
btSn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sn();
}
});
}
CameraDevice mCameraDevice;
CameraManager cameraManager;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void openCamera() throws CameraAccessException {
cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != 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;
}
assert cameraManager != null;
cameraManager.openCamera("0", new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
mCameraDevice = camera;
createCameraPreviewSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
}
}, null);
}
CaptureRequest.Builder previewRequestBuilder;
CaptureRequest previewRequest;
CameraCaptureSession captureSession;
ImageReader imageReader;
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void createCameraPreviewSession() {
try {
// 获取指定摄像头的特性
CameraCharacteristics characteristics = null;
characteristics = cameraManager.getCameraCharacteristics("0");
// 获取摄像头支持的配置属性
StreamConfigurationMap map = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
// 获取摄像头支持的最大尺寸
Size largest = Collections.max(
Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
new CompareSizesByArea());
imageReader = ImageReader.newInstance(largest.getWidth(),largest.getHeight(),
ImageFormat.JPEG, 1);
SurfaceControl surfaceControl = surfaceView.getSurfaceControl();
Surface surface = new Surface(surfaceControl);
// 创建作为预览的CaptureRequest.Builder
previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
// 将textureView的surface作为CaptureRequest.Builder的目标
previewRequestBuilder.addTarget(surface);
// 创建CameraCaptureSession,该对象负责管理处理预览请求和拍照请求
mCameraDevice.createCaptureSession(Arrays.asList(surface,imageReader.getSurface()), new CameraCaptureSession.StateCallback()
{
@Override
public void onConfigured(CameraCaptureSession cameraCaptureSession) {
// 如果摄像头为null,直接结束方法
if (null == mCameraDevice) {
return;
}
// 当摄像头已经准备好时,开始显示预览
captureSession = cameraCaptureSession;
try {
// 开始显示相机预览
previewRequest = previewRequestBuilder.build();
// 设置预览时连续捕获图像数据
captureSession.setRepeatingRequest(previewRequest,
null, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
Toast.makeText(MainActivity.this, "配置失败!"
, Toast.LENGTH_SHORT).show();
}
}, null
);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void sn(){
try {
CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
previewRequestBuilder.addTarget(imageReader.getSurface());
captureSession.stopRepeating();
captureSession.capture(previewRequestBuilder.build(), new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
try {
captureSession.setRepeatingRequest(previewRequest,null,null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
},null);
imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
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);
ivBitmap.setImageBitmap(bitmap);
image.close();
}
},null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
static class CompareSizesByArea implements Comparator<Size> {
@Override
public int compare(Size lhs, Size rhs) {
// 强转为long保证不会发生溢出
return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
(long) rhs.getWidth() * rhs.getHeight());
}
}
}
具体说明请查询api文档
SurfaceView
用途:创建surfaceView并回调状态
方法:
surfaceView.getHolder.addCallback(new SurfaceHolder.Callback());
CameraManager
用途:管理camera,此处开启摄像头,摄像头回调开启状态
方法:
cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
cameraManager.openCamera("0", new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
mCameraDevice = camera;
//摄像头开启时开始预览
createCameraPreviewSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
}
}, null);
CameraDevice
用途:返回CaptureRequest.Builder对象,创建CameraCaptureSession对象
方法:
mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mCameraDevice.createCaptureSession(Arrays.asList(surface,imageReader.getSurface()), new CameraCaptureSession.StateCallback()
{
@Override
public void onConfigured(CameraCaptureSession cameraCaptureSession) {
// 如果摄像头为null,直接结束方法
if (null == mCameraDevice) {
return;
}
// 当摄像头已经准备好时,开始显示预览
captureSession = cameraCaptureSession;
try {
// 开始显示相机预览
previewRequest = previewRequestBuilder.build();
// 设置预览时连续捕获图像数据
captureSession.setRepeatingRequest(previewRequest,
null, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
Toast.makeText(MainActivity.this, "配置失败!"
, Toast.LENGTH_SHORT).show();
}
}, null
);
CaptureRequest
用途:拍照请求作为session参数,可由CaptureRequest.Builder的buidler方法生成
CaptureRequest.Builder
用途:提供许多配置项,预览
方法:
//聚焦、曝光等配置
captureRequestBuilder.set();
//将预览流填入surface
captureRequestBuilder.addTarget(surface);
CameraCaptureSession
用途:控制拍照,捕获图像,需要CaptureRequest作为参数
方法:
captureSession.stopRepeating();
captureSession.capture(previewRequestBuilder.build(), new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
try {
captureSession.setRepeatingRequest(previewRequest,null,null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
},null);
ImageReader
用途:捕获图像到ImageReader,用于转成Image
方法:
//将预览流填入imageReader.surface,以下方法提供imageReader.surface的数据来源
captureRequestBuilder.addTarget(imageReader.surface);
imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
}
},new Handler());
//将预览流传入ImageReader
captureRequestBuilder.addTarget(ImageReader.getsurface());
Image
用途:可转化为byte数组,转bitmap、存图等
方法:
Image image = imageReader.acquireNextImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
image.close();
1:拍照时提示captureRequest包含一个未配置输入/输出的surface,我们拍照时调用了一句代码是captureRequestBuilder.addTarget(imageReader.getSurface()),这里的surface(imageReader.getSurface())未在创建session时加进List outputs:
报错在199行的previewRequestBuilder.build(),其实是187行的ImageReader.getSufce()存在问题
解决方法:createCaptureSession的时候把surface放进List带进去
2:拍照存Image时,拍了一定数量的照片后发现不走imageReader.setOnImageAvailableListener()的回调方法而且拍照时一直卡在一个画面,后面拿不到图或者报以下错误
解决方法:处理之后记得释放资源
3:拍照时发现拍出来的图片显示不出来,可能是图片太大了,我的手机支持最大像素尺寸6000*8000
尝试方法:尝试修改下尺寸imageReader的尺寸,此处的largest.getWidth(),largest.getHeight()适当的改小一些
1.需要申请权限
2.surfaceView大小此处未做调整,预览出来的画面有点变形,拍照生成的图片会有270度变化,这些需要我们调整