目前只是简单的实现的预览和拍照,照片没有处理,所以拍出来的不是正的。
权限先加好。
1.定义TextureView
要预览camera就要先定义一个用来显示的控件。布局很简单,不上代码了。
首先看TextureView是否可用,可用就直接去打开camera,否则设置一个监听,当TextureView可用的时候再去打开。
@Override
protected void onResume() {
startBackgroundThread();
if (mTextureView.isAvailable()) {
openCamera(mTextureView.getWidth(), mTextureView.getHeight());
} else {
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
}
super.onResume();
}
private final TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
openCamera(width,height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
};
这里还要开线程,就是camera跑的地方。当程序关闭记得关掉。
private void startBackgroundThread() {
mBackgroundThread = new HandlerThread("camera");
mBackgroundThread.start();
mBackgroundHeandler = new Handler(mBackgroundThread.getLooper());
}
private void stopBackgroundThread() {
mBackgroundThread.quitSafely();
try {
mBackgroundThread.join();
mBackgroundThread = null;
mBackgroundHeandler = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
2.设置预览的一些参数
private void setupCameraOutputs(int width, int height) {
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
for (String cameraId : manager.getCameraIdList()) {
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
//不使用前置摄像头
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT)
continue;
//获取摄像头支持的配置属性
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null)
continue;
//获得摄像头支持的最大尺寸
Size largest = Collections.max(
Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
new CompareSizesByArea());
//preview最大不能超过1920x1080,不然camera会出错,不能预览。
mPreviewSize = new Size(MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT);
mCameraId = cameraId;
return;
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
/**
* Compares two {@code Size}s based on their areas.
*/
static class CompareSizesByArea implements Comparator<Size> {
@Override
public int compare(Size lhs, Size rhs) {
// We cast here to ensure the multiplications won't overflow
return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
(long) rhs.getWidth() * rhs.getHeight());
}
}
3.打开camera
private void openCamera(int width, int height) {
setupCameraOutputs(width, height);
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
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;
}
manager.openCamera(mCameraId, mStateCallback, mBackgroundHeandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
mStateCallback是一个回调函数,查看camera的状态,如果camera已经打开了,就会走onOpened,然后我们就去显示预览画面。
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
mCameraDevice = cameraDevice;
createCaptureSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
cameraDevice.close();
mCameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int i) {
cameraDevice.close();
mCameraDevice = null;
}
};
4.显示预览
private void createCaptureSession() {
try {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
Surface mSurface = new Surface(texture);
mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mCaptureRequestBuilder.addTarget(mSurface);
//这里的list里面只有mSurface,如果要拍照还要加一个ImageReader
mCameraDevice.createCaptureSession(Arrays.asList(mSurface), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
mCameraCaptureSession = cameraCaptureSession;
mCaptureRequest = mCaptureRequestBuilder.build();
try {
mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, mCaptureCallback, mBackgroundHeandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private final CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
}
};
到这里,就能正常预览camera画面了
退出程序记得关闭camera和线程
@Override
protected void onPause() {
stopBackgroundThread();
closeCamera();
super.onPause();
}
private void closeCamera() {
if (mCameraCaptureSession != null) {
mCameraCaptureSession.close();
mCameraCaptureSession = null;
}
if (mCameraDevice != null) {
mCameraDevice.close();
mCameraDevice = null;
}
}
拍照
这里没有考虑照片旋转,所以拍完的照片不是和preview的画面一样是正的。以后在加。
在设置参数的时候新建一个ImageReader,代码和前面一样,就多了2行,ImageReader的宽高不能超过(1920,1080)不然会有问题,为什么我就不知道了,我在google的camera2的Demo中看到了。
private void setupCameraOutputs(int width, int height) {
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
for (String cameraId : manager.getCameraIdList()) {
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT)
continue;
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null)
continue;
Size largest = Collections.max(
Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
new CompareSizesByArea());
List<Size> list = new ArrayList<>();
Collections.addAll(list, map.getOutputSizes(ImageFormat.JPEG));
for (Size size : list) {
Log.i("aaron", "------- ");
Log.i("aaron", " " + size);
}
Log.i("aaron", "largest " + largest.getWidth() + " " + largest.getHeight());
Log.i("aaron", "texture " + mTextureView.getWidth() + " " + mTextureView.getHeight());
//选择camera最大支持的size来拍照
mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 2);
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHeandler);
//preview最大不能超过1920x1080,不然camera会出错,不能预览。
mPreviewSize = new Size(width, height);
mCameraId = cameraId;
return;
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
在创建预览的时候把ImageReader加进去。
mImageReader.getSurface()
mCameraDevice.createCaptureSession(Arrays.asList(mSurface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
mCameraCaptureSession = cameraCaptureSession;
mCaptureRequest = mCaptureRequestBuilder.build();
try {
//这里的CaptureCallback会一直回调,因为画面一直在preview
mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, mCaptureCallback, mBackgroundHeandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
为按钮添加点击拍照功能。
这里的captureRequestBuilder是用来拍照的,然后addTarget把ImageReader加进去,拍照后就会有数据到ImageReader的回调函数了。
拍照的时候不能预览,所以要先关闭预览,拍完在重新开启预览。
private void takePicture() {
try {
CaptureRequest.Builder captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
//添加ImageReader,当拍照时就会走ImageReader的回调,之前设置的。
captureRequestBuilder.addTarget(mImageReader.getSurface());
CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
//拍照完成,重新preview
try {
mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, mCaptureCallback, mBackgroundHeandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
};
//停止preview,然后去拍照
mCameraCaptureSession.stopRepeating();
mCameraCaptureSession.abortCaptures();
//这里的CaptureCallback在拍照的时候才会回调。和preview那里的不一样,preview那里会一直回调。
mCameraCaptureSession.capture(captureRequestBuilder.build(), captureCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
拍到的照片在mOnImageAvailableListener回调中,然后去开线程保存。
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
= new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
//保存拍到的照片
mBackgroundHeandler.post(new ImageSaver(reader.acquireNextImage(), mFile));
Log.i("aaron", "onImageAvailable");
}
};
/**
* Saves a JPEG {@link Image} into the specified {@link File}.
*/
private static class ImageSaver implements Runnable {
/**
* The JPEG image
*/
private final Image mImage;
/**
* The file we save the image into.
*/
private final File mFile;
ImageSaver(Image image, File file) {
mImage = image;
mFile = file;
}
@Override
public void run() {
ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
FileOutputStream output = null;
try {
output = new FileOutputStream(mFile);
output.write(bytes);
} catch (IOException e) {
e.printStackTrace();
} finally {
mImage.close();
if (null != output) {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
文件就随便在哪里创建下
mFile = new File(Environment.getExternalStorageDirectory().toString() +
"/Pictures/ab.jpg");
最后在程序退出关闭camera的时候把ImageReader一起关了
private void closeCamera() {
if (mCameraCaptureSession != null) {
mCameraCaptureSession.close();
mCameraCaptureSession = null;
}
if (mCameraDevice != null) {
mCameraDevice.close();
mCameraDevice = null;
}
if (null != mImageReader) {
mImageReader.close();
mImageReader = null;
}
}