Android5之后,Camera的api有了很大变化,称为Camera2
Camera2的功能更多更强大,同时也更加复杂
在camera2中管道pipeline(也可以叫做流),照片,视频都是从流中获取的。
分析这张图,可以简单得到预览其实有三步
- 打开相机,创建与相机的会话(即建立管道)
- 发起CaptureRequest,获取流内容
- 建立Surface,将内容输出到Surface(即输出到手机屏幕)
当然,这只是将相机预览流程进行了抽象,实际相机预览流程没有那么简单,下面是一个简单的demo,实现相机预览
入口MainActivity
package com.example.camera2test;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.FrameLayout;
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity";
private Camera2Preview mCameraPreview;
private FrameLayout parentView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCameraPreview = new Camera2Preview(this);
parentView = (FrameLayout) findViewById(R.id.camera_preview);
parentView.addView((View) mCameraPreview);
findViewById(R.id.button_switch).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCameraPreview.switchCameraFacing();
}
});
}
@Override
protected void onResume() {
super.onResume();
if (mCameraPreview != null)
mCameraPreview.onResume();
}
@Override
protected void onPause() {
super.onPause();
if (mCameraPreview != null)
mCameraPreview.onPause();
}
}
接口CameraView
/**
* 功能描述
*
* @author y50003490
* @since 2019-08-20
*/
public interface CameraView {
/**
* 与生命周期onResume调用
*/
void onResume();
/**
* 与生命周期onPause调用
*/
void onPause();
/**
* 切换相机摄像头
*/
void switchCameraFacing();
}
相机预览Camera2Preview
package com.example.camera2test;
import androidx.annotation.NonNull;
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.Matrix;
import android.graphics.RectF;
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.AttributeSet;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import java.util.Arrays;
public class Camera2Preview extends TextureView implements CameraView {
private static final String TAG = "Camera2Preview";
private Context mContext;
public Camera2Preview(Context context) {
this(context, null);
}
public Camera2Preview(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public Camera2Preview(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
}
/**
* ID of the current {@link CameraDevice}.
*/
private String mCameraId;
/**
* 相机预览要用到的{@link CameraCaptureSession } .
*/
private CameraCaptureSession mCaptureSession;
/**
* A reference to the opened {@link CameraDevice}.
*/
private CameraDevice mCameraDevice;
/**
* 相机预览的 {@link android.util.Size}
*/
private Size mPreviewSize;
/**
* 用于运行不应该阻止UI的任务的附加线程。
*/
private HandlerThread mBackgroundThread;
/**
* 在后台运行任务的Handler。
*/
private Handler mBackgroundHandler;
/**
* {@link CaptureRequest.Builder} for the camera1 preview
*/
private CaptureRequest.Builder mPreviewRequestBuilder;
/**
* {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder}
*/
private CaptureRequest mPreviewRequest;
/**
* 当前相机(前置、后置)
*/
private int mCurrentCameraFacing = CameraCharacteristics.LENS_FACING_BACK;
/**
* 同生命周期 onResume
*/
@Override
public void onResume() {
startBackgroundThread();
// 当关闭并打开屏幕时,SurfaceTexture已经可用,并且不会调用“onSurfaceTextureAvailable”。
// 在这种情况下,我们可以打开相机并从这里开始预览(否则,我们将等待,直到Surface在SurfaceTextureListener中准备好)。
if (isAvailable()) {
Log.d(TAG, "onResume: openCamera");
openCamera();
} else {
setSurfaceTextureListener(mSurfaceTextureListener);
Log.d(TAG, "onResume: is not Available");
}
}
/**
* 同生命周期 onPause
*/
@Override
public void onPause() {
closeCamera();
stopBackgroundThread();
Log.d(TAG, "onPause: close Camera");
}
/**
* 打开指定的相机(mCameraId)
*/
private void openCamera() {
Log.d(TAG, "openCamera: ");
if (mCameraId == null) {
mCameraId = String.valueOf(0);
}
CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
try {
//检查相机权限
if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "openCamera: no Camera Permission");
return;
}
//打开对应mCameraId的摄像头
Log.d(TAG, "openCamera: 2");
manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
Log.d(TAG, "openCamera: ERROR");
e.printStackTrace();
} finally {
Log.d(TAG, "finally: openCamera");
}
}
/**
* 关闭相机
*/
private void closeCamera() {
if (null != mCaptureSession) {
mCaptureSession.close();
mCaptureSession = null;
}
if (null != mCameraDevice) {
mCameraDevice.close();
mCameraDevice = null;
}
}
/**
* {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state.
*/
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
Log.d(TAG, "onOpened: ");
// 当相机打开时调用此方法。我们在这里开始相机预览。
mCameraDevice = cameraDevice;
//创建相机预览会话
createCameraPreviewSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
Log.d(TAG, "onDisconnected: ");
cameraDevice.close();
mCameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int error) {
Log.d(TAG, "onError: ");
cameraDevice.close();
mCameraDevice = null;
Log.e(TAG, "CameraDevice.StateCallback onError errorCode= " + error);
}
};
/**
* 创建一个新的 {@link CameraCaptureSession} 用于相机预览.
*/
private void createCameraPreviewSession() {
try {
//获得SurfaceTexture
SurfaceTexture texture = getSurfaceTexture();
if(texture == null){
Log.d(TAG, "createCameraPreviewSession: texture is null");
}
// 我们需要开始预览输出Surface
Surface surface = new Surface(texture);
// 我们建立了一个具有输出Surface的捕获器。
mPreviewRequestBuilder
= mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
// 这里,我们创建了一个用于相机预览的CameraCaptureSession
// test delete m mImageReader.getSurface()
mCameraDevice.createCaptureSession(Arrays.asList(surface),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
Log.d(TAG, "onConfigured: ");
// 相机已经关闭
if (null == mCameraDevice) {
return;
}
// 当session准备好后,我们开始显示预览
mCaptureSession = cameraCaptureSession;
try {
// 相机预览时应连续自动对焦
//mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// 设置闪光灯在必要时自动打开
//setAutoFlash(mPreviewRequestBuilder);
// 最终,显示相机预览
mPreviewRequest = mPreviewRequestBuilder.build();
//test delte mCaptureCallback
mCaptureSession.setRepeatingRequest(mPreviewRequest,
null, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(
@NonNull CameraCaptureSession cameraCaptureSession) {
Log.e(TAG, "CameraCaptureSession.StateCallback onConfigureFailed");
}
}, null
);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
/**
* 启动后台线程和Handler.
*/
private void startBackgroundThread() {
mBackgroundThread = new HandlerThread("CameraBackground");
mBackgroundThread.start();
mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}
/**
* 停止后台线程和Handler.
*/
private void stopBackgroundThread() {
mBackgroundThread.quitSafely();
try {
mBackgroundThread.join();
mBackgroundThread = null;
mBackgroundHandler = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a
* {@link TextureView}.
*/
private final TextureView.SurfaceTextureListener mSurfaceTextureListener
= new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
openCamera();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
}
};
/**
* 配置必要的 {@link android.graphics.Matrix} 转换为 `mTextureView`.
* <p>
* 该方法应该在setUpCameraOutputs中确定相机预览大小以及“mTextureView”的大小固定之后调用。
*
* @param viewWidth The width of `mTextureView`
* @param viewHeight The height of `mTextureView`
*/
private void configureTransform(int viewWidth, int viewHeight) {
if (null == mPreviewSize || null == mContext) {
return;
}
WindowManager mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
int rotation = mWindowManager.getDefaultDisplay().getRotation();
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
float scale = Math.max(
(float) viewHeight / mPreviewSize.getHeight(),
(float) viewWidth / mPreviewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
setTransform(matrix);
}
@Override
public void switchCameraFacing() {
mCurrentCameraFacing = mCurrentCameraFacing == CameraCharacteristics.LENS_FACING_BACK ?
CameraCharacteristics.LENS_FACING_FRONT : CameraCharacteristics.LENS_FACING_BACK;
mCameraId = String.valueOf(mCurrentCameraFacing);
if (mCameraId == null) {
mCameraId = String.valueOf(1);
}
closeCamera();
openCamera();
}
}