I'm building a camera app based on Camera2, but the picture I save is not matching the latest one I saw on my surface view. It seems that the Preview session works but when I ask for a capture, the new request stop the preview and capture the image. The surface view freeze on the latest pic and that create a gap between the time I press the shutter button (preview running and request capture) and the onCaptureCompleted from the capture request.
Here is the preview session
private void createCameraPreviewSession() {
try {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
Log.d(TAG, "here is the width of texture" + mPreviewSize.getWidth());
Log.d(TAG, "here is the height of texture" +mPreviewSize.getHeight());
Surface surface = new Surface(texture);
mPreviewRequestBuilder
= mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
if (null == mCameraDevice) {
return;
}
mCaptureSession = cameraCaptureSession;
try {
mPreviewRequest = mPreviewRequestBuilder.build();
mCaptureSession.setRepeatingRequest(mPreviewRequest,
mCaptureCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(
@NonNull CameraCaptureSession cameraCaptureSession) {
showToast("Failed");
}
}, null
);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
the mCaptureCallback is defined as below :
private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
private void process(CaptureResult result) {
switch (mState) {
case STATE_PREVIEW:
break;
case STATE_CAPTURE:
mState = STATE_PREVIEW;
capturePicture();
break;
}
}
@Override
public void onCaptureProgressed(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull CaptureResult partialResult) {
process(partialResult);
}
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull TotalCaptureResult result) {
TotalCaptureResult iResult = result;
Log.d(TAG, "Frame on Completed: "+result.getFrameNumber());
process(result);
}
}
What's happening is that I repeating the preview and it works. the process is just used to keep it running and nothing happened until the mState is set to CAPTURE.
It's set to capture when we click on the shutter button. When I click on the button, I call:
private void takePicture(){
try {
mFile = ImageSaver.generateNewFileImage();
mState = STATE_CAPTURE;
mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
mBackgroundHandler);
} catch (CameraAccessException e) {
Log.e(TAG,"Camera exception",e);
}
}
I call then CapturePicture as mState is in Capture as defined in the mCaptureCallback
private void capturePicture() {
mTakePictureRunnable = new Runnable() {
@Override
public void run() {
takePictureNow();
}
};
mBackgroundHandler.post(mTakePictureRunnable);
}
the takePicutreNow is defined
private void takePictureNow() {
Log.d(TAG, "Running captureStillPicture");
try {
if (null == mCameraDevice) {
return;
}
// This is the CaptureRequest.Builder that we use to take a picture.
final CaptureRequest.Builder captureBuilder =
mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(mImageReader.getSurface());
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
Log.d(TAG, "here is the width of texture" + mPreviewSize.getWidth());
Log.d(TAG, "here is the height of texture" + mPreviewSize.getHeight());
Surface surface = new Surface(texture);
captureBuilder.addTarget(surface);
// Orientation
int rotation = getWindowManager().getDefaultDisplay().getRotation();
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
//Location if needed
boolean Location_Saved = CameraSettings.Instance().getBoolean(CameraSettings.SAVE_LOCATION,
getResources().getBoolean(R.bool.action_camera_settings_dflt_location));
if(Location_Saved == true) {
captureBuilder.set(CaptureRequest.JPEG_GPS_LOCATION, mLocationManager.getCurrentLocation());
} else {
captureBuilder.set(CaptureRequest.JPEG_GPS_LOCATION, null);
}
CameraCaptureSession.CaptureCallback CaptureCallback
= new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureStarted(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull long timestamp,
@NonNull long framenumber) {
playShutterSound();
showShutterAnimation();
}
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull TotalCaptureResult result) {
Log.d(TAG, mFile.toString());
mState = STATE_PREVIEW;
}
};
mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);
mCaptureSession.stopRepeating();
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
This process is working but I got a lag between the press and the imagesave and the latest pic I saw on the screen from the preview is not exactly the same saved if I move quickly.
It seems that the capture do not update the surface and the surface only show when we were in Preview
Any idea to see what I save ? Thanks