Methods Summary |
---|
public Image | acquireLatestImage()
Acquire the latest {@link Image} from the ImageReader's queue, dropping older
{@link Image images}. Returns {@code null} if no new image is available.
This operation will acquire all the images possible from the ImageReader,
but {@link #close} all images that aren't the latest. This function is
recommended to use over {@link #acquireNextImage} for most use-cases, as it's
more suited for real-time processing.
Note that {@link #getMaxImages maxImages} should be at least 2 for
{@link #acquireLatestImage} to be any different than {@link #acquireNextImage} -
discarding all-but-the-newest {@link Image} requires temporarily acquiring two
{@link Image Images} at once. Or more generally, calling {@link #acquireLatestImage}
with less than two images of margin, that is
{@code (maxImages - currentAcquiredImages < 2)} will not discard as expected.
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 IllegalStateException}
will be thrown until more images are
released with {@link Image#close}.
Image image = acquireNextImage();
if (image == null) {
return null;
}
try {
for (;;) {
Image next = acquireNextImageNoThrowISE();
if (next == null) {
Image result = image;
image = null;
return result;
}
image.close();
image = next;
}
} finally {
if (image != null) {
image.close();
}
}
|
public Image | acquireNextImage()
Acquire the next Image from the ImageReader's queue. Returns {@code null} if
no new image is available.
Warning: Consider using {@link #acquireLatestImage()} instead, as it will
automatically release older images, and allow slower-running processing routines to catch
up to the newest frame. Usage of {@link #acquireNextImage} is recommended for
batch/background processing. Incorrectly using this function can cause images to appear
with an ever-increasing delay, followed by a complete stall where no new images seem to
appear.
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 IllegalStateException} will be thrown until more images are released with
{@link Image#close}.
SurfaceImage si = new SurfaceImage();
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);
}
|
public Image | acquireNextImageNoThrowISE()Don't throw IllegalStateException if there are too many images acquired.
SurfaceImage si = new SurfaceImage();
return acquireNextSurfaceImage(si) == ACQUIRE_SUCCESS ? si : null;
|
private int | acquireNextSurfaceImage(android.media.ImageReader$SurfaceImage si)Attempts to acquire the next image from the underlying native implementation.
Note that unexpected failures will throw at the JNI level.
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;
|
public void | close()Free up all the resources associated with this ImageReader.
After calling this method, this ImageReader can not be used. Calling
any methods on this ImageReader and Images previously provided by
{@link #acquireNextImage} or {@link #acquireLatestImage}
will result in an {@link IllegalStateException}, and attempting to read from
{@link ByteBuffer ByteBuffers} returned by an earlier
{@link Image.Plane#getBuffer Plane#getBuffer} call will
have undefined behavior.
setOnImageAvailableListener(null, null);
nativeClose();
|
protected void | finalize()
try {
close();
} finally {
super.finalize();
}
|
public int | getHeight()The default height of {@link Image Images}, in pixels.
The height may be overridden by the producer sending buffers to this
ImageReader's Surface. If so, the actual height of the images can be
found using {@link Image#getHeight}.
return mHeight;
|
public int | getImageFormat()The default {@link ImageFormat image format} of {@link Image Images}.
Some color formats may be overridden by the producer sending buffers to
this ImageReader's Surface if the default color format allows. ImageReader
guarantees that all {@link Image Images} acquired from ImageReader
(for example, with {@link #acquireNextImage}) will have a "compatible"
format to what was specified in {@link #newInstance}.
As of now, each format is only compatible to itself.
The actual format of the images can be found using {@link Image#getFormat}.
return mFormat;
|
public int | getMaxImages()Maximum number of images that can be acquired from the ImageReader by any time (for example,
with {@link #acquireNextImage}).
An image is considered acquired after it's returned by a function from ImageReader, and
until the Image is {@link Image#close closed} to release the image back to the ImageReader.
Attempting to acquire more than {@code maxImages} concurrently will result in the
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.
return mMaxImages;
|
private int | getNumPlanesFromFormat()Only a subset of the formats defined in
{@link android.graphics.ImageFormat ImageFormat} and
{@link android.graphics.PixelFormat PixelFormat} are supported by
ImageReader. When reading RGB data from a surface, the formats defined in
{@link android.graphics.PixelFormat PixelFormat} can be used, when
reading YUV, JPEG or raw sensor data (for example, from camera or video
decoder), formats from {@link android.graphics.ImageFormat ImageFormat}
are used.
switch (mFormat) {
case ImageFormat.YV12:
case ImageFormat.YUV_420_888:
case ImageFormat.NV21:
return 3;
case ImageFormat.NV16:
return 2;
case PixelFormat.RGB_565:
case PixelFormat.RGBA_8888:
case PixelFormat.RGBX_8888:
case PixelFormat.RGB_888:
case ImageFormat.JPEG:
case ImageFormat.YUY2:
case ImageFormat.Y8:
case ImageFormat.Y16:
case ImageFormat.RAW_SENSOR:
case ImageFormat.RAW10:
return 1;
default:
throw new UnsupportedOperationException(
String.format("Invalid format specified %d", mFormat));
}
|
public android.view.Surface | getSurface()Get a {@link Surface} that can be used to produce {@link Image Images} for this
{@code ImageReader}.
Until valid image data is rendered into this {@link Surface}, the
{@link #acquireNextImage} method will return {@code null}. Only one source
can be producing data into this Surface at the same time, although the
same {@link Surface} can be reused with a different API once the first source is
disconnected from the {@link Surface}.
return mSurface;
|
public int | getWidth()The default width of {@link Image Images}, in pixels.
The width may be overridden by the producer sending buffers to this
ImageReader's Surface. If so, the actual width of the images can be
found using {@link Image#getWidth}.
return mWidth;
|
private static native void | nativeClassInit()We use a class initializer to allow the native code to cache some
field offsets.
|
private native synchronized void | nativeClose()
|
private native synchronized android.view.Surface | nativeGetSurface()
|
private native synchronized int | nativeImageSetup(Image i)
|
private native synchronized void | nativeInit(java.lang.Object weakSelf, int w, int h, int fmt, int maxImgs)
|
private native synchronized void | nativeReleaseImage(Image i)
|
public static android.media.ImageReader | newInstance(int width, int height, int format, int maxImages)Create a new reader for images of the desired size and format.
The {@code maxImages} parameter determines the maximum number of {@link Image}
objects that can be be acquired from the {@code ImageReader}
simultaneously. Requesting more buffers will use up more memory, so it is
important to use only the minimum number necessary for the use case.
The valid sizes and formats depend on the source of the image
data.
return new ImageReader(width, height, format, maxImages);
|
private static void | postEventFromNative(java.lang.Object selfRef)Called from Native code when an Event happens.
This may be called from an arbitrary Binder thread, so access to the ImageReader must be
synchronized appropriately.
@SuppressWarnings("unchecked")
WeakReference<ImageReader> weakSelf = (WeakReference<ImageReader>)selfRef;
final ImageReader ir = weakSelf.get();
if (ir == null) {
return;
}
final Handler handler;
synchronized (ir.mListenerLock) {
handler = ir.mListenerHandler;
}
if (handler != null) {
handler.sendEmptyMessage(0);
}
|
private void | releaseImage(Image i)Return the frame to the ImageReader for reuse.
if (! (i instanceof SurfaceImage) ) {
throw new IllegalArgumentException(
"This image was not produced by an ImageReader");
}
SurfaceImage si = (SurfaceImage) i;
if (si.getReader() != this) {
throw new IllegalArgumentException(
"This image was not produced by this ImageReader");
}
si.clearSurfacePlanes();
nativeReleaseImage(i);
si.setImageValid(false);
|
public void | setOnImageAvailableListener(android.media.ImageReader$OnImageAvailableListener listener, android.os.Handler handler)Register a listener to be invoked when a new image becomes available
from the ImageReader.
synchronized (mListenerLock) {
if (listener != null) {
Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
if (looper == null) {
throw new IllegalArgumentException(
"handler is null but the current thread is not a looper");
}
if (mListenerHandler == null || mListenerHandler.getLooper() != looper) {
mListenerHandler = new ListenerHandler(looper);
}
mListener = listener;
} else {
mListener = null;
mListenerHandler = null;
}
}
|