I need to use Camera2 API on my application. (Api21+)
I found the next sample:
https://github.com/googlesamples/android-Camera2Basic
I downloaded it and started on my phone. When I pressed the "Picture" Button, it's call the takePhoto method.
private void takePicture() {
lockFocus();
}
It is a State machine. Sometimes this machine stuck on STATE_WAITING_LOCK.
My device is waiting for Focus, but nothing happens! (Yes, my device is support auto focus)
case STATE_WAITING_LOCK: {
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
if (afState == null) {
captureStillPicture();
} else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
// CONTROL_AE_STATE can be null on some devices
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null ||
aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
mState = STATE_PICTURE_TAKEN;
captureStillPicture();
} else {
runPrecaptureSequence();
}
}
break;
}
What is the good solution for this problem?
And this program sometimes crashed here:
private void unlockFocus() {
try {
// Reset the auto-focus trigger
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
setAutoFlash(mPreviewRequestBuilder);
/*HERE*/ mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
mBackgroundHandler);
// After this, the camera will go back to the normal state of preview.
mState = STATE_PREVIEW;
mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback,
mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
Why can't focus my device?
It seems that your program stucks sometimes in CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED where according to documentation "The AF algorithm has been unable to focus. The lens is not moving." link
Try to cancel the AF_TRIGGER and start it again. Something like this:
if (afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
getRidOfNotFocusedLock();
}
And:
private void getRidOfNotFocusedLock(){
try {
mPreviewRequestBuilder.set(
CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
mCaptureSession.capture(
captureRequestBuilder.build(), captureSessionCaptureCallback, backgroundHandler);
mPreviewRequestBuilder.set(
CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
When I found Lautern's answer, I had to adjust it a little, as it wasn't working for me.
As the program stucks sometimes in CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED, you can try to restart the AF_TRIGGER:
if (afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
restartFocus();
}
private void restartFocus(){
try {
mPreviewRequestBuilder.set(
CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
mCaptureSession.capture(
captureRequestBuilder.build(),
captureSessionCaptureCallback,
mBackgroundHandler);
lockFocus();
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void lockFocus() {
try {
mPreviewRequestBuilder.set(
CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_START);
mState = STATE_WAIT_LOCK;
mCaptureSession.capture(
mPreviewRequestBuilder.build(),
mPreviewCaptureCallback,
mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
As some time has passed, since I implemented this, I'm not entirely sure why I had to restart the capture session. But without it, it doesn't work for me.
Related
I am not able to get a constant framerate with the camera2 API using MediaRecorder on the Galaxy S9 front camera.
Essentially I am using the example Project from https://github.com/googlesamples/android-Camera2Video but stripped it down to find the error. I removed the mTextureView and use only the mediaRecorder surface.
Here are the relevant code snippets:
#Override
protected void setUpMediaRecorder() throws IOException {
final Activity activity = getActivity();
if (null == activity) {
return;
}
// Camcorder Profile QUALITY_HIGH doenst work
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setVideoEncodingBitRate(30000000);
mMediaRecorder.setVideoFrameRate(30);
mMediaRecorder.setVideoSize(2560, 1440);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
mMediaRecorder.prepare();
}
#Override
protected void setUpCaptureRequestBuilder(CaptureRequest.Builder builder) {
Range<Integer> range = getRange();
builder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, range);
}
// get highest range
private Range<Integer> getRange() {
CameraManager mCameraManager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);
CameraCharacteristics chars = null;
try {
chars = mCameraManager.getCameraCharacteristics(mCameraManager.getCameraIdList()[1]);
} catch (CameraAccessException e) {
e.printStackTrace();
}
Range<Integer>[] ranges = chars.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
Range<Integer> result = null;
for (Range<Integer> range : ranges) {
if (result == null) {
result = range;
} else {
int upper = range.getUpper();
int lower = range.getLower();
if (upper >= result.getUpper() && lower >= result.getLower()) {
result = range;
}
}
}
return result;
}
protected void startRecordingVideo() {
if (null == mCameraDevice) {
return;
}
try {
closeCaptureSession();
setUpMediaRecorder();
mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
List<Surface> surfaces = new ArrayList<>();
// Set up Surface for the MediaRecorder
Surface recorderSurface = mMediaRecorder.getSurface();
surfaces.add(recorderSurface);
mPreviewBuilder.addTarget(recorderSurface);
// Start a capture session
// Once the session starts, we can update the UI and start recording
mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(#NonNull CameraCaptureSession cameraCaptureSession) {
mCaptureSession = cameraCaptureSession;
setUpCaptureRequestBuilder(mPreviewBuilder);
if (null == mCameraDevice) {
return;
}
try {
mCaptureSession.setRepeatingRequest(mPreviewBuilder.build(), null, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
// UI
mButtonVideo.setText(R.string.stop);
mIsRecordingVideo = true;
// Start recording
mMediaRecorder.start();
}
});
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession cameraCaptureSession) {
Activity activity = getActivity();
if (null != activity) {
Toast.makeText(activity, "Failed", Toast.LENGTH_SHORT).show();
}
}
}, mBackgroundHandler);
} catch (CameraAccessException | IOException e) {
e.printStackTrace();
}
}
getRange() returns a Range of [30,30]. So it should record at 30fps. If I move the camera to some darker areas the frame rate suddenly drops. If locking the AE while moving to a light area the framerate is stable, even in dark areas.
So it seems to have something to do with AE but can not figure out what. Any help?
I have gone through the android docs as well as the camera2basic repo and have been able to display camera preview frames on a TextureView. I have two buttons: one that can record the video, the other can capture still images and store it in the device's memory. What I wanna achieve is something different, I want to have all the camera frames being sent to the server in a sequence via sockets in real-time. I have broken down task into smaller chuncks and am presently trying to capture all the camera frames as jpeg images.
This is the CameraCaptureSession.CaptureCallback code whereby I am calling savePreviewShot() which actually replicates the original captureStillPicture() function of Camera2basic repo:
private CameraCaptureSession.CaptureCallback mCaptureCallback
= new CameraCaptureSession.CaptureCallback() {
private void process(CaptureResult result) {
switch (mState) {
case STATE_PREVIEW: {
// We have nothing to do when the camera preview is working normally.
savePreviewShot();
break;
}
case STATE_WAITING_LOCK: {
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
if (afState == null) {
captureStillPicture();
} else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
// CONTROL_AE_STATE can be null on some devices
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null ||
aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
mState = STATE_PICTURE_TAKEN;
captureStillPicture();
} else {
runPrecaptureSequence();
}
}
break;
}
case STATE_WAITING_PRECAPTURE: {
// CONTROL_AE_STATE can be null on some devices
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null ||
aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
mState = STATE_WAITING_NON_PRECAPTURE;
}
break;
}
case STATE_WAITING_NON_PRECAPTURE: {
// CONTROL_AE_STATE can be null on some devices
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
mState = STATE_PICTURE_TAKEN;
captureStillPicture();
}
break;
}
}
}
This is the savePreviewShot() function:
private void savePreviewShot(){
try {
final Activity activity = getActivity();
if (null == activity || null == mCameraDevice) {
return;
}
// This is the CaptureRequest.Builder that we use to take a picture.
final CaptureRequest.Builder captureBuilder =
mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureBuilder.addTarget(mImageReader.getSurface());
// Orientation
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
CameraCaptureSession.CaptureCallback CaptureCallback
= new CameraCaptureSession.CaptureCallback() {
#Override
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
TotalCaptureResult result) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss:SSS");
Date resultdate = new Date(System.currentTimeMillis());
String mFileName = sdf.format(resultdate);
mFile = new File(getActivity().getExternalFilesDir(null), "pic "+mFileName+" preview.jpg");
Log.i("Saved file", ""+mFile.toString());
unlockFocus();
}
};
mCaptureSession.stopRepeating();
mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);
} catch (Exception e) {
e.printStackTrace();
}
};
However, as you might have figured it out already, such a call causes tremendous lag in displaying the preview on the map and moreover, I am not getting the frames saved as well. Hope you have got the aim I have and the problem I am facing. I have already gone through lots of stuffs on SO and Google but none of them did help.
Have the same exact problem and I was wondering if you got a fix. What I did was set an ImageReader;
private ImageReader videoReader;
private final ImageReader.OnImageAvailableListener onVideoFrameAvailableListener
Loaded it when i openned the camera
videoReader = ImageReader.newInstance(videoImageSize.getWidth(), videoImageSize.getHeight(), ImageFormat.JPEG, 2);
videoReader.setOnImageAvailableListener(onVideoFrameAvailableListener, backgroundHandler);
Added the Surface to the captureSession and processed the image in the listener usingframe = reader.acquireLatestImage();
But yeah, lags a lot the preview and don't know what the solution may be.
Hi I'm using camera2basic example to implement my camera2 application. I can't find any good example to implement touch to focus with camera2 api. Currently the code i'm using for touch to focus is this:
private void setFocusArea(MotionEvent event) {
if (mCameraId == null) return;
CameraManager cm = (CameraManager)getActivity().getSystemService(Context.CAMERA_SERVICE);
CameraCharacteristics cc = null;
try {
cc = cm.getCameraCharacteristics(mCameraId);
} catch (CameraAccessException e) {
e.printStackTrace();
}
int myX = (int)event.getX();
int myY = (int)event.getY();
MeteringRectangle focusArea = new MeteringRectangle(myX-100,myY-100,200,200,MeteringRectangle.METERING_WEIGHT_DONT_CARE);
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
try {
mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
mBackgroundHandler);
// After this, the camera will go back to the normal state of preview.
mState = STATE_PREVIEW;
} catch (CameraAccessException e){
// log
}
if (isMeteringAreaAESupported(cc)) {
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_REGIONS,
new MeteringRectangle[]{focusArea});
}
if (isMeteringAreaAFSupported(cc)) {
mPreviewRequestBuilder
.set(CaptureRequest.CONTROL_AF_REGIONS, new MeteringRectangle[]{focusArea});
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_AUTO);
}
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_START);
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CameraMetadata.CONTROL_AE_PRECAPTURE_TRIGGER_START);
try {
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback,
mBackgroundHandler);
mManualFocusEngaged = true;
} catch (CameraAccessException e) {
// error handling
}
}
But the problem is that it shows strange behavior, with auto-flash on it keeps repeating the auto-focus sequence for unlimited times also it doesnot seem to focus on the touched area. I tried changing
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
to:
mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
this stopped the repeating auto-focus sequence but it still doesn't focus on the touched area and the flash just blinks for less than a second instead of a normal focus sequence. Please help me with this or guide me to a working touch to focus example. Thanks
Your problem is setting the AF region's control.
Calculate the region that you want to set Focus
stop current session mPreviewSession.stopRepeating()
Start AF trigger!!!
3.1. Safely start to make the AF region IDLE
3.2. then start AF trigger
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE);
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
Capture once to apply your settings
Check if AF and AE regions are supported or not is supported If supported then apply this region
if ( isMeteringAreaAESupported()) {
//System.out.println("AE regions are supported");
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AE_REGIONS, new MeteringRectangle[]{focusArea});
}
if ( isMeteringAreaAFSupported()) {
//System.out.println("AF regions are supported");
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, new MeteringRectangle[]{focusArea});
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
}
Again capture once to set the focus
mPreviewCaptureSession.capture(mCaptureRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
inside mCaptureCallback you should cancel AF trigger, but the documentation says AF trigger can be null in some device so I did like
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE);
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, null);
The last things is mPreviewCaptureSession.setRepeatingRequest(mCaptureRequestBuilder.build(), null, mBackgroundHandler);
EDIT
Here is the working example
private void setFocusArea(int focus_point_x, int focus_point_y) throws CameraAccessException {
if (cameraId == null || mManualFocusEngaged) return;
if (mCameraManager == null){
mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
}
MeteringRectangle focusArea = null;
if (mCameraManager != null) {
if (mCameraCharacteristics == null) {
mCameraCharacteristics = mCameraManager.getCameraCharacteristics(cameraId);
}
final Rect sensorArraySize = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
int y = focus_point_x;
int x = focus_point_y;
if (sensorArraySize != null) {
y = (int)(((float)focus_point_x / currentWidth) * (float)sensorArraySize.height());
x = (int)(((float)focus_point_y / currentHeight) * (float)sensorArraySize.width());
}
final int halfTouchLength = 150;
focusArea = new MeteringRectangle(Math.max(x - halfTouchLength, 0),
Math.max(y - halfTouchLength, 0),
halfTouchLength * 2,
halfTouchLength * 2,
MeteringRectangle.METERING_WEIGHT_MAX - 1);
}
CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
#Override
public void onCaptureCompleted(#NonNull CameraCaptureSession session, #NonNull CaptureRequest request, #NonNull TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
mManualFocusEngaged = false;
if (request.getTag().equals(FOCUS_TAG)) { // previously getTag == "Focus_tag"
//the focus trigger is complete -
//resume repeating (preview surface will get frames), clear AF trigger
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE);
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, null);// As documentation says AF_trigger can be null in some device
try {
mCurrentCameraCaptureSession.setRepeatingRequest(mCaptureRequestBuilder.build(), null, mBackgroundHandler);
} catch (CameraAccessException e) {
// error handling
}
}
}
#Override
public void onCaptureFailed(#NonNull CameraCaptureSession session, #NonNull CaptureRequest request, #NonNull CaptureFailure failure) {
super.onCaptureFailed(session, request, failure);
mManualFocusEngaged = false;
}
};
mCurrentCameraCaptureSession.stopRepeating(); // Destroy current session
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE);
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
mCurrentCameraCaptureSession.capture(mCaptureRequestBuilder.build(), mCaptureCallback, mBackgroundHandler); //Set all settings for once
if ( isMeteringAreaAESupported()) {
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AE_REGIONS, new MeteringRectangle[]{focusArea});
}
if ( isMeteringAreaAFSupported()) {
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, new MeteringRectangle[]{focusArea});
mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
}
mCaptureRequestBuilder.setTag(FOCUS_TAG); //it will be checked inside mCaptureCallback
mCurrentCameraCaptureSession.capture(mCaptureRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
mManualFocusEngaged = true;
}
private boolean isMeteringAreaAFSupported() { // AF stands for AutoFocus
Integer afRegion = mCameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
return afRegion != null && afRegion >= 1;
}
private boolean isMeteringAreaAESupported() {//AE stands for AutoExposure
Integer aeState = mCameraCharacteristics.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE);
return aeState!=null && aeState >=1;
}
Hope it helps.
Enjoy coding
i was reading about many reports about this, but still couldn't find the answer for me.
Sometimes, after capturing with camera2, an exception is throwing: java.lang.IllegalStateException: Session has been closed; further changes are illegal.
I tried to check all the sessions not being null, when was doing something, but still get this.
Any suggestion ? anything else should i handle?=, which i didn't ?
private CameraCaptureSession.CaptureCallback mCaptureCallback
= new CameraCaptureSession.CaptureCallback() {
private void process(CaptureResult result) {
switch (mState) {
case STATE_PREVIEW: {
if(touchFocusEnabled) {
letSetCaptureSessionOnce = true;
mState = STATE_WAITING_LOCK;
try {
// Reset the auto-focus trigger
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
mHandler);
} catch (CameraAccessException e) {
L.e("CameraLolipop --- CameraCaptureSession.CaptureCallback " + e);
}
} else {
if(letSetCaptureSessionOnce) {
try {
if ((null != mCaptureSession) && (isCameraOpen)) {
mState = STATE_PREVIEW;
mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
mHandler);
letSetCaptureSessionOnce = true;
}
} catch (CameraAccessException e) {
L.e("CameraLolipop --- CameraCaptureSession.CaptureCallback " + e);
}
}
}
break;
}
case STATE_WAITING_LOCK: {
if(touchFocusEnabled) {
mState = STATE_PICTURE_TAKEN;
touchFocusEnabled = false;
try {
if((mCaptureSession != null) && (isCameraOpen)){
mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
mHandler);
}
} catch (CameraAccessException e) {
L.e("CameraLolipop --- STATE_WAITING_LOCK " + e);
}
return;
}
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
L.d("Focus state ", "STATE_WAITING_LOCK");
boolean fixedFocus = isFixedFocus();
if (afState == null) {
// if ((burstMode) && (getSupportedHardwareLevel() == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)) {
// captureStillPictureBurst();
// } else {
captureStillPicture();
// }
} else if ((CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) || fixedFocus) {
L.d("Focus state ", "CONTROL_AF_STATE_FOCUSED_LOCKED or CONTROL_AF_STATE_NOT_FOCUSED_LOCKED");
// CONTROL_AE_STATE can be null on some devices
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null ||
aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
mState = STATE_PICTURE_TAKEN;
if ((burstMode) && (getSupportedHardwareLevel() == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)) {
// captureStillPictureBurst();
captureStillPicture();
} else {
captureStillPicture();
}
} else {
runPrecaptureSequence();
}
}
break;
}
case STATE_WAITING_PRECAPTURE: {
L.d("Focus state ", "STATE_WAITING_PRECAPTURE");
// CONTROL_AE_STATE can be null on some devices
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null ||
aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED || isFixedFocus()) {
mState = STATE_WAITING_NON_PRECAPTURE;
}
break;
}
case STATE_WAITING_NON_PRECAPTURE: {
L.d("Focus state ", "STATE_WAITING_NON_PRECAPTURE");
// CONTROL_AE_STATE can be null on some devices
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
mState = STATE_PICTURE_TAKEN;
if ((burstMode) && (getSupportedHardwareLevel() == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)) {
// captureStillPictureBurst();
captureStillPicture();
} else {
captureStillPicture();
}
}
break;
}
case STATE_PICTURE_TAKEN: {
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
L.d("TOUCH", " afState " + afState);
mState = STATE_PREVIEW;
if(afState == 4) {
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
}
if(afState == 0) {
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
mPreviewRequestBuilder
.set(CaptureRequest.CONTROL_AF_REGIONS, null);
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
}
}, 2500);
}
break;
}
}
}
#Override
public void onCaptureProgressed(CameraCaptureSession session,
CaptureRequest request,
CaptureResult partialResult) {
super.onCaptureProgressed(session, request, partialResult);
// process(partialResult);
}
#Override
public void onCaptureCompleted(CameraCaptureSession session,
CaptureRequest request,
TotalCaptureResult result) {
// if ((mState != STATE_PREVIEW) || (touchFocusEnabled)) {
super.onCaptureCompleted(session, request, result);
if(session != null) {
process(result);
}
}
};
and here is where session is being created:
private CameraCaptureSession.StateCallback mSessionPreviewStateCallback = new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(CameraCaptureSession cameraCaptureSession) {
L.i("Thread", "onConfigured---->" + Thread.currentThread().getName());
// The camera is already closed
if(null == mCameraDevice) {
return;
}
try {
mCaptureSession = cameraCaptureSession;
mCameraSessionIsClosed = false;
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
if((isCameraOpen) && (mCaptureSession != null)){
mPreviewRequest = mPreviewRequestBuilder.build();
mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mHandler);
}
L.d("ex0003", "Test... mSessionPreviewStateCallback ");
} catch (CameraAccessException e) {
L.e("CameraLolipop --- openCamera() " + e);
}
}
#Override
public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
Toast.makeText(getActivity(), "onConfigureFailed---Preview", Toast.LENGTH_SHORT).show();
}
};
and in onPause closeCamera() is being called:
try {
if(mCameraDevice == null && mCameraIsClosed) {
return;
}
if((mCaptureSession != null) && (isCameraOpen)) {
try {
mCaptureSession.abortCaptures();
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
mCameraOpenCloseLock.acquire();
if (null != mCaptureSession) {
mCaptureSession.close();
mCaptureSession = null;
mCameraSessionIsClosed = true;
}
if (null != mCameraDevice) {
mCameraDevice.close();
mCameraDevice = null;
isCameraOpen = false;
mCameraIsClosed = true;
}
if (null != mImageReader) {
mImageReader.close();
mImageReader = null;
}
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
} finally {
mCameraOpenCloseLock.release();
}
and after stopBackgroundThread() and super.onPause() are being called.
In my case problem was createCaptureRequest being called multiple when another is already in process(onConfigured/onConfigureFailed called). What I end up was creating a boolean variable that keeps track if camera_preview is already in process, if so then do not make another createCaptureRequest.
Check if all the camera configurations have been set with a flag, as #Rohit suggested or give some delay to the repeatingRequest of your mCaptureSession while it setting the configuration for you in parallel in the background. In my case I solved this exception by giving a delay of 500 mS to set the repeatingRequest like this:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
try {
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),
mCaptureCallback, null);
} catch (CameraAccessException e) {
Log.e(TAG, "Failed to start camera preview because it couldn't access camera", e);
} catch (IllegalStateException e) {
Log.e(TAG, "Failed to start camera preview.", e);
}
}
}, 500);
Also, the same problem occurs when you first time grant permissions and initially start your camera preview(app crashes), but there you will get CameraAccessException stating "Failed to start camera session". In that case, you should give the same amount of delay while creating capture sessions like below:
void startCaptureSession() {
if (!isCameraOpened() || !mPreview.isReady() || mImageReader == null) {
return;
}
previewSize = chooseOptimalSize();
mPreview.setBufferSize(previewSize.getWidth(), previewSize.getHeight());
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
try {
Surface surface = mPreview.getSurface();
mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
mCamera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
mSessionCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
throw new RuntimeException("Failed to start camera session");
}
}
}, 500);
}
I hope it helps.
In the documentation it says:
if this session is no longer active, either because the session was explicitly closed, a new session has been created or the camera device has been closed.
If you are using the camera2api sample-project, this means: You either call openCamera(), createCameraPreviewSession() or closeCamera() while another createCameraPreviewSession() was called aswell.
I solved this with a global variable, that is always set to true, when the function createCameraPreviewSession() is called; and is set to false, 10MS after createCameraPreviewSession() was finished. If this variable is true, I block all new createCameraPreviewSession() calls and also all closeCamera() calls.
This solution worked for me. However, I don't know if there is a fundamental reason, why you should not use this. If somebody knows better, please let me know.
I'm not sure if this will work, but try it and let me know if it worked.
private CameraCaptureSession.StateCallback mSessionPreviewStateCallback = new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(CameraCaptureSession cameraCaptureSession) {
L.i("Thread", "onConfigured---->" + Thread.currentThread().getName());
// The camera is already closed
if(null == mCameraDevice) {
return;
}
try {
mCaptureSession = cameraCaptureSession;
mCameraSessionIsClosed = false;
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
if((isCameraOpen) && (mCaptureSession != null)){
mPreviewRequest = mPreviewRequestBuilder.build();
mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mHandler);
}
L.d("ex0003", "Test... mSessionPreviewStateCallback ");
} catch (CameraAccessException e) {
Log.e(TAG, "Failed to start camera preview because it couldn't access camera", e);
} catch (IllegalStateException e) {
Log.e(TAG, "Failed to start camera preview.", e);
}
}
#Override
public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
Toast.makeText(getActivity(), "onConfigureFailed---Preview", Toast.LENGTH_SHORT).show();
}
#Override
public void onClosed(#NonNull CameraCaptureSession session) {
if (mCaptureSession != null && mCaptureSession.equals(session)) {
mCaptureSession = null;
}
}
};
This error occurs if the camera is opened before setting all the configuration to the preview request builder. Hence you must first set all the parameters like the TextureView size, camera output size etc.
Here is code from google's sample app:
setUpCameraOutputs(width, height);
configureTransform(width, height);
Activity activity = getActivity();
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
I developed app with Camera2 api support. App provides preview on camera and do some realtime processing. On most devices everythings work fine. But On Samsung S5 mini I'm getting just 2 frames per second. I can't imagine why ?! Do you have any suggestions ?
EDIT:
Open previewing:
private void createCameraPreviewSession() {
try {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
// We configure the size of default buffer to be the size of camera preview we want.
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
// This is the output Surface we need to start preview.
Surface surface = new Surface(texture);
// We set up a CaptureRequest.Builder with the output Surface.
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
// Here, we create a CameraCaptureSession for camera preview.
mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(#NonNull CameraCaptureSession cameraCaptureSession) {
// The camera is already closed
if (null == mCameraDevice) {
return;
}
// When the session is ready, we start displaying the preview.
mCaptureSession = cameraCaptureSession;
try {
// Flash is automatically enabled when necessary.
mCameraInfo.setRequestParameters(mPreviewRequestBuilder);
// Finally, we start displaying the camera preview.
mPreviewRequest = mPreviewRequestBuilder.build();
mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession cameraCaptureSession) {
showToast("Failed - configure camera");
getActivity().finish();
}
}, null
);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
Capture callback:
private CameraCaptureSession.CaptureCallback mCaptureCallback
= new CameraCaptureSession.CaptureCallback() {
private void process(CaptureResult result) {
switch (mState) {
case STATE_PREVIEW: {
if(mOverlaySurfaceView.isImageBorderLocked()){
takePicture();
}
// We have nothing to do when the camera preview is working normally.
break;
}
case STATE_WAITING_LOCK: {
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
if (afState == null) {
captureStillPicture();
} else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState ||
CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
// CONTROL_AE_STATE can be null on some devices
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null ||
aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
mState = STATE_PICTURE_TAKEN;
captureStillPicture();
} else {
runPrecaptureSequence();
}
}
else{
runPrecaptureSequence();
}
break;
}
case STATE_WAITING_PRECAPTURE: {
// CONTROL_AE_STATE can be null on some devices
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null ||
aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
mState = STATE_WAITING_NON_PRECAPTURE;
}
break;
}
case STATE_WAITING_NON_PRECAPTURE: {
// CONTROL_AE_STATE can be null on some devices
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
mState = STATE_PICTURE_TAKEN;
captureStillPicture();
}
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) {
process(result);
}
#Override
public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, CaptureFailure failure) {
Log.e(LOG_TITLE, "Capture failed");
super.onCaptureFailed(session, request, failure);
}
};
Thank you for any help.
EDIT 2:
I found the reason of slow performance. In the preview mode I called mTextureView.getBitmap() inside onSurfaceTextureUpdated. This operation is too expensive. Because when new frame was arrived I constructed new Bitmap. I canceled this call and now is everything running smoothly.