I am trying to make an android app that takes the frames from the camera and then do some image processing on it. I managed to make the camera preview, but when i quit the app (for example open multitasking or go to the homepage of my phone), and then go back to the app, the app freezes. I am totally new to the Android camera2 API so I assume it is something stupid I did wrong. Also, my code is based on several examples I found online, and I do not understand everything at a 100%, but I have the feeling there are 'useless' lines of code in the program. If it is the case, could someone tell me which ones? Thank you in advance.
Here is my code (I am using Android Studio):
public String TAG = MainActivity.class.getSimpleName();
TextureView textureView;
TextView runtime, FPS, angle;
boolean firstRun = true;
byte[] nv21;
int ySize, uSize, vSize;
int rowStride, pixelStride;
long time1, time2, time, timeMS;
float fps;
DecimalFormat decimalFormat = new DecimalFormat("#.###");
byte count = 0;
CameraManager cameraManager;
CameraDevice cameraDevice;
String[] cameraIDs;
String cameraID;
Size size;
HandlerThread handlerThread;
Handler backgroundHandler;
ImageReader imageReader;
CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
cameraDevice = camera;
createCaptureSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
}
};
ImageReader.OnImageAvailableListener onImageAvailableListener = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
runOnUiThread(new Runnable() {
@Override
public void run() {
// calculate and display FPS
time1 = System.currentTimeMillis();
count += 1;
timeMS = time1 - time2;
time += timeMS;
fps += 1000 / ((float) timeMS);
if (count >= 5) {
time /= 5;
fps /= 5;
runtime.setText(String.valueOf(time) + " ms");
FPS.setText(decimalFormat.format(fps) + " FPS");
time = 0;
fps = 0;
count = 0;
}
}
});
Image image = reader.acquireLatestImage();
// do the image processing
image.close();
time2 = System.currentTimeMillis();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textureView = findViewById(R.id.textureView);
runtime = findViewById(R.id.runtime);
FPS = findViewById(R.id.fps);
angle = findViewById(R.id.angle);
time2 = System.currentTimeMillis();
}
@Override
protected void onResume() {
super.onResume();
openBackgroundHandler();
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
openBackgroundHandler();
setupCamera();
openCamera();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
});
}
@Override
protected void onPause() {
super.onPause();
closeBackgroundHandler();
}
private void setupCamera() {
cameraManager = (CameraManager) getSystemService(CAMERA_SERVICE);
try {
cameraIDs = cameraManager.getCameraIdList();
cameraID = cameraIDs[2]; // 0 = main back camera || 1 = selfie camera || 2 = wide angle camera
Log.i(TAG, String.valueOf(cameraIDs.length));
Log.i(TAG, cameraID);
CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraID);
StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
size = streamConfigurationMap.getOutputSizes(SurfaceTexture.class)[0];
} catch (CameraAccessException e) {
e.printStackTrace();
}
imageReader = ImageReader.newInstance(size.getWidth(), size.getHeight(), ImageFormat.YUV_420_888, 3);
imageReader.setOnImageAvailableListener(onImageAvailableListener, backgroundHandler);
}
@SuppressLint("MissingPermission")
private void openCamera() {
try {
cameraManager.openCamera(cameraID, stateCallback, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void openBackgroundHandler() {
handlerThread = new HandlerThread("camera_app");
handlerThread.start();
backgroundHandler = new Handler(handlerThread.getLooper());
}
private void closeBackgroundHandler() {
handlerThread.quit();
handlerThread = null;
backgroundHandler = null;
}
private void createCaptureSession() {
SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
surfaceTexture.setDefaultBufferSize(size.getWidth(), size.getHeight());
final Surface previewSurface = new Surface(surfaceTexture);
List surfaces = new ArrayList<>();
surfaces.add(previewSurface);
final Surface readerSurface = imageReader.getSurface();
surfaces.add(readerSurface);
try {
//cameraDevice.createCaptureSession(Collections.singletonList(surface), new CameraCaptureSession.StateCallback() {
cameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
try {
CaptureRequest.Builder captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequestBuilder.addTarget(readerSurface);
captureRequestBuilder.addTarget(previewSurface);
session.setRepeatingRequest(captureRequestBuilder.build(), null, backgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
}
}, backgroundHandler);
} catch (CameraAccessException e1) {
e1.printStackTrace();
}
}