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:
committed by
WebRTC LUCI CQ
parent
1460e15a45
commit
424b420a22
@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user