Fix NPE when accessing Android camera focus modes

Looks like getSupportedFocusModes() may return null, despite the documentation stating otherwise.

Bug: webrtc:13032
Change-Id: I0119b8a97be9ef4340c3e93f16e2dcaa899f2f3c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/227288
Reviewed-by: Xavier Lepaul‎ <xalep@webrtc.org>
Commit-Queue: Xavier Lepaul‎ <xalep@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#34873}
This commit is contained in:
Saúl Ibarra Corretgé
2021-08-26 10:49:46 +02:00
committed by WebRTC LUCI CQ
parent 1460e15a45
commit 424b420a22

View File

@ -11,6 +11,7 @@
package org.webrtc; package org.webrtc;
import android.content.Context; import android.content.Context;
import android.hardware.Camera;
import android.os.Handler; import android.os.Handler;
import android.os.SystemClock; import android.os.SystemClock;
import java.io.IOException; import java.io.IOException;
@ -39,8 +40,8 @@ class Camera1Session implements CameraSession {
private final Context applicationContext; private final Context applicationContext;
private final SurfaceTextureHelper surfaceTextureHelper; private final SurfaceTextureHelper surfaceTextureHelper;
private final int cameraId; private final int cameraId;
private final android.hardware.Camera camera; private final Camera camera;
private final android.hardware.Camera.CameraInfo info; private final Camera.CameraInfo info;
private final CaptureFormat captureFormat; private final CaptureFormat captureFormat;
// Used only for stats. Only used on the camera thread. // Used only for stats. Only used on the camera thread.
private final long constructionTimeNs; // Construction time of this class. private final long constructionTimeNs; // Construction time of this class.
@ -66,17 +67,17 @@ class Camera1Session implements CameraSession {
return; return;
} }
final android.hardware.Camera camera; final Camera camera;
try { try {
camera = android.hardware.Camera.open(cameraId); camera = Camera.open(cameraId);
} catch (RuntimeException e) { } catch (RuntimeException e) {
callback.onFailure(FailureType.ERROR, e.getMessage()); callback.onFailure(FailureType.ERROR, e.getMessage());
return; return;
} }
if (camera == null) { if (camera == null) {
callback.onFailure(FailureType.ERROR, callback.onFailure(
"android.hardware.Camera.open returned null for camera id = " + cameraId); FailureType.ERROR, "Camera.open returned null for camera id = " + cameraId);
return; return;
} }
@ -88,12 +89,12 @@ class Camera1Session implements CameraSession {
return; return;
} }
final android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo(); final Camera.CameraInfo info = new Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info); Camera.getCameraInfo(cameraId, info);
final CaptureFormat captureFormat; final CaptureFormat captureFormat;
try { try {
final android.hardware.Camera.Parameters parameters = camera.getParameters(); final Camera.Parameters parameters = camera.getParameters();
captureFormat = findClosestCaptureFormat(parameters, width, height, framerate); captureFormat = findClosestCaptureFormat(parameters, width, height, framerate);
final Size pictureSize = findClosestPictureSize(parameters, width, height); final Size pictureSize = findClosestPictureSize(parameters, width, height);
updateCameraParameters(camera, parameters, captureFormat, pictureSize, captureToTexture); updateCameraParameters(camera, parameters, captureFormat, pictureSize, captureToTexture);
@ -111,7 +112,7 @@ class Camera1Session implements CameraSession {
} }
} }
// Calculate orientation manually and send it as CVO insted. // Calculate orientation manually and send it as CVO instead.
try { try {
camera.setDisplayOrientation(0 /* degrees */); camera.setDisplayOrientation(0 /* degrees */);
} catch (RuntimeException e) { } catch (RuntimeException e) {
@ -124,9 +125,8 @@ class Camera1Session implements CameraSession {
surfaceTextureHelper, cameraId, camera, info, captureFormat, constructionTimeNs)); surfaceTextureHelper, cameraId, camera, info, captureFormat, constructionTimeNs));
} }
private static void updateCameraParameters(android.hardware.Camera camera, private static void updateCameraParameters(Camera camera, Camera.Parameters parameters,
android.hardware.Camera.Parameters parameters, CaptureFormat captureFormat, Size pictureSize, CaptureFormat captureFormat, Size pictureSize, boolean captureToTexture) {
boolean captureToTexture) {
final List<String> focusModes = parameters.getSupportedFocusModes(); final List<String> focusModes = parameters.getSupportedFocusModes();
parameters.setPreviewFpsRange(captureFormat.framerate.min, captureFormat.framerate.max); parameters.setPreviewFpsRange(captureFormat.framerate.min, captureFormat.framerate.max);
@ -139,14 +139,14 @@ class Camera1Session implements CameraSession {
if (parameters.isVideoStabilizationSupported()) { if (parameters.isVideoStabilizationSupported()) {
parameters.setVideoStabilization(true); parameters.setVideoStabilization(true);
} }
if (focusModes.contains(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { if (focusModes != null && focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
parameters.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
} }
camera.setParameters(parameters); camera.setParameters(parameters);
} }
private static CaptureFormat findClosestCaptureFormat( private static CaptureFormat findClosestCaptureFormat(
android.hardware.Camera.Parameters parameters, int width, int height, int framerate) { Camera.Parameters parameters, int width, int height, int framerate) {
// Find closest supported format for `width` x `height` @ `framerate`. // Find closest supported format for `width` x `height` @ `framerate`.
final List<CaptureFormat.FramerateRange> supportedFramerates = final List<CaptureFormat.FramerateRange> supportedFramerates =
Camera1Enumerator.convertFramerates(parameters.getSupportedPreviewFpsRange()); Camera1Enumerator.convertFramerates(parameters.getSupportedPreviewFpsRange());
@ -162,16 +162,14 @@ class Camera1Session implements CameraSession {
return new CaptureFormat(previewSize.width, previewSize.height, fpsRange); return new CaptureFormat(previewSize.width, previewSize.height, fpsRange);
} }
private static Size findClosestPictureSize( private static Size findClosestPictureSize(Camera.Parameters parameters, int width, int height) {
android.hardware.Camera.Parameters parameters, int width, int height) {
return CameraEnumerationAndroid.getClosestSupportedSize( return CameraEnumerationAndroid.getClosestSupportedSize(
Camera1Enumerator.convertSizes(parameters.getSupportedPictureSizes()), width, height); Camera1Enumerator.convertSizes(parameters.getSupportedPictureSizes()), width, height);
} }
private Camera1Session(Events events, boolean captureToTexture, Context applicationContext, private Camera1Session(Events events, boolean captureToTexture, Context applicationContext,
SurfaceTextureHelper surfaceTextureHelper, int cameraId, android.hardware.Camera camera, SurfaceTextureHelper surfaceTextureHelper, int cameraId, Camera camera,
android.hardware.Camera.CameraInfo info, CaptureFormat captureFormat, Camera.CameraInfo info, CaptureFormat captureFormat, long constructionTimeNs) {
long constructionTimeNs) {
Logging.d(TAG, "Create new camera1 session on camera " + cameraId); Logging.d(TAG, "Create new camera1 session on camera " + cameraId);
this.cameraThreadHandler = new Handler(); this.cameraThreadHandler = new Handler();
@ -208,18 +206,18 @@ class Camera1Session implements CameraSession {
state = SessionState.RUNNING; state = SessionState.RUNNING;
camera.setErrorCallback(new android.hardware.Camera.ErrorCallback() { camera.setErrorCallback(new Camera.ErrorCallback() {
@Override @Override
public void onError(int error, android.hardware.Camera camera) { public void onError(int error, Camera camera) {
String errorMessage; String errorMessage;
if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) { if (error == Camera.CAMERA_ERROR_SERVER_DIED) {
errorMessage = "Camera server died!"; errorMessage = "Camera server died!";
} else { } else {
errorMessage = "Camera error: " + error; errorMessage = "Camera error: " + error;
} }
Logging.e(TAG, errorMessage); Logging.e(TAG, errorMessage);
stopInternal(); stopInternal();
if (error == android.hardware.Camera.CAMERA_ERROR_EVICTED) { if (error == Camera.CAMERA_ERROR_EVICTED) {
events.onCameraDisconnected(Camera1Session.this); events.onCameraDisconnected(Camera1Session.this);
} else { } else {
events.onCameraError(Camera1Session.this, errorMessage); events.onCameraError(Camera1Session.this, errorMessage);
@ -251,7 +249,7 @@ class Camera1Session implements CameraSession {
state = SessionState.STOPPED; state = SessionState.STOPPED;
surfaceTextureHelper.stopListening(); surfaceTextureHelper.stopListening();
// Note: stopPreview or other driver code might deadlock. Deadlock in // Note: stopPreview or other driver code might deadlock. Deadlock in
// android.hardware.Camera._stopPreview(Native Method) has been observed on // Camera._stopPreview(Native Method) has been observed on
// Nexus 5 (hammerhead), OS version LMY48I. // Nexus 5 (hammerhead), OS version LMY48I.
camera.stopPreview(); camera.stopPreview();
camera.release(); camera.release();
@ -277,10 +275,10 @@ class Camera1Session implements CameraSession {
// Undo the mirror that the OS "helps" us with. // Undo the mirror that the OS "helps" us with.
// http://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int) // http://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)
final VideoFrame modifiedFrame = new VideoFrame( final VideoFrame modifiedFrame =
CameraSession.createTextureBufferWithModifiedTransformMatrix( new VideoFrame(CameraSession.createTextureBufferWithModifiedTransformMatrix(
(TextureBufferImpl) frame.getBuffer(), (TextureBufferImpl) frame.getBuffer(),
/* mirror= */ info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT, /* mirror= */ info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT,
/* rotation= */ 0), /* rotation= */ 0),
/* rotation= */ getFrameOrientation(), frame.getTimestampNs()); /* rotation= */ getFrameOrientation(), frame.getTimestampNs());
events.onFrameCaptured(Camera1Session.this, modifiedFrame); events.onFrameCaptured(Camera1Session.this, modifiedFrame);
@ -289,9 +287,9 @@ class Camera1Session implements CameraSession {
} }
private void listenForBytebufferFrames() { private void listenForBytebufferFrames() {
camera.setPreviewCallbackWithBuffer(new android.hardware.Camera.PreviewCallback() { camera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
@Override @Override
public void onPreviewFrame(final byte[] data, android.hardware.Camera callbackCamera) { public void onPreviewFrame(final byte[] data, Camera callbackCamera) {
checkIsOnCameraThread(); checkIsOnCameraThread();
if (callbackCamera != camera) { if (callbackCamera != camera) {
@ -328,7 +326,7 @@ class Camera1Session implements CameraSession {
private int getFrameOrientation() { private int getFrameOrientation() {
int rotation = CameraSession.getDeviceOrientation(applicationContext); int rotation = CameraSession.getDeviceOrientation(applicationContext);
if (info.facing == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK) { if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
rotation = 360 - rotation; rotation = 360 - rotation;
} }
return (info.orientation + rotation) % 360; return (info.orientation + rotation) % 360;