diff options
| -rw-r--r-- | api/current.txt | 11 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java | 4 | ||||
| -rw-r--r-- | media/java/android/media/Image.java | 3 | ||||
| -rw-r--r-- | media/java/android/media/ImageReader.java | 145 | ||||
| -rw-r--r-- | media/jni/android_media_ImageReader.cpp | 35 | ||||
| -rw-r--r-- | media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java | 91 |
6 files changed, 136 insertions, 153 deletions
diff --git a/api/current.txt b/api/current.txt index c7a311b997be..a67512ae89ca 100644 --- a/api/current.txt +++ b/api/current.txt @@ -12350,8 +12350,8 @@ package android.media { } public class ImageReader implements java.lang.AutoCloseable { - method public android.media.Image acquireLatestImage() throws android.media.ImageReader.MaxImagesAcquiredException; - method public android.media.Image acquireNextImage() throws android.media.ImageReader.MaxImagesAcquiredException; + method public android.media.Image acquireLatestImage(); + method public android.media.Image acquireNextImage(); method public void close(); method public int getHeight(); method public int getImageFormat(); @@ -12362,13 +12362,6 @@ package android.media { method public void setOnImageAvailableListener(android.media.ImageReader.OnImageAvailableListener, android.os.Handler); } - public static class ImageReader.MaxImagesAcquiredException extends java.lang.Exception { - ctor public ImageReader.MaxImagesAcquiredException(); - ctor public ImageReader.MaxImagesAcquiredException(java.lang.String); - ctor public ImageReader.MaxImagesAcquiredException(java.lang.String, java.lang.Throwable); - ctor public ImageReader.MaxImagesAcquiredException(java.lang.Throwable); - } - public static abstract interface ImageReader.OnImageAvailableListener { method public abstract void onImageAvailable(android.media.ImageReader); } diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java index 8278579c0fa8..a2e9ae89e255 100644 --- a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java +++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java @@ -26,7 +26,6 @@ import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.media.Image; import android.media.ImageReader; -import android.media.ImageReader.MaxImagesAcquiredException; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -427,9 +426,6 @@ public class VirtualDisplayTest extends AndroidTestCase { image.close(); } } - } catch (MaxImagesAcquiredException e) { - // We should never try to consume more buffers than maxImages. - throw new IllegalStateException(e); } finally { mImageReaderLock.unlock(); } diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java index 915b09e5a37c..a346e176984e 100644 --- a/media/java/android/media/Image.java +++ b/media/java/android/media/Image.java @@ -40,8 +40,7 @@ import java.lang.AutoCloseable; * availability of new Images once * {@link ImageReader#getMaxImages the maximum outstanding image count} is * reached. When this happens, the function acquiring new Images will typically - * throw a - * {@link ImageReader.MaxImagesAcquiredException MaxImagesAcquiredException}.</p> + * throw an {@link IllegalStateException}.</p> * * @see ImageReader */ diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java index e5bcb3e5b0cc..aee83627bb0e 100644 --- a/media/java/android/media/ImageReader.java +++ b/media/java/android/media/ImageReader.java @@ -49,43 +49,20 @@ import java.nio.ByteOrder; public class ImageReader implements AutoCloseable { /** - * <p> - * This exception is thrown when the user of an {@link ImageReader} tries to acquire a new - * {@link Image} when the maximum number of {@link Image Images} have already been acquired. - * The maximum number is determined by the {@code maxBuffers} argument of - * {@link ImageReader#newInstance newInstance}. - * </p> - * - * <p> - * To recover from this exception, release existing {@link Image images} back to the - * reader with {@link Image#close}. - * </p> - * - * @see Image#close - * @see ImageReader#acquireLatestImage - * @see ImageReader#acquireNextImage + * Returned by nativeImageSetup when acquiring the image was successful. */ - public static class MaxImagesAcquiredException extends Exception { - /** - * Suppress Eclipse warnings - */ - private static final long serialVersionUID = 761231231236L; - - public MaxImagesAcquiredException() { - } - - public MaxImagesAcquiredException(String message) { - super(message); - } - - public MaxImagesAcquiredException(String message, Throwable throwable) { - super(message, throwable); - } - - public MaxImagesAcquiredException(Throwable throwable) { - super(throwable); - } - } + private static final int ACQUIRE_SUCCESS = 0; + /** + * Returned by nativeImageSetup when we couldn't acquire the buffer, + * because there were no buffers available to acquire. + */ + private static final int ACQUIRE_NO_BUFS = 1; + /** + * Returned by nativeImageSetup when we couldn't acquire the buffer + * because the consumer has already acquired {@maxImages} and cannot + * acquire more than that. + */ + private static final int ACQUIRE_MAX_IMAGES = 2; /** * <p>Create a new reader for images of the desired size and format.</p> @@ -195,7 +172,7 @@ public class ImageReader implements AutoCloseable { * </p> * * <p>Attempting to acquire more than {@code maxImages} concurrently will result in the - * acquire function throwing a {@link MaxImagesAcquiredException}. Furthermore, + * acquire function throwing a {@link IllegalStateException}. Furthermore, * while the max number of images have been acquired by the ImageReader user, the producer * enqueueing additional images may stall until at least one image has been released. </p> * @@ -243,26 +220,26 @@ public class ImageReader implements AutoCloseable { * {@code (maxImages - currentAcquiredImages < 2)} will not discard as expected. * </p> * <p> - * This operation will fail by throwing an {@link MaxImagesAcquiredException} if + * This operation will fail by throwing an {@link IllegalStateException} if * {@code maxImages} have been acquired with {@link #acquireLatestImage} or * {@link #acquireNextImage}. In particular a sequence of {@link #acquireLatestImage} * calls greater than {@link #getMaxImages} without calling {@link Image#close} in-between - * will exhaust the underlying queue. At such a time, {@link MaxImagesAcquiredException} + * will exhaust the underlying queue. At such a time, {@link IllegalStateException} * will be thrown until more images are * released with {@link Image#close}. * </p> * * @return latest frame of image data, or {@code null} if no image data is available. - * @throws MaxImagesAcquiredException if too many images are currently acquired + * @throws IllegalStateException if too many images are currently acquired */ - public Image acquireLatestImage() throws MaxImagesAcquiredException { + public Image acquireLatestImage() { Image image = acquireNextImage(); if (image == null) { return null; } try { for (;;) { - Image next = acquireNextImageNoThrow(); + Image next = acquireNextImageNoThrowISE(); if (next == null) { Image result = image; image = null; @@ -278,12 +255,48 @@ public class ImageReader implements AutoCloseable { } } - private Image acquireNextImageNoThrow() { - try { - return acquireNextImage(); - } catch (MaxImagesAcquiredException ex) { - return null; + /** + * Don't throw IllegalStateException if there are too many images acquired. + * + * @return Image if acquiring succeeded, or null otherwise. + * + * @hide + */ + public Image acquireNextImageNoThrowISE() { + SurfaceImage si = new SurfaceImage(); + return acquireNextSurfaceImage(si) == ACQUIRE_SUCCESS ? si : null; + } + + /** + * Attempts to acquire the next image from the underlying native implementation. + * + * <p> + * Note that unexpected failures will throw at the JNI level. + * </p> + * + * @param si A blank SurfaceImage. + * @return One of the {@code ACQUIRE_*} codes that determine success or failure. + * + * @see #ACQUIRE_MAX_IMAGES + * @see #ACQUIRE_NO_BUFS + * @see #ACQUIRE_SUCCESS + */ + private int acquireNextSurfaceImage(SurfaceImage si) { + + int status = nativeImageSetup(si); + + switch (status) { + case ACQUIRE_SUCCESS: + si.createSurfacePlanes(); + si.setImageValid(true); + case ACQUIRE_NO_BUFS: + case ACQUIRE_MAX_IMAGES: + break; + default: + throw new AssertionError("Unknown nativeImageSetup return code " + status); } + + return status; } /** @@ -301,28 +314,36 @@ public class ImageReader implements AutoCloseable { * </p> * * <p> - * This operation will fail by throwing an {@link MaxImagesAcquiredException} if + * This operation will fail by throwing an {@link IllegalStateException} if * {@code maxImages} have been acquired with {@link #acquireNextImage} or * {@link #acquireLatestImage}. In particular a sequence of {@link #acquireNextImage} or * {@link #acquireLatestImage} calls greater than {@link #getMaxImages maxImages} without * calling {@link Image#close} in-between will exhaust the underlying queue. At such a time, - * {@link MaxImagesAcquiredException} will be thrown until more images are released with + * {@link IllegalStateException} will be thrown until more images are released with * {@link Image#close}. * </p> * * @return a new frame of image data, or {@code null} if no image data is available. - * @throws MaxImagesAcquiredException if {@code maxImages} images are currently acquired + * @throws IllegalStateException if {@code maxImages} images are currently acquired * @see #acquireLatestImage */ - public Image acquireNextImage() throws MaxImagesAcquiredException { + public Image acquireNextImage() { SurfaceImage si = new SurfaceImage(); - if (nativeImageSetup(si)) { - // create SurfacePlane objects - si.createSurfacePlanes(); - si.setImageValid(true); - return si; + int status = acquireNextSurfaceImage(si); + + switch (status) { + case ACQUIRE_SUCCESS: + return si; + case ACQUIRE_NO_BUFS: + return null; + case ACQUIRE_MAX_IMAGES: + throw new IllegalStateException( + String.format( + "maxImages (%d) has already been acquired, " + + "call #close before acquiring more.", mMaxImages)); + default: + throw new AssertionError("Unknown nativeImageSetup return code " + status); } - return null; } /** @@ -658,7 +679,15 @@ public class ImageReader implements AutoCloseable { private synchronized native void nativeClose(); private synchronized native void nativeReleaseImage(Image i); private synchronized native Surface nativeGetSurface(); - private synchronized native boolean nativeImageSetup(Image i); + + /** + * @return A return code {@code ACQUIRE_*} + * + * @see #ACQUIRE_SUCCESS + * @see #ACQUIRE_NO_BUFS + * @see #ACQUIRE_MAX_IMAGES + */ + private synchronized native int nativeImageSetup(Image i); /** * We use a class initializer to allow the native code to cache some diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp index 56ae6b44a03f..e0195c6734c4 100644 --- a/media/jni/android_media_ImageReader.cpp +++ b/media/jni/android_media_ImageReader.cpp @@ -43,13 +43,16 @@ using namespace android; -static const char* const MaxImagesAcquiredException = - "android/media/ImageReader$MaxImagesAcquiredException"; - enum { IMAGE_READER_MAX_NUM_PLANES = 3, }; +enum { + ACQUIRE_SUCCESS = 0, + ACQUIRE_NO_BUFFERS = 1, + ACQUIRE_MAX_IMAGES = 2, +}; + static struct { jfieldID mNativeContext; jmethodID postEventFromNative; @@ -685,14 +688,14 @@ static void ImageReader_imageRelease(JNIEnv* env, jobject thiz, jobject image) ctx->returnLockedBuffer(buffer); } -static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz, +static jint ImageReader_imageSetup(JNIEnv* env, jobject thiz, jobject image) { ALOGV("%s:", __FUNCTION__); JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz); if (ctx == NULL) { jniThrowRuntimeException(env, "ImageReaderContext is not initialized"); - return false; + return -1; } CpuConsumer* consumer = ctx->getCpuConsumer(); @@ -700,27 +703,22 @@ static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz, if (buffer == NULL) { ALOGW("Unable to acquire a lockedBuffer, very likely client tries to lock more than" " maxImages buffers"); - jniThrowException(env, MaxImagesAcquiredException, - "Too many outstanding images, close existing images" - " to be able to acquire more."); - return false; + return ACQUIRE_MAX_IMAGES; } status_t res = consumer->lockNextBuffer(buffer); if (res != NO_ERROR) { if (res != BAD_VALUE /*no buffers*/) { if (res == NOT_ENOUGH_DATA) { - jniThrowException(env, MaxImagesAcquiredException, - "Too many outstanding images, close existing images" - " to be able to acquire more."); + return ACQUIRE_MAX_IMAGES; } else { ALOGE("%s Fail to lockNextBuffer with error: %d ", __FUNCTION__, res); - jniThrowExceptionFmt(env, "java/lang/IllegalStateException", + jniThrowExceptionFmt(env, "java/lang/AssertionError", "Unknown error (%d) when we tried to lock buffer.", res); } } - return false; + return ACQUIRE_NO_BUFFERS; } // Check if the left-top corner of the crop rect is origin, we currently assume this point is @@ -730,7 +728,7 @@ static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz, ALOGE("crop left: %d, top = %d", lt.x, lt.y); jniThrowException(env, "java/lang/UnsupportedOperationException", "crop left top corner need to at origin"); - return false; + return -1; } // Check if the producer buffer configurations match what ImageReader configured. @@ -761,6 +759,7 @@ static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz, jniThrowExceptionFmt(env, "java/lang/IllegalStateException", "Producer buffer size: %dx%d, doesn't match ImageReader configured size: %dx%d", outputWidth, outputHeight, imageReaderWidth, imageReaderHeight); + return -1; } if (ctx->getBufferFormat() != buffer->format) { @@ -777,14 +776,14 @@ static jboolean ImageReader_imageSetup(JNIEnv* env, jobject thiz, buffer->format, ctx->getBufferFormat()); jniThrowException(env, "java/lang/UnsupportedOperationException", msg.string()); - return false; + return -1; } // Set SurfaceImage instance member variables Image_setBuffer(env, image, buffer); env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp, static_cast<jlong>(buffer->timestamp)); - return true; + return ACQUIRE_SUCCESS; } static jobject ImageReader_getSurface(JNIEnv* env, jobject thiz) @@ -855,7 +854,7 @@ 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;)Z", (void*)ImageReader_imageSetup }, + {"nativeImageSetup", "(Landroid/media/Image;)I", (void*)ImageReader_imageSetup }, {"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface }, }; diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java index 900fff48ed0d..f6cd9901cd39 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/ImageReaderTest.java @@ -23,7 +23,6 @@ import android.media.Image; import android.media.Image.Plane; import android.media.ImageReader; import android.media.ImageReader.OnImageAvailableListener; -import android.media.ImageReader.MaxImagesAcquiredException; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; @@ -67,7 +66,6 @@ public class ImageReaderTest extends AndroidTestCase { { mock(Plane.class); mock(OnImageAvailableListener.class); - mock(MaxImagesAcquiredException.class); } } @@ -83,8 +81,9 @@ public class ImageReaderTest extends AndroidTestCase { * Return null when there is nothing in the image queue. */ @SmallTest - public void testGetLatestImageEmpty() throws MaxImagesAcquiredException { + public void testGetLatestImageEmpty() { when(mReader.acquireNextImage()).thenReturn(null); + when(mReader.acquireNextImageNoThrowISE()).thenReturn(null); assertEquals(null, mReader.acquireLatestImage()); } @@ -92,8 +91,9 @@ public class ImageReaderTest extends AndroidTestCase { * Return the last image from the image queue, close up the rest. */ @SmallTest - public void testGetLatestImage1() throws MaxImagesAcquiredException { - when(mReader.acquireNextImage()).thenReturn(mImage1).thenReturn(null); + public void testGetLatestImage1() { + when(mReader.acquireNextImage()).thenReturn(mImage1); + when(mReader.acquireNextImageNoThrowISE()).thenReturn(null); assertEquals(mImage1, mReader.acquireLatestImage()); verify(mImage1, never()).close(); } @@ -102,8 +102,9 @@ public class ImageReaderTest extends AndroidTestCase { * Return the last image from the image queue, close up the rest. */ @SmallTest - public void testGetLatestImage2() throws MaxImagesAcquiredException { - when(mReader.acquireNextImage()).thenReturn(mImage1).thenReturn(mImage2).thenReturn(null); + public void testGetLatestImage2() { + when(mReader.acquireNextImage()).thenReturn(mImage1); + when(mReader.acquireNextImageNoThrowISE()).thenReturn(mImage2).thenReturn(null); assertEquals(mImage2, mReader.acquireLatestImage()); verify(mImage1, atLeastOnce()).close(); verify(mImage2, never()).close(); @@ -113,10 +114,11 @@ public class ImageReaderTest extends AndroidTestCase { * Return the last image from the image queue, close up the rest. */ @SmallTest - public void testGetLatestImage3() throws MaxImagesAcquiredException { - when(mReader.acquireNextImage()).thenReturn(mImage1).thenReturn(mImage2). - thenReturn(mImage3). - thenReturn(null); + public void testGetLatestImage3() { + when(mReader.acquireNextImage()).thenReturn(mImage1); + when(mReader.acquireNextImageNoThrowISE()).thenReturn(mImage2). + thenReturn(mImage3). + thenReturn(null); assertEquals(mImage3, mReader.acquireLatestImage()); verify(mImage1, atLeastOnce()).close(); verify(mImage2, atLeastOnce()).close(); @@ -124,64 +126,27 @@ public class ImageReaderTest extends AndroidTestCase { } /** - * Return null if get a MaxImagesAcquiredException with no images in the queue. + * Return null if get a IllegalStateException with no images in the queue. */ @SmallTest - public void testGetLatestImageTooManyBuffersAcquiredEmpty() throws MaxImagesAcquiredException { - when(mReader.acquireNextImage()).thenThrow(new MaxImagesAcquiredException()); + public void testGetLatestImageTooManyBuffersAcquiredEmpty() { + when(mReader.acquireNextImage()).thenThrow(new IllegalStateException()); try { mReader.acquireLatestImage(); - fail("Expected MaxImagesAcquiredException to be thrown"); - } catch(MaxImagesAcquiredException e) { + fail("Expected IllegalStateException to be thrown"); + } catch(IllegalStateException e) { } } /** - * Return the last image before we get a MaxImagesAcquiredException. Close up the rest. - */ - @SmallTest - public void testGetLatestImageTooManyBuffersAcquired1() throws MaxImagesAcquiredException { - when(mReader.acquireNextImage()).thenReturn(mImage1). - thenThrow(new MaxImagesAcquiredException()); - assertEquals(mImage1, mReader.acquireLatestImage()); - verify(mImage1, never()).close(); - } - - /** - * Return the last image before we get a MaxImagesAcquiredException. Close up the rest. - */ - @SmallTest - public void testGetLatestImageTooManyBuffersAcquired2() throws MaxImagesAcquiredException { - - when(mReader.acquireNextImage()).thenReturn(mImage1).thenReturn(mImage2). - thenThrow(new MaxImagesAcquiredException()); - assertEquals(mImage2, mReader.acquireLatestImage()); - verify(mImage1, atLeastOnce()).close(); - verify(mImage2, never()).close(); - } - - /** - * Return the last image before we get a MaxImagesAcquiredException. Close up the rest. - */ - @SmallTest - public void testGetLatestImageTooManyBuffersAcquired3() throws MaxImagesAcquiredException { - when(mReader.acquireNextImage()).thenReturn(mImage1).thenReturn(mImage2). - thenReturn(mImage3). - thenThrow(new MaxImagesAcquiredException()); - assertEquals(mImage3, mReader.acquireLatestImage()); - verify(mImage1, atLeastOnce()).close(); - verify(mImage2, atLeastOnce()).close(); - verify(mImage3, never()).close(); - } - - /** * All images are cleaned up when we get an unexpected Error. */ @SmallTest - public void testGetLatestImageExceptionalError() throws MaxImagesAcquiredException { - when(mReader.acquireNextImage()).thenReturn(mImage1).thenReturn(mImage2). - thenReturn(mImage3). - thenThrow(new OutOfMemoryError()); + public void testGetLatestImageExceptionalError() { + when(mReader.acquireNextImage()).thenReturn(mImage1); + when(mReader.acquireNextImageNoThrowISE()).thenReturn(mImage2). + thenReturn(mImage3). + thenThrow(new OutOfMemoryError()); try { mReader.acquireLatestImage(); fail("Impossible"); @@ -197,10 +162,12 @@ public class ImageReaderTest extends AndroidTestCase { * All images are cleaned up when we get an unexpected RuntimeException. */ @SmallTest - public void testGetLatestImageExceptionalRuntime() throws MaxImagesAcquiredException { - when(mReader.acquireNextImage()).thenReturn(mImage1).thenReturn(mImage2). - thenReturn(mImage3). - thenThrow(new RuntimeException()); + public void testGetLatestImageExceptionalRuntime() { + + when(mReader.acquireNextImage()).thenReturn(mImage1); + when(mReader.acquireNextImageNoThrowISE()).thenReturn(mImage2). + thenReturn(mImage3). + thenThrow(new RuntimeException()); try { mReader.acquireLatestImage(); fail("Impossible"); |