Android Camera2 implements preview function
- 2021-10-24 23:52:08
- OfStack
1. Overview
Recently, I am doing some projects about face recognition, which need the preview function of Android camera. After consulting relevant information on the Internet, it is found that the original Camera API has been replaced by Camera2 API in Android 5.0 and later versions.
The brand-new Camera2 is modified on the basis of Camera, which greatly improves the camera function of Android system. It realizes the working process of camera preview through the following classes and methods:
• ES 16EN: Camera manager, mainly used to detect system cameras, open system cameras, etc.;
• ES 18EN: used to describe the system camera, which can be used to turn off the camera, create a camera session, send a photo request, etc.;
• ES 20 EN: used to describe various features supported by cameras;
• CameraCaptureSession: When the program needs to preview and take pictures, it needs to be realized through CameraCaptureSession first. The session is previewed by calling the method setRepeatingRequest ();
• ES 26EN: represents a capture request, which is used to describe various parameter settings for capturing pictures;
• CameraRequest. Builder: Responsible for generating CameraRequest objects.
2. Camera preview
The following through the source code to explain how to use Camera2 to achieve the preview function of the camera.
2.1 Camera Permission Settings
<uses-permission android:name="android.permission.CAMERA" />
2.2 App Layout
•activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000"
tools:context=".MainActivity">
</FrameLayout>
•fragment_camera.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=".CameraFragment">
<com.lightweh.camera2preview.AutoFitTextureView
android:id="@+id/textureView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
</RelativeLayout>
2.3 Camera Custom View
public class AutoFitTextureView extends TextureView {
private int mRatioWidth = 0;
private int mRatioHeight = 0;
public AutoFitTextureView(Context context) {
this(context, null);
}
public AutoFitTextureView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setAspectRatio(int width, int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Size cannot be negative.");
}
mRatioWidth = width;
mRatioHeight = height;
requestLayout();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (0 == mRatioWidth || 0 == mRatioHeight) {
setMeasuredDimension(width, height);
} else {
if (width < height * mRatioWidth / mRatioHeight) {
setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
} else {
setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
}
}
}
}
2.4 Dynamic Application for Camera Permission
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_PERMISSION = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (hasPermission()) {
if (null == savedInstanceState) {
setFragment();
}
} else {
requestPermission();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
if (requestCode == REQUEST_PERMISSION) {
if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
setFragment();
} else {
requestPermission();
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
// Permission judgment, when the system version is greater than 23 It is necessary to determine whether to obtain permissions
private boolean hasPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
} else {
return true;
}
}
// Request camera permissions
private void requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
Toast.makeText(MainActivity.this, "Camera permission are required for this demo", Toast.LENGTH_LONG).show();
}
requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_PERMISSION);
}
}
// Activate the camera Fragment
private void setFragment() {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, CameraFragment.newInstance())
.commitNowAllowingStateLoss();
}
}
2.5 Open Camera Preview
First, in onResume (), we need to open an HandlerThread, and then build an Handler with the Looper object of this thread for camera callback.
@Override
public void onResume() {
super.onResume();
startBackgroundThread();
// When the screen is turned off and turned back on, the SurfaceTexture is
// already available, and "onSurfaceTextureAvailable" will not be called. In
// that case, we can open a camera and start preview from here (otherwise, we
// wait until the surface is ready in the SurfaceTextureListener).
if (mTextureView.isAvailable()) {
openCamera(mTextureView.getWidth(), mTextureView.getHeight());
} else {
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
}
}
private void startBackgroundThread() {
mBackgroundThread = new HandlerThread("CameraBackground");
mBackgroundThread.start();
mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}
At the same time, there is a corresponding HandlerThread shutdown method in onPause ().
When the screen is closed and reopened, SurfaceTexture is ready, and the onSurfaceTextureAvailable callback will not be triggered at this time. Therefore, if we judge that mTextureView is available, we will turn on the camera directly, otherwise we will wait for SurfaceTexture callback to be ready before turning on the camera.
private void openCamera(int width, int height) {
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
return;
}
setUpCameraOutputs(width, height);
configureTransform(width, height);
Activity activity = getActivity();
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
}
}
When turning on the camera, we first judge whether we have camera permission, then call setUpCameraOutputs function to set camera parameters (including specifying camera, preview direction of camera and setting preview size, etc.), then call configureTransform function to adjust the size and direction of preview picture, and finally get CameraManager object to turn on the camera. Because the camera may be accessed by other processes at the same time, it needs to be locked when turning on the camera.
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
mCameraOpenCloseLock.release();
mCameraDevice = cameraDevice;
createCameraPreviewSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
mCameraOpenCloseLock.release();
cameraDevice.close();
mCameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int error) {
mCameraOpenCloseLock.release();
cameraDevice.close();
mCameraDevice = null;
Activity activity = getActivity();
if (null != activity) {
activity.finish();
}
}
};
When the camera is turned on, the state change callback function mStateCallback of the camera will also be specified. If the camera is turned on successfully, the camera preview session will be created.
private void createCameraPreviewSession() {
try {
// Get texture Instances
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
// Settings TextureView Buffer size
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
// Get Surface Display preview data
Surface surface = new Surface(texture);
// Build a request suitable for camera preview
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
// Settings surface As a display interface for preview data
mPreviewRequestBuilder.addTarget(surface);
// Create a camera capture session for preview
mCameraDevice.createCaptureSession(Arrays.asList(surface),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
// Return if the camera is turned off
if (null == mCameraDevice) {
return;
}
// Open a preview if the session is ready
mCaptureSession = cameraCaptureSession;
try {
// Autofocus
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
mPreviewRequest = mPreviewRequestBuilder.build();
// Set up the request to capture data repeatedly and preview the interface 1 Straight display screen
mCaptureSession.setRepeatingRequest(mPreviewRequest,
null, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(
@NonNull CameraCaptureSession cameraCaptureSession) {
showToast("Failed");
}
}, null
);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
The above is the main process of Camera2 API to realize camera preview.
3. Demo source code
Github: Camera2Preview
4. Reference
• https://github.com/googlesamples/android-Camera2Basic
Summarize