ImageReader.java revision dd0643202de80cc4ced37d1844e722c8a5e89154
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.media; 18 19import android.graphics.ImageFormat; 20import android.graphics.PixelFormat; 21import android.os.Handler; 22import android.os.Looper; 23import android.view.Surface; 24 25import java.lang.ref.WeakReference; 26import java.nio.ByteBuffer; 27import java.nio.ByteOrder; 28 29/** 30 * <p>The ImageReader class allows direct application access to image data 31 * rendered into a {@link android.view.Surface}</p> 32 * 33 * <p>Several Android media API classes accept Surface objects as targets to 34 * render to, including {@link MediaPlayer}, {@link MediaCodec}, 35 * {@link android.hardware.camera2.CameraDevice}, and 36 * {@link android.renderscript.Allocation RenderScript Allocations}. The image 37 * sizes and formats that can be used with each source vary, and should be 38 * checked in the documentation for the specific API.</p> 39 * 40 * <p>The image data is encapsulated in {@link Image} objects, and multiple such 41 * objects can be accessed at the same time, up to the number specified by the 42 * {@code maxImages} constructor parameter. New images sent to an ImageReader 43 * through its Surface are queued until accessed through the 44 * {@link #getNextImage} call. Due to memory limits, an image source will 45 * eventually stall or drop Images in trying to render to the Surface if the 46 * ImageReader does not obtain and release Images at a rate equal to the 47 * production rate.</p> 48 */ 49public final class ImageReader implements AutoCloseable { 50 51 /** 52 * <p>Create a new reader for images of the desired size and format.</p> 53 * 54 * <p>The maxImages parameter determines the maximum number of {@link Image} 55 * objects that can be be acquired from the ImageReader 56 * simultaneously. Requesting more buffers will use up more memory, so it is 57 * important to use only the minimum number necessary for the use case.</p> 58 * 59 * <p>The valid sizes and formats depend on the source of the image 60 * data.</p> 61 * 62 * @param width the width in pixels of the Images that this reader will 63 * produce. 64 * @param height the height in pixels of the Images that this reader will 65 * produce. 66 * @param format the format of the Image that this reader will produce. This 67 * must be one of the {@link android.graphics.ImageFormat} or 68 * {@link android.graphics.PixelFormat} constants. 69 * @param maxImages the maximum number of images the user will want to 70 * access simultaneously. This should be as small as possible to limit 71 * memory use. Once maxImages Images are obtained by the user, one of them 72 * has to be released before a new Image will become available for access 73 * through getNextImage(). Must be greater than 0. 74 * 75 * @see Image 76 */ 77 public ImageReader(int width, int height, int format, int maxImages) { 78 mWidth = width; 79 mHeight = height; 80 mFormat = format; 81 mMaxImages = maxImages; 82 83 if (width < 1 || height < 1) { 84 throw new IllegalArgumentException( 85 "The image dimensions must be positive"); 86 } 87 if (mMaxImages < 1) { 88 throw new IllegalArgumentException( 89 "Maximum outstanding image count must be at least 1"); 90 } 91 92 mNumPlanes = getNumPlanesFromFormat(); 93 94 nativeInit(new WeakReference<ImageReader>(this), width, height, format, maxImages); 95 96 mSurface = nativeGetSurface(); 97 } 98 99 public int getWidth() { 100 return mWidth; 101 } 102 103 public int getHeight() { 104 return mHeight; 105 } 106 107 public int getImageFormat() { 108 return mFormat; 109 } 110 111 public int getMaxImages() { 112 return mMaxImages; 113 } 114 115 /** 116 * <p>Get a Surface that can be used to produce Images for this 117 * ImageReader.</p> 118 * 119 * <p>Until valid image data is rendered into this Surface, the 120 * {@link #getNextImage} method will return {@code null}. Only one source 121 * can be producing data into this Surface at the same time, although the 122 * same Surface can be reused with a different API once the first source is 123 * disconnected from the Surface.</p> 124 * 125 * @return A Surface to use for a drawing target for various APIs. 126 */ 127 public Surface getSurface() { 128 return mSurface; 129 } 130 131 /** 132 * <p> 133 * Get the next Image from the ImageReader's queue. Returns {@code null} if 134 * no new image is available. 135 * </p> 136 * <p> 137 * This operation will fail by throwing an 138 * {@link Surface.OutOfResourcesException OutOfResourcesException} if too 139 * many images have been acquired with {@link #getNextImage}. In particular 140 * a sequence of {@link #getNextImage} calls greater than {@link #getMaxImages} 141 * without calling {@link Image#close} or {@link #releaseImage} in-between 142 * will exhaust the underlying queue. At such a time, 143 * {@link Surface.OutOfResourcesException OutOfResourcesException} will be 144 * thrown until more images are released with {@link Image#close} or 145 * {@link #releaseImage}. 146 * </p> 147 * 148 * @return a new frame of image data, or {@code null} if no image data is 149 * available. 150 * @throws Surface.OutOfResourcesException if too many images are currently 151 * acquired 152 */ 153 public Image getNextImage() { 154 SurfaceImage si = new SurfaceImage(); 155 if (nativeImageSetup(si)) { 156 // create SurfacePlane objects 157 si.createSurfacePlanes(); 158 si.setImageValid(true); 159 return si; 160 } 161 return null; 162 } 163 164 /** 165 * <p>Return the frame to the ImageReader for reuse.</p> 166 */ 167 public void releaseImage(Image i) { 168 if (! (i instanceof SurfaceImage) ) { 169 throw new IllegalArgumentException( 170 "This image was not produced by an ImageReader"); 171 } 172 SurfaceImage si = (SurfaceImage) i; 173 if (si.getReader() != this) { 174 throw new IllegalArgumentException( 175 "This image was not produced by this ImageReader"); 176 } 177 178 si.clearSurfacePlanes(); 179 nativeReleaseImage(i); 180 si.setImageValid(false); 181 } 182 183 /** 184 * Register a listener to be invoked when a new image becomes available 185 * from the ImageReader. 186 * @param listener the listener that will be run 187 * @param handler The handler on which the listener should be invoked, or null 188 * if the listener should be invoked on the calling thread's looper. 189 * 190 * @throws IllegalArgumentException if no handler specified and the calling thread has no looper 191 */ 192 public void setImageAvailableListener(OnImageAvailableListener listener, Handler handler) { 193 mImageListener = listener; 194 195 Looper looper; 196 mHandler = handler; 197 if (mHandler == null) { 198 if ((looper = Looper.myLooper()) != null) { 199 mHandler = new Handler(); 200 } else { 201 throw new IllegalArgumentException( 202 "Looper doesn't exist in the calling thread"); 203 } 204 } 205 } 206 207 /** 208 * Callback interface for being notified that a new image is available. 209 * The onImageAvailable is called per image basis, that is, callback fires for every new frame 210 * available from ImageReader. 211 */ 212 public interface OnImageAvailableListener { 213 /** 214 * Callback that is called when a new image is available from ImageReader. 215 * @param reader the ImageReader the callback is associated with. 216 * @see ImageReader 217 * @see Image 218 */ 219 void onImageAvailable(ImageReader reader); 220 } 221 222 /** 223 * Free up all the resources associated with this ImageReader. After 224 * Calling this method, this ImageReader can not be used. calling 225 * any methods on this ImageReader and Images previously provided by {@link #getNextImage} 226 * will result in an IllegalStateException, and attempting to read from 227 * ByteBuffers returned by an earlier {@code Plane#getBuffer} call will 228 * have undefined behavior. 229 */ 230 @Override 231 public void close() { 232 nativeClose(); 233 } 234 235 @Override 236 protected void finalize() throws Throwable { 237 try { 238 close(); 239 } finally { 240 super.finalize(); 241 } 242 } 243 244 /** 245 * Only a subset of the formats defined in {@link android.graphics.ImageFormat} and 246 * {@link android.graphics.PixelFormat} are supported by ImageReader. When reading RGB 247 * data from a surface, the formats defined in {@link android.graphics.PixelFormat} 248 * can be used, when reading YUV, JPEG or raw sensor data ( for example, from camera 249 * or video decoder), formats from {@link android.graphics.ImageFormat} are used. 250 */ 251 private int getNumPlanesFromFormat() { 252 switch (mFormat) { 253 case ImageFormat.YV12: 254 case ImageFormat.YUV_420_888: 255 case ImageFormat.NV21: 256 return 3; 257 case ImageFormat.NV16: 258 return 2; 259 case PixelFormat.RGB_565: 260 case PixelFormat.RGBA_8888: 261 case PixelFormat.RGBX_8888: 262 case PixelFormat.RGB_888: 263 case ImageFormat.JPEG: 264 case ImageFormat.YUY2: 265 case ImageFormat.Y8: 266 case ImageFormat.Y16: 267 case ImageFormat.RAW_SENSOR: 268 return 1; 269 default: 270 throw new UnsupportedOperationException( 271 String.format("Invalid format specified %d", mFormat)); 272 } 273 } 274 275 /** 276 * Called from Native code when an Event happens. 277 */ 278 private static void postEventFromNative(Object selfRef) { 279 @SuppressWarnings("unchecked") 280 WeakReference<ImageReader> weakSelf = (WeakReference<ImageReader>)selfRef; 281 final ImageReader ir = weakSelf.get(); 282 if (ir == null) { 283 return; 284 } 285 286 if (ir.mHandler != null) { 287 ir.mHandler.post(new Runnable() { 288 @Override 289 public void run() { 290 ir.mImageListener.onImageAvailable(ir); 291 } 292 }); 293 } 294 } 295 296 private final int mWidth; 297 private final int mHeight; 298 private final int mFormat; 299 private final int mMaxImages; 300 private final int mNumPlanes; 301 private final Surface mSurface; 302 303 private Handler mHandler; 304 private OnImageAvailableListener mImageListener; 305 306 /** 307 * This field is used by native code, do not access or modify. 308 */ 309 private long mNativeContext; 310 311 private class SurfaceImage implements android.media.Image { 312 public SurfaceImage() { 313 mIsImageValid = false; 314 } 315 316 @Override 317 public void close() { 318 if (mIsImageValid) { 319 ImageReader.this.releaseImage(this); 320 } 321 } 322 323 public ImageReader getReader() { 324 return ImageReader.this; 325 } 326 327 @Override 328 public int getFormat() { 329 if (mIsImageValid) { 330 return ImageReader.this.mFormat; 331 } else { 332 throw new IllegalStateException("Image is already released"); 333 } 334 } 335 336 @Override 337 public int getWidth() { 338 if (mIsImageValid) { 339 return ImageReader.this.mWidth; 340 } else { 341 throw new IllegalStateException("Image is already released"); 342 } 343 } 344 345 @Override 346 public int getHeight() { 347 if (mIsImageValid) { 348 return ImageReader.this.mHeight; 349 } else { 350 throw new IllegalStateException("Image is already released"); 351 } 352 } 353 354 @Override 355 public long getTimestamp() { 356 if (mIsImageValid) { 357 return mTimestamp; 358 } else { 359 throw new IllegalStateException("Image is already released"); 360 } 361 } 362 363 @Override 364 public Plane[] getPlanes() { 365 if (mIsImageValid) { 366 // Shallow copy is fine. 367 return mPlanes.clone(); 368 } else { 369 throw new IllegalStateException("Image is already released"); 370 } 371 } 372 373 @Override 374 protected final void finalize() throws Throwable { 375 try { 376 close(); 377 } finally { 378 super.finalize(); 379 } 380 } 381 382 private void setImageValid(boolean isValid) { 383 mIsImageValid = isValid; 384 } 385 386 private boolean isImageValid() { 387 return mIsImageValid; 388 } 389 390 private void clearSurfacePlanes() { 391 if (mIsImageValid) { 392 for (int i = 0; i < mPlanes.length; i++) { 393 if (mPlanes[i] != null) { 394 mPlanes[i].clearBuffer(); 395 mPlanes[i] = null; 396 } 397 } 398 } 399 } 400 401 private void createSurfacePlanes() { 402 mPlanes = new SurfacePlane[ImageReader.this.mNumPlanes]; 403 for (int i = 0; i < ImageReader.this.mNumPlanes; i++) { 404 mPlanes[i] = nativeCreatePlane(i); 405 } 406 } 407 private class SurfacePlane implements android.media.Image.Plane { 408 // SurfacePlane instance is created by native code when a new SurfaceImage is created 409 private SurfacePlane(int index, int rowStride, int pixelStride) { 410 mIndex = index; 411 mRowStride = rowStride; 412 mPixelStride = pixelStride; 413 } 414 415 @Override 416 public ByteBuffer getBuffer() { 417 if (SurfaceImage.this.isImageValid() == false) { 418 throw new IllegalStateException("Image is already released"); 419 } 420 if (mBuffer != null) { 421 return mBuffer; 422 } else { 423 mBuffer = SurfaceImage.this.nativeImageGetBuffer(mIndex); 424 // Set the byteBuffer order according to host endianness (native order), 425 // otherwise, the byteBuffer order defaults to ByteOrder.BIG_ENDIAN. 426 return mBuffer.order(ByteOrder.nativeOrder()); 427 } 428 } 429 430 @Override 431 public int getPixelStride() { 432 if (SurfaceImage.this.isImageValid()) { 433 return mPixelStride; 434 } else { 435 throw new IllegalStateException("Image is already released"); 436 } 437 } 438 439 @Override 440 public int getRowStride() { 441 if (SurfaceImage.this.isImageValid()) { 442 return mRowStride; 443 } else { 444 throw new IllegalStateException("Image is already released"); 445 } 446 } 447 448 private void clearBuffer() { 449 mBuffer = null; 450 } 451 452 final private int mIndex; 453 final private int mPixelStride; 454 final private int mRowStride; 455 456 private ByteBuffer mBuffer; 457 } 458 459 /** 460 * This field is used to keep track of native object and used by native code only. 461 * Don't modify. 462 */ 463 private long mLockedBuffer; 464 465 /** 466 * This field is set by native code during nativeImageSetup(). 467 */ 468 private long mTimestamp; 469 470 private SurfacePlane[] mPlanes; 471 private boolean mIsImageValid; 472 473 private synchronized native ByteBuffer nativeImageGetBuffer(int idx); 474 private synchronized native SurfacePlane nativeCreatePlane(int idx); 475 } 476 477 private synchronized native void nativeInit(Object weakSelf, int w, int h, 478 int fmt, int maxImgs); 479 private synchronized native void nativeClose(); 480 private synchronized native void nativeReleaseImage(Image i); 481 private synchronized native Surface nativeGetSurface(); 482 private synchronized native boolean nativeImageSetup(Image i); 483 484 /* 485 * We use a class initializer to allow the native code to cache some 486 * field offsets. 487 */ 488 private static native void nativeClassInit(); 489 static { 490 System.loadLibrary("media_jni"); 491 nativeClassInit(); 492 } 493} 494