diff options
author | Ruben Brunk <rubenbrunk@google.com> | 2014-06-24 16:06:54 -0700 |
---|---|---|
committer | Ruben Brunk <rubenbrunk@google.com> | 2014-06-25 13:00:50 -0700 |
commit | ef14da32804b06bac872c9e0e14ce0e52120a0bd (patch) | |
tree | ae685637f4549774fa90c998f6d57f78f695aa53 /core/java/android/hardware | |
parent | bcd836519713103fede1455a52e1deaa43548edb (diff) | |
download | frameworks_base-ef14da32804b06bac872c9e0e14ce0e52120a0bd.zip frameworks_base-ef14da32804b06bac872c9e0e14ce0e52120a0bd.tar.gz frameworks_base-ef14da32804b06bac872c9e0e14ce0e52120a0bd.tar.bz2 |
camera2: Fix handling for abandoned bufferqueues.
Bug: 15116722
- Adds exception handling utility for legacy device.
- Skip/ignore abandoned surfaces in legacy camera.
Change-Id: Id4de587779d3bc2415e22b10bcc841cc46ac5f1d
Diffstat (limited to 'core/java/android/hardware')
5 files changed, 234 insertions, 70 deletions
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java index b6264dc..4b39092 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java +++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java @@ -35,6 +35,7 @@ import android.view.Surface; import java.util.ArrayList; import java.util.List; +import static android.hardware.camera2.legacy.LegacyExceptionUtils.*; import static android.hardware.camera2.utils.CameraBinderDecorator.*; import static com.android.internal.util.Preconditions.*; @@ -183,8 +184,8 @@ public class LegacyCameraDevice implements AutoCloseable { * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible * format. */ - static boolean needsConversion(Surface s) { - int nativeType = LegacyCameraDevice.nativeDetectSurfaceType(s); + static boolean needsConversion(Surface s) throws BufferQueueAbandonedException { + int nativeType = detectSurfaceType(s); return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 || nativeType == ImageFormat.NV21; } @@ -377,28 +378,71 @@ public class LegacyCameraDevice implements AutoCloseable { * @throws NullPointerException if the {@code surface} was {@code null} * @throws IllegalStateException if the {@code surface} was invalid */ - static Size getSurfaceSize(Surface surface) { + static Size getSurfaceSize(Surface surface) throws BufferQueueAbandonedException { checkNotNull(surface); int[] dimens = new int[2]; - nativeDetectSurfaceDimens(surface, /*out*/dimens); + LegacyExceptionUtils.throwOnError(nativeDetectSurfaceDimens(surface, /*out*/dimens)); return new Size(dimens[0], dimens[1]); } - protected static native int nativeDetectSurfaceType(Surface surface); + static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException { + checkNotNull(surface); + return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface)); + } + + static void configureSurface(Surface surface, int width, int height, + int pixelFormat) throws BufferQueueAbandonedException { + checkNotNull(surface); + checkArgumentPositive(width, "width must be positive."); + checkArgumentPositive(height, "height must be positive."); + + LegacyExceptionUtils.throwOnError(nativeConfigureSurface(surface, width, height, + pixelFormat)); + } + + static void produceFrame(Surface surface, byte[] pixelBuffer, int width, + int height, int pixelFormat) + throws BufferQueueAbandonedException { + checkNotNull(surface); + checkNotNull(pixelBuffer); + checkArgumentPositive(width, "width must be positive."); + checkArgumentPositive(height, "height must be positive."); + + LegacyExceptionUtils.throwOnError(nativeProduceFrame(surface, pixelBuffer, width, height, + pixelFormat)); + } + + static void setSurfaceFormat(Surface surface, int pixelFormat) + throws BufferQueueAbandonedException { + checkNotNull(surface); + + LegacyExceptionUtils.throwOnError(nativeSetSurfaceFormat(surface, pixelFormat)); + } + + static void setSurfaceDimens(Surface surface, int width, int height) + throws BufferQueueAbandonedException { + checkNotNull(surface); + checkArgumentPositive(width, "width must be positive."); + checkArgumentPositive(height, "height must be positive."); + + LegacyExceptionUtils.throwOnError(nativeSetSurfaceDimens(surface, width, height)); + } + + private static native int nativeDetectSurfaceType(Surface surface); - protected static native void nativeDetectSurfaceDimens(Surface surface, + private static native int nativeDetectSurfaceDimens(Surface surface, /*out*/int[/*2*/] dimens); - protected static native void nativeConfigureSurface(Surface surface, int width, int height, + private static native int nativeConfigureSurface(Surface surface, int width, int height, int pixelFormat); - protected static native void nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width, + private static native int nativeProduceFrame(Surface surface, byte[] pixelBuffer, int width, int height, int pixelFormat); - protected static native void nativeSetSurfaceFormat(Surface surface, int pixelFormat); + private static native int nativeSetSurfaceFormat(Surface surface, int pixelFormat); - protected static native void nativeSetSurfaceDimens(Surface surface, int width, int height); + private static native int nativeSetSurfaceDimens(Surface surface, int width, int height); } diff --git a/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java b/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java new file mode 100644 index 0000000..7e0c01b --- /dev/null +++ b/core/java/android/hardware/camera2/legacy/LegacyExceptionUtils.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.camera2.legacy; + +import android.hardware.camera2.utils.CameraBinderDecorator; +import android.util.AndroidException; + +/** + * Utility class containing exception handling used solely by the compatibility mode shim. + */ +public class LegacyExceptionUtils { + private static final String TAG = "LegacyExceptionUtils"; + + /** + * Checked exception thrown when a BufferQueue has been abandoned by its consumer. + */ + public static class BufferQueueAbandonedException extends AndroidException { + public BufferQueueAbandonedException () {} + + public BufferQueueAbandonedException(String name) { + super(name); + } + + public BufferQueueAbandonedException(String name, Throwable cause) { + super(name, cause); + } + + public BufferQueueAbandonedException(Exception cause) { + super(cause); + } + } + + /** + * Throw error codes used by legacy device methods as exceptions. + * + * <p>Non-negative return values are passed through, negative return values are thrown as + * exceptions.</p> + * + * @param errorFlag error to throw as an exception. + * @throws {@link BufferQueueAbandonedException} for {@link CameraBinderDecorator#ENODEV}. + * @throws {@link UnsupportedOperationException} for an unknown negative error code. + * @return {@code errorFlag} if the value was non-negative, throws otherwise. + */ + public static int throwOnError(int errorFlag) throws BufferQueueAbandonedException { + switch (errorFlag) { + case CameraBinderDecorator.NO_ERROR: { + return CameraBinderDecorator.NO_ERROR; + } + case CameraBinderDecorator.ENODEV: { + throw new BufferQueueAbandonedException(); + } + } + + if (errorFlag < 0) { + throw new UnsupportedOperationException("Unknown error " + errorFlag); + } + return errorFlag; + } + + private LegacyExceptionUtils() { + throw new AssertionError(); + } +} diff --git a/core/java/android/hardware/camera2/legacy/RequestHolder.java b/core/java/android/hardware/camera2/legacy/RequestHolder.java index 8a9052f..e674736 100644 --- a/core/java/android/hardware/camera2/legacy/RequestHolder.java +++ b/core/java/android/hardware/camera2/legacy/RequestHolder.java @@ -18,6 +18,7 @@ package android.hardware.camera2.legacy; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.impl.CameraMetadataNative; +import android.util.Log; import android.view.Surface; import java.util.Collection; @@ -26,6 +27,7 @@ import java.util.Collection; * Immutable container for a single capture request and associated information. */ public class RequestHolder { + private static final String TAG = "RequestHolder"; private final boolean mRepeating; private final CaptureRequest mRequest; @@ -89,8 +91,12 @@ public class RequestHolder { */ public boolean hasJpegTargets() { for (Surface s : getHolderTargets()) { - if (jpegType(s)) { - return true; + try { + if (jpegType(s)) { + return true; + } + } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { + Log.w(TAG, "Surface abandoned, skipping...", e); } } return false; @@ -102,8 +108,12 @@ public class RequestHolder { */ public boolean hasPreviewTargets() { for (Surface s : getHolderTargets()) { - if (previewType(s)) { - return true; + try { + if (previewType(s)) { + return true; + } + } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { + Log.w(TAG, "Surface abandoned, skipping...", e); } } return false; @@ -115,8 +125,12 @@ public class RequestHolder { */ public Surface getFirstPreviewTarget() { for (Surface s : getHolderTargets()) { - if (previewType(s)) { - return s; + try { + if (previewType(s)) { + return s; + } + } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { + Log.w(TAG, "Surface abandoned, skipping...", e); } } return null; @@ -128,8 +142,9 @@ public class RequestHolder { * @param s a {@link Surface} to check. * @return true if the surface requires a jpeg buffer. */ - public static boolean jpegType(Surface s) { - if (LegacyCameraDevice.nativeDetectSurfaceType(s) == + public static boolean jpegType(Surface s) + throws LegacyExceptionUtils.BufferQueueAbandonedException { + if (LegacyCameraDevice.detectSurfaceType(s) == CameraMetadataNative.NATIVE_JPEG_FORMAT) { return true; } @@ -149,8 +164,9 @@ public class RequestHolder { * @param s a {@link Surface} to check. * @return true if the surface requires a non-jpeg buffer type. */ - public static boolean previewType(Surface s) { - if (LegacyCameraDevice.nativeDetectSurfaceType(s) != + public static boolean previewType(Surface s) + throws LegacyExceptionUtils.BufferQueueAbandonedException { + if (LegacyCameraDevice.detectSurfaceType(s) != CameraMetadataNative.NATIVE_JPEG_FORMAT) { return true; } diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java index efc2b0e..73b8465 100644 --- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java @@ -201,11 +201,15 @@ public class RequestThreadManager { return; } for (Surface s : holder.getHolderTargets()) { - if (RequestHolder.jpegType(s)) { - Log.i(TAG, "Producing jpeg buffer..."); - LegacyCameraDevice.nativeSetSurfaceDimens(s, data.length, /*height*/1); - LegacyCameraDevice.nativeProduceFrame(s, data, data.length, /*height*/1, - CameraMetadataNative.NATIVE_JPEG_FORMAT); + try { + if (RequestHolder.jpegType(s)) { + Log.i(TAG, "Producing jpeg buffer..."); + LegacyCameraDevice.setSurfaceDimens(s, data.length, /*height*/1); + LegacyCameraDevice.produceFrame(s, data, data.length, /*height*/1, + CameraMetadataNative.NATIVE_JPEG_FORMAT); + } + } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { + Log.w(TAG, "Surface abandoned, dropping frame. ", e); } } mReceivedJpeg.open(); @@ -323,14 +327,18 @@ public class RequestThreadManager { if (outputs != null) { for (Surface s : outputs) { - int format = LegacyCameraDevice.nativeDetectSurfaceType(s); - switch (format) { - case CameraMetadataNative.NATIVE_JPEG_FORMAT: - mCallbackOutputs.add(s); - break; - default: - mPreviewOutputs.add(s); - break; + try { + int format = LegacyCameraDevice.detectSurfaceType(s); + switch (format) { + case CameraMetadataNative.NATIVE_JPEG_FORMAT: + mCallbackOutputs.add(s); + break; + default: + mPreviewOutputs.add(s); + break; + } + } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { + Log.w(TAG, "Surface abandoned, skipping...", e); } } } @@ -338,9 +346,12 @@ public class RequestThreadManager { if (mPreviewOutputs.size() > 0) { List<Size> outputSizes = new ArrayList<>(outputs.size()); for (Surface s : mPreviewOutputs) { - int[] dimens = {0, 0}; - LegacyCameraDevice.nativeDetectSurfaceDimens(s, dimens); - outputSizes.add(new Size(dimens[0], dimens[1])); + try { + Size size = LegacyCameraDevice.getSurfaceSize(s); + outputSizes.add(size); + } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { + Log.w(TAG, "Surface abandoned, skipping...", e); + } } Size largestOutput = findLargestByArea(outputSizes); @@ -434,14 +445,18 @@ public class RequestThreadManager { */ List<Size> configuredJpegSizes = new ArrayList<Size>(); for (Surface callbackSurface : callbackOutputs) { - int format = LegacyCameraDevice.nativeDetectSurfaceType(callbackSurface); + try { + int format = LegacyCameraDevice.detectSurfaceType(callbackSurface); - if (format != CameraMetadataNative.NATIVE_JPEG_FORMAT) { - continue; // Ignore non-JPEG callback formats - } + if (format != CameraMetadataNative.NATIVE_JPEG_FORMAT) { + continue; // Ignore non-JPEG callback formats + } - Size jpegSize = LegacyCameraDevice.getSurfaceSize(callbackSurface); - configuredJpegSizes.add(jpegSize); + Size jpegSize = LegacyCameraDevice.getSurfaceSize(callbackSurface); + configuredJpegSizes.add(jpegSize); + } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { + Log.w(TAG, "Surface abandoned, skipping...", e); + } } if (!configuredJpegSizes.isEmpty()) { /* diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java index bbc7005..e38624a 100644 --- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java +++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java @@ -26,6 +26,7 @@ import android.opengl.GLES11Ext; import android.opengl.GLES20; import android.opengl.Matrix; import android.util.Log; +import android.util.Size; import android.view.Surface; import java.nio.ByteBuffer; @@ -338,22 +339,25 @@ public class SurfaceTextureRenderer { } int maxLength = 0; - int[] dimens = new int[2]; for (EGLSurfaceHolder holder : surfaces) { - LegacyCameraDevice.nativeDetectSurfaceDimens(holder.surface, dimens); - int length = dimens[0] * dimens[1]; - // Find max surface size, ensure PBuffer can hold this many pixels - maxLength = (length > maxLength) ? length : maxLength; - int[] surfaceAttribs = { - EGL14.EGL_WIDTH, dimens[0], - EGL14.EGL_HEIGHT, dimens[1], - EGL14.EGL_NONE - }; - holder.width = dimens[0]; - holder.height = dimens[1]; - holder.eglSurface = - EGL14.eglCreatePbufferSurface(mEGLDisplay, mConfigs, surfaceAttribs, 0); - checkEglError("eglCreatePbufferSurface"); + try { + Size size = LegacyCameraDevice.getSurfaceSize(holder.surface); + int length = size.getWidth() * size.getHeight(); + // Find max surface size, ensure PBuffer can hold this many pixels + maxLength = (length > maxLength) ? length : maxLength; + int[] surfaceAttribs = { + EGL14.EGL_WIDTH, size.getWidth(), + EGL14.EGL_HEIGHT, size.getHeight(), + EGL14.EGL_NONE + }; + holder.width = size.getWidth(); + holder.height = size.getHeight(); + holder.eglSurface = + EGL14.eglCreatePbufferSurface(mEGLDisplay, mConfigs, surfaceAttribs, 0); + checkEglError("eglCreatePbufferSurface"); + } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { + Log.w(TAG, "Surface abandoned, skipping...", e); + } } mPBufferPixels = ByteBuffer.allocateDirect(maxLength * PBUFFER_PIXEL_BYTES) .order(ByteOrder.nativeOrder()); @@ -438,15 +442,19 @@ public class SurfaceTextureRenderer { for (Surface s : surfaces) { // If pixel conversions aren't handled by egl, use a pbuffer - if (LegacyCameraDevice.needsConversion(s)) { - LegacyCameraDevice.nativeSetSurfaceFormat(s, ImageFormat.YV12); - EGLSurfaceHolder holder = new EGLSurfaceHolder(); - holder.surface = s; - mConversionSurfaces.add(holder); - } else { - EGLSurfaceHolder holder = new EGLSurfaceHolder(); - holder.surface = s; - mSurfaces.add(holder); + try { + if (LegacyCameraDevice.needsConversion(s)) { + LegacyCameraDevice.setSurfaceFormat(s, ImageFormat.YV12); + EGLSurfaceHolder holder = new EGLSurfaceHolder(); + holder.surface = s; + mConversionSurfaces.add(holder); + } else { + EGLSurfaceHolder holder = new EGLSurfaceHolder(); + holder.surface = s; + mSurfaces.add(holder); + } + } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { + Log.w(TAG, "Surface abandoned, skipping configuration... ", e); } } @@ -503,10 +511,14 @@ public class SurfaceTextureRenderer { GLES20.glReadPixels(/*x*/ 0, /*y*/ 0, holder.width, holder.height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mPBufferPixels); checkGlError("glReadPixels"); - int format = LegacyCameraDevice.nativeDetectSurfaceType(holder.surface); - LegacyCameraDevice.nativeProduceFrame(holder.surface, mPBufferPixels.array(), - holder.width, holder.height, format); - swapBuffers(holder.eglSurface); + try { + int format = LegacyCameraDevice.detectSurfaceType(holder.surface); + LegacyCameraDevice.produceFrame(holder.surface, mPBufferPixels.array(), + holder.width, holder.height, format); + swapBuffers(holder.eglSurface); + } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { + Log.w(TAG, "Surface abandoned, dropping frame. ", e); + } } } } |