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


Related articles: