RK Android使用的是标准的 Camera HAL3框架,可以支持android下标准的摄像头编程方式,如camera2,这个摄像头默认被系统占用。
YY3588上有两组CSI接口,默认适配GC8034摄像头模组,接线如下
下面介绍如何使用Camera2框架实现视频预览。
在第一章节创建的app基础上,增加一个按钮和一个视频预览窗口textureView,进行画面预览。
由于camera相关的操作比较多,这里新增一个名为CameraTextureView的class,继承textureView,此class源码如下
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) {
// 每帧更新时调用
}
}
这个class中,调用 startCameraPreview 开始摄像头预览,调用 stopCameraPreview 停止预览,开始预览和停止预览的功能都是在一个新建的线程中进行,避免阻塞主线程
然后在layout中增加这个class的引用,这里用两个 CameraTextureView 显示前后摄像头
<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" />
在 onCreate 中添加按钮的逻辑,按下按钮执行 toggleCameraPreview 开启或停止摄像头预览
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();
}
});
下面代码中,使用 toggleCameraPreview ,在按钮按下时,开启或关闭摄像头预览。
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);
}
}
此外,要使用camera2的api,需要在 AndroidManifest.xml 中添加
<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" />
此app运行效果如下,可以看到按下 Start Back Preview 和 Start Front Preview 按钮后两个 textureView 分别出现两个摄像头画面