diff options
7 files changed, 214 insertions, 107 deletions
diff --git a/core/java/android/hardware/camera2/legacy/GLThreadManager.java b/core/java/android/hardware/camera2/legacy/GLThreadManager.java index 64c532b..b160d2a 100644 --- a/core/java/android/hardware/camera2/legacy/GLThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/GLThreadManager.java @@ -22,6 +22,8 @@ import android.os.ConditionVariable; import android.os.Handler; import android.os.Message; import android.util.Log; +import android.util.Pair; +import android.util.Size; import android.view.Surface; import java.util.Collection; @@ -57,11 +59,11 @@ public class GLThreadManager { */ private static class ConfigureHolder { public final ConditionVariable condition; - public final Collection<Surface> surfaces; + public final Collection<Pair<Surface, Size>> surfaces; public final CaptureCollector collector; - public ConfigureHolder(ConditionVariable condition, Collection<Surface> surfaces, - CaptureCollector collector) { + public ConfigureHolder(ConditionVariable condition, Collection<Pair<Surface, + Size>> surfaces, CaptureCollector collector) { this.condition = condition; this.surfaces = surfaces; this.collector = collector; @@ -202,10 +204,12 @@ public class GLThreadManager { * Configure the GL renderer for the given set of output surfaces, and block until * this configuration has been applied. * - * @param surfaces a collection of {@link android.view.Surface}s to configure. + * @param surfaces a collection of pairs of {@link android.view.Surface}s and their + * corresponding sizes to configure. * @param collector a {@link CaptureCollector} to retrieve requests from. */ - public void setConfigurationAndWait(Collection<Surface> surfaces, CaptureCollector collector) { + public void setConfigurationAndWait(Collection<Pair<Surface, Size>> surfaces, + CaptureCollector collector) { checkNotNull(collector, "collector must not be null"); Handler handler = mGLHandlerThread.getHandler(); diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java index 3a976ba..3043d13 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java +++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java @@ -24,7 +24,6 @@ import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.impl.CameraDeviceImpl; import android.hardware.camera2.impl.CaptureResultExtras; import android.hardware.camera2.ICameraDeviceCallbacks; -import android.hardware.camera2.params.StreamConfiguration; import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.camera2.utils.ArrayUtils; import android.hardware.camera2.utils.CameraBinderDecorator; @@ -36,6 +35,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.RemoteException; import android.util.Log; +import android.util.Pair; import android.util.Size; import android.view.Surface; @@ -78,6 +78,15 @@ public class LegacyCameraDevice implements AutoCloseable { private final Handler mResultHandler; private static final int ILLEGAL_VALUE = -1; + // Keep up to date with values in hardware/libhardware/include/hardware/gralloc.h + private static final int GRALLOC_USAGE_RENDERSCRIPT = 0x00100000; + private static final int GRALLOC_USAGE_SW_READ_OFTEN = 0x00000003; + private static final int GRALLOC_USAGE_HW_TEXTURE = 0x00000100; + private static final int GRALLOC_USAGE_HW_COMPOSER = 0x00000800; + private static final int GRALLOC_USAGE_HW_VIDEO_ENCODER = 0x00010000; + + private static final int MAX_DIMEN_FOR_ROUNDING = 1080; // maximum allowed width for rounding + private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) { if (holder == null) { return new CaptureResultExtras(ILLEGAL_VALUE, ILLEGAL_VALUE, ILLEGAL_VALUE, @@ -276,6 +285,7 @@ public class LegacyCameraDevice implements AutoCloseable { * on success. */ public int configureOutputs(List<Surface> outputs) { + List<Pair<Surface, Size>> sizedSurfaces = new ArrayList<>(); if (outputs != null) { for (Surface output : outputs) { if (output == null) { @@ -289,16 +299,25 @@ public class LegacyCameraDevice implements AutoCloseable { try { Size s = getSurfaceSize(output); int surfaceType = detectSurfaceType(output); - Size[] sizes = streamConfigurations.getOutputSizes(surfaceType); + int usageFlags = detectSurfaceUsageFlags(output); + // Keep up to date with allowed consumer types in + // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp + int disallowedFlags = GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_RENDERSCRIPT; + int allowedFlags = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_OFTEN | + GRALLOC_USAGE_HW_COMPOSER; + boolean flexibleConsumer = ((usageFlags & disallowedFlags) == 0 && + (usageFlags & allowedFlags) != 0); + + Size[] sizes = streamConfigurations.getOutputSizes(surfaceType); if (sizes == null) { // WAR: Override default format to IMPLEMENTATION_DEFINED for b/9487482 if ((surfaceType >= LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888 && surfaceType <= LegacyMetadataMapper.HAL_PIXEL_FORMAT_BGRA_8888)) { - // YUV_420_888 is always present in LEGACY for all IMPLEMENTATION_DEFINED - // output sizes, and is publicly visible in the API (i.e. - // {@code #getOutputSizes} works here). + // YUV_420_888 is always present in LEGACY for all + // IMPLEMENTATION_DEFINED output sizes, and is publicly visible in the + // API (i.e. {@code #getOutputSizes} works here). sizes = streamConfigurations.getOutputSizes(ImageFormat.YUV_420_888); } else if (surfaceType == LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB) { sizes = streamConfigurations.getOutputSizes(ImageFormat.JPEG); @@ -306,12 +325,18 @@ public class LegacyCameraDevice implements AutoCloseable { } if (!ArrayUtils.contains(sizes, s)) { - String reason = (sizes == null) ? "format is invalid." : - ("size not in valid set: " + Arrays.toString(sizes)); - Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format 0x%x is" - + " not valid, %s", s.getWidth(), s.getHeight(), surfaceType, - reason)); - return BAD_VALUE; + if (flexibleConsumer && (s = findClosestSize(s, sizes)) != null) { + sizedSurfaces.add(new Pair<>(output, s)); + } else { + String reason = (sizes == null) ? "format is invalid." : + ("size not in valid set: " + Arrays.toString(sizes)); + Log.e(TAG, String.format("Surface with size (w=%d, h=%d) and format " + + "0x%x is not valid, %s", s.getWidth(), s.getHeight(), + surfaceType, reason)); + return BAD_VALUE; + } + } else { + sizedSurfaces.add(new Pair<>(output, s)); } } catch (BufferQueueAbandonedException e) { Log.e(TAG, "Surface bufferqueue is abandoned, cannot configure as output: ", e); @@ -323,7 +348,7 @@ public class LegacyCameraDevice implements AutoCloseable { boolean success = false; if (mDeviceState.setConfiguring()) { - mRequestThreadManager.configure(outputs); + mRequestThreadManager.configure(sizedSurfaces); success = mDeviceState.setIdle(); } @@ -473,6 +498,31 @@ public class LegacyCameraDevice implements AutoCloseable { } } + static long findEuclidDistSquare(Size a, Size b) { + long d0 = a.getWidth() - b.getWidth(); + long d1 = a.getHeight() - b.getHeight(); + return d0 * d0 + d1 * d1; + } + + // Keep up to date with rounding behavior in + // frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp + static Size findClosestSize(Size size, Size[] supportedSizes) { + if (size == null || supportedSizes == null) { + return null; + } + Size bestSize = null; + for (Size s : supportedSizes) { + if (s.equals(size)) { + return size; + } else if (s.getWidth() <= MAX_DIMEN_FOR_ROUNDING && (bestSize == null || + LegacyCameraDevice.findEuclidDistSquare(size, s) < + LegacyCameraDevice.findEuclidDistSquare(bestSize, s))) { + bestSize = s; + } + } + return bestSize; + } + /** * Query the surface for its currently configured default buffer size. * @param surface a non-{@code null} {@code Surface} @@ -490,6 +540,11 @@ public class LegacyCameraDevice implements AutoCloseable { return new Size(dimens[0], dimens[1]); } + static int detectSurfaceUsageFlags(Surface surface) { + checkNotNull(surface); + return nativeDetectSurfaceUsageFlags(surface); + } + static int detectSurfaceType(Surface surface) throws BufferQueueAbandonedException { checkNotNull(surface); return LegacyExceptionUtils.throwOnError(nativeDetectSurfaceType(surface)); @@ -608,5 +663,7 @@ public class LegacyCameraDevice implements AutoCloseable { private static native int nativeSetNextTimestamp(Surface surface, long timestamp); + private static native int nativeDetectSurfaceUsageFlags(Surface surface); + static native int nativeGetJpegFooterSize(); } diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java index 35deb71..6535a4e 100644 --- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java @@ -41,6 +41,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.concurrent.TimeUnit; @@ -116,9 +117,10 @@ public class RequestThreadManager { */ private static class ConfigureHolder { public final ConditionVariable condition; - public final Collection<Surface> surfaces; + public final Collection<Pair<Surface, Size>> surfaces; - public ConfigureHolder(ConditionVariable condition, Collection<Surface> surfaces) { + public ConfigureHolder(ConditionVariable condition, Collection<Pair<Surface, + Size>> surfaces) { this.condition = condition; this.surfaces = surfaces; } @@ -317,7 +319,7 @@ public class RequestThreadManager { startPreview(); } - private void configureOutputs(Collection<Surface> outputs) { + private void configureOutputs(Collection<Pair<Surface, Size>> outputs) { if (DEBUG) { String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces"); Log.d(TAG, "configureOutputs with " + outputsStr); @@ -346,10 +348,15 @@ public class RequestThreadManager { mJpegSurfaceIds.clear(); mPreviewTexture = null; + List<Size> previewOutputSizes = new ArrayList<>(); + List<Size> callbackOutputSizes = new ArrayList<>(); + int facing = mCharacteristics.get(CameraCharacteristics.LENS_FACING); int orientation = mCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); if (outputs != null) { - for (Surface s : outputs) { + for (Pair<Surface, Size> outPair : outputs) { + Surface s = outPair.first; + Size outSize = outPair.second; try { int format = LegacyCameraDevice.detectSurfaceType(s); LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation); @@ -362,9 +369,11 @@ public class RequestThreadManager { } mJpegSurfaceIds.add(LegacyCameraDevice.getSurfaceId(s)); mCallbackOutputs.add(s); + callbackOutputSizes.add(outSize); break; default: mPreviewOutputs.add(s); + previewOutputSizes.add(outSize); break; } } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { @@ -391,18 +400,9 @@ public class RequestThreadManager { mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX], bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]); - if (mPreviewOutputs.size() > 0) { - List<Size> outputSizes = new ArrayList<>(outputs.size()); - for (Surface s : mPreviewOutputs) { - try { - Size size = LegacyCameraDevice.getSurfaceSize(s); - outputSizes.add(size); - } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { - Log.w(TAG, "Surface abandoned, skipping...", e); - } - } + if (previewOutputSizes.size() > 0) { - Size largestOutput = SizeAreaComparator.findLargestByArea(outputSizes); + Size largestOutput = SizeAreaComparator.findLargestByArea(previewOutputSizes); // Find largest jpeg dimension - assume to have the same aspect ratio as sensor. Size largestJpegDimen = ParameterUtils.getLargestSupportedJpegSizeByArea(mParams); @@ -439,7 +439,8 @@ public class RequestThreadManager { } } - Size smallestSupportedJpegSize = calculatePictureSize(mCallbackOutputs, mParams); + Size smallestSupportedJpegSize = calculatePictureSize(mCallbackOutputs, + callbackOutputSizes, mParams); if (smallestSupportedJpegSize != null) { /* * Set takePicture size to the smallest supported JPEG size large enough @@ -457,7 +458,12 @@ public class RequestThreadManager { mGLThreadManager.start(); } mGLThreadManager.waitUntilStarted(); - mGLThreadManager.setConfigurationAndWait(mPreviewOutputs, mCaptureCollector); + List<Pair<Surface, Size>> previews = new ArrayList<>(); + Iterator<Size> previewSizeIter = previewOutputSizes.iterator(); + for (Surface p : mPreviewOutputs) { + previews.add(new Pair<>(p, previewSizeIter.next())); + } + mGLThreadManager.setConfigurationAndWait(previews, mCaptureCollector); mGLThreadManager.allowNewFrames(); mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture(); if (mPreviewTexture != null) { @@ -499,26 +505,25 @@ public class RequestThreadManager { * {@code null} if the {@code callbackOutputs} did not have any {@code JPEG} * surfaces. */ - private Size calculatePictureSize( - Collection<Surface> callbackOutputs, Camera.Parameters params) { + private Size calculatePictureSize( List<Surface> callbackOutputs, + List<Size> callbackSizes, Camera.Parameters params) { /* * Find the largest JPEG size (if any), from the configured outputs: * - the api1 picture size should be set to the smallest legal size that's at least as large * as the largest configured JPEG size */ - List<Size> configuredJpegSizes = new ArrayList<Size>(); + if (callbackOutputs.size() != callbackSizes.size()) { + throw new IllegalStateException("Input collections must be same length"); + } + List<Size> configuredJpegSizes = new ArrayList<>(); + Iterator<Size> sizeIterator = callbackSizes.iterator(); for (Surface callbackSurface : callbackOutputs) { - try { - + Size jpegSize = sizeIterator.next(); if (!LegacyCameraDevice.containsSurfaceId(callbackSurface, mJpegSurfaceIds)) { continue; // Ignore non-JPEG callback formats } - Size jpegSize = LegacyCameraDevice.getSurfaceSize(callbackSurface); configuredJpegSizes.add(jpegSize); - } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { - Log.w(TAG, "Surface abandoned, skipping...", e); - } } if (!configuredJpegSizes.isEmpty()) { /* @@ -994,7 +999,7 @@ public class RequestThreadManager { * * @param outputs a {@link java.util.Collection} of outputs to configure. */ - public void configure(Collection<Surface> outputs) { + public void configure(Collection<Pair<Surface, Size>> outputs) { Handler handler = mRequestThread.waitAndGetHandler(); final ConditionVariable condition = new ConditionVariable(/*closed*/false); ConfigureHolder holder = new ConfigureHolder(condition, outputs); diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java index c0d1d5e..4853b81 100644 --- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java +++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java @@ -397,16 +397,9 @@ public class SurfaceTextureRenderer { EGL14.EGL_NONE }; for (EGLSurfaceHolder holder : surfaces) { - try { - Size size = LegacyCameraDevice.getSurfaceSize(holder.surface); - holder.width = size.getWidth(); - holder.height = size.getHeight(); - holder.eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mConfigs, - holder.surface, surfaceAttribs, /*offset*/ 0); - checkEglError("eglCreateWindowSurface"); - } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) { - Log.w(TAG, "Surface abandoned, skipping...", e); - } + holder.eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mConfigs, + holder.surface, surfaceAttribs, /*offset*/ 0); + checkEglError("eglCreateWindowSurface"); } } @@ -417,24 +410,17 @@ public class SurfaceTextureRenderer { int maxLength = 0; for (EGLSurfaceHolder holder : surfaces) { - 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); - } + int length = holder.width * holder.height; + // Find max surface size, ensure PBuffer can hold this many pixels + maxLength = (length > maxLength) ? length : maxLength; + int[] surfaceAttribs = { + EGL14.EGL_WIDTH, holder.width, + EGL14.EGL_HEIGHT, holder.height, + EGL14.EGL_NONE + }; + holder.eglSurface = + EGL14.eglCreatePbufferSurface(mEGLDisplay, mConfigs, surfaceAttribs, 0); + checkEglError("eglCreatePbufferSurface"); } mPBufferPixels = ByteBuffer.allocateDirect(maxLength * PBUFFER_PIXEL_BYTES) .order(ByteOrder.nativeOrder()); @@ -569,7 +555,7 @@ public class SurfaceTextureRenderer { * * @param surfaces a {@link Collection} of surfaces. */ - public void configureSurfaces(Collection<Surface> surfaces) { + public void configureSurfaces(Collection<Pair<Surface, Size>> surfaces) { releaseEGLContext(); if (surfaces == null || surfaces.size() == 0) { @@ -577,18 +563,20 @@ public class SurfaceTextureRenderer { return; } - for (Surface s : surfaces) { + for (Pair<Surface, Size> p : surfaces) { + Surface s = p.first; + Size surfaceSize = p.second; // If pixel conversions aren't handled by egl, use a pbuffer try { + EGLSurfaceHolder holder = new EGLSurfaceHolder(); + holder.surface = s; + holder.width = surfaceSize.getWidth(); + holder.height = surfaceSize.getHeight(); if (LegacyCameraDevice.needsConversion(s)) { // Always override to YV12 output for YUV surface formats. 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) { @@ -672,10 +660,11 @@ public class SurfaceTextureRenderer { List<Long> targetSurfaceIds = LegacyCameraDevice.getSurfaceIds(targetSurfaces); for (EGLSurfaceHolder holder : mSurfaces) { if (LegacyCameraDevice.containsSurfaceId(holder.surface, targetSurfaceIds)) { - makeCurrent(holder.eglSurface); - try { + try{ LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width, holder.height); + makeCurrent(holder.eglSurface); + LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second); drawFrame(mSurfaceTexture, holder.width, holder.height); swapBuffers(holder.eglSurface); @@ -695,10 +684,11 @@ public class SurfaceTextureRenderer { try { int format = LegacyCameraDevice.detectSurfaceType(holder.surface); + LegacyCameraDevice.setSurfaceDimens(holder.surface, holder.width, + holder.height); LegacyCameraDevice.setNextTimestamp(holder.surface, captureHolder.second); 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); } diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp index 8440a0e..b44c829 100644 --- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp +++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp @@ -470,6 +470,26 @@ static jint LegacyCameraDevice_nativeDetectSurfaceDimens(JNIEnv* env, jobject th return NO_ERROR; } +static jint LegacyCameraDevice_nativeDetectSurfaceUsageFlags(JNIEnv* env, jobject thiz, + jobject surface) { + ALOGV("nativeDetectSurfaceUsageFlags"); + + sp<ANativeWindow> anw; + if ((anw = getNativeWindow(env, surface)) == NULL) { + jniThrowException(env, "Ljava/lang/UnsupportedOperationException;", + "Could not retrieve native window from surface."); + return BAD_VALUE; + } + int32_t usage = 0; + status_t err = anw->query(anw.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS, &usage); + if(err != NO_ERROR) { + jniThrowException(env, "Ljava/lang/UnsupportedOperationException;", + "Error while querying surface usage bits"); + return err; + } + return usage; +} + static jint LegacyCameraDevice_nativeDetectTextureDimens(JNIEnv* env, jobject thiz, jobject surfaceTexture, jintArray dimens) { ALOGV("nativeDetectTextureDimens"); @@ -713,6 +733,9 @@ static JNINativeMethod gCameraDeviceMethods[] = { { "nativeGetJpegFooterSize", "()I", (void *)LegacyCameraDevice_nativeGetJpegFooterSize }, + { "nativeDetectSurfaceUsageFlags", + "(Landroid/view/Surface;)I", + (void *)LegacyCameraDevice_nativeDetectSurfaceUsageFlags }, }; // Get all the required offsets in java class and register native functions diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java index b541454..fed0f13 100644 --- a/media/java/android/media/ImageReader.java +++ b/media/java/android/media/ImageReader.java @@ -577,7 +577,11 @@ public class ImageReader implements AutoCloseable { @Override public int getWidth() { if (mIsImageValid) { - return ImageReader.this.mWidth; + if (mWidth == -1) { + mWidth = (getFormat() == ImageFormat.JPEG) ? ImageReader.this.getWidth() : + nativeGetWidth(); + } + return mWidth; } else { throw new IllegalStateException("Image is already released"); } @@ -586,7 +590,11 @@ public class ImageReader implements AutoCloseable { @Override public int getHeight() { if (mIsImageValid) { - return ImageReader.this.mHeight; + if (mHeight == -1) { + mHeight = (getFormat() == ImageFormat.JPEG) ? ImageReader.this.getHeight() : + nativeGetHeight(); + } + return mHeight; } else { throw new IllegalStateException("Image is already released"); } @@ -711,9 +719,13 @@ public class ImageReader implements AutoCloseable { private SurfacePlane[] mPlanes; private boolean mIsImageValid; + private int mHeight = -1; + private int mWidth = -1; private synchronized native ByteBuffer nativeImageGetBuffer(int idx, int readerFormat); private synchronized native SurfacePlane nativeCreatePlane(int idx, int readerFormat); + private synchronized native int nativeGetWidth(); + private synchronized native int nativeGetHeight(); } private synchronized native void nativeInit(Object weakSelf, int w, int h, diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp index 7830c80..3f4736d 100644 --- a/media/jni/android_media_ImageReader.cpp +++ b/media/jni/android_media_ImageReader.cpp @@ -615,6 +615,24 @@ static jint Image_imageGetRowStride(JNIEnv* env, CpuConsumer::LockedBuffer* buff return rowStride; } +static int Image_getBufferWidth(CpuConsumer::LockedBuffer* buffer) { + if (buffer == NULL) return -1; + + if (!buffer->crop.isEmpty()) { + return buffer->crop.getWidth(); + } + return buffer->width; +} + +static int Image_getBufferHeight(CpuConsumer::LockedBuffer* buffer) { + if (buffer == NULL) return -1; + + if (!buffer->crop.isEmpty()) { + return buffer->crop.getHeight(); + } + return buffer->height; +} + // ---------------------------------------------------------------------------- static void ImageReader_classInit(JNIEnv* env, jclass clazz) @@ -795,33 +813,16 @@ static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz, } // Check if the producer buffer configurations match what ImageReader configured. - // We want to fail for the very first image because this case is too bad. - int outputWidth = buffer->width; - int outputHeight = buffer->height; - - // Correct width/height when crop is set. - if (!buffer->crop.isEmpty()) { - outputWidth = buffer->crop.getWidth(); - outputHeight = buffer->crop.getHeight(); - } + int outputWidth = Image_getBufferWidth(buffer); + int outputHeight = Image_getBufferHeight(buffer); int imgReaderFmt = ctx->getBufferFormat(); int imageReaderWidth = ctx->getBufferWidth(); int imageReaderHeight = ctx->getBufferHeight(); if ((buffer->format != HAL_PIXEL_FORMAT_BLOB) && (imgReaderFmt != HAL_PIXEL_FORMAT_BLOB) && - (imageReaderWidth != outputWidth || imageReaderHeight > outputHeight)) { - /** - * For video decoder, the buffer height is actually the vertical stride, - * which is always >= actual image height. For future, decoder need provide - * right crop rectangle to CpuConsumer to indicate the actual image height, - * see bug 9563986. After this bug is fixed, we can enforce the height equal - * check. Right now, only make sure buffer height is no less than ImageReader - * height. - */ - jniThrowExceptionFmt(env, "java/lang/IllegalStateException", - "Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d", - outputWidth, outputHeight, imageReaderWidth, imageReaderHeight); - return -1; + (imageReaderWidth != outputWidth || imageReaderHeight != outputHeight)) { + ALOGV("%s: Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d", + __FUNCTION__, outputWidth, outputHeight, imageReaderWidth, imageReaderHeight); } int bufFmt = buffer->format; @@ -934,6 +935,19 @@ static jobject Image_getByteBuffer(JNIEnv* env, jobject thiz, int idx, int reade return byteBuffer; } +static jint Image_getWidth(JNIEnv* env, jobject thiz) +{ + CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz); + return Image_getBufferWidth(buffer); +} + +static jint Image_getHeight(JNIEnv* env, jobject thiz) +{ + CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz); + return Image_getBufferHeight(buffer); +} + + } // extern "C" // ---------------------------------------------------------------------------- @@ -943,14 +957,16 @@ static JNINativeMethod gImageReaderMethods[] = { {"nativeInit", "(Ljava/lang/Object;IIII)V", (void*)ImageReader_init }, {"nativeClose", "()V", (void*)ImageReader_close }, {"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease }, - {"nativeImageSetup", "(Landroid/media/Image;)I", (void*)ImageReader_imageSetup }, + {"nativeImageSetup", "(Landroid/media/Image;)I", (void*)ImageReader_imageSetup }, {"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface }, }; static JNINativeMethod gImageMethods[] = { {"nativeImageGetBuffer", "(II)Ljava/nio/ByteBuffer;", (void*)Image_getByteBuffer }, {"nativeCreatePlane", "(II)Landroid/media/ImageReader$SurfaceImage$SurfacePlane;", - (void*)Image_createSurfacePlane }, + (void*)Image_createSurfacePlane }, + {"nativeGetWidth", "()I", (void*)Image_getWidth }, + {"nativeGetHeight", "()I", (void*)Image_getHeight }, }; int register_android_media_ImageReader(JNIEnv *env) { |