可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm working with the new Camera2 API on a Samsung S5. The supported hardware level this device is reporting is LEGACY
, which is fine.
However, I cannot seem to be able to auto-focus on this device. The request to trigger auto-focus looks like this:
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO); previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START); state = STATE_PREVIEW; try { captureSession.setRepeatingRequest(previewRequestBuilder.build(), captureCallback, backgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); }
After the request is sent, the result of the request is always CONTROL_AF_STATE_ACTIVE_SCAN
and occasionally CONTROL_AF_STATE_NOT_FOCUSED_LOCKED
.
The strange thing is that, when the state is CONTROL_AF_STATE_NOT_FOCUSED_LOCKED
, the auto-focus goes back into the CONTROL_AF_STATE_ACTIVE_SCAN
state for a while and then back to CONTROL_AF_STATE_NOT_FOCUSED_LOCKED
, resulting in a infinite focus loop. According to the docs, when state is CONTROL_AF_STATE_NOT_FOCUSED_LOCKED
...
The lens will remain stationary until the AF mode (android.control.afMode) is changed or a new AF trigger is sent to the camera device (android.control.afTrigger).
I'm wondering if this discrepancy is because of the fact that the hardware level is LEGACY
and that I should go back to using the deprecated Camera API, but that seems crazy for such a prevalent feature such as auto focus.
Is there any reccomendations how how to treat devices that are reporting LEGACY
?
回答1:
I branched form google's Camera2Basic example and changed it to use CaptureRequest.CONTROL_AF_MODE_AUTO
instead of CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
You can take the project from git and test it - https://github.com/pinhassi/android-Camera2Basic
Or just add this to Camera2BasicFragment
:
private static final long LOCK_FOCUS_DELAY_ON_FOCUSED = 5000; private static final long LOCK_FOCUS_DELAY_ON_UNFOCUSED = 1000; private Integer mLastAfState = null; private Handler mUiHandler = new Handler(); // UI handler private Runnable mLockAutoFocusRunnable = new Runnable() { @Override public void run() { lockAutoFocus(); } }; public void lockAutoFocus() { try { // This is how to tell the camera to lock focus. mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START); CaptureRequest captureRequest = mPreviewRequestBuilder.build(); mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, null); // prevent CONTROL_AF_TRIGGER_START from calling over and over again mCaptureSession.capture(captureRequest, mCaptureCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } /** * * @return */ private float getMinimumFocusDistance() { if (mCameraId == null) return 0; Float minimumLens = null; try { CameraManager manager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE); CameraCharacteristics c = manager.getCameraCharacteristics(mCameraId); minimumLens = c.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE); } catch (Exception e) { Log.e(TAG, "isHardwareLevelSupported Error", e); } if (minimumLens != null) return minimumLens; return 0; } /** * * @return */ private boolean isAutoFocusSupported() { return isHardwareLevelSupported(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) || getMinimumFocusDistance() > 0; } // Returns true if the device supports the required hardware level, or better. @TargetApi(Build.VERSION_CODES.LOLLIPOP) private boolean isHardwareLevelSupported(int requiredLevel) { boolean res = false; if (mCameraId == null) return res; try { CameraManager manager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE); CameraCharacteristics cameraCharacteristics = manager.getCameraCharacteristics(mCameraId); int deviceLevel = cameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); switch (deviceLevel) { case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3: Log.d(TAG, "Camera support level: INFO_SUPPORTED_HARDWARE_LEVEL_3"); break; case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL: Log.d(TAG, "Camera support level: INFO_SUPPORTED_HARDWARE_LEVEL_FULL"); break; case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY: Log.d(TAG, "Camera support level: INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY"); break; case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED: Log.d(TAG, "Camera support level: INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED"); break; default: Log.d(TAG, "Unknown INFO_SUPPORTED_HARDWARE_LEVEL: " + deviceLevel); break; } if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) { res = requiredLevel == deviceLevel; } else { // deviceLevel is not LEGACY, can use numerical sort res = requiredLevel <= deviceLevel; } } catch (Exception e) { Log.e(TAG, "isHardwareLevelSupported Error", e); } return res; }
Then, add to STATE_PREVIEW
block:
case STATE_PREVIEW: { // We have nothing to do when the camera preview is working normally. // TODO: handle auto focus Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); if (afState != null && !afState.equals(mLastAfState)) { switch (afState) { case CaptureResult.CONTROL_AF_STATE_INACTIVE: Log.d(TAG, "CaptureResult.CONTROL_AF_STATE_INACTIVE"); lockAutoFocus(); break; case CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN: Log.d(TAG, "CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN"); break; case CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED: Log.d(TAG, "CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED"); mUiHandler.removeCallbacks(mLockAutoFocusRunnable); mUiHandler.postDelayed(mLockAutoFocusRunnable, LOCK_FOCUS_DELAY_ON_FOCUSED); break; case CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: mUiHandler.removeCallbacks(mLockAutoFocusRunnable); mUiHandler.postDelayed(mLockAutoFocusRunnable, LOCK_FOCUS_DELAY_ON_UNFOCUSED); Log.d(TAG, "CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED"); break; case CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED: mUiHandler.removeCallbacks(mLockAutoFocusRunnable); //mUiHandler.postDelayed(mLockAutoFocusRunnable, LOCK_FOCUS_DELAY_ON_UNFOCUSED); Log.d(TAG, "CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED"); break; case CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN: Log.d(TAG, "CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN"); break; case CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED: mUiHandler.removeCallbacks(mLockAutoFocusRunnable); //mUiHandler.postDelayed(mLockAutoFocusRunnable, LOCK_FOCUS_DELAY_ON_FOCUSED); Log.d(TAG, "CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED"); break; } } mLastAfState = afState; break; }
And replace all occurrences of:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
With:
if (isAutoFocusSupported()) mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO); else mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
回答2:
I think the issue is with your setRepeatingRequest. As far as I know, CaptureRequest.CONTROL_AF_MODE_AUTO should only cause an autofocus to occur once, but setRepeatingRequest will send continuous requests. Try using capture instead:
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO); previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START); state = STATE_PREVIEW; try { mCaptureSession.capture(mPreviewRequestBuilder.build(), mPreCaptureCallback, mBackgroundHandler); } catch (Exception e) {e.printStackTrace();}
回答3:
I experience the same issue with a Galaxy Note 4 running Android 5.1.1 - while the same code works fine on a variety of other Android devices. There have been reports of similar issues with Galaxy-S4/S5/S6.
http://developer.samsung.com/forum/board/thread/view.do?boardName=SDK&messageId=289824&startId=zzzzz~ https://www.youtube.com/watch?v=lnMoYZwVaFM
So to anwer you question: This is most likely a bug in Samsung's implementation of the Camera-2 implementation - which seems to be of very low quality, unfourtunately.
回答4:
The Samsung S5 with autofocus returned INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
, which means it does not support Camera2
api.
I have the below filter for using camera in my application.
if (Build.VERSION.SDK_INT >= 21 && isDeviceCompatibleOfCamera2()) { // Use camera2 } else { // Use old camera } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public boolean isDeviceCompatibleOfCamera2() { try { CameraManager manager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE); String backCameraId = manager.getCameraIdList()[0]; CameraCharacteristics backCameraInfo = manager.getCameraCharacteristics(backCameraId); int level = backCameraInfo.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); return level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL; } catch (CameraAccessException e) { ETLog.d(TAG, "Device not compatible of camera2 api" + e); } return false; }