RK Android uses the standard Camera HAL3 framework, which can support standard camera programming methods under android, such as camera2, this camera is occupied by the system by default.
There are two sets of CSI interfaces on YY3588, which adapt to GC8034 camera module by default, and the wiring is as follows
The following section describes how to use Camera2 framework to realize video preview.
On the basis of the app created in the first chapter, add a button and a video preview window textureView for screen preview.
Due to more camera-related operations, here is a new class named CameraTextureView, inheriting textureView, the class source code is as follows
package com.example.testdemo;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
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.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.AttributeSet;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import java.util.Arrays;
public class CameraTextureView extends TextureView implements TextureView.SurfaceTextureListener {
private CameraDevice cameraDevice;
private CameraCaptureSession cameraCaptureSession;
private Handler backgroundHandler;
private HandlerThread backgroundThread;
private String cameraId;
public CameraTextureView(Context context) {
this(context, null);
}
public CameraTextureView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CameraTextureView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setSurfaceTextureListener(this);
}
public void startCameraPreview(boolean isFront) {
if (backgroundHandler != null) {
backgroundHandler.post(() -> {
try {
openCamera(isFront);
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
public void stopCameraPreview() {
if (backgroundHandler != null) {
backgroundHandler.post(() -> {
try {
closeCamera();
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
private void startBackgroundThread() {
if (backgroundThread == null) {
backgroundThread = new HandlerThread("CameraBackground");
backgroundThread.start();
backgroundHandler = new Handler(backgroundThread.getLooper());
}
}
private void stopBackgroundThread() {
if (backgroundThread != null) {
backgroundThread.quitSafely();
try {
backgroundThread.join();
backgroundThread = null;
backgroundHandler = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void openCamera(boolean isFront) {
CameraManager manager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE);
try {
cameraId = getCameraId(manager,isFront);
if (cameraId == null) {
return;
}
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
Size[] previewSizes = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
.getOutputSizes(SurfaceTexture.class);
Size previewSize = previewSizes[0];
if (getSurfaceTexture() != null) {
getSurfaceTexture().setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
Surface surface = new Surface(getSurfaceTexture());
if (ActivityCompat.checkSelfPermission(this.getContext(), 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(cameraId, new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
cameraDevice = camera;
createCameraPreviewSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
camera.close();
cameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
camera.close();
cameraDevice = null;
}
}, backgroundHandler);
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private String getCameraId(CameraManager manager,boolean isFront) throws CameraAccessException {
for (String id : manager.getCameraIdList()) {
CameraCharacteristics characteristics = manager.getCameraCharacteristics(id);
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if(isFront) {
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
return id;
}
} else {
if (facing != null && facing == CameraCharacteristics.LENS_FACING_BACK) {
return id;
}
}
}
return null;
}
private void createCameraPreviewSession() {
try {
SurfaceTexture texture = getSurfaceTexture();
if (texture == null || cameraDevice == null) {
return;
}
Surface surface = new Surface(texture);
final CaptureRequest.Builder previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(surface);
cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
if (cameraDevice == null) {
return;
}
cameraCaptureSession = session;
try {
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
CaptureRequest previewRequest = previewRequestBuilder.build();
cameraCaptureSession.setRepeatingRequest(previewRequest, null, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
}
}, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void closeCamera() {
if (cameraCaptureSession != null) {
cameraCaptureSession.close();
cameraCaptureSession = null;
}
if (cameraDevice != null) {
cameraDevice.close();
cameraDevice = null;
}
}
// SurfaceTextureListener
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
startBackgroundThread();
// startCameraPreview();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
stopCameraPreview();
stopBackgroundThread();
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
}
In this class, call startCameraPreview to start the camera preview, call stopCameraPreview to stop the preview, the function of start preview and stop preview are in a new thread to avoid blocking the main thread.
Then add a reference to this class in the layout, here we use two CameraTextureView to display the front and rear cameras.
<com.example.testdemo.CameraTextureView
android:id="@+id/cameraTextureView"
android:layout_width="300dp"
android:layout_height="150dp"
app:layout_constraintEnd_toEndOf="@+id/guideline_vertical_1"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/guideline_vertical"
app:layout_constraintTop_toBottomOf="@+id/guideline_horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="0.5" />
<com.example.testdemo.CameraTextureView
android:id="@+id/cameraTextureView_1"
android:layout_width="300dp"
android:layout_height="150dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/guideline_vertical_1"
app:layout_constraintTop_toBottomOf="@+id/guideline_horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="0.5" />
Add button logic to onCreate, pressing the button performs toggleCameraPreview to turn on or off the camera preview
button4.setText("Start Back Preview");
button4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
toggleBackCameraPreview();
}
});
button5.setText("Start Front Preview");
button5.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
toggleFrontCameraPreview();
}
});
The following code uses toggleCameraPreview to turn the camera preview on or off when the button is pressed.
private void toggleBackCameraPreview() {
if (isBackPreviewing) {
isBackPreviewing = false;
cameraTextureViewBack.stopCameraPreview();
button4.setText("Start Back Preview");
mTextLinewriter.writeLine("stop back camera test... \n");
cameraTextureViewBack.setVisibility(View.INVISIBLE);
} else {
isBackPreviewing = true;
cameraTextureViewBack.startCameraPreview(false);
button4.setText("Stop Back Preview");
mTextLinewriter.writeLine("start back camera test... \n");
cameraTextureViewBack.setVisibility(View.VISIBLE);
}
}
private void toggleFrontCameraPreview() {
if (isFrontPreviewing) {
isFrontPreviewing = false;
cameraTextureViewFront.stopCameraPreview();
button5.setText("Start Front Preview");
mTextLinewriter.writeLine("stop Front camera test... \n");
cameraTextureViewFront.setVisibility(View.INVISIBLE);
} else {
isFrontPreviewing = true;
cameraTextureViewFront.startCameraPreview(true);
button5.setText("Stop Front Preview");
mTextLinewriter.writeLine("start Front camera test... \n");
cameraTextureViewFront.setVisibility(View.VISIBLE);
}
}
Additionally, to use the camera2 api, you need to add to AndroidManifest.xml the
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.any" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
The app works as follows, you can see the two textureViews appearing after pressing the Start Back Preview and Start Front Preview buttons.